initial commit

This commit is contained in:
Klein
2024-09-26 23:40:15 -04:00
commit 271de5e6b3
168 changed files with 35751 additions and 0 deletions

1
README.md Normal file
View File

@@ -0,0 +1 @@
# aarch64DWM

38
aarch64dwm/LICENSE Normal file
View File

@@ -0,0 +1,38 @@
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>
© 2020-2022 Chris Down <chris@chrisdown.name>
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.

45
aarch64dwm/Makefile Normal file
View File

@@ -0,0 +1,45 @@
# 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: dwm
.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 clean dist install uninstall

48
aarch64dwm/README Normal file
View File

@@ -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.

View File

@@ -0,0 +1,34 @@
static const char col_gray1[] = "#ffffff";
static const char col_gray3[] = "#ffffff";
static const char col_gray4[] = "#ffffff";
static const char col_gray2[] = "#ffffff";
static const char col_cyan[] = "#ffffff";
static const char tag1[] = "#bf8427";
static const char tag2[] = "#5865F2";
static const char tag3[] = "#FF5500";
static const char tag4[] = "#FF609A";
static const char tag5[] = "#842291";
static const char tag6[] = "#900C3F";
static const char tag7[] = "#67AFA5";
static const char tag8[] = "#1DB954";
static const char tag9[] = "#FFFFFF";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_cyan },
[SchemeSel] = { col_gray4, col_cyan, col_gray2 },
[SchemeTitle] = { col_gray4, col_cyan, col_cyan },
};
static const char *tagsel[][2] = {
{ tag1, col_gray1 },
{ tag2, col_gray1 },
{ tag3, col_gray1 },
{ tag4, col_gray1 },
{ tag5, col_gray1 },
{ tag6, col_gray1 },
{ tag7, col_gray1 },
{ tag8, col_gray1 },
{ tag9, col_gray1 },
};

34
aarch64dwm/colors/space.h Normal file
View File

@@ -0,0 +1,34 @@
static const char col_gray1[] = "#0f161e";
static const char col_gray3[] = "#fff7d0";
static const char col_gray4[] = "#FF609A";
static const char col_gray2[] = "#282A2E";
static const char col_cyan[] = "#0f161e";
static const char tag1[] = "#bf8427";
static const char tag2[] = "#5865F2";
static const char tag3[] = "#FF5500";
static const char tag4[] = "#FF609A";
static const char tag5[] = "#842291";
static const char tag6[] = "#900C3F";
static const char tag7[] = "#67AFA5";
static const char tag8[] = "#1DB954";
static const char tag9[] = "#FFFFFF";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_cyan },
[SchemeSel] = { col_gray4, col_cyan, col_gray2 },
[SchemeTitle] = { col_gray4, col_cyan, col_cyan },
};
static const char *tagsel[][2] = {
{ tag1, col_gray1 },
{ tag2, col_gray1 },
{ tag3, col_gray1 },
{ tag4, col_gray1 },
{ tag5, col_gray1 },
{ tag6, col_gray1 },
{ tag7, col_gray1 },
{ tag8, col_gray1 },
{ tag9, col_gray1 },
};

148
aarch64dwm/config.def.h Normal file
View File

@@ -0,0 +1,148 @@
/* See LICENSE file for copyright and license details. */
/* 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 systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
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 int user_bh = 0; /* 0 means that dwm will calculate bar height, >= 1 means dwm will user_bh as bar height */
#define ICONSIZE 16 /*icon size*/
#define ICONSPACING 5 /* Space between icn and title */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
[SchemeTitle] = { col_gray4, col_cyan, col_cyan },
};
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const char *tagsel[][2] = {
{ "#ffffff", "#ff0000" },
{ "#ffffff", "#ff7f00" },
{ "#000000", "#ffff00" },
{ "#000000", "#00ff00" },
{ "#ffffff", "#0000ff" },
{ "#ffffff", "#4b0082" },
{ "#ffffff", "#9400d3" },
{ "#000000", "#ffffff" },
{ "#ffffff", "#000000" },
};
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating CenterThisWindow? monitor */
{ "st", NULL, NULL, 0, 0, 1, -1 },
{ "Gimp", NULL, NULL, 0, 1, 0, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, 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 int lockfullscreen = 0; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* custom symbols for nr. of clients in monocle layout */
/* when clients >= LENGTH(monocles), uses the last element */
static const char *monocles[] = { "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]", "[9+]" };
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(CHAIN,KEY,TAG) \
{ MODKEY, CHAIN, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, CHAIN, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, CHAIN, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, CHAIN, 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 *termcmd[] = { "st", NULL };
#include "movestack.c"
static const Key keys[] = {
/* modifier chain key key function argument */
{ MODKEY, -1, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, -1, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, -1, XK_b, togglebar, {0} },
{ MODKEY, -1, XK_j, focusstack, {.i = +1 } },
{ MODKEY, -1, XK_k, focusstack, {.i = -1 } },
{ MODKEY, -1, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, -1, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, -1, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, -1, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, -1, XK_x, movestack, {.i = +1 },
{ MODKEY, -1, XK_z, movestack, {.i = -1 },
{ MODKEY, -1, XK_Return, zoom, {0} },
{ MODKEY, -1, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, -1, XK_c, killclient, {0} },
{ MODKEY, -1, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, -1, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, -1, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, -1, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, -1, XK_space, togglefloating, {0} },
{ MODKEY, -1, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, -1, XK_0, tag, {.ui = ~0 } },
{ MODKEY, -1, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, -1, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, -1, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, -1, XK_period, tagmon, {.i = +1 } },
TAGKEYS( -1, XK_1, 0)
TAGKEYS( -1, XK_2, 1)
TAGKEYS( -1, XK_3, 2)
TAGKEYS( -1, XK_4, 3)
TAGKEYS( -1, XK_5, 4)
TAGKEYS( -1, XK_6, 5)
TAGKEYS( -1, XK_7, 6)
TAGKEYS( -1, XK_8, 7)
TAGKEYS( -1, XK_9, 8)
{ MODKEY|ShiftMask, -1, XK_q, quit, {0} },
{ MODKEY|ControlMask|ShiftMask, -1, XK_q, quit, {1} },
{ MODKEY, XK_a, XK_d, spawn, {.v = dmenucmd } },
{ MODKEY, XK_a, XK_t, spawn, {.v = termcmd } },
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const 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} },
};

View File

@@ -0,0 +1,145 @@
/* See LICENSE file for copyright and license details. */
/* 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 systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
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 int user_bh = 0; /* 0 means that dwm will calculate bar height, >= 1 means dwm will user_bh as bar height */
#define ICONSIZE 16 /*icon size*/
#define ICONSPACING 5 /* Space between icn and title */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
[SchemeTitle] = { col_gray4, col_cyan, col_cyan },
};
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const char *tagsel[][2] = {
{ "#ffffff", "#ff0000" },
{ "#ffffff", "#ff7f00" },
{ "#000000", "#ffff00" },
{ "#000000", "#00ff00" },
{ "#ffffff", "#0000ff" },
{ "#ffffff", "#4b0082" },
{ "#ffffff", "#9400d3" },
{ "#000000", "#ffffff" },
{ "#ffffff", "#000000" },
};
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating CenterThisWindow? monitor */
{ "st", NULL, NULL, 0, 0, 1, -1 },
{ "Gimp", NULL, NULL, 0, 1, 0, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, 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 int lockfullscreen = 0; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* custom symbols for nr. of clients in monocle layout */
/* when clients >= LENGTH(monocles), uses the last element */
static const char *monocles[] = { "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]", "[9+]" };
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(CHAIN,KEY,TAG) \
{ MODKEY, CHAIN, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, CHAIN, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, CHAIN, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, CHAIN, 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 *termcmd[] = { "st", NULL };
static const Key keys[] = {
/* modifier chain key key function argument */
{ MODKEY, -1, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, -1, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, -1, XK_b, togglebar, {0} },
{ MODKEY, -1, XK_j, focusstack, {.i = +1 } },
{ MODKEY, -1, XK_k, focusstack, {.i = -1 } },
{ MODKEY, -1, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, -1, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, -1, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, -1, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, -1, XK_Return, zoom, {0} },
{ MODKEY, -1, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, -1, XK_c, killclient, {0} },
{ MODKEY, -1, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, -1, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, -1, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, -1, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, -1, XK_space, togglefloating, {0} },
{ MODKEY, -1, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, -1, XK_0, tag, {.ui = ~0 } },
{ MODKEY, -1, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, -1, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, -1, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, -1, XK_period, tagmon, {.i = +1 } },
TAGKEYS( -1, XK_1, 0)
TAGKEYS( -1, XK_2, 1)
TAGKEYS( -1, XK_3, 2)
TAGKEYS( -1, XK_4, 3)
TAGKEYS( -1, XK_5, 4)
TAGKEYS( -1, XK_6, 5)
TAGKEYS( -1, XK_7, 6)
TAGKEYS( -1, XK_8, 7)
TAGKEYS( -1, XK_9, 8)
{ MODKEY|ShiftMask, -1, XK_q, quit, {0} },
{ MODKEY|ControlMask|ShiftMask, -1, XK_q, quit, {1} },
{ MODKEY, XK_a, XK_d, spawn, {.v = dmenucmd } },
{ MODKEY, XK_a, XK_t, spawn, {.v = termcmd } },
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const 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} },
};

View File

@@ -0,0 +1,19 @@
--- config.def.h
+++ config.def.h
@@ -60,6 +60,7 @@ 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 *termcmd[] = { "st", NULL };
+#include "movestack.c"
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
@@ -71,6 +72,8 @@ static Key keys[] = {
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },

34
aarch64dwm/config.h Normal file
View File

@@ -0,0 +1,34 @@
/* See LICENSE file for copyright and license details. */
#include "colors/space.h"
#include "keys.h"
#define ICONSIZE 16 /* icon size */
#define ICONSPACING 5 /* Space between icon and title */
/* appearance */
static const unsigned int borderpx = 2; /* 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 systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
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 int user_bh = 0; /* 0 means that dwm will calculate bar height, >= 1 means dwm will user_bh as bar height */
static const char *fonts[] = { "monospace:size=9", "DroidSansMono Nerd Font:size=9", "Noto Color Emoji:size=9", "Cairo:size=9" };
/* tagging */
//static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const char *tags[] = { "\uf17c", "\uf1d8", "\uf0ac", "\uf109", "\uf09b", "\uf02d", "\uf044", "\uf1bc", "\uf120" };
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating CenterThisWindow? monitor */
{ "st", NULL, NULL, 0, 0, 1, -1 },
{ "Gimp", NULL, NULL, 0, 1, 0, -1 },
};

39
aarch64dwm/config.mk Normal file
View File

@@ -0,0 +1,39 @@
# dwm version
VERSION = 6.5
# 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
#MANPREFIX = ${PREFIX}/man
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender -lImlib2
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -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

39
aarch64dwm/config.mk.orig Normal file
View File

@@ -0,0 +1,39 @@
# dwm version
VERSION = 6.5
# 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
#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=700L -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

525
aarch64dwm/drw.c Normal file
View File

@@ -0,0 +1,525 @@
/* 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 <Imlib2.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->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, screen)), 0, NULL);
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->picture)
XRenderFreePicture(drw->dpy, drw->picture);
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, DefaultVisual(drw->dpy, drw->screen)), 0, NULL);
}
void
drw_free(Drw *drw)
{
XRenderFreePicture(drw->dpy, drw->picture);
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_fontset_free(drw->fonts);
free(drw);
}
/* This function is an implementation detail. Library users should use
* drw_fontset_create instead.
*/
static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = NULL;
if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield the
* same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in missing-character
* rectangles being drawn, at least with some fonts. */
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
return NULL;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n");
return NULL;
}
} else {
die("no font specified.");
}
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;
}
Picture
drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) {
Pixmap pm;
Picture pic;
GC gc;
if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) {
XImage img = {
srcw, srch, 0, ZPixmap, src,
ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
32, 0, 32,
0, 0, 0
};
XInitImage(&img);
pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32);
gc = XCreateGC(drw->dpy, pm, 0, NULL);
XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch);
XFreeGC(drw->dpy, gc);
pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
XFreePixmap(drw->dpy, pm);
XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0);
XTransform xf;
xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0;
xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0;
xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536;
XRenderSetPictureTransform(drw->dpy, pic, &xf);
} else {
Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src);
if (!origin) return None;
imlib_context_set_image(origin);
imlib_image_set_has_alpha(1);
Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth);
imlib_free_image_and_decache();
if (!scaled) return None;
imlib_context_set_image(scaled);
imlib_image_set_has_alpha(1);
XImage img = {
dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(),
ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
32, 0, 32,
0, 0, 0
};
XInitImage(&img);
pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32);
gc = XCreateGC(drw->dpy, pm, 0, NULL);
XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth);
imlib_free_image_and_decache();
XFreeGC(drw->dpy, gc);
pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
XFreePixmap(drw->dpy, pm);
}
return pic;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}
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_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic)
{
if (!drw)
return;
XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h);
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
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);
}

450
aarch64dwm/drw.c.orig Normal file
View File

@@ -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);
}

62
aarch64dwm/drw.h Normal file
View File

@@ -0,0 +1,62 @@
/* 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;
Picture picture;
GC gc;
Clr *scheme;
Fnt *fonts;
} Drw;
/* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
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);
Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h);
/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

58
aarch64dwm/drw.h.orig Normal file
View File

@@ -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, 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);
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);

BIN
aarch64dwm/drw.o Normal file

Binary file not shown.

BIN
aarch64dwm/dwm Executable file

Binary file not shown.

186
aarch64dwm/dwm.1 Normal file
View File

@@ -0,0 +1,186 @@
.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 stderr, 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.
.TP
.B Mod1\-Control\-Shift\-q
Restart 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 SIGNALS
.TP
.B SIGHUP - 1
Restart the dwm process.
.TP
.B SIGTERM - 15
Cleanly terminate the dwm process.
.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.

2774
aarch64dwm/dwm.c Normal file

File diff suppressed because it is too large Load Diff

2774
aarch64dwm/dwm.c.orig Normal file

File diff suppressed because it is too large Load Diff

20
aarch64dwm/dwm.c.rej Normal file
View File

@@ -0,0 +1,20 @@
--- dwm.c
+++ dwm.c
@@ -59,7 +59,7 @@
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
-enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeTitle }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
@@ -731,7 +731,7 @@ drawbar(Monitor *m)
if ((w = m->ww - tw - x) > bh) {
if (m->sel) {
- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_setscheme(drw, scheme[m == selmon ? SchemeTitle : 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);

BIN
aarch64dwm/dwm.o Normal file

Binary file not shown.

BIN
aarch64dwm/dwm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

81
aarch64dwm/keys.h Normal file
View File

@@ -0,0 +1,81 @@
#include "layouts.h"
#include "movestack.c"
#include <X11/XF86keysym.h>
#define TERMINAL "st" // default terminal
/* key definitions */
#define MODKEY Mod4Mask
#define ALTMOD Mod1Mask
#define TAGKEYS(CHAIN,KEY,TAG) \
{ MODKEY, CHAIN, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, CHAIN, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, CHAIN, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, CHAIN, 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 const char dmenufont[] = "monospace:size=10";
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { TERMINAL, NULL };
static const Key keys[] = {
/* modifier chain key key function argument */
{ MODKEY, -1, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, -1, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, -1, XK_b, togglebar, {0} },
{ MODKEY, -1, XK_j, focusstack, {.i = +1 } },
{ MODKEY, -1, XK_k, focusstack, {.i = -1 } },
{ MODKEY, -1, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, -1, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, -1, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, -1, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, -1, XK_x, movestack, {.i = +1 } },
{ MODKEY, -1, XK_z, movestack, {.i = -1 } },
{ MODKEY, -1, XK_Return, zoom, {0} },
{ MODKEY, -1, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, -1, XK_c, killclient, {0} },
{ MODKEY, -1, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, -1, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, -1, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, -1, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, -1, XK_space, togglefloating, {0} },
{ MODKEY, -1, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, -1, XK_0, tag, {.ui = ~0 } },
{ MODKEY, -1, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, -1, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, -1, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, -1, XK_period, tagmon, {.i = +1 } },
TAGKEYS( -1, XK_1, 0)
TAGKEYS( -1, XK_2, 1)
TAGKEYS( -1, XK_3, 2)
TAGKEYS( -1, XK_4, 3)
TAGKEYS( -1, XK_5, 4)
TAGKEYS( -1, XK_6, 5)
TAGKEYS( -1, XK_7, 6)
TAGKEYS( -1, XK_8, 7)
TAGKEYS( -1, XK_9, 8)
{ MODKEY|ShiftMask, -1, XK_q, quit, {0} },
{ MODKEY|ControlMask|ShiftMask, -1, XK_q, quit, {1} },
{ MODKEY, XK_a, XK_d, spawn, {.v = dmenucmd } },
{ MODKEY, XK_a, XK_t, spawn, {.v = termcmd } },
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = {
/* click event mask button function argument */
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
{ 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} },
};

17
aarch64dwm/layouts.h Normal file
View File

@@ -0,0 +1,17 @@
/* 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 int lockfullscreen = 0; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* custom symbols for nr. of clients in monocle layout */
/* when clients >= LENGTH(monocles), uses the last element */
static const char *monocles[] = { "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]", "[9+]" };

48
aarch64dwm/movestack.c Normal file
View File

@@ -0,0 +1,48 @@
void
movestack(const Arg *arg) {
Client *c = NULL, *p = NULL, *pc = NULL, *i;
if(arg->i > 0) {
/* find the client after selmon->sel */
for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
if(!c)
for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
}
else {
/* find the client before selmon->sel */
for(i = selmon->clients; i != selmon->sel; i = i->next)
if(ISVISIBLE(i) && !i->isfloating)
c = i;
if(!c)
for(; i; i = i->next)
if(ISVISIBLE(i) && !i->isfloating)
c = i;
}
/* find the client before selmon->sel and c */
for(i = selmon->clients; i && (!p || !pc); i = i->next) {
if(i->next == selmon->sel)
p = i;
if(i->next == c)
pc = i;
}
/* swap c and selmon->sel selmon->clients in the selmon->clients list */
if(c && c != selmon->sel) {
Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next;
selmon->sel->next = c->next==selmon->sel?c:c->next;
c->next = temp;
if(p && p != c)
p->next = c;
if(pc && pc != selmon->sel)
pc->next = selmon->sel;
if(selmon->sel == selmon->clients)
selmon->clients = c;
else if(c == selmon->clients)
selmon->clients = selmon->sel;
arrange(selmon);
}
}

View File

@@ -0,0 +1,12 @@
diff -up dwm/dwm.c dwmmod/dwm.c
--- dwm/dwm.c 2020-06-25 00:21:30.383692180 -0300
+++ dwmmod/dwm.c 2020-06-25 00:20:35.643692330 -0300
@@ -1057,6 +1057,8 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
+ c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2;
+ c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2;
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)

