Initial commit with patches - still need to customize all the keys and the BAR!!!
This commit is contained in:
commit
e8b6d29122
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
config.h
|
||||||
|
drw.o
|
||||||
|
dwm
|
||||||
|
dwm.o
|
||||||
|
util.o
|
||||||
38
LICENSE
Normal file
38
LICENSE
Normal 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.
|
||||||
67
Makefile
Normal file
67
Makefile
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# 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}
|
||||||
|
|
||||||
|
# FreeBSD users, prefix all ifdef, else and endif statements with a . for this to work (e.g. .ifdef)
|
||||||
|
|
||||||
|
ifdef YAJLLIBS
|
||||||
|
all: dwm dwm-msg
|
||||||
|
else
|
||||||
|
all: dwm
|
||||||
|
endif
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
${CC} -c ${CFLAGS} $<
|
||||||
|
|
||||||
|
${OBJ}: config.h config.mk
|
||||||
|
|
||||||
|
config.h:
|
||||||
|
cp config.def.h $@
|
||||||
|
|
||||||
|
dwm: ${OBJ}
|
||||||
|
${CC} -o $@ ${OBJ} ${LDFLAGS}
|
||||||
|
|
||||||
|
ifdef YAJLLIBS
|
||||||
|
dwm-msg:
|
||||||
|
${CC} -o $@ patch/ipc/dwm-msg.c ${LDFLAGS}
|
||||||
|
endif
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
|
||||||
|
rm -f dwm-msg
|
||||||
|
|
||||||
|
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
|
||||||
|
ifdef YAJLLIBS
|
||||||
|
cp -f dwm-msg ${DESTDIR}${PREFIX}/bin
|
||||||
|
endif
|
||||||
|
chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
|
||||||
|
ifdef YAJLLIBS
|
||||||
|
chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-msg
|
||||||
|
endif
|
||||||
|
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
|
||||||
|
chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
|
||||||
|
mkdir -p ${DESTDIR}${PREFIX}/share/xsessions
|
||||||
|
test -f ${DESTDIR}${PREFIX}/share/xsessions/dwm.desktop || cp -n dwm.desktop ${DESTDIR}${PREFIX}/share/xsessions
|
||||||
|
chmod 644 ${DESTDIR}${PREFIX}/share/xsessions/dwm.desktop
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f ${DESTDIR}${PREFIX}/bin/dwm\
|
||||||
|
${DESTDIR}${MANPREFIX}/man1/dwm.1\
|
||||||
|
${DESTDIR}${PREFIX}/share/xsessions/dwm.desktop
|
||||||
|
|
||||||
|
.PHONY: all clean dist install uninstall
|
||||||
48
README
Normal file
48
README
Normal 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.
|
||||||
330
config.def.h
Normal file
330
config.def.h
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
/* Helper macros for spawning commands */
|
||||||
|
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
|
||||||
|
#define CMD(...) { .v = (const char*[]){ __VA_ARGS__, NULL } }
|
||||||
|
|
||||||
|
/* appearance */
|
||||||
|
static const unsigned int borderpx = 1; /* border pixel of windows */
|
||||||
|
static const unsigned int snap = 32; /* snap pixel */
|
||||||
|
static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
|
||||||
|
static const int scalepreview = 4; /* Tag preview scaling */
|
||||||
|
static const int usealtbar = 1; /* 1 means use non-dwm status bar */
|
||||||
|
static const char *altbarclass = "Polybar"; /* Alternate bar class name */
|
||||||
|
static const char *altbarcmd = "$HOME/bar.sh"; /* Alternate bar launch command */
|
||||||
|
static const int showbar = 1; /* 0 means no bar */
|
||||||
|
static const int topbar = 1; /* 0 means bottom bar */
|
||||||
|
/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */
|
||||||
|
static const int statusmon = 'A';
|
||||||
|
static const unsigned int systrayspacing = 2; /* systray spacing */
|
||||||
|
static const int showsystray = 1; /* 0 means no systray */
|
||||||
|
|
||||||
|
/* alt-tab configuration */
|
||||||
|
static const unsigned int tabmodkey = 0x40; /* (Alt) when this key is held down the alt-tab functionality stays active. Must be the same modifier as used to run alttabstart */
|
||||||
|
static const unsigned int tabcyclekey = 0x17; /* (Tab) when this key is hit the menu moves one position forward in client stack. Must be the same key as used to run alttabstart */
|
||||||
|
static const unsigned int tabposy = 1; /* tab position on Y axis, 0 = top, 1 = center, 2 = bottom */
|
||||||
|
static const unsigned int tabposx = 1; /* tab position on X axis, 0 = left, 1 = center, 2 = right */
|
||||||
|
static const unsigned int maxwtab = 600; /* tab menu width */
|
||||||
|
static const unsigned int maxhtab = 200; /* tab menu height */
|
||||||
|
|
||||||
|
/* Indicators: see patch/bar_indicators.h for options */
|
||||||
|
static int tagindicatortype = INDICATOR_TOP_LEFT_SQUARE;
|
||||||
|
static int tiledindicatortype = INDICATOR_NONE;
|
||||||
|
static int floatindicatortype = INDICATOR_TOP_LEFT_SQUARE;
|
||||||
|
static const char *fonts[] = { "monospace:size=10" };
|
||||||
|
static const char dmenufont[] = "monospace:size=10";
|
||||||
|
|
||||||
|
static char c000000[] = "#000000"; // placeholder value
|
||||||
|
|
||||||
|
static char normfgcolor[] = "#bbbbbb";
|
||||||
|
static char normbgcolor[] = "#222222";
|
||||||
|
static char normbordercolor[] = "#444444";
|
||||||
|
static char normfloatcolor[] = "#db8fd9";
|
||||||
|
|
||||||
|
static char selfgcolor[] = "#eeeeee";
|
||||||
|
static char selbgcolor[] = "#005577";
|
||||||
|
static char selbordercolor[] = "#005577";
|
||||||
|
static char selfloatcolor[] = "#005577";
|
||||||
|
|
||||||
|
static char titlenormfgcolor[] = "#bbbbbb";
|
||||||
|
static char titlenormbgcolor[] = "#222222";
|
||||||
|
static char titlenormbordercolor[] = "#444444";
|
||||||
|
static char titlenormfloatcolor[] = "#db8fd9";
|
||||||
|
|
||||||
|
static char titleselfgcolor[] = "#eeeeee";
|
||||||
|
static char titleselbgcolor[] = "#005577";
|
||||||
|
static char titleselbordercolor[] = "#005577";
|
||||||
|
static char titleselfloatcolor[] = "#005577";
|
||||||
|
|
||||||
|
static char tagsnormfgcolor[] = "#bbbbbb";
|
||||||
|
static char tagsnormbgcolor[] = "#222222";
|
||||||
|
static char tagsnormbordercolor[] = "#444444";
|
||||||
|
static char tagsnormfloatcolor[] = "#db8fd9";
|
||||||
|
|
||||||
|
static char tagsselfgcolor[] = "#eeeeee";
|
||||||
|
static char tagsselbgcolor[] = "#005577";
|
||||||
|
static char tagsselbordercolor[] = "#005577";
|
||||||
|
static char tagsselfloatcolor[] = "#005577";
|
||||||
|
|
||||||
|
static char hidnormfgcolor[] = "#005577";
|
||||||
|
static char hidselfgcolor[] = "#227799";
|
||||||
|
static char hidnormbgcolor[] = "#222222";
|
||||||
|
static char hidselbgcolor[] = "#222222";
|
||||||
|
|
||||||
|
static char urgfgcolor[] = "#bbbbbb";
|
||||||
|
static char urgbgcolor[] = "#222222";
|
||||||
|
static char urgbordercolor[] = "#ff0000";
|
||||||
|
static char urgfloatcolor[] = "#db8fd9";
|
||||||
|
|
||||||
|
static char scratchselfgcolor[] = "#FFF7D4";
|
||||||
|
static char scratchselbgcolor[] = "#77547E";
|
||||||
|
static char scratchselbordercolor[] = "#894B9F";
|
||||||
|
static char scratchselfloatcolor[] = "#894B9F";
|
||||||
|
|
||||||
|
static char scratchnormfgcolor[] = "#FFF7D4";
|
||||||
|
static char scratchnormbgcolor[] = "#664C67";
|
||||||
|
static char scratchnormbordercolor[] = "#77547E";
|
||||||
|
static char scratchnormfloatcolor[] = "#77547E";
|
||||||
|
|
||||||
|
static char *colors[][ColCount] = {
|
||||||
|
/* fg bg border float */
|
||||||
|
[SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor },
|
||||||
|
[SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor },
|
||||||
|
[SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor },
|
||||||
|
[SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor },
|
||||||
|
[SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor },
|
||||||
|
[SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor },
|
||||||
|
[SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, c000000, c000000 },
|
||||||
|
[SchemeHidSel] = { hidselfgcolor, hidselbgcolor, c000000, c000000 },
|
||||||
|
[SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor },
|
||||||
|
[SchemeScratchSel] = { scratchselfgcolor, scratchselbgcolor, scratchselbordercolor, scratchselfloatcolor },
|
||||||
|
[SchemeScratchNorm] = { scratchnormfgcolor, scratchnormbgcolor, scratchnormbordercolor, scratchnormfloatcolor },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const autostart[] = {
|
||||||
|
"st", NULL,
|
||||||
|
NULL /* terminate */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *scratchpadcmd[] = {"s", "st", "-n", "spterm", NULL};
|
||||||
|
|
||||||
|
/* Tags
|
||||||
|
* In a traditional dwm the number of tags in use can be changed simply by changing the number
|
||||||
|
* of strings in the tags array. This build does things a bit different which has some added
|
||||||
|
* benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* 1) static char *tagicons[][NUMTAGS*2] = {
|
||||||
|
* [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" },
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 2) static char *tagicons[][1] = {
|
||||||
|
* [DEFAULT_TAGS] = { "•" },
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* The first example would result in the tags on the first monitor to be 1 through 9, while the
|
||||||
|
* tags for the second monitor would be named A through I. A third monitor would start again at
|
||||||
|
* 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags
|
||||||
|
* count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in
|
||||||
|
* the array. This can be changed to *3 to add separate icons for a third monitor.
|
||||||
|
*
|
||||||
|
* For the second example each tag would be represented as a bullet point. Both cases work the
|
||||||
|
* same from a technical standpoint - the icon index is derived from the tag index and the monitor
|
||||||
|
* index. If the icon index is is greater than the number of tag icons then it will wrap around
|
||||||
|
* until it an icon matches. Similarly if there are two tag icons then it would alternate between
|
||||||
|
* them. This works seamlessly with alternative tags and alttagsdecoration patches.
|
||||||
|
*/
|
||||||
|
static char *tagicons[][NUMTAGS] =
|
||||||
|
{
|
||||||
|
[DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
|
||||||
|
[ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" },
|
||||||
|
[ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* There are two options when it comes to per-client rules:
|
||||||
|
* - a typical struct table or
|
||||||
|
* - using the RULE macro
|
||||||
|
*
|
||||||
|
* A traditional struct table looks like this:
|
||||||
|
* // class instance title wintype tags mask isfloating monitor
|
||||||
|
* { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 },
|
||||||
|
* { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 },
|
||||||
|
*
|
||||||
|
* The RULE macro has the default values set for each field allowing you to only
|
||||||
|
* specify the values that are relevant for your rule, e.g.
|
||||||
|
*
|
||||||
|
* RULE(.class = "Gimp", .tags = 1 << 4)
|
||||||
|
* RULE(.class = "Firefox", .tags = 1 << 7)
|
||||||
|
*
|
||||||
|
* Refer to the Rule struct definition for the list of available fields depending on
|
||||||
|
* the patches you enable.
|
||||||
|
*/
|
||||||
|
static const Rule rules[] = {
|
||||||
|
/* xprop(1):
|
||||||
|
* WM_CLASS(STRING) = instance, class
|
||||||
|
* WM_NAME(STRING) = title
|
||||||
|
* WM_WINDOW_ROLE(STRING) = role
|
||||||
|
* _NET_WM_WINDOW_TYPE(ATOM) = wintype
|
||||||
|
*/
|
||||||
|
RULE(.wintype = WTYPE "DIALOG", .isfloating = 1)
|
||||||
|
RULE(.wintype = WTYPE "UTILITY", .isfloating = 1)
|
||||||
|
RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1)
|
||||||
|
RULE(.wintype = WTYPE "SPLASH", .isfloating = 1)
|
||||||
|
RULE(.class = "Gimp", .tags = 1 << 4)
|
||||||
|
RULE(.class = "Firefox", .tags = 1 << 7)
|
||||||
|
RULE(.instance = "spterm", .scratchkey = 's', .isfloating = 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const MonitorRule monrules[] = {
|
||||||
|
/* monitor tag layout mfact nmaster showbar topbar */
|
||||||
|
{ 1, -1, 2, -1, -1, -1, -1 }, // use a different layout for the second monitor
|
||||||
|
{ -1, -1, 0, -1, -1, -1, -1 }, // default
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Bar rules allow you to configure what is shown where on the bar, as well as
|
||||||
|
* introducing your own bar modules.
|
||||||
|
*
|
||||||
|
* monitor:
|
||||||
|
* -1 show on all monitors
|
||||||
|
* 0 show on monitor 0
|
||||||
|
* 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?)
|
||||||
|
* bar - bar index, 0 is default, 1 is extrabar
|
||||||
|
* alignment - how the module is aligned compared to other modules
|
||||||
|
* widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions
|
||||||
|
* name - does nothing, intended for visual clue and for logging / debugging
|
||||||
|
*/
|
||||||
|
static const BarRule barrules[] = {
|
||||||
|
/* monitor bar alignment widthfunc drawfunc clickfunc hoverfunc name */
|
||||||
|
{ -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, hover_tags, "tags" },
|
||||||
|
{ 0, 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, NULL, "systray" },
|
||||||
|
{ -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, NULL, "layout" },
|
||||||
|
{ statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, NULL, "status" },
|
||||||
|
{ -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, NULL, "wintitle" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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 = 0; /* 1 means respect size hints in tiled resizals */
|
||||||
|
static const int lockfullscreen = 1; /* 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 },
|
||||||
|
{ "[\\]", dwindle },
|
||||||
|
{ "HHH", grid },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* key definitions */
|
||||||
|
#define MODKEY Mod4Mask
|
||||||
|
#define TAGKEYS(KEY,TAG) \
|
||||||
|
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
|
||||||
|
|
||||||
|
/* commands */
|
||||||
|
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
|
||||||
|
static const char *dmenucmd[] = {
|
||||||
|
"dmenu_run",
|
||||||
|
"-m", dmenumon,
|
||||||
|
"-fn", dmenufont,
|
||||||
|
"-nb", normbgcolor,
|
||||||
|
"-nf", normfgcolor,
|
||||||
|
"-sb", selbgcolor,
|
||||||
|
"-sf", selfgcolor,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
static const char *termcmd[] = { "kitty", NULL };
|
||||||
|
|
||||||
|
static const 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|ShiftMask, XK_h, setcfact, {.f = +0.25} },
|
||||||
|
{ MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} },
|
||||||
|
{ MODKEY|ShiftMask, XK_o, setcfact, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_Return, zoom, {0} },
|
||||||
|
{ Mod1Mask, XK_Tab, alttabstart, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_e, killclient, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_q, quit, {0} },
|
||||||
|
{ MODKEY|ControlMask, XK_q, quit, {1} },
|
||||||
|
{ 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_grave, togglescratch, {.v = scratchpadcmd } },
|
||||||
|
{ MODKEY|ControlMask, XK_grave, setscratch, {.v = scratchpadcmd } },
|
||||||
|
{ MODKEY|ShiftMask, XK_grave, removescratch, {.v = scratchpadcmd } },
|
||||||
|
{ 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 } },
|
||||||
|
{ MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } },
|
||||||
|
{ MODKEY|ControlMask, XK_period, cyclelayout, {.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)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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} },
|
||||||
|
{ ClkClientWin, MODKEY|ShiftMask, Button3, dragcfact, {0} },
|
||||||
|
{ ClkClientWin, MODKEY|ShiftMask, Button1, dragmfact, {0} },
|
||||||
|
{ ClkTagBar, 0, Button1, view, {0} },
|
||||||
|
{ ClkTagBar, 0, Button3, toggleview, {0} },
|
||||||
|
{ ClkTagBar, MODKEY, Button1, tag, {0} },
|
||||||
|
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *ipcsockpath = "/tmp/dwm.sock";
|
||||||
|
static IPCCommand ipccommands[] = {
|
||||||
|
IPCCOMMAND( focusmon, 1, {ARG_TYPE_SINT} ),
|
||||||
|
IPCCOMMAND( focusstack, 1, {ARG_TYPE_SINT} ),
|
||||||
|
IPCCOMMAND( incnmaster, 1, {ARG_TYPE_SINT} ),
|
||||||
|
IPCCOMMAND( killclient, 1, {ARG_TYPE_SINT} ),
|
||||||
|
IPCCOMMAND( quit, 1, {ARG_TYPE_NONE} ),
|
||||||
|
IPCCOMMAND( setlayoutsafe, 1, {ARG_TYPE_PTR} ),
|
||||||
|
IPCCOMMAND( setmfact, 1, {ARG_TYPE_FLOAT} ),
|
||||||
|
IPCCOMMAND( setstatus, 1, {ARG_TYPE_STR} ),
|
||||||
|
IPCCOMMAND( tag, 1, {ARG_TYPE_UINT} ),
|
||||||
|
IPCCOMMAND( tagmon, 1, {ARG_TYPE_UINT} ),
|
||||||
|
IPCCOMMAND( togglebar, 1, {ARG_TYPE_NONE} ),
|
||||||
|
IPCCOMMAND( togglefloating, 1, {ARG_TYPE_NONE} ),
|
||||||
|
IPCCOMMAND( toggletag, 1, {ARG_TYPE_UINT} ),
|
||||||
|
IPCCOMMAND( toggleview, 1, {ARG_TYPE_UINT} ),
|
||||||
|
IPCCOMMAND( view, 1, {ARG_TYPE_UINT} ),
|
||||||
|
IPCCOMMAND( zoom, 1, {ARG_TYPE_NONE} ),
|
||||||
|
IPCCOMMAND( setcfact, 1, {ARG_TYPE_FLOAT} ),
|
||||||
|
IPCCOMMAND( cyclelayout, 1, {ARG_TYPE_SINT} ),
|
||||||
|
};
|
||||||
73
config.mk
Normal file
73
config.mk
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
# FreeBSD (uncomment)
|
||||||
|
#X11INC = /usr/local/include
|
||||||
|
#X11LIB = /usr/local/lib
|
||||||
|
|
||||||
|
# Xinerama, comment if you don't want it
|
||||||
|
XINERAMALIBS = -lXinerama
|
||||||
|
XINERAMAFLAGS = -DXINERAMA
|
||||||
|
|
||||||
|
# freetype
|
||||||
|
FREETYPELIBS = -lfontconfig -lXft
|
||||||
|
FREETYPEINC = /usr/include/freetype2
|
||||||
|
# FreeBSD (uncomment)
|
||||||
|
#FREETYPEINC = /usr/local/include/freetype2
|
||||||
|
# OpenBSD (uncomment)
|
||||||
|
#FREETYPEINC = ${X11INC}/freetype2
|
||||||
|
# OpenBSD - Uncomment this for the swallow patch / SWALLOW_PATCH
|
||||||
|
#KVMLIB = -lkvm
|
||||||
|
|
||||||
|
# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH)
|
||||||
|
#XRENDER = -lXrender
|
||||||
|
|
||||||
|
# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH
|
||||||
|
#MPDCLIENT = -lmpdclient
|
||||||
|
|
||||||
|
# Uncomment for the pango patch / BAR_PANGO_PATCH
|
||||||
|
#PANGOINC = `pkg-config --cflags xft pango pangoxft`
|
||||||
|
#PANGOLIB = `pkg-config --libs xft pango pangoxft`
|
||||||
|
|
||||||
|
# Uncomment for the ipc patch / IPC_PATCH
|
||||||
|
YAJLLIBS = -lyajl
|
||||||
|
YAJLINC = -I/usr/include/yajl
|
||||||
|
|
||||||
|
# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH
|
||||||
|
#XEXTLIB = -lXext
|
||||||
|
|
||||||
|
# Uncomment this for the swallow patch / SWALLOW_PATCH
|
||||||
|
XCBLIBS = -lX11-xcb -lxcb -lxcb-res
|
||||||
|
|
||||||
|
# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH
|
||||||
|
IMLIB2LIBS = -lImlib2
|
||||||
|
|
||||||
|
# Uncomment for the bidi patch
|
||||||
|
#BDINC = `pkg-config --cflags fribidi`
|
||||||
|
#BDLIBS = `pkg-config --libs fribidi`
|
||||||
|
|
||||||
|
# includes and libs
|
||||||
|
INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} ${BDINC}
|
||||||
|
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} $(BDLIBS)
|
||||||
|
|
||||||
|
# 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-unused-function -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
|
||||||
|
LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
# Solaris
|
||||||
|
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
|
||||||
|
#LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
# compiler and linker
|
||||||
|
CC = cc
|
||||||
447
drw.c
Normal file
447
drw.c
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
static int
|
||||||
|
utf8decode(const char *s_in, long *u, int *err)
|
||||||
|
{
|
||||||
|
static const unsigned char lens[] = {
|
||||||
|
/* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
/* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
|
||||||
|
/* 110XX */ 2, 2, 2, 2,
|
||||||
|
/* 1110X */ 3, 3,
|
||||||
|
/* 11110 */ 4,
|
||||||
|
/* 11111 */ 0, /* invalid */
|
||||||
|
};
|
||||||
|
static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
|
||||||
|
static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
|
||||||
|
|
||||||
|
const unsigned char *s = (const unsigned char *)s_in;
|
||||||
|
int len = lens[*s >> 3];
|
||||||
|
*u = UTF_INVALID;
|
||||||
|
*err = 1;
|
||||||
|
if (len == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
long cp = s[0] & leading_mask[len - 1];
|
||||||
|
for (int i = 1; i < len; ++i) {
|
||||||
|
if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
|
||||||
|
return i;
|
||||||
|
cp = (cp << 6) | (s[i] & 0x3F);
|
||||||
|
}
|
||||||
|
/* out of range, surrogate, overlong encoding */
|
||||||
|
if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
|
||||||
|
return len;
|
||||||
|
|
||||||
|
*err = 0;
|
||||||
|
*u = cp;
|
||||||
|
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,
|
||||||
|
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, Bool markup)
|
||||||
|
{
|
||||||
|
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, utf8err, 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, invalid_width;
|
||||||
|
static const char invalid[] = "<EFBFBD>";
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (w < lpad)
|
||||||
|
return x + w;
|
||||||
|
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, "...", markup);
|
||||||
|
if (!invalid_width && render)
|
||||||
|
invalid_width = drw_fontset_getwidth(drw, invalid, markup);
|
||||||
|
while (1) {
|
||||||
|
ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
|
||||||
|
utf8str = text;
|
||||||
|
nextfont = NULL;
|
||||||
|
while (*text) {
|
||||||
|
utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
|
||||||
|
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) {
|
||||||
|
text += utf8charlen;
|
||||||
|
utf8strlen += utf8err ? 0 : utf8charlen;
|
||||||
|
ew += utf8err ? 0 : tmpw;
|
||||||
|
} else {
|
||||||
|
nextfont = curfont;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overflow || !charexists || nextfont || utf8err)
|
||||||
|
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 (utf8err && (!render || invalid_width < w)) {
|
||||||
|
if (render)
|
||||||
|
drw_text(drw, x, y, w, h, 0, invalid, invert, markup);
|
||||||
|
x += invalid_width;
|
||||||
|
w -= invalid_width;
|
||||||
|
}
|
||||||
|
if (render && overflow)
|
||||||
|
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert, markup);
|
||||||
|
|
||||||
|
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, Bool markup)
|
||||||
|
{
|
||||||
|
if (!drw || !drw->fonts || !text)
|
||||||
|
return 0;
|
||||||
|
return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
66
drw.h
Normal file
66
drw.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* 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, ColFloat, ColCount }; /* 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_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
|
||||||
|
void drw_fontset_free(Fnt* set);
|
||||||
|
unsigned int drw_fontset_getwidth(Drw *drw, const char *text, Bool markup);
|
||||||
|
|
||||||
|
/* Colorscheme abstraction */
|
||||||
|
void drw_clr_create(
|
||||||
|
Drw *drw,
|
||||||
|
Clr *dest,
|
||||||
|
const char *clrname
|
||||||
|
);
|
||||||
|
Clr *drw_scm_create(
|
||||||
|
Drw *drw,
|
||||||
|
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, Bool markup);
|
||||||
|
|
||||||
|
/* Map functions */
|
||||||
|
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
|
||||||
|
|
||||||
176
dwm.1
Normal file
176
dwm.1
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
.TH DWM 1 dwm\-VERSION
|
||||||
|
.SH NAME
|
||||||
|
dwm \- dynamic window manager
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B dwm
|
||||||
|
.RB [ \-v ]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
dwm is a dynamic window manager for X. It manages windows in tiled, monocle
|
||||||
|
and floating layouts. Either layout can be applied dynamically, optimising the
|
||||||
|
environment for the application in use and the task performed.
|
||||||
|
.P
|
||||||
|
In tiled layouts windows are managed in a master and stacking area. The master
|
||||||
|
area on the left contains one window by default, and the stacking area on the
|
||||||
|
right contains all other windows. The number of master area windows can be
|
||||||
|
adjusted from zero to an arbitrary number. In monocle layout all windows are
|
||||||
|
maximised to the screen size. In floating layout windows can be resized and
|
||||||
|
moved freely. Dialog windows are always managed floating, regardless of the
|
||||||
|
layout applied.
|
||||||
|
.P
|
||||||
|
Windows are grouped by tags. Each window can be tagged with one or multiple
|
||||||
|
tags. Selecting certain tags displays all windows with these tags.
|
||||||
|
.P
|
||||||
|
Each screen contains a small status bar which displays all available tags, the
|
||||||
|
layout, the title of the focused window, and the text read from the root window
|
||||||
|
name property, if the screen is focused. A floating window is indicated with an
|
||||||
|
empty square and a maximised floating window is indicated with a filled square
|
||||||
|
before the windows title. The selected tags are indicated with a different
|
||||||
|
color. The tags of the focused window are indicated with a filled square in the
|
||||||
|
top left corner. The tags which are applied to one or more windows are
|
||||||
|
indicated with an empty square in the top left corner.
|
||||||
|
.P
|
||||||
|
dwm draws a small border around windows to indicate the focus state.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
prints version information to 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.
|
||||||
|
.SS Mouse commands
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button1
|
||||||
|
Move focused window while dragging. Tiled windows will be toggled to the floating state.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button2
|
||||||
|
Toggles focused window between floating and tiled state.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button3
|
||||||
|
Resize focused window while dragging. Tiled windows will be toggled to the floating state.
|
||||||
|
.SH CUSTOMIZATION
|
||||||
|
dwm is customized by creating a custom config.h and (re)compiling the source
|
||||||
|
code. This keeps it fast, secure and simple.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dmenu (1),
|
||||||
|
.BR st (1)
|
||||||
|
.SH ISSUES
|
||||||
|
Java applications which use the XToolkit/XAWT backend may draw grey windows
|
||||||
|
only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
|
||||||
|
JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
|
||||||
|
are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the
|
||||||
|
environment variable
|
||||||
|
.BR AWT_TOOLKIT=MToolkit
|
||||||
|
(to use the older Motif backend instead) or running
|
||||||
|
.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D
|
||||||
|
or
|
||||||
|
.B wmname LG3D
|
||||||
|
(to pretend that a non-reparenting window manager is running that the
|
||||||
|
XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
|
||||||
|
.BR _JAVA_AWT_WM_NONREPARENTING=1 .
|
||||||
|
.SH BUGS
|
||||||
|
Send all bug reports with a patch to hackers@suckless.org.
|
||||||
7
dwm.desktop
Normal file
7
dwm.desktop
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Encoding=UTF-8
|
||||||
|
Name=Dwm
|
||||||
|
Comment=Dynamic window manager
|
||||||
|
Exec=dwm
|
||||||
|
Icon=dwm
|
||||||
|
Type=XSession
|
||||||
211
patch/alttab.c
Normal file
211
patch/alttab.c
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
int alttabn; /* move that many clients forward */
|
||||||
|
int ntabs; /* number of active clients in tag */
|
||||||
|
int isalt;
|
||||||
|
Client **altsnext; /* array of all clients in the tag */
|
||||||
|
Window alttabwin;
|
||||||
|
|
||||||
|
void
|
||||||
|
alttab()
|
||||||
|
{
|
||||||
|
Monitor *m = selmon;
|
||||||
|
|
||||||
|
/* move to next window */
|
||||||
|
if (m->sel && m->sel->snext) {
|
||||||
|
alttabn++;
|
||||||
|
if (alttabn >= ntabs)
|
||||||
|
alttabn = 0; /* reset alttabn */
|
||||||
|
|
||||||
|
focus(altsnext[alttabn]);
|
||||||
|
restack(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* redraw tab */
|
||||||
|
XRaiseWindow(dpy, alttabwin);
|
||||||
|
drawalttab(ntabs, 0, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
alttabend()
|
||||||
|
{
|
||||||
|
Monitor *m = selmon;
|
||||||
|
Client *buff;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!isalt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Move all clients between first and choosen position,
|
||||||
|
* one down in stack and put choosen client to the first position
|
||||||
|
* so they remain in right order for the next time that alt-tab is used
|
||||||
|
*/
|
||||||
|
if (ntabs > 1) {
|
||||||
|
if (alttabn != 0) { /* if user picked original client do nothing */
|
||||||
|
buff = altsnext[alttabn];
|
||||||
|
if (alttabn > 1)
|
||||||
|
for (i = alttabn; i > 0; i--)
|
||||||
|
altsnext[i] = altsnext[i - 1];
|
||||||
|
else /* swap them if there are just 2 clients */
|
||||||
|
altsnext[alttabn] = altsnext[0];
|
||||||
|
altsnext[0] = buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restack clients */
|
||||||
|
for (i = ntabs - 1; i >= 0; i--) {
|
||||||
|
focus(altsnext[i]);
|
||||||
|
restack(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(altsnext); /* free list of clients */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* destroy the window */
|
||||||
|
isalt = 0;
|
||||||
|
ntabs = 0;
|
||||||
|
XUnmapWindow(dpy, alttabwin);
|
||||||
|
XDestroyWindow(dpy, alttabwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drawalttab(int nwins, int first, Monitor *m)
|
||||||
|
{
|
||||||
|
Client *c;
|
||||||
|
int i, h;
|
||||||
|
int y = 0;
|
||||||
|
int px = m->mx;
|
||||||
|
int py = m->my;
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
XSetWindowAttributes wa = {
|
||||||
|
.override_redirect = True,
|
||||||
|
.background_pixmap = ParentRelative,
|
||||||
|
.event_mask = ButtonPressMask|ExposureMask
|
||||||
|
};
|
||||||
|
|
||||||
|
/* decide position of tabwin */
|
||||||
|
if (tabposx == 1)
|
||||||
|
px = m->mx + (m->mw / 2) - (maxwtab / 2);
|
||||||
|
else if (tabposx == 2)
|
||||||
|
px = m->mx + m->mw - maxwtab;
|
||||||
|
|
||||||
|
if (tabposy == 1)
|
||||||
|
py = m->my + (m->mh / 2) - (maxhtab / 2);
|
||||||
|
else if (tabposy == 2)
|
||||||
|
py = m->my + m->mh - maxhtab;
|
||||||
|
|
||||||
|
h = maxhtab;
|
||||||
|
|
||||||
|
alttabwin = XCreateWindow(dpy, root, px, py, maxwtab, maxhtab, 2, DefaultDepth(dpy, screen),
|
||||||
|
CopyFromParent, DefaultVisual(dpy, screen),
|
||||||
|
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
|
||||||
|
|
||||||
|
XDefineCursor(dpy, alttabwin, cursor[CurNormal]->cursor);
|
||||||
|
XMapRaised(dpy, alttabwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
h = maxhtab / ntabs;
|
||||||
|
for (i = 0; i < ntabs; i++) { /* draw all clients into tabwin */
|
||||||
|
c = altsnext[i];
|
||||||
|
if (!ISVISIBLE(c))
|
||||||
|
continue;
|
||||||
|
if (HIDDEN(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
drw_setscheme(drw, scheme[c == m->sel ? SchemeSel : SchemeNorm]);
|
||||||
|
drw_text(drw, 0, y, maxwtab, h, 0, c->name, 0, 0);
|
||||||
|
y += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
drw_setscheme(drw, scheme[SchemeNorm]);
|
||||||
|
drw_map(drw, alttabwin, 0, 0, maxwtab, maxhtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
alttabstart(const Arg *arg)
|
||||||
|
{
|
||||||
|
Client *c;
|
||||||
|
Monitor *m = selmon;
|
||||||
|
int grabbed;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
altsnext = NULL;
|
||||||
|
if (alttabwin)
|
||||||
|
alttabend();
|
||||||
|
|
||||||
|
if (isalt == 1) {
|
||||||
|
alttabend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isalt = 1;
|
||||||
|
alttabn = 0;
|
||||||
|
ntabs = 0;
|
||||||
|
|
||||||
|
for (c = m->clients; c; c = c->next) {
|
||||||
|
if (!ISVISIBLE(c))
|
||||||
|
continue;
|
||||||
|
if (HIDDEN(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++ntabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ntabs) {
|
||||||
|
alttabend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
altsnext = (Client **) malloc(ntabs * sizeof(Client *));
|
||||||
|
|
||||||
|
for (i = 0, c = m->stack; c; c = c->snext) {
|
||||||
|
if (!ISVISIBLE(c))
|
||||||
|
continue;
|
||||||
|
if (HIDDEN(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
altsnext[i] = c;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawalttab(ntabs, 1, m);
|
||||||
|
|
||||||
|
struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
|
||||||
|
|
||||||
|
/* grab keyboard (take all input from keyboard) */
|
||||||
|
grabbed = 1;
|
||||||
|
for (i = 0; i < 1000; i++) {
|
||||||
|
if (XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
|
||||||
|
break;
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
if (i == 1000 - 1)
|
||||||
|
grabbed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
XEvent event;
|
||||||
|
alttab();
|
||||||
|
|
||||||
|
if (grabbed == 0) {
|
||||||
|
alttabend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (grabbed) {
|
||||||
|
XNextEvent(dpy, &event);
|
||||||
|
if (event.type == KeyPress || event.type == KeyRelease) {
|
||||||
|
if (event.type == KeyRelease && event.xkey.keycode == tabmodkey) /* if mod key is released break cycle */
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (event.type == KeyPress) {
|
||||||
|
if (event.xkey.keycode == tabcyclekey) { /* if tab is pressed move to the next window */
|
||||||
|
alttab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c = m->sel;
|
||||||
|
alttabend();
|
||||||
|
|
||||||
|
XUngrabKeyboard(dpy, CurrentTime);
|
||||||
|
focus(c);
|
||||||
|
restack(m);
|
||||||
|
}
|
||||||
5
patch/alttab.h
Normal file
5
patch/alttab.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static void drawalttab(int nwins, int first, Monitor *m);
|
||||||
|
static void alttabstart(const Arg *arg);
|
||||||
|
static void alttabend();
|
||||||
12
patch/attachx.c
Normal file
12
patch/attachx.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
void
|
||||||
|
attachx(Client *c)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!(c->mon->sel == NULL || c->mon->sel == c || c->mon->sel->isfloating)) {
|
||||||
|
c->next = c->mon->sel->next;
|
||||||
|
c->mon->sel->next = c;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
attach(c); // master (default)
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/attachx.h
Normal file
2
patch/attachx.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void attachx(Client *c);
|
||||||
|
|
||||||
39
patch/bar.c
Normal file
39
patch/bar.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
void
|
||||||
|
barhover(XEvent *e, Bar *bar)
|
||||||
|
{
|
||||||
|
const BarRule *br;
|
||||||
|
Monitor *m = bar->mon;
|
||||||
|
XMotionEvent *ev = &e->xmotion;
|
||||||
|
BarArg barg = { 0, 0, 0, 0 };
|
||||||
|
int r;
|
||||||
|
|
||||||
|
for (r = 0; r < LENGTH(barrules); r++) {
|
||||||
|
br = &barrules[r];
|
||||||
|
if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->hoverfunc == NULL)
|
||||||
|
continue;
|
||||||
|
if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num)
|
||||||
|
continue;
|
||||||
|
if (bar->x[r] > ev->x || ev->x > bar->x[r] + bar->w[r])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
barg.x = ev->x - bar->x[r];
|
||||||
|
barg.y = ev->y - bar->borderpx;
|
||||||
|
barg.w = bar->w[r];
|
||||||
|
barg.h = bar->bh - 2 * bar->borderpx;
|
||||||
|
|
||||||
|
br->hoverfunc(bar, &barg, ev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bar *
|
||||||
|
wintobar(Window win)
|
||||||
|
{
|
||||||
|
Monitor *m;
|
||||||
|
Bar *bar;
|
||||||
|
for (m = mons; m; m = m->next)
|
||||||
|
for (bar = m->bar; bar; bar = bar->next)
|
||||||
|
if (bar->win == win)
|
||||||
|
return bar;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
2
patch/bar.h
Normal file
2
patch/bar.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void barhover(XEvent *e, Bar *bar);
|
||||||
|
static Bar *wintobar(Window win);
|
||||||
90
patch/bar_anybar.c
Normal file
90
patch/bar_anybar.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
void
|
||||||
|
managealtbar(Window win, XWindowAttributes *wa)
|
||||||
|
{
|
||||||
|
Monitor *m;
|
||||||
|
Bar *bar;
|
||||||
|
int i = 0;
|
||||||
|
if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height)))
|
||||||
|
return;
|
||||||
|
for (bar = m->bar; bar && bar->win && bar->next; bar = bar->next); // find last bar
|
||||||
|
if (!bar) {
|
||||||
|
bar = m->bar = ecalloc(1, sizeof(Bar));
|
||||||
|
bar->topbar = topbar;
|
||||||
|
} else if (bar && bar->win) {
|
||||||
|
i = bar->idx + 1;
|
||||||
|
bar->next = ecalloc(1, sizeof(Bar));
|
||||||
|
bar->next->topbar = topbar;
|
||||||
|
bar = bar->next;
|
||||||
|
}
|
||||||
|
bar->external = 1;
|
||||||
|
bar->showbar = 1;
|
||||||
|
bar->mon = m;
|
||||||
|
bar->idx = i;
|
||||||
|
bar->borderpx = 0;
|
||||||
|
bar->win = win;
|
||||||
|
bar->bw = wa->width;
|
||||||
|
bar->bh = wa->height;
|
||||||
|
updatebarpos(m);
|
||||||
|
arrange(m);
|
||||||
|
XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
|
||||||
|
XMapWindow(dpy, win);
|
||||||
|
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
|
||||||
|
arrange(selmon);
|
||||||
|
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
|
||||||
|
(unsigned char *) &win, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spawnbar()
|
||||||
|
{
|
||||||
|
if (*altbarcmd)
|
||||||
|
system(altbarcmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unmanagealtbar(Window w)
|
||||||
|
{
|
||||||
|
Monitor *m = wintomon(w);
|
||||||
|
Bar *bar, *next, *prev = NULL;
|
||||||
|
|
||||||
|
if (!m)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (bar = m->bar; bar && bar->win; bar = next) {
|
||||||
|
next = bar->next;
|
||||||
|
if (bar->win == w) {
|
||||||
|
if (prev)
|
||||||
|
prev->next = next;
|
||||||
|
else
|
||||||
|
m->bar = next;
|
||||||
|
free(bar);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = bar;
|
||||||
|
}
|
||||||
|
updatebarpos(m);
|
||||||
|
arrange(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
wmclasscontains(Window win, const char *class, const char *name)
|
||||||
|
{
|
||||||
|
XClassHint ch = { NULL, NULL };
|
||||||
|
int res = 1;
|
||||||
|
|
||||||
|
if (XGetClassHint(dpy, win, &ch)) {
|
||||||
|
if (ch.res_name && strstr(ch.res_name, name) == NULL)
|
||||||
|
res = 0;
|
||||||
|
if (ch.res_class && strstr(ch.res_class, class) == NULL)
|
||||||
|
res = 0;
|
||||||
|
} else
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
if (ch.res_class)
|
||||||
|
XFree(ch.res_class);
|
||||||
|
if (ch.res_name)
|
||||||
|
XFree(ch.res_name);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
5
patch/bar_anybar.h
Normal file
5
patch/bar_anybar.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
static void managealtbar(Window win, XWindowAttributes *wa);
|
||||||
|
static void spawnbar();
|
||||||
|
static void unmanagealtbar(Window w);
|
||||||
|
static int wmclasscontains(Window win, const char *class, const char *name);
|
||||||
|
|
||||||
52
patch/bar_ewmhtags.c
Normal file
52
patch/bar_ewmhtags.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
void
|
||||||
|
setcurrentdesktop(void)
|
||||||
|
{
|
||||||
|
long data[] = { 0 };
|
||||||
|
XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setdesktopnames(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
XTextProperty text;
|
||||||
|
char *tags[NUMTAGS];
|
||||||
|
for (i = 0; i < NUMTAGS; i++)
|
||||||
|
tags[i] = tagicon(selmon, i);
|
||||||
|
Xutf8TextListToTextProperty(dpy, tags, NUMTAGS, XUTF8StringStyle, &text);
|
||||||
|
XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setfloatinghint(Client *c)
|
||||||
|
{
|
||||||
|
Atom target = XInternAtom(dpy, "_IS_FLOATING", 0);
|
||||||
|
unsigned int floating[1] = {c->isfloating};
|
||||||
|
XChangeProperty(dpy, c->win, target, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)floating, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setnumdesktops(void)
|
||||||
|
{
|
||||||
|
long data[] = { NUMTAGS };
|
||||||
|
XChangeProperty(dpy, root, netatom[NetNumberOfDesktops], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setviewport(void)
|
||||||
|
{
|
||||||
|
long data[] = { 0, 0 };
|
||||||
|
XChangeProperty(dpy, root, netatom[NetDesktopViewport], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
updatecurrentdesktop(void)
|
||||||
|
{
|
||||||
|
long rawdata[] = { selmon->tagset[selmon->seltags] };
|
||||||
|
int i = 0;
|
||||||
|
while (*rawdata >> (i + 1)) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
long data[] = { i };
|
||||||
|
XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
|
||||||
|
}
|
||||||
7
patch/bar_ewmhtags.h
Normal file
7
patch/bar_ewmhtags.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
static void setcurrentdesktop(void);
|
||||||
|
static void setdesktopnames(void);
|
||||||
|
static void setfloatinghint(Client *c);
|
||||||
|
static void setnumdesktops(void);
|
||||||
|
static void setviewport(void);
|
||||||
|
static void updatecurrentdesktop(void);
|
||||||
|
|
||||||
104
patch/bar_indicators.c
Normal file
104
patch/bar_indicators.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/* Indicator properties, you can override these in your config.h if you want. */
|
||||||
|
#ifndef TAGSINDICATOR
|
||||||
|
#define TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on
|
||||||
|
#endif
|
||||||
|
#ifndef TAGSPX
|
||||||
|
#define TAGSPX 5 // # pixels for tag grid boxes
|
||||||
|
#endif
|
||||||
|
#ifndef TAGSROWS
|
||||||
|
#define TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type)
|
||||||
|
{
|
||||||
|
int i, boxw, boxs, indn = 0;
|
||||||
|
if (!(occ & 1 << tag) || type == INDICATOR_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
boxs = drw->fonts->h / 9;
|
||||||
|
boxw = drw->fonts->h / 6 + 2;
|
||||||
|
if (filled == -1)
|
||||||
|
filled = m == selmon && m->sel && m->sel->tags & 1 << tag;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
case INDICATOR_TOP_LEFT_SQUARE:
|
||||||
|
drw_rect(drw, x + boxs, y + boxs, boxw, boxw, filled, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_TOP_LEFT_LARGER_SQUARE:
|
||||||
|
drw_rect(drw, x + boxs + 2, y + boxs+1, boxw+1, boxw+1, filled, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_TOP_BAR:
|
||||||
|
drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), boxw/2, filled, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_TOP_BAR_SLIM:
|
||||||
|
drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), 1, 0, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_BOTTOM_BAR:
|
||||||
|
drw_rect(drw, x + boxw, y + h - boxw/2, w - ( 2 * boxw + 1), boxw/2, filled, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_BOTTOM_BAR_SLIM:
|
||||||
|
drw_rect(drw, x + boxw, y + h - 1, w - ( 2 * boxw + 1), 1, 0, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_BOX:
|
||||||
|
drw_rect(drw, x + boxw, y, w - 2 * boxw, h, 0, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_BOX_WIDER:
|
||||||
|
drw_rect(drw, x + boxw/2, y, w - boxw, h, 0, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_BOX_FULL:
|
||||||
|
drw_rect(drw, x, y, w - 2, h, 0, invert);
|
||||||
|
break;
|
||||||
|
case INDICATOR_CLIENT_DOTS:
|
||||||
|
for (c = m->clients; c; c = c->next) {
|
||||||
|
if (c->tags & (1 << tag)) {
|
||||||
|
drw_rect(drw, x, 1 + (indn * 2), m->sel == c ? 6 : 1, 1, 1, invert);
|
||||||
|
indn++;
|
||||||
|
}
|
||||||
|
if (h <= 1 + (indn * 2)) {
|
||||||
|
indn = 0;
|
||||||
|
x += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INDICATOR_RIGHT_TAGS:
|
||||||
|
if (!c)
|
||||||
|
break;
|
||||||
|
for (i = 0; i < NUMTAGS; i++) {
|
||||||
|
drw_rect(drw,
|
||||||
|
( x + w - 2 - ((NUMTAGS / TAGSROWS) * TAGSPX)
|
||||||
|
- (i % (NUMTAGS/TAGSROWS)) + ((i % (NUMTAGS / TAGSROWS)) * TAGSPX)
|
||||||
|
),
|
||||||
|
( y + 2 + ((i / (NUMTAGS/TAGSROWS)) * TAGSPX)
|
||||||
|
- ((i / (NUMTAGS/TAGSROWS)))
|
||||||
|
),
|
||||||
|
TAGSPX, TAGSPX, (c->tags >> i) & 1, 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INDICATOR_PLUS_AND_LARGER_SQUARE:
|
||||||
|
boxs += 2;
|
||||||
|
boxw += 2;
|
||||||
|
/* falls through */
|
||||||
|
case INDICATOR_PLUS_AND_SQUARE:
|
||||||
|
drw_rect(drw, x + boxs, y + boxs, boxw % 2 ? boxw : boxw + 1, boxw % 2 ? boxw : boxw + 1, filled, invert);
|
||||||
|
/* falls through */
|
||||||
|
case INDICATOR_PLUS:
|
||||||
|
if (!(boxw % 2))
|
||||||
|
boxw += 1;
|
||||||
|
drw_rect(drw, x + boxs + boxw / 2, y + boxs, 1, boxw, filled, invert); // |
|
||||||
|
drw_rect(drw, x + boxs, y + boxs + boxw / 2, boxw + 1, 1, filled, invert); // ‒
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert)
|
||||||
|
{
|
||||||
|
if (c->isfloating)
|
||||||
|
drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatindicatortype);
|
||||||
|
else
|
||||||
|
drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, tiledindicatortype);
|
||||||
|
}
|
||||||
|
|
||||||
21
patch/bar_indicators.h
Normal file
21
patch/bar_indicators.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
enum {
|
||||||
|
INDICATOR_NONE,
|
||||||
|
INDICATOR_TOP_LEFT_SQUARE,
|
||||||
|
INDICATOR_TOP_LEFT_LARGER_SQUARE,
|
||||||
|
INDICATOR_TOP_BAR,
|
||||||
|
INDICATOR_TOP_BAR_SLIM,
|
||||||
|
INDICATOR_BOTTOM_BAR,
|
||||||
|
INDICATOR_BOTTOM_BAR_SLIM,
|
||||||
|
INDICATOR_BOX,
|
||||||
|
INDICATOR_BOX_WIDER,
|
||||||
|
INDICATOR_BOX_FULL,
|
||||||
|
INDICATOR_CLIENT_DOTS,
|
||||||
|
INDICATOR_RIGHT_TAGS,
|
||||||
|
INDICATOR_PLUS,
|
||||||
|
INDICATOR_PLUS_AND_SQUARE,
|
||||||
|
INDICATOR_PLUS_AND_LARGER_SQUARE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type);
|
||||||
|
static void drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert);
|
||||||
|
|
||||||
17
patch/bar_ltsymbol.c
Normal file
17
patch/bar_ltsymbol.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
int
|
||||||
|
width_ltsymbol(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
return TEXTW(bar->mon->ltsymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
draw_ltsymbol(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, bar->mon->ltsymbol, 0, False);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
click_ltsymbol(Bar *bar, Arg *arg, BarArg *a)
|
||||||
|
{
|
||||||
|
return ClkLtSymbol;
|
||||||
|
}
|
||||||
3
patch/bar_ltsymbol.h
Normal file
3
patch/bar_ltsymbol.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
static int width_ltsymbol(Bar *bar, BarArg *a);
|
||||||
|
static int draw_ltsymbol(Bar *bar, BarArg *a);
|
||||||
|
static int click_ltsymbol(Bar *bar, Arg *arg, BarArg *a);
|
||||||
18
patch/bar_status.c
Normal file
18
patch/bar_status.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
int
|
||||||
|
width_status(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
return TEXTWM(stext);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
draw_status(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, stext, 0, True);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
click_status(Bar *bar, Arg *arg, BarArg *a)
|
||||||
|
{
|
||||||
|
return ClkStatusText;
|
||||||
|
}
|
||||||
|
|
||||||
4
patch/bar_status.h
Normal file
4
patch/bar_status.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
static int width_status(Bar *bar, BarArg *a);
|
||||||
|
static int draw_status(Bar *bar, BarArg *a);
|
||||||
|
static int click_status(Bar *bar, Arg *arg, BarArg *a);
|
||||||
|
|
||||||
190
patch/bar_systray.c
Normal file
190
patch/bar_systray.c
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
static Systray *systray = NULL;
|
||||||
|
static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
|
||||||
|
|
||||||
|
int
|
||||||
|
width_systray(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
unsigned int w = 0;
|
||||||
|
Client *i;
|
||||||
|
if (!systray)
|
||||||
|
return 1;
|
||||||
|
if (showsystray) {
|
||||||
|
for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next);
|
||||||
|
if (!w)
|
||||||
|
XMoveWindow(dpy, systray->win, -systray->h, bar->by);
|
||||||
|
}
|
||||||
|
return w ? w + lrpad - systrayspacing : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
draw_systray(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
if (!showsystray)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
XSetWindowAttributes wa;
|
||||||
|
XWindowChanges wc;
|
||||||
|
Client *i;
|
||||||
|
unsigned int w;
|
||||||
|
|
||||||
|
if (!systray) {
|
||||||
|
/* init systray */
|
||||||
|
if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
|
||||||
|
die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
|
||||||
|
|
||||||
|
wa.override_redirect = True;
|
||||||
|
wa.event_mask = ButtonPressMask|ExposureMask;
|
||||||
|
wa.border_pixel = 0;
|
||||||
|
systray->h = MIN(a->h, drw->fonts->h);
|
||||||
|
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
|
||||||
|
systray->win = XCreateSimpleWindow(dpy, root, bar->bx + a->x + lrpad / 2, -systray->h, MIN(a->w, 1), systray->h, 0, 0, scheme[SchemeNorm][ColBg].pixel);
|
||||||
|
XChangeWindowAttributes(dpy, systray->win, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &wa);
|
||||||
|
|
||||||
|
XSelectInput(dpy, systray->win, SubstructureNotifyMask);
|
||||||
|
XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
|
||||||
|
PropModeReplace, (unsigned char *)&systrayorientation, 1);
|
||||||
|
XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32,
|
||||||
|
PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1);
|
||||||
|
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 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
systray->bar = bar;
|
||||||
|
|
||||||
|
wc.stack_mode = Above;
|
||||||
|
wc.sibling = bar->win;
|
||||||
|
XConfigureWindow(dpy, systray->win, CWSibling|CWStackMode, &wc);
|
||||||
|
|
||||||
|
drw_setscheme(drw, scheme[SchemeNorm]);
|
||||||
|
for (w = 0, i = systray->icons; i; i = i->next) {
|
||||||
|
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
|
||||||
|
XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
|
||||||
|
XMapRaised(dpy, i->win);
|
||||||
|
i->x = w;
|
||||||
|
XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
|
||||||
|
w += i->w;
|
||||||
|
if (i->next)
|
||||||
|
w += systrayspacing;
|
||||||
|
if (i->mon != bar->mon)
|
||||||
|
i->mon = bar->mon;
|
||||||
|
}
|
||||||
|
|
||||||
|
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
|
||||||
|
XChangeWindowAttributes(dpy, systray->win, CWBackPixel, &wa);
|
||||||
|
XClearWindow(dpy, systray->win);
|
||||||
|
|
||||||
|
XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by + a->y + (a->h - systray->h) / 2: -systray->h), MAX(w, 1), systray->h);
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
click_systray(Bar *bar, Arg *arg, BarArg *a)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
XReparentWindow(dpy, i->win, root, 0, 0);
|
||||||
|
free(i);
|
||||||
|
drawbarwin(systray->bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
resizerequest(XEvent *e)
|
||||||
|
{
|
||||||
|
XResizeRequestEvent *ev = &e->xresizerequest;
|
||||||
|
Client *i;
|
||||||
|
|
||||||
|
if ((i = wintosystrayicon(ev->window))) {
|
||||||
|
updatesystrayicongeom(i, ev->width, ev->height);
|
||||||
|
drawbarwin(systray->bar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
updatesystrayicongeom(Client *i, int w, int h)
|
||||||
|
{
|
||||||
|
if (!systray)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int icon_height = systray->h;
|
||||||
|
if (i) {
|
||||||
|
i->h = icon_height;
|
||||||
|
if (w == h)
|
||||||
|
i->w = icon_height;
|
||||||
|
else if (h == icon_height)
|
||||||
|
i->w = w;
|
||||||
|
else
|
||||||
|
i->w = (int) ((float)icon_height * ((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 > icon_height) {
|
||||||
|
if (i->w == i->h)
|
||||||
|
i->w = icon_height;
|
||||||
|
else
|
||||||
|
i->w = (int) ((float)icon_height * ((float)i->w / (float)i->h));
|
||||||
|
i->h = icon_height;
|
||||||
|
}
|
||||||
|
if (i->w > 2 * icon_height)
|
||||||
|
i->w = icon_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
updatesystrayiconstate(Client *i, XPropertyEvent *ev)
|
||||||
|
{
|
||||||
|
long flags;
|
||||||
|
int code = 0;
|
||||||
|
|
||||||
|
if (!showsystray || !systray || !i || ev->atom != xatom[XembedInfo] ||
|
||||||
|
!(flags = getatomprop(i, xatom[XembedInfo], 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *
|
||||||
|
wintosystrayicon(Window w)
|
||||||
|
{
|
||||||
|
if (!systray)
|
||||||
|
return NULL;
|
||||||
|
Client *i = NULL;
|
||||||
|
if (!showsystray || !w)
|
||||||
|
return i;
|
||||||
|
for (i = systray->icons; i && i->win != w; i = i->next);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
40
patch/bar_systray.h
Normal file
40
patch/bar_systray.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#define SYSTEM_TRAY_REQUEST_DOCK 0
|
||||||
|
#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 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 { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
|
||||||
|
|
||||||
|
typedef struct Systray Systray;
|
||||||
|
struct Systray {
|
||||||
|
Window win;
|
||||||
|
Client *icons;
|
||||||
|
Bar *bar;
|
||||||
|
int h;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* bar integration */
|
||||||
|
static int width_systray(Bar *bar, BarArg *a);
|
||||||
|
static int draw_systray(Bar *bar, BarArg *a);
|
||||||
|
static int click_systray(Bar *bar, Arg *arg, BarArg *a);
|
||||||
|
|
||||||
|
/* function declarations */
|
||||||
|
static void removesystrayicon(Client *i);
|
||||||
|
static void resizerequest(XEvent *e);
|
||||||
|
static void updatesystrayicongeom(Client *i, int w, int h);
|
||||||
|
static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
|
||||||
|
static Client *wintosystrayicon(Window w);
|
||||||
|
|
||||||
9
patch/bar_tagicons.c
Normal file
9
patch/bar_tagicons.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
char *
|
||||||
|
tagicon(Monitor *m, int tag)
|
||||||
|
{
|
||||||
|
int tagindex = tag + NUMTAGS * m->num;
|
||||||
|
if (tagindex >= LENGTH(tagicons[DEFAULT_TAGS]))
|
||||||
|
tagindex = tagindex % LENGTH(tagicons[DEFAULT_TAGS]);
|
||||||
|
return tagicons[DEFAULT_TAGS][tagindex];
|
||||||
|
}
|
||||||
|
|
||||||
8
patch/bar_tagicons.h
Normal file
8
patch/bar_tagicons.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
enum {
|
||||||
|
DEFAULT_TAGS,
|
||||||
|
ALTERNATIVE_TAGS,
|
||||||
|
ALT_TAGS_DECORATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
static char * tagicon(Monitor *m, int tag);
|
||||||
|
|
||||||
91
patch/bar_tagpreview.c
Normal file
91
patch/bar_tagpreview.c
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include <Imlib2.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
createpreview(Monitor *m)
|
||||||
|
{
|
||||||
|
if (m->tagwin) {
|
||||||
|
XMoveResizeWindow(
|
||||||
|
dpy, m->tagwin,
|
||||||
|
m->mx,
|
||||||
|
m->bar->by + bh,
|
||||||
|
m->mw / scalepreview,
|
||||||
|
m->mh / scalepreview
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XSetWindowAttributes wa = {
|
||||||
|
.override_redirect = True,
|
||||||
|
.background_pixmap = ParentRelative,
|
||||||
|
.event_mask = ButtonPressMask|ExposureMask
|
||||||
|
};
|
||||||
|
|
||||||
|
m->tagwin = XCreateWindow(dpy, root, m->wx, m->bar->by + bh, m->mw / scalepreview, m->mh / scalepreview, 0,
|
||||||
|
DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
|
||||||
|
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa
|
||||||
|
);
|
||||||
|
XDefineCursor(dpy, m->tagwin, cursor[CurNormal]->cursor);
|
||||||
|
XMapRaised(dpy, m->tagwin);
|
||||||
|
XUnmapWindow(dpy, m->tagwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hidetagpreview(Monitor *m)
|
||||||
|
{
|
||||||
|
m->previewshow = 0;
|
||||||
|
XUnmapWindow(dpy, m->tagwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
showtagpreview(int tag, int x, int y)
|
||||||
|
{
|
||||||
|
Monitor *m = selmon;
|
||||||
|
|
||||||
|
if (!m->tagwin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m->tagmap[tag]) {
|
||||||
|
XSetWindowBackgroundPixmap(dpy, m->tagwin, m->tagmap[tag]);
|
||||||
|
XCopyArea(dpy, m->tagmap[tag], m->tagwin, drw->gc, 0, 0, m->mw / scalepreview, m->mh / scalepreview, 0, 0);
|
||||||
|
XMoveWindow(dpy, m->tagwin, x, y);
|
||||||
|
XSync(dpy, False);
|
||||||
|
XMapWindow(dpy, m->tagwin);
|
||||||
|
} else
|
||||||
|
XUnmapWindow(dpy, m->tagwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tagpreviewswitchtag(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned int occ = 0;
|
||||||
|
Client *c;
|
||||||
|
Imlib_Image image;
|
||||||
|
Monitor *m = selmon;
|
||||||
|
|
||||||
|
if (!m->tagwin)
|
||||||
|
createpreview(m);
|
||||||
|
|
||||||
|
for (c = m->clients; c; c = c->next)
|
||||||
|
occ |= c->tags;
|
||||||
|
for (i = 0; i < NUMTAGS; i++) {
|
||||||
|
if (m->tagset[m->seltags] & 1 << i) {
|
||||||
|
if (m->tagmap[i] != 0) {
|
||||||
|
XFreePixmap(dpy, m->tagmap[i]);
|
||||||
|
m->tagmap[i] = 0;
|
||||||
|
}
|
||||||
|
if (occ & 1 << i) {
|
||||||
|
image = imlib_create_image(sw, sh);
|
||||||
|
imlib_context_set_image(image);
|
||||||
|
imlib_context_set_display(dpy);
|
||||||
|
imlib_context_set_visual(DefaultVisual(dpy, screen));
|
||||||
|
imlib_context_set_drawable(root);
|
||||||
|
imlib_copy_drawable_to_image(0, m->mx, m->my, m->mw ,m->mh, 0, 0, 1);
|
||||||
|
m->tagmap[i] = XCreatePixmap(dpy, m->tagwin, m->mw / scalepreview, m->mh / scalepreview, DefaultDepth(dpy, screen));
|
||||||
|
imlib_context_set_drawable(m->tagmap[i]);
|
||||||
|
imlib_render_image_part_on_drawable_at_size(0, 0, m->mw, m->mh, 0, 0, m->mw / scalepreview, m->mh / scalepreview);
|
||||||
|
imlib_free_image();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
patch/bar_tagpreview.h
Normal file
4
patch/bar_tagpreview.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
static void createpreview(Monitor *m);
|
||||||
|
static void hidetagpreview(Monitor *m);
|
||||||
|
static void showtagpreview(int tag, int x, int y);
|
||||||
|
static void tagpreviewswitchtag(void);
|
||||||
98
patch/bar_tags.c
Normal file
98
patch/bar_tags.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
int
|
||||||
|
width_tags(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
int w, i;
|
||||||
|
|
||||||
|
for (w = 0, i = 0; i < NUMTAGS; i++) {
|
||||||
|
w += TEXTW(tagicon(bar->mon, i));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
draw_tags(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
int invert;
|
||||||
|
int w, x = a->x;
|
||||||
|
unsigned int i, occ = 0, urg = 0;
|
||||||
|
char *icon;
|
||||||
|
Client *c;
|
||||||
|
Monitor *m = bar->mon;
|
||||||
|
|
||||||
|
for (c = m->clients; c; c = c->next) {
|
||||||
|
occ |= c->tags;
|
||||||
|
if (c->isurgent)
|
||||||
|
urg |= c->tags;
|
||||||
|
}
|
||||||
|
for (i = 0; i < NUMTAGS; i++) {
|
||||||
|
|
||||||
|
icon = tagicon(bar->mon, i);
|
||||||
|
invert = 0;
|
||||||
|
w = TEXTW(icon);
|
||||||
|
drw_setscheme(drw, scheme[
|
||||||
|
m->tagset[m->seltags] & 1 << i
|
||||||
|
? SchemeTagsSel
|
||||||
|
: urg & 1 << i
|
||||||
|
? SchemeUrg
|
||||||
|
: SchemeTagsNorm
|
||||||
|
]);
|
||||||
|
drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False);
|
||||||
|
drawindicator(m, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype);
|
||||||
|
x += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
click_tags(Bar *bar, Arg *arg, BarArg *a)
|
||||||
|
{
|
||||||
|
int i = 0, x = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x += TEXTW(tagicon(bar->mon, i));
|
||||||
|
} while (a->x >= x && ++i < NUMTAGS);
|
||||||
|
if (i < NUMTAGS) {
|
||||||
|
arg->ui = 1 << i;
|
||||||
|
}
|
||||||
|
if (selmon->previewshow != 0) {
|
||||||
|
hidetagpreview(selmon);
|
||||||
|
}
|
||||||
|
return ClkTagBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
hover_tags(Bar *bar, BarArg *a, XMotionEvent *ev)
|
||||||
|
{
|
||||||
|
int i = 0, x = lrpad / 2;
|
||||||
|
int px, py;
|
||||||
|
Monitor *m = bar->mon;
|
||||||
|
int ov = 0;
|
||||||
|
int oh = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x += TEXTW(tagicon(bar->mon, i));
|
||||||
|
} while (a->x >= x && ++i < NUMTAGS);
|
||||||
|
|
||||||
|
if (i < NUMTAGS) {
|
||||||
|
if ((i + 1) != selmon->previewshow && !(selmon->tagset[selmon->seltags] & 1 << i)) {
|
||||||
|
if (bar->by > m->my + m->mh / 2) // bottom bar
|
||||||
|
py = bar->by - m->mh / scalepreview - oh;
|
||||||
|
else // top bar
|
||||||
|
py = bar->by + bar->bh + oh;
|
||||||
|
px = bar->bx + ev->x - m->mw / scalepreview / 2;
|
||||||
|
if (px + m->mw / scalepreview > m->mx + m->mw)
|
||||||
|
px = m->wx + m->ww - m->mw / scalepreview - ov;
|
||||||
|
else if (px < bar->bx)
|
||||||
|
px = m->wx + ov;
|
||||||
|
selmon->previewshow = i + 1;
|
||||||
|
showtagpreview(i, px, py);
|
||||||
|
} else if (selmon->tagset[selmon->seltags] & 1 << i) {
|
||||||
|
hidetagpreview(selmon);
|
||||||
|
}
|
||||||
|
} else if (selmon->previewshow != 0) {
|
||||||
|
hidetagpreview(selmon);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
4
patch/bar_tags.h
Normal file
4
patch/bar_tags.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
static int width_tags(Bar *bar, BarArg *a);
|
||||||
|
static int draw_tags(Bar *bar, BarArg *a);
|
||||||
|
static int click_tags(Bar *bar, Arg *arg, BarArg *a);
|
||||||
|
static int hover_tags(Bar *bar, BarArg *a, XMotionEvent *ev);
|
||||||
46
patch/bar_wintitle.c
Normal file
46
patch/bar_wintitle.c
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
int
|
||||||
|
width_wintitle(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
return a->w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
draw_wintitle(Bar *bar, BarArg *a)
|
||||||
|
{
|
||||||
|
int x = a->x, w = a->w;
|
||||||
|
Monitor *m = bar->mon;
|
||||||
|
Client *c = m->sel;
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
|
drw_setscheme(drw, scheme[SchemeTitleNorm]);
|
||||||
|
drw_rect(drw, x, a->y, w, a->h, 1, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tpad = lrpad / 2;
|
||||||
|
int tx = x;
|
||||||
|
int tw = w;
|
||||||
|
|
||||||
|
drw_setscheme(drw, scheme[m == selmon ? SchemeTitleSel : SchemeTitleNorm]);
|
||||||
|
|
||||||
|
if (w <= TEXTW("A") - lrpad + tpad) // reduce text padding if wintitle is too small
|
||||||
|
tpad = (w - TEXTW("A") + lrpad < 0 ? 0 : (w - TEXTW("A") + lrpad) / 2);
|
||||||
|
|
||||||
|
XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel);
|
||||||
|
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, a->y, w, a->h);
|
||||||
|
|
||||||
|
tx += tpad;
|
||||||
|
tw -= lrpad;
|
||||||
|
|
||||||
|
drw_text(drw, tx, a->y, tw, a->h, 0, c->name, 0, False);
|
||||||
|
|
||||||
|
drawstateindicator(m, c, 1, x, a->y, w, a->h, 0, 0, c->isfixed);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
click_wintitle(Bar *bar, Arg *arg, BarArg *a)
|
||||||
|
{
|
||||||
|
return ClkWinTitle;
|
||||||
|
}
|
||||||
|
|
||||||
4
patch/bar_wintitle.h
Normal file
4
patch/bar_wintitle.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
static int width_wintitle(Bar *bar, BarArg *a);
|
||||||
|
static int draw_wintitle(Bar *bar, BarArg *a);
|
||||||
|
static int click_wintitle(Bar *bar, Arg *arg, BarArg *a);
|
||||||
|
|
||||||
24
patch/cfacts.c
Normal file
24
patch/cfacts.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
void
|
||||||
|
setcfact(const Arg *arg)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
Client *c;
|
||||||
|
|
||||||
|
c = selmon->sel;
|
||||||
|
|
||||||
|
if (!arg || !c || !selmon->lt[selmon->sellt]->arrange)
|
||||||
|
return;
|
||||||
|
if (!arg->f)
|
||||||
|
f = 1.0;
|
||||||
|
else if (arg->f > 4.0) // set fact absolutely
|
||||||
|
f = arg->f - 4.0;
|
||||||
|
else
|
||||||
|
f = arg->f + c->cfact;
|
||||||
|
if (f < 0.25)
|
||||||
|
f = 0.25;
|
||||||
|
else if (f > 4.0)
|
||||||
|
f = 4.0;
|
||||||
|
c->cfact = f;
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/cfacts.h
Normal file
2
patch/cfacts.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void setcfact(const Arg *arg);
|
||||||
|
|
||||||
37
patch/cool_autostart.c
Normal file
37
patch/cool_autostart.c
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* dwm will keep pid's of processes from autostart array and kill them at quit */
|
||||||
|
static pid_t *autostart_pids;
|
||||||
|
static size_t autostart_len;
|
||||||
|
|
||||||
|
/* execute command from autostart array */
|
||||||
|
static void
|
||||||
|
autostart_exec()
|
||||||
|
{
|
||||||
|
const char *const *p;
|
||||||
|
struct sigaction sa;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
/* count entries */
|
||||||
|
for (p = autostart; *p; autostart_len++, p++)
|
||||||
|
while (*++p);
|
||||||
|
|
||||||
|
autostart_pids = malloc(autostart_len * sizeof(pid_t));
|
||||||
|
for (p = autostart; *p; i++, p++) {
|
||||||
|
if ((autostart_pids[i] = fork()) == 0) {
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
/* Restore SIGCHLD sighandler to default before spawning a program */
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
sa.sa_handler = SIG_DFL;
|
||||||
|
sigaction(SIGCHLD, &sa, NULL);
|
||||||
|
|
||||||
|
execvp(*p, (char *const *)p);
|
||||||
|
fprintf(stderr, "dwm: execvp %s\n", *p);
|
||||||
|
perror(" failed");
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
/* skip arguments */
|
||||||
|
while (*++p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/cool_autostart.h
Normal file
2
patch/cool_autostart.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void autostart_exec(void);
|
||||||
|
|
||||||
10
patch/cyclelayouts.c
Normal file
10
patch/cyclelayouts.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
void
|
||||||
|
cyclelayout(const Arg *arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int num_layouts = LENGTH(layouts);
|
||||||
|
|
||||||
|
for (i = 0; i < num_layouts && &layouts[i] != selmon->lt[selmon->sellt]; i++);
|
||||||
|
i += arg->i;
|
||||||
|
setlayout(&((Arg) { .v = &layouts[(i % num_layouts + num_layouts) % num_layouts] })); // modulo
|
||||||
|
}
|
||||||
2
patch/cyclelayouts.h
Normal file
2
patch/cyclelayouts.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void cyclelayout(const Arg *arg);
|
||||||
|
|
||||||
66
patch/dragcfact.c
Normal file
66
patch/dragcfact.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
void
|
||||||
|
dragcfact(const Arg *arg)
|
||||||
|
{
|
||||||
|
int prev_x, prev_y, dist_x, dist_y;
|
||||||
|
float fact;
|
||||||
|
Client *c;
|
||||||
|
XEvent ev;
|
||||||
|
Time lasttime = 0;
|
||||||
|
|
||||||
|
if (!(c = selmon->sel))
|
||||||
|
return;
|
||||||
|
if (c->isfloating) {
|
||||||
|
resizemouse(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
restack(selmon);
|
||||||
|
|
||||||
|
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
|
||||||
|
None, cursor[CurIronCross]->cursor, CurrentTime) != GrabSuccess)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2);
|
||||||
|
|
||||||
|
prev_x = prev_y = -999999;
|
||||||
|
|
||||||
|
do {
|
||||||
|
XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
|
||||||
|
switch(ev.type) {
|
||||||
|
case ConfigureRequest:
|
||||||
|
case Expose:
|
||||||
|
case MapRequest:
|
||||||
|
handler[ev.type](&ev);
|
||||||
|
break;
|
||||||
|
case MotionNotify:
|
||||||
|
if ((ev.xmotion.time - lasttime) <= (1000 / 60))
|
||||||
|
continue;
|
||||||
|
lasttime = ev.xmotion.time;
|
||||||
|
if (prev_x == -999999) {
|
||||||
|
prev_x = ev.xmotion.x_root;
|
||||||
|
prev_y = ev.xmotion.y_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
dist_x = ev.xmotion.x - prev_x;
|
||||||
|
dist_y = ev.xmotion.y - prev_y;
|
||||||
|
|
||||||
|
if (abs(dist_x) > abs(dist_y)) {
|
||||||
|
fact = (float) 4.0 * dist_x / c->mon->ww;
|
||||||
|
} else {
|
||||||
|
fact = (float) -4.0 * dist_y / c->mon->wh;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fact)
|
||||||
|
setcfact(&((Arg) { .f = fact }));
|
||||||
|
|
||||||
|
prev_x = ev.xmotion.x;
|
||||||
|
prev_y = ev.xmotion.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (ev.type != ButtonRelease);
|
||||||
|
|
||||||
|
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2);
|
||||||
|
|
||||||
|
XUngrabPointer(dpy, CurrentTime);
|
||||||
|
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/dragcfact.h
Normal file
2
patch/dragcfact.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void dragcfact(const Arg *arg);
|
||||||
|
|
||||||
109
patch/dragmfact.c
Normal file
109
patch/dragmfact.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
void
|
||||||
|
dragmfact(const Arg *arg)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
int py, px; // pointer coordinates
|
||||||
|
int ax, ay, aw, ah; // area position, width and height
|
||||||
|
int center = 0, horizontal = 0, mirror = 0, fixed = 0; // layout configuration
|
||||||
|
double fact;
|
||||||
|
Monitor *m;
|
||||||
|
XEvent ev;
|
||||||
|
Time lasttime = 0;
|
||||||
|
|
||||||
|
m = selmon;
|
||||||
|
|
||||||
|
Client *c;
|
||||||
|
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
|
||||||
|
|
||||||
|
ax = m->wx;
|
||||||
|
ay = m->wy;
|
||||||
|
ah = m->wh;
|
||||||
|
aw = m->ww;
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* do not allow mfact to be modified under certain conditions */
|
||||||
|
if (!m->lt[m->sellt]->arrange // floating layout
|
||||||
|
|| (!fixed && m->nmaster && n <= m->nmaster) // no master
|
||||||
|
|| m->lt[m->sellt]->arrange == &monocle
|
||||||
|
|| m->lt[m->sellt]->arrange == &grid
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (center) {
|
||||||
|
if (horizontal) {
|
||||||
|
px = ax + aw / 2;
|
||||||
|
py = ay + ah / 2 + ah * m->mfact / 2.0;
|
||||||
|
} else { // vertical split
|
||||||
|
px = ax + aw / 2 + aw * m->mfact / 2.0;
|
||||||
|
py = ay + ah / 2;
|
||||||
|
}
|
||||||
|
} else if (horizontal) {
|
||||||
|
px = ax + aw / 2;
|
||||||
|
if (mirror)
|
||||||
|
py = ay + (ah * (1.0 - m->mfact));
|
||||||
|
else
|
||||||
|
py = ay + (ah * m->mfact);
|
||||||
|
} else { // vertical split
|
||||||
|
if (mirror)
|
||||||
|
px = ax + (aw * m->mfact);
|
||||||
|
else
|
||||||
|
px = ax + (aw * m->mfact);
|
||||||
|
py = ay + ah / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
|
||||||
|
None, cursor[horizontal ? CurResizeVertArrow : CurResizeHorzArrow]->cursor, CurrentTime) != GrabSuccess)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XWarpPointer(dpy, None, root, 0, 0, 0, 0, px, py);
|
||||||
|
|
||||||
|
do {
|
||||||
|
XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
|
||||||
|
switch(ev.type) {
|
||||||
|
case ConfigureRequest:
|
||||||
|
case Expose:
|
||||||
|
case MapRequest:
|
||||||
|
handler[ev.type](&ev);
|
||||||
|
break;
|
||||||
|
case MotionNotify:
|
||||||
|
if ((ev.xmotion.time - lasttime) <= (1000 / 40))
|
||||||
|
continue;
|
||||||
|
if (lasttime != 0) {
|
||||||
|
px = ev.xmotion.x;
|
||||||
|
py = ev.xmotion.y;
|
||||||
|
}
|
||||||
|
lasttime = ev.xmotion.time;
|
||||||
|
|
||||||
|
if (center)
|
||||||
|
if (horizontal)
|
||||||
|
if (py - ay > ah / 2)
|
||||||
|
fact = (double) 1.0 - (ay + ah - py) * 2 / (double) ah;
|
||||||
|
else
|
||||||
|
fact = (double) 1.0 - (py - ay) * 2 / (double) ah;
|
||||||
|
else
|
||||||
|
if (px - ax > aw / 2)
|
||||||
|
fact = (double) 1.0 - (ax + aw - px) * 2 / (double) aw;
|
||||||
|
else
|
||||||
|
fact = (double) 1.0 - (px - ax) * 2 / (double) aw;
|
||||||
|
else
|
||||||
|
if (horizontal)
|
||||||
|
fact = (double) (py - ay) / (double) ah;
|
||||||
|
else
|
||||||
|
fact = (double) (px - ax) / (double) aw;
|
||||||
|
|
||||||
|
if (!center && mirror)
|
||||||
|
fact = 1.0 - fact;
|
||||||
|
|
||||||
|
setmfact(&((Arg) { .f = 1.0 + fact }));
|
||||||
|
px = ev.xmotion.x;
|
||||||
|
py = ev.xmotion.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (ev.type != ButtonRelease);
|
||||||
|
|
||||||
|
XUngrabPointer(dpy, CurrentTime);
|
||||||
|
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/dragmfact.h
Normal file
2
patch/dragmfact.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void dragmfact(const Arg *arg);
|
||||||
|
|
||||||
41
patch/include.c
Normal file
41
patch/include.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* Bar functionality */
|
||||||
|
#include "bar_indicators.c"
|
||||||
|
#include "bar_tagicons.c"
|
||||||
|
#include "bar.c"
|
||||||
|
|
||||||
|
#include "bar_anybar.c"
|
||||||
|
#include "bar_ewmhtags.c"
|
||||||
|
#include "bar_ltsymbol.c"
|
||||||
|
#include "bar_status.c"
|
||||||
|
#include "bar_tagpreview.c"
|
||||||
|
#include "bar_tags.c"
|
||||||
|
#include "bar_wintitle.c"
|
||||||
|
#include "bar_systray.c"
|
||||||
|
|
||||||
|
/* Other patches */
|
||||||
|
#include "alttab.c"
|
||||||
|
#include "attachx.c"
|
||||||
|
#include "cfacts.c"
|
||||||
|
#include "cool_autostart.c"
|
||||||
|
#include "cyclelayouts.c"
|
||||||
|
#include "dragcfact.c"
|
||||||
|
#include "ipc.c"
|
||||||
|
#ifdef VERSION
|
||||||
|
#include "ipc/IPCClient.c"
|
||||||
|
#include "ipc/yajl_dumps.c"
|
||||||
|
#include "ipc/ipc.c"
|
||||||
|
#include "ipc/util.c"
|
||||||
|
#endif
|
||||||
|
#include "movestack.c"
|
||||||
|
#include "pertag.c"
|
||||||
|
#include "restartsig.c"
|
||||||
|
#include "renamed_scratchpads.c"
|
||||||
|
#include "swallow.c"
|
||||||
|
#include "dragmfact.c"
|
||||||
|
/* Layouts */
|
||||||
|
#include "layout_facts.c"
|
||||||
|
#include "layout_fibonacci.c"
|
||||||
|
#include "layout_grid.c"
|
||||||
|
#include "layout_monocle.c"
|
||||||
|
#include "layout_tile.c"
|
||||||
|
|
||||||
36
patch/include.h
Normal file
36
patch/include.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* Bar functionality */
|
||||||
|
#include "bar_indicators.h"
|
||||||
|
#include "bar_tagicons.h"
|
||||||
|
#include "bar.h"
|
||||||
|
|
||||||
|
#include "bar_anybar.h"
|
||||||
|
#include "bar_ewmhtags.h"
|
||||||
|
#include "bar_ltsymbol.h"
|
||||||
|
#include "bar_status.h"
|
||||||
|
#include "bar_tags.h"
|
||||||
|
#include "bar_tagpreview.h"
|
||||||
|
#include "bar_wintitle.h"
|
||||||
|
#include "bar_systray.h"
|
||||||
|
|
||||||
|
/* Other patches */
|
||||||
|
#include "alttab.h"
|
||||||
|
#include "attachx.h"
|
||||||
|
#include "cfacts.h"
|
||||||
|
#include "cool_autostart.h"
|
||||||
|
#include "cyclelayouts.h"
|
||||||
|
#include "dragcfact.h"
|
||||||
|
#include "dragmfact.h"
|
||||||
|
#include "ipc.h"
|
||||||
|
#include "ipc/ipc.h"
|
||||||
|
#include "ipc/util.h"
|
||||||
|
#include "movestack.h"
|
||||||
|
#include "pertag.h"
|
||||||
|
#include "restartsig.h"
|
||||||
|
#include "renamed_scratchpads.h"
|
||||||
|
#include "swallow.h"
|
||||||
|
/* Layouts */
|
||||||
|
#include "layout_fibonacci.h"
|
||||||
|
#include "layout_grid.h"
|
||||||
|
#include "layout_monocle.h"
|
||||||
|
#include "layout_tile.h"
|
||||||
|
|
||||||
72
patch/ipc.c
Normal file
72
patch/ipc.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
static int epoll_fd;
|
||||||
|
static int dpy_fd;
|
||||||
|
static Monitor *lastselmon;
|
||||||
|
|
||||||
|
int
|
||||||
|
handlexevent(struct epoll_event *ev)
|
||||||
|
{
|
||||||
|
if (ev->events & EPOLLIN) {
|
||||||
|
XEvent ev;
|
||||||
|
while (running && XPending(dpy)) {
|
||||||
|
XNextEvent(dpy, &ev);
|
||||||
|
|
||||||
|
if (handler[ev.type]) {
|
||||||
|
handler[ev.type](&ev); /* call handler */
|
||||||
|
ipc_send_events(mons, &lastselmon, selmon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ev-> events & EPOLLHUP)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setlayoutsafe(const Arg *arg)
|
||||||
|
{
|
||||||
|
const Layout *ltptr = (Layout *)arg->v;
|
||||||
|
if (ltptr == 0)
|
||||||
|
setlayout(arg);
|
||||||
|
for (int i = 0; i < LENGTH(layouts); i++) {
|
||||||
|
if (ltptr == &layouts[i])
|
||||||
|
setlayout(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setupepoll(void)
|
||||||
|
{
|
||||||
|
epoll_fd = epoll_create1(0);
|
||||||
|
dpy_fd = ConnectionNumber(dpy);
|
||||||
|
struct epoll_event dpy_event;
|
||||||
|
|
||||||
|
// Initialize struct to 0
|
||||||
|
memset(&dpy_event, 0, sizeof(dpy_event));
|
||||||
|
|
||||||
|
DEBUG("Display socket is fd %d\n", dpy_fd);
|
||||||
|
|
||||||
|
if (epoll_fd == -1)
|
||||||
|
fputs("Failed to create epoll file descriptor", stderr);
|
||||||
|
|
||||||
|
dpy_event.events = EPOLLIN;
|
||||||
|
dpy_event.data.fd = dpy_fd;
|
||||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dpy_fd, &dpy_event)) {
|
||||||
|
fputs("Failed to add display file descriptor to epoll", stderr);
|
||||||
|
close(epoll_fd);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipc_init(ipcsockpath, epoll_fd, ipccommands, LENGTH(ipccommands)) < 0)
|
||||||
|
fputs("Failed to initialize IPC\n", stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setstatus(const Arg *arg)
|
||||||
|
{
|
||||||
|
Monitor *m;
|
||||||
|
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
|
||||||
|
strcpy(stext, "dwm-"VERSION);
|
||||||
|
for (m = mons; m; m = m->next)
|
||||||
|
drawbar(m);
|
||||||
|
}
|
||||||
|
|
||||||
7
patch/ipc.h
Normal file
7
patch/ipc.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
static int handlexevent(struct epoll_event *ev);
|
||||||
|
static void setlayoutsafe(const Arg *arg);
|
||||||
|
static void setupepoll(void);
|
||||||
|
static void setstatus(const Arg *arg);
|
||||||
|
|
||||||
67
patch/ipc/IPCClient.c
Normal file
67
patch/ipc/IPCClient.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "IPCClient.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
IPCClient *
|
||||||
|
ipc_client_new(int fd)
|
||||||
|
{
|
||||||
|
IPCClient *c = (IPCClient *)malloc(sizeof(IPCClient));
|
||||||
|
|
||||||
|
if (c == NULL) return NULL;
|
||||||
|
|
||||||
|
// Initialize struct
|
||||||
|
memset(&c->event, 0, sizeof(struct epoll_event));
|
||||||
|
|
||||||
|
c->buffer_size = 0;
|
||||||
|
c->buffer = NULL;
|
||||||
|
c->fd = fd;
|
||||||
|
c->event.data.fd = fd;
|
||||||
|
c->next = NULL;
|
||||||
|
c->prev = NULL;
|
||||||
|
c->subscriptions = 0;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipc_list_add_client(IPCClientList *list, IPCClient *nc)
|
||||||
|
{
|
||||||
|
DEBUG("Adding client with fd %d to list\n", nc->fd);
|
||||||
|
|
||||||
|
if (*list == NULL) {
|
||||||
|
// List is empty, point list at first client
|
||||||
|
*list = nc;
|
||||||
|
} else {
|
||||||
|
IPCClient *c;
|
||||||
|
// Go to last client in list
|
||||||
|
for (c = *list; c && c->next; c = c->next)
|
||||||
|
;
|
||||||
|
c->next = nc;
|
||||||
|
nc->prev = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipc_list_remove_client(IPCClientList *list, IPCClient *c)
|
||||||
|
{
|
||||||
|
IPCClient *cprev = c->prev;
|
||||||
|
IPCClient *cnext = c->next;
|
||||||
|
|
||||||
|
if (cprev != NULL) cprev->next = c->next;
|
||||||
|
if (cnext != NULL) cnext->prev = c->prev;
|
||||||
|
if (c == *list) *list = c->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCClient *
|
||||||
|
ipc_list_get_client(IPCClientList list, int fd)
|
||||||
|
{
|
||||||
|
for (IPCClient *c = list; c; c = c->next) {
|
||||||
|
if (c->fd == fd) return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
62
patch/ipc/IPCClient.h
Normal file
62
patch/ipc/IPCClient.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#ifndef IPC_CLIENT_H_
|
||||||
|
#define IPC_CLIENT_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
typedef struct IPCClient IPCClient;
|
||||||
|
/**
|
||||||
|
* This structure contains the details of an IPC Client and pointers for a
|
||||||
|
* linked list
|
||||||
|
*/
|
||||||
|
struct IPCClient {
|
||||||
|
int fd;
|
||||||
|
int subscriptions;
|
||||||
|
|
||||||
|
char *buffer;
|
||||||
|
uint32_t buffer_size;
|
||||||
|
|
||||||
|
struct epoll_event event;
|
||||||
|
IPCClient *next;
|
||||||
|
IPCClient *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef IPCClient *IPCClientList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate memory for new IPCClient with the specified file descriptor and
|
||||||
|
* initialize struct.
|
||||||
|
*
|
||||||
|
* @param fd File descriptor of IPC client
|
||||||
|
*
|
||||||
|
* @return Address to allocated IPCClient struct
|
||||||
|
*/
|
||||||
|
IPCClient *ipc_client_new(int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an IPC Client to the specified list
|
||||||
|
*
|
||||||
|
* @param list Address of the list to add the client to
|
||||||
|
* @param nc Address of the IPCClient
|
||||||
|
*/
|
||||||
|
void ipc_list_add_client(IPCClientList *list, IPCClient *nc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an IPCClient from the specified list
|
||||||
|
*
|
||||||
|
* @param list Address of the list to remove the client from
|
||||||
|
* @param c Address of the IPCClient
|
||||||
|
*/
|
||||||
|
void ipc_list_remove_client(IPCClientList *list, IPCClient *c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an IPCClient from the specified IPCClient list
|
||||||
|
*
|
||||||
|
* @param list List to remove the client from
|
||||||
|
* @param fd File descriptor of the IPCClient
|
||||||
|
*/
|
||||||
|
IPCClient *ipc_list_get_client(IPCClientList list, int fd);
|
||||||
|
|
||||||
|
#endif // IPC_CLIENT_H_
|
||||||
|
|
||||||
549
patch/ipc/dwm-msg.c
Normal file
549
patch/ipc/dwm-msg.c
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <yajl/yajl_gen.h>
|
||||||
|
|
||||||
|
#define IPC_MAGIC "DWM-IPC"
|
||||||
|
// clang-format off
|
||||||
|
#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C' }
|
||||||
|
// clang-format on
|
||||||
|
#define IPC_MAGIC_LEN 7 // Not including null char
|
||||||
|
|
||||||
|
#define IPC_EVENT_TAG_CHANGE "tag_change_event"
|
||||||
|
#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event"
|
||||||
|
#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event"
|
||||||
|
#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event"
|
||||||
|
#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event"
|
||||||
|
#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event"
|
||||||
|
|
||||||
|
#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
|
||||||
|
#define YINT(num) yajl_gen_integer(gen, num)
|
||||||
|
#define YDOUBLE(num) yajl_gen_double(gen, num)
|
||||||
|
#define YBOOL(v) yajl_gen_bool(gen, v)
|
||||||
|
#define YNULL() yajl_gen_null(gen)
|
||||||
|
#define YARR(body) \
|
||||||
|
{ \
|
||||||
|
yajl_gen_array_open(gen); \
|
||||||
|
body; \
|
||||||
|
yajl_gen_array_close(gen); \
|
||||||
|
}
|
||||||
|
#define YMAP(body) \
|
||||||
|
{ \
|
||||||
|
yajl_gen_map_open(gen); \
|
||||||
|
body; \
|
||||||
|
yajl_gen_map_close(gen); \
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef unsigned long Window;
|
||||||
|
|
||||||
|
const char *DEFAULT_SOCKET_PATH = "/tmp/dwm.sock";
|
||||||
|
static int sock_fd = -1;
|
||||||
|
static unsigned int ignore_reply = 0;
|
||||||
|
|
||||||
|
typedef enum IPCMessageType {
|
||||||
|
IPC_TYPE_RUN_COMMAND = 0,
|
||||||
|
IPC_TYPE_GET_MONITORS = 1,
|
||||||
|
IPC_TYPE_GET_TAGS = 2,
|
||||||
|
IPC_TYPE_GET_LAYOUTS = 3,
|
||||||
|
IPC_TYPE_GET_DWM_CLIENT = 4,
|
||||||
|
IPC_TYPE_SUBSCRIBE = 5,
|
||||||
|
IPC_TYPE_EVENT = 6
|
||||||
|
} IPCMessageType;
|
||||||
|
|
||||||
|
// Every IPC message must begin with this
|
||||||
|
typedef struct dwm_ipc_header {
|
||||||
|
uint8_t magic[IPC_MAGIC_LEN];
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t type;
|
||||||
|
} __attribute((packed)) dwm_ipc_header_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
recv_message(uint8_t *msg_type, uint32_t *reply_size, uint8_t **reply)
|
||||||
|
{
|
||||||
|
uint32_t read_bytes = 0;
|
||||||
|
const int32_t to_read = sizeof(dwm_ipc_header_t);
|
||||||
|
char header[to_read];
|
||||||
|
char *walk = header;
|
||||||
|
|
||||||
|
// Try to read header
|
||||||
|
while (read_bytes < to_read) {
|
||||||
|
ssize_t n = read(sock_fd, header + read_bytes, to_read - read_bytes);
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
if (read_bytes == 0) {
|
||||||
|
fprintf(stderr, "Unexpectedly reached EOF while reading header.");
|
||||||
|
fprintf(stderr,
|
||||||
|
"Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n",
|
||||||
|
read_bytes, to_read);
|
||||||
|
return -2;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unexpectedly reached EOF while reading header.");
|
||||||
|
fprintf(stderr,
|
||||||
|
"Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n",
|
||||||
|
read_bytes, to_read);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
} else if (n == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bytes += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if magic string in header matches
|
||||||
|
if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) {
|
||||||
|
fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n",
|
||||||
|
IPC_MAGIC_LEN, walk, IPC_MAGIC);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
walk += IPC_MAGIC_LEN;
|
||||||
|
|
||||||
|
// Extract reply size
|
||||||
|
memcpy(reply_size, walk, sizeof(uint32_t));
|
||||||
|
walk += sizeof(uint32_t);
|
||||||
|
|
||||||
|
// Extract message type
|
||||||
|
memcpy(msg_type, walk, sizeof(uint8_t));
|
||||||
|
walk += sizeof(uint8_t);
|
||||||
|
|
||||||
|
(*reply) = malloc(*reply_size);
|
||||||
|
|
||||||
|
// Extract payload
|
||||||
|
read_bytes = 0;
|
||||||
|
while (read_bytes < *reply_size) {
|
||||||
|
ssize_t n = read(sock_fd, *reply + read_bytes, *reply_size - read_bytes);
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
fprintf(stderr, "Unexpectedly reached EOF while reading payload.");
|
||||||
|
fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n",
|
||||||
|
read_bytes, *reply_size);
|
||||||
|
free(*reply);
|
||||||
|
return -2;
|
||||||
|
} else if (n == -1) {
|
||||||
|
if (errno == EINTR || errno == EAGAIN) continue;
|
||||||
|
free(*reply);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bytes += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
read_socket(IPCMessageType *msg_type, uint32_t *msg_size, char **msg)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
while (ret != 0) {
|
||||||
|
ret = recv_message((uint8_t *)msg_type, msg_size, (uint8_t **)msg);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
// Try again (non-fatal error)
|
||||||
|
if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue;
|
||||||
|
|
||||||
|
fprintf(stderr, "Error receiving response from socket. ");
|
||||||
|
fprintf(stderr, "The connection might have been lost.\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
write_socket(const void *buf, size_t count)
|
||||||
|
{
|
||||||
|
size_t written = 0;
|
||||||
|
|
||||||
|
while (written < count) {
|
||||||
|
const ssize_t n =
|
||||||
|
write(sock_fd, ((uint8_t *)buf) + written, count - written);
|
||||||
|
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
written += n;
|
||||||
|
}
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
connect_to_socket()
|
||||||
|
{
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
|
||||||
|
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
// Initialize struct to 0
|
||||||
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||||
|
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strcpy(addr.sun_path, DEFAULT_SOCKET_PATH);
|
||||||
|
|
||||||
|
connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un));
|
||||||
|
|
||||||
|
sock_fd = sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
send_message(IPCMessageType msg_type, uint32_t msg_size, uint8_t *msg)
|
||||||
|
{
|
||||||
|
dwm_ipc_header_t header = {
|
||||||
|
.magic = IPC_MAGIC_ARR, .size = msg_size, .type = msg_type};
|
||||||
|
|
||||||
|
size_t header_size = sizeof(dwm_ipc_header_t);
|
||||||
|
size_t total_size = header_size + msg_size;
|
||||||
|
|
||||||
|
uint8_t buffer[total_size];
|
||||||
|
|
||||||
|
// Copy header to buffer
|
||||||
|
memcpy(buffer, &header, header_size);
|
||||||
|
// Copy message to buffer
|
||||||
|
memcpy(buffer + header_size, msg, header.size);
|
||||||
|
|
||||||
|
write_socket(buffer, total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_float(const char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
int is_dot_used = 0;
|
||||||
|
int is_minus_used = 0;
|
||||||
|
|
||||||
|
// Floats can only have one decimal point in between or digits
|
||||||
|
// Optionally, floats can also be below zero (negative)
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (isdigit(s[i]))
|
||||||
|
continue;
|
||||||
|
else if (!is_dot_used && s[i] == '.' && i != 0 && i != len - 1) {
|
||||||
|
is_dot_used = 1;
|
||||||
|
continue;
|
||||||
|
} else if (!is_minus_used && s[i] == '-' && i == 0) {
|
||||||
|
is_minus_used = 1;
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_unsigned_int(const char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
// Unsigned int can only have digits
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (isdigit(s[i]))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_signed_int(const char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
// Signed int can only have digits and a negative sign at the start
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (isdigit(s[i]))
|
||||||
|
continue;
|
||||||
|
else if (i == 0 && s[i] == '-') {
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flush_socket_reply()
|
||||||
|
{
|
||||||
|
IPCMessageType reply_type;
|
||||||
|
uint32_t reply_size;
|
||||||
|
char *reply;
|
||||||
|
|
||||||
|
read_socket(&reply_type, &reply_size, &reply);
|
||||||
|
|
||||||
|
free(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_socket_reply()
|
||||||
|
{
|
||||||
|
IPCMessageType reply_type;
|
||||||
|
uint32_t reply_size;
|
||||||
|
char *reply;
|
||||||
|
|
||||||
|
read_socket(&reply_type, &reply_size, &reply);
|
||||||
|
|
||||||
|
printf("%.*s\n", reply_size, reply);
|
||||||
|
fflush(stdout);
|
||||||
|
free(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_command(const char *name, char *args[], int argc)
|
||||||
|
{
|
||||||
|
const unsigned char *msg;
|
||||||
|
size_t msg_size;
|
||||||
|
|
||||||
|
yajl_gen gen = yajl_gen_alloc(NULL);
|
||||||
|
|
||||||
|
// Message format:
|
||||||
|
// {
|
||||||
|
// "command": "<name>",
|
||||||
|
// "args": [ ... ]
|
||||||
|
// }
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("command"); YSTR(name);
|
||||||
|
YSTR("args"); YARR(
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
if (is_signed_int(args[i])) {
|
||||||
|
long long num = atoll(args[i]);
|
||||||
|
YINT(num);
|
||||||
|
} else if (is_float(args[i])) {
|
||||||
|
float num = atof(args[i]);
|
||||||
|
YDOUBLE(num);
|
||||||
|
} else {
|
||||||
|
YSTR(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
yajl_gen_get_buf(gen, &msg, &msg_size);
|
||||||
|
|
||||||
|
send_message(IPC_TYPE_RUN_COMMAND, msg_size, (uint8_t *)msg);
|
||||||
|
|
||||||
|
if (!ignore_reply)
|
||||||
|
print_socket_reply();
|
||||||
|
else
|
||||||
|
flush_socket_reply();
|
||||||
|
|
||||||
|
yajl_gen_free(gen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_monitors()
|
||||||
|
{
|
||||||
|
send_message(IPC_TYPE_GET_MONITORS, 1, (uint8_t *)"");
|
||||||
|
print_socket_reply();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_tags()
|
||||||
|
{
|
||||||
|
send_message(IPC_TYPE_GET_TAGS, 1, (uint8_t *)"");
|
||||||
|
print_socket_reply();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_layouts()
|
||||||
|
{
|
||||||
|
send_message(IPC_TYPE_GET_LAYOUTS, 1, (uint8_t *)"");
|
||||||
|
print_socket_reply();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_dwm_client(Window win)
|
||||||
|
{
|
||||||
|
const unsigned char *msg;
|
||||||
|
size_t msg_size;
|
||||||
|
|
||||||
|
yajl_gen gen = yajl_gen_alloc(NULL);
|
||||||
|
|
||||||
|
// Message format:
|
||||||
|
// {
|
||||||
|
// "client_window_id": "<win>"
|
||||||
|
// }
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("client_window_id"); YINT(win);
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
yajl_gen_get_buf(gen, &msg, &msg_size);
|
||||||
|
|
||||||
|
send_message(IPC_TYPE_GET_DWM_CLIENT, msg_size, (uint8_t *)msg);
|
||||||
|
|
||||||
|
print_socket_reply();
|
||||||
|
|
||||||
|
yajl_gen_free(gen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
subscribe(const char *event)
|
||||||
|
{
|
||||||
|
const unsigned char *msg;
|
||||||
|
size_t msg_size;
|
||||||
|
|
||||||
|
yajl_gen gen = yajl_gen_alloc(NULL);
|
||||||
|
|
||||||
|
// Message format:
|
||||||
|
// {
|
||||||
|
// "event": "<event>",
|
||||||
|
// "action": "subscribe"
|
||||||
|
// }
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("event"); YSTR(event);
|
||||||
|
YSTR("action"); YSTR("subscribe");
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
yajl_gen_get_buf(gen, &msg, &msg_size);
|
||||||
|
|
||||||
|
send_message(IPC_TYPE_SUBSCRIBE, msg_size, (uint8_t *)msg);
|
||||||
|
|
||||||
|
if (!ignore_reply)
|
||||||
|
print_socket_reply();
|
||||||
|
else
|
||||||
|
flush_socket_reply();
|
||||||
|
|
||||||
|
yajl_gen_free(gen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage_error(const char *prog_name, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
fprintf(stderr, "Error: ");
|
||||||
|
vfprintf(stderr, format, args);
|
||||||
|
fprintf(stderr, "\nusage: %s <command> [...]\n", prog_name);
|
||||||
|
fprintf(stderr, "Try '%s help'\n", prog_name);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_usage(const char *name)
|
||||||
|
{
|
||||||
|
printf("usage: %s [options] <command> [...]\n", name);
|
||||||
|
puts("");
|
||||||
|
puts("Commands:");
|
||||||
|
puts(" run_command <name> [args...] Run an IPC command");
|
||||||
|
puts("");
|
||||||
|
puts(" get_monitors Get monitor properties");
|
||||||
|
puts("");
|
||||||
|
puts(" get_tags Get list of tags");
|
||||||
|
puts("");
|
||||||
|
puts(" get_layouts Get list of layouts");
|
||||||
|
puts("");
|
||||||
|
puts(" get_dwm_client <window_id> Get dwm client proprties");
|
||||||
|
puts("");
|
||||||
|
puts(" subscribe [events...] Subscribe to specified events");
|
||||||
|
puts(" Options: " IPC_EVENT_TAG_CHANGE ",");
|
||||||
|
puts(" " IPC_EVENT_LAYOUT_CHANGE ",");
|
||||||
|
puts(" " IPC_EVENT_CLIENT_FOCUS_CHANGE ",");
|
||||||
|
puts(" " IPC_EVENT_MONITOR_FOCUS_CHANGE ",");
|
||||||
|
puts(" " IPC_EVENT_FOCUSED_TITLE_CHANGE ",");
|
||||||
|
puts(" " IPC_EVENT_FOCUSED_STATE_CHANGE);
|
||||||
|
puts("");
|
||||||
|
puts(" help Display this message");
|
||||||
|
puts("");
|
||||||
|
puts("Options:");
|
||||||
|
puts(" --ignore-reply Don't print reply messages from");
|
||||||
|
puts(" run_command and subscribe.");
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
const char *prog_name = argv[0];
|
||||||
|
|
||||||
|
connect_to_socket();
|
||||||
|
if (sock_fd == -1) {
|
||||||
|
fprintf(stderr, "Failed to connect to socket\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
if (i < argc && strcmp(argv[i], "--ignore-reply") == 0) {
|
||||||
|
ignore_reply = 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= argc) usage_error(prog_name, "Expected an argument, got none");
|
||||||
|
|
||||||
|
if (!argc || strcmp(argv[i], "help") == 0)
|
||||||
|
print_usage(prog_name);
|
||||||
|
else if (strcmp(argv[i], "run_command") == 0) {
|
||||||
|
if (++i >= argc) usage_error(prog_name, "No command specified");
|
||||||
|
// Command name
|
||||||
|
char *command = argv[i];
|
||||||
|
// Command arguments are everything after command name
|
||||||
|
char **command_args = argv + ++i;
|
||||||
|
// Number of command arguments
|
||||||
|
int command_argc = argc - i;
|
||||||
|
run_command(command, command_args, command_argc);
|
||||||
|
} else if (strcmp(argv[i], "get_monitors") == 0) {
|
||||||
|
get_monitors();
|
||||||
|
} else if (strcmp(argv[i], "get_tags") == 0) {
|
||||||
|
get_tags();
|
||||||
|
} else if (strcmp(argv[i], "get_layouts") == 0) {
|
||||||
|
get_layouts();
|
||||||
|
} else if (strcmp(argv[i], "get_dwm_client") == 0) {
|
||||||
|
if (++i < argc) {
|
||||||
|
if (is_unsigned_int(argv[i])) {
|
||||||
|
Window win = atol(argv[i]);
|
||||||
|
get_dwm_client(win);
|
||||||
|
} else
|
||||||
|
usage_error(prog_name, "Expected unsigned integer argument");
|
||||||
|
} else
|
||||||
|
usage_error(prog_name, "Expected the window id");
|
||||||
|
} else if (strcmp(argv[i], "subscribe") == 0) {
|
||||||
|
if (++i < argc) {
|
||||||
|
for (int j = i; j < argc; j++) subscribe(argv[j]);
|
||||||
|
} else
|
||||||
|
usage_error(prog_name, "Expected event name");
|
||||||
|
// Keep listening for events forever
|
||||||
|
while (1) {
|
||||||
|
print_socket_reply();
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
usage_error(prog_name, "Invalid argument '%s'", argv[i]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
1202
patch/ipc/ipc.c
Normal file
1202
patch/ipc/ipc.c
Normal file
File diff suppressed because it is too large
Load Diff
320
patch/ipc/ipc.h
Normal file
320
patch/ipc/ipc.h
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
#ifndef IPC_H_
|
||||||
|
#define IPC_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <yajl/yajl_gen.h>
|
||||||
|
|
||||||
|
#include "IPCClient.h"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#define IPC_MAGIC "DWM-IPC"
|
||||||
|
#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C'}
|
||||||
|
#define IPC_MAGIC_LEN 7 // Not including null char
|
||||||
|
|
||||||
|
#define IPCCOMMAND(FUNC, ARGC, TYPES) \
|
||||||
|
{ #FUNC, {FUNC }, ARGC, (ArgType[ARGC])TYPES }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
typedef enum IPCMessageType {
|
||||||
|
IPC_TYPE_RUN_COMMAND = 0,
|
||||||
|
IPC_TYPE_GET_MONITORS = 1,
|
||||||
|
IPC_TYPE_GET_TAGS = 2,
|
||||||
|
IPC_TYPE_GET_LAYOUTS = 3,
|
||||||
|
IPC_TYPE_GET_DWM_CLIENT = 4,
|
||||||
|
IPC_TYPE_SUBSCRIBE = 5,
|
||||||
|
IPC_TYPE_EVENT = 6
|
||||||
|
} IPCMessageType;
|
||||||
|
|
||||||
|
typedef enum IPCEvent {
|
||||||
|
IPC_EVENT_TAG_CHANGE = 1 << 0,
|
||||||
|
IPC_EVENT_CLIENT_FOCUS_CHANGE = 1 << 1,
|
||||||
|
IPC_EVENT_LAYOUT_CHANGE = 1 << 2,
|
||||||
|
IPC_EVENT_MONITOR_FOCUS_CHANGE = 1 << 3,
|
||||||
|
IPC_EVENT_FOCUSED_TITLE_CHANGE = 1 << 4,
|
||||||
|
IPC_EVENT_FOCUSED_STATE_CHANGE = 1 << 5
|
||||||
|
} IPCEvent;
|
||||||
|
|
||||||
|
typedef enum IPCSubscriptionAction {
|
||||||
|
IPC_ACTION_UNSUBSCRIBE = 0,
|
||||||
|
IPC_ACTION_SUBSCRIBE = 1
|
||||||
|
} IPCSubscriptionAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every IPC packet starts with this structure
|
||||||
|
*/
|
||||||
|
typedef struct dwm_ipc_header {
|
||||||
|
uint8_t magic[IPC_MAGIC_LEN];
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t type;
|
||||||
|
} __attribute((packed)) dwm_ipc_header_t;
|
||||||
|
|
||||||
|
typedef enum ArgType {
|
||||||
|
ARG_TYPE_NONE = 0,
|
||||||
|
ARG_TYPE_UINT = 1,
|
||||||
|
ARG_TYPE_SINT = 2,
|
||||||
|
ARG_TYPE_FLOAT = 3,
|
||||||
|
ARG_TYPE_PTR = 4,
|
||||||
|
ARG_TYPE_STR = 5
|
||||||
|
} ArgType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IPCCommand function can have either of these function signatures
|
||||||
|
*/
|
||||||
|
typedef union ArgFunction {
|
||||||
|
void (*single_param)(const Arg *);
|
||||||
|
void (*array_param)(const Arg *, int);
|
||||||
|
} ArgFunction;
|
||||||
|
|
||||||
|
typedef struct IPCCommand {
|
||||||
|
char *name;
|
||||||
|
ArgFunction func;
|
||||||
|
unsigned int argc;
|
||||||
|
ArgType *arg_types;
|
||||||
|
} IPCCommand;
|
||||||
|
|
||||||
|
typedef struct IPCParsedCommand {
|
||||||
|
char *name;
|
||||||
|
Arg *args;
|
||||||
|
ArgType *arg_types;
|
||||||
|
unsigned int argc;
|
||||||
|
} IPCParsedCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the IPC socket and the IPC module
|
||||||
|
*
|
||||||
|
* @param socket_path Path to create the socket at
|
||||||
|
* @param epoll_fd File descriptor for epoll
|
||||||
|
* @param commands Address of IPCCommands array defined in config.h
|
||||||
|
* @param commands_len Length of commands[] array
|
||||||
|
*
|
||||||
|
* @return int The file descriptor of the socket if it was successfully created,
|
||||||
|
* -1 otherwise
|
||||||
|
*/
|
||||||
|
int ipc_init(const char *socket_path, const int p_epoll_fd,
|
||||||
|
IPCCommand commands[], const int commands_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninitialize the socket and module. Free allocated memory and restore static
|
||||||
|
* variables to their state before ipc_init
|
||||||
|
*/
|
||||||
|
void ipc_cleanup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file descriptor of the IPC socket
|
||||||
|
*
|
||||||
|
* @return int File descriptor of IPC socket, -1 if socket not created.
|
||||||
|
*/
|
||||||
|
int ipc_get_sock_fd();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get address to IPCClient with specified file descriptor
|
||||||
|
*
|
||||||
|
* @param fd File descriptor of IPC Client
|
||||||
|
*
|
||||||
|
* @return Address to IPCClient with specified file descriptor, -1 otherwise
|
||||||
|
*/
|
||||||
|
IPCClient *ipc_get_client(int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an IPC client exists with the specified file descriptor
|
||||||
|
*
|
||||||
|
* @param fd File descriptor
|
||||||
|
*
|
||||||
|
* @return int 1 if client exists, 0 otherwise
|
||||||
|
*/
|
||||||
|
int ipc_is_client_registered(int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect an IPCClient from the socket and remove the client from the list
|
||||||
|
* of known connected clients
|
||||||
|
*
|
||||||
|
* @param c Address of IPCClient
|
||||||
|
*
|
||||||
|
* @return 0 if the client's file descriptor was closed successfully, the
|
||||||
|
* result of executing close() on the file descriptor otherwise.
|
||||||
|
*/
|
||||||
|
int ipc_drop_client(IPCClient *c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept an IPC Client requesting to connect to the socket and add it to the
|
||||||
|
* list of clients
|
||||||
|
*
|
||||||
|
* @return File descriptor of new client, -1 on error
|
||||||
|
*/
|
||||||
|
int ipc_accept_client();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an incoming message from an accepted IPC client
|
||||||
|
*
|
||||||
|
* @param c Address of IPCClient
|
||||||
|
* @param msg_type Address to IPCMessageType variable which will be assigned
|
||||||
|
* the message type of the received message
|
||||||
|
* @param msg_size Address to uint32_t variable which will be assigned the size
|
||||||
|
* of the received message
|
||||||
|
* @param msg Address to char* variable which will be assigned the address of
|
||||||
|
* the received message. This must be freed using free().
|
||||||
|
*
|
||||||
|
* @return 0 on success, -1 on error reading message, -2 if reading the message
|
||||||
|
* resulted in EAGAIN, EINTR, or EWOULDBLOCK.
|
||||||
|
*/
|
||||||
|
int ipc_read_client(IPCClient *c, IPCMessageType *msg_type, uint32_t *msg_size,
|
||||||
|
char **msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write any pending buffer of the client to the client's socket
|
||||||
|
*
|
||||||
|
* @param c Client whose buffer to write
|
||||||
|
*
|
||||||
|
* @return Number of bytes written >= 0, -1 otherwise. errno will still be set
|
||||||
|
* from the write operation.
|
||||||
|
*/
|
||||||
|
ssize_t ipc_write_client(IPCClient *c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a message in the specified client's buffer.
|
||||||
|
*
|
||||||
|
* @param c Client to prepare message for
|
||||||
|
* @param msg_type Type of message to prepare
|
||||||
|
* @param msg_size Size of the message in bytes. Should not exceed
|
||||||
|
* MAX_MESSAGE_SIZE
|
||||||
|
* @param msg Message to prepare (not including header). This pointer can be
|
||||||
|
* freed after the function invocation.
|
||||||
|
*/
|
||||||
|
void ipc_prepare_send_message(IPCClient *c, const IPCMessageType msg_type,
|
||||||
|
const uint32_t msg_size, const char *msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an error message in the specified client's buffer
|
||||||
|
*
|
||||||
|
* @param c Client to prepare message for
|
||||||
|
* @param msg_type Type of message
|
||||||
|
* @param format Format string following vsprintf
|
||||||
|
* @param ... Arguments for format string
|
||||||
|
*/
|
||||||
|
void ipc_prepare_reply_failure(IPCClient *c, IPCMessageType msg_type,
|
||||||
|
const char *format, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a success message in the specified client's buffer
|
||||||
|
*
|
||||||
|
* @param c Client to prepare message for
|
||||||
|
* @param msg_type Type of message
|
||||||
|
*/
|
||||||
|
void ipc_prepare_reply_success(IPCClient *c, IPCMessageType msg_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a tag_change_event to all subscribers. Should be called only when there
|
||||||
|
* has been a tag state change.
|
||||||
|
*
|
||||||
|
* @param mon_num The index of the monitor (Monitor.num property)
|
||||||
|
* @param old_state The old tag state
|
||||||
|
* @param new_state The new (now current) tag state
|
||||||
|
*/
|
||||||
|
void ipc_tag_change_event(const int mon_num, TagState old_state,
|
||||||
|
TagState new_state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a client_focus_change_event to all subscribers. Should be called only
|
||||||
|
* when the client focus changes.
|
||||||
|
*
|
||||||
|
* @param mon_num The index of the monitor (Monitor.num property)
|
||||||
|
* @param old_client The old DWM client selection (Monitor.oldsel)
|
||||||
|
* @param new_client The new (now current) DWM client selection
|
||||||
|
*/
|
||||||
|
void ipc_client_focus_change_event(const int mon_num, Client *old_client,
|
||||||
|
Client *new_client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a layout_change_event to all subscribers. Should be called only
|
||||||
|
* when there has been a layout change.
|
||||||
|
*
|
||||||
|
* @param mon_num The index of the monitor (Monitor.num property)
|
||||||
|
* @param old_symbol The old layout symbol
|
||||||
|
* @param old_layout Address to the old Layout
|
||||||
|
* @param new_symbol The new (now current) layout symbol
|
||||||
|
* @param new_layout Address to the new Layout
|
||||||
|
*/
|
||||||
|
void ipc_layout_change_event(const int mon_num, const char *old_symbol,
|
||||||
|
const Layout *old_layout, const char *new_symbol,
|
||||||
|
const Layout *new_layout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a monitor_focus_change_event to all subscribers. Should be called only
|
||||||
|
* when the monitor focus changes.
|
||||||
|
*
|
||||||
|
* @param last_mon_num The index of the previously selected monitor
|
||||||
|
* @param new_mon_num The index of the newly selected monitor
|
||||||
|
*/
|
||||||
|
void ipc_monitor_focus_change_event(const int last_mon_num,
|
||||||
|
const int new_mon_num);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a focused_title_change_event to all subscribers. Should only be called
|
||||||
|
* if a selected client has a title change.
|
||||||
|
*
|
||||||
|
* @param mon_num Index of the client's monitor
|
||||||
|
* @param client_id Window XID of client
|
||||||
|
* @param old_name Old name of the client window
|
||||||
|
* @param new_name New name of the client window
|
||||||
|
*/
|
||||||
|
void ipc_focused_title_change_event(const int mon_num, const Window client_id,
|
||||||
|
const char *old_name, const char *new_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a focused_state_change_event to all subscribers. Should only be called
|
||||||
|
* if a selected client has a state change.
|
||||||
|
*
|
||||||
|
* @param mon_num Index of the client's monitor
|
||||||
|
* @param client_id Window XID of client
|
||||||
|
* @param old_state Old state of the client
|
||||||
|
* @param new_state New state of the client
|
||||||
|
*/
|
||||||
|
void ipc_focused_state_change_event(const int mon_num, const Window client_id,
|
||||||
|
const ClientState *old_state,
|
||||||
|
const ClientState *new_state);
|
||||||
|
/**
|
||||||
|
* Check to see if an event has occured and call the *_change_event functions
|
||||||
|
* accordingly
|
||||||
|
*
|
||||||
|
* @param mons Address of Monitor pointing to start of linked list
|
||||||
|
* @param lastselmon Address of pointer to previously selected monitor
|
||||||
|
* @param selmon Address of selected Monitor
|
||||||
|
*/
|
||||||
|
void ipc_send_events(Monitor *mons, Monitor **lastselmon, Monitor *selmon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an epoll event caused by a registered IPC client. Read, process, and
|
||||||
|
* handle any received messages from clients. Write pending buffer to client if
|
||||||
|
* the client is ready to receive messages. Drop clients that have sent an
|
||||||
|
* EPOLLHUP.
|
||||||
|
*
|
||||||
|
* @param ev Associated epoll event returned by epoll_wait
|
||||||
|
* @param mons Address of Monitor pointing to start of linked list
|
||||||
|
* @param selmon Address of selected Monitor
|
||||||
|
* @param lastselmon Address of pointer to previously selected monitor
|
||||||
|
* @param tags Array of tag names
|
||||||
|
* @param tags_len Length of tags array
|
||||||
|
* @param layouts Array of available layouts
|
||||||
|
* @param layouts_len Length of layouts array
|
||||||
|
*
|
||||||
|
* @return 0 if event was successfully handled, -1 on any error receiving
|
||||||
|
* or handling incoming messages or unhandled epoll event.
|
||||||
|
*/
|
||||||
|
int ipc_handle_client_epoll_event(struct epoll_event *ev, Monitor *mons,
|
||||||
|
Monitor **lastselmon, Monitor *selmon, const int tags_len,
|
||||||
|
const Layout *layouts, const int layouts_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an epoll event caused by the IPC socket. This function only handles an
|
||||||
|
* EPOLLIN event indicating a new client requesting to connect to the socket.
|
||||||
|
*
|
||||||
|
* @param ev Associated epoll event returned by epoll_wait
|
||||||
|
*
|
||||||
|
* @return 0, if the event was successfully handled, -1 if not an EPOLLIN event
|
||||||
|
* or if a new IPC client connection request could not be accepted.
|
||||||
|
*/
|
||||||
|
int ipc_handle_socket_epoll_event(struct epoll_event *ev);
|
||||||
|
|
||||||
|
#endif /* IPC_H_ */
|
||||||
|
|
||||||
136
patch/ipc/util.c
Normal file
136
patch/ipc/util.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
normalizepath(const char *path, char **normal)
|
||||||
|
{
|
||||||
|
size_t len = strlen(path);
|
||||||
|
*normal = (char *)malloc((len + 1) * sizeof(char));
|
||||||
|
const char *walk = path;
|
||||||
|
const char *match;
|
||||||
|
size_t newlen = 0;
|
||||||
|
|
||||||
|
while ((match = strchr(walk, '/'))) {
|
||||||
|
// Copy everything between match and walk
|
||||||
|
strncpy(*normal + newlen, walk, match - walk);
|
||||||
|
newlen += match - walk;
|
||||||
|
walk += match - walk;
|
||||||
|
|
||||||
|
// Skip all repeating slashes
|
||||||
|
while (*walk == '/')
|
||||||
|
walk++;
|
||||||
|
|
||||||
|
// If not last character in path
|
||||||
|
if (walk != path + len)
|
||||||
|
(*normal)[newlen++] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
(*normal)[newlen++] = '\0';
|
||||||
|
|
||||||
|
// Copy remaining path
|
||||||
|
strcat(*normal, walk);
|
||||||
|
newlen += strlen(walk);
|
||||||
|
|
||||||
|
*normal = (char *)realloc(*normal, newlen * sizeof(char));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parentdir(const char *path, char **parent)
|
||||||
|
{
|
||||||
|
char *normal;
|
||||||
|
char *walk;
|
||||||
|
|
||||||
|
normalizepath(path, &normal);
|
||||||
|
|
||||||
|
// Pointer to last '/'
|
||||||
|
if (!(walk = strrchr(normal, '/'))) {
|
||||||
|
free(normal);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get path up to last '/'
|
||||||
|
size_t len = walk - normal;
|
||||||
|
*parent = (char *)malloc((len + 1) * sizeof(char));
|
||||||
|
|
||||||
|
// Copy path up to last '/'
|
||||||
|
strncpy(*parent, normal, len);
|
||||||
|
// Add null char
|
||||||
|
(*parent)[len] = '\0';
|
||||||
|
|
||||||
|
free(normal);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
mkdirp(const char *path)
|
||||||
|
{
|
||||||
|
char *normal;
|
||||||
|
char *walk;
|
||||||
|
size_t normallen;
|
||||||
|
|
||||||
|
normalizepath(path, &normal);
|
||||||
|
normallen = strlen(normal);
|
||||||
|
walk = normal;
|
||||||
|
|
||||||
|
while (walk < normal + normallen + 1) {
|
||||||
|
// Get length from walk to next /
|
||||||
|
size_t n = strcspn(walk, "/");
|
||||||
|
|
||||||
|
// Skip path /
|
||||||
|
if (n == 0) {
|
||||||
|
walk++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length of current path segment
|
||||||
|
size_t curpathlen = walk - normal + n;
|
||||||
|
char curpath[curpathlen + 1];
|
||||||
|
struct stat s;
|
||||||
|
|
||||||
|
// Copy path segment to stat
|
||||||
|
strncpy(curpath, normal, curpathlen);
|
||||||
|
strcpy(curpath + curpathlen, "");
|
||||||
|
int res = stat(curpath, &s);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
DEBUG("Making directory %s\n", curpath);
|
||||||
|
if (mkdir(curpath, 0700) < 0) {
|
||||||
|
fprintf(stderr, "Failed to make directory %s\n", curpath);
|
||||||
|
perror("");
|
||||||
|
free(normal);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error statting directory %s\n", curpath);
|
||||||
|
perror("");
|
||||||
|
free(normal);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue to next path segment
|
||||||
|
walk += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(normal);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nullterminate(char **str, size_t *len)
|
||||||
|
{
|
||||||
|
if ((*str)[*len - 1] == '\0')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
(*len)++;
|
||||||
|
*str = (char*)realloc(*str, *len * sizeof(char));
|
||||||
|
(*str)[*len - 1] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
5
patch/ipc/util.h
Normal file
5
patch/ipc/util.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
int normalizepath(const char *path, char **normal);
|
||||||
|
int mkdirp(const char *path);
|
||||||
|
int parentdir(const char *path, char **parent);
|
||||||
|
int nullterminate(char **str, size_t *len);
|
||||||
|
|
||||||
356
patch/ipc/yajl_dumps.c
Normal file
356
patch/ipc/yajl_dumps.c
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
#include "yajl_dumps.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_tag(yajl_gen gen, const char *name, const int tag_mask)
|
||||||
|
{
|
||||||
|
if (!name)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("bit_mask"); YINT(tag_mask);
|
||||||
|
YSTR("name"); YSTR(name);
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_tags(yajl_gen gen, int tags_len)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YARR(
|
||||||
|
for (int i = 0; i < tags_len; i++)
|
||||||
|
dump_tag(gen, tagicon(mons, i), 1 << i);
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_client(yajl_gen gen, Client *c)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("name"); YSTR(c->name);
|
||||||
|
YSTR("tags"); YINT(c->tags);
|
||||||
|
YSTR("window_id"); YINT(c->win);
|
||||||
|
YSTR("monitor_number"); YINT(c->mon->num);
|
||||||
|
|
||||||
|
YSTR("geometry"); YMAP(
|
||||||
|
YSTR("current"); YMAP (
|
||||||
|
YSTR("x"); YINT(c->x);
|
||||||
|
YSTR("y"); YINT(c->y);
|
||||||
|
YSTR("width"); YINT(c->w);
|
||||||
|
YSTR("height"); YINT(c->h);
|
||||||
|
)
|
||||||
|
YSTR("old"); YMAP(
|
||||||
|
YSTR("x"); YINT(c->oldx);
|
||||||
|
YSTR("y"); YINT(c->oldy);
|
||||||
|
YSTR("width"); YINT(c->oldw);
|
||||||
|
YSTR("height"); YINT(c->oldh);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("size_hints"); YMAP(
|
||||||
|
YSTR("base"); YMAP(
|
||||||
|
YSTR("width"); YINT(c->basew);
|
||||||
|
YSTR("height"); YINT(c->baseh);
|
||||||
|
)
|
||||||
|
YSTR("step"); YMAP(
|
||||||
|
YSTR("width"); YINT(c->incw);
|
||||||
|
YSTR("height"); YINT(c->inch);
|
||||||
|
)
|
||||||
|
YSTR("max"); YMAP(
|
||||||
|
YSTR("width"); YINT(c->maxw);
|
||||||
|
YSTR("height"); YINT(c->maxh);
|
||||||
|
)
|
||||||
|
YSTR("min"); YMAP(
|
||||||
|
YSTR("width"); YINT(c->minw);
|
||||||
|
YSTR("height"); YINT(c->minh);
|
||||||
|
)
|
||||||
|
YSTR("aspect_ratio"); YMAP(
|
||||||
|
YSTR("min"); YDOUBLE(c->mina);
|
||||||
|
YSTR("max"); YDOUBLE(c->maxa);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("border_width"); YMAP(
|
||||||
|
YSTR("current"); YINT(c->bw);
|
||||||
|
YSTR("old"); YINT(c->oldbw);
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("states"); YMAP(
|
||||||
|
YSTR("is_fixed"); YBOOL(c->isfixed);
|
||||||
|
YSTR("is_floating"); YBOOL(c->isfloating);
|
||||||
|
YSTR("is_urgent"); YBOOL(c->isurgent);
|
||||||
|
YSTR("never_focus"); YBOOL(c->neverfocus);
|
||||||
|
YSTR("old_state"); YBOOL(c->oldstate);
|
||||||
|
YSTR("is_fullscreen"); YBOOL(c->isfullscreen);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_monitor(yajl_gen gen, Monitor *mon, int is_selected)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("master_factor"); YDOUBLE(mon->mfact);
|
||||||
|
YSTR("num_master"); YINT(mon->nmaster);
|
||||||
|
YSTR("num"); YINT(mon->num);
|
||||||
|
YSTR("is_selected"); YBOOL(is_selected);
|
||||||
|
|
||||||
|
YSTR("monitor_geometry"); YMAP(
|
||||||
|
YSTR("x"); YINT(mon->mx);
|
||||||
|
YSTR("y"); YINT(mon->my);
|
||||||
|
YSTR("width"); YINT(mon->mw);
|
||||||
|
YSTR("height"); YINT(mon->mh);
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("window_geometry"); YMAP(
|
||||||
|
YSTR("x"); YINT(mon->wx);
|
||||||
|
YSTR("y"); YINT(mon->wy);
|
||||||
|
YSTR("width"); YINT(mon->ww);
|
||||||
|
YSTR("height"); YINT(mon->wh);
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("tagset"); YMAP(
|
||||||
|
YSTR("current"); YINT(mon->tagset[mon->seltags]);
|
||||||
|
YSTR("old"); YINT(mon->tagset[mon->seltags ^ 1]);
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("tag_state"); dump_tag_state(gen, mon->tagstate);
|
||||||
|
|
||||||
|
YSTR("clients"); YMAP(
|
||||||
|
YSTR("selected"); YINT(mon->sel ? mon->sel->win : 0);
|
||||||
|
YSTR("stack"); YARR(
|
||||||
|
for (Client* c = mon->stack; c; c = c->snext)
|
||||||
|
YINT(c->win);
|
||||||
|
)
|
||||||
|
YSTR("all"); YARR(
|
||||||
|
for (Client* c = mon->clients; c; c = c->next)
|
||||||
|
YINT(c->win);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
YSTR("layout"); YMAP(
|
||||||
|
YSTR("symbol"); YMAP(
|
||||||
|
YSTR("current"); YSTR(mon->ltsymbol);
|
||||||
|
YSTR("old"); YSTR(mon->lastltsymbol);
|
||||||
|
)
|
||||||
|
YSTR("address"); YMAP(
|
||||||
|
YSTR("current"); YINT((uintptr_t)mon->lt[mon->sellt]);
|
||||||
|
YSTR("old"); YINT((uintptr_t)mon->lt[mon->sellt ^ 1]);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (mon->bar) {
|
||||||
|
YSTR("bar"); YMAP(
|
||||||
|
YSTR("y"); YINT(mon->bar->by);
|
||||||
|
YSTR("is_shown"); YBOOL(mon->showbar);
|
||||||
|
YSTR("is_top"); YBOOL(mon->bar->topbar);
|
||||||
|
YSTR("window_id"); YINT(mon->bar->win);
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YARR(
|
||||||
|
for (Monitor *mon = mons; mon; mon = mon->next) {
|
||||||
|
if (mon == selmon)
|
||||||
|
dump_monitor(gen, mon, 1);
|
||||||
|
else
|
||||||
|
dump_monitor(gen, mon, 0);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YARR(
|
||||||
|
for (int i = 0; i < layouts_len; i++) {
|
||||||
|
YMAP(
|
||||||
|
// Check for a NULL pointer. The cycle layouts patch adds an entry at
|
||||||
|
// the end of the layouts array with a NULL pointer for the symbol
|
||||||
|
YSTR("symbol"); YSTR((layouts[i].symbol ? layouts[i].symbol : ""));
|
||||||
|
YSTR("address"); YINT((uintptr_t)(layouts + i));
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_tag_state(yajl_gen gen, TagState state)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("selected"); YINT(state.selected);
|
||||||
|
YSTR("occupied"); YINT(state.occupied);
|
||||||
|
YSTR("urgent"); YINT(state.urgent);
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_tag_event(yajl_gen gen, int mon_num, TagState old_state,
|
||||||
|
TagState new_state)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("tag_change_event"); YMAP(
|
||||||
|
YSTR("monitor_number"); YINT(mon_num);
|
||||||
|
YSTR("old_state"); dump_tag_state(gen, old_state);
|
||||||
|
YSTR("new_state"); dump_tag_state(gen, new_state);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_client_focus_change_event(yajl_gen gen, Client *old_client,
|
||||||
|
Client *new_client, int mon_num)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("client_focus_change_event"); YMAP(
|
||||||
|
YSTR("monitor_number"); YINT(mon_num);
|
||||||
|
YSTR("old_win_id"); old_client == NULL ? YNULL() : YINT(old_client->win);
|
||||||
|
YSTR("new_win_id"); new_client == NULL ? YNULL() : YINT(new_client->win);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_layout_change_event(yajl_gen gen, const int mon_num,
|
||||||
|
const char *old_symbol, const Layout *old_layout,
|
||||||
|
const char *new_symbol, const Layout *new_layout)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("layout_change_event"); YMAP(
|
||||||
|
YSTR("monitor_number"); YINT(mon_num);
|
||||||
|
YSTR("old_symbol"); YSTR(old_symbol);
|
||||||
|
YSTR("old_address"); YINT((uintptr_t)old_layout);
|
||||||
|
YSTR("new_symbol"); YSTR(new_symbol);
|
||||||
|
YSTR("new_address"); YINT((uintptr_t)new_layout);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num,
|
||||||
|
const int new_mon_num)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("monitor_focus_change_event"); YMAP(
|
||||||
|
YSTR("old_monitor_number"); YINT(last_mon_num);
|
||||||
|
YSTR("new_monitor_number"); YINT(new_mon_num);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_focused_title_change_event(yajl_gen gen, const int mon_num,
|
||||||
|
const Window client_id, const char *old_name,
|
||||||
|
const char *new_name)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("focused_title_change_event"); YMAP(
|
||||||
|
YSTR("monitor_number"); YINT(mon_num);
|
||||||
|
YSTR("client_window_id"); YINT(client_id);
|
||||||
|
YSTR("old_name"); YSTR(old_name);
|
||||||
|
YSTR("new_name"); YSTR(new_name);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_client_state(yajl_gen gen, const ClientState *state)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("old_state"); YBOOL(state->oldstate);
|
||||||
|
YSTR("is_fixed"); YBOOL(state->isfixed);
|
||||||
|
YSTR("is_floating"); YBOOL(state->isfloating);
|
||||||
|
YSTR("is_fullscreen"); YBOOL(state->isfullscreen);
|
||||||
|
YSTR("is_urgent"); YBOOL(state->isurgent);
|
||||||
|
YSTR("never_focus"); YBOOL(state->neverfocus);
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_focused_state_change_event(yajl_gen gen, const int mon_num,
|
||||||
|
const Window client_id,
|
||||||
|
const ClientState *old_state,
|
||||||
|
const ClientState *new_state)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("focused_state_change_event"); YMAP(
|
||||||
|
YSTR("monitor_number"); YINT(mon_num);
|
||||||
|
YSTR("client_window_id"); YINT(client_id);
|
||||||
|
YSTR("old_state"); dump_client_state(gen, old_state);
|
||||||
|
YSTR("new_state"); dump_client_state(gen, new_state);
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_error_message(yajl_gen gen, const char *reason)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
YMAP(
|
||||||
|
YSTR("result"); YSTR("error");
|
||||||
|
YSTR("reason"); YSTR(reason);
|
||||||
|
)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
66
patch/ipc/yajl_dumps.h
Normal file
66
patch/ipc/yajl_dumps.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef YAJL_DUMPS_H_
|
||||||
|
#define YAJL_DUMPS_H_
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <yajl/yajl_gen.h>
|
||||||
|
|
||||||
|
#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
|
||||||
|
#define YINT(num) yajl_gen_integer(gen, num)
|
||||||
|
#define YDOUBLE(num) yajl_gen_double(gen, num)
|
||||||
|
#define YBOOL(v) yajl_gen_bool(gen, v)
|
||||||
|
#define YNULL() yajl_gen_null(gen)
|
||||||
|
#define YARR(body) \
|
||||||
|
{ \
|
||||||
|
yajl_gen_array_open(gen); \
|
||||||
|
body; \
|
||||||
|
yajl_gen_array_close(gen); \
|
||||||
|
}
|
||||||
|
#define YMAP(body) \
|
||||||
|
{ \
|
||||||
|
yajl_gen_map_open(gen); \
|
||||||
|
body; \
|
||||||
|
yajl_gen_map_close(gen); \
|
||||||
|
}
|
||||||
|
|
||||||
|
int dump_tag(yajl_gen gen, const char *name, const int tag_mask);
|
||||||
|
|
||||||
|
int dump_tags(yajl_gen gen, int tags_len);
|
||||||
|
|
||||||
|
int dump_client(yajl_gen gen, Client *c);
|
||||||
|
|
||||||
|
int dump_monitor(yajl_gen gen, Monitor *mon, int is_selected);
|
||||||
|
|
||||||
|
int dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon);
|
||||||
|
|
||||||
|
int dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len);
|
||||||
|
|
||||||
|
int dump_tag_state(yajl_gen gen, TagState state);
|
||||||
|
|
||||||
|
int dump_tag_event(yajl_gen gen, int mon_num, TagState old_state,
|
||||||
|
TagState new_state);
|
||||||
|
|
||||||
|
int dump_client_focus_change_event(yajl_gen gen, Client *old_client,
|
||||||
|
Client *new_client, int mon_num);
|
||||||
|
|
||||||
|
int dump_layout_change_event(yajl_gen gen, const int mon_num,
|
||||||
|
const char *old_symbol, const Layout *old_layout,
|
||||||
|
const char *new_symbol, const Layout *new_layout);
|
||||||
|
|
||||||
|
int dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num,
|
||||||
|
const int new_mon_num);
|
||||||
|
|
||||||
|
int dump_focused_title_change_event(yajl_gen gen, const int mon_num,
|
||||||
|
const Window client_id,
|
||||||
|
const char *old_name, const char *new_name);
|
||||||
|
|
||||||
|
int dump_client_state(yajl_gen gen, const ClientState *state);
|
||||||
|
|
||||||
|
int dump_focused_state_change_event(yajl_gen gen, const int mon_num,
|
||||||
|
const Window client_id,
|
||||||
|
const ClientState *old_state,
|
||||||
|
const ClientState *new_state);
|
||||||
|
|
||||||
|
int dump_error_message(yajl_gen gen, const char *reason);
|
||||||
|
|
||||||
|
#endif // YAJL_DUMPS_H_
|
||||||
|
|
||||||
26
patch/layout_facts.c
Normal file
26
patch/layout_facts.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
void
|
||||||
|
getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
float mfacts = 0, sfacts = 0;
|
||||||
|
int mtotal = 0, stotal = 0;
|
||||||
|
Client *c;
|
||||||
|
|
||||||
|
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++)
|
||||||
|
if (n < m->nmaster)
|
||||||
|
mfacts += c->cfact;
|
||||||
|
else
|
||||||
|
sfacts += c->cfact;
|
||||||
|
|
||||||
|
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++)
|
||||||
|
if (n < m->nmaster)
|
||||||
|
mtotal += msize * (c->cfact / mfacts);
|
||||||
|
else
|
||||||
|
stotal += ssize * (c->cfact / sfacts);
|
||||||
|
|
||||||
|
*mf = mfacts; // total factor of master area
|
||||||
|
*sf = sfacts; // total factor of stack area
|
||||||
|
*mr = msize - mtotal; // the remainder (rest) of pixels after a cfacts master split
|
||||||
|
*sr = ssize - stotal; // the remainder (rest) of pixels after a cfacts stack split
|
||||||
|
}
|
||||||
|
|
||||||
92
patch/layout_fibonacci.c
Normal file
92
patch/layout_fibonacci.c
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
void
|
||||||
|
fibonacci(Monitor *m, int s)
|
||||||
|
{
|
||||||
|
unsigned int i, n;
|
||||||
|
int nx, ny, nw, nh;
|
||||||
|
int nv, hrest = 0, wrest = 0, r = 1;
|
||||||
|
Client *c;
|
||||||
|
|
||||||
|
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
|
||||||
|
if (n == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nx = m->wx;
|
||||||
|
ny = m->wy;
|
||||||
|
nw = m->ww;
|
||||||
|
nh = m->wh;
|
||||||
|
|
||||||
|
for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
|
||||||
|
if (r) {
|
||||||
|
if ((i % 2 && nh / 2 <= (bh + 2*c->bw))
|
||||||
|
|| (!(i % 2) && nw / 2 <= (bh + 2*c->bw))) {
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
if (r && i < n - 1) {
|
||||||
|
if (i % 2) {
|
||||||
|
nv = nh / 2;
|
||||||
|
hrest = nh - 2*nv;
|
||||||
|
nh = nv;
|
||||||
|
} else {
|
||||||
|
nv = nw / 2;
|
||||||
|
wrest = nw - 2*nv;
|
||||||
|
nw = nv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i % 4) == 2 && !s)
|
||||||
|
nx += nw;
|
||||||
|
else if ((i % 4) == 3 && !s)
|
||||||
|
ny += nh;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i % 4) == 0) {
|
||||||
|
if (s) {
|
||||||
|
ny += nh;
|
||||||
|
nh += hrest;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nh -= hrest;
|
||||||
|
ny -= nh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((i % 4) == 1) {
|
||||||
|
nx += nw;
|
||||||
|
nw += wrest;
|
||||||
|
}
|
||||||
|
else if ((i % 4) == 2) {
|
||||||
|
ny += nh;
|
||||||
|
nh += hrest;
|
||||||
|
if (i < n - 1)
|
||||||
|
nw += wrest;
|
||||||
|
}
|
||||||
|
else if ((i % 4) == 3) {
|
||||||
|
if (s) {
|
||||||
|
nx += nw;
|
||||||
|
nw -= wrest;
|
||||||
|
} else {
|
||||||
|
nw -= wrest;
|
||||||
|
nx -= nw;
|
||||||
|
nh += hrest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
if (n != 1) {
|
||||||
|
nw = m->ww - m->ww * (1 - m->mfact);
|
||||||
|
wrest = 0;
|
||||||
|
}
|
||||||
|
ny = m->wy;
|
||||||
|
}
|
||||||
|
else if (i == 1)
|
||||||
|
nw = m->ww - nw;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dwindle(Monitor *m)
|
||||||
|
{
|
||||||
|
fibonacci(m, 1);
|
||||||
|
}
|
||||||
|
|
||||||
3
patch/layout_fibonacci.h
Normal file
3
patch/layout_fibonacci.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
static void dwindle(Monitor *m);
|
||||||
|
static void fibonacci(Monitor *m, int s);
|
||||||
|
|
||||||
29
patch/layout_grid.c
Normal file
29
patch/layout_grid.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
void
|
||||||
|
grid(Monitor *m)
|
||||||
|
{
|
||||||
|
unsigned int i, n;
|
||||||
|
int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows;
|
||||||
|
Client *c;
|
||||||
|
|
||||||
|
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
|
||||||
|
|
||||||
|
/* grid dimensions */
|
||||||
|
for (rows = 0; rows <= n/2; rows++)
|
||||||
|
if (rows*rows >= n)
|
||||||
|
break;
|
||||||
|
cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows;
|
||||||
|
|
||||||
|
/* window geoms (cell height/width) */
|
||||||
|
ch = m->wh / (rows ? rows : 1);
|
||||||
|
cw = m->ww / (cols ? cols : 1);
|
||||||
|
chrest = m->wh - ch * rows;
|
||||||
|
cwrest = m->ww - cw * cols;
|
||||||
|
for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
|
||||||
|
cc = i / rows;
|
||||||
|
cr = i % rows;
|
||||||
|
cx = m->wx + cc * cw + MIN(cc, cwrest);
|
||||||
|
cy = m->wy + cr * ch + MIN(cr, chrest);
|
||||||
|
resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/layout_grid.h
Normal file
2
patch/layout_grid.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void grid(Monitor *m);
|
||||||
|
|
||||||
15
patch/layout_monocle.c
Normal file
15
patch/layout_monocle.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
void
|
||||||
|
monocle(Monitor *m)
|
||||||
|
{
|
||||||
|
unsigned int n = 0;
|
||||||
|
Client *c;
|
||||||
|
|
||||||
|
for (c = m->clients; c; c = c->next)
|
||||||
|
if (ISVISIBLE(c))
|
||||||
|
n++;
|
||||||
|
if (n > 0) /* override layout symbol */
|
||||||
|
snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
|
||||||
|
for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
|
||||||
|
resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/layout_monocle.h
Normal file
2
patch/layout_monocle.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void monocle(Monitor *m);
|
||||||
|
|
||||||
38
patch/layout_tile.c
Normal file
38
patch/layout_tile.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
static void
|
||||||
|
tile(Monitor *m)
|
||||||
|
{
|
||||||
|
unsigned int i, n;
|
||||||
|
int mx = 0, my = 0, mh = 0, mw = 0;
|
||||||
|
int sx = 0, sy = 0, sh = 0, sw = 0;
|
||||||
|
float mfacts, sfacts;
|
||||||
|
int mrest, srest;
|
||||||
|
Client *c;
|
||||||
|
|
||||||
|
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sx = mx = m->wx;
|
||||||
|
sy = my = m->wy;
|
||||||
|
sh = mh = m->wh;
|
||||||
|
sw = mw = m->ww;
|
||||||
|
|
||||||
|
if (m->nmaster && n > m->nmaster) {
|
||||||
|
sw = mw * (1 - m->mfact);
|
||||||
|
mw = mw * m->mfact;
|
||||||
|
sx = mx + mw;
|
||||||
|
}
|
||||||
|
|
||||||
|
getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest);
|
||||||
|
|
||||||
|
for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
|
||||||
|
if (i < m->nmaster) {
|
||||||
|
resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) * c->cfact + (i < mrest ? 1 : 0) - (2*c->bw), 0);
|
||||||
|
my += HEIGHT(c);
|
||||||
|
} else {
|
||||||
|
resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) * c->cfact + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0);
|
||||||
|
sy += HEIGHT(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/layout_tile.h
Normal file
2
patch/layout_tile.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void tile(Monitor *m);
|
||||||
|
|
||||||
51
patch/movestack.c
Normal file
51
patch/movestack.c
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
void
|
||||||
|
movestack(const Arg *arg)
|
||||||
|
{
|
||||||
|
Client *c = NULL, *p = NULL, *pc = NULL, *i;
|
||||||
|
if (arg->i > 0) {
|
||||||
|
if (!selmon->sel)
|
||||||
|
return;
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/movestack.h
Normal file
2
patch/movestack.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void movestack(const Arg *arg);
|
||||||
|
|
||||||
28
patch/pertag.c
Normal file
28
patch/pertag.c
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
struct Pertag {
|
||||||
|
unsigned int curtag; /* current tag index */
|
||||||
|
int nmasters[NUMTAGS + 1]; /* number of windows in master area */
|
||||||
|
const Layout *ltidxs[NUMTAGS + 1][2]; /* matrix of tags and layouts indexes */
|
||||||
|
float mfacts[NUMTAGS + 1]; /* mfacts per tag */
|
||||||
|
unsigned int sellts[NUMTAGS + 1]; /* selected layouts */
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
pertagview(const Arg *arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (arg->ui == ~0)
|
||||||
|
selmon->pertag->curtag = 0;
|
||||||
|
else {
|
||||||
|
for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++);
|
||||||
|
selmon->pertag->curtag = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
|
||||||
|
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
|
||||||
|
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
|
||||||
|
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
|
||||||
|
selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
2
patch/pertag.h
Normal file
2
patch/pertag.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
static void pertagview(const Arg *arg);
|
||||||
|
|
||||||
144
patch/renamed_scratchpads.c
Normal file
144
patch/renamed_scratchpads.c
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
void
|
||||||
|
removescratch(const Arg *arg)
|
||||||
|
{
|
||||||
|
Client *c = selmon->sel;
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
c->scratchkey = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setscratch(const Arg *arg)
|
||||||
|
{
|
||||||
|
Client *c = selmon->sel;
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
|
||||||
|
c->scratchkey = ((char**)arg->v)[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawnscratch(const Arg *arg)
|
||||||
|
{
|
||||||
|
if (fork() == 0) {
|
||||||
|
if (dpy)
|
||||||
|
close(ConnectionNumber(dpy));
|
||||||
|
setsid();
|
||||||
|
execvp(((char **)arg->v)[1], ((char **)arg->v)+1);
|
||||||
|
fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[1]);
|
||||||
|
perror(" failed");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
togglescratch(const Arg *arg)
|
||||||
|
{
|
||||||
|
Client *c, *next, *last = NULL, *found = NULL, *monclients = NULL;
|
||||||
|
Monitor *mon;
|
||||||
|
int scratchvisible = 0; // whether the scratchpads are currently visible or not
|
||||||
|
int multimonscratch = 0; // whether we have scratchpads that are placed on multiple monitors
|
||||||
|
int scratchmon = -1; // the monitor where the scratchpads exist
|
||||||
|
int numscratchpads = 0; // count of scratchpads
|
||||||
|
|
||||||
|
/* Looping through monitors and client's twice, the first time to work out whether we need
|
||||||
|
to move clients across from one monitor to another or not */
|
||||||
|
for (mon = mons; mon; mon = mon->next)
|
||||||
|
for (c = mon->clients; c; c = c->next) {
|
||||||
|
if (c->scratchkey != ((char**)arg->v)[0][0])
|
||||||
|
continue;
|
||||||
|
if (scratchmon != -1 && scratchmon != mon->num)
|
||||||
|
multimonscratch = 1;
|
||||||
|
if (c->mon->tagset[c->mon->seltags] & c->tags) // && !HIDDEN(c)
|
||||||
|
++scratchvisible;
|
||||||
|
scratchmon = mon->num;
|
||||||
|
++numscratchpads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now for the real deal. The logic should go like:
|
||||||
|
- hidden scratchpads will be shown
|
||||||
|
- shown scratchpads will be hidden, unless they are being moved to the current monitor
|
||||||
|
- the scratchpads will be moved to the current monitor if they all reside on the same monitor
|
||||||
|
- multiple scratchpads residing on separate monitors will be left in place
|
||||||
|
*/
|
||||||
|
for (mon = mons; mon; mon = mon->next) {
|
||||||
|
for (c = mon->stack; c; c = next) {
|
||||||
|
next = c->snext;
|
||||||
|
if (c->scratchkey != ((char**)arg->v)[0][0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Record the first found scratchpad client for focus purposes, but prioritise the
|
||||||
|
scratchpad on the current monitor if one exists */
|
||||||
|
if (!found || (mon == selmon && found->mon != selmon))
|
||||||
|
found = c;
|
||||||
|
|
||||||
|
/* If scratchpad clients reside on another monitor and we are moving them across then
|
||||||
|
as we are looping through monitors we could be moving a client to a monitor that has
|
||||||
|
not been processed yet, hence we could be processing a scratchpad twice. To avoid
|
||||||
|
this we detach them and add them to a temporary list (monclients) which is to be
|
||||||
|
processed later. */
|
||||||
|
if (!multimonscratch && c->mon != selmon) {
|
||||||
|
detach(c);
|
||||||
|
detachstack(c);
|
||||||
|
c->next = NULL;
|
||||||
|
/* Note that we are adding clients at the end of the list, this is to preserve the
|
||||||
|
order of clients as they were on the adjacent monitor (relevant when tiled) */
|
||||||
|
if (last)
|
||||||
|
last = last->next = c;
|
||||||
|
else
|
||||||
|
last = monclients = c;
|
||||||
|
} else if (scratchvisible == numscratchpads) {
|
||||||
|
c->tags = 0;
|
||||||
|
} else {
|
||||||
|
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchNorm][ColBorder].pixel);
|
||||||
|
c->tags = c->mon->tagset[c->mon->seltags];
|
||||||
|
if (c->isfloating)
|
||||||
|
XRaiseWindow(dpy, c->win);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attach moved scratchpad clients on the selected monitor */
|
||||||
|
for (c = monclients; c; c = next) {
|
||||||
|
next = c->next;
|
||||||
|
mon = c->mon;
|
||||||
|
c->mon = selmon;
|
||||||
|
c->tags = selmon->tagset[selmon->seltags];
|
||||||
|
/* Attach scratchpad clients from other monitors at the bottom of the stack */
|
||||||
|
if (selmon->clients) {
|
||||||
|
for (last = selmon->clients; last && last->next; last = last->next);
|
||||||
|
last->next = c;
|
||||||
|
} else
|
||||||
|
selmon->clients = c;
|
||||||
|
c->next = NULL;
|
||||||
|
attachstack(c);
|
||||||
|
|
||||||
|
/* Center floating scratchpad windows when moved from one monitor to another */
|
||||||
|
if (c->isfloating) {
|
||||||
|
if (c->w > selmon->ww)
|
||||||
|
c->w = selmon->ww - c->bw * 2;
|
||||||
|
if (c->h > selmon->wh)
|
||||||
|
c->h = selmon->wh - c->bw * 2;
|
||||||
|
|
||||||
|
if (numscratchpads > 1) {
|
||||||
|
c->x = c->mon->wx + (c->x - mon->wx) * ((double)(abs(c->mon->ww - WIDTH(c))) / MAX(abs(mon->ww - WIDTH(c)), 1));
|
||||||
|
c->y = c->mon->wy + (c->y - mon->wy) * ((double)(abs(c->mon->wh - HEIGHT(c))) / MAX(abs(mon->wh - HEIGHT(c)), 1));
|
||||||
|
} else if (c->x < c->mon->mx || c->x > c->mon->mx + c->mon->mw ||
|
||||||
|
c->y < c->mon->my || c->y > c->mon->my + c->mon->mh) {
|
||||||
|
c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
|
||||||
|
c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
|
||||||
|
}
|
||||||
|
resizeclient(c, c->x, c->y, c->w, c->h);
|
||||||
|
XRaiseWindow(dpy, c->win);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
focus(ISVISIBLE(found) ? found : NULL);
|
||||||
|
arrange(NULL);
|
||||||
|
if (found->isfloating)
|
||||||
|
XRaiseWindow(dpy, found->win);
|
||||||
|
} else {
|
||||||
|
spawnscratch(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
4
patch/renamed_scratchpads.h
Normal file
4
patch/renamed_scratchpads.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
static void removescratch(const Arg *arg);
|
||||||
|
static void setscratch(const Arg *arg);
|
||||||
|
static void spawnscratch(const Arg *arg);
|
||||||
|
static void togglescratch(const Arg *arg);
|
||||||
16
patch/restartsig.c
Normal file
16
patch/restartsig.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
static int restart = 0;
|
||||||
|
|
||||||
|
void
|
||||||
|
sighup(int unused)
|
||||||
|
{
|
||||||
|
Arg a = {.i = 1};
|
||||||
|
quit(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sigterm(int unused)
|
||||||
|
{
|
||||||
|
Arg a = {.i = 0};
|
||||||
|
quit(&a);
|
||||||
|
}
|
||||||
|
|
||||||
3
patch/restartsig.h
Normal file
3
patch/restartsig.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
static void sighup(int unused);
|
||||||
|
static void sigterm(int unused);
|
||||||
|
|
||||||
215
patch/swallow.c
Normal file
215
patch/swallow.c
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
#include <X11/Xlib-xcb.h>
|
||||||
|
#include <xcb/res.h>
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <kvm.h>
|
||||||
|
#endif /* __OpenBSD__ */
|
||||||
|
|
||||||
|
static int scanner;
|
||||||
|
static xcb_connection_t *xcon;
|
||||||
|
|
||||||
|
int
|
||||||
|
swallow(Client *p, Client *c)
|
||||||
|
{
|
||||||
|
Client *s;
|
||||||
|
XWindowChanges wc;
|
||||||
|
|
||||||
|
if (c->noswallow > 0 || c->isterminal)
|
||||||
|
return 0;
|
||||||
|
if (c->noswallow < 0 && !swallowfloating && c->isfloating)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
XMapWindow(dpy, c->win);
|
||||||
|
|
||||||
|
detach(c);
|
||||||
|
detachstack(c);
|
||||||
|
|
||||||
|
setclientstate(c, WithdrawnState);
|
||||||
|
XUnmapWindow(dpy, p->win);
|
||||||
|
|
||||||
|
p->swallowing = c;
|
||||||
|
c->mon = p->mon;
|
||||||
|
|
||||||
|
Window w = p->win;
|
||||||
|
p->win = c->win;
|
||||||
|
c->win = w;
|
||||||
|
|
||||||
|
XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace,
|
||||||
|
(unsigned char *) &(p->win), 1);
|
||||||
|
|
||||||
|
updatetitle(p);
|
||||||
|
s = scanner ? c : p;
|
||||||
|
setfloatinghint(s);
|
||||||
|
|
||||||
|
wc.border_width = p->bw;
|
||||||
|
XConfigureWindow(dpy, p->win, CWBorderWidth, &wc);
|
||||||
|
XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h);
|
||||||
|
|
||||||
|
XSetWindowBorder(dpy, p->win, scheme[SchemeNorm][ColBorder].pixel);
|
||||||
|
|
||||||
|
arrange(p->mon);
|
||||||
|
configure(p);
|
||||||
|
updateclientlist();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unswallow(Client *c)
|
||||||
|
{
|
||||||
|
XWindowChanges wc;
|
||||||
|
c->win = c->swallowing->win;
|
||||||
|
|
||||||
|
free(c->swallowing);
|
||||||
|
c->swallowing = NULL;
|
||||||
|
|
||||||
|
XDeleteProperty(dpy, c->win, netatom[NetClientList]);
|
||||||
|
|
||||||
|
/* unfullscreen the client */
|
||||||
|
setfullscreen(c, 0);
|
||||||
|
updatetitle(c);
|
||||||
|
arrange(c->mon);
|
||||||
|
XMapWindow(dpy, c->win);
|
||||||
|
|
||||||
|
wc.border_width = c->bw;
|
||||||
|
XConfigureWindow(dpy, c->win, CWBorderWidth, &wc);
|
||||||
|
XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
|
||||||
|
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
|
||||||
|
|
||||||
|
setfloatinghint(c);
|
||||||
|
setclientstate(c, NormalState);
|
||||||
|
arrange(c->mon);
|
||||||
|
focus(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t
|
||||||
|
winpid(Window w)
|
||||||
|
{
|
||||||
|
pid_t result = 0;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
xcb_res_client_id_spec_t spec = {0};
|
||||||
|
spec.client = w;
|
||||||
|
spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
|
||||||
|
|
||||||
|
xcb_generic_error_t *e = NULL;
|
||||||
|
xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
|
||||||
|
xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
return (pid_t)0;
|
||||||
|
|
||||||
|
xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
|
||||||
|
for (; i.rem; xcb_res_client_id_value_next(&i)) {
|
||||||
|
spec = i.data->spec;
|
||||||
|
if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
|
||||||
|
uint32_t *t = xcb_res_client_id_value_value(i.data);
|
||||||
|
result = *t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(r);
|
||||||
|
|
||||||
|
if (result == (pid_t)-1)
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
#endif /* __linux__ */
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
unsigned long len, bytes;
|
||||||
|
unsigned char *prop;
|
||||||
|
pid_t ret;
|
||||||
|
|
||||||
|
if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 1), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = *(pid_t*)prop;
|
||||||
|
XFree(prop);
|
||||||
|
result = ret;
|
||||||
|
#endif /* __OpenBSD__ */
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t
|
||||||
|
getparentprocess(pid_t p)
|
||||||
|
{
|
||||||
|
unsigned int v = 0;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
FILE *f;
|
||||||
|
char buf[256];
|
||||||
|
snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
|
||||||
|
|
||||||
|
if (!(f = fopen(buf, "r")))
|
||||||
|
return (pid_t)0;
|
||||||
|
|
||||||
|
if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1)
|
||||||
|
v = (pid_t)0;
|
||||||
|
fclose(f);
|
||||||
|
#endif /* __linux__ */
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
int n;
|
||||||
|
kvm_t *kd;
|
||||||
|
struct kinfo_proc *kp;
|
||||||
|
|
||||||
|
kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||||
|
if (!kd)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
|
||||||
|
v = kp->p_ppid;
|
||||||
|
#endif /* __OpenBSD__ */
|
||||||
|
return (pid_t)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
isdescprocess(pid_t p, pid_t c)
|
||||||
|
{
|
||||||
|
while (p != c && c != 0)
|
||||||
|
c = getparentprocess(c);
|
||||||
|
|
||||||
|
return (int)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *
|
||||||
|
termforwin(const Client *w)
|
||||||
|
{
|
||||||
|
Client *c;
|
||||||
|
Monitor *m;
|
||||||
|
|
||||||
|
if (!w->pid || w->isterminal)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
c = selmon->sel;
|
||||||
|
if (c && c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
|
||||||
|
return c;
|
||||||
|
|
||||||
|
for (m = mons; m; m = m->next) {
|
||||||
|
for (c = m->clients; c; c = c->next) {
|
||||||
|
if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *
|
||||||
|
swallowingclient(Window w)
|
||||||
|
{
|
||||||
|
Client *c;
|
||||||
|
Monitor *m;
|
||||||
|
|
||||||
|
for (m = mons; m; m = m->next) {
|
||||||
|
for (c = m->clients; c; c = c->next) {
|
||||||
|
if (c->swallowing && c->swallowing->win == w)
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
8
patch/swallow.h
Normal file
8
patch/swallow.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
static pid_t getparentprocess(pid_t p);
|
||||||
|
static int isdescprocess(pid_t p, pid_t c);
|
||||||
|
static int swallow(Client *p, Client *c);
|
||||||
|
static Client *swallowingclient(Window w);
|
||||||
|
static Client *termforwin(const Client *c);
|
||||||
|
static void unswallow(Client *c);
|
||||||
|
static pid_t winpid(Window w);
|
||||||
|
|
||||||
43
transient.c
Normal file
43
transient.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* 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
util.c
Normal file
36
util.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
die(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int saved_errno;
|
||||||
|
|
||||||
|
saved_errno = errno;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (fmt[0] && fmt[strlen(fmt)-1] == ':')
|
||||||
|
fprintf(stderr, " %s", strerror(saved_errno));
|
||||||
|
fputc('\n', stderr);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
ecalloc(size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
if (!(p = calloc(nmemb, size)))
|
||||||
|
die("calloc:");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
20
util.h
Normal file
20
util.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||||
|
#endif
|
||||||
|
#ifndef MIN
|
||||||
|
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||||
|
#endif
|
||||||
|
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
|
||||||
|
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DEBUG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void die(const char *fmt, ...);
|
||||||
|
void *ecalloc(size_t nmemb, size_t size);
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user