Compare commits

...

11 commits

Author SHA1 Message Date
f8a668b807 dwm-ipc 2024-11-29 17:31:12 -08:00
e046d713c0 eww altbarclass 2024-11-29 15:30:54 -08:00
547fe8d0fa fixing compile errors 2024-11-29 12:47:52 -08:00
88b6b06b7d anybar 2024-11-29 12:41:23 -08:00
b6f968c9ff self restart 2024-11-29 12:11:12 -08:00
708b6505ee preserve on restart 2024-11-29 12:09:03 -08:00
735c37fe07 pertag diff applied 2024-11-29 12:08:38 -08:00
cb95844a12 obsidian tag settings 2024-11-29 12:08:38 -08:00
8494d965ef kitty editor 2024-11-29 12:03:54 -08:00
b0db46bdd6 dwm config 2024-11-29 12:03:54 -08:00
0b8ed8c29b clangd config 2024-11-29 12:03:54 -08:00
19 changed files with 6681 additions and 45 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.cache

66
IPCClient.c Normal file
View file

@ -0,0 +1,66 @@
#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;
}

61
IPCClient.h Normal file
View file

@ -0,0 +1,61 @@
#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_

68
compile_commands.json Normal file
View file

@ -0,0 +1,68 @@
[
{
"arguments": [
"/nix/store/vh9fsdhgxcnab2qk7vdp2palkkn6j3cp-gcc-wrapper-13.3.0/bin/cc",
"-c",
"-std=c99",
"-pedantic",
"-Wall",
"-Wno-deprecated-declarations",
"-Os",
"-I/usr/X11R6/include",
"-I/usr/include/freetype2",
"-I/usr/include/yajl",
"-D_DEFAULT_SOURCE",
"-D_BSD_SOURCE",
"-D_XOPEN_SOURCE=700L",
"-DVERSION=\"6.5\"",
"-DXINERAMA",
"drw.c"
],
"directory": "/home/ravenshade/Documents/projects/dwm",
"file": "/home/ravenshade/Documents/projects/dwm/drw.c"
},
{
"arguments": [
"/nix/store/vh9fsdhgxcnab2qk7vdp2palkkn6j3cp-gcc-wrapper-13.3.0/bin/cc",
"-c",
"-std=c99",
"-pedantic",
"-Wall",
"-Wno-deprecated-declarations",
"-Os",
"-I/usr/X11R6/include",
"-I/usr/include/freetype2",
"-I/usr/include/yajl",
"-D_DEFAULT_SOURCE",
"-D_BSD_SOURCE",
"-D_XOPEN_SOURCE=700L",
"-DVERSION=\"6.5\"",
"-DXINERAMA",
"dwm.c"
],
"directory": "/home/ravenshade/Documents/projects/dwm",
"file": "/home/ravenshade/Documents/projects/dwm/dwm.c"
},
{
"arguments": [
"/nix/store/vh9fsdhgxcnab2qk7vdp2palkkn6j3cp-gcc-wrapper-13.3.0/bin/cc",
"-c",
"-std=c99",
"-pedantic",
"-Wall",
"-Wno-deprecated-declarations",
"-Os",
"-I/usr/X11R6/include",
"-I/usr/include/freetype2",
"-I/usr/include/yajl",
"-D_DEFAULT_SOURCE",
"-D_BSD_SOURCE",
"-D_XOPEN_SOURCE=700L",
"-DVERSION=\"6.5\"",
"-DXINERAMA",
"util.c"
],
"directory": "/home/ravenshade/Documents/projects/dwm",
"file": "/home/ravenshade/Documents/projects/dwm/util.c"
}
]

View file