View File

@@ -0,0 +1,25 @@
diff --git a/config.def.h b/config.def.h
index 1c0b587..9814500 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,6 +5,7 @@ static const unsigned int borderpx = 1; /* border pixel of 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 int user_bh = 0; /* 0 means that dwm will calculate bar height, >= 1 means dwm will user_bh as bar height */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
diff --git a/dwm.c b/dwm.c
index 4465af1..2c27cb3 100644
--- a/dwm.c
+++ b/dwm.c
@@ -1545,7 +1545,7 @@ setup(void)
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
- bh = drw->fonts->h + 2;
+ bh = user_bh ? user_bh : drw->fonts->h + 2;
updategeom();
/* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);

View File

@@ -0,0 +1,67 @@
diff -up dwm-6.2-orig/config.def.h dwm-6.2-modd/config.def.h
--- dwm-6.2-orig/config.def.h 2019-02-02 16:55:28.000000000 +0400
+++ dwm-6.2-modd/config.def.h 2021-04-25 16:05:22.569759243 +0400
@@ -26,9 +26,10 @@ static const Rule rules[] = {
* 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 },
+ /* class instance title tags mask isfloating CenterThisWindow? monitor */
+ { "st", NULL, NULL, 0, 0, 1, -1 },
+ { "Gimp", NULL, NULL, 0, 1, 0, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 },
};
/* layout(s) */
diff -up dwm-6.2-orig/dwm.c dwm-6.2-modd/dwm.c
--- dwm-6.2-orig/dwm.c 2019-02-02 16:55:28.000000000 +0400
+++ dwm-6.2-modd/dwm.c 2021-04-25 16:06:15.368310756 +0400
@@ -92,7 +92,7 @@ struct Client {
int basew, baseh, incw, inch, maxw, maxh, minw, minh;
int bw, oldbw;
unsigned int tags;
- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, CenterThisWindow;
Client *next;
Client *snext;
Monitor *mon;
@@ -138,6 +138,7 @@ typedef struct {
const char *title;
unsigned int tags;
int isfloating;
+ int CenterThisWindow;
int monitor;
} Rule;
@@ -286,6 +287,7 @@ applyrules(Client *c)
/* rule matching */
c->isfloating = 0;
+ c->CenterThisWindow = 0;
c->tags = 0;
XGetClassHint(dpy, c->win, &ch);
class = ch.res_class ? ch.res_class : broken;
@@ -298,6 +300,7 @@ applyrules(Client *c)
&& (!r->instance || strstr(instance, r->instance)))
{
c->isfloating = r->isfloating;
+ c->CenterThisWindow = r->CenterThisWindow;
c->tags |= r->tags;
for (m = mons; m && m->num != r->monitor; m = m->next);
if (m)
@@ -1694,6 +1697,13 @@ tile(Monitor *m)
resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
ty += HEIGHT(c);
}
+
+ if (n == 1 && selmon->sel->CenterThisWindow)
+ resizeclient(selmon->sel,
+ (selmon->mw - selmon->mw * 0.5) / 2,
+ (selmon->mh - selmon->mh * 0.5) / 2,
+ selmon->mw * 0.5,
+ selmon->mh * 0.5);
}
void

View File

@@ -0,0 +1,41 @@
From 630859138bb960e2aea41d47e68c48ec020daf5c Mon Sep 17 00:00:00 2001
From: ben evolver <>
Date: Sun, 17 Apr 2022 08:07:24 -0500
Subject: [PATCH] add alternative symbols for nr. of active clients in monocle layout
---
config.def.h | 4 ++++
dwm.c | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/config.def.h b/config.def.h
index a2ac963..f49dfdf 100644
--- a/config.def.h
+++ b/config.def.h
@@ -44,6 +44,10 @@ static const Layout layouts[] = {
{ "[M]", monocle },
};
+/* custom symbols for nr. of clients in monocle layout */
+/* when clients >= LENGTH(monocles), uses the last element */
+static const char *monocles[] = { "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]", "[9+]" };
+
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
diff --git a/dwm.c b/dwm.c
index 0fc328a..e47ba70 100644
--- a/dwm.c
+++ b/dwm.c
@@ -1116,7 +1116,7 @@ monocle(Monitor *m)
if (ISVISIBLE(c))
n++;
if (n > 0) /* override layout symbol */
- snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "%s", monocles[MIN(n, LENGTH(monocles)) - 1]);
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);
}
--
2.35.1

View File

