aboutsummaryrefslogtreecommitdiff
path: root/keep/manual-programs/suckless
diff options
context:
space:
mode:
Diffstat (limited to 'keep/manual-programs/suckless')
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/LICENSE30
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/Makefile64
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/README24
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/arg.h49
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/config.h23
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/config.mk32
-rwxr-xr-xkeep/manual-programs/suckless/dmenu-5.2/dmenubin0 -> 42664 bytes
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/dmenu.1194
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/dmenu.c791
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/dmenu.obin0 -> 31672 bytes
-rwxr-xr-xkeep/manual-programs/suckless/dmenu-5.2/dmenu_path13
-rwxr-xr-xkeep/manual-programs/suckless/dmenu-5.2/dmenu_run2
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/drw.c450
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/drw.h58
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/drw.obin0 -> 11000 bytes
-rwxr-xr-xkeep/manual-programs/suckless/dmenu-5.2/stestbin0 -> 16432 bytes
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/stest.190
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/stest.c109
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/stest.obin0 -> 5256 bytes
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/util.c36
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/util.h8
-rw-r--r--keep/manual-programs/suckless/dmenu-5.2/util.obin0 -> 2224 bytes
-rw-r--r--keep/manual-programs/suckless/dwm-bak/LICENSE37
-rw-r--r--keep/manual-programs/suckless/dwm-bak/Makefile51
-rw-r--r--keep/manual-programs/suckless/dwm-bak/README48
-rw-r--r--keep/manual-programs/suckless/dwm-bak/awesome-glyphs.txt34
-rw-r--r--keep/manual-programs/suckless/dwm-bak/config.def.h.orig142
-rw-r--r--keep/manual-programs/suckless/dwm-bak/config.h146
-rw-r--r--keep/manual-programs/suckless/dwm-bak/config.mk38
-rw-r--r--keep/manual-programs/suckless/dwm-bak/drw.c435
-rw-r--r--keep/manual-programs/suckless/dwm-bak/drw.h57
-rw-r--r--keep/manual-programs/suckless/dwm-bak/drw.obin0 -> 10488 bytes
-rwxr-xr-xkeep/manual-programs/suckless/dwm-bak/dwmbin0 -> 74816 bytes
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm-autostart-20161205-bb3bd6f.diff39
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm-fullgaps-6.2.diff95
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm-systray-6.2.diff746
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm.1176
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm.c2524
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm.c.orig2516
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm.obin0 -> 69016 bytes
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwm.pngbin0 -> 373 bytes
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwmstatus/LICENSE21
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwmstatus/Makefile49
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwmstatus/config.mk30
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus-temperature.c15
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus.c227
-rw-r--r--keep/manual-programs/suckless/dwm-bak/dwmstatus/new-acpi-battery.c55
-rw-r--r--keep/manual-programs/suckless/dwm-bak/endx5
-rw-r--r--keep/manual-programs/suckless/dwm-bak/shiftview.c19
-rw-r--r--keep/manual-programs/suckless/dwm-bak/transient.c42
-rw-r--r--keep/manual-programs/suckless/dwm-bak/util.c35
-rw-r--r--keep/manual-programs/suckless/dwm-bak/util.h8
-rw-r--r--keep/manual-programs/suckless/dwm-bak/util.obin0 -> 2224 bytes
-rw-r--r--keep/manual-programs/suckless/dwm/LICENSE37
-rw-r--r--keep/manual-programs/suckless/dwm/Makefile51
-rw-r--r--keep/manual-programs/suckless/dwm/README48
-rw-r--r--keep/manual-programs/suckless/dwm/awesome-glyphs.txt34
-rw-r--r--keep/manual-programs/suckless/dwm/config.def.h.orig146
-rw-r--r--keep/manual-programs/suckless/dwm/config.h151
-rw-r--r--keep/manual-programs/suckless/dwm/config.mk38
-rw-r--r--keep/manual-programs/suckless/dwm/drw.c435
-rw-r--r--keep/manual-programs/suckless/dwm/drw.h57
-rw-r--r--keep/manual-programs/suckless/dwm/drw.obin0 -> 10408 bytes
-rwxr-xr-xkeep/manual-programs/suckless/dwm/dwmbin0 -> 74704 bytes
-rw-r--r--keep/manual-programs/suckless/dwm/dwm-autostart-20161205-bb3bd6f.diff39
-rw-r--r--keep/manual-programs/suckless/dwm/dwm-bottomstack-6.1.diff101
-rw-r--r--keep/manual-programs/suckless/dwm/dwm-fullgaps-6.2.diff95
-rw-r--r--keep/manual-programs/suckless/dwm/dwm-systray-6.2.diff746
-rw-r--r--keep/manual-programs/suckless/dwm/dwm.1176
-rw-r--r--keep/manual-programs/suckless/dwm/dwm.c2588
-rw-r--r--keep/manual-programs/suckless/dwm/dwm.c.orig2524
-rw-r--r--keep/manual-programs/suckless/dwm/dwm.obin0 -> 69688 bytes
-rw-r--r--keep/manual-programs/suckless/dwm/dwm.pngbin0 -> 373 bytes
-rw-r--r--keep/manual-programs/suckless/dwm/dwmstatus/LICENSE21
-rw-r--r--keep/manual-programs/suckless/dwm/dwmstatus/Makefile49
-rw-r--r--keep/manual-programs/suckless/dwm/dwmstatus/config.mk30
-rw-r--r--keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus-temperature.c15
-rw-r--r--keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus.c227
-rw-r--r--keep/manual-programs/suckless/dwm/dwmstatus/new-acpi-battery.c55
-rw-r--r--keep/manual-programs/suckless/dwm/endx5
-rw-r--r--keep/manual-programs/suckless/dwm/shiftview.c19
-rw-r--r--keep/manual-programs/suckless/dwm/transient.c42
-rw-r--r--keep/manual-programs/suckless/dwm/util.c35
-rw-r--r--keep/manual-programs/suckless/dwm/util.h8
-rw-r--r--keep/manual-programs/suckless/dwm/util.obin0 -> 2224 bytes
-rw-r--r--keep/manual-programs/suckless/surf/FAQ.md10
-rw-r--r--keep/manual-programs/suckless/surf/LICENSE57
-rw-r--r--keep/manual-programs/suckless/surf/Makefile76
-rw-r--r--keep/manual-programs/suckless/surf/README40
-rw-r--r--keep/manual-programs/suckless/surf/TODO.md10
-rw-r--r--keep/manual-programs/suckless/surf/arg.h48
-rw-r--r--keep/manual-programs/suckless/surf/common.h1
-rw-r--r--keep/manual-programs/suckless/surf/config.def.h205
-rw-r--r--keep/manual-programs/suckless/surf/config.def.h.orig200
-rw-r--r--keep/manual-programs/suckless/surf/config.h205
-rw-r--r--keep/manual-programs/suckless/surf/config.mk33
-rw-r--r--keep/manual-programs/suckless/surf/surfbin0 -> 71408 bytes
-rw-r--r--keep/manual-programs/suckless/surf/surf-modal-20190209-d068a38.diff134
-rw-r--r--keep/manual-programs/suckless/surf/surf-open.sh32
-rw-r--r--keep/manual-programs/suckless/surf/surf-uri-aliases-20220930-089272b.diff58
-rw-r--r--keep/manual-programs/suckless/surf/surf.1305
-rw-r--r--keep/manual-programs/suckless/surf/surf.c2155
-rw-r--r--keep/manual-programs/suckless/surf/surf.c.orig2143
-rw-r--r--keep/manual-programs/suckless/surf/surf.obin0 -> 71896 bytes
-rw-r--r--keep/manual-programs/suckless/surf/surf.pngbin0 -> 240 bytes
-rw-r--r--keep/manual-programs/suckless/surf/webext-surf.c166
-rw-r--r--keep/manual-programs/suckless/surf/webext-surf.obin0 -> 6504 bytes
-rw-r--r--keep/manual-programs/suckless/surf/webext-surf.sobin0 -> 17264 bytes
108 files changed, 23213 insertions, 0 deletions
diff --git a/keep/manual-programs/suckless/dmenu-5.2/LICENSE b/keep/manual-programs/suckless/dmenu-5.2/LICENSE
new file mode 100644
index 0000000..2a64b28
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/LICENSE
@@ -0,0 +1,30 @@
+MIT/X Consortium License
+
+© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
+© 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com>
+© 2006-2007 Michał Janeczek <janeczek@gmail.com>
+© 2007 Kris Maglione <jg@suckless.org>
+© 2009 Gottox <gottox@s01.de>
+© 2009 Markus Schnalke <meillo@marmaro.de>
+© 2009 Evan Gates <evan.gates@gmail.com>
+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2014-2022 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2015-2019 Quentin Rameau <quinq@fifth.space>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/keep/manual-programs/suckless/dmenu-5.2/Makefile b/keep/manual-programs/suckless/dmenu-5.2/Makefile
new file mode 100644
index 0000000..a03a95c
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/Makefile
@@ -0,0 +1,64 @@
+# dmenu - dynamic menu
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+SRC = drw.c dmenu.c stest.c util.c
+OBJ = $(SRC:.c=.o)
+
+all: options dmenu stest
+
+options:
+ @echo dmenu build options:
+ @echo "CFLAGS = $(CFLAGS)"
+ @echo "LDFLAGS = $(LDFLAGS)"
+ @echo "CC = $(CC)"
+
+.c.o:
+ $(CC) -c $(CFLAGS) $<
+
+config.h:
+ cp config.def.h $@
+
+$(OBJ): arg.h config.h config.mk drw.h
+
+dmenu: dmenu.o drw.o util.o
+ $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS)
+
+stest: stest.o
+ $(CC) -o $@ stest.o $(LDFLAGS)
+
+clean:
+ rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz
+
+dist: clean
+ mkdir -p dmenu-$(VERSION)
+ cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\
+ drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\
+ dmenu-$(VERSION)
+ tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION)
+ gzip dmenu-$(VERSION).tar
+ rm -rf dmenu-$(VERSION)
+
+install: all
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/stest
+ mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
+ sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1
+ chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
+ chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1
+
+uninstall:
+ rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\
+ $(DESTDIR)$(PREFIX)/bin/dmenu_path\
+ $(DESTDIR)$(PREFIX)/bin/dmenu_run\
+ $(DESTDIR)$(PREFIX)/bin/stest\
+ $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\
+ $(DESTDIR)$(MANPREFIX)/man1/stest.1
+
+.PHONY: all options clean dist install uninstall
diff --git a/keep/manual-programs/suckless/dmenu-5.2/README b/keep/manual-programs/suckless/dmenu-5.2/README
new file mode 100644
index 0000000..a8fcdfe
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/README
@@ -0,0 +1,24 @@
+dmenu - dynamic menu
+====================
+dmenu is an efficient dynamic menu for X.
+
+
+Requirements
+------------
+In order to build dmenu you need the Xlib header files.
+
+
+Installation
+------------
+Edit config.mk to match your local setup (dmenu is installed into
+the /usr/local namespace by default).
+
+Afterwards enter the following command to build and install dmenu
+(if necessary as root):
+
+ make clean install
+
+
+Running dmenu
+-------------
+See the man page for details.
diff --git a/keep/manual-programs/suckless/dmenu-5.2/arg.h b/keep/manual-programs/suckless/dmenu-5.2/arg.h
new file mode 100644
index 0000000..e94e02b
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/arg.h
@@ -0,0 +1,49 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0] == '-'\
+ && argv[0][1];\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+
+#define ARGEND }\
+ }
+
+#define ARGC() argc_
+
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ ((x), abort(), (char *)0) :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/keep/manual-programs/suckless/dmenu-5.2/config.h b/keep/manual-programs/suckless/dmenu-5.2/config.h
new file mode 100644
index 0000000..ad70a45
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/config.h
@@ -0,0 +1,23 @@
+/* See LICENSE file for copyright and license details. */
+/* Default settings; can be overriden by command line. */
+
+static int topbar = 0; /* -b option; if 0, dmenu appears at bottom */
+/* -fn option overrides fonts[0]; default X11 font or font set */
+static const char *fonts[] = {
+ "monospace:size=10"
+};
+static const char *prompt = NULL; /* -p option; prompt to the left of input field */
+static const char *colors[SchemeLast][2] = {
+ /* fg bg */
+ [SchemeNorm] = { "#bbbbbb", "#222222" },
+ [SchemeSel] = { "#eeeeee", "#005577" },
+ [SchemeOut] = { "#000000", "#00ffff" },
+};
+/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+static unsigned int lines = 0;
+
+/*
+ * Characters not considered part of a word while deleting words
+ * for example: " /?\"&[]"
+ */
+static const char worddelimiters[] = " ";
diff --git a/keep/manual-programs/suckless/dmenu-5.2/config.mk b/keep/manual-programs/suckless/dmenu-5.2/config.mk
new file mode 100644
index 0000000..566348b
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/config.mk
@@ -0,0 +1,32 @@
+# dmenu version
+VERSION = 5.2
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/share/man
+
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+# Xinerama, comment if you don't want it
+XINERAMALIBS = -lXinerama
+XINERAMAFLAGS = -DXINERAMA
+
+# freetype
+FREETYPELIBS = -lfontconfig -lXft
+FREETYPEINC = /usr/include/freetype2
+# OpenBSD (uncomment)
+#FREETYPEINC = $(X11INC)/freetype2
+#MANPREFIX = ${PREFIX}/man
+
+# includes and libs
+INCS = -I$(X11INC) -I$(FREETYPEINC)
+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
+CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS)
+LDFLAGS = $(LIBS)
+
+# compiler and linker
+CC = cc
diff --git a/keep/manual-programs/suckless/dmenu-5.2/dmenu b/keep/manual-programs/suckless/dmenu-5.2/dmenu
new file mode 100755
index 0000000..0b4106f
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/dmenu
Binary files differ
diff --git a/keep/manual-programs/suckless/dmenu-5.2/dmenu.1 b/keep/manual-programs/suckless/dmenu-5.2/dmenu.1
new file mode 100644
index 0000000..323f93c
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/dmenu.1
@@ -0,0 +1,194 @@
+.TH DMENU 1 dmenu\-VERSION
+.SH NAME
+dmenu \- dynamic menu
+.SH SYNOPSIS
+.B dmenu
+.RB [ \-bfiv ]
+.RB [ \-l
+.IR lines ]
+.RB [ \-m
+.IR monitor ]
+.RB [ \-p
+.IR prompt ]
+.RB [ \-fn
+.IR font ]
+.RB [ \-nb
+.IR color ]
+.RB [ \-nf
+.IR color ]
+.RB [ \-sb
+.IR color ]
+.RB [ \-sf
+.IR color ]
+.RB [ \-w
+.IR windowid ]
+.P
+.BR dmenu_run " ..."
+.SH DESCRIPTION
+.B dmenu
+is a dynamic menu for X, which reads a list of newline\-separated items from
+stdin. When the user selects an item and presses Return, their choice is printed
+to stdout and dmenu terminates. Entering text will narrow the items to those
+matching the tokens in the input.
+.P
+.B dmenu_run
+is a script used by
+.IR dwm (1)
+which lists programs in the user's $PATH and runs the result in their $SHELL.
+.SH OPTIONS
+.TP
+.B \-b
+dmenu appears at the bottom of the screen.
+.TP
+.B \-f
+dmenu grabs the keyboard before reading stdin if not reading from a tty. This
+is faster, but will lock up X until stdin reaches end\-of\-file.
+.TP
+.B \-i
+dmenu matches menu items case insensitively.
+.TP
+.BI \-l " lines"
+dmenu lists items vertically, with the given number of lines.
+.TP
+.BI \-m " monitor"
+dmenu is displayed on the monitor number supplied. Monitor numbers are starting
+from 0.
+.TP
+.BI \-p " prompt"
+defines the prompt to be displayed to the left of the input field.
+.TP
+.BI \-fn " font"
+defines the font or font set used.
+.TP
+.BI \-nb " color"
+defines the normal background color.
+.IR #RGB ,
+.IR #RRGGBB ,
+and X color names are supported.
+.TP
+.BI \-nf " color"
+defines the normal foreground color.
+.TP
+.BI \-sb " color"
+defines the selected background color.
+.TP
+.BI \-sf " color"
+defines the selected foreground color.
+.TP
+.B \-v
+prints version information to stdout, then exits.
+.TP
+.BI \-w " windowid"
+embed into windowid.
+.SH USAGE
+dmenu is completely controlled by the keyboard. Items are selected using the
+arrow keys, page up, page down, home, and end.
+.TP
+.B Tab
+Copy the selected item to the input field.
+.TP
+.B Return
+Confirm selection. Prints the selected item to stdout and exits, returning
+success.
+.TP
+.B Ctrl-Return
+Confirm selection. Prints the selected item to stdout and continues.
+.TP
+.B Shift\-Return
+Confirm input. Prints the input text to stdout and exits, returning success.
+.TP
+.B Escape
+Exit without selecting an item, returning failure.
+.TP
+.B Ctrl-Left
+Move cursor to the start of the current word
+.TP
+.B Ctrl-Right
+Move cursor to the end of the current word
+.TP
+.B C\-a
+Home
+.TP
+.B C\-b
+Left
+.TP
+.B C\-c
+Escape
+.TP
+.B C\-d
+Delete
+.TP
+.B C\-e
+End
+.TP
+.B C\-f
+Right
+.TP
+.B C\-g
+Escape
+.TP
+.B C\-h
+Backspace
+.TP
+.B C\-i
+Tab
+.TP
+.B C\-j
+Return
+.TP
+.B C\-J
+Shift-Return
+.TP
+.B C\-k
+Delete line right
+.TP
+.B C\-m
+Return
+.TP
+.B C\-M
+Shift-Return
+.TP
+.B C\-n
+Down
+.TP
+.B C\-p
+Up
+.TP
+.B C\-u
+Delete line left
+.TP
+.B C\-w
+Delete word left
+.TP
+.B C\-y
+Paste from primary X selection
+.TP
+.B C\-Y
+Paste from X clipboard
+.TP
+.B M\-b
+Move cursor to the start of the current word
+.TP
+.B M\-f
+Move cursor to the end of the current word
+.TP
+.B M\-g
+Home
+.TP
+.B M\-G
+End
+.TP
+.B M\-h
+Up
+.TP
+.B M\-j
+Page down
+.TP
+.B M\-k
+Page up
+.TP
+.B M\-l
+Down
+.SH SEE ALSO
+.IR dwm (1),
+.IR stest (1)
diff --git a/keep/manual-programs/suckless/dmenu-5.2/dmenu.c b/keep/manual-programs/suckless/dmenu-5.2/dmenu.c
new file mode 100644
index 0000000..7cf253b
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/dmenu.c
@@ -0,0 +1,791 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+/* macros */
+#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
+ * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+/* enums */
+enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+
+struct item {
+ char *text;
+ struct item *left, *right;
+ int out;
+};
+
+static char text[BUFSIZ] = "";
+static char *embed;
+static int bh, mw, mh;
+static int inputw = 0, promptw;
+static int lrpad; /* sum of left and right padding */
+static size_t cursor;
+static struct item *items = NULL;
+static struct item *matches, *matchend;
+static struct item *prev, *curr, *next, *sel;
+static int mon = -1, screen;
+
+static Atom clip, utf8;
+static Display *dpy;
+static Window root, parentwin, win;
+static XIC xic;
+
+static Drw *drw;
+static Clr *scheme[SchemeLast];
+
+#include "config.h"
+
+static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
+static char *(*fstrstr)(const char *, const char *) = strstr;
+
+static unsigned int
+textw_clamp(const char *str, unsigned int n)
+{
+ unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
+ return MIN(w, n);
+}
+
+static void
+appenditem(struct item *item, struct item **list, struct item **last)
+{
+ if (*last)
+ (*last)->right = item;
+ else
+ *list = item;
+
+ item->left = *last;
+ item->right = NULL;
+ *last = item;
+}
+
+static void
+calcoffsets(void)
+{
+ int i, n;
+
+ if (lines > 0)
+ n = lines * bh;
+ else
+ n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
+ /* calculate which items will begin the next page and previous page */
+ for (i = 0, next = curr; next; next = next->right)
+ if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)
+ break;
+ for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
+ if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
+ break;
+}
+
+static void
+cleanup(void)
+{
+ size_t i;
+
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ for (i = 0; i < SchemeLast; i++)
+ free(scheme[i]);
+ for (i = 0; items && items[i].text; ++i)
+ free(items[i].text);
+ free(items);
+ drw_free(drw);
+ XSync(dpy, False);
+ XCloseDisplay(dpy);
+}
+
+static char *
+cistrstr(const char *h, const char *n)
+{
+ size_t i;
+
+ if (!n[0])
+ return (char *)h;
+
+ for (; *h; ++h) {
+ for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
+ tolower((unsigned char)h[i]); ++i)
+ ;
+ if (n[i] == '\0')
+ return (char *)h;
+ }
+ return NULL;
+}
+
+static int
+drawitem(struct item *item, int x, int y, int w)
+{
+ if (item == sel)
+ drw_setscheme(drw, scheme[SchemeSel]);
+ else if (item->out)
+ drw_setscheme(drw, scheme[SchemeOut]);
+ else
+ drw_setscheme(drw, scheme[SchemeNorm]);
+
+ return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
+}
+
+static void
+drawmenu(void)
+{
+ unsigned int curpos;
+ struct item *item;
+ int x = 0, y = 0, w;
+
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, 0, 0, mw, mh, 1, 1);
+
+ if (prompt && *prompt) {
+ drw_setscheme(drw, scheme[SchemeSel]);
+ x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
+ }
+ /* draw input field */
+ w = (lines > 0 || !matches) ? mw - x : inputw;
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+
+ curpos = TEXTW(text) - TEXTW(&text[cursor]);
+ if ((curpos += lrpad / 2 - 1) < w) {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
+ }
+
+ if (lines > 0) {
+ /* draw vertical list */
+ for (item = curr; item != next; item = item->right)
+ drawitem(item, x, y += bh, mw - x);
+ } else if (matches) {
+ /* draw horizontal list */
+ x += inputw;
+ w = TEXTW("<");
+ if (curr->left) {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
+ }
+ x += w;
+ for (item = curr; item != next; item = item->right)
+ x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
+ if (next) {
+ w = TEXTW(">");
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
+ }
+ }
+ drw_map(drw, win, 0, 0, mw, mh);
+}
+
+static void
+grabfocus(void)
+{
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 };
+ Window focuswin;
+ int i, revertwin;
+
+ for (i = 0; i < 100; ++i) {
+ XGetInputFocus(dpy, &focuswin, &revertwin);
+ if (focuswin == win)
+ return;
+ XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
+ nanosleep(&ts, NULL);
+ }
+ die("cannot grab focus");
+}
+
+static void
+grabkeyboard(void)
+{
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
+ int i;
+
+ if (embed)
+ return;
+ /* try to grab keyboard, we may have to wait for another process to ungrab */
+ for (i = 0; i < 1000; i++) {
+ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
+ GrabModeAsync, CurrentTime) == GrabSuccess)
+ return;
+ nanosleep(&ts, NULL);
+ }
+ die("cannot grab keyboard");
+}
+
+static void
+match(void)
+{
+ static char **tokv = NULL;
+ static int tokn = 0;
+
+ char buf[sizeof text], *s;
+ int i, tokc = 0;
+ size_t len, textsize;
+ struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
+
+ strcpy(buf, text);
+ /* separate input text into tokens to be matched individually */
+ for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
+ if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
+ die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
+ len = tokc ? strlen(tokv[0]) : 0;
+
+ matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
+ textsize = strlen(text) + 1;
+ for (item = items; item && item->text; item++) {
+ for (i = 0; i < tokc; i++)
+ if (!fstrstr(item->text, tokv[i]))
+ break;
+ if (i != tokc) /* not all tokens match */
+ continue;
+ /* exact matches go first, then prefixes, then substrings */
+ if (!tokc || !fstrncmp(text, item->text, textsize))
+ appenditem(item, &matches, &matchend);
+ else if (!fstrncmp(tokv[0], item->text, len))
+ appenditem(item, &lprefix, &prefixend);
+ else
+ appenditem(item, &lsubstr, &substrend);
+ }
+ if (lprefix) {
+ if (matches) {
+ matchend->right = lprefix;
+ lprefix->left = matchend;
+ } else
+ matches = lprefix;
+ matchend = prefixend;
+ }
+ if (lsubstr) {
+ if (matches) {
+ matchend->right = lsubstr;
+ lsubstr->left = matchend;
+ } else
+ matches = lsubstr;
+ matchend = substrend;
+ }
+ curr = sel = matches;
+ calcoffsets();
+}
+
+static void
+insert(const char *str, ssize_t n)
+{
+ if (strlen(text) + n > sizeof text - 1)
+ return;
+ /* move existing text out of the way, insert new text, and update cursor */
+ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
+ if (n > 0)
+ memcpy(&text[cursor], str, n);
+ cursor += n;
+ match();
+}
+
+static size_t
+nextrune(int inc)
+{
+ ssize_t n;
+
+ /* return location of next utf8 rune in the given direction (+1 or -1) */
+ for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
+ ;
+ return n;
+}
+
+static void
+movewordedge(int dir)
+{
+ if (dir < 0) { /* move cursor to the start of the word*/
+ while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
+ cursor = nextrune(-1);
+ while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
+ cursor = nextrune(-1);
+ } else { /* move cursor to the end of the word */
+ while (text[cursor] && strchr(worddelimiters, text[cursor]))
+ cursor = nextrune(+1);
+ while (text[cursor] && !strchr(worddelimiters, text[cursor]))
+ cursor = nextrune(+1);
+ }
+}
+
+static void
+keypress(XKeyEvent *ev)
+{
+ char buf[32];
+ int len;
+ KeySym ksym;
+ Status status;
+
+ len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
+ switch (status) {
+ default: /* XLookupNone, XBufferOverflow */
+ return;
+ case XLookupChars:
+ goto insert;
+ case XLookupKeySym:
+ case XLookupBoth:
+ break;
+ }
+
+ if (ev->state & ControlMask) {
+ switch(ksym) {
+ case XK_a: ksym = XK_Home; break;
+ case XK_b: ksym = XK_Left; break;
+ case XK_c: ksym = XK_Escape; break;
+ case XK_d: ksym = XK_Delete; break;
+ case XK_e: ksym = XK_End; break;
+ case XK_f: ksym = XK_Right; break;
+ case XK_g: ksym = XK_Escape; break;
+ case XK_h: ksym = XK_BackSpace; break;
+ case XK_i: ksym = XK_Tab; break;
+ case XK_j: /* fallthrough */
+ case XK_J: /* fallthrough */
+ case XK_m: /* fallthrough */
+ case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
+ case XK_n: ksym = XK_Down; break;
+ case XK_p: ksym = XK_Up; break;
+
+ case XK_k: /* delete right */
+ text[cursor] = '\0';
+ match();
+ break;
+ case XK_u: /* delete left */
+ insert(NULL, 0 - cursor);
+ break;
+ case XK_w: /* delete word */
+ while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
+ insert(NULL, nextrune(-1) - cursor);
+ while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
+ insert(NULL, nextrune(-1) - cursor);
+ break;
+ case XK_y: /* paste selection */
+ case XK_Y:
+ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
+ utf8, utf8, win, CurrentTime);
+ return;
+ case XK_Left:
+ case XK_KP_Left:
+ movewordedge(-1);
+ goto draw;
+ case XK_Right:
+ case XK_KP_Right:
+ movewordedge(+1);
+ goto draw;
+ case XK_Return:
+ case XK_KP_Enter:
+ break;
+ case XK_bracketleft:
+ cleanup();
+ exit(1);
+ default:
+ return;
+ }
+ } else if (ev->state & Mod1Mask) {
+ switch(ksym) {
+ case XK_b:
+ movewordedge(-1);
+ goto draw;
+ case XK_f:
+ movewordedge(+1);
+ goto draw;
+ case XK_g: ksym = XK_Home; break;
+ case XK_G: ksym = XK_End; break;
+ case XK_h: ksym = XK_Up; break;
+ case XK_j: ksym = XK_Next; break;
+ case XK_k: ksym = XK_Prior; break;
+ case XK_l: ksym = XK_Down; break;
+ default:
+ return;
+ }
+ }
+
+ switch(ksym) {
+ default:
+insert:
+ if (!iscntrl((unsigned char)*buf))
+ insert(buf, len);
+ break;
+ case XK_Delete:
+ case XK_KP_Delete:
+ if (text[cursor] == '\0')
+ return;
+ cursor = nextrune(+1);
+ /* fallthrough */
+ case XK_BackSpace:
+ if (cursor == 0)
+ return;
+ insert(NULL, nextrune(-1) - cursor);
+ break;
+ case XK_End:
+ case XK_KP_End:
+ if (text[cursor] != '\0') {
+ cursor = strlen(text);
+ break;
+ }
+ if (next) {
+ /* jump to end of list and position items in reverse */
+ curr = matchend;
+ calcoffsets();
+ curr = prev;
+ calcoffsets();
+ while (next && (curr = curr->right))
+ calcoffsets();
+ }
+ sel = matchend;
+ break;
+ case XK_Escape:
+ cleanup();
+ exit(1);
+ case XK_Home:
+ case XK_KP_Home:
+ if (sel == matches) {
+ cursor = 0;
+ break;
+ }
+ sel = curr = matches;
+ calcoffsets();
+ break;
+ case XK_Left:
+ case XK_KP_Left:
+ if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
+ cursor = nextrune(-1);
+ break;
+ }
+ if (lines > 0)
+ return;
+ /* fallthrough */
+ case XK_Up:
+ case XK_KP_Up:
+ if (sel && sel->left && (sel = sel->left)->right == curr) {
+ curr = prev;
+ calcoffsets();
+ }
+ break;
+ case XK_Next:
+ case XK_KP_Next:
+ if (!next)
+ return;
+ sel = curr = next;
+ calcoffsets();
+ break;
+ case XK_Prior:
+ case XK_KP_Prior:
+ if (!prev)
+ return;
+ sel = curr = prev;
+ calcoffsets();
+ break;
+ case XK_Return:
+ case XK_KP_Enter:
+ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
+ if (!(ev->state & ControlMask)) {
+ cleanup();
+ exit(0);
+ }
+ if (sel)
+ sel->out = 1;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ if (text[cursor] != '\0') {
+ cursor = nextrune(+1);
+ break;
+ }
+ if (lines > 0)
+ return;
+ /* fallthrough */
+ case XK_Down:
+ case XK_KP_Down:
+ if (sel && sel->right && (sel = sel->right) == next) {
+ curr = next;
+ calcoffsets();
+ }
+ break;
+ case XK_Tab:
+ if (!sel)
+ return;
+ cursor = strnlen(sel->text, sizeof text - 1);
+ memcpy(text, sel->text, cursor);
+ text[cursor] = '\0';
+ match();
+ break;
+ }
+
+draw:
+ drawmenu();
+}
+
+static void
+paste(void)
+{
+ char *p, *q;
+ int di;
+ unsigned long dl;
+ Atom da;
+
+ /* we have been given the current selection, now insert it into input */
+ if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
+ utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
+ == Success && p) {
+ insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
+ XFree(p);
+ }
+ drawmenu();
+}
+
+static void
+readstdin(void)
+{
+ char *line = NULL;
+ size_t i, junk, size = 0;
+ ssize_t len;
+
+ /* read each line from stdin and add it to the item list */
+ for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) {
+ if (i + 1 >= size / sizeof *items)
+ if (!(items = realloc(items, (size += BUFSIZ))))
+ die("cannot realloc %zu bytes:", size);
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ items[i].text = line;
+ items[i].out = 0;
+ }
+ if (items)
+ items[i].text = NULL;
+ lines = MIN(lines, i);
+}
+
+static void
+run(void)
+{
+ XEvent ev;
+
+ while (!XNextEvent(dpy, &ev)) {
+ if (XFilterEvent(&ev, win))
+ continue;
+ switch(ev.type) {
+ case DestroyNotify:
+ if (ev.xdestroywindow.window != win)
+ break;
+ cleanup();
+ exit(1);
+ case Expose:
+ if (ev.xexpose.count == 0)
+ drw_map(drw, win, 0, 0, mw, mh);
+ break;
+ case FocusIn:
+ /* regrab focus from parent window */
+ if (ev.xfocus.window != win)
+ grabfocus();
+ break;
+ case KeyPress:
+ keypress(&ev.xkey);
+ break;
+ case SelectionNotify:
+ if (ev.xselection.property == utf8)
+ paste();
+ break;
+ case VisibilityNotify:
+ if (ev.xvisibility.state != VisibilityUnobscured)
+ XRaiseWindow(dpy, win);
+ break;
+ }
+ }
+}
+
+static void
+setup(void)
+{
+ int x, y, i, j;
+ unsigned int du;
+ XSetWindowAttributes swa;
+ XIM xim;
+ Window w, dw, *dws;
+ XWindowAttributes wa;
+ XClassHint ch = {"dmenu", "dmenu"};
+#ifdef XINERAMA
+ XineramaScreenInfo *info;
+ Window pw;
+ int a, di, n, area = 0;
+#endif
+ /* init appearance */
+ for (j = 0; j < SchemeLast; j++)
+ scheme[j] = drw_scm_create(drw, colors[j], 2);
+
+ clip = XInternAtom(dpy, "CLIPBOARD", False);
+ utf8 = XInternAtom(dpy, "UTF8_STRING", False);
+
+ /* calculate menu geometry */
+ bh = drw->fonts->h + 2;
+ lines = MAX(lines, 0);
+ mh = (lines + 1) * bh;
+#ifdef XINERAMA
+ i = 0;
+ if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
+ XGetInputFocus(dpy, &w, &di);
+ if (mon >= 0 && mon < n)
+ i = mon;
+ else if (w != root && w != PointerRoot && w != None) {
+ /* find top-level window containing current input focus */
+ do {
+ if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
+ XFree(dws);
+ } while (w != root && w != pw);
+ /* find xinerama screen with which the window intersects most */
+ if (XGetWindowAttributes(dpy, pw, &wa))
+ for (j = 0; j < n; j++)
+ if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
+ area = a;
+ i = j;
+ }
+ }
+ /* no focused window is on screen, so use pointer location instead */
+ if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
+ for (i = 0; i < n; i++)
+ if (INTERSECT(x, y, 1, 1, info[i]) != 0)
+ break;
+
+ x = info[i].x_org;
+ y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
+ mw = info[i].width;
+ XFree(info);
+ } else
+#endif
+ {
+ if (!XGetWindowAttributes(dpy, parentwin, &wa))
+ die("could not get embedding window attributes: 0x%lx",
+ parentwin);
+ x = 0;
+ y = topbar ? 0 : wa.height - mh;
+ mw = wa.width;
+ }
+ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
+ inputw = mw / 3; /* input width: ~33% of monitor width */
+ match();
+
+ /* create menu window */
+ swa.override_redirect = True;
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+ XSetClassHint(dpy, win, &ch);
+
+
+ /* input methods */
+ if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
+ die("XOpenIM failed: could not open input device");
+
+ xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNClientWindow, win, XNFocusWindow, win, NULL);
+
+ XMapRaised(dpy, win);
+ if (embed) {
+ XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
+ if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
+ for (i = 0; i < du && dws[i] != win; ++i)
+ XSelectInput(dpy, dws[i], FocusChangeMask);
+ XFree(dws);
+ }
+ grabfocus();
+ }
+ drw_resize(drw, mw, mh);
+ drawmenu();
+}
+
+static void
+usage(void)
+{
+ die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
+}
+
+int
+main(int argc, char *argv[])
+{
+ XWindowAttributes wa;
+ int i, fast = 0;
+
+ for (i = 1; i < argc; i++)
+ /* these options take no arguments */
+ if (!strcmp(argv[i], "-v")) { /* prints version information */
+ puts("dmenu-"VERSION);
+ exit(0);
+ } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
+ topbar = 0;
+ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
+ fast = 1;
+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
+ fstrncmp = strncasecmp;
+ fstrstr = cistrstr;
+ } else if (i + 1 == argc)
+ usage();
+ /* these options take one argument */
+ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
+ lines = atoi(argv[++i]);
+ else if (!strcmp(argv[i], "-m"))
+ mon = atoi(argv[++i]);
+ else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
+ prompt = argv[++i];
+ else if (!strcmp(argv[i], "-fn")) /* font or font set */
+ fonts[0] = argv[++i];
+ else if (!strcmp(argv[i], "-nb")) /* normal background color */
+ colors[SchemeNorm][ColBg] = argv[++i];
+ else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
+ colors[SchemeNorm][ColFg] = argv[++i];
+ else if (!strcmp(argv[i], "-sb")) /* selected background color */
+ colors[SchemeSel][ColBg] = argv[++i];
+ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
+ colors[SchemeSel][ColFg] = argv[++i];
+ else if (!strcmp(argv[i], "-w")) /* embedding window id */
+ embed = argv[++i];
+ else
+ usage();
+
+ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fputs("warning: no locale support\n", stderr);
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("cannot open display");
+ screen = DefaultScreen(dpy);
+ root = RootWindow(dpy, screen);
+ if (!embed || !(parentwin = strtol(embed, NULL, 0)))
+ parentwin = root;
+ if (!XGetWindowAttributes(dpy, parentwin, &wa))
+ die("could not get embedding window attributes: 0x%lx",
+ parentwin);
+ drw = drw_create(dpy, screen, root, wa.width, wa.height);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+
+#ifdef __OpenBSD__
+ if (pledge("stdio rpath", NULL) == -1)
+ die("pledge");
+#endif
+
+ if (fast && !isatty(0)) {
+ grabkeyboard();
+ readstdin();
+ } else {
+ readstdin();
+ grabkeyboard();
+ }
+ setup();
+ run();
+
+ return 1; /* unreachable */
+}
diff --git a/keep/manual-programs/suckless/dmenu-5.2/dmenu.o b/keep/manual-programs/suckless/dmenu-5.2/dmenu.o
new file mode 100644
index 0000000..3a5ebea
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/dmenu.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dmenu-5.2/dmenu_path b/keep/manual-programs/suckless/dmenu-5.2/dmenu_path
new file mode 100755
index 0000000..3a7cda7
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/dmenu_path
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}"
+cache="$cachedir/dmenu_run"
+
+[ ! -e "$cachedir" ] && mkdir -p "$cachedir"
+
+IFS=:
+if stest -dqr -n "$cache" $PATH; then
+ stest -flx $PATH | sort -u | tee "$cache"
+else
+ cat "$cache"
+fi
diff --git a/keep/manual-programs/suckless/dmenu-5.2/dmenu_run b/keep/manual-programs/suckless/dmenu-5.2/dmenu_run
new file mode 100755
index 0000000..834ede5
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/dmenu_run
@@ -0,0 +1,2 @@
+#!/bin/sh
+dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &
diff --git a/keep/manual-programs/suckless/dmenu-5.2/drw.c b/keep/manual-programs/suckless/dmenu-5.2/drw.c
new file mode 100644
index 0000000..a58a2b4
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/drw.c
@@ -0,0 +1,450 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+#define UTF_INVALID 0xFFFD
+#define UTF_SIZ 4
+
+static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+static long
+utf8decodebyte(const char c, size_t *i)
+{
+ for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
+ if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
+ return (unsigned char)c & ~utfmask[*i];
+ return 0;
+}
+
+static size_t
+utf8validate(long *u, size_t i)
+{
+ if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+ *u = UTF_INVALID;
+ for (i = 1; *u > utfmax[i]; ++i)
+ ;
+ return i;
+}
+
+static size_t
+utf8decode(const char *c, long *u, size_t clen)
+{
+ size_t i, j, len, type;
+ long udecoded;
+
+ *u = UTF_INVALID;
+ if (!clen)
+ return 0;
+ udecoded = utf8decodebyte(c[0], &len);
+ if (!BETWEEN(len, 1, UTF_SIZ))
+ return 1;
+ for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+ udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
+ if (type)
+ return j;
+ }
+ if (j < len)
+ return 0;
+ *u = udecoded;
+ utf8validate(u, len);
+
+ return len;
+}
+
+Drw *
+drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
+{
+ Drw *drw = ecalloc(1, sizeof(Drw));
+
+ drw->dpy = dpy;
+ drw->screen = screen;
+ drw->root = root;
+ drw->w = w;
+ drw->h = h;
+ drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+ drw->gc = XCreateGC(dpy, root, 0, NULL);
+ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+ return drw;
+}
+
+void
+drw_resize(Drw *drw, unsigned int w, unsigned int h)
+{
+ if (!drw)
+ return;
+
+ drw->w = w;
+ drw->h = h;
+ if (drw->drawable)
+ XFreePixmap(drw->dpy, drw->drawable);
+ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+}
+
+void
+drw_free(Drw *drw)
+{
+ XFreePixmap(drw->dpy, drw->drawable);
+ XFreeGC(drw->dpy, drw->gc);
+ drw_fontset_free(drw->fonts);
+ free(drw);
+}
+
+/* This function is an implementation detail. Library users should use
+ * drw_fontset_create instead.
+ */
+static Fnt *
+xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
+{
+ Fnt *font;
+ XftFont *xfont = NULL;
+ FcPattern *pattern = NULL;
+
+ if (fontname) {
+ /* Using the pattern found at font->xfont->pattern does not yield the
+ * same substitution results as using the pattern returned by
+ * FcNameParse; using the latter results in the desired fallback
+ * behaviour whereas the former just results in missing-character
+ * rectangles being drawn, at least with some fonts. */
+ if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
+ fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
+ return NULL;
+ }
+ if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
+ fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
+ XftFontClose(drw->dpy, xfont);
+ return NULL;
+ }
+ } else if (fontpattern) {
+ if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
+ fprintf(stderr, "error, cannot load font from pattern.\n");
+ return NULL;
+ }
+ } else {
+ die("no font specified.");
+ }
+
+ font = ecalloc(1, sizeof(Fnt));
+ font->xfont = xfont;
+ font->pattern = pattern;
+ font->h = xfont->ascent + xfont->descent;
+ font->dpy = drw->dpy;
+
+ return font;
+}
+
+static void
+xfont_free(Fnt *font)
+{
+ if (!font)
+ return;
+ if (font->pattern)
+ FcPatternDestroy(font->pattern);
+ XftFontClose(font->dpy, font->xfont);
+ free(font);
+}
+
+Fnt*
+drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
+{
+ Fnt *cur, *ret = NULL;
+ size_t i;
+
+ if (!drw || !fonts)
+ return NULL;
+
+ for (i = 1; i <= fontcount; i++) {
+ if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
+ cur->next = ret;
+ ret = cur;
+ }
+ }
+ return (drw->fonts = ret);
+}
+
+void
+drw_fontset_free(Fnt *font)
+{
+ if (font) {
+ drw_fontset_free(font->next);
+ xfont_free(font);
+ }
+}
+
+void
+drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+{
+ if (!drw || !dest || !clrname)
+ return;
+
+ if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen),
+ clrname, dest))
+ die("error, cannot allocate color '%s'", clrname);
+}
+
+/* Wrapper to create color schemes. The caller has to call free(3) on the
+ * returned color scheme when done using it. */
+Clr *
+drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+{
+ size_t i;
+ Clr *ret;
+
+ /* need at least two colors for a scheme */
+ if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
+ return NULL;
+
+ for (i = 0; i < clrcount; i++)
+ drw_clr_create(drw, &ret[i], clrnames[i]);
+ return ret;
+}
+
+void
+drw_setfontset(Drw *drw, Fnt *set)
+{
+ if (drw)
+ drw->fonts = set;
+}
+
+void
+drw_setscheme(Drw *drw, Clr *scm)
+{
+ if (drw)
+ drw->scheme = scm;
+}
+
+void
+drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
+{
+ if (!drw || !drw->scheme)
+ return;
+ XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
+ if (filled)
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ else
+ XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
+}
+
+int
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
+{
+ int i, ty, ellipsis_x = 0;
+ unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
+ XftDraw *d = NULL;
+ Fnt *usedfont, *curfont, *nextfont;
+ int utf8strlen, utf8charlen, render = x || y || w || h;
+ long utf8codepoint = 0;
+ const char *utf8str;
+ FcCharSet *fccharset;
+ FcPattern *fcpattern;
+ FcPattern *match;
+ XftResult result;
+ int charexists = 0, overflow = 0;
+ /* keep track of a couple codepoints for which we have no match. */
+ enum { nomatches_len = 64 };
+ static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
+ static unsigned int ellipsis_width = 0;
+
+ if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
+ return 0;
+
+ if (!render) {
+ w = invert ? invert : ~invert;
+ } else {
+ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ d = XftDrawCreate(drw->dpy, drw->drawable,
+ DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen));
+ x += lpad;
+ w -= lpad;
+ }
+
+ usedfont = drw->fonts;
+ if (!ellipsis_width && render)
+ ellipsis_width = drw_fontset_getwidth(drw, "...");
+ while (1) {
+ ew = ellipsis_len = utf8strlen = 0;
+ utf8str = text;
+ nextfont = NULL;
+ while (*text) {
+ utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+ for (curfont = drw->fonts; curfont; curfont = curfont->next) {
+ charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
+ if (charexists) {
+ drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
+ if (ew + ellipsis_width <= w) {
+ /* keep track where the ellipsis still fits */
+ ellipsis_x = x + ew;
+ ellipsis_w = w - ew;
+ ellipsis_len = utf8strlen;
+ }
+
+ if (ew + tmpw > w) {
+ overflow = 1;
+ /* called from drw_fontset_getwidth_clamp():
+ * it wants the width AFTER the overflow
+ */
+ if (!render)
+ x += tmpw;
+ else
+ utf8strlen = ellipsis_len;
+ } else if (curfont == usedfont) {
+ utf8strlen += utf8charlen;
+ text += utf8charlen;
+ ew += tmpw;
+ } else {
+ nextfont = curfont;
+ }
+ break;
+ }
+ }
+
+ if (overflow || !charexists || nextfont)
+ break;
+ else
+ charexists = 0;
+ }
+
+ if (utf8strlen) {
+ if (render) {
+ ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
+ XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
+ usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
+ }
+ x += ew;
+ w -= ew;
+ }
+ if (render && overflow)
+ drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
+
+ if (!*text || overflow) {
+ break;
+ } else if (nextfont) {
+ charexists = 0;
+ usedfont = nextfont;
+ } else {
+ /* Regardless of whether or not a fallback font is found, the
+ * character must be drawn. */
+ charexists = 1;
+
+ for (i = 0; i < nomatches_len; ++i) {
+ /* avoid calling XftFontMatch if we know we won't find a match */
+ if (utf8codepoint == nomatches.codepoint[i])
+ goto no_match;
+ }
+
+ fccharset = FcCharSetCreate();
+ FcCharSetAddChar(fccharset, utf8codepoint);
+
+ if (!drw->fonts->pattern) {
+ /* Refer to the comment in xfont_create for more information. */
+ die("the first font in the cache must be loaded from a font string.");
+ }
+
+ fcpattern = FcPatternDuplicate(drw->fonts->pattern);
+ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+ FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
+
+ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+ FcDefaultSubstitute(fcpattern);
+ match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
+
+ FcCharSetDestroy(fccharset);
+ FcPatternDestroy(fcpattern);
+
+ if (match) {
+ usedfont = xfont_create(drw, NULL, match);
+ if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
+ for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
+ ; /* NOP */
+ curfont->next = usedfont;
+ } else {
+ xfont_free(usedfont);
+ nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
+no_match:
+ usedfont = drw->fonts;
+ }
+ }
+ }
+ }
+ if (d)
+ XftDrawDestroy(d);
+
+ return x + (render ? w : 0);
+}
+
+void
+drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
+{
+ if (!drw)
+ return;
+
+ XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
+ XSync(drw->dpy, False);
+}
+
+unsigned int
+drw_fontset_getwidth(Drw *drw, const char *text)
+{
+ if (!drw || !drw->fonts || !text)
+ return 0;
+ return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
+}
+
+unsigned int
+drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
+{
+ unsigned int tmp = 0;
+ if (drw && drw->fonts && text && n)
+ tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
+ return MIN(n, tmp);
+}
+
+void
+drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
+{
+ XGlyphInfo ext;
+
+ if (!font || !text)
+ return;
+
+ XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+ if (w)
+ *w = ext.xOff;
+ if (h)
+ *h = font->h;
+}
+
+Cur *
+drw_cur_create(Drw *drw, int shape)
+{
+ Cur *cur;
+
+ if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
+ return NULL;
+
+ cur->cursor = XCreateFontCursor(drw->dpy, shape);
+
+ return cur;
+}
+
+void
+drw_cur_free(Drw *drw, Cur *cursor)
+{
+ if (!cursor)
+ return;
+
+ XFreeCursor(drw->dpy, cursor->cursor);
+ free(cursor);
+}
diff --git a/keep/manual-programs/suckless/dmenu-5.2/drw.h b/keep/manual-programs/suckless/dmenu-5.2/drw.h
new file mode 100644
index 0000000..fd7631b
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/drw.h
@@ -0,0 +1,58 @@
+/* See LICENSE file for copyright and license details. */
+
+typedef struct {
+ Cursor cursor;
+} Cur;
+
+typedef struct Fnt {
+ Display *dpy;
+ unsigned int h;
+ XftFont *xfont;
+ FcPattern *pattern;
+ struct Fnt *next;
+} Fnt;
+
+enum { ColFg, ColBg }; /* Clr scheme index */
+typedef XftColor Clr;
+
+typedef struct {
+ unsigned int w, h;
+ Display *dpy;
+ int screen;
+ Window root;
+ Drawable drawable;
+ GC gc;
+ Clr *scheme;
+ Fnt *fonts;
+} Drw;
+
+/* Drawable abstraction */
+Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
+void drw_resize(Drw *drw, unsigned int w, unsigned int h);
+void drw_free(Drw *drw);
+
+/* Fnt abstraction */
+Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
+void drw_fontset_free(Fnt* set);
+unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
+unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
+
+/* Colorscheme abstraction */
+void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
+Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
+
+/* Cursor abstraction */
+Cur *drw_cur_create(Drw *drw, int shape);
+void drw_cur_free(Drw *drw, Cur *cursor);
+
+/* Drawing context manipulation */
+void drw_setfontset(Drw *drw, Fnt *set);
+void drw_setscheme(Drw *drw, Clr *scm);
+
+/* Drawing functions */
+void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+
+/* Map functions */
+void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/keep/manual-programs/suckless/dmenu-5.2/drw.o b/keep/manual-programs/suckless/dmenu-5.2/drw.o
new file mode 100644
index 0000000..249df4c
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/drw.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dmenu-5.2/stest b/keep/manual-programs/suckless/dmenu-5.2/stest
new file mode 100755
index 0000000..592d3a0
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/stest
Binary files differ
diff --git a/keep/manual-programs/suckless/dmenu-5.2/stest.1 b/keep/manual-programs/suckless/dmenu-5.2/stest.1
new file mode 100644
index 0000000..2667d8a
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/stest.1
@@ -0,0 +1,90 @@
+.TH STEST 1 dmenu\-VERSION
+.SH NAME
+stest \- filter a list of files by properties
+.SH SYNOPSIS
+.B stest
+.RB [ -abcdefghlpqrsuwx ]
+.RB [ -n
+.IR file ]
+.RB [ -o
+.IR file ]
+.RI [ file ...]
+.SH DESCRIPTION
+.B stest
+takes a list of files and filters by the files' properties, analogous to
+.IR test (1).
+Files which pass all tests are printed to stdout. If no files are given, stest
+reads files from stdin.
+.SH OPTIONS
+.TP
+.B \-a
+Test hidden files.
+.TP
+.B \-b
+Test that files are block specials.
+.TP
+.B \-c
+Test that files are character specials.
+.TP
+.B \-d
+Test that files are directories.
+.TP
+.B \-e
+Test that files exist.
+.TP
+.B \-f
+Test that files are regular files.
+.TP
+.B \-g
+Test that files have their set-group-ID flag set.
+.TP
+.B \-h
+Test that files are symbolic links.
+.TP
+.B \-l
+Test the contents of a directory given as an argument.
+.TP
+.BI \-n " file"
+Test that files are newer than
+.IR file .
+.TP
+.BI \-o " file"
+Test that files are older than
+.IR file .
+.TP
+.B \-p
+Test that files are named pipes.
+.TP
+.B \-q
+No files are printed, only the exit status is returned.
+.TP
+.B \-r
+Test that files are readable.
+.TP
+.B \-s
+Test that files are not empty.
+.TP
+.B \-u
+Test that files have their set-user-ID flag set.
+.TP
+.B \-v
+Invert the sense of tests, only failing files pass.
+.TP
+.B \-w
+Test that files are writable.
+.TP
+.B \-x
+Test that files are executable.
+.SH EXIT STATUS
+.TP
+.B 0
+At least one file passed all tests.
+.TP
+.B 1
+No files passed all tests.
+.TP
+.B 2
+An error occurred.
+.SH SEE ALSO
+.IR dmenu (1),
+.IR test (1)
diff --git a/keep/manual-programs/suckless/dmenu-5.2/stest.c b/keep/manual-programs/suckless/dmenu-5.2/stest.c
new file mode 100644
index 0000000..e27d3a5
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/stest.c
@@ -0,0 +1,109 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "arg.h"
+char *argv0;
+
+#define FLAG(x) (flag[(x)-'a'])
+
+static void test(const char *, const char *);
+static void usage(void);
+
+static int match = 0;
+static int flag[26];
+static struct stat old, new;
+
+static void
+test(const char *path, const char *name)
+{
+ struct stat st, ln;
+
+ if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */
+ && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */
+ && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */
+ && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */
+ && (!FLAG('e') || access(path, F_OK) == 0) /* exists */
+ && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */
+ && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */
+ && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */
+ && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */
+ && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */
+ && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */
+ && (!FLAG('r') || access(path, R_OK) == 0) /* readable */
+ && (!FLAG('s') || st.st_size > 0) /* not empty */
+ && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */
+ && (!FLAG('w') || access(path, W_OK) == 0) /* writable */
+ && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */
+ if (FLAG('q'))
+ exit(0);
+ match = 1;
+ puts(name);
+ }
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] "
+ "[-n file] [-o file] [file...]\n", argv0);
+ exit(2); /* like test(1) return > 1 on error */
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct dirent *d;
+ char path[PATH_MAX], *line = NULL, *file;
+ size_t linesiz = 0;
+ ssize_t n;
+ DIR *dir;
+ int r;
+
+ ARGBEGIN {
+ case 'n': /* newer than file */
+ case 'o': /* older than file */
+ file = EARGF(usage());
+ if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old))))
+ perror(file);
+ break;
+ default:
+ /* miscellaneous operators */
+ if (strchr("abcdefghlpqrsuvwx", ARGC()))
+ FLAG(ARGC()) = 1;
+ else
+ usage(); /* unknown flag */
+ } ARGEND;
+
+ if (!argc) {
+ /* read list from stdin */
+ while ((n = getline(&line, &linesiz, stdin)) > 0) {
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+ test(line, line);
+ }
+ free(line);
+ } else {
+ for (; argc; argc--, argv++) {
+ if (FLAG('l') && (dir = opendir(*argv))) {
+ /* test directory contents */
+ while ((d = readdir(dir))) {
+ r = snprintf(path, sizeof path, "%s/%s",
+ *argv, d->d_name);
+ if (r >= 0 && (size_t)r < sizeof path)
+ test(path, d->d_name);
+ }
+ closedir(dir);
+ } else {
+ test(*argv, *argv);
+ }
+ }
+ }
+ return match ? 0 : 1;
+}
diff --git a/keep/manual-programs/suckless/dmenu-5.2/stest.o b/keep/manual-programs/suckless/dmenu-5.2/stest.o
new file mode 100644
index 0000000..8a784af
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/stest.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dmenu-5.2/util.c b/keep/manual-programs/suckless/dmenu-5.2/util.c
new file mode 100644
index 0000000..96b82c9
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/util.c
@@ -0,0 +1,36 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = calloc(nmemb, size)))
+ die("calloc:");
+ return p;
+}
diff --git a/keep/manual-programs/suckless/dmenu-5.2/util.h b/keep/manual-programs/suckless/dmenu-5.2/util.h
new file mode 100644
index 0000000..f633b51
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/util.h
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+
+void die(const char *fmt, ...);
+void *ecalloc(size_t nmemb, size_t size);
diff --git a/keep/manual-programs/suckless/dmenu-5.2/util.o b/keep/manual-programs/suckless/dmenu-5.2/util.o
new file mode 100644
index 0000000..8e55fa1
--- /dev/null
+++ b/keep/manual-programs/suckless/dmenu-5.2/util.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm-bak/LICENSE b/keep/manual-programs/suckless/dwm-bak/LICENSE
new file mode 100644
index 0000000..d221f09
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/LICENSE
@@ -0,0 +1,37 @@
+MIT/X Consortium License
+
+© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
+© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
+© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
+© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
+© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
+© 2007-2009 Christof Musik <christof at sendfax dot de>
+© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
+© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
+© 2008 Martin Hurton <martin dot hurton at gmail dot com>
+© 2008 Neale Pickett <neale dot woozle dot org>
+© 2009 Mate Nagy <mnagy at port70 dot net>
+© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2011 Christoph Lohmann <20h@r-36.net>
+© 2015-2016 Quentin Rameau <quinq@fifth.space>
+© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
+© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/keep/manual-programs/suckless/dwm-bak/Makefile b/keep/manual-programs/suckless/dwm-bak/Makefile
new file mode 100644
index 0000000..77bcbc0
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/Makefile
@@ -0,0 +1,51 @@
+# dwm - dynamic window manager
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+SRC = drw.c dwm.c util.c
+OBJ = ${SRC:.c=.o}
+
+all: options dwm
+
+options:
+ @echo dwm build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+${OBJ}: config.h config.mk
+
+config.h:
+ cp config.def.h $@
+
+dwm: ${OBJ}
+ ${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+clean:
+ rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
+
+dist: clean
+ mkdir -p dwm-${VERSION}
+ cp -R LICENSE Makefile README config.def.h config.mk\
+ dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION}
+ tar -cf dwm-${VERSION}.tar dwm-${VERSION}
+ gzip dwm-${VERSION}.tar
+ rm -rf dwm-${VERSION}
+
+install: all
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ cp -f dwm ${DESTDIR}${PREFIX}/bin
+ chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
+ mkdir -p ${DESTDIR}${MANPREFIX}/man1
+ sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
+ chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
+
+uninstall:
+ rm -f ${DESTDIR}${PREFIX}/bin/dwm\
+ ${DESTDIR}${MANPREFIX}/man1/dwm.1
+
+.PHONY: all options clean dist install uninstall
diff --git a/keep/manual-programs/suckless/dwm-bak/README b/keep/manual-programs/suckless/dwm-bak/README
new file mode 100644
index 0000000..95d4fd0
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/README
@@ -0,0 +1,48 @@
+dwm - dynamic window manager
+============================
+dwm is an extremely fast, small, and dynamic window manager for X.
+
+
+Requirements
+------------
+In order to build dwm you need the Xlib header files.
+
+
+Installation
+------------
+Edit config.mk to match your local setup (dwm is installed into
+the /usr/local namespace by default).
+
+Afterwards enter the following command to build and install dwm (if
+necessary as root):
+
+ make clean install
+
+
+Running dwm
+-----------
+Add the following line to your .xinitrc to start dwm using startx:
+
+ exec dwm
+
+In order to connect dwm to a specific display, make sure that
+the DISPLAY environment variable is set correctly, e.g.:
+
+ DISPLAY=foo.bar:1 exec dwm
+
+(This will start dwm on display :1 of the host foo.bar.)
+
+In order to display status info in the bar, you can do something
+like this in your .xinitrc:
+
+ while xsetroot -name "`date` `uptime | sed 's/.*,//'`"
+ do
+ sleep 1
+ done &
+ exec dwm
+
+
+Configuration
+-------------
+The configuration of dwm is done by creating a custom config.h
+and (re)compiling the source code.
diff --git a/keep/manual-programs/suckless/dwm-bak/awesome-glyphs.txt b/keep/manual-programs/suckless/dwm-bak/awesome-glyphs.txt
new file mode 100644
index 0000000..7541568
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/awesome-glyphs.txt
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/keep/manual-programs/suckless/dwm-bak/config.def.h.orig b/keep/manual-programs/suckless/dwm-bak/config.def.h.orig
new file mode 100644
index 0000000..5312ecb
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/config.def.h.orig
@@ -0,0 +1,142 @@
+/* See LICENSE file for copyright and license details. */
+
+/* appearance */
+static const unsigned int borderpx = 2; /* border pixel size of windows */
+static const unsigned int gappx = 5; /* gaps size between windows */
+static const unsigned int snap = 32; /* snap pixel */
+static const int showbar = 1; /* 0 means no bar */
+static const int topbar = 1; /* 0 means bottom bar */
+static const char *fonts[] = { "monospace:size=12", "fontawesome:size=12" };
+static const char dmenufont[] = "monospace:size=11";
+//background color
+static const char col_gray1[] = "#222222";
+//inactive window border color
+static const char col_gray2[] = "#444444";
+//font color
+static const char col_gray3[] = "#bbbbbb";
+//current tag and current window font color
+static const char col_gray4[] = "#eeeeee";
+//Top bar second color (blue) and active window border color
+static const char col_cyan[] = "#f59542";
+static const char *colors[][3] = {
+ /* fg bg border */
+ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
+ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
+};
+
+/* tagging */
+//tag names (upper left)
+static const char *tags[] = { "", "", "", "", "", "", "", "", "", "" };
+
+static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+ /* class instance title tags mask isfloating monitor */
+ { "Gimp", NULL, NULL, 0, 1, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
+};
+
+/* layout(s) */
+static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+static const int nmaster = 1; /* number of clients in master area */
+static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
+
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* key definitions */
+#define MODKEY Mod4Mask
+#define TAGKEYS(KEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
+static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+//static const char *filemanager[] = { "
+//launches htop
+static const char *monitor[] = { "/usr/bin/htop", NULL };
+//sets st as the default terminal
+//static const char *termcmd[] = { "st", NULL };
+//sets urxvt as the default terminal
+static const char *termcmd[] = { "urxvt", NULL };
+//volume controls
+static const char *upvol[] = { "amixer", "-q", "set", "Master", "5%+", "unmute", NULL };
+static const char *downvol[] = { "amixer", "-q", "set", "Master", "5%-", "unmute", NULL };
+static const char *mutevol[] = { "amixer", "-q", "set", "Master", "toggle", NULL };
+
+#include "shiftview.c"
+static char *endx[] = { "/bin/sh", "-c", "endx", "externalpipe", NULL };
+static Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XK_d, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_t, togglebar, {0} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+ { MODKEY, XK_u, incnmaster, {.i = -1 } },
+ { MODKEY, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY, XK_z, zoom, {0} },
+ { MODKEY, XK_Tab, view, {0} },
+ { MODKEY, XK_q, killclient, {0} },
+ { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY|ShiftMask, XK_space, setlayout, {0} },
+ { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, XK_comma, focusmon, {.i = -1 } },
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY, XK_minus, setgaps, {.i = -1 } },
+ { MODKEY, XK_equal, setgaps, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY, XK_n, shiftview, { .i = +1 } },
+ { MODKEY, XK_b, shiftview, { .i = -1 } },
+ { MODKEY, XK_F8, spawn, {.v = upvol } },
+ { MODKEY, XK_F7, spawn, {.v = downvol } },
+ { MODKEY, XK_F5, spawn, {.v = mutevol } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
+};
+
+/* button definitions */
+/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
+static Button buttons[] = {
+ /* click event mask button function argument */
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+ { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+};
+
diff --git a/keep/manual-programs/suckless/dwm-bak/config.h b/keep/manual-programs/suckless/dwm-bak/config.h
new file mode 100644
index 0000000..c9b7665
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/config.h
@@ -0,0 +1,146 @@
+/* See LICENSE file for copyright and license details. */
+
+/* appearance */
+static const unsigned int borderpx = 2; /* border pixel size of windows */
+static const unsigned int gappx = 5; /* gaps size between windows */
+static const unsigned int snap = 32; /* snap pixel */
+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
+static const unsigned int systrayspacing = 2; /* systray spacing */
+static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
+static const int showsystray = 1; /* 0 means no systray */
+static const int showbar = 1; /* 0 means no bar */
+static const int topbar = 0; /* 0 means bottom bar */
+static const char *fonts[] = { "monospace:size=12", "fontawesome:size=12" };
+static const char dmenufont[] = "monospace:size=11";
+//background color
+static const char col_gray1[] = "#222222";
+//inactive window border color
+static const char col_gray2[] = "#444444";
+//font color
+static const char col_gray3[] = "#bbbbbb";
+//current tag and current window font color
+static const char col_gray4[] = "#eeeeee";
+//Top bar second color (blue) and active window border color
+static const char col_cyan[] = "#8558e6";
+static const char *colors[][3] = {
+ /* fg bg border */
+ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
+ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
+};
+
+/* tagging */
+//tag names (upper left)
+static const char *tags[] = { "", "", "", "", "", "", "", "", "", "" };
+
+static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+ /* class instance title tags mask isfloating monitor */
+ { "Gimp", NULL, NULL, 0, 1, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 1, -1 },
+};
+
+/* layout(s) */
+static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+static const int nmaster = 1; /* number of clients in master area */
+static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
+
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* key definitions */
+#define MODKEY Mod4Mask
+#define TAGKEYS(KEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
+static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+//static const char *filemanager[] = { "
+//launches htop
+static const char *monitor[] = { "/usr/bin/htop", NULL };
+//sets st as the default terminal
+//static const char *termcmd[] = { "st", NULL };
+//sets urxvt as the default terminal
+static const char *termcmd[] = { "urxvt", NULL };
+//volume controls
+static const char *upvol[] = { "amixer", "-q", "set", "Master", "5%+", "unmute", NULL };
+static const char *downvol[] = { "amixer", "-q", "set", "Master", "5%-", "unmute", NULL };
+static const char *mutevol[] = { "amixer", "-q", "set", "Master", "toggle", NULL };
+
+#include "shiftview.c"
+static char *endx[] = { "/bin/sh", "-c", "endx", "externalpipe", NULL };
+static Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XK_d, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_t, togglebar, {0} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+ { MODKEY, XK_u, incnmaster, {.i = -1 } },
+ { MODKEY, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY, XK_z, zoom, {0} },
+ { MODKEY, XK_Tab, view, {0} },
+ { MODKEY, XK_q, killclient, {0} },
+ { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY|ShiftMask, XK_space, setlayout, {0} },
+ { MODKEY, XK_space, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, XK_comma, focusmon, {.i = -1 } },
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY, XK_minus, setgaps, {.i = -1 } },
+ { MODKEY, XK_equal, setgaps, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY, XK_n, shiftview, { .i = +1 } },
+ { MODKEY, XK_b, shiftview, { .i = -1 } },
+ { MODKEY, XK_F8, spawn, {.v = upvol } },
+ { MODKEY, XK_F7, spawn, {.v = downvol } },
+ { MODKEY, XK_F5, spawn, {.v = mutevol } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
+};
+
+/* button definitions */
+/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
+static Button buttons[] = {
+ /* click event mask button function argument */
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+ { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+};
+
diff --git a/keep/manual-programs/suckless/dwm-bak/config.mk b/keep/manual-programs/suckless/dwm-bak/config.mk
new file mode 100644
index 0000000..6d36cb7
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/config.mk
@@ -0,0 +1,38 @@
+# dwm version
+VERSION = 6.2
+
+# Customize below to fit your system
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+# Xinerama, comment if you don't want it
+XINERAMALIBS = -lXinerama
+XINERAMAFLAGS = -DXINERAMA
+
+# freetype
+FREETYPELIBS = -lfontconfig -lXft
+FREETYPEINC = /usr/include/freetype2
+# OpenBSD (uncomment)
+#FREETYPEINC = ${X11INC}/freetype2
+
+# includes and libs
+INCS = -I${X11INC} -I${FREETYPEINC}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
+#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
+CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = ${LIBS}
+
+# Solaris
+#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
+#LDFLAGS = ${LIBS}
+
+# compiler and linker
+CC = cc
diff --git a/keep/manual-programs/suckless/dwm-bak/drw.c b/keep/manual-programs/suckless/dwm-bak/drw.c
new file mode 100644
index 0000000..8fd1ca4
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/drw.c
@@ -0,0 +1,435 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+#define UTF_INVALID 0xFFFD
+#define UTF_SIZ 4
+
+static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+static long
+utf8decodebyte(const char c, size_t *i)
+{
+ for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
+ if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
+ return (unsigned char)c & ~utfmask[*i];
+ return 0;
+}
+
+static size_t
+utf8validate(long *u, size_t i)
+{
+ if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+ *u = UTF_INVALID;
+ for (i = 1; *u > utfmax[i]; ++i)
+ ;
+ return i;
+}
+
+static size_t
+utf8decode(const char *c, long *u, size_t clen)
+{
+ size_t i, j, len, type;
+ long udecoded;
+
+ *u = UTF_INVALID;
+ if (!clen)
+ return 0;
+ udecoded = utf8decodebyte(c[0], &len);
+ if (!BETWEEN(len, 1, UTF_SIZ))
+ return 1;
+ for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+ udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
+ if (type)
+ return j;
+ }
+ if (j < len)
+ return 0;
+ *u = udecoded;
+ utf8validate(u, len);
+
+ return len;
+}
+
+Drw *
+drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
+{
+ Drw *drw = ecalloc(1, sizeof(Drw));
+
+ drw->dpy = dpy;
+ drw->screen = screen;
+ drw->root = root;
+ drw->w = w;
+ drw->h = h;
+ drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+ drw->gc = XCreateGC(dpy, root, 0, NULL);
+ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+ return drw;
+}
+
+void
+drw_resize(Drw *drw, unsigned int w, unsigned int h)
+{
+ if (!drw)
+ return;
+
+ drw->w = w;
+ drw->h = h;
+ if (drw->drawable)
+ XFreePixmap(drw->dpy, drw->drawable);
+ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+}
+
+void
+drw_free(Drw *drw)
+{
+ XFreePixmap(drw->dpy, drw->drawable);
+ XFreeGC(drw->dpy, drw->gc);
+ free(drw);
+}
+
+/* This function is an implementation detail. Library users should use
+ * drw_fontset_create instead.
+ */
+static Fnt *
+xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
+{
+ Fnt *font;
+ XftFont *xfont = NULL;
+ FcPattern *pattern = NULL;
+
+ if (fontname) {
+ /* Using the pattern found at font->xfont->pattern does not yield the
+ * same substitution results as using the pattern returned by
+ * FcNameParse; using the latter results in the desired fallback
+ * behaviour whereas the former just results in missing-character
+ * rectangles being drawn, at least with some fonts. */
+ if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
+ fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
+ return NULL;
+ }
+ if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
+ fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
+ XftFontClose(drw->dpy, xfont);
+ return NULL;
+ }
+ } else if (fontpattern) {
+ if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
+ fprintf(stderr, "error, cannot load font from pattern.\n");
+ return NULL;
+ }
+ } else {
+ die("no font specified.");
+ }
+
+ /* Do not allow using color fonts. This is a workaround for a BadLength
+ * error from Xft with color glyphs. Modelled on the Xterm workaround. See
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
+ * https://lists.suckless.org/dev/1701/30932.html
+ * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
+ * and lots more all over the internet.
+ */
+ FcBool iscol;
+ if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
+ XftFontClose(drw->dpy, xfont);
+ return NULL;
+ }
+
+ font = ecalloc(1, sizeof(Fnt));
+ font->xfont = xfont;
+ font->pattern = pattern;
+ font->h = xfont->ascent + xfont->descent;
+ font->dpy = drw->dpy;
+
+ return font;
+}
+
+static void
+xfont_free(Fnt *font)
+{
+ if (!font)
+ return;
+ if (font->pattern)
+ FcPatternDestroy(font->pattern);
+ XftFontClose(font->dpy, font->xfont);
+ free(font);
+}
+
+Fnt*
+drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
+{
+ Fnt *cur, *ret = NULL;
+ size_t i;
+
+ if (!drw || !fonts)
+ return NULL;
+
+ for (i = 1; i <= fontcount; i++) {
+ if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
+ cur->next = ret;
+ ret = cur;
+ }
+ }
+ return (drw->fonts = ret);
+}
+
+void
+drw_fontset_free(Fnt *font)
+{
+ if (font) {
+ drw_fontset_free(font->next);
+ xfont_free(font);
+ }
+}
+
+void
+drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+{
+ if (!drw || !dest || !clrname)
+ return;
+
+ if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen),
+ clrname, dest))
+ die("error, cannot allocate color '%s'", clrname);
+}
+
+/* Wrapper to create color schemes. The caller has to call free(3) on the
+ * returned color scheme when done using it. */
+Clr *
+drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+{
+ size_t i;
+ Clr *ret;
+
+ /* need at least two colors for a scheme */
+ if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
+ return NULL;
+
+ for (i = 0; i < clrcount; i++)
+ drw_clr_create(drw, &ret[i], clrnames[i]);
+ return ret;
+}
+
+void
+drw_setfontset(Drw *drw, Fnt *set)
+{
+ if (drw)
+ drw->fonts = set;
+}
+
+void
+drw_setscheme(Drw *drw, Clr *scm)
+{
+ if (drw)
+ drw->scheme = scm;
+}
+
+void
+drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
+{
+ if (!drw || !drw->scheme)
+ return;
+ XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
+ if (filled)
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ else
+ XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
+}
+
+int
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
+{
+ char buf[1024];
+ int ty;
+ unsigned int ew;
+ XftDraw *d = NULL;
+ Fnt *usedfont, *curfont, *nextfont;
+ size_t i, len;
+ int utf8strlen, utf8charlen, render = x || y || w || h;
+ long utf8codepoint = 0;
+ const char *utf8str;
+ FcCharSet *fccharset;
+ FcPattern *fcpattern;
+ FcPattern *match;
+ XftResult result;
+ int charexists = 0;
+
+ if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
+ return 0;
+
+ if (!render) {
+ w = ~w;
+ } else {
+ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ d = XftDrawCreate(drw->dpy, drw->drawable,
+ DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen));
+ x += lpad;
+ w -= lpad;
+ }
+
+ usedfont = drw->fonts;
+ while (1) {
+ utf8strlen = 0;
+ utf8str = text;
+ nextfont = NULL;
+ while (*text) {
+ utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+ for (curfont = drw->fonts; curfont; curfont = curfont->next) {
+ charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
+ if (charexists) {
+ if (curfont == usedfont) {
+ utf8strlen += utf8charlen;
+ text += utf8charlen;
+ } else {
+ nextfont = curfont;
+ }
+ break;
+ }
+ }
+
+ if (!charexists || nextfont)
+ break;
+ else
+ charexists = 0;
+ }
+
+ if (utf8strlen) {
+ drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
+ /* shorten text if necessary */
+ for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
+ drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
+
+ if (len) {
+ memcpy(buf, utf8str, len);
+ buf[len] = '\0';
+ if (len < utf8strlen)
+ for (i = len; i && i > len - 3; buf[--i] = '.')
+ ; /* NOP */
+
+ if (render) {
+ ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
+ XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
+ usedfont->xfont, x, ty, (XftChar8 *)buf, len);
+ }
+ x += ew;
+ w -= ew;
+ }
+ }
+
+ if (!*text) {
+ break;
+ } else if (nextfont) {
+ charexists = 0;
+ usedfont = nextfont;
+ } else {
+ /* Regardless of whether or not a fallback font is found, the
+ * character must be drawn. */
+ charexists = 1;
+
+ fccharset = FcCharSetCreate();
+ FcCharSetAddChar(fccharset, utf8codepoint);
+
+ if (!drw->fonts->pattern) {
+ /* Refer to the comment in xfont_create for more information. */
+ die("the first font in the cache must be loaded from a font string.");
+ }
+
+ fcpattern = FcPatternDuplicate(drw->fonts->pattern);
+ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+ FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
+ FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
+
+ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+ FcDefaultSubstitute(fcpattern);
+ match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
+
+ FcCharSetDestroy(fccharset);
+ FcPatternDestroy(fcpattern);
+
+ if (match) {
+ usedfont = xfont_create(drw, NULL, match);
+ if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
+ for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
+ ; /* NOP */
+ curfont->next = usedfont;
+ } else {
+ xfont_free(usedfont);
+ usedfont = drw->fonts;
+ }
+ }
+ }
+ }
+ if (d)
+ XftDrawDestroy(d);
+
+ return x + (render ? w : 0);
+}
+
+void
+drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
+{
+ if (!drw)
+ return;
+
+ XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
+ XSync(drw->dpy, False);
+}
+
+unsigned int
+drw_fontset_getwidth(Drw *drw, const char *text)
+{
+ if (!drw || !drw->fonts || !text)
+ return 0;
+ return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
+}
+
+void
+drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
+{
+ XGlyphInfo ext;
+
+ if (!font || !text)
+ return;
+
+ XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+ if (w)
+ *w = ext.xOff;
+ if (h)
+ *h = font->h;
+}
+
+Cur *
+drw_cur_create(Drw *drw, int shape)
+{
+ Cur *cur;
+
+ if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
+ return NULL;
+
+ cur->cursor = XCreateFontCursor(drw->dpy, shape);
+
+ return cur;
+}
+
+void
+drw_cur_free(Drw *drw, Cur *cursor)
+{
+ if (!cursor)
+ return;
+
+ XFreeCursor(drw->dpy, cursor->cursor);
+ free(cursor);
+}
diff --git a/keep/manual-programs/suckless/dwm-bak/drw.h b/keep/manual-programs/suckless/dwm-bak/drw.h
new file mode 100644
index 0000000..4bcd5ad
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/drw.h
@@ -0,0 +1,57 @@
+/* See LICENSE file for copyright and license details. */
+
+typedef struct {
+ Cursor cursor;
+} Cur;
+
+typedef struct Fnt {
+ Display *dpy;
+ unsigned int h;
+ XftFont *xfont;
+ FcPattern *pattern;
+ struct Fnt *next;
+} Fnt;
+
+enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
+typedef XftColor Clr;
+
+typedef struct {
+ unsigned int w, h;
+ Display *dpy;
+ int screen;
+ Window root;
+ Drawable drawable;
+ GC gc;
+ Clr *scheme;
+ Fnt *fonts;
+} Drw;
+
+/* Drawable abstraction */
+Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
+void drw_resize(Drw *drw, unsigned int w, unsigned int h);
+void drw_free(Drw *drw);
+
+/* Fnt abstraction */
+Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
+void drw_fontset_free(Fnt* set);
+unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
+
+/* Colorscheme abstraction */
+void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
+Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
+
+/* Cursor abstraction */
+Cur *drw_cur_create(Drw *drw, int shape);
+void drw_cur_free(Drw *drw, Cur *cursor);
+
+/* Drawing context manipulation */
+void drw_setfontset(Drw *drw, Fnt *set);
+void drw_setscheme(Drw *drw, Clr *scm);
+
+/* Drawing functions */
+void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+
+/* Map functions */
+void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/keep/manual-programs/suckless/dwm-bak/drw.o b/keep/manual-programs/suckless/dwm-bak/drw.o
new file mode 100644
index 0000000..d50849c
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/drw.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm b/keep/manual-programs/suckless/dwm-bak/dwm
new file mode 100755
index 0000000..c682555
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm-autostart-20161205-bb3bd6f.diff b/keep/manual-programs/suckless/dwm-bak/dwm-autostart-20161205-bb3bd6f.diff
new file mode 100644
index 0000000..6f11eaf
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm-autostart-20161205-bb3bd6f.diff
@@ -0,0 +1,39 @@
+commit 5918623c5bd7fda155bf9dc3d33890c4ae1722d0
+Author: Simon Bremer <simon.bremer@tum.de>
+Date: Thu Dec 22 17:31:07 2016 +0100
+
+ Applied and fixed autostart patch for previous version;
+
+diff --git a/dwm.c b/dwm.c
+index d27cb67..066ed71 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h);
+ static void resizemouse(const Arg *arg);
+ static void restack(Monitor *m);
+ static void run(void);
++static void runAutostart(void);
+ static void scan(void);
+ static int sendevent(Client *c, Atom proto);
+ static void sendmon(Client *c, Monitor *m);
+@@ -1386,6 +1387,12 @@ run(void)
+ }
+
+ void
++runAutostart(void) {
++ system("cd ~/.dwm; ./autostart_blocking.sh");
++ system("cd ~/.dwm; ./autostart.sh &");
++}
++
++void
+ scan(void)
+ {
+ unsigned int i, num;
+@@ -2145,6 +2152,7 @@ main(int argc, char *argv[])
+ checkotherwm();
+ setup();
+ scan();
++ runAutostart();
+ run();
+ cleanup();
+ XCloseDisplay(dpy);
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm-fullgaps-6.2.diff b/keep/manual-programs/suckless/dwm-bak/dwm-fullgaps-6.2.diff
new file mode 100644
index 0000000..7206aec
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm-fullgaps-6.2.diff
@@ -0,0 +1,95 @@
+diff --git a/config.def.h b/config.def.h
+index 1c0b587..38d2f6c 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,7 @@
+
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
++static const unsigned int gappx = 5; /* gaps between windows */
+ static const unsigned int snap = 32; /* snap pixel */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
+@@ -84,6 +85,9 @@ static Key keys[] = {
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
++ { MODKEY, XK_minus, setgaps, {.i = -1 } },
++ { MODKEY, XK_equal, setgaps, {.i = +1 } },
++ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+diff --git a/dwm.c b/dwm.c
+index 4465af1..4363627 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -119,6 +119,7 @@ struct Monitor {
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
++ int gappx; /* gaps between windows */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+@@ -199,6 +200,7 @@ static void sendmon(Client *c, Monitor *m);
+ static void setclientstate(Client *c, long state);
+ static void setfocus(Client *c);
+ static void setfullscreen(Client *c, int fullscreen);
++static void setgaps(const Arg *arg);
+ static void setlayout(const Arg *arg);
+ static void setmfact(const Arg *arg);
+ static void setup(void);
+@@ -638,6 +640,7 @@ createmon(void)
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+ m->topbar = topbar;
++ m->gappx = gappx;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+@@ -1497,6 +1500,16 @@ setfullscreen(Client *c, int fullscreen)
+ }
+ }
+
++void
++setgaps(const Arg *arg)
++{
++ if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
++ selmon->gappx = 0;
++ else
++ selmon->gappx += arg->i;
++ arrange(selmon);
++}
++
+ void
+ setlayout(const Arg *arg)
+ {
+@@ -1683,16 +1696,16 @@ tile(Monitor *m)
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->ww * m->mfact : 0;
+ else
+- mw = m->ww;
+- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
++ mw = m->ww - m->gappx;
++ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+- h = (m->wh - my) / (MIN(n, m->nmaster) - i);
+- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0);
+- my += HEIGHT(c);
++ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
++ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
++ my += HEIGHT(c) + m->gappx;
+ } else {
+- h = (m->wh - ty) / (n - i);
+- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
+- ty += HEIGHT(c);
++ h = (m->wh - ty) / (n - i) - m->gappx;
++ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
++ ty += HEIGHT(c) + m->gappx;
+ }
+ }
+
+--
+2.20.1
+
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm-systray-6.2.diff b/keep/manual-programs/suckless/dwm-bak/dwm-systray-6.2.diff
new file mode 100644
index 0000000..27187ac
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm-systray-6.2.diff
@@ -0,0 +1,746 @@
+From 4001ccae7b1a41bdcb247b0cf095a51af7b68c28 Mon Sep 17 00:00:00 2001
+From: Igor Gevka <igor.gevka@gmail.com>
+Date: Sun, 16 Feb 2020 15:03:10 -0800
+Subject: [PATCH] [PATCH] Implements a system tray for dwm.
+
+Original author: Jan Christoph Ebersbach <jceb@e-jc.de>, inspired by http://code.google.com/p/dwm-plus
+URL: http://dwm.suckless.org/patches/systray
+dwm 6.2 port by Igor Gevka <igor.gevka@gmail.com>
+---
+ config.def.h | 4 +
+ dwm.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 382 insertions(+), 26 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 1c0b587..2d824d1 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -3,6 +3,10 @@
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
++static const unsigned int systrayspacing = 2; /* systray spacing */
++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
++static const int showsystray = 1; /* 0 means no systray */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
+ static const char *fonts[] = { "monospace:size=10" };
+diff --git a/dwm.c b/dwm.c
+index 4465af1..3e361fa 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -57,12 +57,30 @@
+ #define TAGMASK ((1 << LENGTH(tags)) - 1)
+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
++#define SYSTEM_TRAY_REQUEST_DOCK 0
++
++/* XEMBED messages */
++#define XEMBED_EMBEDDED_NOTIFY 0
++#define XEMBED_WINDOW_ACTIVATE 1
++#define XEMBED_FOCUS_IN 4
++#define XEMBED_MODALITY_ON 10
++
++#define XEMBED_MAPPED (1 << 0)
++#define XEMBED_WINDOW_ACTIVATE 1
++#define XEMBED_WINDOW_DEACTIVATE 2
++
++#define VERSION_MAJOR 0
++#define VERSION_MINOR 0
++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
++
+ /* enums */
+ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+ enum { SchemeNorm, SchemeSel }; /* color schemes */
+ enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+@@ -141,6 +159,12 @@ typedef struct {
+ int monitor;
+ } Rule;
+
++typedef struct Systray Systray;
++struct Systray {
++ Window win;
++ Client *icons;
++};
++
+ /* function declarations */
+ static void applyrules(Client *c);
+ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+@@ -169,8 +193,10 @@ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
++static Atom getatomprop(Client *c, Atom prop);
+ static int getrootptr(int *x, int *y);
+ static long getstate(Window w);
++static unsigned int getsystraywidth();
+ static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
+ static void grabbuttons(Client *c, int focused);
+ static void grabkeys(void);
+@@ -188,13 +214,16 @@ static void pop(Client *);
+ static void propertynotify(XEvent *e);
+ static void quit(const Arg *arg);
+ static Monitor *recttomon(int x, int y, int w, int h);
++static void removesystrayicon(Client *i);
+ static void resize(Client *c, int x, int y, int w, int h, int interact);
++static void resizebarwin(Monitor *m);
+ static void resizeclient(Client *c, int x, int y, int w, int h);
+ static void resizemouse(const Arg *arg);
++static void resizerequest(XEvent *e);
+ static void restack(Monitor *m);
+ static void run(void);
+ static void scan(void);
+-static int sendevent(Client *c, Atom proto);
++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+ static void sendmon(Client *c, Monitor *m);
+ static void setclientstate(Client *c, long state);
+ static void setfocus(Client *c);
+@@ -206,6 +235,7 @@ static void seturgent(Client *c, int urg);
+ static void showhide(Client *c);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static Monitor *systraytomon(Monitor *m);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *);
+@@ -223,18 +253,23 @@ static int updategeom(void);
+ static void updatenumlockmask(void);
+ static void updatesizehints(Client *c);
+ static void updatestatus(void);
++static void updatesystray(void);
++static void updatesystrayicongeom(Client *i, int w, int h);
++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+ static void updatetitle(Client *c);
+ static void updatewindowtype(Client *c);
+ static void updatewmhints(Client *c);
+ static void view(const Arg *arg);
+ static Client *wintoclient(Window w);
+ static Monitor *wintomon(Window w);
++static Client *wintosystrayicon(Window w);
+ static int xerror(Display *dpy, XErrorEvent *ee);
+ static int xerrordummy(Display *dpy, XErrorEvent *ee);
+ static int xerrorstart(Display *dpy, XErrorEvent *ee);
+ static void zoom(const Arg *arg);
+
+ /* variables */
++static Systray *systray = NULL;
+ static const char broken[] = "broken";
+ static char stext[256];
+ static int screen;
+@@ -257,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
++ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+ };
+-static Atom wmatom[WMLast], netatom[NetLast];
++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+ static int running = 1;
+ static Cur *cursor[CurLast];
+ static Clr **scheme;
+@@ -439,7 +475,7 @@ buttonpress(XEvent *e)
+ arg.ui = 1 << i;
+ } else if (ev->x < x + blw)
+ click = ClkLtSymbol;
+- else if (ev->x > selmon->ww - TEXTW(stext))
++ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+@@ -482,6 +518,11 @@ cleanup(void)
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while (mons)
+ cleanupmon(mons);
++ if (showsystray) {
++ XUnmapWindow(dpy, systray->win);
++ XDestroyWindow(dpy, systray->win);
++ free(systray);
++ }
+ for (i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for (i = 0; i < LENGTH(colors); i++)
+@@ -512,9 +553,57 @@ cleanupmon(Monitor *mon)
+ void
+ clientmessage(XEvent *e)
+ {
++ XWindowAttributes wa;
++ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
++ /* add systray icons */
++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
++ if (!(c = (Client *)calloc(1, sizeof(Client))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
++ if (!(c->win = cme->data.l[2])) {
++ free(c);
++ return;
++ }
++ c->mon = selmon;
++ c->next = systray->icons;
++ systray->icons = c;
++ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
++ /* use sane defaults */
++ wa.width = bh;
++ wa.height = bh;
++ wa.border_width = 0;
++ }
++ c->x = c->oldx = c->y = c->oldy = 0;
++ c->w = c->oldw = wa.width;
++ c->h = c->oldh = wa.height;
++ c->oldbw = wa.border_width;
++ c->bw = 0;
++ c->isfloating = True;
++ /* reuse tags field as mapped status */
++ c->tags = 1;
++ updatesizehints(c);
++ updatesystrayicongeom(c, wa.width, wa.height);
++ XAddToSaveSet(dpy, c->win);
++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
++ XReparentWindow(dpy, c->win, systray->win, 0, 0);
++ /* use parents background color */
++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ /* FIXME not sure if I have to send these events, too */
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ XSync(dpy, False);
++ resizebarwin(selmon);
++ updatesystray();
++ setclientstate(c, NormalState);
++ }
++ return;
++ }
+ if (!c)
+ return;
+ if (cme->message_type == netatom[NetWMState]) {
+@@ -567,7 +656,7 @@ configurenotify(XEvent *e)
+ for (c = m->clients; c; c = c->next)
+ if (c->isfullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ resizebarwin(m);
+ }
+ focus(NULL);
+ arrange(NULL);
+@@ -652,6 +741,11 @@ destroynotify(XEvent *e)
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
++ else if ((c = wintosystrayicon(ev->window))) {
++ removesystrayicon(c);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ }
+
+ void
+@@ -695,19 +789,23 @@ dirtomon(int dir)
+ void
+ drawbar(Monitor *m)
+ {
+- int x, w, sw = 0;
++ int x, w, sw = 0, stw = 0;
+ int boxs = drw->fonts->h / 9;
+ int boxw = drw->fonts->h / 6 + 2;
+ unsigned int i, occ = 0, urg = 0;
+ Client *c;
+
++ if(showsystray && m == systraytomon(m))
++ stw = getsystraywidth();
++
+ /* draw status first so it can be overdrawn by tags later */
+ if (m == selmon) { /* status is only drawn on selected monitor */
+ drw_setscheme(drw, scheme[SchemeNorm]);
+- sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
+- drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
++ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
++ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
++ resizebarwin(m);
+ for (c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+@@ -728,7 +826,7 @@ drawbar(Monitor *m)
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+- if ((w = m->ww - sw - x) > bh) {
++ if ((w = m->ww - sw - stw - x) > bh) {
+ if (m->sel) {
+ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+@@ -739,7 +837,7 @@ drawbar(Monitor *m)
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ }
+ }
+- drw_map(drw, m->barwin, 0, 0, m->ww, bh);
++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
+ }
+
+ void
+@@ -776,8 +874,11 @@ expose(XEvent *e)
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+- if (ev->count == 0 && (m = wintomon(ev->window)))
++ if (ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
++ if (m == selmon)
++ updatesystray();
++ }
+ }
+
+ void
+@@ -862,10 +963,17 @@ getatomprop(Client *c, Atom prop)
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
++ /* FIXME getatomprop should return the number of items and a pointer to
++ * the stored data instead of this workaround */
++ Atom req = XA_ATOM;
++ if (prop == xatom[XembedInfo])
++ req = xatom[XembedInfo];
+
+- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
++ if (da == xatom[XembedInfo] && dl == 2)
++ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+@@ -899,6 +1007,16 @@ getstate(Window w)
+ return result;
+ }
+
++unsigned int
++getsystraywidth()
++{
++ unsigned int w = 0;
++ Client *i;
++ if(showsystray)
++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
++ return w ? w + systrayspacing : 1;
++}
++
+ int
+ gettextprop(Window w, Atom atom, char *text, unsigned int size)
+ {
+@@ -1003,7 +1121,7 @@ killclient(const Arg *arg)
+ {
+ if (!selmon->sel)
+ return;
+- if (!sendevent(selmon->sel, wmatom[WMDelete])) {
++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+@@ -1091,6 +1209,12 @@ maprequest(XEvent *e)
+ {
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
++ Client *i;
++ if ((i = wintosystrayicon(ev->window))) {
++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+
+ if (!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+@@ -1215,6 +1339,16 @@ propertynotify(XEvent *e)
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
++ if ((c = wintosystrayicon(ev->window))) {
++ if (ev->atom == XA_WM_NORMAL_HINTS) {
++ updatesizehints(c);
++ updatesystrayicongeom(c, c->w, c->h);
++ }
++ else
++ updatesystrayiconstate(c, ev);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if (ev->state == PropertyDelete)
+@@ -1265,6 +1399,20 @@ recttomon(int x, int y, int w, int h)
+ return r;
+ }
+
++void
++removesystrayicon(Client *i)
++{
++ Client **ii;
++
++ if (!showsystray || !i)
++ return;
++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
++ if (ii)
++ *ii = i->next;
++ free(i);
++}
++
++
+ void
+ resize(Client *c, int x, int y, int w, int h, int interact)
+ {
+@@ -1272,6 +1420,14 @@ resize(Client *c, int x, int y, int w, int h, int interact)
+ resizeclient(c, x, y, w, h);
+ }
+
++void
++resizebarwin(Monitor *m) {
++ unsigned int w = m->ww;
++ if (showsystray && m == systraytomon(m))
++ w -= getsystraywidth();
++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
++}
++
+ void
+ resizeclient(Client *c, int x, int y, int w, int h)
+ {
+@@ -1344,6 +1500,19 @@ resizemouse(const Arg *arg)
+ }
+ }
+
++void
++resizerequest(XEvent *e)
++{
++ XResizeRequestEvent *ev = &e->xresizerequest;
++ Client *i;
++
++ if ((i = wintosystrayicon(ev->window))) {
++ updatesystrayicongeom(i, ev->width, ev->height);
++ resizebarwin(selmon);
++ updatesystray();
++ }
++}
++
+ void
+ restack(Monitor *m)
+ {
+@@ -1433,26 +1602,36 @@ setclientstate(Client *c, long state)
+ }
+
+ int
+-sendevent(Client *c, Atom proto)
++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
+ {
+ int n;
+- Atom *protocols;
++ Atom *protocols, mt;
+ int exists = 0;
+ XEvent ev;
+
+- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
+- while (!exists && n--)
+- exists = protocols[n] == proto;
+- XFree(protocols);
++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
++ mt = wmatom[WMProtocols];
++ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
++ while (!exists && n--)
++ exists = protocols[n] == proto;
++ XFree(protocols);
++ }
++ }
++ else {
++ exists = True;
++ mt = proto;
+ }
+ if (exists) {
+ ev.type = ClientMessage;
+- ev.xclient.window = c->win;
+- ev.xclient.message_type = wmatom[WMProtocols];
++ ev.xclient.window = w;
++ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+- ev.xclient.data.l[0] = proto;
+- ev.xclient.data.l[1] = CurrentTime;
+- XSendEvent(dpy, c->win, False, NoEventMask, &ev);
++ ev.xclient.data.l[0] = d0;
++ ev.xclient.data.l[1] = d1;
++ ev.xclient.data.l[2] = d2;
++ ev.xclient.data.l[3] = d3;
++ ev.xclient.data.l[4] = d4;
++ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+ }
+@@ -1466,7 +1645,7 @@ setfocus(Client *c)
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+- sendevent(c, wmatom[WMTakeFocus]);
++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+ }
+
+ void
+@@ -1555,6 +1734,10 @@ setup(void)
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+@@ -1562,6 +1745,9 @@ setup(void)
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+@@ -1570,6 +1756,8 @@ setup(void)
+ scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
+ for (i = 0; i < LENGTH(colors); i++)
+ scheme[i] = drw_scm_create(drw, colors[i], 3);
++ /* init system tray */
++ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+@@ -1701,7 +1889,18 @@ togglebar(const Arg *arg)
+ {
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
++ resizebarwin(selmon);
++ if (showsystray) {
++ XWindowChanges wc;
++ if (!selmon->showbar)
++ wc.y = -bh;
++ else if (selmon->showbar) {
++ wc.y = 0;
++ if (!selmon->topbar)
++ wc.y = selmon->mh - bh;
++ }
++ XConfigureWindow(dpy, systray->win, CWY, &wc);
++ }
+ arrange(selmon);
+ }
+
+@@ -1796,11 +1995,18 @@ unmapnotify(XEvent *e)
+ else
+ unmanage(c, 0);
+ }
++ else if ((c = wintosystrayicon(ev->window))) {
++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
++ * _not_ destroy them. We map those windows back */
++ XMapRaised(dpy, c->win);
++ updatesystray();
++ }
+ }
+
+ void
+ updatebars(void)
+ {
++ unsigned int w;
+ Monitor *m;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+@@ -1811,10 +2017,15 @@ updatebars(void)
+ for (m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
++ w = m->ww;
++ if (showsystray && m == systraytomon(m))
++ w -= getsystraywidth();
++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
++ if (showsystray && m == systraytomon(m))
++ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+@@ -1990,6 +2201,121 @@ updatestatus(void)
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ strcpy(stext, "dwm-"VERSION);
+ drawbar(selmon);
++ updatesystray();
++}
++
++void
++updatesystrayicongeom(Client *i, int w, int h)
++{
++ if (i) {
++ i->h = bh;
++ if (w == h)
++ i->w = bh;
++ else if (h == bh)
++ i->w = w;
++ else
++ i->w = (int) ((float)bh * ((float)w / (float)h));
++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
++ /* force icons into the systray dimensions if they don't want to */
++ if (i->h > bh) {
++ if (i->w == i->h)
++ i->w = bh;
++ else
++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
++ i->h = bh;
++ }
++ }
++}
++
++void
++updatesystrayiconstate(Client *i, XPropertyEvent *ev)
++{
++ long flags;
++ int code = 0;
++
++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
++ !(flags = getatomprop(i, xatom[XembedInfo])))
++ return;
++
++ if (flags & XEMBED_MAPPED && !i->tags) {
++ i->tags = 1;
++ code = XEMBED_WINDOW_ACTIVATE;
++ XMapRaised(dpy, i->win);
++ setclientstate(i, NormalState);
++ }
++ else if (!(flags & XEMBED_MAPPED) && i->tags) {
++ i->tags = 0;
++ code = XEMBED_WINDOW_DEACTIVATE;
++ XUnmapWindow(dpy, i->win);
++ setclientstate(i, WithdrawnState);
++ }
++ else
++ return;
++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
++ systray->win, XEMBED_EMBEDDED_VERSION);
++}
++
++void
++updatesystray(void)
++{
++ XSetWindowAttributes wa;
++ XWindowChanges wc;
++ Client *i;
++ Monitor *m = systraytomon(NULL);
++ unsigned int x = m->mx + m->mw;
++ unsigned int w = 1;
++
++ if (!showsystray)
++ return;
++ if (!systray) {
++ /* init systray */
++ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
++ wa.event_mask = ButtonPressMask | ExposureMask;
++ wa.override_redirect = True;
++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
++ XMapRaised(dpy, systray->win);
++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
++ XSync(dpy, False);
++ }
++ else {
++ fprintf(stderr, "dwm: unable to obtain system tray.\n");
++ free(systray);
++ systray = NULL;
++ return;
++ }
++ }
++ for (w = 0, i = systray->icons; i; i = i->next) {
++ /* make sure the background color stays the same */
++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
++ XMapRaised(dpy, i->win);
++ w += systrayspacing;
++ i->x = w;
++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
++ w += i->w;
++ if (i->mon != m)
++ i->mon = m;
++ }
++ w = w ? w + systrayspacing : 1;
++ x -= w;
++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
++ wc.stack_mode = Above; wc.sibling = m->barwin;
++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
++ XMapWindow(dpy, systray->win);
++ XMapSubwindows(dpy, systray->win);
++ /* redraw background */
++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
++ XSync(dpy, False);
+ }
+
+ void
+@@ -2057,6 +2383,16 @@ wintoclient(Window w)
+ return NULL;
+ }
+
++Client *
++wintosystrayicon(Window w) {
++ Client *i = NULL;
++
++ if (!showsystray || !w)
++ return i;
++ for (i = systray->icons; i && i->win != w; i = i->next) ;
++ return i;
++}
++
+ Monitor *
+ wintomon(Window w)
+ {
+@@ -2110,6 +2446,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
+ return -1;
+ }
+
++Monitor *
++systraytomon(Monitor *m) {
++ Monitor *t;
++ int i, n;
++ if(!systraypinning) {
++ if(!m)
++ return selmon;
++ return m == selmon ? m : NULL;
++ }
++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
++ if(systraypinningfailfirst && n < systraypinning)
++ return mons;
++ return t;
++}
++
+ void
+ zoom(const Arg *arg)
+ {
+--
+2.17.1
+
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm.1 b/keep/manual-programs/suckless/dwm-bak/dwm.1
new file mode 100644
index 0000000..13b3729
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm.1
@@ -0,0 +1,176 @@
+.TH DWM 1 dwm\-VERSION
+.SH NAME
+dwm \- dynamic window manager
+.SH SYNOPSIS
+.B dwm
+.RB [ \-v ]
+.SH DESCRIPTION
+dwm is a dynamic window manager for X. It manages windows in tiled, monocle
+and floating layouts. Either layout can be applied dynamically, optimising the
+environment for the application in use and the task performed.
+.P
+In tiled layouts windows are managed in a master and stacking area. The master
+area on the left contains one window by default, and the stacking area on the
+right contains all other windows. The number of master area windows can be
+adjusted from zero to an arbitrary number. In monocle layout all windows are
+maximised to the screen size. In floating layout windows can be resized and
+moved freely. Dialog windows are always managed floating, regardless of the
+layout applied.
+.P
+Windows are grouped by tags. Each window can be tagged with one or multiple
+tags. Selecting certain tags displays all windows with these tags.
+.P
+Each screen contains a small status bar which displays all available tags, the
+layout, the title of the focused window, and the text read from the root window
+name property, if the screen is focused. A floating window is indicated with an
+empty square and a maximised floating window is indicated with a filled square
+before the windows title. The selected tags are indicated with a different
+color. The tags of the focused window are indicated with a filled square in the
+top left corner. The tags which are applied to one or more windows are
+indicated with an empty square in the top left corner.
+.P
+dwm draws a small border around windows to indicate the focus state.
+.SH OPTIONS
+.TP
+.B \-v
+prints version information to standard output, then exits.
+.SH USAGE
+.SS Status bar
+.TP
+.B X root window name
+is read and displayed in the status text area. It can be set with the
+.BR xsetroot (1)
+command.
+.TP
+.B Button1
+click on a tag label to display all windows with that tag, click on the layout
+label toggles between tiled and floating layout.
+.TP
+.B Button3
+click on a tag label adds/removes all windows with that tag to/from the view.
+.TP
+.B Mod1\-Button1
+click on a tag label applies that tag to the focused window.
+.TP
+.B Mod1\-Button3
+click on a tag label adds/removes that tag to/from the focused window.
+.SS Keyboard commands
+.TP
+.B Mod1\-Shift\-Return
+Start
+.BR st(1).
+.TP
+.B Mod1\-p
+Spawn
+.BR dmenu(1)
+for launching other programs.
+.TP
+.B Mod1\-,
+Focus previous screen, if any.
+.TP
+.B Mod1\-.
+Focus next screen, if any.
+.TP
+.B Mod1\-Shift\-,
+Send focused window to previous screen, if any.
+.TP
+.B Mod1\-Shift\-.
+Send focused window to next screen, if any.
+.TP
+.B Mod1\-b
+Toggles bar on and off.
+.TP
+.B Mod1\-t
+Sets tiled layout.
+.TP
+.B Mod1\-f
+Sets floating layout.
+.TP
+.B Mod1\-m
+Sets monocle layout.
+.TP
+.B Mod1\-space
+Toggles between current and previous layout.
+.TP
+.B Mod1\-j
+Focus next window.
+.TP
+.B Mod1\-k
+Focus previous window.
+.TP
+.B Mod1\-i
+Increase number of windows in master area.
+.TP
+.B Mod1\-d
+Decrease number of windows in master area.
+.TP
+.B Mod1\-l
+Increase master area size.
+.TP
+.B Mod1\-h
+Decrease master area size.
+.TP
+.B Mod1\-Return
+Zooms/cycles focused window to/from master area (tiled layouts only).
+.TP
+.B Mod1\-Shift\-c
+Close focused window.
+.TP
+.B Mod1\-Shift\-space
+Toggle focused window between tiled and floating state.
+.TP
+.B Mod1\-Tab
+Toggles to the previously selected tags.
+.TP
+.B Mod1\-Shift\-[1..n]
+Apply nth tag to focused window.
+.TP
+.B Mod1\-Shift\-0
+Apply all tags to focused window.
+.TP
+.B Mod1\-Control\-Shift\-[1..n]
+Add/remove nth tag to/from focused window.
+.TP
+.B Mod1\-[1..n]
+View all windows with nth tag.
+.TP
+.B Mod1\-0
+View all windows with any tag.
+.TP
+.B Mod1\-Control\-[1..n]
+Add/remove all windows with nth tag to/from the view.
+.TP
+.B Mod1\-Shift\-q
+Quit dwm.
+.SS Mouse commands
+.TP
+.B Mod1\-Button1
+Move focused window while dragging. Tiled windows will be toggled to the floating state.
+.TP
+.B Mod1\-Button2
+Toggles focused window between floating and tiled state.
+.TP
+.B Mod1\-Button3
+Resize focused window while dragging. Tiled windows will be toggled to the floating state.
+.SH CUSTOMIZATION
+dwm is customized by creating a custom config.h and (re)compiling the source
+code. This keeps it fast, secure and simple.
+.SH SEE ALSO
+.BR dmenu (1),
+.BR st (1)
+.SH ISSUES
+Java applications which use the XToolkit/XAWT backend may draw grey windows
+only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
+JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
+are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the
+environment variable
+.BR AWT_TOOLKIT=MToolkit
+(to use the older Motif backend instead) or running
+.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D
+or
+.B wmname LG3D
+(to pretend that a non-reparenting window manager is running that the
+XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
+.BR _JAVA_AWT_WM_NONREPARENTING=1 .
+.SH BUGS
+Send all bug reports with a patch to hackers@suckless.org.
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm.c b/keep/manual-programs/suckless/dwm-bak/dwm.c
new file mode 100644
index 0000000..f4e0d0b
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm.c
@@ -0,0 +1,2524 @@
+/* See LICENSE file for copyright and license details.
+ *
+ * dynamic window manager is designed like any other X client as well. It is
+ * driven through handling X events. In contrast to other X clients, a window
+ * manager selects for SubstructureRedirectMask on the root window, to receive
+ * events about window (dis-)appearance. Only one X connection at a time is
+ * allowed to select for this event mask.
+ *
+ * The event handlers of dwm are organized in an array which is accessed
+ * whenever a new event has been fetched. This allows event dispatching
+ * in O(1) time.
+ *
+ * Each child of the root window is called a client, except windows which have
+ * set the override_redirect flag. Clients are organized in a linked client
+ * list on each monitor, the focus history is remembered through a stack list
+ * on each monitor. Each client contains a bit array to indicate the tags of a
+ * client.
+ *
+ * Keys and tagging rules are organized as arrays and defined in config.h.
+ *
+ * To understand everything else, start reading main().
+ */
+#include <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif /* XINERAMA */
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+/* macros */
+#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
+#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
+#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
+ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
+#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
+#define WIDTH(X) ((X)->w + 2 * (X)->bw)
+#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
+#define TAGMASK ((1 << LENGTH(tags)) - 1)
+#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+
+/* XEMBED messages */
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_IN 4
+#define XEMBED_MODALITY_ON 10
+
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_WINDOW_DEACTIVATE 2
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 0
+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
+
+/* enums */
+enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+typedef union {
+ int i;
+ unsigned int ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int click;
+ unsigned int mask;
+ unsigned int button;
+ void (*func)(const Arg *arg);
+ const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct Client Client;
+struct Client {
+ char name[256];
+ float mina, maxa;
+ int x, y, w, h;
+ int oldx, oldy, oldw, oldh;
+ int basew, baseh, incw, inch, maxw, maxh, minw, minh;
+ int bw, oldbw;
+ unsigned int tags;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+ Window win;
+};
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+} Layout;
+
+struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ int gappx; /* gaps between windows */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ int showbar;
+ int topbar;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
+ const Layout *lt[2];
+};
+
+typedef struct {
+ const char *class;
+ const char *instance;
+ const char *title;
+ unsigned int tags;
+ int isfloating;
+ int monitor;
+} Rule;
+
+typedef struct Systray Systray;
+struct Systray {
+ Window win;
+ Client *icons;
+};
+
+/* function declarations */
+static void applyrules(Client *c);
+static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+static void arrange(Monitor *m);
+static void arrangemon(Monitor *m);
+static void attach(Client *c);
+static void attachstack(Client *c);
+static void buttonpress(XEvent *e);
+static void checkotherwm(void);
+static void cleanup(void);
+static void cleanupmon(Monitor *mon);
+static void clientmessage(XEvent *e);
+static void configure(Client *c);
+static void configurenotify(XEvent *e);
+static void configurerequest(XEvent *e);
+static Monitor *createmon(void);
+static void destroynotify(XEvent *e);
+static void detach(Client *c);
+static void detachstack(Client *c);
+static Monitor *dirtomon(int dir);
+static void drawbar(Monitor *m);
+static void drawbars(void);
+static void enternotify(XEvent *e);
+static void expose(XEvent *e);
+static void focus(Client *c);
+static void focusin(XEvent *e);
+static void focusmon(const Arg *arg);
+static void focusstack(const Arg *arg);
+static Atom getatomprop(Client *c, Atom prop);
+static int getrootptr(int *x, int *y);
+static long getstate(Window w);
+static unsigned int getsystraywidth();
+static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
+static void grabbuttons(Client *c, int focused);
+static void grabkeys(void);
+static void incnmaster(const Arg *arg);
+static void keypress(XEvent *e);
+static void killclient(const Arg *arg);
+static void manage(Window w, XWindowAttributes *wa);
+static void mappingnotify(XEvent *e);
+static void maprequest(XEvent *e);
+static void monocle(Monitor *m);
+static void motionnotify(XEvent *e);
+static void movemouse(const Arg *arg);
+static Client *nexttiled(Client *c);
+static void pop(Client *);
+static void propertynotify(XEvent *e);
+static void quit(const Arg *arg);
+static Monitor *recttomon(int x, int y, int w, int h);
+static void removesystrayicon(Client *i);
+static void resize(Client *c, int x, int y, int w, int h, int interact);
+static void resizebarwin(Monitor *m);
+static void resizeclient(Client *c, int x, int y, int w, int h);
+static void resizemouse(const Arg *arg);
+static void resizerequest(XEvent *e);
+static void restack(Monitor *m);
+static void run(void);
+static void runAutostart(void);
+static void scan(void);
+static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+static void sendmon(Client *c, Monitor *m);
+static void setclientstate(Client *c, long state);
+static void setfocus(Client *c);
+static void setfullscreen(Client *c, int fullscreen);
+static void setgaps(const Arg *arg);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setup(void);
+static void seturgent(Client *c, int urg);
+static void showhide(Client *c);
+static void sigchld(int unused);
+static void spawn(const Arg *arg);
+static Monitor *systraytomon(Monitor *m);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *);
+static void togglebar(const Arg *arg);
+static void togglefloating(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void unfocus(Client *c, int setfocus);
+static void unmanage(Client *c, int destroyed);
+static void unmapnotify(XEvent *e);
+static void updatebarpos(Monitor *m);
+static void updatebars(void);
+static void updateclientlist(void);
+static int updategeom(void);
+static void updatenumlockmask(void);
+static void updatesizehints(Client *c);
+static void updatestatus(void);
+static void updatesystray(void);
+static void updatesystrayicongeom(Client *i, int w, int h);
+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+static void updatetitle(Client *c);
+static void updatewindowtype(Client *c);
+static void updatewmhints(Client *c);
+static void view(const Arg *arg);
+static Client *wintoclient(Window w);
+static Monitor *wintomon(Window w);
+static Client *wintosystrayicon(Window w);
+static int xerror(Display *dpy, XErrorEvent *ee);
+static int xerrordummy(Display *dpy, XErrorEvent *ee);
+static int xerrorstart(Display *dpy, XErrorEvent *ee);
+static void zoom(const Arg *arg);
+
+/* variables */
+static Systray *systray = NULL;
+static const char broken[] = "broken";
+static char stext[256];
+static int screen;
+static int sw, sh; /* X display screen geometry width, height */
+static int bh, blw = 0; /* bar geometry */
+static int lrpad; /* sum of left and right padding for text */
+static int (*xerrorxlib)(Display *, XErrorEvent *);
+static unsigned int numlockmask = 0;
+static void (*handler[LASTEvent]) (XEvent *) = {
+ [ButtonPress] = buttonpress,
+ [ClientMessage] = clientmessage,
+ [ConfigureRequest] = configurerequest,
+ [ConfigureNotify] = configurenotify,
+ [DestroyNotify] = destroynotify,
+ //[EnterNotify] = enternotify,
+ [Expose] = expose,
+ [FocusIn] = focusin,
+ [KeyPress] = keypress,
+ [MappingNotify] = mappingnotify,
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
+ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+};
+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+static int running = 1;
+static Cur *cursor[CurLast];
+static Clr **scheme;
+static Display *dpy;
+static Drw *drw;
+static Monitor *mons, *selmon;
+static Window root, wmcheckwin;
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* compile-time check if all tags fit into an unsigned int bit array. */
+struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+/* function implementations */
+void
+applyrules(Client *c)
+{
+ const char *class, *instance;
+ unsigned int i;
+ const Rule *r;
+ Monitor *m;
+ XClassHint ch = { NULL, NULL };
+
+ /* rule matching */
+ c->isfloating = 0;
+ c->tags = 0;
+ XGetClassHint(dpy, c->win, &ch);
+ class = ch.res_class ? ch.res_class : broken;
+ instance = ch.res_name ? ch.res_name : broken;
+
+ for (i = 0; i < LENGTH(rules); i++) {
+ r = &rules[i];
+ if ((!r->title || strstr(c->name, r->title))
+ && (!r->class || strstr(class, r->class))
+ && (!r->instance || strstr(instance, r->instance)))
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+ if (m)
+ c->mon = m;
+ }
+ }
+ if (ch.res_class)
+ XFree(ch.res_class);
+ if (ch.res_name)
+ XFree(ch.res_name);
+ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
+}
+
+int
+applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
+{
+ int baseismin;
+ Monitor *m = c->mon;
+
+ /* set minimum possible */
+ *w = MAX(1, *w);
+ *h = MAX(1, *h);
+ if (interact) {
+ if (*x > sw)
+ *x = sw - WIDTH(c);
+ if (*y > sh)
+ *y = sh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw < 0)
+ *x = 0;
+ if (*y + *h + 2 * c->bw < 0)
+ *y = 0;
+ } else {
+ if (*x >= m->wx + m->ww)
+ *x = m->wx + m->ww - WIDTH(c);
+ if (*y >= m->wy + m->wh)
+ *y = m->wy + m->wh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw <= m->wx)
+ *x = m->wx;
+ if (*y + *h + 2 * c->bw <= m->wy)
+ *y = m->wy;
+ }
+ if (*h < bh)
+ *h = bh;
+ if (*w < bh)
+ *w = bh;
+ if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ /* see last two sentences in ICCCM 4.1.2.3 */
+ baseismin = c->basew == c->minw && c->baseh == c->minh;
+ if (!baseismin) { /* temporarily remove base dimensions */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for aspect limits */
+ if (c->mina > 0 && c->maxa > 0) {
+ if (c->maxa < (float)*w / *h)
+ *w = *h * c->maxa + 0.5;
+ else if (c->mina < (float)*h / *w)
+ *h = *w * c->mina + 0.5;
+ }
+ if (baseismin) { /* increment calculation requires this */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for increment value */
+ if (c->incw)
+ *w -= *w % c->incw;
+ if (c->inch)
+ *h -= *h % c->inch;
+ /* restore base dimensions */
+ *w = MAX(*w + c->basew, c->minw);
+ *h = MAX(*h + c->baseh, c->minh);
+ if (c->maxw)
+ *w = MIN(*w, c->maxw);
+ if (c->maxh)
+ *h = MIN(*h, c->maxh);
+ }
+ return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
+}
+
+void
+arrange(Monitor *m)
+{
+ if (m)
+ showhide(m->stack);
+ else for (m = mons; m; m = m->next)
+ showhide(m->stack);
+ if (m) {
+ arrangemon(m);
+ restack(m);
+ } else for (m = mons; m; m = m->next)
+ arrangemon(m);
+}
+
+void
+arrangemon(Monitor *m)
+{
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+}
+
+void
+attach(Client *c)
+{
+ c->next = c->mon->clients;
+ c->mon->clients = c;
+}
+
+void
+attachstack(Client *c)
+{
+ c->snext = c->mon->stack;
+ c->mon->stack = c;
+}
+
+void
+buttonpress(XEvent *e)
+{
+ unsigned int i, x, click;
+ Arg arg = {0};
+ Client *c;
+ Monitor *m;
+ XButtonPressedEvent *ev = &e->xbutton;
+
+ click = ClkRootWin;
+ /* focus monitor if necessary */
+ if ((m = wintomon(ev->window)) && m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ if (ev->window == selmon->barwin) {
+ i = x = 0;
+ do
+ x += TEXTW(tags[i]);
+ while (ev->x >= x && ++i < LENGTH(tags));
+ if (i < LENGTH(tags)) {
+ click = ClkTagBar;
+ arg.ui = 1 << i;
+ } else if (ev->x < x + blw)
+ click = ClkLtSymbol;
+ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+ } else if ((c = wintoclient(ev->window))) {
+ focus(c);
+ restack(selmon);
+ XAllowEvents(dpy, ReplayPointer, CurrentTime);
+ click = ClkClientWin;
+ }
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+ buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+}
+
+void
+checkotherwm(void)
+{
+ xerrorxlib = XSetErrorHandler(xerrorstart);
+ /* this causes an error if some other window manager is running */
+ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XSync(dpy, False);
+}
+
+void
+cleanup(void)
+{
+ Arg a = {.ui = ~0};
+ Layout foo = { "", NULL };
+ Monitor *m;
+ size_t i;
+
+ view(&a);
+ selmon->lt[selmon->sellt] = &foo;
+ for (m = mons; m; m = m->next)
+ while (m->stack)
+ unmanage(m->stack, 0);
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while (mons)
+ cleanupmon(mons);
+ if (showsystray) {
+ XUnmapWindow(dpy, systray->win);
+ XDestroyWindow(dpy, systray->win);
+ free(systray);
+ }
+ for (i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for (i = 0; i < LENGTH(colors); i++)
+ free(scheme[i]);
+ XDestroyWindow(dpy, wmcheckwin);
+ drw_free(drw);
+ XSync(dpy, False);
+ XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+}
+
+void
+cleanupmon(Monitor *mon)
+{
+ Monitor *m;
+
+ if (mon == mons)
+ mons = mons->next;
+ else {
+ for (m = mons; m && m->next != mon; m = m->next);
+ m->next = mon->next;
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
+ free(mon);
+}
+
+void
+clientmessage(XEvent *e)
+{
+ XWindowAttributes wa;
+ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
+ /* add systray icons */
+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
+ if (!(c = (Client *)calloc(1, sizeof(Client))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
+ if (!(c->win = cme->data.l[2])) {
+ free(c);
+ return;
+ }
+ c->mon = selmon;
+ c->next = systray->icons;
+ systray->icons = c;
+ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
+ /* use sane defaults */
+ wa.width = bh;
+ wa.height = bh;
+ wa.border_width = 0;
+ }
+ c->x = c->oldx = c->y = c->oldy = 0;
+ c->w = c->oldw = wa.width;
+ c->h = c->oldh = wa.height;
+ c->oldbw = wa.border_width;
+ c->bw = 0;
+ c->isfloating = True;
+ /* reuse tags field as mapped status */
+ c->tags = 1;
+ updatesizehints(c);
+ updatesystrayicongeom(c, wa.width, wa.height);
+ XAddToSaveSet(dpy, c->win);
+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
+ XClassHint ch = {"dwmsystray", "dwmsystray"};
+ XSetClassHint(dpy, c->win, &ch);
+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
+ /* use parents background color */
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ /* FIXME not sure if I have to send these events, too */
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ XSync(dpy, False);
+ resizebarwin(selmon);
+ updatesystray();
+ setclientstate(c, NormalState);
+ }
+ return;
+ }
+ if (!c)
+ return;
+ if (cme->message_type == netatom[NetWMState]) {
+ if (cme->data.l[1] == netatom[NetWMFullscreen]
+ || cme->data.l[2] == netatom[NetWMFullscreen])
+ setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
+ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
+ } else if (cme->message_type == netatom[NetActiveWindow]) {
+ if (c != selmon->sel && !c->isurgent)
+ seturgent(c, 1);
+ }
+}
+
+void
+configure(Client *c)
+{
+ XConfigureEvent ce;
+
+ ce.type = ConfigureNotify;
+ ce.display = dpy;
+ ce.event = c->win;
+ ce.window = c->win;
+ ce.x = c->x;
+ ce.y = c->y;
+ ce.width = c->w;
+ ce.height = c->h;
+ ce.border_width = c->bw;
+ ce.above = None;
+ ce.override_redirect = False;
+ XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
+}
+
+void
+configurenotify(XEvent *e)
+{
+ Monitor *m;
+ Client *c;
+ XConfigureEvent *ev = &e->xconfigure;
+ int dirty;
+
+ /* TODO: updategeom handling sucks, needs to be simplified */
+ if (ev->window == root) {
+ dirty = (sw != ev->width || sh != ev->height);
+ sw = ev->width;
+ sh = ev->height;
+ if (updategeom() || dirty) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next)
+ if (c->isfullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+ resizebarwin(m);
+ }
+ focus(NULL);
+ arrange(NULL);
+ }
+ }
+}
+
+void
+configurerequest(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XConfigureRequestEvent *ev = &e->xconfigurerequest;
+ XWindowChanges wc;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->value_mask & CWBorderWidth)
+ c->bw = ev->border_width;
+ else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
+ m = c->mon;
+ if (ev->value_mask & CWX) {
+ c->oldx = c->x;
+ c->x = m->mx + ev->x;
+ }
+ if (ev->value_mask & CWY) {
+ c->oldy = c->y;
+ c->y = m->my + ev->y;
+ }
+ if (ev->value_mask & CWWidth) {
+ c->oldw = c->w;
+ c->w = ev->width;
+ }
+ if (ev->value_mask & CWHeight) {
+ c->oldh = c->h;
+ c->h = ev->height;
+ }
+ if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
+ c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
+ if ((c->y + c->h) > m->my + m->mh && c->isfloating)
+ c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
+ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
+ configure(c);
+ if (ISVISIBLE(c))
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ } else
+ configure(c);
+ } else {
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+ wc.border_width = ev->border_width;
+ wc.sibling = ev->above;
+ wc.stack_mode = ev->detail;
+ XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
+ }
+ XSync(dpy, False);
+}
+
+Monitor *
+createmon(void)
+{
+ Monitor *m;
+
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+ m->topbar = topbar;
+ m->gappx = gappx;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ return m;
+}
+
+void
+destroynotify(XEvent *e)
+{
+ Client *c;
+ XDestroyWindowEvent *ev = &e->xdestroywindow;
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
+ else if ((c = wintosystrayicon(ev->window))) {
+ removesystrayicon(c);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+detach(Client *c)
+{
+ Client **tc;
+
+ for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
+ *tc = c->next;
+}
+
+void
+detachstack(Client *c)
+{
+ Client **tc, *t;
+
+ for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
+ *tc = c->snext;
+
+ if (c == c->mon->sel) {
+ for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
+ c->mon->sel = t;
+ }
+}
+
+Monitor *
+dirtomon(int dir)
+{
+ Monitor *m = NULL;
+
+ if (dir > 0) {
+ if (!(m = selmon->next))
+ m = mons;
+ } else if (selmon == mons)
+ for (m = mons; m->next; m = m->next);
+ else
+ for (m = mons; m->next != selmon; m = m->next);
+ return m;
+}
+
+void
+drawbar(Monitor *m)
+{
+ int x, w, sw = 0, stw = 0;
+ int boxs = drw->fonts->h / 9;
+ int boxw = drw->fonts->h / 6 + 2;
+ unsigned int i, occ = 0, urg = 0;
+ Client *c;
+
+ if(showsystray && m == systraytomon(m))
+ stw = getsystraywidth();
+
+ /* draw status first so it can be overdrawn by tags later */
+ if (m == selmon) { /* status is only drawn on selected monitor */
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
+ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
+ resizebarwin(m);
+ for (c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ x = 0;
+ for (i = 0; i < LENGTH(tags); i++) {
+ w = TEXTW(tags[i]);
+ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+ if (occ & 1 << i)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw,
+ m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+ urg & 1 << i);
+ x += w;
+ }
+ w = blw = TEXTW(m->ltsymbol);
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+ if ((w = m->ww - sw - stw - x) > bh) {
+ if (m->sel) {
+ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+ if (m->sel->isfloating)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
+ } else {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ }
+ }
+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
+}
+
+void
+drawbars(void)
+{
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ drawbar(m);
+}
+
+void
+enternotify(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XCrossingEvent *ev = &e->xcrossing;
+
+ if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
+ return;
+ c = wintoclient(ev->window);
+ m = c ? c->mon : wintomon(ev->window);
+ if (m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ } else if (!c || c == selmon->sel)
+ return;
+ focus(c);
+}
+
+void
+expose(XEvent *e)
+{
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+ if (ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
+ if (m == selmon)
+ updatesystray();
+ }
+}
+
+void
+focus(Client *c)
+{
+ if (!c || !ISVISIBLE(c))
+ for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
+ if (selmon->sel && selmon->sel != c)
+ unfocus(selmon->sel, 0);
+ if (c) {
+ if (c->mon != selmon)
+ selmon = c->mon;
+ if (c->isurgent)
+ seturgent(c, 0);
+ detachstack(c);
+ attachstack(c);
+ grabbuttons(c, 1);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
+ setfocus(c);
+ } else {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+ selmon->sel = c;
+ drawbars();
+}
+
+/* there are some broken focus acquiring clients needing extra handling */
+void
+focusin(XEvent *e)
+{
+ XFocusChangeEvent *ev = &e->xfocus;
+
+ if (selmon->sel && ev->window != selmon->sel->win)
+ setfocus(selmon->sel);
+}
+
+void
+focusmon(const Arg *arg)
+{
+ Monitor *m;
+
+ if (!mons->next)
+ return;
+ if ((m = dirtomon(arg->i)) == selmon)
+ return;
+ unfocus(selmon->sel, 0);
+ selmon = m;
+ focus(NULL);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ Client *c = NULL, *i;
+
+ if (!selmon->sel)
+ return;
+ if (arg->i > 0) {
+ for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
+ if (!c)
+ for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
+ } else {
+ for (i = selmon->clients; i != selmon->sel; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ if (!c)
+ for (; i; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ }
+ if (c) {
+ focus(c);
+ restack(selmon);
+ }
+}
+
+Atom
+getatomprop(Client *c, Atom prop)
+{
+ int di;
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
+ /* FIXME getatomprop should return the number of items and a pointer to
+ * the stored data instead of this workaround */
+ Atom req = XA_ATOM;
+ if (prop == xatom[XembedInfo])
+ req = xatom[XembedInfo];
+
+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
+ if (da == xatom[XembedInfo] && dl == 2)
+ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+}
+
+int
+getrootptr(int *x, int *y)
+{
+ int di;
+ unsigned int dui;
+ Window dummy;
+
+ return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
+}
+
+long
+getstate(Window w)
+{
+ int format;
+ long result = -1;
+ unsigned char *p = NULL;
+ unsigned long n, extra;
+ Atom real;
+
+ if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
+ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
+ return -1;
+ if (n != 0)
+ result = *p;
+ XFree(p);
+ return result;
+}
+
+unsigned int
+getsystraywidth()
+{
+ unsigned int w = 0;
+ Client *i;
+ if(showsystray)
+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
+ return w ? w + systrayspacing : 1;
+}
+
+int
+gettextprop(Window w, Atom atom, char *text, unsigned int size)
+{
+ char **list = NULL;
+ int n;
+ XTextProperty name;
+
+ if (!text || size == 0)
+ return 0;
+ text[0] = '\0';
+ if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
+ return 0;
+ if (name.encoding == XA_STRING)
+ strncpy(text, (char *)name.value, size - 1);
+ else {
+ if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
+ strncpy(text, *list, size - 1);
+ XFreeStringList(list);
+ }
+ }
+ text[size - 1] = '\0';
+ XFree(name.value);
+ return 1;
+}
+
+void
+grabbuttons(Client *c, int focused)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ if (!focused)
+ XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
+ BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].click == ClkClientWin)
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabButton(dpy, buttons[i].button,
+ buttons[i].mask | modifiers[j],
+ c->win, False, BUTTONMASK,
+ GrabModeAsync, GrabModeSync, None, None);
+ }
+}
+
+void
+grabkeys(void)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ KeyCode code;
+
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ for (i = 0; i < LENGTH(keys); i++)
+ if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
+ True, GrabModeAsync, GrabModeAsync);
+ }
+}
+
+void
+incnmaster(const Arg *arg)
+{
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
+#ifdef XINERAMA
+static int
+isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
+{
+ while (n--)
+ if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
+ && unique[n].width == info->width && unique[n].height == info->height)
+ return 0;
+ return 1;
+}
+#endif /* XINERAMA */
+
+void
+keypress(XEvent *e)
+{
+ unsigned int i;
+ KeySym keysym;
+ XKeyEvent *ev;
+
+ ev = &e->xkey;
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
+ for (i = 0; i < LENGTH(keys); i++)
+ if (keysym == keys[i].keysym
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+ && keys[i].func)
+ keys[i].func(&(keys[i].arg));
+}
+
+void
+killclient(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+ XKillClient(dpy, selmon->sel->win);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+}
+
+void
+manage(Window w, XWindowAttributes *wa)
+{
+ Client *c, *t = NULL;
+ Window trans = None;
+ XWindowChanges wc;
+
+ c = ecalloc(1, sizeof(Client));
+ c->win = w;
+ /* geometry */
+ c->x = c->oldx = wa->x;
+ c->y = c->oldy = wa->y;
+ c->w = c->oldw = wa->width;
+ c->h = c->oldh = wa->height;
+ c->oldbw = wa->border_width;
+
+ updatetitle(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+ c->mon = t->mon;
+ c->tags = t->tags;
+ } else {
+ c->mon = selmon;
+ applyrules(c);
+ }
+
+ if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
+ c->x = c->mon->mx + c->mon->mw - WIDTH(c);
+ if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
+ c->y = c->mon->my + c->mon->mh - HEIGHT(c);
+ c->x = MAX(c->x, c->mon->mx);
+ /* only fix client y-offset, if the client center might cover the bar */
+ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
+ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
+ c->bw = borderpx;
+
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, w, CWBorderWidth, &wc);
+ XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
+ configure(c); /* propagates border_width, if size doesn't change */
+ updatewindowtype(c);
+ updatesizehints(c);
+ updatewmhints(c);
+ XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
+ grabbuttons(c, 0);
+ if (!c->isfloating)
+ c->isfloating = c->oldstate = trans != None || c->isfixed;
+ if (c->isfloating)
+ XRaiseWindow(dpy, c->win);
+ attach(c);
+ attachstack(c);
+ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+ XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
+ setclientstate(c, NormalState);
+ if (c->mon == selmon)
+ unfocus(selmon->sel, 0);
+ c->mon->sel = c;
+ arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ focus(NULL);
+}
+
+void
+mappingnotify(XEvent *e)
+{
+ XMappingEvent *ev = &e->xmapping;
+
+ XRefreshKeyboardMapping(ev);
+ if (ev->request == MappingKeyboard)
+ grabkeys();
+}
+
+void
+maprequest(XEvent *e)
+{
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
+ Client *i;
+ if ((i = wintosystrayicon(ev->window))) {
+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+
+ if (!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+ if (wa.override_redirect)
+ return;
+ if (!wintoclient(ev->window))
+ manage(ev->window, &wa);
+}
+
+void
+monocle(Monitor *m)
+{
+ unsigned int n = 0;
+ Client *c;
+
+ for (c = m->clients; c; c = c->next)
+ if (ISVISIBLE(c))
+ n++;
+ if (n > 0) /* override layout symbol */
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+ for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
+ resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
+}
+
+void
+motionnotify(XEvent *e)
+{
+ static Monitor *mon = NULL;
+ Monitor *m;
+ XMotionEvent *ev = &e->xmotion;
+
+ if (ev->window != root)
+ return;
+ if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ mon = m;
+}
+
+void
+movemouse(const Arg *arg)
+{
+ int x, y, ocx, ocy, nx, ny;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ if (!getrootptr(&x, &y))
+ return;
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nx = ocx + (ev.xmotion.x - x);
+ ny = ocy + (ev.xmotion.y - y);
+ if (abs(selmon->wx - nx) < snap)
+ nx = selmon->wx;
+ else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
+ nx = selmon->wx + selmon->ww - WIDTH(c);
+ if (abs(selmon->wy - ny) < snap)
+ ny = selmon->wy;
+ else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
+ ny = selmon->wy + selmon->wh - HEIGHT(c);
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
+ togglefloating(NULL);
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, nx, ny, c->w, c->h, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XUngrabPointer(dpy, CurrentTime);
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+Client *
+nexttiled(Client *c)
+{
+ for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
+ return c;
+}
+
+void
+pop(Client *c)
+{
+ detach(c);
+ attach(c);
+ focus(c);
+ arrange(c->mon);
+}
+
+void
+propertynotify(XEvent *e)
+{
+ Client *c;
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
+ if ((c = wintosystrayicon(ev->window))) {
+ if (ev->atom == XA_WM_NORMAL_HINTS) {
+ updatesizehints(c);
+ updatesystrayicongeom(c, c->w, c->h);
+ }
+ else
+ updatesystrayiconstate(c, ev);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+ if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if (ev->state == PropertyDelete)
+ return; /* ignore */
+ else if ((c = wintoclient(ev->window))) {
+ switch(ev->atom) {
+ default: break;
+ case XA_WM_TRANSIENT_FOR:
+ if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
+ (c->isfloating = (wintoclient(trans)) != NULL))
+ arrange(c->mon);
+ break;
+ case XA_WM_NORMAL_HINTS:
+ updatesizehints(c);
+ break;
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
+ break;
+ }
+ if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if (c == c->mon->sel)
+ drawbar(c->mon);
+ }
+ if (ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+ }
+}
+
+void
+quit(const Arg *arg)
+{
+ running = 0;
+}
+
+Monitor *
+recttomon(int x, int y, int w, int h)
+{
+ Monitor *m, *r = selmon;
+ int a, area = 0;
+
+ for (m = mons; m; m = m->next)
+ if ((a = INTERSECT(x, y, w, h, m)) > area) {
+ area = a;
+ r = m;
+ }
+ return r;
+}
+
+void
+removesystrayicon(Client *i)
+{
+ Client **ii;
+
+ if (!showsystray || !i)
+ return;
+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
+ if (ii)
+ *ii = i->next;
+ free(i);
+}
+
+
+void
+resize(Client *c, int x, int y, int w, int h, int interact)
+{
+ if (applysizehints(c, &x, &y, &w, &h, interact))
+ resizeclient(c, x, y, w, h);
+}
+
+void
+resizebarwin(Monitor *m) {
+ unsigned int w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
+}
+
+void
+resizeclient(Client *c, int x, int y, int w, int h)
+{
+ XWindowChanges wc;
+
+ c->oldx = c->x; c->x = wc.x = x;
+ c->oldy = c->y; c->y = wc.y = y;
+ c->oldw = c->w; c->w = wc.width = w;
+ c->oldh = c->h; c->h = wc.height = h;
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
+ configure(c);
+ XSync(dpy, False);
+}
+
+void
+resizemouse(const Arg *arg)
+{
+ int ocx, ocy, nw, nh;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
+ nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
+ if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
+ && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
+ {
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
+ togglefloating(NULL);
+ }
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, c->x, c->y, nw, nh, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ XUngrabPointer(dpy, CurrentTime);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+void
+resizerequest(XEvent *e)
+{
+ XResizeRequestEvent *ev = &e->xresizerequest;
+ Client *i;
+
+ if ((i = wintosystrayicon(ev->window))) {
+ updatesystrayicongeom(i, ev->width, ev->height);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+restack(Monitor *m)
+{
+ Client *c;
+ XEvent ev;
+ XWindowChanges wc;
+
+ drawbar(m);
+ if (!m->sel)
+ return;
+ if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
+ XRaiseWindow(dpy, m->sel->win);
+ if (m->lt[m->sellt]->arrange) {
+ wc.stack_mode = Below;
+ wc.sibling = m->barwin;
+ for (c = m->stack; c; c = c->snext)
+ if (!c->isfloating && ISVISIBLE(c)) {
+ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
+ wc.sibling = c->win;
+ }
+ }
+ XSync(dpy, False);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+}
+
+void
+run(void)
+{
+ XEvent ev;
+ /* main event loop */
+ XSync(dpy, False);
+ while (running && !XNextEvent(dpy, &ev))
+ if (handler[ev.type])
+ handler[ev.type](&ev); /* call handler */
+}
+
+void
+runAutostart(void) {
+ system("cd ~/.config/dwm; ./autostart_blocking.sh");
+ system("cd ~/.config/dwm; ./autostart.sh &");
+}
+
+void
+scan(void)
+{
+ unsigned int i, num;
+ Window d1, d2, *wins = NULL;
+ XWindowAttributes wa;
+
+ if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
+ for (i = 0; i < num; i++) {
+ if (!XGetWindowAttributes(dpy, wins[i], &wa)
+ || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
+ continue;
+ if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
+ manage(wins[i], &wa);
+ }
+ for (i = 0; i < num; i++) { /* now the transients */
+ if (!XGetWindowAttributes(dpy, wins[i], &wa))
+ continue;
+ if (XGetTransientForHint(dpy, wins[i], &d1)
+ && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
+ manage(wins[i], &wa);
+ }
+ if (wins)
+ XFree(wins);
+ }
+}
+
+void
+sendmon(Client *c, Monitor *m)
+{
+ if (c->mon == m)
+ return;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->mon = m;
+ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ focus(NULL);
+ arrange(NULL);
+}
+
+void
+setclientstate(Client *c, long state)
+{
+ long data[] = { state, None };
+
+ XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
+ PropModeReplace, (unsigned char *)data, 2);
+}
+
+int
+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
+{
+ int n;
+ Atom *protocols, mt;
+ int exists = 0;
+ XEvent ev;
+
+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
+ mt = wmatom[WMProtocols];
+ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
+ while (!exists && n--)
+ exists = protocols[n] == proto;
+ XFree(protocols);
+ }
+ }
+ else {
+ exists = True;
+ mt = proto;
+ }
+ if (exists) {
+ ev.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = d0;
+ ev.xclient.data.l[1] = d1;
+ ev.xclient.data.l[2] = d2;
+ ev.xclient.data.l[3] = d3;
+ ev.xclient.data.l[4] = d4;
+ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+}
+
+void
+setfocus(Client *c)
+{
+ if (!c->neverfocus) {
+ XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
+ XChangeProperty(dpy, root, netatom[NetActiveWindow],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+ if (fullscreen && !c->isfullscreen) {
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
+ c->isfullscreen = 1;
+ c->oldstate = c->isfloating;
+ c->oldbw = c->bw;
+ c->bw = 0;
+ c->isfloating = 1;
+ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
+ XRaiseWindow(dpy, c->win);
+ } else if (!fullscreen && c->isfullscreen){
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)0, 0);
+ c->isfullscreen = 0;
+ c->isfloating = c->oldstate;
+ c->bw = c->oldbw;
+ c->x = c->oldx;
+ c->y = c->oldy;
+ c->w = c->oldw;
+ c->h = c->oldh;
+ resizeclient(c, c->x, c->y, c->w, c->h);
+ arrange(c->mon);
+ }
+}
+
+void
+setgaps(const Arg *arg)
+{
+ if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
+ selmon->gappx = 0;
+ else
+ selmon->gappx += arg->i;
+ arrange(selmon);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if (selmon->sel)
+ arrange(selmon);
+ else
+ drawbar(selmon);
+}
+
+/* arg > 1.0 will set mfact absolutely */
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !selmon->lt[selmon->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ selmon->mfact = f;
+ arrange(selmon);
+}
+
+void
+setup(void)
+{
+ int i;
+ XSetWindowAttributes wa;
+ Atom utf8string;
+
+ /* clean up any zombies immediately */
+ sigchld(0);
+
+ /* init screen */
+ screen = DefaultScreen(dpy);
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
+ root = RootWindow(dpy, screen);
+ drw = drw_create(dpy, screen, root, sw, sh);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+ bh = drw->fonts->h + 2;
+ updategeom();
+ /* init atoms */
+ utf8string = XInternAtom(dpy, "UTF8_STRING", False);
+ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+ netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+ cursor[CurMove] = drw_cur_create(drw, XC_fleur);
+ /* init appearance */
+ scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
+ for (i = 0; i < LENGTH(colors); i++)
+ scheme[i] = drw_scm_create(drw, colors[i], 3);
+ /* init system tray */
+ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+ /* supporting window for NetWMCheck */
+ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
+ PropModeReplace, (unsigned char *) "dwm", 3);
+ XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ /* EWMH support per view */
+ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) netatom, NetLast);
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ /* select events */
+ wa.cursor = cursor[CurNormal]->cursor;
+ wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
+ |ButtonPressMask|PointerMotionMask|EnterWindowMask
+ |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
+ XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
+ XSelectInput(dpy, root, wa.event_mask);
+ grabkeys();
+ focus(NULL);
+}
+
+
+void
+seturgent(Client *c, int urg)
+{
+ XWMHints *wmh;
+
+ c->isurgent = urg;
+ if (!(wmh = XGetWMHints(dpy, c->win)))
+ return;
+ wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
+ XSetWMHints(dpy, c->win, wmh);
+ XFree(wmh);
+}
+
+void
+showhide(Client *c)
+{
+ if (!c)
+ return;
+ if (ISVISIBLE(c)) {
+ /* show clients top down */
+ XMoveWindow(dpy, c->win, c->x, c->y);
+ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+ resize(c, c->x, c->y, c->w, c->h, 0);
+ showhide(c->snext);
+ } else {
+ /* hide clients bottom up */
+ showhide(c->snext);
+ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
+ }
+}
+
+void
+sigchld(int unused)
+{
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("can't install SIGCHLD handler:");
+ while (0 < waitpid(-1, NULL, WNOHANG));
+}
+
+void
+spawn(const Arg *arg)
+{
+ if (arg->v == dmenucmd)
+ dmenumon[0] = '0' + selmon->num;
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ setsid();
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
+ perror(" failed");
+ exit(EXIT_SUCCESS);
+ }
+}
+
+void
+tag(const Arg *arg)
+{
+ if (selmon->sel && arg->ui & TAGMASK) {
+ selmon->sel->tags = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+tagmon(const Arg *arg)
+{
+ if (!selmon->sel || !mons->next)
+ return;
+ sendmon(selmon->sel, dirtomon(arg->i));
+}
+
+void
+tile(Monitor *m)
+{
+ unsigned int i, n, h, mw, my, ty;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (n == 0)
+ return;
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->ww * m->mfact : 0;
+ else
+ mw = m->ww - m->gappx;
+ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
+ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
+ my += HEIGHT(c) + m->gappx;
+ } else {
+ h = (m->wh - ty) / (n - i) - m->gappx;
+ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
+ ty += HEIGHT(c) + m->gappx;
+ }
+}
+
+void
+togglebar(const Arg *arg)
+{
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+ resizebarwin(selmon);
+ if (showsystray) {
+ XWindowChanges wc;
+ if (!selmon->showbar)
+ wc.y = -bh;
+ else if (selmon->showbar) {
+ wc.y = 0;
+ if (!selmon->topbar)
+ wc.y = selmon->mh - bh;
+ }
+ XConfigureWindow(dpy, systray->win, CWY, &wc);
+ }
+ arrange(selmon);
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
+ return;
+ selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
+ if (selmon->sel->isfloating)
+ resize(selmon->sel, selmon->sel->x, selmon->sel->y,
+ selmon->sel->w, selmon->sel->h, 0);
+ arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ unsigned int newtags;
+
+ if (!selmon->sel)
+ return;
+ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
+ selmon->sel->tags = newtags;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+toggleview(const Arg *arg)
+{
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
+
+ if (newtagset) {
+ selmon->tagset[selmon->seltags] = newtagset;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+unfocus(Client *c, int setfocus)
+{
+ if (!c)
+ return;
+ grabbuttons(c, 0);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
+ if (setfocus) {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+}
+
+void
+unmanage(Client *c, int destroyed)
+{
+ Monitor *m = c->mon;
+ XWindowChanges wc;
+
+ detach(c);
+ detachstack(c);
+ if (!destroyed) {
+ wc.border_width = c->oldbw;
+ XGrabServer(dpy); /* avoid race conditions */
+ XSetErrorHandler(xerrordummy);
+ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ setclientstate(c, WithdrawnState);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+ free(c);
+ focus(NULL);
+ updateclientlist();
+ arrange(m);
+}
+
+void
+unmapnotify(XEvent *e)
+{
+ Client *c;
+ XUnmapEvent *ev = &e->xunmap;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->send_event)
+ setclientstate(c, WithdrawnState);
+ else
+ unmanage(c, 0);
+ }
+ else if ((c = wintosystrayicon(ev->window))) {
+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
+ * _not_ destroy them. We map those windows back */
+ XMapRaised(dpy, c->win);
+ updatesystray();
+ }
+}
+
+void
+updatebars(void)
+{
+ unsigned int w;
+ Monitor *m;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .background_pixmap = ParentRelative,
+ .event_mask = ButtonPressMask|ExposureMask
+ };
+ XClassHint ch = {"dwm", "dwm"};
+ for (m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+ w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ if (showsystray && m == systraytomon(m))
+ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+}
+
+void
+updatebarpos(Monitor *m)
+{
+ m->wy = m->my;
+ m->wh = m->mh;
+ if (m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+ m->wy = m->topbar ? m->wy + bh : m->wy;
+ } else
+ m->by = -bh;
+}
+
+void
+updateclientlist()
+{
+ Client *c;
+ Monitor *m;
+
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ XChangeProperty(dpy, root, netatom[NetClientList],
+ XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+}
+
+int
+updategeom(void)
+{
+ int dirty = 0;
+
+#ifdef XINERAMA
+ if (XineramaIsActive(dpy)) {
+ int i, j, n, nn;
+ Client *c;
+ Monitor *m;
+ XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
+ XineramaScreenInfo *unique = NULL;
+
+ for (n = 0, m = mons; m; m = m->next, n++);
+ /* only consider unique geometries as separate screens */
+ unique = ecalloc(nn, sizeof(XineramaScreenInfo));
+ for (i = 0, j = 0; i < nn; i++)
+ if (isuniquegeom(unique, j, &info[i]))
+ memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
+ XFree(info);
+ nn = j;
+ if (n <= nn) { /* new monitors available */
+ for (i = 0; i < (nn - n); i++) {
+ for (m = mons; m && m->next; m = m->next);
+ if (m)
+ m->next = createmon();
+ else
+ mons = createmon();
+ }
+ for (i = 0, m = mons; i < nn && m; m = m->next, i++)
+ if (i >= n
+ || unique[i].x_org != m->mx || unique[i].y_org != m->my
+ || unique[i].width != m->mw || unique[i].height != m->mh)
+ {
+ dirty = 1;
+ m->num = i;
+ m->mx = m->wx = unique[i].x_org;
+ m->my = m->wy = unique[i].y_org;
+ m->mw = m->ww = unique[i].width;
+ m->mh = m->wh = unique[i].height;
+ updatebarpos(m);
+ }
+ } else { /* less monitors available nn < n */
+ for (i = nn; i < n; i++) {
+ for (m = mons; m && m->next; m = m->next);
+ while ((c = m->clients)) {
+ dirty = 1;
+ m->clients = c->next;
+ detachstack(c);
+ c->mon = mons;
+ attach(c);
+ attachstack(c);
+ }
+ if (m == selmon)
+ selmon = mons;
+ cleanupmon(m);
+ }
+ }
+ free(unique);
+ } else
+#endif /* XINERAMA */
+ { /* default monitor setup */
+ if (!mons)
+ mons = createmon();
+ if (mons->mw != sw || mons->mh != sh) {
+ dirty = 1;
+ mons->mw = mons->ww = sw;
+ mons->mh = mons->wh = sh;
+ updatebarpos(mons);
+ }
+ }
+ if (dirty) {
+ selmon = mons;
+ selmon = wintomon(root);
+ }
+ return dirty;
+}
+
+void
+updatenumlockmask(void)
+{
+ unsigned int i, j;
+ XModifierKeymap *modmap;
+
+ numlockmask = 0;
+ modmap = XGetModifierMapping(dpy);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < modmap->max_keypermod; j++)
+ if (modmap->modifiermap[i * modmap->max_keypermod + j]
+ == XKeysymToKeycode(dpy, XK_Num_Lock))
+ numlockmask = (1 << i);
+ XFreeModifiermap(modmap);
+}
+
+void
+updatesizehints(Client *c)
+{
+ long msize;
+ XSizeHints size;
+
+ if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
+ /* size is uninitialized, ensure that size.flags aren't used */
+ size.flags = PSize;
+ if (size.flags & PBaseSize) {
+ c->basew = size.base_width;
+ c->baseh = size.base_height;
+ } else if (size.flags & PMinSize) {
+ c->basew = size.min_width;
+ c->baseh = size.min_height;
+ } else
+ c->basew = c->baseh = 0;
+ if (size.flags & PResizeInc) {
+ c->incw = size.width_inc;
+ c->inch = size.height_inc;
+ } else
+ c->incw = c->inch = 0;
+ if (size.flags & PMaxSize) {
+ c->maxw = size.max_width;
+ c->maxh = size.max_height;
+ } else
+ c->maxw = c->maxh = 0;
+ if (size.flags & PMinSize) {
+ c->minw = size.min_width;
+ c->minh = size.min_height;
+ } else if (size.flags & PBaseSize) {
+ c->minw = size.base_width;
+ c->minh = size.base_height;
+ } else
+ c->minw = c->minh = 0;
+ if (size.flags & PAspect) {
+ c->mina = (float)size.min_aspect.y / size.min_aspect.x;
+ c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
+ } else
+ c->maxa = c->mina = 0.0;
+ c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
+}
+
+void
+updatestatus(void)
+{
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ strcpy(stext, "dwm-"VERSION);
+ drawbar(selmon);
+ updatesystray();
+}
+
+void
+updatesystrayicongeom(Client *i, int w, int h)
+{
+ if (i) {
+ i->h = bh;
+ if (w == h)
+ i->w = bh;
+ else if (h == bh)
+ i->w = w;
+ else
+ i->w = (int) ((float)bh * ((float)w / (float)h));
+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
+ /* force icons into the systray dimensions if they don't want to */
+ if (i->h > bh) {
+ if (i->w == i->h)
+ i->w = bh;
+ else
+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
+ i->h = bh;
+ }
+ }
+}
+
+void
+updatesystrayiconstate(Client *i, XPropertyEvent *ev)
+{
+ long flags;
+ int code = 0;
+
+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
+ !(flags = getatomprop(i, xatom[XembedInfo])))
+ return;
+
+ if (flags & XEMBED_MAPPED && !i->tags) {
+ i->tags = 1;
+ code = XEMBED_WINDOW_ACTIVATE;
+ XMapRaised(dpy, i->win);
+ setclientstate(i, NormalState);
+ }
+ else if (!(flags & XEMBED_MAPPED) && i->tags) {
+ i->tags = 0;
+ code = XEMBED_WINDOW_DEACTIVATE;
+ XUnmapWindow(dpy, i->win);
+ setclientstate(i, WithdrawnState);
+ }
+ else
+ return;
+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
+ systray->win, XEMBED_EMBEDDED_VERSION);
+}
+
+void
+updatesystray(void)
+{
+ XSetWindowAttributes wa;
+ XWindowChanges wc;
+ Client *i;
+ Monitor *m = systraytomon(NULL);
+ unsigned int x = m->mx + m->mw;
+ unsigned int w = 1;
+
+ if (!showsystray)
+ return;
+ if (!systray) {
+ /* init systray */
+ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
+ wa.event_mask = ButtonPressMask | ExposureMask;
+ wa.override_redirect = True;
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
+ XMapRaised(dpy, systray->win);
+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
+ XSync(dpy, False);
+ }
+ else {
+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
+ free(systray);
+ systray = NULL;
+ return;
+ }
+ }
+ for (w = 0, i = systray->icons; i; i = i->next) {
+ /* make sure the background color stays the same */
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
+ XMapRaised(dpy, i->win);
+ w += systrayspacing;
+ i->x = w;
+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
+ w += i->w;
+ if (i->mon != m)
+ i->mon = m;
+ }
+ w = w ? w + systrayspacing : 1;
+ x -= w;
+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
+ wc.stack_mode = Above; wc.sibling = m->barwin;
+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
+ XMapWindow(dpy, systray->win);
+ XMapSubwindows(dpy, systray->win);
+ /* redraw background */
+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
+ XSync(dpy, False);
+}
+
+void
+updatetitle(Client *c)
+{
+ if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
+ gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
+ if (c->name[0] == '\0') /* hack to mark broken clients */
+ strcpy(c->name, broken);
+}
+
+void
+updatewindowtype(Client *c)
+{
+ Atom state = getatomprop(c, netatom[NetWMState]);
+ Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
+
+ if (state == netatom[NetWMFullscreen])
+ setfullscreen(c, 1);
+ if (wtype == netatom[NetWMWindowTypeDialog])
+ c->isfloating = 1;
+}
+
+void
+updatewmhints(Client *c)
+{
+ XWMHints *wmh;
+
+ if ((wmh = XGetWMHints(dpy, c->win))) {
+ if (c == selmon->sel && wmh->flags & XUrgencyHint) {
+ wmh->flags &= ~XUrgencyHint;
+ XSetWMHints(dpy, c->win, wmh);
+ } else
+ c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
+ if (wmh->flags & InputHint)
+ c->neverfocus = !wmh->input;
+ else
+ c->neverfocus = 0;
+ XFree(wmh);
+ }
+}
+
+void
+view(const Arg *arg)
+{
+ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+}
+
+Client *
+wintoclient(Window w)
+{
+ Client *c;
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ if (c->win == w)
+ return c;
+ return NULL;
+}
+
+Client *
+wintosystrayicon(Window w) {
+ Client *i = NULL;
+
+ if (!showsystray || !w)
+ return i;
+ for (i = systray->icons; i && i->win != w; i = i->next) ;
+ return i;
+}
+
+Monitor *
+wintomon(Window w)
+{
+ int x, y;
+ Client *c;
+ Monitor *m;
+
+ if (w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for (m = mons; m; m = m->next)
+ if (w == m->barwin)
+ return m;
+ if ((c = wintoclient(w)))
+ return c->mon;
+ return selmon;
+}
+
+/* There's no way to check accesses to destroyed windows, thus those cases are
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
+ * default error handler, which may call exit. */
+int
+xerror(Display *dpy, XErrorEvent *ee)
+{
+ if (ee->error_code == BadWindow
+ || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
+ || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
+ || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
+ || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
+ || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
+ || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
+ return 0;
+ fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
+ ee->request_code, ee->error_code);
+ return xerrorxlib(dpy, ee); /* may call exit */
+}
+
+int
+xerrordummy(Display *dpy, XErrorEvent *ee)
+{
+ return 0;
+}
+
+/* Startup Error handler to check if another window manager
+ * is already running. */
+int
+xerrorstart(Display *dpy, XErrorEvent *ee)
+{
+ die("dwm: another window manager is already running");
+ return -1;
+}
+
+Monitor *
+systraytomon(Monitor *m) {
+ Monitor *t;
+ int i, n;
+ if(!systraypinning) {
+ if(!m)
+ return selmon;
+ return m == selmon ? m : NULL;
+ }
+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
+ if(systraypinningfailfirst && n < systraypinning)
+ return mons;
+ return t;
+}
+
+void
+zoom(const Arg *arg)
+{
+ Client *c = selmon->sel;
+
+ if (!selmon->lt[selmon->sellt]->arrange
+ || (selmon->sel && selmon->sel->isfloating))
+ return;
+ if (c == nexttiled(selmon->clients))
+ if (!c || !(c = nexttiled(c->next)))
+ return;
+ pop(c);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc == 2 && !strcmp("-v", argv[1]))
+ die("dwm-"VERSION);
+ else if (argc != 1)
+ die("usage: dwm [-v]");
+ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fputs("warning: no locale support\n", stderr);
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("dwm: cannot open display");
+ checkotherwm();
+ setup();
+#ifdef __OpenBSD__
+ if (pledge("stdio rpath proc exec", NULL) == -1)
+ die("pledge");
+#endif /* __OpenBSD__ */
+ scan();
+ runAutostart();
+ run();
+ cleanup();
+ XCloseDisplay(dpy);
+ return EXIT_SUCCESS;
+}
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm.c.orig b/keep/manual-programs/suckless/dwm-bak/dwm.c.orig
new file mode 100644
index 0000000..0f0365a
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm.c.orig
@@ -0,0 +1,2516 @@
+/* See LICENSE file for copyright and license details.
+ *
+ * dynamic window manager is designed like any other X client as well. It is
+ * driven through handling X events. In contrast to other X clients, a window
+ * manager selects for SubstructureRedirectMask on the root window, to receive
+ * events about window (dis-)appearance. Only one X connection at a time is
+ * allowed to select for this event mask.
+ *
+ * The event handlers of dwm are organized in an array which is accessed
+ * whenever a new event has been fetched. This allows event dispatching
+ * in O(1) time.
+ *
+ * Each child of the root window is called a client, except windows which have
+ * set the override_redirect flag. Clients are organized in a linked client
+ * list on each monitor, the focus history is remembered through a stack list
+ * on each monitor. Each client contains a bit array to indicate the tags of a
+ * client.
+ *
+ * Keys and tagging rules are organized as arrays and defined in config.h.
+ *
+ * To understand everything else, start reading main().
+ */
+#include <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif /* XINERAMA */
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+/* macros */
+#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
+#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
+#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
+ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
+#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
+#define WIDTH(X) ((X)->w + 2 * (X)->bw)
+#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
+#define TAGMASK ((1 << LENGTH(tags)) - 1)
+#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+
+/* XEMBED messages */
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_IN 4
+#define XEMBED_MODALITY_ON 10
+
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_WINDOW_DEACTIVATE 2
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 0
+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
+
+/* enums */
+enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+typedef union {
+ int i;
+ unsigned int ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int click;
+ unsigned int mask;
+ unsigned int button;
+ void (*func)(const Arg *arg);
+ const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct Client Client;
+struct Client {
+ char name[256];
+ float mina, maxa;
+ int x, y, w, h;
+ int oldx, oldy, oldw, oldh;
+ int basew, baseh, incw, inch, maxw, maxh, minw, minh;
+ int bw, oldbw;
+ unsigned int tags;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+ Window win;
+};
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+} Layout;
+
+struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ int gappx; /* gaps between windows */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ int showbar;
+ int topbar;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
+ const Layout *lt[2];
+};
+
+typedef struct {
+ const char *class;
+ const char *instance;
+ const char *title;
+ unsigned int tags;
+ int isfloating;
+ int monitor;
+} Rule;
+
+typedef struct Systray Systray;
+struct Systray {
+ Window win;
+ Client *icons;
+};
+
+/* function declarations */
+static void applyrules(Client *c);
+static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+static void arrange(Monitor *m);
+static void arrangemon(Monitor *m);
+static void attach(Client *c);
+static void attachstack(Client *c);
+static void buttonpress(XEvent *e);
+static void checkotherwm(void);
+static void cleanup(void);
+static void cleanupmon(Monitor *mon);
+static void clientmessage(XEvent *e);
+static void configure(Client *c);
+static void configurenotify(XEvent *e);
+static void configurerequest(XEvent *e);
+static Monitor *createmon(void);
+static void destroynotify(XEvent *e);
+static void detach(Client *c);
+static void detachstack(Client *c);
+static Monitor *dirtomon(int dir);
+static void drawbar(Monitor *m);
+static void drawbars(void);
+static void enternotify(XEvent *e);
+static void expose(XEvent *e);
+static void focus(Client *c);
+static void focusin(XEvent *e);
+static void focusmon(const Arg *arg);
+static void focusstack(const Arg *arg);
+static Atom getatomprop(Client *c, Atom prop);
+static int getrootptr(int *x, int *y);
+static long getstate(Window w);
+static unsigned int getsystraywidth();
+static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
+static void grabbuttons(Client *c, int focused);
+static void grabkeys(void);
+static void incnmaster(const Arg *arg);
+static void keypress(XEvent *e);
+static void killclient(const Arg *arg);
+static void manage(Window w, XWindowAttributes *wa);
+static void mappingnotify(XEvent *e);
+static void maprequest(XEvent *e);
+static void monocle(Monitor *m);
+static void motionnotify(XEvent *e);
+static void movemouse(const Arg *arg);
+static Client *nexttiled(Client *c);
+static void pop(Client *);
+static void propertynotify(XEvent *e);
+static void quit(const Arg *arg);
+static Monitor *recttomon(int x, int y, int w, int h);
+static void removesystrayicon(Client *i);
+static void resize(Client *c, int x, int y, int w, int h, int interact);
+static void resizebarwin(Monitor *m);
+static void resizeclient(Client *c, int x, int y, int w, int h);
+static void resizemouse(const Arg *arg);
+static void resizerequest(XEvent *e);
+static void restack(Monitor *m);
+static void run(void);
+static void scan(void);
+static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+static void sendmon(Client *c, Monitor *m);
+static void setclientstate(Client *c, long state);
+static void setfocus(Client *c);
+static void setfullscreen(Client *c, int fullscreen);
+static void setgaps(const Arg *arg);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setup(void);
+static void seturgent(Client *c, int urg);
+static void showhide(Client *c);
+static void sigchld(int unused);
+static void spawn(const Arg *arg);
+static Monitor *systraytomon(Monitor *m);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *);
+static void togglebar(const Arg *arg);
+static void togglefloating(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void unfocus(Client *c, int setfocus);
+static void unmanage(Client *c, int destroyed);
+static void unmapnotify(XEvent *e);
+static void updatebarpos(Monitor *m);
+static void updatebars(void);
+static void updateclientlist(void);
+static int updategeom(void);
+static void updatenumlockmask(void);
+static void updatesizehints(Client *c);
+static void updatestatus(void);
+static void updatesystray(void);
+static void updatesystrayicongeom(Client *i, int w, int h);
+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+static void updatetitle(Client *c);
+static void updatewindowtype(Client *c);
+static void updatewmhints(Client *c);
+static void view(const Arg *arg);
+static Client *wintoclient(Window w);
+static Monitor *wintomon(Window w);
+static Client *wintosystrayicon(Window w);
+static int xerror(Display *dpy, XErrorEvent *ee);
+static int xerrordummy(Display *dpy, XErrorEvent *ee);
+static int xerrorstart(Display *dpy, XErrorEvent *ee);
+static void zoom(const Arg *arg);
+
+/* variables */
+static Systray *systray = NULL;
+static const char broken[] = "broken";
+static char stext[256];
+static int screen;
+static int sw, sh; /* X display screen geometry width, height */
+static int bh, blw = 0; /* bar geometry */
+static int lrpad; /* sum of left and right padding for text */
+static int (*xerrorxlib)(Display *, XErrorEvent *);
+static unsigned int numlockmask = 0;
+static void (*handler[LASTEvent]) (XEvent *) = {
+ [ButtonPress] = buttonpress,
+ [ClientMessage] = clientmessage,
+ [ConfigureRequest] = configurerequest,
+ [ConfigureNotify] = configurenotify,
+ [DestroyNotify] = destroynotify,
+ //[EnterNotify] = enternotify,
+ [Expose] = expose,
+ [FocusIn] = focusin,
+ [KeyPress] = keypress,
+ [MappingNotify] = mappingnotify,
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
+ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+};
+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+static int running = 1;
+static Cur *cursor[CurLast];
+static Clr **scheme;
+static Display *dpy;
+static Drw *drw;
+static Monitor *mons, *selmon;
+static Window root, wmcheckwin;
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* compile-time check if all tags fit into an unsigned int bit array. */
+struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+/* function implementations */
+void
+applyrules(Client *c)
+{
+ const char *class, *instance;
+ unsigned int i;
+ const Rule *r;
+ Monitor *m;
+ XClassHint ch = { NULL, NULL };
+
+ /* rule matching */
+ c->isfloating = 0;
+ c->tags = 0;
+ XGetClassHint(dpy, c->win, &ch);
+ class = ch.res_class ? ch.res_class : broken;
+ instance = ch.res_name ? ch.res_name : broken;
+
+ for (i = 0; i < LENGTH(rules); i++) {
+ r = &rules[i];
+ if ((!r->title || strstr(c->name, r->title))
+ && (!r->class || strstr(class, r->class))
+ && (!r->instance || strstr(instance, r->instance)))
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+ if (m)
+ c->mon = m;
+ }
+ }
+ if (ch.res_class)
+ XFree(ch.res_class);
+ if (ch.res_name)
+ XFree(ch.res_name);
+ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
+}
+
+int
+applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
+{
+ int baseismin;
+ Monitor *m = c->mon;
+
+ /* set minimum possible */
+ *w = MAX(1, *w);
+ *h = MAX(1, *h);
+ if (interact) {
+ if (*x > sw)
+ *x = sw - WIDTH(c);
+ if (*y > sh)
+ *y = sh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw < 0)
+ *x = 0;
+ if (*y + *h + 2 * c->bw < 0)
+ *y = 0;
+ } else {
+ if (*x >= m->wx + m->ww)
+ *x = m->wx + m->ww - WIDTH(c);
+ if (*y >= m->wy + m->wh)
+ *y = m->wy + m->wh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw <= m->wx)
+ *x = m->wx;
+ if (*y + *h + 2 * c->bw <= m->wy)
+ *y = m->wy;
+ }
+ if (*h < bh)
+ *h = bh;
+ if (*w < bh)
+ *w = bh;
+ if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ /* see last two sentences in ICCCM 4.1.2.3 */
+ baseismin = c->basew == c->minw && c->baseh == c->minh;
+ if (!baseismin) { /* temporarily remove base dimensions */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for aspect limits */
+ if (c->mina > 0 && c->maxa > 0) {
+ if (c->maxa < (float)*w / *h)
+ *w = *h * c->maxa + 0.5;
+ else if (c->mina < (float)*h / *w)
+ *h = *w * c->mina + 0.5;
+ }
+ if (baseismin) { /* increment calculation requires this */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for increment value */
+ if (c->incw)
+ *w -= *w % c->incw;
+ if (c->inch)
+ *h -= *h % c->inch;
+ /* restore base dimensions */
+ *w = MAX(*w + c->basew, c->minw);
+ *h = MAX(*h + c->baseh, c->minh);
+ if (c->maxw)
+ *w = MIN(*w, c->maxw);
+ if (c->maxh)
+ *h = MIN(*h, c->maxh);
+ }
+ return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
+}
+
+void
+arrange(Monitor *m)
+{
+ if (m)
+ showhide(m->stack);
+ else for (m = mons; m; m = m->next)
+ showhide(m->stack);
+ if (m) {
+ arrangemon(m);
+ restack(m);
+ } else for (m = mons; m; m = m->next)
+ arrangemon(m);
+}
+
+void
+arrangemon(Monitor *m)
+{
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+}
+
+void
+attach(Client *c)
+{
+ c->next = c->mon->clients;
+ c->mon->clients = c;
+}
+
+void
+attachstack(Client *c)
+{
+ c->snext = c->mon->stack;
+ c->mon->stack = c;
+}
+
+void
+buttonpress(XEvent *e)
+{
+ unsigned int i, x, click;
+ Arg arg = {0};
+ Client *c;
+ Monitor *m;
+ XButtonPressedEvent *ev = &e->xbutton;
+
+ click = ClkRootWin;
+ /* focus monitor if necessary */
+ if ((m = wintomon(ev->window)) && m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ if (ev->window == selmon->barwin) {
+ i = x = 0;
+ do
+ x += TEXTW(tags[i]);
+ while (ev->x >= x && ++i < LENGTH(tags));
+ if (i < LENGTH(tags)) {
+ click = ClkTagBar;
+ arg.ui = 1 << i;
+ } else if (ev->x < x + blw)
+ click = ClkLtSymbol;
+ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+ } else if ((c = wintoclient(ev->window))) {
+ focus(c);
+ restack(selmon);
+ XAllowEvents(dpy, ReplayPointer, CurrentTime);
+ click = ClkClientWin;
+ }
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+ buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+}
+
+void
+checkotherwm(void)
+{
+ xerrorxlib = XSetErrorHandler(xerrorstart);
+ /* this causes an error if some other window manager is running */
+ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XSync(dpy, False);
+}
+
+void
+cleanup(void)
+{
+ Arg a = {.ui = ~0};
+ Layout foo = { "", NULL };
+ Monitor *m;
+ size_t i;
+
+ view(&a);
+ selmon->lt[selmon->sellt] = &foo;
+ for (m = mons; m; m = m->next)
+ while (m->stack)
+ unmanage(m->stack, 0);
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while (mons)
+ cleanupmon(mons);
+ if (showsystray) {
+ XUnmapWindow(dpy, systray->win);
+ XDestroyWindow(dpy, systray->win);
+ free(systray);
+ }
+ for (i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for (i = 0; i < LENGTH(colors); i++)
+ free(scheme[i]);
+ XDestroyWindow(dpy, wmcheckwin);
+ drw_free(drw);
+ XSync(dpy, False);
+ XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+}
+
+void
+cleanupmon(Monitor *mon)
+{
+ Monitor *m;
+
+ if (mon == mons)
+ mons = mons->next;
+ else {
+ for (m = mons; m && m->next != mon; m = m->next);
+ m->next = mon->next;
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
+ free(mon);
+}
+
+void
+clientmessage(XEvent *e)
+{
+ XWindowAttributes wa;
+ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
+ /* add systray icons */
+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
+ if (!(c = (Client *)calloc(1, sizeof(Client))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
+ if (!(c->win = cme->data.l[2])) {
+ free(c);
+ return;
+ }
+ c->mon = selmon;
+ c->next = systray->icons;
+ systray->icons = c;
+ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
+ /* use sane defaults */
+ wa.width = bh;
+ wa.height = bh;
+ wa.border_width = 0;
+ }
+ c->x = c->oldx = c->y = c->oldy = 0;
+ c->w = c->oldw = wa.width;
+ c->h = c->oldh = wa.height;
+ c->oldbw = wa.border_width;
+ c->bw = 0;
+ c->isfloating = True;
+ /* reuse tags field as mapped status */
+ c->tags = 1;
+ updatesizehints(c);
+ updatesystrayicongeom(c, wa.width, wa.height);
+ XAddToSaveSet(dpy, c->win);
+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
+ XClassHint ch = {"dwmsystray", "dwmsystray"};
+ XSetClassHint(dpy, c->win, &ch);
+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
+ /* use parents background color */
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ /* FIXME not sure if I have to send these events, too */
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ XSync(dpy, False);
+ resizebarwin(selmon);
+ updatesystray();
+ setclientstate(c, NormalState);
+ }
+ return;
+ }
+ if (!c)
+ return;
+ if (cme->message_type == netatom[NetWMState]) {
+ if (cme->data.l[1] == netatom[NetWMFullscreen]
+ || cme->data.l[2] == netatom[NetWMFullscreen])
+ setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
+ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
+ } else if (cme->message_type == netatom[NetActiveWindow]) {
+ if (c != selmon->sel && !c->isurgent)
+ seturgent(c, 1);
+ }
+}
+
+void
+configure(Client *c)
+{
+ XConfigureEvent ce;
+
+ ce.type = ConfigureNotify;
+ ce.display = dpy;
+ ce.event = c->win;
+ ce.window = c->win;
+ ce.x = c->x;
+ ce.y = c->y;
+ ce.width = c->w;
+ ce.height = c->h;
+ ce.border_width = c->bw;
+ ce.above = None;
+ ce.override_redirect = False;
+ XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
+}
+
+void
+configurenotify(XEvent *e)
+{
+ Monitor *m;
+ Client *c;
+ XConfigureEvent *ev = &e->xconfigure;
+ int dirty;
+
+ /* TODO: updategeom handling sucks, needs to be simplified */
+ if (ev->window == root) {
+ dirty = (sw != ev->width || sh != ev->height);
+ sw = ev->width;
+ sh = ev->height;
+ if (updategeom() || dirty) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next)
+ if (c->isfullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+ resizebarwin(m);
+ }
+ focus(NULL);
+ arrange(NULL);
+ }
+ }
+}
+
+void
+configurerequest(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XConfigureRequestEvent *ev = &e->xconfigurerequest;
+ XWindowChanges wc;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->value_mask & CWBorderWidth)
+ c->bw = ev->border_width;
+ else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
+ m = c->mon;
+ if (ev->value_mask & CWX) {
+ c->oldx = c->x;
+ c->x = m->mx + ev->x;
+ }
+ if (ev->value_mask & CWY) {
+ c->oldy = c->y;
+ c->y = m->my + ev->y;
+ }
+ if (ev->value_mask & CWWidth) {
+ c->oldw = c->w;
+ c->w = ev->width;
+ }
+ if (ev->value_mask & CWHeight) {
+ c->oldh = c->h;
+ c->h = ev->height;
+ }
+ if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
+ c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
+ if ((c->y + c->h) > m->my + m->mh && c->isfloating)
+ c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
+ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
+ configure(c);
+ if (ISVISIBLE(c))
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ } else
+ configure(c);
+ } else {
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+ wc.border_width = ev->border_width;
+ wc.sibling = ev->above;
+ wc.stack_mode = ev->detail;
+ XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
+ }
+ XSync(dpy, False);
+}
+
+Monitor *
+createmon(void)
+{
+ Monitor *m;
+
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+ m->topbar = topbar;
+ m->gappx = gappx;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ return m;
+}
+
+void
+destroynotify(XEvent *e)
+{
+ Client *c;
+ XDestroyWindowEvent *ev = &e->xdestroywindow;
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
+ else if ((c = wintosystrayicon(ev->window))) {
+ removesystrayicon(c);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+detach(Client *c)
+{
+ Client **tc;
+
+ for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
+ *tc = c->next;
+}
+
+void
+detachstack(Client *c)
+{
+ Client **tc, *t;
+
+ for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
+ *tc = c->snext;
+
+ if (c == c->mon->sel) {
+ for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
+ c->mon->sel = t;
+ }
+}
+
+Monitor *
+dirtomon(int dir)
+{
+ Monitor *m = NULL;
+
+ if (dir > 0) {
+ if (!(m = selmon->next))
+ m = mons;
+ } else if (selmon == mons)
+ for (m = mons; m->next; m = m->next);
+ else
+ for (m = mons; m->next != selmon; m = m->next);
+ return m;
+}
+
+void
+drawbar(Monitor *m)
+{
+ int x, w, sw = 0, stw = 0;
+ int boxs = drw->fonts->h / 9;
+ int boxw = drw->fonts->h / 6 + 2;
+ unsigned int i, occ = 0, urg = 0;
+ Client *c;
+
+ if(showsystray && m == systraytomon(m))
+ stw = getsystraywidth();
+
+ /* draw status first so it can be overdrawn by tags later */
+ if (m == selmon) { /* status is only drawn on selected monitor */
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
+ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
+ resizebarwin(m);
+ for (c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ x = 0;
+ for (i = 0; i < LENGTH(tags); i++) {
+ w = TEXTW(tags[i]);
+ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+ if (occ & 1 << i)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw,
+ m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+ urg & 1 << i);
+ x += w;
+ }
+ w = blw = TEXTW(m->ltsymbol);
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+ if ((w = m->ww - sw - stw - x) > bh) {
+ if (m->sel) {
+ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+ if (m->sel->isfloating)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
+ } else {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ }
+ }
+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
+}
+
+void
+drawbars(void)
+{
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ drawbar(m);
+}
+
+void
+enternotify(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XCrossingEvent *ev = &e->xcrossing;
+
+ if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
+ return;
+ c = wintoclient(ev->window);
+ m = c ? c->mon : wintomon(ev->window);
+ if (m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ } else if (!c || c == selmon->sel)
+ return;
+ focus(c);
+}
+
+void
+expose(XEvent *e)
+{
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+ if (ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
+ if (m == selmon)
+ updatesystray();
+ }
+}
+
+void
+focus(Client *c)
+{
+ if (!c || !ISVISIBLE(c))
+ for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
+ if (selmon->sel && selmon->sel != c)
+ unfocus(selmon->sel, 0);
+ if (c) {
+ if (c->mon != selmon)
+ selmon = c->mon;
+ if (c->isurgent)
+ seturgent(c, 0);
+ detachstack(c);
+ attachstack(c);
+ grabbuttons(c, 1);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
+ setfocus(c);
+ } else {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+ selmon->sel = c;
+ drawbars();
+}
+
+/* there are some broken focus acquiring clients needing extra handling */
+void
+focusin(XEvent *e)
+{
+ XFocusChangeEvent *ev = &e->xfocus;
+
+ if (selmon->sel && ev->window != selmon->sel->win)
+ setfocus(selmon->sel);
+}
+
+void
+focusmon(const Arg *arg)
+{
+ Monitor *m;
+
+ if (!mons->next)
+ return;
+ if ((m = dirtomon(arg->i)) == selmon)
+ return;
+ unfocus(selmon->sel, 0);
+ selmon = m;
+ focus(NULL);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ Client *c = NULL, *i;
+
+ if (!selmon->sel)
+ return;
+ if (arg->i > 0) {
+ for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
+ if (!c)
+ for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
+ } else {
+ for (i = selmon->clients; i != selmon->sel; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ if (!c)
+ for (; i; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ }
+ if (c) {
+ focus(c);
+ restack(selmon);
+ }
+}
+
+Atom
+getatomprop(Client *c, Atom prop)
+{
+ int di;
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
+ /* FIXME getatomprop should return the number of items and a pointer to
+ * the stored data instead of this workaround */
+ Atom req = XA_ATOM;
+ if (prop == xatom[XembedInfo])
+ req = xatom[XembedInfo];
+
+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
+ if (da == xatom[XembedInfo] && dl == 2)
+ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+}
+
+int
+getrootptr(int *x, int *y)
+{
+ int di;
+ unsigned int dui;
+ Window dummy;
+
+ return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
+}
+
+long
+getstate(Window w)
+{
+ int format;
+ long result = -1;
+ unsigned char *p = NULL;
+ unsigned long n, extra;
+ Atom real;
+
+ if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
+ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
+ return -1;
+ if (n != 0)
+ result = *p;
+ XFree(p);
+ return result;
+}
+
+unsigned int
+getsystraywidth()
+{
+ unsigned int w = 0;
+ Client *i;
+ if(showsystray)
+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
+ return w ? w + systrayspacing : 1;
+}
+
+int
+gettextprop(Window w, Atom atom, char *text, unsigned int size)
+{
+ char **list = NULL;
+ int n;
+ XTextProperty name;
+
+ if (!text || size == 0)
+ return 0;
+ text[0] = '\0';
+ if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
+ return 0;
+ if (name.encoding == XA_STRING)
+ strncpy(text, (char *)name.value, size - 1);
+ else {
+ if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
+ strncpy(text, *list, size - 1);
+ XFreeStringList(list);
+ }
+ }
+ text[size - 1] = '\0';
+ XFree(name.value);
+ return 1;
+}
+
+void
+grabbuttons(Client *c, int focused)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ if (!focused)
+ XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
+ BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].click == ClkClientWin)
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabButton(dpy, buttons[i].button,
+ buttons[i].mask | modifiers[j],
+ c->win, False, BUTTONMASK,
+ GrabModeAsync, GrabModeSync, None, None);
+ }
+}
+
+void
+grabkeys(void)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ KeyCode code;
+
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ for (i = 0; i < LENGTH(keys); i++)
+ if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
+ True, GrabModeAsync, GrabModeAsync);
+ }
+}
+
+void
+incnmaster(const Arg *arg)
+{
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
+#ifdef XINERAMA
+static int
+isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
+{
+ while (n--)
+ if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
+ && unique[n].width == info->width && unique[n].height == info->height)
+ return 0;
+ return 1;
+}
+#endif /* XINERAMA */
+
+void
+keypress(XEvent *e)
+{
+ unsigned int i;
+ KeySym keysym;
+ XKeyEvent *ev;
+
+ ev = &e->xkey;
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
+ for (i = 0; i < LENGTH(keys); i++)
+ if (keysym == keys[i].keysym
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+ && keys[i].func)
+ keys[i].func(&(keys[i].arg));
+}
+
+void
+killclient(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+ XKillClient(dpy, selmon->sel->win);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+}
+
+void
+manage(Window w, XWindowAttributes *wa)
+{
+ Client *c, *t = NULL;
+ Window trans = None;
+ XWindowChanges wc;
+
+ c = ecalloc(1, sizeof(Client));
+ c->win = w;
+ /* geometry */
+ c->x = c->oldx = wa->x;
+ c->y = c->oldy = wa->y;
+ c->w = c->oldw = wa->width;
+ c->h = c->oldh = wa->height;
+ c->oldbw = wa->border_width;
+
+ updatetitle(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+ c->mon = t->mon;
+ c->tags = t->tags;
+ } else {
+ c->mon = selmon;
+ applyrules(c);
+ }
+
+ if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
+ c->x = c->mon->mx + c->mon->mw - WIDTH(c);
+ if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
+ c->y = c->mon->my + c->mon->mh - HEIGHT(c);
+ c->x = MAX(c->x, c->mon->mx);
+ /* only fix client y-offset, if the client center might cover the bar */
+ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
+ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
+ c->bw = borderpx;
+
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, w, CWBorderWidth, &wc);
+ XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
+ configure(c); /* propagates border_width, if size doesn't change */
+ updatewindowtype(c);
+ updatesizehints(c);
+ updatewmhints(c);
+ XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
+ grabbuttons(c, 0);
+ if (!c->isfloating)
+ c->isfloating = c->oldstate = trans != None || c->isfixed;
+ if (c->isfloating)
+ XRaiseWindow(dpy, c->win);
+ attach(c);
+ attachstack(c);
+ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+ XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
+ setclientstate(c, NormalState);
+ if (c->mon == selmon)
+ unfocus(selmon->sel, 0);
+ c->mon->sel = c;
+ arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ focus(NULL);
+}
+
+void
+mappingnotify(XEvent *e)
+{
+ XMappingEvent *ev = &e->xmapping;
+
+ XRefreshKeyboardMapping(ev);
+ if (ev->request == MappingKeyboard)
+ grabkeys();
+}
+
+void
+maprequest(XEvent *e)
+{
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
+ Client *i;
+ if ((i = wintosystrayicon(ev->window))) {
+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+
+ if (!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+ if (wa.override_redirect)
+ return;
+ if (!wintoclient(ev->window))
+ manage(ev->window, &wa);
+}
+
+void
+monocle(Monitor *m)
+{
+ unsigned int n = 0;
+ Client *c;
+
+ for (c = m->clients; c; c = c->next)
+ if (ISVISIBLE(c))
+ n++;
+ if (n > 0) /* override layout symbol */
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+ for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
+ resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
+}
+
+void
+motionnotify(XEvent *e)
+{
+ static Monitor *mon = NULL;
+ Monitor *m;
+ XMotionEvent *ev = &e->xmotion;
+
+ if (ev->window != root)
+ return;
+ if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ mon = m;
+}
+
+void
+movemouse(const Arg *arg)
+{
+ int x, y, ocx, ocy, nx, ny;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ if (!getrootptr(&x, &y))
+ return;
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nx = ocx + (ev.xmotion.x - x);
+ ny = ocy + (ev.xmotion.y - y);
+ if (abs(selmon->wx - nx) < snap)
+ nx = selmon->wx;
+ else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
+ nx = selmon->wx + selmon->ww - WIDTH(c);
+ if (abs(selmon->wy - ny) < snap)
+ ny = selmon->wy;
+ else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
+ ny = selmon->wy + selmon->wh - HEIGHT(c);
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
+ togglefloating(NULL);
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, nx, ny, c->w, c->h, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XUngrabPointer(dpy, CurrentTime);
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+Client *
+nexttiled(Client *c)
+{
+ for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
+ return c;
+}
+
+void
+pop(Client *c)
+{
+ detach(c);
+ attach(c);
+ focus(c);
+ arrange(c->mon);
+}
+
+void
+propertynotify(XEvent *e)
+{
+ Client *c;
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
+ if ((c = wintosystrayicon(ev->window))) {
+ if (ev->atom == XA_WM_NORMAL_HINTS) {
+ updatesizehints(c);
+ updatesystrayicongeom(c, c->w, c->h);
+ }
+ else
+ updatesystrayiconstate(c, ev);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+ if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if (ev->state == PropertyDelete)
+ return; /* ignore */
+ else if ((c = wintoclient(ev->window))) {
+ switch(ev->atom) {
+ default: break;
+ case XA_WM_TRANSIENT_FOR:
+ if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
+ (c->isfloating = (wintoclient(trans)) != NULL))
+ arrange(c->mon);
+ break;
+ case XA_WM_NORMAL_HINTS:
+ updatesizehints(c);
+ break;
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
+ break;
+ }
+ if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if (c == c->mon->sel)
+ drawbar(c->mon);
+ }
+ if (ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+ }
+}
+
+void
+quit(const Arg *arg)
+{
+ running = 0;
+}
+
+Monitor *
+recttomon(int x, int y, int w, int h)
+{
+ Monitor *m, *r = selmon;
+ int a, area = 0;
+
+ for (m = mons; m; m = m->next)
+ if ((a = INTERSECT(x, y, w, h, m)) > area) {
+ area = a;
+ r = m;
+ }
+ return r;
+}
+
+void
+removesystrayicon(Client *i)
+{
+ Client **ii;
+
+ if (!showsystray || !i)
+ return;
+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
+ if (ii)
+ *ii = i->next;
+ free(i);
+}
+
+
+void
+resize(Client *c, int x, int y, int w, int h, int interact)
+{
+ if (applysizehints(c, &x, &y, &w, &h, interact))
+ resizeclient(c, x, y, w, h);
+}
+
+void
+resizebarwin(Monitor *m) {
+ unsigned int w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
+}
+
+void
+resizeclient(Client *c, int x, int y, int w, int h)
+{
+ XWindowChanges wc;
+
+ c->oldx = c->x; c->x = wc.x = x;
+ c->oldy = c->y; c->y = wc.y = y;
+ c->oldw = c->w; c->w = wc.width = w;
+ c->oldh = c->h; c->h = wc.height = h;
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
+ configure(c);
+ XSync(dpy, False);
+}
+
+void
+resizemouse(const Arg *arg)
+{
+ int ocx, ocy, nw, nh;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
+ nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
+ if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
+ && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
+ {
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
+ togglefloating(NULL);
+ }
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, c->x, c->y, nw, nh, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ XUngrabPointer(dpy, CurrentTime);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+void
+resizerequest(XEvent *e)
+{
+ XResizeRequestEvent *ev = &e->xresizerequest;
+ Client *i;
+
+ if ((i = wintosystrayicon(ev->window))) {
+ updatesystrayicongeom(i, ev->width, ev->height);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+restack(Monitor *m)
+{
+ Client *c;
+ XEvent ev;
+ XWindowChanges wc;
+
+ drawbar(m);
+ if (!m->sel)
+ return;
+ if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
+ XRaiseWindow(dpy, m->sel->win);
+ if (m->lt[m->sellt]->arrange) {
+ wc.stack_mode = Below;
+ wc.sibling = m->barwin;
+ for (c = m->stack; c; c = c->snext)
+ if (!c->isfloating && ISVISIBLE(c)) {
+ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
+ wc.sibling = c->win;
+ }
+ }
+ XSync(dpy, False);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+}
+
+void
+run(void)
+{
+ XEvent ev;
+ /* main event loop */
+ XSync(dpy, False);
+ while (running && !XNextEvent(dpy, &ev))
+ if (handler[ev.type])
+ handler[ev.type](&ev); /* call handler */
+}
+
+void
+scan(void)
+{
+ unsigned int i, num;
+ Window d1, d2, *wins = NULL;
+ XWindowAttributes wa;
+
+ if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
+ for (i = 0; i < num; i++) {
+ if (!XGetWindowAttributes(dpy, wins[i], &wa)
+ || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
+ continue;
+ if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
+ manage(wins[i], &wa);
+ }
+ for (i = 0; i < num; i++) { /* now the transients */
+ if (!XGetWindowAttributes(dpy, wins[i], &wa))
+ continue;
+ if (XGetTransientForHint(dpy, wins[i], &d1)
+ && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
+ manage(wins[i], &wa);
+ }
+ if (wins)
+ XFree(wins);
+ }
+}
+
+void
+sendmon(Client *c, Monitor *m)
+{
+ if (c->mon == m)
+ return;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->mon = m;
+ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ focus(NULL);
+ arrange(NULL);
+}
+
+void
+setclientstate(Client *c, long state)
+{
+ long data[] = { state, None };
+
+ XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
+ PropModeReplace, (unsigned char *)data, 2);
+}
+
+int
+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
+{
+ int n;
+ Atom *protocols, mt;
+ int exists = 0;
+ XEvent ev;
+
+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
+ mt = wmatom[WMProtocols];
+ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
+ while (!exists && n--)
+ exists = protocols[n] == proto;
+ XFree(protocols);
+ }
+ }
+ else {
+ exists = True;
+ mt = proto;
+ }
+ if (exists) {
+ ev.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = d0;
+ ev.xclient.data.l[1] = d1;
+ ev.xclient.data.l[2] = d2;
+ ev.xclient.data.l[3] = d3;
+ ev.xclient.data.l[4] = d4;
+ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+}
+
+void
+setfocus(Client *c)
+{
+ if (!c->neverfocus) {
+ XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
+ XChangeProperty(dpy, root, netatom[NetActiveWindow],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+ if (fullscreen && !c->isfullscreen) {
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
+ c->isfullscreen = 1;
+ c->oldstate = c->isfloating;
+ c->oldbw = c->bw;
+ c->bw = 0;
+ c->isfloating = 1;
+ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
+ XRaiseWindow(dpy, c->win);
+ } else if (!fullscreen && c->isfullscreen){
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)0, 0);
+ c->isfullscreen = 0;
+ c->isfloating = c->oldstate;
+ c->bw = c->oldbw;
+ c->x = c->oldx;
+ c->y = c->oldy;
+ c->w = c->oldw;
+ c->h = c->oldh;
+ resizeclient(c, c->x, c->y, c->w, c->h);
+ arrange(c->mon);
+ }
+}
+
+void
+setgaps(const Arg *arg)
+{
+ if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
+ selmon->gappx = 0;
+ else
+ selmon->gappx += arg->i;
+ arrange(selmon);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if (selmon->sel)
+ arrange(selmon);
+ else
+ drawbar(selmon);
+}
+
+/* arg > 1.0 will set mfact absolutely */
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !selmon->lt[selmon->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ selmon->mfact = f;
+ arrange(selmon);
+}
+
+void
+setup(void)
+{
+ int i;
+ XSetWindowAttributes wa;
+ Atom utf8string;
+
+ /* clean up any zombies immediately */
+ sigchld(0);
+
+ /* init screen */
+ screen = DefaultScreen(dpy);
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
+ root = RootWindow(dpy, screen);
+ drw = drw_create(dpy, screen, root, sw, sh);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+ bh = drw->fonts->h + 2;
+ updategeom();
+ /* init atoms */
+ utf8string = XInternAtom(dpy, "UTF8_STRING", False);
+ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+ netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+ cursor[CurMove] = drw_cur_create(drw, XC_fleur);
+ /* init appearance */
+ scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
+ for (i = 0; i < LENGTH(colors); i++)
+ scheme[i] = drw_scm_create(drw, colors[i], 3);
+ /* init system tray */
+ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+ /* supporting window for NetWMCheck */
+ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
+ PropModeReplace, (unsigned char *) "dwm", 3);
+ XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ /* EWMH support per view */
+ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) netatom, NetLast);
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ /* select events */
+ wa.cursor = cursor[CurNormal]->cursor;
+ wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
+ |ButtonPressMask|PointerMotionMask|EnterWindowMask
+ |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
+ XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
+ XSelectInput(dpy, root, wa.event_mask);
+ grabkeys();
+ focus(NULL);
+}
+
+
+void
+seturgent(Client *c, int urg)
+{
+ XWMHints *wmh;
+
+ c->isurgent = urg;
+ if (!(wmh = XGetWMHints(dpy, c->win)))
+ return;
+ wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
+ XSetWMHints(dpy, c->win, wmh);
+ XFree(wmh);
+}
+
+void
+showhide(Client *c)
+{
+ if (!c)
+ return;
+ if (ISVISIBLE(c)) {
+ /* show clients top down */
+ XMoveWindow(dpy, c->win, c->x, c->y);
+ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+ resize(c, c->x, c->y, c->w, c->h, 0);
+ showhide(c->snext);
+ } else {
+ /* hide clients bottom up */
+ showhide(c->snext);
+ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
+ }
+}
+
+void
+sigchld(int unused)
+{
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("can't install SIGCHLD handler:");
+ while (0 < waitpid(-1, NULL, WNOHANG));
+}
+
+void
+spawn(const Arg *arg)
+{
+ if (arg->v == dmenucmd)
+ dmenumon[0] = '0' + selmon->num;
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ setsid();
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
+ perror(" failed");
+ exit(EXIT_SUCCESS);
+ }
+}
+
+void
+tag(const Arg *arg)
+{
+ if (selmon->sel && arg->ui & TAGMASK) {
+ selmon->sel->tags = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+tagmon(const Arg *arg)
+{
+ if (!selmon->sel || !mons->next)
+ return;
+ sendmon(selmon->sel, dirtomon(arg->i));
+}
+
+void
+tile(Monitor *m)
+{
+ unsigned int i, n, h, mw, my, ty;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (n == 0)
+ return;
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->ww * m->mfact : 0;
+ else
+ mw = m->ww - m->gappx;
+ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
+ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
+ my += HEIGHT(c) + m->gappx;
+ } else {
+ h = (m->wh - ty) / (n - i) - m->gappx;
+ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
+ ty += HEIGHT(c) + m->gappx;
+ }
+}
+
+void
+togglebar(const Arg *arg)
+{
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+ resizebarwin(selmon);
+ if (showsystray) {
+ XWindowChanges wc;
+ if (!selmon->showbar)
+ wc.y = -bh;
+ else if (selmon->showbar) {
+ wc.y = 0;
+ if (!selmon->topbar)
+ wc.y = selmon->mh - bh;
+ }
+ XConfigureWindow(dpy, systray->win, CWY, &wc);
+ }
+ arrange(selmon);
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
+ return;
+ selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
+ if (selmon->sel->isfloating)
+ resize(selmon->sel, selmon->sel->x, selmon->sel->y,
+ selmon->sel->w, selmon->sel->h, 0);
+ arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ unsigned int newtags;
+
+ if (!selmon->sel)
+ return;
+ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
+ selmon->sel->tags = newtags;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+toggleview(const Arg *arg)
+{
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
+
+ if (newtagset) {
+ selmon->tagset[selmon->seltags] = newtagset;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+unfocus(Client *c, int setfocus)
+{
+ if (!c)
+ return;
+ grabbuttons(c, 0);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
+ if (setfocus) {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+}
+
+void
+unmanage(Client *c, int destroyed)
+{
+ Monitor *m = c->mon;
+ XWindowChanges wc;
+
+ detach(c);
+ detachstack(c);
+ if (!destroyed) {
+ wc.border_width = c->oldbw;
+ XGrabServer(dpy); /* avoid race conditions */
+ XSetErrorHandler(xerrordummy);
+ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ setclientstate(c, WithdrawnState);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+ free(c);
+ focus(NULL);
+ updateclientlist();
+ arrange(m);
+}
+
+void
+unmapnotify(XEvent *e)
+{
+ Client *c;
+ XUnmapEvent *ev = &e->xunmap;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->send_event)
+ setclientstate(c, WithdrawnState);
+ else
+ unmanage(c, 0);
+ }
+ else if ((c = wintosystrayicon(ev->window))) {
+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
+ * _not_ destroy them. We map those windows back */
+ XMapRaised(dpy, c->win);
+ updatesystray();
+ }
+}
+
+void
+updatebars(void)
+{
+ unsigned int w;
+ Monitor *m;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .background_pixmap = ParentRelative,
+ .event_mask = ButtonPressMask|ExposureMask
+ };
+ XClassHint ch = {"dwm", "dwm"};
+ for (m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+ w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ if (showsystray && m == systraytomon(m))
+ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+}
+
+void
+updatebarpos(Monitor *m)
+{
+ m->wy = m->my;
+ m->wh = m->mh;
+ if (m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+ m->wy = m->topbar ? m->wy + bh : m->wy;
+ } else
+ m->by = -bh;
+}
+
+void
+updateclientlist()
+{
+ Client *c;
+ Monitor *m;
+
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ XChangeProperty(dpy, root, netatom[NetClientList],
+ XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+}
+
+int
+updategeom(void)
+{
+ int dirty = 0;
+
+#ifdef XINERAMA
+ if (XineramaIsActive(dpy)) {
+ int i, j, n, nn;
+ Client *c;
+ Monitor *m;
+ XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
+ XineramaScreenInfo *unique = NULL;
+
+ for (n = 0, m = mons; m; m = m->next, n++);
+ /* only consider unique geometries as separate screens */
+ unique = ecalloc(nn, sizeof(XineramaScreenInfo));
+ for (i = 0, j = 0; i < nn; i++)
+ if (isuniquegeom(unique, j, &info[i]))
+ memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
+ XFree(info);
+ nn = j;
+ if (n <= nn) { /* new monitors available */
+ for (i = 0; i < (nn - n); i++) {
+ for (m = mons; m && m->next; m = m->next);
+ if (m)
+ m->next = createmon();
+ else
+ mons = createmon();
+ }
+ for (i = 0, m = mons; i < nn && m; m = m->next, i++)
+ if (i >= n
+ || unique[i].x_org != m->mx || unique[i].y_org != m->my
+ || unique[i].width != m->mw || unique[i].height != m->mh)
+ {
+ dirty = 1;
+ m->num = i;
+ m->mx = m->wx = unique[i].x_org;
+ m->my = m->wy = unique[i].y_org;
+ m->mw = m->ww = unique[i].width;
+ m->mh = m->wh = unique[i].height;
+ updatebarpos(m);
+ }
+ } else { /* less monitors available nn < n */
+ for (i = nn; i < n; i++) {
+ for (m = mons; m && m->next; m = m->next);
+ while ((c = m->clients)) {
+ dirty = 1;
+ m->clients = c->next;
+ detachstack(c);
+ c->mon = mons;
+ attach(c);
+ attachstack(c);
+ }
+ if (m == selmon)
+ selmon = mons;
+ cleanupmon(m);
+ }
+ }
+ free(unique);
+ } else
+#endif /* XINERAMA */
+ { /* default monitor setup */
+ if (!mons)
+ mons = createmon();
+ if (mons->mw != sw || mons->mh != sh) {
+ dirty = 1;
+ mons->mw = mons->ww = sw;
+ mons->mh = mons->wh = sh;
+ updatebarpos(mons);
+ }
+ }
+ if (dirty) {
+ selmon = mons;
+ selmon = wintomon(root);
+ }
+ return dirty;
+}
+
+void
+updatenumlockmask(void)
+{
+ unsigned int i, j;
+ XModifierKeymap *modmap;
+
+ numlockmask = 0;
+ modmap = XGetModifierMapping(dpy);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < modmap->max_keypermod; j++)
+ if (modmap->modifiermap[i * modmap->max_keypermod + j]
+ == XKeysymToKeycode(dpy, XK_Num_Lock))
+ numlockmask = (1 << i);
+ XFreeModifiermap(modmap);
+}
+
+void
+updatesizehints(Client *c)
+{
+ long msize;
+ XSizeHints size;
+
+ if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
+ /* size is uninitialized, ensure that size.flags aren't used */
+ size.flags = PSize;
+ if (size.flags & PBaseSize) {
+ c->basew = size.base_width;
+ c->baseh = size.base_height;
+ } else if (size.flags & PMinSize) {
+ c->basew = size.min_width;
+ c->baseh = size.min_height;
+ } else
+ c->basew = c->baseh = 0;
+ if (size.flags & PResizeInc) {
+ c->incw = size.width_inc;
+ c->inch = size.height_inc;
+ } else
+ c->incw = c->inch = 0;
+ if (size.flags & PMaxSize) {
+ c->maxw = size.max_width;
+ c->maxh = size.max_height;
+ } else
+ c->maxw = c->maxh = 0;
+ if (size.flags & PMinSize) {
+ c->minw = size.min_width;
+ c->minh = size.min_height;
+ } else if (size.flags & PBaseSize) {
+ c->minw = size.base_width;
+ c->minh = size.base_height;
+ } else
+ c->minw = c->minh = 0;
+ if (size.flags & PAspect) {
+ c->mina = (float)size.min_aspect.y / size.min_aspect.x;
+ c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
+ } else
+ c->maxa = c->mina = 0.0;
+ c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
+}
+
+void
+updatestatus(void)
+{
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ strcpy(stext, "dwm-"VERSION);
+ drawbar(selmon);
+ updatesystray();
+}
+
+void
+updatesystrayicongeom(Client *i, int w, int h)
+{
+ if (i) {
+ i->h = bh;
+ if (w == h)
+ i->w = bh;
+ else if (h == bh)
+ i->w = w;
+ else
+ i->w = (int) ((float)bh * ((float)w / (float)h));
+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
+ /* force icons into the systray dimensions if they don't want to */
+ if (i->h > bh) {
+ if (i->w == i->h)
+ i->w = bh;
+ else
+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
+ i->h = bh;
+ }
+ }
+}
+
+void
+updatesystrayiconstate(Client *i, XPropertyEvent *ev)
+{
+ long flags;
+ int code = 0;
+
+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
+ !(flags = getatomprop(i, xatom[XembedInfo])))
+ return;
+
+ if (flags & XEMBED_MAPPED && !i->tags) {
+ i->tags = 1;
+ code = XEMBED_WINDOW_ACTIVATE;
+ XMapRaised(dpy, i->win);
+ setclientstate(i, NormalState);
+ }
+ else if (!(flags & XEMBED_MAPPED) && i->tags) {
+ i->tags = 0;
+ code = XEMBED_WINDOW_DEACTIVATE;
+ XUnmapWindow(dpy, i->win);
+ setclientstate(i, WithdrawnState);
+ }
+ else
+ return;
+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
+ systray->win, XEMBED_EMBEDDED_VERSION);
+}
+
+void
+updatesystray(void)
+{
+ XSetWindowAttributes wa;
+ XWindowChanges wc;
+ Client *i;
+ Monitor *m = systraytomon(NULL);
+ unsigned int x = m->mx + m->mw;
+ unsigned int w = 1;
+
+ if (!showsystray)
+ return;
+ if (!systray) {
+ /* init systray */
+ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
+ wa.event_mask = ButtonPressMask | ExposureMask;
+ wa.override_redirect = True;
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
+ XMapRaised(dpy, systray->win);
+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
+ XSync(dpy, False);
+ }
+ else {
+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
+ free(systray);
+ systray = NULL;
+ return;
+ }
+ }
+ for (w = 0, i = systray->icons; i; i = i->next) {
+ /* make sure the background color stays the same */
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
+ XMapRaised(dpy, i->win);
+ w += systrayspacing;
+ i->x = w;
+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
+ w += i->w;
+ if (i->mon != m)
+ i->mon = m;
+ }
+ w = w ? w + systrayspacing : 1;
+ x -= w;
+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
+ wc.stack_mode = Above; wc.sibling = m->barwin;
+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
+ XMapWindow(dpy, systray->win);
+ XMapSubwindows(dpy, systray->win);
+ /* redraw background */
+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
+ XSync(dpy, False);
+}
+
+void
+updatetitle(Client *c)
+{
+ if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
+ gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
+ if (c->name[0] == '\0') /* hack to mark broken clients */
+ strcpy(c->name, broken);
+}
+
+void
+updatewindowtype(Client *c)
+{
+ Atom state = getatomprop(c, netatom[NetWMState]);
+ Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
+
+ if (state == netatom[NetWMFullscreen])
+ setfullscreen(c, 1);
+ if (wtype == netatom[NetWMWindowTypeDialog])
+ c->isfloating = 1;
+}
+
+void
+updatewmhints(Client *c)
+{
+ XWMHints *wmh;
+
+ if ((wmh = XGetWMHints(dpy, c->win))) {
+ if (c == selmon->sel && wmh->flags & XUrgencyHint) {
+ wmh->flags &= ~XUrgencyHint;
+ XSetWMHints(dpy, c->win, wmh);
+ } else
+ c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
+ if (wmh->flags & InputHint)
+ c->neverfocus = !wmh->input;
+ else
+ c->neverfocus = 0;
+ XFree(wmh);
+ }
+}
+
+void
+view(const Arg *arg)
+{
+ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+}
+
+Client *
+wintoclient(Window w)
+{
+ Client *c;
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ if (c->win == w)
+ return c;
+ return NULL;
+}
+
+Client *
+wintosystrayicon(Window w) {
+ Client *i = NULL;
+
+ if (!showsystray || !w)
+ return i;
+ for (i = systray->icons; i && i->win != w; i = i->next) ;
+ return i;
+}
+
+Monitor *
+wintomon(Window w)
+{
+ int x, y;
+ Client *c;
+ Monitor *m;
+
+ if (w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for (m = mons; m; m = m->next)
+ if (w == m->barwin)
+ return m;
+ if ((c = wintoclient(w)))
+ return c->mon;
+ return selmon;
+}
+
+/* There's no way to check accesses to destroyed windows, thus those cases are
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
+ * default error handler, which may call exit. */
+int
+xerror(Display *dpy, XErrorEvent *ee)
+{
+ if (ee->error_code == BadWindow
+ || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
+ || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
+ || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
+ || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
+ || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
+ || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
+ return 0;
+ fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
+ ee->request_code, ee->error_code);
+ return xerrorxlib(dpy, ee); /* may call exit */
+}
+
+int
+xerrordummy(Display *dpy, XErrorEvent *ee)
+{
+ return 0;
+}
+
+/* Startup Error handler to check if another window manager
+ * is already running. */
+int
+xerrorstart(Display *dpy, XErrorEvent *ee)
+{
+ die("dwm: another window manager is already running");
+ return -1;
+}
+
+Monitor *
+systraytomon(Monitor *m) {
+ Monitor *t;
+ int i, n;
+ if(!systraypinning) {
+ if(!m)
+ return selmon;
+ return m == selmon ? m : NULL;
+ }
+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
+ if(systraypinningfailfirst && n < systraypinning)
+ return mons;
+ return t;
+}
+
+void
+zoom(const Arg *arg)
+{
+ Client *c = selmon->sel;
+
+ if (!selmon->lt[selmon->sellt]->arrange
+ || (selmon->sel && selmon->sel->isfloating))
+ return;
+ if (c == nexttiled(selmon->clients))
+ if (!c || !(c = nexttiled(c->next)))
+ return;
+ pop(c);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc == 2 && !strcmp("-v", argv[1]))
+ die("dwm-"VERSION);
+ else if (argc != 1)
+ die("usage: dwm [-v]");
+ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fputs("warning: no locale support\n", stderr);
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("dwm: cannot open display");
+ checkotherwm();
+ setup();
+#ifdef __OpenBSD__
+ if (pledge("stdio rpath proc exec", NULL) == -1)
+ die("pledge");
+#endif /* __OpenBSD__ */
+ scan();
+ run();
+ cleanup();
+ XCloseDisplay(dpy);
+ return EXIT_SUCCESS;
+}
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm.o b/keep/manual-programs/suckless/dwm-bak/dwm.o
new file mode 100644
index 0000000..2dd20f6
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm-bak/dwm.png b/keep/manual-programs/suckless/dwm-bak/dwm.png
new file mode 100644
index 0000000..b1f9ba7
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwm.png
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm-bak/dwmstatus/LICENSE b/keep/manual-programs/suckless/dwm-bak/dwmstatus/LICENSE
new file mode 100644
index 0000000..855608d
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwmstatus/LICENSE
@@ -0,0 +1,21 @@
+MIT/X Consortium License
+
+© 2011-2018 Christoph Lohmann <20h@r-36.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/keep/manual-programs/suckless/dwm-bak/dwmstatus/Makefile b/keep/manual-programs/suckless/dwm-bak/dwmstatus/Makefile
new file mode 100644
index 0000000..62240a5
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwmstatus/Makefile
@@ -0,0 +1,49 @@
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+SRC = ${NAME}.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${NAME}
+
+options:
+ @echo ${NAME} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+.c.o:
+ @echo CC $<
+ @${CC} -c ${CFLAGS} $<
+
+${OBJ}: config.mk
+
+${NAME}: ${OBJ}
+ @echo CC -o $@
+ @${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+clean:
+ @echo cleaning
+ @rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz
+
+dist: clean
+ @echo creating dist tarball
+ @mkdir -p ${NAME}-${VERSION}
+ @cp -R Makefile LICENSE config.mk \
+ ${SRC} ${NAME}-${VERSION}
+ @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION}
+ @gzip ${NAME}-${VERSION}.tar
+ @rm -rf ${NAME}-${VERSION}
+
+install: all
+ @echo installing executable file to ${DESTDIR}${PREFIX}/bin
+ @mkdir -p ${DESTDIR}${PREFIX}/bin
+ @cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
+ @chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
+
+uninstall:
+ @echo removing executable file from ${DESTDIR}${PREFIX}/bin
+ @rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
+
+.PHONY: all options clean dist install uninstall
diff --git a/keep/manual-programs/suckless/dwm-bak/dwmstatus/config.mk b/keep/manual-programs/suckless/dwm-bak/dwmstatus/config.mk
new file mode 100644
index 0000000..0ec9e47
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwmstatus/config.mk
@@ -0,0 +1,30 @@
+NAME = dwmstatus
+VERSION = 1.0
+
+# Customize below to fit your system
+
+# paths
+PREFIX = /usr
+MANPREFIX = ${PREFIX}/share/man
+
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+# includes and libs
+INCS = -I. -I/usr/include -I${X11INC}
+LIBS = -L/usr/lib -lc -L${X11LIB} -lX11
+
+# flags
+CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE
+CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
+#CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = -g ${LIBS}
+#LDFLAGS = -s ${LIBS}
+
+# Solaris
+#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
+#LDFLAGS = ${LIBS}
+
+# compiler and linker
+CC = cc
+
diff --git a/keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus-temperature.c b/keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus-temperature.c
new file mode 100644
index 0000000..13f27f6
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus-temperature.c
@@ -0,0 +1,15 @@
+/*
+ * gettemperature("/sys/class/hwmon/hwmon0/device", "temp1_input");
+ */
+
+char *
+gettemperature(char *base, char *sensor)
+{
+ char *co;
+
+ co = readfile(base, sensor);
+ if (co == NULL)
+ return smprintf("");
+ return smprintf("%02.0f°C", atof(co) / 1000);
+}
+
diff --git a/keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus.c b/keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus.c
new file mode 100644
index 0000000..d2a4b03
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwmstatus/dwmstatus.c
@@ -0,0 +1,227 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#define _BSD_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <X11/Xlib.h>
+
+char *tzargentina = "America/Buenos_Aires";
+char *tzutc = "UTC";
+char *tzberlin = "Europe/Berlin";
+
+static Display *dpy;
+
+char *
+smprintf(char *fmt, ...)
+{
+ va_list fmtargs;
+ char *ret;
+ int len;
+
+ va_start(fmtargs, fmt);
+ len = vsnprintf(NULL, 0, fmt, fmtargs);
+ va_end(fmtargs);
+
+ ret = malloc(++len);
+ if (ret == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+
+ va_start(fmtargs, fmt);
+ vsnprintf(ret, len, fmt, fmtargs);
+ va_end(fmtargs);
+
+ return ret;
+}
+
+void
+settz(char *tzname)
+{
+ setenv("TZ", tzname, 1);
+}
+
+char *
+mktimes(char *fmt, char *tzname)
+{
+ char buf[129];
+ time_t tim;
+ struct tm *timtm;
+
+ settz(tzname);
+ tim = time(NULL);
+ timtm = localtime(&tim);
+ if (timtm == NULL)
+ return smprintf("");
+
+ if (!strftime(buf, sizeof(buf)-1, fmt, timtm)) {
+ fprintf(stderr, "strftime == 0\n");
+ return smprintf("");
+ }
+
+ return smprintf("%s", buf);
+}
+
+void
+setstatus(char *str)
+{
+ XStoreName(dpy, DefaultRootWindow(dpy), str);
+ XSync(dpy, False);
+}
+
+char *
+loadavg(void)
+{
+ double avgs[3];
+
+ if (getloadavg(avgs, 3) < 0)
+ return smprintf("");
+
+ return smprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
+}
+
+char *
+readfile(char *base, char *file)
+{
+ char *path, line[513];
+ FILE *fd;
+
+ memset(line, 0, sizeof(line));
+
+ path = smprintf("%s/%s", base, file);
+ fd = fopen(path, "r");
+ free(path);
+ if (fd == NULL)
+ return NULL;
+
+ if (fgets(line, sizeof(line)-1, fd) == NULL)
+ return NULL;
+ fclose(fd);
+
+ return smprintf("%s", line);
+}
+
+char *
+getbattery(char *base)
+{
+ char *co, status;
+ int descap, remcap;
+
+ descap = -1;
+ remcap = -1;
+
+ co = readfile(base, "present");
+ if (co == NULL)
+ return smprintf("");
+ if (co[0] != '1') {
+ free(co);
+ return smprintf("not present");
+ }
+ free(co);
+
+ co = readfile(base, "charge_full_design");
+ if (co == NULL) {
+ co = readfile(base, "energy_full_design");
+ if (co == NULL)
+ return smprintf("");
+ }
+ sscanf(co, "%d", &descap);
+ free(co);
+
+ co = readfile(base, "charge_now");
+ if (co == NULL) {
+ co = readfile(base, "energy_now");
+ if (co == NULL)
+ return smprintf("");
+ }
+ sscanf(co, "%d", &remcap);
+ free(co);
+
+ co = readfile(base, "status");
+ if (!strncmp(co, "Discharging", 11)) {
+ status = '-';
+ } else if(!strncmp(co, "Charging", 8)) {
+ status = '+';
+ } else {
+ status = '?';
+ }
+
+ if (remcap < 0 || descap < 0)
+ return smprintf("invalid");
+
+ return smprintf("%.0f%%%c", ((float)remcap / (float)descap) * 100, status);
+}
+
+char *
+gettemperature(char *base, char *sensor)
+{
+ char *co;
+
+ co = readfile(base, sensor);
+ if (co == NULL)
+ return smprintf("");
+ return smprintf("%02.0f°C", atof(co) / 1000);
+}
+
+int
+main(void)
+{
+ char *status;
+ char *avgs;
+ char *bat;
+ char *bat1;
+ char *tmar;
+ char *tmutc;
+ char *tmbln;
+ char *t0, *t1, *t2;
+
+ if (!(dpy = XOpenDisplay(NULL))) {
+ fprintf(stderr, "dwmstatus: cannot open display.\n");
+ return 1;
+ }
+
+ for (;;sleep(60)) {
+ avgs = loadavg();
+ bat = getbattery("/sys/class/power_supply/BAT0");
+ bat1 = getbattery("/sys/class/power_supply/BAT1");
+ tmar = mktimes("%H:%M", tzargentina);
+ tmutc = mktimes("%H:%M", tzutc);
+ tmbln = mktimes("KW %W %a %d %b %H:%M %Z %Y", tzberlin);
+ t0 = gettemperature("/sys/devices/virtual/hwmon/hwmon0", "temp1_input");
+ t1 = gettemperature("/sys/devices/virtual/hwmon/hwmon2", "temp1_input");
+ t2 = gettemperature("/sys/devices/virtual/hwmon/hwmon4", "temp1_input");
+
+ status = smprintf("T:%s|%s|%s L:%s B:%s|%s A:%s U:%s %s",
+ t0, t1, t2, avgs, bat, bat1, tmar, tmutc,
+ tmbln);
+ setstatus(status);
+
+ free(t0);
+ free(t1);
+ free(t2);
+ free(avgs);
+ free(bat);
+ free(bat1);
+ free(tmar);
+ free(tmutc);
+ free(tmbln);
+ free(status);
+ }
+
+ XCloseDisplay(dpy);
+
+ return 0;
+}
+
diff --git a/keep/manual-programs/suckless/dwm-bak/dwmstatus/new-acpi-battery.c b/keep/manual-programs/suckless/dwm-bak/dwmstatus/new-acpi-battery.c
new file mode 100644
index 0000000..cda0084
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/dwmstatus/new-acpi-battery.c
@@ -0,0 +1,55 @@
+char *
+readfile(char *base, char *file)
+{
+ char *path, line[513];
+ FILE *fd;
+
+ memset(line, 0, sizeof(line));
+
+ path = smprintf("%s/%s", base, file);
+ fd = fopen(path, "r");
+ if (fd == NULL) {
+ perror("fopen");
+ exit(1);
+ }
+ free(path);
+
+ if (fgets(line, sizeof(line)-1, fd) == NULL) {
+ perror("fgets");
+ exit(1);
+ }
+ fclose(fd);
+
+ return smprintf("%s", line);
+}
+
+char *
+getbattery(char *base)
+{
+ char *co;
+ int descap, remcap;
+
+ descap = -1;
+ remcap = -1;
+
+ co = readfile(base, "present");
+ if (co[0] != '1') {
+ free(co);
+ return smprintf("not present");
+ }
+ free(co);
+
+ co = readfile(base, "charge_full_design");
+ sscanf(co, "%d", &descap);
+ free(co);
+
+ co = readfile(base, "charge_now");
+ sscanf(co, "%d", &remcap);
+ free(co);
+
+ if (remcap < 0 || descap < 0)
+ return smprintf("invalid");
+
+ return smprintf("%.0f", ((float)remcap / (float)descap) * 100);
+}
+
diff --git a/keep/manual-programs/suckless/dwm-bak/endx b/keep/manual-programs/suckless/dwm-bak/endx
new file mode 100644
index 0000000..982d44f
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/endx
@@ -0,0 +1,5 @@
+#!/bin/sh
+#gives you a dmenu prompt to confirm if you want to exit X
+
+[ $(echo -e "No\nYes" | dmenu -i -p "$1") \
+== "Yes" ] && $2
diff --git a/keep/manual-programs/suckless/dwm-bak/shiftview.c b/keep/manual-programs/suckless/dwm-bak/shiftview.c
new file mode 100644
index 0000000..e82053a
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/shiftview.c
@@ -0,0 +1,19 @@
+/** Function to shift the current view to the left/right
+ *
+ * @param: "arg->i" stores the number of tags to shift right (positive value)
+ * or left (negative value)
+ */
+void
+shiftview(const Arg *arg) {
+ Arg shifted;
+
+ if(arg->i > 0) // left circular shift
+ shifted.ui = (selmon->tagset[selmon->seltags] << arg->i)
+ | (selmon->tagset[selmon->seltags] >> (LENGTH(tags) - arg->i));
+
+ else // right circular shift
+ shifted.ui = selmon->tagset[selmon->seltags] >> (- arg->i)
+ | selmon->tagset[selmon->seltags] << (LENGTH(tags) + arg->i);
+
+ view(&shifted);
+}
diff --git a/keep/manual-programs/suckless/dwm-bak/transient.c b/keep/manual-programs/suckless/dwm-bak/transient.c
new file mode 100644
index 0000000..040adb5
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/transient.c
@@ -0,0 +1,42 @@
+/* cc transient.c -o transient -lX11 */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main(void) {
+ Display *d;
+ Window r, f, t = None;
+ XSizeHints h;
+ XEvent e;
+
+ d = XOpenDisplay(NULL);
+ if (!d)
+ exit(1);
+ r = DefaultRootWindow(d);
+
+ f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0);
+ h.min_width = h.max_width = h.min_height = h.max_height = 400;
+ h.flags = PMinSize | PMaxSize;
+ XSetWMNormalHints(d, f, &h);
+ XStoreName(d, f, "floating");
+ XMapWindow(d, f);
+
+ XSelectInput(d, f, ExposureMask);
+ while (1) {
+ XNextEvent(d, &e);
+
+ if (t == None) {
+ sleep(5);
+ t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0);
+ XSetTransientForHint(d, t, f);
+ XStoreName(d, t, "transient");
+ XMapWindow(d, t);
+ XSelectInput(d, t, ExposureMask);
+ }
+ }
+
+ XCloseDisplay(d);
+ exit(0);
+}
diff --git a/keep/manual-programs/suckless/dwm-bak/util.c b/keep/manual-programs/suckless/dwm-bak/util.c
new file mode 100644
index 0000000..fe044fc
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/util.c
@@ -0,0 +1,35 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = calloc(nmemb, size)))
+ die("calloc:");
+ return p;
+}
+
+void
+die(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
diff --git a/keep/manual-programs/suckless/dwm-bak/util.h b/keep/manual-programs/suckless/dwm-bak/util.h
new file mode 100644
index 0000000..f633b51
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/util.h
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+
+void die(const char *fmt, ...);
+void *ecalloc(size_t nmemb, size_t size);
diff --git a/keep/manual-programs/suckless/dwm-bak/util.o b/keep/manual-programs/suckless/dwm-bak/util.o
new file mode 100644
index 0000000..8e55fa1
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm-bak/util.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm/LICENSE b/keep/manual-programs/suckless/dwm/LICENSE
new file mode 100644
index 0000000..d221f09
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/LICENSE
@@ -0,0 +1,37 @@
+MIT/X Consortium License
+
+© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
+© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
+© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
+© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
+© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
+© 2007-2009 Christof Musik <christof at sendfax dot de>
+© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
+© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
+© 2008 Martin Hurton <martin dot hurton at gmail dot com>
+© 2008 Neale Pickett <neale dot woozle dot org>
+© 2009 Mate Nagy <mnagy at port70 dot net>
+© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2011 Christoph Lohmann <20h@r-36.net>
+© 2015-2016 Quentin Rameau <quinq@fifth.space>
+© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
+© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/keep/manual-programs/suckless/dwm/Makefile b/keep/manual-programs/suckless/dwm/Makefile
new file mode 100644
index 0000000..77bcbc0
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/Makefile
@@ -0,0 +1,51 @@
+# dwm - dynamic window manager
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+SRC = drw.c dwm.c util.c
+OBJ = ${SRC:.c=.o}
+
+all: options dwm
+
+options:
+ @echo dwm build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+.c.o:
+ ${CC} -c ${CFLAGS} $<
+
+${OBJ}: config.h config.mk
+
+config.h:
+ cp config.def.h $@
+
+dwm: ${OBJ}
+ ${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+clean:
+ rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
+
+dist: clean
+ mkdir -p dwm-${VERSION}
+ cp -R LICENSE Makefile README config.def.h config.mk\
+ dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION}
+ tar -cf dwm-${VERSION}.tar dwm-${VERSION}
+ gzip dwm-${VERSION}.tar
+ rm -rf dwm-${VERSION}
+
+install: all
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ cp -f dwm ${DESTDIR}${PREFIX}/bin
+ chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
+ mkdir -p ${DESTDIR}${MANPREFIX}/man1
+ sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
+ chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
+
+uninstall:
+ rm -f ${DESTDIR}${PREFIX}/bin/dwm\
+ ${DESTDIR}${MANPREFIX}/man1/dwm.1
+
+.PHONY: all options clean dist install uninstall
diff --git a/keep/manual-programs/suckless/dwm/README b/keep/manual-programs/suckless/dwm/README
new file mode 100644
index 0000000..95d4fd0
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/README
@@ -0,0 +1,48 @@
+dwm - dynamic window manager
+============================
+dwm is an extremely fast, small, and dynamic window manager for X.
+
+
+Requirements
+------------
+In order to build dwm you need the Xlib header files.
+
+
+Installation
+------------
+Edit config.mk to match your local setup (dwm is installed into
+the /usr/local namespace by default).
+
+Afterwards enter the following command to build and install dwm (if
+necessary as root):
+
+ make clean install
+
+
+Running dwm
+-----------
+Add the following line to your .xinitrc to start dwm using startx:
+
+ exec dwm
+
+In order to connect dwm to a specific display, make sure that
+the DISPLAY environment variable is set correctly, e.g.:
+
+ DISPLAY=foo.bar:1 exec dwm
+
+(This will start dwm on display :1 of the host foo.bar.)
+
+In order to display status info in the bar, you can do something
+like this in your .xinitrc:
+
+ while xsetroot -name "`date` `uptime | sed 's/.*,//'`"
+ do
+ sleep 1
+ done &
+ exec dwm
+
+
+Configuration
+-------------
+The configuration of dwm is done by creating a custom config.h
+and (re)compiling the source code.
diff --git a/keep/manual-programs/suckless/dwm/awesome-glyphs.txt b/keep/manual-programs/suckless/dwm/awesome-glyphs.txt
new file mode 100644
index 0000000..7541568
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/awesome-glyphs.txt
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/keep/manual-programs/suckless/dwm/config.def.h.orig b/keep/manual-programs/suckless/dwm/config.def.h.orig
new file mode 100644
index 0000000..c9b7665
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/config.def.h.orig
@@ -0,0 +1,146 @@
+/* See LICENSE file for copyright and license details. */
+
+/* appearance */
+static const unsigned int borderpx = 2; /* border pixel size of windows */
+static const unsigned int gappx = 5; /* gaps size between windows */
+static const unsigned int snap = 32; /* snap pixel */
+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
+static const unsigned int systrayspacing = 2; /* systray spacing */
+static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
+static const int showsystray = 1; /* 0 means no systray */
+static const int showbar = 1; /* 0 means no bar */
+static const int topbar = 0; /* 0 means bottom bar */
+static const char *fonts[] = { "monospace:size=12", "fontawesome:size=12" };
+static const char dmenufont[] = "monospace:size=11";
+//background color
+static const char col_gray1[] = "#222222";
+//inactive window border color
+static const char col_gray2[] = "#444444";
+//font color
+static const char col_gray3[] = "#bbbbbb";
+//current tag and current window font color
+static const char col_gray4[] = "#eeeeee";
+//Top bar second color (blue) and active window border color
+static const char col_cyan[] = "#8558e6";
+static const char *colors[][3] = {
+ /* fg bg border */
+ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
+ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
+};
+
+/* tagging */
+//tag names (upper left)
+static const char *tags[] = { "", "", "", "", "", "", "", "", "", "" };
+
+static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+ /* class instance title tags mask isfloating monitor */
+ { "Gimp", NULL, NULL, 0, 1, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 1, -1 },
+};
+
+/* layout(s) */
+static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+static const int nmaster = 1; /* number of clients in master area */
+static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
+
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* key definitions */
+#define MODKEY Mod4Mask
+#define TAGKEYS(KEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
+static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+//static const char *filemanager[] = { "
+//launches htop
+static const char *monitor[] = { "/usr/bin/htop", NULL };
+//sets st as the default terminal
+//static const char *termcmd[] = { "st", NULL };
+//sets urxvt as the default terminal
+static const char *termcmd[] = { "urxvt", NULL };
+//volume controls
+static const char *upvol[] = { "amixer", "-q", "set", "Master", "5%+", "unmute", NULL };
+static const char *downvol[] = { "amixer", "-q", "set", "Master", "5%-", "unmute", NULL };
+static const char *mutevol[] = { "amixer", "-q", "set", "Master", "toggle", NULL };
+
+#include "shiftview.c"
+static char *endx[] = { "/bin/sh", "-c", "endx", "externalpipe", NULL };
+static Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XK_d, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_t, togglebar, {0} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+ { MODKEY, XK_u, incnmaster, {.i = -1 } },
+ { MODKEY, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY, XK_z, zoom, {0} },
+ { MODKEY, XK_Tab, view, {0} },
+ { MODKEY, XK_q, killclient, {0} },
+ { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY|ShiftMask, XK_space, setlayout, {0} },
+ { MODKEY, XK_space, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, XK_comma, focusmon, {.i = -1 } },
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY, XK_minus, setgaps, {.i = -1 } },
+ { MODKEY, XK_equal, setgaps, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY, XK_n, shiftview, { .i = +1 } },
+ { MODKEY, XK_b, shiftview, { .i = -1 } },
+ { MODKEY, XK_F8, spawn, {.v = upvol } },
+ { MODKEY, XK_F7, spawn, {.v = downvol } },
+ { MODKEY, XK_F5, spawn, {.v = mutevol } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
+};
+
+/* button definitions */
+/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
+static Button buttons[] = {
+ /* click event mask button function argument */
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+ { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+};
+
diff --git a/keep/manual-programs/suckless/dwm/config.h b/keep/manual-programs/suckless/dwm/config.h
new file mode 100644
index 0000000..6d75362
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/config.h
@@ -0,0 +1,151 @@
+/* See LICENSE file for copyright and license details. */
+
+/* appearance */
+static const unsigned int borderpx = 2; /* border pixel size of windows */
+static const unsigned int gappx = 5; /* gaps size between windows */
+static const unsigned int snap = 32; /* snap pixel */
+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
+static const unsigned int systrayspacing = 2; /* systray spacing */
+static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
+static const int showsystray = 1; /* 0 means no systray */
+static const int showbar = 1; /* 0 means no bar */
+static const int topbar = 0; /* 0 means bottom bar */
+static const char *fonts[] = { "monospace:size=11", "fontawesome:size=11" };
+static const char dmenufont[] = "monospace:size=10";
+//background color
+static const char col_gray1[] = "#222222";
+//inactive window border color
+static const char col_gray2[] = "#444444";
+//font color
+static const char col_gray3[] = "#bbbbbb";
+//current tag and current window font color
+static const char col_gray4[] = "#eeeeee";
+//Top bar second color (blue) and active window border color
+static const char col_cyan[] = "#8558e6";
+static const char *colors[][3] = {
+ /* fg bg border */
+ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
+ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
+};
+
+/* tagging */
+//tag names (upper left)
+static const char *tags[] = { "", "", "", "", "", "", "", "", "", "" };
+
+static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+ /* class instance title tags mask isfloating monitor */
+ { "Gimp", NULL, NULL, 0, 1, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 1, -1 },
+};
+
+/* layout(s) */
+static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
+static const int nmaster = 1; /* number of clients in master area */
+static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
+
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+ { "TTT", bstack },
+ { "===", bstackhoriz },
+};
+
+/* key definitions */
+#define MODKEY Mod4Mask
+#define TAGKEYS(KEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
+//static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+static const char *dmenucmd[] = { "rofi", "-show", "drun"};
+//static const char *filemanager[] = { "
+//launches htop
+static const char *monitor[] = { "/usr/bin/htop", NULL };
+//sets st as the default terminal
+//static const char *termcmd[] = { "st", NULL };
+//sets urxvt as the default terminal
+static const char *termcmd[] = { "wezterm", NULL };
+//volume controls
+static const char *upvol[] = { "amixer", "-q", "set", "Master", "5%+", "unmute", NULL };
+static const char *downvol[] = { "amixer", "-q", "set", "Master", "5%-", "unmute", NULL };
+static const char *mutevol[] = { "amixer", "-q", "set", "Master", "toggle", NULL };
+
+#include "shiftview.c"
+static char *endx[] = { "/bin/sh", "-c", "endx", "externalpipe", NULL };
+static Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XK_d, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_t, togglebar, {0} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, XK_i, incnmaster, {.i = +1 } },
+ { MODKEY, XK_u, incnmaster, {.i = -1 } },
+ { MODKEY, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY, XK_z, zoom, {0} },
+ { MODKEY, XK_Tab, view, {0} },
+ { MODKEY, XK_q, killclient, {0} },
+ { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY|ShiftMask, XK_u, setlayout, {.v = &layouts[3]} },
+ { MODKEY|ShiftMask, XK_o, setlayout, {.v = &layouts[4]} },
+ { MODKEY|ShiftMask, XK_space, setlayout, {0} },
+ { MODKEY, XK_space, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, XK_comma, focusmon, {.i = -1 } },
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY, XK_minus, setgaps, {.i = -1 } },
+ { MODKEY, XK_equal, setgaps, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY, XK_n, shiftview, { .i = +1 } },
+ { MODKEY, XK_b, shiftview, { .i = -1 } },
+ { MODKEY, XK_F8, spawn, {.v = upvol } },
+ { MODKEY, XK_F7, spawn, {.v = downvol } },
+ { MODKEY, XK_F5, spawn, {.v = mutevol } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
+};
+
+/* button definitions */
+/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
+static Button buttons[] = {
+ /* click event mask button function argument */
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+ { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+};
+
diff --git a/keep/manual-programs/suckless/dwm/config.mk b/keep/manual-programs/suckless/dwm/config.mk
new file mode 100644
index 0000000..6d36cb7
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/config.mk
@@ -0,0 +1,38 @@
+# dwm version
+VERSION = 6.2
+
+# Customize below to fit your system
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+# Xinerama, comment if you don't want it
+XINERAMALIBS = -lXinerama
+XINERAMAFLAGS = -DXINERAMA
+
+# freetype
+FREETYPELIBS = -lfontconfig -lXft
+FREETYPEINC = /usr/include/freetype2
+# OpenBSD (uncomment)
+#FREETYPEINC = ${X11INC}/freetype2
+
+# includes and libs
+INCS = -I${X11INC} -I${FREETYPEINC}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+
+# flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
+#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
+CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = ${LIBS}
+
+# Solaris
+#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
+#LDFLAGS = ${LIBS}
+
+# compiler and linker
+CC = cc
diff --git a/keep/manual-programs/suckless/dwm/drw.c b/keep/manual-programs/suckless/dwm/drw.c
new file mode 100644
index 0000000..8fd1ca4
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/drw.c
@@ -0,0 +1,435 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+#define UTF_INVALID 0xFFFD
+#define UTF_SIZ 4
+
+static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+static long
+utf8decodebyte(const char c, size_t *i)
+{
+ for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
+ if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
+ return (unsigned char)c & ~utfmask[*i];
+ return 0;
+}
+
+static size_t
+utf8validate(long *u, size_t i)
+{
+ if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+ *u = UTF_INVALID;
+ for (i = 1; *u > utfmax[i]; ++i)
+ ;
+ return i;
+}
+
+static size_t
+utf8decode(const char *c, long *u, size_t clen)
+{
+ size_t i, j, len, type;
+ long udecoded;
+
+ *u = UTF_INVALID;
+ if (!clen)
+ return 0;
+ udecoded = utf8decodebyte(c[0], &len);
+ if (!BETWEEN(len, 1, UTF_SIZ))
+ return 1;
+ for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+ udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
+ if (type)
+ return j;
+ }
+ if (j < len)
+ return 0;
+ *u = udecoded;
+ utf8validate(u, len);
+
+ return len;
+}
+
+Drw *
+drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
+{
+ Drw *drw = ecalloc(1, sizeof(Drw));
+
+ drw->dpy = dpy;
+ drw->screen = screen;
+ drw->root = root;
+ drw->w = w;
+ drw->h = h;
+ drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+ drw->gc = XCreateGC(dpy, root, 0, NULL);
+ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+ return drw;
+}
+
+void
+drw_resize(Drw *drw, unsigned int w, unsigned int h)
+{
+ if (!drw)
+ return;
+
+ drw->w = w;
+ drw->h = h;
+ if (drw->drawable)
+ XFreePixmap(drw->dpy, drw->drawable);
+ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+}
+
+void
+drw_free(Drw *drw)
+{
+ XFreePixmap(drw->dpy, drw->drawable);
+ XFreeGC(drw->dpy, drw->gc);
+ free(drw);
+}
+
+/* This function is an implementation detail. Library users should use
+ * drw_fontset_create instead.
+ */
+static Fnt *
+xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
+{
+ Fnt *font;
+ XftFont *xfont = NULL;
+ FcPattern *pattern = NULL;
+
+ if (fontname) {
+ /* Using the pattern found at font->xfont->pattern does not yield the
+ * same substitution results as using the pattern returned by
+ * FcNameParse; using the latter results in the desired fallback
+ * behaviour whereas the former just results in missing-character
+ * rectangles being drawn, at least with some fonts. */
+ if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
+ fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
+ return NULL;
+ }
+ if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
+ fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
+ XftFontClose(drw->dpy, xfont);
+ return NULL;
+ }
+ } else if (fontpattern) {
+ if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
+ fprintf(stderr, "error, cannot load font from pattern.\n");
+ return NULL;
+ }
+ } else {
+ die("no font specified.");
+ }
+
+ /* Do not allow using color fonts. This is a workaround for a BadLength
+ * error from Xft with color glyphs. Modelled on the Xterm workaround. See
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
+ * https://lists.suckless.org/dev/1701/30932.html
+ * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
+ * and lots more all over the internet.
+ */
+ FcBool iscol;
+ if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
+ XftFontClose(drw->dpy, xfont);
+ return NULL;
+ }
+
+ font = ecalloc(1, sizeof(Fnt));
+ font->xfont = xfont;
+ font->pattern = pattern;
+ font->h = xfont->ascent + xfont->descent;
+ font->dpy = drw->dpy;
+
+ return font;
+}
+
+static void
+xfont_free(Fnt *font)
+{
+ if (!font)
+ return;
+ if (font->pattern)
+ FcPatternDestroy(font->pattern);
+ XftFontClose(font->dpy, font->xfont);
+ free(font);
+}
+
+Fnt*
+drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
+{
+ Fnt *cur, *ret = NULL;
+ size_t i;
+
+ if (!drw || !fonts)
+ return NULL;
+
+ for (i = 1; i <= fontcount; i++) {
+ if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
+ cur->next = ret;
+ ret = cur;
+ }
+ }
+ return (drw->fonts = ret);
+}
+
+void
+drw_fontset_free(Fnt *font)
+{
+ if (font) {
+ drw_fontset_free(font->next);
+ xfont_free(font);
+ }
+}
+
+void
+drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+{
+ if (!drw || !dest || !clrname)
+ return;
+
+ if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen),
+ clrname, dest))
+ die("error, cannot allocate color '%s'", clrname);
+}
+
+/* Wrapper to create color schemes. The caller has to call free(3) on the
+ * returned color scheme when done using it. */
+Clr *
+drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+{
+ size_t i;
+ Clr *ret;
+
+ /* need at least two colors for a scheme */
+ if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
+ return NULL;
+
+ for (i = 0; i < clrcount; i++)
+ drw_clr_create(drw, &ret[i], clrnames[i]);
+ return ret;
+}
+
+void
+drw_setfontset(Drw *drw, Fnt *set)
+{
+ if (drw)
+ drw->fonts = set;
+}
+
+void
+drw_setscheme(Drw *drw, Clr *scm)
+{
+ if (drw)
+ drw->scheme = scm;
+}
+
+void
+drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
+{
+ if (!drw || !drw->scheme)
+ return;
+ XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
+ if (filled)
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ else
+ XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
+}
+
+int
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
+{
+ char buf[1024];
+ int ty;
+ unsigned int ew;
+ XftDraw *d = NULL;
+ Fnt *usedfont, *curfont, *nextfont;
+ size_t i, len;
+ int utf8strlen, utf8charlen, render = x || y || w || h;
+ long utf8codepoint = 0;
+ const char *utf8str;
+ FcCharSet *fccharset;
+ FcPattern *fcpattern;
+ FcPattern *match;
+ XftResult result;
+ int charexists = 0;
+
+ if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
+ return 0;
+
+ if (!render) {
+ w = ~w;
+ } else {
+ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ d = XftDrawCreate(drw->dpy, drw->drawable,
+ DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen));
+ x += lpad;
+ w -= lpad;
+ }
+
+ usedfont = drw->fonts;
+ while (1) {
+ utf8strlen = 0;
+ utf8str = text;
+ nextfont = NULL;
+ while (*text) {
+ utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+ for (curfont = drw->fonts; curfont; curfont = curfont->next) {
+ charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
+ if (charexists) {
+ if (curfont == usedfont) {
+ utf8strlen += utf8charlen;
+ text += utf8charlen;
+ } else {
+ nextfont = curfont;
+ }
+ break;
+ }
+ }
+
+ if (!charexists || nextfont)
+ break;
+ else
+ charexists = 0;
+ }
+
+ if (utf8strlen) {
+ drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
+ /* shorten text if necessary */
+ for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
+ drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
+
+ if (len) {
+ memcpy(buf, utf8str, len);
+ buf[len] = '\0';
+ if (len < utf8strlen)
+ for (i = len; i && i > len - 3; buf[--i] = '.')
+ ; /* NOP */
+
+ if (render) {
+ ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
+ XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
+ usedfont->xfont, x, ty, (XftChar8 *)buf, len);
+ }
+ x += ew;
+ w -= ew;
+ }
+ }
+
+ if (!*text) {
+ break;
+ } else if (nextfont) {
+ charexists = 0;
+ usedfont = nextfont;
+ } else {
+ /* Regardless of whether or not a fallback font is found, the
+ * character must be drawn. */
+ charexists = 1;
+
+ fccharset = FcCharSetCreate();
+ FcCharSetAddChar(fccharset, utf8codepoint);
+
+ if (!drw->fonts->pattern) {
+ /* Refer to the comment in xfont_create for more information. */
+ die("the first font in the cache must be loaded from a font string.");
+ }
+
+ fcpattern = FcPatternDuplicate(drw->fonts->pattern);
+ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+ FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
+ FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
+
+ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+ FcDefaultSubstitute(fcpattern);
+ match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
+
+ FcCharSetDestroy(fccharset);
+ FcPatternDestroy(fcpattern);
+
+ if (match) {
+ usedfont = xfont_create(drw, NULL, match);
+ if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
+ for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
+ ; /* NOP */
+ curfont->next = usedfont;
+ } else {
+ xfont_free(usedfont);
+ usedfont = drw->fonts;
+ }
+ }
+ }
+ }
+ if (d)
+ XftDrawDestroy(d);
+
+ return x + (render ? w : 0);
+}
+
+void
+drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
+{
+ if (!drw)
+ return;
+
+ XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
+ XSync(drw->dpy, False);
+}
+
+unsigned int
+drw_fontset_getwidth(Drw *drw, const char *text)
+{
+ if (!drw || !drw->fonts || !text)
+ return 0;
+ return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
+}
+
+void
+drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
+{
+ XGlyphInfo ext;
+
+ if (!font || !text)
+ return;
+
+ XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+ if (w)
+ *w = ext.xOff;
+ if (h)
+ *h = font->h;
+}
+
+Cur *
+drw_cur_create(Drw *drw, int shape)
+{
+ Cur *cur;
+
+ if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
+ return NULL;
+
+ cur->cursor = XCreateFontCursor(drw->dpy, shape);
+
+ return cur;
+}
+
+void
+drw_cur_free(Drw *drw, Cur *cursor)
+{
+ if (!cursor)
+ return;
+
+ XFreeCursor(drw->dpy, cursor->cursor);
+ free(cursor);
+}
diff --git a/keep/manual-programs/suckless/dwm/drw.h b/keep/manual-programs/suckless/dwm/drw.h
new file mode 100644
index 0000000..4bcd5ad
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/drw.h
@@ -0,0 +1,57 @@
+/* See LICENSE file for copyright and license details. */
+
+typedef struct {
+ Cursor cursor;
+} Cur;
+
+typedef struct Fnt {
+ Display *dpy;
+ unsigned int h;
+ XftFont *xfont;
+ FcPattern *pattern;
+ struct Fnt *next;
+} Fnt;
+
+enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
+typedef XftColor Clr;
+
+typedef struct {
+ unsigned int w, h;
+ Display *dpy;
+ int screen;
+ Window root;
+ Drawable drawable;
+ GC gc;
+ Clr *scheme;
+ Fnt *fonts;
+} Drw;
+
+/* Drawable abstraction */
+Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
+void drw_resize(Drw *drw, unsigned int w, unsigned int h);
+void drw_free(Drw *drw);
+
+/* Fnt abstraction */
+Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
+void drw_fontset_free(Fnt* set);
+unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
+
+/* Colorscheme abstraction */
+void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
+Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
+
+/* Cursor abstraction */
+Cur *drw_cur_create(Drw *drw, int shape);
+void drw_cur_free(Drw *drw, Cur *cursor);
+
+/* Drawing context manipulation */
+void drw_setfontset(Drw *drw, Fnt *set);
+void drw_setscheme(Drw *drw, Clr *scm);
+
+/* Drawing functions */
+void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+
+/* Map functions */
+void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/keep/manual-programs/suckless/dwm/drw.o b/keep/manual-programs/suckless/dwm/drw.o
new file mode 100644
index 0000000..ec6e29f
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/drw.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm/dwm b/keep/manual-programs/suckless/dwm/dwm
new file mode 100755
index 0000000..705dfb9
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm/dwm-autostart-20161205-bb3bd6f.diff b/keep/manual-programs/suckless/dwm/dwm-autostart-20161205-bb3bd6f.diff
new file mode 100644
index 0000000..6f11eaf
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm-autostart-20161205-bb3bd6f.diff
@@ -0,0 +1,39 @@
+commit 5918623c5bd7fda155bf9dc3d33890c4ae1722d0
+Author: Simon Bremer <simon.bremer@tum.de>
+Date: Thu Dec 22 17:31:07 2016 +0100
+
+ Applied and fixed autostart patch for previous version;
+
+diff --git a/dwm.c b/dwm.c
+index d27cb67..066ed71 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h);
+ static void resizemouse(const Arg *arg);
+ static void restack(Monitor *m);
+ static void run(void);
++static void runAutostart(void);
+ static void scan(void);
+ static int sendevent(Client *c, Atom proto);
+ static void sendmon(Client *c, Monitor *m);
+@@ -1386,6 +1387,12 @@ run(void)
+ }
+
+ void
++runAutostart(void) {
++ system("cd ~/.dwm; ./autostart_blocking.sh");
++ system("cd ~/.dwm; ./autostart.sh &");
++}
++
++void
+ scan(void)
+ {
+ unsigned int i, num;
+@@ -2145,6 +2152,7 @@ main(int argc, char *argv[])
+ checkotherwm();
+ setup();
+ scan();
++ runAutostart();
+ run();
+ cleanup();
+ XCloseDisplay(dpy);
diff --git a/keep/manual-programs/suckless/dwm/dwm-bottomstack-6.1.diff b/keep/manual-programs/suckless/dwm/dwm-bottomstack-6.1.diff
new file mode 100644
index 0000000..9fdffd5
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm-bottomstack-6.1.diff
@@ -0,0 +1,101 @@
+diff --git a/config.def.h b/config.def.h
+index 7054c06..554f1db 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -39,6 +39,8 @@ static const Layout layouts[] = {
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
++ { "TTT", bstack },
++ { "===", bstackhoriz },
+ };
+
+ /* key definitions */
+@@ -74,6 +76,8 @@ static Key keys[] = {
+ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
++ { MODKEY, XK_u, setlayout, {.v = &layouts[3]} },
++ { MODKEY, XK_o, setlayout, {.v = &layouts[4]} },
+ { MODKEY, XK_space, setlayout, {0} },
+ { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+diff --git a/dwm.c b/dwm.c
+index 0362114..c313b5e 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -233,6 +233,8 @@ static int xerror(Display *dpy, XErrorEvent *ee);
+ static int xerrordummy(Display *dpy, XErrorEvent *ee);
+ static int xerrorstart(Display *dpy, XErrorEvent *ee);
+ static void zoom(const Arg *arg);
++static void bstack(Monitor *m);
++static void bstackhoriz(Monitor *m);
+
+ /* variables */
+ static const char broken[] = "broken";
+@@ -2139,3 +2141,65 @@ main(int argc, char *argv[])
+ XCloseDisplay(dpy);
+ return EXIT_SUCCESS;
+ }
++
++static void
++bstack(Monitor *m) {
++ int w, h, mh, mx, tx, ty, tw;
++ unsigned int i, n;
++ Client *c;
++
++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
++ if (n == 0)
++ return;
++ if (n > m->nmaster) {
++ mh = m->nmaster ? m->mfact * m->wh : 0;
++ tw = m->ww / (n - m->nmaster);
++ ty = m->wy + mh;
++ } else {
++ mh = m->wh;
++ tw = m->ww;
++ ty = m->wy;
++ }
++ for (i = mx = 0, tx = m->wx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
++ if (i < m->nmaster) {
++ w = (m->ww - mx) / (MIN(n, m->nmaster) - i);
++ resize(c, m->wx + mx, m->wy, w - (2 * c->bw), mh - (2 * c->bw), 0);
++ mx += WIDTH(c);
++ } else {
++ h = m->wh - mh;
++ resize(c, tx, ty, tw - (2 * c->bw), h - (2 * c->bw), 0);
++ if (tw != m->ww)
++ tx += WIDTH(c);
++ }
++ }
++}
++
++static void
++bstackhoriz(Monitor *m) {
++ int w, mh, mx, tx, ty, th;
++ unsigned int i, n;
++ Client *c;
++
++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
++ if (n == 0)
++ return;
++ if (n > m->nmaster) {
++ mh = m->nmaster ? m->mfact * m->wh : 0;
++ th = (m->wh - mh) / (n - m->nmaster);
++ ty = m->wy + mh;
++ } else {
++ th = mh = m->wh;
++ ty = m->wy;
++ }
++ for (i = mx = 0, tx = m->wx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
++ if (i < m->nmaster) {
++ w = (m->ww - mx) / (MIN(n, m->nmaster) - i);
++ resize(c, m->wx + mx, m->wy, w - (2 * c->bw), mh - (2 * c->bw), 0);
++ mx += WIDTH(c);
++ } else {
++ resize(c, tx, ty, m->ww - (2 * c->bw), th - (2 * c->bw), 0);
++ if (th != m->wh)
++ ty += HEIGHT(c);
++ }
++ }
++}
diff --git a/keep/manual-programs/suckless/dwm/dwm-fullgaps-6.2.diff b/keep/manual-programs/suckless/dwm/dwm-fullgaps-6.2.diff
new file mode 100644
index 0000000..7206aec
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm-fullgaps-6.2.diff
@@ -0,0 +1,95 @@
+diff --git a/config.def.h b/config.def.h
+index 1c0b587..38d2f6c 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,7 @@
+
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
++static const unsigned int gappx = 5; /* gaps between windows */
+ static const unsigned int snap = 32; /* snap pixel */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
+@@ -84,6 +85,9 @@ static Key keys[] = {
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
++ { MODKEY, XK_minus, setgaps, {.i = -1 } },
++ { MODKEY, XK_equal, setgaps, {.i = +1 } },
++ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+diff --git a/dwm.c b/dwm.c
+index 4465af1..4363627 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -119,6 +119,7 @@ struct Monitor {
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
++ int gappx; /* gaps between windows */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+@@ -199,6 +200,7 @@ static void sendmon(Client *c, Monitor *m);
+ static void setclientstate(Client *c, long state);
+ static void setfocus(Client *c);
+ static void setfullscreen(Client *c, int fullscreen);
++static void setgaps(const Arg *arg);
+ static void setlayout(const Arg *arg);
+ static void setmfact(const Arg *arg);
+ static void setup(void);
+@@ -638,6 +640,7 @@ createmon(void)
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+ m->topbar = topbar;
++ m->gappx = gappx;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+@@ -1497,6 +1500,16 @@ setfullscreen(Client *c, int fullscreen)
+ }
+ }
+
++void
++setgaps(const Arg *arg)
++{
++ if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
++ selmon->gappx = 0;
++ else
++ selmon->gappx += arg->i;
++ arrange(selmon);
++}
++
+ void
+ setlayout(const Arg *arg)
+ {
+@@ -1683,16 +1696,16 @@ tile(Monitor *m)
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->ww * m->mfact : 0;
+ else
+- mw = m->ww;
+- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
++ mw = m->ww - m->gappx;
++ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+- h = (m->wh - my) / (MIN(n, m->nmaster) - i);
+- resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0);
+- my += HEIGHT(c);
++ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
++ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
++ my += HEIGHT(c) + m->gappx;
+ } else {
+- h = (m->wh - ty) / (n - i);
+- resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
+- ty += HEIGHT(c);
++ h = (m->wh - ty) / (n - i) - m->gappx;
++ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
++ ty += HEIGHT(c) + m->gappx;
+ }
+ }
+
+--
+2.20.1
+
diff --git a/keep/manual-programs/suckless/dwm/dwm-systray-6.2.diff b/keep/manual-programs/suckless/dwm/dwm-systray-6.2.diff
new file mode 100644
index 0000000..27187ac
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm-systray-6.2.diff
@@ -0,0 +1,746 @@
+From 4001ccae7b1a41bdcb247b0cf095a51af7b68c28 Mon Sep 17 00:00:00 2001
+From: Igor Gevka <igor.gevka@gmail.com>
+Date: Sun, 16 Feb 2020 15:03:10 -0800
+Subject: [PATCH] [PATCH] Implements a system tray for dwm.
+
+Original author: Jan Christoph Ebersbach <jceb@e-jc.de>, inspired by http://code.google.com/p/dwm-plus
+URL: http://dwm.suckless.org/patches/systray
+dwm 6.2 port by Igor Gevka <igor.gevka@gmail.com>
+---
+ config.def.h | 4 +
+ dwm.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 382 insertions(+), 26 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 1c0b587..2d824d1 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -3,6 +3,10 @@
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
++static const unsigned int systrayspacing = 2; /* systray spacing */
++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
++static const int showsystray = 1; /* 0 means no systray */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
+ static const char *fonts[] = { "monospace:size=10" };
+diff --git a/dwm.c b/dwm.c
+index 4465af1..3e361fa 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -57,12 +57,30 @@
+ #define TAGMASK ((1 << LENGTH(tags)) - 1)
+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
++#define SYSTEM_TRAY_REQUEST_DOCK 0
++
++/* XEMBED messages */
++#define XEMBED_EMBEDDED_NOTIFY 0
++#define XEMBED_WINDOW_ACTIVATE 1
++#define XEMBED_FOCUS_IN 4
++#define XEMBED_MODALITY_ON 10
++
++#define XEMBED_MAPPED (1 << 0)
++#define XEMBED_WINDOW_ACTIVATE 1
++#define XEMBED_WINDOW_DEACTIVATE 2
++
++#define VERSION_MAJOR 0
++#define VERSION_MINOR 0
++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
++
+ /* enums */
+ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+ enum { SchemeNorm, SchemeSel }; /* color schemes */
+ enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+@@ -141,6 +159,12 @@ typedef struct {
+ int monitor;
+ } Rule;
+
++typedef struct Systray Systray;
++struct Systray {
++ Window win;
++ Client *icons;
++};
++
+ /* function declarations */
+ static void applyrules(Client *c);
+ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+@@ -169,8 +193,10 @@ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
++static Atom getatomprop(Client *c, Atom prop);
+ static int getrootptr(int *x, int *y);
+ static long getstate(Window w);
++static unsigned int getsystraywidth();
+ static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
+ static void grabbuttons(Client *c, int focused);
+ static void grabkeys(void);
+@@ -188,13 +214,16 @@ static void pop(Client *);
+ static void propertynotify(XEvent *e);
+ static void quit(const Arg *arg);
+ static Monitor *recttomon(int x, int y, int w, int h);
++static void removesystrayicon(Client *i);
+ static void resize(Client *c, int x, int y, int w, int h, int interact);
++static void resizebarwin(Monitor *m);
+ static void resizeclient(Client *c, int x, int y, int w, int h);
+ static void resizemouse(const Arg *arg);
++static void resizerequest(XEvent *e);
+ static void restack(Monitor *m);
+ static void run(void);
+ static void scan(void);
+-static int sendevent(Client *c, Atom proto);
++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+ static void sendmon(Client *c, Monitor *m);
+ static void setclientstate(Client *c, long state);
+ static void setfocus(Client *c);
+@@ -206,6 +235,7 @@ static void seturgent(Client *c, int urg);
+ static void showhide(Client *c);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static Monitor *systraytomon(Monitor *m);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *);
+@@ -223,18 +253,23 @@ static int updategeom(void);
+ static void updatenumlockmask(void);
+ static void updatesizehints(Client *c);
+ static void updatestatus(void);
++static void updatesystray(void);
++static void updatesystrayicongeom(Client *i, int w, int h);
++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+ static void updatetitle(Client *c);
+ static void updatewindowtype(Client *c);
+ static void updatewmhints(Client *c);
+ static void view(const Arg *arg);
+ static Client *wintoclient(Window w);
+ static Monitor *wintomon(Window w);
++static Client *wintosystrayicon(Window w);
+ static int xerror(Display *dpy, XErrorEvent *ee);
+ static int xerrordummy(Display *dpy, XErrorEvent *ee);
+ static int xerrorstart(Display *dpy, XErrorEvent *ee);
+ static void zoom(const Arg *arg);
+
+ /* variables */
++static Systray *systray = NULL;
+ static const char broken[] = "broken";
+ static char stext[256];
+ static int screen;
+@@ -257,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
++ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+ };
+-static Atom wmatom[WMLast], netatom[NetLast];
++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+ static int running = 1;
+ static Cur *cursor[CurLast];
+ static Clr **scheme;
+@@ -439,7 +475,7 @@ buttonpress(XEvent *e)
+ arg.ui = 1 << i;
+ } else if (ev->x < x + blw)
+ click = ClkLtSymbol;
+- else if (ev->x > selmon->ww - TEXTW(stext))
++ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+@@ -482,6 +518,11 @@ cleanup(void)
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while (mons)
+ cleanupmon(mons);
++ if (showsystray) {
++ XUnmapWindow(dpy, systray->win);
++ XDestroyWindow(dpy, systray->win);
++ free(systray);
++ }
+ for (i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for (i = 0; i < LENGTH(colors); i++)
+@@ -512,9 +553,57 @@ cleanupmon(Monitor *mon)
+ void
+ clientmessage(XEvent *e)
+ {
++ XWindowAttributes wa;
++ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
++ /* add systray icons */
++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
++ if (!(c = (Client *)calloc(1, sizeof(Client))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
++ if (!(c->win = cme->data.l[2])) {
++ free(c);
++ return;
++ }
++ c->mon = selmon;
++ c->next = systray->icons;
++ systray->icons = c;
++ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
++ /* use sane defaults */
++ wa.width = bh;
++ wa.height = bh;
++ wa.border_width = 0;
++ }
++ c->x = c->oldx = c->y = c->oldy = 0;
++ c->w = c->oldw = wa.width;
++ c->h = c->oldh = wa.height;
++ c->oldbw = wa.border_width;
++ c->bw = 0;
++ c->isfloating = True;
++ /* reuse tags field as mapped status */
++ c->tags = 1;
++ updatesizehints(c);
++ updatesystrayicongeom(c, wa.width, wa.height);
++ XAddToSaveSet(dpy, c->win);
++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
++ XReparentWindow(dpy, c->win, systray->win, 0, 0);
++ /* use parents background color */
++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ /* FIXME not sure if I have to send these events, too */
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
++ XSync(dpy, False);
++ resizebarwin(selmon);
++ updatesystray();
++ setclientstate(c, NormalState);
++ }
++ return;
++ }
+ if (!c)
+ return;
+ if (cme->message_type == netatom[NetWMState]) {
+@@ -567,7 +656,7 @@ configurenotify(XEvent *e)
+ for (c = m->clients; c; c = c->next)
+ if (c->isfullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
++ resizebarwin(m);
+ }
+ focus(NULL);
+ arrange(NULL);
+@@ -652,6 +741,11 @@ destroynotify(XEvent *e)
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
++ else if ((c = wintosystrayicon(ev->window))) {
++ removesystrayicon(c);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ }
+
+ void
+@@ -695,19 +789,23 @@ dirtomon(int dir)
+ void
+ drawbar(Monitor *m)
+ {
+- int x, w, sw = 0;
++ int x, w, sw = 0, stw = 0;
+ int boxs = drw->fonts->h / 9;
+ int boxw = drw->fonts->h / 6 + 2;
+ unsigned int i, occ = 0, urg = 0;
+ Client *c;
+
++ if(showsystray && m == systraytomon(m))
++ stw = getsystraywidth();
++
+ /* draw status first so it can be overdrawn by tags later */
+ if (m == selmon) { /* status is only drawn on selected monitor */
+ drw_setscheme(drw, scheme[SchemeNorm]);
+- sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
+- drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
++ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
++ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
++ resizebarwin(m);
+ for (c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+@@ -728,7 +826,7 @@ drawbar(Monitor *m)
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+- if ((w = m->ww - sw - x) > bh) {
++ if ((w = m->ww - sw - stw - x) > bh) {
+ if (m->sel) {
+ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+@@ -739,7 +837,7 @@ drawbar(Monitor *m)
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ }
+ }
+- drw_map(drw, m->barwin, 0, 0, m->ww, bh);
++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
+ }
+
+ void
+@@ -776,8 +874,11 @@ expose(XEvent *e)
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+- if (ev->count == 0 && (m = wintomon(ev->window)))
++ if (ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
++ if (m == selmon)
++ updatesystray();
++ }
+ }
+
+ void
+@@ -862,10 +963,17 @@ getatomprop(Client *c, Atom prop)
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
++ /* FIXME getatomprop should return the number of items and a pointer to
++ * the stored data instead of this workaround */
++ Atom req = XA_ATOM;
++ if (prop == xatom[XembedInfo])
++ req = xatom[XembedInfo];
+
+- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
++ if (da == xatom[XembedInfo] && dl == 2)
++ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+@@ -899,6 +1007,16 @@ getstate(Window w)
+ return result;
+ }
+
++unsigned int
++getsystraywidth()
++{
++ unsigned int w = 0;
++ Client *i;
++ if(showsystray)
++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
++ return w ? w + systrayspacing : 1;
++}
++
+ int
+ gettextprop(Window w, Atom atom, char *text, unsigned int size)
+ {
+@@ -1003,7 +1121,7 @@ killclient(const Arg *arg)
+ {
+ if (!selmon->sel)
+ return;
+- if (!sendevent(selmon->sel, wmatom[WMDelete])) {
++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+@@ -1091,6 +1209,12 @@ maprequest(XEvent *e)
+ {
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
++ Client *i;
++ if ((i = wintosystrayicon(ev->window))) {
++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+
+ if (!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+@@ -1215,6 +1339,16 @@ propertynotify(XEvent *e)
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
++ if ((c = wintosystrayicon(ev->window))) {
++ if (ev->atom == XA_WM_NORMAL_HINTS) {
++ updatesizehints(c);
++ updatesystrayicongeom(c, c->w, c->h);
++ }
++ else
++ updatesystrayiconstate(c, ev);
++ resizebarwin(selmon);
++ updatesystray();
++ }
+ if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if (ev->state == PropertyDelete)
+@@ -1265,6 +1399,20 @@ recttomon(int x, int y, int w, int h)
+ return r;
+ }
+
++void
++removesystrayicon(Client *i)
++{
++ Client **ii;
++
++ if (!showsystray || !i)
++ return;
++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
++ if (ii)
++ *ii = i->next;
++ free(i);
++}
++
++
+ void
+ resize(Client *c, int x, int y, int w, int h, int interact)
+ {
+@@ -1272,6 +1420,14 @@ resize(Client *c, int x, int y, int w, int h, int interact)
+ resizeclient(c, x, y, w, h);
+ }
+
++void
++resizebarwin(Monitor *m) {
++ unsigned int w = m->ww;
++ if (showsystray && m == systraytomon(m))
++ w -= getsystraywidth();
++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
++}
++
+ void
+ resizeclient(Client *c, int x, int y, int w, int h)
+ {
+@@ -1344,6 +1500,19 @@ resizemouse(const Arg *arg)
+ }
+ }
+
++void
++resizerequest(XEvent *e)
++{
++ XResizeRequestEvent *ev = &e->xresizerequest;
++ Client *i;
++
++ if ((i = wintosystrayicon(ev->window))) {
++ updatesystrayicongeom(i, ev->width, ev->height);
++ resizebarwin(selmon);
++ updatesystray();
++ }
++}
++
+ void
+ restack(Monitor *m)
+ {
+@@ -1433,26 +1602,36 @@ setclientstate(Client *c, long state)
+ }
+
+ int
+-sendevent(Client *c, Atom proto)
++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
+ {
+ int n;
+- Atom *protocols;
++ Atom *protocols, mt;
+ int exists = 0;
+ XEvent ev;
+
+- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
+- while (!exists && n--)
+- exists = protocols[n] == proto;
+- XFree(protocols);
++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
++ mt = wmatom[WMProtocols];
++ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
++ while (!exists && n--)
++ exists = protocols[n] == proto;
++ XFree(protocols);
++ }
++ }
++ else {
++ exists = True;
++ mt = proto;
+ }
+ if (exists) {
+ ev.type = ClientMessage;
+- ev.xclient.window = c->win;
+- ev.xclient.message_type = wmatom[WMProtocols];
++ ev.xclient.window = w;
++ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+- ev.xclient.data.l[0] = proto;
+- ev.xclient.data.l[1] = CurrentTime;
+- XSendEvent(dpy, c->win, False, NoEventMask, &ev);
++ ev.xclient.data.l[0] = d0;
++ ev.xclient.data.l[1] = d1;
++ ev.xclient.data.l[2] = d2;
++ ev.xclient.data.l[3] = d3;
++ ev.xclient.data.l[4] = d4;
++ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+ }
+@@ -1466,7 +1645,7 @@ setfocus(Client *c)
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+- sendevent(c, wmatom[WMTakeFocus]);
++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+ }
+
+ void
+@@ -1555,6 +1734,10 @@ setup(void)
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+@@ -1562,6 +1745,9 @@ setup(void)
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+@@ -1570,6 +1756,8 @@ setup(void)
+ scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
+ for (i = 0; i < LENGTH(colors); i++)
+ scheme[i] = drw_scm_create(drw, colors[i], 3);
++ /* init system tray */
++ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+@@ -1701,7 +1889,18 @@ togglebar(const Arg *arg)
+ {
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
++ resizebarwin(selmon);
++ if (showsystray) {
++ XWindowChanges wc;
++ if (!selmon->showbar)
++ wc.y = -bh;
++ else if (selmon->showbar) {
++ wc.y = 0;
++ if (!selmon->topbar)
++ wc.y = selmon->mh - bh;
++ }
++ XConfigureWindow(dpy, systray->win, CWY, &wc);
++ }
+ arrange(selmon);
+ }
+
+@@ -1796,11 +1995,18 @@ unmapnotify(XEvent *e)
+ else
+ unmanage(c, 0);
+ }
++ else if ((c = wintosystrayicon(ev->window))) {
++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
++ * _not_ destroy them. We map those windows back */
++ XMapRaised(dpy, c->win);
++ updatesystray();
++ }
+ }
+
+ void
+ updatebars(void)
+ {
++ unsigned int w;
+ Monitor *m;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+@@ -1811,10 +2017,15 @@ updatebars(void)
+ for (m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
++ w = m->ww;
++ if (showsystray && m == systraytomon(m))
++ w -= getsystraywidth();
++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
++ if (showsystray && m == systraytomon(m))
++ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+@@ -1990,6 +2201,121 @@ updatestatus(void)
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ strcpy(stext, "dwm-"VERSION);
+ drawbar(selmon);
++ updatesystray();
++}
++
++void
++updatesystrayicongeom(Client *i, int w, int h)
++{
++ if (i) {
++ i->h = bh;
++ if (w == h)
++ i->w = bh;
++ else if (h == bh)
++ i->w = w;
++ else
++ i->w = (int) ((float)bh * ((float)w / (float)h));
++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
++ /* force icons into the systray dimensions if they don't want to */
++ if (i->h > bh) {
++ if (i->w == i->h)
++ i->w = bh;
++ else
++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
++ i->h = bh;
++ }
++ }
++}
++
++void
++updatesystrayiconstate(Client *i, XPropertyEvent *ev)
++{
++ long flags;
++ int code = 0;
++
++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
++ !(flags = getatomprop(i, xatom[XembedInfo])))
++ return;
++
++ if (flags & XEMBED_MAPPED && !i->tags) {
++ i->tags = 1;
++ code = XEMBED_WINDOW_ACTIVATE;
++ XMapRaised(dpy, i->win);
++ setclientstate(i, NormalState);
++ }
++ else if (!(flags & XEMBED_MAPPED) && i->tags) {
++ i->tags = 0;
++ code = XEMBED_WINDOW_DEACTIVATE;
++ XUnmapWindow(dpy, i->win);
++ setclientstate(i, WithdrawnState);
++ }
++ else
++ return;
++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
++ systray->win, XEMBED_EMBEDDED_VERSION);
++}
++
++void
++updatesystray(void)
++{
++ XSetWindowAttributes wa;
++ XWindowChanges wc;
++ Client *i;
++ Monitor *m = systraytomon(NULL);
++ unsigned int x = m->mx + m->mw;
++ unsigned int w = 1;
++
++ if (!showsystray)
++ return;
++ if (!systray) {
++ /* init systray */
++ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
++ wa.event_mask = ButtonPressMask | ExposureMask;
++ wa.override_redirect = True;
++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
++ XMapRaised(dpy, systray->win);
++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
++ XSync(dpy, False);
++ }
++ else {
++ fprintf(stderr, "dwm: unable to obtain system tray.\n");
++ free(systray);
++ systray = NULL;
++ return;
++ }
++ }
++ for (w = 0, i = systray->icons; i; i = i->next) {
++ /* make sure the background color stays the same */
++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
++ XMapRaised(dpy, i->win);
++ w += systrayspacing;
++ i->x = w;
++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
++ w += i->w;
++ if (i->mon != m)
++ i->mon = m;
++ }
++ w = w ? w + systrayspacing : 1;
++ x -= w;
++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
++ wc.stack_mode = Above; wc.sibling = m->barwin;
++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
++ XMapWindow(dpy, systray->win);
++ XMapSubwindows(dpy, systray->win);
++ /* redraw background */
++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
++ XSync(dpy, False);
+ }
+
+ void
+@@ -2057,6 +2383,16 @@ wintoclient(Window w)
+ return NULL;
+ }
+
++Client *
++wintosystrayicon(Window w) {
++ Client *i = NULL;
++
++ if (!showsystray || !w)
++ return i;
++ for (i = systray->icons; i && i->win != w; i = i->next) ;
++ return i;
++}
++
+ Monitor *
+ wintomon(Window w)
+ {
+@@ -2110,6 +2446,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
+ return -1;
+ }
+
++Monitor *
++systraytomon(Monitor *m) {
++ Monitor *t;
++ int i, n;
++ if(!systraypinning) {
++ if(!m)
++ return selmon;
++ return m == selmon ? m : NULL;
++ }
++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
++ if(systraypinningfailfirst && n < systraypinning)
++ return mons;
++ return t;
++}
++
+ void
+ zoom(const Arg *arg)
+ {
+--
+2.17.1
+
diff --git a/keep/manual-programs/suckless/dwm/dwm.1 b/keep/manual-programs/suckless/dwm/dwm.1
new file mode 100644
index 0000000..13b3729
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm.1
@@ -0,0 +1,176 @@
+.TH DWM 1 dwm\-VERSION
+.SH NAME
+dwm \- dynamic window manager
+.SH SYNOPSIS
+.B dwm
+.RB [ \-v ]
+.SH DESCRIPTION
+dwm is a dynamic window manager for X. It manages windows in tiled, monocle
+and floating layouts. Either layout can be applied dynamically, optimising the
+environment for the application in use and the task performed.
+.P
+In tiled layouts windows are managed in a master and stacking area. The master
+area on the left contains one window by default, and the stacking area on the
+right contains all other windows. The number of master area windows can be
+adjusted from zero to an arbitrary number. In monocle layout all windows are
+maximised to the screen size. In floating layout windows can be resized and
+moved freely. Dialog windows are always managed floating, regardless of the
+layout applied.
+.P
+Windows are grouped by tags. Each window can be tagged with one or multiple
+tags. Selecting certain tags displays all windows with these tags.
+.P
+Each screen contains a small status bar which displays all available tags, the
+layout, the title of the focused window, and the text read from the root window
+name property, if the screen is focused. A floating window is indicated with an
+empty square and a maximised floating window is indicated with a filled square
+before the windows title. The selected tags are indicated with a different
+color. The tags of the focused window are indicated with a filled square in the
+top left corner. The tags which are applied to one or more windows are
+indicated with an empty square in the top left corner.
+.P
+dwm draws a small border around windows to indicate the focus state.
+.SH OPTIONS
+.TP
+.B \-v
+prints version information to standard output, then exits.
+.SH USAGE
+.SS Status bar
+.TP
+.B X root window name
+is read and displayed in the status text area. It can be set with the
+.BR xsetroot (1)
+command.
+.TP
+.B Button1
+click on a tag label to display all windows with that tag, click on the layout
+label toggles between tiled and floating layout.
+.TP
+.B Button3
+click on a tag label adds/removes all windows with that tag to/from the view.
+.TP
+.B Mod1\-Button1
+click on a tag label applies that tag to the focused window.
+.TP
+.B Mod1\-Button3
+click on a tag label adds/removes that tag to/from the focused window.
+.SS Keyboard commands
+.TP
+.B Mod1\-Shift\-Return
+Start
+.BR st(1).
+.TP
+.B Mod1\-p
+Spawn
+.BR dmenu(1)
+for launching other programs.
+.TP
+.B Mod1\-,
+Focus previous screen, if any.
+.TP
+.B Mod1\-.
+Focus next screen, if any.
+.TP
+.B Mod1\-Shift\-,
+Send focused window to previous screen, if any.
+.TP
+.B Mod1\-Shift\-.
+Send focused window to next screen, if any.
+.TP
+.B Mod1\-b
+Toggles bar on and off.
+.TP
+.B Mod1\-t
+Sets tiled layout.
+.TP
+.B Mod1\-f
+Sets floating layout.
+.TP
+.B Mod1\-m
+Sets monocle layout.
+.TP
+.B Mod1\-space
+Toggles between current and previous layout.
+.TP
+.B Mod1\-j
+Focus next window.
+.TP
+.B Mod1\-k
+Focus previous window.
+.TP
+.B Mod1\-i
+Increase number of windows in master area.
+.TP
+.B Mod1\-d
+Decrease number of windows in master area.
+.TP
+.B Mod1\-l
+Increase master area size.
+.TP
+.B Mod1\-h
+Decrease master area size.
+.TP
+.B Mod1\-Return
+Zooms/cycles focused window to/from master area (tiled layouts only).
+.TP
+.B Mod1\-Shift\-c
+Close focused window.
+.TP
+.B Mod1\-Shift\-space
+Toggle focused window between tiled and floating state.
+.TP
+.B Mod1\-Tab
+Toggles to the previously selected tags.
+.TP
+.B Mod1\-Shift\-[1..n]
+Apply nth tag to focused window.
+.TP
+.B Mod1\-Shift\-0
+Apply all tags to focused window.
+.TP
+.B Mod1\-Control\-Shift\-[1..n]
+Add/remove nth tag to/from focused window.
+.TP
+.B Mod1\-[1..n]
+View all windows with nth tag.
+.TP
+.B Mod1\-0
+View all windows with any tag.
+.TP
+.B Mod1\-Control\-[1..n]
+Add/remove all windows with nth tag to/from the view.
+.TP
+.B Mod1\-Shift\-q
+Quit dwm.
+.SS Mouse commands
+.TP
+.B Mod1\-Button1
+Move focused window while dragging. Tiled windows will be toggled to the floating state.
+.TP
+.B Mod1\-Button2
+Toggles focused window between floating and tiled state.
+.TP
+.B Mod1\-Button3
+Resize focused window while dragging. Tiled windows will be toggled to the floating state.
+.SH CUSTOMIZATION
+dwm is customized by creating a custom config.h and (re)compiling the source
+code. This keeps it fast, secure and simple.
+.SH SEE ALSO
+.BR dmenu (1),
+.BR st (1)
+.SH ISSUES
+Java applications which use the XToolkit/XAWT backend may draw grey windows
+only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
+JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
+are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the
+environment variable
+.BR AWT_TOOLKIT=MToolkit
+(to use the older Motif backend instead) or running
+.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D
+or
+.B wmname LG3D
+(to pretend that a non-reparenting window manager is running that the
+XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
+.BR _JAVA_AWT_WM_NONREPARENTING=1 .
+.SH BUGS
+Send all bug reports with a patch to hackers@suckless.org.
diff --git a/keep/manual-programs/suckless/dwm/dwm.c b/keep/manual-programs/suckless/dwm/dwm.c
new file mode 100644
index 0000000..8890d40
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm.c
@@ -0,0 +1,2588 @@
+/* See LICENSE file for copyright and license details.
+ *
+ * dynamic window manager is designed like any other X client as well. It is
+ * driven through handling X events. In contrast to other X clients, a window
+ * manager selects for SubstructureRedirectMask on the root window, to receive
+ * events about window (dis-)appearance. Only one X connection at a time is
+ * allowed to select for this event mask.
+ *
+ * The event handlers of dwm are organized in an array which is accessed
+ * whenever a new event has been fetched. This allows event dispatching
+ * in O(1) time.
+ *
+ * Each child of the root window is called a client, except windows which have
+ * set the override_redirect flag. Clients are organized in a linked client
+ * list on each monitor, the focus history is remembered through a stack list
+ * on each monitor. Each client contains a bit array to indicate the tags of a
+ * client.
+ *
+ * Keys and tagging rules are organized as arrays and defined in config.h.
+ *
+ * To understand everything else, start reading main().
+ */
+#include <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif /* XINERAMA */
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+/* macros */
+#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
+#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
+#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
+ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
+#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
+#define WIDTH(X) ((X)->w + 2 * (X)->bw)
+#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
+#define TAGMASK ((1 << LENGTH(tags)) - 1)
+#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+
+/* XEMBED messages */
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_IN 4
+#define XEMBED_MODALITY_ON 10
+
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_WINDOW_DEACTIVATE 2
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 0
+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
+
+/* enums */
+enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+typedef union {
+ int i;
+ unsigned int ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int click;
+ unsigned int mask;
+ unsigned int button;
+ void (*func)(const Arg *arg);
+ const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct Client Client;
+struct Client {
+ char name[256];
+ float mina, maxa;
+ int x, y, w, h;
+ int oldx, oldy, oldw, oldh;
+ int basew, baseh, incw, inch, maxw, maxh, minw, minh;
+ int bw, oldbw;
+ unsigned int tags;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+ Window win;
+};
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+} Layout;
+
+struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ int gappx; /* gaps between windows */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ int showbar;
+ int topbar;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
+ const Layout *lt[2];
+};
+
+typedef struct {
+ const char *class;
+ const char *instance;
+ const char *title;
+ unsigned int tags;
+ int isfloating;
+ int monitor;
+} Rule;
+
+typedef struct Systray Systray;
+struct Systray {
+ Window win;
+ Client *icons;
+};
+
+/* function declarations */
+static void applyrules(Client *c);
+static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+static void arrange(Monitor *m);
+static void arrangemon(Monitor *m);
+static void attach(Client *c);
+static void attachstack(Client *c);
+static void buttonpress(XEvent *e);
+static void checkotherwm(void);
+static void cleanup(void);
+static void cleanupmon(Monitor *mon);
+static void clientmessage(XEvent *e);
+static void configure(Client *c);
+static void configurenotify(XEvent *e);
+static void configurerequest(XEvent *e);
+static Monitor *createmon(void);
+static void destroynotify(XEvent *e);
+static void detach(Client *c);
+static void detachstack(Client *c);
+static Monitor *dirtomon(int dir);
+static void drawbar(Monitor *m);
+static void drawbars(void);
+static void enternotify(XEvent *e);
+static void expose(XEvent *e);
+static void focus(Client *c);
+static void focusin(XEvent *e);
+static void focusmon(const Arg *arg);
+static void focusstack(const Arg *arg);
+static Atom getatomprop(Client *c, Atom prop);
+static int getrootptr(int *x, int *y);
+static long getstate(Window w);
+static unsigned int getsystraywidth();
+static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
+static void grabbuttons(Client *c, int focused);
+static void grabkeys(void);
+static void incnmaster(const Arg *arg);
+static void keypress(XEvent *e);
+static void killclient(const Arg *arg);
+static void manage(Window w, XWindowAttributes *wa);
+static void mappingnotify(XEvent *e);
+static void maprequest(XEvent *e);
+static void monocle(Monitor *m);
+static void motionnotify(XEvent *e);
+static void movemouse(const Arg *arg);
+static Client *nexttiled(Client *c);
+static void pop(Client *);
+static void propertynotify(XEvent *e);
+static void quit(const Arg *arg);
+static Monitor *recttomon(int x, int y, int w, int h);
+static void removesystrayicon(Client *i);
+static void resize(Client *c, int x, int y, int w, int h, int interact);
+static void resizebarwin(Monitor *m);
+static void resizeclient(Client *c, int x, int y, int w, int h);
+static void resizemouse(const Arg *arg);
+static void resizerequest(XEvent *e);
+static void restack(Monitor *m);
+static void run(void);
+static void runAutostart(void);
+static void scan(void);
+static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+static void sendmon(Client *c, Monitor *m);
+static void setclientstate(Client *c, long state);
+static void setfocus(Client *c);
+static void setfullscreen(Client *c, int fullscreen);
+static void setgaps(const Arg *arg);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setup(void);
+static void seturgent(Client *c, int urg);
+static void showhide(Client *c);
+static void sigchld(int unused);
+static void spawn(const Arg *arg);
+static Monitor *systraytomon(Monitor *m);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *);
+static void togglebar(const Arg *arg);
+static void togglefloating(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void unfocus(Client *c, int setfocus);
+static void unmanage(Client *c, int destroyed);
+static void unmapnotify(XEvent *e);
+static void updatebarpos(Monitor *m);
+static void updatebars(void);
+static void updateclientlist(void);
+static int updategeom(void);
+static void updatenumlockmask(void);
+static void updatesizehints(Client *c);
+static void updatestatus(void);
+static void updatesystray(void);
+static void updatesystrayicongeom(Client *i, int w, int h);
+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+static void updatetitle(Client *c);
+static void updatewindowtype(Client *c);
+static void updatewmhints(Client *c);
+static void view(const Arg *arg);
+static Client *wintoclient(Window w);
+static Monitor *wintomon(Window w);
+static Client *wintosystrayicon(Window w);
+static int xerror(Display *dpy, XErrorEvent *ee);
+static int xerrordummy(Display *dpy, XErrorEvent *ee);
+static int xerrorstart(Display *dpy, XErrorEvent *ee);
+static void zoom(const Arg *arg);
+static void bstack(Monitor *m);
+static void bstackhoriz(Monitor *m);
+
+/* variables */
+static Systray *systray = NULL;
+static const char broken[] = "broken";
+static char stext[256];
+static int screen;
+static int sw, sh; /* X display screen geometry width, height */
+static int bh, blw = 0; /* bar geometry */
+static int lrpad; /* sum of left and right padding for text */
+static int (*xerrorxlib)(Display *, XErrorEvent *);
+static unsigned int numlockmask = 0;
+static void (*handler[LASTEvent]) (XEvent *) = {
+ [ButtonPress] = buttonpress,
+ [ClientMessage] = clientmessage,
+ [ConfigureRequest] = configurerequest,
+ [ConfigureNotify] = configurenotify,
+ [DestroyNotify] = destroynotify,
+ //[EnterNotify] = enternotify,
+ [Expose] = expose,
+ [FocusIn] = focusin,
+ [KeyPress] = keypress,
+ [MappingNotify] = mappingnotify,
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
+ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+};
+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+static int running = 1;
+static Cur *cursor[CurLast];
+static Clr **scheme;
+static Display *dpy;
+static Drw *drw;
+static Monitor *mons, *selmon;
+static Window root, wmcheckwin;
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* compile-time check if all tags fit into an unsigned int bit array. */
+struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+/* function implementations */
+void
+applyrules(Client *c)
+{
+ const char *class, *instance;
+ unsigned int i;
+ const Rule *r;
+ Monitor *m;
+ XClassHint ch = { NULL, NULL };
+
+ /* rule matching */
+ c->isfloating = 0;
+ c->tags = 0;
+ XGetClassHint(dpy, c->win, &ch);
+ class = ch.res_class ? ch.res_class : broken;
+ instance = ch.res_name ? ch.res_name : broken;
+
+ for (i = 0; i < LENGTH(rules); i++) {
+ r = &rules[i];
+ if ((!r->title || strstr(c->name, r->title))
+ && (!r->class || strstr(class, r->class))
+ && (!r->instance || strstr(instance, r->instance)))
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+ if (m)
+ c->mon = m;
+ }
+ }
+ if (ch.res_class)
+ XFree(ch.res_class);
+ if (ch.res_name)
+ XFree(ch.res_name);
+ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
+}
+
+int
+applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
+{
+ int baseismin;
+ Monitor *m = c->mon;
+
+ /* set minimum possible */
+ *w = MAX(1, *w);
+ *h = MAX(1, *h);
+ if (interact) {
+ if (*x > sw)
+ *x = sw - WIDTH(c);
+ if (*y > sh)
+ *y = sh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw < 0)
+ *x = 0;
+ if (*y + *h + 2 * c->bw < 0)
+ *y = 0;
+ } else {
+ if (*x >= m->wx + m->ww)
+ *x = m->wx + m->ww - WIDTH(c);
+ if (*y >= m->wy + m->wh)
+ *y = m->wy + m->wh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw <= m->wx)
+ *x = m->wx;
+ if (*y + *h + 2 * c->bw <= m->wy)
+ *y = m->wy;
+ }
+ if (*h < bh)
+ *h = bh;
+ if (*w < bh)
+ *w = bh;
+ if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ /* see last two sentences in ICCCM 4.1.2.3 */
+ baseismin = c->basew == c->minw && c->baseh == c->minh;
+ if (!baseismin) { /* temporarily remove base dimensions */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for aspect limits */
+ if (c->mina > 0 && c->maxa > 0) {
+ if (c->maxa < (float)*w / *h)
+ *w = *h * c->maxa + 0.5;
+ else if (c->mina < (float)*h / *w)
+ *h = *w * c->mina + 0.5;
+ }
+ if (baseismin) { /* increment calculation requires this */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for increment value */
+ if (c->incw)
+ *w -= *w % c->incw;
+ if (c->inch)
+ *h -= *h % c->inch;
+ /* restore base dimensions */
+ *w = MAX(*w + c->basew, c->minw);
+ *h = MAX(*h + c->baseh, c->minh);
+ if (c->maxw)
+ *w = MIN(*w, c->maxw);
+ if (c->maxh)
+ *h = MIN(*h, c->maxh);
+ }
+ return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
+}
+
+void
+arrange(Monitor *m)
+{
+ if (m)
+ showhide(m->stack);
+ else for (m = mons; m; m = m->next)
+ showhide(m->stack);
+ if (m) {
+ arrangemon(m);
+ restack(m);
+ } else for (m = mons; m; m = m->next)
+ arrangemon(m);
+}
+
+void
+arrangemon(Monitor *m)
+{
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+}
+
+void
+attach(Client *c)
+{
+ c->next = c->mon->clients;
+ c->mon->clients = c;
+}
+
+void
+attachstack(Client *c)
+{
+ c->snext = c->mon->stack;
+ c->mon->stack = c;
+}
+
+void
+buttonpress(XEvent *e)
+{
+ unsigned int i, x, click;
+ Arg arg = {0};
+ Client *c;
+ Monitor *m;
+ XButtonPressedEvent *ev = &e->xbutton;
+
+ click = ClkRootWin;
+ /* focus monitor if necessary */
+ if ((m = wintomon(ev->window)) && m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ if (ev->window == selmon->barwin) {
+ i = x = 0;
+ do
+ x += TEXTW(tags[i]);
+ while (ev->x >= x && ++i < LENGTH(tags));
+ if (i < LENGTH(tags)) {
+ click = ClkTagBar;
+ arg.ui = 1 << i;
+ } else if (ev->x < x + blw)
+ click = ClkLtSymbol;
+ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+ } else if ((c = wintoclient(ev->window))) {
+ focus(c);
+ restack(selmon);
+ XAllowEvents(dpy, ReplayPointer, CurrentTime);
+ click = ClkClientWin;
+ }
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+ buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+}
+
+void
+checkotherwm(void)
+{
+ xerrorxlib = XSetErrorHandler(xerrorstart);
+ /* this causes an error if some other window manager is running */
+ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XSync(dpy, False);
+}
+
+void
+cleanup(void)
+{
+ Arg a = {.ui = ~0};
+ Layout foo = { "", NULL };
+ Monitor *m;
+ size_t i;
+
+ view(&a);
+ selmon->lt[selmon->sellt] = &foo;
+ for (m = mons; m; m = m->next)
+ while (m->stack)
+ unmanage(m->stack, 0);
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while (mons)
+ cleanupmon(mons);
+ if (showsystray) {
+ XUnmapWindow(dpy, systray->win);
+ XDestroyWindow(dpy, systray->win);
+ free(systray);
+ }
+ for (i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for (i = 0; i < LENGTH(colors); i++)
+ free(scheme[i]);
+ XDestroyWindow(dpy, wmcheckwin);
+ drw_free(drw);
+ XSync(dpy, False);
+ XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+}
+
+void
+cleanupmon(Monitor *mon)
+{
+ Monitor *m;
+
+ if (mon == mons)
+ mons = mons->next;
+ else {
+ for (m = mons; m && m->next != mon; m = m->next);
+ m->next = mon->next;
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
+ free(mon);
+}
+
+void
+clientmessage(XEvent *e)
+{
+ XWindowAttributes wa;
+ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
+ /* add systray icons */
+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
+ if (!(c = (Client *)calloc(1, sizeof(Client))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
+ if (!(c->win = cme->data.l[2])) {
+ free(c);
+ return;
+ }
+ c->mon = selmon;
+ c->next = systray->icons;
+ systray->icons = c;
+ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
+ /* use sane defaults */
+ wa.width = bh;
+ wa.height = bh;
+ wa.border_width = 0;
+ }
+ c->x = c->oldx = c->y = c->oldy = 0;
+ c->w = c->oldw = wa.width;
+ c->h = c->oldh = wa.height;
+ c->oldbw = wa.border_width;
+ c->bw = 0;
+ c->isfloating = True;
+ /* reuse tags field as mapped status */
+ c->tags = 1;
+ updatesizehints(c);
+ updatesystrayicongeom(c, wa.width, wa.height);
+ XAddToSaveSet(dpy, c->win);
+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
+ XClassHint ch = {"dwmsystray", "dwmsystray"};
+ XSetClassHint(dpy, c->win, &ch);
+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
+ /* use parents background color */
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ /* FIXME not sure if I have to send these events, too */
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ XSync(dpy, False);
+ resizebarwin(selmon);
+ updatesystray();
+ setclientstate(c, NormalState);
+ }
+ return;
+ }
+ if (!c)
+ return;
+ if (cme->message_type == netatom[NetWMState]) {
+ if (cme->data.l[1] == netatom[NetWMFullscreen]
+ || cme->data.l[2] == netatom[NetWMFullscreen])
+ setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
+ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
+ } else if (cme->message_type == netatom[NetActiveWindow]) {
+ if (c != selmon->sel && !c->isurgent)
+ seturgent(c, 1);
+ }
+}
+
+void
+configure(Client *c)
+{
+ XConfigureEvent ce;
+
+ ce.type = ConfigureNotify;
+ ce.display = dpy;
+ ce.event = c->win;
+ ce.window = c->win;
+ ce.x = c->x;
+ ce.y = c->y;
+ ce.width = c->w;
+ ce.height = c->h;
+ ce.border_width = c->bw;
+ ce.above = None;
+ ce.override_redirect = False;
+ XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
+}
+
+void
+configurenotify(XEvent *e)
+{
+ Monitor *m;
+ Client *c;
+ XConfigureEvent *ev = &e->xconfigure;
+ int dirty;
+
+ /* TODO: updategeom handling sucks, needs to be simplified */
+ if (ev->window == root) {
+ dirty = (sw != ev->width || sh != ev->height);
+ sw = ev->width;
+ sh = ev->height;
+ if (updategeom() || dirty) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next)
+ if (c->isfullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+ resizebarwin(m);
+ }
+ focus(NULL);
+ arrange(NULL);
+ }
+ }
+}
+
+void
+configurerequest(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XConfigureRequestEvent *ev = &e->xconfigurerequest;
+ XWindowChanges wc;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->value_mask & CWBorderWidth)
+ c->bw = ev->border_width;
+ else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
+ m = c->mon;
+ if (ev->value_mask & CWX) {
+ c->oldx = c->x;
+ c->x = m->mx + ev->x;
+ }
+ if (ev->value_mask & CWY) {
+ c->oldy = c->y;
+ c->y = m->my + ev->y;
+ }
+ if (ev->value_mask & CWWidth) {
+ c->oldw = c->w;
+ c->w = ev->width;
+ }
+ if (ev->value_mask & CWHeight) {
+ c->oldh = c->h;
+ c->h = ev->height;
+ }
+ if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
+ c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
+ if ((c->y + c->h) > m->my + m->mh && c->isfloating)
+ c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
+ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
+ configure(c);
+ if (ISVISIBLE(c))
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ } else
+ configure(c);
+ } else {
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+ wc.border_width = ev->border_width;
+ wc.sibling = ev->above;
+ wc.stack_mode = ev->detail;
+ XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
+ }
+ XSync(dpy, False);
+}
+
+Monitor *
+createmon(void)
+{
+ Monitor *m;
+
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+ m->topbar = topbar;
+ m->gappx = gappx;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ return m;
+}
+
+void
+destroynotify(XEvent *e)
+{
+ Client *c;
+ XDestroyWindowEvent *ev = &e->xdestroywindow;
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
+ else if ((c = wintosystrayicon(ev->window))) {
+ removesystrayicon(c);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+detach(Client *c)
+{
+ Client **tc;
+
+ for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
+ *tc = c->next;
+}
+
+void
+detachstack(Client *c)
+{
+ Client **tc, *t;
+
+ for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
+ *tc = c->snext;
+
+ if (c == c->mon->sel) {
+ for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
+ c->mon->sel = t;
+ }
+}
+
+Monitor *
+dirtomon(int dir)
+{
+ Monitor *m = NULL;
+
+ if (dir > 0) {
+ if (!(m = selmon->next))
+ m = mons;
+ } else if (selmon == mons)
+ for (m = mons; m->next; m = m->next);
+ else
+ for (m = mons; m->next != selmon; m = m->next);
+ return m;
+}
+
+void
+drawbar(Monitor *m)
+{
+ int x, w, sw = 0, stw = 0;
+ int boxs = drw->fonts->h / 9;
+ int boxw = drw->fonts->h / 6 + 2;
+ unsigned int i, occ = 0, urg = 0;
+ Client *c;
+
+ if(showsystray && m == systraytomon(m))
+ stw = getsystraywidth();
+
+ /* draw status first so it can be overdrawn by tags later */
+ if (m == selmon) { /* status is only drawn on selected monitor */
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
+ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
+ resizebarwin(m);
+ for (c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ x = 0;
+ for (i = 0; i < LENGTH(tags); i++) {
+ w = TEXTW(tags[i]);
+ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+ if (occ & 1 << i)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw,
+ m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+ urg & 1 << i);
+ x += w;
+ }
+ w = blw = TEXTW(m->ltsymbol);
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+ if ((w = m->ww - sw - stw - x) > bh) {
+ if (m->sel) {
+ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+ if (m->sel->isfloating)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
+ } else {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ }
+ }
+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
+}
+
+void
+drawbars(void)
+{
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ drawbar(m);
+}
+
+void
+enternotify(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XCrossingEvent *ev = &e->xcrossing;
+
+ if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
+ return;
+ c = wintoclient(ev->window);
+ m = c ? c->mon : wintomon(ev->window);
+ if (m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ } else if (!c || c == selmon->sel)
+ return;
+ focus(c);
+}
+
+void
+expose(XEvent *e)
+{
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+ if (ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
+ if (m == selmon)
+ updatesystray();
+ }
+}
+
+void
+focus(Client *c)
+{
+ if (!c || !ISVISIBLE(c))
+ for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
+ if (selmon->sel && selmon->sel != c)
+ unfocus(selmon->sel, 0);
+ if (c) {
+ if (c->mon != selmon)
+ selmon = c->mon;
+ if (c->isurgent)
+ seturgent(c, 0);
+ detachstack(c);
+ attachstack(c);
+ grabbuttons(c, 1);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
+ setfocus(c);
+ } else {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+ selmon->sel = c;
+ drawbars();
+}
+
+/* there are some broken focus acquiring clients needing extra handling */
+void
+focusin(XEvent *e)
+{
+ XFocusChangeEvent *ev = &e->xfocus;
+
+ if (selmon->sel && ev->window != selmon->sel->win)
+ setfocus(selmon->sel);
+}
+
+void
+focusmon(const Arg *arg)
+{
+ Monitor *m;
+
+ if (!mons->next)
+ return;
+ if ((m = dirtomon(arg->i)) == selmon)
+ return;
+ unfocus(selmon->sel, 0);
+ selmon = m;
+ focus(NULL);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ Client *c = NULL, *i;
+
+ if (!selmon->sel)
+ return;
+ if (arg->i > 0) {
+ for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
+ if (!c)
+ for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
+ } else {
+ for (i = selmon->clients; i != selmon->sel; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ if (!c)
+ for (; i; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ }
+ if (c) {
+ focus(c);
+ restack(selmon);
+ }
+}
+
+Atom
+getatomprop(Client *c, Atom prop)
+{
+ int di;
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
+ /* FIXME getatomprop should return the number of items and a pointer to
+ * the stored data instead of this workaround */
+ Atom req = XA_ATOM;
+ if (prop == xatom[XembedInfo])
+ req = xatom[XembedInfo];
+
+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
+ if (da == xatom[XembedInfo] && dl == 2)
+ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+}
+
+int
+getrootptr(int *x, int *y)
+{
+ int di;
+ unsigned int dui;
+ Window dummy;
+
+ return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
+}
+
+long
+getstate(Window w)
+{
+ int format;
+ long result = -1;
+ unsigned char *p = NULL;
+ unsigned long n, extra;
+ Atom real;
+
+ if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
+ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
+ return -1;
+ if (n != 0)
+ result = *p;
+ XFree(p);
+ return result;
+}
+
+unsigned int
+getsystraywidth()
+{
+ unsigned int w = 0;
+ Client *i;
+ if(showsystray)
+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
+ return w ? w + systrayspacing : 1;
+}
+
+int
+gettextprop(Window w, Atom atom, char *text, unsigned int size)
+{
+ char **list = NULL;
+ int n;
+ XTextProperty name;
+
+ if (!text || size == 0)
+ return 0;
+ text[0] = '\0';
+ if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
+ return 0;
+ if (name.encoding == XA_STRING)
+ strncpy(text, (char *)name.value, size - 1);
+ else {
+ if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
+ strncpy(text, *list, size - 1);
+ XFreeStringList(list);
+ }
+ }
+ text[size - 1] = '\0';
+ XFree(name.value);
+ return 1;
+}
+
+void
+grabbuttons(Client *c, int focused)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ if (!focused)
+ XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
+ BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].click == ClkClientWin)
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabButton(dpy, buttons[i].button,
+ buttons[i].mask | modifiers[j],
+ c->win, False, BUTTONMASK,
+ GrabModeAsync, GrabModeSync, None, None);
+ }
+}
+
+void
+grabkeys(void)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ KeyCode code;
+
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ for (i = 0; i < LENGTH(keys); i++)
+ if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
+ True, GrabModeAsync, GrabModeAsync);
+ }
+}
+
+void
+incnmaster(const Arg *arg)
+{
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
+#ifdef XINERAMA
+static int
+isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
+{
+ while (n--)
+ if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
+ && unique[n].width == info->width && unique[n].height == info->height)
+ return 0;
+ return 1;
+}
+#endif /* XINERAMA */
+
+void
+keypress(XEvent *e)
+{
+ unsigned int i;
+ KeySym keysym;
+ XKeyEvent *ev;
+
+ ev = &e->xkey;
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
+ for (i = 0; i < LENGTH(keys); i++)
+ if (keysym == keys[i].keysym
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+ && keys[i].func)
+ keys[i].func(&(keys[i].arg));
+}
+
+void
+killclient(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+ XKillClient(dpy, selmon->sel->win);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+}
+
+void
+manage(Window w, XWindowAttributes *wa)
+{
+ Client *c, *t = NULL;
+ Window trans = None;
+ XWindowChanges wc;
+
+ c = ecalloc(1, sizeof(Client));
+ c->win = w;
+ /* geometry */
+ c->x = c->oldx = wa->x;
+ c->y = c->oldy = wa->y;
+ c->w = c->oldw = wa->width;
+ c->h = c->oldh = wa->height;
+ c->oldbw = wa->border_width;
+
+ updatetitle(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+ c->mon = t->mon;
+ c->tags = t->tags;
+ } else {
+ c->mon = selmon;
+ applyrules(c);
+ }
+
+ if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
+ c->x = c->mon->mx + c->mon->mw - WIDTH(c);
+ if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
+ c->y = c->mon->my + c->mon->mh - HEIGHT(c);
+ c->x = MAX(c->x, c->mon->mx);
+ /* only fix client y-offset, if the client center might cover the bar */
+ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
+ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
+ c->bw = borderpx;
+
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, w, CWBorderWidth, &wc);
+ XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
+ configure(c); /* propagates border_width, if size doesn't change */
+ updatewindowtype(c);
+ updatesizehints(c);
+ updatewmhints(c);
+ XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
+ grabbuttons(c, 0);
+ if (!c->isfloating)
+ c->isfloating = c->oldstate = trans != None || c->isfixed;
+ if (c->isfloating)
+ XRaiseWindow(dpy, c->win);
+ attach(c);
+ attachstack(c);
+ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+ XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
+ setclientstate(c, NormalState);
+ if (c->mon == selmon)
+ unfocus(selmon->sel, 0);
+ c->mon->sel = c;
+ arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ focus(NULL);
+}
+
+void
+mappingnotify(XEvent *e)
+{
+ XMappingEvent *ev = &e->xmapping;
+
+ XRefreshKeyboardMapping(ev);
+ if (ev->request == MappingKeyboard)
+ grabkeys();
+}
+
+void
+maprequest(XEvent *e)
+{
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
+ Client *i;
+ if ((i = wintosystrayicon(ev->window))) {
+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+
+ if (!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+ if (wa.override_redirect)
+ return;
+ if (!wintoclient(ev->window))
+ manage(ev->window, &wa);
+}
+
+void
+monocle(Monitor *m)
+{
+ unsigned int n = 0;
+ Client *c;
+
+ for (c = m->clients; c; c = c->next)
+ if (ISVISIBLE(c))
+ n++;
+ if (n > 0) /* override layout symbol */
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+ for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
+ resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
+}
+
+void
+motionnotify(XEvent *e)
+{
+ static Monitor *mon = NULL;
+ Monitor *m;
+ XMotionEvent *ev = &e->xmotion;
+
+ if (ev->window != root)
+ return;
+ if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ mon = m;
+}
+
+void
+movemouse(const Arg *arg)
+{
+ int x, y, ocx, ocy, nx, ny;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ if (!getrootptr(&x, &y))
+ return;
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nx = ocx + (ev.xmotion.x - x);
+ ny = ocy + (ev.xmotion.y - y);
+ if (abs(selmon->wx - nx) < snap)
+ nx = selmon->wx;
+ else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
+ nx = selmon->wx + selmon->ww - WIDTH(c);
+ if (abs(selmon->wy - ny) < snap)
+ ny = selmon->wy;
+ else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
+ ny = selmon->wy + selmon->wh - HEIGHT(c);
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
+ togglefloating(NULL);
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, nx, ny, c->w, c->h, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XUngrabPointer(dpy, CurrentTime);
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+Client *
+nexttiled(Client *c)
+{
+ for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
+ return c;
+}
+
+void
+pop(Client *c)
+{
+ detach(c);
+ attach(c);
+ focus(c);
+ arrange(c->mon);
+}
+
+void
+propertynotify(XEvent *e)
+{
+ Client *c;
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
+ if ((c = wintosystrayicon(ev->window))) {
+ if (ev->atom == XA_WM_NORMAL_HINTS) {
+ updatesizehints(c);
+ updatesystrayicongeom(c, c->w, c->h);
+ }
+ else
+ updatesystrayiconstate(c, ev);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+ if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if (ev->state == PropertyDelete)
+ return; /* ignore */
+ else if ((c = wintoclient(ev->window))) {
+ switch(ev->atom) {
+ default: break;
+ case XA_WM_TRANSIENT_FOR:
+ if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
+ (c->isfloating = (wintoclient(trans)) != NULL))
+ arrange(c->mon);
+ break;
+ case XA_WM_NORMAL_HINTS:
+ updatesizehints(c);
+ break;
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
+ break;
+ }
+ if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if (c == c->mon->sel)
+ drawbar(c->mon);
+ }
+ if (ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+ }
+}
+
+void
+quit(const Arg *arg)
+{
+ running = 0;
+}
+
+Monitor *
+recttomon(int x, int y, int w, int h)
+{
+ Monitor *m, *r = selmon;
+ int a, area = 0;
+
+ for (m = mons; m; m = m->next)
+ if ((a = INTERSECT(x, y, w, h, m)) > area) {
+ area = a;
+ r = m;
+ }
+ return r;
+}
+
+void
+removesystrayicon(Client *i)
+{
+ Client **ii;
+
+ if (!showsystray || !i)
+ return;
+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
+ if (ii)
+ *ii = i->next;
+ free(i);
+}
+
+
+void
+resize(Client *c, int x, int y, int w, int h, int interact)
+{
+ if (applysizehints(c, &x, &y, &w, &h, interact))
+ resizeclient(c, x, y, w, h);
+}
+
+void
+resizebarwin(Monitor *m) {
+ unsigned int w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
+}
+
+void
+resizeclient(Client *c, int x, int y, int w, int h)
+{
+ XWindowChanges wc;
+
+ c->oldx = c->x; c->x = wc.x = x;
+ c->oldy = c->y; c->y = wc.y = y;
+ c->oldw = c->w; c->w = wc.width = w;
+ c->oldh = c->h; c->h = wc.height = h;
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
+ configure(c);
+ XSync(dpy, False);
+}
+
+void
+resizemouse(const Arg *arg)
+{
+ int ocx, ocy, nw, nh;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
+ nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
+ if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
+ && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
+ {
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
+ togglefloating(NULL);
+ }
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, c->x, c->y, nw, nh, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ XUngrabPointer(dpy, CurrentTime);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+void
+resizerequest(XEvent *e)
+{
+ XResizeRequestEvent *ev = &e->xresizerequest;
+ Client *i;
+
+ if ((i = wintosystrayicon(ev->window))) {
+ updatesystrayicongeom(i, ev->width, ev->height);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+restack(Monitor *m)
+{
+ Client *c;
+ XEvent ev;
+ XWindowChanges wc;
+
+ drawbar(m);
+ if (!m->sel)
+ return;
+ if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
+ XRaiseWindow(dpy, m->sel->win);
+ if (m->lt[m->sellt]->arrange) {
+ wc.stack_mode = Below;
+ wc.sibling = m->barwin;
+ for (c = m->stack; c; c = c->snext)
+ if (!c->isfloating && ISVISIBLE(c)) {
+ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
+ wc.sibling = c->win;
+ }
+ }
+ XSync(dpy, False);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+}
+
+void
+run(void)
+{
+ XEvent ev;
+ /* main event loop */
+ XSync(dpy, False);
+ while (running && !XNextEvent(dpy, &ev))
+ if (handler[ev.type])
+ handler[ev.type](&ev); /* call handler */
+}
+
+void
+runAutostart(void) {
+ system("cd ~/.config/dwm; ./autostart_blocking.sh");
+ system("cd ~/.config/dwm; ./autostart.sh &");
+}
+
+void
+scan(void)
+{
+ unsigned int i, num;
+ Window d1, d2, *wins = NULL;
+ XWindowAttributes wa;
+
+ if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
+ for (i = 0; i < num; i++) {
+ if (!XGetWindowAttributes(dpy, wins[i], &wa)
+ || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
+ continue;
+ if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
+ manage(wins[i], &wa);
+ }
+ for (i = 0; i < num; i++) { /* now the transients */
+ if (!XGetWindowAttributes(dpy, wins[i], &wa))
+ continue;
+ if (XGetTransientForHint(dpy, wins[i], &d1)
+ && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
+ manage(wins[i], &wa);
+ }
+ if (wins)
+ XFree(wins);
+ }
+}
+
+void
+sendmon(Client *c, Monitor *m)
+{
+ if (c->mon == m)
+ return;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->mon = m;
+ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ focus(NULL);
+ arrange(NULL);
+}
+
+void
+setclientstate(Client *c, long state)
+{
+ long data[] = { state, None };
+
+ XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
+ PropModeReplace, (unsigned char *)data, 2);
+}
+
+int
+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
+{
+ int n;
+ Atom *protocols, mt;
+ int exists = 0;
+ XEvent ev;
+
+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
+ mt = wmatom[WMProtocols];
+ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
+ while (!exists && n--)
+ exists = protocols[n] == proto;
+ XFree(protocols);
+ }
+ }
+ else {
+ exists = True;
+ mt = proto;
+ }
+ if (exists) {
+ ev.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = d0;
+ ev.xclient.data.l[1] = d1;
+ ev.xclient.data.l[2] = d2;
+ ev.xclient.data.l[3] = d3;
+ ev.xclient.data.l[4] = d4;
+ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+}
+
+void
+setfocus(Client *c)
+{
+ if (!c->neverfocus) {
+ XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
+ XChangeProperty(dpy, root, netatom[NetActiveWindow],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+ if (fullscreen && !c->isfullscreen) {
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
+ c->isfullscreen = 1;
+ c->oldstate = c->isfloating;
+ c->oldbw = c->bw;
+ c->bw = 0;
+ c->isfloating = 1;
+ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
+ XRaiseWindow(dpy, c->win);
+ } else if (!fullscreen && c->isfullscreen){
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)0, 0);
+ c->isfullscreen = 0;
+ c->isfloating = c->oldstate;
+ c->bw = c->oldbw;
+ c->x = c->oldx;
+ c->y = c->oldy;
+ c->w = c->oldw;
+ c->h = c->oldh;
+ resizeclient(c, c->x, c->y, c->w, c->h);
+ arrange(c->mon);
+ }
+}
+
+void
+setgaps(const Arg *arg)
+{
+ if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
+ selmon->gappx = 0;
+ else
+ selmon->gappx += arg->i;
+ arrange(selmon);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if (selmon->sel)
+ arrange(selmon);
+ else
+ drawbar(selmon);
+}
+
+/* arg > 1.0 will set mfact absolutely */
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !selmon->lt[selmon->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ selmon->mfact = f;
+ arrange(selmon);
+}
+
+void
+setup(void)
+{
+ int i;
+ XSetWindowAttributes wa;
+ Atom utf8string;
+
+ /* clean up any zombies immediately */
+ sigchld(0);
+
+ /* init screen */
+ screen = DefaultScreen(dpy);
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
+ root = RootWindow(dpy, screen);
+ drw = drw_create(dpy, screen, root, sw, sh);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+ bh = drw->fonts->h + 2;
+ updategeom();
+ /* init atoms */
+ utf8string = XInternAtom(dpy, "UTF8_STRING", False);
+ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+ netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+ cursor[CurMove] = drw_cur_create(drw, XC_fleur);
+ /* init appearance */
+ scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
+ for (i = 0; i < LENGTH(colors); i++)
+ scheme[i] = drw_scm_create(drw, colors[i], 3);
+ /* init system tray */
+ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+ /* supporting window for NetWMCheck */
+ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
+ PropModeReplace, (unsigned char *) "dwm", 3);
+ XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ /* EWMH support per view */
+ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) netatom, NetLast);
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ /* select events */
+ wa.cursor = cursor[CurNormal]->cursor;
+ wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
+ |ButtonPressMask|PointerMotionMask|EnterWindowMask
+ |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
+ XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
+ XSelectInput(dpy, root, wa.event_mask);
+ grabkeys();
+ focus(NULL);
+}
+
+
+void
+seturgent(Client *c, int urg)
+{
+ XWMHints *wmh;
+
+ c->isurgent = urg;
+ if (!(wmh = XGetWMHints(dpy, c->win)))
+ return;
+ wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
+ XSetWMHints(dpy, c->win, wmh);
+ XFree(wmh);
+}
+
+void
+showhide(Client *c)
+{
+ if (!c)
+ return;
+ if (ISVISIBLE(c)) {
+ /* show clients top down */
+ XMoveWindow(dpy, c->win, c->x, c->y);
+ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+ resize(c, c->x, c->y, c->w, c->h, 0);
+ showhide(c->snext);
+ } else {
+ /* hide clients bottom up */
+ showhide(c->snext);
+ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
+ }
+}
+
+void
+sigchld(int unused)
+{
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("can't install SIGCHLD handler:");
+ while (0 < waitpid(-1, NULL, WNOHANG));
+}
+
+void
+spawn(const Arg *arg)
+{
+ if (arg->v == dmenucmd)
+ dmenumon[0] = '0' + selmon->num;
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ setsid();
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
+ perror(" failed");
+ exit(EXIT_SUCCESS);
+ }
+}
+
+void
+tag(const Arg *arg)
+{
+ if (selmon->sel && arg->ui & TAGMASK) {
+ selmon->sel->tags = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+tagmon(const Arg *arg)
+{
+ if (!selmon->sel || !mons->next)
+ return;
+ sendmon(selmon->sel, dirtomon(arg->i));
+}
+
+void
+tile(Monitor *m)
+{
+ unsigned int i, n, h, mw, my, ty;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (n == 0)
+ return;
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->ww * m->mfact : 0;
+ else
+ mw = m->ww - m->gappx;
+ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
+ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
+ my += HEIGHT(c) + m->gappx;
+ } else {
+ h = (m->wh - ty) / (n - i) - m->gappx;
+ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
+ ty += HEIGHT(c) + m->gappx;
+ }
+}
+
+void
+togglebar(const Arg *arg)
+{
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+ resizebarwin(selmon);
+ if (showsystray) {
+ XWindowChanges wc;
+ if (!selmon->showbar)
+ wc.y = -bh;
+ else if (selmon->showbar) {
+ wc.y = 0;
+ if (!selmon->topbar)
+ wc.y = selmon->mh - bh;
+ }
+ XConfigureWindow(dpy, systray->win, CWY, &wc);
+ }
+ arrange(selmon);
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
+ return;
+ selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
+ if (selmon->sel->isfloating)
+ resize(selmon->sel, selmon->sel->x, selmon->sel->y,
+ selmon->sel->w, selmon->sel->h, 0);
+ arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ unsigned int newtags;
+
+ if (!selmon->sel)
+ return;
+ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
+ selmon->sel->tags = newtags;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+toggleview(const Arg *arg)
+{
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
+
+ if (newtagset) {
+ selmon->tagset[selmon->seltags] = newtagset;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+unfocus(Client *c, int setfocus)
+{
+ if (!c)
+ return;
+ grabbuttons(c, 0);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
+ if (setfocus) {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+}
+
+void
+unmanage(Client *c, int destroyed)
+{
+ Monitor *m = c->mon;
+ XWindowChanges wc;
+
+ detach(c);
+ detachstack(c);
+ if (!destroyed) {
+ wc.border_width = c->oldbw;
+ XGrabServer(dpy); /* avoid race conditions */
+ XSetErrorHandler(xerrordummy);
+ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ setclientstate(c, WithdrawnState);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+ free(c);
+ focus(NULL);
+ updateclientlist();
+ arrange(m);
+}
+
+void
+unmapnotify(XEvent *e)
+{
+ Client *c;
+ XUnmapEvent *ev = &e->xunmap;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->send_event)
+ setclientstate(c, WithdrawnState);
+ else
+ unmanage(c, 0);
+ }
+ else if ((c = wintosystrayicon(ev->window))) {
+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
+ * _not_ destroy them. We map those windows back */
+ XMapRaised(dpy, c->win);
+ updatesystray();
+ }
+}
+
+void
+updatebars(void)
+{
+ unsigned int w;
+ Monitor *m;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .background_pixmap = ParentRelative,
+ .event_mask = ButtonPressMask|ExposureMask
+ };
+ XClassHint ch = {"dwm", "dwm"};
+ for (m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+ w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ if (showsystray && m == systraytomon(m))
+ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+}
+
+void
+updatebarpos(Monitor *m)
+{
+ m->wy = m->my;
+ m->wh = m->mh;
+ if (m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+ m->wy = m->topbar ? m->wy + bh : m->wy;
+ } else
+ m->by = -bh;
+}
+
+void
+updateclientlist()
+{
+ Client *c;
+ Monitor *m;
+
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ XChangeProperty(dpy, root, netatom[NetClientList],
+ XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+}
+
+int
+updategeom(void)
+{
+ int dirty = 0;
+
+#ifdef XINERAMA
+ if (XineramaIsActive(dpy)) {
+ int i, j, n, nn;
+ Client *c;
+ Monitor *m;
+ XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
+ XineramaScreenInfo *unique = NULL;
+
+ for (n = 0, m = mons; m; m = m->next, n++);
+ /* only consider unique geometries as separate screens */
+ unique = ecalloc(nn, sizeof(XineramaScreenInfo));
+ for (i = 0, j = 0; i < nn; i++)
+ if (isuniquegeom(unique, j, &info[i]))
+ memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
+ XFree(info);
+ nn = j;
+ if (n <= nn) { /* new monitors available */
+ for (i = 0; i < (nn - n); i++) {
+ for (m = mons; m && m->next; m = m->next);
+ if (m)
+ m->next = createmon();
+ else
+ mons = createmon();
+ }
+ for (i = 0, m = mons; i < nn && m; m = m->next, i++)
+ if (i >= n
+ || unique[i].x_org != m->mx || unique[i].y_org != m->my
+ || unique[i].width != m->mw || unique[i].height != m->mh)
+ {
+ dirty = 1;
+ m->num = i;
+ m->mx = m->wx = unique[i].x_org;
+ m->my = m->wy = unique[i].y_org;
+ m->mw = m->ww = unique[i].width;
+ m->mh = m->wh = unique[i].height;
+ updatebarpos(m);
+ }
+ } else { /* less monitors available nn < n */
+ for (i = nn; i < n; i++) {
+ for (m = mons; m && m->next; m = m->next);
+ while ((c = m->clients)) {
+ dirty = 1;
+ m->clients = c->next;
+ detachstack(c);
+ c->mon = mons;
+ attach(c);
+ attachstack(c);
+ }
+ if (m == selmon)
+ selmon = mons;
+ cleanupmon(m);
+ }
+ }
+ free(unique);
+ } else
+#endif /* XINERAMA */
+ { /* default monitor setup */
+ if (!mons)
+ mons = createmon();
+ if (mons->mw != sw || mons->mh != sh) {
+ dirty = 1;
+ mons->mw = mons->ww = sw;
+ mons->mh = mons->wh = sh;
+ updatebarpos(mons);
+ }
+ }
+ if (dirty) {
+ selmon = mons;
+ selmon = wintomon(root);
+ }
+ return dirty;
+}
+
+void
+updatenumlockmask(void)
+{
+ unsigned int i, j;
+ XModifierKeymap *modmap;
+
+ numlockmask = 0;
+ modmap = XGetModifierMapping(dpy);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < modmap->max_keypermod; j++)
+ if (modmap->modifiermap[i * modmap->max_keypermod + j]
+ == XKeysymToKeycode(dpy, XK_Num_Lock))
+ numlockmask = (1 << i);
+ XFreeModifiermap(modmap);
+}
+
+void
+updatesizehints(Client *c)
+{
+ long msize;
+ XSizeHints size;
+
+ if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
+ /* size is uninitialized, ensure that size.flags aren't used */
+ size.flags = PSize;
+ if (size.flags & PBaseSize) {
+ c->basew = size.base_width;
+ c->baseh = size.base_height;
+ } else if (size.flags & PMinSize) {
+ c->basew = size.min_width;
+ c->baseh = size.min_height;
+ } else
+ c->basew = c->baseh = 0;
+ if (size.flags & PResizeInc) {
+ c->incw = size.width_inc;
+ c->inch = size.height_inc;
+ } else
+ c->incw = c->inch = 0;
+ if (size.flags & PMaxSize) {
+ c->maxw = size.max_width;
+ c->maxh = size.max_height;
+ } else
+ c->maxw = c->maxh = 0;
+ if (size.flags & PMinSize) {
+ c->minw = size.min_width;
+ c->minh = size.min_height;
+ } else if (size.flags & PBaseSize) {
+ c->minw = size.base_width;
+ c->minh = size.base_height;
+ } else
+ c->minw = c->minh = 0;
+ if (size.flags & PAspect) {
+ c->mina = (float)size.min_aspect.y / size.min_aspect.x;
+ c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
+ } else
+ c->maxa = c->mina = 0.0;
+ c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
+}
+
+void
+updatestatus(void)
+{
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ strcpy(stext, "dwm-"VERSION);
+ drawbar(selmon);
+ updatesystray();
+}
+
+void
+updatesystrayicongeom(Client *i, int w, int h)
+{
+ if (i) {
+ i->h = bh;
+ if (w == h)
+ i->w = bh;
+ else if (h == bh)
+ i->w = w;
+ else
+ i->w = (int) ((float)bh * ((float)w / (float)h));
+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
+ /* force icons into the systray dimensions if they don't want to */
+ if (i->h > bh) {
+ if (i->w == i->h)
+ i->w = bh;
+ else
+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
+ i->h = bh;
+ }
+ }
+}
+
+void
+updatesystrayiconstate(Client *i, XPropertyEvent *ev)
+{
+ long flags;
+ int code = 0;
+
+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
+ !(flags = getatomprop(i, xatom[XembedInfo])))
+ return;
+
+ if (flags & XEMBED_MAPPED && !i->tags) {
+ i->tags = 1;
+ code = XEMBED_WINDOW_ACTIVATE;
+ XMapRaised(dpy, i->win);
+ setclientstate(i, NormalState);
+ }
+ else if (!(flags & XEMBED_MAPPED) && i->tags) {
+ i->tags = 0;
+ code = XEMBED_WINDOW_DEACTIVATE;
+ XUnmapWindow(dpy, i->win);
+ setclientstate(i, WithdrawnState);
+ }
+ else
+ return;
+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
+ systray->win, XEMBED_EMBEDDED_VERSION);
+}
+
+void
+updatesystray(void)
+{
+ XSetWindowAttributes wa;
+ XWindowChanges wc;
+ Client *i;
+ Monitor *m = systraytomon(NULL);
+ unsigned int x = m->mx + m->mw;
+ unsigned int w = 1;
+
+ if (!showsystray)
+ return;
+ if (!systray) {
+ /* init systray */
+ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
+ wa.event_mask = ButtonPressMask | ExposureMask;
+ wa.override_redirect = True;
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
+ XMapRaised(dpy, systray->win);
+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
+ XSync(dpy, False);
+ }
+ else {
+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
+ free(systray);
+ systray = NULL;
+ return;
+ }
+ }
+ for (w = 0, i = systray->icons; i; i = i->next) {
+ /* make sure the background color stays the same */
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
+ XMapRaised(dpy, i->win);
+ w += systrayspacing;
+ i->x = w;
+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
+ w += i->w;
+ if (i->mon != m)
+ i->mon = m;
+ }
+ w = w ? w + systrayspacing : 1;
+ x -= w;
+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
+ wc.stack_mode = Above; wc.sibling = m->barwin;
+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
+ XMapWindow(dpy, systray->win);
+ XMapSubwindows(dpy, systray->win);
+ /* redraw background */
+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
+ XSync(dpy, False);
+}
+
+void
+updatetitle(Client *c)
+{
+ if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
+ gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
+ if (c->name[0] == '\0') /* hack to mark broken clients */
+ strcpy(c->name, broken);
+}
+
+void
+updatewindowtype(Client *c)
+{
+ Atom state = getatomprop(c, netatom[NetWMState]);
+ Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
+
+ if (state == netatom[NetWMFullscreen])
+ setfullscreen(c, 1);
+ if (wtype == netatom[NetWMWindowTypeDialog])
+ c->isfloating = 1;
+}
+
+void
+updatewmhints(Client *c)
+{
+ XWMHints *wmh;
+
+ if ((wmh = XGetWMHints(dpy, c->win))) {
+ if (c == selmon->sel && wmh->flags & XUrgencyHint) {
+ wmh->flags &= ~XUrgencyHint;
+ XSetWMHints(dpy, c->win, wmh);
+ } else
+ c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
+ if (wmh->flags & InputHint)
+ c->neverfocus = !wmh->input;
+ else
+ c->neverfocus = 0;
+ XFree(wmh);
+ }
+}
+
+void
+view(const Arg *arg)
+{
+ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+}
+
+Client *
+wintoclient(Window w)
+{
+ Client *c;
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ if (c->win == w)
+ return c;
+ return NULL;
+}
+
+Client *
+wintosystrayicon(Window w) {
+ Client *i = NULL;
+
+ if (!showsystray || !w)
+ return i;
+ for (i = systray->icons; i && i->win != w; i = i->next) ;
+ return i;
+}
+
+Monitor *
+wintomon(Window w)
+{
+ int x, y;
+ Client *c;
+ Monitor *m;
+
+ if (w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for (m = mons; m; m = m->next)
+ if (w == m->barwin)
+ return m;
+ if ((c = wintoclient(w)))
+ return c->mon;
+ return selmon;
+}
+
+/* There's no way to check accesses to destroyed windows, thus those cases are
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
+ * default error handler, which may call exit. */
+int
+xerror(Display *dpy, XErrorEvent *ee)
+{
+ if (ee->error_code == BadWindow
+ || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
+ || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
+ || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
+ || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
+ || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
+ || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
+ return 0;
+ fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
+ ee->request_code, ee->error_code);
+ return xerrorxlib(dpy, ee); /* may call exit */
+}
+
+int
+xerrordummy(Display *dpy, XErrorEvent *ee)
+{
+ return 0;
+}
+
+/* Startup Error handler to check if another window manager
+ * is already running. */
+int
+xerrorstart(Display *dpy, XErrorEvent *ee)
+{
+ die("dwm: another window manager is already running");
+ return -1;
+}
+
+Monitor *
+systraytomon(Monitor *m) {
+ Monitor *t;
+ int i, n;
+ if(!systraypinning) {
+ if(!m)
+ return selmon;
+ return m == selmon ? m : NULL;
+ }
+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
+ if(systraypinningfailfirst && n < systraypinning)
+ return mons;
+ return t;
+}
+
+void
+zoom(const Arg *arg)
+{
+ Client *c = selmon->sel;
+
+ if (!selmon->lt[selmon->sellt]->arrange
+ || (selmon->sel && selmon->sel->isfloating))
+ return;
+ if (c == nexttiled(selmon->clients))
+ if (!c || !(c = nexttiled(c->next)))
+ return;
+ pop(c);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc == 2 && !strcmp("-v", argv[1]))
+ die("dwm-"VERSION);
+ else if (argc != 1)
+ die("usage: dwm [-v]");
+ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fputs("warning: no locale support\n", stderr);
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("dwm: cannot open display");
+ checkotherwm();
+ setup();
+#ifdef __OpenBSD__
+ if (pledge("stdio rpath proc exec", NULL) == -1)
+ die("pledge");
+#endif /* __OpenBSD__ */
+ scan();
+ runAutostart();
+ run();
+ cleanup();
+ XCloseDisplay(dpy);
+ return EXIT_SUCCESS;
+}
+
+static void
+bstack(Monitor *m) {
+ int w, h, mh, mx, tx, ty, tw;
+ unsigned int i, n;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (n == 0)
+ return;
+ if (n > m->nmaster) {
+ mh = m->nmaster ? m->mfact * m->wh : 0;
+ tw = m->ww / (n - m->nmaster);
+ ty = m->wy + mh;
+ } else {
+ mh = m->wh;
+ tw = m->ww;
+ ty = m->wy;
+ }
+ for (i = mx = 0, tx = m->wx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
+ if (i < m->nmaster) {
+ w = (m->ww - mx) / (MIN(n, m->nmaster) - i);
+ resize(c, m->wx + mx, m->wy, w - (2 * c->bw), mh - (2 * c->bw), 0);
+ mx += WIDTH(c);
+ } else {
+ h = m->wh - mh;
+ resize(c, tx, ty, tw - (2 * c->bw), h - (2 * c->bw), 0);
+ if (tw != m->ww)
+ tx += WIDTH(c);
+ }
+ }
+}
+
+static void
+bstackhoriz(Monitor *m) {
+ int w, mh, mx, tx, ty, th;
+ unsigned int i, n;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (n == 0)
+ return;
+ if (n > m->nmaster) {
+ mh = m->nmaster ? m->mfact * m->wh : 0;
+ th = (m->wh - mh) / (n - m->nmaster);
+ ty = m->wy + mh;
+ } else {
+ th = mh = m->wh;
+ ty = m->wy;
+ }
+ for (i = mx = 0, tx = m->wx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
+ if (i < m->nmaster) {
+ w = (m->ww - mx) / (MIN(n, m->nmaster) - i);
+ resize(c, m->wx + mx, m->wy, w - (2 * c->bw), mh - (2 * c->bw), 0);
+ mx += WIDTH(c);
+ } else {
+ resize(c, tx, ty, m->ww - (2 * c->bw), th - (2 * c->bw), 0);
+ if (th != m->wh)
+ ty += HEIGHT(c);
+ }
+ }
+}
diff --git a/keep/manual-programs/suckless/dwm/dwm.c.orig b/keep/manual-programs/suckless/dwm/dwm.c.orig
new file mode 100644
index 0000000..f4e0d0b
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm.c.orig
@@ -0,0 +1,2524 @@
+/* See LICENSE file for copyright and license details.
+ *
+ * dynamic window manager is designed like any other X client as well. It is
+ * driven through handling X events. In contrast to other X clients, a window
+ * manager selects for SubstructureRedirectMask on the root window, to receive
+ * events about window (dis-)appearance. Only one X connection at a time is
+ * allowed to select for this event mask.
+ *
+ * The event handlers of dwm are organized in an array which is accessed
+ * whenever a new event has been fetched. This allows event dispatching
+ * in O(1) time.
+ *
+ * Each child of the root window is called a client, except windows which have
+ * set the override_redirect flag. Clients are organized in a linked client
+ * list on each monitor, the focus history is remembered through a stack list
+ * on each monitor. Each client contains a bit array to indicate the tags of a
+ * client.
+ *
+ * Keys and tagging rules are organized as arrays and defined in config.h.
+ *
+ * To understand everything else, start reading main().
+ */
+#include <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif /* XINERAMA */
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+/* macros */
+#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
+#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
+#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
+ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
+#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
+#define WIDTH(X) ((X)->w + 2 * (X)->bw)
+#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
+#define TAGMASK ((1 << LENGTH(tags)) - 1)
+#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+
+/* XEMBED messages */
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_IN 4
+#define XEMBED_MODALITY_ON 10
+
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_WINDOW_DEACTIVATE 2
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 0
+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
+
+/* enums */
+enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
+enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
+enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
+ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+
+typedef union {
+ int i;
+ unsigned int ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int click;
+ unsigned int mask;
+ unsigned int button;
+ void (*func)(const Arg *arg);
+ const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct Client Client;
+struct Client {
+ char name[256];
+ float mina, maxa;
+ int x, y, w, h;
+ int oldx, oldy, oldw, oldh;
+ int basew, baseh, incw, inch, maxw, maxh, minw, minh;
+ int bw, oldbw;
+ unsigned int tags;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+ Window win;
+};
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+} Layout;
+
+struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+ int nmaster;
+ int num;
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ int gappx; /* gaps between windows */
+ unsigned int seltags;
+ unsigned int sellt;
+ unsigned int tagset[2];
+ int showbar;
+ int topbar;
+ Client *clients;
+ Client *sel;
+ Client *stack;
+ Monitor *next;
+ Window barwin;
+ const Layout *lt[2];
+};
+
+typedef struct {
+ const char *class;
+ const char *instance;
+ const char *title;
+ unsigned int tags;
+ int isfloating;
+ int monitor;
+} Rule;
+
+typedef struct Systray Systray;
+struct Systray {
+ Window win;
+ Client *icons;
+};
+
+/* function declarations */
+static void applyrules(Client *c);
+static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+static void arrange(Monitor *m);
+static void arrangemon(Monitor *m);
+static void attach(Client *c);
+static void attachstack(Client *c);
+static void buttonpress(XEvent *e);
+static void checkotherwm(void);
+static void cleanup(void);
+static void cleanupmon(Monitor *mon);
+static void clientmessage(XEvent *e);
+static void configure(Client *c);
+static void configurenotify(XEvent *e);
+static void configurerequest(XEvent *e);
+static Monitor *createmon(void);
+static void destroynotify(XEvent *e);
+static void detach(Client *c);
+static void detachstack(Client *c);
+static Monitor *dirtomon(int dir);
+static void drawbar(Monitor *m);
+static void drawbars(void);
+static void enternotify(XEvent *e);
+static void expose(XEvent *e);
+static void focus(Client *c);
+static void focusin(XEvent *e);
+static void focusmon(const Arg *arg);
+static void focusstack(const Arg *arg);
+static Atom getatomprop(Client *c, Atom prop);
+static int getrootptr(int *x, int *y);
+static long getstate(Window w);
+static unsigned int getsystraywidth();
+static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
+static void grabbuttons(Client *c, int focused);
+static void grabkeys(void);
+static void incnmaster(const Arg *arg);
+static void keypress(XEvent *e);
+static void killclient(const Arg *arg);
+static void manage(Window w, XWindowAttributes *wa);
+static void mappingnotify(XEvent *e);
+static void maprequest(XEvent *e);
+static void monocle(Monitor *m);
+static void motionnotify(XEvent *e);
+static void movemouse(const Arg *arg);
+static Client *nexttiled(Client *c);
+static void pop(Client *);
+static void propertynotify(XEvent *e);
+static void quit(const Arg *arg);
+static Monitor *recttomon(int x, int y, int w, int h);
+static void removesystrayicon(Client *i);
+static void resize(Client *c, int x, int y, int w, int h, int interact);
+static void resizebarwin(Monitor *m);
+static void resizeclient(Client *c, int x, int y, int w, int h);
+static void resizemouse(const Arg *arg);
+static void resizerequest(XEvent *e);
+static void restack(Monitor *m);
+static void run(void);
+static void runAutostart(void);
+static void scan(void);
+static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
+static void sendmon(Client *c, Monitor *m);
+static void setclientstate(Client *c, long state);
+static void setfocus(Client *c);
+static void setfullscreen(Client *c, int fullscreen);
+static void setgaps(const Arg *arg);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setup(void);
+static void seturgent(Client *c, int urg);
+static void showhide(Client *c);
+static void sigchld(int unused);
+static void spawn(const Arg *arg);
+static Monitor *systraytomon(Monitor *m);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *);
+static void togglebar(const Arg *arg);
+static void togglefloating(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void unfocus(Client *c, int setfocus);
+static void unmanage(Client *c, int destroyed);
+static void unmapnotify(XEvent *e);
+static void updatebarpos(Monitor *m);
+static void updatebars(void);
+static void updateclientlist(void);
+static int updategeom(void);
+static void updatenumlockmask(void);
+static void updatesizehints(Client *c);
+static void updatestatus(void);
+static void updatesystray(void);
+static void updatesystrayicongeom(Client *i, int w, int h);
+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
+static void updatetitle(Client *c);
+static void updatewindowtype(Client *c);
+static void updatewmhints(Client *c);
+static void view(const Arg *arg);
+static Client *wintoclient(Window w);
+static Monitor *wintomon(Window w);
+static Client *wintosystrayicon(Window w);
+static int xerror(Display *dpy, XErrorEvent *ee);
+static int xerrordummy(Display *dpy, XErrorEvent *ee);
+static int xerrorstart(Display *dpy, XErrorEvent *ee);
+static void zoom(const Arg *arg);
+
+/* variables */
+static Systray *systray = NULL;
+static const char broken[] = "broken";
+static char stext[256];
+static int screen;
+static int sw, sh; /* X display screen geometry width, height */
+static int bh, blw = 0; /* bar geometry */
+static int lrpad; /* sum of left and right padding for text */
+static int (*xerrorxlib)(Display *, XErrorEvent *);
+static unsigned int numlockmask = 0;
+static void (*handler[LASTEvent]) (XEvent *) = {
+ [ButtonPress] = buttonpress,
+ [ClientMessage] = clientmessage,
+ [ConfigureRequest] = configurerequest,
+ [ConfigureNotify] = configurenotify,
+ [DestroyNotify] = destroynotify,
+ //[EnterNotify] = enternotify,
+ [Expose] = expose,
+ [FocusIn] = focusin,
+ [KeyPress] = keypress,
+ [MappingNotify] = mappingnotify,
+ [MapRequest] = maprequest,
+ [MotionNotify] = motionnotify,
+ [PropertyNotify] = propertynotify,
+ [ResizeRequest] = resizerequest,
+ [UnmapNotify] = unmapnotify
+};
+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
+static int running = 1;
+static Cur *cursor[CurLast];
+static Clr **scheme;
+static Display *dpy;
+static Drw *drw;
+static Monitor *mons, *selmon;
+static Window root, wmcheckwin;
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* compile-time check if all tags fit into an unsigned int bit array. */
+struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+/* function implementations */
+void
+applyrules(Client *c)
+{
+ const char *class, *instance;
+ unsigned int i;
+ const Rule *r;
+ Monitor *m;
+ XClassHint ch = { NULL, NULL };
+
+ /* rule matching */
+ c->isfloating = 0;
+ c->tags = 0;
+ XGetClassHint(dpy, c->win, &ch);
+ class = ch.res_class ? ch.res_class : broken;
+ instance = ch.res_name ? ch.res_name : broken;
+
+ for (i = 0; i < LENGTH(rules); i++) {
+ r = &rules[i];
+ if ((!r->title || strstr(c->name, r->title))
+ && (!r->class || strstr(class, r->class))
+ && (!r->instance || strstr(instance, r->instance)))
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+ if (m)
+ c->mon = m;
+ }
+ }
+ if (ch.res_class)
+ XFree(ch.res_class);
+ if (ch.res_name)
+ XFree(ch.res_name);
+ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
+}
+
+int
+applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
+{
+ int baseismin;
+ Monitor *m = c->mon;
+
+ /* set minimum possible */
+ *w = MAX(1, *w);
+ *h = MAX(1, *h);
+ if (interact) {
+ if (*x > sw)
+ *x = sw - WIDTH(c);
+ if (*y > sh)
+ *y = sh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw < 0)
+ *x = 0;
+ if (*y + *h + 2 * c->bw < 0)
+ *y = 0;
+ } else {
+ if (*x >= m->wx + m->ww)
+ *x = m->wx + m->ww - WIDTH(c);
+ if (*y >= m->wy + m->wh)
+ *y = m->wy + m->wh - HEIGHT(c);
+ if (*x + *w + 2 * c->bw <= m->wx)
+ *x = m->wx;
+ if (*y + *h + 2 * c->bw <= m->wy)
+ *y = m->wy;
+ }
+ if (*h < bh)
+ *h = bh;
+ if (*w < bh)
+ *w = bh;
+ if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ /* see last two sentences in ICCCM 4.1.2.3 */
+ baseismin = c->basew == c->minw && c->baseh == c->minh;
+ if (!baseismin) { /* temporarily remove base dimensions */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for aspect limits */
+ if (c->mina > 0 && c->maxa > 0) {
+ if (c->maxa < (float)*w / *h)
+ *w = *h * c->maxa + 0.5;
+ else if (c->mina < (float)*h / *w)
+ *h = *w * c->mina + 0.5;
+ }
+ if (baseismin) { /* increment calculation requires this */
+ *w -= c->basew;
+ *h -= c->baseh;
+ }
+ /* adjust for increment value */
+ if (c->incw)
+ *w -= *w % c->incw;
+ if (c->inch)
+ *h -= *h % c->inch;
+ /* restore base dimensions */
+ *w = MAX(*w + c->basew, c->minw);
+ *h = MAX(*h + c->baseh, c->minh);
+ if (c->maxw)
+ *w = MIN(*w, c->maxw);
+ if (c->maxh)
+ *h = MIN(*h, c->maxh);
+ }
+ return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
+}
+
+void
+arrange(Monitor *m)
+{
+ if (m)
+ showhide(m->stack);
+ else for (m = mons; m; m = m->next)
+ showhide(m->stack);
+ if (m) {
+ arrangemon(m);
+ restack(m);
+ } else for (m = mons; m; m = m->next)
+ arrangemon(m);
+}
+
+void
+arrangemon(Monitor *m)
+{
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+}
+
+void
+attach(Client *c)
+{
+ c->next = c->mon->clients;
+ c->mon->clients = c;
+}
+
+void
+attachstack(Client *c)
+{
+ c->snext = c->mon->stack;
+ c->mon->stack = c;
+}
+
+void
+buttonpress(XEvent *e)
+{
+ unsigned int i, x, click;
+ Arg arg = {0};
+ Client *c;
+ Monitor *m;
+ XButtonPressedEvent *ev = &e->xbutton;
+
+ click = ClkRootWin;
+ /* focus monitor if necessary */
+ if ((m = wintomon(ev->window)) && m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ if (ev->window == selmon->barwin) {
+ i = x = 0;
+ do
+ x += TEXTW(tags[i]);
+ while (ev->x >= x && ++i < LENGTH(tags));
+ if (i < LENGTH(tags)) {
+ click = ClkTagBar;
+ arg.ui = 1 << i;
+ } else if (ev->x < x + blw)
+ click = ClkLtSymbol;
+ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+ } else if ((c = wintoclient(ev->window))) {
+ focus(c);
+ restack(selmon);
+ XAllowEvents(dpy, ReplayPointer, CurrentTime);
+ click = ClkClientWin;
+ }
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+ buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+}
+
+void
+checkotherwm(void)
+{
+ xerrorxlib = XSetErrorHandler(xerrorstart);
+ /* this causes an error if some other window manager is running */
+ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XSync(dpy, False);
+}
+
+void
+cleanup(void)
+{
+ Arg a = {.ui = ~0};
+ Layout foo = { "", NULL };
+ Monitor *m;
+ size_t i;
+
+ view(&a);
+ selmon->lt[selmon->sellt] = &foo;
+ for (m = mons; m; m = m->next)
+ while (m->stack)
+ unmanage(m->stack, 0);
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ while (mons)
+ cleanupmon(mons);
+ if (showsystray) {
+ XUnmapWindow(dpy, systray->win);
+ XDestroyWindow(dpy, systray->win);
+ free(systray);
+ }
+ for (i = 0; i < CurLast; i++)
+ drw_cur_free(drw, cursor[i]);
+ for (i = 0; i < LENGTH(colors); i++)
+ free(scheme[i]);
+ XDestroyWindow(dpy, wmcheckwin);
+ drw_free(drw);
+ XSync(dpy, False);
+ XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+}
+
+void
+cleanupmon(Monitor *mon)
+{
+ Monitor *m;
+
+ if (mon == mons)
+ mons = mons->next;
+ else {
+ for (m = mons; m && m->next != mon; m = m->next);
+ m->next = mon->next;
+ }
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
+ free(mon);
+}
+
+void
+clientmessage(XEvent *e)
+{
+ XWindowAttributes wa;
+ XSetWindowAttributes swa;
+ XClientMessageEvent *cme = &e->xclient;
+ Client *c = wintoclient(cme->window);
+
+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
+ /* add systray icons */
+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
+ if (!(c = (Client *)calloc(1, sizeof(Client))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
+ if (!(c->win = cme->data.l[2])) {
+ free(c);
+ return;
+ }
+ c->mon = selmon;
+ c->next = systray->icons;
+ systray->icons = c;
+ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
+ /* use sane defaults */
+ wa.width = bh;
+ wa.height = bh;
+ wa.border_width = 0;
+ }
+ c->x = c->oldx = c->y = c->oldy = 0;
+ c->w = c->oldw = wa.width;
+ c->h = c->oldh = wa.height;
+ c->oldbw = wa.border_width;
+ c->bw = 0;
+ c->isfloating = True;
+ /* reuse tags field as mapped status */
+ c->tags = 1;
+ updatesizehints(c);
+ updatesystrayicongeom(c, wa.width, wa.height);
+ XAddToSaveSet(dpy, c->win);
+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
+ XClassHint ch = {"dwmsystray", "dwmsystray"};
+ XSetClassHint(dpy, c->win, &ch);
+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
+ /* use parents background color */
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ /* FIXME not sure if I have to send these events, too */
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ XSync(dpy, False);
+ resizebarwin(selmon);
+ updatesystray();
+ setclientstate(c, NormalState);
+ }
+ return;
+ }
+ if (!c)
+ return;
+ if (cme->message_type == netatom[NetWMState]) {
+ if (cme->data.l[1] == netatom[NetWMFullscreen]
+ || cme->data.l[2] == netatom[NetWMFullscreen])
+ setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
+ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
+ } else if (cme->message_type == netatom[NetActiveWindow]) {
+ if (c != selmon->sel && !c->isurgent)
+ seturgent(c, 1);
+ }
+}
+
+void
+configure(Client *c)
+{
+ XConfigureEvent ce;
+
+ ce.type = ConfigureNotify;
+ ce.display = dpy;
+ ce.event = c->win;
+ ce.window = c->win;
+ ce.x = c->x;
+ ce.y = c->y;
+ ce.width = c->w;
+ ce.height = c->h;
+ ce.border_width = c->bw;
+ ce.above = None;
+ ce.override_redirect = False;
+ XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
+}
+
+void
+configurenotify(XEvent *e)
+{
+ Monitor *m;
+ Client *c;
+ XConfigureEvent *ev = &e->xconfigure;
+ int dirty;
+
+ /* TODO: updategeom handling sucks, needs to be simplified */
+ if (ev->window == root) {
+ dirty = (sw != ev->width || sh != ev->height);
+ sw = ev->width;
+ sh = ev->height;
+ if (updategeom() || dirty) {
+ drw_resize(drw, sw, bh);
+ updatebars();
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next)
+ if (c->isfullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+ resizebarwin(m);
+ }
+ focus(NULL);
+ arrange(NULL);
+ }
+ }
+}
+
+void
+configurerequest(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XConfigureRequestEvent *ev = &e->xconfigurerequest;
+ XWindowChanges wc;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->value_mask & CWBorderWidth)
+ c->bw = ev->border_width;
+ else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
+ m = c->mon;
+ if (ev->value_mask & CWX) {
+ c->oldx = c->x;
+ c->x = m->mx + ev->x;
+ }
+ if (ev->value_mask & CWY) {
+ c->oldy = c->y;
+ c->y = m->my + ev->y;
+ }
+ if (ev->value_mask & CWWidth) {
+ c->oldw = c->w;
+ c->w = ev->width;
+ }
+ if (ev->value_mask & CWHeight) {
+ c->oldh = c->h;
+ c->h = ev->height;
+ }
+ if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
+ c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
+ if ((c->y + c->h) > m->my + m->mh && c->isfloating)
+ c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
+ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
+ configure(c);
+ if (ISVISIBLE(c))
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ } else
+ configure(c);
+ } else {
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+ wc.border_width = ev->border_width;
+ wc.sibling = ev->above;
+ wc.stack_mode = ev->detail;
+ XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
+ }
+ XSync(dpy, False);
+}
+
+Monitor *
+createmon(void)
+{
+ Monitor *m;
+
+ m = ecalloc(1, sizeof(Monitor));
+ m->tagset[0] = m->tagset[1] = 1;
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+ m->topbar = topbar;
+ m->gappx = gappx;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ return m;
+}
+
+void
+destroynotify(XEvent *e)
+{
+ Client *c;
+ XDestroyWindowEvent *ev = &e->xdestroywindow;
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
+ else if ((c = wintosystrayicon(ev->window))) {
+ removesystrayicon(c);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+detach(Client *c)
+{
+ Client **tc;
+
+ for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
+ *tc = c->next;
+}
+
+void
+detachstack(Client *c)
+{
+ Client **tc, *t;
+
+ for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
+ *tc = c->snext;
+
+ if (c == c->mon->sel) {
+ for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
+ c->mon->sel = t;
+ }
+}
+
+Monitor *
+dirtomon(int dir)
+{
+ Monitor *m = NULL;
+
+ if (dir > 0) {
+ if (!(m = selmon->next))
+ m = mons;
+ } else if (selmon == mons)
+ for (m = mons; m->next; m = m->next);
+ else
+ for (m = mons; m->next != selmon; m = m->next);
+ return m;
+}
+
+void
+drawbar(Monitor *m)
+{
+ int x, w, sw = 0, stw = 0;
+ int boxs = drw->fonts->h / 9;
+ int boxw = drw->fonts->h / 6 + 2;
+ unsigned int i, occ = 0, urg = 0;
+ Client *c;
+
+ if(showsystray && m == systraytomon(m))
+ stw = getsystraywidth();
+
+ /* draw status first so it can be overdrawn by tags later */
+ if (m == selmon) { /* status is only drawn on selected monitor */
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */
+ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
+ resizebarwin(m);
+ for (c = m->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ x = 0;
+ for (i = 0; i < LENGTH(tags); i++) {
+ w = TEXTW(tags[i]);
+ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+ if (occ & 1 << i)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw,
+ m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+ urg & 1 << i);
+ x += w;
+ }
+ w = blw = TEXTW(m->ltsymbol);
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+ if ((w = m->ww - sw - stw - x) > bh) {
+ if (m->sel) {
+ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+ if (m->sel->isfloating)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
+ } else {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ }
+ }
+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
+}
+
+void
+drawbars(void)
+{
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ drawbar(m);
+}
+
+void
+enternotify(XEvent *e)
+{
+ Client *c;
+ Monitor *m;
+ XCrossingEvent *ev = &e->xcrossing;
+
+ if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
+ return;
+ c = wintoclient(ev->window);
+ m = c ? c->mon : wintomon(ev->window);
+ if (m != selmon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ } else if (!c || c == selmon->sel)
+ return;
+ focus(c);
+}
+
+void
+expose(XEvent *e)
+{
+ Monitor *m;
+ XExposeEvent *ev = &e->xexpose;
+
+ if (ev->count == 0 && (m = wintomon(ev->window))) {
+ drawbar(m);
+ if (m == selmon)
+ updatesystray();
+ }
+}
+
+void
+focus(Client *c)
+{
+ if (!c || !ISVISIBLE(c))
+ for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
+ if (selmon->sel && selmon->sel != c)
+ unfocus(selmon->sel, 0);
+ if (c) {
+ if (c->mon != selmon)
+ selmon = c->mon;
+ if (c->isurgent)
+ seturgent(c, 0);
+ detachstack(c);
+ attachstack(c);
+ grabbuttons(c, 1);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
+ setfocus(c);
+ } else {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+ selmon->sel = c;
+ drawbars();
+}
+
+/* there are some broken focus acquiring clients needing extra handling */
+void
+focusin(XEvent *e)
+{
+ XFocusChangeEvent *ev = &e->xfocus;
+
+ if (selmon->sel && ev->window != selmon->sel->win)
+ setfocus(selmon->sel);
+}
+
+void
+focusmon(const Arg *arg)
+{
+ Monitor *m;
+
+ if (!mons->next)
+ return;
+ if ((m = dirtomon(arg->i)) == selmon)
+ return;
+ unfocus(selmon->sel, 0);
+ selmon = m;
+ focus(NULL);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ Client *c = NULL, *i;
+
+ if (!selmon->sel)
+ return;
+ if (arg->i > 0) {
+ for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
+ if (!c)
+ for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
+ } else {
+ for (i = selmon->clients; i != selmon->sel; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ if (!c)
+ for (; i; i = i->next)
+ if (ISVISIBLE(i))
+ c = i;
+ }
+ if (c) {
+ focus(c);
+ restack(selmon);
+ }
+}
+
+Atom
+getatomprop(Client *c, Atom prop)
+{
+ int di;
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da, atom = None;
+ /* FIXME getatomprop should return the number of items and a pointer to
+ * the stored data instead of this workaround */
+ Atom req = XA_ATOM;
+ if (prop == xatom[XembedInfo])
+ req = xatom[XembedInfo];
+
+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
+ if (da == xatom[XembedInfo] && dl == 2)
+ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+}
+
+int
+getrootptr(int *x, int *y)
+{
+ int di;
+ unsigned int dui;
+ Window dummy;
+
+ return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
+}
+
+long
+getstate(Window w)
+{
+ int format;
+ long result = -1;
+ unsigned char *p = NULL;
+ unsigned long n, extra;
+ Atom real;
+
+ if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
+ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
+ return -1;
+ if (n != 0)
+ result = *p;
+ XFree(p);
+ return result;
+}
+
+unsigned int
+getsystraywidth()
+{
+ unsigned int w = 0;
+ Client *i;
+ if(showsystray)
+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
+ return w ? w + systrayspacing : 1;
+}
+
+int
+gettextprop(Window w, Atom atom, char *text, unsigned int size)
+{
+ char **list = NULL;
+ int n;
+ XTextProperty name;
+
+ if (!text || size == 0)
+ return 0;
+ text[0] = '\0';
+ if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
+ return 0;
+ if (name.encoding == XA_STRING)
+ strncpy(text, (char *)name.value, size - 1);
+ else {
+ if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
+ strncpy(text, *list, size - 1);
+ XFreeStringList(list);
+ }
+ }
+ text[size - 1] = '\0';
+ XFree(name.value);
+ return 1;
+}
+
+void
+grabbuttons(Client *c, int focused)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ if (!focused)
+ XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
+ BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].click == ClkClientWin)
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabButton(dpy, buttons[i].button,
+ buttons[i].mask | modifiers[j],
+ c->win, False, BUTTONMASK,
+ GrabModeAsync, GrabModeSync, None, None);
+ }
+}
+
+void
+grabkeys(void)
+{
+ updatenumlockmask();
+ {
+ unsigned int i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ KeyCode code;
+
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ for (i = 0; i < LENGTH(keys); i++)
+ if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
+ True, GrabModeAsync, GrabModeAsync);
+ }
+}
+
+void
+incnmaster(const Arg *arg)
+{
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
+#ifdef XINERAMA
+static int
+isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
+{
+ while (n--)
+ if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
+ && unique[n].width == info->width && unique[n].height == info->height)
+ return 0;
+ return 1;
+}
+#endif /* XINERAMA */
+
+void
+keypress(XEvent *e)
+{
+ unsigned int i;
+ KeySym keysym;
+ XKeyEvent *ev;
+
+ ev = &e->xkey;
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
+ for (i = 0; i < LENGTH(keys); i++)
+ if (keysym == keys[i].keysym
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+ && keys[i].func)
+ keys[i].func(&(keys[i].arg));
+}
+
+void
+killclient(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
+ XGrabServer(dpy);
+ XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+ XKillClient(dpy, selmon->sel->win);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+}
+
+void
+manage(Window w, XWindowAttributes *wa)
+{
+ Client *c, *t = NULL;
+ Window trans = None;
+ XWindowChanges wc;
+
+ c = ecalloc(1, sizeof(Client));
+ c->win = w;
+ /* geometry */
+ c->x = c->oldx = wa->x;
+ c->y = c->oldy = wa->y;
+ c->w = c->oldw = wa->width;
+ c->h = c->oldh = wa->height;
+ c->oldbw = wa->border_width;
+
+ updatetitle(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+ c->mon = t->mon;
+ c->tags = t->tags;
+ } else {
+ c->mon = selmon;
+ applyrules(c);
+ }
+
+ if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
+ c->x = c->mon->mx + c->mon->mw - WIDTH(c);
+ if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
+ c->y = c->mon->my + c->mon->mh - HEIGHT(c);
+ c->x = MAX(c->x, c->mon->mx);
+ /* only fix client y-offset, if the client center might cover the bar */
+ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
+ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
+ c->bw = borderpx;
+
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, w, CWBorderWidth, &wc);
+ XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
+ configure(c); /* propagates border_width, if size doesn't change */
+ updatewindowtype(c);
+ updatesizehints(c);
+ updatewmhints(c);
+ XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
+ grabbuttons(c, 0);
+ if (!c->isfloating)
+ c->isfloating = c->oldstate = trans != None || c->isfixed;
+ if (c->isfloating)
+ XRaiseWindow(dpy, c->win);
+ attach(c);
+ attachstack(c);
+ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+ XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
+ setclientstate(c, NormalState);
+ if (c->mon == selmon)
+ unfocus(selmon->sel, 0);
+ c->mon->sel = c;
+ arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ focus(NULL);
+}
+
+void
+mappingnotify(XEvent *e)
+{
+ XMappingEvent *ev = &e->xmapping;
+
+ XRefreshKeyboardMapping(ev);
+ if (ev->request == MappingKeyboard)
+ grabkeys();
+}
+
+void
+maprequest(XEvent *e)
+{
+ static XWindowAttributes wa;
+ XMapRequestEvent *ev = &e->xmaprequest;
+ Client *i;
+ if ((i = wintosystrayicon(ev->window))) {
+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+
+ if (!XGetWindowAttributes(dpy, ev->window, &wa))
+ return;
+ if (wa.override_redirect)
+ return;
+ if (!wintoclient(ev->window))
+ manage(ev->window, &wa);
+}
+
+void
+monocle(Monitor *m)
+{
+ unsigned int n = 0;
+ Client *c;
+
+ for (c = m->clients; c; c = c->next)
+ if (ISVISIBLE(c))
+ n++;
+ if (n > 0) /* override layout symbol */
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+ for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
+ resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
+}
+
+void
+motionnotify(XEvent *e)
+{
+ static Monitor *mon = NULL;
+ Monitor *m;
+ XMotionEvent *ev = &e->xmotion;
+
+ if (ev->window != root)
+ return;
+ if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
+ unfocus(selmon->sel, 1);
+ selmon = m;
+ focus(NULL);
+ }
+ mon = m;
+}
+
+void
+movemouse(const Arg *arg)
+{
+ int x, y, ocx, ocy, nx, ny;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ if (!getrootptr(&x, &y))
+ return;
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nx = ocx + (ev.xmotion.x - x);
+ ny = ocy + (ev.xmotion.y - y);
+ if (abs(selmon->wx - nx) < snap)
+ nx = selmon->wx;
+ else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
+ nx = selmon->wx + selmon->ww - WIDTH(c);
+ if (abs(selmon->wy - ny) < snap)
+ ny = selmon->wy;
+ else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
+ ny = selmon->wy + selmon->wh - HEIGHT(c);
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
+ togglefloating(NULL);
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, nx, ny, c->w, c->h, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XUngrabPointer(dpy, CurrentTime);
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+Client *
+nexttiled(Client *c)
+{
+ for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
+ return c;
+}
+
+void
+pop(Client *c)
+{
+ detach(c);
+ attach(c);
+ focus(c);
+ arrange(c->mon);
+}
+
+void
+propertynotify(XEvent *e)
+{
+ Client *c;
+ Window trans;
+ XPropertyEvent *ev = &e->xproperty;
+
+ if ((c = wintosystrayicon(ev->window))) {
+ if (ev->atom == XA_WM_NORMAL_HINTS) {
+ updatesizehints(c);
+ updatesystrayicongeom(c, c->w, c->h);
+ }
+ else
+ updatesystrayiconstate(c, ev);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+ if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ updatestatus();
+ else if (ev->state == PropertyDelete)
+ return; /* ignore */
+ else if ((c = wintoclient(ev->window))) {
+ switch(ev->atom) {
+ default: break;
+ case XA_WM_TRANSIENT_FOR:
+ if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
+ (c->isfloating = (wintoclient(trans)) != NULL))
+ arrange(c->mon);
+ break;
+ case XA_WM_NORMAL_HINTS:
+ updatesizehints(c);
+ break;
+ case XA_WM_HINTS:
+ updatewmhints(c);
+ drawbars();
+ break;
+ }
+ if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
+ updatetitle(c);
+ if (c == c->mon->sel)
+ drawbar(c->mon);
+ }
+ if (ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+ }
+}
+
+void
+quit(const Arg *arg)
+{
+ running = 0;
+}
+
+Monitor *
+recttomon(int x, int y, int w, int h)
+{
+ Monitor *m, *r = selmon;
+ int a, area = 0;
+
+ for (m = mons; m; m = m->next)
+ if ((a = INTERSECT(x, y, w, h, m)) > area) {
+ area = a;
+ r = m;
+ }
+ return r;
+}
+
+void
+removesystrayicon(Client *i)
+{
+ Client **ii;
+
+ if (!showsystray || !i)
+ return;
+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
+ if (ii)
+ *ii = i->next;
+ free(i);
+}
+
+
+void
+resize(Client *c, int x, int y, int w, int h, int interact)
+{
+ if (applysizehints(c, &x, &y, &w, &h, interact))
+ resizeclient(c, x, y, w, h);
+}
+
+void
+resizebarwin(Monitor *m) {
+ unsigned int w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
+}
+
+void
+resizeclient(Client *c, int x, int y, int w, int h)
+{
+ XWindowChanges wc;
+
+ c->oldx = c->x; c->x = wc.x = x;
+ c->oldy = c->y; c->y = wc.y = y;
+ c->oldw = c->w; c->w = wc.width = w;
+ c->oldh = c->h; c->h = wc.height = h;
+ wc.border_width = c->bw;
+ XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
+ configure(c);
+ XSync(dpy, False);
+}
+
+void
+resizemouse(const Arg *arg)
+{
+ int ocx, ocy, nw, nh;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+ return;
+ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
+ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
+ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
+ return;
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+ continue;
+ lasttime = ev.xmotion.time;
+
+ nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
+ nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
+ if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
+ && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
+ {
+ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
+ && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
+ togglefloating(NULL);
+ }
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+ resize(c, c->x, c->y, nw, nh, 1);
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
+ XUngrabPointer(dpy, CurrentTime);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
+ sendmon(c, m);
+ selmon = m;
+ focus(NULL);
+ }
+}
+
+void
+resizerequest(XEvent *e)
+{
+ XResizeRequestEvent *ev = &e->xresizerequest;
+ Client *i;
+
+ if ((i = wintosystrayicon(ev->window))) {
+ updatesystrayicongeom(i, ev->width, ev->height);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
+restack(Monitor *m)
+{
+ Client *c;
+ XEvent ev;
+ XWindowChanges wc;
+
+ drawbar(m);
+ if (!m->sel)
+ return;
+ if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
+ XRaiseWindow(dpy, m->sel->win);
+ if (m->lt[m->sellt]->arrange) {
+ wc.stack_mode = Below;
+ wc.sibling = m->barwin;
+ for (c = m->stack; c; c = c->snext)
+ if (!c->isfloating && ISVISIBLE(c)) {
+ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
+ wc.sibling = c->win;
+ }
+ }
+ XSync(dpy, False);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+}
+
+void
+run(void)
+{
+ XEvent ev;
+ /* main event loop */
+ XSync(dpy, False);
+ while (running && !XNextEvent(dpy, &ev))
+ if (handler[ev.type])
+ handler[ev.type](&ev); /* call handler */
+}
+
+void
+runAutostart(void) {
+ system("cd ~/.config/dwm; ./autostart_blocking.sh");
+ system("cd ~/.config/dwm; ./autostart.sh &");
+}
+
+void
+scan(void)
+{
+ unsigned int i, num;
+ Window d1, d2, *wins = NULL;
+ XWindowAttributes wa;
+
+ if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
+ for (i = 0; i < num; i++) {
+ if (!XGetWindowAttributes(dpy, wins[i], &wa)
+ || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
+ continue;
+ if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
+ manage(wins[i], &wa);
+ }
+ for (i = 0; i < num; i++) { /* now the transients */
+ if (!XGetWindowAttributes(dpy, wins[i], &wa))
+ continue;
+ if (XGetTransientForHint(dpy, wins[i], &d1)
+ && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
+ manage(wins[i], &wa);
+ }
+ if (wins)
+ XFree(wins);
+ }
+}
+
+void
+sendmon(Client *c, Monitor *m)
+{
+ if (c->mon == m)
+ return;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->mon = m;
+ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ focus(NULL);
+ arrange(NULL);
+}
+
+void
+setclientstate(Client *c, long state)
+{
+ long data[] = { state, None };
+
+ XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
+ PropModeReplace, (unsigned char *)data, 2);
+}
+
+int
+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
+{
+ int n;
+ Atom *protocols, mt;
+ int exists = 0;
+ XEvent ev;
+
+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
+ mt = wmatom[WMProtocols];
+ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
+ while (!exists && n--)
+ exists = protocols[n] == proto;
+ XFree(protocols);
+ }
+ }
+ else {
+ exists = True;
+ mt = proto;
+ }
+ if (exists) {
+ ev.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = mt;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = d0;
+ ev.xclient.data.l[1] = d1;
+ ev.xclient.data.l[2] = d2;
+ ev.xclient.data.l[3] = d3;
+ ev.xclient.data.l[4] = d4;
+ XSendEvent(dpy, w, False, mask, &ev);
+ }
+ return exists;
+}
+
+void
+setfocus(Client *c)
+{
+ if (!c->neverfocus) {
+ XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
+ XChangeProperty(dpy, root, netatom[NetActiveWindow],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
+ }
+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+ if (fullscreen && !c->isfullscreen) {
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
+ c->isfullscreen = 1;
+ c->oldstate = c->isfloating;
+ c->oldbw = c->bw;
+ c->bw = 0;
+ c->isfloating = 1;
+ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
+ XRaiseWindow(dpy, c->win);
+ } else if (!fullscreen && c->isfullscreen){
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)0, 0);
+ c->isfullscreen = 0;
+ c->isfloating = c->oldstate;
+ c->bw = c->oldbw;
+ c->x = c->oldx;
+ c->y = c->oldy;
+ c->w = c->oldw;
+ c->h = c->oldh;
+ resizeclient(c, c->x, c->y, c->w, c->h);
+ arrange(c->mon);
+ }
+}
+
+void
+setgaps(const Arg *arg)
+{
+ if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
+ selmon->gappx = 0;
+ else
+ selmon->gappx += arg->i;
+ arrange(selmon);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if (selmon->sel)
+ arrange(selmon);
+ else
+ drawbar(selmon);
+}
+
+/* arg > 1.0 will set mfact absolutely */
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !selmon->lt[selmon->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ selmon->mfact = f;
+ arrange(selmon);
+}
+
+void
+setup(void)
+{
+ int i;
+ XSetWindowAttributes wa;
+ Atom utf8string;
+
+ /* clean up any zombies immediately */
+ sigchld(0);
+
+ /* init screen */
+ screen = DefaultScreen(dpy);
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
+ root = RootWindow(dpy, screen);
+ drw = drw_create(dpy, screen, root, sw, sh);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+ bh = drw->fonts->h + 2;
+ updategeom();
+ /* init atoms */
+ utf8string = XInternAtom(dpy, "UTF8_STRING", False);
+ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
+ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
+ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
+ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+ netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
+ /* init cursors */
+ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+ cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+ cursor[CurMove] = drw_cur_create(drw, XC_fleur);
+ /* init appearance */
+ scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
+ for (i = 0; i < LENGTH(colors); i++)
+ scheme[i] = drw_scm_create(drw, colors[i], 3);
+ /* init system tray */
+ updatesystray();
+ /* init bars */
+ updatebars();
+ updatestatus();
+ /* supporting window for NetWMCheck */
+ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
+ PropModeReplace, (unsigned char *) "dwm", 3);
+ XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *) &wmcheckwin, 1);
+ /* EWMH support per view */
+ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) netatom, NetLast);
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ /* select events */
+ wa.cursor = cursor[CurNormal]->cursor;
+ wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
+ |ButtonPressMask|PointerMotionMask|EnterWindowMask
+ |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
+ XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
+ XSelectInput(dpy, root, wa.event_mask);
+ grabkeys();
+ focus(NULL);
+}
+
+
+void
+seturgent(Client *c, int urg)
+{
+ XWMHints *wmh;
+
+ c->isurgent = urg;
+ if (!(wmh = XGetWMHints(dpy, c->win)))
+ return;
+ wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
+ XSetWMHints(dpy, c->win, wmh);
+ XFree(wmh);
+}
+
+void
+showhide(Client *c)
+{
+ if (!c)
+ return;
+ if (ISVISIBLE(c)) {
+ /* show clients top down */
+ XMoveWindow(dpy, c->win, c->x, c->y);
+ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+ resize(c, c->x, c->y, c->w, c->h, 0);
+ showhide(c->snext);
+ } else {
+ /* hide clients bottom up */
+ showhide(c->snext);
+ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
+ }
+}
+
+void
+sigchld(int unused)
+{
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("can't install SIGCHLD handler:");
+ while (0 < waitpid(-1, NULL, WNOHANG));
+}
+
+void
+spawn(const Arg *arg)
+{
+ if (arg->v == dmenucmd)
+ dmenumon[0] = '0' + selmon->num;
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ setsid();
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
+ perror(" failed");
+ exit(EXIT_SUCCESS);
+ }
+}
+
+void
+tag(const Arg *arg)
+{
+ if (selmon->sel && arg->ui & TAGMASK) {
+ selmon->sel->tags = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+tagmon(const Arg *arg)
+{
+ if (!selmon->sel || !mons->next)
+ return;
+ sendmon(selmon->sel, dirtomon(arg->i));
+}
+
+void
+tile(Monitor *m)
+{
+ unsigned int i, n, h, mw, my, ty;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (n == 0)
+ return;
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->ww * m->mfact : 0;
+ else
+ mw = m->ww - m->gappx;
+ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
+ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
+ my += HEIGHT(c) + m->gappx;
+ } else {
+ h = (m->wh - ty) / (n - i) - m->gappx;
+ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
+ ty += HEIGHT(c) + m->gappx;
+ }
+}
+
+void
+togglebar(const Arg *arg)
+{
+ selmon->showbar = !selmon->showbar;
+ updatebarpos(selmon);
+ resizebarwin(selmon);
+ if (showsystray) {
+ XWindowChanges wc;
+ if (!selmon->showbar)
+ wc.y = -bh;
+ else if (selmon->showbar) {
+ wc.y = 0;
+ if (!selmon->topbar)
+ wc.y = selmon->mh - bh;
+ }
+ XConfigureWindow(dpy, systray->win, CWY, &wc);
+ }
+ arrange(selmon);
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ if (!selmon->sel)
+ return;
+ if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
+ return;
+ selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
+ if (selmon->sel->isfloating)
+ resize(selmon->sel, selmon->sel->x, selmon->sel->y,
+ selmon->sel->w, selmon->sel->h, 0);
+ arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ unsigned int newtags;
+
+ if (!selmon->sel)
+ return;
+ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
+ selmon->sel->tags = newtags;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+toggleview(const Arg *arg)
+{
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
+
+ if (newtagset) {
+ selmon->tagset[selmon->seltags] = newtagset;
+ focus(NULL);
+ arrange(selmon);
+ }
+}
+
+void
+unfocus(Client *c, int setfocus)
+{
+ if (!c)
+ return;
+ grabbuttons(c, 0);
+ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
+ if (setfocus) {
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ }
+}
+
+void
+unmanage(Client *c, int destroyed)
+{
+ Monitor *m = c->mon;
+ XWindowChanges wc;
+
+ detach(c);
+ detachstack(c);
+ if (!destroyed) {
+ wc.border_width = c->oldbw;
+ XGrabServer(dpy); /* avoid race conditions */
+ XSetErrorHandler(xerrordummy);
+ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
+ setclientstate(c, WithdrawnState);
+ XSync(dpy, False);
+ XSetErrorHandler(xerror);
+ XUngrabServer(dpy);
+ }
+ free(c);
+ focus(NULL);
+ updateclientlist();
+ arrange(m);
+}
+
+void
+unmapnotify(XEvent *e)
+{
+ Client *c;
+ XUnmapEvent *ev = &e->xunmap;
+
+ if ((c = wintoclient(ev->window))) {
+ if (ev->send_event)
+ setclientstate(c, WithdrawnState);
+ else
+ unmanage(c, 0);
+ }
+ else if ((c = wintosystrayicon(ev->window))) {
+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
+ * _not_ destroy them. We map those windows back */
+ XMapRaised(dpy, c->win);
+ updatesystray();
+ }
+}
+
+void
+updatebars(void)
+{
+ unsigned int w;
+ Monitor *m;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .background_pixmap = ParentRelative,
+ .event_mask = ButtonPressMask|ExposureMask
+ };
+ XClassHint ch = {"dwm", "dwm"};
+ for (m = mons; m; m = m->next) {
+ if (m->barwin)
+ continue;
+ w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ if (showsystray && m == systraytomon(m))
+ XMapRaised(dpy, systray->win);
+ XMapRaised(dpy, m->barwin);
+ XSetClassHint(dpy, m->barwin, &ch);
+ }
+}
+
+void
+updatebarpos(Monitor *m)
+{
+ m->wy = m->my;
+ m->wh = m->mh;
+ if (m->showbar) {
+ m->wh -= bh;
+ m->by = m->topbar ? m->wy : m->wy + m->wh;
+ m->wy = m->topbar ? m->wy + bh : m->wy;
+ } else
+ m->by = -bh;
+}
+
+void
+updateclientlist()
+{
+ Client *c;
+ Monitor *m;
+
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ XChangeProperty(dpy, root, netatom[NetClientList],
+ XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+}
+
+int
+updategeom(void)
+{
+ int dirty = 0;
+
+#ifdef XINERAMA
+ if (XineramaIsActive(dpy)) {
+ int i, j, n, nn;
+ Client *c;
+ Monitor *m;
+ XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
+ XineramaScreenInfo *unique = NULL;
+
+ for (n = 0, m = mons; m; m = m->next, n++);
+ /* only consider unique geometries as separate screens */
+ unique = ecalloc(nn, sizeof(XineramaScreenInfo));
+ for (i = 0, j = 0; i < nn; i++)
+ if (isuniquegeom(unique, j, &info[i]))
+ memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
+ XFree(info);
+ nn = j;
+ if (n <= nn) { /* new monitors available */
+ for (i = 0; i < (nn - n); i++) {
+ for (m = mons; m && m->next; m = m->next);
+ if (m)
+ m->next = createmon();
+ else
+ mons = createmon();
+ }
+ for (i = 0, m = mons; i < nn && m; m = m->next, i++)
+ if (i >= n
+ || unique[i].x_org != m->mx || unique[i].y_org != m->my
+ || unique[i].width != m->mw || unique[i].height != m->mh)
+ {
+ dirty = 1;
+ m->num = i;
+ m->mx = m->wx = unique[i].x_org;
+ m->my = m->wy = unique[i].y_org;
+ m->mw = m->ww = unique[i].width;
+ m->mh = m->wh = unique[i].height;
+ updatebarpos(m);
+ }
+ } else { /* less monitors available nn < n */
+ for (i = nn; i < n; i++) {
+ for (m = mons; m && m->next; m = m->next);
+ while ((c = m->clients)) {
+ dirty = 1;
+ m->clients = c->next;
+ detachstack(c);
+ c->mon = mons;
+ attach(c);
+ attachstack(c);
+ }
+ if (m == selmon)
+ selmon = mons;
+ cleanupmon(m);
+ }
+ }
+ free(unique);
+ } else
+#endif /* XINERAMA */
+ { /* default monitor setup */
+ if (!mons)
+ mons = createmon();
+ if (mons->mw != sw || mons->mh != sh) {
+ dirty = 1;
+ mons->mw = mons->ww = sw;
+ mons->mh = mons->wh = sh;
+ updatebarpos(mons);
+ }
+ }
+ if (dirty) {
+ selmon = mons;
+ selmon = wintomon(root);
+ }
+ return dirty;
+}
+
+void
+updatenumlockmask(void)
+{
+ unsigned int i, j;
+ XModifierKeymap *modmap;
+
+ numlockmask = 0;
+ modmap = XGetModifierMapping(dpy);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < modmap->max_keypermod; j++)
+ if (modmap->modifiermap[i * modmap->max_keypermod + j]
+ == XKeysymToKeycode(dpy, XK_Num_Lock))
+ numlockmask = (1 << i);
+ XFreeModifiermap(modmap);
+}
+
+void
+updatesizehints(Client *c)
+{
+ long msize;
+ XSizeHints size;
+
+ if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
+ /* size is uninitialized, ensure that size.flags aren't used */
+ size.flags = PSize;
+ if (size.flags & PBaseSize) {
+ c->basew = size.base_width;
+ c->baseh = size.base_height;
+ } else if (size.flags & PMinSize) {
+ c->basew = size.min_width;
+ c->baseh = size.min_height;
+ } else
+ c->basew = c->baseh = 0;
+ if (size.flags & PResizeInc) {
+ c->incw = size.width_inc;
+ c->inch = size.height_inc;
+ } else
+ c->incw = c->inch = 0;
+ if (size.flags & PMaxSize) {
+ c->maxw = size.max_width;
+ c->maxh = size.max_height;
+ } else
+ c->maxw = c->maxh = 0;
+ if (size.flags & PMinSize) {
+ c->minw = size.min_width;
+ c->minh = size.min_height;
+ } else if (size.flags & PBaseSize) {
+ c->minw = size.base_width;
+ c->minh = size.base_height;
+ } else
+ c->minw = c->minh = 0;
+ if (size.flags & PAspect) {
+ c->mina = (float)size.min_aspect.y / size.min_aspect.x;
+ c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
+ } else
+ c->maxa = c->mina = 0.0;
+ c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
+}
+
+void
+updatestatus(void)
+{
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ strcpy(stext, "dwm-"VERSION);
+ drawbar(selmon);
+ updatesystray();
+}
+
+void
+updatesystrayicongeom(Client *i, int w, int h)
+{
+ if (i) {
+ i->h = bh;
+ if (w == h)
+ i->w = bh;
+ else if (h == bh)
+ i->w = w;
+ else
+ i->w = (int) ((float)bh * ((float)w / (float)h));
+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
+ /* force icons into the systray dimensions if they don't want to */
+ if (i->h > bh) {
+ if (i->w == i->h)
+ i->w = bh;
+ else
+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
+ i->h = bh;
+ }
+ }
+}
+
+void
+updatesystrayiconstate(Client *i, XPropertyEvent *ev)
+{
+ long flags;
+ int code = 0;
+
+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
+ !(flags = getatomprop(i, xatom[XembedInfo])))
+ return;
+
+ if (flags & XEMBED_MAPPED && !i->tags) {
+ i->tags = 1;
+ code = XEMBED_WINDOW_ACTIVATE;
+ XMapRaised(dpy, i->win);
+ setclientstate(i, NormalState);
+ }
+ else if (!(flags & XEMBED_MAPPED) && i->tags) {
+ i->tags = 0;
+ code = XEMBED_WINDOW_DEACTIVATE;
+ XUnmapWindow(dpy, i->win);
+ setclientstate(i, WithdrawnState);
+ }
+ else
+ return;
+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
+ systray->win, XEMBED_EMBEDDED_VERSION);
+}
+
+void
+updatesystray(void)
+{
+ XSetWindowAttributes wa;
+ XWindowChanges wc;
+ Client *i;
+ Monitor *m = systraytomon(NULL);
+ unsigned int x = m->mx + m->mw;
+ unsigned int w = 1;
+
+ if (!showsystray)
+ return;
+ if (!systray) {
+ /* init systray */
+ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
+ wa.event_mask = ButtonPressMask | ExposureMask;
+ wa.override_redirect = True;
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
+ XMapRaised(dpy, systray->win);
+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
+ XSync(dpy, False);
+ }
+ else {
+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
+ free(systray);
+ systray = NULL;
+ return;
+ }
+ }
+ for (w = 0, i = systray->icons; i; i = i->next) {
+ /* make sure the background color stays the same */
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
+ XMapRaised(dpy, i->win);
+ w += systrayspacing;
+ i->x = w;
+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
+ w += i->w;
+ if (i->mon != m)
+ i->mon = m;
+ }
+ w = w ? w + systrayspacing : 1;
+ x -= w;
+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
+ wc.stack_mode = Above; wc.sibling = m->barwin;
+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
+ XMapWindow(dpy, systray->win);
+ XMapSubwindows(dpy, systray->win);
+ /* redraw background */
+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
+ XSync(dpy, False);
+}
+
+void
+updatetitle(Client *c)
+{
+ if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
+ gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
+ if (c->name[0] == '\0') /* hack to mark broken clients */
+ strcpy(c->name, broken);
+}
+
+void
+updatewindowtype(Client *c)
+{
+ Atom state = getatomprop(c, netatom[NetWMState]);
+ Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
+
+ if (state == netatom[NetWMFullscreen])
+ setfullscreen(c, 1);
+ if (wtype == netatom[NetWMWindowTypeDialog])
+ c->isfloating = 1;
+}
+
+void
+updatewmhints(Client *c)
+{
+ XWMHints *wmh;
+
+ if ((wmh = XGetWMHints(dpy, c->win))) {
+ if (c == selmon->sel && wmh->flags & XUrgencyHint) {
+ wmh->flags &= ~XUrgencyHint;
+ XSetWMHints(dpy, c->win, wmh);
+ } else
+ c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
+ if (wmh->flags & InputHint)
+ c->neverfocus = !wmh->input;
+ else
+ c->neverfocus = 0;
+ XFree(wmh);
+ }
+}
+
+void
+view(const Arg *arg)
+{
+ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+}
+
+Client *
+wintoclient(Window w)
+{
+ Client *c;
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+ for (c = m->clients; c; c = c->next)
+ if (c->win == w)
+ return c;
+ return NULL;
+}
+
+Client *
+wintosystrayicon(Window w) {
+ Client *i = NULL;
+
+ if (!showsystray || !w)
+ return i;
+ for (i = systray->icons; i && i->win != w; i = i->next) ;
+ return i;
+}
+
+Monitor *
+wintomon(Window w)
+{
+ int x, y;
+ Client *c;
+ Monitor *m;
+
+ if (w == root && getrootptr(&x, &y))
+ return recttomon(x, y, 1, 1);
+ for (m = mons; m; m = m->next)
+ if (w == m->barwin)
+ return m;
+ if ((c = wintoclient(w)))
+ return c->mon;
+ return selmon;
+}
+
+/* There's no way to check accesses to destroyed windows, thus those cases are
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
+ * default error handler, which may call exit. */
+int
+xerror(Display *dpy, XErrorEvent *ee)
+{
+ if (ee->error_code == BadWindow
+ || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
+ || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
+ || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
+ || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
+ || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
+ || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
+ || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
+ return 0;
+ fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
+ ee->request_code, ee->error_code);
+ return xerrorxlib(dpy, ee); /* may call exit */
+}
+
+int
+xerrordummy(Display *dpy, XErrorEvent *ee)
+{
+ return 0;
+}
+
+/* Startup Error handler to check if another window manager
+ * is already running. */
+int
+xerrorstart(Display *dpy, XErrorEvent *ee)
+{
+ die("dwm: another window manager is already running");
+ return -1;
+}
+
+Monitor *
+systraytomon(Monitor *m) {
+ Monitor *t;
+ int i, n;
+ if(!systraypinning) {
+ if(!m)
+ return selmon;
+ return m == selmon ? m : NULL;
+ }
+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
+ if(systraypinningfailfirst && n < systraypinning)
+ return mons;
+ return t;
+}
+
+void
+zoom(const Arg *arg)
+{
+ Client *c = selmon->sel;
+
+ if (!selmon->lt[selmon->sellt]->arrange
+ || (selmon->sel && selmon->sel->isfloating))
+ return;
+ if (c == nexttiled(selmon->clients))
+ if (!c || !(c = nexttiled(c->next)))
+ return;
+ pop(c);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc == 2 && !strcmp("-v", argv[1]))
+ die("dwm-"VERSION);
+ else if (argc != 1)
+ die("usage: dwm [-v]");
+ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fputs("warning: no locale support\n", stderr);
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("dwm: cannot open display");
+ checkotherwm();
+ setup();
+#ifdef __OpenBSD__
+ if (pledge("stdio rpath proc exec", NULL) == -1)
+ die("pledge");
+#endif /* __OpenBSD__ */
+ scan();
+ runAutostart();
+ run();
+ cleanup();
+ XCloseDisplay(dpy);
+ return EXIT_SUCCESS;
+}
diff --git a/keep/manual-programs/suckless/dwm/dwm.o b/keep/manual-programs/suckless/dwm/dwm.o
new file mode 100644
index 0000000..fd58ec3
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm.o
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm/dwm.png b/keep/manual-programs/suckless/dwm/dwm.png
new file mode 100644
index 0000000..b1f9ba7
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwm.png
Binary files differ
diff --git a/keep/manual-programs/suckless/dwm/dwmstatus/LICENSE b/keep/manual-programs/suckless/dwm/dwmstatus/LICENSE
new file mode 100644
index 0000000..855608d
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwmstatus/LICENSE
@@ -0,0 +1,21 @@
+MIT/X Consortium License
+
+© 2011-2018 Christoph Lohmann <20h@r-36.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/keep/manual-programs/suckless/dwm/dwmstatus/Makefile b/keep/manual-programs/suckless/dwm/dwmstatus/Makefile
new file mode 100644
index 0000000..62240a5
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwmstatus/Makefile
@@ -0,0 +1,49 @@
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+SRC = ${NAME}.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${NAME}
+
+options:
+ @echo ${NAME} build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+.c.o:
+ @echo CC $<
+ @${CC} -c ${CFLAGS} $<
+
+${OBJ}: config.mk
+
+${NAME}: ${OBJ}
+ @echo CC -o $@
+ @${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+clean:
+ @echo cleaning
+ @rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz
+
+dist: clean
+ @echo creating dist tarball
+ @mkdir -p ${NAME}-${VERSION}
+ @cp -R Makefile LICENSE config.mk \
+ ${SRC} ${NAME}-${VERSION}
+ @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION}
+ @gzip ${NAME}-${VERSION}.tar
+ @rm -rf ${NAME}-${VERSION}
+
+install: all
+ @echo installing executable file to ${DESTDIR}${PREFIX}/bin
+ @mkdir -p ${DESTDIR}${PREFIX}/bin
+ @cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
+ @chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
+
+uninstall:
+ @echo removing executable file from ${DESTDIR}${PREFIX}/bin
+ @rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
+
+.PHONY: all options clean dist install uninstall
diff --git a/keep/manual-programs/suckless/dwm/dwmstatus/config.mk b/keep/manual-programs/suckless/dwm/dwmstatus/config.mk
new file mode 100644
index 0000000..0ec9e47
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwmstatus/config.mk
@@ -0,0 +1,30 @@
+NAME = dwmstatus
+VERSION = 1.0
+
+# Customize below to fit your system
+
+# paths
+PREFIX = /usr
+MANPREFIX = ${PREFIX}/share/man
+
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+# includes and libs
+INCS = -I. -I/usr/include -I${X11INC}
+LIBS = -L/usr/lib -lc -L${X11LIB} -lX11
+
+# flags
+CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE
+CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
+#CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = -g ${LIBS}
+#LDFLAGS = -s ${LIBS}
+
+# Solaris
+#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
+#LDFLAGS = ${LIBS}
+
+# compiler and linker
+CC = cc
+
diff --git a/keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus-temperature.c b/keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus-temperature.c
new file mode 100644
index 0000000..13f27f6
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus-temperature.c
@@ -0,0 +1,15 @@
+/*
+ * gettemperature("/sys/class/hwmon/hwmon0/device", "temp1_input");
+ */
+
+char *
+gettemperature(char *base, char *sensor)
+{
+ char *co;
+
+ co = readfile(base, sensor);
+ if (co == NULL)
+ return smprintf("");
+ return smprintf("%02.0f°C", atof(co) / 1000);
+}
+
diff --git a/keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus.c b/keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus.c
new file mode 100644
index 0000000..d2a4b03
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwmstatus/dwmstatus.c
@@ -0,0 +1,227 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#define _BSD_SOURCE
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <X11/Xlib.h>
+
+char *tzargentina = "America/Buenos_Aires";
+char *tzutc = "UTC";
+char *tzberlin = "Europe/Berlin";
+
+static Display *dpy;
+
+char *
+smprintf(char *fmt, ...)
+{
+ va_list fmtargs;
+ char *ret;
+ int len;
+
+ va_start(fmtargs, fmt);
+ len = vsnprintf(NULL, 0, fmt, fmtargs);
+ va_end(fmtargs);
+
+ ret = malloc(++len);
+ if (ret == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+
+ va_start(fmtargs, fmt);
+ vsnprintf(ret, len, fmt, fmtargs);
+ va_end(fmtargs);
+
+ return ret;
+}
+
+void
+settz(char *tzname)
+{
+ setenv("TZ", tzname, 1);
+}
+
+char *
+mktimes(char *fmt, char *tzname)
+{
+ char buf[129];
+ time_t tim;
+ struct tm *timtm;
+
+ settz(tzname);
+ tim = time(NULL);
+ timtm = localtime(&tim);
+ if (timtm == NULL)
+ return smprintf("");
+
+ if (!strftime(buf, sizeof(buf)-1, fmt, timtm)) {
+ fprintf(stderr, "strftime == 0\n");
+ return smprintf("");
+ }
+
+ return smprintf("%s", buf);
+}
+
+void
+setstatus(char *str)
+{
+ XStoreName(dpy, DefaultRootWindow(dpy), str);
+ XSync(dpy, False);
+}
+
+char *
+loadavg(void)
+{
+ double avgs[3];
+
+ if (getloadavg(avgs, 3) < 0)
+ return smprintf("");
+
+ return smprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
+}
+
+char *
+readfile(char *base, char *file)
+{
+ char *path, line[513];
+ FILE *fd;
+
+ memset(line, 0, sizeof(line));
+
+ path = smprintf("%s/%s", base, file);
+ fd = fopen(path, "r");
+ free(path);
+ if (fd == NULL)
+ return NULL;
+
+ if (fgets(line, sizeof(line)-1, fd) == NULL)
+ return NULL;
+ fclose(fd);
+
+ return smprintf("%s", line);
+}
+
+char *
+getbattery(char *base)
+{
+ char *co, status;
+ int descap, remcap;
+
+ descap = -1;
+ remcap = -1;
+
+ co = readfile(base, "present");
+ if (co == NULL)
+ return smprintf("");
+ if (co[0] != '1') {
+ free(co);
+ return smprintf("not present");
+ }
+ free(co);
+
+ co = readfile(base, "charge_full_design");
+ if (co == NULL) {
+ co = readfile(base, "energy_full_design");
+ if (co == NULL)
+ return smprintf("");
+ }
+ sscanf(co, "%d", &descap);
+ free(co);
+
+ co = readfile(base, "charge_now");
+ if (co == NULL) {
+ co = readfile(base, "energy_now");
+ if (co == NULL)
+ return smprintf("");
+ }
+ sscanf(co, "%d", &remcap);
+ free(co);
+
+ co = readfile(base, "status");
+ if (!strncmp(co, "Discharging", 11)) {
+ status = '-';
+ } else if(!strncmp(co, "Charging", 8)) {
+ status = '+';
+ } else {
+ status = '?';
+ }
+
+ if (remcap < 0 || descap < 0)
+ return smprintf("invalid");
+
+ return smprintf("%.0f%%%c", ((float)remcap / (float)descap) * 100, status);
+}
+
+char *
+gettemperature(char *base, char *sensor)
+{
+ char *co;
+
+ co = readfile(base, sensor);
+ if (co == NULL)
+ return smprintf("");
+ return smprintf("%02.0f°C", atof(co) / 1000);
+}
+
+int
+main(void)
+{
+ char *status;
+ char *avgs;
+ char *bat;
+ char *bat1;
+ char *tmar;
+ char *tmutc;
+ char *tmbln;
+ char *t0, *t1, *t2;
+
+ if (!(dpy = XOpenDisplay(NULL))) {
+ fprintf(stderr, "dwmstatus: cannot open display.\n");
+ return 1;
+ }
+
+ for (;;sleep(60)) {
+ avgs = loadavg();
+ bat = getbattery("/sys/class/power_supply/BAT0");
+ bat1 = getbattery("/sys/class/power_supply/BAT1");
+ tmar = mktimes("%H:%M", tzargentina);
+ tmutc = mktimes("%H:%M", tzutc);
+ tmbln = mktimes("KW %W %a %d %b %H:%M %Z %Y", tzberlin);
+ t0 = gettemperature("/sys/devices/virtual/hwmon/hwmon0", "temp1_input");
+ t1 = gettemperature("/sys/devices/virtual/hwmon/hwmon2", "temp1_input");
+ t2 = gettemperature("/sys/devices/virtual/hwmon/hwmon4", "temp1_input");
+
+ status = smprintf("T:%s|%s|%s L:%s B:%s|%s A:%s U:%s %s",
+ t0, t1, t2, avgs, bat, bat1, tmar, tmutc,
+ tmbln);
+ setstatus(status);
+
+ free(t0);
+ free(t1);
+ free(t2);
+ free(avgs);
+ free(bat);
+ free(bat1);
+ free(tmar);
+ free(tmutc);
+ free(tmbln);
+ free(status);
+ }
+
+ XCloseDisplay(dpy);
+
+ return 0;
+}
+
diff --git a/keep/manual-programs/suckless/dwm/dwmstatus/new-acpi-battery.c b/keep/manual-programs/suckless/dwm/dwmstatus/new-acpi-battery.c
new file mode 100644
index 0000000..cda0084
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/dwmstatus/new-acpi-battery.c
@@ -0,0 +1,55 @@
+char *
+readfile(char *base, char *file)
+{
+ char *path, line[513];
+ FILE *fd;
+
+ memset(line, 0, sizeof(line));
+
+ path = smprintf("%s/%s", base, file);
+ fd = fopen(path, "r");
+ if (fd == NULL) {
+ perror("fopen");
+ exit(1);
+ }
+ free(path);
+
+ if (fgets(line, sizeof(line)-1, fd) == NULL) {
+ perror("fgets");
+ exit(1);
+ }
+ fclose(fd);
+
+ return smprintf("%s", line);
+}
+
+char *
+getbattery(char *base)
+{
+ char *co;
+ int descap, remcap;
+
+ descap = -1;
+ remcap = -1;
+
+ co = readfile(base, "present");
+ if (co[0] != '1') {
+ free(co);
+ return smprintf("not present");
+ }
+ free(co);
+
+ co = readfile(base, "charge_full_design");
+ sscanf(co, "%d", &descap);
+ free(co);
+
+ co = readfile(base, "charge_now");
+ sscanf(co, "%d", &remcap);
+ free(co);
+
+ if (remcap < 0 || descap < 0)
+ return smprintf("invalid");
+
+ return smprintf("%.0f", ((float)remcap / (float)descap) * 100);
+}
+
diff --git a/keep/manual-programs/suckless/dwm/endx b/keep/manual-programs/suckless/dwm/endx
new file mode 100644
index 0000000..982d44f
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/endx
@@ -0,0 +1,5 @@
+#!/bin/sh
+#gives you a dmenu prompt to confirm if you want to exit X
+
+[ $(echo -e "No\nYes" | dmenu -i -p "$1") \
+== "Yes" ] && $2
diff --git a/keep/manual-programs/suckless/dwm/shiftview.c b/keep/manual-programs/suckless/dwm/shiftview.c
new file mode 100644
index 0000000..e82053a
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/shiftview.c
@@ -0,0 +1,19 @@
+/** Function to shift the current view to the left/right
+ *
+ * @param: "arg->i" stores the number of tags to shift right (positive value)
+ * or left (negative value)
+ */
+void
+shiftview(const Arg *arg) {
+ Arg shifted;
+
+ if(arg->i > 0) // left circular shift
+ shifted.ui = (selmon->tagset[selmon->seltags] << arg->i)
+ | (selmon->tagset[selmon->seltags] >> (LENGTH(tags) - arg->i));
+
+ else // right circular shift
+ shifted.ui = selmon->tagset[selmon->seltags] >> (- arg->i)
+ | selmon->tagset[selmon->seltags] << (LENGTH(tags) + arg->i);
+
+ view(&shifted);
+}
diff --git a/keep/manual-programs/suckless/dwm/transient.c b/keep/manual-programs/suckless/dwm/transient.c
new file mode 100644
index 0000000..040adb5
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/transient.c
@@ -0,0 +1,42 @@
+/* cc transient.c -o transient -lX11 */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main(void) {
+ Display *d;
+ Window r, f, t = None;
+ XSizeHints h;
+ XEvent e;
+
+ d = XOpenDisplay(NULL);
+ if (!d)
+ exit(1);
+ r = DefaultRootWindow(d);
+
+ f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0);
+ h.min_width = h.max_width = h.min_height = h.max_height = 400;
+ h.flags = PMinSize | PMaxSize;
+ XSetWMNormalHints(d, f, &h);
+ XStoreName(d, f, "floating");
+ XMapWindow(d, f);
+
+ XSelectInput(d, f, ExposureMask);
+ while (1) {
+ XNextEvent(d, &e);
+
+ if (t == None) {
+ sleep(5);
+ t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0);
+ XSetTransientForHint(d, t, f);
+ XStoreName(d, t, "transient");
+ XMapWindow(d, t);
+ XSelectInput(d, t, ExposureMask);
+ }
+ }
+
+ XCloseDisplay(d);
+ exit(0);
+}
diff --git a/keep/manual-programs/suckless/dwm/util.c b/keep/manual-programs/suckless/dwm/util.c
new file mode 100644
index 0000000..fe044fc
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/util.c
@@ -0,0 +1,35 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = calloc(nmemb, size)))
+ die("calloc:");
+ return p;
+}
+
+void
+die(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
diff --git a/keep/manual-programs/suckless/dwm/util.h b/keep/manual-programs/suckless/dwm/util.h
new file mode 100644
index 0000000..f633b51
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/util.h
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+
+void die(const char *fmt, ...);
+void *ecalloc(size_t nmemb, size_t size);
diff --git a/keep/manual-programs/suckless/dwm/util.o b/keep/manual-programs/suckless/dwm/util.o
new file mode 100644
index 0000000..aa3779b
--- /dev/null
+++ b/keep/manual-programs/suckless/dwm/util.o
Binary files differ
diff --git a/keep/manual-programs/suckless/surf/FAQ.md b/keep/manual-programs/suckless/surf/FAQ.md
new file mode 100644
index 0000000..48e6097
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/FAQ.md
@@ -0,0 +1,10 @@
+# Frequently Asked Questions
+
+## Surf is starting up slowly. What might be causing this?
+
+The first suspect for such behaviour is the plugin handling. Run surf on
+the commandline and see if there are errors because of “nspluginwrapper”
+or failed RPCs to them. If that is true, go to ~/.mozilla/plugins and
+try removing stale links to plugins not on your system anymore. This
+will stop surf from trying to load them.
+
diff --git a/keep/manual-programs/suckless/surf/LICENSE b/keep/manual-programs/suckless/surf/LICENSE
new file mode 100644
index 0000000..809d284
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/LICENSE
@@ -0,0 +1,57 @@
+MIT/X Consortium License
+
+© 2009-2010 Enno Boland <tox@s01.de>
+© 2009 Thomas Menari <spaceinvader@chaotika.org>
+© 2009 Simon Rozet <simon@rozet.name>
+© 2009 Andrew Antle <andrew.antle@gmail.com>
+© 2010-2011 pancake <nopcode.org>
+© 2011-2013 Anselm R Garbe <anselm@garbe.us>
+© 2011-2012 Troels Henriksen <athas@sigkill.dk>
+© 2011 Connor Lane Smith <cls@lubutu.com>
+© 2012-2017 Christoph Lohmann <20h@r-36.net>
+© 2013 Shayan Pooya <shayan@liveve.org>
+© 2013 Jens Nyberg <jens.nyberg@gmail.com>
+© 2013 Carlos J. Torres <vlaadbrain@gmail.com>
+© 2013 Alexander Sedov <alex0player@gmail.com>
+© 2013 Nick White <git@njw.me.uk>
+© 2013 David Dufberg <david@dufberg.se>
+© 2014-2021 Quentin Rameau <quinq@fifth.space>
+© 2014-2016 Markus Teich <markus.teich@stusta.mhn.de>
+© 2015 Jakukyo Friel <weakish@gmail.com>
+© 2015 Ben Woolley <tautolog@gmail.com>
+© 2015 Greg Reagle <greg.reagle@umbc.edu>
+© 2015 GhostAV <ghostav@riseup.net>
+© 2015-2017 Ivan Tham <pickfire@riseup.net>
+© 2015 Alexander Huemer <alexander.huemer@xx.vu>
+© 2015 Michael Stevens <mstevens@etla.org>
+© 2015 Felix Janda <felix.janda@posteo.de>
+© 2016 Charles Lehner <cel@celehner.com>
+© 2016 Dmitry Bogatov <KAction@gnu.org>
+© 2017 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2017 ssd <ssd@mailless.org>
+© 2017 Constantine Bytensky <kostya3@gmail.com>
+© 2017 Eon S. Jeon <esjeon@hyunmu.am>
+© 2017 Jochen Sprickerhof <git@jochen.sprickerhof.de>
+© 2018 nzl <uruabi@gmail.com>
+© 2018 Eddie Thieda <eddie.thieda@gmail.com>
+© 2018 Leonardo Taccari <iamleot@gmail.com>
+© 2019 efe <efe@efe.kim>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/keep/manual-programs/suckless/surf/Makefile b/keep/manual-programs/suckless/surf/Makefile
new file mode 100644
index 0000000..1edf820
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/Makefile
@@ -0,0 +1,76 @@
+# surf - simple browser
+# See LICENSE file for copyright and license details.
+.POSIX:
+
+include config.mk
+
+SRC = surf.c
+WSRC = webext-surf.c
+OBJ = $(SRC:.c=.o)
+WOBJ = $(WSRC:.c=.o)
+WLIB = $(WSRC:.c=.so)
+
+all: options surf $(WLIB)
+
+options:
+ @echo surf build options:
+ @echo "CC = $(CC)"
+ @echo "CFLAGS = $(SURFCFLAGS) $(CFLAGS)"
+ @echo "WEBEXTCFLAGS = $(WEBEXTCFLAGS) $(CFLAGS)"
+ @echo "LDFLAGS = $(LDFLAGS)"
+
+surf: $(OBJ)
+ $(CC) $(SURFLDFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
+
+$(OBJ) $(WOBJ): config.h common.h config.mk
+
+config.h:
+ cp config.def.h $@
+
+$(OBJ): $(SRC)
+ $(CC) $(SURFCFLAGS) $(CFLAGS) -c $(SRC)
+
+$(WLIB): $(WOBJ)
+ $(CC) -shared -Wl,-soname,$@ $(LDFLAGS) -o $@ $? $(WEBEXTLIBS)
+
+$(WOBJ): $(WSRC)
+ $(CC) $(WEBEXTCFLAGS) $(CFLAGS) -c $(WSRC)
+
+clean:
+ rm -f surf $(OBJ)
+ rm -f $(WLIB) $(WOBJ)
+
+distclean: clean
+ rm -f config.h surf-$(VERSION).tar.gz
+
+dist: distclean
+ mkdir -p surf-$(VERSION)
+ cp -R LICENSE Makefile config.mk config.def.h README \
+ surf-open.sh arg.h TODO.md surf.png \
+ surf.1 common.h $(SRC) $(WSRC) surf-$(VERSION)
+ tar -cf surf-$(VERSION).tar surf-$(VERSION)
+ gzip surf-$(VERSION).tar
+ rm -rf surf-$(VERSION)
+
+install: all
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp -f surf $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/surf
+ mkdir -p $(DESTDIR)$(LIBDIR)
+ cp -f $(WLIB) $(DESTDIR)$(LIBDIR)
+ for wlib in $(WLIB); do \
+ chmod 644 $(DESTDIR)$(LIBDIR)/$$wlib; \
+ done
+ mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ sed "s/VERSION/$(VERSION)/g" < surf.1 > $(DESTDIR)$(MANPREFIX)/man1/surf.1
+ chmod 644 $(DESTDIR)$(MANPREFIX)/man1/surf.1
+
+uninstall:
+ rm -f $(DESTDIR)$(PREFIX)/bin/surf
+ rm -f $(DESTDIR)$(MANPREFIX)/man1/surf.1
+ for wlib in $(WLIB); do \
+ rm -f $(DESTDIR)$(LIBDIR)/$$wlib; \
+ done
+ - rmdir $(DESTDIR)$(LIBDIR)
+
+.PHONY: all options distclean clean dist install uninstall
diff --git a/keep/manual-programs/suckless/surf/README b/keep/manual-programs/suckless/surf/README
new file mode 100644
index 0000000..da4577f
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/README
@@ -0,0 +1,40 @@
+surf - simple webkit-based browser
+==================================
+surf is a simple Web browser based on WebKit/GTK+.
+
+Requirements
+------------
+In order to build surf you need GTK+ and Webkit/GTK+ header files.
+
+In order to use the functionality of the url-bar, also install dmenu[0].
+
+Installation
+------------
+Edit config.mk to match your local setup (surf is installed into
+the /usr/local namespace by default).
+
+Afterwards enter the following command to build and install surf (if
+necessary as root):
+
+ make clean install
+
+Running surf
+------------
+run
+ surf [URI]
+
+See the manpage for further options.
+
+Running surf in tabbed
+----------------------
+For running surf in tabbed[1] there is a script included in the distribution,
+which is run like this:
+
+ surf-open.sh [URI]
+
+Further invocations of the script will run surf with the specified URI in this
+instance of tabbed.
+
+[0] http://tools.suckless.org/dmenu
+[1] http://tools.suckless.org/tabbed
+
diff --git a/keep/manual-programs/suckless/surf/TODO.md b/keep/manual-programs/suckless/surf/TODO.md
new file mode 100644
index 0000000..da5f44d
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/TODO.md
@@ -0,0 +1,10 @@
+# TODO
+
+* suckless adblocking
+* replace twitch() with proper gtk calls to make scrollbars reappear
+* replace webkit with something sane
+* add video player options
+ * play in plugin
+ * play in video player
+ * call command with URI (quvi + cclive)
+
diff --git a/keep/manual-programs/suckless/surf/arg.h b/keep/manual-programs/suckless/surf/arg.h
new file mode 100644
index 0000000..ba3fb3f
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/arg.h
@@ -0,0 +1,48 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0] == '-'\
+ && argv[0][1];\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+#define ARGEND }\
+ }
+
+#define ARGC() argc_
+
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ ((x), abort(), (char *)0) :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/keep/manual-programs/suckless/surf/common.h b/keep/manual-programs/suckless/surf/common.h
new file mode 100644
index 0000000..3990c42
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/common.h
@@ -0,0 +1 @@
+#define MSGBUFSZ 8
diff --git a/keep/manual-programs/suckless/surf/config.def.h b/keep/manual-programs/suckless/surf/config.def.h
new file mode 100644
index 0000000..4744f37
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/config.def.h
@@ -0,0 +1,205 @@
+/* modifier 0 means no modifier */
+static int surfuseragent = 1; /* Append Surf version to default WebKit user agent */
+static char *fulluseragent = ""; /* Or override the whole user agent string */
+static char *scriptfile = "~/.surf/script.js";
+static char *styledir = "~/.surf/styles/";
+static char *certdir = "~/.surf/certificates/";
+static char *cachedir = "~/.surf/cache/";
+static char *cookiefile = "~/.surf/cookies.txt";
+
+/* Webkit default features */
+/* Highest priority value will be used.
+ * Default parameters are priority 0
+ * Per-uri parameters are priority 1
+ * Command parameters are priority 2
+ */
+static Parameter defconfig[ParameterLast] = {
+ /* parameter Arg value priority */
+ [AccessMicrophone] = { { .i = 0 }, },
+ [AccessWebcam] = { { .i = 0 }, },
+ [Certificate] = { { .i = 0 }, },
+ [CaretBrowsing] = { { .i = 0 }, },
+ [CookiePolicies] = { { .v = "@Aa" }, },
+ [DarkMode] = { { .i = 0 }, },
+ [DefaultCharset] = { { .v = "UTF-8" }, },
+ [DiskCache] = { { .i = 1 }, },
+ [DNSPrefetch] = { { .i = 0 }, },
+ [Ephemeral] = { { .i = 0 }, },
+ [FileURLsCrossAccess] = { { .i = 0 }, },
+ [FontSize] = { { .i = 12 }, },
+ [Geolocation] = { { .i = 0 }, },
+ [HideBackground] = { { .i = 0 }, },
+ [Inspector] = { { .i = 1 }, },
+ [JavaScript] = { { .i = 1 }, },
+ [KioskMode] = { { .i = 0 }, },
+ [LoadImages] = { { .i = 1 }, },
+ [MediaManualPlay] = { { .i = 1 }, },
+ [PreferredLanguages] = { { .v = (char *[]){ NULL } }, },
+ [RunInFullscreen] = { { .i = 0 }, },
+ [ScrollBars] = { { .i = 1 }, },
+ [ShowIndicators] = { { .i = 1 }, },
+ [SiteQuirks] = { { .i = 1 }, },
+ [SmoothScrolling] = { { .i = 0 }, },
+ [SpellChecking] = { { .i = 0 }, },
+ [SpellLanguages] = { { .v = ((char *[]){ "en_US", NULL }) }, },
+ [StrictTLS] = { { .i = 1 }, },
+ [Style] = { { .i = 1 }, },
+ [WebGL] = { { .i = 0 }, },
+ [ZoomLevel] = { { .f = 1.0 }, },
+};
+
+static UriParameters uriparams[] = {
+ { "(://|\\.)suckless\\.org(/|$)", {
+ [JavaScript] = { { .i = 0 }, 1 },
+ }, },
+};
+
+/* default window size: width, height */
+static int winsize[] = { 800, 600 };
+
+static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE |
+ WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+
+#define PROMPT_GO "Go:"
+#define PROMPT_FIND "Find:"
+
+/* SETPROP(readprop, setprop, prompt)*/
+#define SETPROP(r, s, p) { \
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "prop=\"$(printf '%b' \"$(xprop -id $1 "r" " \
+ "| sed -e 's/^"r"(UTF8_STRING) = \"\\(.*\\)\"/\\1/' " \
+ " -e 's/\\\\\\(.\\)/\\1/g')\" " \
+ "| dmenu -p '"p"' -w $1)\" " \
+ "&& xprop -id $1 -f "s" 8u -set "s" \"$prop\"", \
+ "surf-setprop", winid, NULL \
+ } \
+}
+
+/* DOWNLOAD(URI, referer) */
+#define DOWNLOAD(u, r) { \
+ .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\
+ "curl -g -L -J -O -A \"$1\" -b \"$2\" -c \"$2\"" \
+ " -e \"$3\" \"$4\"; read", \
+ "surf-download", useragent, cookiefile, r, u, NULL \
+ } \
+}
+
+/* PLUMB(URI) */
+/* This called when some URI which does not begin with "about:",
+ * "http://" or "https://" should be opened.
+ */
+#define PLUMB(u) {\
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "xdg-open \"$0\"", u, NULL \
+ } \
+}
+
+/* VIDEOPLAY(URI) */
+#define VIDEOPLAY(u) {\
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "mpv --really-quiet \"$0\"", u, NULL \
+ } \
+}
+
+/* styles */
+/*
+ * The iteration will stop at the first match, beginning at the beginning of
+ * the list.
+ */
+static SiteSpecific styles[] = {
+ /* regexp file in $styledir */
+ { ".*", "default.css" },
+};
+
+/* certificates */
+/*
+ * Provide custom certificate for urls
+ */
+static SiteSpecific certs[] = {
+ /* regexp file in $certdir */
+ { "://suckless\\.org/", "suckless.org.crt" },
+};
+
+#define MODKEY GDK_CONTROL_MASK
+
+/* hotkeys */
+/*
+ * If you use anything else but MODKEY and GDK_SHIFT_MASK, don't forget to
+ * edit the CLEANMASK() macro.
+ */
+static Key keys[] = {
+ /* modifier keyval function arg */
+ { 0, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) },
+ { 0, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+ { 0, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+
+ { 0, GDK_KEY_i, insert, { .i = 1 } },
+ { 0, GDK_KEY_Escape, insert, { .i = 0 } },
+
+ { 0, GDK_KEY_c, stop, { 0 } },
+
+ { MODKEY, GDK_KEY_r, reload, { .i = 1 } },
+ { 0, GDK_KEY_r, reload, { .i = 0 } },
+
+ { 0, GDK_KEY_l, navigate, { .i = +1 } },
+ { 0, GDK_KEY_h, navigate, { .i = -1 } },
+
+ /* vertical and horizontal scrolling, in viewport percentage */
+ { 0, GDK_KEY_j, scrollv, { .i = +10 } },
+ { 0, GDK_KEY_k, scrollv, { .i = -10 } },
+ { 0, GDK_KEY_space, scrollv, { .i = +50 } },
+ { 0, GDK_KEY_b, scrollv, { .i = -50 } },
+ { 0, GDK_KEY_i, scrollh, { .i = +10 } },
+ { 0, GDK_KEY_u, scrollh, { .i = -10 } },
+
+
+ { 0|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } },
+ { 0, GDK_KEY_minus, zoom, { .i = -1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_plus, zoom, { .i = +1 } },
+ { 0, GDK_KEY_equal, zoom, { .i = 0 } },
+
+ { 0, GDK_KEY_p, clipboard, { .i = 1 } },
+ { 0, GDK_KEY_y, clipboard, { .i = 0 } },
+
+ { 0, GDK_KEY_n, find, { .i = +1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } },
+
+ { MODKEY, GDK_KEY_p, print, { 0 } },
+ { MODKEY, GDK_KEY_t, showcert, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } },
+ { 0, GDK_KEY_F11, togglefullscreen, { 0 } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o, toggleinspector, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c, toggle, { .i = CaretBrowsing } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g, toggle, { .i = Geolocation } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_s, toggle, { .i = JavaScript } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i, toggle, { .i = LoadImages } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } },
+};
+
+/* button definitions */
+/* target can be OnDoc, OnLink, OnImg, OnMedia, OnEdit, OnBar, OnSel, OnAny */
+static Button buttons[] = {
+ /* target event mask button function argument stop event */
+ { OnLink, 0, 2, clicknewwindow, { .i = 0 }, 1 },
+ { OnLink, MODKEY, 2, clicknewwindow, { .i = 1 }, 1 },
+ { OnLink, MODKEY, 1, clicknewwindow, { .i = 1 }, 1 },
+ { OnAny, 0, 8, clicknavigate, { .i = -1 }, 1 },
+ { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 },
+ { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 },
+};
+
+/* Put here your aliases */
+static Alias aliases[] = {
+ /* Alias URI */
+ { "ddg", "https://duckduckgo.com" },
+ { "brave", "https://search.brave.com" },
+ { "searx", "https://search.bus-hit.me" },
+ { "wiki", "https://www.wikipedia.org" }
+};
diff --git a/keep/manual-programs/suckless/surf/config.def.h.orig b/keep/manual-programs/suckless/surf/config.def.h.orig
new file mode 100644
index 0000000..e340bab
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/config.def.h.orig
@@ -0,0 +1,200 @@
+/* modifier 0 means no modifier */
+static int surfuseragent = 1; /* Append Surf version to default WebKit user agent */
+static char *fulluseragent = ""; /* Or override the whole user agent string */
+static char *scriptfile = "~/.surf/script.js";
+static char *styledir = "~/.surf/styles/";
+static char *certdir = "~/.surf/certificates/";
+static char *cachedir = "~/.surf/cache/";
+static char *cookiefile = "~/.surf/cookies.txt";
+
+/* Webkit default features */
+/* Highest priority value will be used.
+ * Default parameters are priority 0
+ * Per-uri parameters are priority 1
+ * Command parameters are priority 2
+ */
+static Parameter defconfig[ParameterLast] = {
+ /* parameter Arg value priority */
+ [AccessMicrophone] = { { .i = 0 }, },
+ [AccessWebcam] = { { .i = 0 }, },
+ [Certificate] = { { .i = 0 }, },
+ [CaretBrowsing] = { { .i = 0 }, },
+ [CookiePolicies] = { { .v = "@Aa" }, },
+ [DarkMode] = { { .i = 0 }, },
+ [DefaultCharset] = { { .v = "UTF-8" }, },
+ [DiskCache] = { { .i = 1 }, },
+ [DNSPrefetch] = { { .i = 0 }, },
+ [Ephemeral] = { { .i = 0 }, },
+ [FileURLsCrossAccess] = { { .i = 0 }, },
+ [FontSize] = { { .i = 12 }, },
+ [Geolocation] = { { .i = 0 }, },
+ [HideBackground] = { { .i = 0 }, },
+ [Inspector] = { { .i = 0 }, },
+ [JavaScript] = { { .i = 1 }, },
+ [KioskMode] = { { .i = 0 }, },
+ [LoadImages] = { { .i = 1 }, },
+ [MediaManualPlay] = { { .i = 1 }, },
+ [PreferredLanguages] = { { .v = (char *[]){ NULL } }, },
+ [RunInFullscreen] = { { .i = 0 }, },
+ [ScrollBars] = { { .i = 1 }, },
+ [ShowIndicators] = { { .i = 1 }, },
+ [SiteQuirks] = { { .i = 1 }, },
+ [SmoothScrolling] = { { .i = 0 }, },
+ [SpellChecking] = { { .i = 0 }, },
+ [SpellLanguages] = { { .v = ((char *[]){ "en_US", NULL }) }, },
+ [StrictTLS] = { { .i = 1 }, },
+ [Style] = { { .i = 1 }, },
+ [WebGL] = { { .i = 0 }, },
+ [ZoomLevel] = { { .f = 1.0 }, },
+};
+
+static UriParameters uriparams[] = {
+ { "(://|\\.)suckless\\.org(/|$)", {
+ [JavaScript] = { { .i = 0 }, 1 },
+ }, },
+};
+
+/* default window size: width, height */
+static int winsize[] = { 800, 600 };
+
+static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE |
+ WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+
+#define PROMPT_GO "Go:"
+#define PROMPT_FIND "Find:"
+
+/* SETPROP(readprop, setprop, prompt)*/
+#define SETPROP(r, s, p) { \
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "prop=\"$(printf '%b' \"$(xprop -id $1 "r" " \
+ "| sed -e 's/^"r"(UTF8_STRING) = \"\\(.*\\)\"/\\1/' " \
+ " -e 's/\\\\\\(.\\)/\\1/g')\" " \
+ "| dmenu -p '"p"' -w $1)\" " \
+ "&& xprop -id $1 -f "s" 8u -set "s" \"$prop\"", \
+ "surf-setprop", winid, NULL \
+ } \
+}
+
+/* DOWNLOAD(URI, referer) */
+#define DOWNLOAD(u, r) { \
+ .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\
+ "curl -g -L -J -O -A \"$1\" -b \"$2\" -c \"$2\"" \
+ " -e \"$3\" \"$4\"; read", \
+ "surf-download", useragent, cookiefile, r, u, NULL \
+ } \
+}
+
+/* PLUMB(URI) */
+/* This called when some URI which does not begin with "about:",
+ * "http://" or "https://" should be opened.
+ */
+#define PLUMB(u) {\
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "xdg-open \"$0\"", u, NULL \
+ } \
+}
+
+/* VIDEOPLAY(URI) */
+#define VIDEOPLAY(u) {\
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "mpv --really-quiet \"$0\"", u, NULL \
+ } \
+}
+
+/* styles */
+/*
+ * The iteration will stop at the first match, beginning at the beginning of
+ * the list.
+ */
+static SiteSpecific styles[] = {
+ /* regexp file in $styledir */
+ { ".*", "default.css" },
+};
+
+/* certificates */
+/*
+ * Provide custom certificate for urls
+ */
+static SiteSpecific certs[] = {
+ /* regexp file in $certdir */
+ { "://suckless\\.org/", "suckless.org.crt" },
+};
+
+#define MODKEY GDK_CONTROL_MASK
+
+/* hotkeys */
+/*
+ * If you use anything else but MODKEY and GDK_SHIFT_MASK, don't forget to
+ * edit the CLEANMASK() macro.
+ */
+static Key keys[] = {
+ /* modifier keyval function arg */
+ { MODKEY, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) },
+ { MODKEY, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+ { MODKEY, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+
+ { 0, GDK_KEY_Escape, stop, { 0 } },
+ { MODKEY, GDK_KEY_c, stop, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r, reload, { .i = 1 } },
+ { MODKEY, GDK_KEY_r, reload, { .i = 0 } },
+
+ { MODKEY, GDK_KEY_l, navigate, { .i = +1 } },
+ { MODKEY, GDK_KEY_h, navigate, { .i = -1 } },
+
+ /* vertical and horizontal scrolling, in viewport percentage */
+ { MODKEY, GDK_KEY_j, scrollv, { .i = +10 } },
+ { MODKEY, GDK_KEY_k, scrollv, { .i = -10 } },
+ { MODKEY, GDK_KEY_space, scrollv, { .i = +50 } },
+ { MODKEY, GDK_KEY_b, scrollv, { .i = -50 } },
+ { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } },
+ { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } },
+
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } },
+ { MODKEY, GDK_KEY_minus, zoom, { .i = -1 } },
+ { MODKEY, GDK_KEY_plus, zoom, { .i = +1 } },
+
+ { MODKEY, GDK_KEY_p, clipboard, { .i = 1 } },
+ { MODKEY, GDK_KEY_y, clipboard, { .i = 0 } },
+
+ { MODKEY, GDK_KEY_n, find, { .i = +1 } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p, print, { 0 } },
+ { MODKEY, GDK_KEY_t, showcert, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } },
+ { 0, GDK_KEY_F11, togglefullscreen, { 0 } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o, toggleinspector, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c, toggle, { .i = CaretBrowsing } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g, toggle, { .i = Geolocation } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_s, toggle, { .i = JavaScript } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i, toggle, { .i = LoadImages } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } },
+};
+
+/* button definitions */
+/* target can be OnDoc, OnLink, OnImg, OnMedia, OnEdit, OnBar, OnSel, OnAny */
+static Button buttons[] = {
+ /* target event mask button function argument stop event */
+ { OnLink, 0, 2, clicknewwindow, { .i = 0 }, 1 },
+ { OnLink, MODKEY, 2, clicknewwindow, { .i = 1 }, 1 },
+ { OnLink, MODKEY, 1, clicknewwindow, { .i = 1 }, 1 },
+ { OnAny, 0, 8, clicknavigate, { .i = -1 }, 1 },
+ { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 },
+ { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 },
+};
+
+/* Put here your aliases */
+static Alias aliases[] = {
+ /* Alias URI */
+ { "ddg", "https://duckduckgo.com" },
+ { "wiki", "https://www.wikipedia.org" }
+};
diff --git a/keep/manual-programs/suckless/surf/config.h b/keep/manual-programs/suckless/surf/config.h
new file mode 100644
index 0000000..9cbd06b
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/config.h
@@ -0,0 +1,205 @@
+/* modifier 0 means no modifier */
+static int surfuseragent = 1; /* Append Surf version to default WebKit user agent */
+static char *fulluseragent = ""; /* Or override the whole user agent string */
+static char *scriptfile = "~/.surf/script.js";
+static char *styledir = "~/.surf/styles/";
+static char *certdir = "~/.surf/certificates/";
+static char *cachedir = "~/.surf/cache/";
+static char *cookiefile = "~/.surf/cookies.txt";
+
+/* Webkit default features */
+/* Highest priority value will be used.
+ * Default parameters are priority 0
+ * Per-uri parameters are priority 1
+ * Command parameters are priority 2
+ */
+static Parameter defconfig[ParameterLast] = {
+ /* parameter Arg value priority */
+ [AccessMicrophone] = { { .i = 0 }, },
+ [AccessWebcam] = { { .i = 0 }, },
+ [Certificate] = { { .i = 0 }, },
+ [CaretBrowsing] = { { .i = 0 }, },
+ [CookiePolicies] = { { .v = "@Aa" }, },
+ [DarkMode] = { { .i = 0 }, },
+ [DefaultCharset] = { { .v = "UTF-8" }, },
+ [DiskCache] = { { .i = 1 }, },
+ [DNSPrefetch] = { { .i = 0 }, },
+ [Ephemeral] = { { .i = 0 }, },
+ [FileURLsCrossAccess] = { { .i = 0 }, },
+ [FontSize] = { { .i = 12 }, },
+ [Geolocation] = { { .i = 0 }, },
+ [HideBackground] = { { .i = 0 }, },
+ [Inspector] = { { .i = 1 }, },
+ [JavaScript] = { { .i = 1 }, },
+ [KioskMode] = { { .i = 0 }, },
+ [LoadImages] = { { .i = 1 }, },
+ [MediaManualPlay] = { { .i = 1 }, },
+ [PreferredLanguages] = { { .v = (char *[]){ NULL } }, },
+ [RunInFullscreen] = { { .i = 0 }, },
+ [ScrollBars] = { { .i = 1 }, },
+ [ShowIndicators] = { { .i = 1 }, },
+ [SiteQuirks] = { { .i = 1 }, },
+ [SmoothScrolling] = { { .i = 0 }, },
+ [SpellChecking] = { { .i = 0 }, },
+ [SpellLanguages] = { { .v = ((char *[]){ "en_US", NULL }) }, },
+ [StrictTLS] = { { .i = 1 }, },
+ [Style] = { { .i = 1 }, },
+ [WebGL] = { { .i = 0 }, },
+ [ZoomLevel] = { { .f = 1.0 }, },
+};
+
+static UriParameters uriparams[] = {
+ { "(://|\\.)suckless\\.org(/|$)", {
+ [JavaScript] = { { .i = 0 }, 1 },
+ }, },
+};
+
+/* default window size: width, height */
+static int winsize[] = { 800, 600 };
+
+static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE |
+ WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+
+#define PROMPT_GO "Go:"
+#define PROMPT_FIND "Find:"
+
+/* SETPROP(readprop, setprop, prompt)*/
+#define SETPROP(r, s, p) { \
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "prop=\"$(printf '%b' \"$(xprop -id $1 "r" " \
+ "| sed -e 's/^"r"(UTF8_STRING) = \"\\(.*\\)\"/\\1/' " \
+ " -e 's/\\\\\\(.\\)/\\1/g')\" " \
+ "| rofi -dmenu -p '"p"' -w $1)\" " \
+ "&& xprop -id $1 -f "s" 8u -set "s" \"$prop\"", \
+ "surf-setprop", winid, NULL \
+ } \
+}
+
+/* DOWNLOAD(URI, referer) */
+#define DOWNLOAD(u, r) { \
+ .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\
+ "curl -g -L -J -O -A \"$1\" -b \"$2\" -c \"$2\"" \
+ " -e \"$3\" \"$4\"; read", \
+ "surf-download", useragent, cookiefile, r, u, NULL \
+ } \
+}
+
+/* PLUMB(URI) */
+/* This called when some URI which does not begin with "about:",
+ * "http://" or "https://" should be opened.
+ */
+#define PLUMB(u) {\
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "xdg-open \"$0\"", u, NULL \
+ } \
+}
+
+/* VIDEOPLAY(URI) */
+#define VIDEOPLAY(u) {\
+ .v = (const char *[]){ "/bin/sh", "-c", \
+ "mpv --really-quiet \"$0\"", u, NULL \
+ } \
+}
+
+/* styles */
+/*
+ * The iteration will stop at the first match, beginning at the beginning of
+ * the list.
+ */
+static SiteSpecific styles[] = {
+ /* regexp file in $styledir */
+ { ".*", "default.css" },
+};
+
+/* certificates */
+/*
+ * Provide custom certificate for urls
+ */
+static SiteSpecific certs[] = {
+ /* regexp file in $certdir */
+ { "://suckless\\.org/", "suckless.org.crt" },
+};
+
+#define MODKEY GDK_CONTROL_MASK
+
+/* hotkeys */
+/*
+ * If you use anything else but MODKEY and GDK_SHIFT_MASK, don't forget to
+ * edit the CLEANMASK() macro.
+ */
+static Key keys[] = {
+ /* modifier keyval function arg */
+ { 0, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) },
+ { 0, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+ { 0, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+
+ { 0, GDK_KEY_i, insert, { .i = 1 } },
+ { 0, GDK_KEY_Escape, insert, { .i = 0 } },
+
+ { 0, GDK_KEY_c, stop, { 0 } },
+
+ { MODKEY, GDK_KEY_r, reload, { .i = 1 } },
+ { 0, GDK_KEY_r, reload, { .i = 0 } },
+
+ { 0, GDK_KEY_l, navigate, { .i = +1 } },
+ { 0, GDK_KEY_h, navigate, { .i = -1 } },
+
+ /* vertical and horizontal scrolling, in viewport percentage */
+ { 0, GDK_KEY_j, scrollv, { .i = +10 } },
+ { 0, GDK_KEY_k, scrollv, { .i = -10 } },
+ { 0, GDK_KEY_space, scrollv, { .i = +50 } },
+ { 0, GDK_KEY_b, scrollv, { .i = -50 } },
+ { 0, GDK_KEY_i, scrollh, { .i = +10 } },
+ { 0, GDK_KEY_u, scrollh, { .i = -10 } },
+
+
+ { 0|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } },
+ { 0, GDK_KEY_minus, zoom, { .i = -1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_plus, zoom, { .i = +1 } },
+ { 0, GDK_KEY_equal, zoom, { .i = 0 } },
+
+ { 0, GDK_KEY_p, clipboard, { .i = 1 } },
+ { 0, GDK_KEY_y, clipboard, { .i = 0 } },
+
+ { 0, GDK_KEY_n, find, { .i = +1 } },
+ { 0|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } },
+
+ { MODKEY, GDK_KEY_p, print, { 0 } },
+ { MODKEY, GDK_KEY_t, showcert, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } },
+ { 0, GDK_KEY_F11, togglefullscreen, { 0 } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o, toggleinspector, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c, toggle, { .i = CaretBrowsing } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g, toggle, { .i = Geolocation } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_s, toggle, { .i = JavaScript } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i, toggle, { .i = LoadImages } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } },
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_d, toggle, { .i = DarkMode } },
+};
+
+/* button definitions */
+/* target can be OnDoc, OnLink, OnImg, OnMedia, OnEdit, OnBar, OnSel, OnAny */
+static Button buttons[] = {
+ /* target event mask button function argument stop event */
+ { OnLink, 0, 2, clicknewwindow, { .i = 0 }, 1 },
+ { OnLink, MODKEY, 2, clicknewwindow, { .i = 1 }, 1 },
+ { OnLink, MODKEY, 1, clicknewwindow, { .i = 1 }, 1 },
+ { OnAny, 0, 8, clicknavigate, { .i = -1 }, 1 },
+ { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 },
+ { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 },
+};
+
+/* Put here your aliases */
+static Alias aliases[] = {
+ /* Alias URI */
+ { "ddg", "https://duckduckgo.com" },
+ { "brave", "https://search.brave.com" },
+ { "searx", "https://search.bus-hit.me" },
+ { "wiki", "https://www.wikipedia.org" }
+};
diff --git a/keep/manual-programs/suckless/surf/config.mk b/keep/manual-programs/suckless/surf/config.mk
new file mode 100644
index 0000000..e55ffd9
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/config.mk
@@ -0,0 +1,33 @@
+# surf version
+VERSION = 2.1
+
+# Customize below to fit your system
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/share/man
+LIBPREFIX = $(PREFIX)/lib
+LIBDIR = $(LIBPREFIX)/surf
+
+X11INC = `pkg-config --cflags x11`
+X11LIB = `pkg-config --libs x11`
+
+GTKINC = `pkg-config --cflags gtk+-3.0 gcr-3 webkit2gtk-4.0`
+GTKLIB = `pkg-config --libs gtk+-3.0 gcr-3 webkit2gtk-4.0`
+WEBEXTINC = `pkg-config --cflags webkit2gtk-4.0 webkit2gtk-web-extension-4.0 gio-2.0`
+WEBEXTLIBS = `pkg-config --libs webkit2gtk-4.0 webkit2gtk-web-extension-4.0 gio-2.0`
+
+# includes and libs
+INCS = $(X11INC) $(GTKINC)
+LIBS = $(X11LIB) $(GTKLIB) -lgthread-2.0
+
+# flags
+CPPFLAGS = -DVERSION=\"$(VERSION)\" -DGCR_API_SUBJECT_TO_CHANGE \
+ -DLIBPREFIX=\"$(LIBPREFIX)\" -DWEBEXTDIR=\"$(LIBDIR)\" \
+ -D_DEFAULT_SOURCE
+SURFCFLAGS = -fPIC $(INCS) $(CPPFLAGS)
+WEBEXTCFLAGS = -fPIC $(WEBEXTINC)
+
+# compiler
+#CC = c99
+CC = gcc -std=c99
diff --git a/keep/manual-programs/suckless/surf/surf b/keep/manual-programs/suckless/surf/surf
new file mode 100644
index 0000000..2bc25ab
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf
Binary files differ
diff --git a/keep/manual-programs/suckless/surf/surf-modal-20190209-d068a38.diff b/keep/manual-programs/suckless/surf/surf-modal-20190209-d068a38.diff
new file mode 100644
index 0000000..f6508f0
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf-modal-20190209-d068a38.diff
@@ -0,0 +1,134 @@
+From 74a98d9600c50d50b9323cf8e459c88eb15da557 Mon Sep 17 00:00:00 2001
+From: efe <efe@efe.kim>
+Date: Sat, 9 Feb 2019 13:16:51 -0500
+Subject: [PATCH] Modal behaviour, 'i' to insert 'Esc' to get to the normal
+ mode
+
+---
+ config.def.h | 53 +++++++++++++++++++++++++++-------------------------
+ surf.c | 14 +++++++++++++-
+ 2 files changed, 41 insertions(+), 26 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 34265f6..8b7d5a2 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -130,41 +130,44 @@ static SiteSpecific certs[] = {
+ */
+ static Key keys[] = {
+ /* modifier keyval function arg */
+- { MODKEY, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) },
+- { MODKEY, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+- { MODKEY, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
++ { 0, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) },
++ { 0, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
++ { 0, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) },
+
+- { 0, GDK_KEY_Escape, stop, { 0 } },
+- { MODKEY, GDK_KEY_c, stop, { 0 } },
++ { 0, GDK_KEY_i, insert, { .i = 1 } },
++ { 0, GDK_KEY_Escape, insert, { .i = 0 } },
+
+- { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r, reload, { .i = 1 } },
+- { MODKEY, GDK_KEY_r, reload, { .i = 0 } },
++ { 0, GDK_KEY_c, stop, { 0 } },
+
+- { MODKEY, GDK_KEY_l, navigate, { .i = +1 } },
+- { MODKEY, GDK_KEY_h, navigate, { .i = -1 } },
++ { MODKEY, GDK_KEY_r, reload, { .i = 1 } },
++ { 0, GDK_KEY_r, reload, { .i = 0 } },
++
++ { 0, GDK_KEY_l, navigate, { .i = +1 } },
++ { 0, GDK_KEY_h, navigate, { .i = -1 } },
+
+ /* vertical and horizontal scrolling, in viewport percentage */
+- { MODKEY, GDK_KEY_j, scrollv, { .i = +10 } },
+- { MODKEY, GDK_KEY_k, scrollv, { .i = -10 } },
+- { MODKEY, GDK_KEY_space, scrollv, { .i = +50 } },
+- { MODKEY, GDK_KEY_b, scrollv, { .i = -50 } },
+- { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } },
+- { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } },
++ { 0, GDK_KEY_j, scrollv, { .i = +10 } },
++ { 0, GDK_KEY_k, scrollv, { .i = -10 } },
++ { 0, GDK_KEY_space, scrollv, { .i = +50 } },
++ { 0, GDK_KEY_b, scrollv, { .i = -50 } },
++ { 0, GDK_KEY_i, scrollh, { .i = +10 } },
++ { 0, GDK_KEY_u, scrollh, { .i = -10 } },
+
+
+- { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } },
+- { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } },
+- { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } },
+- { MODKEY, GDK_KEY_minus, zoom, { .i = -1 } },
+- { MODKEY, GDK_KEY_plus, zoom, { .i = +1 } },
++ { 0|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } },
++ { 0|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } },
++ { 0|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } },
++ { 0, GDK_KEY_minus, zoom, { .i = -1 } },
++ { 0|GDK_SHIFT_MASK, GDK_KEY_plus, zoom, { .i = +1 } },
++ { 0, GDK_KEY_equal, zoom, { .i = 0 } },
+
+- { MODKEY, GDK_KEY_p, clipboard, { .i = 1 } },
+- { MODKEY, GDK_KEY_y, clipboard, { .i = 0 } },
++ { 0, GDK_KEY_p, clipboard, { .i = 1 } },
++ { 0, GDK_KEY_y, clipboard, { .i = 0 } },
+
+- { MODKEY, GDK_KEY_n, find, { .i = +1 } },
+- { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } },
++ { 0, GDK_KEY_n, find, { .i = +1 } },
++ { 0|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } },
+
+- { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p, print, { 0 } },
++ { MODKEY, GDK_KEY_p, print, { 0 } },
+ { MODKEY, GDK_KEY_t, showcert, { 0 } },
+
+ { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } },
+diff --git a/surf.c b/surf.c
+index 2b54e3c..f4cbe68 100644
+--- a/surf.c
++++ b/surf.c
+@@ -175,6 +175,7 @@ static void spawn(Client *c, const Arg *a);
+ static void msgext(Client *c, char type, const Arg *a);
+ static void destroyclient(Client *c);
+ static void cleanup(void);
++static int insertmode = 0;
+
+ /* GTK/WebKit */
+ static WebKitWebView *newview(Client *c, WebKitWebView *rv);
+@@ -231,6 +232,7 @@ static void togglefullscreen(Client *c, const Arg *a);
+ static void togglecookiepolicy(Client *c, const Arg *a);
+ static void toggleinspector(Client *c, const Arg *a);
+ static void find(Client *c, const Arg *a);
++static void insert(Client *c, const Arg *a);
+
+ /* Buttons */
+ static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h);
+@@ -1333,7 +1335,11 @@ winevent(GtkWidget *w, GdkEvent *e, Client *c)
+ updatetitle(c);
+ break;
+ case GDK_KEY_PRESS:
+- if (!curconfig[KioskMode].val.i) {
++ if (!curconfig[KioskMode].val.i &&
++ !insertmode ||
++ CLEANMASK(e->key.state) == (MODKEY|GDK_SHIFT_MASK) ||
++ CLEANMASK(e->key.state) == (MODKEY) ||
++ gdk_keyval_to_lower(e->key.keyval) == (GDK_KEY_Escape)) {
+ for (i = 0; i < LENGTH(keys); ++i) {
+ if (gdk_keyval_to_lower(e->key.keyval) ==
+ keys[i].keyval &&
+@@ -1947,6 +1953,12 @@ find(Client *c, const Arg *a)
+ }
+ }
+
++void
++insert(Client *c, const Arg *a)
++{
++ insertmode = (a->i);
++}
++
+ void
+ clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h)
+ {
+--
+2.20.1
+
diff --git a/keep/manual-programs/suckless/surf/surf-open.sh b/keep/manual-programs/suckless/surf/surf-open.sh
new file mode 100644
index 0000000..c22edc2
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf-open.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# See the LICENSE file for copyright and license details.
+#
+
+xidfile="$HOME/tmp/tabbed-surf.xid"
+uri=""
+
+if [ "$#" -gt 0 ];
+then
+ uri="$1"
+fi
+
+runtabbed() {
+ tabbed -dn tabbed-surf -r 2 surf -e '' "$uri" >"$xidfile" \
+ 2>/dev/null &
+}
+
+if [ ! -r "$xidfile" ];
+then
+ runtabbed
+else
+ xid=$(cat "$xidfile")
+ xprop -id "$xid" >/dev/null 2>&1
+ if [ $? -gt 0 ];
+ then
+ runtabbed
+ else
+ surf -e "$xid" "$uri" >/dev/null 2>&1 &
+ fi
+fi
+
diff --git a/keep/manual-programs/suckless/surf/surf-uri-aliases-20220930-089272b.diff b/keep/manual-programs/suckless/surf/surf-uri-aliases-20220930-089272b.diff
new file mode 100644
index 0000000..98ed58d
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf-uri-aliases-20220930-089272b.diff
@@ -0,0 +1,58 @@
+From 089272be1459bc91800215d39f483a2584ae99cc Mon Sep 17 00:00:00 2001
+From: r4v10l1 <29655971+r4v10l1@users.noreply.github.com>
+Date: Fri, 30 Sep 2022 18:59:15 +0200
+Subject: [PATCH] Alias patch
+
+---
+ config.def.h | 7 +++++++
+ surf.c | 12 ++++++++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/config.def.h b/config.def.h
+index 93cfeeb..2b19183 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -194,3 +194,10 @@ static Button buttons[] = {
+ { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 },
+ { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 },
+ };
++
++/* Put here your aliases */
++static Alias aliases[] = {
++ /* Alias URI */
++ { "ddg", "https://duckduckgo.com" },
++ { "wikipedia", "https://www.wikipedia.org" }
++};
+diff --git a/surf.c b/surf.c
+index 474c01b..25f8528 100644
+--- a/surf.c
++++ b/surf.c
+@@ -144,6 +144,11 @@ typedef struct {
+ regex_t re;
+ } SiteSpecific;
+
++typedef struct {
++ const char* alias;
++ const char* uri;
++} Alias;
++
+ /* Surf */
+ static void die(const char *errstr, ...);
+ static void usage(void);
+@@ -567,6 +572,13 @@ loaduri(Client *c, const Arg *a)
+ if (g_strcmp0(uri, "") == 0)
+ return;
+
++ for (int i = 0; i < LENGTH(aliases); i++) {
++ if (strcmp(aliases[i].alias, uri) == 0) {
++ uri = aliases[i].uri;
++ break;
++ }
++ }
++
+ if (g_str_has_prefix(uri, "http://") ||
+ g_str_has_prefix(uri, "https://") ||
+ g_str_has_prefix(uri, "file://") ||
+--
+2.37.3
+
diff --git a/keep/manual-programs/suckless/surf/surf.1 b/keep/manual-programs/suckless/surf/surf.1
new file mode 100644
index 0000000..23117b1
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf.1
@@ -0,0 +1,305 @@
+.TH SURF 1 surf\-VERSION
+.SH NAME
+surf \- simple webkit-based browser
+.SH SYNOPSIS
+.B surf
+.RB [-bBdDfFgGiIkKmMnNpPsStTvwxX]
+.RB [-a\ cookiepolicies]
+.RB [-c\ cookiefile]
+.RB [-C\ stylefile]
+.RB [-e\ xid]
+.RB [-r\ scriptfile]
+.RB [-u\ useragent]
+.RB [-z\ zoomlevel]
+.RB [URI]
+.SH DESCRIPTION
+surf is a simple Web browser based on WebKit/GTK+. It is able
+to display websites and follow links. It supports the XEmbed protocol
+which makes it possible to embed it in another application. Furthermore,
+one can point surf to another URI by setting its XProperties.
+.SH OPTIONS
+.TP
+.B \-a cookiepolicies
+Define the order of
+.I cookie policies\fR.
+The default is "@Aa" but could be
+redefined in the
+.IR config.h ,
+with "A" meaning to
+accept all cookies, "a" to deny all cookies and "@", which tells surf to
+accept no third party cookies.
+.TP
+.B \-b
+Disable Scrollbars.
+.TP
+.B \-B
+Enable Scrollbars.
+.TP
+.B \-c cookiefile
+Specify the
+.I cookiefile
+to use.
+.TP
+.B \-C stylefile
+Specify the user
+.IR stylefile .
+This does disable the site-specific styles.
+.TP
+.B \-d
+Disable the disk cache.
+.TP
+.B \-D
+Enable the disk cache.
+.TP
+.B \-e xid
+Reparents to window specified by
+.IR xid .
+.TP
+.B \-f
+Start surf in windowed mode (not fullscreen).
+.TP
+.B \-F
+Start surf in fullscreen mode.
+.TP
+.B \-g
+Disable giving the geolocation to websites.
+.TP
+.B \-G
+Enable giving the geolocation to websites.
+.TP
+.B \-i
+Disable Images.
+.TP
+.B \-I
+Enable Images.
+.TP
+.B \-k
+Disable kiosk mode (disable key strokes and right click).
+.TP
+.B \-K
+Enable kiosk mode (disable key strokes and right click).
+.TP
+.B \-m
+Disable application of user style sheets.
+.TP
+.B \-M
+Enable application of user style sheets.
+.TP
+.B \-n
+Disable the Web Inspector (Developer Tools).
+.TP
+.B \-N
+Enable the Web Inspector (Developer Tools).
+.TP
+.B \-r scriptfile
+Specify the user
+.IR scriptfile .
+.TP
+.B \-s
+Disable Javascript.
+.TP
+.B \-S
+Enable Javascript.
+.TP
+.B \-t
+Disable strict TLS check.
+.TP
+.B \-T
+Enable strict TLS check.
+.TP
+.B \-u useragent
+Specify the
+.I useragent
+which surf should use.
+.TP
+.B \-v
+Prints version information to standard output, then exits.
+.TP
+.B \-w
+Prints xid to standard output. This can be used to script the browser in for
+example
+.BR xdotool(1) .
+.TP
+.B -x
+Disable custom certificates.
+.TP
+.B -X
+Enable custom certificates.
+.TP
+.B \-z zoomlevel
+Specify the
+.I zoomlevel
+which surf should use.
+.SH USAGE
+.B Escape
+Stops loading current page or stops download.
+.TP
+.B Ctrl\-h
+Walks back the history.
+.TP
+.B Ctrl\-l
+Walks forward the history.
+.TP
+.B Ctrl\-k
+Scrolls page upwards.
+.TP
+.B Ctrl\-j
+Scrolls page downwards.
+.TP
+.B Ctrl\-b
+Scroll up one whole page view.
+.TP
+.B Ctrl\-Space
+Scroll down one whole page view.
+.TP
+.B Ctrl\-i
+Scroll horizontally to the right.
+.TP
+.B Ctrl\-u
+Scroll horizontally to the left.
+.TP
+.B Ctrl\-Shift\-k or Ctrl\-+
+Zooms page in.
+.TP
+.B Ctrl\-Shift\-j or Ctrl\--
+Zooms page out.
+.TP
+.B Ctrl\-Shift\-q
+Resets Zoom.
+.TP
+.B Ctrl\-f and Ctrl\-/
+Opens the search-bar.
+.TP
+.B Ctrl\-n
+Go to next search result.
+.TP
+.B Ctrl\-Shift\-n
+Go to previous search result.
+.TP
+.B Ctrl\-g
+Opens the URL-bar (requires dmenu installed).
+.TP
+.B Ctrl\-p
+Loads URI from primary selection.
+.TP
+.B Ctrl\-Shift\-p
+Calls Printpage Dialog.
+.TP
+.B Ctrl\-r
+Reloads the website.
+.TP
+.B Ctrl\-Shift\-r
+Reloads the website without using the cache.
+.TP
+.B Ctrl\-y
+Copies current URI to primary selection.
+.TP
+.B Ctrl\-t
+Display the current TLS certificate in a popup window.
+.TP
+.B Ctrl\-Shift\-a
+Toggle through the the
+.I cookie policies\fR.
+This will not reload the page.
+.TP
+.B Ctrl\-Shift\-b
+Toggle scrollbars. This will reload the page.
+.TP
+.B Ctrl\-Shift\-c
+Toggle caret browsing. This will reload the page.
+.TP
+.B Ctrl\-Shift\-d
+Toggle dark mode.
+.TP
+.B Ctrl\-Shift\-i
+Toggle auto-loading of images. This will reload the page.
+.TP
+.B Ctrl\-Shift\-m
+Toggle if the
+.I stylefile
+file should be loaded. This will reload the page.
+.TP
+.B Ctrl\-Shift\-o
+Open the Web Inspector (Developer Tools) window for the current page.
+.TP
+.B Ctrl\-Shift\-s
+Toggle script execution. This will reload the page.
+.TP
+.B Ctrl\-Shift\-t
+Toggle strict TLS check. This will reload the page.
+.TP
+.B F11
+Toggle fullscreen mode.
+.SH INDICATORS OF OPERATION
+Surf is showing indicators of operation in front of the site title.
+For all indicators, unless otherwise specified, a lower case letter means disabled and an upper case letter means enabled.
+.TP
+.B A
+all cookies accepted
+.TP
+.B a
+no cookies accepted
+.TP
+.B @
+all except third-party cookies accepted
+.TP
+.B c C
+caret browsing
+.TP
+.B g G
+geolocation
+.TP
+.B d D
+disk cache
+.TP
+.B i I
+images
+.TP
+.B s S
+scripts
+.TP
+.B m M
+styles
+.TP
+.B x X
+custom certificates
+.TP
+.B t T
+strict TLS
+.SH INDICATORS OF WEB PAGE
+The second part of the indicators specifies modes of the web page itself.
+.SS First character: encryption
+.TP
+.B -
+unencrypted
+.TP
+.B T
+encrypted (TLS)
+.TP
+.B U
+attempted encryption but failed
+.SS Second character: proxying
+.TP
+.B -
+no proxy
+.TP
+.B P
+using proxy
+.SH ENVIRONMENT
+.B SURF_USERAGENT
+If this variable is set upon startup, surf will use it as the
+.I useragent
+string.
+.TP
+.B http_proxy
+If this variable is set and not empty upon startup, surf will use it as the http proxy.
+.SH SIGNALS
+Surf will reload the current page on
+.BR SIGHUP .
+.SH SEE ALSO
+.BR dmenu(1),
+.BR xprop(1),
+.BR tabbed(1),
+.BR xdotool(1)
+.SH BUGS
+Please report them!
diff --git a/keep/manual-programs/suckless/surf/surf.c b/keep/manual-programs/suckless/surf/surf.c
new file mode 100644
index 0000000..ecb9ab4
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf.c
@@ -0,0 +1,2155 @@
+/* See LICENSE file for copyright and license details.
+ *
+ * To understand surf, start reading main().
+ */
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <limits.h>
+#include <pwd.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <gio/gunixfdlist.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
+#include <gcr/gcr.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <webkit2/webkit2.h>
+#include <X11/X.h>
+#include <X11/Xatom.h>
+#include <glib.h>
+
+#include "arg.h"
+#include "common.h"
+
+#define LENGTH(x) (sizeof(x) / sizeof(x[0]))
+#define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK))
+
+enum { AtomFind, AtomGo, AtomUri, AtomUTF8, AtomLast };
+
+enum {
+ OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
+ OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK,
+ OnImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE,
+ OnMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA,
+ OnEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE,
+ OnBar = WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR,
+ OnSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION,
+ OnAny = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel,
+};
+
+typedef enum {
+ AccessMicrophone,
+ AccessWebcam,
+ CaretBrowsing,
+ Certificate,
+ CookiePolicies,
+ DarkMode,
+ DiskCache,
+ DefaultCharset,
+ DNSPrefetch,
+ Ephemeral,
+ FileURLsCrossAccess,
+ FontSize,
+ Geolocation,
+ HideBackground,
+ Inspector,
+ JavaScript,
+ KioskMode,
+ LoadImages,
+ MediaManualPlay,
+ PreferredLanguages,
+ RunInFullscreen,
+ ScrollBars,
+ ShowIndicators,
+ SiteQuirks,
+ SmoothScrolling,
+ SpellChecking,
+ SpellLanguages,
+ StrictTLS,
+ Style,
+ WebGL,
+ ZoomLevel,
+ ParameterLast
+} ParamName;
+
+typedef union {
+ int i;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ Arg val;
+ int prio;
+} Parameter;
+
+typedef struct Client {
+ GtkWidget *win;
+ WebKitWebView *view;
+ WebKitSettings *settings;
+ WebKitWebContext *context;
+ WebKitWebInspector *inspector;
+ WebKitFindController *finder;
+ WebKitHitTestResult *mousepos;
+ GTlsCertificate *cert, *failedcert;
+ GTlsCertificateFlags tlserr;
+ Window xid;
+ guint64 pageid;
+ int progress, fullscreen, https, insecure, errorpage;
+ const char *title, *overtitle, *targeturi;
+ const char *needle;
+ struct Client *next;
+} Client;
+
+typedef struct {
+ guint mod;
+ guint keyval;
+ void (*func)(Client *c, const Arg *a);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ unsigned int target;
+ unsigned int mask;
+ guint button;
+ void (*func)(Client *c, const Arg *a, WebKitHitTestResult *h);
+ const Arg arg;
+ unsigned int stopevent;
+} Button;
+
+typedef struct {
+ const char *uri;
+ Parameter config[ParameterLast];
+ regex_t re;
+} UriParameters;
+
+typedef struct {
+ char *regex;
+ char *file;
+ regex_t re;
+} SiteSpecific;
+
+typedef struct {
+ const char* alias;
+ const char* uri;
+} Alias;
+
+/* Surf */
+static void die(const char *errstr, ...);
+static void usage(void);
+static void setup(void);
+static void sigchld(int unused);
+static void sighup(int unused);
+static char *buildfile(const char *path);
+static char *buildpath(const char *path);
+static char *untildepath(const char *path);
+static const char *getuserhomedir(const char *user);
+static const char *getcurrentuserhomedir(void);
+static Client *newclient(Client *c);
+static void loaduri(Client *c, const Arg *a);
+static const char *geturi(Client *c);
+static void setatom(Client *c, int a, const char *v);
+static const char *getatom(Client *c, int a);
+static void updatetitle(Client *c);
+static void gettogglestats(Client *c);
+static void getpagestats(Client *c);
+static WebKitCookieAcceptPolicy cookiepolicy_get(void);
+static char cookiepolicy_set(const WebKitCookieAcceptPolicy p);
+static void seturiparameters(Client *c, const char *uri, ParamName *params);
+static void setparameter(Client *c, int refresh, ParamName p, const Arg *a);
+static const char *getcert(const char *uri);
+static void setcert(Client *c, const char *file);
+static const char *getstyle(const char *uri);
+static void setstyle(Client *c, const char *file);
+static void runscript(Client *c);
+static void evalscript(Client *c, const char *jsstr, ...);
+static void updatewinid(Client *c);
+static void handleplumb(Client *c, const char *uri);
+static void newwindow(Client *c, const Arg *a, int noembed);
+static void spawn(Client *c, const Arg *a);
+static void msgext(Client *c, char type, const Arg *a);
+static void destroyclient(Client *c);
+static void cleanup(void);
+static int insertmode = 0;
+
+/* GTK/WebKit */
+static WebKitWebView *newview(Client *c, WebKitWebView *rv);
+static void initwebextensions(WebKitWebContext *wc, Client *c);
+static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a,
+ Client *c);
+static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c);
+static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
+ gpointer d);
+static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
+static void showview(WebKitWebView *v, Client *c);
+static GtkWidget *createwindow(Client *c);
+static gboolean loadfailedtls(WebKitWebView *v, gchar *uri,
+ GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c);
+static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c);
+static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c);
+static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c);
+static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h,
+ guint modifiers, Client *c);
+static gboolean permissionrequested(WebKitWebView *v,
+ WebKitPermissionRequest *r, Client *c);
+static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
+ WebKitPolicyDecisionType dt, Client *c);
+static void decidenavigation(WebKitPolicyDecision *d, Client *c);
+static void decidenewwindow(WebKitPolicyDecision *d, Client *c);
+static void decideresource(WebKitPolicyDecision *d, Client *c);
+static void insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e,
+ Client *c);
+static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d,
+ Client *c);
+static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c);
+static void download(Client *c, WebKitURIResponse *r);
+static gboolean viewusrmsgrcv(WebKitWebView *v, WebKitUserMessage *m,
+ gpointer u);
+static void webprocessterminated(WebKitWebView *v,
+ WebKitWebProcessTerminationReason r,
+ Client *c);
+static void closeview(WebKitWebView *v, Client *c);
+static void destroywin(GtkWidget* w, Client *c);
+
+/* Hotkeys */
+static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
+static void reload(Client *c, const Arg *a);
+static void print(Client *c, const Arg *a);
+static void showcert(Client *c, const Arg *a);
+static void clipboard(Client *c, const Arg *a);
+static void zoom(Client *c, const Arg *a);
+static void scrollv(Client *c, const Arg *a);
+static void scrollh(Client *c, const Arg *a);
+static void navigate(Client *c, const Arg *a);
+static void stop(Client *c, const Arg *a);
+static void toggle(Client *c, const Arg *a);
+static void togglefullscreen(Client *c, const Arg *a);
+static void togglecookiepolicy(Client *c, const Arg *a);
+static void toggleinspector(Client *c, const Arg *a);
+static void find(Client *c, const Arg *a);
+static void insert(Client *c, const Arg *a);
+
+/* Buttons */
+static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h);
+static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h);
+static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h);
+
+static char winid[64];
+static char togglestats[11];
+static char pagestats[2];
+static Atom atoms[AtomLast];
+static Window embed;
+static int showxid;
+static int cookiepolicy;
+static Display *dpy;
+static Client *clients;
+static GdkDevice *gdkkb;
+static char *stylefile;
+static const char *useragent;
+static Parameter *curconfig;
+static int modparams[ParameterLast];
+static int spair[2];
+char *argv0;
+
+static ParamName loadtransient[] = {
+ Certificate,
+ CookiePolicies,
+ DiskCache,
+ DNSPrefetch,
+ FileURLsCrossAccess,
+ JavaScript,
+ LoadImages,
+ PreferredLanguages,
+ ShowIndicators,
+ StrictTLS,
+ ParameterLast
+};
+
+static ParamName loadcommitted[] = {
+// AccessMicrophone,
+// AccessWebcam,
+ CaretBrowsing,
+ DarkMode,
+ DefaultCharset,
+ FontSize,
+ Geolocation,
+ HideBackground,
+ Inspector,
+// KioskMode,
+ MediaManualPlay,
+ RunInFullscreen,
+ ScrollBars,
+ SiteQuirks,
+ SmoothScrolling,
+ SpellChecking,
+ SpellLanguages,
+ Style,
+ ZoomLevel,
+ ParameterLast
+};
+
+static ParamName loadfinished[] = {
+ ParameterLast
+};
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+void
+die(const char *errstr, ...)
+{
+ va_list ap;
+
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+usage(void)
+{
+ die("usage: surf [-bBdDfFgGiIkKmMnNsStTvwxX]\n"
+ "[-a cookiepolicies ] [-c cookiefile] [-C stylefile] [-e xid]\n"
+ "[-r scriptfile] [-u useragent] [-z zoomlevel] [uri]\n");
+}
+
+void
+setup(void)
+{
+ GIOChannel *gchanin;
+ GdkDisplay *gdpy;
+ int i, j;
+
+ /* clean up any zombies immediately */
+ sigchld(0);
+ if (signal(SIGHUP, sighup) == SIG_ERR)
+ die("Can't install SIGHUP handler");
+
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("Can't open default display");
+
+ /* atoms */
+ atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
+ atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
+ atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
+ atoms[AtomUTF8] = XInternAtom(dpy, "UTF8_STRING", False);
+
+ gtk_init(NULL, NULL);
+
+ gdpy = gdk_display_get_default();
+
+ curconfig = defconfig;
+
+ /* dirs and files */
+ cookiefile = buildfile(cookiefile);
+ scriptfile = buildfile(scriptfile);
+ certdir = buildpath(certdir);
+ if (curconfig[Ephemeral].val.i)
+ cachedir = NULL;
+ else
+ cachedir = buildpath(cachedir);
+
+ gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy));
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) < 0) {
+ fputs("Unable to create sockets\n", stderr);
+ spair[0] = spair[1] = -1;
+ } else {
+ gchanin = g_io_channel_unix_new(spair[0]);
+ g_io_channel_set_encoding(gchanin, NULL, NULL);
+ g_io_channel_set_flags(gchanin, g_io_channel_get_flags(gchanin)
+ | G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_close_on_unref(gchanin, TRUE);
+ }
+
+
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) {
+ certs[i].file = g_strconcat(certdir, "/", certs[i].file,
+ NULL);
+ } else {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ certs[i].regex);
+ certs[i].regex = NULL;
+ }
+ }
+
+ if (!stylefile) {
+ styledir = buildpath(styledir);
+ for (i = 0; i < LENGTH(styles); ++i) {
+ if (!regcomp(&(styles[i].re), styles[i].regex,
+ REG_EXTENDED)) {
+ styles[i].file = g_strconcat(styledir, "/",
+ styles[i].file, NULL);
+ } else {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ styles[i].regex);
+ styles[i].regex = NULL;
+ }
+ }
+ g_free(styledir);
+ } else {
+ stylefile = buildfile(stylefile);
+ }
+
+ for (i = 0; i < LENGTH(uriparams); ++i) {
+ if (regcomp(&(uriparams[i].re), uriparams[i].uri,
+ REG_EXTENDED)) {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ uriparams[i].uri);
+ uriparams[i].uri = NULL;
+ continue;
+ }
+
+ /* copy default parameters with higher priority */
+ for (j = 0; j < ParameterLast; ++j) {
+ if (defconfig[j].prio >= uriparams[i].config[j].prio)
+ uriparams[i].config[j] = defconfig[j];
+ }
+ }
+}
+
+void
+sigchld(int unused)
+{
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("Can't install SIGCHLD handler");
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+}
+
+void
+sighup(int unused)
+{
+ Arg a = { .i = 0 };
+ Client *c;
+
+ for (c = clients; c; c = c->next)
+ reload(c, &a);
+}
+
+char *
+buildfile(const char *path)
+{
+ char *dname, *bname, *bpath, *fpath;
+ FILE *f;
+
+ dname = g_path_get_dirname(path);
+ bname = g_path_get_basename(path);
+
+ bpath = buildpath(dname);
+ g_free(dname);
+
+ fpath = g_build_filename(bpath, bname, NULL);
+ g_free(bpath);
+ g_free(bname);
+
+ if (!(f = fopen(fpath, "a")))
+ die("Could not open file: %s\n", fpath);
+
+ g_chmod(fpath, 0600); /* always */
+ fclose(f);
+
+ return fpath;
+}
+
+static const char*
+getuserhomedir(const char *user)
+{
+ struct passwd *pw = getpwnam(user);
+
+ if (!pw)
+ die("Can't get user %s login information.\n", user);
+
+ return pw->pw_dir;
+}
+
+static const char*
+getcurrentuserhomedir(void)
+{
+ const char *homedir;
+ const char *user;
+ struct passwd *pw;
+
+ homedir = getenv("HOME");
+ if (homedir)
+ return homedir;
+
+ user = getenv("USER");
+ if (user)
+ return getuserhomedir(user);
+
+ pw = getpwuid(getuid());
+ if (!pw)
+ die("Can't get current user home directory\n");
+
+ return pw->pw_dir;
+}
+
+char *
+buildpath(const char *path)
+{
+ char *apath, *fpath;
+
+ if (path[0] == '~')
+ apath = untildepath(path);
+ else
+ apath = g_strdup(path);
+
+ /* creating directory */
+ if (g_mkdir_with_parents(apath, 0700) < 0)
+ die("Could not access directory: %s\n", apath);
+
+ fpath = realpath(apath, NULL);
+ g_free(apath);
+
+ return fpath;
+}
+
+char *
+untildepath(const char *path)
+{
+ char *apath, *name, *p;
+ const char *homedir;
+
+ if (path[1] == '/' || path[1] == '\0') {
+ p = (char *)&path[1];
+ homedir = getcurrentuserhomedir();
+ } else {
+ if ((p = strchr(path, '/')))
+ name = g_strndup(&path[1], p - (path + 1));
+ else
+ name = g_strdup(&path[1]);
+
+ homedir = getuserhomedir(name);
+ g_free(name);
+ }
+ apath = g_build_filename(homedir, p, NULL);
+ return apath;
+}
+
+Client *
+newclient(Client *rc)
+{
+ Client *c;
+
+ if (!(c = calloc(1, sizeof(Client))))
+ die("Cannot malloc!\n");
+
+ c->next = clients;
+ clients = c;
+
+ c->progress = 100;
+ c->view = newview(c, rc ? rc->view : NULL);
+
+ return c;
+}
+
+void
+loaduri(Client *c, const Arg *a)
+{
+ struct stat st;
+ char *url, *path, *apath;
+ const char *uri = a->v;
+
+ if (g_strcmp0(uri, "") == 0)
+ return;
+
+ for (int i = 0; i < LENGTH(aliases); i++) {
+ if (strcmp(aliases[i].alias, uri) == 0) {
+ uri = aliases[i].uri;
+ break;
+ }
+ }
+
+ if (g_str_has_prefix(uri, "http://") ||
+ g_str_has_prefix(uri, "https://") ||
+ g_str_has_prefix(uri, "file://") ||
+ g_str_has_prefix(uri, "webkit://") ||
+ g_str_has_prefix(uri, "about:")) {
+ url = g_strdup(uri);
+ } else {
+ if (uri[0] == '~')
+ apath = untildepath(uri);
+ else
+ apath = (char *)uri;
+ if (!stat(apath, &st) && (path = realpath(apath, NULL))) {
+ url = g_strdup_printf("file://%s", path);
+ free(path);
+ } else {
+ url = g_strdup_printf("http://%s", uri);
+ }
+ if (apath != uri)
+ free(apath);
+ }
+
+ setatom(c, AtomUri, url);
+
+ if (strcmp(url, geturi(c)) == 0) {
+ reload(c, a);
+ } else {
+ webkit_web_view_load_uri(c->view, url);
+ updatetitle(c);
+ }
+
+ g_free(url);
+}
+
+const char *
+geturi(Client *c)
+{
+ const char *uri;
+
+ if (!(uri = webkit_web_view_get_uri(c->view)))
+ uri = "about:blank";
+ return uri;
+}
+
+void
+setatom(Client *c, int a, const char *v)
+{
+ XChangeProperty(dpy, c->xid,
+ atoms[a], atoms[AtomUTF8], 8, PropModeReplace,
+ (unsigned char *)v, strlen(v) + 1);
+ XSync(dpy, False);
+}
+
+const char *
+getatom(Client *c, int a)
+{
+ static char buf[BUFSIZ];
+ Atom adummy;
+ int idummy;
+ unsigned long ldummy;
+ unsigned char *p = NULL;
+
+ XSync(dpy, False);
+ XGetWindowProperty(dpy, c->xid,
+ atoms[a], 0L, BUFSIZ, False, atoms[AtomUTF8],
+ &adummy, &idummy, &ldummy, &ldummy, &p);
+ if (p)
+ strncpy(buf, (char *)p, LENGTH(buf) - 1);
+ else
+ buf[0] = '\0';
+ XFree(p);
+
+ return buf;
+}
+
+void
+updatetitle(Client *c)
+{
+ char *title;
+ const char *name = c->overtitle ? c->overtitle :
+ c->title ? c->title : "";
+
+ if (curconfig[ShowIndicators].val.i) {
+ gettogglestats(c);
+ getpagestats(c);
+
+ if (c->progress != 100)
+ title = g_strdup_printf("[%i%%] %s:%s | %s",
+ c->progress, togglestats, pagestats, name);
+ else
+ title = g_strdup_printf("%s:%s | %s",
+ togglestats, pagestats, name);
+
+ gtk_window_set_title(GTK_WINDOW(c->win), title);
+ g_free(title);
+ } else {
+ gtk_window_set_title(GTK_WINDOW(c->win), name);
+ }
+}
+
+void
+gettogglestats(Client *c)
+{
+ togglestats[0] = cookiepolicy_set(cookiepolicy_get());
+ togglestats[1] = curconfig[CaretBrowsing].val.i ? 'C' : 'c';
+ togglestats[2] = curconfig[Geolocation].val.i ? 'G' : 'g';
+ togglestats[3] = curconfig[DiskCache].val.i ? 'D' : 'd';
+ togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i';
+ togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's';
+ togglestats[6] = curconfig[Style].val.i ? 'M' : 'm';
+ togglestats[8] = curconfig[Certificate].val.i ? 'X' : 'x';
+ togglestats[9] = curconfig[StrictTLS].val.i ? 'T' : 't';
+}
+
+void
+getpagestats(Client *c)
+{
+ if (c->https)
+ pagestats[0] = (c->tlserr || c->insecure) ? 'U' : 'T';
+ else
+ pagestats[0] = '-';
+ pagestats[1] = '\0';
+}
+
+WebKitCookieAcceptPolicy
+cookiepolicy_get(void)
+{
+ switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) {
+ case 'a':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER;
+ case '@':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY;
+ default: /* fallthrough */
+ case 'A':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS;
+ }
+}
+
+char
+cookiepolicy_set(const WebKitCookieAcceptPolicy p)
+{
+ switch (p) {
+ case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER:
+ return 'a';
+ case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY:
+ return '@';
+ default: /* fallthrough */
+ case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS:
+ return 'A';
+ }
+}
+
+void
+seturiparameters(Client *c, const char *uri, ParamName *params)
+{
+ Parameter *config, *uriconfig = NULL;
+ int i, p;
+
+ for (i = 0; i < LENGTH(uriparams); ++i) {
+ if (uriparams[i].uri &&
+ !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) {
+ uriconfig = uriparams[i].config;
+ break;
+ }
+ }
+
+ curconfig = uriconfig ? uriconfig : defconfig;
+
+ for (i = 0; (p = params[i]) != ParameterLast; ++i) {
+ switch(p) {
+ default: /* FALLTHROUGH */
+ if (!(defconfig[p].prio < curconfig[p].prio ||
+ defconfig[p].prio < modparams[p]))
+ continue;
+ case Certificate:
+ case CookiePolicies:
+ case Style:
+ setparameter(c, 0, p, &curconfig[p].val);
+ }
+ }
+}
+
+void
+setparameter(Client *c, int refresh, ParamName p, const Arg *a)
+{
+ GdkRGBA bgcolor = { 0 };
+
+ modparams[p] = curconfig[p].prio;
+
+ switch (p) {
+ case AccessMicrophone:
+ return; /* do nothing */
+ case AccessWebcam:
+ return; /* do nothing */
+ case CaretBrowsing:
+ webkit_settings_set_enable_caret_browsing(c->settings, a->i);
+ refresh = 0;
+ break;
+ case Certificate:
+ if (a->i)
+ setcert(c, geturi(c));
+ return; /* do not update */
+ case CookiePolicies:
+ webkit_cookie_manager_set_accept_policy(
+ webkit_web_context_get_cookie_manager(c->context),
+ cookiepolicy_get());
+ refresh = 0;
+ break;
+ case DarkMode:
+ g_object_set(gtk_settings_get_default(),
+ "gtk-application-prefer-dark-theme", a->i, NULL);
+ return;
+ case DiskCache:
+ webkit_web_context_set_cache_model(c->context, a->i ?
+ WEBKIT_CACHE_MODEL_WEB_BROWSER :
+ WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+ return; /* do not update */
+ case DefaultCharset:
+ webkit_settings_set_default_charset(c->settings, a->v);
+ return; /* do not update */
+ case DNSPrefetch:
+ webkit_settings_set_enable_dns_prefetching(c->settings, a->i);
+ return; /* do not update */
+ case FileURLsCrossAccess:
+ webkit_settings_set_allow_file_access_from_file_urls(
+ c->settings, a->i);
+ webkit_settings_set_allow_universal_access_from_file_urls(
+ c->settings, a->i);
+ return; /* do not update */
+ case FontSize:
+ webkit_settings_set_default_font_size(c->settings, a->i);
+ return; /* do not update */
+ case Geolocation:
+ refresh = 0;
+ break;
+ case HideBackground:
+ if (a->i)
+ webkit_web_view_set_background_color(c->view, &bgcolor);
+ return; /* do not update */
+ case Inspector:
+ webkit_settings_set_enable_developer_extras(c->settings, a->i);
+ return; /* do not update */
+ case JavaScript:
+ webkit_settings_set_enable_javascript(c->settings, a->i);
+ break;
+ case KioskMode:
+ return; /* do nothing */
+ case LoadImages:
+ webkit_settings_set_auto_load_images(c->settings, a->i);
+ break;
+ case MediaManualPlay:
+ webkit_settings_set_media_playback_requires_user_gesture(
+ c->settings, a->i);
+ break;
+ case PreferredLanguages:
+ return; /* do nothing */
+ case RunInFullscreen:
+ return; /* do nothing */
+ case ScrollBars:
+ /* Disabled until we write some WebKitWebExtension for
+ * manipulating the DOM directly.
+ enablescrollbars = !enablescrollbars;
+ evalscript(c, "document.documentElement.style.overflow = '%s'",
+ enablescrollbars ? "auto" : "hidden");
+ */
+ return; /* do not update */
+ case ShowIndicators:
+ break;
+ case SmoothScrolling:
+ webkit_settings_set_enable_smooth_scrolling(c->settings, a->i);
+ return; /* do not update */
+ case SiteQuirks:
+ webkit_settings_set_enable_site_specific_quirks(
+ c->settings, a->i);
+ break;
+ case SpellChecking:
+ webkit_web_context_set_spell_checking_enabled(
+ c->context, a->i);
+ return; /* do not update */
+ case SpellLanguages:
+ return; /* do nothing */
+ case StrictTLS:
+ webkit_website_data_manager_set_tls_errors_policy(
+ webkit_web_view_get_website_data_manager(c->view), a->i ?
+ WEBKIT_TLS_ERRORS_POLICY_FAIL :
+ WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ break;
+ case Style:
+ webkit_user_content_manager_remove_all_style_sheets(
+ webkit_web_view_get_user_content_manager(c->view));
+ if (a->i)
+ setstyle(c, getstyle(geturi(c)));
+ refresh = 0;
+ break;
+ case WebGL:
+ webkit_settings_set_enable_webgl(c->settings, a->i);
+ break;
+ case ZoomLevel:
+ webkit_web_view_set_zoom_level(c->view, a->f);
+ return; /* do not update */
+ default:
+ return; /* do nothing */
+ }
+
+ updatetitle(c);
+ if (refresh)
+ reload(c, a);
+}
+
+const char *
+getcert(const char *uri)
+{
+ int i;
+
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (certs[i].regex &&
+ !regexec(&(certs[i].re), uri, 0, NULL, 0))
+ return certs[i].file;
+ }
+
+ return NULL;
+}
+
+void
+setcert(Client *c, const char *uri)
+{
+ const char *file = getcert(uri);
+ char *host;
+ GTlsCertificate *cert;
+
+ if (!file)
+ return;
+
+ if (!(cert = g_tls_certificate_new_from_file(file, NULL))) {
+ fprintf(stderr, "Could not read certificate file: %s\n", file);
+ return;
+ }
+
+ if ((uri = strstr(uri, "https://"))) {
+ uri += sizeof("https://") - 1;
+ host = g_strndup(uri, strchr(uri, '/') - uri);
+ webkit_web_context_allow_tls_certificate_for_host(c->context,
+ cert, host);
+ g_free(host);
+ }
+
+ g_object_unref(cert);
+
+}
+
+const char *
+getstyle(const char *uri)
+{
+ int i;
+
+ if (stylefile)
+ return stylefile;
+
+ for (i = 0; i < LENGTH(styles); ++i) {
+ if (styles[i].regex &&
+ !regexec(&(styles[i].re), uri, 0, NULL, 0))
+ return styles[i].file;
+ }
+
+ return "";
+}
+
+void
+setstyle(Client *c, const char *file)
+{
+ gchar *style;
+
+ if (!g_file_get_contents(file, &style, NULL, NULL)) {
+ fprintf(stderr, "Could not read style file: %s\n", file);
+ return;
+ }
+
+ webkit_user_content_manager_add_style_sheet(
+ webkit_web_view_get_user_content_manager(c->view),
+ webkit_user_style_sheet_new(style,
+ WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+ WEBKIT_USER_STYLE_LEVEL_USER,
+ NULL, NULL));
+
+ g_free(style);
+}
+
+void
+runscript(Client *c)
+{
+ gchar *script;
+ gsize l;
+
+ if (g_file_get_contents(scriptfile, &script, &l, NULL) && l)
+ evalscript(c, "%s", script);
+ g_free(script);
+}
+
+void
+evalscript(Client *c, const char *jsstr, ...)
+{
+ va_list ap;
+ gchar *script;
+
+ va_start(ap, jsstr);
+ script = g_strdup_vprintf(jsstr, ap);
+ va_end(ap);
+
+ webkit_web_view_evaluate_javascript(c->view, script, -1,
+ NULL, NULL, NULL, NULL, NULL);
+ g_free(script);
+}
+
+void
+updatewinid(Client *c)
+{
+ snprintf(winid, LENGTH(winid), "%lu", c->xid);
+}
+
+void
+handleplumb(Client *c, const char *uri)
+{
+ Arg a = (Arg)PLUMB(uri);
+ spawn(c, &a);
+}
+
+void
+newwindow(Client *c, const Arg *a, int noembed)
+{
+ int i = 0;
+ char tmp[64];
+ const char *cmd[29], *uri;
+ const Arg arg = { .v = cmd };
+
+ cmd[i++] = argv0;
+ cmd[i++] = "-a";
+ cmd[i++] = curconfig[CookiePolicies].val.v;
+ cmd[i++] = curconfig[ScrollBars].val.i ? "-B" : "-b";
+ if (cookiefile && g_strcmp0(cookiefile, "")) {
+ cmd[i++] = "-c";
+ cmd[i++] = cookiefile;
+ }
+ if (stylefile && g_strcmp0(stylefile, "")) {
+ cmd[i++] = "-C";
+ cmd[i++] = stylefile;
+ }
+ cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d";
+ if (embed && !noembed) {
+ cmd[i++] = "-e";
+ snprintf(tmp, LENGTH(tmp), "%lu", embed);
+ cmd[i++] = tmp;
+ }
+ cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ;
+ cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ;
+ cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ;
+ cmd[i++] = curconfig[KioskMode].val.i ? "-K" : "-k" ;
+ cmd[i++] = curconfig[Style].val.i ? "-M" : "-m" ;
+ cmd[i++] = curconfig[Inspector].val.i ? "-N" : "-n" ;
+ if (scriptfile && g_strcmp0(scriptfile, "")) {
+ cmd[i++] = "-r";
+ cmd[i++] = scriptfile;
+ }
+ cmd[i++] = curconfig[JavaScript].val.i ? "-S" : "-s";
+ cmd[i++] = curconfig[StrictTLS].val.i ? "-T" : "-t";
+ if (fulluseragent && g_strcmp0(fulluseragent, "")) {
+ cmd[i++] = "-u";
+ cmd[i++] = fulluseragent;
+ }
+ if (showxid)
+ cmd[i++] = "-w";
+ cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ;
+ /* do not keep zoom level */
+ cmd[i++] = "--";
+ if ((uri = a->v))
+ cmd[i++] = uri;
+ cmd[i] = NULL;
+
+ spawn(c, &arg);
+}
+
+void
+spawn(Client *c, const Arg *a)
+{
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ close(spair[0]);
+ close(spair[1]);
+ setsid();
+ execvp(((char **)a->v)[0], (char **)a->v);
+ fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]);
+ perror(" failed");
+ exit(1);
+ }
+}
+
+void
+destroyclient(Client *c)
+{
+ Client *p;
+
+ webkit_web_view_stop_loading(c->view);
+ /* Not needed, has already been called
+ gtk_widget_destroy(c->win);
+ */
+
+ for (p = clients; p && p->next != c; p = p->next)
+ ;
+ if (p)
+ p->next = c->next;
+ else
+ clients = c->next;
+ free(c);
+}
+
+void
+cleanup(void)
+{
+ while (clients)
+ destroyclient(clients);
+
+ close(spair[0]);
+ close(spair[1]);
+ g_free(cookiefile);
+ g_free(scriptfile);
+ g_free(stylefile);
+ g_free(cachedir);
+ XCloseDisplay(dpy);
+}
+
+WebKitWebView *
+newview(Client *c, WebKitWebView *rv)
+{
+ WebKitWebView *v;
+ WebKitSettings *settings;
+ WebKitWebContext *context;
+ WebKitCookieManager *cookiemanager;
+ WebKitUserContentManager *contentmanager;
+
+ /* Webview */
+ if (rv) {
+ v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv));
+ context = webkit_web_view_get_context(v);
+ settings = webkit_web_view_get_settings(v);
+ } else {
+ settings = webkit_settings_new_with_settings(
+ "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
+ "allow-universal-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
+ "auto-load-images", curconfig[LoadImages].val.i,
+ "default-charset", curconfig[DefaultCharset].val.v,
+ "default-font-size", curconfig[FontSize].val.i,
+ "enable-caret-browsing", curconfig[CaretBrowsing].val.i,
+ "enable-developer-extras", curconfig[Inspector].val.i,
+ "enable-dns-prefetching", curconfig[DNSPrefetch].val.i,
+ "enable-html5-database", curconfig[DiskCache].val.i,
+ "enable-html5-local-storage", curconfig[DiskCache].val.i,
+ "enable-javascript", curconfig[JavaScript].val.i,
+ "enable-site-specific-quirks", curconfig[SiteQuirks].val.i,
+ "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i,
+ "enable-webgl", curconfig[WebGL].val.i,
+ "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i,
+ NULL);
+/* For more interesting settings, have a look at
+ * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */
+
+ if (strcmp(fulluseragent, "")) {
+ webkit_settings_set_user_agent(settings, fulluseragent);
+ } else if (surfuseragent) {
+ webkit_settings_set_user_agent_with_application_details(
+ settings, "Surf", VERSION);
+ }
+ useragent = webkit_settings_get_user_agent(settings);
+
+ contentmanager = webkit_user_content_manager_new();
+
+ if (curconfig[Ephemeral].val.i) {
+ context = webkit_web_context_new_ephemeral();
+ } else {
+ context = webkit_web_context_new_with_website_data_manager(
+ webkit_website_data_manager_new(
+ "base-cache-directory", cachedir,
+ "base-data-directory", cachedir,
+ NULL));
+ }
+
+ cookiemanager = webkit_web_context_get_cookie_manager(context);
+
+ /* TLS */
+ webkit_website_data_manager_set_tls_errors_policy(
+ webkit_web_context_get_website_data_manager(context),
+ curconfig[StrictTLS].val.i ? WEBKIT_TLS_ERRORS_POLICY_FAIL :
+ WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ /* disk cache */
+ webkit_web_context_set_cache_model(context,
+ curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER :
+ WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+
+ /* Currently only works with text file to be compatible with curl */
+ if (!curconfig[Ephemeral].val.i)
+ webkit_cookie_manager_set_persistent_storage(cookiemanager,
+ cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
+ /* cookie policy */
+ webkit_cookie_manager_set_accept_policy(cookiemanager,
+ cookiepolicy_get());
+ /* languages */
+ webkit_web_context_set_preferred_languages(context,
+ curconfig[PreferredLanguages].val.v);
+ webkit_web_context_set_spell_checking_languages(context,
+ curconfig[SpellLanguages].val.v);
+ webkit_web_context_set_spell_checking_enabled(context,
+ curconfig[SpellChecking].val.i);
+
+ g_signal_connect(G_OBJECT(context), "download-started",
+ G_CALLBACK(downloadstarted), c);
+ g_signal_connect(G_OBJECT(context), "initialize-web-extensions",
+ G_CALLBACK(initwebextensions), c);
+
+ v = g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "settings", settings,
+ "user-content-manager", contentmanager,
+ "web-context", context,
+ NULL);
+ }
+
+ g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress",
+ G_CALLBACK(progresschanged), c);
+ g_signal_connect(G_OBJECT(v), "notify::title",
+ G_CALLBACK(titlechanged), c);
+ g_signal_connect(G_OBJECT(v), "button-release-event",
+ G_CALLBACK(buttonreleased), c);
+ g_signal_connect(G_OBJECT(v), "close",
+ G_CALLBACK(closeview), c);
+ g_signal_connect(G_OBJECT(v), "create",
+ G_CALLBACK(createview), c);
+ g_signal_connect(G_OBJECT(v), "decide-policy",
+ G_CALLBACK(decidepolicy), c);
+ g_signal_connect(G_OBJECT(v), "insecure-content-detected",
+ G_CALLBACK(insecurecontent), c);
+ g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors",
+ G_CALLBACK(loadfailedtls), c);
+ g_signal_connect(G_OBJECT(v), "load-changed",
+ G_CALLBACK(loadchanged), c);
+ g_signal_connect(G_OBJECT(v), "mouse-target-changed",
+ G_CALLBACK(mousetargetchanged), c);
+ g_signal_connect(G_OBJECT(v), "permission-request",
+ G_CALLBACK(permissionrequested), c);
+ g_signal_connect(G_OBJECT(v), "ready-to-show",
+ G_CALLBACK(showview), c);
+ g_signal_connect(G_OBJECT(v), "user-message-received",
+ G_CALLBACK(viewusrmsgrcv), c);
+ g_signal_connect(G_OBJECT(v), "web-process-terminated",
+ G_CALLBACK(webprocessterminated), c);
+
+ c->context = context;
+ c->settings = settings;
+
+ setparameter(c, 0, DarkMode, &curconfig[DarkMode].val);
+
+ return v;
+}
+
+void
+initwebextensions(WebKitWebContext *wc, Client *c)
+{
+ webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR);
+}
+
+GtkWidget *
+createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c)
+{
+ Client *n;
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ /*
+ * popup windows of type “other” are almost always triggered
+ * by user gesture, so inverse the logic here
+ */
+/* instead of this, compare destination uri to mouse-over uri for validating window */
+ if (webkit_navigation_action_is_user_gesture(a))
+ return NULL;
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
+ n = newclient(c);
+ break;
+ default:
+ return NULL;
+ }
+
+ return GTK_WIDGET(n->view);
+}
+
+gboolean
+buttonreleased(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ WebKitHitTestResultContext element;
+ int i;
+
+ element = webkit_hit_test_result_get_context(c->mousepos);
+
+ for (i = 0; i < LENGTH(buttons); ++i) {
+ if (element & buttons[i].target &&
+ e->button.button == buttons[i].button &&
+ CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) &&
+ buttons[i].func) {
+ buttons[i].func(c, &buttons[i].arg, c->mousepos);
+ return buttons[i].stopevent;
+ }
+ }
+
+ return FALSE;
+}
+
+GdkFilterReturn
+processx(GdkXEvent *e, GdkEvent *event, gpointer d)
+{
+ Client *c = (Client *)d;
+ XPropertyEvent *ev;
+ Arg a;
+
+ if (((XEvent *)e)->type == PropertyNotify) {
+ ev = &((XEvent *)e)->xproperty;
+ if (ev->state == PropertyNewValue) {
+ if (ev->atom == atoms[AtomFind]) {
+ find(c, NULL);
+
+ return GDK_FILTER_REMOVE;
+ } else if (ev->atom == atoms[AtomGo]) {
+ a.v = getatom(c, AtomGo);
+ loaduri(c, &a);
+
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+gboolean
+winevent(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ int i;
+
+ switch (e->type) {
+ case GDK_ENTER_NOTIFY:
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+ break;
+ case GDK_KEY_PRESS:
+ if (!curconfig[KioskMode].val.i &&
+ !insertmode ||
+ CLEANMASK(e->key.state) == (MODKEY|GDK_SHIFT_MASK) ||
+ CLEANMASK(e->key.state) == (MODKEY) ||
+ gdk_keyval_to_lower(e->key.keyval) == (GDK_KEY_Escape)) {
+ for (i = 0; i < LENGTH(keys); ++i) {
+ if (gdk_keyval_to_lower(e->key.keyval) ==
+ keys[i].keyval &&
+ CLEANMASK(e->key.state) == keys[i].mod &&
+ keys[i].func) {
+ updatewinid(c);
+ keys[i].func(c, &(keys[i].arg));
+ return TRUE;
+ }
+ }
+ }
+ case GDK_LEAVE_NOTIFY:
+ c->overtitle = NULL;
+ updatetitle(c);
+ break;
+ case GDK_WINDOW_STATE:
+ if (e->window_state.changed_mask ==
+ GDK_WINDOW_STATE_FULLSCREEN)
+ c->fullscreen = e->window_state.new_window_state &
+ GDK_WINDOW_STATE_FULLSCREEN;
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+void
+showview(WebKitWebView *v, Client *c)
+{
+ GdkRGBA bgcolor = { 0 };
+ GdkWindow *gwin;
+
+ c->finder = webkit_web_view_get_find_controller(c->view);
+ c->inspector = webkit_web_view_get_inspector(c->view);
+
+ c->pageid = webkit_web_view_get_page_id(c->view);
+ c->win = createwindow(c);
+
+ gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
+ gtk_widget_show_all(c->win);
+ gtk_widget_grab_focus(GTK_WIDGET(c->view));
+
+ gwin = gtk_widget_get_window(GTK_WIDGET(c->win));
+ c->xid = gdk_x11_window_get_xid(gwin);
+ updatewinid(c);
+ if (showxid) {
+ gdk_display_sync(gtk_widget_get_display(c->win));
+ puts(winid);
+ fflush(stdout);
+ }
+
+ if (curconfig[HideBackground].val.i)
+ webkit_web_view_set_background_color(c->view, &bgcolor);
+
+ if (!curconfig[KioskMode].val.i) {
+ gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
+ gdk_window_add_filter(gwin, processx, c);
+ }
+
+ if (curconfig[RunInFullscreen].val.i)
+ togglefullscreen(c, NULL);
+
+ if (curconfig[ZoomLevel].val.f != 1.0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f);
+
+ setatom(c, AtomFind, "");
+ setatom(c, AtomUri, "about:blank");
+}
+
+GtkWidget *
+createwindow(Client *c)
+{
+ char *wmstr;
+ GtkWidget *w;
+
+ if (embed) {
+ w = gtk_plug_new(embed);
+ } else {
+ w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ wmstr = g_path_get_basename(argv0);
+ gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf");
+ g_free(wmstr);
+
+ wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid);
+ gtk_window_set_role(GTK_WINDOW(w), wmstr);
+ g_free(wmstr);
+
+ gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]);
+ }
+
+ g_signal_connect(G_OBJECT(w), "destroy",
+ G_CALLBACK(destroywin), c);
+ g_signal_connect(G_OBJECT(w), "enter-notify-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "key-press-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "leave-notify-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "window-state-event",
+ G_CALLBACK(winevent), c);
+
+ return w;
+}
+
+gboolean
+loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c)
+{
+ GString *errmsg = g_string_new(NULL);
+ gchar *html, *pem;
+
+ c->failedcert = g_object_ref(cert);
+ c->tlserr = err;
+ c->errorpage = 1;
+
+ if (err & G_TLS_CERTIFICATE_UNKNOWN_CA)
+ g_string_append(errmsg,
+ "The signing certificate authority is not known.<br>");
+ if (err & G_TLS_CERTIFICATE_BAD_IDENTITY)
+ g_string_append(errmsg,
+ "The certificate does not match the expected identity "
+ "of the site that it was retrieved from.<br>");
+ if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED)
+ g_string_append(errmsg,
+ "The certificate's activation time "
+ "is still in the future.<br>");
+ if (err & G_TLS_CERTIFICATE_EXPIRED)
+ g_string_append(errmsg, "The certificate has expired.<br>");
+ if (err & G_TLS_CERTIFICATE_REVOKED)
+ g_string_append(errmsg,
+ "The certificate has been revoked according to "
+ "the GTlsConnection's certificate revocation list.<br>");
+ if (err & G_TLS_CERTIFICATE_INSECURE)
+ g_string_append(errmsg,
+ "The certificate's algorithm is considered insecure.<br>");
+ if (err & G_TLS_CERTIFICATE_GENERIC_ERROR)
+ g_string_append(errmsg,
+ "Some error occurred validating the certificate.<br>");
+
+ g_object_get(cert, "certificate-pem", &pem, NULL);
+ html = g_strdup_printf("<p>Could not validate TLS for “%s”<br>%s</p>"
+ "<p>You can inspect the following certificate "
+ "with Ctrl-t (default keybinding).</p>"
+ "<p><pre>%s</pre></p>", uri, errmsg->str, pem);
+ g_free(pem);
+ g_string_free(errmsg, TRUE);
+
+ webkit_web_view_load_alternate_html(c->view, html, uri, NULL);
+ g_free(html);
+
+ return TRUE;
+}
+
+void
+loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
+{
+ const char *uri = geturi(c);
+
+ switch (e) {
+ case WEBKIT_LOAD_STARTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ c->https = c->insecure = 0;
+ seturiparameters(c, uri, loadtransient);
+ if (c->errorpage)
+ c->errorpage = 0;
+ else
+ g_clear_object(&c->failedcert);
+ break;
+ case WEBKIT_LOAD_REDIRECTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ seturiparameters(c, uri, loadtransient);
+ break;
+ case WEBKIT_LOAD_COMMITTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ seturiparameters(c, uri, loadcommitted);
+ c->https = webkit_web_view_get_tls_info(c->view, &c->cert,
+ &c->tlserr);
+ break;
+ case WEBKIT_LOAD_FINISHED:
+ seturiparameters(c, uri, loadfinished);
+ /* Disabled until we write some WebKitWebExtension for
+ * manipulating the DOM directly.
+ evalscript(c, "document.documentElement.style.overflow = '%s'",
+ enablescrollbars ? "auto" : "hidden");
+ */
+ runscript(c);
+ break;
+ }
+ updatetitle(c);
+}
+
+void
+progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c)
+{
+ c->progress = webkit_web_view_get_estimated_load_progress(c->view) *
+ 100;
+ updatetitle(c);
+}
+
+void
+titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c)
+{
+ c->title = webkit_web_view_get_title(c->view);
+ updatetitle(c);
+}
+
+gboolean
+viewusrmsgrcv(WebKitWebView *v, WebKitUserMessage *m, gpointer unused)
+{
+ WebKitUserMessage *r;
+ GUnixFDList *gfd;
+ const char *name;
+
+ name = webkit_user_message_get_name(m);
+ if (strcmp(name, "page-created") != 0) {
+ fprintf(stderr, "surf: Unknown UserMessage: %s\n", name);
+ return TRUE;
+ }
+
+ if (spair[1] < 0)
+ return TRUE;
+
+ gfd = g_unix_fd_list_new_from_array(&spair[1], 1);
+ r = webkit_user_message_new_with_fd_list("surf-pipe", NULL, gfd);
+
+ webkit_user_message_send_reply(m, r);
+
+ return TRUE;
+}
+
+void
+mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
+ Client *c)
+{
+ WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h);
+
+ /* Keep the hit test to know where is the pointer on the next click */
+ c->mousepos = h;
+
+ if (hc & OnLink)
+ c->targeturi = webkit_hit_test_result_get_link_uri(h);
+ else if (hc & OnImg)
+ c->targeturi = webkit_hit_test_result_get_image_uri(h);
+ else if (hc & OnMedia)
+ c->targeturi = webkit_hit_test_result_get_media_uri(h);
+ else
+ c->targeturi = NULL;
+
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+}
+
+gboolean
+permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c)
+{
+ ParamName param = ParameterLast;
+
+ if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) {
+ param = Geolocation;
+ } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(r)) {
+ if (webkit_user_media_permission_is_for_audio_device(
+ WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r)))
+ param = AccessMicrophone;
+ else if (webkit_user_media_permission_is_for_video_device(
+ WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r)))
+ param = AccessWebcam;
+ } else {
+ return FALSE;
+ }
+
+ if (curconfig[param].val.i)
+ webkit_permission_request_allow(r);
+ else
+ webkit_permission_request_deny(r);
+
+ return TRUE;
+}
+
+gboolean
+decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
+ WebKitPolicyDecisionType dt, Client *c)
+{
+ switch (dt) {
+ case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
+ decidenavigation(d, c);
+ break;
+ case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
+ decidenewwindow(d, c);
+ break;
+ case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
+ decideresource(d, c);
+ break;
+ default:
+ webkit_policy_decision_ignore(d);
+ break;
+ }
+ return TRUE;
+}
+
+void
+decidenavigation(WebKitPolicyDecision *d, Client *c)
+{
+ WebKitNavigationAction *a =
+ webkit_navigation_policy_decision_get_navigation_action(
+ WEBKIT_NAVIGATION_POLICY_DECISION(d));
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ default:
+ /* Do not navigate to links with a "_blank" target (popup) */
+ if (webkit_navigation_action_get_frame_name(a)) {
+ webkit_policy_decision_ignore(d);
+ } else {
+ /* Filter out navigation to different domain ? */
+ /* get action→urirequest, copy and load in new window+view
+ * on Ctrl+Click ? */
+ webkit_policy_decision_use(d);
+ }
+ break;
+ }
+}
+
+void
+decidenewwindow(WebKitPolicyDecision *d, Client *c)
+{
+ Arg arg;
+ WebKitNavigationAction *a =
+ webkit_navigation_policy_decision_get_navigation_action(
+ WEBKIT_NAVIGATION_POLICY_DECISION(d));
+
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
+ /* Filter domains here */
+/* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
+ * test for link clicked but no button ? */
+ arg.v = webkit_uri_request_get_uri(
+ webkit_navigation_action_get_request(a));
+ newwindow(c, &arg, 0);
+ break;
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ default:
+ break;
+ }
+
+ webkit_policy_decision_ignore(d);
+}
+
+void
+decideresource(WebKitPolicyDecision *d, Client *c)
+{
+ int i, isascii = 1;
+ WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d);
+ WebKitURIResponse *res =
+ webkit_response_policy_decision_get_response(r);
+ const gchar *uri = webkit_uri_response_get_uri(res);
+
+ if (g_str_has_suffix(uri, "/favicon.ico")) {
+ webkit_policy_decision_ignore(d);
+ return;
+ }
+
+ if (!g_str_has_prefix(uri, "http://")
+ && !g_str_has_prefix(uri, "https://")
+ && !g_str_has_prefix(uri, "about:")
+ && !g_str_has_prefix(uri, "file://")
+ && !g_str_has_prefix(uri, "webkit://")
+ && !g_str_has_prefix(uri, "data:")
+ && !g_str_has_prefix(uri, "blob:")
+ && strlen(uri) > 0) {
+ for (i = 0; i < strlen(uri); i++) {
+ if (!g_ascii_isprint(uri[i])) {
+ isascii = 0;
+ break;
+ }
+ }
+ if (isascii) {
+ handleplumb(c, uri);
+ webkit_policy_decision_ignore(d);
+ return;
+ }
+ }
+
+ if (webkit_response_policy_decision_is_mime_type_supported(r)) {
+ webkit_policy_decision_use(d);
+ } else {
+ webkit_policy_decision_ignore(d);
+ download(c, res);
+ }
+}
+
+void
+insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c)
+{
+ c->insecure = 1;
+}
+
+void
+downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c)
+{
+ g_signal_connect(G_OBJECT(d), "notify::response",
+ G_CALLBACK(responsereceived), c);
+}
+
+void
+responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c)
+{
+ download(c, webkit_download_get_response(d));
+ webkit_download_cancel(d);
+}
+
+void
+download(Client *c, WebKitURIResponse *r)
+{
+ Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c));
+ spawn(c, &a);
+}
+
+void
+webprocessterminated(WebKitWebView *v, WebKitWebProcessTerminationReason r,
+ Client *c)
+{
+ fprintf(stderr, "web process terminated: %s\n",
+ r == WEBKIT_WEB_PROCESS_CRASHED ? "crashed" : "no memory");
+ closeview(v, c);
+}
+
+void
+closeview(WebKitWebView *v, Client *c)
+{
+ gtk_widget_destroy(c->win);
+}
+
+void
+destroywin(GtkWidget* w, Client *c)
+{
+ destroyclient(c);
+ if (!clients)
+ gtk_main_quit();
+}
+
+void
+pasteuri(GtkClipboard *clipboard, const char *text, gpointer d)
+{
+ Arg a = {.v = text };
+ if (text)
+ loaduri((Client *) d, &a);
+}
+
+void
+reload(Client *c, const Arg *a)
+{
+ if (a->i)
+ webkit_web_view_reload_bypass_cache(c->view);
+ else
+ webkit_web_view_reload(c->view);
+}
+
+void
+print(Client *c, const Arg *a)
+{
+ webkit_print_operation_run_dialog(webkit_print_operation_new(c->view),
+ GTK_WINDOW(c->win));
+}
+
+void
+showcert(Client *c, const Arg *a)
+{
+ GTlsCertificate *cert = c->failedcert ? c->failedcert : c->cert;
+ GcrCertificate *gcrt;
+ GByteArray *crt;
+ GtkWidget *win;
+ GcrCertificateWidget *wcert;
+
+ if (!cert)
+ return;
+
+ g_object_get(cert, "certificate", &crt, NULL);
+ gcrt = gcr_simple_certificate_new(crt->data, crt->len);
+ g_byte_array_unref(crt);
+
+ win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ wcert = gcr_certificate_widget_new(gcrt);
+ g_object_unref(gcrt);
+
+ gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(wcert));
+ gtk_widget_show_all(win);
+}
+
+void
+clipboard(Client *c, const Arg *a)
+{
+ if (a->i) { /* load clipboard uri */
+ gtk_clipboard_request_text(gtk_clipboard_get(
+ GDK_SELECTION_PRIMARY),
+ pasteuri, c);
+ } else { /* copy uri */
+ gtk_clipboard_set_text(gtk_clipboard_get(
+ GDK_SELECTION_PRIMARY), c->targeturi
+ ? c->targeturi : geturi(c), -1);
+ }
+}
+
+void
+zoom(Client *c, const Arg *a)
+{
+ if (a->i > 0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f + 0.1);
+ else if (a->i < 0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f - 0.1);
+ else
+ webkit_web_view_set_zoom_level(c->view, 1.0);
+
+ curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view);
+}
+
+static void
+msgext(Client *c, char type, const Arg *a)
+{
+ static unsigned char msg[MSGBUFSZ];
+ int ret;
+
+ if (spair[0] < 0)
+ return;
+
+ ret = snprintf(msg, sizeof(msg), "%c%c%c",
+ (unsigned char)c->pageid, type, (signed char)a->i);
+ if (ret >= sizeof(msg)) {
+ fprintf(stderr, "surf: message too long: %d\n", ret);
+ return;
+ }
+
+ if (send(spair[0], msg, ret, 0) != ret)
+ fprintf(stderr, "surf: error sending: %hhu/%c/%d (%d)\n",
+ (unsigned char)c->pageid, type, a->i, ret);
+}
+
+void
+scrollv(Client *c, const Arg *a)
+{
+ msgext(c, 'v', a);
+}
+
+void
+scrollh(Client *c, const Arg *a)
+{
+ msgext(c, 'h', a);
+}
+
+void
+navigate(Client *c, const Arg *a)
+{
+ if (a->i < 0)
+ webkit_web_view_go_back(c->view);
+ else if (a->i > 0)
+ webkit_web_view_go_forward(c->view);
+}
+
+void
+stop(Client *c, const Arg *a)
+{
+ webkit_web_view_stop_loading(c->view);
+}
+
+void
+toggle(Client *c, const Arg *a)
+{
+ curconfig[a->i].val.i ^= 1;
+ setparameter(c, 1, (ParamName)a->i, &curconfig[a->i].val);
+}
+
+void
+togglefullscreen(Client *c, const Arg *a)
+{
+ /* toggling value is handled in winevent() */
+ if (c->fullscreen)
+ gtk_window_unfullscreen(GTK_WINDOW(c->win));
+ else
+ gtk_window_fullscreen(GTK_WINDOW(c->win));
+}
+
+void
+togglecookiepolicy(Client *c, const Arg *a)
+{
+ ++cookiepolicy;
+ cookiepolicy %= strlen(curconfig[CookiePolicies].val.v);
+
+ setparameter(c, 0, CookiePolicies, NULL);
+}
+
+void
+toggleinspector(Client *c, const Arg *a)
+{
+ if (webkit_web_inspector_is_attached(c->inspector))
+ webkit_web_inspector_close(c->inspector);
+ else if (curconfig[Inspector].val.i)
+ webkit_web_inspector_show(c->inspector);
+}
+
+void
+find(Client *c, const Arg *a)
+{
+ const char *s, *f;
+
+ if (a && a->i) {
+ if (a->i > 0)
+ webkit_find_controller_search_next(c->finder);
+ else
+ webkit_find_controller_search_previous(c->finder);
+ } else {
+ s = getatom(c, AtomFind);
+ f = webkit_find_controller_get_search_text(c->finder);
+
+ if (g_strcmp0(f, s) == 0) /* reset search */
+ webkit_find_controller_search(c->finder, "", findopts,
+ G_MAXUINT);
+
+ webkit_find_controller_search(c->finder, s, findopts,
+ G_MAXUINT);
+
+ if (strcmp(s, "") == 0)
+ webkit_find_controller_search_finish(c->finder);
+ }
+}
+
+void
+insert(Client *c, const Arg *a)
+{
+ insertmode = (a->i);
+}
+
+void
+clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h)
+{
+ navigate(c, a);
+}
+
+void
+clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h)
+{
+ Arg arg;
+
+ arg.v = webkit_hit_test_result_get_link_uri(h);
+ newwindow(c, &arg, a->i);
+}
+
+void
+clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h)
+{
+ Arg arg;
+
+ arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h));
+ spawn(c, &arg);
+}
+
+int
+main(int argc, char *argv[])
+{
+ Arg arg;
+ Client *c;
+
+ memset(&arg, 0, sizeof(arg));
+
+ /* command line args */
+ ARGBEGIN {
+ case 'a':
+ defconfig[CookiePolicies].val.v = EARGF(usage());
+ defconfig[CookiePolicies].prio = 2;
+ break;
+ case 'b':
+ defconfig[ScrollBars].val.i = 0;
+ defconfig[ScrollBars].prio = 2;
+ break;
+ case 'B':
+ defconfig[ScrollBars].val.i = 1;
+ defconfig[ScrollBars].prio = 2;
+ break;
+ case 'c':
+ cookiefile = EARGF(usage());
+ break;
+ case 'C':
+ stylefile = EARGF(usage());
+ break;
+ case 'd':
+ defconfig[DiskCache].val.i = 0;
+ defconfig[DiskCache].prio = 2;
+ break;
+ case 'D':
+ defconfig[DiskCache].val.i = 1;
+ defconfig[DiskCache].prio = 2;
+ break;
+ case 'e':
+ embed = strtol(EARGF(usage()), NULL, 0);
+ break;
+ case 'f':
+ defconfig[RunInFullscreen].val.i = 0;
+ defconfig[RunInFullscreen].prio = 2;
+ break;
+ case 'F':
+ defconfig[RunInFullscreen].val.i = 1;
+ defconfig[RunInFullscreen].prio = 2;
+ break;
+ case 'g':
+ defconfig[Geolocation].val.i = 0;
+ defconfig[Geolocation].prio = 2;
+ break;
+ case 'G':
+ defconfig[Geolocation].val.i = 1;
+ defconfig[Geolocation].prio = 2;
+ break;
+ case 'i':
+ defconfig[LoadImages].val.i = 0;
+ defconfig[LoadImages].prio = 2;
+ break;
+ case 'I':
+ defconfig[LoadImages].val.i = 1;
+ defconfig[LoadImages].prio = 2;
+ break;
+ case 'k':
+ defconfig[KioskMode].val.i = 0;
+ defconfig[KioskMode].prio = 2;
+ break;
+ case 'K':
+ defconfig[KioskMode].val.i = 1;
+ defconfig[KioskMode].prio = 2;
+ break;
+ case 'm':
+ defconfig[Style].val.i = 0;
+ defconfig[Style].prio = 2;
+ break;
+ case 'M':
+ defconfig[Style].val.i = 1;
+ defconfig[Style].prio = 2;
+ break;
+ case 'n':
+ defconfig[Inspector].val.i = 0;
+ defconfig[Inspector].prio = 2;
+ break;
+ case 'N':
+ defconfig[Inspector].val.i = 1;
+ defconfig[Inspector].prio = 2;
+ break;
+ case 'r':
+ scriptfile = EARGF(usage());
+ break;
+ case 's':
+ defconfig[JavaScript].val.i = 0;
+ defconfig[JavaScript].prio = 2;
+ break;
+ case 'S':
+ defconfig[JavaScript].val.i = 1;
+ defconfig[JavaScript].prio = 2;
+ break;
+ case 't':
+ defconfig[StrictTLS].val.i = 0;
+ defconfig[StrictTLS].prio = 2;
+ break;
+ case 'T':
+ defconfig[StrictTLS].val.i = 1;
+ defconfig[StrictTLS].prio = 2;
+ break;
+ case 'u':
+ fulluseragent = EARGF(usage());
+ break;
+ case 'v':
+ die("surf-"VERSION", see LICENSE for © details\n");
+ case 'w':
+ showxid = 1;
+ break;
+ case 'x':
+ defconfig[Certificate].val.i = 0;
+ defconfig[Certificate].prio = 2;
+ break;
+ case 'X':
+ defconfig[Certificate].val.i = 1;
+ defconfig[Certificate].prio = 2;
+ break;
+ case 'z':
+ defconfig[ZoomLevel].val.f = strtof(EARGF(usage()), NULL);
+ defconfig[ZoomLevel].prio = 2;
+ break;
+ default:
+ usage();
+ } ARGEND;
+ if (argc > 0)
+ arg.v = argv[0];
+ else
+ arg.v = "about:blank";
+
+ setup();
+ c = newclient(NULL);
+ showview(NULL, c);
+
+ loaduri(c, &arg);
+ updatetitle(c);
+
+ gtk_main();
+ cleanup();
+
+ return 0;
+}
diff --git a/keep/manual-programs/suckless/surf/surf.c.orig b/keep/manual-programs/suckless/surf/surf.c.orig
new file mode 100644
index 0000000..594a658
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf.c.orig
@@ -0,0 +1,2143 @@
+/* See LICENSE file for copyright and license details.
+ *
+ * To understand surf, start reading main().
+ */
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <limits.h>
+#include <pwd.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <gio/gunixfdlist.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
+#include <gcr/gcr.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <webkit2/webkit2.h>
+#include <X11/X.h>
+#include <X11/Xatom.h>
+#include <glib.h>
+
+#include "arg.h"
+#include "common.h"
+
+#define LENGTH(x) (sizeof(x) / sizeof(x[0]))
+#define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK))
+
+enum { AtomFind, AtomGo, AtomUri, AtomUTF8, AtomLast };
+
+enum {
+ OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
+ OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK,
+ OnImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE,
+ OnMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA,
+ OnEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE,
+ OnBar = WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR,
+ OnSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION,
+ OnAny = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel,
+};
+
+typedef enum {
+ AccessMicrophone,
+ AccessWebcam,
+ CaretBrowsing,
+ Certificate,
+ CookiePolicies,
+ DarkMode,
+ DiskCache,
+ DefaultCharset,
+ DNSPrefetch,
+ Ephemeral,
+ FileURLsCrossAccess,
+ FontSize,
+ Geolocation,
+ HideBackground,
+ Inspector,
+ JavaScript,
+ KioskMode,
+ LoadImages,
+ MediaManualPlay,
+ PreferredLanguages,
+ RunInFullscreen,
+ ScrollBars,
+ ShowIndicators,
+ SiteQuirks,
+ SmoothScrolling,
+ SpellChecking,
+ SpellLanguages,
+ StrictTLS,
+ Style,
+ WebGL,
+ ZoomLevel,
+ ParameterLast
+} ParamName;
+
+typedef union {
+ int i;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ Arg val;
+ int prio;
+} Parameter;
+
+typedef struct Client {
+ GtkWidget *win;
+ WebKitWebView *view;
+ WebKitSettings *settings;
+ WebKitWebContext *context;
+ WebKitWebInspector *inspector;
+ WebKitFindController *finder;
+ WebKitHitTestResult *mousepos;
+ GTlsCertificate *cert, *failedcert;
+ GTlsCertificateFlags tlserr;
+ Window xid;
+ guint64 pageid;
+ int progress, fullscreen, https, insecure, errorpage;
+ const char *title, *overtitle, *targeturi;
+ const char *needle;
+ struct Client *next;
+} Client;
+
+typedef struct {
+ guint mod;
+ guint keyval;
+ void (*func)(Client *c, const Arg *a);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ unsigned int target;
+ unsigned int mask;
+ guint button;
+ void (*func)(Client *c, const Arg *a, WebKitHitTestResult *h);
+ const Arg arg;
+ unsigned int stopevent;
+} Button;
+
+typedef struct {
+ const char *uri;
+ Parameter config[ParameterLast];
+ regex_t re;
+} UriParameters;
+
+typedef struct {
+ char *regex;
+ char *file;
+ regex_t re;
+} SiteSpecific;
+
+typedef struct {
+ const char* alias;
+ const char* uri;
+} Alias;
+
+/* Surf */
+static void die(const char *errstr, ...);
+static void usage(void);
+static void setup(void);
+static void sigchld(int unused);
+static void sighup(int unused);
+static char *buildfile(const char *path);
+static char *buildpath(const char *path);
+static char *untildepath(const char *path);
+static const char *getuserhomedir(const char *user);
+static const char *getcurrentuserhomedir(void);
+static Client *newclient(Client *c);
+static void loaduri(Client *c, const Arg *a);
+static const char *geturi(Client *c);
+static void setatom(Client *c, int a, const char *v);
+static const char *getatom(Client *c, int a);
+static void updatetitle(Client *c);
+static void gettogglestats(Client *c);
+static void getpagestats(Client *c);
+static WebKitCookieAcceptPolicy cookiepolicy_get(void);
+static char cookiepolicy_set(const WebKitCookieAcceptPolicy p);
+static void seturiparameters(Client *c, const char *uri, ParamName *params);
+static void setparameter(Client *c, int refresh, ParamName p, const Arg *a);
+static const char *getcert(const char *uri);
+static void setcert(Client *c, const char *file);
+static const char *getstyle(const char *uri);
+static void setstyle(Client *c, const char *file);
+static void runscript(Client *c);
+static void evalscript(Client *c, const char *jsstr, ...);
+static void updatewinid(Client *c);
+static void handleplumb(Client *c, const char *uri);
+static void newwindow(Client *c, const Arg *a, int noembed);
+static void spawn(Client *c, const Arg *a);
+static void msgext(Client *c, char type, const Arg *a);
+static void destroyclient(Client *c);
+static void cleanup(void);
+
+/* GTK/WebKit */
+static WebKitWebView *newview(Client *c, WebKitWebView *rv);
+static void initwebextensions(WebKitWebContext *wc, Client *c);
+static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a,
+ Client *c);
+static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c);
+static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
+ gpointer d);
+static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
+static void showview(WebKitWebView *v, Client *c);
+static GtkWidget *createwindow(Client *c);
+static gboolean loadfailedtls(WebKitWebView *v, gchar *uri,
+ GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c);
+static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c);
+static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c);
+static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c);
+static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h,
+ guint modifiers, Client *c);
+static gboolean permissionrequested(WebKitWebView *v,
+ WebKitPermissionRequest *r, Client *c);
+static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
+ WebKitPolicyDecisionType dt, Client *c);
+static void decidenavigation(WebKitPolicyDecision *d, Client *c);
+static void decidenewwindow(WebKitPolicyDecision *d, Client *c);
+static void decideresource(WebKitPolicyDecision *d, Client *c);
+static void insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e,
+ Client *c);
+static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d,
+ Client *c);
+static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c);
+static void download(Client *c, WebKitURIResponse *r);
+static gboolean viewusrmsgrcv(WebKitWebView *v, WebKitUserMessage *m,
+ gpointer u);
+static void webprocessterminated(WebKitWebView *v,
+ WebKitWebProcessTerminationReason r,
+ Client *c);
+static void closeview(WebKitWebView *v, Client *c);
+static void destroywin(GtkWidget* w, Client *c);
+
+/* Hotkeys */
+static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
+static void reload(Client *c, const Arg *a);
+static void print(Client *c, const Arg *a);
+static void showcert(Client *c, const Arg *a);
+static void clipboard(Client *c, const Arg *a);
+static void zoom(Client *c, const Arg *a);
+static void scrollv(Client *c, const Arg *a);
+static void scrollh(Client *c, const Arg *a);
+static void navigate(Client *c, const Arg *a);
+static void stop(Client *c, const Arg *a);
+static void toggle(Client *c, const Arg *a);
+static void togglefullscreen(Client *c, const Arg *a);
+static void togglecookiepolicy(Client *c, const Arg *a);
+static void toggleinspector(Client *c, const Arg *a);
+static void find(Client *c, const Arg *a);
+
+/* Buttons */
+static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h);
+static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h);
+static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h);
+
+static char winid[64];
+static char togglestats[11];
+static char pagestats[2];
+static Atom atoms[AtomLast];
+static Window embed;
+static int showxid;
+static int cookiepolicy;
+static Display *dpy;
+static Client *clients;
+static GdkDevice *gdkkb;
+static char *stylefile;
+static const char *useragent;
+static Parameter *curconfig;
+static int modparams[ParameterLast];
+static int spair[2];
+char *argv0;
+
+static ParamName loadtransient[] = {
+ Certificate,
+ CookiePolicies,
+ DiskCache,
+ DNSPrefetch,
+ FileURLsCrossAccess,
+ JavaScript,
+ LoadImages,
+ PreferredLanguages,
+ ShowIndicators,
+ StrictTLS,
+ ParameterLast
+};
+
+static ParamName loadcommitted[] = {
+// AccessMicrophone,
+// AccessWebcam,
+ CaretBrowsing,
+ DarkMode,
+ DefaultCharset,
+ FontSize,
+ Geolocation,
+ HideBackground,
+ Inspector,
+// KioskMode,
+ MediaManualPlay,
+ RunInFullscreen,
+ ScrollBars,
+ SiteQuirks,
+ SmoothScrolling,
+ SpellChecking,
+ SpellLanguages,
+ Style,
+ ZoomLevel,
+ ParameterLast
+};
+
+static ParamName loadfinished[] = {
+ ParameterLast
+};
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+void
+die(const char *errstr, ...)
+{
+ va_list ap;
+
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+usage(void)
+{
+ die("usage: surf [-bBdDfFgGiIkKmMnNsStTvwxX]\n"
+ "[-a cookiepolicies ] [-c cookiefile] [-C stylefile] [-e xid]\n"
+ "[-r scriptfile] [-u useragent] [-z zoomlevel] [uri]\n");
+}
+
+void
+setup(void)
+{
+ GIOChannel *gchanin;
+ GdkDisplay *gdpy;
+ int i, j;
+
+ /* clean up any zombies immediately */
+ sigchld(0);
+ if (signal(SIGHUP, sighup) == SIG_ERR)
+ die("Can't install SIGHUP handler");
+
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("Can't open default display");
+
+ /* atoms */
+ atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
+ atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
+ atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
+ atoms[AtomUTF8] = XInternAtom(dpy, "UTF8_STRING", False);
+
+ gtk_init(NULL, NULL);
+
+ gdpy = gdk_display_get_default();
+
+ curconfig = defconfig;
+
+ /* dirs and files */
+ cookiefile = buildfile(cookiefile);
+ scriptfile = buildfile(scriptfile);
+ certdir = buildpath(certdir);
+ if (curconfig[Ephemeral].val.i)
+ cachedir = NULL;
+ else
+ cachedir = buildpath(cachedir);
+
+ gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy));
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) < 0) {
+ fputs("Unable to create sockets\n", stderr);
+ spair[0] = spair[1] = -1;
+ } else {
+ gchanin = g_io_channel_unix_new(spair[0]);
+ g_io_channel_set_encoding(gchanin, NULL, NULL);
+ g_io_channel_set_flags(gchanin, g_io_channel_get_flags(gchanin)
+ | G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_close_on_unref(gchanin, TRUE);
+ }
+
+
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) {
+ certs[i].file = g_strconcat(certdir, "/", certs[i].file,
+ NULL);
+ } else {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ certs[i].regex);
+ certs[i].regex = NULL;
+ }
+ }
+
+ if (!stylefile) {
+ styledir = buildpath(styledir);
+ for (i = 0; i < LENGTH(styles); ++i) {
+ if (!regcomp(&(styles[i].re), styles[i].regex,
+ REG_EXTENDED)) {
+ styles[i].file = g_strconcat(styledir, "/",
+ styles[i].file, NULL);
+ } else {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ styles[i].regex);
+ styles[i].regex = NULL;
+ }
+ }
+ g_free(styledir);
+ } else {
+ stylefile = buildfile(stylefile);
+ }
+
+ for (i = 0; i < LENGTH(uriparams); ++i) {
+ if (regcomp(&(uriparams[i].re), uriparams[i].uri,
+ REG_EXTENDED)) {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ uriparams[i].uri);
+ uriparams[i].uri = NULL;
+ continue;
+ }
+
+ /* copy default parameters with higher priority */
+ for (j = 0; j < ParameterLast; ++j) {
+ if (defconfig[j].prio >= uriparams[i].config[j].prio)
+ uriparams[i].config[j] = defconfig[j];
+ }
+ }
+}
+
+void
+sigchld(int unused)
+{
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("Can't install SIGCHLD handler");
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+}
+
+void
+sighup(int unused)
+{
+ Arg a = { .i = 0 };
+ Client *c;
+
+ for (c = clients; c; c = c->next)
+ reload(c, &a);
+}
+
+char *
+buildfile(const char *path)
+{
+ char *dname, *bname, *bpath, *fpath;
+ FILE *f;
+
+ dname = g_path_get_dirname(path);
+ bname = g_path_get_basename(path);
+
+ bpath = buildpath(dname);
+ g_free(dname);
+
+ fpath = g_build_filename(bpath, bname, NULL);
+ g_free(bpath);
+ g_free(bname);
+
+ if (!(f = fopen(fpath, "a")))
+ die("Could not open file: %s\n", fpath);
+
+ g_chmod(fpath, 0600); /* always */
+ fclose(f);
+
+ return fpath;
+}
+
+static const char*
+getuserhomedir(const char *user)
+{
+ struct passwd *pw = getpwnam(user);
+
+ if (!pw)
+ die("Can't get user %s login information.\n", user);
+
+ return pw->pw_dir;
+}
+
+static const char*
+getcurrentuserhomedir(void)
+{
+ const char *homedir;
+ const char *user;
+ struct passwd *pw;
+
+ homedir = getenv("HOME");
+ if (homedir)
+ return homedir;
+
+ user = getenv("USER");
+ if (user)
+ return getuserhomedir(user);
+
+ pw = getpwuid(getuid());
+ if (!pw)
+ die("Can't get current user home directory\n");
+
+ return pw->pw_dir;
+}
+
+char *
+buildpath(const char *path)
+{
+ char *apath, *fpath;
+
+ if (path[0] == '~')
+ apath = untildepath(path);
+ else
+ apath = g_strdup(path);
+
+ /* creating directory */
+ if (g_mkdir_with_parents(apath, 0700) < 0)
+ die("Could not access directory: %s\n", apath);
+
+ fpath = realpath(apath, NULL);
+ g_free(apath);
+
+ return fpath;
+}
+
+char *
+untildepath(const char *path)
+{
+ char *apath, *name, *p;
+ const char *homedir;
+
+ if (path[1] == '/' || path[1] == '\0') {
+ p = (char *)&path[1];
+ homedir = getcurrentuserhomedir();
+ } else {
+ if ((p = strchr(path, '/')))
+ name = g_strndup(&path[1], p - (path + 1));
+ else
+ name = g_strdup(&path[1]);
+
+ homedir = getuserhomedir(name);
+ g_free(name);
+ }
+ apath = g_build_filename(homedir, p, NULL);
+ return apath;
+}
+
+Client *
+newclient(Client *rc)
+{
+ Client *c;
+
+ if (!(c = calloc(1, sizeof(Client))))
+ die("Cannot malloc!\n");
+
+ c->next = clients;
+ clients = c;
+
+ c->progress = 100;
+ c->view = newview(c, rc ? rc->view : NULL);
+
+ return c;
+}
+
+void
+loaduri(Client *c, const Arg *a)
+{
+ struct stat st;
+ char *url, *path, *apath;
+ const char *uri = a->v;
+
+ if (g_strcmp0(uri, "") == 0)
+ return;
+
+ for (int i = 0; i < LENGTH(aliases); i++) {
+ if (strcmp(aliases[i].alias, uri) == 0) {
+ uri = aliases[i].uri;
+ break;
+ }
+ }
+
+ if (g_str_has_prefix(uri, "http://") ||
+ g_str_has_prefix(uri, "https://") ||
+ g_str_has_prefix(uri, "file://") ||
+ g_str_has_prefix(uri, "webkit://") ||
+ g_str_has_prefix(uri, "about:")) {
+ url = g_strdup(uri);
+ } else {
+ if (uri[0] == '~')
+ apath = untildepath(uri);
+ else
+ apath = (char *)uri;
+ if (!stat(apath, &st) && (path = realpath(apath, NULL))) {
+ url = g_strdup_printf("file://%s", path);
+ free(path);
+ } else {
+ url = g_strdup_printf("http://%s", uri);
+ }
+ if (apath != uri)
+ free(apath);
+ }
+
+ setatom(c, AtomUri, url);
+
+ if (strcmp(url, geturi(c)) == 0) {
+ reload(c, a);
+ } else {
+ webkit_web_view_load_uri(c->view, url);
+ updatetitle(c);
+ }
+
+ g_free(url);
+}
+
+const char *
+geturi(Client *c)
+{
+ const char *uri;
+
+ if (!(uri = webkit_web_view_get_uri(c->view)))
+ uri = "about:blank";
+ return uri;
+}
+
+void
+setatom(Client *c, int a, const char *v)
+{
+ XChangeProperty(dpy, c->xid,
+ atoms[a], atoms[AtomUTF8], 8, PropModeReplace,
+ (unsigned char *)v, strlen(v) + 1);
+ XSync(dpy, False);
+}
+
+const char *
+getatom(Client *c, int a)
+{
+ static char buf[BUFSIZ];
+ Atom adummy;
+ int idummy;
+ unsigned long ldummy;
+ unsigned char *p = NULL;
+
+ XSync(dpy, False);
+ XGetWindowProperty(dpy, c->xid,
+ atoms[a], 0L, BUFSIZ, False, atoms[AtomUTF8],
+ &adummy, &idummy, &ldummy, &ldummy, &p);
+ if (p)
+ strncpy(buf, (char *)p, LENGTH(buf) - 1);
+ else
+ buf[0] = '\0';
+ XFree(p);
+
+ return buf;
+}
+
+void
+updatetitle(Client *c)
+{
+ char *title;
+ const char *name = c->overtitle ? c->overtitle :
+ c->title ? c->title : "";
+
+ if (curconfig[ShowIndicators].val.i) {
+ gettogglestats(c);
+ getpagestats(c);
+
+ if (c->progress != 100)
+ title = g_strdup_printf("[%i%%] %s:%s | %s",
+ c->progress, togglestats, pagestats, name);
+ else
+ title = g_strdup_printf("%s:%s | %s",
+ togglestats, pagestats, name);
+
+ gtk_window_set_title(GTK_WINDOW(c->win), title);
+ g_free(title);
+ } else {
+ gtk_window_set_title(GTK_WINDOW(c->win), name);
+ }
+}
+
+void
+gettogglestats(Client *c)
+{
+ togglestats[0] = cookiepolicy_set(cookiepolicy_get());
+ togglestats[1] = curconfig[CaretBrowsing].val.i ? 'C' : 'c';
+ togglestats[2] = curconfig[Geolocation].val.i ? 'G' : 'g';
+ togglestats[3] = curconfig[DiskCache].val.i ? 'D' : 'd';
+ togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i';
+ togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's';
+ togglestats[6] = curconfig[Style].val.i ? 'M' : 'm';
+ togglestats[8] = curconfig[Certificate].val.i ? 'X' : 'x';
+ togglestats[9] = curconfig[StrictTLS].val.i ? 'T' : 't';
+}
+
+void
+getpagestats(Client *c)
+{
+ if (c->https)
+ pagestats[0] = (c->tlserr || c->insecure) ? 'U' : 'T';
+ else
+ pagestats[0] = '-';
+ pagestats[1] = '\0';
+}
+
+WebKitCookieAcceptPolicy
+cookiepolicy_get(void)
+{
+ switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) {
+ case 'a':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER;
+ case '@':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY;
+ default: /* fallthrough */
+ case 'A':
+ return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS;
+ }
+}
+
+char
+cookiepolicy_set(const WebKitCookieAcceptPolicy p)
+{
+ switch (p) {
+ case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER:
+ return 'a';
+ case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY:
+ return '@';
+ default: /* fallthrough */
+ case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS:
+ return 'A';
+ }
+}
+
+void
+seturiparameters(Client *c, const char *uri, ParamName *params)
+{
+ Parameter *config, *uriconfig = NULL;
+ int i, p;
+
+ for (i = 0; i < LENGTH(uriparams); ++i) {
+ if (uriparams[i].uri &&
+ !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) {
+ uriconfig = uriparams[i].config;
+ break;
+ }
+ }
+
+ curconfig = uriconfig ? uriconfig : defconfig;
+
+ for (i = 0; (p = params[i]) != ParameterLast; ++i) {
+ switch(p) {
+ default: /* FALLTHROUGH */
+ if (!(defconfig[p].prio < curconfig[p].prio ||
+ defconfig[p].prio < modparams[p]))
+ continue;
+ case Certificate:
+ case CookiePolicies:
+ case Style:
+ setparameter(c, 0, p, &curconfig[p].val);
+ }
+ }
+}
+
+void
+setparameter(Client *c, int refresh, ParamName p, const Arg *a)
+{
+ GdkRGBA bgcolor = { 0 };
+
+ modparams[p] = curconfig[p].prio;
+
+ switch (p) {
+ case AccessMicrophone:
+ return; /* do nothing */
+ case AccessWebcam:
+ return; /* do nothing */
+ case CaretBrowsing:
+ webkit_settings_set_enable_caret_browsing(c->settings, a->i);
+ refresh = 0;
+ break;
+ case Certificate:
+ if (a->i)
+ setcert(c, geturi(c));
+ return; /* do not update */
+ case CookiePolicies:
+ webkit_cookie_manager_set_accept_policy(
+ webkit_web_context_get_cookie_manager(c->context),
+ cookiepolicy_get());
+ refresh = 0;
+ break;
+ case DarkMode:
+ g_object_set(gtk_settings_get_default(),
+ "gtk-application-prefer-dark-theme", a->i, NULL);
+ return;
+ case DiskCache:
+ webkit_web_context_set_cache_model(c->context, a->i ?
+ WEBKIT_CACHE_MODEL_WEB_BROWSER :
+ WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+ return; /* do not update */
+ case DefaultCharset:
+ webkit_settings_set_default_charset(c->settings, a->v);
+ return; /* do not update */
+ case DNSPrefetch:
+ webkit_settings_set_enable_dns_prefetching(c->settings, a->i);
+ return; /* do not update */
+ case FileURLsCrossAccess:
+ webkit_settings_set_allow_file_access_from_file_urls(
+ c->settings, a->i);
+ webkit_settings_set_allow_universal_access_from_file_urls(
+ c->settings, a->i);
+ return; /* do not update */
+ case FontSize:
+ webkit_settings_set_default_font_size(c->settings, a->i);
+ return; /* do not update */
+ case Geolocation:
+ refresh = 0;
+ break;
+ case HideBackground:
+ if (a->i)
+ webkit_web_view_set_background_color(c->view, &bgcolor);
+ return; /* do not update */
+ case Inspector:
+ webkit_settings_set_enable_developer_extras(c->settings, a->i);
+ return; /* do not update */
+ case JavaScript:
+ webkit_settings_set_enable_javascript(c->settings, a->i);
+ break;
+ case KioskMode:
+ return; /* do nothing */
+ case LoadImages:
+ webkit_settings_set_auto_load_images(c->settings, a->i);
+ break;
+ case MediaManualPlay:
+ webkit_settings_set_media_playback_requires_user_gesture(
+ c->settings, a->i);
+ break;
+ case PreferredLanguages:
+ return; /* do nothing */
+ case RunInFullscreen:
+ return; /* do nothing */
+ case ScrollBars:
+ /* Disabled until we write some WebKitWebExtension for
+ * manipulating the DOM directly.
+ enablescrollbars = !enablescrollbars;
+ evalscript(c, "document.documentElement.style.overflow = '%s'",
+ enablescrollbars ? "auto" : "hidden");
+ */
+ return; /* do not update */
+ case ShowIndicators:
+ break;
+ case SmoothScrolling:
+ webkit_settings_set_enable_smooth_scrolling(c->settings, a->i);
+ return; /* do not update */
+ case SiteQuirks:
+ webkit_settings_set_enable_site_specific_quirks(
+ c->settings, a->i);
+ break;
+ case SpellChecking:
+ webkit_web_context_set_spell_checking_enabled(
+ c->context, a->i);
+ return; /* do not update */
+ case SpellLanguages:
+ return; /* do nothing */
+ case StrictTLS:
+ webkit_website_data_manager_set_tls_errors_policy(
+ webkit_web_view_get_website_data_manager(c->view), a->i ?
+ WEBKIT_TLS_ERRORS_POLICY_FAIL :
+ WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ break;
+ case Style:
+ webkit_user_content_manager_remove_all_style_sheets(
+ webkit_web_view_get_user_content_manager(c->view));
+ if (a->i)
+ setstyle(c, getstyle(geturi(c)));
+ refresh = 0;
+ break;
+ case WebGL:
+ webkit_settings_set_enable_webgl(c->settings, a->i);
+ break;
+ case ZoomLevel:
+ webkit_web_view_set_zoom_level(c->view, a->f);
+ return; /* do not update */
+ default:
+ return; /* do nothing */
+ }
+
+ updatetitle(c);
+ if (refresh)
+ reload(c, a);
+}
+
+const char *
+getcert(const char *uri)
+{
+ int i;
+
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (certs[i].regex &&
+ !regexec(&(certs[i].re), uri, 0, NULL, 0))
+ return certs[i].file;
+ }
+
+ return NULL;
+}
+
+void
+setcert(Client *c, const char *uri)
+{
+ const char *file = getcert(uri);
+ char *host;
+ GTlsCertificate *cert;
+
+ if (!file)
+ return;
+
+ if (!(cert = g_tls_certificate_new_from_file(file, NULL))) {
+ fprintf(stderr, "Could not read certificate file: %s\n", file);
+ return;
+ }
+
+ if ((uri = strstr(uri, "https://"))) {
+ uri += sizeof("https://") - 1;
+ host = g_strndup(uri, strchr(uri, '/') - uri);
+ webkit_web_context_allow_tls_certificate_for_host(c->context,
+ cert, host);
+ g_free(host);
+ }
+
+ g_object_unref(cert);
+
+}
+
+const char *
+getstyle(const char *uri)
+{
+ int i;
+
+ if (stylefile)
+ return stylefile;
+
+ for (i = 0; i < LENGTH(styles); ++i) {
+ if (styles[i].regex &&
+ !regexec(&(styles[i].re), uri, 0, NULL, 0))
+ return styles[i].file;
+ }
+
+ return "";
+}
+
+void
+setstyle(Client *c, const char *file)
+{
+ gchar *style;
+
+ if (!g_file_get_contents(file, &style, NULL, NULL)) {
+ fprintf(stderr, "Could not read style file: %s\n", file);
+ return;
+ }
+
+ webkit_user_content_manager_add_style_sheet(
+ webkit_web_view_get_user_content_manager(c->view),
+ webkit_user_style_sheet_new(style,
+ WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+ WEBKIT_USER_STYLE_LEVEL_USER,
+ NULL, NULL));
+
+ g_free(style);
+}
+
+void
+runscript(Client *c)
+{
+ gchar *script;
+ gsize l;
+
+ if (g_file_get_contents(scriptfile, &script, &l, NULL) && l)
+ evalscript(c, "%s", script);
+ g_free(script);
+}
+
+void
+evalscript(Client *c, const char *jsstr, ...)
+{
+ va_list ap;
+ gchar *script;
+
+ va_start(ap, jsstr);
+ script = g_strdup_vprintf(jsstr, ap);
+ va_end(ap);
+
+ webkit_web_view_evaluate_javascript(c->view, script, -1,
+ NULL, NULL, NULL, NULL, NULL);
+ g_free(script);
+}
+
+void
+updatewinid(Client *c)
+{
+ snprintf(winid, LENGTH(winid), "%lu", c->xid);
+}
+
+void
+handleplumb(Client *c, const char *uri)
+{
+ Arg a = (Arg)PLUMB(uri);
+ spawn(c, &a);
+}
+
+void
+newwindow(Client *c, const Arg *a, int noembed)
+{
+ int i = 0;
+ char tmp[64];
+ const char *cmd[29], *uri;
+ const Arg arg = { .v = cmd };
+
+ cmd[i++] = argv0;
+ cmd[i++] = "-a";
+ cmd[i++] = curconfig[CookiePolicies].val.v;
+ cmd[i++] = curconfig[ScrollBars].val.i ? "-B" : "-b";
+ if (cookiefile && g_strcmp0(cookiefile, "")) {
+ cmd[i++] = "-c";
+ cmd[i++] = cookiefile;
+ }
+ if (stylefile && g_strcmp0(stylefile, "")) {
+ cmd[i++] = "-C";
+ cmd[i++] = stylefile;
+ }
+ cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d";
+ if (embed && !noembed) {
+ cmd[i++] = "-e";
+ snprintf(tmp, LENGTH(tmp), "%lu", embed);
+ cmd[i++] = tmp;
+ }
+ cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ;
+ cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ;
+ cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ;
+ cmd[i++] = curconfig[KioskMode].val.i ? "-K" : "-k" ;
+ cmd[i++] = curconfig[Style].val.i ? "-M" : "-m" ;
+ cmd[i++] = curconfig[Inspector].val.i ? "-N" : "-n" ;
+ if (scriptfile && g_strcmp0(scriptfile, "")) {
+ cmd[i++] = "-r";
+ cmd[i++] = scriptfile;
+ }
+ cmd[i++] = curconfig[JavaScript].val.i ? "-S" : "-s";
+ cmd[i++] = curconfig[StrictTLS].val.i ? "-T" : "-t";
+ if (fulluseragent && g_strcmp0(fulluseragent, "")) {
+ cmd[i++] = "-u";
+ cmd[i++] = fulluseragent;
+ }
+ if (showxid)
+ cmd[i++] = "-w";
+ cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ;
+ /* do not keep zoom level */
+ cmd[i++] = "--";
+ if ((uri = a->v))
+ cmd[i++] = uri;
+ cmd[i] = NULL;
+
+ spawn(c, &arg);
+}
+
+void
+spawn(Client *c, const Arg *a)
+{
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ close(spair[0]);
+ close(spair[1]);
+ setsid();
+ execvp(((char **)a->v)[0], (char **)a->v);
+ fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]);
+ perror(" failed");
+ exit(1);
+ }
+}
+
+void
+destroyclient(Client *c)
+{
+ Client *p;
+
+ webkit_web_view_stop_loading(c->view);
+ /* Not needed, has already been called
+ gtk_widget_destroy(c->win);
+ */
+
+ for (p = clients; p && p->next != c; p = p->next)
+ ;
+ if (p)
+ p->next = c->next;
+ else
+ clients = c->next;
+ free(c);
+}
+
+void
+cleanup(void)
+{
+ while (clients)
+ destroyclient(clients);
+
+ close(spair[0]);
+ close(spair[1]);
+ g_free(cookiefile);
+ g_free(scriptfile);
+ g_free(stylefile);
+ g_free(cachedir);
+ XCloseDisplay(dpy);
+}
+
+WebKitWebView *
+newview(Client *c, WebKitWebView *rv)
+{
+ WebKitWebView *v;
+ WebKitSettings *settings;
+ WebKitWebContext *context;
+ WebKitCookieManager *cookiemanager;
+ WebKitUserContentManager *contentmanager;
+
+ /* Webview */
+ if (rv) {
+ v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv));
+ context = webkit_web_view_get_context(v);
+ settings = webkit_web_view_get_settings(v);
+ } else {
+ settings = webkit_settings_new_with_settings(
+ "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
+ "allow-universal-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
+ "auto-load-images", curconfig[LoadImages].val.i,
+ "default-charset", curconfig[DefaultCharset].val.v,
+ "default-font-size", curconfig[FontSize].val.i,
+ "enable-caret-browsing", curconfig[CaretBrowsing].val.i,
+ "enable-developer-extras", curconfig[Inspector].val.i,
+ "enable-dns-prefetching", curconfig[DNSPrefetch].val.i,
+ "enable-html5-database", curconfig[DiskCache].val.i,
+ "enable-html5-local-storage", curconfig[DiskCache].val.i,
+ "enable-javascript", curconfig[JavaScript].val.i,
+ "enable-site-specific-quirks", curconfig[SiteQuirks].val.i,
+ "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i,
+ "enable-webgl", curconfig[WebGL].val.i,
+ "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i,
+ NULL);
+/* For more interesting settings, have a look at
+ * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */
+
+ if (strcmp(fulluseragent, "")) {
+ webkit_settings_set_user_agent(settings, fulluseragent);
+ } else if (surfuseragent) {
+ webkit_settings_set_user_agent_with_application_details(
+ settings, "Surf", VERSION);
+ }
+ useragent = webkit_settings_get_user_agent(settings);
+
+ contentmanager = webkit_user_content_manager_new();
+
+ if (curconfig[Ephemeral].val.i) {
+ context = webkit_web_context_new_ephemeral();
+ } else {
+ context = webkit_web_context_new_with_website_data_manager(
+ webkit_website_data_manager_new(
+ "base-cache-directory", cachedir,
+ "base-data-directory", cachedir,
+ NULL));
+ }
+
+ cookiemanager = webkit_web_context_get_cookie_manager(context);
+
+ /* TLS */
+ webkit_website_data_manager_set_tls_errors_policy(
+ webkit_web_context_get_website_data_manager(context),
+ curconfig[StrictTLS].val.i ? WEBKIT_TLS_ERRORS_POLICY_FAIL :
+ WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+ /* disk cache */
+ webkit_web_context_set_cache_model(context,
+ curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER :
+ WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+
+ /* Currently only works with text file to be compatible with curl */
+ if (!curconfig[Ephemeral].val.i)
+ webkit_cookie_manager_set_persistent_storage(cookiemanager,
+ cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
+ /* cookie policy */
+ webkit_cookie_manager_set_accept_policy(cookiemanager,
+ cookiepolicy_get());
+ /* languages */
+ webkit_web_context_set_preferred_languages(context,
+ curconfig[PreferredLanguages].val.v);
+ webkit_web_context_set_spell_checking_languages(context,
+ curconfig[SpellLanguages].val.v);
+ webkit_web_context_set_spell_checking_enabled(context,
+ curconfig[SpellChecking].val.i);
+
+ g_signal_connect(G_OBJECT(context), "download-started",
+ G_CALLBACK(downloadstarted), c);
+ g_signal_connect(G_OBJECT(context), "initialize-web-extensions",
+ G_CALLBACK(initwebextensions), c);
+
+ v = g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "settings", settings,
+ "user-content-manager", contentmanager,
+ "web-context", context,
+ NULL);
+ }
+
+ g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress",
+ G_CALLBACK(progresschanged), c);
+ g_signal_connect(G_OBJECT(v), "notify::title",
+ G_CALLBACK(titlechanged), c);
+ g_signal_connect(G_OBJECT(v), "button-release-event",
+ G_CALLBACK(buttonreleased), c);
+ g_signal_connect(G_OBJECT(v), "close",
+ G_CALLBACK(closeview), c);
+ g_signal_connect(G_OBJECT(v), "create",
+ G_CALLBACK(createview), c);
+ g_signal_connect(G_OBJECT(v), "decide-policy",
+ G_CALLBACK(decidepolicy), c);
+ g_signal_connect(G_OBJECT(v), "insecure-content-detected",
+ G_CALLBACK(insecurecontent), c);
+ g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors",
+ G_CALLBACK(loadfailedtls), c);
+ g_signal_connect(G_OBJECT(v), "load-changed",
+ G_CALLBACK(loadchanged), c);
+ g_signal_connect(G_OBJECT(v), "mouse-target-changed",
+ G_CALLBACK(mousetargetchanged), c);
+ g_signal_connect(G_OBJECT(v), "permission-request",
+ G_CALLBACK(permissionrequested), c);
+ g_signal_connect(G_OBJECT(v), "ready-to-show",
+ G_CALLBACK(showview), c);
+ g_signal_connect(G_OBJECT(v), "user-message-received",
+ G_CALLBACK(viewusrmsgrcv), c);
+ g_signal_connect(G_OBJECT(v), "web-process-terminated",
+ G_CALLBACK(webprocessterminated), c);
+
+ c->context = context;
+ c->settings = settings;
+
+ setparameter(c, 0, DarkMode, &curconfig[DarkMode].val);
+
+ return v;
+}
+
+void
+initwebextensions(WebKitWebContext *wc, Client *c)
+{
+ webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR);
+}
+
+GtkWidget *
+createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c)
+{
+ Client *n;
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ /*
+ * popup windows of type “other” are almost always triggered
+ * by user gesture, so inverse the logic here
+ */
+/* instead of this, compare destination uri to mouse-over uri for validating window */
+ if (webkit_navigation_action_is_user_gesture(a))
+ return NULL;
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
+ n = newclient(c);
+ break;
+ default:
+ return NULL;
+ }
+
+ return GTK_WIDGET(n->view);
+}
+
+gboolean
+buttonreleased(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ WebKitHitTestResultContext element;
+ int i;
+
+ element = webkit_hit_test_result_get_context(c->mousepos);
+
+ for (i = 0; i < LENGTH(buttons); ++i) {
+ if (element & buttons[i].target &&
+ e->button.button == buttons[i].button &&
+ CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) &&
+ buttons[i].func) {
+ buttons[i].func(c, &buttons[i].arg, c->mousepos);
+ return buttons[i].stopevent;
+ }
+ }
+
+ return FALSE;
+}
+
+GdkFilterReturn
+processx(GdkXEvent *e, GdkEvent *event, gpointer d)
+{
+ Client *c = (Client *)d;
+ XPropertyEvent *ev;
+ Arg a;
+
+ if (((XEvent *)e)->type == PropertyNotify) {
+ ev = &((XEvent *)e)->xproperty;
+ if (ev->state == PropertyNewValue) {
+ if (ev->atom == atoms[AtomFind]) {
+ find(c, NULL);
+
+ return GDK_FILTER_REMOVE;
+ } else if (ev->atom == atoms[AtomGo]) {
+ a.v = getatom(c, AtomGo);
+ loaduri(c, &a);
+
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+gboolean
+winevent(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ int i;
+
+ switch (e->type) {
+ case GDK_ENTER_NOTIFY:
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+ break;
+ case GDK_KEY_PRESS:
+ if (!curconfig[KioskMode].val.i) {
+ for (i = 0; i < LENGTH(keys); ++i) {
+ if (gdk_keyval_to_lower(e->key.keyval) ==
+ keys[i].keyval &&
+ CLEANMASK(e->key.state) == keys[i].mod &&
+ keys[i].func) {
+ updatewinid(c);
+ keys[i].func(c, &(keys[i].arg));
+ return TRUE;
+ }
+ }
+ }
+ case GDK_LEAVE_NOTIFY:
+ c->overtitle = NULL;
+ updatetitle(c);
+ break;
+ case GDK_WINDOW_STATE:
+ if (e->window_state.changed_mask ==
+ GDK_WINDOW_STATE_FULLSCREEN)
+ c->fullscreen = e->window_state.new_window_state &
+ GDK_WINDOW_STATE_FULLSCREEN;
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+void
+showview(WebKitWebView *v, Client *c)
+{
+ GdkRGBA bgcolor = { 0 };
+ GdkWindow *gwin;
+
+ c->finder = webkit_web_view_get_find_controller(c->view);
+ c->inspector = webkit_web_view_get_inspector(c->view);
+
+ c->pageid = webkit_web_view_get_page_id(c->view);
+ c->win = createwindow(c);
+
+ gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
+ gtk_widget_show_all(c->win);
+ gtk_widget_grab_focus(GTK_WIDGET(c->view));
+
+ gwin = gtk_widget_get_window(GTK_WIDGET(c->win));
+ c->xid = gdk_x11_window_get_xid(gwin);
+ updatewinid(c);
+ if (showxid) {
+ gdk_display_sync(gtk_widget_get_display(c->win));
+ puts(winid);
+ fflush(stdout);
+ }
+
+ if (curconfig[HideBackground].val.i)
+ webkit_web_view_set_background_color(c->view, &bgcolor);
+
+ if (!curconfig[KioskMode].val.i) {
+ gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
+ gdk_window_add_filter(gwin, processx, c);
+ }
+
+ if (curconfig[RunInFullscreen].val.i)
+ togglefullscreen(c, NULL);
+
+ if (curconfig[ZoomLevel].val.f != 1.0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f);
+
+ setatom(c, AtomFind, "");
+ setatom(c, AtomUri, "about:blank");
+}
+
+GtkWidget *
+createwindow(Client *c)
+{
+ char *wmstr;
+ GtkWidget *w;
+
+ if (embed) {
+ w = gtk_plug_new(embed);
+ } else {
+ w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ wmstr = g_path_get_basename(argv0);
+ gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf");
+ g_free(wmstr);
+
+ wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid);
+ gtk_window_set_role(GTK_WINDOW(w), wmstr);
+ g_free(wmstr);
+
+ gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]);
+ }
+
+ g_signal_connect(G_OBJECT(w), "destroy",
+ G_CALLBACK(destroywin), c);
+ g_signal_connect(G_OBJECT(w), "enter-notify-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "key-press-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "leave-notify-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "window-state-event",
+ G_CALLBACK(winevent), c);
+
+ return w;
+}
+
+gboolean
+loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c)
+{
+ GString *errmsg = g_string_new(NULL);
+ gchar *html, *pem;
+
+ c->failedcert = g_object_ref(cert);
+ c->tlserr = err;
+ c->errorpage = 1;
+
+ if (err & G_TLS_CERTIFICATE_UNKNOWN_CA)
+ g_string_append(errmsg,
+ "The signing certificate authority is not known.<br>");
+ if (err & G_TLS_CERTIFICATE_BAD_IDENTITY)
+ g_string_append(errmsg,
+ "The certificate does not match the expected identity "
+ "of the site that it was retrieved from.<br>");
+ if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED)
+ g_string_append(errmsg,
+ "The certificate's activation time "
+ "is still in the future.<br>");
+ if (err & G_TLS_CERTIFICATE_EXPIRED)
+ g_string_append(errmsg, "The certificate has expired.<br>");
+ if (err & G_TLS_CERTIFICATE_REVOKED)
+ g_string_append(errmsg,
+ "The certificate has been revoked according to "
+ "the GTlsConnection's certificate revocation list.<br>");
+ if (err & G_TLS_CERTIFICATE_INSECURE)
+ g_string_append(errmsg,
+ "The certificate's algorithm is considered insecure.<br>");
+ if (err & G_TLS_CERTIFICATE_GENERIC_ERROR)
+ g_string_append(errmsg,
+ "Some error occurred validating the certificate.<br>");
+
+ g_object_get(cert, "certificate-pem", &pem, NULL);
+ html = g_strdup_printf("<p>Could not validate TLS for “%s”<br>%s</p>"
+ "<p>You can inspect the following certificate "
+ "with Ctrl-t (default keybinding).</p>"
+ "<p><pre>%s</pre></p>", uri, errmsg->str, pem);
+ g_free(pem);
+ g_string_free(errmsg, TRUE);
+
+ webkit_web_view_load_alternate_html(c->view, html, uri, NULL);
+ g_free(html);
+
+ return TRUE;
+}
+
+void
+loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
+{
+ const char *uri = geturi(c);
+
+ switch (e) {
+ case WEBKIT_LOAD_STARTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ c->https = c->insecure = 0;
+ seturiparameters(c, uri, loadtransient);
+ if (c->errorpage)
+ c->errorpage = 0;
+ else
+ g_clear_object(&c->failedcert);
+ break;
+ case WEBKIT_LOAD_REDIRECTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ seturiparameters(c, uri, loadtransient);
+ break;
+ case WEBKIT_LOAD_COMMITTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ seturiparameters(c, uri, loadcommitted);
+ c->https = webkit_web_view_get_tls_info(c->view, &c->cert,
+ &c->tlserr);
+ break;
+ case WEBKIT_LOAD_FINISHED:
+ seturiparameters(c, uri, loadfinished);
+ /* Disabled until we write some WebKitWebExtension for
+ * manipulating the DOM directly.
+ evalscript(c, "document.documentElement.style.overflow = '%s'",
+ enablescrollbars ? "auto" : "hidden");
+ */
+ runscript(c);
+ break;
+ }
+ updatetitle(c);
+}
+
+void
+progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c)
+{
+ c->progress = webkit_web_view_get_estimated_load_progress(c->view) *
+ 100;
+ updatetitle(c);
+}
+
+void
+titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c)
+{
+ c->title = webkit_web_view_get_title(c->view);
+ updatetitle(c);
+}
+
+gboolean
+viewusrmsgrcv(WebKitWebView *v, WebKitUserMessage *m, gpointer unused)
+{
+ WebKitUserMessage *r;
+ GUnixFDList *gfd;
+ const char *name;
+
+ name = webkit_user_message_get_name(m);
+ if (strcmp(name, "page-created") != 0) {
+ fprintf(stderr, "surf: Unknown UserMessage: %s\n", name);
+ return TRUE;
+ }
+
+ if (spair[1] < 0)
+ return TRUE;
+
+ gfd = g_unix_fd_list_new_from_array(&spair[1], 1);
+ r = webkit_user_message_new_with_fd_list("surf-pipe", NULL, gfd);
+
+ webkit_user_message_send_reply(m, r);
+
+ return TRUE;
+}
+
+void
+mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
+ Client *c)
+{
+ WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h);
+
+ /* Keep the hit test to know where is the pointer on the next click */
+ c->mousepos = h;
+
+ if (hc & OnLink)
+ c->targeturi = webkit_hit_test_result_get_link_uri(h);
+ else if (hc & OnImg)
+ c->targeturi = webkit_hit_test_result_get_image_uri(h);
+ else if (hc & OnMedia)
+ c->targeturi = webkit_hit_test_result_get_media_uri(h);
+ else
+ c->targeturi = NULL;
+
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+}
+
+gboolean
+permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c)
+{
+ ParamName param = ParameterLast;
+
+ if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) {
+ param = Geolocation;
+ } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(r)) {
+ if (webkit_user_media_permission_is_for_audio_device(
+ WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r)))
+ param = AccessMicrophone;
+ else if (webkit_user_media_permission_is_for_video_device(
+ WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r)))
+ param = AccessWebcam;
+ } else {
+ return FALSE;
+ }
+
+ if (curconfig[param].val.i)
+ webkit_permission_request_allow(r);
+ else
+ webkit_permission_request_deny(r);
+
+ return TRUE;
+}
+
+gboolean
+decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
+ WebKitPolicyDecisionType dt, Client *c)
+{
+ switch (dt) {
+ case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
+ decidenavigation(d, c);
+ break;
+ case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
+ decidenewwindow(d, c);
+ break;
+ case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
+ decideresource(d, c);
+ break;
+ default:
+ webkit_policy_decision_ignore(d);
+ break;
+ }
+ return TRUE;
+}
+
+void
+decidenavigation(WebKitPolicyDecision *d, Client *c)
+{
+ WebKitNavigationAction *a =
+ webkit_navigation_policy_decision_get_navigation_action(
+ WEBKIT_NAVIGATION_POLICY_DECISION(d));
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ default:
+ /* Do not navigate to links with a "_blank" target (popup) */
+ if (webkit_navigation_action_get_frame_name(a)) {
+ webkit_policy_decision_ignore(d);
+ } else {
+ /* Filter out navigation to different domain ? */
+ /* get action→urirequest, copy and load in new window+view
+ * on Ctrl+Click ? */
+ webkit_policy_decision_use(d);
+ }
+ break;
+ }
+}
+
+void
+decidenewwindow(WebKitPolicyDecision *d, Client *c)
+{
+ Arg arg;
+ WebKitNavigationAction *a =
+ webkit_navigation_policy_decision_get_navigation_action(
+ WEBKIT_NAVIGATION_POLICY_DECISION(d));
+
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
+ /* Filter domains here */
+/* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
+ * test for link clicked but no button ? */
+ arg.v = webkit_uri_request_get_uri(
+ webkit_navigation_action_get_request(a));
+ newwindow(c, &arg, 0);
+ break;
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ default:
+ break;
+ }
+
+ webkit_policy_decision_ignore(d);
+}
+
+void
+decideresource(WebKitPolicyDecision *d, Client *c)
+{
+ int i, isascii = 1;
+ WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d);
+ WebKitURIResponse *res =
+ webkit_response_policy_decision_get_response(r);
+ const gchar *uri = webkit_uri_response_get_uri(res);
+
+ if (g_str_has_suffix(uri, "/favicon.ico")) {
+ webkit_policy_decision_ignore(d);
+ return;
+ }
+
+ if (!g_str_has_prefix(uri, "http://")
+ && !g_str_has_prefix(uri, "https://")
+ && !g_str_has_prefix(uri, "about:")
+ && !g_str_has_prefix(uri, "file://")
+ && !g_str_has_prefix(uri, "webkit://")
+ && !g_str_has_prefix(uri, "data:")
+ && !g_str_has_prefix(uri, "blob:")
+ && strlen(uri) > 0) {
+ for (i = 0; i < strlen(uri); i++) {
+ if (!g_ascii_isprint(uri[i])) {
+ isascii = 0;
+ break;
+ }
+ }
+ if (isascii) {
+ handleplumb(c, uri);
+ webkit_policy_decision_ignore(d);
+ return;
+ }
+ }
+
+ if (webkit_response_policy_decision_is_mime_type_supported(r)) {
+ webkit_policy_decision_use(d);
+ } else {
+ webkit_policy_decision_ignore(d);
+ download(c, res);
+ }
+}
+
+void
+insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c)
+{
+ c->insecure = 1;
+}
+
+void
+downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c)
+{
+ g_signal_connect(G_OBJECT(d), "notify::response",
+ G_CALLBACK(responsereceived), c);
+}
+
+void
+responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c)
+{
+ download(c, webkit_download_get_response(d));
+ webkit_download_cancel(d);
+}
+
+void
+download(Client *c, WebKitURIResponse *r)
+{
+ Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c));
+ spawn(c, &a);
+}
+
+void
+webprocessterminated(WebKitWebView *v, WebKitWebProcessTerminationReason r,
+ Client *c)
+{
+ fprintf(stderr, "web process terminated: %s\n",
+ r == WEBKIT_WEB_PROCESS_CRASHED ? "crashed" : "no memory");
+ closeview(v, c);
+}
+
+void
+closeview(WebKitWebView *v, Client *c)
+{
+ gtk_widget_destroy(c->win);
+}
+
+void
+destroywin(GtkWidget* w, Client *c)
+{
+ destroyclient(c);
+ if (!clients)
+ gtk_main_quit();
+}
+
+void
+pasteuri(GtkClipboard *clipboard, const char *text, gpointer d)
+{
+ Arg a = {.v = text };
+ if (text)
+ loaduri((Client *) d, &a);
+}
+
+void
+reload(Client *c, const Arg *a)
+{
+ if (a->i)
+ webkit_web_view_reload_bypass_cache(c->view);
+ else
+ webkit_web_view_reload(c->view);
+}
+
+void
+print(Client *c, const Arg *a)
+{
+ webkit_print_operation_run_dialog(webkit_print_operation_new(c->view),
+ GTK_WINDOW(c->win));
+}
+
+void
+showcert(Client *c, const Arg *a)
+{
+ GTlsCertificate *cert = c->failedcert ? c->failedcert : c->cert;
+ GcrCertificate *gcrt;
+ GByteArray *crt;
+ GtkWidget *win;
+ GcrCertificateWidget *wcert;
+
+ if (!cert)
+ return;
+
+ g_object_get(cert, "certificate", &crt, NULL);
+ gcrt = gcr_simple_certificate_new(crt->data, crt->len);
+ g_byte_array_unref(crt);
+
+ win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ wcert = gcr_certificate_widget_new(gcrt);
+ g_object_unref(gcrt);
+
+ gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(wcert));
+ gtk_widget_show_all(win);
+}
+
+void
+clipboard(Client *c, const Arg *a)
+{
+ if (a->i) { /* load clipboard uri */
+ gtk_clipboard_request_text(gtk_clipboard_get(
+ GDK_SELECTION_PRIMARY),
+ pasteuri, c);
+ } else { /* copy uri */
+ gtk_clipboard_set_text(gtk_clipboard_get(
+ GDK_SELECTION_PRIMARY), c->targeturi
+ ? c->targeturi : geturi(c), -1);
+ }
+}
+
+void
+zoom(Client *c, const Arg *a)
+{
+ if (a->i > 0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f + 0.1);
+ else if (a->i < 0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f - 0.1);
+ else
+ webkit_web_view_set_zoom_level(c->view, 1.0);
+
+ curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view);
+}
+
+static void
+msgext(Client *c, char type, const Arg *a)
+{
+ static unsigned char msg[MSGBUFSZ];
+ int ret;
+
+ if (spair[0] < 0)
+ return;
+
+ ret = snprintf(msg, sizeof(msg), "%c%c%c",
+ (unsigned char)c->pageid, type, (signed char)a->i);
+ if (ret >= sizeof(msg)) {
+ fprintf(stderr, "surf: message too long: %d\n", ret);
+ return;
+ }
+
+ if (send(spair[0], msg, ret, 0) != ret)
+ fprintf(stderr, "surf: error sending: %hhu/%c/%d (%d)\n",
+ (unsigned char)c->pageid, type, a->i, ret);
+}
+
+void
+scrollv(Client *c, const Arg *a)
+{
+ msgext(c, 'v', a);
+}
+
+void
+scrollh(Client *c, const Arg *a)
+{
+ msgext(c, 'h', a);
+}
+
+void
+navigate(Client *c, const Arg *a)
+{
+ if (a->i < 0)
+ webkit_web_view_go_back(c->view);
+ else if (a->i > 0)
+ webkit_web_view_go_forward(c->view);
+}
+
+void
+stop(Client *c, const Arg *a)
+{
+ webkit_web_view_stop_loading(c->view);
+}
+
+void
+toggle(Client *c, const Arg *a)
+{
+ curconfig[a->i].val.i ^= 1;
+ setparameter(c, 1, (ParamName)a->i, &curconfig[a->i].val);
+}
+
+void
+togglefullscreen(Client *c, const Arg *a)
+{
+ /* toggling value is handled in winevent() */
+ if (c->fullscreen)
+ gtk_window_unfullscreen(GTK_WINDOW(c->win));
+ else
+ gtk_window_fullscreen(GTK_WINDOW(c->win));
+}
+
+void
+togglecookiepolicy(Client *c, const Arg *a)
+{
+ ++cookiepolicy;
+ cookiepolicy %= strlen(curconfig[CookiePolicies].val.v);
+
+ setparameter(c, 0, CookiePolicies, NULL);
+}
+
+void
+toggleinspector(Client *c, const Arg *a)
+{
+ if (webkit_web_inspector_is_attached(c->inspector))
+ webkit_web_inspector_close(c->inspector);
+ else if (curconfig[Inspector].val.i)
+ webkit_web_inspector_show(c->inspector);
+}
+
+void
+find(Client *c, const Arg *a)
+{
+ const char *s, *f;
+
+ if (a && a->i) {
+ if (a->i > 0)
+ webkit_find_controller_search_next(c->finder);
+ else
+ webkit_find_controller_search_previous(c->finder);
+ } else {
+ s = getatom(c, AtomFind);
+ f = webkit_find_controller_get_search_text(c->finder);
+
+ if (g_strcmp0(f, s) == 0) /* reset search */
+ webkit_find_controller_search(c->finder, "", findopts,
+ G_MAXUINT);
+
+ webkit_find_controller_search(c->finder, s, findopts,
+ G_MAXUINT);
+
+ if (strcmp(s, "") == 0)
+ webkit_find_controller_search_finish(c->finder);
+ }
+}
+
+void
+clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h)
+{
+ navigate(c, a);
+}
+
+void
+clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h)
+{
+ Arg arg;
+
+ arg.v = webkit_hit_test_result_get_link_uri(h);
+ newwindow(c, &arg, a->i);
+}
+
+void
+clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h)
+{
+ Arg arg;
+
+ arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h));
+ spawn(c, &arg);
+}
+
+int
+main(int argc, char *argv[])
+{
+ Arg arg;
+ Client *c;
+
+ memset(&arg, 0, sizeof(arg));
+
+ /* command line args */
+ ARGBEGIN {
+ case 'a':
+ defconfig[CookiePolicies].val.v = EARGF(usage());
+ defconfig[CookiePolicies].prio = 2;
+ break;
+ case 'b':
+ defconfig[ScrollBars].val.i = 0;
+ defconfig[ScrollBars].prio = 2;
+ break;
+ case 'B':
+ defconfig[ScrollBars].val.i = 1;
+ defconfig[ScrollBars].prio = 2;
+ break;
+ case 'c':
+ cookiefile = EARGF(usage());
+ break;
+ case 'C':
+ stylefile = EARGF(usage());
+ break;
+ case 'd':
+ defconfig[DiskCache].val.i = 0;
+ defconfig[DiskCache].prio = 2;
+ break;
+ case 'D':
+ defconfig[DiskCache].val.i = 1;
+ defconfig[DiskCache].prio = 2;
+ break;
+ case 'e':
+ embed = strtol(EARGF(usage()), NULL, 0);
+ break;
+ case 'f':
+ defconfig[RunInFullscreen].val.i = 0;
+ defconfig[RunInFullscreen].prio = 2;
+ break;
+ case 'F':
+ defconfig[RunInFullscreen].val.i = 1;
+ defconfig[RunInFullscreen].prio = 2;
+ break;
+ case 'g':
+ defconfig[Geolocation].val.i = 0;
+ defconfig[Geolocation].prio = 2;
+ break;
+ case 'G':
+ defconfig[Geolocation].val.i = 1;
+ defconfig[Geolocation].prio = 2;
+ break;
+ case 'i':
+ defconfig[LoadImages].val.i = 0;
+ defconfig[LoadImages].prio = 2;
+ break;
+ case 'I':
+ defconfig[LoadImages].val.i = 1;
+ defconfig[LoadImages].prio = 2;
+ break;
+ case 'k':
+ defconfig[KioskMode].val.i = 0;
+ defconfig[KioskMode].prio = 2;
+ break;
+ case 'K':
+ defconfig[KioskMode].val.i = 1;
+ defconfig[KioskMode].prio = 2;
+ break;
+ case 'm':
+ defconfig[Style].val.i = 0;
+ defconfig[Style].prio = 2;
+ break;
+ case 'M':
+ defconfig[Style].val.i = 1;
+ defconfig[Style].prio = 2;
+ break;
+ case 'n':
+ defconfig[Inspector].val.i = 0;
+ defconfig[Inspector].prio = 2;
+ break;
+ case 'N':
+ defconfig[Inspector].val.i = 1;
+ defconfig[Inspector].prio = 2;
+ break;
+ case 'r':
+ scriptfile = EARGF(usage());
+ break;
+ case 's':
+ defconfig[JavaScript].val.i = 0;
+ defconfig[JavaScript].prio = 2;
+ break;
+ case 'S':
+ defconfig[JavaScript].val.i = 1;
+ defconfig[JavaScript].prio = 2;
+ break;
+ case 't':
+ defconfig[StrictTLS].val.i = 0;
+ defconfig[StrictTLS].prio = 2;
+ break;
+ case 'T':
+ defconfig[StrictTLS].val.i = 1;
+ defconfig[StrictTLS].prio = 2;
+ break;
+ case 'u':
+ fulluseragent = EARGF(usage());
+ break;
+ case 'v':
+ die("surf-"VERSION", see LICENSE for © details\n");
+ case 'w':
+ showxid = 1;
+ break;
+ case 'x':
+ defconfig[Certificate].val.i = 0;
+ defconfig[Certificate].prio = 2;
+ break;
+ case 'X':
+ defconfig[Certificate].val.i = 1;
+ defconfig[Certificate].prio = 2;
+ break;
+ case 'z':
+ defconfig[ZoomLevel].val.f = strtof(EARGF(usage()), NULL);
+ defconfig[ZoomLevel].prio = 2;
+ break;
+ default:
+ usage();
+ } ARGEND;
+ if (argc > 0)
+ arg.v = argv[0];
+ else
+ arg.v = "about:blank";
+
+ setup();
+ c = newclient(NULL);
+ showview(NULL, c);
+
+ loaduri(c, &arg);
+ updatetitle(c);
+
+ gtk_main();
+ cleanup();
+
+ return 0;
+}
diff --git a/keep/manual-programs/suckless/surf/surf.o b/keep/manual-programs/suckless/surf/surf.o
new file mode 100644
index 0000000..f7e1b3e
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf.o
Binary files differ
diff --git a/keep/manual-programs/suckless/surf/surf.png b/keep/manual-programs/suckless/surf/surf.png
new file mode 100644
index 0000000..f5b2ab1
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/surf.png
Binary files differ
diff --git a/keep/manual-programs/suckless/surf/webext-surf.c b/keep/manual-programs/suckless/surf/webext-surf.c
new file mode 100644
index 0000000..7063fa7
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/webext-surf.c
@@ -0,0 +1,166 @@
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <webkit2/webkit-web-extension.h>
+#include <webkitdom/webkitdom.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+
+#include "common.h"
+
+#define LENGTH(x) (sizeof(x) / sizeof(x[0]))
+
+static WebKitWebExtension *webext;
+static int sock;
+
+/*
+ * Return:
+ * 0 No data processed: need more data
+ * > 0 Amount of data processed or discarded
+ */
+static size_t
+evalmsg(char *msg, size_t sz)
+{
+ char js[48];
+ WebKitWebPage *page;
+ JSCContext *jsc;
+ JSCValue *jsv;
+
+ if (!(page = webkit_web_extension_get_page(webext, msg[0])))
+ return sz;
+
+ if (sz < 2)
+ return 0;
+
+ jsc = webkit_frame_get_js_context(webkit_web_page_get_main_frame(page));
+ jsv = NULL;
+
+ switch (msg[1]) {
+ case 'h':
+ if (sz < 3) {
+ sz = 0;
+ break;
+ }
+ sz = 3;
+ snprintf(js, sizeof(js),
+ "window.scrollBy(window.innerWidth/100*%hhd,0);",
+ msg[2]);
+ jsv = jsc_context_evaluate(jsc, js, -1);
+ break;
+ case 'v':
+ if (sz < 3) {
+ sz = 0;
+ break;
+ }
+ sz = 3;
+ snprintf(js, sizeof(js),
+ "window.scrollBy(0,window.innerHeight/100*%hhd);",
+ msg[2]);
+ jsv = jsc_context_evaluate(jsc, js, -1);
+ break;
+ default:
+ fprintf(stderr, "%s:%d:evalmsg: unknown cmd(%zu): '%#x'\n",
+ __FILE__, __LINE__, sz, msg[1]);
+ }
+
+ g_object_unref(jsc);
+ if (jsv)
+ g_object_unref(jsv);
+
+ return sz;
+}
+
+static gboolean
+readsock(GIOChannel *s, GIOCondition c, gpointer unused)
+{
+ static char msg[MSGBUFSZ];
+ static size_t msgoff;
+ GError *gerr = NULL;
+ size_t sz;
+ gsize rsz;
+
+ if (g_io_channel_read_chars(s, msg+msgoff, sizeof(msg)-msgoff, &rsz, &gerr) !=
+ G_IO_STATUS_NORMAL) {
+ if (gerr) {
+ fprintf(stderr, "webext: error reading socket: %s\n",
+ gerr->message);
+ g_error_free(gerr);
+ }
+ return TRUE;
+ }
+ if (msgoff >= sizeof(msg)) {
+ fprintf(stderr, "%s:%d:%s: msgoff: %zu", __FILE__, __LINE__, __func__, msgoff);
+ return FALSE;
+ }
+
+ for (rsz += msgoff; rsz; rsz -= sz) {
+ sz = evalmsg(msg, rsz);
+ if (sz == 0) {
+ /* need more data */
+ break;
+ }
+ if (sz != rsz) {
+ /* continue processing message */
+ memmove(msg, msg+sz, rsz-sz);
+ }
+ }
+ msgoff = rsz;
+
+ return TRUE;
+}
+
+static void
+pageusermessagereply(GObject *o, GAsyncResult *r, gpointer page)
+{
+ WebKitUserMessage *m;
+ GUnixFDList *gfd;
+ GIOChannel *gchansock;
+ const char *name;
+ int nfd;
+
+ m = webkit_web_page_send_message_to_view_finish(page, r, NULL);
+ name = webkit_user_message_get_name(m);
+ if (strcmp(name, "surf-pipe") != 0) {
+ fprintf(stderr, "webext-surf: Unknown User Reply: %s\n", name);
+ return;
+ }
+
+ gfd = webkit_user_message_get_fd_list(m);
+ if ((nfd = g_unix_fd_list_get_length(gfd)) != 1) {
+ fprintf(stderr, "webext-surf: Too many file-descriptors: %d\n", nfd);
+ return;
+ }
+
+ sock = g_unix_fd_list_get(gfd, 0, NULL);
+
+ gchansock = g_io_channel_unix_new(sock);
+ g_io_channel_set_encoding(gchansock, NULL, NULL);
+ g_io_channel_set_flags(gchansock, g_io_channel_get_flags(gchansock)
+ | G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_close_on_unref(gchansock, TRUE);
+ g_io_add_watch(gchansock, G_IO_IN, readsock, NULL);
+}
+
+void
+pagecreated(WebKitWebExtension *e, WebKitWebPage *p, gpointer unused)
+{
+ WebKitUserMessage *msg;
+
+ msg = webkit_user_message_new("page-created", NULL);
+ webkit_web_page_send_message_to_view(p, msg, NULL, pageusermessagereply, p);
+}
+
+G_MODULE_EXPORT void
+webkit_web_extension_initialize(WebKitWebExtension *e)
+{
+ webext = e;
+
+ g_signal_connect(G_OBJECT(e), "page-created",
+ G_CALLBACK(pagecreated), NULL);
+}
diff --git a/keep/manual-programs/suckless/surf/webext-surf.o b/keep/manual-programs/suckless/surf/webext-surf.o
new file mode 100644
index 0000000..4dc740f
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/webext-surf.o
Binary files differ
diff --git a/keep/manual-programs/suckless/surf/webext-surf.so b/keep/manual-programs/suckless/surf/webext-surf.so
new file mode 100644
index 0000000..39e59d4
--- /dev/null
+++ b/keep/manual-programs/suckless/surf/webext-surf.so
Binary files differ