@ -1,23 +1,29 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int borderpx = 0; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *fonts[] = { "JetBrainsMono Nerd Font:size=10" };
static const char dmenufont[] = "JetBrainsMono Nerd Font:size=10";
static const char col_text[] = "#e0def4";
static const char col_subtle[] = "#908caa";
static const char col_muted[] = "#6e6a86";
static const char col_base[] = "#191724";
static const char col_iris[] = "#c4a7e7";
static const char col_h_low[] = "#21202e";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
[SchemeNorm] = { col_muted, col_h_low, col_base },
[SchemeSel] = { col_text, col_base, col_iris },
};
// anybar config
static const int usealtbar = 1; /* 1 means use non-dwm status bar */
static const char *altbarclass = "Eww"; /* Alternate bar class name */
static const char *altbarcmd = "$HOME/bar.sh"; /* Alternate bar launch command */
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -28,7 +34,11 @@ static const Rule rules[] = {
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
{ "discord", NULL, NULL, 1 << 1, 0, 1 },
{ "calendar", NULL, NULL, 1 << 4, 0, 1 },
{ "vesktop", NULL, NULL, 1 << 1, 0, 1 },
{ "obsidian", NULL, NULL, 1 << 3, 0, 1 },
{ "nannou", NULL, NULL, 0, 1, -1 },
};
/* layout(s) */
@ -45,7 +55,7 @@ static const Layout layouts[] = {
};
/* key definitions */
#define MODKEY Mod1Mask
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
@ -57,13 +67,17 @@ static const Layout layouts[] = {
/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_h_low, "-nf", col_muted, "-sb", col_base, "-sf", col_text, NULL };
static const char *termcmd[] = { "kitty", NULL };
static const char *scrnshotcmd[] = { "flameshot", "gui", NULL };
#include "selfrestart.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ ControlMask, XK_Print, spawn, {.v = scrnshotcmd } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
@ -95,6 +109,7 @@ static const Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
{ MODKEY|ShiftMask, XK_r, self_restart, {0} },
};
/* button definitions */
@ -114,3 +129,21 @@ static const Button buttons[] = {
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
static const char *ipcsockpath = "/tmp/dwm.sock";
static IPCCommand ipccommands[] = {
IPCCOMMAND( view, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( toggleview, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( tag, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( toggletag, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( tagmon, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( focusmon, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( focusstack, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( zoom, 1, {ARG_TYPE_NONE} ),
IPCCOMMAND( incnmaster, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( killclient, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( togglefloating, 1, {ARG_TYPE_NONE} ),
IPCCOMMAND( setmfact, 1, {ARG_TYPE_FLOAT} ),
IPCCOMMAND( setlayoutsafe, 1, {ARG_TYPE_PTR} ),
IPCCOMMAND( quit, 1, {ARG_TYPE_NONE} )
};

149
config.h Normal file
View file

@ -0,0 +1,149 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const unsigned int borderpx = 0; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "JetBrainsMono Nerd Font:size=10" };
static const char dmenufont[] = "JetBrainsMono Nerd Font:size=10";
static const char col_text[] = "#e0def4";
static const char col_subtle[] = "#908caa";
static const char col_muted[] = "#6e6a86";
static const char col_base[] = "#191724";
static const char col_iris[] = "#c4a7e7";
static const char col_h_low[] = "#21202e";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_muted, col_h_low, col_base },
[SchemeSel] = { col_text, col_base, col_iris },
};
// anybar config
static const int usealtbar = 1; /* 1 means use non-dwm status bar */
static const char *altbarclass = "Eww"; /* Alternate bar class name */
static const char *altbarcmd = "$HOME/bar.sh"; /* Alternate bar launch command */
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "discord", NULL, NULL, 1 << 1, 0, 1 },
{ "calendar", NULL, NULL, 1 << 4, 0, 1 },
{ "vesktop", NULL, NULL, 1 << 1, 0, 1 },
{ "obsidian", NULL, NULL, 1 << 3, 0, 1 },
{ "nannou", NULL, NULL, 0, 1, -1 },
};
/* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
static const 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 },
};
/* key definitions */
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_h_low, "-nf", col_muted, "-sb", col_base, "-sf", col_text, NULL };
static const char *termcmd[] = { "kitty", NULL };
static const char *scrnshotcmd[] = { "flameshot", "gui", NULL };
#include "selfrestart.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ ControlMask, XK_Print, spawn, {.v = scrnshotcmd } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
TAGKEYS( XK_4, 3)
TAGKEYS( XK_5, 4)
TAGKEYS( XK_6, 5)
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
{ MODKEY|ShiftMask, XK_r, self_restart, {0} },
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
{ ClkTagBar, 0, Button1, view, {0} },
{ ClkTagBar, 0, Button3, toggleview, {0} },
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
static const char *ipcsockpath = "/tmp/dwm.sock";
static IPCCommand ipccommands[] = {
IPCCOMMAND( view, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( toggleview, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( tag, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( toggletag, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( tagmon, 1, {ARG_TYPE_UINT} ),
IPCCOMMAND( focusmon, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( focusstack, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( zoom, 1, {ARG_TYPE_NONE} ),
IPCCOMMAND( incnmaster, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( killclient, 1, {ARG_TYPE_SINT} ),
IPCCOMMAND( togglefloating, 1, {ARG_TYPE_NONE} ),
IPCCOMMAND( setmfact, 1, {ARG_TYPE_FLOAT} ),
IPCCOMMAND( setlayoutsafe, 1, {ARG_TYPE_PTR} ),
IPCCOMMAND( quit, 1, {ARG_TYPE_NONE} )
};

View file

@ -21,9 +21,13 @@ FREETYPEINC = /usr/include/freetype2
#FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
# yajl
YAJLLIBS = -lyajl
YAJLINC = /usr/include/yajl
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
INCS = -I${X11INC} -I${FREETYPEINC} -I${YAJLINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${YAJLLIBS}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}

395
dwm.c
View file

@ -30,6 +30,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
@ -47,8 +48,8 @@
/* macros */
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
* MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->mx+(m)->mw) - MAX((x),(m)->mx)) \
* MAX(0, MIN((y)+(h),(m)->my+(m)->mh) - MAX((y),(m)->my)))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
@ -61,14 +62,28 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetLast }; /* EWMH atoms */
enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
typedef struct TagState TagState;
struct TagState {
int selected;
int occupied;
int urgent;
};
typedef struct ClientState ClientState;
struct ClientState {
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
};
typedef union {
int i;
unsigned int ui;
unsigned long ui;
float f;
const void *v;
} Arg;
@ -96,6 +111,7 @@ struct Client {
Client *snext;
Monitor *mon;
Window win;
ClientState prevstate;
};
typedef struct {
@ -110,25 +126,31 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
typedef struct Pertag Pertag;
struct Monitor {
char ltsymbol[16];
char lastltsymbol[16];
float mfact;
int nmaster;
int num;
int by; /* bar geometry */
int by, bh; /* bar geometry */
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
TagState tagstate;
int showbar;
int topbar;
Client *clients;
Client *sel;
Client *lastsel;
Client *stack;
Monitor *next;
Window barwin;
const Layout *lt[2];
const Layout *lastlt;
Pertag *pertag;
};
typedef struct {
@ -174,10 +196,12 @@ static long getstate(Window w);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, int focused);
static void grabkeys(void);
static int handlexevent(struct epoll_event *ev);
static void incnmaster(const Arg *arg);
static void keypress(XEvent *e);
static void killclient(const Arg *arg);
static void manage(Window w, XWindowAttributes *wa);
static void managealtbar(Window win, XWindowAttributes *wa);
static void mappingnotify(XEvent *e);
static void maprequest(XEvent *e);
static void monocle(Monitor *m);
@ -197,14 +221,18 @@ static void scan(void);
static int sendevent(Client *c, Atom proto);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
static void setclienttagprop(Client *c);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
static void setlayoutsafe(const Arg *arg);
static void setmfact(const Arg *arg);
static void setup(void);
static void setupepoll(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void spawn(const Arg *arg);
static void spawnbar(void);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *m);
@ -214,6 +242,7 @@ static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unfocus(Client *c, int setfocus);
static void unmanage(Client *c, int destroyed);
static void unmanagealtbar(Window w);
static void unmapnotify(XEvent *e);
static void updatebarpos(Monitor *m);
static void updatebars(void);
@ -228,6 +257,7 @@ static void updatewmhints(Client *c);
static void view(const Arg *arg);
static Client *wintoclient(Window w);
static Monitor *wintomon(Window w);
static int wmclasscontains(Window win, const char *class, const char *name);
static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
@ -258,18 +288,37 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[PropertyNotify] = propertynotify,
[UnmapNotify] = unmapnotify
};
static Atom wmatom[WMLast], netatom[NetLast];
static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
static int epoll_fd;
static int dpy_fd;
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Monitor *mons, *selmon, *lastselmon;
static Window root, wmcheckwin;
#include "ipc.h"
/* configuration, allows nested code to access above variables */
#include "config.h"
#ifdef VERSION
#include "IPCClient.c"
#include "yajl_dumps.c"
#include "ipc.c"
#endif
struct Pertag {
unsigned int curtag, prevtag; /* current and previous tag */
int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
};
/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
@ -493,6 +542,12 @@ cleanup(void)
XSync(dpy, False);
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
ipc_cleanup();
if (close(epoll_fd) < 0) {
fprintf(stderr, "Failed to close epoll file descriptor\n");
}
}
void
@ -506,8 +561,10 @@ cleanupmon(Monitor *mon)
for (m = mons; m && m->next != mon; m = m->next);
m->next = mon->next;
}
XUnmapWindow(dpy, mon->barwin);
XDestroyWindow(dpy, mon->barwin);
if (!usealtbar) {
XUnmapWindow(dpy, mon->barwin);
XDestroyWindow(dpy, mon->barwin);
}
free(mon);
}
@ -569,7 +626,7 @@ configurenotify(XEvent *e)
for (c = m->clients; c; c = c->next)
if (c->isfullscreen)
resizeclient(c, m->mx, m->my, m->mw, m->mh);
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, m->bh);
}
focus(NULL);
arrange(NULL);
@ -633,6 +690,7 @@ Monitor *
createmon(void)
{
Monitor *m;
unsigned int i;
m = ecalloc(1, sizeof(Monitor));
m->tagset[0] = m->tagset[1] = 1;
@ -640,9 +698,24 @@ createmon(void)
m->nmaster = nmaster;
m->showbar = showbar;
m->topbar = topbar;
m->bh = bh;
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
m->pertag = ecalloc(1, sizeof(Pertag));
m->pertag->curtag = m->pertag->prevtag = 1;
for (i = 0; i <= LENGTH(tags); i++) {
m->pertag->nmasters[i] = m->nmaster;
m->pertag->mfacts[i] = m->mfact;
m->pertag->ltidxs[i][0] = m->lt[0];
m->pertag->ltidxs[i][1] = m->lt[1];
m->pertag->sellts[i] = m->sellt;
m->pertag->showbars[i] = m->showbar;
}
return m;
}
@ -650,10 +723,13 @@ void
destroynotify(XEvent *e)
{
Client *c;
Monitor *m;
XDestroyWindowEvent *ev = &e->xdestroywindow;
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
else if ((m = wintomon(ev->window)) && m->barwin == ev->window)
unmanagealtbar(ev->window);
}
void
@ -697,6 +773,9 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
if (usealtbar)
return;
int x, w, tw = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
@ -976,10 +1055,29 @@ grabkeys(void)
}
}
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
incnmaster(const Arg *arg)
{
selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
arrange(selmon);
}
@ -1067,6 +1165,26 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
{
int format;
unsigned long *data, n, extra;
Monitor *m;
Atom atom;
if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL,
&atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) {
c->tags = *data;
for (m = mons; m; m = m->next) {
if (m->num == *(data+1)) {
c->mon = m;
break;
}
}
}
if (n > 0)
XFree(data);
}
setclienttagprop(c);
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@ -1087,6 +1205,25 @@ manage(Window w, XWindowAttributes *wa)
focus(NULL);
}
void
managealtbar(Window win, XWindowAttributes *wa)
{
Monitor *m;
if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height)))
return;
m->barwin = win;
m->by = wa->y;
bh = m->bh = wa->height;
updatebarpos(m);
arrange(m);
XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height);
XMapWindow(dpy, win);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &win, 1);
}
void
mappingnotify(XEvent *e)
{
@ -1105,7 +1242,9 @@ maprequest(XEvent *e)
if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
return;
if (!wintoclient(ev->window))
if (wmclasscontains(ev->window, altbarclass, ""))
managealtbar(ev->window, &wa);
else if (!wintoclient(ev->window))
manage(ev->window, &wa);
}
@ -1381,12 +1520,40 @@ restack(Monitor *m)
void
run(void)
{
XEvent ev;
/* main event loop */
int event_count = 0;
const int MAX_EVENTS = 10;
struct epoll_event events[MAX_EVENTS];
XSync(dpy, False);
while (running && !XNextEvent(dpy, &ev))
if (handler[ev.type])
handler[ev.type](&ev); /* call handler */
/* main event loop */
while (running) {
event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < event_count; i++) {
int event_fd = events[i].data.fd;
DEBUG("Got event from fd %d\n", event_fd);
if (event_fd == dpy_fd) {
// -1 means EPOLLHUP
if (handlexevent(events + i) == -1)
return;
} else if (event_fd == ipc_get_sock_fd()) {
ipc_handle_socket_epoll_event(events + i);
} else if (ipc_is_client_registered(event_fd)){
if (ipc_handle_client_epoll_event(events + i, mons, &lastselmon, selmon,
tags, LENGTH(tags), layouts, LENGTH(layouts)) < 0) {
fprintf(stderr, "Error handling IPC event on fd %d\n", event_fd);
}
} else {
fprintf(stderr, "Got event from unknown fd %d, ptr %p, u32 %d, u64 %lu",
event_fd, events[i].data.ptr, events[i].data.u32,
events[i].data.u64);
fprintf(stderr, " with events %d\n", events[i].events);
return;
}
}
}
}
void
@ -1401,7 +1568,9 @@ scan(void)
if (!XGetWindowAttributes(dpy, wins[i], &wa)
|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
continue;
if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
if (wmclasscontains(wins[i], altbarclass, ""))
managealtbar(wins[i], &wa);
else if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
manage(wins[i], &wa);
}
for (i = 0; i < num; i++) { /* now the transients */
@ -1428,6 +1597,7 @@ sendmon(Client *c, Monitor *m)
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c);
attachstack(c);
setclienttagprop(c);
focus(NULL);
arrange(NULL);
}
@ -1510,9 +1680,9 @@ void
setlayout(const Arg *arg)
{
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
selmon->sellt ^= 1;
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
if (arg && arg->v)
selmon->lt[selmon->sellt] = (Layout *)arg->v;
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if (selmon->sel)
arrange(selmon);
@ -1520,6 +1690,18 @@ setlayout(const Arg *arg)
drawbar(selmon);
}
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);
}
}
/* arg > 1.0 will set mfact absolutely */
void
setmfact(const Arg *arg)
@ -1531,7 +1713,7 @@ setmfact(const Arg *arg)
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.05 || f > 0.95)
return;
selmon->mfact = f;
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
arrange(selmon);
}
@ -1561,7 +1743,7 @@ setup(void)
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
bh = drw->fonts->h + 2;
bh = usealtbar ? 0 : drw->fonts->h + 2;
updategeom();
/* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);
@ -1578,6 +1760,10 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False);
/* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
@ -1601,6 +1787,7 @@ setup(void)
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
XDeleteProperty(dpy, root, netatom[NetClientInfo]);
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
@ -1610,6 +1797,37 @@ setup(void)
XSelectInput(dpy, root, wa.event_mask);
grabkeys();
focus(NULL);
setupepoll();
spawnbar();
}
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
@ -1665,11 +1883,29 @@ spawn(const Arg *arg)
}
}
void
spawnbar()
{
if (*altbarcmd)
system(altbarcmd);
}
void
setclienttagprop(Client *c)
{
long data[] = { (long) c->tags, (long) c->mon->num };
XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32,
PropModeReplace, (unsigned char *) data, 2);
}
void
tag(const Arg *arg)
{
Client *c;
if (selmon->sel && arg->ui & TAGMASK) {
c = selmon->sel;
selmon->sel->tags = arg->ui & TAGMASK;
setclienttagprop(c);
focus(NULL);
arrange(selmon);
}
@ -1714,9 +1950,9 @@ tile(Monitor *m)
void
togglebar(const Arg *arg)
{
selmon->showbar = !selmon->showbar;
selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, selmon->bh);
arrange(selmon);
}
@ -1744,6 +1980,7 @@ toggletag(const Arg *arg)
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
setclienttagprop(selmon->sel);
focus(NULL);
arrange(selmon);
}
@ -1753,9 +1990,33 @@ void
toggleview(const Arg *arg)
{
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
int i;
if (newtagset) {
selmon->tagset[selmon->seltags] = newtagset;
if (newtagset == ~0) {
selmon->pertag->prevtag = selmon->pertag->curtag;
selmon->pertag->curtag = 0;
}
/* test if the user did not select the same tag */
if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
selmon->pertag->prevtag = selmon->pertag->curtag;
for (i = 0; !(newtagset & 1 << i); i++) ;
selmon->pertag->curtag = i + 1;
}
/* apply settings for this view */
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];
if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
togglebar(NULL);
focus(NULL);
arrange(selmon);
}
@ -1800,10 +2061,26 @@ unmanage(Client *c, int destroyed)
arrange(m);
}
void
unmanagealtbar(Window w)
{
Monitor *m = wintomon(w);
if (!m)
return;
m->barwin = 0;
m->by = 0;
m->bh = 0;
updatebarpos(m);
arrange(m);
}
void
unmapnotify(XEvent *e)
{
Client *c;
Monitor *m;
XUnmapEvent *ev = &e->xunmap;
if ((c = wintoclient(ev->window))) {
@ -1811,12 +2088,16 @@ unmapnotify(XEvent *e)
setclientstate(c, WithdrawnState);
else
unmanage(c, 0);
}
} else if ((m = wintomon(ev->window)) && m->barwin == ev->window)
unmanagealtbar(ev->window);
}
void
updatebars(void)
{
if (usealtbar)
return;
Monitor *m;
XSetWindowAttributes wa = {
.override_redirect = True,
@ -1842,11 +2123,11 @@ updatebarpos(Monitor *m)
m->wy = m->my;
m->wh = m->mh;
if (m->showbar) {
m->wh -= bh;
m->wh -= m->bh;
m->by = m->topbar ? m->wy : m->wy + m->wh;
m->wy = m->topbar ? m->wy + bh : m->wy;
m->wy = m->topbar ? m->wy + m->bh : m->wy;
} else
m->by = -bh;
m->by = -m->bh;
}
void
@ -2012,10 +2293,18 @@ updatestatus(void)
void
updatetitle(Client *c)
{
char oldname[sizeof(c->name)];
strcpy(oldname, c->name);
if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
if (c->name[0] == '\0') /* hack to mark broken clients */
strcpy(c->name, broken);
for (Monitor *m = mons; m; m = m->next) {
if (m->sel == c && strcmp(oldname, c->name) != 0)
ipc_focused_title_change_event(m->num, c->win, oldname, c->name);
}
}
void
@ -2052,11 +2341,37 @@ updatewmhints(Client *c)
void
view(const Arg *arg)
{
int i;
unsigned int tmptag;
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
selmon->seltags ^= 1; /* toggle sel tagset */
if (arg->ui & TAGMASK)
if (arg->ui & TAGMASK) {
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
selmon->pertag->prevtag = selmon->pertag->curtag;
if (arg->ui == ~0)
selmon->pertag->curtag = 0;
else {
for (i = 0; !(arg->ui & 1 << i); i++) ;
selmon->pertag->curtag = i + 1;
}
} else {
tmptag = selmon->pertag->prevtag;
selmon->pertag->prevtag = selmon->pertag->curtag;
selmon->pertag->curtag = tmptag;
}
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];
if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
togglebar(NULL);
focus(NULL);
arrange(selmon);
}
@ -2091,6 +2406,28 @@ wintomon(Window w)
return selmon;
}
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;
}
/* There's no way to check accesses to destroyed windows, thus those cases are
* ignored (especially on UnmapNotify's). Other types of errors call Xlibs
* default error handler, which may call exit. */