@@ -0,0 +1,120 @@
From 33c7811ca7280be7890851f5a83fa8d1a3313374 Mon Sep 17 00:00:00 2001
From: Sebastian LaVine <mail@smlavine.com>
Date: Wed, 14 Jul 2021 11:22:34 -0400
Subject: [PATCH] Set new lockfullscreen variable to 0
This more properly fixes the problem introduced by 67d76bd than the
previous patch revision does.
---
config.def.h | 2 +-
dwm.c | 28 ++--------------------------
2 files changed, 3 insertions(+), 27 deletions(-)
diff --git a/config.def.h b/config.def.h
index a2ac963..1b46cb4 100644
--- a/config.def.h
+++ b/config.def.h
@@ -35,7 +35,7 @@ static const Rule rules[] = {
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 int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
+static const int lockfullscreen = 0; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
diff --git a/dwm.c b/dwm.c
index 5e4d494..968e256 100644
--- a/dwm.c
+++ b/dwm.c
@@ -522,7 +522,7 @@ clientmessage(XEvent *e)
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)));
+ || cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */));
} else if (cme->message_type == netatom[NetActiveWindow]) {
if (c != selmon->sel && !c->isurgent)
seturgent(c, 1);
@@ -552,7 +552,6 @@ void
configurenotify(XEvent *e)
{
Monitor *m;
- Client *c;
XConfigureEvent *ev = &e->xconfigure;
int dirty;
@@ -565,9 +564,6 @@ configurenotify(XEvent *e)
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);
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
}
focus(NULL);
@@ -1144,8 +1140,6 @@ movemouse(const Arg *arg)
if (!(c = selmon->sel))
return;
- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
- return;
restack(selmon);
ocx = c->x;
ocy = c->y;
@@ -1299,8 +1293,6 @@ resizemouse(const Arg *arg)
if (!(c = selmon->sel))
return;
- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
- return;
restack(selmon);
ocx = c->x;
ocy = c->y;
@@ -1477,24 +1469,10 @@ setfullscreen(Client *c, int fullscreen)
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);
}
}
@@ -1619,7 +1597,7 @@ showhide(Client *c)
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)
+ if (!c->mon->lt[c->mon->sellt]->arrange || c->isfloating)
resize(c, c->x, c->y, c->w, c->h, 0);
showhide(c->snext);
} else {
@@ -1713,8 +1691,6 @@ 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,
--
2.32.0

View File

@@ -0,0 +1,266 @@
From e6c2d5fdc6010a22d6cd74485cb0b3e74467d0da Mon Sep 17 00:00:00 2001
From: braunbearded <braunbearded1@gmail.com>
Date: Wed, 29 Jul 2020 18:37:47 +0200
Subject: [PATCH 1/4] chain key bindings
---
dwm.c | 34 +++++++++++++++++++++++++++++-----
1 file changed, 29 insertions(+), 5 deletions(-)
diff --git a/dwm.c b/dwm.c
index 9fd0286..7298c5e 100644
--- a/dwm.c
+++ b/dwm.c
@@ -101,6 +101,7 @@ struct Client {
typedef struct {
unsigned int mod;
+ KeySym chain;
KeySym keysym;
void (*func)(const Arg *);
const Arg arg;
@@ -268,6 +269,7 @@ static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
+static KeySym keychain = -1;
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -954,13 +956,18 @@ grabkeys(void)
unsigned int i, j;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
KeyCode code;
+ KeyCode chain;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < LENGTH(keys); i++)
- if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
+ if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) {
+ if (keys[i].chain != -1 &&
+ ((chain = XKeysymToKeycode(dpy, keys[i].chain))))
+ code = chain;
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
True, GrabModeAsync, GrabModeAsync);
+ }
}
}
@@ -989,14 +996,31 @@ keypress(XEvent *e)
unsigned int i;
KeySym keysym;
XKeyEvent *ev;
+ int current = 0;
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)
+ for (i = 0; i < LENGTH(keys); i++) {
+ if (keysym == keys[i].keysym && keys[i].chain == -1
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+ && keys[i].func)
+ keys[i].func(&(keys[i].arg));
+ else if (keysym == keys[i].chain && keychain == -1
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+ && keys[i].func) {
+ current = 1;
+ keychain = keysym;
+ XGrabKey(dpy, AnyKey, AnyModifier, root, True, GrabModeAsync,
+ GrabModeAsync);
+ } else if (!current && keysym == keys[i].keysym
+ && keys[i].chain == keychain
+ && keys[i].func)
keys[i].func(&(keys[i].arg));
+ }
+ if (!current) {
+ keychain = -1;
+ grabkeys();
+ }
}
void
--
2.28.0
From ad3d15cf7df3286d35728afef823c3163898e2db Mon Sep 17 00:00:00 2001
From: braunbearded <braunbearded1@gmail.com>
Date: Wed, 29 Jul 2020 18:38:15 +0200
Subject: [PATCH 2/4] update default bindings
---
config.def.h | 80 +++++++++++++++++++++++++++-------------------------
1 file changed, 41 insertions(+), 39 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..c7cab16 100644
--- a/config.def.h
+++ b/config.def.h
@@ -45,11 +45,11 @@ static const Layout layouts[] = {
/* key definitions */
#define MODKEY Mod1Mask
-#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} },
+#define TAGKEYS(CHAIN,KEY,TAG) \
+ { MODKEY, CHAIN, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask, CHAIN, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|ShiftMask, CHAIN, KEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask|ShiftMask, CHAIN, 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 } }
@@ -60,40 +60,42 @@ static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont,
static const char *termcmd[] = { "st", NULL };
static Key keys[] = {
- /* modifier key function argument */
- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
- { MODKEY, XK_b, togglebar, {0} },
- { MODKEY, XK_j, focusstack, {.i = +1 } },
- { MODKEY, XK_k, focusstack, {.i = -1 } },
- { MODKEY, XK_i, incnmaster, {.i = +1 } },
- { MODKEY, XK_d, incnmaster, {.i = -1 } },
- { MODKEY, XK_h, setmfact, {.f = -0.05} },
- { MODKEY, XK_l, setmfact, {.f = +0.05} },
- { MODKEY, XK_Return, zoom, {0} },
- { MODKEY, XK_Tab, view, {0} },
- { MODKEY|ShiftMask, XK_c, killclient, {0} },
- { MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
- { MODKEY, 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|ShiftMask, XK_comma, tagmon, {.i = -1 } },
- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
- 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} },
+ /* modifier chain key key function argument */
+ { MODKEY, -1, XK_p, spawn, {.v = dmenucmd } },
+ { MODKEY|ShiftMask, -1, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, -1, XK_b, togglebar, {0} },
+ { MODKEY, -1, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, -1, XK_k, focusstack, {.i = -1 } },
+ { MODKEY, -1, XK_i, incnmaster, {.i = +1 } },
+ { MODKEY, -1, XK_d, incnmaster, {.i = -1 } },
+ { MODKEY, -1, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, -1, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY, -1, XK_Return, zoom, {0} },
+ { MODKEY, -1, XK_Tab, view, {0} },
+ { MODKEY|ShiftMask, -1, XK_c, killclient, {0} },
+ { MODKEY, -1, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, -1, XK_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY, -1, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, -1, XK_space, setlayout, {0} },
+ { MODKEY|ShiftMask, -1, XK_space, togglefloating, {0} },
+ { MODKEY, -1, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, -1, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, -1, XK_comma, focusmon, {.i = -1 } },
+ { MODKEY, -1, XK_period, focusmon, {.i = +1 } },
+ { MODKEY|ShiftMask, -1, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, -1, XK_period, tagmon, {.i = +1 } },
+ TAGKEYS( -1, XK_1, 0)
+ TAGKEYS( -1, XK_2, 1)
+ TAGKEYS( -1, XK_3, 2)
+ TAGKEYS( -1, XK_4, 3)
+ TAGKEYS( -1, XK_5, 4)
+ TAGKEYS( -1, XK_6, 5)
+ TAGKEYS( -1, XK_7, 6)
+ TAGKEYS( -1, XK_8, 7)
+ TAGKEYS( -1, XK_9, 8)
+ { MODKEY|ShiftMask, -1, XK_q, quit, {0} },
+ { MODKEY, XK_a, XK_d, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_a, XK_t, spawn, {.v = termcmd } },
};
/* button definitions */
--
2.28.0
From e9f3eec82010fd6083dc57f058902a1aab2d14ea Mon Sep 17 00:00:00 2001
From: braunbearded <braunbearded1@gmail.com>
Date: Wed, 29 Jul 2020 19:07:07 +0200
Subject: [PATCH 3/4] fix bug for mod key ignore
---
dwm.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/dwm.c b/dwm.c
index 7298c5e..aee56d4 100644
--- a/dwm.c
+++ b/dwm.c
@@ -1013,6 +1013,7 @@ keypress(XEvent *e)
XGrabKey(dpy, AnyKey, AnyModifier, root, True, GrabModeAsync,
GrabModeAsync);
} else if (!current && keysym == keys[i].keysym
+ && keychain != -1
&& keys[i].chain == keychain
&& keys[i].func)
keys[i].func(&(keys[i].arg));
--
2.28.0
From 053e3a2b2ff87805a15f3fe2f82a7d8bf0ab9b7a Mon Sep 17 00:00:00 2001
From: braunbearded <braunbearded1@gmail.com>
Date: Wed, 29 Jul 2020 21:25:23 +0200
Subject: [PATCH 4/4] listen for all keys inkl modifier after prefix
---
dwm.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/dwm.c b/dwm.c
index aee56d4..dea8f6a 100644
--- a/dwm.c
+++ b/dwm.c
@@ -993,10 +993,11 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
void
keypress(XEvent *e)
{
- unsigned int i;
+ unsigned int i, j;
KeySym keysym;
XKeyEvent *ev;
int current = 0;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
ev = &e->xkey;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
@@ -1010,8 +1011,9 @@ keypress(XEvent *e)
&& keys[i].func) {
current = 1;
keychain = keysym;
- XGrabKey(dpy, AnyKey, AnyModifier, root, True, GrabModeAsync,
- GrabModeAsync);
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(dpy, AnyKey, 0 | modifiers[j], root,
+ True, GrabModeAsync, GrabModeAsync);
} else if (!current && keysym == keys[i].keysym
&& keychain != -1
&& keys[i].chain == keychain
--
2.28.0

View File

@@ -0,0 +1,95 @@
From 9a4037dc0ef56f91c009317e78e9e3790dafbb58 Mon Sep 17 00:00:00 2001
From: BrunoCooper17 <BrunoCooper17@outlook.com>
Date: Mon, 15 Nov 2021 14:04:53 -0600
Subject: [PATCH] MoveStack patch
This plugin allows you to move clients around in the stack and swap them
with the master. It emulates the behavior off mod+shift+j and mod+shift+k
in Xmonad. movestack(+1) will swap the client with the current focus with
the next client. movestack(-1) will swap the client with the current focus
with the previous client.
---
config.def.h | 3 +++
movestack.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+)
create mode 100644 movestack.c
diff --git a/config.def.h b/config.def.h
index a2ac963..33efa5b 100644
--- a/config.def.h
+++ b/config.def.h
@@ -60,6 +60,7 @@ 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 *termcmd[] = { "st", NULL };
+#include "movestack.c"
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
@@ -71,6 +72,8 @@ static Key keys[] = {
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
diff --git a/movestack.c b/movestack.c
new file mode 100644
index 0000000..520f4ae
--- /dev/null
+++ b/movestack.c
@@ -0,0 +1,48 @@
+void
+movestack(const Arg *arg) {
+ Client *c = NULL, *p = NULL, *pc = NULL, *i;
+
+ if(arg->i > 0) {
+ /* find the client after selmon->sel */
+ for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
+ if(!c)
+ for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
+
+ }
+ else {
+ /* find the client before selmon->sel */
+ for(i = selmon->clients; i != selmon->sel; i = i->next)
+ if(ISVISIBLE(i) && !i->isfloating)
+ c = i;
+ if(!c)
+ for(; i; i = i->next)
+ if(ISVISIBLE(i) && !i->isfloating)
+ c = i;
+ }
+ /* find the client before selmon->sel and c */
+ for(i = selmon->clients; i && (!p || !pc); i = i->next) {
+ if(i->next == selmon->sel)
+ p = i;
+ if(i->next == c)
+ pc = i;
+ }
+
+ /* swap c and selmon->sel selmon->clients in the selmon->clients list */
+ if(c && c != selmon->sel) {
+ Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next;
+ selmon->sel->next = c->next==selmon->sel?c:c->next;
+ c->next = temp;
+
+ if(p && p != c)
+ p->next = c;
+ if(pc && pc != selmon->sel)
+ pc->next = selmon->sel;
+
+ if(selmon->sel == selmon->clients)
+ selmon->clients = c;
+ else if(c == selmon->clients)
+ selmon->clients = selmon->sel;
+
+ arrange(selmon);
+ }
+}
\ No newline at end of file
--
2.33.1

View File

@@ -0,0 +1,59 @@
diff --git a/config.def.h b/config.def.h
index 1c0b587..3fb5cf8 100644
--- a/config.def.h
+++ b/config.def.h
@@ -21,6 +21,18 @@ static const char *colors[][3] = {
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+static const char *tagsel[][2] = {
+ { "#ffffff", "#ff0000" },
+ { "#ffffff", "#ff7f00" },
+ { "#000000", "#ffff00" },
+ { "#000000", "#00ff00" },
+ { "#ffffff", "#0000ff" },
+ { "#ffffff", "#4b0082" },
+ { "#ffffff", "#9400d3" },
+ { "#000000", "#ffffff" },
+ { "#ffffff", "#000000" },
+};
+
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
diff --git a/dwm.c b/dwm.c
index b0b3466..c16d5f5 100644
--- a/dwm.c
+++ b/dwm.c
@@ -264,6 +264,7 @@ static Atom wmatom[WMLast], netatom[NetLast];
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
+static Clr **tagscheme;
static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
@@ -717,7 +718,7 @@ drawbar(Monitor *m)
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_setscheme(drw, (m->tagset[m->seltags] & 1 << i ? tagscheme[i] : scheme[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,
@@ -1568,9 +1569,14 @@ setup(void)
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
cursor[CurMove] = drw_cur_create(drw, XC_fleur);
/* init appearance */
+ if (LENGTH(tags) > LENGTH(tagsel))
+ die("too few color schemes for the tags");
scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
for (i = 0; i < LENGTH(colors); i++)
scheme[i] = drw_scm_create(drw, colors[i], 3);
+ tagscheme = ecalloc(LENGTH(tagsel), sizeof(Clr *));
+ for (i = 0; i < LENGTH(tagsel); i++)
+ tagscheme[i] = drw_scm_create(drw, tagsel[i], 2);
/* init bars */
updatebars();
updatestatus();

View File

@@ -0,0 +1,139 @@
From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001
From: Christopher Drelich <cd@cdrakka.com>
Date: Wed, 23 May 2018 22:50:38 -0400
Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM
handlers.
Modified quit() to restart if it receives arg .i = 1
MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that.
Signal handlers were handled for SIGHUP and SIGTERM.
If dwm receives these signals it calls quit() with
arg .i = to 1 or 0, respectively.
To restart dwm:
MOD+CTRL+SHIFT+Q
or
kill -HUP dwmpid
To quit dwm cleanly:
MOD+SHIFT+Q
or
kill -TERM dwmpid
---
config.def.h | 1 +
dwm.1 | 10 ++++++++++
dwm.c | 22 ++++++++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/config.def.h b/config.def.h
index a9ac303..e559429 100644
--- a/config.def.h
+++ b/config.def.h
@@ -94,6 +94,7 @@ static Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
+ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
};
/* button definitions */
diff --git a/dwm.1 b/dwm.1
index 13b3729..36a331c 100644
--- a/dwm.1
+++ b/dwm.1
@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view.
.TP
.B Mod1\-Shift\-q
Quit dwm.
+.TP
+.B Mod1\-Control\-Shift\-q
+Restart dwm.
.SS Mouse commands
.TP
.B Mod1\-Button1
@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float
.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 SIGNALS
+.TP
+.B SIGHUP - 1
+Restart the dwm process.
+.TP
+.B SIGTERM - 15
+Cleanly terminate the dwm process.
.SH SEE ALSO
.BR dmenu (1),
.BR st (1)
diff --git a/dwm.c b/dwm.c
index bb95e26..286eecd 100644
--- a/dwm.c
+++ b/dwm.c
@@ -205,6 +205,8 @@ static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void sigchld(int unused);
+static void sighup(int unused);
+static void sigterm(int unused);
static void spawn(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
@@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[UnmapNotify] = unmapnotify
};
static Atom wmatom[WMLast], netatom[NetLast];
+static int restart = 0;
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e)
void
quit(const Arg *arg)
{
+ if(arg->i) restart = 1;
running = 0;
}
@@ -1536,6 +1540,9 @@ setup(void)
/* clean up any zombies immediately */
sigchld(0);
+ signal(SIGHUP, sighup);
+ signal(SIGTERM, sigterm);
+
/* init screen */
screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen);
@@ -1637,6 +1644,20 @@ sigchld(int unused)
}
void
+sighup(int unused)
+{
+ Arg a = {.i = 1};
+ quit(&a);
+}
+
+void
+sigterm(int unused)
+{
+ Arg a = {.i = 0};
+ quit(&a);
+}
+
+void
spawn(const Arg *arg)
{
if (arg->v == dmenucmd)
@@ -2139,6 +2160,7 @@ main(int argc, char *argv[])
setup();
scan();
run();
+ if(restart) execvp(argv[0], argv);
cleanup();
XCloseDisplay(dpy);
return EXIT_SUCCESS;
--
2.7.4

View File

@@ -0,0 +1,879 @@
diff --git a/config.def.h b/config.def.h
index 061ad66..a308a7e 100644
--- a/config.def.h
+++ b/config.def.h
@@ -3,6 +3,11 @@
/* 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 systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
+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" };
@@ -112,4 +117,3 @@ static const Button buttons[] = {
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
-
diff --git a/dwm.c b/dwm.c
index e5efb6a..79a46f5 100644
--- a/dwm.c
+++ b/dwm.c
@@ -57,12 +57,27 @@
#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 +156,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);
@@ -163,6 +184,7 @@ static void detachstack(Client *c);
static Monitor *dirtomon(int dir);
static void drawbar(Monitor *m);
static void drawbars(void);
+static int drawstatusbar(Monitor *m, int bh, char* text);
static void enternotify(XEvent *e);
static void expose(XEvent *e);
static void focus(Client *c);
@@ -172,6 +194,7 @@ 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);
@@ -189,13 +212,16 @@ static void pop(Client *c);
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);
@@ -207,6 +233,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 *m);
@@ -224,20 +251,25 @@ 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 char stext[1024];
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar height */
@@ -258,9 +290,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;
@@ -442,7 +475,7 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + TEXTW(selmon->ltsymbol))
click = ClkLtSymbol;
- else if (ev->x > selmon->ww - (int)TEXTW(stext))
+ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
click = ClkStatusText;
else
click = ClkWinTitle;
@@ -485,9 +518,16 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root);
while (mons)
cleanupmon(mons);
- for (i = 0; i < CurLast; i++)
+
+ 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++)
+ for (i = 0; i < LENGTH(colors) + 1; i++)
free(scheme[i]);
free(scheme);
XDestroyWindow(dpy, wmcheckwin);
@@ -516,9 +556,58 @@ 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]) {
@@ -571,7 +660,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);
@@ -656,6 +745,11 @@ destroynotify(XEvent *e)
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
+ else if ((c = wintosystrayicon(ev->window))) {
+ removesystrayicon(c);
+ resizebarwin(selmon);
+ updatesystray();
+ }
}
void
@@ -696,10 +790,119 @@ dirtomon(int dir)
return m;
}
+int
+drawstatusbar(Monitor *m, int bh, char* stext) {
+ int ret, i, w, x, len;
+ short isCode = 0;
+ char *text;
+ char *p;
+
+ len = strlen(stext) + 1 ;
+ if (!(text = (char*) malloc(sizeof(char)*len)))
+ die("malloc");
+ p = text;
+ memcpy(text, stext, len);
+
+ /* compute width of the status text */
+ w = 0;
+ i = -1;
+ while (text[++i]) {
+ if (text[i] == '^') {
+ if (!isCode) {
+ isCode = 1;
+ text[i] = '\0';
+ w += TEXTW(text) - lrpad;
+ text[i] = '^';
+ if (text[++i] == 'f')
+ w += atoi(text + ++i);
+ } else {
+ isCode = 0;
+ text = text + i + 1;
+ i = -1;
+ }
+ }
+ }
+ if (!isCode)
+ w += TEXTW(text) - lrpad;
+ else
+ isCode = 0;
+ text = p;
+
+ w += 2; /* 1px padding on both sides */
+ ret = m->ww - w;
+ x = m->ww - w - getsystraywidth();
+
+ drw_setscheme(drw, scheme[LENGTH(colors)]);
+ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg];
+ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg];
+ drw_rect(drw, x, 0, w, bh, 1, 1);
+ x++;
+
+ /* process status text */
+ i = -1;
+ while (text[++i]) {
+ if (text[i] == '^' && !isCode) {
+ isCode = 1;
+
+ text[i] = '\0';
+ w = TEXTW(text) - lrpad;
+ drw_text(drw, x, 0, w, bh, 0, text, 0);
+
+ x += w;
+
+ /* process code */
+ while (text[++i] != '^') {
+ if (text[i] == 'c') {
+ char buf[8];
+ memcpy(buf, (char*)text+i+1, 7);
+ buf[7] = '\0';
+ drw_clr_create(drw, &drw->scheme[ColFg], buf);
+ i += 7;
+ } else if (text[i] == 'b') {
+ char buf[8];
+ memcpy(buf, (char*)text+i+1, 7);
+ buf[7] = '\0';
+ drw_clr_create(drw, &drw->scheme[ColBg], buf);
+ i += 7;
+ } else if (text[i] == 'd') {
+ drw->scheme[ColFg] = scheme[SchemeNorm][ColFg];
+ drw->scheme[ColBg] = scheme[SchemeNorm][ColBg];
+ } else if (text[i] == 'r') {
+ int rx = atoi(text + ++i);
+ while (text[++i] != ',');
+ int ry = atoi(text + ++i);
+ while (text[++i] != ',');
+ int rw = atoi(text + ++i);
+ while (text[++i] != ',');
+ int rh = atoi(text + ++i);
+
+ drw_rect(drw, rx + x, ry, rw, rh, 1, 0);
+ } else if (text[i] == 'f') {
+ x += atoi(text + ++i);
+ }
+ }
+
+ text = text + i + 1;
+ i=-1;
+ isCode = 0;
+ }
+ }
+
+ if (!isCode) {
+ w = TEXTW(text) - lrpad;
+ drw_text(drw, x, 0, w, bh, 0, text, 0);
+ }
+
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ free(p);
+
+ return ret;
+}
+
void
drawbar(Monitor *m)
{
- int x, w, tw = 0;
+ int x, w, tw = 0, stw = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0;
@@ -708,13 +911,15 @@ drawbar(Monitor *m)
if (!m->showbar)
return;
+ if(showsystray && m == systraytomon(m) && !systrayonleft)
+ 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]);
- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
+ tw = m->ww - drawstatusbar(m, bh, stext);
}
+ resizebarwin(m);
for (c = m->clients; c; c = c->next) {
occ |= c->tags;
if (c->isurgent)
@@ -735,7 +940,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 - tw - x) > bh) {
+ if ((w = m->ww - tw - 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);
@@ -746,7 +951,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
@@ -783,8 +988,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
@@ -870,9 +1078,17 @@ getatomprop(Client *c, Atom prop)
unsigned char *p = NULL;
Atom da, atom = None;
- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
+ /* 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;
@@ -906,6 +1122,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)
{
@@ -1008,7 +1234,8 @@ 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);
@@ -1095,6 +1322,14 @@ 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) || wa.override_redirect)
return;
if (!wintoclient(ev->window))
@@ -1216,7 +1451,18 @@ propertynotify(XEvent *e)
Window trans;
XPropertyEvent *ev = &e->xproperty;
- if ((ev->window == root) && (ev->atom == XA_WM_NAME))
+ 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 */
@@ -1266,6 +1512,19 @@ 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)
{
@@ -1273,6 +1532,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) && !systrayonleft)
+ w -= getsystraywidth();
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
+}
+
void
resizeclient(Client *c, int x, int y, int w, int h)
{
@@ -1345,6 +1612,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)
{
@@ -1434,26 +1714,37 @@ 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;
}
@@ -1467,7 +1758,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,22 +1846,32 @@ setup(void)
wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
- netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
- netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[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);
- /* init cursors */
+ 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 *));
+ scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *));
+ scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3);
for (i = 0; i < LENGTH(colors); i++)
scheme[i] = drw_scm_create(drw, colors[i], 3);
+ /* init system tray */
+ updatesystray();
/* init bars */
updatebars();
updatestatus();
@@ -1699,7 +2000,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);
}
@@ -1795,11 +2107,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,
@@ -1810,10 +2129,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 +2314,125 @@ 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 sw = TEXTW(stext) - lrpad + systrayspacing;
+ unsigned int w = 1;
+
+ if (!showsystray)
+ return;
+ if (systrayonleft)
+ x -= sw + lrpad / 2;
+ 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 +2500,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 +2563,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)
{

View File

@@ -0,0 +1,47 @@
From 45a45a0e67f3841d8c4aed2c52b57c2a7ddf2a9a Mon Sep 17 00:00:00 2001
From: Jack Bird <jack.bird@durham.ac.uk>
Date: Sun, 15 Aug 2021 23:15:52 +0100
Subject: [PATCH] Updated title color patch for 7162335
---
config.def.h | 1 +
dwm.c | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/config.def.h b/config.def.h
index a2ac963..5b9ae00 100644
--- a/config.def.h
+++ b/config.def.h
@@ -16,6 +16,7 @@ static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
+ [SchemeTitle] = { col_gray4, col_cyan, col_cyan },
};
/* tagging */
diff --git a/dwm.c b/dwm.c
index 5e4d494..73d335e 100644
--- a/dwm.c
+++ b/dwm.c
@@ -59,7 +59,7 @@
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
-enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeTitle }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
@@ -731,7 +731,7 @@ drawbar(Monitor *m)
if ((w = m->ww - tw - x) > bh) {
if (m->sel) {
- drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
+ drw_setscheme(drw, scheme[m == selmon ? SchemeTitle : 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);
--
2.32.0

View File

@@ -0,0 +1,371 @@
diff --git a/config.def.h b/config.def.h
index a2ac963..322d181 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,6 +5,8 @@ static const unsigned int borderpx = 1; /* border pixel of 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 */
+#define ICONSIZE 16 /* icon size */
+#define ICONSPACING 5 /* space between icon and title */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
diff --git a/config.mk b/config.mk
index b6eb7e0..f3c01b0 100644
--- a/config.mk
+++ b/config.mk
@@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender -lImlib2
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
diff --git a/drw.c b/drw.c
index 4cdbcbe..9b474c5 100644
--- a/drw.c
+++ b/drw.c
@@ -4,6 +4,7 @@
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
+#include <Imlib2.h>
#include "drw.h"
#include "util.h"
@@ -71,6 +72,7 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->w = w;
drw->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+ drw->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, screen)), 0, NULL);
drw->gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
@@ -85,14 +87,18 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
drw->w = w;
drw->h = h;
+ if (drw->picture)
+ XRenderFreePicture(drw->dpy, drw->picture);
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+ drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, DefaultVisual(drw->dpy, drw->screen)), 0, NULL);
}
void
drw_free(Drw *drw)
{
+ XRenderFreePicture(drw->dpy, drw->picture);
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_fontset_free(drw->fonts);
@@ -236,6 +242,67 @@ drw_setscheme(Drw *drw, Clr *scm)
drw->scheme = scm;
}
+Picture
+drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) {
+ Pixmap pm;
+ Picture pic;
+ GC gc;
+
+ if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) {
+ XImage img = {
+ srcw, srch, 0, ZPixmap, src,
+ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
+ 32, 0, 32,
+ 0, 0, 0
+ };
+ XInitImage(&img);
+
+ pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32);
+ gc = XCreateGC(drw->dpy, pm, 0, NULL);
+ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch);
+ XFreeGC(drw->dpy, gc);
+
+ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
+ XFreePixmap(drw->dpy, pm);
+
+ XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0);
+ XTransform xf;
+ xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0;
+ xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0;
+ xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536;
+ XRenderSetPictureTransform(drw->dpy, pic, &xf);
+ } else {
+ Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src);
+ if (!origin) return None;
+ imlib_context_set_image(origin);
+ imlib_image_set_has_alpha(1);
+ Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth);
+ imlib_free_image_and_decache();
+ if (!scaled) return None;
+ imlib_context_set_image(scaled);
+ imlib_image_set_has_alpha(1);
+
+ XImage img = {
+ dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(),
+ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
+ 32, 0, 32,
+ 0, 0, 0
+ };
+ XInitImage(&img);
+
+ pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32);
+ gc = XCreateGC(drw->dpy, pm, 0, NULL);
+ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth);
+ imlib_free_image_and_decache();
+ XFreeGC(drw->dpy, gc);
+
+ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
+ XFreePixmap(drw->dpy, pm);
+ }
+
+ return pic;
+}
+
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
@@ -379,6 +446,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
return x + (render ? w : 0);
}
+void
+drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic)
+{
+ if (!drw)
+ return;
+ XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h);
+}
+
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
diff --git a/drw.h b/drw.h
index 4bcd5ad..71aefa2 100644
--- a/drw.h
+++ b/drw.h
@@ -21,6 +21,7 @@ typedef struct {
int screen;
Window root;
Drawable drawable;
+ Picture picture;
GC gc;
Clr *scheme;
Fnt *fonts;
@@ -49,9 +50,12 @@ void drw_cur_free(Drw *drw, Cur *cursor);
void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *drw, Clr *scm);
+Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h);
+
/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/dwm.c b/dwm.c
index a96f33c..033ccec 100644
--- a/dwm.c
+++ b/dwm.c
@@ -28,6 +28,8 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <limits.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <X11/cursorfont.h>
@@ -60,7 +62,7 @@
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
-enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
@@ -93,6 +95,7 @@ struct Client {
int bw, oldbw;
unsigned int tags;
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ unsigned int icw, ich; Picture icon;
Client *next;
Client *snext;
Monitor *mon;
@@ -170,6 +173,7 @@ 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 Picture geticonprop(Window w, unsigned int *icw, unsigned int *ich);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
@@ -214,6 +218,7 @@ 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 freeicon(Client *c);
static void unfocus(Client *c, int setfocus);
static void unmanage(Client *c, int destroyed);
static void unmapnotify(XEvent *e);
@@ -225,6 +230,7 @@ static void updatenumlockmask(void);
static void updatesizehints(Client *c);
static void updatestatus(void);
static void updatetitle(Client *c);
+static void updateicon(Client *c);
static void updatewindowtype(Client *c);
static void updatewmhints(Client *c);
static void view(const Arg *arg);
@@ -735,7 +741,8 @@ drawbar(Monitor *m)
if ((w = m->ww - tw - 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);
+ drw_text(drw, x, 0, w, bh, lrpad / 2 + (m->sel->icon ? m->sel->icw + ICONSPACING : 0), m->sel->name, 0);
+ if (m->sel->icon) drw_pic(drw, x + lrpad / 2, (bh - m->sel->ich) / 2, m->sel->icw, m->sel->ich, m->sel->icon);
if (m->sel->isfloating)
drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
} else {
@@ -875,6 +882,67 @@ getatomprop(Client *c, Atom prop)
return atom;
}
+static uint32_t prealpha(uint32_t p) {
+ uint8_t a = p >> 24u;
+ uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u;
+ uint32_t g = (a * (p & 0x00FF00u)) >> 8u;
+ return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u);
+}
+
+Picture
+geticonprop(Window win, unsigned int *picw, unsigned int *pich)
+{
+ int format;
+ unsigned long n, extra, *p = NULL;
+ Atom real;
+
+ if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType,
+ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
+ return None;
+ if (n == 0 || format != 32) { XFree(p); return None; }
+
+ unsigned long *bstp = NULL;
+ uint32_t w, h, sz;
+ {
+ unsigned long *i; const unsigned long *end = p + n;
+ uint32_t bstd = UINT32_MAX, d, m;
+ for (i = p; i < end - 1; i += sz) {
+ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; }
+ if ((sz = w * h) > end - i) break;
+ if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; }
+ }
+ if (!bstp) {
+ for (i = p; i < end - 1; i += sz) {
+ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; }
+ if ((sz = w * h) > end - i) break;
+ if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; }
+ }
+ }
+ if (!bstp) { XFree(p); return None; }
+ }
+
+ if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return None; }
+
+ uint32_t icw, ich;
+ if (w <= h) {
+ ich = ICONSIZE; icw = w * ICONSIZE / h;
+ if (icw == 0) icw = 1;
+ }
+ else {
+ icw = ICONSIZE; ich = h * ICONSIZE / w;
+ if (ich == 0) ich = 1;
+ }
+ *picw = icw; *pich = ich;
+
+ uint32_t i, *bstp32 = (uint32_t *)bstp;
+ for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = prealpha(bstp[i]);
+
+ Picture ret = drw_picture_create_resized(drw, (char *)bstp, w, h, icw, ich);
+ XFree(p);
+
+ return ret;
+}
+
int
getrootptr(int *x, int *y)
{
@@ -1034,6 +1102,7 @@ manage(Window w, XWindowAttributes *wa)
c->h = c->oldh = wa->height;
c->oldbw = wa->border_width;
+ updateicon(c);
updatetitle(c);
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
c->mon = t->mon;
@@ -1244,6 +1313,11 @@ propertynotify(XEvent *e)
if (c == c->mon->sel)
drawbar(c->mon);
}
+ else if (ev->atom == netatom[NetWMIcon]) {
+ updateicon(c);
+ if (c == c->mon->sel)
+ drawbar(c->mon);
+ }
if (ev->atom == netatom[NetWMWindowType])
updatewindowtype(c);
}
@@ -1560,6 +1634,7 @@ setup(void)
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
+ netatom[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", False);
netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
@@ -1752,6 +1827,15 @@ toggleview(const Arg *arg)
}
}
+void
+freeicon(Client *c)
+{
+ if (c->icon) {
+ XRenderFreePicture(dpy, c->icon);
+ c->icon = None;
+ }
+}
+
void
unfocus(Client *c, int setfocus)
{
@@ -1773,6 +1857,7 @@ unmanage(Client *c, int destroyed)
detach(c);
detachstack(c);
+ freeicon(c);
if (!destroyed) {
wc.border_width = c->oldbw;
XGrabServer(dpy); /* avoid race conditions */
@@ -2007,6 +2092,13 @@ updatetitle(Client *c)
strcpy(c->name, broken);
}
+void
+updateicon(Client *c)
+{
+ freeicon(c);
+ c->icon = geticonprop(c->win, &c->icw, &c->ich);
+}
+
void
updatewindowtype(Client *c)
{

42
aarch64dwm/transient.c Normal file
View File

@@ -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);
}

