/* appearance */
static const char font[] = "YOzNFb 8";
-static const char normbordercolor[] = "#000000";
-static const char normbgcolor[] = "#000000";
-static const char normfgcolor[] = "#bbbbbb";
-static const char selbordercolor[] = "#0048bf";
-static const char selbgcolor[] = "#000000";
-static const char selfgcolor[] = "#0048bf"; /* default: eeeeee */
+
+static struct color_set theme[PalLast] = {
+ { /* PalNormal */
+ .border = "#ffffff",
+ .background = "#ffffff",
+ .foreground = "#000000"
+ }, { /* PalSelected */
+ .border = "#bb2323",
+ .background = "#bb2323",
+ .foreground = "#ffffff"
+ }, { /* PalNotSelected */
+ .border = "#486c9c",
+ .background = "#486c9c",
+ .foreground = "#ffffff"
+ }, { /* PalOther */
+ .border = "#d2d2d2",
+ .background = "#d2d2d2",
+ .foreground = "#ffffff"
+ }
+};
+
+static const char dmenu_normal_bg[] = "#ffffff";
+static const char dmenu_normal_fg[] = "#000000";
+static const char dmenu_sel_bg[] = "#bb2323";
+static const char dmenu_sel_fg[] = "#ffffff";
+
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const Bool topbar = True; /* False means bottom bar */
static const Bool statusmarkup = True; /* True means use pango markup in status message */
/* tagging */
-static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" };
static const struct rule rules[] = {
- /* class instance title tags mask isfloating monitor */
+ /* class instance title tags mask isfloating workspace */
{ "Gimp", NULL, NULL, 0, True, -1 },
{ "Firefox", NULL, NULL, 0, False, -1 },
};
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
- { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
- { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
- { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
- { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
+ { MODKEY, KEY, show_workspace, {.ui = TAG} }, \
+ { MODKEY|ShiftMask, KEY, move_focused, {.ui = TAG} },
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
-static const char *dmenucmd[] = { "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
+static const char *dmenucmd[] = { "dmenu_run",
+ "-fn", font,
+ "-nb", dmenu_normal_bg,
+ "-nf", dmenu_normal_fg,
+ "-sb", dmenu_sel_bg,
+ "-sf", dmenu_sel_fg,
+ NULL };
static const char *termcmd[] = { "uxterm", NULL };
static struct key keys[] = {
- /* modifier key function argument */
-
- /* get the zap keyboard shortcut back */
- { MODKEY|ControlMask, XK_BackSpace, quit, {0} },
- /* invoke dmenu */
- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
- /* open a terminal */
- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
-#ifndef M10K
- { MODKEY, XK_b, togglebar, {0} },
-#endif /* ! M10K */
- /* move focus to left screen */
- { MODKEY, XK_q, focusmon, {.i = -1 } },
- /* move window to left screen */
- { MODKEY|ShiftMask, XK_q, tagmon, {.i = -1 } },
- /* move focus to right screen */
- { MODKEY, XK_e, focusmon, {.i = +1 } },
- /* move window to right screen */
- { MODKEY|ShiftMask, XK_e, tagmon, {.i = +1 } },
- /* focus on left/above window (bookshelf/bookstack) */
- { MODKEY, XK_a, focusstack, {.i = -1 } },
- /* focus on right/below window (bookshelf/bookstack) */
- { MODKEY, XK_d, focusstack, {.i = +1 } },
- /* move window to master */
- { MODKEY, XK_Return, zoom, {0} },
- /* move to previous tag */
- { MODKEY, XK_Tab, view, {0} },
- /* kill focused window */
- { MODKEY|ShiftMask, XK_x, killclient, {0} },
- { MODKEY, XK_t, setlayout, {.v = &layouts[LAYOUT_BOOKSHELF]} },
- { MODKEY, XK_y, setlayout, {.v = &layouts[LAYOUT_BOOKSTACK]} },
- { MODKEY, XK_r, setlayout, {.v = &layouts[LAYOUT_FLOAT]} },
- { MODKEY, XK_i, kbptr_move, {.ui = KBPTR_NORTH} },
- { MODKEY | ShiftMask, XK_i, kbptr_move, {.ui = KBPTR_NORTH | KBPTR_HALFSTEP} },
- { MODKEY, XK_l, kbptr_move, {.ui = KBPTR_EAST} },
- { MODKEY | ShiftMask, XK_l, kbptr_move, {.ui = KBPTR_EAST | KBPTR_HALFSTEP} },
- { MODKEY, XK_k, kbptr_move, {.ui = KBPTR_SOUTH} },
- { MODKEY | ShiftMask, XK_k, kbptr_move, {.ui = KBPTR_SOUTH | KBPTR_HALFSTEP} },
- { MODKEY, XK_j, kbptr_move, {.ui = KBPTR_WEST} },
- { MODKEY | ShiftMask, XK_j, kbptr_move, {.ui = KBPTR_WEST | KBPTR_HALFSTEP} },
- { MODKEY, XK_u, kbptr_move, {.ui = KBPTR_CENTER} },
- { MODKEY, XK_semicolon, kbptr_click, {.ui = KBPTR_LEFT} },
- { MODKEY | ShiftMask, XK_semicolon, kbptr_click, {.ui = KBPTR_RIGHT} },
- { MODKEY | ControlMask, XK_semicolon, kbptr_click, {.ui = KBPTR_MIDDLE} },
-#ifndef M10K
- { MODKEY, XK_space, setlayout, {0} },
- { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
-#endif /* ! M10K */
- /* show all tags */
- { MODKEY, XK_0, view, {.ui = ~0 } },
- /* move focused window to all tags */
- { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
- TAGKEYS( XK_1, 0)
- TAGKEYS( XK_2, 1)
- TAGKEYS( XK_3, 2)
- TAGKEYS( XK_4, 3)
- TAGKEYS( XK_5, 4)
- TAGKEYS( XK_6, 5)
- TAGKEYS( XK_7, 6)
- TAGKEYS( XK_8, 7)
- TAGKEYS( XK_9, 8)
+ /* modifier key function argument */
+
+ /* get the zap keyboard shortcut back */
+ { MODKEY|ControlMask, XK_BackSpace, quit, {0} },
+ /* invoke dmenu */
+ { MODKEY, XK_p, spawn, {.v = dmenucmd} },
+ /* open a terminal */
+ { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd} },
+ /* move focus to left screen */
+ { MODKEY, XK_q, focusmon, {.i = -1} },
+ /* move window to left screen */
+ { MODKEY|ShiftMask, XK_q, tagmon, {.i = -1} },
+ /* move focus to right screen */
+ { MODKEY, XK_e, focusmon, {.i = +1} },
+ /* move window to right screen */
+ { MODKEY|ShiftMask, XK_e, tagmon, {.i = +1} },
+ /* focus on left/above window (bookshelf/bookstack) */
+ { MODKEY, XK_a, focusstack, {.i = -1} },
+ /* focus on right/below window (bookshelf/bookstack) */
+ { MODKEY, XK_d, focusstack, {.i = +1} },
+ /* move to previous tag */
+ { MODKEY, XK_Tab, show_workspace, {.i = -1} },
+ /* kill focused window */
+ { MODKEY|ShiftMask, XK_x, killclient, {0} },
+
+ { MODKEY, XK_t, setlayout, { .v = &layouts[LAYOUT_BOOKSHELF]} },
+ { MODKEY, XK_y, setlayout, { .v = &layouts[LAYOUT_BOOKSTACK]} },
+
+ { MODKEY, XK_i, kbptr_move, {.ui = KBPTR_NORTH} },
+ { MODKEY | ShiftMask, XK_i, kbptr_move, {.ui = KBPTR_NORTH | KBPTR_HALFSTEP} },
+ { MODKEY, XK_l, kbptr_move, {.ui = KBPTR_EAST} },
+ { MODKEY | ShiftMask, XK_l, kbptr_move, {.ui = KBPTR_EAST | KBPTR_HALFSTEP} },
+ { MODKEY, XK_k, kbptr_move, {.ui = KBPTR_SOUTH} },
+ { MODKEY | ShiftMask, XK_k, kbptr_move, {.ui = KBPTR_SOUTH | KBPTR_HALFSTEP} },
+ { MODKEY, XK_j, kbptr_move, {.ui = KBPTR_WEST} },
+ { MODKEY | ShiftMask, XK_j, kbptr_move, {.ui = KBPTR_WEST | KBPTR_HALFSTEP} },
+ { MODKEY, XK_u, kbptr_move, {.ui = KBPTR_CENTER} },
+ { MODKEY, XK_semicolon, kbptr_click, {.ui = KBPTR_LEFT} },
+ { MODKEY | ShiftMask, XK_semicolon, kbptr_click, {.ui = KBPTR_RIGHT} },
+ { MODKEY | ControlMask, XK_semicolon, kbptr_click, {.ui = KBPTR_MIDDLE} },
+
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ TAGKEYS( XK_0, 9)
+ TAGKEYS( XK_minus, 10)
+ TAGKEYS( XK_asciicircum, 11)
};
/* button definitions */
/* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static struct button buttons[] = {
- /* click event mask button function argument */
- { ClkLtSymbol, 0, Button1, setlayout, {0} },
- { ClkWinTitle, 0, Button2, zoom, {0} },
-#ifndef M10K
- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
-#endif /* ! M10K */
- { ClkClientWin, MODKEY, Button1, movemouse, {0} },
- { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
- { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
- { ClkTagBar, 0, Button1, view, {0} },
- { ClkTagBar, 0, Button3, toggleview, {0} },
- { ClkTagBar, MODKEY, Button1, tag, {0} },
- { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+ /* click event mask button function argument */
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, show_workspace, {0} },
+ { ClkTagBar, MODKEY, Button1, move_focused, {0} },
};
* 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 linked client
- * list on each monitor, the focus history is remembered through a stack list
- * on each monitor. Each client contains a bit array to indicate the tags of a
- * client.
- *
* Keys and tagging rules are organized as arrays and defined in config.h.
*
* To understand everything else, start reading main().
#include <X11/Xft/Xft.h>
#include <pango/pango.h>
#include <pango/pangoxft.h>
-#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
-#endif /* XINERAMA */
#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
#define ALLMODMASK (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
#define ALLMASK (ShiftMask | ControlMask | ALLMODMASK)
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & ALLMASK)
-#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
+#define ISVISIBLE(C) ((C->workspace->viewer))
#define LENGTH(X) (sizeof X / sizeof X[0])
#ifndef MAX
#define MAX(A, B) ((A) > (B) ? (A) : (B))
ColLast
};
+enum {
+ PalNormal,
+ PalSelected,
+ PalNotSelected,
+ PalOther,
+ PalLast
+};
+
/* EWMH atoms */
enum {
NetSupported,
int h;
};
+struct workspace;
+struct client;
+struct monitor;
+
struct client {
char name[256];
float mina;
int w;
int oldw;
} border;
- unsigned int tags;
Bool isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
struct client *next;
- struct client *snext;
- struct monitor *mon;
+ struct workspace *workspace;
Window win;
};
+struct palette {
+ unsigned long color[ColLast];
+ XftColor xcolor[ColLast];
+};
+
+struct color_set {
+ char *border;
+ char *background;
+ char *foreground;
+};
+
struct draw_context {
struct rect geom;
- unsigned long norm[ColLast];
- unsigned long sel[ColLast];
+ struct palette palette[PalLast];
Drawable drawable;
GC gc;
-
- struct {
- XftColor norm[ColLast];
- XftColor sel[ColLast];
- XftDraw *drawable;
- } xft;
+ XftDraw *draw;
struct {
int ascent;
struct rect geom;
struct rect win_geom;
- unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
+ struct workspace *workspace;
+ struct workspace *previous_workspace;
Bool topbar;
- struct client *clients;
- struct client *sel;
- struct client *stack;
struct monitor *next;
Window barwin;
const struct layout *lt[2];
};
+struct workspace {
+ struct client *clients;
+ struct client *focused;
+ struct monitor *viewer;
+ int num;
+};
+
struct rule {
const char *class;
const char *instance;
const char *title;
unsigned int tags;
Bool isfloating;
- int monitor;
+ int workspace;
};
#define KBPTR_CENTER 0
static struct kbptr kbptr = { 0, 0, 0, 0 };
-/* function declarations */
-static void applyrules(struct client *c);
-static Bool applysizehints(struct client *c, int *x, int *y,
- int *w, int *h, Bool interact);
-static void arrange(struct monitor *m);
-static void arrangemon(struct monitor *m);
-static void attach(struct client *c);
-static void attachstack(struct client *c);
+static void workspace_add_client(struct workspace *workspace,
+ struct client *client);
+static void workspace_remove_client(struct workspace *workspace,
+ struct client *client);
+static struct workspace* workspace_get_current(void);
+static int workspace_is_urgent(struct workspace *workspace);
+static void workspace_update(struct workspace *workspace);
+
+static int monitor_has_focus(struct monitor *mon);
+static void monitor_focus(struct monitor *mon);
+static void monitor_free(struct monitor *mon);
+static int monitor_is_floating(struct monitor *mon);
+static void monitor_resize_barwin(struct monitor *m);
+
+static void client_move_to_workspace(struct client *client, struct workspace *workspace);
+
+static void client_apply_rules(struct client *c);
+static Bool client_apply_size_hints(struct client *c, int *x, int *y,
+ int *w, int *h, Bool interact);
+static void monitor_arrange_clients(struct monitor *m);
static void buttonpress(XEvent *e);
static int checkotherwm(void);
static void cleanup(void);
-static void cleanupmon(struct monitor *mon);
static void clearurgent(struct client *c);
static void clientmessage(XEvent *e);
-static void configure(struct client *c);
+static void client_configure(struct client *c);
static void configurenotify(XEvent *e);
static void configurerequest(XEvent *e);
static struct monitor *createmon(void);
static void destroynotify(XEvent *e);
static void detach(struct client *c);
-static void detachstack(struct client *c);
static void die(const char *errstr, ...);
static struct monitor *dirtomon(int dir);
static void drawbar(struct monitor *m);
static void drawbars(void);
-static void drawsquare(Bool filled, Bool empty, Bool invert,
- unsigned long col[ColLast]);
-static void drawtext(const char *text, unsigned long col[ColLast],
- Bool invert);
+static void drawsquare(Bool filled, Bool empty, Bool invert, int palette);
+static void drawtext(const char *text, int pal, Bool invert);
static void enternotify(XEvent *e);
static void expose(XEvent *e);
static void focus(struct client *c);
static void focusin(XEvent *e);
-static void focusmon(const union arg *arg);
-static void focusstack(const union arg *arg);
+
static unsigned long getcolor(const char *colstr, XftColor *color);
static Bool getrootptr(int *x, int *y);
static long getstate(Window w);
static void grabkeys(void);
static void initfont(const char *fontstr);
static void keypress(XEvent *e);
-static void killclient(const union arg *arg);
+
static void manage(Window w, XWindowAttributes *wa);
static void mappingnotify(XEvent *e);
static void maprequest(XEvent *e);
static void motionnotify(XEvent *e);
-static void movemouse(const union arg *arg);
+
static struct client *nexttiled(struct client *c);
-static void pop(struct client *);
static void propertynotify(XEvent *e);
-static void quit(const union arg *arg);
+
static struct monitor *recttomon(int x, int y, int w, int h);
-static void resize(struct client *c, int x, int y, int w, int h, Bool interact);
-static void resizebarwin(struct monitor *m);
-static void resizeclient(struct client *c, int x, int y, int w, int h);
-static void resizemouse(const union arg *arg);
+static void client_resize_hints(struct client *c, int x, int y, int w, int h, Bool interact);
+static void client_resize(struct client *c, int x, int y, int w, int h);
+
static void restack(struct monitor *m);
static void run(void);
static void scan(void);
static void setclientstate(struct client *c, long state);
static void setfocus(struct client *c);
static void setfullscreen(struct client *c, Bool fullscreen);
-static void setlayout(const union arg *arg);
static void setup(void);
static void showhide(struct client *c);
static void sigchld(int unused);
-static void spawn(const union arg *arg);
-static void tag(const union arg *arg);
-static void tagmon(const union arg *arg);
+
static int textnw(const char *text, unsigned int len);
-static void togglefloating(const union arg *arg);
-static void toggletag(const union arg *arg);
-static void toggleview(const union arg *arg);
+
static void unfocus(struct client *c, Bool setfocus);
static void unmanage(struct client *c, Bool destroyed);
static void unmapnotify(XEvent *e);
static void updatebarpos(struct monitor *m);
static void updatebars(void);
static void updatenumlockmask(void);
-static void updatesizehints(struct client *c);
+static void client_update_size_hints(struct client *c);
static void updatestatus(void);
-static void updatewindowtype(struct client *c);
-static void updatetitle(struct client *c);
-static void updatewmhints(struct client *c);
-static void view(const union arg *arg);
+static void client_update_window_type(struct client *c);
+static void client_update_title(struct client *c);
+static void client_update_wm_hints(struct client *c);
static struct client *wintoclient(Window w);
static struct monitor *wintomon(Window w);
static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
-static void zoom(const union arg *arg);
static Atom getatomprop(struct client *c, Atom prop);
static void bookshelf(struct monitor *mon);
static void bookstack(struct monitor *mon);
+static void spawn(const union arg *arg);
+static void tagmon(const union arg *arg);
+static void togglefloating(const union arg *arg);
+static void show_workspace(const union arg *arg);
+static void move_focused(const union arg *arg);
+static void focusmon(const union arg *arg);
+static void focusstack(const union arg *arg);
+static void killclient(const union arg *arg);
+static void movemouse(const union arg *arg);
+static void quit(const union arg *arg);
+static void resizemouse(const union arg *arg);
+static void setlayout(const union arg *arg);
+
+
static const char broken[] = "broken";
static char stext[512];
static int screen;
static int screen_width;
static int screen_height;
+static int num_monitors = 0;
static int bar_height;
static int bar_lwidth = 0; /* bar geometry */
static int (*xerrorxlib)(Display *, XErrorEvent *);
static struct monitor *mons = NULL;
static struct monitor *selmon = NULL;
static Window root;
+static struct workspace workspaces[12] = { 0 };
/* configuration, allows nested code to access above variables */
#include "config.h"
char limitexceeded[LENGTH(tags) > 31 ? -1 : 1];
};
-/* function implementations */
-void applyrules(struct client *c)
+static int rule_match(struct rule const *rule, XClassHint *class_hint, const char *client_name)
{
const char *class, *instance;
- unsigned int i;
- const struct rule *r;
- struct monitor *m;
- XClassHint ch = { NULL, NULL };
- /* rule matching */
- c->isfloating = c->tags = 0;
- XGetClassHint(dpy, c->win, &ch);
- class = ch.res_class ? ch.res_class : broken;
- instance = ch.res_name ? ch.res_name : broken;
+ class = class_hint->res_class ? class_hint->res_class : broken;
+ instance = class_hint->res_name ? class_hint->res_name : broken;
- for(i = 0; i < LENGTH(rules); i++) {
- r = &rules[i];
+ if(rule->title && !strstr(client_name, rule->title)) {
+ return(FALSE);
+ }
- if((!r->title || strstr(c->name, r->title)) &&
- (!r->class || strstr(class, r->class)) &&
- (!r->instance || strstr(instance, r->instance))) {
- c->isfloating = r->isfloating;
- c->tags |= r->tags;
+ if(rule->class && !strstr(class, rule->class)) {
+ return(FALSE);
+ }
- for(m = mons; m && m->num != r->monitor; m = m->next);
+ if(rule->instance && !strstr(instance, rule->instance)) {
+ return(FALSE);
+ }
- if(m) {
- c->mon = m;
+ return(TRUE);
+}
+
+void client_apply_rules(struct client *client)
+{
+ struct workspace *workspace;
+ XClassHint class_hint;
+ unsigned int i;
+
+ memset(&class_hint, 0, sizeof(class_hint));
+ client->isfloating = 0;
+ workspace = workspace_get_current();
+
+ XGetClassHint(dpy, client->win, &class_hint);
+
+ for(i = 0; i < LENGTH(rules); i++) {
+ if(rule_match(&rules[i], &class_hint, client->name)) {
+ client->isfloating = rules[i].isfloating;
+
+ if(rules[i].workspace > 0) {
+ workspace = &workspaces[rules[i].workspace];
}
}
}
- if(ch.res_class) {
- XFree(ch.res_class);
+ if(class_hint.res_class) {
+ XFree(class_hint.res_class);
}
- if(ch.res_name) {
- XFree(ch.res_name);
+ if(class_hint.res_name) {
+ XFree(class_hint.res_name);
}
- c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
+ client_move_to_workspace(client, workspace);
return;
}
return(client->geom.h + 2 * client->border.w);
}
-Bool applysizehints(struct client *c, int *x, int *y, int *w, int *h, Bool interact)
+static Bool client_apply_size_hints(struct client *c,
+ int *x, int *y, int *w, int *h,
+ Bool interact)
{
Bool baseismin;
- struct monitor *m;
+ struct monitor *mon;
- m = c->mon;
+ mon = c->workspace->viewer;
/* set minimum possible */
*w = MAX(1, *w);
*y = 0;
}
} else {
- if(*x >= m->win_geom.x + m->win_geom.w) {
- *x = m->win_geom.x + m->win_geom.w - client_get_width(c);
+ if(*x >= mon->win_geom.x + mon->win_geom.w) {
+ *x = mon->win_geom.x + mon->win_geom.w - client_get_width(c);
}
- if(*y >= m->win_geom.y + m->win_geom.h) {
- *y = m->win_geom.y + m->win_geom.h - client_get_height(c);
+ if(*y >= mon->win_geom.y + mon->win_geom.h) {
+ *y = mon->win_geom.y + mon->win_geom.h - client_get_height(c);
}
- if(*x + *w + 2 * c->border.w <= m->win_geom.x) {
- *x = m->win_geom.x;
+ if(*x + *w + 2 * c->border.w <= mon->win_geom.x) {
+ *x = mon->win_geom.x;
}
- if(*y + *h + 2 * c->border.w <= m->win_geom.y) {
- *y = m->win_geom.y;
+ if(*y + *h + 2 * c->border.w <= mon->win_geom.y) {
+ *y = mon->win_geom.y;
}
}
*w = bar_height;
}
- if(resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+ if(resizehints || c->isfloating || (mon && !monitor_is_floating(mon))) {
/*
* "The min_aspect and max_aspect fields are fractions with the
* numerator first and the denominator second, and they allow a
return(*x != c->geom.x || *y != c->geom.y || *w != c->geom.w || *h != c->geom.h);
}
-void arrange(struct monitor *m)
+void workspace_update(struct workspace *workspace)
{
- if(m) {
- showhide(m->stack);
- } else {
- for(m = mons; m; m = m->next) {
- showhide(m->stack);
- }
- }
+ if(workspace) {
+ showhide(workspace->clients);
- if(m) {
- arrangemon(m);
+ if(workspace->viewer) {
+ monitor_arrange_clients(workspace->viewer);
+ }
} else {
- for(m = mons; m; m = m->next) {
- arrangemon(m);
+ int i;
+
+ for(i = 0; i < LENGTH(workspaces); i++) {
+ workspace_update(&workspaces[i]);
}
}
return;
}
-void arrangemon(struct monitor *m)
+void monitor_arrange_clients(struct monitor *monitor)
{
- strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
+ strncpy(monitor->ltsymbol,
+ monitor->lt[monitor->sellt]->symbol,
+ sizeof(monitor->ltsymbol));
- if(m->lt[m->sellt]->arrange) {
- m->lt[m->sellt]->arrange(m);
+ if(!monitor_is_floating(monitor)) {
+ monitor->lt[monitor->sellt]->arrange(monitor);
}
- restack(m);
+ restack(monitor);
return;
}
-void attach(struct client *c)
+static void monitor_focus(struct monitor *mon)
{
- c->next = c->mon->clients;
- c->mon->clients = c;
+ if(selmon->workspace->focused) {
+ unfocus(selmon->workspace->focused, True);
+ }
+
+ selmon = mon;
+ focus(NULL);
return;
}
-void attachstack(struct client *c)
+static int monitor_has_focus(struct monitor *mon)
{
- c->snext = c->mon->stack;
- c->mon->stack = c;
+ if(mon == selmon) {
+ return(TRUE);
+ }
- return;
+ return(FALSE);
}
-void buttonpress(XEvent *e)
+void buttonpress(XEvent *event)
{
unsigned int i, x, click;
- union arg arg = {0};
- struct client *c;
- struct monitor *m;
- XButtonPressedEvent *ev = &e->xbutton;
+ union arg arg;
+ struct client *client;
+ struct monitor *event_monitor;
+ XButtonPressedEvent *ev;
+ ev = &event->xbutton;
+ arg.ui = 0;
click = ClkRootWin;
- /* focus monitor if necessary */
- if((m = wintomon(ev->window)) && m != selmon) {
- unfocus(selmon->sel, True);
- selmon = m;
- focus(NULL);
+ event_monitor = wintomon(ev->window);
+
+ if(event_monitor && !monitor_has_focus(event_monitor)) {
+ monitor_focus(event_monitor);
}
if(ev->window == selmon->barwin) {
- i = x = 0;
+ i = 0;
+ x = 0;
do {
x += TEXTW(tags[i]);
if(i < LENGTH(tags)) {
click = ClkTagBar;
- arg.ui = 1 << i;
+ arg.ui = i;
} else if(ev->x < x + bar_lwidth) {
click = ClkLtSymbol;
} else if(ev->x > selmon->win_geom.w - TEXTW(stext)) {
} else {
click = ClkWinTitle;
}
- } else if((c = wintoclient(ev->window))) {
- focus(c);
+ } else if((client = wintoclient(ev->window))) {
+ focus(client);
click = ClkClientWin;
}
void cleanup(void)
{
struct layout foo;
- struct monitor *m;
union arg a;
int i;
foo.symbol = "";
foo.arrange = NULL;
- view(&a);
+ show_workspace(&a);
selmon->lt[selmon->sellt] = &foo;
- for(m = mons; m; m = m->next) {
- while(m->stack) {
- unmanage(m->stack, False);
+ for(i = 0; i < LENGTH(workspaces); i++) {
+ while(workspaces[i].clients) {
+ unmanage(workspaces[i].clients, False);
}
}
XUngrabKey(dpy, AnyKey, AnyModifier, root);
XFreePixmap(dpy, dc.drawable);
- for(i = ColBorder; i < ColLast; i++) {
- XftColorFree(dpy, DefaultVisual(dpy, screen),
- DefaultColormap(dpy, screen),
- dc.xft.norm + i);
- XftColorFree(dpy, DefaultVisual(dpy, screen),
- DefaultColormap(dpy, screen),
- dc.xft.sel + i);
+ for(i = 0; i < PalLast; i++) {
+ int col;
+
+ for(col = 0; col < ColLast; col++) {
+ XftColorFree(dpy, DefaultVisual(dpy, screen),
+ DefaultColormap(dpy, screen),
+ &dc.palette[i].xcolor[col]);
+ }
}
- XftDrawDestroy(dc.xft.drawable);
+ XftDrawDestroy(dc.draw);
g_object_unref(dc.font.layout);
XFreeGC(dpy, dc.gc);
XFreeCursor(dpy, cursor[CurNormal]);
XFreeCursor(dpy, cursor[CurMove]);
while(mons) {
- cleanupmon(mons);
+ monitor_free(mons);
}
XSync(dpy, False);
return;
}
-void cleanupmon(struct monitor *mon)
+static void monitor_free(struct monitor *mon)
{
- struct monitor *m;
+ struct monitor **cur;
- if(mon == mons) {
- mons = mons->next;
- } else {
- for(m = mons; m && m->next != mon; m = m->next);
- m->next = mon->next;
+ for(cur = &mons; *cur; cur = &((*cur)->next)) {
+ if(*cur == mon) {
+ *cur = mon->next;
+ break;
+ }
}
XUnmapWindow(dpy, mon->barwin);
XDestroyWindow(dpy, mon->barwin);
free(mon);
+ num_monitors--;
return;
}
-void clearurgent(struct client *c)
+void clearurgent(struct client *client)
{
- XWMHints *wmh;
+ XWMHints *hints;
- c->isurgent = False;
- if(!(wmh = XGetWMHints(dpy, c->win)))
+ client->isurgent = False;
+ hints = XGetWMHints(dpy, client->win);
+
+ if(!hints) {
return;
- wmh->flags &= ~XUrgencyHint;
- XSetWMHints(dpy, c->win, wmh);
- XFree(wmh);
+ }
+
+ hints->flags &= ~XUrgencyHint;
+ XSetWMHints(dpy, client->win, hints);
+ XFree(hints);
return;
}
-void clientmessage(XEvent *e)
+void clientmessage(XEvent *event)
{
- XClientMessageEvent *cme;
- struct client *c;
+ XClientMessageEvent *cmsg_event;
+ struct client *client;
- cme = &e->xclient;
- c = wintoclient(cme->window);
+ cmsg_event = &event->xclient;
+ client = wintoclient(cmsg_event->window);
- if(!c) {
+ if(!client) {
return;
}
- if(cme->message_type == netatom[NetWMState]) {
- if(cme->data.l[1] == netatom[NetWMFullscreen] ||
- cme->data.l[2] == netatom[NetWMFullscreen]) {
+ if(cmsg_event->message_type == netatom[NetWMState]) {
+ if(cmsg_event->data.l[1] == netatom[NetWMFullscreen] ||
+ cmsg_event->data.l[2] == netatom[NetWMFullscreen]) {
int fs;
- fs = cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ ||
- (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ &&
- !c->isfullscreen);
+ fs = cmsg_event->data.l[0] == 1 /* _NET_WM_STATE_ADD */ ||
+ (cmsg_event->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ &&
+ !client->isfullscreen);
- setfullscreen(c, fs);
+ setfullscreen(client, fs);
}
- } else if(cme->message_type == netatom[NetActiveWindow]) {
- if(!ISVISIBLE(c)) {
- c->mon->seltags ^= 1;
- c->mon->tagset[c->mon->seltags] = c->tags;
+ } else if(cmsg_event->message_type == netatom[NetActiveWindow]) {
+ focus(client);
+
+ if(client->workspace->viewer) {
+ workspace_update(client->workspace);
}
- pop(c);
}
return;
}
-void configure(struct client *c)
+void client_configure(struct client *c)
{
XConfigureEvent ce;
dc.drawable = XCreatePixmap(dpy, root, screen_width, bar_height,
DefaultDepth(dpy, screen));
- XftDrawChange(dc.xft.drawable, dc.drawable);
+ XftDrawChange(dc.draw, dc.drawable);
updatebars();
for(m = mons; m; m = m->next) {
- resizebarwin(m);
+ monitor_resize_barwin(m);
}
focus(NULL);
- arrange(NULL);
+ workspace_update(NULL);
}
}
if((c = wintoclient(ev->window))) {
if(ev->value_mask & CWBorderWidth) {
c->border.w = ev->border_width;
- } else if(c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
- m = c->mon;
+ } else if(c->isfloating || monitor_is_floating(selmon)) {
+ m = c->workspace->viewer;
- if(ev->value_mask & CWX) {
- c->old_geom.x = c->geom.x;
- c->geom.x = m->geom.x + ev->x;
- }
- if(ev->value_mask & CWY) {
- c->old_geom.y = c->geom.y;
- c->geom.y = m->geom.y + ev->y;
- }
- if(ev->value_mask & CWWidth) {
- c->old_geom.w = c->geom.w;
- c->geom.w = ev->width;
- }
- if(ev->value_mask & CWHeight) {
- c->old_geom.h = c->geom.h;
- c->geom.h = ev->height;
- }
-
- if(c->isfloating) {
- if((c->geom.x + c->geom.w) > m->geom.x + m->geom.w) {
- /* center in x direction */
- c->geom.x = m->geom.x +
- (m->geom.w / 2 - client_get_width(c) / 2);
+ if(m) {
+ if(ev->value_mask & CWX) {
+ c->old_geom.x = c->geom.x;
+ c->geom.x = m->geom.x + ev->x;
}
- if((c->geom.y + c->geom.h) > m->geom.y + m->geom.h) {
- /* center in y direction */
- c->geom.y = m->geom.y +
- (m->geom.h / 2 - client_get_height(c) / 2);
+ if(ev->value_mask & CWY) {
+ c->old_geom.y = c->geom.y;
+ c->geom.y = m->geom.y + ev->y;
+ }
+ if(ev->value_mask & CWWidth) {
+ c->old_geom.w = c->geom.w;
+ c->geom.w = ev->width;
+ }
+ if(ev->value_mask & CWHeight) {
+ c->old_geom.h = c->geom.h;
+ c->geom.h = ev->height;
}
- }
- if((ev->value_mask & (CWX|CWY)) &&
- !(ev->value_mask & (CWWidth|CWHeight))) {
- configure(c);
- }
+ if(c->isfloating) {
+ if((c->geom.x + c->geom.w) > m->geom.x + m->geom.w) {
+ /* center in x direction */
+ c->geom.x = m->geom.x +
+ (m->geom.w / 2 - client_get_width(c) / 2);
+ }
+ if((c->geom.y + c->geom.h) > m->geom.y + m->geom.h) {
+ /* center in y direction */
+ c->geom.y = m->geom.y +
+ (m->geom.h / 2 - client_get_height(c) / 2);
+ }
+ }
- if(ISVISIBLE(c)) {
- XMoveResizeWindow(dpy, c->win, c->geom.x, c->geom.y,
- c->geom.w, c->geom.h);
+ if((ev->value_mask & (CWX | CWY)) &&
+ !(ev->value_mask & (CWWidth | CWHeight))) {
+ client_configure(c);
+ }
+
+ if(ISVISIBLE(c)) {
+ XMoveResizeWindow(dpy, c->win, c->geom.x, c->geom.y,
+ c->geom.w, c->geom.h);
+ }
}
} else {
- configure(c);
+ client_configure(c);
}
} else {
wc.x = ev->x;
struct monitor *createmon(void)
{
struct monitor *m;
+ int i;
m = (struct monitor *)calloc(1, sizeof(*m));
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ for(i = 0; i < LENGTH(workspaces); i++) {
+ if(!workspaces[i].viewer) {
+ m->workspace = &workspaces[i];
+ m->previous_workspace = &workspaces[i];
+ workspaces[i].viewer = m;
+ break;
+ }
+ }
+
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ num_monitors++;
return(m);
}
return;
}
-void detach(struct client *c)
+static void workspace_add_client(struct workspace *workspace,
+ struct client *client)
{
- struct client **tc;
+ struct client **cur;
+
+ for(cur = &(workspace->clients); *cur; cur = &((*cur)->next));
+ *cur = client;
- for(tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
- *tc = c->next;
+ client->workspace = workspace;
+ client->next = NULL;
return;
}
-void detachstack(struct client *c)
+static void workspace_remove_client(struct workspace *workspace,
+ struct client *client)
{
- struct client **tc, *t;
+ struct client **cur;
- for(tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
- *tc = c->snext;
-
- if(c == c->mon->sel) {
- for(t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
- c->mon->sel = t;
+ for(cur = &(workspace->clients); *cur; cur = &((*cur)->next)) {
+ if(*cur == client) {
+ *cur = client->next;
+ break;
+ }
}
return;
}
+static struct workspace* workspace_get_current(void)
+{
+ return(selmon->workspace);
+}
+
+static void detach(struct client *client)
+{
+ workspace_remove_client(client->workspace, client);
+ return;
+}
+
void die(const char *errstr, ...)
{
va_list ap;
return(m);
}
-void wipe_bar(struct monitor *mon, unsigned long *col)
+void wipe_bar(struct monitor *mon, int pal)
{
- XSetForeground(dpy, dc.gc, col[ColBG]);
+ XSetForeground(dpy, dc.gc, dc.palette[pal].color[ColBG]);
XFillRectangle(dpy, dc.drawable, dc.gc,
0, 0, mon->win_geom.w, bar_height);
return;
}
-void drawbar(struct monitor *m)
+static int workspace_is_urgent(struct workspace *workspace)
{
- int x;
- unsigned int i, occ = 0, urg = 0;
- unsigned long *col;
- struct client *c;
-
- resizebarwin(m);
-
- for(c = m->clients; c; c = c->next) {
- occ |= c->tags;
+ struct client *client;
- if(c->isurgent) {
- urg |= c->tags;
+ for(client = workspace->clients; client; client = client->next) {
+ if(client->isurgent) {
+ return(TRUE);
}
}
- wipe_bar(m, m == selmon ? dc.sel : dc.norm);
+ return(FALSE);
+}
+
+void drawbar(struct monitor *m)
+{
+ struct client *focused;
+ unsigned int i;
+ int x;
+
+ monitor_resize_barwin(m);
+ wipe_bar(m, m == selmon ? PalSelected : PalNormal);
+ focused = selmon->workspace->focused;
dc.geom.x = 0;
for(i = 0; i < LENGTH(tags); i++) {
+ int pal;
+ int urgent;
+
dc.geom.w = TEXTW(tags[i]);
- col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
- drawtext(tags[i], col, urg & 1 << i);
- drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
- occ & 1 << i, urg & 1 << i, col);
+ urgent = workspace_is_urgent(&workspaces[i]);
+
+ if(!workspaces[i].viewer) {
+ pal = PalNormal;
+ } else if(workspaces[i].viewer == m) {
+ pal = m == selmon ? PalSelected : PalNotSelected;
+ } else {
+ pal = PalOther;
+ }
+
+ drawtext(tags[i], pal, urgent);
+ drawsquare(m == selmon && focused && focused->workspace->num == i,
+ workspaces[i].clients ? TRUE : FALSE, urgent, pal);
dc.geom.x += dc.geom.w;
}
dc.geom.w = TEXTW(m->ltsymbol);
bar_lwidth = dc.geom.w;
- drawtext(m->ltsymbol, dc.norm, False);
+ drawtext(m->ltsymbol, PalNormal, False);
dc.geom.x += dc.geom.w;
x = dc.geom.x;
dc.geom.w = m->win_geom.w - x;
}
- drawtext(stext, dc.norm, False);
+ drawtext(stext, PalNormal, False);
XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0,
m->win_geom.w, bar_height, 0, 0);
return;
}
-void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast])
+void drawsquare(Bool filled, Bool empty, Bool invert, int palette)
{
int x;
- XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
+ XSetForeground(dpy, dc.gc, dc.palette[palette].color[invert ? ColBG : ColFG]);
x = (dc.font.ascent + dc.font.descent + 2) / 4;
+
if(filled) {
XFillRectangle(dpy, dc.drawable, dc.gc,
dc.geom.x + 1, dc.geom.y + 1,
return;
}
-void drawtext(const char *text, unsigned long col[ColLast], Bool invert)
+void drawtext(const char *text, int pal, Bool invert)
{
char buf[512];
int i, x, y, h, len, olen;
- XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
+ XSetForeground(dpy, dc.gc, dc.palette[pal].color[invert ? ColFG : ColBG]);
XFillRectangle(dpy, dc.drawable, dc.gc,
dc.geom.x, dc.geom.y,
dc.geom.w, dc.geom.h);
/* shorten text if necessary (this could wreak havoc with pango markup but fortunately
dc.w is adjusted to the width of the status text and not the other way around) */
- for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.geom.w - h; len--);
+ for(len = MIN(olen, sizeof(buf)); len && textnw(text, len) > dc.geom.w - h; len--);
if(len <= 0) {
return;
for(i = len; i && i > len - 3; buf[--i] = '.');
}
- if(text == stext && statusmarkup) {
+ /* FIXME: Remove statusmarkup */
+ if(text == stext && statusmarkup) { /* FIXME: stext? */
pango_layout_set_markup(dc.font.layout, buf, len);
} else {
pango_layout_set_text(dc.font.layout, buf, len);
}
- pango_xft_render_layout(dc.xft.drawable,
- (col == dc.norm ? dc.xft.norm : dc.xft.sel) +
- (invert ? ColBG : ColFG),
+ pango_xft_render_layout(dc.draw, &dc.palette[pal].xcolor[invert ? ColBG : ColFG],
dc.font.layout, x * PANGO_SCALE, y * PANGO_SCALE);
+
if(text == stext && statusmarkup) {
/* clear markup attributes */
pango_layout_set_attributes(dc.font.layout, NULL);
}
c = wintoclient(ev->window);
- m = c ? c->mon : wintomon(ev->window);
+ m = c ? c->workspace->viewer : wintomon(ev->window);
if(m != selmon) {
- unfocus(selmon->sel, True);
+ unfocus(selmon->workspace->focused, True);
selmon = m;
- } else if(!c || c == selmon->sel) {
+ } else if(!c || c == selmon->workspace->focused) {
return;
}
void focus(struct client *c)
{
+ struct client *focused;
+
if(!c || !ISVISIBLE(c)) {
- for(c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
+ for(c = selmon->workspace->clients; c && !ISVISIBLE(c); c = c->next);
}
- /* was if(selmon->sel) */
- if(selmon->sel && selmon->sel != c) {
- unfocus(selmon->sel, False);
+ focused = selmon->workspace->focused;
+
+ if(focused && focused != c) {
+ unfocus(focused, False);
}
if(c) {
- if(c->mon != selmon) {
- selmon = c->mon;
- }
+ selmon = c->workspace->viewer;
if(c->isurgent) {
clearurgent(c);
}
- detachstack(c);
- attachstack(c);
grabbuttons(c, True);
- XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
+ XSetWindowBorder(dpy, c->win, dc.palette[PalSelected].color[ColBorder]);
setfocus(c);
} else {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
}
- selmon->sel = c;
+ selmon->workspace->focused = c;
drawbars();
return;
void focusin(XEvent *e)
{
/* there are some broken focus acquiring clients */
- XFocusChangeEvent *ev = &e->xfocus;
+ XFocusChangeEvent *ev;
+ struct client *focused;
+
+ ev = &e->xfocus;
+ focused = selmon->workspace->focused;
- if(selmon->sel && ev->window != selmon->sel->win) {
- setfocus(selmon->sel);
+ if(focused && ev->window != focused->win) {
+ setfocus(focused);
}
return;
void focusmon(const union arg *arg)
{
struct monitor *m;
+ union arg ptrdst;
if(!mons->next) {
return;
return;
}
- unfocus(selmon->sel, True);
+ unfocus(selmon->workspace->focused, True);
selmon = m;
focus(NULL);
+ ptrdst.ui = KBPTR_CENTER;
+ kbptr_move(&ptrdst);
+
return;
}
void focusstack(const union arg *arg)
{
- struct client *c = NULL, *i;
+ struct client *focused;
+ struct client *c;
+
+ focused = selmon->workspace->focused;
+ c = NULL;
- if(!selmon->sel) {
+ if(!focused) {
return;
}
if(arg->i > 0) {
- for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
+ for(c = focused->next; c && !ISVISIBLE(c); c = c->next);
+
if(!c) {
- for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
+ for(c = selmon->workspace->clients; c && !ISVISIBLE(c); c = c->next);
}
} else {
- for(i = selmon->clients; i != selmon->sel; i = i->next) {
+ struct client *i;
+
+ for(i = selmon->workspace->clients; i != focused; i = i->next) {
if(ISVISIBLE(i)) {
c = i;
}
}
if(c) {
+ union arg arg;
+
focus(c);
restack(selmon);
+
+ arg.ui = KBPTR_CENTER;
+ kbptr_move(&arg);
}
return;
void killclient(const union arg *arg)
{
- if(selmon->sel && !sendevent(selmon->sel->win, wmatom[WMDelete],
- NoEventMask, wmatom[WMDelete],
- CurrentTime, 0 , 0, 0)) {
+ struct client *focused;
+
+ focused = selmon->workspace->focused;
+
+ if(focused && !sendevent(focused->win, wmatom[WMDelete],
+ NoEventMask, wmatom[WMDelete],
+ CurrentTime, 0 , 0, 0)) {
XGrabServer(dpy);
XSetErrorHandler(xerrordummy);
XSetCloseDownMode(dpy, DestroyAll);
- XKillClient(dpy, selmon->sel->win);
+ XKillClient(dpy, focused->win);
XSync(dpy, False);
XSetErrorHandler(xerror);
XUngrabServer(dpy);
{
struct client *c;
struct client *t;
+ struct monitor *mon;
Window trans;
XWindowChanges wc;
}
c->win = w;
- updatetitle(c);
+ client_update_title(c);
if(XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
- c->mon = t->mon;
- c->tags = t->tags;
+ c->workspace->viewer = t->workspace->viewer;
} else {
- c->mon = selmon;
- applyrules(c);
+ client_apply_rules(c);
}
- /* geometry */
+ mon = c->workspace->viewer;
+
c->geom.x = wa->x;
c->geom.y = wa->y;
c->geom.w = wa->width;
c->border.oldw = wa->border_width;
- if(c->geom.x + client_get_width(c) > c->mon->geom.x + c->mon->geom.w) {
- c->geom.x = c->mon->geom.x + c->mon->geom.w - client_get_width(c);
+ if(c->geom.x + client_get_width(c) > mon->geom.x + mon->geom.w) {
+ c->geom.x = mon->geom.x + mon->geom.w - client_get_width(c);
}
- if(c->geom.y + client_get_height(c) > c->mon->geom.y + c->mon->geom.h) {
- c->geom.y = c->mon->geom.y + c->mon->geom.h - client_get_height(c);
+ if(c->geom.y + client_get_height(c) > mon->geom.y + mon->geom.h) {
+ c->geom.y = mon->geom.y + mon->geom.h - client_get_height(c);
}
- c->geom.x = MAX(c->geom.x, c->mon->geom.x);
+ c->geom.x = MAX(c->geom.x, mon->geom.x);
/* only fix client y-offset, if the client center might cover the bar */
- c->geom.y = MAX(c->geom.y, (c->mon->by == c->mon->geom.y &&
- (c->geom.x + (c->geom.w / 2) >= c->mon->win_geom.x) &&
- (c->geom.x + (c->geom.w / 2) < c->mon->win_geom.x +
- c->mon->win_geom.w)) ? bar_height : c->mon->geom.y);
+ c->geom.y = MAX(c->geom.y, (mon->by == mon->geom.y &&
+ (c->geom.x + (c->geom.w / 2) >= mon->win_geom.x) &&
+ (c->geom.x + (c->geom.w / 2) < mon->win_geom.x +
+ mon->win_geom.w)) ? bar_height : mon->geom.y);
c->border.w = borderpx;
wc.border_width = c->border.w;
XConfigureWindow(dpy, w, CWBorderWidth, &wc);
- XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
- configure(c); /* propagates border_width, if size doesn't change */
- updatewindowtype(c);
- updatesizehints(c);
- updatewmhints(c);
+ XSetWindowBorder(dpy, w, dc.palette[PalNormal].color[ColBorder]);
+
+ client_configure(c); /* propagates border_width, if size doesn't change */
+ client_update_window_type(c);
+ client_update_size_hints(c);
+ client_update_wm_hints(c);
+
XSelectInput(dpy, w, EnterWindowMask | FocusChangeMask |
PropertyChangeMask | StructureNotifyMask);
grabbuttons(c, False);
XRaiseWindow(dpy, c->win);
}
- attach(c);
- attachstack(c);
+ workspace_add_client(mon->workspace, c);
/* some windows require this */
XMoveResizeWindow(dpy, c->win, c->geom.x + 2 * screen_width,
c->geom.y, c->geom.w, c->geom.h);
setclientstate(c, NormalState);
- if (c->mon == selmon) {
- unfocus(selmon->sel, False);
+ if(mon == selmon) {
+ unfocus(selmon->workspace->focused, False);
}
- c->mon->sel = c;
- arrange(c->mon);
+ c->workspace->focused = c;
+ workspace_update(mon->workspace);
XMapWindow(dpy, c->win);
focus(NULL);
unsigned int n = 0;
struct client *c;
- for(c = m->clients; c; c = c->next) {
+ for(c = m->workspace->clients; c; c = c->next) {
if(ISVISIBLE(c)) {
n++;
}
snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
}
- for(c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
+ for(c = nexttiled(m->workspace->clients); c; c = nexttiled(c->next)) {
int border;
border = 2 + c->border.w;
- resize(c, m->win_geom.x, m->win_geom.y,
- m->win_geom.w - border, m->win_geom.h - border,
- False);
+ client_resize_hints(c, m->win_geom.x, m->win_geom.y,
+ m->win_geom.w - border, m->win_geom.h - border,
+ False);
}
return;
struct monitor *m;
XEvent ev;
- if(!(c = selmon->sel)) {
+ if(!(c = selmon->workspace->focused)) {
return;
}
client_get_height(c);
}
- if(!c->isfloating && selmon->lt[selmon->sellt]->arrange &&
+ if(!c->isfloating && !monitor_is_floating(selmon) &&
(abs(nx - c->geom.x) > snap ||
abs(ny - c->geom.y) > snap)) {
togglefloating(NULL);
}
}
- if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
- resize(c, nx, ny, c->geom.w, c->geom.h, True);
+
+ if(monitor_is_floating(selmon) || c->isfloating) {
+ client_resize_hints(c, nx, ny, c->geom.w, c->geom.h, True);
+ }
+
break;
}
} while(ev.type != ButtonRelease);
return(c);
}
-void pop(struct client *c)
-{
- detach(c);
- attach(c);
- focus(c);
- arrange(c->mon);
-
- return;
-}
-
void propertynotify(XEvent *e)
{
struct client *c;
case XA_WM_TRANSIENT_FOR:
if(!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
(c->isfloating = (wintoclient(trans)) != NULL))
- arrange(c->mon);
+ workspace_update(c->workspace);
break;
case XA_WM_NORMAL_HINTS:
- updatesizehints(c);
+ client_update_size_hints(c);
break;
case XA_WM_HINTS:
- updatewmhints(c);
+ client_update_wm_hints(c);
drawbars();
break;
}
if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
- updatetitle(c);
+ client_update_title(c);
drawbars();
}
if(ev->atom == netatom[NetWMWindowType]) {
- updatewindowtype(c);
+ client_update_window_type(c);
}
}
return(r);
}
-void resize(struct client *c, int x, int y, int w, int h, Bool interact)
+void client_resize_hints(struct client *c, int x, int y, int w, int h, Bool interact)
{
- if(applysizehints(c, &x, &y, &w, &h, interact)) {
- resizeclient(c, x, y, w, h);
+ if(client_apply_size_hints(c, &x, &y, &w, &h, interact)) {
+ client_resize(c, x, y, w, h);
}
return;
}
-void resizebarwin(struct monitor *m)
+void monitor_resize_barwin(struct monitor *m)
{
XMoveResizeWindow(dpy, m->barwin, m->win_geom.x, m->by, m->win_geom.w, bar_height);
return;
}
-void resizeclient(struct client *c, int x, int y, int w, int h)
+void client_resize(struct client *c, int x, int y, int w, int h)
{
XWindowChanges wc;
wc.border_width = c->border.w;
XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
- configure(c);
+ client_configure(c);
XSync(dpy, False);
return;
struct monitor *m;
XEvent ev;
- if(!(c = selmon->sel)) {
+ if(!(c = selmon->workspace->focused)) {
return;
}
c->geom.w + c->border.w - 1,
c->geom.h + c->border.w - 1);
+ m = c->workspace->viewer;
+
do {
XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
case MotionNotify:
nw = MAX(ev.xmotion.x - ocx - 2 * c->border.w + 1, 1);
nh = MAX(ev.xmotion.y - ocy - 2 * c->border.w + 1, 1);
- if(c->mon->win_geom.x + nw >= selmon->win_geom.x &&
- c->mon->win_geom.x + nw <= (selmon->win_geom.x +
- selmon->win_geom.w) &&
- c->mon->win_geom.y + nh >= selmon->win_geom.y &&
- c->mon->win_geom.y + nh <= (selmon->win_geom.y +
- selmon->win_geom.h)) {
- if(!c->isfloating && selmon->lt[selmon->sellt]->arrange
- && (abs(nw - c->geom.w) > snap ||
- abs(nh - c->geom.h) > snap)) {
+ if(m->win_geom.x + nw >= selmon->win_geom.x &&
+ m->win_geom.x + nw <= (selmon->win_geom.x +
+ selmon->win_geom.w) &&
+ m->win_geom.y + nh >= selmon->win_geom.y &&
+ m->win_geom.y + nh <= (selmon->win_geom.y +
+ selmon->win_geom.h)) {
+ if(!c->isfloating && !monitor_is_floating(selmon) &&
+ (abs(nw - c->geom.w) > snap || abs(nh - c->geom.h) > snap)) {
togglefloating(NULL);
}
}
- if(!selmon->lt[selmon->sellt]->arrange || c->isfloating) {
- resize(c, c->geom.x, c->geom.y, nw, nh, True);
+ if(monitor_is_floating(selmon) || c->isfloating) {
+ client_resize_hints(c, c->geom.x, c->geom.y, nw, nh, True);
}
break;
return;
}
-void restack(struct monitor *m)
+void restack(struct monitor *monitor)
{
- struct client *c;
+ struct client *focused;
XEvent ev;
XWindowChanges wc;
- drawbar(m);
+ drawbar(monitor);
+
+ focused = monitor->workspace->focused;
- if(!m->sel) {
+ if(!focused) {
return;
}
- if(m->sel->isfloating || !m->lt[m->sellt]->arrange) {
- XRaiseWindow(dpy, m->sel->win);
+ if(focused->isfloating || monitor_is_floating(monitor)) {
+ XRaiseWindow(dpy, focused->win);
}
- if(m->lt[m->sellt]->arrange) {
+ if(!monitor_is_floating(monitor)) {
+ struct client *client;
+
wc.stack_mode = Below;
- wc.sibling = m->barwin;
- for(c = m->stack; c; c = c->snext) {
- if(!c->isfloating && ISVISIBLE(c)) {
- XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
- wc.sibling = c->win;
+ wc.sibling = monitor->barwin;
+
+ for(client = monitor->workspace->clients; client; client = client->next) {
+ if(!client->isfloating && ISVISIBLE(client)) {
+ XConfigureWindow(dpy, client->win, CWSibling|CWStackMode, &wc);
+ wc.sibling = client->win;
}
}
}
return;
}
-void sendmon(struct client *c, struct monitor *m)
+void sendmon(struct client *client, struct monitor *monitor)
{
- if(c->mon == m) {
+ if(client->workspace == monitor->workspace) {
return;
}
- unfocus(c, True);
- detach(c);
- detachstack(c);
- c->mon = m;
- c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
- attach(c);
- attachstack(c);
+ client_move_to_workspace(client, monitor->workspace);
focus(NULL);
- arrange(NULL);
+ workspace_update(NULL);
return;
}
-void setclientstate(struct client *c, long state)
+void setclientstate(struct client *client, long state)
{
long data[] = { state, None };
- XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
+ XChangeProperty(dpy, client->win, wmatom[WMState], wmatom[WMState], 32,
PropModeReplace, (unsigned char*)data, 2);
return;
static void kbptr_move(const union arg *arg)
{
- if(!selmon || !selmon->sel) {
+ if(!selmon || !selmon->workspace->focused) {
return;
}
- kbptr_move_on(selmon->sel, arg);
+ kbptr_move_on(selmon->workspace->focused, arg);
return;
}
{
struct client *client;
- if(!selmon || !selmon->sel) {
+ if(!selmon || !selmon->workspace->focused) {
return;
}
- client = selmon->sel;
+ client = selmon->workspace->focused;
do_button(client, arg->ui, ButtonPress);
usleep(100000);
return;
}
-void setfocus(struct client *c)
+void setfocus(struct client *client)
{
- union arg ptrdst = { .ui = KBPTR_CENTER };
-
- if(!c->neverfocus) {
- XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
+ if(!client->neverfocus) {
+ XSetInputFocus(dpy, client->win, RevertToPointerRoot, CurrentTime);
}
- sendevent(c->win, wmatom[WMTakeFocus], NoEventMask,
+ sendevent(client->win, wmatom[WMTakeFocus], NoEventMask,
wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
- kbptr_move_on(c, &ptrdst);
-
return;
}
-void setfullscreen(struct client *c, Bool fullscreen)
+void setfullscreen(struct client *client, Bool fullscreen)
{
if(fullscreen) {
- XChangeProperty(dpy, c->win, netatom[NetWMState],
+ XChangeProperty(dpy, client->win, netatom[NetWMState],
XA_ATOM, 32, PropModeReplace,
(unsigned char*)&netatom[NetWMFullscreen], 1);
- c->isfullscreen = True;
- c->oldstate = c->isfloating;
- c->border.oldw = c->border.w;
- c->border.w = 0;
- c->isfloating = True;
- resizeclient(c, c->mon->geom.x, c->mon->geom.y,
- c->mon->geom.w, c->mon->geom.h);
- XRaiseWindow(dpy, c->win);
+ client->isfullscreen = True;
+ client->oldstate = client->isfloating;
+ client->border.oldw = client->border.w;
+ client->border.w = 0;
+ client->isfloating = True;
+ client_resize(client, client->workspace->viewer->geom.x, client->workspace->viewer->geom.y,
+ client->workspace->viewer->geom.w, client->workspace->viewer->geom.h);
+ XRaiseWindow(dpy, client->win);
} else {
- XChangeProperty(dpy, c->win, netatom[NetWMState],
+ XChangeProperty(dpy, client->win, netatom[NetWMState],
XA_ATOM, 32, PropModeReplace,
(unsigned char*)0, 0);
- c->isfullscreen = False;
- c->isfloating = c->oldstate;
- c->border.w = c->border.oldw;
- memcpy(&(c->geom), &(c->old_geom), sizeof(c->geom));
- resizeclient(c, c->geom.x, c->geom.y, c->geom.w, c->geom.h);
- arrange(c->mon);
+ client->isfullscreen = False;
+ client->isfloating = client->oldstate;
+ client->border.w = client->border.oldw;
+ memcpy(&(client->geom), &(client->old_geom), sizeof(client->geom));
+ client_resize(client, client->geom.x, client->geom.y,
+ client->geom.w, client->geom.h);
+ workspace_update(client->workspace);
}
return;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol,
sizeof(selmon->ltsymbol));
- if(selmon->sel) {
- arrange(selmon);
+ if(selmon->workspace->focused) {
+ workspace_update(selmon->workspace);
} else {
drawbar(selmon);
}
return;
}
-void setup(void)
+static void palette_init(struct palette *pal, struct color_set *set)
+{
+ pal->color[ColBorder] = getcolor(set->border, &pal->xcolor[ColBorder]);
+ pal->color[ColBG] = getcolor(set->background, &pal->xcolor[ColBG]);
+ pal->color[ColFG] = getcolor(set->foreground, &pal->xcolor[ColFG]);
+
+ return;
+}
+
+static void setup(void)
{
XSetWindowAttributes wa;
+ int i;
+
+ memset(workspaces, 0, sizeof(workspaces));
+
+ for(i = 0; i < LENGTH(workspaces); i++) {
+ workspaces[i].num = i;
+ }
/* clean up any zombies immediately */
sigchld(0);
cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
/* init appearance */
- dc.norm[ColBorder] = getcolor(normbordercolor, dc.xft.norm + ColBorder);
- dc.norm[ColBG] = getcolor(normbgcolor, dc.xft.norm + ColBG);
- dc.norm[ColFG] = getcolor(normfgcolor, dc.xft.norm + ColFG);
- dc.sel[ColBorder] = getcolor(selbordercolor, dc.xft.sel + ColBorder);
- dc.sel[ColBG] = getcolor(selbgcolor, dc.xft.sel + ColBG);
- dc.sel[ColFG] = getcolor(selfgcolor, dc.xft.sel + ColFG);
+ for(i = 0; i < PalLast; i++) {
+ palette_init(&dc.palette[i], &theme[i]);
+ }
+
dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen),
bar_height, DefaultDepth(dpy, screen));
- dc.xft.drawable = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen),
+ dc.draw = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen),
DefaultColormap(dpy, screen));
dc.gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
return;
}
-void showhide(struct client *c)
+static int monitor_is_floating(struct monitor *mon)
{
- if(!c) {
+ if(mon->lt[mon->sellt]->arrange) {
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+void showhide(struct client *client)
+{
+ struct monitor *monitor;
+
+ if(!client) {
return;
}
- if(ISVISIBLE(c)) { /* show clients top down */
- XMoveWindow(dpy, c->win, c->geom.x, c->geom.y);
+ monitor = client->workspace->viewer;
+
+ if(monitor) { /* show clients top down */
+ XMoveWindow(dpy, client->win, client->geom.x, client->geom.y);
- if((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) &&
- !c->isfullscreen) {
- resize(c, c->geom.x, c->geom.y, c->geom.w, c->geom.h, False);
+ if((monitor_is_floating(monitor) || client->isfloating) && !client->isfullscreen) {
+ client_resize_hints(client, client->geom.x, client->geom.y,
+ client->geom.w, client->geom.h, False);
}
- showhide(c->snext);
+ showhide(client->next);
} else { /* hide clients bottom up */
- showhide(c->snext);
- XMoveWindow(dpy, c->win, client_get_width(c) * -2, c->geom.y);
+ showhide(client->next);
+
+ /* FIXME: Shouldn't XUnmapWindow() be used here? */
+ XMoveWindow(dpy, client->win, client_get_width(client) * -2, client->geom.y);
}
return;
return;
}
-void tag(const union arg *arg)
+static void client_move_to_workspace(struct client *client, struct workspace *workspace)
+{
+ if(client->workspace) {
+ workspace_remove_client(client->workspace, client);
+ }
+
+ workspace_add_client(workspace, client);
+
+ return;
+}
+
+static void move_focused(const union arg *arg)
+{
+ struct client *focused;
+
+ focused = selmon->workspace->focused;
+
+ if(focused) {
+ client_move_to_workspace(focused, &workspaces[arg->ui]);
+ focus(NULL);
+ workspace_update(selmon->workspace);
+ workspace_update(&workspaces[arg->ui]);
+ }
+
+ return;
+}
+
+#if 0
+static void tag(const union arg *arg)
{
if(selmon->sel && arg->ui & TAGMASK) {
selmon->sel->tags = arg->ui & TAGMASK;
focus(NULL);
- arrange(selmon);
+ workspace_update(selmon->workspace);
}
return;
}
+#endif
-void tagmon(const union arg *arg)
+static void tagmon(const union arg *arg)
{
- if(selmon->sel && mons->next) {
- sendmon(selmon->sel, dirtomon(arg->i));
+ if(selmon->workspace->focused && mons->next) {
+ sendmon(selmon->workspace->focused, dirtomon(arg->i));
}
return;
}
-int textnw(const char *text, unsigned int len)
+static int textnw(const char *text, unsigned int len)
{
PangoRectangle r;
return(r.width / PANGO_SCALE);
}
-static int count_tiled_clients(struct monitor *mon)
+static int count_tiled_clients(struct workspace *workspace)
{
struct client *tiled;
int n;
- tiled = nexttiled(mon->clients);
+ tiled = nexttiled(workspace->clients);
n = 0;
while(tiled) {
return(n);
}
-static void bookshelf(struct monitor *m)
+static void bookshelf(struct monitor *monitor)
{
int n, w, x;
int extraw;
- struct client *c;
+ struct client *client;
- n = count_tiled_clients(m);
+ n = count_tiled_clients(monitor->workspace);
if(!n) {
/* nothing to do */
* first implemented this on a laptop and things broke when I
* plugged in a second screen at work.
*/
- w = m->win_geom.w / n;
- x = m->win_geom.x;
+ w = monitor->win_geom.w / n;
+ x = monitor->win_geom.x;
/* make the first window a bit larger if the width can't be divided evenly */
- extraw = m->win_geom.w - (w * n);
+ extraw = monitor->win_geom.w - (w * n);
/* assign positions and sizes to all clients on this screen */
- for(c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
+ for(client = nexttiled(monitor->workspace->clients); client; client = nexttiled(client->next)) {
int border;
- border = 2 * c->border.w;
- c->incw = 0;
- c->inch = 0;
+ border = 2 * client->border.w;
+ client->incw = 0;
+ client->inch = 0;
- resize(c, x, m->win_geom.y, w + extraw - border,
- m->win_geom.h - border, False);
+ client_resize_hints(client, x, monitor->win_geom.y, w + extraw - border,
+ monitor->win_geom.h - border, False);
/*
* Since all clients are horizontally aligned and the
* same size, only the x coordinate changes.
*/
- x += client_get_width(c);
+ x += client_get_width(client);
extraw = 0;
}
return;
}
-static void bookstack(struct monitor *m)
+static void bookstack(struct monitor *monitor)
{
int n, h, y;
int extrah;
- struct client *c;
+ struct client *client;
/*
* The logic is essentially the same as in bookshelf(), though
* much more vertical, literally speaking.
*/
- n = count_tiled_clients(m);
+ n = count_tiled_clients(monitor->workspace);
if(!n) {
return;
}
- h = m->win_geom.h / n;
- y = m->win_geom.y;
- extrah = m->win_geom.h - (h * n);
+ h = monitor->win_geom.h / n;
+ y = monitor->win_geom.y;
+ extrah = monitor->win_geom.h - (h * n);
- for(c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
+ for(client = nexttiled(monitor->workspace->clients); client; client = nexttiled(client->next)) {
int border;
- border = 2 * c->border.w;
- c->incw = 0;
- c->inch = 0;
+ border = 2 * client->border.w;
+ client->incw = 0;
+ client->inch = 0;
- resize(c, m->win_geom.x, y, m->win_geom.w - border,
- h + extrah - border, False);
- y += client_get_height(c);
+ client_resize_hints(client, monitor->win_geom.x, y, monitor->win_geom.w - border,
+ h + extrah - border, False);
+ y += client_get_height(client);
extrah = 0;
}
void togglefloating(const union arg *arg)
{
- if(!selmon->sel) {
- return;
- }
-
- selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
-
- if(selmon->sel->isfloating) {
- resize(selmon->sel, selmon->sel->geom.x, selmon->sel->geom.y,
- selmon->sel->geom.w, selmon->sel->geom.h, False);
- }
+ struct client *focused;
- arrange(selmon);
-
- return;
-}
-
-void toggletag(const union arg *arg)
-{
- unsigned int newtags;
+ focused = selmon->workspace->focused;
- if(!selmon->sel) {
+ if(!focused) {
return;
}
- newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ focused->isfloating = !focused->isfloating || focused->isfixed;
- if(newtags) {
- selmon->sel->tags = newtags;
- focus(NULL);
- arrange(selmon);
+ if(focused->isfloating) {
+ client_resize_hints(focused, focused->geom.x, focused->geom.y,
+ focused->geom.w, focused->geom.h, False);
}
- return;
-}
-
-void toggleview(const union arg *arg)
-{
- unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
-
- if(newtagset) {
- selmon->tagset[selmon->seltags] = newtagset;
- focus(NULL);
- arrange(selmon);
- }
+ workspace_update(selmon->workspace);
return;
}
-void unfocus(struct client *c, Bool setfocus)
+void unfocus(struct client *client, Bool setfocus)
{
- if(!c) {
+ if(!client) {
return;
}
- grabbuttons(c, False);
- XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]);
+ grabbuttons(client, False);
+ XSetWindowBorder(dpy, client->win, dc.palette[PalNormal].color[ColBorder]);
if(setfocus) {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
return;
}
-void unmanage(struct client *c, Bool destroyed)
+void unmanage(struct client *client, Bool destroyed)
{
- struct monitor *m = c->mon;
+ struct monitor *monitor;
XWindowChanges wc;
+ monitor = client->workspace->viewer;
+
/* The server grab construct avoids race conditions. */
- detach(c);
- detachstack(c);
+ detach(client);
if(!destroyed) {
- wc.border_width = c->border.oldw;
+ wc.border_width = client->border.oldw;
XGrabServer(dpy);
XSetErrorHandler(xerrordummy);
- XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
- XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
- setclientstate(c, WithdrawnState);
+ XConfigureWindow(dpy, client->win, CWBorderWidth, &wc); /* restore border */
+ XUngrabButton(dpy, AnyButton, AnyModifier, client->win);
+ setclientstate(client, WithdrawnState);
XSync(dpy, False);
XSetErrorHandler(xerror);
XUngrabServer(dpy);
}
- free(c);
+ free(client);
focus(NULL);
- arrange(m);
+ workspace_update(monitor->workspace);
return;
}
-void unmapnotify(XEvent *e)
+void unmapnotify(XEvent *event)
{
- struct client *c;
- XUnmapEvent *ev = &e->xunmap;
+ struct client *client;
+ XUnmapEvent *ev;
- if((c = wintoclient(ev->window))) {
+ ev = &event->xunmap;
+
+ if((client = wintoclient(ev->window))) {
if(ev->send_event) {
- setclientstate(c, WithdrawnState);
+ setclientstate(client, WithdrawnState);
} else {
- unmanage(c, False);
+ unmanage(client, False);
}
}
void updatebars(void)
{
- unsigned int w;
- struct monitor *m;
+ struct monitor *monitor;
- XSetWindowAttributes wa = {
+ XSetWindowAttributes attrs = {
.override_redirect = True,
.background_pixmap = ParentRelative,
.event_mask = ButtonPressMask|ExposureMask
};
- for(m = mons; m; m = m->next) {
- w = m->win_geom.w;
-
- m->barwin = XCreateWindow(dpy, root, m->win_geom.x, m->by,
- w, bar_height, 0, DefaultDepth(dpy, screen),
- CopyFromParent, DefaultVisual(dpy, screen),
- CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+ for(monitor = mons; monitor; monitor = monitor->next) {
+ monitor->barwin = XCreateWindow(dpy, root, monitor->win_geom.x, monitor->by,
+ monitor->win_geom.w, bar_height, 0,
+ DefaultDepth(dpy, screen), CopyFromParent,
+ DefaultVisual(dpy, screen),
+ CWOverrideRedirect | CWBackPixmap | CWEventMask,
+ &attrs);
- XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
- XMapRaised(dpy, m->barwin);
+ XDefineCursor(dpy, monitor->barwin, cursor[CurNormal]);
+ XMapRaised(dpy, monitor->barwin);
}
return;
}
-void updatebarpos(struct monitor *m)
+void updatebarpos(struct monitor *monitor)
{
- m->win_geom.y = m->geom.y;
- m->win_geom.h = m->geom.h;
- m->win_geom.h -= bar_height;
- m->by = m->topbar ? m->win_geom.y : m->win_geom.y + m->win_geom.h;
- m->win_geom.y = m->topbar ? m->win_geom.y + bar_height : m->win_geom.y;
+ monitor->win_geom.y = monitor->geom.y;
+ monitor->win_geom.h = monitor->geom.h;
+ monitor->win_geom.h -= bar_height;
+ monitor->by = monitor->topbar ? monitor->win_geom.y : monitor->win_geom.y + monitor->win_geom.h;
+ monitor->win_geom.y = monitor->topbar ? monitor->win_geom.y + bar_height : monitor->win_geom.y;
return;
}
{
Bool dirty = False;
-#ifdef XINERAMA
if(XineramaIsActive(dpy)) {
int i, j, n, nn;
- struct client *c;
- struct monitor *m;
XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
XineramaScreenInfo *unique = NULL;
- for(n = 0, m = mons; m; m = m->next, n++);
+ n = num_monitors;
/* only consider unique geometries as separate screens */
if(!(unique = (XineramaScreenInfo *)malloc(sizeof(*unique) * nn))) {
nn = j;
if(n <= nn) {
+ struct monitor *monitor;
+
for(i = 0; i < (nn - n); i++) { /* new monitors available */
struct monitor **ptr;
*ptr = createmon();
}
- for(i = 0, m = mons; i < nn && m; m = m->next, i++) {
- if(i >= n || (unique[i].x_org != m->geom.x ||
- unique[i].y_org != m->geom.y ||
- unique[i].width != m->geom.w ||
- unique[i].height != m->geom.h)) {
+ for(i = 0, monitor = mons; i < nn && monitor; monitor = monitor->next, i++) {
+ if(i >= n || (unique[i].x_org != monitor->geom.x ||
+ unique[i].y_org != monitor->geom.y ||
+ unique[i].width != monitor->geom.w ||
+ unique[i].height != monitor->geom.h)) {
dirty = True;
- m->num = i;
- m->geom.x = unique[i].x_org;
- m->geom.y = unique[i].y_org;
- m->geom.w = unique[i].width;
- m->geom.h = unique[i].height;
+ monitor->num = i;
+ monitor->geom.x = unique[i].x_org;
+ monitor->geom.y = unique[i].y_org;
+ monitor->geom.w = unique[i].width;
+ monitor->geom.h = unique[i].height;
- memcpy(&(m->win_geom), &(m->geom),
- sizeof(m->win_geom));
+ memcpy(&(monitor->win_geom), &(monitor->geom),
+ sizeof(monitor->win_geom));
- updatebarpos(m);
+ updatebarpos(monitor);
}
}
} else { /* less monitors available nn < n */
- for(i = nn; i < n; i++) {
- for(m = mons; m && m->next; m = m->next);
+ struct monitor *monitor;
- while(m->clients) {
- dirty = True;
- c = m->clients;
- m->clients = c->next;
- detachstack(c);
- c->mon = mons;
- attach(c);
- attachstack(c);
- }
+ for(i = nn; i < n; i++) {
+ for(monitor = mons; monitor && monitor->next; monitor = monitor->next);
- if(m == selmon) {
+ if(monitor == selmon) {
selmon = mons;
}
- cleanupmon(m);
+ monitor_free(monitor);
}
}
free(unique);
- } else
-#endif /* XINERAMA */
- /* default monitor setup */
- {
+ } else {
if(!mons) {
mons = createmon();
}
return;
}
-void updatesizehints(struct client *c)
+void client_update_size_hints(struct client *client)
{
long msize;
XSizeHints size;
- if(!XGetWMNormalHints(dpy, c->win, &size, &msize)) {
+ if(!XGetWMNormalHints(dpy, client->win, &size, &msize)) {
/* size is uninitialized, ensure that size.flags aren't used */
size.flags = PSize;
}
if(size.flags & PBaseSize) {
- c->basew = size.base_width;
- c->baseh = size.base_height;
+ client->basew = size.base_width;
+ client->baseh = size.base_height;
} else if(size.flags & PMinSize) {
- c->basew = size.min_width;
- c->baseh = size.min_height;
+ client->basew = size.min_width;
+ client->baseh = size.min_height;
} else {
- c->basew = c->baseh = 0;
+ client->basew = client->baseh = 0;
}
if(size.flags & PResizeInc) {
- c->incw = size.width_inc;
- c->inch = size.height_inc;
+ client->incw = size.width_inc;
+ client->inch = size.height_inc;
} else {
- c->incw = c->inch = 0;
+ client->incw = client->inch = 0;
}
if(size.flags & PMaxSize) {
- c->maxw = size.max_width;
- c->maxh = size.max_height;
+ client->maxw = size.max_width;
+ client->maxh = size.max_height;
} else {
- c->maxw = c->maxh = 0;
+ client->maxw = client->maxh = 0;
}
if(size.flags & PMinSize) {
- c->minw = size.min_width;
- c->minh = size.min_height;
+ client->minw = size.min_width;
+ client->minh = size.min_height;
} else if(size.flags & PBaseSize) {
- c->minw = size.base_width;
- c->minh = size.base_height;
+ client->minw = size.base_width;
+ client->minh = size.base_height;
} else {
- c->minw = c->minh = 0;
+ client->minw = client->minh = 0;
}
if(size.flags & PAspect) {
- c->mina = (float)size.min_aspect.y / size.min_aspect.x;
- c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
+ client->mina = (float)size.min_aspect.y / size.min_aspect.x;
+ client->maxa = (float)size.max_aspect.x / size.max_aspect.y;
} else {
- c->maxa = c->mina = 0.0;
+ client->maxa = client->mina = 0.0;
}
- c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
- && c->maxw == c->minw && c->maxh == c->minh);
+ client->isfixed = (client->maxw && client->minw && client->maxh && client->minh &&
+ client->maxw == client->minw && client->maxh == client->minh);
return;
}
-void updatetitle(struct client *c)
+void client_update_title(struct client *client)
{
- if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) {
- gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
+ if(!gettextprop(client->win, netatom[NetWMName], client->name, sizeof(client->name))) {
+ gettextprop(client->win, XA_WM_NAME, client->name, sizeof(client->name));
}
- if(c->name[0] == '\0') {
+ if(client->name[0] == '\0') {
/* hack to mark broken clients */
- strcpy(c->name, broken);
+ strcpy(client->name, broken);
}
return;
return;
}
-void updatewindowtype(struct client *c)
+void client_update_window_type(struct client *client)
{
- Atom state = getatomprop(c, netatom[NetWMState]);
- Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
+ Atom state = getatomprop(client, netatom[NetWMState]);
+ Atom wtype = getatomprop(client, netatom[NetWMWindowType]);
if(state == netatom[NetWMFullscreen]) {
- setfullscreen(c, True);
+ setfullscreen(client, True);
}
if(wtype == netatom[NetWMWindowTypeDialog]) {
- c->isfloating = True;
+ client->isfloating = True;
}
return;
}
-void updatewmhints(struct client *c)
+void client_update_wm_hints(struct client *client)
{
- XWMHints *wmh;
+ XWMHints *hints;
- if((wmh = XGetWMHints(dpy, c->win))) {
- if(c == selmon->sel && wmh->flags & XUrgencyHint) {
- wmh->flags &= ~XUrgencyHint;
- XSetWMHints(dpy, c->win, wmh);
+ if((hints = XGetWMHints(dpy, client->win))) {
+ if(client == selmon->workspace->focused && hints->flags & XUrgencyHint) {
+ hints->flags &= ~XUrgencyHint;
+ XSetWMHints(dpy, client->win, hints);
} else {
- c->isurgent = (wmh->flags & XUrgencyHint) ? True : False;
+ client->isurgent = (hints->flags & XUrgencyHint) ? True : False;
}
- if(wmh->flags & InputHint) {
- c->neverfocus = !wmh->input;
- } else {
- c->neverfocus = False;
- }
+ client->neverfocus = (hints->flags & InputHint) ? !hints->input : False;
- XFree(wmh);
+ XFree(hints);
}
return;
}
-void view(const union arg *arg)
+static void monitor_swap_workspace(struct monitor *mona, struct monitor *monb)
{
- if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) {
+ struct workspace *swap;
+
+ mona->previous_workspace = mona->workspace;
+ monb->previous_workspace = monb->workspace;
+
+ swap = mona->workspace;
+ mona->workspace = monb->workspace;
+ monb->workspace = swap;
+
+ mona->workspace->viewer = mona;
+ monb->workspace->viewer = monb;
+
+ workspace_update(NULL);
+
+ return;
+}
+
+static void monitor_show_workspace(struct monitor *mon, struct workspace *workspace)
+{
+ mon->workspace->viewer = NULL;
+ mon->previous_workspace = mon->workspace;
+ mon->workspace = workspace;
+ workspace->viewer = mon;
+
+ workspace_update(NULL);
+
+ return;
+}
+
+static void show_workspace(const union arg *arg)
+{
+ struct workspace *target;
+ struct workspace *current;
+
+ target = arg->ui < LENGTH(workspaces) ? &workspaces[arg->ui] : selmon->previous_workspace;
+ current = workspace_get_current();
+
+ if(current == target) {
return;
}
- selmon->seltags ^= 1; /* toggle sel tagset */
-
- if(arg->ui & TAGMASK) {
- selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ if(target->viewer) {
+ /*
+ * Target workspace is visible on another monitor. We
+ * can't have two monitors show the same workspace, so
+ * we swap workspaces instead.
+ */
+ monitor_swap_workspace(selmon, target->viewer);
+ } else {
+ monitor_show_workspace(selmon, target);
}
- arrange(selmon);
focus(NULL);
return;
}
-struct client *wintoclient(Window w)
+struct client *wintoclient(Window window)
{
- struct client *c;
- struct monitor *m;
+ struct client *client;
+ struct monitor *monitor;
- for(m = mons; m; m = m->next) {
- for(c = m->clients; c; c = c->next) {
- if(c->win == w) {
- return(c);
+ for(monitor = mons; monitor; monitor = monitor->next) {
+ for(client = monitor->workspace->clients; client; client = client->next) {
+ if(client->win == window) {
+ return(client);
}
}
}
return(NULL);
}
-struct monitor *wintomon(Window w)
+struct monitor *wintomon(Window window)
{
+ struct client *client;
+ struct monitor *monitor;
int x, y;
- struct client *c;
- struct monitor *m;
- if(w == root && getrootptr(&x, &y)) {
+ if(window == root && getrootptr(&x, &y)) {
return(recttomon(x, y, 1, 1));
}
- for(m = mons; m; m = m->next) {
- if(w == m->barwin) {
- return(m);
+ for(monitor = mons; monitor; monitor = monitor->next) {
+ if(window == monitor->barwin) {
+ return(monitor);
}
}
- if((c = wintoclient(w))) {
- return(c->mon);
+ if((client = wintoclient(window))) {
+ return(client->workspace->viewer);
}
return(selmon);
return(-1);
}
-void zoom(const union arg *arg)
-{
- struct client *c = selmon->sel;
-
- if(!selmon->lt[selmon->sellt]->arrange ||
- (selmon->sel && selmon->sel->isfloating)) {
- return;
- }
-
- if(c == nexttiled(selmon->clients)) {
- if(!c || !(c = nexttiled(c->next))){
- return;
- }
- }
-
- pop(c);
- return;
-}
-
int main(int argc, char *argv[])
{
if(argc == 2 && !strcmp("-v", argv[1])) {
die("d "VERSION", © 2020-2021 Matthias Kruk, based on dwm-6.0\n");
} else if(argc != 1) {
- die("usage: dwm [-v]\n");
+ die("usage: d [-v]\n");
}
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) {