1202
ipc.c Normal file

File diff suppressed because it is too large Load diff

320
ipc.h Normal file
View 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 char *tags[], 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_ */

335
patches/anybar.diff Normal file
View file

@ -0,0 +1,335 @@
From 782f63d8f858b1c14df38aaf623438d7ea2f75e1 Mon Sep 17 00:00:00 2001
From: mihirlad55 <mihirlad55@gmail.com>
Date: Mon, 10 Aug 2020 01:39:35 +0000
Subject: [PATCH] Add support for managing external status bars
This patch allows dwm to manage other status bars such as
polybar/lemonbar without them needing to set override-redirect. For
all intents and purposes, DWM treats this bar as if it were its own
and as a result helps the status bar and DWM live in harmony.
This has a few advantages
* The bar does not block fullscreen windows
* DWM makes room for the status bar, so windows do not overlap the bar
* The bar can be hidden/killed and DWM will not keep an unsightly gap
where the bar was
* DWM receives EnterNotify events when your cursor enters the bar
To use another status bar, set usealtbar to 1 in your config.h and set
altbarclass to the class name (can be found using xprop) to the class
name of your status bar. Also make sure that if your status bar will
be displayed on top, topbar is set to 1 in your config, and if it will
be displayed on bottom, topbar is set to 0. This patch does not
support bars that are not docked at the top or at the bottom of your
monitor.
The patch is developed at https://github.com/mihirlad55/dwm-anybar
---
config.def.h | 3 ++
dwm.c | 114 ++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 103 insertions(+), 14 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..d0d60aa 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,6 +5,9 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
+static const int 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 char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
diff --git a/dwm.c b/dwm.c
index 9fd0286..f149ab4 100644
--- a/dwm.c
+++ b/dwm.c
@@ -47,8 +47,8 @@
/* macros */
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
-#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
- * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
+#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->mx+(m)->mw) - MAX((x),(m)->mx)) \
+ * MAX(0, MIN((y)+(h),(m)->my+(m)->mh) - MAX((y),(m)->my)))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
@@ -116,7 +116,7 @@ struct Monitor {
float mfact;
int nmaster;
int num;
- int by; /* bar geometry */
+ int by, bh; /* bar geometry */
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
unsigned int seltags;
@@ -179,6 +179,7 @@ static void incnmaster(const Arg *arg);
static void keypress(XEvent *e);
static void killclient(const Arg *arg);
static void manage(Window w, XWindowAttributes *wa);
+static void managealtbar(Window win, XWindowAttributes *wa);
static void mappingnotify(XEvent *e);
static void maprequest(XEvent *e);
static void monocle(Monitor *m);
@@ -207,6 +208,7 @@ static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void sigchld(int unused);
static void spawn(const Arg *arg);
+static void spawnbar();
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *);
@@ -216,6 +218,7 @@ static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unfocus(Client *c, int setfocus);
static void unmanage(Client *c, int destroyed);
+static void unmanagealtbar(Window w);
static void unmapnotify(XEvent *e);
static void updatebarpos(Monitor *m);
static void updatebars(void);
@@ -230,6 +233,7 @@ static void updatewmhints(Client *c);
static void view(const Arg *arg);
static Client *wintoclient(Window w);
static Monitor *wintomon(Window w);
+static int wmclasscontains(Window win, const char *class, const char *name);
static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
@@ -505,8 +509,10 @@ cleanupmon(Monitor *mon)
for (m = mons; m && m->next != mon; m = m->next);
m->next = mon->next;
}
- XUnmapWindow(dpy, mon->barwin);
- XDestroyWindow(dpy, mon->barwin);
+ if (!usealtbar) {
+ XUnmapWindow(dpy, mon->barwin);
+ XDestroyWindow(dpy, mon->barwin);
+ }
free(mon);
}
@@ -568,7 +574,7 @@ configurenotify(XEvent *e)
for (c = m->clients; c; c = c->next)
if (c->isfullscreen)
resizeclient(c, m->mx, m->my, m->mw, m->mh);
- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, m->bh);
}
focus(NULL);
arrange(NULL);
@@ -639,6 +645,7 @@ createmon(void)
m->nmaster = nmaster;
m->showbar = showbar;
m->topbar = topbar;
+ m->bh = bh;
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
@@ -649,10 +656,13 @@ void
destroynotify(XEvent *e)
{
Client *c;
+ Monitor *m;
XDestroyWindowEvent *ev = &e->xdestroywindow;
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
+ else if ((m = wintomon(ev->window)) && m->barwin == ev->window)
+ unmanagealtbar(ev->window);
}
void
@@ -696,6 +706,9 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
+ if (usealtbar)
+ return;
+
int x, w, tw = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
@@ -1077,6 +1090,25 @@ manage(Window w, XWindowAttributes *wa)
focus(NULL);
}
+void
+managealtbar(Window win, XWindowAttributes *wa)
+{
+ Monitor *m;
+ if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height)))
+ return;
+
+ m->barwin = win;
+ m->by = wa->y;
+ bh = m->bh = wa->height;
+ updatebarpos(m);
+ arrange(m);
+ XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
+ XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height);
+ XMapWindow(dpy, win);
+ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &win, 1);
+}
+
void
mappingnotify(XEvent *e)
{
@@ -1097,7 +1129,9 @@ maprequest(XEvent *e)
return;
if (wa.override_redirect)
return;
- if (!wintoclient(ev->window))
+ if (wmclasscontains(ev->window, altbarclass, ""))
+ managealtbar(ev->window, &wa);
+ else if (!wintoclient(ev->window))
manage(ev->window, &wa);
}
@@ -1393,7 +1427,9 @@ scan(void)
if (!XGetWindowAttributes(dpy, wins[i], &wa)
|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
continue;
- if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
+ if (wmclasscontains(wins[i], altbarclass, ""))
+ managealtbar(wins[i], &wa);
+ else if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
manage(wins[i], &wa);
}
for (i = 0; i < num; i++) { /* now the transients */
@@ -1546,7 +1582,7 @@ setup(void)
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
- bh = drw->fonts->h + 2;
+ bh = usealtbar ? 0 : drw->fonts->h + 2;
updategeom();
/* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);
@@ -1595,6 +1631,7 @@ setup(void)
XSelectInput(dpy, root, wa.event_mask);
grabkeys();
focus(NULL);
+ spawnbar();
}
@@ -1653,6 +1690,13 @@ spawn(const Arg *arg)
}
}
+void
+spawnbar()
+{
+ if (*altbarcmd)
+ system(altbarcmd);
+}
+
void
tag(const Arg *arg)
{
@@ -1704,7 +1748,7 @@ togglebar(const Arg *arg)
{
selmon->showbar = !selmon->showbar;
updatebarpos(selmon);
- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
+ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, selmon->bh);
arrange(selmon);
}
@@ -1787,10 +1831,26 @@ unmanage(Client *c, int destroyed)
arrange(m);
}
+void
+unmanagealtbar(Window w)
+{
+ Monitor *m = wintomon(w);
+
+ if (!m)
+ return;
+
+ m->barwin = 0;
+ m->by = 0;
+ m->bh = 0;
+ updatebarpos(m);
+ arrange(m);
+}
+
void
unmapnotify(XEvent *e)
{
Client *c;
+ Monitor *m;
XUnmapEvent *ev = &e->xunmap;
if ((c = wintoclient(ev->window))) {
@@ -1798,12 +1858,16 @@ unmapnotify(XEvent *e)
setclientstate(c, WithdrawnState);
else
unmanage(c, 0);
- }
+ } else if ((m = wintomon(ev->window)) && m->barwin == ev->window)
+ unmanagealtbar(ev->window);
}
void
updatebars(void)
{
+ if (usealtbar)
+ return;
+
Monitor *m;
XSetWindowAttributes wa = {
.override_redirect = True,
@@ -1829,11 +1893,11 @@ updatebarpos(Monitor *m)
m->wy = m->my;
m->wh = m->mh;
if (m->showbar) {
- m->wh -= bh;
+ m->wh -= m->bh;
m->by = m->topbar ? m->wy : m->wy + m->wh;
- m->wy = m->topbar ? m->wy + bh : m->wy;
+ m->wy = m->topbar ? m->wy + m->bh : m->wy;
} else
- m->by = -bh;
+ m->by = -m->bh;
}
void
@@ -2077,6 +2141,28 @@ wintomon(Window w)
return selmon;
}
+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;
+}
+
/* There's no way to check accesses to destroyed windows, thus those cases are
* ignored (especially on UnmapNotify's). Other types of errors call Xlibs
* default error handler, which may call exit. */
--
2.28.0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,118 @@
From 713fa8650f5a20006451ebcccf57a4512e83bae8 Mon Sep 17 00:00:00 2001
From: Arda Atci <arda@phytech.io>
Date: Wed, 18 May 2022 17:23:16 +0300
Subject: [PATCH] preserve clients on old tags when renewing dwm
By default, when dwm is recompiled-restarted all clients will
lose it's current tag and collapse to first tag. This patch preserves
clients on old tags, however note that layout order is not preserved.
---
dwm.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/dwm.c b/dwm.c
index a96f33c..a12e0bd 100644
--- a/dwm.c
+++ b/dwm.c
@@ -62,7 +62,7 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+ NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@@ -198,6 +198,7 @@ static void scan(void);
static int sendevent(Client *c, Atom proto);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
+static void setclienttagprop(Client *c);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
@@ -1060,6 +1061,26 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
+ {
+ int format;
+ unsigned long *data, n, extra;
+ Monitor *m;
+ Atom atom;
+ if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL,
+ &atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) {
+ c->tags = *data;
+ for (m = mons; m; m = m->next) {
+ if (m->num == *(data+1)) {
+ c->mon = m;
+ break;
+ }
+ }
+ }
+ if (n > 0)
+ XFree(data);
+ }
+ setclienttagprop(c);
+
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@@ -1423,6 +1444,7 @@ sendmon(Client *c, Monitor *m)
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c);
attachstack(c);
+ setclienttagprop(c);
focus(NULL);
arrange(NULL);
}
@@ -1566,6 +1588,7 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False);
/* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
@@ -1589,6 +1612,7 @@ setup(void)
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
+ XDeleteProperty(dpy, root, netatom[NetClientInfo]);
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
@@ -1656,11 +1680,22 @@ spawn(const Arg *arg)
}
}
+void
+setclienttagprop(Client *c)
+{
+ long data[] = { (long) c->tags, (long) c->mon->num };
+ XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *) data, 2);
+}
+
void
tag(const Arg *arg)
{
+ Client *c;
if (selmon->sel && arg->ui & TAGMASK) {
+ c = selmon->sel;
selmon->sel->tags = arg->ui & TAGMASK;
+ setclienttagprop(c);
focus(NULL);
arrange(selmon);
}
@@ -1735,6 +1770,7 @@ toggletag(const Arg *arg)
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
+ setclienttagprop(selmon->sel);
focus(NULL);
arrange(selmon);
}
--
2.36.1

101
patches/selfrestart.diff Normal file
View file

@ -0,0 +1,101 @@
# HG changeset patch
# User Barbu Paul - Gheorghe <barbu.paul.gheorghe@gmail.com>
# Date 1354650884 -7200
# Node ID 6c472a21a5887c5295a331c48c4da188ec2c8413
# Parent aaab44133a6830c9a00263731d098c01cc1d6fb5
selfrestart now magically locates the current dwm (no need to hardcode a path)
diff -r aaab44133a68 -r 6c472a21a588 config.def.h
--- a/config.def.h Tue Dec 04 21:54:44 2012 +0200
+++ b/config.def.h Tue Dec 04 21:54:44 2012 +0200
@@ -54,6 +54,8 @@
static const char *termcmd[] = { "urxvtc", NULL };
static const char *filemancmd[] = { "thunar", NULL };
+#include "selfrestart.c"
+
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_r, spawn, {.v = dmenucmd } },
@@ -89,6 +91,7 @@
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_r, self_restart, {0} },
{ MODKEY|ShiftMask, XK_q, quit, {0} },
};
@@ -108,4 +111,3 @@
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
-
diff -r aaab44133a68 -r 6c472a21a588 selfrestart.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/selfrestart.c Tue Dec 04 21:54:44 2012 +0200
@@ -0,0 +1,65 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * Magically finds the current's executable path
+ *
+ * I'm doing the do{}while(); trick because Linux (what I'm running) is not
+ * POSIX compilant and so lstat() cannot be trusted on /proc entries
+ *
+ * @return char* the path of the current executable
+ */
+char *get_dwm_path(){
+ struct stat s;
+ int r, length, rate = 42;
+ char *path = NULL;
+
+ if(lstat("/proc/self/exe", &s) == -1){
+ perror("lstat:");
+ return NULL;
+ }
+
+ length = s.st_size + 1 - rate;
+
+ do{
+ length+=rate;
+
+ free(path);
+ path = malloc(sizeof(char) * length);
+
+ if(path == NULL){
+ perror("malloc:");
+ return NULL;
+ }
+
+ r = readlink("/proc/self/exe", path, length);
+
+ if(r == -1){
+ perror("readlink:");
+ return NULL;
+ }
+ }while(r >= length);
+
+ path[r] = '\0';
+
+ return path;
+}
+
+/**
+ * self-restart
+ *
+ * Initially inspired by: Yu-Jie Lin
+ * https://sites.google.com/site/yjlnotes/notes/dwm
+ */
+void self_restart(const Arg *arg) {
+ char *const argv[] = {get_dwm_path(), NULL};
+
+ if(argv[0] == NULL){
+ return;
+ }
+
+ execv(argv[0], argv);
+}