36
aarch64dwm/util.c Normal file
View File

@@ -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;
}

8
aarch64dwm/util.h Normal file
View File

@@ -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);

BIN
aarch64dwm/util.o Normal file

Binary file not shown.

View File

@@ -0,0 +1,11 @@
#Colors for status bar, allows for various theme Colors
## Default Colors
black="#000000"
white="#ffffff"
grey="#555555"
green="#00ff00"
yellow="#ffff00"
red="#ff0000"

55
aarch64statusbar/install.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# DWM dependency check (uncomment if necessary)
# if ! pgrep -x "dwm" > /dev/null; then
# echo "DWM is not running. Please ensure that DWM is installed and is your window manager."
# exit 1
# fi
# Check for the status2D dependency
if command_exists dwm; then
echo "DWM is installed. Make sure you have the status2D patch applied for proper rendering."
fi
# Required packages list
required_packages=("grep" "gawk" "procps" "coreutils" "lm-sensors" "network-manager" "x11-xserver-utils")
# Install required packages
for pkg in "${required_packages[@]}"; do
if ! dpkg -s "$pkg" >/dev/null 2>&1; then
echo "Package $pkg is not installed. Installing..."
sudo apt update
sudo apt install -y "$pkg"
if [ $? -ne 0 ]; then
echo "Failed to install $pkg. Please install it manually, or its equivalent, and edit the source code."
exit 1
fi
else
echo "Package $pkg is already installed."
fi
done
# Copy statusbar.sh to /usr/local/bin and make it executable
sudo cp statusbar.sh /usr/local/bin/statusbar
sudo chmod +x /usr/local/bin/statusbar
# Create the directory and copy colorvars.sh
PREFIX="$HOME/.local/share/statusbar"
mkdir -p "$PREFIX"
cp colorvars.sh "$PREFIX"
# Setup the systemd service
SERVICE_DIR="$HOME/.config/systemd/user"
mkdir -p "$SERVICE_DIR"
cp statusbar.service "$SERVICE_DIR"
# Enable and restart the service
systemctl --user enable statusbar.service
systemctl --user restart statusbar.service
echo "Installation completed successfully. The statusbar is installed and the service has been enabled and restarted."

View File

@@ -0,0 +1,14 @@
[Unit]
[Unit]
Description=SystemBar for DWM from suckless
After=default.target
[Service]
Type=simple
ExecStart=/usr/local/bin/statusbar
Restart=on-failure
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/klein/.Xauthority
[Install]
WantedBy=default.target

234
aarch64statusbar/statusbar.sh Executable file
View File

