@ -36,10 +36,13 @@
# include <X11/Xlib.h>
# include <X11/Xproto.h>
# include <X11/Xutil.h>
# include <X11/Xresource.h>
# ifdef XINERAMA
# include <X11/extensions/Xinerama.h>
# endif /* XINERAMA */
# include <X11/Xft/Xft.h>
# include <X11/Xlib-xcb.h>
# include <xcb/res.h>
# include "drw.h"
# include "util.h"
@ -64,8 +67,8 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen , NetActiveWindow , NetWMWindowType ,
NetWMWindowTypeDialog , NetClientList , NetLast } ; /* EWMH atoms */
enum { WMProtocols , WMDelete , WMState , WMTakeFocus , WMLast } ; /* default atoms */
enum { ClkTagBar , ClkLtSymbol , ClkStatusText , Clk WinTitle ,
Clk ClientWin, Clk RootWin, ClkLast } ; /* clicks */
enum { ClkTagBar , ClkLtSymbol , ClkStatusText , Clk Client Win,
Clk RootWin, ClkLast } ; /* clicks */
typedef union {
int i ;
@ -92,9 +95,11 @@ struct Client {
int basew , baseh , incw , inch , maxw , maxh , minw , minh ;
int bw , oldbw ;
unsigned int tags ;
int isfixed , isfloating , isurgent , neverfocus , oldstate , isfullscreen ;
int isfixed , iscentered , isfloating , isurgent , neverfocus , oldstate , isfullscreen , isterminal , noswallow ;
pid_t pid ;
Client * next ;
Client * snext ;
Client * swallowing ;
Monitor * mon ;
Window win ;
} ;
@ -119,6 +124,7 @@ struct Monitor {
int by ; /* bar geometry */
int mx , my , mw , mh ; /* screen size */
int wx , wy , ww , wh ; /* window area */
int gappx ; /* gaps between windows */
unsigned int seltags ;
unsigned int sellt ;
unsigned int tagset [ 2 ] ;
@ -137,10 +143,26 @@ typedef struct {
const char * instance ;
const char * title ;
unsigned int tags ;
int iscentered ;
int isfloating ;
int isterminal ;
int noswallow ;
int monitor ;
} Rule ;
/* Xresources preferences */
enum resource_type {
STRING = 0 ,
INTEGER = 1 ,
FLOAT = 2
} ;
typedef struct {
char * name ;
enum resource_type type ;
void * dst ;
} ResourcePref ;
/* function declarations */
static void applyrules ( Client * c ) ;
static int applysizehints ( Client * c , int * x , int * y , int * w , int * h , int interact ) ;
@ -200,6 +222,7 @@ static void sendmon(Client *c, Monitor *m);
static void setclientstate ( Client * c , long state ) ;
static void setfocus ( Client * c ) ;
static void setfullscreen ( Client * c , int fullscreen ) ;
static void setgaps ( const Arg * arg ) ;
static void setlayout ( const Arg * arg ) ;
static void setmfact ( const Arg * arg ) ;
static void setup ( void ) ;
@ -234,10 +257,19 @@ static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy ( Display * dpy , XErrorEvent * ee ) ;
static int xerrorstart ( Display * dpy , XErrorEvent * ee ) ;
static void zoom ( const Arg * arg ) ;
static void load_xresources ( void ) ;
static void resource_load ( XrmDatabase db , char * name , enum resource_type rtype , void * dst ) ;
static pid_t getparentprocess ( pid_t p ) ;
static int isdescprocess ( pid_t p , pid_t c ) ;
static Client * swallowingclient ( Window w ) ;
static Client * termforwin ( const Client * c ) ;
static pid_t winpid ( Window w ) ;
/* variables */
static const char broken [ ] = " broken " ;
static char stext [ 256 ] ;
static int scanner ;
static int screen ;
static int sw , sh ; /* X display screen geometry width, height */
static int bh , blw = 0 ; /* bar geometry */
@ -269,6 +301,8 @@ static Drw *drw;
static Monitor * mons , * selmon ;
static Window root , wmcheckwin ;
static xcb_connection_t * xcon ;
/* configuration, allows nested code to access above variables */
# include "config.h"
@ -286,6 +320,8 @@ applyrules(Client *c)
XClassHint ch = { NULL , NULL } ;
/* rule matching */
c - > noswallow = - 1 ;
c - > iscentered = 0 ;
c - > isfloating = 0 ;
c - > tags = 0 ;
XGetClassHint ( dpy , c - > win , & ch ) ;
@ -298,6 +334,9 @@ applyrules(Client *c)
& & ( ! r - > class | | strstr ( class , r - > class ) )
& & ( ! r - > instance | | strstr ( instance , r - > instance ) ) )
{
c - > iscentered = r - > iscentered ;
c - > isterminal = r - > isterminal ;
c - > noswallow = r - > noswallow ;
c - > isfloating = r - > isfloating ;
c - > tags | = r - > tags ;
for ( m = mons ; m & & m - > num ! = r - > monitor ; m = m - > next ) ;
@ -414,10 +453,65 @@ attachstack(Client *c)
c - > mon - > stack = c ;
}
void
swallow ( Client * p , Client * c )
{
Client * s ;
if ( c - > noswallow > 0 | | c - > isterminal )
return ;
if ( c - > noswallow < 0 & & ! swallowfloating & & c - > isfloating )
return ;
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 ;
XMoveResizeWindow ( dpy , p - > win , s - > x , s - > y , s - > w , s - > h ) ;
arrange ( p - > mon ) ;
configure ( p ) ;
updateclientlist ( ) ;
}
void
unswallow ( Client * c )
{
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 ) ;
XMoveResizeWindow ( dpy , c - > win , c - > x , c - > y , c - > w , c - > h ) ;
setclientstate ( c , NormalState ) ;
focus ( NULL ) ;
arrange ( c - > mon ) ;
}
void
buttonpress ( XEvent * e )
{
unsigned int i , x , click ;
unsigned int i , x , click , occ = 0 ;
Arg arg = { 0 } ;
Client * c ;
Monitor * m ;
@ -432,18 +526,21 @@ buttonpress(XEvent *e)
}
if ( ev - > window = = selmon - > barwin ) {
i = x = 0 ;
do
for ( c = m - > clients ; c ; c = c - > next )
occ | = c - > tags = = 255 ? 0 : c - > tags ;
do {
/* do not reserve space for vacant tags */
if ( ! ( occ & 1 < < i | | m - > tagset [ m - > seltags ] & 1 < < i ) )
continue ;
x + = TEXTW ( tags [ i ] ) ;
while ( ev - > x > = x & & + + i < LENGTH ( tags ) ) ;
} while ( ev - > x > = x & & + + i < LENGTH ( tags ) ) ;
if ( i < LENGTH ( tags ) ) {
click = ClkTagBar ;
arg . ui = 1 < < i ;
} else if ( ev - > x < x + blw )
click = ClkLtSymbol ;
else if ( ev - > x > selmon - > ww - TEXTW ( stext ) )
click = ClkStatusText ;
else
click = Clk WinTitle ;
click = ClkStatusText ;
} else if ( ( c = wintoclient ( ev - > window ) ) ) {
focus ( c ) ;
restack ( selmon ) ;
@ -639,6 +736,7 @@ createmon(void)
m - > nmaster = nmaster ;
m - > showbar = showbar ;
m - > topbar = topbar ;
m - > gappx = gappx ;
m - > lt [ 0 ] = & layouts [ 0 ] ;
m - > lt [ 1 ] = & layouts [ 1 % LENGTH ( layouts ) ] ;
strncpy ( m - > ltsymbol , layouts [ 0 ] . symbol , sizeof m - > ltsymbol ) ;
@ -653,6 +751,9 @@ destroynotify(XEvent *e)
if ( ( c = wintoclient ( ev - > window ) ) )
unmanage ( c , 1 ) ;
else if ( ( c = swallowingclient ( ev - > window ) ) )
unmanage ( c - > swallowing , 1 ) ;
}
void
@ -697,8 +798,6 @@ void
drawbar ( Monitor * m )
{
int x , w , tw = 0 ;
int boxs = drw - > fonts - > h / 9 ;
int boxw = drw - > fonts - > h / 6 + 2 ;
unsigned int i , occ = 0 , urg = 0 ;
Client * c ;
@ -710,19 +809,19 @@ drawbar(Monitor *m)
}
for ( c = m - > clients ; c ; c = c - > next ) {
occ | = c - > tags ;
occ | = c - > tags = = 255 ? 0 : c - > tags ;
if ( c - > isurgent )
urg | = c - > tags ;
}
x = 0 ;
for ( i = 0 ; i < LENGTH ( tags ) ; i + + ) {
/* do not draw vacant tags */
if ( ! ( occ & 1 < < i | | m - > tagset [ m - > seltags ] & 1 < < i ) )
continue ;
w = TEXTW ( tags [ i ] ) ;
drw_setscheme ( drw , scheme [ m - > tagset [ m - > seltags ] & 1 < < i ? SchemeSel : SchemeNorm ] ) ;
drw_text ( drw , x , 0 , w , bh , lrpad / 2 , tags [ i ] , urg & 1 < < i ) ;
if ( occ & 1 < < i )
drw_rect ( drw , x + boxs , boxs , boxw , boxw ,
m = = selmon & & selmon - > sel & & selmon - > sel - > tags & 1 < < i ,
urg & 1 < < i ) ;
x + = w ;
}
w = blw = TEXTW ( m - > ltsymbol ) ;
@ -730,15 +829,8 @@ drawbar(Monitor *m)
x = drw_text ( drw , x , 0 , w , bh , lrpad / 2 , m - > ltsymbol , 0 ) ;
if ( ( w = m - > ww - tw - x ) > bh ) {
if ( m - > sel ) {
drw_setscheme ( drw , scheme [ m = = selmon ? SchemeSel : SchemeNorm ] ) ;
drw_text ( drw , x , 0 , w , bh , lrpad / 2 , m - > sel - > name , 0 ) ;
if ( m - > sel - > isfloating )
drw_rect ( drw , x + boxs , boxs , boxw , boxw , m - > sel - > isfixed , 0 ) ;
} else {
drw_setscheme ( drw , scheme [ SchemeNorm ] ) ;
drw_rect ( drw , x , 0 , w , bh , 1 , 1 ) ;
}
drw_setscheme ( drw , scheme [ SchemeNorm ] ) ;
drw_rect ( drw , x , 0 , w , bh , 1 , 1 ) ;
}
drw_map ( drw , m - > barwin , 0 , 0 , m - > ww , bh ) ;
}
@ -1018,12 +1110,13 @@ killclient(const Arg *arg)
void
manage ( Window w , XWindowAttributes * wa )
{
Client * c , * t = NULL ;
Client * c , * t = NULL , * term = NULL ;
Window trans = None ;
XWindowChanges wc ;
c = ecalloc ( 1 , sizeof ( Client ) ) ;
c - > win = w ;
c - > pid = winpid ( w ) ;
/* geometry */
c - > x = c - > oldx = wa - > x ;
c - > y = c - > oldy = wa - > y ;
@ -1038,6 +1131,7 @@ manage(Window w, XWindowAttributes *wa)
} else {
c - > mon = selmon ;
applyrules ( c ) ;
term = termforwin ( c ) ;
}
if ( c - > x + WIDTH ( c ) > c - > mon - > mx + c - > mon - > mw )
@ -1057,6 +1151,10 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype ( c ) ;
updatesizehints ( c ) ;
updatewmhints ( c ) ;
if ( c - > iscentered ) {
c - > x = c - > mon - > mx + ( c - > mon - > mw - WIDTH ( c ) ) / 2 ;
c - > y = c - > mon - > my + ( c - > mon - > mh - HEIGHT ( c ) ) / 2 ;
}
XSelectInput ( dpy , w , EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask ) ;
grabbuttons ( c , 0 ) ;
if ( ! c - > isfloating )
@ -1074,6 +1172,8 @@ manage(Window w, XWindowAttributes *wa)
c - > mon - > sel = c ;
arrange ( c - > mon ) ;
XMapWindow ( dpy , c - > win ) ;
if ( term )
swallow ( term , c ) ;
focus ( NULL ) ;
}
@ -1236,11 +1336,8 @@ propertynotify(XEvent *e)
drawbars ( ) ;
break ;
}
if ( ev - > atom = = XA_WM_NAME | | ev - > atom = = netatom [ NetWMName ] ) {
if ( ev - > atom = = XA_WM_NAME | | ev - > atom = = netatom [ NetWMName ] )
updatetitle ( c ) ;
if ( c = = c - > mon - > sel )
drawbar ( c - > mon ) ;
}
if ( ev - > atom = = netatom [ NetWMWindowType ] )
updatewindowtype ( c ) ;
}
@ -1384,7 +1481,9 @@ run(void)
void
scan ( void )
{
scanner = 1 ;
unsigned int i , num ;
char swin [ 256 ] ;
Window d1 , d2 , * wins = NULL ;
XWindowAttributes wa ;
@ -1395,6 +1494,8 @@ scan(void)
continue ;
if ( wa . map_state = = IsViewable | | getstate ( wins [ i ] ) = = IconicState )
manage ( wins [ i ] , & wa ) ;
else if ( gettextprop ( wins [ i ] , netatom [ NetClientList ] , swin , sizeof swin ) )
manage ( wins [ i ] , & wa ) ;
}
for ( i = 0 ; i < num ; i + + ) { /* now the transients */
if ( ! XGetWindowAttributes ( dpy , wins [ i ] , & wa ) )
@ -1406,6 +1507,7 @@ scan(void)
if ( wins )
XFree ( wins ) ;
}
scanner = 0 ;
}
void
@ -1498,6 +1600,16 @@ setfullscreen(Client *c, int fullscreen)
}
}
void
setgaps ( const Arg * arg )
{
if ( ( arg - > i = = 0 ) | | ( selmon - > gappx + arg - > i < 0 ) )
selmon - > gappx = 0 ;
else
selmon - > gappx + = arg - > i ;
arrange ( selmon ) ;
}
void
setlayout ( const Arg * arg )
{
@ -1684,18 +1796,18 @@ tile(Monitor *m)
if ( n > m - > nmaster )
mw = m - > nmaster ? m - > ww * m - > mfact : 0 ;
else
mw = m - > ww ;
for ( i = my = ty = 0 , c = nexttiled ( m - > clients ) ; c ; c = nexttiled ( c - > next ) , i + + )
mw = m - > ww - m - > gappx ;
for ( i = 0 , my = ty = m - > gappx , c = nexttiled ( m - > clients ) ; c ; c = nexttiled ( c - > next ) , i + + )
if ( i < m - > nmaster ) {
h = ( m - > wh - my ) / ( MIN ( n , m - > nmaster ) - i ) ;
resize ( c , m - > wx , m - > wy + my , mw - ( 2 * c - > bw ) , h - ( 2 * c - > bw ) , 0 ) ;
if ( my + HEIGHT ( c ) < m - > wh )
my + = HEIGHT ( c ) ;
h = ( m - > wh - my ) / ( MIN ( n , m - > nmaster ) - i ) - m - > gappx ;
resize ( c , m - > wx + m - > gappx , m - > wy + my , mw - ( 2 * c - > bw ) - m - > gappx , h - ( 2 * c - > bw ) , 0 ) ;
if ( my + HEIGHT ( c ) + m - > gappx < m - > wh )
my + = HEIGHT ( c ) + m - > gappx ;
} else {
h = ( m - > wh - ty ) / ( n - i ) ;
resize ( c , m - > wx + mw , m - > wy + ty , m - > ww - mw - ( 2 * c - > bw ) , h - ( 2 * c - > bw ) , 0 ) ;
if ( ty + HEIGHT ( c ) < m - > wh )
ty + = HEIGHT ( c ) ;
h = ( m - > wh - ty ) / ( n - i ) - m - > gappx ;
resize ( c , m - > wx + mw + m - > gappx , m - > wy + ty , m - > ww - mw - ( 2 * c - > bw ) - 2 * m - > gappx , h - ( 2 * c - > bw ) , 0 ) ;
if ( ty + HEIGHT ( c ) + m - > gappx < m - > wh )
ty + = HEIGHT ( c ) + m - > gappx ;
}
}
@ -1768,6 +1880,20 @@ unmanage(Client *c, int destroyed)
Monitor * m = c - > mon ;
XWindowChanges wc ;
if ( c - > swallowing ) {
unswallow ( c ) ;
return ;
}
Client * s = swallowingclient ( c - > win ) ;
if ( s ) {
free ( s - > swallowing ) ;
s - > swallowing = NULL ;
arrange ( m ) ;
focus ( NULL ) ;
return ;
}
detach ( c ) ;
detachstack ( c ) ;
if ( ! destroyed ) {
@ -1782,9 +1908,12 @@ unmanage(Client *c, int destroyed)
XUngrabServer ( dpy ) ;
}
free ( c ) ;
focus ( NULL ) ;
updateclientlist ( ) ;
arrange ( m ) ;
if ( ! s ) {
arrange ( m ) ;
focus ( NULL ) ;
updateclientlist ( ) ;
}
}
void
@ -2012,8 +2141,10 @@ updatewindowtype(Client *c)
if ( state = = netatom [ NetWMFullscreen ] )
setfullscreen ( c , 1 ) ;
if ( wtype = = netatom [ NetWMWindowTypeDialog ] )
if ( wtype = = netatom [ NetWMWindowTypeDialog ] ) {
c - > iscentered = 1 ;
c - > isfloating = 1 ;
}
}
void
@ -2047,6 +2178,110 @@ view(const Arg *arg)
arrange ( selmon ) ;
}
pid_t
winpid ( Window w )
{
pid_t result = 0 ;
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 ;
return result ;
}
pid_t
getparentprocess ( pid_t p )
{
unsigned int v = 0 ;
# if defined(__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 ) ;
# elif defined(__FreeBSD__)
struct kinfo_proc * proc = kinfo_getproc ( p ) ;
if ( ! proc )
return ( pid_t ) 0 ;
v = proc - > ki_ppid ;
free ( proc ) ;
# endif
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 ;
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 ;
}
Client *
wintoclient ( Window w )
{
@ -2127,6 +2362,60 @@ zoom(const Arg *arg)
pop ( c ) ;
}
void
resource_load ( XrmDatabase db , char * name , enum resource_type rtype , void * dst )
{
char * sdst = NULL ;
int * idst = NULL ;
float * fdst = NULL ;
sdst = dst ;
idst = dst ;
fdst = dst ;
char fullname [ 256 ] ;
char * type ;
XrmValue ret ;
snprintf ( fullname , sizeof ( fullname ) , " %s.%s " , " dwm " , name ) ;
fullname [ sizeof ( fullname ) - 1 ] = ' \0 ' ;
XrmGetResource ( db , fullname , " * " , & type , & ret ) ;
if ( ! ( ret . addr = = NULL | | strncmp ( " String " , type , 64 ) ) )
{
switch ( rtype ) {
case STRING :
strcpy ( sdst , ret . addr ) ;
break ;
case INTEGER :
* idst = strtoul ( ret . addr , NULL , 10 ) ;
break ;
case FLOAT :
* fdst = strtof ( ret . addr , NULL ) ;
break ;
}
}
}
void
load_xresources ( void )
{
Display * display ;
char * resm ;
XrmDatabase db ;
ResourcePref * p ;
display = XOpenDisplay ( NULL ) ;
resm = XResourceManagerString ( display ) ;
if ( ! resm )
return ;
db = XrmGetStringDatabase ( resm ) ;
for ( p = resources ; p < resources + LENGTH ( resources ) ; p + + )
resource_load ( db , p - > name , p - > type , p - > dst ) ;
XCloseDisplay ( display ) ;
}
int
main ( int argc , char * argv [ ] )
{
@ -2138,7 +2427,11 @@ main(int argc, char *argv[])
fputs ( " warning: no locale support \n " , stderr ) ;
if ( ! ( dpy = XOpenDisplay ( NULL ) ) )
die ( " dwm: cannot open display " ) ;
if ( ! ( xcon = XGetXCBConnection ( dpy ) ) )
die ( " dwm: cannot get xcb connection \n " ) ;
checkotherwm ( ) ;
XrmInitialize ( ) ;
load_xresources ( ) ;
setup ( ) ;
# ifdef __OpenBSD__
if ( pledge ( " stdio rpath proc exec " , NULL ) = = - 1 )