first import

This commit is contained in:
Julien Danjou 2007-09-05 20:15:00 +02:00
commit f7173bd79d
31 changed files with 4088 additions and 0 deletions

26
LICENSE Normal file
View File

@ -0,0 +1,26 @@
MIT/X Consortium License
© 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2007 Jukka Salmi <jukka at salmi dot ch>
© 2007 Premysl Hruby <dfenze at gmail dot com>
© 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
© 2007 Julien Danjou <julien at danjou dot info>
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.

62
Makefile Normal file
View File

@ -0,0 +1,62 @@
# jdwm - Julien's dynamic window manager
# © 2007 Julien Danjou
# © 2006-2007 Anselm R. Garbe, Sander van Dijk
include config.mk
SRC = client.c draw.c event.c layout.c jdwm.c tag.c util.c config.c
OBJ = ${SRC:.c=.o} ${LAYOUTS:.c=.o}
all: options jdwm
options:
@echo jdwm build options:
@echo "LAYOUTS = ${LAYOUTS}"
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o:
@echo -e \\t\(CC\) $<
@${CC} -c ${CFLAGS} $< -o $@
${OBJ}: jdwm.h config.mk
jdwm: ${OBJ}
@echo -e \\t\(CC\) ${OBJ} -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
clean:
@echo cleaning
@rm -f jdwm ${OBJ} jdwm-${VERSION}.tar.gz
dist: clean
@echo creating dist tarball
@mkdir -p jdwm-${VERSION}
@cp -R LICENSE Makefile README config.*.h config.mk \
jdwm.1 jdwm.h grid.h tile.h mem.h ${SRC} ${LAYOUTS} jdwm-${VERSION}
@tar -cf jdwm-${VERSION}.tar jdwm-${VERSION}
@gzip jdwm-${VERSION}.tar
@rm -rf jdwm-${VERSION}
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f jdwm ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/jdwm
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" < jdwm.1 > ${DESTDIR}${MANPREFIX}/man1/jdwm.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/jdwm.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/jdwm
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/jdwm.1
doc:
@echo generating documentation
@doxygen jdwm.doxygen
.PHONY: all options clean dist install uninstall doc

47
README Normal file
View File