@@ -0,0 +1,234 @@
#!/usr/bin/env bash
# Source color vars
source "$HOME/.local/share/statusbar/colorvars.sh"
# Define Basic Dimentions
base_x=0
base_y=2
max_height=15
bar_width=3
gap=1
cpu() {
local cpu_line1=$(grep '^cpu ' /proc/stat)
sleep 2
local cpu_line2=$(grep '^cpu ' /proc/stat)
local -a cpu1=(${cpu_line1//cpu/})
local -a cpu2=(${cpu_line2//cpu/})
local total1=0
local total2=0
local idle1=${cpu1[3]}
local idle2=${cpu2[3]}
for i in "${cpu1[@]}"; do
total1=$((total1 + i))
done
for i in "${cpu2[@]}"; do
total2=$((total2 + i))
done
local total_delta=$((total2 - total1))
local idle_delta=$((idle2 - idle1))
local usage=$((100 * (total_delta - idle_delta) / total_delta))
local usage_height=$(( (max_height * usage) / 100 ))
local usage_y=$((base_y + max_height - usage_height))
local color=$white
if [ $usage -gt 50 ]; then
color=$red
fi
local status_line=""
status_line+="^c${grey}^^r${base_x},${base_y},${bar_width},${max_height}^"
status_line+="^c${color}^^r${base_x},${usage_y},${bar_width},${usage_height}^"
status_line+="^d^^f4^" # buffer of 1
# local topcon=$( ps -eo %cpu,comm --sort=-%cpu | head -n 2 | tail -n 1 | awk '{print $2}')
# topcon="${topcon:0:5}" # trunkate output
echo "{CPU:${status_line}${usage}%"
}
ram() {
local m_mem=$(free -m)
local t_mem=$(echo "$m_mem" | awk '/^Mem:/ {print $2}')
local u_mem=$(echo "$m_mem" | awk '/^Mem:/ {print $3}')
local p_mem=$(awk "BEGIN {printf \"%.0f\", ($u_mem/$t_mem)*100}")
local usage_height=$((max_height * p_mem / 100))
local usage_y=$((base_y + max_height - usage_height))
local status_line=""
status_line+="^c$grey^^r$base_x,${base_y},${bar_width},${max_height}^"
status_line+="^c$white^^r${base_x},${usage_y},${bar_width},${usage_height}^"
status_line+="^d^^f4^" # buffer of 1
status_line+="${p_mem}%"
echo "M:$status_line"
}
swap() {
local m_swap=$(free -m)
local t_swap=$(echo "$m_swap" | awk '/^Swap:/ {print $2}')
local u_swap=$(echo "$m_swap" | awk '/^Swap:/ {print $3}')
if [[ "$u_swap" -eq 0 ]]; then
return
fi
local p_swap=$(awk "BEGIN {printf \"%.0f\", ($u_swap/$t_swap)*100}")
local usage_height=$((max_height * u_swap / 100))
local usage_y=$((base_y + max_height - usage_height))
local status_line=""
status_line+="^c$grey^^r$base_x,${base_y},${bar_width},${max_height}^"
status_line+="^c$white^^r${base_x},${usage_y},${bar_width},${usage_height}^"
status_line+="^d^^f4^" # buffer of 1
status_line+="${p_swap}%"
echo "|S:$status_line"
}
disk() {
local usage_p2=$(df -h | grep '/dev/mmcblk0p2' | awk '{print $5}' | tr -d '%')
local usage_p4=$(df -h | grep '/dev/mmcblk0p1' | awk '{print $5}' | tr -d '%')
if [[ ! "$usage_p2" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
usage_p2=0
fi
if [[ ! "$usage_p4" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
usage_p4=0
fi
local status_line=""
local -A usages=(
[p2]=$usage_p2
[p4]=$usage_p4
)
for partition in p2 p4; do
local percentage=${usages[$partition]}
local usage_height=$(($percentage * $max_height / 100))
local usage_y=$((base_y + max_height - usage_height))
status_line+="^c$grey^^r${base_x},${base_y},${bar_width},${max_height}^"
status_line+="^c$white^^r${base_x},${usage_y},${bar_width},${usage_height}^"
base_x=$((base_x + bar_width + gap))
done
status_line+="^d^^f8^" #Buffer of 1
echo "DF:${status_line}R:${usage_p2}%-U:${usage_p4}%"
}
cpu_temperature(){
local temp=$(awk '{print int($1 / 1000)}' /sys/class/thermal/thermal_zone0/temp)
local max_temp=80
local color=$white
if [ "$temp" -gt "$max_temp" ]; then
color=$red
elif [ "$temp" -lt "$max_temp" ]; then
color=$green
fi
local adj_y=5
local usage_height=$(($temp * $max_height / $max_temp))
local usage_y=$((adj_y + 10 - usage_height))
local temp_icon="^c$black^"
temp_icon+="^r2,${base_y},5,${max_height}^" #BG bar
temp_icon+="^c$color^"
temp_icon+="^r3,${usage_y},3,${usage_height}^" #C bar
temp_icon+="^c$black^"
temp_icon+="^r1,11,8,5^" #two rectangles
temp_icon+="^r2,10,6,7^" #to mimic a circle
temp_icon+="^d^^f11^" #Buffer of 1
echo "${temp_icon}${temp}°C"
}
battery() {
local throttled=$(cat /sys/devices/platform/soc/soc:firmware/get_throttled)
local capacity=0
local status=""
if [ $((throttled & 0x1)) -ne 0 ]; then
status+="Under-voltage detected"
fi
if [ $((throttled & 0x2)) -ne 0 ]; then
status+="ARM frequency capped"
fi
if [ $((throttled & 0x4)) -ne 0 ]; then
status+="Currently throttled"
fi
if [ $((throttled & 0x8)) -ne 0 ]; then
status+="Soft temperature limit active"
fi
if [ $((throttled & 0x10000)) -ne 0 ]; then
status+="Under-voltage has occurred since last reboot"
fi
if [ $((throttled & 0x20000)) -ne 0 ]; then
status+="ARM frequency capped has occurred since last reboot"
fi
if [ $((throttled & 0x40000)) -ne 0 ]; then
status+="Throttling has occurred since last reboot"
fi
if [ $((throttled & 0x80000)) -ne 0 ]; then
status+="Soft temperature limit has occurred since last reboot"
fi
if [ "$throttled" -eq 0 ]; then
status+="No issues detected"
capacity=100
fi
# Check the core voltage using vcgencmd
local volt=$(vcgencmd measure_volts core | awk -F'=' '{print $2}')
local fill_width=$(($capacity * 9 / 100)) # corresponds to width of bar
local battery_icon="^c$black^"
battery_icon+="^r1,6,13,8^"
battery_icon+="^c$grey^"
battery_icon+="^r3,8,9,4^"
battery_icon+="^c$green^"
battery_icon+="^r3,8,$fill_width,4^"
battery_icon+="^c$black^"
battery_icon+="^r14,7,2,4^"
battery_icon+="^d^^f17^" #Buffer of 1
echo "${battery_icon}${volt}"
}
wifi() {
local iface=$(ip -o link show | grep -v "lo:" | awk -F': ' '{print $2}');
local ssid=$(nmcli -t -f active,ssid dev wifi | grep '^yes' | cut -d':' -f2)
ssid="${ssid:-No WiFi}"
ssid="${ssid:0:5}"
local dwm=$(grep "$iface" /proc/net/wireless | awk '{ print int($4) }')
if [ "$ssid" = "No WiFi" ]; then
local signal=0
else
local signal_normalized=$(( (dwm + 90) * 100 / 60 ))
if [ $signal_normalized -gt 100 ]; then
signal=100
elif [ $signal_normalized -lt 0 ]; then
signal=0
else
signal=$signal_normalized
fi
fi
local color=$white
if [ $signal -ge 66 ]; then
color=$green
elif [ $signal -le 33 ]; then
color=$red
elif [ $signal -gt 33 ] && [ $signal -lt 66 ]; then
color=$yellow
fi
local max_bars=5
local bars_filled=$((signal / 20))
local wifi_icon="^c$color^"
for i in 1 2 3 4 5; do
local width=$(( (2 * i + 1) ))
local height=$(( (2 * i + 1) ))
local adj_y=$((max_height - height))
if [ $i -le $bars_filled ]; then
wifi_icon+="^c$color^"
else
wifi_icon+="^c$grey^"
fi
wifi_icon+="^r$((base_x + 2 * ( i - 3 ))),$((adj_y + 1)),$width,$height^"
done
wifi_icon+="^d^^f7^"
echo " ${wifi_icon}${ssid} ${signal}%}"
}
status(){
echo "$(cpu)|$(ram)|$(swap)$(disk)|$(cpu_temperature)|$(battery)|$(wifi)"
}
while true; do
DISPLAY=:0
xsetroot -name "$(status)"
done

30
dmenu/LICENSE Normal file
View File

@@ -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.

58
dmenu/Makefile Normal file
View File

@@ -0,0 +1,58 @@
# 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: dmenu stest
.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 clean dist install uninstall

24
dmenu/README Normal file
View File

@@ -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.

49
dmenu/arg.h Normal file
View File

@@ -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

28
dmenu/config.def.h Normal file
View File

@@ -0,0 +1,28 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */
static int topbar = 1; /* -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" },
[SchemeBorder] = { "#282a2e", NULL },
};
/* -l and -g options; controls number of lines and columns in grid if > 0 */
static unsigned int lines = 6;
static unsigned int columns = 4;
/*
* Characters not considered part of a word while deleting words
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";
/* Size of the window border */
static unsigned int border_width = 3;

27
dmenu/config.def.h.orig Normal file
View File

@@ -0,0 +1,27 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */
static int topbar = 1; /* -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 and -g options; controls number of lines and columns in grid if > 0 */
static unsigned int lines = 6;
static unsigned int columns = 4;
/*
* Characters not considered part of a word while deleting words
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";
/* Size of the window border */
static unsigned int border_width = 3;

10
dmenu/config.def.h.rej Normal file
View File

@@ -0,0 +1,10 @@
--- config.def.h
+++ config.def.h
@@ -22,3 +23,7 @@ static unsigned int lines = 0;
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";
+
+/* Size of the window border */
+static unsigned int border_width = 0;
+

27
dmenu/config.h Normal file
View File

@@ -0,0 +1,27 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */
static int topbar = 1; /* -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] = {
/* Scheme fg bg */
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
};
/* -l and -g options; controls number of lines and columns in grid if > 0 */
static unsigned int lines = 6;
static unsigned int columns = 4;
/*
* Characters not considered part of a word while deleting words
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";
/* Size of the window border */
static unsigned int border_width = 3;

32
dmenu/config.mk Normal file
View File

@@ -0,0 +1,32 @@
# dmenu version
VERSION = 5.3
# 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

BIN
dmenu/dmenu Executable file

Binary file not shown.

199
dmenu/dmenu.1 Normal file
View File

@@ -0,0 +1,199 @@
.TH DMENU 1 dmenu\-VERSION
.SH NAME
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
.RB [ \-g
.IR columns ]
.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 \-g " columns"
dmenu lists items in a grid with the given number of columns.
.TP
.BI \-l " lines"
dmenu lists items in a grid 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)

838
dmenu/dmenu.c Normal file
View File

@@ -0,0 +1,838 @@
/* 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 TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeBorder, 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 * columns * bh) - 1;
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 int
max_textw(void)
{
int len = 0;
for (struct item *item = items; item && item->text; item++)
len = MAX(TEXTW(item->text), len);
return len;
}
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 int
drawdate(int x, int y, int w)
{
char date[128];
time_t t = time(NULL);
struct tm *tm = localtime(&t);
/* Hour:Minute DayOfTheWeek DayOfTheMonth Month Year */
strftime(date, sizeof(date), "%H:%M %A %d %B %Y", tm);
drw_setscheme(drw, scheme[SchemeSel]);
int r = drw_text(drw, x, y, w, bh, lrpad / 2, date, 0);
return r;
}
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 grid */
int i = 0;
for (item = curr; item != next; item = item->right, i++)
drawitem(
item,
x + ((i / lines) * ((mw - x) / columns)),
y + (((i % lines) + 1) * bh),
(mw - x) / columns
);
} 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(">")));
drawdate(x, lines * bh, w);
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[64];
int len;
KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) {
default: /* XLookupNone, XBufferOverflow */
return;
case XLookupChars: /* composed string from input method */
goto insert;
case XLookupKeySym:
case XLookupBoth: /* a KeySym and a string are returned: use keysym */
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, itemsiz = 0, linesiz = 0;
ssize_t len;
/* read each line from stdin and add it to the item list */
for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
if (i + 1 >= itemsiz) {
itemsiz += 256;
if (!(items = realloc(items, itemsiz * sizeof(*items))))
die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
}
if (line[len - 1] == '\n')
line[len - 1] = '\0';
if (!(items[i].text = strdup(line)))
die("strdup:");
items[i].out = 0;
}
free(line);
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;
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
#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;
mw = MIN(MAX(max_textw() + promptw, 100), info[i].width);
x = info[i].x_org + ((info[i].width - mw) / 2);
y = info[i].y_org + ((info[i].height - mh) / 2);
XFree(info);
} else
#endif
{
if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx",
parentwin);
mw = MIN(MAX(max_textw() + promptw, 100), wa.width);
x = (wa.width - mw) / 2;
y = (wa.height - mh) / 2;
}
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, border_width,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
if (border_width)
XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
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) {
XReparentWindow(dpy, win, parentwin, x, y);
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], "-g")) { /* number of columns in grid */
columns = atoi(argv[++i]);
if (lines == 0) lines = 1;
} else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */
lines = atoi(argv[++i]);
if (columns == 0) columns = 1;
} 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 if (!strcmp(argv[i], "-bw"))
border_width = atoi(argv[++i]); /* border width */
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 */
}

820
dmenu/dmenu.c.orig Normal file
View File

@@ -0,0 +1,820 @@
/* 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 TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeBorder, 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 * columns * 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 int
max_textw(void)
{
int len = 0;
for (struct item *item = items; item && item->text; item++)
len = MAX(TEXTW(item->text), len);
return len;
}
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 grid */
int i = 0;
for (item = curr; item != next; item = item->right, i++)
drawitem(
item,
x + ((i / lines) * ((mw - x) / columns)),
y + (((i % lines) + 1) * bh),
(mw - x) / columns
);
} 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[64];
int len;
KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) {
default: /* XLookupNone, XBufferOverflow */
return;
case XLookupChars: /* composed string from input method */
goto insert;
case XLookupKeySym:
case XLookupBoth: /* a KeySym and a string are returned: use keysym */
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, itemsiz = 0, linesiz = 0;
ssize_t len;
/* read each line from stdin and add it to the item list */
for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
if (i + 1 >= itemsiz) {
itemsiz += 256;
if (!(items = realloc(items, itemsiz * sizeof(*items))))
die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
}
if (line[len - 1] == '\n')
line[len - 1] = '\0';
if (!(items[i].text = strdup(line)))
die("strdup:");
items[i].out = 0;
}
free(line);
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;
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
#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;
mw = MIN(MAX(max_textw() + promptw, 100), info[i].width);
x = info[i].x_org + ((info[i].width - mw) / 2);
y = info[i].y_org + ((info[i].height - mh) / 2);
XFree(info);
} else
#endif
{
if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx",
parentwin);
mw = MIN(MAX(max_textw() + promptw, 100), wa.width);
x = (wa.width - mw) / 2;
y = (wa.height - mh) / 2;
}
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, border_width,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
if (border_width)
XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
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) {
XReparentWindow(dpy, win, parentwin, x, y);
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], "-g")) { /* number of columns in grid */
columns = atoi(argv[++i]);
if (lines == 0) lines = 1;
} else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */
lines = atoi(argv[++i]);
if (columns == 0) columns = 1;
} 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 if (!strcmp(argv[i], "-bw"))
border_width = atoi(argv[++i]); /* border width */
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 */
}

20
dmenu/dmenu.c.rej Normal file
View File

@@ -0,0 +1,20 @@
--- dmenu.c
+++ dmenu.c
@@ -84,7 +84,7 @@ calcoffsets(void)
int i, n;
if (lines > 0)
- n = lines * bh;
+ n = (lines * bh) - 1;
else
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */
@@ -188,6 +204,8 @@ drawmenu(void)
/* draw vertical list */
for (item = curr; item != next; item = item->right)
drawitem(item, x, y += bh, mw - x);
+
+ drawdate(x, lines * bh, w);
} else if (matches) {
/* draw horizontal list */
x += inputw;

BIN
dmenu/dmenu.o Normal file

Binary file not shown.

13
dmenu/dmenu_path Executable file
View File

@@ -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

2
dmenu/dmenu_run Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

451
dmenu/drw.c Normal file
View File

@@ -0,0 +1,451 @@
/* 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 ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
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. */
static unsigned int nomatches[128], ellipsis_width;
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;
hash = (unsigned int)utf8codepoint;
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
h1 = (hash >> 17) % LENGTH(nomatches);
/* avoid expensive XftFontMatch call when we know we won't find a match */
if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
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[nomatches[h0] ? h1 : h0] = 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);
}

58
dmenu/drw.h Normal file
View File

@@ -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);

BIN
dmenu/drw.o Normal file

Binary file not shown.

View File