34
selfrestart.c Normal file
View file

@ -0,0 +1,34 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
/**
* Magically finds the current's executable path
*
* I'm doing the do{}while(); trick because Linux (what I'm running) is not
* POSIX compilant and so lstat() cannot be trusted on /proc entries
*
* @return char* the path of the current executable
*/
char *get_dwm_path(){
// Sorry non nixos users
return "/run/current-system/sw/bin/dwm";
}
/**
* self-restart
*
* Initially inspired by: Yu-Jie Lin
* https://sites.google.com/site/yjlnotes/notes/dwm
*/
void self_restart(const Arg *arg) {
char *const argv[] = {get_dwm_path(), NULL};
if(argv[0] == NULL){
return;
}
execv(argv[0], argv);
}

135
util.c
View file

@ -3,6 +3,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include "util.h"
@ -25,6 +27,139 @@ die(const char *fmt, ...)
exit(1);
}
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;
}
void *
ecalloc(size_t nmemb, size_t size)
{

10
util.h
View file

@ -5,5 +5,15 @@
#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);
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);

351
yajl_dumps.c Normal file
View file

@ -0,0 +1,351 @@
#include "yajl_dumps.h"
#include <stdint.h>
int
dump_tag(yajl_gen gen, const char *name, const int tag_mask)
{
// 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, const char *tags[], int tags_len)
{
// clang-format off
YARR(
for (int i = 0; i < tags_len; i++)
dump_tag(gen, tags[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]);
)
)
YSTR("bar"); YMAP(
YSTR("y"); YINT(mon->by);
YSTR("is_shown"); YBOOL(mon->showbar);
YSTR("is_top"); YBOOL(mon->topbar);
YSTR("window_id"); YINT(mon->barwin);
)
)
// 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;
}

65
yajl_dumps.h Normal file
View file

@ -0,0 +1,65 @@
#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, const char *tags[], 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_