@ -0,0 +1,47 @@
jdwm - Julien's dynamic window manager
============================
jdwm is an extremely fast, small, and dynamic window manager for X.
It's based on dwm.
Requirements
------------
In order to build jdwm you need the Xlib header files.
Installation
------------
Edit config.mk to match your local setup (jdwm is installed into
the /usr/local namespace by default).
Afterwards enter the following command to build and install jdwm (if
necessary as root):
make clean install
Running jdwm
-----------
Add the following line to your .xinitrc to start jdwm using startx
or to .xsession to start jdwm using gdm/kdm/xdm...:
exec jdwm
In order to connect jdwm to a specific display, make sure that
the DISPLAY environment variable is set correctly, e.g.:
DISPLAY=foo.bar:1 exec jdwm
(This will start jdwm 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 true
do
echo `date` `uptime | sed 's/.*,//'`
sleep 1
done | jdwm
Configuration
-------------
The configuration of jdwm is done by creating a custom config.h
and (re)compiling the source code.

657
client.c Normal file
View File

@ -0,0 +1,657 @@
/* See LICENSE file for copyright and license details. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "jdwm.h"
#include "util.h"
#include "layout.h"
#include "tag.h"
#include "layouts/floating.h"
/* extern */
extern int sx, sy, sw, sh; /* screen geometry */
extern int wax, way, wah, waw; /* windowarea geometry */
extern Client *clients, *sel, *stack; /* global client list and stack */
extern Bool selscreen;
extern Atom jdwmprops, wmatom[WMLast], netatom[NetLast];
/* static */
static char prop[128];
/** Attach client stack to clients stacks
* \param c the client
*/
static inline void
attachstack(Client * c)
{
c->snext = stack;
stack = c;
}
/** Detach client from stack
* \param c the client
*/
static inline void
detachstack(Client * c)
{
Client **tc;
for(tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
*tc = c->snext;
}
/** Grab or ungrab buttons when a client is focused
* \param c client
* \param focused True if client is focused
* \param modkey Mod key mask
* \param numlockmask Numlock mask
*/
static void
grabbuttons(Client * c, Bool focused, KeySym modkey, unsigned int numlockmask)
{
XUngrabButton(c->display, AnyButton, AnyModifier, c->win);
if(focused)
{
XGrabButton(c->display, Button1, modkey, c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button1, modkey | LockMask, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button1, modkey | numlockmask, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button1, modkey | numlockmask | LockMask,
c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button2, modkey, c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button2, modkey | LockMask, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button2, modkey | numlockmask, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button2, modkey | numlockmask | LockMask,
c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button3, modkey, c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button3, modkey | LockMask, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button3, modkey | numlockmask, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button3, modkey | numlockmask | LockMask,
c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
XUngrabButton(c->display, AnyButton, AnyModifier, DefaultRootWindow(c->display));
}
else
{
XGrabButton(c->display, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button4, NoSymbol, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button4, LockMask, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button4, numlockmask, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button4, numlockmask | LockMask, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button5, NoSymbol, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button5, LockMask, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button5, numlockmask, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
XGrabButton(c->display, Button5, numlockmask | LockMask, DefaultRootWindow(c->display), False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
}
}
/** XXX: No idea
* \param c the client
* \return True if atom has WMDelete
*/
static Bool
isprotodel(Client * c)
{
int i, n;
Atom *protocols;
Bool ret = False;
if(XGetWMProtocols(c->display, c->win, &protocols, &n))
{
for(i = 0; !ret && i < n; i++)
if(protocols[i] == wmatom[WMDelete])
ret = True;
XFree(protocols);
}
return ret;
}
/** XXX: No idea
* \param c the client
* \param state no idea
*/
static void
setclientstate(Client * c, long state)
{
long data[] = { state, None };
XChangeProperty(c->display, c->win, wmatom[WMState], wmatom[WMState], 32,
PropModeReplace, (unsigned char *) data, 2);
}
/* extern */
/** Attach client to the beginning of the clients stack
* \param c the client
*/
inline void
attach(Client * c)
{
if(clients)
clients->prev = c;
c->next = clients;
clients = c;
}
inline void
updatetitle(Client * c)
{
if(!gettextprop(c->display, c->win, netatom[NetWMName], c->name, sizeof c->name))
gettextprop(c->display, c->win, wmatom[WMName], c->name, sizeof c->name);
}
/** Ban client
* \param c the client
*/
void
ban(Client * c)
{
if(c->isbanned)
return;
XUnmapWindow(c->display, c->win);
setclientstate(c, IconicState);
c->isbanned = True;
c->unmapped++;
}
/** Configure client
* \param c the client
*/
void
configure(Client * c)
{
XConfigureEvent ce;
ce.type = ConfigureNotify;
ce.display = c->display;
ce.event = c->win;
ce.window = c->win;
ce.x = c->x;
ce.y = c->y;
ce.width = c->w;
ce.height = c->h;
ce.border_width = c->border;
ce.above = None;
ce.override_redirect = False;
XSendEvent(c->display, c->win, False, StructureNotifyMask, (XEvent *) & ce);
}
void
detach(Client * c)
{
if(c->prev)
c->prev->next = c->next;
if(c->next)
c->next->prev = c->prev;
if(c == clients)
clients = c->next;
c->next = c->prev = NULL;
}
/** Give focus to client, or to first client if c is NULL
* \param disp Display ref
* \param drawcontext drawcontext ref
* \param c client
* \param jdwmconf jdwm config
*/
void
focus(Display *disp, DC *drawcontext, Client * c, jdwm_config *jdwmconf)
{
/* if c is NULL or invisible, take next client in the stack */
if((!c && selscreen) || (c && !isvisible(c, jdwmconf->ntags)))
for(c = stack; c && !isvisible(c, jdwmconf->ntags); c = c->snext);
/* if a client was selected but it's not the current client, unfocus it */
if(sel && sel != c)
{
grabbuttons(sel, False, jdwmconf->modkey, jdwmconf->numlockmask);
XSetWindowBorder(sel->display, sel->win, drawcontext->norm[ColBorder]);
setclienttrans(sel, jdwmconf->opacity_unfocused, 0);
}
if(c)
{
detachstack(c);
attachstack(c);
grabbuttons(c, True, jdwmconf->modkey, jdwmconf->numlockmask);
}
if(!selscreen)
return;
sel = c;
drawstatus(disp, jdwmconf);
if(sel)
{
XSetWindowBorder(sel->display, sel->win, drawcontext->sel[ColBorder]);
XSetInputFocus(sel->display, sel->win, RevertToPointerRoot, CurrentTime);
for(c = stack; c; c = c->snext)
if(c != sel)
setclienttrans(c, jdwmconf->opacity_unfocused, 0);
setclienttrans(sel, -1, 0);
}
else
XSetInputFocus(disp, DefaultRootWindow(disp), RevertToPointerRoot, CurrentTime);
}
/** Kill selected client
* \param disp Display ref
* \param jdwmconf jdwm config
* \param arg unused
* \ingroup ui_callback
*/
void
uicb_killclient(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf __attribute__ ((unused)),
const char *arg __attribute__ ((unused)))
{
XEvent ev;
if(!sel)
return;
if(isprotodel(sel))
{
ev.type = ClientMessage;
ev.xclient.window = sel->win;
ev.xclient.message_type = wmatom[WMProtocols];
ev.xclient.format = 32;
ev.xclient.data.l[0] = wmatom[WMDelete];
ev.xclient.data.l[1] = CurrentTime;
XSendEvent(sel->display, sel->win, False, NoEventMask, &ev);
}
else
XKillClient(sel->display, sel->win);
}
static Bool
loadprops(Client * c, int ntags)
{
int i;
Bool result = False;
if(gettextprop(c->display, c->win, jdwmprops, prop, sizeof(prop)))
{
for(i = 0; i < ntags && i < ssizeof(prop) - 1 && prop[i] != '\0'; i++)
if((c->tags[i] = prop[i] == '1'))
result = True;
if(i < ssizeof(prop) - 1 && prop[i] != '\0')
c->isfloating = prop[i] == '1';
}
return result;
}
void
manage(Display * disp, DC *drawcontext, Window w, XWindowAttributes * wa, jdwm_config *jdwmconf)
{
int i;
Client *c, *t = NULL;
Window trans;
Status rettrans;
XWindowChanges wc;
c = p_new(Client, 1);
c->tags = p_new(Bool, jdwmconf->ntags);
c->win = w;
c->ftview = True;
c->x = c->rw = wa->x;
c->y = c->ry = wa->y;
c->w = c->rw = wa->width;
c->h = c->rh = wa->height;
c->oldborder = wa->border_width;
c->display = disp;
if(c->w == sw && c->h == sh)
{
c->x = sx;
c->y = sy;
c->border = wa->border_width;
}
else
{
if(c->x + c->w + 2 * c->border > wax + waw)
c->x = c->rx = wax + waw - c->w - 2 * c->border;
if(c->y + c->h + 2 * c->border > way + wah)
c->y = c->ry = way + wah - c->h - 2 * c->border;
if(c->x < wax)
c->x = c->rx = wax;
if(c->y < way)
c->y = c->ry = way;
c->border = jdwmconf->borderpx;
}
wc.border_width = c->border;
XConfigureWindow(disp, w, CWBorderWidth, &wc);
XSetWindowBorder(disp, w, drawcontext->norm[ColBorder]);
configure(c); /* propagates border_width, if size doesn't change */
updatesizehints(c);
XSelectInput(disp, w, StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
grabbuttons(c, False, jdwmconf->modkey, jdwmconf->numlockmask);
updatetitle(c);
if((rettrans = XGetTransientForHint(disp, w, &trans) == Success))
for(t = clients; t && t->win != trans; t = t->next);
if(t)
for(i = 0; i < jdwmconf->ntags; i++)
c->tags[i] = t->tags[i];
if(!loadprops(c, jdwmconf->ntags))
applyrules(c, jdwmconf);
if(!c->isfloating)
c->isfloating = (rettrans == Success) || c->isfixed;
saveprops(c, jdwmconf->ntags);
attach(c);
attachstack(c);
XMoveResizeWindow(disp, c->win, c->x, c->y, c->w, c->h); /* some windows require this */
ban(c);
arrange(disp, jdwmconf);
}
void
resize(Client * c, int x, int y, int w, int h, Bool sizehints)
{
double dx, dy, max, min, ratio;
XWindowChanges wc;
if(sizehints)
{
if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0)
{
dx = (double) (w - c->basew);
dy = (double) (h - c->baseh);
min = (double) (c->minax) / (double) (c->minay);
max = (double) (c->maxax) / (double) (c->maxay);
ratio = dx / dy;
if(max > 0 && min > 0 && ratio > 0)
{
if(ratio < min)
{
dy = (dx * min + dy) / (min * min + 1);
dx = dy * min;
w = (int) dx + c->basew;
h = (int) dy + c->baseh;
}
else if(ratio > max)
{
dy = (dx * min + dy) / (max * max + 1);
dx = dy * min;
w = (int) dx + c->basew;
h = (int) dy + c->baseh;
}
}
}
if(c->minw && w < c->minw)
w = c->minw;
if(c->minh && h < c->minh)
h = c->minh;
if(c->maxw && w > c->maxw)
w = c->maxw;
if(c->maxh && h > c->maxh)
h = c->maxh;
if(c->incw)
w -= (w - c->basew) % c->incw;
if(c->inch)
h -= (h - c->baseh) % c->inch;
}
if(w <= 0 || h <= 0)
return;
/* offscreen appearance fixes */
if(x > sw)
x = sw - w - 2 * c->border;
if(y > sh)
y = sh - h - 2 * c->border;
if(x + w + 2 * c->border < sx)
x = sx;
if(y + h + 2 * c->border < sy)
y = sy;
if(c->x != x || c->y != y || c->w != w || c->h != h)
{
c->x = wc.x = x;
c->y = wc.y = y;
c->w = wc.width = w;
c->h = wc.height = h;
wc.border_width = c->border;
XConfigureWindow(c->display, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
configure(c);
XSync(c->display, False);
}
}
void
uicb_moveresize(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf,
const char *arg)
{
int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh;
char xabs, yabs, wabs, habs;
int mx, my, dx, dy, nmx, nmy;
unsigned int dui;
Window dummy;
if(!IS_ARRANGE(floating))
if(!sel || !sel->isfloating || sel->isfixed || !arg)
return;
if(sscanf(arg, "%d%c %d%c %d%c %d%c", &x, &xabs, &y, &yabs, &w, &wabs, &h, &habs) != 8)
return;
nx = xabs == 'x' ? sel->x + x : x;
ny = yabs == 'y' ? sel->y + y : y;
nw = wabs == 'w' ? sel->w + w : w;
nh = habs == 'h' ? sel->h + h : h;
ox = sel->x;
oy = sel->y;
ow = sel->w;
oh = sel->h;
Bool xqp = XQueryPointer(sel->display, DefaultRootWindow(sel->display), &dummy, &dummy, &mx, &my, &dx, &dy, &dui);
resize(sel, nx, ny, nw, nh, True);
if (xqp && ox <= mx && (ox + ow) >= mx && oy <= my && (oy + oh) >= my)
{
nmx = mx-ox+sel->w-ow-1 < 0 ? 0 : mx-ox+sel->w-ow-1;
nmy = my-oy+sel->h-oh-1 < 0 ? 0 : my-oy+sel->h-oh-1;
XWarpPointer(sel->display, None, sel->win, 0, 0, 0, 0, nmx, nmy);
}
}
void
saveprops(Client * c, int ntags)
{
int i;
for(i = 0; i < ntags && i < ssizeof(prop) - 1; i++)
prop[i] = c->tags[i] ? '1' : '0';
if(i < ssizeof(prop) - 1)
prop[i++] = c->isfloating ? '1' : '0';
prop[i] = '\0';
XChangeProperty(c->display, c->win, jdwmprops, XA_STRING, 8,
PropModeReplace, (unsigned char *) prop, i);
}
void
unban(Client * c)
{
if(!c->isbanned)
return;
XMapWindow(c->display, c->win);
setclientstate(c, NormalState);
c->isbanned = False;
}
void
unmanage(Client * c, DC *drawcontext, long state, jdwm_config *jdwmconf)
{
XWindowChanges wc;
wc.border_width = c->oldborder;
/* The server grab construct avoids race conditions. */
XGrabServer(c->display);
XConfigureWindow(c->display, c->win, CWBorderWidth, &wc); /* restore border */
detach(c);
detachstack(c);
if(sel == c)
focus(c->display, drawcontext, NULL, jdwmconf);
XUngrabButton(c->display, AnyButton, AnyModifier, c->win);
setclientstate(c, state);
XSync(c->display, False);
XSetErrorHandler(xerror);
XUngrabServer(c->display);
if(state != NormalState)
arrange(c->display, jdwmconf);
p_delete(&c->tags);
p_delete(&c);
}
void
updatesizehints(Client * c)
{
long msize;
XSizeHints size;
if(!XGetWMNormalHints(c->display, c->win, &size, &msize) || !size.flags)
size.flags = PSize;
c->flags = size.flags;
if(c->flags & PBaseSize)
{
c->basew = size.base_width;
c->baseh = size.base_height;
}
else if(c->flags & PMinSize)
{
c->basew = size.min_width;
c->baseh = size.min_height;
}
else
c->basew = c->baseh = 0;
if(c->flags & PResizeInc)
{
c->incw = size.width_inc;
c->inch = size.height_inc;
}
else
c->incw = c->inch = 0;
if(c->flags & PMaxSize)
{
c->maxw = size.max_width;
c->maxh = size.max_height;
}
else
c->maxw = c->maxh = 0;
if(c->flags & PMinSize)
{
c->minw = size.min_width;
c->minh = size.min_height;
}
else if(c->flags & PBaseSize)
{
c->minw = size.base_width;
c->minh = size.base_height;
}
else
c->minw = c->minh = 0;
if(c->flags & PAspect)
{
c->minax = size.min_aspect.x;
c->maxax = size.max_aspect.x;
c->minay = size.min_aspect.y;
c->maxay = size.max_aspect.y;
}
else
c->minax = c->maxax = c->minay = c->maxay = 0;
c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
&& c->maxw == c->minw && c->maxh == c->minh);
}
void
setclienttrans(Client *c, double opacity, unsigned int current_opacity)
{
unsigned int real_opacity = 0xffffffff;
if(opacity >= 0 && opacity <= 100)
{
real_opacity = ((opacity / 100.0) * 0xffffffff) + current_opacity;
XChangeProperty(c->display, c->win,
XInternAtom(c->display, "_NET_WM_WINDOW_OPACITY", False),
XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &real_opacity, 1L);
}
else
XDeleteProperty(c->display, c->win, XInternAtom(c->display, "_NET_WM_WINDOW_OPACITY", False));
XSync(c->display, False);
}
void
uicb_settrans(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf __attribute__ ((unused)),
const char *arg)
{
unsigned int current_opacity = 0;
double delta = 100.0;
unsigned char *data;
Atom actual;
int format;
unsigned long n, left;
int set_prop = 0;
if(!sel)
return;
if(arg && sscanf(arg, "%lf", &delta))
{
if(arg[0] == '+' || arg[0] == '-')
{
XGetWindowProperty(sel->display, sel->win, XInternAtom(sel->display, "_NET_WM_WINDOW_OPACITY", False),
0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left,
(unsigned char **) &data);
if(data)
{
memcpy(&current_opacity, data, sizeof(double));
XFree((void *) data);
delta += ((current_opacity * 100.0) / 0xffffffff);
}
else
{
delta += 100.0;
set_prop = 1;
}
}
}
if(delta <= 0.0)
delta = 0.0;
else if(delta > 100.0)
{
delta = 100.0;
set_prop = 1;
}
if(delta == 100.0 && !set_prop)
setclienttrans(sel, -1, 0);
else
setclienttrans(sel, delta, current_opacity);
}

49
client.h Normal file
View File

@ -0,0 +1,49 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_CLIENT_H
#define JDWM_CLIENT_H
/* mask shorthands, used in event.c and client.c */
#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
#include "draw.h"
typedef struct Client Client;
struct Client
{
char name[256];
int x, y, w, h;
int rx, ry, rw, rh; /* revert geometry */
int basew, baseh, incw, inch, maxw, maxh, minw, minh;
int minax, maxax, minay, maxay;
int unmapped;
long flags;
int border, oldborder;
Bool isbanned, isfixed, ismax, isfloating, wasfloating;
Bool *tags;
Client *next;
Client *prev;
Client *snext;
Window win;
Display * display;
Bool ftview; /* first time viewed on new layout */
};
void attach(Client *); /* attaches c to global client list */
void ban(Client *); /* bans c */
void configure(Client *); /* send synthetic configure event */
void detach(Client *); /* detaches c from global client list */
void focus(Display *, DC *, Client *, jdwm_config *); /* focus c if visible && !NULL, or focus top visible */
void manage(Display *, DC *, Window, XWindowAttributes *, jdwm_config *); /* manage new client */
void resize(Client *, int, int, int, int, Bool); /* resize with given coordinates c */
void unban(Client *); /* unbans c */
void unmanage(Client *, DC *, long, jdwm_config *); /* unmanage c */
void updatesizehints(Client *); /* update the size hint variables of c */
void updatetitle(Client *); /* update the name of c */
void saveprops(Client * c, int); /* saves client properties */
void uicb_killclient(Display *, jdwm_config *, const char *); /* kill client */
void uicb_moveresize(Display *, jdwm_config *, const char *); /* move and resize window */
void uicb_settrans(Display *, jdwm_config *, const char *);
void setclienttrans(Client *c, double, unsigned int);
#endif

405
config.c Normal file
View File

@ -0,0 +1,405 @@
/* See LICENSE file for copyright and license details. */
/**
* \defgroup ui_callback
*/
#include <stdlib.h>
#include <string.h>
#include <libconfig.h>
#include <X11/keysym.h>
#include "jdwm.h"
#include "util.h"
#include "layout.h"
#include "tag.h"
#include "layouts/tile.h"
#include "layouts/grid.h"
#include "layouts/spiral.h"
#include "layouts/floating.h"
/* static */
static void initfont(const char *, Display *, DC *);
static unsigned long initcolor(const char *colstr, Display *, int);
static unsigned int get_numlockmask(Display *);
/** Main configuration object for parsing*/
config_t jdwmlibconf;
/** Current bar position */
int bpos;
/** Link a name to a function */
typedef struct
{
const char *name;
void *func;
} NameFuncLink;
/** Link a name to a key symbol */
typedef struct
{
const char *name;
KeySym keysym;
} KeyMod;
/** List of keyname and corresponding X11 mask codes */
static const KeyMod KeyModList[] = { {"Shift", ShiftMask},
{"Lock", LockMask},
{"Control", ControlMask},
{"Mod1", Mod1Mask},
{"Mod2", Mod2Mask},
{"Mod3", Mod3Mask},
{"Mod4", Mod4Mask},
{"Mod5", Mod5Mask},
{"None", 0}
};
/** List of available layouts and link between name and functions */
static const NameFuncLink LayoutsList[] = { {"tile", tile},
{"tileleft", tileleft},
{"floating", floating},
{"grid", grid},
{"spiral", spiral},
{"dwindle", dwindle},
{"bstack", bstack},
{"bstackportrait", bstackportrait},
{NULL, NULL}
};
/** List of available UI bindable callbacks and functions */
static const NameFuncLink KeyfuncList[] = {
/* util.c */
{"spawn", spawn},
/* client.c */
{"killclient", uicb_killclient},
{"moveresize", uicb_moveresize},
{"settrans", uicb_settrans},
/* config.c */
{"reload", uicb_reload},
/* tag.c */
{"tag", uicb_tag},
{"togglefloating", uicb_togglefloating},
{"toggleview", uicb_toggleview},
{"toggletag", uicb_toggletag},
{"view", uicb_view},
{"viewprevtags", uicb_viewprevtags},
/* layout.c */
{"setlayout", uicb_setlayout},
{"togglebar", uicb_togglebar},
{"focusnext", uicb_focusnext},
{"focusprev", uicb_focusprev},
{"togglemax", uicb_togglemax},
{"toggleverticalmax", uicb_toggleverticalmax},
{"togglehorizontalmax", uicb_togglehorizontalmax},
{"zoom", uicb_zoom},
/* layouts/tile.c */
{"setmwfact", uicb_setmwfact},
{"incnmaster", uicb_incnmaster},
/* jdwm.c */
{"quit", uicb_quit},
{NULL, NULL}
};
/** Lookup for a key mask from its name
* \param keyname Key name
* \return Key mask or 0 if not found
*/
static KeySym
key_mask_lookup(const char *keyname)
{
int i;
if(keyname)
for(i = 0; KeyModList[i].name; i++)
if(!strcmp(keyname, KeyModList[i].name))
return KeyModList[i].keysym;
return 0;
};
/** Lookup for a function pointer from its name
* in the given NameFuncLink list
* \param funcname Function name
* \param list Function and name link list
* \return function pointer
*/
static void *
name_func_lookup(const char *funcname, const NameFuncLink * list)
{
int i;
if(funcname && list)
for(i = 0; list[i].name; i++)
if(!strcmp(funcname, list[i].name))
return list[i].func;
return NULL;
}
/** \todo remove screen */
extern int screen;
/** \todo remove dc */
extern DC dc;
/** Reload configuration file
* \param disp Display ref
* \param arg unused
* \ingroup ui_callback
* \todo not really working nor safe I guess
*/
void
uicb_reload(Display *disp, jdwm_config *jdwmconf, const char *arg __attribute__ ((unused)))
{
config_destroy(&jdwmlibconf);
p_delete(&jdwmconf->rules);
p_delete(&jdwmconf->tags);
p_delete(&jdwmconf->layouts);
parse_config(disp, screen, &dc, jdwmconf);
}
static void
set_default_config(jdwm_config *jdwmconf)
{
strcpy(jdwmconf->statustext, "jdwm-" VERSION);
}
/** Parse configuration file and initialize some stuff
* \param disp Display ref
* \param scr Screen number
* \param drawcontext Draw context
*/
void
parse_config(Display * disp, int scr, DC * drawcontext, jdwm_config *jdwmconf)
{
config_setting_t *conftags;
config_setting_t *conflayouts, *confsublayouts;
config_setting_t *confrules, *confsubrules;
config_setting_t *confkeys, *confsubkeys, *confkeysmasks, *confkeymaskelem;
int i, j;
const char *tmp, *homedir;
char *confpath;
set_default_config(jdwmconf);
homedir = getenv("HOME");
confpath = p_new(char, strlen(homedir) + strlen(JDWM_CONFIG_FILE) + 2);
strcpy(confpath, homedir);
strcat(confpath, "/");
strcat(confpath, JDWM_CONFIG_FILE);
config_init(&jdwmlibconf);
if(config_read_file(&jdwmlibconf, confpath) == CONFIG_FALSE)
eprint("error parsing configuration file at line %d: %s\n",
config_error_line(&jdwmlibconf), config_error_text(&jdwmlibconf));
/* tags */
conftags = config_lookup(&jdwmlibconf, "jdwm.tags");
if(!conftags)
eprint("tags not found in configuration file\n");
jdwmconf->ntags = config_setting_length(conftags);
jdwmconf->tags = p_new(const char *, jdwmconf->ntags);
for(i = 0; (tmp = config_setting_get_string_elem(conftags, i)); i++)
jdwmconf->tags[i] = tmp;
/* layouts */
conflayouts = config_lookup(&jdwmlibconf, "jdwm.layouts");
if(!conflayouts)
eprint("layouts not found in configuration file\n");
jdwmconf->nlayouts = config_setting_length(conflayouts);
jdwmconf->layouts = p_new(Layout, jdwmconf->nlayouts + 1);
for(i = 0; (confsublayouts = config_setting_get_elem(conflayouts, i)); i++)
{
jdwmconf->layouts[i].symbol = config_setting_get_string_elem(confsublayouts, 0);
jdwmconf->layouts[i].arrange =
name_func_lookup(config_setting_get_string_elem(confsublayouts, 1), LayoutsList);
if(!jdwmconf->layouts[i].arrange)
eprint("unknown layout in configuration file: %s", tmp);
}
jdwmconf->layouts[i].symbol = NULL;
jdwmconf->layouts[i].arrange = NULL;
/** \todo put this in set_default_layout */
jdwmconf->current_layout = jdwmconf->layouts;
/* rules */
confrules = config_lookup(&jdwmlibconf, "jdwm.rules");
if(!confrules)
eprint("rules not found in configuration file\n");
jdwmconf->nrules = config_setting_length(confrules);
jdwmconf->rules = p_new(Rule, jdwmconf->nrules);
for(i = 0; (confsubrules = config_setting_get_elem(confrules, i)); i++)
{
jdwmconf->rules[i].prop = config_setting_get_string(config_setting_get_member(confsubrules, "name"));
jdwmconf->rules[i].tags = config_setting_get_string(config_setting_get_member(confsubrules, "tags"));
if(jdwmconf->rules[i].tags && !strlen(jdwmconf->rules[i].tags))
jdwmconf->rules[i].tags = NULL;
jdwmconf->rules[i].isfloating =
config_setting_get_bool(config_setting_get_member(confsubrules, "float"));
}
/* modkey */
jdwmconf->modkey = key_mask_lookup(config_lookup_string(&jdwmlibconf, "jdwm.modkey"));
/* find numlock mask */
jdwmconf->numlockmask = get_numlockmask(disp);
/* keys */
confkeys = config_lookup(&jdwmlibconf, "jdwm.keys");
if(!confkeys)
eprint("keys not found in configuration file\n");
jdwmconf->nkeys = config_setting_length(confkeys);
jdwmconf->keys = p_new(Key, jdwmconf->nkeys);
for(i = 0; (confsubkeys = config_setting_get_elem(confkeys, i)); i++)
{
confkeysmasks = config_setting_get_elem(confsubkeys, 0);
for(j = 0; (confkeymaskelem = config_setting_get_elem(confkeysmasks, j)); j++)
jdwmconf->keys[i].mod |= key_mask_lookup(config_setting_get_string(confkeymaskelem));
jdwmconf->keys[i].keysym = XStringToKeysym(config_setting_get_string_elem(confsubkeys, 1));
jdwmconf->keys[i].func =
name_func_lookup(config_setting_get_string_elem(confsubkeys, 2), KeyfuncList);
jdwmconf->keys[i].arg = config_setting_get_string_elem(confsubkeys, 3);
}
/* barpos */
tmp = config_lookup_string(&jdwmlibconf, "jdwm.barpos");
if(!strncmp(tmp, "BarTop", 6))
jdwmconf->bpos = BarTop;
else if(!strncmp(tmp, "BarBot", 6))
jdwmconf->bpos = BarBot;
else if(!strncmp(tmp, "BarOff", 6))
jdwmconf->bpos = BarOff;
bpos = jdwmconf->bpos;
/* borderpx */
jdwmconf->borderpx = config_lookup_int(&jdwmlibconf, "jdwm.borderpx");
/* opacity */
jdwmconf->opacity_unfocused = config_lookup_int(&jdwmlibconf, "jdwm.opacity_unfocused");
if(jdwmconf->opacity_unfocused >= 100)
jdwmconf->opacity_unfocused = -1;
/* snap */
jdwmconf->snap = config_lookup_int(&jdwmlibconf, "jdwm.snap");
/* nmaster */
jdwmconf->nmaster = config_lookup_int(&jdwmlibconf, "jdwm.nmaster");
/* mwfact */
jdwmconf->mwfact = config_lookup_float(&jdwmlibconf, "jdwm.mwfact");
/* font */
initfont(config_lookup_string(&jdwmlibconf, "jdwm.font"), disp, drawcontext);
/* colors */
dc.norm[ColBorder] = initcolor(config_lookup_string(&jdwmlibconf, "jdwm.normal_border_color"),
disp, scr);
dc.norm[ColBG] = initcolor(config_lookup_string(&jdwmlibconf, "jdwm.normal_bg_color"), disp, scr);
dc.norm[ColFG] = initcolor(config_lookup_string(&jdwmlibconf, "jdwm.normal_fg_color"), disp, scr);
dc.sel[ColBorder] = initcolor(config_lookup_string(&jdwmlibconf, "jdwm.focus_border_color"),
disp, scr);
dc.sel[ColBG] = initcolor(config_lookup_string(&jdwmlibconf, "jdwm.focus_bg_color"), disp, scr);
dc.sel[ColFG] = initcolor(config_lookup_string(&jdwmlibconf, "jdwm.focus_fg_color"), disp, scr);
p_delete(&confpath);
}
/** Initialize font from X side and store in draw context
* \param fontstr Font name
* \param disp Display ref
* \param drawcontext Draw context
*/
static void
initfont(const char *fontstr, Display * disp, DC * drawcontext)
{
char *def, **missing;
int i, n;
missing = NULL;
if(drawcontext->font.set)
XFreeFontSet(disp, drawcontext->font.set);
drawcontext->font.set = XCreateFontSet(disp, fontstr, &missing, &n, &def);
if(missing)
{
while(n--)
fprintf(stderr, "jdwm: missing fontset: %s\n", missing[n]);
XFreeStringList(missing);
}
if(drawcontext->font.set)
{
XFontSetExtents *font_extents;
XFontStruct **xfonts;
char **font_names;
drawcontext->font.ascent = drawcontext->font.descent = 0;
font_extents = XExtentsOfFontSet(drawcontext->font.set);
n = XFontsOfFontSet(drawcontext->font.set, &xfonts, &font_names);
for(i = 0, drawcontext->font.ascent = 0, drawcontext->font.descent = 0; i < n; i++)
{
if(drawcontext->font.ascent < (*xfonts)->ascent)
drawcontext->font.ascent = (*xfonts)->ascent;
if(drawcontext->font.descent < (*xfonts)->descent)
drawcontext->font.descent = (*xfonts)->descent;
xfonts++;
}
}
else
{
if(drawcontext->font.xfont)
XFreeFont(disp, drawcontext->font.xfont);
drawcontext->font.xfont = NULL;
if(!(drawcontext->font.xfont = XLoadQueryFont(disp, fontstr)))
eprint("error, cannot load font: '%s'\n", fontstr);
drawcontext->font.ascent = drawcontext->font.xfont->ascent;
drawcontext->font.descent = drawcontext->font.xfont->descent;
}
drawcontext->font.height = drawcontext->font.ascent + drawcontext->font.descent;
}
static unsigned int
get_numlockmask(Display *disp)
{
XModifierKeymap *modmap;
unsigned int mask = 0;
int i, j;
modmap = XGetModifierMapping(disp);
for(i = 0; i < 8; i++)
for(j = 0; j < modmap->max_keypermod; j++)
{
if(modmap->modifiermap[i * modmap->max_keypermod + j]
== XKeysymToKeycode(disp, XK_Num_Lock))
mask = (1 << i);
}
XFreeModifiermap(modmap);
return mask;
}
/** Initialize color from X side
* \param colorstr Color code
* \param disp Display ref
* \param scr Screen number
* \return XColor pixel
*/
static unsigned long
initcolor(const char *colstr, Display * disp, int scr)
{
Colormap cmap = DefaultColormap(disp, scr);
XColor color;
if(!XAllocNamedColor(disp, cmap, colstr, &color, &color))
eprint("error, cannot allocate color '%s'\n", colstr);
return color.pixel;
}

101
config.h Normal file
View File

@ -0,0 +1,101 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_CONFIG_H
#define JDWM_CONFIG_H
#define JDWM_CONFIG_FILE ".jdwmrc"
#include <X11/Xlib.h>
/** Bar possible position */
enum
{ BarTop, BarBot, BarOff };
enum
{ ColBorder, ColFG, ColBG, ColLast }; /* color */
typedef struct
{
int x, y, w, h;
unsigned long norm[ColLast];
unsigned long sel[ColLast];
Drawable drawable;
GC gc;
struct
{
int ascent;
int descent;
int height;
XFontSet set;
XFontStruct *xfont;
} font;
} DC;
typedef struct
{
const char *prop;
const char *tags;
Bool isfloating;
} Rule;
typedef struct jdwm_config jdwm_config;
typedef struct
{
const char *symbol;
void (*arrange) (Display *, jdwm_config *);
} Layout;
typedef struct
{
unsigned long mod;
KeySym keysym;
void (*func) (Display *, jdwm_config *, const char *);
const char *arg;
} Key;
/** Main configuration structure */
struct jdwm_config
{
/** Tag list */
const char **tags;
/** Number of tags in **tags */
int ntags;
/** Layout list */
Layout *layouts;
/** Number of layouts in *layouts */
int nlayouts;
/** Rules list */
Rule *rules;
/** Number of rules in *rules */
int nrules;
/** Keys binding list */
Key *keys;
/** Number of keys binding in *keys */
int nkeys;
/** Default modkey */
KeySym modkey;
/** Numlock mask */
unsigned int numlockmask;
/** Bar position */
int bpos;
/** Border size */
int borderpx;
/** Master width factor */
double mwfact;
/** Number of pixels to snap windows */
int snap;
/** Number of master windows */
int nmaster;
/** Transparency of unfocused clients */
int opacity_unfocused;
/** Text displayed in bar */
char statustext[256];
/** Current layout */
Layout * current_layout;
};
void parse_config(Display *, int, DC *, jdwm_config *); /* parse configuration file */
void uicb_reload(Display *, jdwm_config *, const char *); /* reload configuration file */
#endif

25
config.mk Normal file
View File

@ -0,0 +1,25 @@
# jdwm version
VERSION = 0.0
# Customize below to fit your system
# additional layouts beside floating
LAYOUTS = layouts/tile.c layouts/grid.c layouts/spiral.c layouts/floating.c
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/include/X11
X11LIB = /usr/lib/X11
# includes and libs
INCS = -I. -I/usr/include -I${X11INC} `pkg-config --cflags libconfig`
LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 `pkg-config --libs libconfig`
# flags
CFLAGS = -fgnu89-inline -std=gnu99 -ggdb3 -pipe -Wall -Wextra -W -Wchar-subscripts -Wundef -Wshadow -Wcast-align -Wwrite-strings -Wsign-compare -Wunused -Wuninitialized -Winit-self -Wpointer-arith -Wredundant-decls -Wno-format-zero-length -Wmissing-prototypes -Wmissing-format-attribute -Wmissing-noreturn -O2 ${INCS} -DVERSION=\"${VERSION}\"
LDFLAGS = -ggdb3 ${LIBS}
# compiler and linker
CC = cc

160
draw.c Normal file
View File

@ -0,0 +1,160 @@
/* See LICENSE file for copyright and license details. */
#include <string.h>
#include "layout.h"
extern int sw; /* screen geometry */
extern int bh, blw; /* bar height, bar layout label width */
extern Window barwin;
extern DC dc; /* global draw context */
extern Client *clients, *sel, *stack; /* global client list and stack */
extern Bool *seltags;
/* static */
static unsigned int
textnw(const char *text, unsigned int len)
{
XRectangle r;
if(dc.font.set)
{
XmbTextExtents(dc.font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc.font.xfont, text, len);
}
static void
drawtext(Display *disp, const char *text, unsigned long col[ColLast])
{
int x, y, w, h;
static char buf[256];
unsigned int len, olen;
XRectangle r = { dc.x, dc.y, dc.w, dc.h };
XSetForeground(disp, dc.gc, col[ColBG]);
XFillRectangles(disp, dc.drawable, dc.gc, &r, 1);
if(!text)
return;
w = 0;
olen = len = strlen(text);
if(len >= sizeof buf)
len = sizeof buf - 1;
memcpy(buf, text, len);
buf[len] = 0;
h = dc.font.ascent + dc.font.descent;
y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
x = dc.x + (h / 2);
/* shorten text if necessary */
while(len && (w = textnw(buf, len)) > dc.w - h)
buf[--len] = 0;
if(len < olen)
{
if(len > 1)
buf[len - 1] = '.';
if(len > 2)
buf[len - 2] = '.';
if(len > 3)
buf[len - 3] = '.';
}
if(w > dc.w)
return; /* too long */
XSetForeground(disp, dc.gc, col[ColFG]);
if(dc.font.set)
XmbDrawString(disp, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
else
XDrawString(disp, dc.drawable, dc.gc, x, y, buf, len);
}
static void
drawsquare(Bool filled, Bool empty, unsigned long col[ColLast], Display *disp)
{
int x;
XGCValues gcv;
XRectangle r = { dc.x, dc.y, dc.w, dc.h };
gcv.foreground = col[ColFG];
XChangeGC(disp, dc.gc, GCForeground, &gcv);
x = (dc.font.ascent + dc.font.descent + 2) / 4;
r.x = dc.x + 1;
r.y = dc.y + 1;
if(filled)
{
r.width = r.height = x + 1;
XFillRectangles(disp, dc.drawable, dc.gc, &r, 1);
}
else if(empty)
{
r.width = r.height = x;
XDrawRectangles(disp, dc.drawable, dc.gc, &r, 1);
}
}
static Bool
isoccupied(unsigned int t)
{
Client *c;
for(c = clients; c; c = c->next)
if(c->tags[t])
return True;
return False;
}
/* extern */
void
drawstatus(Display *disp, jdwm_config * jdwmconf)
{
int x, i;
dc.x = dc.y = 0;
for(i = 0; i < jdwmconf->ntags; i++)
{
dc.w = textw(jdwmconf->tags[i]);
if(seltags[i])
{
drawtext(disp, jdwmconf->tags[i], dc.sel);
drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel, disp);
}
else
{
drawtext(disp, jdwmconf->tags[i], dc.norm);
drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm, disp);
}
dc.x += dc.w;
}
dc.w = blw;
drawtext(disp, jdwmconf->current_layout->symbol, dc.norm);
x = dc.x + dc.w;
dc.w = textw(jdwmconf->statustext);
dc.x = sw - dc.w;
if(dc.x < x)
{
dc.x = x;
dc.w = sw - x;
}
drawtext(disp, jdwmconf->statustext, dc.norm);
if((dc.w = dc.x - x) > bh)
{
dc.x = x;
if(sel)
{
drawtext(disp, sel->name, dc.sel);
drawsquare(sel->ismax, sel->isfloating, dc.sel, disp);
}
else
drawtext(disp, NULL, dc.norm);
}
XCopyArea(disp, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
XSync(disp, False);
}
inline unsigned int
textw(const char *text)
{
return textnw(text, strlen(text)) + dc.font.height;
}

11
draw.h Normal file
View File

@ -0,0 +1,11 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_DRAW_H
#define JDWM_DRAW_H
#include "config.h"
void drawstatus(Display *, jdwm_config *); /* draw the bar */
inline unsigned int textw(const char *text); /* return the width of text in px */
#endif

412
event.c Normal file
View File

@ -0,0 +1,412 @@
/* See LICENSE file for copyright and license details. */
#include <stdlib.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "jdwm.h"
#include "util.h"
#include "event.h"
#include "layout.h"
#include "tag.h"
#include "layouts/tile.h"
#include "layouts/floating.h"
/* extern */
extern int screen, sw, sh; /* screen geometry */
extern int wax, way, wah, waw; /* windowarea geometry */
extern int bh, blw; /* bar height, bar layout label width */
extern Window barwin;
extern DC dc; /* global draw context */
extern Cursor cursor[CurLast];
extern Client *clients, *sel; /* global client list */
extern Bool selscreen;
extern Atom netatom[NetLast];
void (*handler[LASTEvent]) (XEvent *, jdwm_config *); /* event handler */
#define CLEANMASK(mask) (mask & ~(jdwmconf->numlockmask | LockMask))
#define MOUSEMASK (BUTTONMASK | PointerMotionMask)
static Client *
getclient(Window w)
{
Client *c;
for(c = clients; c && c->win != w; c = c->next);
return c;
}
static void
movemouse(Client * c, jdwm_config *jdwmconf)
{
int x1, y1, ocx, ocy, di, nx, ny;
unsigned int dui;
Window dummy;
XEvent ev;
ocx = nx = c->x;
ocy = ny = c->y;
if(XGrabPointer(c->display, DefaultRootWindow(c->display), False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
None, cursor[CurMove], CurrentTime) != GrabSuccess)
return;
XQueryPointer(c->display, DefaultRootWindow(c->display), &dummy, &dummy, &x1, &y1, &di, &di, &dui);
for(;;)
{
XMaskEvent(c->display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
switch (ev.type)
{
case ButtonRelease:
XUngrabPointer(c->display, CurrentTime);
return;
case ConfigureRequest:
case Expose:
case MapRequest:
handler[ev.type] (&ev, jdwmconf);
break;
case MotionNotify:
XSync(c->display, False);
nx = ocx + (ev.xmotion.x - x1);
ny = ocy + (ev.xmotion.y - y1);
if(abs(wax + nx) < jdwmconf->snap)
nx = wax;
else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < jdwmconf->snap)
nx = wax + waw - c->w - 2 * c->border;
if(abs(way - ny) < jdwmconf->snap)
ny = way;
else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < jdwmconf->snap)
ny = way + wah - c->h - 2 * c->border;
resize(c, nx, ny, c->w, c->h, False);
break;
}
}
}
static void
resizemouse(Client * c, jdwm_config *jdwmconf)
{
int ocx, ocy;
int nw, nh;
XEvent ev;
ocx = c->x;
ocy = c->y;
if(XGrabPointer(c->display, DefaultRootWindow(c->display), False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
None, cursor[CurResize], CurrentTime) != GrabSuccess)
return;
c->ismax = False;
XWarpPointer(c->display, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
for(;;)
{
XMaskEvent(c->display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
switch (ev.type)
{
case ButtonRelease:
XWarpPointer(c->display, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
XUngrabPointer(c->display, CurrentTime);
while(XCheckMaskEvent(c->display, EnterWindowMask, &ev));
return;
case ConfigureRequest:
case Expose:
case MapRequest:
handler[ev.type] (&ev, jdwmconf);
break;
case MotionNotify:
XSync(c->display, False);
if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
nw = 1;
if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
nh = 1;
resize(c, c->x, c->y, nw, nh, True);
break;
}
}
}
static void
buttonpress(XEvent * e, jdwm_config *jdwmconf)
{
int i, x;
Client *c;
XButtonPressedEvent *ev = &e->xbutton;
if(barwin == ev->window)
{
x = 0;
for(i = 0; i < jdwmconf->ntags; i++)
{
x += textw(jdwmconf->tags[i]);
if(ev->x < x)
{
if(ev->button == Button1)
{
if(ev->state & jdwmconf->modkey)
uicb_tag(e->xany.display, jdwmconf, jdwmconf->tags[i]);
else
uicb_view(e->xany.display, jdwmconf, jdwmconf->tags[i]);
}
else if(ev->button == Button3)
{
if(ev->state & jdwmconf->modkey)
uicb_toggletag(e->xany.display, jdwmconf, jdwmconf->tags[i]);
else
uicb_toggleview(e->xany.display, jdwmconf, jdwmconf->tags[i]);
}
return;
}
}
if((ev->x < x + blw) && ev->button == Button1)
uicb_setlayout(e->xany.display, jdwmconf, NULL);
}
else if((c = getclient(ev->window)))
{
focus(c->display, &dc, c, jdwmconf);
if(CLEANMASK(ev->state) != jdwmconf->modkey)
return;
if(ev->button == Button1 && (IS_ARRANGE(floating) || c->isfloating))
{
restack(e->xany.display, jdwmconf);
movemouse(c, jdwmconf);
}
else if(ev->button == Button2)
uicb_zoom(e->xany.display, jdwmconf, NULL);
else if(ev->button == Button3 && (IS_ARRANGE(floating) || c->isfloating) && !c->isfixed)
{
restack(e->xany.display, jdwmconf);
resizemouse(c, jdwmconf);
}
}
else if(DefaultRootWindow(e->xany.display) == ev->window && !sel)
{
if(ev->button == Button4)
uicb_tag_viewnext(e->xany.display, jdwmconf, NULL);
else if(ev->button == Button5)
uicb_tag_viewprev(e->xany.display, jdwmconf, NULL);
}
}
static void
configurerequest(XEvent * e, jdwm_config *jdwmconf __attribute__ ((unused)))
{
Client *c;
XConfigureRequestEvent *ev = &e->xconfigurerequest;
XWindowChanges wc;
if((c = getclient(ev->window)))
{
c->ismax = False;
if(ev->value_mask & CWBorderWidth)
c->border = ev->border_width;
if(c->isfixed || c->isfloating || IS_ARRANGE(floating))
{
if(ev->value_mask & CWX)
c->x = ev->x;
if(ev->value_mask & CWY)
c->y = ev->y;
if(ev->value_mask & CWWidth)
c->w = ev->width;
if(ev->value_mask & CWHeight)
c->h = ev->height;
if((c->x + c->w) > sw && c->isfloating)
c->x = sw / 2 - c->w / 2; /* center in x direction */
if((c->y + c->h) > sh && c->isfloating)
c->y = sh / 2 - c->h / 2; /* center in y direction */
if((ev->value_mask & (CWX | CWY)) && !(ev->value_mask & (CWWidth | CWHeight)))
configure(c);
if(isvisible(c, jdwmconf->ntags))
XMoveResizeWindow(e->xany.display, c->win, c->x, c->y, c->w, c->h);
}
else
configure(c);
}
else
{
wc.x = ev->x;
wc.y = ev->y;
wc.width = ev->width;
wc.height = ev->height;
wc.border_width = ev->border_width;
wc.sibling = ev->above;
wc.stack_mode = ev->detail;
XConfigureWindow(e->xany.display, ev->window, ev->value_mask, &wc);
}
XSync(e->xany.display, False);
}
static void
configurenotify(XEvent * e, jdwm_config *jdwmconf)
{
XConfigureEvent *ev = &e->xconfigure;
if(ev->window == DefaultRootWindow(e->xany.display) && (ev->width != sw || ev->height != sh))
{
sw = ev->width;
sh = ev->height;
XFreePixmap(e->xany.display, dc.drawable);
dc.drawable = XCreatePixmap(e->xany.display, DefaultRootWindow(e->xany.display), sw, bh, DefaultDepth(e->xany.display, screen));
XResizeWindow(e->xany.display, barwin, sw, bh);
updatebarpos(e->xany.display);
arrange(e->xany.display, jdwmconf);
}
}
static void
destroynotify(XEvent * e, jdwm_config *jdwmconf)
{
Client *c;
XDestroyWindowEvent *ev = &e->xdestroywindow;
if((c = getclient(ev->window)))
unmanage(c, &dc, WithdrawnState, jdwmconf);
}
static void
enternotify(XEvent * e, jdwm_config *jdwmconf)
{
Client *c;
XCrossingEvent *ev = &e->xcrossing;
if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
return;
if((c = getclient(ev->window)))
focus(c->display, &dc, c, jdwmconf);
else if(ev->window == DefaultRootWindow(e->xany.display))
{
selscreen = True;
focus(e->xany.display, &dc, NULL, jdwmconf);
}
}
static void
expose(XEvent * e, jdwm_config *jdwmconf)
{
XExposeEvent *ev = &e->xexpose;
if(!ev->count && barwin == ev->window)
drawstatus(e->xany.display, jdwmconf);
}
static void
keypress(XEvent * e, jdwm_config *jdwmconf)
{
int i;
KeySym keysym;
XKeyEvent *ev = &e->xkey;
keysym = XKeycodeToKeysym(e->xany.display, (KeyCode) ev->keycode, 0);
for(i = 0; i < jdwmconf->nkeys; i++)
if(keysym == jdwmconf->keys[i].keysym
&& CLEANMASK(jdwmconf->keys[i].mod) == CLEANMASK(ev->state) && jdwmconf->keys[i].func)
jdwmconf->keys[i].func(e->xany.display, jdwmconf, jdwmconf->keys[i].arg);
}
static void
leavenotify(XEvent * e, jdwm_config *jdwmconf)
{
XCrossingEvent *ev = &e->xcrossing;
if((ev->window == DefaultRootWindow(e->xany.display)) && !ev->same_screen)
{
selscreen = False;
focus(e->xany.display, &dc, NULL, jdwmconf);
}
}
static void
mappingnotify(XEvent * e, jdwm_config *jdwmconf)
{
XMappingEvent *ev = &e->xmapping;
XRefreshKeyboardMapping(ev);
if(ev->request == MappingKeyboard)
grabkeys(e->xany.display, jdwmconf);
}
static void
maprequest(XEvent * e, jdwm_config *jdwmconf)
{
static XWindowAttributes wa;
XMapRequestEvent *ev = &e->xmaprequest;
if(!XGetWindowAttributes(e->xany.display, ev->window, &wa))
return;
if(wa.override_redirect)
return;
if(!getclient(ev->window))
manage(e->xany.display, &dc, ev->window, &wa, jdwmconf);
}
static void
propertynotify(XEvent * e, jdwm_config *jdwmconf)
{
Client *c;
Window trans;
XPropertyEvent *ev = &e->xproperty;
if(ev->state == PropertyDelete)
return; /* ignore */
if((c = getclient(ev->window)))
{
switch (ev->atom)
{
default:
break;
case XA_WM_TRANSIENT_FOR:
XGetTransientForHint(e->xany.display, c->win, &trans);
if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
arrange(e->xany.display, jdwmconf);
break;
case XA_WM_NORMAL_HINTS:
updatesizehints(c);
break;
}
if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName])
{
updatetitle(c);
if(c == sel)
drawstatus(e->xany.display, jdwmconf);
}
}
}
static void
unmapnotify(XEvent * e, jdwm_config *jdwmconf)
{
Client *c;
XUnmapEvent *ev = &e->xunmap;
if((c = getclient(ev->window)) && ev->event == DefaultRootWindow(e->xany.display) && (ev->send_event || !c->unmapped--))
unmanage(c, &dc, WithdrawnState, jdwmconf);
}
/* extern */
void (*handler[LASTEvent]) (XEvent *, jdwm_config *) =
{
[ButtonPress] = buttonpress,
[ConfigureRequest] = configurerequest,
[ConfigureNotify] = configurenotify,
[DestroyNotify] = destroynotify,
[EnterNotify] = enternotify,
[LeaveNotify] = leavenotify,
[Expose] = expose,
[KeyPress] = keypress,
[MappingNotify] = mappingnotify,
[MapRequest] = maprequest,[PropertyNotify] = propertynotify,[UnmapNotify] = unmapnotify};
void
grabkeys(Display *disp, jdwm_config *jdwmconf)
{
int i;
KeyCode code;
XUngrabKey(disp, AnyKey, AnyModifier, DefaultRootWindow(disp));
for(i = 0; i < jdwmconf->nkeys; i++)
{
code = XKeysymToKeycode(disp, jdwmconf->keys[i].keysym);
XGrabKey(disp, code, jdwmconf->keys[i].mod, DefaultRootWindow(disp), True, GrabModeAsync, GrabModeAsync);
XGrabKey(disp, code, jdwmconf->keys[i].mod | LockMask, DefaultRootWindow(disp), True, GrabModeAsync, GrabModeAsync);
XGrabKey(disp, code, jdwmconf->keys[i].mod | jdwmconf->numlockmask, DefaultRootWindow(disp), True, GrabModeAsync, GrabModeAsync);
XGrabKey(disp, code, jdwmconf->keys[i].mod | jdwmconf->numlockmask | LockMask, DefaultRootWindow(disp), True,
GrabModeAsync, GrabModeAsync);
}
}

10
event.h Normal file
View File

@ -0,0 +1,10 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_EVENT_H
#define JDWM_EVENT_H
#include "config.h"
void grabkeys(Display *, jdwm_config *); /* grab all keys defined in config */
#endif

153
jdwm.1 Normal file
View File

@ -0,0 +1,153 @@
.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 and
floating layouts. Either layout can be applied dynamically, optimizing the
environment for the application in use and the task performed.
.P
In tiled layout windows are managed in a master and stacking area. The master
area contains the window which currently needs most attention, whereas the
stacking area contains all other windows. 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
dwm contains a small status bar which displays all available tags, the layout,
the title of the focused window, and the text read from standard input. A
floating window is indicated with an empty square and a maximized
floating window is indicated with a filled square before the windows
title. The selected tags are indicated with a different color. The tags of
the focused window are indicated with a filled square in the top left
corner. The tags which are applied to one or more windows are indicated
with an empty square in the top left corner.
.P
dwm draws a small border around windows to indicate the focus state.
.SH OPTIONS
.TP
.B \-v
prints version information to standard output, then exits.
.SH USAGE
.SS Status bar
.TP
.B Standard input
is read and displayed in the status text area.
.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 xterm.
.TP
.B Mod1\-Return
Zooms/cycles current window to/from master area (tiled layout only).
.TP
.B Mod1\-b
Shows/hides the status bar.
.TP
.B Mod1\-h
Decreases the master area width about 5% (tiled layout only).
.TP
.B Mod1\-j
Focus next window.
.TP
.B Mod1\-k
Focus previous window.
.TP
.B Mod1\-l
Increases the master area width about 5% (tiled layout only).
.TP
.B Mod1\-m
Toggles maximization of current window (floating layout only).
.TP
.B Mod1\-Shift\-[1..n]
Apply
.RB nth
tag to current window.
.TP
.B Mod1\-Shift\-0
Apply all tags to current window.
.TP
.B Mod1\-Control\-Shift\-[1..n]
Add/remove
.B nth
tag to/from current window.
.TP
.B Mod1\-Shift\-c
Close focused window.
.TP
.B Mod1\-space
Toggle between tiled and floating layout (affects all windows).
.TP
.B Mod1\-Shift\-space
Toggle focused window between tiled and floating state (tiled layout only).
.TP
.B Mod1\-[1..n]
View all windows with
.BR nth
tag.
.TP
.B Mod1\-0
View all windows with any tag.
.TP
.B Mod1\-Control\-[1..n]
Add/remove all windows with
.BR nth
tag to/from the view.
.TP
.B Mod1\-Shift\-q
Quit dwm.
.SS Mouse commands
.TP
.B Mod1\-Button1
Move current window while dragging (floating layout only).
.TP
.B Mod1\-Button2
Zooms/cycles current window to/from master area (tiled layout only).
.TP
.B Mod1\-Button3
Resize current window while dragging (floating layout only).
.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)
.SH BUGS
The status bar may display
.BR "EOF"
when dwm has been started by an X session manager like
.BR xdm (1),
because those close standard output before executing dwm.
.P
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. As a workaround
you can use JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or you
can set the following environment variable (to use the older Motif
backend instead):
.BR AWT_TOOLKIT=MToolkit .
.P
Recent GTK 2.10.9+ versions contain a broken
.BR Save\-As
file dialog implementation,
which requests to reconfigure its window size in an endless loop. However, its
window is still respondable during this state, so you can simply ignore the flicker
until a new GTK version appears, which will fix this bug, approximately
GTK 2.10.12+ versions.

379
jdwm.c Normal file
View File

@ -0,0 +1,379 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include "jdwm.h"
#include "util.h"
#include "event.h"
#include "layout.h"
#include "tag.h"
/* extern */
extern int bpos; /* bar position */
extern void (*handler[LASTEvent]) (XEvent *, jdwm_config *); /* event handler */
int screen, sx, sy, sw, sh, wax, way, waw, wah;
int bh;
Atom jdwmprops, wmatom[WMLast], netatom[NetLast];
Bool *seltags, *prevtags;;
Bool selscreen = True;
Client *clients = NULL;
Client *sel = NULL;
Client *stack = NULL;
Cursor cursor[CurLast];
DC dc;
Window barwin;
Layout ** taglayouts;
/* static */
static int (*xerrorxlib) (Display *, XErrorEvent *);
static Bool otherwm = False, readin = True;
static Bool running = True;
Bool
gettextprop(Display *disp, Window w, Atom atom, char *text, unsigned int size)
{
char **list = NULL;
int n;
XTextProperty name;
if(!text || size == 0)
return False;
text[0] = '\0';
XGetTextProperty(disp, w, &name, atom);
if(!name.nitems)
return False;
if(name.encoding == XA_STRING)
strncpy(text, (char *) name.value, size - 1);
else if(XmbTextPropertyToTextList(disp, &name, &list, &n) >= Success && n > 0 && *list)
{
strncpy(text, *list, size - 1);
XFreeStringList(list);
}
text[size - 1] = '\0';
XFree(name.value);
return True;
}
static void
cleanup(Display *disp, jdwm_config *jdwmconf)
{
close(STDIN_FILENO);
while(stack)
{
unban(stack);
unmanage(stack, &dc, NormalState, jdwmconf);
}
if(dc.font.set)
XFreeFontSet(disp, dc.font.set);
else
XFreeFont(disp, dc.font.xfont);
XUngrabKey(disp, AnyKey, AnyModifier, DefaultRootWindow(disp));
XFreePixmap(disp, dc.drawable);
XFreeGC(disp, dc.gc);
XDestroyWindow(disp, barwin);
XFreeCursor(disp, cursor[CurNormal]);
XFreeCursor(disp, cursor[CurResize]);
XFreeCursor(disp, cursor[CurMove]);
XSetInputFocus(disp, PointerRoot, RevertToPointerRoot, CurrentTime);
XSync(disp, False);
p_delete(&seltags);
p_delete(&prevtags);
p_delete(&taglayouts);
}
static long
getstate(Display *disp, Window w)
{
int format, status;
long result = -1;
unsigned char *p = NULL;
unsigned long n, extra;
Atom real;
status = XGetWindowProperty(disp, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
&real, &format, &n, &extra, (unsigned char **) &p);
if(status != Success)
return -1;
if(n != 0)
result = *p;
XFree(p);
return result;
}
static void
scan(Display *disp, jdwm_config *jdwmconf)
{
unsigned int i, num;
Window *wins, d1, d2;
XWindowAttributes wa;
wins = NULL;
if(XQueryTree(disp, DefaultRootWindow(disp), &d1, &d2, &wins, &num))
for(i = 0; i < num; i++)
{
if(!XGetWindowAttributes(disp, wins[i], &wa)
|| wa.override_redirect || XGetTransientForHint(disp, wins[i], &d1))
continue;
if(wa.map_state == IsViewable || getstate(disp, wins[i]) == IconicState)
manage(disp, &dc, wins[i], &wa, jdwmconf);
}
/* now the transients */
for(i = 0; i < num; i++)
{
if(!XGetWindowAttributes(disp, wins[i], &wa))
continue;
if(XGetTransientForHint(disp, wins[i], &d1)
&& (wa.map_state == IsViewable || getstate(disp, wins[i]) == IconicState))
manage(disp, &dc, wins[i], &wa, jdwmconf);
}
if(wins)
XFree(wins);
}
static void
setup(Display *disp, jdwm_config *jdwmconf)
{
int i;
unsigned int mask;
Window w;
XSetWindowAttributes wa;
/* init atoms */
jdwmprops = XInternAtom(disp, "_JDWM_PROPERTIES", False);
wmatom[WMProtocols] = XInternAtom(disp, "WM_PROTOCOLS", False);
wmatom[WMDelete] = XInternAtom(disp, "WM_DELETE_WINDOW", False);
wmatom[WMName] = XInternAtom(disp, "WM_NAME", False);
wmatom[WMState] = XInternAtom(disp, "WM_STATE", False);
netatom[NetSupported] = XInternAtom(disp, "_NET_SUPPORTED", False);
netatom[NetWMName] = XInternAtom(disp, "_NET_WM_NAME", False);
XChangeProperty(disp, DefaultRootWindow(disp), netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
/* init cursors */
cursor[CurNormal] = XCreateFontCursor(disp, XC_left_ptr);
cursor[CurResize] = XCreateFontCursor(disp, XC_sizing);
cursor[CurMove] = XCreateFontCursor(disp, XC_fleur);
/* select for events */
wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
| EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
wa.cursor = cursor[CurNormal];
XChangeWindowAttributes(disp, DefaultRootWindow(disp), CWEventMask | CWCursor, &wa);
XSelectInput(disp, DefaultRootWindow(disp), wa.event_mask);
grabkeys(disp, jdwmconf);
compileregs(jdwmconf);
seltags = p_new(Bool, jdwmconf->ntags);
seltags[0] = True;
prevtags = p_new(Bool, jdwmconf->ntags);
prevtags[0] = True;
/* geometry */
sx = sy = 0;
sw = DisplayWidth(disp, screen);
sh = DisplayHeight(disp, screen);
initlayouts(jdwmconf);
/* bar */
dc.h = bh = dc.font.height + 2;
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ButtonPressMask | ExposureMask;
barwin = XCreateWindow(disp, DefaultRootWindow(disp), sx, sy, sw, bh, 0,
DefaultDepth(disp, screen), CopyFromParent,
DefaultVisual(disp, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
XDefineCursor(disp, barwin, cursor[CurNormal]);
updatebarpos(disp);
XMapRaised(disp, barwin);
/* pixmap for everything */
dc.drawable = XCreatePixmap(disp, DefaultRootWindow(disp), sw, bh, DefaultDepth(disp, screen));
dc.gc = XCreateGC(disp, DefaultRootWindow(disp), 0, 0);
XSetLineAttributes(disp, dc.gc, 1, LineSolid, CapButt, JoinMiter);
if(!dc.font.set)
XSetFont(disp, dc.gc, dc.font.xfont->fid);
/* multihead support */
selscreen = XQueryPointer(disp, DefaultRootWindow(disp), &w, &w, &i, &i, &i, &i, &mask);
loadjdwmprops(disp, jdwmconf);
}
/*
* Startup Error handler to check if another window manager
* is already running.
*/
static int
xerrorstart(Display * dsply __attribute__ ((unused)), XErrorEvent * ee __attribute__ ((unused)))
{
otherwm = True;
return -1;
}
/* extern */
void
uicb_quit(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf __attribute__((unused)),
const char *arg __attribute__ ((unused)))
{
readin = running = False;
}
void
updatebarpos(Display *disp)
{
XEvent ev;
wax = sx;
way = sy;
wah = sh;
waw = sw;
switch (bpos)
{
default:
wah -= bh;
way += bh;
XMoveWindow(disp, barwin, sx, sy);
break;
case BarBot:
wah -= bh;
XMoveWindow(disp, barwin, sx, sy + wah);
break;
case BarOff:
XMoveWindow(disp, barwin, sx, sy - bh);
break;
}
XSync(disp, False);
while(XCheckMaskEvent(disp, EnterWindowMask, &ev));
}
/* There's no way to check accesses to destroyed windows, thus those cases are
* ignored (especially on UnmapNotify's). Other types of errors call Xlibs
* default error handler, which may call exit.
*/
int
xerror(Display * edpy, XErrorEvent * ee)
{
if(ee->error_code == BadWindow
|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
|| (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
|| (ee->request_code == X_PolyFillRectangle
&& ee->error_code == BadDrawable)
|| (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
|| (ee->request_code == X_ConfigureWindow
&& ee->error_code == BadMatch) || (ee->request_code == X_GrabKey
&& ee->error_code == BadAccess)
|| (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
return 0;
fprintf(stderr, "jdwm: fatal error: request code=%d, error code=%d\n",
ee->request_code, ee->error_code);
return xerrorxlib(edpy, ee); /* may call exit */
}
int
main(int argc, char *argv[])
{
char *p;
int r, xfd;
fd_set rd;
XEvent ev;
Display * dpy;
Window root;
jdwm_config jdwmconf;
if(argc == 2 && !strcmp("-v", argv[1]))
eprint("jdwm-" VERSION " © 2007 Julien Danjou\n");
else if(argc != 1)
eprint("usage: jdwm [-v]\n");
setlocale(LC_CTYPE, "");
if(!(dpy = XOpenDisplay(NULL)))
eprint("jdwm: cannot open display\n");
xfd = ConnectionNumber(dpy);
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
XSetErrorHandler(xerrorstart);
/* this causes an error if some other window manager is running */
XSelectInput(dpy, root, SubstructureRedirectMask);
XSync(dpy, False);
if(otherwm)
eprint("jdwm: another window manager is already running\n");
XSync(dpy, False);
XSetErrorHandler(NULL);
xerrorxlib = XSetErrorHandler(xerror);
XSync(dpy, False);
parse_config(dpy, screen, &dc, &jdwmconf);
setup(dpy, &jdwmconf);
drawstatus(dpy, &jdwmconf);
scan(dpy, &jdwmconf);
XSync(dpy, False);
/* main event loop, also reads status text from stdin */
while(running)
{
FD_ZERO(&rd);
if(readin)
FD_SET(STDIN_FILENO, &rd);
FD_SET(xfd, &rd);
if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1)
{
if(errno == EINTR)
continue;
eprint("select failed\n");
}
if(FD_ISSET(STDIN_FILENO, &rd))
{
switch (r = read(STDIN_FILENO, jdwmconf.statustext, sizeof(jdwmconf.statustext) - 1))
{
case -1:
strncpy(jdwmconf.statustext, strerror(errno), sizeof(jdwmconf.statustext) - 1);
jdwmconf.statustext[sizeof(jdwmconf.statustext) - 1] = '\0';
readin = False;
break;
case 0:
strncpy(jdwmconf.statustext, "EOF", 4);
readin = False;
break;
default:
for(jdwmconf.statustext[r] = '\0', p = jdwmconf.statustext + strlen(jdwmconf.statustext) - 1;
p >= jdwmconf.statustext && *p == '\n'; *p-- = '\0');
for(; p >= jdwmconf.statustext && *p != '\n'; --p);
if(p > jdwmconf.statustext)
strncpy(jdwmconf.statustext, p + 1, sizeof(jdwmconf.statustext));
}
drawstatus(dpy, &jdwmconf);
}
while(XPending(dpy))
{
XNextEvent(dpy, &ev);
if(handler[ev.type])
(handler[ev.type]) (&ev, &jdwmconf); /* call handler */
}
}
cleanup(dpy, &jdwmconf);
XCloseDisplay(dpy);
return 0;
}

276
jdwm.doxygen Normal file
View File

@ -0,0 +1,276 @@
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = jdwm
PROJECT_NUMBER = 0.0
OUTPUT_DIRECTORY = doc
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = YES
EXTRACT_ANON_NSPACES = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = .
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cpp \
*.c++ \
*.d \
*.java \
*.ii \
*.ixx \
*.ipp \
*.i++ \
*.inl \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++ \
*.idl \
*.odl \
*.cs \
*.php \
*.php3 \
*.inc \
*.m \
*.mm \
*.dox \
*.py \
*.C \
*.CC \
*.C++ \
*.II \
*.I++ \
*.H \
*.HH \
*.H++ \
*.CS \
*.PHP \
*.PHP3 \
*.M \
*.MM \
*.PY
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
HTML_DYNAMIC_SECTIONS = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = YES
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = YES
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 1000
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

49
jdwm.h Normal file
View File

@ -0,0 +1,49 @@
/* See LICENSE file for copyright and license details.
*
* Julien's dynamic window manager is designed like any other X client as well.
* It is driven through handling X events. In contrast to other X clients, a
* window manager selects for SubstructureRedirectMask on the root window, to
* receive events about window (dis-)appearance. Only one X connection at a
* time is allowed to select for this event mask.
*
* Calls to fetch an X event from the event queue are blocking. Due reading
* status text from standard input, a select()-driven main loop has been
* implemented which selects for reads on the X connection and STDIN_FILENO to
* handle all data smoothly. The event handlers of jdwm are organized in an
* array which is accessed whenever a new event has been fetched. This allows
* event dispatching in O(1) time.
*
* Each child of the root window is called a client, except windows which have
* set the override_redirect flag. Clients are organized in a global
* doubly-linked client list, the focus history is remembered through a global
* stack list. Each client contains an array of Bools of the same size as the
* global tags array to indicate the tags of a client. For each client jdwm
* creates a small title window, which is resized whenever the (_NET_)WM_NAME
* properties are updated or the client is moved/resized.
*
* Keys and tagging rules are organized as arrays and defined in the config.h
* file. These arrays are kept static in event.o and tag.o respectively,
* because no other part of jdwm needs access to them. The current layout is
* represented by the lt pointer.
*
* To understand everything else, start reading main.c:main().
*/
#ifndef JDWM_JDWM_H
#define JDWM_JDWM_H
#include "config.h"
enum
{ CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum
{ NetSupported, NetWMName, NetLast }; /* EWMH atoms */
enum
{ WMProtocols, WMDelete, WMName, WMState, WMLast }; /* default atoms */
Bool gettextprop(Display *, Window, Atom, char *, unsigned int); /* return text property, UTF-8 compliant */
void updatebarpos(Display *disp); /* updates the bar position */
void uicb_quit(Display *, jdwm_config *, const char *); /* quit jdwm nicely */
int xerror(Display *, XErrorEvent *); /* jdwm's X error handler */
#endif

111
jdwmrc Normal file
View File

@ -0,0 +1,111 @@
# Configuration file for jdwm
jdwm:
{
barpos = "BarTop";
borderpx = 1;
snap = 8;
mwfact = 0.6;
font = "-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*";
tags = ( "1", "2", "3", "4", "5", "6", "7", "8", "9" );
normal_border_color = "#dddddd";
normal_bg_color = "#000000";
normal_fg_color = "#ffffff";
focus_border_color = "#008b8b";
focus_bg_color = "#008b8b";
focus_fg_color = "#ffffff";
layouts = (("[]=", "tile"),
("=[]", "tileleft"),
("+++", "grid"),
# ("(@)", "spiral"),
("[\]", "dwindle"),
("><>", "floating"),
("TTT", "bstack"),
("===", "bstackportrait")
);
nmaster = 2;
rules = ({ name = "Gimp";
tags = "";
float = true;
},
{ name = "MPlayer";
tags = "";
float = true;
},
{ name = "Acroread";
tags = "";
float = true;
},
{ name = "VLC";
tags = "";
float = true;
},
{ name = "pinentry";
tags = "";
float = true;
});
modkey = "Mod4";
keys = ((("Mod4"), "Return", "spawn", "exec urxvt"),
(("Mod4"), "space", "setlayout"),
(("Mod4"), "b", "togglebar"),
(("Mod4"), "j", "focusnext"),
(("Mod4"), "k", "focusprev"),
(("Mod4"), "h", "setmwfact", "-0.05"),
(("Mod4"), "l", "setmwfact", "+0.05"),
(("Mod4", "Shift"), "h", "incnmaster", "1"),
(("Mod4", "Shift"), "l", "incnmaster", "-1"),
(("Mod4"), "m", "togglemax"),
(("Mod4"), "Escape", "viewprevtags"),
(("Mod4", "Control"), "Return", "zoom"),
(("Mod4", "Control"), "space", "togglefloating"),
(("Mod4", "Shift"), "c", "killclient"),
(("Mod4", "Shift"), "q", "quit"),
(("Mod4", "Shift"), "r", "reload"),
(("Mod4"), "0", "view"),
(("Mod4"), "1", "view", "1"),
(("Mod4"), "2", "view", "2"),
(("Mod4"), "3", "view", "3"),
(("Mod4"), "4", "view", "4"),
(("Mod4"), "5", "view", "5"),
(("Mod4"), "6", "view", "6"),
(("Mod4"), "7", "view", "7"),
(("Mod4"), "8", "view", "8"),
(("Mod4"), "9", "view", "9"),
(("Mod4", "Control"), "0", "toggleview"),
(("Mod4", "Control"), "1", "toggleview", "1"),
(("Mod4", "Control"), "2", "toggleview", "2"),
(("Mod4", "Control"), "3", "toggleview", "3"),
(("Mod4", "Control"), "4", "toggleview", "4"),
(("Mod4", "Control"), "5", "toggleview", "5"),
(("Mod4", "Control"), "6", "toggleview", "6"),
(("Mod4", "Control"), "7", "toggleview", "7"),
(("Mod4", "Control"), "8", "toggleview", "8"),
(("Mod4", "Control"), "9", "toggleview", "9"),
(("Mod4", "Control"), "0", "toggleview"),
(("Mod4", "Shift"), "0", "tag"),
(("Mod4", "Shift"), "1", "tag", "1"),
(("Mod4", "Shift"), "2", "tag", "2"),
(("Mod4", "Shift"), "3", "tag", "3"),
(("Mod4", "Shift"), "4", "tag", "4"),
(("Mod4", "Shift"), "5", "tag", "5"),
(("Mod4", "Shift"), "6", "tag", "6"),
(("Mod4", "Shift"), "7", "tag", "7"),
(("Mod4", "Shift"), "8", "tag", "8"),
(("Mod4", "Shift"), "9", "tag", "9"),
(("Mod4", "Shift", "Control"), "0", "toggletag"),
(("Mod4", "Shift", "Control"), "1", "toggletag", "1"),
(("Mod4", "Shift", "Control"), "2", "toggletag", "2"),
(("Mod4", "Shift", "Control"), "3", "toggletag", "3"),
(("Mod4", "Shift", "Control"), "4", "toggletag", "4"),
(("Mod4", "Shift", "Control"), "5", "toggletag", "5"),
(("Mod4", "Shift", "Control"), "6", "toggletag", "6"),
(("Mod4", "Shift", "Control"), "7", "toggletag", "7"),
(("Mod4", "Shift", "Control"), "8", "toggletag", "8"),
(("Mod4", "Shift", "Control"), "9", "toggletag", "9")
);
};

280
layout.c Normal file
View File

@ -0,0 +1,280 @@
/* See LICENSE file for copyright and license details. */
#include <stdlib.h>
#include <string.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "jdwm.h"
#include "layout.h"
#include "tag.h"
#include "layouts/floating.h"
#include "util.h"
int blw = 0;
/* static */
static char prop[128];
/* extern */
extern Layout ** taglayouts;
extern int wax, way, wah, waw; /* windowarea geometry */
extern int bpos; /* bar position */
extern Window barwin;
extern Client *clients, *sel; /* global client list */
extern Bool *seltags;
extern Atom jdwmprops;
extern DC dc;
void
arrange(Display * disp, jdwm_config *jdwmconf)
{
Client *c;
for(c = clients; c; c = c->next)
if(isvisible(c, jdwmconf->ntags))
unban(c);
else
ban(c);
jdwmconf->current_layout->arrange(disp, jdwmconf);
focus(disp, &dc, NULL, jdwmconf);
restack(disp, jdwmconf);
}
void
uicb_focusnext(Display *disp __attribute__ ((unused)),
jdwm_config * jdwmconf,
const char *arg __attribute__ ((unused)))
{
Client *c;
if(!sel)
return;
for(c = sel->next; c && !isvisible(c, jdwmconf->ntags); c = c->next);
if(!c)
for(c = clients; c && !isvisible(c, jdwmconf->ntags); c = c->next);
if(c)
{
focus(c->display, &dc, c, jdwmconf);
restack(c->display, jdwmconf);
}
}
void
uicb_focusprev(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
Client *c;
if(!sel)
return;
for(c = sel->prev; c && !isvisible(c, jdwmconf->ntags); c = c->prev);
if(!c)
{
for(c = clients; c && c->next; c = c->next);
for(; c && !isvisible(c, jdwmconf->ntags); c = c->prev);
}
if(c)
{
focus(c->display, &dc, c, jdwmconf);
restack(c->display, jdwmconf);
}
}
void
initlayouts(jdwm_config * jdwmconf)
{
int w, i;
for(blw = i = 0; i < jdwmconf->nlayouts; i++)
{
w = textw(jdwmconf->layouts[i].symbol);
if(w > blw)
blw = w;
}
taglayouts = p_new(Layout *, jdwmconf->ntags);
for(i = 0; i < jdwmconf->ntags; i++)
taglayouts[i] = jdwmconf->layouts;
}
void
loadjdwmprops(Display *disp, jdwm_config * jdwmconf)
{
int i;
if(gettextprop(disp, DefaultRootWindow(disp), jdwmprops, prop, sizeof(prop)))
{
for(i = 0; i < jdwmconf->ntags && i < ssizeof(prop) - 1 && prop[i] != '\0'; i++)
seltags[i] = prop[i] == '1';
}
}
inline Client *
nexttiled(Client * c, int ntags)
{
for(; c && (c->isfloating || !isvisible(c, ntags)); c = c->next);
return c;
}
void
restack(Display * disp, jdwm_config *jdwmconf)
{
Client *c;
XEvent ev;
XWindowChanges wc;
drawstatus(disp, jdwmconf);
if(!sel)
return;
if(sel->isfloating || IS_ARRANGE(floating))
XRaiseWindow(disp, sel->win);
if(!IS_ARRANGE(floating))
{
wc.stack_mode = Below;
wc.sibling = barwin;
if(!sel->isfloating)
{
XConfigureWindow(disp, sel->win, CWSibling | CWStackMode, &wc);
wc.sibling = sel->win;
}
for(c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags))
{
if(c == sel)
continue;
XConfigureWindow(disp, c->win, CWSibling | CWStackMode, &wc);
wc.sibling = c->win;
}
}
XSync(disp, False);
while(XCheckMaskEvent(disp, EnterWindowMask, &ev));
}
void
savejdwmprops(Display *disp, jdwm_config *jdwmconf)
{
int i;
for(i = 0; i < jdwmconf->ntags && i < ssizeof(prop) - 1; i++)
prop[i] = seltags[i] ? '1' : '0';
prop[i] = '\0';
XChangeProperty(disp, DefaultRootWindow(disp), jdwmprops, XA_STRING, 8, PropModeReplace, (unsigned char *) prop, i);
}
void
uicb_setlayout(Display *disp, jdwm_config * jdwmconf, const char *arg)
{
int i, j;
Client *c;
if(!arg)
{
if(!(++jdwmconf->current_layout)->symbol)
jdwmconf->current_layout = &jdwmconf->layouts[0];
}
else
{
i = strtol(arg, NULL, 10);
if(i < 0 || i >= jdwmconf->nlayouts)
return;
jdwmconf->current_layout = &jdwmconf->layouts[i];
}
for(c = clients; c; c = c->next)
c->ftview = True;
if(sel)
arrange(disp, jdwmconf);
else
drawstatus(disp, jdwmconf);
savejdwmprops(disp, jdwmconf);
for(j = 0; j < jdwmconf->ntags; j++)
if (seltags[j])
taglayouts[j] = jdwmconf->current_layout;
}
void
uicb_togglebar(Display *disp,
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
if(bpos == BarOff)
bpos = (jdwmconf->bpos == BarOff) ? BarTop : jdwmconf->bpos;
else
bpos = BarOff;
updatebarpos(disp);
arrange(disp, jdwmconf);
}
static void
maximize(int x, int y, int w, int h, jdwm_config *jdwmconf)
{
XEvent ev;
if(!sel)
return;
if((sel->ismax = !sel->ismax))
{
sel->wasfloating = sel->isfloating;
sel->isfloating = True;
sel->rx = sel->x;
sel->ry = sel->y;
sel->rw = sel->w;
sel->rh = sel->h;
resize(sel, x, y, w, h, True);
}
else if(sel->isfloating)
resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
else
sel->isfloating = False;
drawstatus(sel->display, jdwmconf);
while(XCheckMaskEvent(sel->display, EnterWindowMask, &ev));
}
void
uicb_togglemax(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
maximize(wax, way, waw - 2 * jdwmconf->borderpx, wah - 2 * jdwmconf->borderpx, jdwmconf);
}
void
uicb_toggleverticalmax(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
if(sel)
maximize(sel->x, way, sel->w, wah - 2 * jdwmconf->borderpx, jdwmconf);
}
void
uicb_togglehorizontalmax(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
if(sel)
maximize(wax, sel->y, waw - 2 * jdwmconf->borderpx, sel->h, jdwmconf);
}
void
uicb_zoom(Display *disp __attribute__ ((unused)),
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
Client *c;
if(!sel || ((c = sel) == nexttiled(clients, jdwmconf->ntags) && !(c = nexttiled(c->next, jdwmconf->ntags))))
return;
detach(c);
attach(c);
focus(c->display, &dc, c, jdwmconf);
arrange(c->display, jdwmconf);
}

25
layout.h Normal file
View File

@ -0,0 +1,25 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_LAYOUT_H
#define JDWM_LAYOUT_H
#include "client.h"
#define IS_ARRANGE(layout) (layout == jdwmconf->current_layout->arrange)
void arrange(Display *, jdwm_config *); /* arranges all windows depending on the layout in use */
void initlayouts(jdwm_config *); /* initialize layout array */
Client *nexttiled(Client *, int); /* returns tiled successor of c */
void restack(Display *, jdwm_config *); /* restores z layers of all clients */
void uicb_focusnext(Display *, jdwm_config *, const char *); /* focuses next visible client */
void uicb_focusprev(Display *, jdwm_config *, const char *); /* focuses prev visible client */
void uicb_setlayout(Display *, jdwm_config *, const char *); /* sets layout, NULL means next layout */
void uicb_togglebar(Display *, jdwm_config *, const char *); /* shows/hides the bar */
void uicb_togglemax(Display *, jdwm_config *, const char *); /* toggles maximization of floating client */
void uicb_toggleverticalmax(Display *, jdwm_config *, const char *);
void uicb_togglehorizontalmax(Display *, jdwm_config *, const char *);
void uicb_zoom(Display *, jdwm_config *, const char *); /* set current window first in stack */
void loadjdwmprops(Display *, jdwm_config *);
void savejdwmprops(Display *disp, jdwm_config *);
#endif

25
layouts/floating.c Normal file
View File

@ -0,0 +1,25 @@
/* See LICENSE file for copyright and license details. */
#include "tag.h"
#include "layouts/floating.h"
/* extern */
extern Client *clients; /* global client */
void
floating(Display *disp __attribute__ ((unused)), jdwm_config *jdwmconf)
{ /* default floating layout */
Client *c;
for(c = clients; c; c = c->next)
if(isvisible(c, jdwmconf->ntags))
{
if(c->ftview)
{
resize(c, c->rx, c->ry, c->rw, c->rh, True);
c->ftview = False;
}
else
resize(c, c->x, c->y, c->w, c->h, True);
}
}

8
layouts/floating.h Normal file
View File

@ -0,0 +1,8 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_FLOATING_H
#define JDWM_FLOATING_H
void floating(Display *, jdwm_config *); /* floating layout */
#endif

50
layouts/grid.c Normal file
View File

@ -0,0 +1,50 @@
/* See LICENSE file for copyright and license details. */
#include "grid.h"
#include "layout.h"
#include "tag.h"
extern int wah, waw; /* windowarea geometry */
extern int bh, bpos; /* bar height, bar position */
extern Client *clients; /* global client list and stack */
extern DC dc;
void
grid(Display *disp, jdwm_config *jdwmconf)
{
unsigned int i, n, cx, cy, cw, ch, aw, ah, cols, rows;
Client *c;
for(n = 0, c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags))
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 = wah / (rows ? rows : 1);
cw = waw / (cols ? cols : 1);
for(i = 0, c = clients; c; c = c->next)
if(isvisible(c, jdwmconf->ntags))
{
unban(c);
if(c->isfloating)
continue;
c->ismax = False;
cx = (i / rows) * cw;
cy = (i % rows) * ch + (bpos == BarTop ? bh : 0); // bh? adjust
/* adjust height/width of last row/column's windows */
ah = ((i + 1) % rows == 0) ? wah - ch * rows : 0;
aw = (i >= rows * (cols - 1)) ? waw - cw * cols : 0;
resize(c, cx, cy, cw - 2 * c->border + aw, ch - 2 * c->border + ah, False);
i++;
}
else
ban(c);
focus(disp, &dc, NULL, jdwmconf);
restack(disp, jdwmconf);
}

11
layouts/grid.h Normal file
View File

@ -0,0 +1,11 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_GRID_H
#define JDWM_GRID_H
#include "config.h"
/* grid.c */
void grid(Display *, jdwm_config *);
#endif

77
layouts/spiral.c Normal file
View File

@ -0,0 +1,77 @@
/* See LICENSE file for copyright and license details. */
#include "layout.h"
#include "tag.h"
#include "spiral.h"
extern int wax, way, wah, waw; /* windowarea geometry */
extern Client *clients; /* global client list */
extern DC dc;
static void
fibonacci(Display *disp, jdwm_config *jdwmconf, int shape)
{
int n, nx, ny, nh, nw, i;
Client *c;
nx = wax;
ny = 0;
nw = waw;
nh = wah;
for(n = 0, c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags))
n++;
for(i = 0, c = clients; c; c = c->next)
{
c->ismax = False;
if((i % 2 && nh / 2 > 2 * c->border)
|| (!(i % 2) && nw / 2 > 2 * c->border))
{
if(i < n - 1)
{
if(i % 2)
nh /= 2;
else
nw /= 2;
if((i % 4) == 2 && !shape)
ny += nh;
}
if((i % 4) == 0)
{
if(shape)
ny += nh;
else
ny -= nh;
}
else if((i % 4) == 1)
nx += nw;
else if((i % 4) == 2)
ny += nh;
else if((i % 4) == 3)
{
if(shape)
nx += nw;
else
nx -= nw;
}
if(i == 0)
ny = way;
i++;
}
resize(c, nx, ny, nw - 2 * c->border, nh - 2 * c->border, False);
}
focus(disp, &dc, NULL, jdwmconf);
restack(disp, jdwmconf);
}
void
dwindle(Display *disp, jdwm_config *jdwmconf)
{
fibonacci(disp, jdwmconf, 1);
}
void
spiral(Display *disp, jdwm_config *jdwmconf)
{
fibonacci(disp, jdwmconf, 0);
}

9
layouts/spiral.h Normal file
View File

@ -0,0 +1,9 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_SPIRAL_H
#define JDWM_SPIRAL_H
void dwindle(Display *, jdwm_config *); /* dwindle windows */
void spiral(Display *, jdwm_config *); /* spiral windows */
#endif

201
layouts/tile.c Normal file
View File

@ -0,0 +1,201 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include "layout.h"
#include "layouts/tile.h"
/* extern */
extern int wax, way, wah, waw; /* windowarea geometry */
extern int bh; /* bar height */
extern Client *sel, *clients;
/* static */
static double mwfact = 0.6;
static void _tile(jdwm_config *, const Bool); /* arranges all windows tiled */
static int nmaster = 2;
void
uicb_incnmaster(Display *disp,
jdwm_config *jdwmconf,
const char * arg)
{
int i;
if(!IS_ARRANGE(tile) && !IS_ARRANGE(tileleft) && !IS_ARRANGE(bstack) && !IS_ARRANGE(bstackportrait))
return;
if(!arg)
nmaster = jdwmconf->nmaster;
else
{
i = strtol(arg, (char **) NULL, 10);
if((nmaster + i) < 1 || wah / (nmaster + i) <= 2 * jdwmconf->borderpx)
return;
nmaster += i;
}
if(sel)
arrange(disp, jdwmconf);
else
drawstatus(disp, jdwmconf);
}
void
uicb_setmwfact(Display *disp,
jdwm_config * jdwmconf,
const char *arg)
{
double delta;
if(!IS_ARRANGE(tile) && !IS_ARRANGE(tileleft) && !IS_ARRANGE(bstack) && !IS_ARRANGE(bstackportrait))
return;
/* arg handling, manipulate mwfact */
if(!arg)
mwfact = jdwmconf->mwfact;
else if(1 == sscanf(arg, "%lf", &delta))
{
if(arg[0] != '+' && arg[0] != '-')
mwfact = delta;
else
mwfact += delta;
if(mwfact < 0.1)
mwfact = 0.1;
else if(mwfact > 0.9)
mwfact = 0.9;
}
arrange(disp, jdwmconf);
}
static void
_tile(jdwm_config *jdwmconf, const Bool right)
{
unsigned int nx, ny, nw, nh, mw;
int n, th, i, mh;
Client *c;
for(n = 0, c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags))
n++;
/* window geoms */
mh = (n <= nmaster) ? wah / (n > 0 ? n : 1) : wah / nmaster;
mw = (n <= nmaster) ? waw : mwfact * waw;
th = (n > nmaster) ? wah / (n - nmaster) : 0;
if(n > nmaster && th < bh)
th = wah;
nx = wax;
ny = way;
for(i = 0, c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags), i++)
{
c->ismax = False;
if(i < nmaster)
{ /* master */
ny = way + i * mh;
if(!right && i == 0)
nx += (waw - mw);
nw = mw - 2 * c->border;
nh = mh;
if(i + 1 == (n < nmaster ? n : nmaster)) /* remainder */
nh = wah - mh * i;
nh -= 2 * c->border;
}
else
{ /* tile window */
if(i == nmaster)
{
ny = way;
if(right)
nx += mw;
else
nx = 0;
}
nw = waw - mw - 2 * c->border;
if(i + 1 == n) /* remainder */
nh = (way + wah) - ny - 2 * c->border;
else
nh = th - 2 * c->border;
}
resize(c, nx, ny, nw, nh, False);
if(n > nmaster && th != wah)
ny += nh + 2 * c->border;
}
}
void
tile(Display *disp __attribute__ ((unused)), jdwm_config *jdwmconf)
{
_tile(jdwmconf, True);
}
void
tileleft(Display *disp __attribute__ ((unused)), jdwm_config *jdwmconf)
{
_tile(jdwmconf, False);
}
static void
_bstack(jdwm_config *jdwmconf, Bool portrait)
{
int i, n, nx, ny, nw, nh, mw, mh, tw, th;
Client *c;
for(n = 0, c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags))
n++;
/* window geoms */
mh = (n > nmaster) ? (wah * mwfact) / nmaster : wah / (n > 0 ? n : 1);
mw = waw;
th = (n > nmaster) ? (wah * (1 - mwfact)) / (portrait ? 1 : n - nmaster) : 0;
tw = (n > nmaster) ? waw / (portrait ? n - nmaster : 1) : 0;
for(i = 0, c = nexttiled(clients, jdwmconf->ntags); c; c = nexttiled(c->next, jdwmconf->ntags), i++)
{
c->ismax = False;
nx = wax;
ny = way;
if(i < nmaster)
{
ny += i * mh;
nw = mw - 2 * c->border;
nh = mh - 2 * c->border;
}
else if(portrait)
{
nx += (i - nmaster) * tw;
ny += mh * nmaster;
nw = tw - 2 * c->border;
nh = th - 2 * c->border + 1;
}
else
{
ny += mh * nmaster;
nw = tw - 2 * c->border;
if(th > 2 * c->border)
{
ny += (i - nmaster) * th;
nh = th - 2 * c->border;
if (i == n - 1)
nh += (n > nmaster) ? wah - mh - th * (n - nmaster) : 0;
}
else
nh = wah - 2 * c->border;
}
resize(c, nx, ny, nw, nh, False);
}
}
void
bstack(Display *disp __attribute__ ((unused)), jdwm_config *jdwmconf)
{
_bstack(jdwmconf, False);
}
void
bstackportrait(Display *disp __attribute__ ((unused)), jdwm_config *jdwmconf)
{
_bstack(jdwmconf, True);
}

13
layouts/tile.h Normal file
View File

@ -0,0 +1,13 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_TILE_H
#define JDWM_TILE_H
void uicb_incnmaster(Display *, jdwm_config *, const char *); /* change number of master windows */
void uicb_setmwfact(Display *, jdwm_config *, const char *); /* sets master width factor */
void tile(Display *, jdwm_config *);
void tileleft(Display *, jdwm_config *);
void bstack(Display *, jdwm_config *);
void bstackportrait(Display *, jdwm_config *);
#endif

313
tag.c Normal file
View File

@ -0,0 +1,313 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xutil.h>
#include "util.h"
#include "layout.h"
#include "tag.h"
extern Client *sel; /* global client list */
extern Bool *seltags, *prevtags;
extern Layout ** taglayouts;
static Regs *regs = NULL;
static char prop[512];
/** This function returns the index of
* the tag given un argument in *tags
* \param tag_to_find tag name
* \return index of tag
*/
static int
idxoftag(const char *tag_to_find, const char **tags, int ntags)
{
int i;
if(!tag_to_find)
return 0;
for(i = 0; i < ntags; i++)
if(!strcmp(tags[i], tag_to_find))
return i;
return 0;
}
void
applyrules(Client * c, jdwm_config *jdwmconf)
{
int i, j;
regmatch_t tmp;
Bool matched = False;
XClassHint ch = { 0, 0 };
/* rule matching */
XGetClassHint(c->display, c->win, &ch);
snprintf(prop, sizeof(prop), "%s:%s:%s",
ch.res_class ? ch.res_class : "", ch.res_name ? ch.res_name : "", c->name);
for(i = 0; i < jdwmconf->nrules; i++)
if(regs[i].propregex && !regexec(regs[i].propregex, prop, 1, &tmp, 0))
{
c->isfloating = jdwmconf->rules[i].isfloating;
for(j = 0; regs[i].tagregex && j < jdwmconf->ntags; j++)
if(!regexec(regs[i].tagregex, jdwmconf->tags[j], 1, &tmp, 0))
{
matched = True;
c->tags[j] = True;
}
}
if(ch.res_class)
XFree(ch.res_class);
if(ch.res_name)
XFree(ch.res_name);
if(!matched)
for(i = 0; i < jdwmconf->ntags; i++)
c->tags[i] = seltags[i];
}
void
compileregs(jdwm_config * jdwmconf)
{
int i;
regex_t *reg;
if(regs)
return;
regs = p_new(Regs, jdwmconf->nrules);
for(i = 0; i < jdwmconf->nrules; i++)
{
if(jdwmconf->rules[i].prop)
{
reg = p_new(regex_t, 1);
if(regcomp(reg, jdwmconf->rules[i].prop, REG_EXTENDED))
p_delete(&reg);
else
regs[i].propregex = reg;
}
if(jdwmconf->rules[i].tags)
{
reg = p_new(regex_t, 1);
if(regcomp(reg, jdwmconf->rules[i].tags, REG_EXTENDED))
p_delete(&reg);
else
regs[i].tagregex = reg;
}
}
}
/** Returns True if a client is visible on
* one of the currently selected tag, false otherwise.
* \param c Client
* \return True or False
*/
Bool
isvisible(Client * c, int ntags)
{
int i;
for(i = 0; i < ntags; i++)
if(c->tags[i] && seltags[i])
return True;
return False;
}
/** Tag selected window with tag
* \param disp Display ref
* \param arg Tag name
* \ingroup ui_callback
*/
void
uicb_tag(Display *disp, jdwm_config *jdwmconf, const char *arg)
{
int i;
if(!sel)
return;
for(i = 0; i < jdwmconf->ntags; i++)
sel->tags[i] = arg == NULL;
i = idxoftag(arg, jdwmconf->tags, jdwmconf->ntags);
if(i >= 0 && i < jdwmconf->ntags)
sel->tags[i] = True;
saveprops(sel, jdwmconf->ntags);
arrange(disp, jdwmconf);
}
/** Toggle floating state of a client
* \param disp Display ref
* \param arg unused
* \ingroup ui_callback
*/
void
uicb_togglefloating(Display *disp,
jdwm_config * jdwmconf,
const char *arg __attribute__ ((unused)))
{
if(!sel)
return;
sel->isfloating = !sel->isfloating;
if(sel->isfloating)
/*restore last known float dimensions*/
resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
else
{
/*save last known float dimensions*/
sel->rx = sel->x;
sel->ry = sel->y;
sel->rw = sel->w;
sel->rh = sel->h;
}
saveprops(sel, jdwmconf->ntags);
arrange(disp, jdwmconf);
}
/** Toggle tag view
* \param disp Display ref
* \param arg Tag name
* \ingroup ui_callback
*/
void
uicb_toggletag(Display *disp,
jdwm_config *jdwmconf,
const char *arg)
{
unsigned int i;
int j;
if(!sel)
return;
i = idxoftag(arg, jdwmconf->tags, jdwmconf->ntags);
sel->tags[i] = !sel->tags[i];
for(j = 0; j < jdwmconf->ntags && !sel->tags[j]; j++);
if(j == jdwmconf->ntags)
sel->tags[i] = True;
saveprops(sel, jdwmconf->ntags);
arrange(disp, jdwmconf);
}
/** Add a tag to viewed tags
* \param disp Display ref
* \param arg Tag name
* \ingroup ui_callback
*/
void
uicb_toggleview(Display *disp,
jdwm_config *jdwmconf,
const char *arg)
{
unsigned int i;
int j;
i = idxoftag(arg, jdwmconf->tags, jdwmconf->ntags);
seltags[i] = !seltags[i];
for(j = 0; j < jdwmconf->ntags && !seltags[j]; j++);
if(j == jdwmconf->ntags)
seltags[i] = True; /* cannot toggle last view */
savejdwmprops(disp, jdwmconf);
arrange(disp, jdwmconf);
}
/**
* \param disp Display ref
* \param arg
* \ingroup ui_callback
*/
void
uicb_view(Display *disp,
jdwm_config *jdwmconf,
const char *arg)
{
int i;
for(i = 0; i < jdwmconf->ntags; i++)
{
prevtags[i] = seltags[i];
seltags[i] = arg == NULL;
}
i = idxoftag(arg, jdwmconf->tags, jdwmconf->ntags);
if(i >= 0 && i < jdwmconf->ntags)
{
seltags[i] = True;
jdwmconf->current_layout = taglayouts[i];
}
savejdwmprops(disp, jdwmconf);
arrange(disp, jdwmconf);
}
/** View previously selected tags
* \param disp Display ref
* \param arg unused
* \ingroup ui_callback
*/
void
uicb_viewprevtags(Display * disp,
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
int i;
Bool t;
for(i = 0; i < jdwmconf->ntags; i++)
{
t = seltags[i];
seltags[i] = prevtags[i];
prevtags[i] = t;
}
arrange(disp, jdwmconf);
}
/** View next tag
* \param disp Display ref
* \param arg unused
* \ingroup ui_callback
*/
void
uicb_tag_viewnext(Display *disp,
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
int i;
int firsttag = -1;
for(i = 0; i < jdwmconf->ntags; i++)
{
if(firsttag < 0 && seltags[i])
firsttag = i;
seltags[i] = False;
}
if(++firsttag >= jdwmconf->ntags)
firsttag = 0;
seltags[firsttag] = True;
savejdwmprops(disp, jdwmconf);
arrange(disp, jdwmconf);
}
/** View previous tag
* \param disp Display ref
* \param arg unused
* \ingroup ui_callback
*/
void
uicb_tag_viewprev(Display *disp,
jdwm_config *jdwmconf,
const char *arg __attribute__ ((unused)))
{
int i;
int firsttag = -1;
for(i = jdwmconf->ntags - 1; i >= 0; i--)
{
if(firsttag < 0 && seltags[i])
firsttag = i;
seltags[i] = False;
}
if(--firsttag < 0)
firsttag = jdwmconf->ntags - 1;
seltags[firsttag] = True;
savejdwmprops(disp, jdwmconf);
arrange(disp, jdwmconf);
}

27
tag.h Normal file
View File

@ -0,0 +1,27 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_TAG_H
#define JDWM_TAG_H
#include <regex.h>
#include "client.h"
void compileregs(jdwm_config *); /* initialize regexps of rules defined in config.h */
Bool isvisible(Client *, int); /* returns True if client is visible */
void applyrules(Client * c, jdwm_config *); /* applies rules to c */
void uicb_tag(Display *, jdwm_config *, const char *); /* tags sel with arg's index */
void uicb_togglefloating(Display *, jdwm_config *, const char *); /* toggles sel between floating/tiled state */
void uicb_toggletag(Display *, jdwm_config *, const char *); /* toggles sel tags with arg's index */
void uicb_toggleview(Display *, jdwm_config *, const char *); /* toggles the tag with arg's index (in)visible */
void uicb_view(Display *, jdwm_config *, const char *); /* views the tag with arg's index */
void uicb_viewprevtags(Display *, jdwm_config *, const char *);
void uicb_tag_viewnext(Display *, jdwm_config *, const char *); /* view only tag just after the first selected */
void uicb_tag_viewprev(Display *, jdwm_config *, const char *); /* view only tag just before the first selected */
typedef struct
{
regex_t *propregex;
regex_t *tagregex;
} Regs;
#endif

50
util.c Normal file
View File

@ -0,0 +1,50 @@
/* See LICENSE file for copyright and license details. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include "util.h"
void
eprint(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
void
spawn(Display * disp,
jdwm_config * jdwmconf __attribute__ ((unused)),
const char *arg)
{
static char *shell = NULL;
if(!shell && !(shell = getenv("SHELL")))
shell = strdup("/bin/sh");
if(!arg)
return;
/* The double-fork construct avoids zombie processes and keeps the code
* * clean from stupid signal handlers. */
if(fork() == 0)
{
if(fork() == 0)
{
if(disp)
close(ConnectionNumber(disp));
setsid();
execl(shell, shell, "-c", arg, (char *) NULL);
fprintf(stderr, "jdwm: execl '%s -c %s'", shell, arg);
perror(" failed");
}
exit(EXIT_SUCCESS);
}
wait(0);
}

66
util.h Normal file
View File

@ -0,0 +1,66 @@
/* See LICENSE file for copyright and license details. */
#ifndef JDWM_MEM_H
#define JDWM_MEM_H
#include "config.h"
#define ssizeof(foo) (ssize_t)sizeof(foo)
#define countof(foo) (ssizeof(foo) / ssizeof(foo[0]))
#define p_new(type, count) ((type *)xmalloc(sizeof(type) * (count)))
#define p_clear(p, count) ((void)memset((p), 0, sizeof(*(p)) * (count)))
#define p_realloc(pp, count) xrealloc((void*)(pp) sizeof(**(pp) * (count)))
#ifdef __GNUC__
#define p_delete(mem_pp) \
do { \
typeof(**(mem_pp)) **__ptr = (mem_pp); \
free(*__ptr); \
*__ptr = NULL; \
} while(0)
#else
#define p_delete(mem_p) \
do { \
void *__ptr = (mem_p); \
free(*__ptr); \
*(void **)__ptr = NULL; \
} while (0)
#endif
static inline void * __attribute__ ((malloc)) xmalloc(ssize_t size)
{
void *ptr;
if(size <= 0)
return NULL;
ptr = calloc(1, size);
if(!ptr)
abort();
return ptr;
}
static inline void
xrealloc(void **ptr, ssize_t newsize)
{
if(newsize <= 0)
p_delete(ptr);
else
{
*ptr = realloc(*ptr, newsize);
if(!*ptr)
abort();
}
}
void eprint(const char *, ...) __attribute__ ((noreturn)) __attribute__ ((format(printf, 1, 2)));
void spawn(Display *, jdwm_config *, const char *);
#endif