@@ -0,0 +1,37 @@
diff --git a/config.def.h b/config.def.h
index 1edb647..dd3eb31 100644
--- a/config.def.h
+++ b/config.def.h
@@ -21,3 +21,6 @@ static unsigned int lines = 0;
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";
+
+/* Size of the window border */
+static unsigned int border_width = 0;
diff --git a/dmenu.c b/dmenu.c
index 27b7a30..7c130fc 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -684,9 +684,11 @@ setup(void)
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,
+ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+ if (border_width)
+ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
XSetClassHint(dpy, win, &ch);
@@ -757,6 +759,8 @@ main(int argc, char *argv[])
colors[SchemeSel][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-w")) /* embedding window id */
embed = argv[++i];
+ else if (!strcmp(argv[i], "-bw"))
+ border_width = atoi(argv[++i]); /* border width */
else
usage();

View File

@@ -0,0 +1,71 @@
From 8dbea77ad7b320d9c7e07990274ea74835785084 Mon Sep 17 00:00:00 2001
From: Markus Hanetzok <markus@hanetzok.net>
Date: Sun, 14 May 2023 01:04:09 +0200
Subject: [PATCH] Makes border colors independent from SelBg
---
config.def.h | 5 +++++
dmenu.c | 9 +++++++--
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1edb647..ae41b06 100644
--- a/config.def.h
+++ b/config.def.h
@@ -12,6 +12,7 @@ static const char *colors[SchemeLast][2] = {
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
+ [SchemeBorder] = { "#cccccc", NULL },
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0;
@@ -21,3 +22,7 @@ static unsigned int lines = 0;
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";
+
+/* Size of the window border */
+static unsigned int border_width = 0;
+
diff --git a/dmenu.c b/dmenu.c
index 62f1089..7f063b6 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -26,7 +26,7 @@
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeOut, SchemeBorder, SchemeLast }; /* color schemes */
struct item {
char *text;
@@ -685,9 +685,11 @@ setup(void)
swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
- win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
+ win = XCreateWindow(dpy, root, x, y, mw, mh, border_width,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+ if (border_width)
+ XSetWindowBorder(dpy, win, scheme[SchemeBorder][ColFg].pixel);
XSetClassHint(dpy, win, &ch);
@@ -759,6 +761,8 @@ main(int argc, char *argv[])
colors[SchemeSel][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-w")) /* embedding window id */
embed = argv[++i];
+ else if (!strcmp(argv[i], "-bw"))
+ border_width = atoi(argv[++i]); /* border width */
else
usage();
@@ -795,3 +799,4 @@ main(int argc, char *argv[])
return 1; /* unreachable */
}
+
--
2.40.1

View File

@@ -0,0 +1,56 @@
diff --git a/dmenu.c b/dmenu.c
index 5e9c367..2268ea9 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -88,6 +88,15 @@ calcoffsets(void)
break;
}
+static int
+max_textw(void)
+{
+ int len = 0;
+ for (struct item *item = items; item && item->text; item++)
+ len = MAX(TEXTW(item->text), len);
+ return len;
+}
+
static void
cleanup(void)
{
@@ -598,6 +607,7 @@ setup(void)
bh = drw->fonts->h + 2;
lines = MAX(lines, 0);
mh = (lines + 1) * bh;
+ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
#ifdef XINERAMA
i = 0;
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
@@ -624,9 +634,9 @@ setup(void)
if (INTERSECT(x, y, 1, 1, info[i]))
break;
- x = info[i].x_org;
- y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
- mw = info[i].width;
+ mw = MIN(MAX(max_textw() + promptw, 100), info[i].width);
+ x = info[i].x_org + ((info[i].width - mw) / 2);
+ y = info[i].y_org + ((info[i].height - mh) / 2);
XFree(info);
} else
#endif
@@ -634,11 +644,10 @@ setup(void)
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;
+ mw = MIN(MAX(max_textw() + promptw, 100), wa.width);
+ x = (wa.width - mw) / 2;
+ y = (wa.height - mh) / 2;
}
- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
inputw = MIN(inputw, mw/3);
match();

View File

@@ -0,0 +1,57 @@
From 64fb38e3778addc0272eb8793deb909e639e5746 Mon Sep 17 00:00:00 2001
From: piotr-marendowski <piotr-marendowski@tutanota.com>
Date: Thu, 6 Jul 2023 10:03:03 +0000
Subject: [PATCH] Displays date and time at the bottom of the vertical layout.
---
dmenu.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/dmenu.c b/dmenu.c
index 7cf253b..5428f67 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -84,7 +84,7 @@ calcoffsets(void)
int i, n;
if (lines > 0)
- n = lines * bh;
+ n = (lines * bh) - 1;
else
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */
@@ -143,6 +143,22 @@ drawitem(struct item *item, int x, int y, int w)
return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
}
+static int
+drawdate(int x, int y, int w)
+{
+ char date[128];
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+
+ /* Hour:Minute DayOfTheWeek DayOfTheMonth Month Year */
+ strftime(date, sizeof(date), "%H:%M %A %d %B %Y", tm);
+
+ drw_setscheme(drw, scheme[SchemeSel]);
+
+ int r = drw_text(drw, x, y, w, bh, lrpad / 2, date, 0);
+ return r;
+}
+
static void
drawmenu(void)
{
@@ -172,6 +188,8 @@ drawmenu(void)
/* draw vertical list */
for (item = curr; item != next; item = item->right)
drawitem(item, x, y += bh, mw - x);
+
+ drawdate(x, lines * bh, w);
} else if (matches) {
/* draw horizontal list */
x += inputw;
--
2.41.0

View File

@@ -0,0 +1,107 @@
From 39ab9676914bd0d8105d0f96bbd7611a53077438 Mon Sep 17 00:00:00 2001
From: Miles Alan <m@milesalan.com>
Date: Sat, 4 Jul 2020 11:19:04 -0500
Subject: [PATCH] Add -g option to display entries in the given number of grid
columns
This option can be used in conjunction with -l to format dmenu's options in
arbitrary size grids. For example, to create a 4 column by 6 line grid, you
could use: dmenu -g 4 -l 6
---
config.def.h | 3 ++-
dmenu.1 | 7 ++++++-
dmenu.c | 22 ++++++++++++++++------
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1edb647..96cf3c9 100644
--- a/config.def.h
+++ b/config.def.h
@@ -13,8 +13,9 @@ static const char *colors[SchemeLast][2] = {
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
};
-/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+/* -l and -g options; controls number of lines and columns in grid if > 0 */
static unsigned int lines = 0;
+static unsigned int columns = 0;
/*
* Characters not considered part of a word while deleting words
diff --git a/dmenu.1 b/dmenu.1
index 323f93c..d0a734a 100644
--- a/dmenu.1
+++ b/dmenu.1
@@ -4,6 +4,8 @@ dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
+.RB [ \-g
+.IR columns ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@@ -47,8 +49,11 @@ is faster, but will lock up X until stdin reaches end\-of\-file.
.B \-i
dmenu matches menu items case insensitively.
.TP
+.BI \-g " columns"
+dmenu lists items in a grid with the given number of columns.
+.TP
.BI \-l " lines"
-dmenu lists items vertically, with the given number of lines.
+dmenu lists items in a grid with the given number of lines.
.TP
.BI \-m " monitor"
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
diff --git a/dmenu.c b/dmenu.c
index 6b8f51b..d79b6bb 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -77,7 +77,7 @@ calcoffsets(void)
int i, n;
if (lines > 0)
- n = lines * bh;
+ n = lines * columns * bh;
else
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */
@@ -152,9 +152,15 @@ drawmenu(void)
}
if (lines > 0) {
- /* draw vertical list */
- for (item = curr; item != next; item = item->right)
- drawitem(item, x, y += bh, mw - x);
+ /* draw grid */
+ int i = 0;
+ for (item = curr; item != next; item = item->right, i++)
+ drawitem(
+ item,
+ x + ((i / lines) * ((mw - x) / columns)),
+ y + (((i % lines) + 1) * bh),
+ (mw - x) / columns
+ );
} else if (matches) {
/* draw horizontal list */
x += inputw;
@@ -708,9 +714,13 @@ main(int argc, char *argv[])
} else if (i + 1 == argc)
usage();
/* these options take one argument */
- else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
+ else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */
+ columns = atoi(argv[++i]);
+ if (lines == 0) lines = 1;
+ } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */
lines = atoi(argv[++i]);
- else if (!strcmp(argv[i], "-m"))
+ if (columns == 0) columns = 1;
+ } 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];
--
2.23.1

BIN
dmenu/stest Executable file

Binary file not shown.

90
dmenu/stest.1 Normal file
View File

@@ -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)

109
dmenu/stest.c Normal file
View File

@@ -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;
}

BIN
dmenu/stest.o Normal file

Binary file not shown.

36
dmenu/util.c Normal file
View File

@@ -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;
}

9
dmenu/util.h Normal file
View File

@@ -0,0 +1,9 @@
/* 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))
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);

BIN
dmenu/util.o Normal file

Binary file not shown.

2
pinentry-dmenu/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
build
*.o

280
pinentry-dmenu/LICENSE Normal file
View File

@@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

66
pinentry-dmenu/Makefile Normal file
View File

@@ -0,0 +1,66 @@
# pinentry-dmenu - dmenu-like stupid pin entry
# See LICENSE file for copyright and license details.
include config.mk
SRC = pinentry-dmenu.c drw.c util.c
OBJ = ${SRC:.c=.o}
OBJ_PIN = pinentry/pinentry.o pinentry/util.o pinentry/password-cache.o pinentry/argparse.o pinentry/secmem.o
all: options pinentry-dmenu
options:
@echo pinentry-dmenu build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
config.h:
@echo creating $@ from config.def.h
@cp config.def.h $@
${OBJ}: config.h config.mk drw.h
pinentry:
$(MAKE) -C pinentry
pinentry-dmenu: pinentry pinentry-dmenu.o drw.o util.o
@echo CC -o $@
@${CC} -o $@ ${OBJ} ${OBJ_PIN} ${LDFLAGS} -lassuan -lgpgme -lgpg-error -lconfig
clean:
@echo cleaning
@rm -f pinentry-dmenu ${OBJ}
$(MAKE) -C pinentry/ clean
dist: clean
@echo creating dist tarball
@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
@echo installing executable to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f pinentry-dmenu ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/pinentry-dmenu
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g;s/DATE/${DATE}/g;s/BUGREPORT/${BUGREPORT}/g" < pinentry-dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/pinentry-dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/pinentry-dmenu.1
uninstall:
@echo removing executable from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/pinentry-dmenu
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/pinentry-dmenu.1
.PHONY: all options clean dist install pinentry uninstall

71
pinentry-dmenu/README.md Normal file
View File

@@ -0,0 +1,71 @@
pinentry-dmenu
==============
pinentry-dmenu is a pinentry program with the charm of [dmenu](https://tools.suckless.org/dmenu).
This program is a fork from [spine](https://gitgud.io/zavok/spine.git) which is also a fork from [dmenu](https://tools.suckless.org/dmenu).
NO FURTHER DEVELOPMENT
----------------------
This project is no longer under development. If you have another opinion feel free to fork it.
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
Config
------
To use pinentry-dmenu add in `~/.gnupg/gpg-agent.conf`:
pinentry-program <absolut path to pinentry-dmenu>
The config is located in `~/.gnupg/pinentry-dmenu.conf`.
Parameter | Default | Description
:------------------ |:----------------- |:-----------
asterisk | * | Defines the symbol which is showed for each typed character
bottom | false | pinentry-dmenu appears at the bottom of the screen
min_password_length | 32 | The minimal space of the password field. This value has affect to the description field after the password field
monitor | -1 | pinentry-dmenu is displayed on the monitor number supplied. Monitor numbers are starting from 0
prompt | "" | Defines the prompt to be displayed to the left of the input field
font | monospace:size=10 | Defines the font or font set used
prompt_bg | #bbbbbb | Defines the prompt background color
prompt_fg | #222222 | Defines the prompt foreground color
normal_bg | #bbbbbb | Defines the normal background color
normal_fg | #222222 | Defines the normal foreground color
select_bg | #eeeeee | Defines the selected background color
select_fg | #005577 | Defines the selected foreground color
desc_bg | #bbbbbb | Defines the description background color
desc_fg | #222222 | Defines the description foreground color
embedded | false | Embed into window
Example
-------
```
asterisk= "# ";
prompt = "$";
font = "Noto Sans UI:size=13";
prompt_fg = "#eeeeee";
prompt_bg = "#d9904a";
normal_fg = "#ffffff";
normal_bg = "#000000";
select_fg = "#eeeeee";
select_bg = "#d9904a";
desc_fg = "#eeeeee";
desc_bg = "#d9904a";
```

21
pinentry-dmenu/config.h Normal file
View File

@@ -0,0 +1,21 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */
static int bottom = 0;
static int embedded = 0;
static int minpwlen = 32;
static int mon = -1;
static int lineheight = 0;
static int min_lineheight = 8;
static const char *asterisk = "*";
static const char *fonts[] = {
"monospace:size=10"
};
static const char *prompt = NULL;
static const char *colors[SchemeLast][4] = {
[SchemePrompt] = { "#bbbbbb", "#222222" },
[SchemeNormal] = { "#bbbbbb", "#222222" },
[SchemeSelect] = { "#eeeeee", "#005577" },
[SchemeDesc] = { "#bbbbbb", "#222222" }
};

33
pinentry-dmenu/config.mk Normal file
View File

@@ -0,0 +1,33 @@
# Pinentry settings
DATE = $$(date +'%B %Y')
VERSION = 0.1
BUGREPORT = https:\/\/github.com\/ritze\/pinentry-dmenu
# 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_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} -DPACKAGE_VERSION=\"${VERSION}\" -DPACKAGE_BUGREPORT=\"${BUGREPORT}\"
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
# Compiler and linker
CC = cc

421
pinentry-dmenu/drw.c Normal file
View File

