├── .gitignore ├── LICENSE ├── README.md ├── apps ├── dmenu │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── arg.h │ ├── config.def.h │ ├── config.h │ ├── config.mk │ ├── dmenu │ ├── dmenu.1 │ ├── dmenu.c │ ├── dmenu.o │ ├── dmenu_path │ ├── dmenu_run │ ├── drw.c │ ├── drw.h │ ├── drw.o │ ├── patches │ │ ├── dmenu-border-4.9.diff │ │ ├── dmenu-center-20200111-8cd37e1.diff │ │ ├── dmenu-fuzzyhighlight-4.9.diff │ │ ├── dmenu-lineheight-4.9.diff │ │ └── dmenu-mousesupporthoverbgcol-5.0.diff │ ├── stest │ ├── stest.1 │ ├── stest.c │ ├── stest.o │ ├── util.c │ ├── util.h │ └── util.o └── conky │ ├── sysinfo.lua │ └── time.lua ├── config.py ├── images ├── app_menu.png ├── lock_screen.png ├── qtile.png ├── tiles.png └── tiles_2.png └── utils ├── icons ├── arch-icons │ ├── arch_dark.png │ └── arch_light.png ├── battery-icons │ └── dark │ │ ├── battery-caution-charging.png │ │ ├── battery-caution.png │ │ ├── battery-empty.png │ │ ├── battery-full-charged.png │ │ ├── battery-full-charging.png │ │ ├── battery-full.png │ │ ├── battery-good-charging.png │ │ ├── battery-good.png │ │ ├── battery-low-charging.png │ │ ├── battery-low.png │ │ └── battery-missing.png ├── bluetooth-icons │ ├── bluetooth_dark_bold.png │ └── bluetooth_light_bold.png ├── brightness-icons │ ├── brightness_dark_bold.png │ └── brightness_light_bold.png ├── clock-icons │ ├── clock_dark_bold.png │ └── clock_light_bold.png ├── cpu-icons │ ├── cpu_dark.png │ └── cpu_dark_bold.png ├── layout-icons │ └── dark │ │ ├── layout-bsp.png │ │ ├── layout-columns.png │ │ ├── layout-floating.png │ │ ├── layout-matrix.png │ │ ├── layout-max.png │ │ ├── layout-monadtall.png │ │ ├── layout-monadthreecol.png │ │ ├── layout-monadwide.png │ │ ├── layout-ratiotile.png │ │ ├── layout-slice.png │ │ ├── layout-spiral.png │ │ ├── layout-stack.png │ │ ├── layout-tile.png │ │ ├── layout-treetab.png │ │ ├── layout-unknown.png │ │ ├── layout-verticaltile.png │ │ ├── layout-wmii.png │ │ └── layout-zoomy.png ├── logout-icons │ └── logout_dark_bold.png ├── memory-icons │ └── memory_dark_bold.png ├── network-icons │ ├── network_dark_bold.png │ ├── network_light_bold.png │ ├── wifi_dark_bold.png │ └── wifi_light_bold.png ├── poweroff-icons │ └── poweroff_dark_bold.png ├── temperature-icons │ └── terperature_dark_bold.png ├── update-icons │ ├── update_dark_bold.png │ └── update_light_bold.png ├── volume-icons │ ├── volume_dark_bold.png │ └── volume_light_bold.png └── window-count-icons │ ├── window_count_dark_bold.png │ └── window_count_light_bold.png └── scripts ├── autostart.sh ├── brightness_dmenu.sh ├── brightness_down.sh ├── brightness_up.sh ├── check_updates.sh ├── clipmenu.sh ├── cmus_next.sh ├── cmus_previous.sh ├── cmus_start_stop.sh ├── dmenu_bluetooth.sh ├── logout_prompt.sh ├── network_manager_dmenu.py ├── poweroff_prompt.sh ├── rectangular_screenshot.sh ├── screenshot.sh ├── shutdown.sh ├── suspend_prompt.sh ├── volume_down.sh ├── volume_mute.sh └── volume_up.sh /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yash Chauhan 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My qtile config 2 | 3 | # Screenshots of the Desktop 4 | 5 | ## 1) Tiles 6 | 7 | ![tiles](images/tiles.png) 8 | 9 | ## 2) Lock Screen 10 | 11 | ![lockscreen](images/lock_screen.png) 12 | 13 | ## 3) Home Screen 14 | 15 | ![qtile](images/qtile.png) 16 | 17 | ## 4) App Menu 18 | 19 | ![app_menu](images/app_menu.png) 20 | -------------------------------------------------------------------------------- /apps/ dmenu/LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2019 Anselm R Garbe 4 | © 2006-2008 Sander van Dijk 5 | © 2006-2007 Michał Janeczek 6 | © 2007 Kris Maglione 7 | © 2009 Gottox 8 | © 2009 Markus Schnalke 9 | © 2009 Evan Gates 10 | © 2010-2012 Connor Lane Smith 11 | © 2014-2019 Hiltjo Posthuma 12 | © 2015-2019 Quentin Rameau 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a 15 | copy of this software and associated documentation files (the "Software"), 16 | to deal in the Software without restriction, including without limitation 17 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 18 | and/or sell copies of the Software, and to permit persons to whom the 19 | Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 27 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 30 | DEALINGS IN THE SOFTWARE. 31 | -------------------------------------------------------------------------------- /apps/ dmenu/Makefile: -------------------------------------------------------------------------------- 1 | # dmenu - dynamic menu 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c dmenu.c stest.c util.c 7 | OBJ = $(SRC:.c=.o) 8 | 9 | all: options dmenu stest 10 | 11 | options: 12 | @echo dmenu build options: 13 | @echo "CFLAGS = $(CFLAGS)" 14 | @echo "LDFLAGS = $(LDFLAGS)" 15 | @echo "CC = $(CC)" 16 | 17 | .c.o: 18 | $(CC) -c $(CFLAGS) $< 19 | 20 | config.h: 21 | cp config.def.h $@ 22 | 23 | $(OBJ): arg.h config.h config.mk drw.h 24 | 25 | dmenu: dmenu.o drw.o util.o 26 | $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) 27 | 28 | stest: stest.o 29 | $(CC) -o $@ stest.o $(LDFLAGS) 30 | 31 | clean: 32 | rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz 33 | 34 | dist: clean 35 | mkdir -p dmenu-$(VERSION) 36 | cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ 37 | drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ 38 | dmenu-$(VERSION) 39 | tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) 40 | gzip dmenu-$(VERSION).tar 41 | rm -rf dmenu-$(VERSION) 42 | 43 | install: all 44 | mkdir -p $(DESTDIR)$(PREFIX)/bin 45 | cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin 46 | chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu 47 | chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path 48 | chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run 49 | chmod 755 $(DESTDIR)$(PREFIX)/bin/stest 50 | mkdir -p $(DESTDIR)$(MANPREFIX)/man1 51 | sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 52 | sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 53 | chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 54 | chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 55 | 56 | uninstall: 57 | rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ 58 | $(DESTDIR)$(PREFIX)/bin/dmenu_path\ 59 | $(DESTDIR)$(PREFIX)/bin/dmenu_run\ 60 | $(DESTDIR)$(PREFIX)/bin/stest\ 61 | $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ 62 | $(DESTDIR)$(MANPREFIX)/man1/stest.1 63 | 64 | .PHONY: all options clean dist install uninstall 65 | -------------------------------------------------------------------------------- /apps/ dmenu/README.md: -------------------------------------------------------------------------------- 1 | # My build of suckless's dmenu 2 | -------------------------------------------------------------------------------- /apps/ dmenu/arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copy me if you can. 3 | * by 20h 4 | */ 5 | 6 | #ifndef ARG_H__ 7 | #define ARG_H__ 8 | 9 | extern char *argv0; 10 | 11 | /* use main(int argc, char *argv[]) */ 12 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ 13 | argv[0] && argv[0][0] == '-'\ 14 | && argv[0][1];\ 15 | argc--, argv++) {\ 16 | char argc_;\ 17 | char **argv_;\ 18 | int brk_;\ 19 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ 20 | argv++;\ 21 | argc--;\ 22 | break;\ 23 | }\ 24 | for (brk_ = 0, argv[0]++, argv_ = argv;\ 25 | argv[0][0] && !brk_;\ 26 | argv[0]++) {\ 27 | if (argv_ != argv)\ 28 | break;\ 29 | argc_ = argv[0][0];\ 30 | switch (argc_) 31 | 32 | #define ARGEND }\ 33 | } 34 | 35 | #define ARGC() argc_ 36 | 37 | #define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ 38 | ((x), abort(), (char *)0) :\ 39 | (brk_ = 1, (argv[0][1] != '\0')?\ 40 | (&argv[0][1]) :\ 41 | (argc--, argv++, argv[0]))) 42 | 43 | #define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ 44 | (char *)0 :\ 45 | (brk_ = 1, (argv[0][1] != '\0')?\ 46 | (&argv[0][1]) :\ 47 | (argc--, argv++, argv[0]))) 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /apps/ dmenu/config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | /* Default settings; can be overriden by command line. */ 3 | 4 | static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ 5 | static int centered = 0; /* -c option; centers dmenu on screen */ 6 | static int min_width = 500; /* minimum width when centered */ 7 | /* -fn option overrides fonts[0]; default X11 font or font set */ 8 | static const char *fonts[] = { 9 | "mononoki:size=14" 10 | }; 11 | static const char *prompt = NULL; /* -p option; prompt to the left of input field */ 12 | static const char *colors[SchemeLast][2] = { 13 | /* fg bg */ 14 | [SchemeNorm] = { "#bbbbbb", "#222222" }, 15 | [SchemeSel] = { "#eeeeee", "#0066ff" }, 16 | [SchemeSelHighlight] = { "#ffc978", "#0066ff" }, 17 | [SchemeNormHighlight] = { "#ffc978", "#222222" }, 18 | [SchemeOut] = { "#000000", "#00ffff" }, 19 | }; 20 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 21 | static unsigned int lines = 0; 22 | static unsigned int lineheight = 0; /* -h option; minimum height of a menu line */ 23 | 24 | /* 25 | * Characters not considered part of a word while deleting words 26 | * for example: " /?\"&[]" 27 | */ 28 | static const char worddelimiters[] = " "; 29 | 30 | /* Size of the window border */ 31 | static const unsigned int border_width = 2; 32 | -------------------------------------------------------------------------------- /apps/ dmenu/config.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | /* Default settings; can be overriden by command line. */ 3 | 4 | static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ 5 | static int centered = 0; /* -c option; centers dmenu on screen */ 6 | static int min_width = 500; /* minimum width when centered */ 7 | /* -fn option overrides fonts[0]; default X11 font or font set */ 8 | static const char *fonts[] = { 9 | "cascadia code:size=16" 10 | }; 11 | static const char *prompt = NULL; /* -p option; prompt to the left of input field */ 12 | static const char *colors[SchemeLast][2] = { 13 | /* fg bg */ 14 | [SchemeNorm] = { "#FFFFFF", "#264653" }, 15 | [SchemeSel] = { "#000000", "#56C3b7" }, 16 | [SchemeSelHighlight] = { "#000000", "#2A9D8F" }, 17 | [SchemeNormHighlight] = { "#FFFFFF", "#203d46" }, 18 | [SchemeOut] = { "#010202", "#00ffff" }, 19 | }; 20 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 21 | static unsigned int lines = 0; 22 | static unsigned int lineheight = 0; /* -h option; minimum height of a menu line */ 23 | 24 | /* 25 | * Characters not considered part of a word while deleting words 26 | * for example: " /?\"&[]" 27 | */ 28 | static const char worddelimiters[] = " "; 29 | 30 | /* Size of the window border */ 31 | static const unsigned int border_width = 4; 32 | -------------------------------------------------------------------------------- /apps/ dmenu/config.mk: -------------------------------------------------------------------------------- 1 | # dmenu version 2 | VERSION = 4.9 3 | 4 | # paths 5 | PREFIX = /usr/local 6 | MANPREFIX = $(PREFIX)/share/man 7 | 8 | X11INC = /usr/X11R6/include 9 | X11LIB = /usr/X11R6/lib 10 | 11 | # Xinerama, comment if you don't want it 12 | XINERAMALIBS = -lXinerama 13 | XINERAMAFLAGS = -DXINERAMA 14 | 15 | # freetype 16 | FREETYPELIBS = -lfontconfig -lXft 17 | FREETYPEINC = /usr/include/freetype2 18 | # OpenBSD (uncomment) 19 | #FREETYPEINC = $(X11INC)/freetype2 20 | 21 | # includes and libs 22 | INCS = -I$(X11INC) -I$(FREETYPEINC) 23 | LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) 24 | 25 | # flags 26 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) 27 | CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) 28 | LDFLAGS = $(LIBS) 29 | 30 | # compiler and linker 31 | CC = cc 32 | -------------------------------------------------------------------------------- /apps/ dmenu/dmenu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/apps/ dmenu/dmenu -------------------------------------------------------------------------------- /apps/ dmenu/dmenu.1: -------------------------------------------------------------------------------- 1 | .TH DMENU 1 dmenu\-VERSION 2 | .SH NAME 3 | dmenu \- dynamic menu 4 | .SH SYNOPSIS 5 | .B dmenu 6 | .RB [ \-bfiv ] 7 | .RB [ \-l 8 | .IR lines ] 9 | .RB [ \-m 10 | .IR monitor ] 11 | .RB [ \-p 12 | .IR prompt ] 13 | .RB [ \-fn 14 | .IR font ] 15 | .RB [ \-nb 16 | .IR color ] 17 | .RB [ \-nf 18 | .IR color ] 19 | .RB [ \-sb 20 | .IR color ] 21 | .RB [ \-sf 22 | .IR color ] 23 | .RB [ \-nhb 24 | .IR color ] 25 | .RB [ \-nhf 26 | .IR color ] 27 | .RB [ \-shb 28 | .IR color ] 29 | .RB [ \-shf 30 | .IR color ] 31 | .RB [ \-w 32 | .IR windowid ] 33 | .P 34 | .BR dmenu_run " ..." 35 | .SH DESCRIPTION 36 | .B dmenu 37 | is a dynamic menu for X, which reads a list of newline\-separated items from 38 | stdin. When the user selects an item and presses Return, their choice is printed 39 | to stdout and dmenu terminates. Entering text will narrow the items to those 40 | matching the tokens in the input. 41 | .P 42 | .B dmenu_run 43 | is a script used by 44 | .IR dwm (1) 45 | which lists programs in the user's $PATH and runs the result in their $SHELL. 46 | .SH OPTIONS 47 | .TP 48 | .B \-b 49 | dmenu appears at the bottom of the screen. 50 | .TP 51 | .B \-c 52 | dmenu appears centered on the screen. 53 | .TP 54 | .B \-f 55 | dmenu grabs the keyboard before reading stdin if not reading from a tty. This 56 | is faster, but will lock up X until stdin reaches end\-of\-file. 57 | .TP 58 | .B \-i 59 | dmenu matches menu items case insensitively. 60 | .TP 61 | .BI \-l " lines" 62 | dmenu lists items vertically, with the given number of lines. 63 | .TP 64 | .BI \-h " height" 65 | dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. 66 | .TP 67 | .BI \-m " monitor" 68 | dmenu is displayed on the monitor number supplied. Monitor numbers are starting 69 | from 0. 70 | .TP 71 | .BI \-p " prompt" 72 | defines the prompt to be displayed to the left of the input field. 73 | .TP 74 | .BI \-fn " font" 75 | defines the font or font set used. 76 | .TP 77 | .BI \-nb " color" 78 | defines the normal background color. 79 | .IR #RGB , 80 | .IR #RRGGBB , 81 | and X color names are supported. 82 | .TP 83 | .BI \-nf " color" 84 | defines the normal foreground color. 85 | .TP 86 | .BI \-sb " color" 87 | defines the selected background color. 88 | .TP 89 | .BI \-sf " color" 90 | defines the selected foreground color. 91 | .TP 92 | .BI \-nhb " color" 93 | defines the normal highlight background color. 94 | .TP 95 | .BI \-nhf " color" 96 | defines the normal highlight foreground color. 97 | .TP 98 | .BI \-shb " color" 99 | defines the selected highlight background color. 100 | .TP 101 | .BI \-shf " color" 102 | defines the selected highlight foreground color. 103 | .TP 104 | .B \-v 105 | prints version information to stdout, then exits. 106 | .TP 107 | .BI \-w " windowid" 108 | embed into windowid. 109 | .SH USAGE 110 | dmenu is completely controlled by the keyboard. Items are selected using the 111 | arrow keys, page up, page down, home, and end. 112 | .TP 113 | .B Tab 114 | Copy the selected item to the input field. 115 | .TP 116 | .B Return 117 | Confirm selection. Prints the selected item to stdout and exits, returning 118 | success. 119 | .TP 120 | .B Ctrl-Return 121 | Confirm selection. Prints the selected item to stdout and continues. 122 | .TP 123 | .B Shift\-Return 124 | Confirm input. Prints the input text to stdout and exits, returning success. 125 | .TP 126 | .B Escape 127 | Exit without selecting an item, returning failure. 128 | .TP 129 | .B Ctrl-Left 130 | Move cursor to the start of the current word 131 | .TP 132 | .B Ctrl-Right 133 | Move cursor to the end of the current word 134 | .TP 135 | .B C\-a 136 | Home 137 | .TP 138 | .B C\-b 139 | Left 140 | .TP 141 | .B C\-c 142 | Escape 143 | .TP 144 | .B C\-d 145 | Delete 146 | .TP 147 | .B C\-e 148 | End 149 | .TP 150 | .B C\-f 151 | Right 152 | .TP 153 | .B C\-g 154 | Escape 155 | .TP 156 | .B C\-h 157 | Backspace 158 | .TP 159 | .B C\-i 160 | Tab 161 | .TP 162 | .B C\-j 163 | Return 164 | .TP 165 | .B C\-J 166 | Shift-Return 167 | .TP 168 | .B C\-k 169 | Delete line right 170 | .TP 171 | .B C\-m 172 | Return 173 | .TP 174 | .B C\-M 175 | Shift-Return 176 | .TP 177 | .B C\-n 178 | Down 179 | .TP 180 | .B C\-p 181 | Up 182 | .TP 183 | .B C\-u 184 | Delete line left 185 | .TP 186 | .B C\-w 187 | Delete word left 188 | .TP 189 | .B C\-y 190 | Paste from primary X selection 191 | .TP 192 | .B C\-Y 193 | Paste from X clipboard 194 | .TP 195 | .B M\-b 196 | Move cursor to the start of the current word 197 | .TP 198 | .B M\-f 199 | Move cursor to the end of the current word 200 | .TP 201 | .B M\-g 202 | Home 203 | .TP 204 | .B M\-G 205 | End 206 | .TP 207 | .B M\-h 208 | Up 209 | .TP 210 | .B M\-j 211 | Page down 212 | .TP 213 | .B M\-k 214 | Page up 215 | .TP 216 | .B M\-l 217 | Down 218 | .SH SEE ALSO 219 | .IR dwm (1), 220 | .IR stest (1) 221 | -------------------------------------------------------------------------------- /apps/ dmenu/dmenu.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #ifdef XINERAMA 15 | #include 16 | #endif 17 | #include 18 | 19 | #include "drw.h" 20 | #include "util.h" 21 | 22 | /* macros */ 23 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ 24 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) 25 | #define LENGTH(X) (sizeof X / sizeof X[0]) 26 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 27 | 28 | /* enums */ 29 | enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, 30 | SchemeOut, SchemeLast }; /* color schemes */ 31 | 32 | 33 | struct item { 34 | char *text; 35 | struct item *left, *right; 36 | int out; 37 | }; 38 | 39 | static char text[BUFSIZ] = ""; 40 | static char *embed; 41 | static int bh, mw, mh; 42 | static int inputw = 0, promptw; 43 | static int lrpad; /* sum of left and right padding */ 44 | static size_t cursor; 45 | static struct item *items = NULL; 46 | static struct item *matches, *matchend; 47 | static struct item *prev, *curr, *next, *sel; 48 | static int mon = -1, screen; 49 | 50 | static Atom clip, utf8; 51 | static Display *dpy; 52 | static Window root, parentwin, win; 53 | static XIC xic; 54 | 55 | static Drw *drw; 56 | static Clr *scheme[SchemeLast]; 57 | 58 | #include "config.h" 59 | 60 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 61 | static char *(*fstrstr)(const char *, const char *) = strstr; 62 | 63 | static void 64 | appenditem(struct item *item, struct item **list, struct item **last) 65 | { 66 | if (*last) 67 | (*last)->right = item; 68 | else 69 | *list = item; 70 | 71 | item->left = *last; 72 | item->right = NULL; 73 | *last = item; 74 | } 75 | 76 | static void 77 | calcoffsets(void) 78 | { 79 | int i, n; 80 | 81 | if (lines > 0) 82 | n = lines * bh; 83 | else 84 | n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); 85 | /* calculate which items will begin the next page and previous page */ 86 | for (i = 0, next = curr; next; next = next->right) 87 | if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) 88 | break; 89 | for (i = 0, prev = curr; prev && prev->left; prev = prev->left) 90 | if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) 91 | break; 92 | } 93 | 94 | static int 95 | max_textw(void) 96 | { 97 | int len = 0; 98 | for (struct item *item = items; item && item->text; item++) 99 | len = MAX(TEXTW(item->text), len); 100 | return len; 101 | } 102 | 103 | static void 104 | cleanup(void) 105 | { 106 | size_t i; 107 | 108 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 109 | for (i = 0; i < SchemeLast; i++) 110 | free(scheme[i]); 111 | drw_free(drw); 112 | XSync(dpy, False); 113 | XCloseDisplay(dpy); 114 | } 115 | 116 | static char * 117 | cistrstr(const char *s, const char *sub) 118 | { 119 | size_t len; 120 | 121 | for (len = strlen(sub); *s; s++) 122 | if (!strncasecmp(s, sub, len)) 123 | return (char *)s; 124 | return NULL; 125 | } 126 | 127 | static void 128 | drawhighlights(struct item *item, int x, int y, int maxw) 129 | { 130 | int i, indent; 131 | char *highlight; 132 | char c; 133 | 134 | if (!(strlen(item->text) && strlen(text))) 135 | return; 136 | 137 | drw_setscheme(drw, scheme[item == sel 138 | ? SchemeSelHighlight 139 | : SchemeNormHighlight]); 140 | for (i = 0, highlight = item->text; *highlight && text[i];) { 141 | if (*highlight == text[i]) { 142 | /* get indentation */ 143 | c = *highlight; 144 | *highlight = '\0'; 145 | indent = TEXTW(item->text); 146 | *highlight = c; 147 | 148 | /* highlight character */ 149 | c = highlight[1]; 150 | highlight[1] = '\0'; 151 | drw_text( 152 | drw, 153 | x + indent - (lrpad / 2), 154 | y, 155 | MIN(maxw - indent, TEXTW(highlight) - lrpad), 156 | bh, 0, highlight, 0 157 | ); 158 | highlight[1] = c; 159 | i++; 160 | } 161 | highlight++; 162 | } 163 | } 164 | 165 | 166 | static int 167 | drawitem(struct item *item, int x, int y, int w) 168 | { 169 | int r; 170 | if (item == sel) 171 | drw_setscheme(drw, scheme[SchemeSel]); 172 | else if (item->out) 173 | drw_setscheme(drw, scheme[SchemeOut]); 174 | else 175 | drw_setscheme(drw, scheme[SchemeNorm]); 176 | 177 | r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 178 | drawhighlights(item, x, y, w); 179 | return r; 180 | } 181 | 182 | static void 183 | drawmenu(void) 184 | { 185 | unsigned int curpos; 186 | struct item *item; 187 | int x = 0, y = 0, fh = drw->fonts->h, w; 188 | 189 | drw_setscheme(drw, scheme[SchemeNorm]); 190 | drw_rect(drw, 0, 0, mw, mh, 1, 1); 191 | 192 | if (prompt && *prompt) { 193 | drw_setscheme(drw, scheme[SchemeSel]); 194 | x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); 195 | } 196 | /* draw input field */ 197 | w = (lines > 0 || !matches) ? mw - x : inputw; 198 | drw_setscheme(drw, scheme[SchemeNorm]); 199 | drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); 200 | 201 | curpos = TEXTW(text) - TEXTW(&text[cursor]); 202 | if ((curpos += lrpad / 2 - 1) < w) { 203 | drw_setscheme(drw, scheme[SchemeNorm]); 204 | drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); 205 | } 206 | 207 | if (lines > 0) { 208 | /* draw vertical list */ 209 | for (item = curr; item != next; item = item->right) 210 | drawitem(item, x, y += bh, mw - x); 211 | } else if (matches) { 212 | /* draw horizontal list */ 213 | x += inputw; 214 | w = TEXTW("<"); 215 | if (curr->left) { 216 | drw_setscheme(drw, scheme[SchemeNorm]); 217 | drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); 218 | } 219 | x += w; 220 | for (item = curr; item != next; item = item->right) 221 | x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); 222 | if (next) { 223 | w = TEXTW(">"); 224 | drw_setscheme(drw, scheme[SchemeNorm]); 225 | drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); 226 | } 227 | } 228 | drw_map(drw, win, 0, 0, mw, mh); 229 | } 230 | 231 | static void 232 | grabfocus(void) 233 | { 234 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; 235 | Window focuswin; 236 | int i, revertwin; 237 | 238 | for (i = 0; i < 100; ++i) { 239 | XGetInputFocus(dpy, &focuswin, &revertwin); 240 | if (focuswin == win) 241 | return; 242 | XSetInputFocus(dpy, win, RevertToParent, CurrentTime); 243 | nanosleep(&ts, NULL); 244 | } 245 | die("cannot grab focus"); 246 | } 247 | 248 | static void 249 | grabkeyboard(void) 250 | { 251 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; 252 | int i; 253 | 254 | if (embed) 255 | return; 256 | /* try to grab keyboard, we may have to wait for another process to ungrab */ 257 | for (i = 0; i < 1000; i++) { 258 | if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, 259 | GrabModeAsync, CurrentTime) == GrabSuccess) 260 | return; 261 | nanosleep(&ts, NULL); 262 | } 263 | die("cannot grab keyboard"); 264 | } 265 | 266 | static void 267 | match(void) 268 | { 269 | static char **tokv = NULL; 270 | static int tokn = 0; 271 | 272 | char buf[sizeof text], *s; 273 | int i, tokc = 0; 274 | size_t len, textsize; 275 | struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; 276 | 277 | strcpy(buf, text); 278 | /* separate input text into tokens to be matched individually */ 279 | for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) 280 | if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) 281 | die("cannot realloc %u bytes:", tokn * sizeof *tokv); 282 | len = tokc ? strlen(tokv[0]) : 0; 283 | 284 | matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; 285 | textsize = strlen(text) + 1; 286 | for (item = items; item && item->text; item++) { 287 | for (i = 0; i < tokc; i++) 288 | if (!fstrstr(item->text, tokv[i])) 289 | break; 290 | if (i != tokc) /* not all tokens match */ 291 | continue; 292 | /* exact matches go first, then prefixes, then substrings */ 293 | if (!tokc || !fstrncmp(text, item->text, textsize)) 294 | appenditem(item, &matches, &matchend); 295 | else if (!fstrncmp(tokv[0], item->text, len)) 296 | appenditem(item, &lprefix, &prefixend); 297 | else 298 | appenditem(item, &lsubstr, &substrend); 299 | } 300 | if (lprefix) { 301 | if (matches) { 302 | matchend->right = lprefix; 303 | lprefix->left = matchend; 304 | } else 305 | matches = lprefix; 306 | matchend = prefixend; 307 | } 308 | if (lsubstr) { 309 | if (matches) { 310 | matchend->right = lsubstr; 311 | lsubstr->left = matchend; 312 | } else 313 | matches = lsubstr; 314 | matchend = substrend; 315 | } 316 | curr = sel = matches; 317 | calcoffsets(); 318 | } 319 | 320 | static void 321 | insert(const char *str, ssize_t n) 322 | { 323 | if (strlen(text) + n > sizeof text - 1) 324 | return; 325 | /* move existing text out of the way, insert new text, and update cursor */ 326 | memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); 327 | if (n > 0) 328 | memcpy(&text[cursor], str, n); 329 | cursor += n; 330 | match(); 331 | } 332 | 333 | static size_t 334 | nextrune(int inc) 335 | { 336 | ssize_t n; 337 | 338 | /* return location of next utf8 rune in the given direction (+1 or -1) */ 339 | for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) 340 | ; 341 | return n; 342 | } 343 | 344 | static void 345 | movewordedge(int dir) 346 | { 347 | if (dir < 0) { /* move cursor to the start of the word*/ 348 | while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 349 | cursor = nextrune(-1); 350 | while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 351 | cursor = nextrune(-1); 352 | } else { /* move cursor to the end of the word */ 353 | while (text[cursor] && strchr(worddelimiters, text[cursor])) 354 | cursor = nextrune(+1); 355 | while (text[cursor] && !strchr(worddelimiters, text[cursor])) 356 | cursor = nextrune(+1); 357 | } 358 | } 359 | 360 | static void 361 | keypress(XKeyEvent *ev) 362 | { 363 | char buf[32]; 364 | int len; 365 | KeySym ksym; 366 | Status status; 367 | 368 | len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); 369 | switch (status) { 370 | default: /* XLookupNone, XBufferOverflow */ 371 | return; 372 | case XLookupChars: 373 | goto insert; 374 | case XLookupKeySym: 375 | case XLookupBoth: 376 | break; 377 | } 378 | 379 | if (ev->state & ControlMask) { 380 | switch(ksym) { 381 | case XK_a: ksym = XK_Home; break; 382 | case XK_b: ksym = XK_Left; break; 383 | case XK_c: ksym = XK_Escape; break; 384 | case XK_d: ksym = XK_Delete; break; 385 | case XK_e: ksym = XK_End; break; 386 | case XK_f: ksym = XK_Right; break; 387 | case XK_g: ksym = XK_Escape; break; 388 | case XK_h: ksym = XK_BackSpace; break; 389 | case XK_i: ksym = XK_Tab; break; 390 | case XK_j: /* fallthrough */ 391 | case XK_J: /* fallthrough */ 392 | case XK_m: /* fallthrough */ 393 | case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; 394 | case XK_n: ksym = XK_Down; break; 395 | case XK_p: ksym = XK_Up; break; 396 | 397 | case XK_k: /* delete right */ 398 | text[cursor] = '\0'; 399 | match(); 400 | break; 401 | case XK_u: /* delete left */ 402 | insert(NULL, 0 - cursor); 403 | break; 404 | case XK_w: /* delete word */ 405 | while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 406 | insert(NULL, nextrune(-1) - cursor); 407 | while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 408 | insert(NULL, nextrune(-1) - cursor); 409 | break; 410 | case XK_y: /* paste selection */ 411 | case XK_Y: 412 | XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 413 | utf8, utf8, win, CurrentTime); 414 | return; 415 | case XK_Left: 416 | movewordedge(-1); 417 | goto draw; 418 | case XK_Right: 419 | movewordedge(+1); 420 | goto draw; 421 | case XK_Return: 422 | case XK_KP_Enter: 423 | break; 424 | case XK_bracketleft: 425 | cleanup(); 426 | exit(1); 427 | default: 428 | return; 429 | } 430 | } else if (ev->state & Mod1Mask) { 431 | switch(ksym) { 432 | case XK_b: 433 | movewordedge(-1); 434 | goto draw; 435 | case XK_f: 436 | movewordedge(+1); 437 | goto draw; 438 | case XK_g: ksym = XK_Home; break; 439 | case XK_G: ksym = XK_End; break; 440 | case XK_h: ksym = XK_Up; break; 441 | case XK_j: ksym = XK_Next; break; 442 | case XK_k: ksym = XK_Prior; break; 443 | case XK_l: ksym = XK_Down; break; 444 | default: 445 | return; 446 | } 447 | } 448 | 449 | switch(ksym) { 450 | default: 451 | insert: 452 | if (!iscntrl(*buf)) 453 | insert(buf, len); 454 | break; 455 | case XK_Delete: 456 | if (text[cursor] == '\0') 457 | return; 458 | cursor = nextrune(+1); 459 | /* fallthrough */ 460 | case XK_BackSpace: 461 | if (cursor == 0) 462 | return; 463 | insert(NULL, nextrune(-1) - cursor); 464 | break; 465 | case XK_End: 466 | if (text[cursor] != '\0') { 467 | cursor = strlen(text); 468 | break; 469 | } 470 | if (next) { 471 | /* jump to end of list and position items in reverse */ 472 | curr = matchend; 473 | calcoffsets(); 474 | curr = prev; 475 | calcoffsets(); 476 | while (next && (curr = curr->right)) 477 | calcoffsets(); 478 | } 479 | sel = matchend; 480 | break; 481 | case XK_Escape: 482 | cleanup(); 483 | exit(1); 484 | case XK_Home: 485 | if (sel == matches) { 486 | cursor = 0; 487 | break; 488 | } 489 | sel = curr = matches; 490 | calcoffsets(); 491 | break; 492 | case XK_Left: 493 | if (cursor > 0 && (!sel || !sel->left || lines > 0)) { 494 | cursor = nextrune(-1); 495 | break; 496 | } 497 | if (lines > 0) 498 | return; 499 | /* fallthrough */ 500 | case XK_Up: 501 | if (sel && sel->left && (sel = sel->left)->right == curr) { 502 | curr = prev; 503 | calcoffsets(); 504 | } 505 | break; 506 | case XK_Next: 507 | if (!next) 508 | return; 509 | sel = curr = next; 510 | calcoffsets(); 511 | break; 512 | case XK_Prior: 513 | if (!prev) 514 | return; 515 | sel = curr = prev; 516 | calcoffsets(); 517 | break; 518 | case XK_Return: 519 | case XK_KP_Enter: 520 | puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); 521 | if (!(ev->state & ControlMask)) { 522 | cleanup(); 523 | exit(0); 524 | } 525 | if (sel) 526 | sel->out = 1; 527 | break; 528 | case XK_Right: 529 | if (text[cursor] != '\0') { 530 | cursor = nextrune(+1); 531 | break; 532 | } 533 | if (lines > 0) 534 | return; 535 | /* fallthrough */ 536 | case XK_Down: 537 | if (sel && sel->right && (sel = sel->right) == next) { 538 | curr = next; 539 | calcoffsets(); 540 | } 541 | break; 542 | case XK_Tab: 543 | if (!sel) 544 | return; 545 | strncpy(text, sel->text, sizeof text - 1); 546 | text[sizeof text - 1] = '\0'; 547 | cursor = strlen(text); 548 | match(); 549 | break; 550 | } 551 | 552 | draw: 553 | drawmenu(); 554 | } 555 | 556 | static void 557 | buttonpress(XEvent *e) 558 | { 559 | struct item *item; 560 | XButtonPressedEvent *ev = &e->xbutton; 561 | int x = 0, y = 0, h = bh, w; 562 | 563 | if (ev->window != win) 564 | return; 565 | 566 | /* right-click: exit */ 567 | if (ev->button == Button3) 568 | exit(1); 569 | 570 | if (prompt && *prompt) 571 | x += promptw; 572 | 573 | /* input field */ 574 | w = (lines > 0 || !matches) ? mw - x : inputw; 575 | 576 | /* left-click on input: clear input, 577 | * NOTE: if there is no left-arrow the space for < is reserved so 578 | * add that to the input width */ 579 | if (ev->button == Button1 && 580 | ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + 581 | ((!prev || !curr->left) ? TEXTW("<") : 0)) || 582 | (lines > 0 && ev->y >= y && ev->y <= y + h))) { 583 | insert(NULL, -cursor); 584 | drawmenu(); 585 | return; 586 | } 587 | /* middle-mouse click: paste selection */ 588 | if (ev->button == Button2) { 589 | XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 590 | utf8, utf8, win, CurrentTime); 591 | drawmenu(); 592 | return; 593 | } 594 | /* scroll up */ 595 | if (ev->button == Button4 && prev) { 596 | sel = curr = prev; 597 | calcoffsets(); 598 | drawmenu(); 599 | return; 600 | } 601 | /* scroll down */ 602 | if (ev->button == Button5 && next) { 603 | sel = curr = next; 604 | calcoffsets(); 605 | drawmenu(); 606 | return; 607 | } 608 | if (ev->button != Button1) 609 | return; 610 | /* disabled below, needs to be fixed */ 611 | /* 612 | if (ev->state & ~ControlMask) 613 | return; 614 | */ 615 | if (lines > 0) { 616 | /* vertical list: (ctrl)left-click on item */ 617 | w = mw - x; 618 | for (item = curr; item != next; item = item->right) { 619 | y += h; 620 | if (ev->y >= y && ev->y <= (y + h)) { 621 | puts(item->text); 622 | if (!(ev->state & ControlMask)) 623 | exit(0); 624 | sel = item; 625 | if (sel) { 626 | sel->out = 1; 627 | drawmenu(); 628 | } 629 | return; 630 | } 631 | } 632 | } else if (matches) { 633 | /* left-click on left arrow */ 634 | x += inputw; 635 | w = TEXTW("<"); 636 | if (prev && curr->left) { 637 | if (ev->x >= x && ev->x <= x + w) { 638 | sel = curr = prev; 639 | calcoffsets(); 640 | drawmenu(); 641 | return; 642 | } 643 | } 644 | /* horizontal list: (ctrl)left-click on item */ 645 | for (item = curr; item != next; item = item->right) { 646 | x += w; 647 | w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); 648 | if (ev->x >= x && ev->x <= x + w) { 649 | puts(item->text); 650 | if (!(ev->state & ControlMask)) 651 | exit(0); 652 | sel = item; 653 | if (sel) { 654 | sel->out = 1; 655 | drawmenu(); 656 | } 657 | return; 658 | } 659 | } 660 | /* left-click on right arrow */ 661 | w = TEXTW(">"); 662 | x = mw - w; 663 | if (next && ev->x >= x && ev->x <= x + w) { 664 | sel = curr = next; 665 | calcoffsets(); 666 | drawmenu(); 667 | return; 668 | } 669 | } 670 | } 671 | 672 | static void 673 | mousemove(XEvent *e) 674 | { 675 | struct item *item; 676 | XPointerMovedEvent *ev = &e->xmotion; 677 | int x = 0, y = 0, h = bh, w; 678 | 679 | if (lines > 0) { 680 | w = mw - x; 681 | for (item = curr; item != next; item = item->right) { 682 | y += h; 683 | if (ev->y >= y && ev->y <= (y + h)) { 684 | sel = item; 685 | calcoffsets(); 686 | drawmenu(); 687 | return; 688 | } 689 | } 690 | } else if (matches) { 691 | x += inputw; 692 | w = TEXTW("<"); 693 | for (item = curr; item != next; item = item->right) { 694 | x += w; 695 | w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); 696 | if (ev->x >= x && ev->x <= x + w) { 697 | sel = item; 698 | calcoffsets(); 699 | drawmenu(); 700 | return; 701 | } 702 | } 703 | } 704 | } 705 | 706 | static void 707 | paste(void) 708 | { 709 | char *p, *q; 710 | int di; 711 | unsigned long dl; 712 | Atom da; 713 | 714 | /* we have been given the current selection, now insert it into input */ 715 | if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, 716 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p) 717 | == Success && p) { 718 | insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); 719 | XFree(p); 720 | } 721 | drawmenu(); 722 | } 723 | 724 | static void 725 | readstdin(void) 726 | { 727 | char buf[sizeof text], *p; 728 | size_t i, imax = 0, size = 0; 729 | unsigned int tmpmax = 0; 730 | 731 | /* read each line from stdin and add it to the item list */ 732 | for (i = 0; fgets(buf, sizeof buf, stdin); i++) { 733 | if (i + 1 >= size / sizeof *items) 734 | if (!(items = realloc(items, (size += BUFSIZ)))) 735 | die("cannot realloc %u bytes:", size); 736 | if ((p = strchr(buf, '\n'))) 737 | *p = '\0'; 738 | if (!(items[i].text = strdup(buf))) 739 | die("cannot strdup %u bytes:", strlen(buf) + 1); 740 | items[i].out = 0; 741 | drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); 742 | if (tmpmax > inputw) { 743 | inputw = tmpmax; 744 | imax = i; 745 | } 746 | } 747 | if (items) 748 | items[i].text = NULL; 749 | inputw = items ? TEXTW(items[imax].text) : 0; 750 | lines = MIN(lines, i); 751 | } 752 | 753 | static void 754 | run(void) 755 | { 756 | XEvent ev; 757 | 758 | while (!XNextEvent(dpy, &ev)) { 759 | if (XFilterEvent(&ev, win)) 760 | continue; 761 | switch(ev.type) { 762 | case DestroyNotify: 763 | if (ev.xdestroywindow.window != win) 764 | break; 765 | cleanup(); 766 | exit(1); 767 | case ButtonPress: 768 | buttonpress(&ev); 769 | break; 770 | case MotionNotify: 771 | mousemove(&ev); 772 | break; 773 | case Expose: 774 | if (ev.xexpose.count == 0) 775 | drw_map(drw, win, 0, 0, mw, mh); 776 | break; 777 | case FocusIn: 778 | /* regrab focus from parent window */ 779 | if (ev.xfocus.window != win) 780 | grabfocus(); 781 | break; 782 | case KeyPress: 783 | keypress(&ev.xkey); 784 | break; 785 | case SelectionNotify: 786 | if (ev.xselection.property == utf8) 787 | paste(); 788 | break; 789 | case VisibilityNotify: 790 | if (ev.xvisibility.state != VisibilityUnobscured) 791 | XRaiseWindow(dpy, win); 792 | break; 793 | } 794 | } 795 | } 796 | 797 | static void 798 | setup(void) 799 | { 800 | int x, y, i, j; 801 | unsigned int du; 802 | XSetWindowAttributes swa; 803 | XIM xim; 804 | Window w, dw, *dws; 805 | XWindowAttributes wa; 806 | XClassHint ch = {"dmenu", "dmenu"}; 807 | #ifdef XINERAMA 808 | XineramaScreenInfo *info; 809 | Window pw; 810 | int a, di, n, area = 0; 811 | #endif 812 | /* init appearance */ 813 | for (j = 0; j < SchemeLast; j++) 814 | scheme[j] = drw_scm_create(drw, colors[j], 2); 815 | 816 | clip = XInternAtom(dpy, "CLIPBOARD", False); 817 | utf8 = XInternAtom(dpy, "UTF8_STRING", False); 818 | 819 | /* calculate menu geometry */ 820 | bh = drw->fonts->h + 2; 821 | bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ 822 | lines = MAX(lines, 0); 823 | mh = (lines + 1) * bh; 824 | promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 825 | #ifdef XINERAMA 826 | i = 0; 827 | if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { 828 | XGetInputFocus(dpy, &w, &di); 829 | if (mon >= 0 && mon < n) 830 | i = mon; 831 | else if (w != root && w != PointerRoot && w != None) { 832 | /* find top-level window containing current input focus */ 833 | do { 834 | if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) 835 | XFree(dws); 836 | } while (w != root && w != pw); 837 | /* find xinerama screen with which the window intersects most */ 838 | if (XGetWindowAttributes(dpy, pw, &wa)) 839 | for (j = 0; j < n; j++) 840 | if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { 841 | area = a; 842 | i = j; 843 | } 844 | } 845 | /* no focused window is on screen, so use pointer location instead */ 846 | if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) 847 | for (i = 0; i < n; i++) 848 | if (INTERSECT(x, y, 1, 1, info[i])) 849 | break; 850 | 851 | if (centered) { 852 | mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); 853 | x = info[i].x_org + ((info[i].width - mw) / 2); 854 | y = info[i].y_org + ((info[i].height - mh) / 2); 855 | } else { 856 | x = info[i].x_org; 857 | y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 858 | mw = info[i].width; 859 | } 860 | 861 | XFree(info); 862 | } else 863 | #endif 864 | { 865 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 866 | die("could not get embedding window attributes: 0x%lx", 867 | parentwin); 868 | 869 | if (centered) { 870 | mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); 871 | x = (wa.width - mw) / 2; 872 | y = (wa.height - mh) / 2; 873 | } else { 874 | x = 0; 875 | y = topbar ? 0 : wa.height - mh; 876 | mw = wa.width; 877 | } 878 | } 879 | inputw = MIN(inputw, mw/3); 880 | match(); 881 | 882 | /* create menu window */ 883 | swa.override_redirect = True; 884 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 885 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | 886 | ButtonPressMask | PointerMotionMask; 887 | win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, 888 | CopyFromParent, CopyFromParent, CopyFromParent, 889 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 890 | XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); 891 | XSetClassHint(dpy, win, &ch); 892 | 893 | 894 | /* input methods */ 895 | if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) 896 | die("XOpenIM failed: could not open input device"); 897 | 898 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 899 | XNClientWindow, win, XNFocusWindow, win, NULL); 900 | 901 | XMapRaised(dpy, win); 902 | if (embed) { 903 | XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); 904 | if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { 905 | for (i = 0; i < du && dws[i] != win; ++i) 906 | XSelectInput(dpy, dws[i], FocusChangeMask); 907 | XFree(dws); 908 | } 909 | grabfocus(); 910 | } 911 | drw_resize(drw, mw, mh); 912 | drawmenu(); 913 | } 914 | 915 | static void 916 | usage(void) 917 | { 918 | fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 919 | " [-h height] [-nb color] [-nf color] [-sb color] [-sf color]\n" 920 | " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr); 921 | exit(1); 922 | } 923 | 924 | int 925 | main(int argc, char *argv[]) 926 | { 927 | XWindowAttributes wa; 928 | int i, fast = 0; 929 | 930 | for (i = 1; i < argc; i++) 931 | /* these options take no arguments */ 932 | if (!strcmp(argv[i], "-v")) { /* prints version information */ 933 | puts("dmenu-"VERSION); 934 | exit(0); 935 | } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ 936 | topbar = 0; 937 | else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ 938 | fast = 1; 939 | else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ 940 | centered = 1; 941 | else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ 942 | fstrncmp = strncasecmp; 943 | fstrstr = cistrstr; 944 | } else if (i + 1 == argc) 945 | usage(); 946 | /* these options take one argument */ 947 | else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ 948 | lines = atoi(argv[++i]); 949 | else if (!strcmp(argv[i], "-m")) 950 | mon = atoi(argv[++i]); 951 | else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ 952 | prompt = argv[++i]; 953 | else if (!strcmp(argv[i], "-fn")) /* font or font set */ 954 | fonts[0] = argv[++i]; 955 | else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ 956 | lineheight = atoi(argv[++i]); 957 | lineheight = MAX(lineheight,8); /* reasonable default in case of value too small/negative */ 958 | } 959 | else if (!strcmp(argv[i], "-nb")) /* normal background color */ 960 | colors[SchemeNorm][ColBg] = argv[++i]; 961 | else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ 962 | colors[SchemeNorm][ColFg] = argv[++i]; 963 | else if (!strcmp(argv[i], "-sb")) /* selected background color */ 964 | colors[SchemeSel][ColBg] = argv[++i]; 965 | else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ 966 | colors[SchemeSel][ColFg] = argv[++i]; 967 | else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ 968 | colors[SchemeNormHighlight][ColBg] = argv[++i]; 969 | else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ 970 | colors[SchemeNormHighlight][ColFg] = argv[++i]; 971 | else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ 972 | colors[SchemeSelHighlight][ColBg] = argv[++i]; 973 | else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ 974 | colors[SchemeSelHighlight][ColFg] = argv[++i]; 975 | else if (!strcmp(argv[i], "-w")) /* embedding window id */ 976 | embed = argv[++i]; 977 | else 978 | usage(); 979 | 980 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 981 | fputs("warning: no locale support\n", stderr); 982 | if (!(dpy = XOpenDisplay(NULL))) 983 | die("cannot open display"); 984 | screen = DefaultScreen(dpy); 985 | root = RootWindow(dpy, screen); 986 | if (!embed || !(parentwin = strtol(embed, NULL, 0))) 987 | parentwin = root; 988 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 989 | die("could not get embedding window attributes: 0x%lx", 990 | parentwin); 991 | drw = drw_create(dpy, screen, root, wa.width, wa.height); 992 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 993 | die("no fonts could be loaded."); 994 | lrpad = drw->fonts->h; 995 | 996 | #ifdef __OpenBSD__ 997 | if (pledge("stdio rpath", NULL) == -1) 998 | die("pledge"); 999 | #endif 1000 | 1001 | if (fast && !isatty(0)) { 1002 | grabkeyboard(); 1003 | readstdin(); 1004 | } else { 1005 | readstdin(); 1006 | grabkeyboard(); 1007 | } 1008 | setup(); 1009 | run(); 1010 | 1011 | return 1; /* unreachable */ 1012 | } 1013 | -------------------------------------------------------------------------------- /apps/ dmenu/dmenu.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/apps/ dmenu/dmenu.o -------------------------------------------------------------------------------- /apps/ dmenu/dmenu_path: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" 4 | cache="$cachedir/dmenu_run" 5 | 6 | [ ! -e "$cachedir" ] && mkdir -p "$cachedir" 7 | 8 | IFS=: 9 | if stest -dqr -n "$cache" $PATH; then 10 | stest -flx $PATH | sort -u | tee "$cache" 11 | else 12 | cat "$cache" 13 | fi 14 | -------------------------------------------------------------------------------- /apps/ dmenu/dmenu_run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & 3 | -------------------------------------------------------------------------------- /apps/ dmenu/drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | drw_fontset_free(drw->fonts); 99 | free(drw); 100 | } 101 | 102 | /* This function is an implementation detail. Library users should use 103 | * drw_fontset_create instead. 104 | */ 105 | static Fnt * 106 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 107 | { 108 | Fnt *font; 109 | XftFont *xfont = NULL; 110 | FcPattern *pattern = NULL; 111 | 112 | if (fontname) { 113 | /* Using the pattern found at font->xfont->pattern does not yield the 114 | * same substitution results as using the pattern returned by 115 | * FcNameParse; using the latter results in the desired fallback 116 | * behaviour whereas the former just results in missing-character 117 | * rectangles being drawn, at least with some fonts. */ 118 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 119 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 120 | return NULL; 121 | } 122 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 123 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 124 | XftFontClose(drw->dpy, xfont); 125 | return NULL; 126 | } 127 | } else if (fontpattern) { 128 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 129 | fprintf(stderr, "error, cannot load font from pattern.\n"); 130 | return NULL; 131 | } 132 | } else { 133 | die("no font specified."); 134 | } 135 | 136 | /* Do not allow using color fonts. This is a workaround for a BadLength 137 | * error from Xft with color glyphs. Modelled on the Xterm workaround. See 138 | * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 139 | * https://lists.suckless.org/dev/1701/30932.html 140 | * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 141 | * and lots more all over the internet. 142 | */ 143 | FcBool iscol; 144 | if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { 145 | XftFontClose(drw->dpy, xfont); 146 | return NULL; 147 | } 148 | 149 | font = ecalloc(1, sizeof(Fnt)); 150 | font->xfont = xfont; 151 | font->pattern = pattern; 152 | font->h = xfont->ascent + xfont->descent; 153 | font->dpy = drw->dpy; 154 | 155 | return font; 156 | } 157 | 158 | static void 159 | xfont_free(Fnt *font) 160 | { 161 | if (!font) 162 | return; 163 | if (font->pattern) 164 | FcPatternDestroy(font->pattern); 165 | XftFontClose(font->dpy, font->xfont); 166 | free(font); 167 | } 168 | 169 | Fnt* 170 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 171 | { 172 | Fnt *cur, *ret = NULL; 173 | size_t i; 174 | 175 | if (!drw || !fonts) 176 | return NULL; 177 | 178 | for (i = 1; i <= fontcount; i++) { 179 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 180 | cur->next = ret; 181 | ret = cur; 182 | } 183 | } 184 | return (drw->fonts = ret); 185 | } 186 | 187 | void 188 | drw_fontset_free(Fnt *font) 189 | { 190 | if (font) { 191 | drw_fontset_free(font->next); 192 | xfont_free(font); 193 | } 194 | } 195 | 196 | void 197 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 198 | { 199 | if (!drw || !dest || !clrname) 200 | return; 201 | 202 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 203 | DefaultColormap(drw->dpy, drw->screen), 204 | clrname, dest)) 205 | die("error, cannot allocate color '%s'", clrname); 206 | } 207 | 208 | /* Wrapper to create color schemes. The caller has to call free(3) on the 209 | * returned color scheme when done using it. */ 210 | Clr * 211 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 212 | { 213 | size_t i; 214 | Clr *ret; 215 | 216 | /* need at least two colors for a scheme */ 217 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 218 | return NULL; 219 | 220 | for (i = 0; i < clrcount; i++) 221 | drw_clr_create(drw, &ret[i], clrnames[i]); 222 | return ret; 223 | } 224 | 225 | void 226 | drw_setfontset(Drw *drw, Fnt *set) 227 | { 228 | if (drw) 229 | drw->fonts = set; 230 | } 231 | 232 | void 233 | drw_setscheme(Drw *drw, Clr *scm) 234 | { 235 | if (drw) 236 | drw->scheme = scm; 237 | } 238 | 239 | void 240 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 241 | { 242 | if (!drw || !drw->scheme) 243 | return; 244 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 245 | if (filled) 246 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 247 | else 248 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 249 | } 250 | 251 | int 252 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 253 | { 254 | char buf[1024]; 255 | int ty; 256 | unsigned int ew; 257 | XftDraw *d = NULL; 258 | Fnt *usedfont, *curfont, *nextfont; 259 | size_t i, len; 260 | int utf8strlen, utf8charlen, render = x || y || w || h; 261 | long utf8codepoint = 0; 262 | const char *utf8str; 263 | FcCharSet *fccharset; 264 | FcPattern *fcpattern; 265 | FcPattern *match; 266 | XftResult result; 267 | int charexists = 0; 268 | 269 | if (!drw || (render && !drw->scheme) || !text || !drw->fonts) 270 | return 0; 271 | 272 | if (!render) { 273 | w = ~w; 274 | } else { 275 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 276 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 277 | d = XftDrawCreate(drw->dpy, drw->drawable, 278 | DefaultVisual(drw->dpy, drw->screen), 279 | DefaultColormap(drw->dpy, drw->screen)); 280 | x += lpad; 281 | w -= lpad; 282 | } 283 | 284 | usedfont = drw->fonts; 285 | while (1) { 286 | utf8strlen = 0; 287 | utf8str = text; 288 | nextfont = NULL; 289 | while (*text) { 290 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 291 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 292 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 293 | if (charexists) { 294 | if (curfont == usedfont) { 295 | utf8strlen += utf8charlen; 296 | text += utf8charlen; 297 | } else { 298 | nextfont = curfont; 299 | } 300 | break; 301 | } 302 | } 303 | 304 | if (!charexists || nextfont) 305 | break; 306 | else 307 | charexists = 0; 308 | } 309 | 310 | if (utf8strlen) { 311 | drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); 312 | /* shorten text if necessary */ 313 | for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) 314 | drw_font_getexts(usedfont, utf8str, len, &ew, NULL); 315 | 316 | if (len) { 317 | memcpy(buf, utf8str, len); 318 | buf[len] = '\0'; 319 | if (len < utf8strlen) 320 | for (i = len; i && i > len - 3; buf[--i] = '.') 321 | ; /* NOP */ 322 | 323 | if (render) { 324 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 325 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 326 | usedfont->xfont, x, ty, (XftChar8 *)buf, len); 327 | } 328 | x += ew; 329 | w -= ew; 330 | } 331 | } 332 | 333 | if (!*text) { 334 | break; 335 | } else if (nextfont) { 336 | charexists = 0; 337 | usedfont = nextfont; 338 | } else { 339 | /* Regardless of whether or not a fallback font is found, the 340 | * character must be drawn. */ 341 | charexists = 1; 342 | 343 | fccharset = FcCharSetCreate(); 344 | FcCharSetAddChar(fccharset, utf8codepoint); 345 | 346 | if (!drw->fonts->pattern) { 347 | /* Refer to the comment in xfont_create for more information. */ 348 | die("the first font in the cache must be loaded from a font string."); 349 | } 350 | 351 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 352 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 353 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 354 | FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); 355 | 356 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 357 | FcDefaultSubstitute(fcpattern); 358 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 359 | 360 | FcCharSetDestroy(fccharset); 361 | FcPatternDestroy(fcpattern); 362 | 363 | if (match) { 364 | usedfont = xfont_create(drw, NULL, match); 365 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 366 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 367 | ; /* NOP */ 368 | curfont->next = usedfont; 369 | } else { 370 | xfont_free(usedfont); 371 | usedfont = drw->fonts; 372 | } 373 | } 374 | } 375 | } 376 | if (d) 377 | XftDrawDestroy(d); 378 | 379 | return x + (render ? w : 0); 380 | } 381 | 382 | void 383 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 384 | { 385 | if (!drw) 386 | return; 387 | 388 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 389 | XSync(drw->dpy, False); 390 | } 391 | 392 | unsigned int 393 | drw_fontset_getwidth(Drw *drw, const char *text) 394 | { 395 | if (!drw || !drw->fonts || !text) 396 | return 0; 397 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 398 | } 399 | 400 | void 401 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 402 | { 403 | XGlyphInfo ext; 404 | 405 | if (!font || !text) 406 | return; 407 | 408 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 409 | if (w) 410 | *w = ext.xOff; 411 | if (h) 412 | *h = font->h; 413 | } 414 | 415 | Cur * 416 | drw_cur_create(Drw *drw, int shape) 417 | { 418 | Cur *cur; 419 | 420 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 421 | return NULL; 422 | 423 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 424 | 425 | return cur; 426 | } 427 | 428 | void 429 | drw_cur_free(Drw *drw, Cur *cursor) 430 | { 431 | if (!cursor) 432 | return; 433 | 434 | XFreeCursor(drw->dpy, cursor->cursor); 435 | free(cursor); 436 | } 437 | -------------------------------------------------------------------------------- /apps/ dmenu/drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 39 | 40 | /* Colorscheme abstraction */ 41 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 42 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 43 | 44 | /* Cursor abstraction */ 45 | Cur *drw_cur_create(Drw *drw, int shape); 46 | void drw_cur_free(Drw *drw, Cur *cursor); 47 | 48 | /* Drawing context manipulation */ 49 | void drw_setfontset(Drw *drw, Fnt *set); 50 | void drw_setscheme(Drw *drw, Clr *scm); 51 | 52 | /* Drawing functions */ 53 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 54 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 55 | 56 | /* Map functions */ 57 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 58 | -------------------------------------------------------------------------------- /apps/ dmenu/drw.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/apps/ dmenu/drw.o -------------------------------------------------------------------------------- /apps/ dmenu/patches/dmenu-border-4.9.diff: -------------------------------------------------------------------------------- 1 | diff -up dmenu-4.9-b/config.def.h dmenu-4.9-a/config.def.h 2 | --- dmenu-4.9-b/config.def.h 2019-02-02 13:55:02.000000000 +0100 3 | +++ dmenu-4.9-a/config.def.h 2019-05-19 02:10:12.740040403 +0200 4 | @@ -21,3 +21,6 @@ static unsigned int lines = 0; 5 | * for example: " /?\"&[]" 6 | */ 7 | static const char worddelimiters[] = " "; 8 | + 9 | +/* Size of the window border */ 10 | +static const unsigned int border_width = 5; 11 | diff -up dmenu-4.9-b/dmenu.c dmenu-4.9-a/dmenu.c 12 | --- dmenu-4.9-b/dmenu.c 2019-02-02 13:55:02.000000000 +0100 13 | +++ dmenu-4.9-a/dmenu.c 2019-05-19 02:11:20.966710117 +0200 14 | @@ -654,9 +654,10 @@ setup(void) 15 | swa.override_redirect = True; 16 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 17 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; 18 | - win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, 19 | + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, 20 | CopyFromParent, CopyFromParent, CopyFromParent, 21 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 22 | + XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); 23 | XSetClassHint(dpy, win, &ch); 24 | 25 | /* open input methods */ 26 | -------------------------------------------------------------------------------- /apps/ dmenu/patches/dmenu-center-20200111-8cd37e1.diff: -------------------------------------------------------------------------------- 1 | From 8cd37e1ab9e7cb025224aeb3543f1a5be8bceb93 Mon Sep 17 00:00:00 2001 2 | From: Nihal Jere 3 | Date: Sat, 11 Jan 2020 21:16:08 -0600 4 | Subject: [PATCH] center patch now has adjustable minimum width 5 | 6 | --- 7 | config.def.h | 2 ++ 8 | dmenu.1 | 3 +++ 9 | dmenu.c | 39 ++++++++++++++++++++++++++++++++------- 10 | 3 files changed, 37 insertions(+), 7 deletions(-) 11 | 12 | diff --git a/config.def.h b/config.def.h 13 | index 1edb647..88ef264 100644 14 | --- a/config.def.h 15 | +++ b/config.def.h 16 | @@ -2,6 +2,8 @@ 17 | /* Default settings; can be overriden by command line. */ 18 | 19 | static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ 20 | +static int centered = 0; /* -c option; centers dmenu on screen */ 21 | +static int min_width = 500; /* minimum width when centered */ 22 | /* -fn option overrides fonts[0]; default X11 font or font set */ 23 | static const char *fonts[] = { 24 | "monospace:size=10" 25 | diff --git a/dmenu.1 b/dmenu.1 26 | index 323f93c..c036baa 100644 27 | --- a/dmenu.1 28 | +++ b/dmenu.1 29 | @@ -40,6 +40,9 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. 30 | .B \-b 31 | dmenu appears at the bottom of the screen. 32 | .TP 33 | +.B \-c 34 | +dmenu appears centered on the screen. 35 | +.TP 36 | .B \-f 37 | dmenu grabs the keyboard before reading stdin if not reading from a tty. This 38 | is faster, but will lock up X until stdin reaches end\-of\-file. 39 | diff --git a/dmenu.c b/dmenu.c 40 | index 65f25ce..041c7f8 100644 41 | --- a/dmenu.c 42 | +++ b/dmenu.c 43 | @@ -89,6 +89,15 @@ calcoffsets(void) 44 | break; 45 | } 46 | 47 | +static int 48 | +max_textw(void) 49 | +{ 50 | + int len = 0; 51 | + for (struct item *item = items; item && item->text; item++) 52 | + len = MAX(TEXTW(item->text), len); 53 | + return len; 54 | +} 55 | + 56 | static void 57 | cleanup(void) 58 | { 59 | @@ -611,6 +620,7 @@ setup(void) 60 | bh = drw->fonts->h + 2; 61 | lines = MAX(lines, 0); 62 | mh = (lines + 1) * bh; 63 | + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 64 | #ifdef XINERAMA 65 | i = 0; 66 | if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { 67 | @@ -637,9 +647,16 @@ setup(void) 68 | if (INTERSECT(x, y, 1, 1, info[i])) 69 | break; 70 | 71 | - x = info[i].x_org; 72 | - y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 73 | - mw = info[i].width; 74 | + if (centered) { 75 | + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); 76 | + x = info[i].x_org + ((info[i].width - mw) / 2); 77 | + y = info[i].y_org + ((info[i].height - mh) / 2); 78 | + } else { 79 | + x = info[i].x_org; 80 | + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 81 | + mw = info[i].width; 82 | + } 83 | + 84 | XFree(info); 85 | } else 86 | #endif 87 | @@ -647,11 +664,17 @@ setup(void) 88 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 89 | die("could not get embedding window attributes: 0x%lx", 90 | parentwin); 91 | - x = 0; 92 | - y = topbar ? 0 : wa.height - mh; 93 | - mw = wa.width; 94 | + 95 | + if (centered) { 96 | + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); 97 | + x = (wa.width - mw) / 2; 98 | + y = (wa.height - mh) / 2; 99 | + } else { 100 | + x = 0; 101 | + y = topbar ? 0 : wa.height - mh; 102 | + mw = wa.width; 103 | + } 104 | } 105 | - promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 106 | inputw = MIN(inputw, mw/3); 107 | match(); 108 | 109 | @@ -709,6 +732,8 @@ main(int argc, char *argv[]) 110 | topbar = 0; 111 | else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ 112 | fast = 1; 113 | + else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ 114 | + centered = 1; 115 | else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ 116 | fstrncmp = strncasecmp; 117 | fstrstr = cistrstr; 118 | -- 119 | 2.24.1 120 | 121 | -------------------------------------------------------------------------------- /apps/ dmenu/patches/dmenu-fuzzyhighlight-4.9.diff: -------------------------------------------------------------------------------- 1 | Author: Chris Noxz 2 | note: This patch is meant to be used together with fuzzymatch 3 | 4 | diff -upN dmenu-4.9/config.def.h dmenu-4.9-fuzzyhighlight/config.def.h 5 | --- dmenu-4.9/config.def.h 2019-02-02 13:55:02.000000000 +0100 6 | +++ dmenu-4.9-fuzzyhighlight/config.def.h 2020-04-04 10:26:36.990890854 +0200 7 | @@ -11,6 +11,8 @@ static const char *colors[SchemeLast][2] 8 | /* fg bg */ 9 | [SchemeNorm] = { "#bbbbbb", "#222222" }, 10 | [SchemeSel] = { "#eeeeee", "#005577" }, 11 | + [SchemeSelHighlight] = { "#ffc978", "#005577" }, 12 | + [SchemeNormHighlight] = { "#ffc978", "#222222" }, 13 | [SchemeOut] = { "#000000", "#00ffff" }, 14 | }; 15 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 16 | diff -upN dmenu-4.9/dmenu.1 dmenu-4.9-fuzzyhighlight/dmenu.1 17 | --- dmenu-4.9/dmenu.1 2019-02-02 13:55:02.000000000 +0100 18 | +++ dmenu-4.9-fuzzyhighlight/dmenu.1 2020-04-04 10:30:16.430054933 +0200 19 | @@ -20,6 +20,14 @@ dmenu \- dynamic menu 20 | .IR color ] 21 | .RB [ \-sf 22 | .IR color ] 23 | +.RB [ \-nhb 24 | +.IR color ] 25 | +.RB [ \-nhf 26 | +.IR color ] 27 | +.RB [ \-shb 28 | +.IR color ] 29 | +.RB [ \-shf 30 | +.IR color ] 31 | .RB [ \-w 32 | .IR windowid ] 33 | .P 34 | @@ -75,6 +83,18 @@ defines the selected background color. 35 | .BI \-sf " color" 36 | defines the selected foreground color. 37 | .TP 38 | +.BI \-nhb " color" 39 | +defines the normal highlight background color. 40 | +.TP 41 | +.BI \-nhf " color" 42 | +defines the normal highlight foreground color. 43 | +.TP 44 | +.BI \-shb " color" 45 | +defines the selected highlight background color. 46 | +.TP 47 | +.BI \-shf " color" 48 | +defines the selected highlight foreground color. 49 | +.TP 50 | .B \-v 51 | prints version information to stdout, then exits. 52 | .TP 53 | diff -upN dmenu-4.9/dmenu.c dmenu-4.9-fuzzyhighlight/dmenu.c 54 | --- dmenu-4.9/dmenu.c 2019-02-02 13:55:02.000000000 +0100 55 | +++ dmenu-4.9-fuzzyhighlight/dmenu.c 2020-04-04 10:27:43.888026309 +0200 56 | @@ -26,7 +26,9 @@ 57 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 58 | 59 | /* enums */ 60 | -enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ 61 | +enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, 62 | + SchemeOut, SchemeLast }; /* color schemes */ 63 | + 64 | 65 | struct item { 66 | char *text; 67 | @@ -113,9 +115,49 @@ cistrstr(const char *s, const char *sub) 68 | return NULL; 69 | } 70 | 71 | +static void 72 | +drawhighlights(struct item *item, int x, int y, int maxw) 73 | +{ 74 | + int i, indent; 75 | + char *highlight; 76 | + char c; 77 | + 78 | + if (!(strlen(item->text) && strlen(text))) 79 | + return; 80 | + 81 | + drw_setscheme(drw, scheme[item == sel 82 | + ? SchemeSelHighlight 83 | + : SchemeNormHighlight]); 84 | + for (i = 0, highlight = item->text; *highlight && text[i];) { 85 | + if (*highlight == text[i]) { 86 | + /* get indentation */ 87 | + c = *highlight; 88 | + *highlight = '\0'; 89 | + indent = TEXTW(item->text); 90 | + *highlight = c; 91 | + 92 | + /* highlight character */ 93 | + c = highlight[1]; 94 | + highlight[1] = '\0'; 95 | + drw_text( 96 | + drw, 97 | + x + indent - (lrpad / 2), 98 | + y, 99 | + MIN(maxw - indent, TEXTW(highlight) - lrpad), 100 | + bh, 0, highlight, 0 101 | + ); 102 | + highlight[1] = c; 103 | + i++; 104 | + } 105 | + highlight++; 106 | + } 107 | +} 108 | + 109 | + 110 | static int 111 | drawitem(struct item *item, int x, int y, int w) 112 | { 113 | + int r; 114 | if (item == sel) 115 | drw_setscheme(drw, scheme[SchemeSel]); 116 | else if (item->out) 117 | @@ -123,7 +165,9 @@ drawitem(struct item *item, int x, int y 118 | else 119 | drw_setscheme(drw, scheme[SchemeNorm]); 120 | 121 | - return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 122 | + r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 123 | + drawhighlights(item, x, y, w); 124 | + return r; 125 | } 126 | 127 | static void 128 | @@ -683,7 +727,8 @@ static void 129 | usage(void) 130 | { 131 | fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 132 | - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); 133 | + " [-nb color] [-nf color] [-sb color] [-sf color]\n" 134 | + " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr); 135 | exit(1); 136 | } 137 | 138 | @@ -724,6 +769,14 @@ main(int argc, char *argv[]) 139 | colors[SchemeSel][ColBg] = argv[++i]; 140 | else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ 141 | colors[SchemeSel][ColFg] = argv[++i]; 142 | + else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ 143 | + colors[SchemeNormHighlight][ColBg] = argv[++i]; 144 | + else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ 145 | + colors[SchemeNormHighlight][ColFg] = argv[++i]; 146 | + else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ 147 | + colors[SchemeSelHighlight][ColBg] = argv[++i]; 148 | + else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ 149 | + colors[SchemeSelHighlight][ColFg] = argv[++i]; 150 | else if (!strcmp(argv[i], "-w")) /* embedding window id */ 151 | embed = argv[++i]; 152 | else 153 | -------------------------------------------------------------------------------- /apps/ dmenu/patches/dmenu-lineheight-4.9.diff: -------------------------------------------------------------------------------- 1 | From 87f92a561c31246f6f9effc0e89ef92677c87746 Mon Sep 17 00:00:00 2001 2 | From: astier 3 | Date: Wed, 27 Feb 2019 21:44:55 +0100 4 | Subject: [PATCH] Add an option which defines the lineheight 5 | 6 | Despite both the panel and dmenu using the same font (a Terminus 12), 7 | dmenu is shorter and the panel is visible from under the dmenu bar. 8 | The appearance can be even more distracting when using similar colors 9 | for background and selections. With the option added by this patch, 10 | dmenu can be launched with a '-h 24', thus completely covering the panel. 11 | --- 12 | config.def.h | 1 + 13 | dmenu.1 | 3 +++ 14 | dmenu.c | 10 ++++++++-- 15 | 3 files changed, 12 insertions(+), 2 deletions(-) 16 | 17 | diff --git a/config.def.h b/config.def.h 18 | index 1edb647..317fa2f 100644 19 | --- a/config.def.h 20 | +++ b/config.def.h 21 | @@ -15,6 +15,7 @@ static const char *colors[SchemeLast][2] = { 22 | }; 23 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 24 | static unsigned int lines = 0; 25 | +static unsigned int lineheight = 0; /* -h option; minimum height of a menu line */ 26 | 27 | /* 28 | * Characters not considered part of a word while deleting words 29 | diff --git a/dmenu.1 b/dmenu.1 30 | index 323f93c..7ef34d2 100644 31 | --- a/dmenu.1 32 | +++ b/dmenu.1 33 | @@ -50,6 +50,9 @@ dmenu matches menu items case insensitively. 34 | .BI \-l " lines" 35 | dmenu lists items vertically, with the given number of lines. 36 | .TP 37 | +.BI \-h " height" 38 | +dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. 39 | +.TP 40 | .BI \-m " monitor" 41 | dmenu is displayed on the monitor number supplied. Monitor numbers are starting 42 | from 0. 43 | diff --git a/dmenu.c b/dmenu.c 44 | index 6b8f51b..45d1946 100644 45 | --- a/dmenu.c 46 | +++ b/dmenu.c 47 | @@ -131,7 +131,7 @@ drawmenu(void) 48 | { 49 | unsigned int curpos; 50 | struct item *item; 51 | - int x = 0, y = 0, w; 52 | + int x = 0, y = 0, fh = drw->fonts->h, w; 53 | 54 | drw_setscheme(drw, scheme[SchemeNorm]); 55 | drw_rect(drw, 0, 0, mw, mh, 1, 1); 56 | @@ -148,7 +148,7 @@ drawmenu(void) 57 | curpos = TEXTW(text) - TEXTW(&text[cursor]); 58 | if ((curpos += lrpad / 2 - 1) < w) { 59 | drw_setscheme(drw, scheme[SchemeNorm]); 60 | - drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); 61 | + drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); 62 | } 63 | 64 | if (lines > 0) { 65 | @@ -604,6 +604,7 @@ setup(void) 66 | 67 | /* calculate menu geometry */ 68 | bh = drw->fonts->h + 2; 69 | + bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ 70 | lines = MAX(lines, 0); 71 | mh = (lines + 1) * bh; 72 | #ifdef XINERAMA 73 | @@ -683,6 +684,7 @@ static void 74 | usage(void) 75 | { 76 | fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 77 | + " [-h height]\n" 78 | " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); 79 | exit(1); 80 | } 81 | @@ -716,6 +718,10 @@ main(int argc, char *argv[]) 82 | prompt = argv[++i]; 83 | else if (!strcmp(argv[i], "-fn")) /* font or font set */ 84 | fonts[0] = argv[++i]; 85 | + else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ 86 | + lineheight = atoi(argv[++i]); 87 | + lineheight = MAX(lineheight,8); /* reasonable default in case of value too small/negative */ 88 | + } 89 | else if (!strcmp(argv[i], "-nb")) /* normal background color */ 90 | colors[SchemeNorm][ColBg] = argv[++i]; 91 | else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ 92 | -- 93 | 2.21.0 94 | 95 | -------------------------------------------------------------------------------- /apps/ dmenu/patches/dmenu-mousesupporthoverbgcol-5.0.diff: -------------------------------------------------------------------------------- 1 | Only in .: config.h 2 | diff -up ../dmenu-5.0/dmenu.c ./dmenu.c 3 | --- ../dmenu-5.0/dmenu.c Wed Sep 2 18:37:07 2020 4 | +++ ./dmenu.c Wed Nov 4 15:25:27 2020 5 | @@ -501,6 +501,156 @@ draw: 6 | } 7 | 8 | static void 9 | +buttonpress(XEvent *e) 10 | +{ 11 | + struct item *item; 12 | + XButtonPressedEvent *ev = &e->xbutton; 13 | + int x = 0, y = 0, h = bh, w; 14 | + 15 | + if (ev->window != win) 16 | + return; 17 | + 18 | + /* right-click: exit */ 19 | + if (ev->button == Button3) 20 | + exit(1); 21 | + 22 | + if (prompt && *prompt) 23 | + x += promptw; 24 | + 25 | + /* input field */ 26 | + w = (lines > 0 || !matches) ? mw - x : inputw; 27 | + 28 | + /* left-click on input: clear input, 29 | + * NOTE: if there is no left-arrow the space for < is reserved so 30 | + * add that to the input width */ 31 | + if (ev->button == Button1 && 32 | + ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + 33 | + ((!prev || !curr->left) ? TEXTW("<") : 0)) || 34 | + (lines > 0 && ev->y >= y && ev->y <= y + h))) { 35 | + insert(NULL, -cursor); 36 | + drawmenu(); 37 | + return; 38 | + } 39 | + /* middle-mouse click: paste selection */ 40 | + if (ev->button == Button2) { 41 | + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 42 | + utf8, utf8, win, CurrentTime); 43 | + drawmenu(); 44 | + return; 45 | + } 46 | + /* scroll up */ 47 | + if (ev->button == Button4 && prev) { 48 | + sel = curr = prev; 49 | + calcoffsets(); 50 | + drawmenu(); 51 | + return; 52 | + } 53 | + /* scroll down */ 54 | + if (ev->button == Button5 && next) { 55 | + sel = curr = next; 56 | + calcoffsets(); 57 | + drawmenu(); 58 | + return; 59 | + } 60 | + if (ev->button != Button1) 61 | + return; 62 | + /* disabled below, needs to be fixed */ 63 | + /* 64 | + if (ev->state & ~ControlMask) 65 | + return; 66 | + */ 67 | + if (lines > 0) { 68 | + /* vertical list: (ctrl)left-click on item */ 69 | + w = mw - x; 70 | + for (item = curr; item != next; item = item->right) { 71 | + y += h; 72 | + if (ev->y >= y && ev->y <= (y + h)) { 73 | + puts(item->text); 74 | + if (!(ev->state & ControlMask)) 75 | + exit(0); 76 | + sel = item; 77 | + if (sel) { 78 | + sel->out = 1; 79 | + drawmenu(); 80 | + } 81 | + return; 82 | + } 83 | + } 84 | + } else if (matches) { 85 | + /* left-click on left arrow */ 86 | + x += inputw; 87 | + w = TEXTW("<"); 88 | + if (prev && curr->left) { 89 | + if (ev->x >= x && ev->x <= x + w) { 90 | + sel = curr = prev; 91 | + calcoffsets(); 92 | + drawmenu(); 93 | + return; 94 | + } 95 | + } 96 | + /* horizontal list: (ctrl)left-click on item */ 97 | + for (item = curr; item != next; item = item->right) { 98 | + x += w; 99 | + w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); 100 | + if (ev->x >= x && ev->x <= x + w) { 101 | + puts(item->text); 102 | + if (!(ev->state & ControlMask)) 103 | + exit(0); 104 | + sel = item; 105 | + if (sel) { 106 | + sel->out = 1; 107 | + drawmenu(); 108 | + } 109 | + return; 110 | + } 111 | + } 112 | + /* left-click on right arrow */ 113 | + w = TEXTW(">"); 114 | + x = mw - w; 115 | + if (next && ev->x >= x && ev->x <= x + w) { 116 | + sel = curr = next; 117 | + calcoffsets(); 118 | + drawmenu(); 119 | + return; 120 | + } 121 | + } 122 | +} 123 | + 124 | +static void 125 | +mousemove(XEvent *e) 126 | +{ 127 | + struct item *item; 128 | + XPointerMovedEvent *ev = &e->xmotion; 129 | + int x = 0, y = 0, h = bh, w; 130 | + 131 | + if (lines > 0) { 132 | + w = mw - x; 133 | + for (item = curr; item != next; item = item->right) { 134 | + y += h; 135 | + if (ev->y >= y && ev->y <= (y + h)) { 136 | + sel = item; 137 | + calcoffsets(); 138 | + drawmenu(); 139 | + return; 140 | + } 141 | + } 142 | + } else if (matches) { 143 | + x += inputw; 144 | + w = TEXTW("<"); 145 | + for (item = curr; item != next; item = item->right) { 146 | + x += w; 147 | + w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); 148 | + if (ev->x >= x && ev->x <= x + w) { 149 | + sel = item; 150 | + calcoffsets(); 151 | + drawmenu(); 152 | + return; 153 | + } 154 | + } 155 | + } 156 | +} 157 | + 158 | +static void 159 | paste(void) 160 | { 161 | char *p, *q; 162 | @@ -561,6 +711,12 @@ run(void) 163 | break; 164 | cleanup(); 165 | exit(1); 166 | + case ButtonPress: 167 | + buttonpress(&ev); 168 | + break; 169 | + case MotionNotify: 170 | + mousemove(&ev); 171 | + break; 172 | case Expose: 173 | if (ev.xexpose.count == 0) 174 | drw_map(drw, win, 0, 0, mw, mh); 175 | @@ -658,7 +814,8 @@ setup(void) 176 | /* create menu window */ 177 | swa.override_redirect = True; 178 | swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 179 | - swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; 180 | + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | 181 | + ButtonPressMask | PointerMotionMask; 182 | win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, 183 | CopyFromParent, CopyFromParent, CopyFromParent, 184 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 185 | -------------------------------------------------------------------------------- /apps/ dmenu/stest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/apps/ dmenu/stest -------------------------------------------------------------------------------- /apps/ dmenu/stest.1: -------------------------------------------------------------------------------- 1 | .TH STEST 1 dmenu\-VERSION 2 | .SH NAME 3 | stest \- filter a list of files by properties 4 | .SH SYNOPSIS 5 | .B stest 6 | .RB [ -abcdefghlpqrsuwx ] 7 | .RB [ -n 8 | .IR file ] 9 | .RB [ -o 10 | .IR file ] 11 | .RI [ file ...] 12 | .SH DESCRIPTION 13 | .B stest 14 | takes a list of files and filters by the files' properties, analogous to 15 | .IR test (1). 16 | Files which pass all tests are printed to stdout. If no files are given, stest 17 | reads files from stdin. 18 | .SH OPTIONS 19 | .TP 20 | .B \-a 21 | Test hidden files. 22 | .TP 23 | .B \-b 24 | Test that files are block specials. 25 | .TP 26 | .B \-c 27 | Test that files are character specials. 28 | .TP 29 | .B \-d 30 | Test that files are directories. 31 | .TP 32 | .B \-e 33 | Test that files exist. 34 | .TP 35 | .B \-f 36 | Test that files are regular files. 37 | .TP 38 | .B \-g 39 | Test that files have their set-group-ID flag set. 40 | .TP 41 | .B \-h 42 | Test that files are symbolic links. 43 | .TP 44 | .B \-l 45 | Test the contents of a directory given as an argument. 46 | .TP 47 | .BI \-n " file" 48 | Test that files are newer than 49 | .IR file . 50 | .TP 51 | .BI \-o " file" 52 | Test that files are older than 53 | .IR file . 54 | .TP 55 | .B \-p 56 | Test that files are named pipes. 57 | .TP 58 | .B \-q 59 | No files are printed, only the exit status is returned. 60 | .TP 61 | .B \-r 62 | Test that files are readable. 63 | .TP 64 | .B \-s 65 | Test that files are not empty. 66 | .TP 67 | .B \-u 68 | Test that files have their set-user-ID flag set. 69 | .TP 70 | .B \-v 71 | Invert the sense of tests, only failing files pass. 72 | .TP 73 | .B \-w 74 | Test that files are writable. 75 | .TP 76 | .B \-x 77 | Test that files are executable. 78 | .SH EXIT STATUS 79 | .TP 80 | .B 0 81 | At least one file passed all tests. 82 | .TP 83 | .B 1 84 | No files passed all tests. 85 | .TP 86 | .B 2 87 | An error occurred. 88 | .SH SEE ALSO 89 | .IR dmenu (1), 90 | .IR test (1) 91 | -------------------------------------------------------------------------------- /apps/ dmenu/stest.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "arg.h" 12 | char *argv0; 13 | 14 | #define FLAG(x) (flag[(x)-'a']) 15 | 16 | static void test(const char *, const char *); 17 | static void usage(void); 18 | 19 | static int match = 0; 20 | static int flag[26]; 21 | static struct stat old, new; 22 | 23 | static void 24 | test(const char *path, const char *name) 25 | { 26 | struct stat st, ln; 27 | 28 | if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ 29 | && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ 30 | && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ 31 | && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ 32 | && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ 33 | && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ 34 | && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ 35 | && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ 36 | && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ 37 | && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ 38 | && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ 39 | && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ 40 | && (!FLAG('s') || st.st_size > 0) /* not empty */ 41 | && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ 42 | && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ 43 | && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ 44 | if (FLAG('q')) 45 | exit(0); 46 | match = 1; 47 | puts(name); 48 | } 49 | } 50 | 51 | static void 52 | usage(void) 53 | { 54 | fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " 55 | "[-n file] [-o file] [file...]\n", argv0); 56 | exit(2); /* like test(1) return > 1 on error */ 57 | } 58 | 59 | int 60 | main(int argc, char *argv[]) 61 | { 62 | struct dirent *d; 63 | char path[PATH_MAX], *line = NULL, *file; 64 | size_t linesiz = 0; 65 | ssize_t n; 66 | DIR *dir; 67 | int r; 68 | 69 | ARGBEGIN { 70 | case 'n': /* newer than file */ 71 | case 'o': /* older than file */ 72 | file = EARGF(usage()); 73 | if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) 74 | perror(file); 75 | break; 76 | default: 77 | /* miscellaneous operators */ 78 | if (strchr("abcdefghlpqrsuvwx", ARGC())) 79 | FLAG(ARGC()) = 1; 80 | else 81 | usage(); /* unknown flag */ 82 | } ARGEND; 83 | 84 | if (!argc) { 85 | /* read list from stdin */ 86 | while ((n = getline(&line, &linesiz, stdin)) > 0) { 87 | if (n && line[n - 1] == '\n') 88 | line[n - 1] = '\0'; 89 | test(line, line); 90 | } 91 | free(line); 92 | } else { 93 | for (; argc; argc--, argv++) { 94 | if (FLAG('l') && (dir = opendir(*argv))) { 95 | /* test directory contents */ 96 | while ((d = readdir(dir))) { 97 | r = snprintf(path, sizeof path, "%s/%s", 98 | *argv, d->d_name); 99 | if (r >= 0 && (size_t)r < sizeof path) 100 | test(path, d->d_name); 101 | } 102 | closedir(dir); 103 | } else { 104 | test(*argv, *argv); 105 | } 106 | } 107 | } 108 | return match ? 0 : 1; 109 | } 110 | -------------------------------------------------------------------------------- /apps/ dmenu/stest.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/apps/ dmenu/stest.o -------------------------------------------------------------------------------- /apps/ dmenu/util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void * 10 | ecalloc(size_t nmemb, size_t size) 11 | { 12 | void *p; 13 | 14 | if (!(p = calloc(nmemb, size))) 15 | die("calloc:"); 16 | return p; 17 | } 18 | 19 | void 20 | die(const char *fmt, ...) { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 28 | fputc(' ', stderr); 29 | perror(NULL); 30 | } else { 31 | fputc('\n', stderr); 32 | } 33 | 34 | exit(1); 35 | } 36 | -------------------------------------------------------------------------------- /apps/ dmenu/util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | 7 | void die(const char *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | -------------------------------------------------------------------------------- /apps/ dmenu/util.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/apps/ dmenu/util.o -------------------------------------------------------------------------------- /apps/conky/sysinfo.lua: -------------------------------------------------------------------------------- 1 | conky.config = { 2 | background = true, 3 | update_interval = 1, 4 | total_run_times = 0, 5 | no_buffers = true, 6 | double_buffer = true, 7 | cpu_avg_samples = 2, 8 | net_avg_samples = 2, 9 | diskio_avg_samples = 10, 10 | 11 | alignment = 'top_right', 12 | gap_x = 130, 13 | gap_y = 110, 14 | 15 | -- minimum_height = 180, 16 | minimum_width = 400, 17 | -- maximum_width = 700, 18 | 19 | draw_shades = false, 20 | draw_outline = false, 21 | draw_borders = false, 22 | draw_graph_borders = true, 23 | 24 | own_window = true, 25 | own_window_class = 'Conky', 26 | own_window_type = 'override', 27 | own_window_transparent = true, 28 | own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager', 29 | own_window_colour = '#000000', 30 | own_window_argb_visual = false, 31 | own_window_argb_value = 0, 32 | 33 | format_human_readable = true, 34 | use_xft = true, 35 | xftalpha = 0.1, 36 | uppercase = false, 37 | override_utf8_locale = true, 38 | 39 | default_color = '#ffffff', 40 | default_shade_color = "#ff0000", 41 | default_outline_color = '#00ff00', 42 | color0 = '#00ffff', 43 | color1 = '#264653', 44 | 45 | } 46 | 47 | conky.text = [[ 48 | ${offset 18}${font Cascadia Code:pixelsize=23}${color0}------------- ${font Cascadia Code:bold:pixelsize=23}${color0}SYSTEM${font Cascadia Code:pixelsize=23}${color0} -------------- 49 | ${offset -20}${font Cascadia Code:pixelsize=15}${color0}$sysname $kernel $alignr $machine 50 | ${offset -5}${font Cascadia Code:pixelsize=15}Date $alignr${time %A, %B %e} 51 | ${offset -5}${font Cascadia Code:pixelsize=15}Uptime $alignr${uptime_short} 52 | 53 | ${offset -2}${font Cascadia Code:pixelsize=23}${color0}------------- ${font Cascadia Code:bold:pixelsize=23}${color0}NETWORK${font Cascadia Code:pixelsize=23}${color0} ------------- 54 | ${offset -20}${font Cascadia Code:pixelsize=15}${color0}Private IP Address $alignr${addr wlp3s0} 55 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Public IP Address $alignr${offset 10}${execi 10 curl -s api.ipify.org} 56 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Down: $alignr${downspeed wlp3s0} 57 | ${offset -5}${downspeedgraph wlp3s0 20,455 #00ffff #00ffff} 58 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Up: $alignr${upspeed wlp3s0} 59 | ${offset -5}${upspeedgraph wlp3s0 20,455 #00ffff #00ffff} 60 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Downloaded: ${totaldown wlp3s0} ${alignr}${font Cascadia Code:bold:pixelsize=15}${color0}Uploaded: ${totalup wlp3s0} 61 | 62 | ${font Cascadia Code:pixelsize=23}${color0}------------ ${font Cascadia Code:bold:pixelsize=23}${color0}PROCESSORS${font Cascadia Code:pixelsize=23}${color0} ----------- 63 | ${offset -20}${font Cascadia Code:pixelsize=15}${color0}Core1: ${cpu cpu1}% (${freq 1}MHz)${alignr}${cpubar cpu1 7, 250} 64 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core2: ${cpu cpu2}% (${freq 2}MHz)${alignr}${cpubar cpu2 7, 250} 65 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core3: ${cpu cpu3}% (${freq 3}MHz)${alignr}${cpubar cpu3 7, 250} 66 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core4: ${cpu cpu4}% (${freq 4}MHz)${alignr}${cpubar cpu4 7, 250} 67 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core5: ${cpu cpu5}% (${freq 5}MHz)${alignr}${cpubar cpu5 7, 250} 68 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core6: ${cpu cpu6}% (${freq 6}MHz)${alignr}${cpubar cpu6 7, 250} 69 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core7: ${cpu cpu7}% (${freq 7}MHz)${alignr}${cpubar cpu7 7, 250} 70 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Core8: ${cpu cpu8}% (${freq 8}MHz)${alignr}${cpubar cpu8 7, 250} 71 | 72 | ${font Cascadia Code:pixelsize=23}${color0}-------------- ${font Cascadia Code:bold:pixelsize=23}${color0}MEMORY${font Cascadia Code:pixelsize=23}${color0} ------------- 73 | ${offset -20}${font Cascadia Code:pixelsize=15}${color0}Read: $alignr${diskio_read} 74 | ${offset -5}${diskiograph_read 20,455 #00ffff #00ffff 750} 75 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Write: $alignr${diskio_write} 76 | ${offset -5}${diskiograph_write 20,455 #00ffff #00ffff 750} 77 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Ram: $mem/$memmax${alignr}${membar 7,250} 78 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}/home: ${fs_used /home}/${fs_size /home}${alignr}${fs_bar 7,250} 79 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Swap: ${swap}/${swapmax}${alignr}${swapbar 7,250} 80 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}Entropy: ${entropy_avail}/${entropy_poolsize}${alignr}${entropy_bar 7,250} 81 | 82 | ${font Cascadia Code:pixelsize=23}${color0}------------ ${font Cascadia Code:bold:pixelsize=23}${color0}PROCESSES${font Cascadia Code:pixelsize=23}${color0} ------------ 83 | ${offset -20}${font Cascadia Code:pixelsize=15}${color0}Total: $alignr${processes} 84 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}${top_mem name 1}$alignr${top cpu 1}% CPU ${top mem 1}% Ram 85 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}${top_mem name 2}$alignr${top cpu 2}% CPU ${top mem 2}% Ram 86 | ${offset -5}${font Cascadia Code:pixelsize=15}${color0}${top_mem name 3}$alignr${top cpu 3}% CPU ${top mem 3}% Ram 87 | ]] -------------------------------------------------------------------------------- /apps/conky/time.lua: -------------------------------------------------------------------------------- 1 | conky.config = { 2 | background = true, 3 | update_interval = 1, 4 | total_run_times = 0, 5 | no_buffers = true, 6 | double_buffer = true, 7 | cpu_avg_samples = 2, 8 | net_avg_samples = 1, 9 | diskio_avg_samples = 10, 10 | 11 | alignment = 'top_left', 12 | gap_x = 100, 13 | gap_y = 400, 14 | 15 | minimum_height = 180, 16 | minimum_width = 5, 17 | -- maximum_width = 700, 18 | 19 | draw_shades = false, 20 | draw_outline = false, 21 | draw_borders = false, 22 | draw_graph_borders = false, 23 | 24 | own_window = true, 25 | own_window_class = 'Conky', 26 | own_window_type = 'override', 27 | own_window_transparent = true, 28 | own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager', 29 | own_window_colour = '#000000', 30 | own_window_argb_visual = false, 31 | own_window_argb_value = 0, 32 | 33 | format_human_readable = true, 34 | use_xft = true, 35 | xftalpha = 0.1, 36 | uppercase = false, 37 | override_utf8_locale = true, 38 | 39 | default_color = '#ffffff', 40 | default_shade_color = "#ff0000", 41 | default_outline_color = '#00ff00', 42 | } 43 | 44 | conky.text = [[ 45 | ${voffset 10}${color #eaeaea}${font DaddyTimeMono Nerd Font:pixelsize=150}${time %I:%M}${voffset -65}${offset 30}${color #00ffff}${font DaddyTimeMono Nerd Font:pixelsize=42}${time %d}${offset 10}${voffset -8}${color EAEAEA}${font DaddyTimeMono Nerd Font:pixelsize=22}${time %B}${offset 10}${voffset 1}${time %Y}${font}${voffset 28}${font DaddyTimeMono Nerd Font:pixelsize=58}${offset -168}${time %A} 46 | ]] -------------------------------------------------------------------------------- /images/app_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/images/app_menu.png -------------------------------------------------------------------------------- /images/lock_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/images/lock_screen.png -------------------------------------------------------------------------------- /images/qtile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/images/qtile.png -------------------------------------------------------------------------------- /images/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/images/tiles.png -------------------------------------------------------------------------------- /images/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/images/tiles_2.png -------------------------------------------------------------------------------- /utils/icons/arch-icons/arch_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/arch-icons/arch_dark.png -------------------------------------------------------------------------------- /utils/icons/arch-icons/arch_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/arch-icons/arch_light.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-caution-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-caution-charging.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-caution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-caution.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-empty.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-full-charged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-full-charged.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-full-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-full-charging.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-full.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-good-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-good-charging.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-good.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-low-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-low-charging.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-low.png -------------------------------------------------------------------------------- /utils/icons/battery-icons/dark/battery-missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/battery-icons/dark/battery-missing.png -------------------------------------------------------------------------------- /utils/icons/bluetooth-icons/bluetooth_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/bluetooth-icons/bluetooth_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/bluetooth-icons/bluetooth_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/bluetooth-icons/bluetooth_light_bold.png -------------------------------------------------------------------------------- /utils/icons/brightness-icons/brightness_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/brightness-icons/brightness_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/brightness-icons/brightness_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/brightness-icons/brightness_light_bold.png -------------------------------------------------------------------------------- /utils/icons/clock-icons/clock_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/clock-icons/clock_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/clock-icons/clock_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/clock-icons/clock_light_bold.png -------------------------------------------------------------------------------- /utils/icons/cpu-icons/cpu_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/cpu-icons/cpu_dark.png -------------------------------------------------------------------------------- /utils/icons/cpu-icons/cpu_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/cpu-icons/cpu_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-bsp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-bsp.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-columns.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-floating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-floating.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-matrix.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-max.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-monadtall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-monadtall.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-monadthreecol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-monadthreecol.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-monadwide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-monadwide.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-ratiotile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-ratiotile.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-slice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-slice.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-spiral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-spiral.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-stack.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-tile.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-treetab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-treetab.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-unknown.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-verticaltile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-verticaltile.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-wmii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-wmii.png -------------------------------------------------------------------------------- /utils/icons/layout-icons/dark/layout-zoomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/layout-icons/dark/layout-zoomy.png -------------------------------------------------------------------------------- /utils/icons/logout-icons/logout_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/logout-icons/logout_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/memory-icons/memory_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/memory-icons/memory_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/network-icons/network_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/network-icons/network_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/network-icons/network_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/network-icons/network_light_bold.png -------------------------------------------------------------------------------- /utils/icons/network-icons/wifi_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/network-icons/wifi_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/network-icons/wifi_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/network-icons/wifi_light_bold.png -------------------------------------------------------------------------------- /utils/icons/poweroff-icons/poweroff_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/poweroff-icons/poweroff_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/temperature-icons/terperature_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/temperature-icons/terperature_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/update-icons/update_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/update-icons/update_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/update-icons/update_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/update-icons/update_light_bold.png -------------------------------------------------------------------------------- /utils/icons/volume-icons/volume_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/volume-icons/volume_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/volume-icons/volume_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/volume-icons/volume_light_bold.png -------------------------------------------------------------------------------- /utils/icons/window-count-icons/window_count_dark_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/window-count-icons/window_count_dark_bold.png -------------------------------------------------------------------------------- /utils/icons/window-count-icons/window_count_light_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yash3001/qtile/fa5d288534e8038e3de59618caa9d5a088d6cc83/utils/icons/window-count-icons/window_count_light_bold.png -------------------------------------------------------------------------------- /utils/scripts/autostart.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | # Compositor 4 | compton --config ~/.config/compton/compton.conf & 5 | 6 | # Conky 7 | killall conky &> /dev/null 8 | conky -c ~/.config/qtile/apps/conky/time.lua & 9 | conky -c ~/.config/qtile/apps/conky/sysinfo.lua & 10 | 11 | # Set Wallpaper 12 | nitrogen --restore & 13 | 14 | # Start the notification daemon 15 | twmnd & 16 | 17 | # Start clipmenu daemon 18 | clipmenud & 19 | 20 | # Start xfce4 power manager 21 | xfce4-power-manager & 22 | 23 | # For reverse scrolling and tap to touch 24 | xinput set-prop 'ELAN1203:00 04F3:307A Touchpad' 'libinput Tapping Enabled' 1 & 25 | xinput set-prop 'ELAN1203:00 04F3:307A Touchpad' 'libinput Natural Scrolling Enabled' 1 & 26 | -------------------------------------------------------------------------------- /utils/scripts/brightness_dmenu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | while [ true ] 4 | do 5 | choice=$(echo -e "Increase 1%\nDecrease 1%\nExit" | dmenu -c -h 40 -l 3 -p "Brightness:") 6 | 7 | if [[ ($choice == "Increase 1%") ]] 8 | then 9 | brightnessctl s 1%+ 10 | fi 11 | 12 | if [[ ($choice == "Decrease 1%") ]] 13 | then 14 | brightnessctl s 1%- 15 | fi 16 | 17 | if [[ ($choice == "Exit") ]] 18 | then 19 | break 20 | fi 21 | 22 | if [[ ($choice == "") ]] 23 | then 24 | break 25 | fi 26 | done -------------------------------------------------------------------------------- /utils/scripts/brightness_down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | brillo -U 1 -------------------------------------------------------------------------------- /utils/scripts/brightness_up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | brillo -A 1 -------------------------------------------------------------------------------- /utils/scripts/check_updates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pacman_updates=$(checkupdates | wc -l) 4 | aur_updates=$(checkupdates-aur | wc -l) 5 | total=$((pacman_updates + aur_updates)) 6 | echo -n $total 7 | -------------------------------------------------------------------------------- /utils/scripts/clipmenu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | clipmenu -c -h 40 -l 10 -p "Clips:" 4 | -------------------------------------------------------------------------------- /utils/scripts/cmus_next.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | if ! pgrep -x cmus ; then 4 | st -e cmus 5 | else 6 | cmus-remote -n 7 | fi 8 | -------------------------------------------------------------------------------- /utils/scripts/cmus_previous.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | if ! pgrep -x cmus ; then 4 | st -e cmus 5 | else 6 | cmus-remote -r 7 | fi 8 | -------------------------------------------------------------------------------- /utils/scripts/cmus_start_stop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | if ! pgrep -x cmus ; then 4 | st -e cmus 5 | else 6 | cmus-remote -u 7 | fi 8 | -------------------------------------------------------------------------------- /utils/scripts/dmenu_bluetooth.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # _ _ _ _ _ _ 3 | # __| |_ __ ___ ___ _ __ _ _ | |__ | |_ _ ___| |_ ___ ___ | |_ | |__ 4 | # / _` | '_ ` _ \ / _ \ '_ \| | | |_____| '_ \| | | | |/ _ \ __/ _ \ / _ \| __|| '_ \ 5 | # | (_| | | | | | | __/ | | | |_| |_____| |_) | | |_| | __/ || (_) | (_) | |_ | | | | 6 | # \__,_|_| |_| |_|\___|_| |_|\__,_| |_.__/|_|\__,_|\___|\__\___/ \___/ \__||_| |_| 7 | # 8 | # Author: Nick Clyde (clydedroid) 9 | # dmenu support by: Layerex 10 | # 11 | # A script that generates a dmenu menu that uses bluetoothctl to 12 | # connect to bluetooth devices and display status info. 13 | # 14 | # Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu) 15 | # Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl) 16 | # 17 | # Depends on: 18 | # Arch repositories: dmenu, bluez-utils (contains bluetoothctl) 19 | 20 | # Constants 21 | divider="---------" 22 | goback="Back" 23 | 24 | # Checks if bluetooth controller is powered on 25 | power_on() { 26 | if bluetoothctl show | grep -F -q "Powered: yes"; then 27 | return 0 28 | else 29 | return 1 30 | fi 31 | } 32 | 33 | # Toggles power state 34 | toggle_power() { 35 | if power_on; then 36 | bluetoothctl power off 37 | show_menu 38 | else 39 | if rfkill list bluetooth | grep -F -q 'blocked: yes'; then 40 | rfkill unblock bluetooth && sleep 3 41 | fi 42 | bluetoothctl power on 43 | show_menu 44 | fi 45 | } 46 | 47 | # Checks if controller is scanning for new devices 48 | scan_on() { 49 | if bluetoothctl show | grep -F -q "Discovering: yes"; then 50 | echo "Scan: on" 51 | return 0 52 | else 53 | echo "Scan: off" 54 | return 1 55 | fi 56 | } 57 | 58 | # Toggles scanning state 59 | toggle_scan() { 60 | if scan_on; then 61 | kill "$(pgrep -F -f "bluetoothctl scan on")" 62 | bluetoothctl scan off 63 | show_menu 64 | else 65 | bluetoothctl scan on & 66 | echo "Scanning..." 67 | sleep 5 68 | show_menu 69 | fi 70 | } 71 | 72 | # Checks if controller is able to pair to devices 73 | pairable_on() { 74 | if bluetoothctl show | grep -F -q "Pairable: yes"; then 75 | echo "Pairable: on" 76 | return 0 77 | else 78 | echo "Pairable: off" 79 | return 1 80 | fi 81 | } 82 | 83 | # Toggles pairable state 84 | toggle_pairable() { 85 | if pairable_on; then 86 | bluetoothctl pairable off 87 | show_menu 88 | else 89 | bluetoothctl pairable on 90 | show_menu 91 | fi 92 | } 93 | 94 | # Checks if controller is discoverable by other devices 95 | discoverable_on() { 96 | if bluetoothctl show | grep -F -q "Discoverable: yes"; then 97 | echo "Discoverable: on" 98 | return 0 99 | else 100 | echo "Discoverable: off" 101 | return 1 102 | fi 103 | } 104 | 105 | # Toggles discoverable state 106 | toggle_discoverable() { 107 | if discoverable_on; then 108 | bluetoothctl discoverable off 109 | show_menu 110 | else 111 | bluetoothctl discoverable on 112 | show_menu 113 | fi 114 | } 115 | 116 | # Checks if a device is connected 117 | device_connected() { 118 | device_info=$(bluetoothctl info "$1") 119 | if echo "$device_info" | grep -F -q "Connected: yes"; then 120 | return 0 121 | else 122 | return 1 123 | fi 124 | } 125 | 126 | # Toggles device connection 127 | toggle_connection() { 128 | if device_connected "$1"; then 129 | bluetoothctl disconnect "$1" 130 | # device_menu "$device" 131 | else 132 | bluetoothctl connect "$1" 133 | # device_menu "$device" 134 | fi 135 | } 136 | 137 | # Checks if a device is paired 138 | device_paired() { 139 | device_info=$(bluetoothctl info "$1") 140 | if echo "$device_info" | grep -F -q "Paired: yes"; then 141 | echo "Paired: yes" 142 | return 0 143 | else 144 | echo "Paired: no" 145 | return 1 146 | fi 147 | } 148 | 149 | # Toggles device paired state 150 | toggle_paired() { 151 | if device_paired "$1"; then 152 | bluetoothctl remove "$1" 153 | device_menu "$device" 154 | else 155 | bluetoothctl pair "$1" 156 | device_menu "$device" 157 | fi 158 | } 159 | 160 | # Checks if a device is trusted 161 | device_trusted() { 162 | device_info=$(bluetoothctl info "$1") 163 | if echo "$device_info" | grep -F -q "Trusted: yes"; then 164 | echo "Trusted: yes" 165 | return 0 166 | else 167 | echo "Trusted: no" 168 | return 1 169 | fi 170 | } 171 | 172 | # Toggles device connection 173 | toggle_trust() { 174 | if device_trusted "$1"; then 175 | bluetoothctl untrust "$1" 176 | device_menu "$device" 177 | else 178 | bluetoothctl trust "$1" 179 | device_menu "$device" 180 | fi 181 | } 182 | 183 | # Prints a short string with the current bluetooth status 184 | # Useful for status bars like polybar, etc. 185 | print_status() { 186 | if power_on; then 187 | printf '' 188 | 189 | mapfile -t paired_devices < <(bluetoothctl paired-devices | grep -F Device | cut -d ' ' -f 2) 190 | counter=0 191 | 192 | for device in "${paired_devices[@]}"; do 193 | if device_connected "$device"; then 194 | device_alias="$(bluetoothctl info "$device" | grep -F "Alias" | cut -d ' ' -f 2-)" 195 | 196 | if [ $counter -gt 0 ]; then 197 | printf ", %s" "$device_alias" 198 | else 199 | printf " %s" "$device_alias" 200 | fi 201 | 202 | ((counter++)) 203 | fi 204 | done 205 | printf "\n" 206 | else 207 | echo "" 208 | fi 209 | } 210 | 211 | # A submenu for a specific device that allows connecting, pairing, and trusting 212 | device_menu() { 213 | device=$1 214 | 215 | # Get device name and mac address 216 | device_name="$(echo "$device" | cut -d ' ' -f 3-)" 217 | mac="$(echo "$device" | cut -d ' ' -f 2)" 218 | 219 | # Build options 220 | if device_connected "$mac"; then 221 | connected="Connected: yes" 222 | else 223 | connected="Connected: no" 224 | fi 225 | paired=$(device_paired "$mac") 226 | trusted=$(device_trusted "$mac") 227 | options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit" 228 | 229 | # Open dmenu menu, read chosen option 230 | chosen="$(echo -e "$options" | run_dmenu "$device_name")" 231 | 232 | # Match chosen option to command 233 | case $chosen in 234 | "" | "$divider") 235 | echo "No option chosen." 236 | ;; 237 | "$connected") 238 | toggle_connection "$mac" 239 | ;; 240 | "$paired") 241 | toggle_paired "$mac" 242 | ;; 243 | "$trusted") 244 | toggle_trust "$mac" 245 | ;; 246 | "$goback") 247 | show_menu 248 | ;; 249 | esac 250 | } 251 | 252 | # Opens a dmenu menu with current bluetooth status and options to connect 253 | show_menu() { 254 | # Get menu options 255 | if power_on; then 256 | power="Power: on" 257 | 258 | # Human-readable names of devices, one per line 259 | # If scan is off, will only list paired devices 260 | devices=$(bluetoothctl devices | grep -F Device | cut -d ' ' -f 3-) 261 | 262 | # Get controller flags 263 | scan=$(scan_on) 264 | pairable=$(pairable_on) 265 | discoverable=$(discoverable_on) 266 | 267 | # Options passed to dmenu 268 | options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit" 269 | else 270 | power="Power: off" 271 | options="$power\nExit" 272 | fi 273 | 274 | # Open dmenu menu, read chosen option 275 | chosen="$(echo -e "$options" | run_dmenu "Bluetooth")" 276 | 277 | # Match chosen option to command 278 | case $chosen in 279 | "" | "$divider") 280 | echo "No option chosen." 281 | ;; 282 | "$power") 283 | toggle_power 284 | ;; 285 | "$scan") 286 | toggle_scan 287 | ;; 288 | "$discoverable") 289 | toggle_discoverable 290 | ;; 291 | "$pairable") 292 | toggle_pairable 293 | ;; 294 | *) 295 | device=$(bluetoothctl devices | grep -F "$chosen") 296 | # Open a submenu if a device is selected 297 | if [[ $device ]]; then device_menu "$device"; fi 298 | ;; 299 | esac 300 | } 301 | 302 | original_args=("$@") 303 | 304 | # dmenu command to pipe into. Extra arguments to dmenu-bluetooth are passed through to dmenu. This 305 | # allows the user to set fonts, sizes, colours, etc. 306 | run_dmenu() { 307 | dmenu -c -h 40 -l 20 "${original_args[@]}" -i -p "$1" 308 | } 309 | 310 | case "$1" in 311 | --status) 312 | print_status 313 | ;; 314 | *) 315 | show_menu 316 | ;; 317 | esac 318 | -------------------------------------------------------------------------------- /utils/scripts/logout_prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | ans=$(echo -e "no\nyes" | dmenu -c -h 40 -l 2 -p "Do you want to logout?") 4 | 5 | if [[ ($ans == "yes") ]] 6 | then 7 | wm=$(wmctrl -m | awk 'NR==1{print $2, $3, $4, $5}') 8 | wm=$(sed 's/[ ]*$//' <<<"$wm") 9 | killall $wm 10 | fi -------------------------------------------------------------------------------- /utils/scripts/network_manager_dmenu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding:utf8 3 | """NetworkManager command line dmenu script. 4 | 5 | To add new connections or enable/disable networking requires policykit 6 | permissions setup per: 7 | https://wiki.archlinux.org/index.php/NetworkManager#Set_up_PolicyKit_permissions 8 | 9 | OR running the script as root 10 | 11 | Add dmenu options and default terminal if desired to 12 | ~/.config/networkmanager-dmenu/config.ini 13 | 14 | """ 15 | import pathlib 16 | import struct 17 | import configparser 18 | import locale 19 | import os 20 | from os.path import expanduser 21 | import shlex 22 | from shutil import which 23 | import sys 24 | import uuid 25 | import subprocess 26 | 27 | import gi 28 | gi.require_version('NM', '1.0') 29 | from gi.repository import GLib, NM # noqa pylint: disable=wrong-import-position 30 | 31 | ENV = os.environ.copy() 32 | ENV['LC_ALL'] = 'C' 33 | ENC = locale.getpreferredencoding() 34 | 35 | CLIENT = NM.Client.new(None) 36 | LOOP = GLib.MainLoop() 37 | CONNS = CLIENT.get_connections() 38 | 39 | CONF = configparser.ConfigParser() 40 | CONF.read(expanduser("~/.config/networkmanager-dmenu/config.ini")) 41 | 42 | 43 | def cli_args(): 44 | """ Don't override dmenu_cmd function arguments with CLI args. Removes -l 45 | and -p if those are passed on the command line. 46 | 47 | Exception: if -l is passed and dmenu_command is not defined, assume that the 48 | user wants to switch dmenu to the vertical layout and include -l. 49 | 50 | Returns: List of additional CLI arguments 51 | 52 | """ 53 | args = sys.argv[1:] 54 | cmd = CONF.get('dmenu', 'dmenu_command', fallback=False) 55 | if "-l" in args or "-p" in args: 56 | for nope in ['-l', '-p'] if cmd is not False else ['-p']: 57 | try: 58 | nope_idx = args.index(nope) 59 | del args[nope_idx] 60 | del args[nope_idx] 61 | except ValueError: 62 | pass 63 | return args 64 | 65 | 66 | def dmenu_pass(command, color): 67 | """Check if dmenu passphrase patch is applied and return the correct command 68 | line arg list 69 | 70 | Args: command - string 71 | color - obscure color string 72 | Returns: list or None 73 | 74 | """ 75 | if command != 'dmenu': 76 | return None 77 | try: 78 | # Check for dmenu password patch 79 | dm_patch = b'P' in subprocess.run(["dmenu", "-h"], 80 | capture_output=True, 81 | check=False).stderr 82 | except FileNotFoundError: 83 | dm_patch = False 84 | return ["-P"] if dm_patch else ["-nb", color, "-nf", color] 85 | 86 | 87 | def dmenu_cmd(num_lines, prompt="Networks", active_lines=None): 88 | """Parse config.ini for menu options 89 | 90 | Args: args - num_lines: number of lines to display 91 | prompt: prompt to show 92 | active_lines: list of line numbers to tag as active 93 | Returns: command invocation (as a list of strings) for example 94 | ["dmenu", "-l", "", "-p", "", "-i"] 95 | 96 | """ 97 | # Create command string 98 | commands = {"dmenu": ["-c","-h", "40", "-l", "20", "-p", str(prompt)], 99 | "rofi": ["-dmenu", "-p", str(prompt), "-l", str(num_lines)], 100 | "bemenu": ["-p", str(prompt)], 101 | "wofi": ["-p", str(prompt)]} 102 | command = shlex.split(CONF.get('dmenu', 'dmenu_command', fallback="dmenu")) 103 | command.extend(cli_args()) 104 | command.extend(commands.get(command[0], [])) 105 | # Rofi Highlighting 106 | rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False) 107 | if rofi_highlight is True and command[0] == "rofi" and active_lines: 108 | command.extend(["-a", ",".join([str(num) for num in active_lines])]) 109 | # Passphrase prompts 110 | obscure = CONF.getboolean('dmenu_passphrase', 'obscure', fallback=False) 111 | if prompt == "Passphrase" and obscure is True: 112 | obscure_color = CONF.get('dmenu_passphrase', 'obscure_color', fallback='#222222') 113 | pass_prompts = {"dmenu": dmenu_pass(command[0], obscure_color), 114 | "rofi": ['-password'], 115 | "bemenu": ['-x'], 116 | "wofi": ['-P']} 117 | command.extend(pass_prompts.get(command[0], [])) 118 | return command 119 | 120 | 121 | def choose_adapter(client): 122 | """If there is more than one wifi adapter installed, ask which one to use 123 | 124 | """ 125 | devices = client.get_devices() 126 | devices = [i for i in devices if i.get_device_type() == NM.DeviceType.WIFI] 127 | if not devices: 128 | return None 129 | if len(devices) == 1: 130 | return devices[0] 131 | device_names = "\n".join([d.get_iface() for d in devices]) 132 | sel = subprocess.run(dmenu_cmd(len(devices), "CHOOSE ADAPTER:"), 133 | capture_output=True, 134 | check=False, 135 | env=ENV, 136 | input=device_names, 137 | encoding=ENC).stdout 138 | if not sel.strip(): 139 | sys.exit() 140 | devices = [i for i in devices if i.get_iface() == sel.strip()] 141 | assert len(devices) == 1 142 | return devices[0] 143 | 144 | 145 | def is_installed(cmd): 146 | """Check if a utility is installed""" 147 | return which(cmd) is not None 148 | 149 | 150 | def bluetooth_get_enabled(): 151 | """Check if bluetooth is enabled via rfkill. 152 | 153 | Returns None if no bluetooth device was found. 154 | """ 155 | # See https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-rfkill 156 | for path in pathlib.Path('/sys/class/rfkill/').glob('rfkill*'): 157 | if (path / 'type').read_text().strip() == 'bluetooth': 158 | return (path / 'soft').read_text().strip() == '0' 159 | return None 160 | 161 | 162 | def create_other_actions(client): 163 | """Return list of other actions that can be taken 164 | 165 | """ 166 | networking_enabled = client.networking_get_enabled() 167 | networking_action = "Disable" if networking_enabled else "Enable" 168 | 169 | wifi_enabled = client.wireless_get_enabled() 170 | wifi_action = "Disable" if wifi_enabled else "Enable" 171 | 172 | bluetooth_enabled = bluetooth_get_enabled() 173 | bluetooth_action = "Disable" if bluetooth_enabled else "Enable" 174 | 175 | actions = [Action(f"{wifi_action} Wifi", toggle_wifi, 176 | not wifi_enabled), 177 | Action(f"{networking_action} Networking", 178 | toggle_networking, not networking_enabled)] 179 | if bluetooth_enabled is not None: 180 | actions.append(Action(f"{bluetooth_action} Bluetooth", 181 | toggle_bluetooth, not bluetooth_enabled)) 182 | actions += [Action("Launch Connection Manager", launch_connection_editor), 183 | Action("Delete a Connection", delete_connection)] 184 | if wifi_enabled: 185 | actions.append(Action("Rescan Wifi Networks", rescan_wifi)) 186 | return actions 187 | 188 | 189 | def rescan_wifi(): 190 | """ 191 | Rescan Wifi Access Points 192 | """ 193 | for dev in CLIENT.get_devices(): 194 | if gi.repository.NM.DeviceWifi == type(dev): 195 | try: 196 | dev.request_scan_async(None, rescan_cb, None) 197 | LOOP.run() 198 | except gi.repository.GLib.Error as err: 199 | # Too frequent rescan error 200 | notify("Wifi rescan failed", urgency="critical") 201 | if not err.code == 6: # pylint: disable=no-member 202 | raise err 203 | 204 | 205 | def rescan_cb(dev, res, data): 206 | """Callback for rescan_wifi. Just for notifications 207 | 208 | """ 209 | if dev.request_scan_finish(res) is True: 210 | notify("Wifi scan complete") 211 | else: 212 | notify("Wifi scan failed", urgency="critical") 213 | LOOP.quit() 214 | 215 | 216 | def ssid_to_utf8(nm_ap): 217 | """ Convert binary ssid to utf-8 """ 218 | ssid = nm_ap.get_ssid() 219 | if not ssid: 220 | return "" 221 | ret = NM.utils_ssid_to_utf8(ssid.get_data()) 222 | return ret 223 | 224 | 225 | def prompt_saved(saved_cons): 226 | """Prompt for a saved connection.""" 227 | actions = create_saved_actions(saved_cons) 228 | sel = get_selection(actions) 229 | sel() 230 | 231 | 232 | def ap_security(nm_ap): 233 | """Parse the security flags to return a string with 'WPA2', etc. """ 234 | flags = nm_ap.get_flags() 235 | wpa_flags = nm_ap.get_wpa_flags() 236 | rsn_flags = nm_ap.get_rsn_flags() 237 | sec_str = "" 238 | if ((flags & getattr(NM, '80211ApFlags').PRIVACY) and 239 | (wpa_flags == 0) and (rsn_flags == 0)): 240 | sec_str += " WEP" 241 | if wpa_flags != 0: 242 | sec_str += " WPA1" 243 | if rsn_flags != 0: 244 | sec_str += " WPA2" 245 | if ((wpa_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_802_1X) or 246 | (rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_802_1X)): 247 | sec_str += " 802.1X" 248 | 249 | # If there is no security use "--" 250 | if sec_str == "": 251 | sec_str = "--" 252 | return sec_str.lstrip() 253 | 254 | 255 | class Action(): # pylint: disable=too-few-public-methods 256 | """Helper class to execute functions from a string variable""" 257 | def __init__(self, 258 | name, 259 | func, 260 | args=None, 261 | active=False): 262 | self.name = name 263 | self.func = func 264 | self.is_active = active 265 | if args is None: 266 | self.args = None 267 | elif isinstance(args, list): 268 | self.args = args 269 | else: 270 | self.args = [args] 271 | 272 | def __str__(self): 273 | return self.name 274 | 275 | def __call__(self): 276 | if self.args is None: 277 | self.func() 278 | else: 279 | self.func(*self.args) 280 | 281 | 282 | def conn_matches_adapter(conn, adapter): 283 | """Return True if the connection is applicable for the given adapter. 284 | 285 | There seem to be two ways for a connection specify what interface it belongs 286 | to: 287 | 288 | - By setting 'mac-address' in [wifi] to the adapter's MAC 289 | - By setting 'interface-name` in [connection] to the adapter's name. 290 | 291 | Depending on how the connection was added, it seems like either 292 | 'mac-address', 'interface-name' or neither of both is set. 293 | """ 294 | # [wifi] mac-address 295 | setting_wireless = conn.get_setting_wireless() 296 | mac = setting_wireless.get_mac_address() 297 | if mac is not None: 298 | return mac == adapter.get_permanent_hw_address() 299 | 300 | # [connection] interface-name 301 | setting_connection = conn.get_setting_connection() 302 | interface = setting_connection.get_interface_name() 303 | if interface is not None: 304 | return interface == adapter.get_iface() 305 | 306 | # Neither is set, let's assume this connection is for multiple/all adapters. 307 | return True 308 | 309 | 310 | def process_ap(nm_ap, is_active, adapter): 311 | """Activate/Deactivate a connection and get password if required""" 312 | if is_active: 313 | CLIENT.deactivate_connection_async(nm_ap, None, deactivate_cb, nm_ap) 314 | LOOP.run() 315 | else: 316 | conns_cur = [i for i in CONNS if 317 | i.get_setting_wireless() is not None and 318 | conn_matches_adapter(i, adapter)] 319 | con = nm_ap.filter_connections(conns_cur) 320 | if len(con) > 1: 321 | raise ValueError("There are multiple connections possible") 322 | 323 | if len(con) == 1: 324 | CLIENT.activate_connection_async(con[0], adapter, nm_ap.get_path(), 325 | None, activate_cb, nm_ap) 326 | LOOP.run() 327 | else: 328 | if ap_security(nm_ap) != "--": 329 | password = get_passphrase() 330 | else: 331 | password = "" 332 | set_new_connection(nm_ap, password, adapter) 333 | 334 | 335 | def activate_cb(dev, res, data): 336 | """Notification if activate connection completed successfully 337 | 338 | """ 339 | try: 340 | conn = dev.activate_connection_finish(res) 341 | except GLib.Error: 342 | conn = None 343 | if conn is not None: 344 | notify(f"Activated {conn.get_id()}") 345 | else: 346 | notify(f"Problem activating {data.get_id()}", urgency="critical") 347 | LOOP.quit() 348 | 349 | 350 | def deactivate_cb(dev, res, data): 351 | """Notification if deactivate connection completed successfully 352 | 353 | """ 354 | if dev.deactivate_connection_finish(res) is True: 355 | notify(f"Deactivated {data.get_id()}") 356 | else: 357 | notify(f"Problem deactivating {data.get_id()}", urgency="critical") 358 | LOOP.quit() 359 | 360 | 361 | def process_vpngsm(con, activate): 362 | """Activate/deactive VPN or GSM connections""" 363 | if activate: 364 | CLIENT.activate_connection_async(con, None, None, 365 | None, activate_cb, con) 366 | else: 367 | CLIENT.deactivate_connection_async(con, None, deactivate_cb, con) 368 | LOOP.run() 369 | 370 | 371 | def create_ap_actions(aps, active_ap, active_connection, adapter): # noqa pylint: disable=too-many-locals,line-too-long 372 | """For each AP in a list, create the string and its attached function 373 | (activate/deactivate) 374 | 375 | """ 376 | active_ap_bssid = active_ap.get_bssid() if active_ap is not None else "" 377 | 378 | names = [ssid_to_utf8(ap) for ap in aps] 379 | max_len_name = max([len(name) for name in names]) if names else 0 380 | secs = [ap_security(ap) for ap in aps] 381 | max_len_sec = max([len(sec) for sec in secs]) if secs else 0 382 | 383 | ap_actions = [] 384 | 385 | for nm_ap, name, sec in zip(aps, names, secs): 386 | bars = NM.utils_wifi_strength_bars(nm_ap.get_strength()) 387 | wifi_chars = CONF.get("dmenu", "wifi_chars", fallback=False) 388 | if wifi_chars: 389 | bars = "".join([wifi_chars[i] for i, j in enumerate(bars) if j == '*']) 390 | is_active = nm_ap.get_bssid() == active_ap_bssid 391 | compact = CONF.getboolean("dmenu", "compact", fallback=False) 392 | if compact: 393 | action_name = f"{name} {sec} {bars}" 394 | else: 395 | action_name = f"{name:<{max_len_name}s} {sec:<{max_len_sec}s} {bars:>4}" 396 | if is_active: 397 | ap_actions.append(Action(action_name, process_ap, 398 | [active_connection, True, adapter], 399 | active=True)) 400 | else: 401 | ap_actions.append(Action(action_name, process_ap, 402 | [nm_ap, False, adapter])) 403 | return ap_actions 404 | 405 | 406 | def create_vpn_actions(vpns, active): 407 | """Create the list of strings to display with associated function 408 | (activate/deactivate) for VPN connections. 409 | 410 | """ 411 | active_vpns = [i for i in active if i.get_vpn()] 412 | return _create_vpngsm_actions(vpns, active_vpns, "VPN") 413 | 414 | 415 | def create_wireguard_actions(wgs, active): 416 | """Create the list of strings to display with associated function 417 | (activate/deactivate) for Wireguard connections. 418 | 419 | """ 420 | active_wgs = [i for i in active if i.get_connection_type() == "wireguard"] 421 | return _create_vpngsm_actions(wgs, active_wgs, "Wireguard") 422 | 423 | 424 | def create_eth_actions(eths, active): 425 | """Create the list of strings to display with associated function 426 | (activate/deactivate) for Ethernet connections. 427 | 428 | """ 429 | active_eths = [i for i in active if 'ethernet' in i.get_connection_type()] 430 | return _create_vpngsm_actions(eths, active_eths, "Eth") 431 | 432 | 433 | def create_gsm_actions(gsms, active): 434 | """Create the list of strings to display with associated function 435 | (activate/deactivate) GSM connections.""" 436 | active_gsms = [i for i in active if 437 | i.get_connection() is not None and 438 | i.get_connection().is_type(NM.SETTING_GSM_SETTING_NAME)] 439 | return _create_vpngsm_actions(gsms, active_gsms, "GSM") 440 | 441 | 442 | def create_blue_actions(blues, active): 443 | """Create the list of strings to display with associated function 444 | (activate/deactivate) Bluetooth connections.""" 445 | active_blues = [i for i in active if 446 | i.get_connection() is not None and 447 | i.get_connection().is_type(NM.SETTING_BLUETOOTH_SETTING_NAME)] 448 | return _create_vpngsm_actions(blues, active_blues, "Bluetooth") 449 | 450 | 451 | def create_saved_actions(saved): 452 | """Create the list of strings to display with associated function 453 | (activate/deactivate) for VPN connections. 454 | 455 | """ 456 | return _create_vpngsm_actions(saved, [], "SAVED") 457 | 458 | 459 | def _create_vpngsm_actions(cons, active_cons, label): 460 | active_con_ids = [a.get_id() for a in active_cons] 461 | actions = [] 462 | for con in cons: 463 | is_active = con.get_id() in active_con_ids 464 | action_name = f"{con.get_id()}:{label}" 465 | if is_active: 466 | active_connection = [a for a in active_cons 467 | if a.get_id() == con.get_id()] 468 | if len(active_connection) != 1: 469 | raise ValueError(f"Multiple active connections match {con.get_id()}") 470 | active_connection = active_connection[0] 471 | 472 | actions.append(Action(action_name, process_vpngsm, 473 | [active_connection, False], active=True)) 474 | else: 475 | actions.append(Action(action_name, process_vpngsm, 476 | [con, True])) 477 | return actions 478 | 479 | 480 | def create_wwan_actions(client): 481 | """Create WWWAN actions 482 | 483 | """ 484 | wwan_enabled = client.wwan_get_enabled() 485 | wwan_action = "Disable" if wwan_enabled else "Enable" 486 | return [Action(f"{wwan_action} WWAN", toggle_wwan, not wwan_enabled)] 487 | 488 | 489 | def combine_actions(eths, aps, vpns, wgs, gsms, blues, wwan, others, saved): 490 | # pylint: disable=too-many-arguments 491 | """Combine all given actions into a list of actions. 492 | 493 | Args: args - eths: list of Actions 494 | aps: list of Actions 495 | vpns: list of Actions 496 | gsms: list of Actions 497 | blues: list of Actions 498 | wwan: list of Actions 499 | others: list of Actions 500 | """ 501 | compact = CONF.getboolean("dmenu", "compact", fallback=False) 502 | empty_action = [Action('', None)] if not compact else [] 503 | all_actions = [] 504 | all_actions += eths + empty_action if eths else [] 505 | all_actions += aps + empty_action if aps else [] 506 | all_actions += vpns + empty_action if vpns else [] 507 | all_actions += wgs + empty_action if wgs else [] 508 | all_actions += gsms + empty_action if (gsms and wwan) else [] 509 | all_actions += blues + empty_action if blues else [] 510 | all_actions += wwan + empty_action if wwan else [] 511 | all_actions += others + empty_action if others else [] 512 | all_actions += saved + empty_action if saved else [] 513 | return all_actions 514 | 515 | 516 | def get_selection(all_actions): 517 | """Spawn dmenu for selection and execute the associated action.""" 518 | rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False) 519 | inp = [] 520 | 521 | if rofi_highlight is True: 522 | inp = [str(action) for action in all_actions] 523 | else: 524 | inp = [('== ' if action.is_active else ' ') + str(action) 525 | for action in all_actions] 526 | active_lines = [index for index, action in enumerate(all_actions) 527 | if action.is_active] 528 | 529 | command = dmenu_cmd(len(inp), active_lines=active_lines) 530 | sel = subprocess.run(command, 531 | capture_output=True, 532 | check=False, 533 | input="\n".join(inp), 534 | encoding=ENC, 535 | env=ENV).stdout 536 | 537 | if not sel.rstrip(): 538 | sys.exit() 539 | 540 | if rofi_highlight is False: 541 | action = [i for i in all_actions 542 | if ((str(i).strip() == str(sel.strip()) 543 | and not i.is_active) or 544 | ('== ' + str(i) == str(sel.rstrip('\n')) 545 | and i.is_active))] 546 | else: 547 | action = [i for i in all_actions if str(i).strip() == sel.strip()] 548 | assert len(action) == 1, f"Selection was ambiguous: '{str(sel.strip())}'" 549 | return action[0] 550 | 551 | 552 | def toggle_networking(enable): 553 | """Enable/disable networking 554 | 555 | Args: enable - boolean 556 | 557 | """ 558 | toggle = GLib.Variant.new_tuple(GLib.Variant.new_boolean(enable)) 559 | try: 560 | CLIENT.dbus_call(NM.DBUS_PATH, NM.DBUS_INTERFACE, "Enable", toggle, 561 | None, -1, None, None, None) 562 | except AttributeError: 563 | # Workaround for older versions of python-gobject 564 | CLIENT.networking_set_enabled(enable) 565 | notify(f"Networking {'enabled' if enable is True else 'disabled'}") 566 | 567 | 568 | def toggle_wifi(enable): 569 | """Enable/disable Wifi 570 | 571 | Args: enable - boolean 572 | 573 | """ 574 | toggle = GLib.Variant.new_boolean(enable) 575 | try: 576 | CLIENT.dbus_set_property(NM.DBUS_PATH, NM.DBUS_INTERFACE, "WirelessEnabled", toggle, 577 | -1, None, None, None) 578 | except AttributeError: 579 | # Workaround for older versions of python-gobject 580 | CLIENT.wireless_set_enabled(enable) 581 | notify(f"Wifi {'enabled' if enable is True else 'disabled'}") 582 | 583 | 584 | def toggle_wwan(enable): 585 | """Enable/disable WWAN 586 | 587 | Args: enable - boolean 588 | 589 | """ 590 | toggle = GLib.Variant.new_boolean(enable) 591 | try: 592 | CLIENT.dbus_set_property(NM.DBUS_PATH, NM.DBUS_INTERFACE, "WwanEnabled", toggle, 593 | -1, None, None, None) 594 | except AttributeError: 595 | # Workaround for older versions of python-gobject 596 | CLIENT.wwan_set_enabled(enable) 597 | notify(f"Wwan {'enabled' if enable is True else 'disabled'}") 598 | 599 | 600 | def toggle_bluetooth(enable): 601 | """Enable/disable Bluetooth 602 | 603 | Args: enable - boolean 604 | 605 | References: 606 | https://github.com/blueman-project/blueman/blob/master/blueman/plugins/mechanism/RfKill.py 607 | https://www.kernel.org/doc/html/latest/driver-api/rfkill.html 608 | https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/linux/rfkill.h?h=v5.8.9 609 | 610 | """ 611 | type_bluetooth = 2 612 | op_change_all = 3 613 | idx = 0 614 | soft_state = 0 if enable else 1 615 | hard_state = 0 616 | 617 | data = struct.pack("IBBBB", idx, type_bluetooth, op_change_all, 618 | soft_state, hard_state) 619 | 620 | try: 621 | with open('/dev/rfkill', 'r+b', buffering=0) as rff: 622 | rff.write(data) 623 | except PermissionError: 624 | notify("Lacking permission to write to /dev/rfkill.", 625 | "Check README for configuration options.", 626 | urgency="critical") 627 | else: 628 | notify(f"Bluetooth {'enabled' if enable else 'disabled'}") 629 | 630 | 631 | def launch_connection_editor(): 632 | """Launch nmtui or the gui nm-connection-editor 633 | 634 | """ 635 | terminal = CONF.get("editor", "terminal", fallback="xterm") 636 | gui_if_available = CONF.getboolean("editor", "gui_if_available", fallback=True) 637 | guis = ["gnome-continue-center", "nm-connection-editor"] 638 | if gui_if_available is True: 639 | for gui in guis: 640 | if is_installed(gui): 641 | subprocess.run(gui, check=False) 642 | return 643 | if is_installed("nmtui"): 644 | subprocess.run([terminal, "-e", "nmtui"], check=False) 645 | return 646 | notify("No network connection editor installed", urgency="critical") 647 | 648 | 649 | def get_passphrase(): 650 | """Get a password 651 | 652 | Returns: string 653 | 654 | """ 655 | pinentry = CONF.get("dmenu", "pinentry", fallback=None) 656 | if pinentry: 657 | pin = "" 658 | out = subprocess.run(pinentry, 659 | capture_output=True, 660 | check=False, 661 | encoding=ENC, 662 | input='setdesc Get network password\ngetpin\n').stdout 663 | if out: 664 | res = out.split("\n")[2] 665 | if res.startswith("D "): 666 | pin = res.split("D ")[1] 667 | return pin 668 | return subprocess.run(dmenu_cmd(0, "Passphrase"), 669 | capture_output=True, 670 | check=False, 671 | encoding=ENC).stdout 672 | 673 | 674 | def delete_connection(): 675 | """Display list of NM connections and delete the selected one 676 | 677 | """ 678 | conn_acts = [Action(i.get_id(), i.delete_async, args=[None, delete_cb, None]) for i in CONNS] 679 | conn_names = "\n".join([str(i) for i in conn_acts]) 680 | sel = subprocess.run(dmenu_cmd(len(conn_acts), "CHOOSE CONNECTION TO DELETE:"), 681 | capture_output=True, 682 | check=False, 683 | input=conn_names, 684 | encoding=ENC, 685 | env=ENV).stdout 686 | if not sel.strip(): 687 | sys.exit() 688 | action = [i for i in conn_acts if str(i) == sel.rstrip("\n")] 689 | assert len(action) == 1, f"Selection was ambiguous: {str(sel)}" 690 | action[0]() 691 | LOOP.run() 692 | 693 | 694 | def delete_cb(dev, res, data): 695 | """Notification if delete completed successfully 696 | 697 | """ 698 | if dev.delete_finish(res) is True: 699 | notify(f"Deleted {dev.get_id()}") 700 | else: 701 | notify(f"Problem deleting {dev.get_id()}", urgency="critical") 702 | LOOP.quit() 703 | 704 | 705 | def set_new_connection(nm_ap, nm_pw, adapter): 706 | """Setup a new NetworkManager connection 707 | 708 | Args: ap - NM.AccessPoint 709 | pw - string 710 | 711 | """ 712 | nm_pw = str(nm_pw).strip() 713 | profile = create_wifi_profile(nm_ap, nm_pw, adapter) 714 | CLIENT.add_and_activate_connection_async(profile, adapter, nm_ap.get_path(), 715 | None, verify_conn, profile) 716 | LOOP.run() 717 | 718 | 719 | def create_wifi_profile(nm_ap, password, adapter): 720 | # pylint: disable=line-too-long 721 | # From https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/gi/add_connection.py 722 | # and https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/dbus/add-wifi-psk-connection.py 723 | # pylint: enable=line-too-long 724 | """Create the NM profile given the AP and passphrase""" 725 | ap_sec = ap_security(nm_ap) 726 | profile = NM.SimpleConnection.new() 727 | 728 | s_con = NM.SettingConnection.new() 729 | s_con.set_property(NM.SETTING_CONNECTION_ID, ssid_to_utf8(nm_ap)) 730 | s_con.set_property(NM.SETTING_CONNECTION_UUID, str(uuid.uuid4())) 731 | s_con.set_property(NM.SETTING_CONNECTION_TYPE, "802-11-wireless") 732 | profile.add_setting(s_con) 733 | 734 | s_wifi = NM.SettingWireless.new() 735 | s_wifi.set_property(NM.SETTING_WIRELESS_SSID, nm_ap.get_ssid()) 736 | s_wifi.set_property(NM.SETTING_WIRELESS_MODE, 'infrastructure') 737 | s_wifi.set_property(NM.SETTING_WIRELESS_MAC_ADDRESS, adapter.get_permanent_hw_address()) 738 | profile.add_setting(s_wifi) 739 | 740 | s_ip4 = NM.SettingIP4Config.new() 741 | s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") 742 | profile.add_setting(s_ip4) 743 | 744 | s_ip6 = NM.SettingIP6Config.new() 745 | s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") 746 | profile.add_setting(s_ip6) 747 | 748 | if ap_sec != "--": 749 | s_wifi_sec = NM.SettingWirelessSecurity.new() 750 | if "WPA" in ap_sec: 751 | s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, 752 | "wpa-psk") 753 | s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_AUTH_ALG, 754 | "open") 755 | s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_PSK, password) 756 | elif "WEP" in ap_sec: 757 | s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, 758 | "None") 759 | s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, 760 | NM.WepKeyType.PASSPHRASE) 761 | s_wifi_sec.set_wep_key(0, password) 762 | profile.add_setting(s_wifi_sec) 763 | 764 | return profile 765 | 766 | 767 | def verify_conn(client, result, data): 768 | """Callback function for add_and_activate_connection_async 769 | 770 | Check if connection completes successfully. Delete the connection if there 771 | is an error. 772 | 773 | """ 774 | try: 775 | act_conn = client.add_and_activate_connection_finish(result) 776 | conn = act_conn.get_connection() 777 | if not all([conn.verify(), 778 | conn.verify_secrets(), 779 | data.verify(), 780 | data.verify_secrets()]): 781 | raise GLib.Error 782 | notify(f"Added {conn.get_id()}") 783 | except GLib.Error: 784 | try: 785 | notify(f"Connection to {conn.get_id()} failed", 786 | urgency="critical") 787 | conn.delete_async(None, None, None) 788 | except UnboundLocalError: 789 | pass 790 | finally: 791 | LOOP.quit() 792 | 793 | 794 | def create_ap_list(adapter, active_connections): 795 | """Generate list of access points. Remove duplicate APs , keeping strongest 796 | ones and the active AP 797 | 798 | Args: adapter 799 | active_connections - list of all active connections 800 | Returns: aps - list of access points 801 | active_ap - active AP 802 | active_ap_con - active Connection 803 | adapter 804 | 805 | """ 806 | aps = [] 807 | ap_names = [] 808 | active_ap = adapter.get_active_access_point() 809 | aps_all = sorted(adapter.get_access_points(), 810 | key=lambda a: a.get_strength(), reverse=True) 811 | conns_cur = [i for i in CONNS if 812 | i.get_setting_wireless() is not None and 813 | conn_matches_adapter(i, adapter)] 814 | try: 815 | ap_conns = active_ap.filter_connections(conns_cur) 816 | active_ap_name = ssid_to_utf8(active_ap) 817 | active_ap_con = [active_conn for active_conn in active_connections 818 | if active_conn.get_connection() in ap_conns] 819 | except AttributeError: 820 | active_ap_name = None 821 | active_ap_con = [] 822 | if len(active_ap_con) > 1: 823 | raise ValueError("Multiple connection profiles match" 824 | " the wireless AP") 825 | active_ap_con = active_ap_con[0] if active_ap_con else None 826 | for nm_ap in aps_all: 827 | ap_name = ssid_to_utf8(nm_ap) 828 | if nm_ap != active_ap and ap_name == active_ap_name: 829 | # Skip adding AP if it's not active but same name as active AP 830 | continue 831 | if ap_name not in ap_names: 832 | ap_names.append(ap_name) 833 | aps.append(nm_ap) 834 | return aps, active_ap, active_ap_con, adapter 835 | 836 | 837 | def notify(message, details=None, urgency="low"): 838 | """Use notify-send if available for notifications 839 | 840 | """ 841 | args = ["-u", urgency, "-a", "networkmanager-dmenu", message] 842 | if details is not None: 843 | args.append(details) 844 | if is_installed("notify-send"): 845 | subprocess.run(["notify-send"] + args, check=False) 846 | 847 | 848 | def run(): # pylint: disable=too-many-locals 849 | """Main script entrypoint""" 850 | active = CLIENT.get_active_connections() 851 | adapter = choose_adapter(CLIENT) 852 | if adapter: 853 | ap_actions = create_ap_actions(*create_ap_list(adapter, active)) 854 | else: 855 | ap_actions = [] 856 | 857 | vpns = [i for i in CONNS if i.is_type(NM.SETTING_VPN_SETTING_NAME)] 858 | try: 859 | wgs = [i for i in CONNS if i.is_type(NM.SETTING_WIREGUARD_SETTING_NAME)] 860 | except AttributeError: 861 | # Workaround for older versions of python-gobject with no wireguard support 862 | wgs = [] 863 | eths = [i for i in CONNS if i.is_type(NM.SETTING_WIRED_SETTING_NAME)] 864 | blues = [i for i in CONNS if i.is_type(NM.SETTING_BLUETOOTH_SETTING_NAME)] 865 | 866 | vpn_actions = create_vpn_actions(vpns, active) 867 | wg_actions = create_wireguard_actions(wgs, active) 868 | eth_actions = create_eth_actions(eths, active) 869 | blue_actions = create_blue_actions(blues, active) 870 | other_actions = create_other_actions(CLIENT) 871 | wwan_installed = is_installed("ModemManager") 872 | if wwan_installed: 873 | gsms = [i for i in CONNS if i.is_type(NM.SETTING_GSM_SETTING_NAME)] 874 | gsm_actions = create_gsm_actions(gsms, active) 875 | wwan_actions = create_wwan_actions(CLIENT) 876 | else: 877 | gsm_actions = [] 878 | wwan_actions = [] 879 | 880 | list_saved = CONF.getboolean('dmenu', 'list_saved', fallback=False) 881 | saved_cons = [i for i in CONNS if i not in vpns + wgs + eths + blues] 882 | if list_saved: 883 | saved_actions = create_saved_actions(saved_cons) 884 | else: 885 | saved_actions = [Action("Saved connections", prompt_saved, [saved_cons])] 886 | 887 | actions = combine_actions(eth_actions, ap_actions, vpn_actions, wg_actions, 888 | gsm_actions, blue_actions, wwan_actions, 889 | other_actions, saved_actions) 890 | sel = get_selection(actions) 891 | sel() 892 | 893 | 894 | if __name__ == '__main__': 895 | run() 896 | 897 | # vim: set et ts=4 sw=4 : 898 | -------------------------------------------------------------------------------- /utils/scripts/poweroff_prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | ans=$(echo -e "no\nyes" | dmenu -c -h 40 -l 2 -p "Do you want to go?") 4 | 5 | if [[ ($ans == "yes") ]] 6 | then 7 | systemctl poweroff 8 | fi -------------------------------------------------------------------------------- /utils/scripts/rectangular_screenshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | # TIME=$(date -R | grep ..:..:.. -o) 4 | # scrot -q 100 -sf "%Y-%m-%d_${TIME}.png" -e 'mv $f ~/Pictures/Screenshots/' 5 | 6 | flameshot gui -------------------------------------------------------------------------------- /utils/scripts/screenshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | # TIME=$(date -R | grep ..:..:.. -o) 4 | # scrot -q 100 "%Y-%m-%d_${TIME}.png" -e 'mv $f ~/Pictures/Screenshots/' 5 | 6 | flameshot full -------------------------------------------------------------------------------- /utils/scripts/shutdown.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | choice=$(echo -e "Logout\nShutdown\nRestart" | dmenu -c -h 40 -l 3 -p "Menu:") 4 | 5 | if [[ ($choice == "Logout") ]] 6 | then 7 | ans=$(echo -e "no\nyes" | dmenu -c -h 40 -l 2 -p "Do you want to logout?") 8 | 9 | if [[ ($ans == "yes") ]] 10 | then 11 | wm=$(wmctrl -m | awk 'NR==1{print $2, $3, $4, $5}') 12 | wm=$(sed 's/[ ]*$//' <<<"$wm") 13 | killall $wm 14 | fi 15 | fi 16 | 17 | if [[ ($choice == "Shutdown") ]] 18 | then 19 | # ans=$(echo -e "No\nYes" | dmenu -c -h 40 -p "Do you want to shutdown") 20 | ans=$(echo -e "no\nyes" | dmenu -c -h 40 -l 2 -p "Do you want to go?") 21 | 22 | if [[ ($ans == "yes") ]] 23 | then 24 | poweroff 25 | fi 26 | fi 27 | 28 | if [[ ($choice == "Restart") ]] 29 | then 30 | ans=$(echo -e "no\nyes" | dmenu -c -h 40 -l 2 -p "Do you want to reboot?") 31 | 32 | if [[ ($ans == "yes") ]] 33 | then 34 | reboot 35 | fi 36 | fi 37 | -------------------------------------------------------------------------------- /utils/scripts/suspend_prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | ans=$(echo -e "no\nyes" | dmenu -c -h 40 -l 2 -p "Do you want suspend?") 4 | 5 | if [[ ($ans == "yes") ]] 6 | then 7 | systemctl suspend 8 | fi -------------------------------------------------------------------------------- /utils/scripts/volume_down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | amixer -q set Master 5%- 4 | -------------------------------------------------------------------------------- /utils/scripts/volume_mute.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | amixer -qD pulse set Master 1+ toggle 4 | -------------------------------------------------------------------------------- /utils/scripts/volume_up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | amixer -q set Master 5%+ 4 | --------------------------------------------------------------------------------