@@ -0,0 +1,421 @@
/* 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.");
}
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);
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);
}

57
pinentry-dmenu/drw.h Normal file
View File

@@ -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 }; /* 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);

BIN
pinentry-dmenu/pinentry-dmenu Executable file

Binary file not shown.

View File

@@ -0,0 +1,228 @@
.TH PINENTRY-DMENU 1 "DATE" pinentry-dmenu\-VERSION "pinentry-dmenu Manual"
.SH NAME
pinentry-dmenu - a pinentry program with the charm of dmenu
.SH DESCRIPTION
.B pinentry-dmenu
is a dmenu- and pinentry-based passphrase dialog called from the
.BR gpg-agent (1)
daemon. It is not intended to be invoked directly.
.SH SYNOPSIS
Set the
.B pinentry-program
in
.IR ~/.gnupg/gpg-agent.conf
to
.B pinentry-dmenu
to use the program as the regular dialog for
.BR gpg-agent .
.PP
The configuration is placed in
.IR ~/.gnupg/pinentry-dmenu.conf .
You can change the path to the config file with the environment variable
.IR GNUPGHOME .
.SH OPTIONS
.TP
.BI "asterisk =" " *"
Defines the symbol which is showed for each typed character.
.TP
.BI "bottom =" " false"
pinentry-dmenu appears at the bottom of the screen.
.TP
.BI "min_password_length =" " 32"
The minimal space of the password field. This value has affect to the description field after the password field.
.TP
.BI "height =" " 8"
Height of pinentry-dmenu in pixels (no less than 8)
.TP
.BI "monitor =" " -1"
pinentry-dmenu is displayed on the monitor number supplied. Monitor numbers are starting from 0.
.TP
.BI "prompt =" " """"
Defines the prompt to be displayed to the left of the input field.
.TP
.BI "font =" " monospace:size=10"
Defines the font or font set used.
.TP
.BI "prompt_bg =" " #bbbbbb"
Defines the prompt background color.
.IR #RGB ,
.I #RRGGBB
and X color names are supported.
.TP
.BI "prompt_fg =" " #222222"
Defines the prompt foreground color.
.TP
.BI "normal_bg =" " #bbbbbb"
Defines the normal background color.
.TP
.BI "normal_fg =" " #222222"
Defines the normal foreground color.
.TP
.BI "select_bg =" " #eeeeee"
Defines the selected background color.
.TP
.BI "select_fg =" " #005577"
Defines the selected foreground color.
.TP
.BI "desc_bg =" " #bbbbbb"
Defines the description background color.
.TP
.BI "desc_fg =" " #222222"
Defines the description foreground color.
.TP
.BI "embedded =" " false"
Embed into window.
.SH USAGE
pinentry-dmenu is completely controlled by the keyboard.
.TP
.B Return
Confirm input
.TP
.B Ctrl-Return
Confirm input
.TP
.B Shift\-Return
Confirm input
.TP
.B Escape
Cancel input
.TP
.B C\-c
Escape
.SS Confirm Mode
.TP
.B Down
Right
.TP
.B End
Right
.TP
.B Home
Left
.TP
.B Next
Right
.TP
.B Prior
Left
.TP
.B Up
Left
.TP
.B g
Cancel input
.TP
.B G
Cancel input
.TP
.B h
Left
.TP
.B j
Left
.TP
.B k
Right
.TP
.B l
Right
.TP
.B n
Confirm with no
.TP
.B N
Confirm with no
.TP
.B y
Confirm with yes
.TP
.B Y
Confirm with yes
.SS Pin Mode
.TP
.B End
Move cursor to the line end
.TP
.B Home
Move cursor to the line begin
.TP
.B C\-a
Home
.TP
.B C\-b
Left
.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\-k
Delete line right
.TP
.B C\-u
Delete line left
.TP
.B C\-v
Paste from primary X selection
.SH EXAMPLES
.sp
.if n \{
.RS 4
.\}
.nf
asterisk= "# ";
prompt = "$";
font = "Noto Sans UI:size=13";
prompt_fg = "#eeeeee";
prompt_bg = "#d9904a";
normal_fg = "#ffffff";
normal_bg = "#000000";
select_fg = "#eeeeee";
select_bg = "#d9904a";
desc_fg = "#eeeeee";
desc_bg = "#d9904a";
.SH AUTHORS
.B pinentry-dmenu
is a fork of
.B dmenu
<https://tools.suckless.org/dmenu>
and uses the api of
.B pinentry
, a GnuPG tool.
.B pinentry-dmenu
itself was written by Moritz Lüdecke <ritze@skweez.net>.
.SH REPORTING BUGS
Report pinentry-dmenu bugs to <BUGREPORT>
.SH SEE ALSO
.BR dmenu (1),
.BR dwm (1),
.BR gpg-agent (1)

View File

@@ -0,0 +1,803 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <libconfig.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <pwd.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"
#include "pinentry/pinentry.h"
#include "pinentry/memory.h"
#define CONFIG_DIR "/.gnupg"
#define CONFIG_FILE "/pinentry-dmenu.conf"
#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)
#define MINDESCLEN 8
enum { SchemePrompt, SchemeNormal, SchemeSelect, SchemeDesc, SchemeLast };
enum { WinPin, WinConfirm };
enum { Ok, NotOk, Cancel };
enum { Nothing, Yes, No };
static int bh, mw, mh;
static int sel;
static int promptw, pdescw;
/* Sum of left and right padding */
static int lrpad;
static size_t cursor;
static int screen;
static char* pin;
static int pin_len;
static char* pin_repeat;
static int pin_repeat_len;
static int repeat;
static Atom clip, utf8;
static Display *dpy;
static Window root, parentwin, win;
static XIC xic;
static Drw *drw;
static Clr *scheme[SchemeLast];
static int timed_out;
static int winmode;
pinentry_t pinentry_info;
#include "config.h"
static int
drawitem(const char* text, Bool sel, int x, int y, int w) {
unsigned int i = (sel) ? SchemeSelect : SchemeNormal;
drw_setscheme(drw, scheme[i]);
return drw_text(drw, x, y, w, bh, lrpad / 2, text, 0);
}
static void
grabfocus(void) {
Window focuswin;
int i, revertwin;
for (i = 0; i < 100; ++i) {
XGetInputFocus(dpy, &focuswin, &revertwin);
if (focuswin == win) {
return;
}
XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
usleep(1000);
}
die("cannot grab focus");
}
static void
grabkeyboard(void) {
int i;
if (embedded) {
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;
}
usleep(1000);
}
die("cannot grab keyboard");
}
static size_t
nextrune(int cursor, 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 && (pin[n] & 0xc0) == 0x80;
n += inc);
return n;
}
static void
setup_pin(char* pin_ptr, int len, int reset) {
pin = pin_ptr;
pin_len = len;
if (reset) {
promptw = (prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
cursor = 0;
if (pin) {
pin[0] = '\0';
}
}
}
static void
insert(const char *str, ssize_t n) {
size_t len = strlen(pin);
// FIXME: Pinentry crashes when increasing the pin buffer the second time.
// Other pinentry programs has a limited passwort field length.
if (len + n > pin_len - 1) {
if (repeat) {
pin_repeat_len = 2 * pin_repeat_len;
pin_repeat = secmem_realloc(pin_repeat, pin_repeat_len);
setup_pin(pin_repeat, pin_repeat_len, 0);
if (!pin_repeat) {
pin_len = 0;
}
} else {
if (!pinentry_setbufferlen(pinentry_info, 2 * pinentry_info->pin_len)) {
pin_len = 0;
} else {
setup_pin(pinentry_info->pin, pinentry_info->pin_len, 0);
}
}
if (pin_len == 0) {
printf("Error: Couldn't allocate secure memory\n");
return;
}
}
/* Move existing text out of the way, insert new text, and update cursor */
memmove(&pin[cursor + n], &pin[cursor], pin_len - cursor - MAX(n, 0));
if (n > 0) {
memcpy(&pin[cursor], str, n);
}
cursor += n;
pin[len + n] = '\0';
}
static void
drawwin(void) {
unsigned int curpos;
int x = 0, fh = drw->fonts->h, pb, pbw = 0, i;
size_t asterlen = strlen(asterisk);
size_t pdesclen;
int leftinput;
char* censort;
char* pprompt = (repeat) ? pinentry_info->repeat_passphrase : pinentry_info->prompt;
int ppromptw = (pprompt) ? TEXTW(pprompt) : 0;
unsigned int censortl = minpwlen * TEXTW(asterisk) / strlen(asterisk);
unsigned int confirml = TEXTW(" YesNo ") + 3 * lrpad;
drw_setscheme(drw, scheme[SchemeNormal]);
drw_rect(drw, 0, 0, mw, mh, 1, 1);
if (prompt) {
drw_setscheme(drw, scheme[SchemePrompt]);
x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
}
if (pprompt) {
drw_setscheme(drw, scheme[SchemePrompt]);
drw_text(drw, x, 0, ppromptw, bh, lrpad / 2, pprompt, 0);
x += ppromptw;
}
if (pinentry_info->description) {
pb = mw - x;
pdesclen = strlen(pinentry_info->description);
if (pb > 0) {
pb -= (winmode == WinPin) ? censortl : confirml;
pbw = MINDESCLEN * pdescw / pdesclen;
pbw = MIN(pbw, pdescw);
if (pb >= pbw) {
pbw = MAX(pbw, pdescw);
pbw = MIN(pbw, pb);
pb = mw - pbw;
for (i = 0; i < pdesclen; i++) {
if (pinentry_info->description[i] == '\n') {
pinentry_info->description[i] = ' ';
}
}
drw_setscheme(drw, scheme[SchemeDesc]);
drw_text(drw, pb, 0, pbw, bh, lrpad / 2, pinentry_info->description,
0);
} else {
pbw = 0;
}
}
}
/* Draw input field */
drw_setscheme(drw, scheme[SchemeNormal]);
if (winmode == WinPin) {
censort = ecalloc(1, asterlen * pin_len);
for (i = 0; i < asterlen * strlen(pin); i += asterlen) {
memcpy(&censort[i], asterisk, asterlen);
}
censort[i+1] = '\n';
leftinput = mw - x - pbw;
drw_text(drw, x, 0, leftinput, bh, lrpad / 2, censort, 0);
drw_font_getexts(drw->fonts, censort, cursor * asterlen, &curpos, NULL);
if ((curpos += lrpad / 2 - 1) < leftinput) {
drw_setscheme(drw, scheme[SchemeNormal]);
drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0);
}
free(censort);
} else {
x += TEXTW(" ");
x = drawitem("No", (sel == No), x, 0, TEXTW("No"));
x = drawitem("Yes", (sel == Yes), x, 0, TEXTW("Yes"));
}
drw_map(drw, win, 0, 0, mw, mh);
}
static void
setup(void) {
int x, y, i = 0;
unsigned int du;
XSetWindowAttributes swa;
XIM xim;
Window w, dw, *dws;
XWindowAttributes wa;
#ifdef XINERAMA
XineramaScreenInfo *info;
Window pw;
int a, j, di, n, area = 0;
#endif
/* Init appearance */
scheme[SchemePrompt] = drw_scm_create(drw, colors[SchemePrompt], 2);
scheme[SchemeNormal] = drw_scm_create(drw, colors[SchemeNormal], 2);
scheme[SchemeSelect] = drw_scm_create(drw, colors[SchemeSelect], 2);
scheme[SchemeDesc] = drw_scm_create(drw, colors[SchemeDesc], 2);
clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
/* Calculate menu geometry */
bh = drw->fonts->h + 2;
bh = MAX(bh, lineheight);
mh = bh;
#ifdef XINERAMA
info = XineramaQueryScreens(dpy, &n);
if (parentwin == root && info) {
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++) {
a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j]);
if (a > 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])) {
break;
}
}
}
x = info[i].x_org;
y = info[i].y_org + (bottom ? info[i].height - mh : 0);
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 = bottom ? wa.height - mh : 0;
mw = wa.width;
}
pdescw = (pinentry_info->description) ? TEXTW(pinentry_info->description) : 0;
/* Create menu window */
swa.override_redirect = True;
swa.background_pixel = scheme[SchemePrompt][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
/* Open input methods */
xim = XOpenIM(dpy, NULL, NULL, NULL);
xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, win, XNFocusWindow, win, NULL);
XMapRaised(dpy, win);
if (embedded) {
XSelectInput(dpy, parentwin, FocusChangeMask);
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);
}
static void
cleanup(void) {
XUngrabKey(dpy, AnyKey, AnyModifier, root);
free(scheme[SchemeDesc]);
free(scheme[SchemeSelect]);
free(scheme[SchemeNormal]);
free(scheme[SchemePrompt]);
drw_free(drw);
XSync(dpy, False);
XCloseDisplay(dpy);
}
static int
keypress_confirm(XKeyEvent *ev, KeySym ksym) {
if (ev->state & ControlMask) {
switch(ksym) {
case XK_c:
pinentry_info->canceled = 1;
sel = No;
return 1;
default:
return 1;
}
}
switch(ksym) {
case XK_KP_Enter:
case XK_Return:
if (sel != Nothing) {
return 1;
}
break;
case XK_y:
case XK_Y:
sel = Yes;
return 1;
case XK_n:
case XK_N:
sel = No;
return 1;
case XK_g:
case XK_G:
case XK_Escape:
pinentry_info->canceled = 1;
sel = No;
return 1;
case XK_h:
case XK_j:
case XK_Home:
case XK_Left:
case XK_Prior:
case XK_Up:
sel = No;
break;
case XK_k:
case XK_l:
case XK_Down:
case XK_End:
case XK_Next:
case XK_Right:
sel = Yes;
break;
}
return 0;
}
static int
keypress_pin(XKeyEvent *ev, KeySym ksym, char* buf, int len) {
int old;
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_k:
old = cursor;
cursor = strlen(pin);
insert(NULL, old - cursor);
break;
case XK_u:
insert(NULL, -cursor);
break;
case XK_v:
XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime);
return 0;
case XK_Return:
case XK_KP_Enter:
break;
case XK_bracketleft:
pinentry_info->canceled = 1;
return 1;
default:
return 1;
}
}
switch(ksym) {
case XK_Delete:
if (pin[cursor] == '\0') {
return 0;
}
cursor = nextrune(cursor, +1);
/* Fallthrough */
case XK_BackSpace:
if (cursor == 0) {
return 0;
}
insert(NULL, nextrune(cursor, -1) - cursor);
break;
case XK_Escape:
pinentry_info->canceled = 1;
return 1;
case XK_Left:
if (cursor > 0) {
cursor = nextrune(cursor, -1);
}
break;
case XK_Right:
if (pin[cursor] != '\0') {
cursor = nextrune(cursor, +1);
}
break;
case XK_Home:
cursor = 0;
break;
case XK_End:
cursor = strlen(pin);
break;
case XK_Return:
case XK_KP_Enter:
return 1;
break;
default:
if (!iscntrl(*buf)) {
insert(buf, len);
}
}
return 0;
}
static int
keypress(XKeyEvent *ev) {
char buf[32];
int len;
int ret = 1;
KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof(buf), &ksym, &status);
if (status != XBufferOverflow) {
if (winmode == WinConfirm) {
ret = keypress_confirm(ev, ksym);
} else {
ret = keypress_pin(ev, ksym, buf, len);
}
if (ret == 0) {
drawwin();
}
}
return ret;
}
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 */
XGetWindowProperty(dpy, win, utf8, 0, pin_len / 4, False, utf8, &da, &di,
&dl, &dl, (unsigned char **)&p);
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t) strlen(p));
XFree(p);
drawwin();
}
void
run(void) {
XEvent ev;
drawwin();
while (!XNextEvent(dpy, &ev)) {
if (XFilterEvent(&ev, win)) {
continue;
}
switch(ev.type) {
case Expose:
if (ev.xexpose.count == 0) {
drw_map(drw, win, 0, 0, mw, mh);
}
break;
case KeyPress:
if (keypress(&ev.xkey)) {
return;
}
break;
case SelectionNotify:
if (ev.xselection.property == utf8) {
paste();
}
break;
case VisibilityNotify:
if (ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
}
break;
}
}
}
static void
catchsig(int sig) {
if (sig == SIGALRM) {
timed_out = 1;
}
}
static void
password(void) {
winmode = WinPin;
repeat = 0;
setup_pin(pinentry_info->pin, pinentry_info->pin_len, 1);
run();
if (!pinentry_info->canceled && pinentry_info->repeat_passphrase) {
repeat = 1;
pin_repeat_len = pinentry_info->pin_len;
pin_repeat = secmem_malloc(pinentry_info->pin_len);
setup_pin(pin_repeat, pin_repeat_len, 1);
run();
pinentry_info->repeat_okay = (strcmp(pinentry_info->pin, pin_repeat) == 0)? 1 : 0;
secmem_free(pin_repeat);
if (!pinentry_info->repeat_okay) {
pinentry_info->result = -1;
return;
}
}
if (pinentry_info->canceled) {
pinentry_info->result = -1;
return;
}
pinentry_info->result = strlen(pinentry_info->pin);
}
static void
confirm(void) {
winmode = WinConfirm;
sel = Nothing;
run();
pinentry_info->result = sel != No;
}
static int
cmdhandler(pinentry_t received_pinentry) {
struct sigaction sa;
XWindowAttributes wa;
pinentry_info = received_pinentry;
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) {
fputs("warning: no locale support\n", stderr);
}
if (!(dpy = XOpenDisplay(pinentry_info->display))) {
die("cannot open display");
}
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
embedded = (pinentry_info->parent_wid) ? embedded : 0;
parentwin = (embedded) ? pinentry_info->parent_wid : 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;
drw_setscheme(drw, scheme[SchemePrompt]);
if (pinentry_info->timeout) {
memset(&sa, 0, sizeof(sa));
sa.sa_handler = catchsig;
sigaction(SIGALRM, &sa, NULL);
alarm(pinentry_info->timeout);
}
grabkeyboard();
setup();
if (pinentry_info->pin) {
do {
password();
} while (!pinentry_info->canceled && pinentry_info->repeat_passphrase
&& !pinentry_info->repeat_okay);
} else {
confirm();
}
cleanup();
return pinentry_info->result;
}
pinentry_cmd_handler_t pinentry_cmd_handler = cmdhandler;
int
main(int argc, char *argv[]) {
Bool bval;
int i, val;
const char *str;
struct passwd *pw;
char path[PATH_MAX];
char *sudo_uid = getenv("SUDO_UID");
char *home = getenv("HOME");
char *gnupghome = getenv("GNUPGHOME");
config_t cfg;
if (gnupghome) {
i = strlen(gnupghome);
strcpy(path, gnupghome);
} else {
/* Get the home dir even if the user used sudo or logged in as root */
if (sudo_uid) {
i = atoi(sudo_uid);
pw = getpwuid(i);
home = pw->pw_dir;
}
i = strlen(home);
strcpy(path, home);
strcpy(&path[i], CONFIG_DIR);
i += strlen(CONFIG_DIR);
}
strcpy(&path[i], CONFIG_FILE);
endpwent();
config_init(&cfg);
/* Read the file. If there is an error, report it and exit. */
if (config_read_file(&cfg, path)) {
if (config_lookup_string(&cfg, "asterisk", &str)) {
asterisk = str;
}
if (config_lookup_bool(&cfg, "bottom", &bval)) {
bottom = bval;
}
if (config_lookup_int(&cfg, "min_password_length", &val)) {
minpwlen = val;
}
if (config_lookup_int(&cfg, "height", &val)) {
lineheight = MAX(val, min_lineheight);
}
if (config_lookup_int(&cfg, "monitor", &val)) {
mon = val;
}
if (config_lookup_string(&cfg, "prompt", &str)) {
prompt = str;
}
if (config_lookup_string(&cfg, "font", &str)) {
fonts[0] = str;
}
if (config_lookup_string(&cfg, "prompt_bg", &str)) {
colors[SchemePrompt][ColBg] = str;
}
if (config_lookup_string(&cfg, "prompt_fg", &str)) {
colors[SchemePrompt][ColFg] = str;
}
if (config_lookup_string(&cfg, "normal_bg", &str)) {
colors[SchemeNormal][ColBg] = str;
}
if (config_lookup_string(&cfg, "normal_fg", &str)) {
colors[SchemeNormal][ColFg] = str;
}
if (config_lookup_string(&cfg, "select_bg", &str)) {
colors[SchemeSelect][ColBg] = str;
}
if (config_lookup_string(&cfg, "select_fg", &str)) {
colors[SchemeSelect][ColFg] = str;
}
if (config_lookup_string(&cfg, "desc_bg", &str)) {
colors[SchemeDesc][ColBg] = str;
}
if (config_lookup_string(&cfg, "desc_fg", &str)) {
colors[SchemeDesc][ColFg] = str;
}
if (config_lookup_bool(&cfg, "embedded", &bval)) {
embedded = bval;
}
} else if ((str = config_error_file(&cfg))) {
fprintf(stderr, "%s:%d: %s\n", config_error_file(&cfg),
config_error_line(&cfg), config_error_text(&cfg));
return(EXIT_FAILURE);
}
pinentry_init("pinentry-dmenu");
pinentry_parse_opts(argc, argv);
if (pinentry_loop()) {
return 1;
}
config_destroy(&cfg);
return 0;
}

View File

@@ -0,0 +1,11 @@
Program: Pinentry
Bug reports: <gpa-dev@gnupg.org>
Security related bug reports: <security@gnupg.org>
License: GPLv2+
Robert Bihlmeyer <robbe@orcus.priv.at>
Werner Koch, g10 Code GmbH <wk@gnupg.org>
Steffen Hansen, Klar<61>lvdalens Datakonsult AB <steffen@klaralvdalens-datakonsult.se>
Marcus Brinkmann, g10 Code GmbH <marcus@g10code.com>
Timo Schulz, g10 Code GmbH
Neal Walfied, g10 Code GmbH <neal@gnu.org>

View File

@@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,21 @@
include ../config.mk
SRC = util.c pinentry.c argparse.c password-cache.c secmem.c
OBJ = ${SRC:.c=.o}
CFLAGS += -DHAVE_MLOCK
all: pinentry
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: pinentry.h argparse.h password-cache.h memory.h util.h
pinentry: pinentry.o argparse.o password-cache.o secmem.o util.o
clean:
@echo cleaning
@rm -f ${OBJ}
.PHONY: all clean pinentry

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
/* argparse.h - Argument parser for option handling.
* Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc.
*
* This file is part of JNLIB, which is a subsystem of GnuPG.
*
* JNLIB is free software; you can redistribute it and/or modify it
* under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* JNLIB is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBJNLIB_ARGPARSE_H
#define LIBJNLIB_ARGPARSE_H
#include <stdio.h>
typedef struct
{
int *argc; /* Pointer to ARGC (value subject to change). */
char ***argv; /* Pointer to ARGV (value subject to change). */
unsigned int flags; /* Global flags. May be set prior to calling the
parser. The parser may change the value. */
int err; /* Print error description for last option.
Either 0, ARGPARSE_PRINT_WARNING or
ARGPARSE_PRINT_ERROR. */
int r_opt; /* Returns option code. */
int r_type; /* Returns type of option value. */
union {
int ret_int;
long ret_long;
unsigned long ret_ulong;
char *ret_str;
} r; /* Return values */
struct {
int idx;
int inarg;
int stopped;
const char *last;
void *aliases;
const void *cur_alias;
void *iio_list;
} internal; /* Private - do not change. */
} ARGPARSE_ARGS;
typedef struct
{
int short_opt;
const char *long_opt;
unsigned int flags;
const char *description; /* Optional option description. */
} ARGPARSE_OPTS;
/* Global flags (ARGPARSE_ARGS). */
#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */
#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return
remaining args with R_OPT set to -1. */
#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */
#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */
#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */
#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */
#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */
#define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */
/* Flags for each option (ARGPARSE_OPTS). The type code may be
ORed with the OPT flags. */
#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */
#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */
#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */
#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */
#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */
#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */
#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */
#define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */
#define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */
#define ARGPARSE_TYPE_MASK 7 /* Mask for the type values (internal). */
/* A set of macros to make option definitions easier to read. */
#define ARGPARSE_x(s,l,t,f,d) \
{ (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) }
#define ARGPARSE_s(s,l,t,d) \
{ (s), (l), ARGPARSE_TYPE_ ## t, (d) }
#define ARGPARSE_s_n(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_NONE, (d) }
#define ARGPARSE_s_i(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_INT, (d) }
#define ARGPARSE_s_s(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_STRING, (d) }
#define ARGPARSE_s_l(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_LONG, (d) }
#define ARGPARSE_s_u(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_ULONG, (d) }
#define ARGPARSE_o(s,l,t,d) \
{ (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_n(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_i(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_s(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_l(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_u(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_p(s,l,t,d) \
{ (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_n(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_i(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_s(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_l(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_u(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op(s,l,t,d) \
{ (s), (l), (ARGPARSE_TYPE_ ## t \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_n(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_i(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_INT \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_s(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_STRING \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_l(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_LONG \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_u(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_ULONG \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_c(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) }
#define ARGPARSE_ignore(s,l) \
{ (s), (l), (ARGPARSE_OPT_IGNORE), "@" }
#define ARGPARSE_group(s,d) \
{ (s), NULL, 0, (d) }
#define ARGPARSE_end() { 0, NULL, 0, NULL }
/* Other constants. */
#define ARGPARSE_PRINT_WARNING 1
#define ARGPARSE_PRINT_ERROR 2
/* Error values. */
#define ARGPARSE_IS_ARG (-1)
#define ARGPARSE_INVALID_OPTION (-2)
#define ARGPARSE_MISSING_ARG (-3)
#define ARGPARSE_KEYWORD_TOO_LONG (-4)
#define ARGPARSE_READ_ERROR (-5)
#define ARGPARSE_UNEXPECTED_ARG (-6)
#define ARGPARSE_INVALID_COMMAND (-7)
#define ARGPARSE_AMBIGUOUS_OPTION (-8)
#define ARGPARSE_AMBIGUOUS_COMMAND (-9)
#define ARGPARSE_INVALID_ALIAS (-10)
#define ARGPARSE_OUT_OF_CORE (-11)
#define ARGPARSE_INVALID_ARG (-12)
int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
int optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
void usage (int level);
const char *strusage (int level);
void set_strusage (const char *(*f)( int ));
void argparse_register_outfnc (int (*fnc)(int, const char *));
#endif /*LIBJNLIB_ARGPARSE_H*/

View File

@@ -0,0 +1,55 @@
/* Quintuple Agent secure memory allocation
* Copyright (C) 1998,1999 Free Software Foundation, Inc.
* Copyright (C) 1999,2000 Robert Bihlmeyer <robbe@orcus.priv.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _MEMORY_H
#define _MEMORY_H
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
/* values for flags, hardcoded in secmem.c */
#define SECMEM_WARN 0
#define SECMEM_DONT_WARN 1
#define SECMEM_SUSPEND_WARN 2
void secmem_init( size_t npool );
void secmem_term( void );
void *secmem_malloc( size_t size );
void *secmem_realloc( void *a, size_t newsize );
void secmem_free( void *a );
int m_is_secure( const void *p );
void secmem_dump_stats(void);
void secmem_set_flags( unsigned flags );
unsigned secmem_get_flags(void);
size_t secmem_get_max_size (void);
#if 0
{
#endif
#ifdef __cplusplus
}
#endif
#endif /* _MEMORY_H */

View File

@@ -0,0 +1,163 @@
/* password-cache.c - Password cache support.
Copyright (C) 2015 g10 Code GmbH
This file is part of PINENTRY.
PINENTRY is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PINENTRY is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_LIBSECRET
# include <libsecret/secret.h>
#endif
#include "password-cache.h"
#include "memory.h"
#ifdef HAVE_LIBSECRET
static const SecretSchema *
gpg_schema (void)
{
static const SecretSchema the_schema = {
"org.gnupg.Passphrase", SECRET_SCHEMA_NONE,
{
{ "stored-by", SECRET_SCHEMA_ATTRIBUTE_STRING },
{ "keygrip", SECRET_SCHEMA_ATTRIBUTE_STRING },
{ "NULL", 0 },
}
};
return &the_schema;
}
static char *
keygrip_to_label (const char *keygrip)
{
char const prefix[] = "GnuPG: ";
char *label;
label = malloc (sizeof (prefix) + strlen (keygrip));
if (label)
{
memcpy (label, prefix, sizeof (prefix) - 1);
strcpy (&label[sizeof (prefix) - 1], keygrip);
}
return label;
}
#endif
void
password_cache_save (const char *keygrip, const char *password)
{
#ifdef HAVE_LIBSECRET
char *label;
GError *error = NULL;
if (! *keygrip)
return;
label = keygrip_to_label (keygrip);
if (! label)
return;
if (! secret_password_store_sync (gpg_schema (),
SECRET_COLLECTION_DEFAULT,
label, password, NULL, &error,
"stored-by", "GnuPG Pinentry",
"keygrip", keygrip, NULL))
{
printf("Failed to cache password for key %s with secret service: %s\n",
keygrip, error->message);
g_error_free (error);
}
free (label);
#else
return;
#endif
}
char *
password_cache_lookup (const char *keygrip)
{
#ifdef HAVE_LIBSECRET
GError *error = NULL;
char *password;
char *password2;
if (! *keygrip)
return NULL;
password = secret_password_lookup_nonpageable_sync
(gpg_schema (), NULL, &error,
"keygrip", keygrip, NULL);
if (error != NULL)
{
printf("Failed to lookup password for key %s with secret service: %s\n",
keygrip, error->message);
g_error_free (error);
return NULL;
}
if (! password)
/* The password for this key is not cached. Just return NULL. */
return NULL;
/* The password needs to be returned in secmem allocated memory. */
password2 = secmem_malloc (strlen (password) + 1);
if (password2)
strcpy(password2, password);
else
printf("secmem_malloc failed: can't copy password!\n");
secret_password_free (password);
return password2;
#else
return NULL;
#endif
}
/* Try and remove the cached password for key grip. Returns -1 on
error, 0 if the key is not found and 1 if the password was
removed. */
int
password_cache_clear (const char *keygrip)
{
#ifdef HAVE_LIBSECRET
GError *error = NULL;
int removed = secret_password_clear_sync (gpg_schema (), NULL, &error,
"keygrip", keygrip, NULL);
if (error != NULL)
{
printf("Failed to clear password for key %s with secret service: %s\n",
keygrip, error->message);
g_debug("%s", error->message);
g_error_free (error);
return -1;
}
if (removed)
return 1;
return 0;
#else
return -1;
#endif
}

View File

@@ -0,0 +1,29 @@
/* password-cache.h - Password cache support interfaces.
Copyright (C) 2015 g10 Code GmbH
This file is part of PINENTRY.
PINENTRY is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PINENTRY is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PASSWORD_CACHE_H
#define PASSWORD_CACHE_H
void password_cache_save (const char *key_grip, const char *password);
char *password_cache_lookup (const char *key_grip);
int password_cache_clear (const char *keygrip);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,280 @@
/* pinentry.h - The interface for the PIN entry support library.
Copyright (C) 2002, 2003, 2010, 2015 g10 Code GmbH
This file is part of PINENTRY.
PINENTRY is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PINENTRY is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PINENTRY_H
#define PINENTRY_H
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
typedef enum {
PINENTRY_COLOR_NONE, PINENTRY_COLOR_DEFAULT,
PINENTRY_COLOR_BLACK, PINENTRY_COLOR_RED,
PINENTRY_COLOR_GREEN, PINENTRY_COLOR_YELLOW,
PINENTRY_COLOR_BLUE, PINENTRY_COLOR_MAGENTA,
PINENTRY_COLOR_CYAN, PINENTRY_COLOR_WHITE
} pinentry_color_t;
struct pinentry
{
/* The window title, or NULL. (Assuan: "SETTITLE TITLE".) */
char *title;
/* The description to display, or NULL. (Assuan: "SETDESC
DESC".) */
char *description;
/* The error message to display, or NULL. (Assuan: "SETERROR
MESSAGE".) */
char *error;
/* The prompt to display, or NULL. (Assuan: "SETPROMPT
prompt".) */
char *prompt;
/* The OK button text to display, or NULL. (Assuan: "SETOK
OK".) */
char *ok;
/* The Not-OK button text to display, or NULL. This is the text for
the alternative option shown by the third button. (Assuan:
"SETNOTOK NOTOK".) */
char *notok;
/* The Cancel button text to display, or NULL. (Assuan: "SETCANCEL
CANCEL".) */
char *cancel;
/* The buffer to store the secret into. */
char *pin;
/* The length of the buffer. */
int pin_len;
/* Whether the pin was read from an external cache (1) or entered by
the user (0). */
int pin_from_cache;
/* The name of the X display to use if X is available and supported.
(Assuan: "OPTION display DISPLAY".) */
char *display;
/* The name of the terminal node to open if X not available or
supported. (Assuan: "OPTION ttyname TTYNAME".) */
char *ttyname;
/* The type of the terminal. (Assuan: "OPTION ttytype TTYTYPE".) */
char *ttytype;
/* The LC_CTYPE value for the terminal. (Assuan: "OPTION lc-ctype
LC_CTYPE".) */
char *lc_ctype;
/* The LC_MESSAGES value for the terminal. (Assuan: "OPTION
lc-messages LC_MESSAGES".) */
char *lc_messages;
/* True if debug mode is requested. */
int debug;
/* The number of seconds before giving up while waiting for user input. */
int timeout;
/* True if caller should grab the keyboard. (Assuan: "OPTION grab"
or "OPTION no-grab".) */
int grab;
/* The window ID of the parent window over which the pinentry window
should be displayed. (Assuan: "OPTION parent-wid WID".) */
int parent_wid;
/* The name of an optional file which will be touched after a curses
entry has been displayed. (Assuan: "OPTION touch-file
FILENAME".) */
char *touch_file;
/* The frontend should set this to -1 if the user canceled the
request, and to the length of the PIN stored in pin
otherwise. */
int result;
/* The frontend should set this if the NOTOK button was pressed. */
int canceled;
/* The frontend should set this to true if an error with the local
conversion occured. */
int locale_err;
/* The frontend should set this to a gpg-error so that commands are
able to return specific error codes. This is an ugly hack due to
the fact that pinentry_cmd_handler_t returns the length of the
passphrase or a negative error code. */
int specific_err;
/* The frontend should set this to true if the window close button
has been used. This flag is used in addition to a regular return
value. */
int close_button;
/* The caller should set this to true if only one button is
required. This is useful for notification dialogs where only a
dismiss button is required. */
int one_button;
/* If true a second prompt for the passphrase is shown and the user
is expected to enter the same passphrase again. Pinentry checks
that both match. (Assuan: "SETREPEAT".) */
char *repeat_passphrase;
/* The string to show if a repeated passphrase does not match.
(Assuan: "SETREPEATERROR ERROR".) */
char *repeat_error_string;
/* Set to true if the passphrase has been entered a second time and
matches the first passphrase. */
int repeat_okay;
/* If this is not NULL, a passphrase quality indicator is shown.
There will also be an inquiry back to the caller to get an
indication of the quality for the passphrase entered so far. The
string is used as a label for the quality bar. (Assuan:
"SETQUALITYBAR LABEL".) */
char *quality_bar;
/* The tooltip to be show for the qualitybar. Malloced or NULL.
(Assuan: "SETQUALITYBAR_TT TOOLTIP".) */
char *quality_bar_tt;
/* For the curses pinentry, the color of error messages. */
pinentry_color_t color_fg;
int color_fg_bright;
pinentry_color_t color_bg;
pinentry_color_t color_so;
int color_so_bright;
/* Malloced and i18ned default strings or NULL. These strings may
include an underscore character to indicate an accelerator key.
A double underscore represents a plain one. */
/* (Assuan: "OPTION default-ok OK"). */
char *default_ok;
/* (Assuan: "OPTION default-cancel CANCEL"). */
char *default_cancel;
/* (Assuan: "OPTION default-prompt PROMPT"). */
char *default_prompt;
/* (Assuan: "OPTION default-pwmngr
SAVE_PASSWORD_WITH_PASSWORD_MANAGER?"). */
char *default_pwmngr;
/* Whether we are allowed to read the password from an external
cache. (Assuan: "OPTION allow-external-password-cache") */
int allow_external_password_cache;
/* We only try the cache once. */
int tried_password_cache;
/* A stable identifier for the key. (Assuan: "SETKEYINFO
KEYINFO".) */
char *keyinfo;
/* Whether we may cache the password (according to the user). */
int may_cache_password;
/* NOTE: If you add any additional fields to this structure, be sure
to update the initializer in pinentry/pinentry.c!!! */
/* For the quality indicator we need to do an inquiry. Thus we need
to save the assuan ctx. */
void *ctx_assuan;
};
typedef struct pinentry *pinentry_t;
/* The pinentry command handler type processes the pinentry request
PIN. If PIN->pin is zero, request a confirmation, otherwise a PIN
entry. On confirmation, the function should return TRUE if
confirmed, and FALSE otherwise. On PIN entry, the function should
return -1 if an error occured or the user cancelled the operation
and 1 otherwise. */
typedef int (*pinentry_cmd_handler_t) (pinentry_t pin);
/* Start the pinentry event loop. The program will start to process
Assuan commands until it is finished or an error occurs. If an
error occurs, -1 is returned and errno indicates the type of an
error. Otherwise, 0 is returned. */
int pinentry_loop (void);
/* The same as above but allows to specify the i/o descriptors.
* infd and outfd will be duplicated in this function so the caller
* still has to close them if necessary.
*/
int pinentry_loop2 (int infd, int outfd);
/* Convert the UTF-8 encoded string TEXT to the encoding given in
LC_CTYPE. Return NULL on error. */
char *pinentry_utf8_to_local (const char *lc_ctype, const char *text);
/* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With
SECURE set to true, use secure memory for the returned buffer.
Return NULL on error. */
char *pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure);
/* Run a quality inquiry for PASSPHRASE of LENGTH. */
int pinentry_inq_quality (pinentry_t pin,
const char *passphrase, size_t length);
/* Try to make room for at least LEN bytes for the pin in the pinentry
PIN. Returns new buffer on success and 0 on failure. */
char *pinentry_setbufferlen (pinentry_t pin, int len);
/* Use the buffer at BUFFER for PIN->PIN. BUFFER must be NULL or
allocated using secmem_alloc. LEN is the size of the buffer. If
it is unknown, but BUFFER is a NUL terminated string, you pass 0 to
just use strlen(buffer)+1. */
void pinentry_setbuffer_use (pinentry_t pin, char *buffer, int len);
/* Initialize the secure memory subsystem, drop privileges and
return. Must be called early. */
void pinentry_init (const char *pgmname);
/* Return true if either DISPLAY is set or ARGV contains the string
"--display". */
int pinentry_have_display (int argc, char **argv);
/* Parse the command line options. May exit the program if only help
or version output is requested. */
void pinentry_parse_opts (int argc, char *argv[]);
/* The caller must define this variable to process assuan commands. */
extern pinentry_cmd_handler_t pinentry_cmd_handler;
#ifdef HAVE_W32_SYSTEM
/* Windows declares sleep as obsolete, but provides a definition for
_sleep but non for the still existing sleep. */
#define sleep(a) _sleep ((a))
#endif /*HAVE_W32_SYSTEM*/
#if 0
{
#endif
#ifdef __cplusplus
}
#endif
#endif /* PINENTRY_H */

Some files were not shown because too many files have changed in this diff Show More