add tags x 3, minimum ewmh to support wmctlr -lG, some logging

This commit is contained in:
seanpringle
2012-09-03 00:38:37 +10:00
parent 515ba26266
commit 9675f3c927
6 changed files with 322 additions and 126 deletions

View File

@@ -79,12 +79,21 @@ binding keys[] = {
// Launcher // Launcher
{ .mod = Mod4Mask, .key = XK_x, .act = ACTION_COMMAND, .data = "dmenu_run" }, { .mod = Mod4Mask, .key = XK_x, .act = ACTION_COMMAND, .data = "dmenu_run" },
// Raise tag groups
{ .mod = Mod4Mask, .key = XK_F1, .act = ACTION_RAISE_TAG, .num = TAG1 },
{ .mod = Mod4Mask, .key = XK_F2, .act = ACTION_RAISE_TAG, .num = TAG2 },
{ .mod = Mod4Mask, .key = XK_F3, .act = ACTION_RAISE_TAG, .num = TAG3 },
// Tag/untag the current window
{ .mod = ShiftMask|Mod4Mask, .key = XK_F1, .act = ACTION_TAG, .num = TAG1 },
{ .mod = ShiftMask|Mod4Mask, .key = XK_F2, .act = ACTION_TAG, .num = TAG2 },
{ .mod = ShiftMask|Mod4Mask, .key = XK_F3, .act = ACTION_TAG, .num = TAG3 },
{ .mod = ShiftMask|ControlMask|Mod4Mask, .key = XK_F1, .act = ACTION_UNTAG, .num = TAG1 },
{ .mod = ShiftMask|ControlMask|Mod4Mask, .key = XK_F2, .act = ACTION_UNTAG, .num = TAG2 },
{ .mod = ShiftMask|ControlMask|Mod4Mask, .key = XK_F3, .act = ACTION_UNTAG, .num = TAG3 },
// Find or start apps by WM_CLASS (lower case match). // Find or start apps by WM_CLASS (lower case match).
{ .mod = AnyModifier, .key = XK_F1, .act = ACTION_FIND_OR_START, .data = "urxvt" }, { .mod = AnyModifier, .key = XK_F1, .act = ACTION_FIND_OR_START, .data = "urxvt" },
{ .mod = AnyModifier, .key = XK_F2, .act = ACTION_FIND_OR_START, .data = "kate" }, { .mod = AnyModifier, .key = XK_F2, .act = ACTION_FIND_OR_START, .data = "chromium" },
{ .mod = AnyModifier, .key = XK_F3, .act = ACTION_FIND_OR_START, .data = "chromium" }, { .mod = AnyModifier, .key = XK_F3, .act = ACTION_FIND_OR_START, .data = "pcmanfm" },
{ .mod = AnyModifier, .key = XK_F4, .act = ACTION_FIND_OR_START, .data = "pcmanfm" },
{ .mod = AnyModifier, .key = XK_F5, .act = ACTION_FIND_OR_START, .data = "firefox" },
{ .mod = AnyModifier, .key = XK_F6, .act = ACTION_FIND_OR_START, .data = "xchat" },
}; };

View File

@@ -7,8 +7,8 @@ int window_get_atom_prop(Window w, Atom atom, Atom *list, int count);
void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count); void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count);
int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count); int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count);
void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int count); void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int count);
void window_set_window_prop(Window w, Atom prop, Window *values, int count);
void ewmh_client_list(); void ewmh_client_list();
void ewmh_active_window(Window w);
client* window_client(Window win); client* window_client(Window win);
int client_state(client *c, Atom state); int client_state(client *c, Atom state);
int client_add_state(client *c, Atom state); int client_add_state(client *c, Atom state);
@@ -30,6 +30,8 @@ void client_active(client *c);
void find_or_start(char *class); void find_or_start(char *class);
void window_listen(Window win); void window_listen(Window win);
void client_review(client *c); void client_review(client *c);
void raise_tag(unsigned long tag);
void client_set_tags(client *c);
void create_notify(XCreateWindowEvent *e); void create_notify(XCreateWindowEvent *e);
void configure_request(XConfigureRequestEvent *e); void configure_request(XConfigureRequestEvent *e);
void map_request(XMapEvent *e); void map_request(XMapEvent *e);

45
xoat.1
View File

@@ -96,6 +96,51 @@ Launch dmenu_run
Launch urxvt Launch urxvt
.RS .RS
.RE .RE
.TP
.B Mod4-F1
Raise windows in tag 1.
.RS
.RE
.TP
.B Shift-Mod4-F1
Place current window in tag 1.
.RS
.RE
.TP
.B Shift-Control-Mod4-F1
Remove current window from tag 1.
.RS
.RE
.TP
.B Mod4-F2
Raise windows in tag 2.
.RS
.RE
.TP
.B Shift-Mod4-F2
Place current window in tag 2.
.RS
.RE
.TP
.B Shift-Control-Mod4-F2
Remove current window from tag 2.
.RS
.RE
.TP
.B Mod4-F3
Raise windows in tag 3.
.RS
.RE
.TP
.B Shift-Mod4-F3
Place current window in tag 3.
.RS
.RE
.TP
.B Shift-Control-Mod4-F3
Remove current window from tag 3.
.RS
.RE
.SH SEE ALSO .SH SEE ALSO
.PP .PP
\f[B]dmenu\f[] (1) \f[B]dmenu\f[] (1)

292
xoat.c
View File

@@ -25,7 +25,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include "xoat.h" #include "xoat.h"
#include "proto.h" #include "proto.h"
#include "config.h" #include "config.h"
@@ -45,6 +44,7 @@ int execsh(char *cmd)
pid_t exec_cmd(char *cmd) pid_t exec_cmd(char *cmd)
{ {
if (!cmd || !cmd[0]) return -1; if (!cmd || !cmd[0]) return -1;
warnx("exec_cmd %s", cmd);
signal(SIGCHLD, catch_exit); signal(SIGCHLD, catch_exit);
pid_t pid = fork(); pid_t pid = fork();
if (!pid) if (!pid)
@@ -118,11 +118,14 @@ void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int co
XChangeProperty(display, w, prop, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)values, count); XChangeProperty(display, w, prop, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)values, count);
} }
// update _NET_CLIENT_LIST_STACKING void window_set_window_prop(Window w, Atom prop, Window *values, int count)
{
XChangeProperty(display, w, prop, XA_WINDOW, 32, PropModeReplace, (unsigned char*)values, count);
}
void ewmh_client_list() void ewmh_client_list()
{ {
int i; stack all, wins; int i; stack all, wins;
memset(&all, 0, sizeof(stack));
memset(&wins, 0, sizeof(stack)); memset(&wins, 0, sizeof(stack));
windows_visible(&all); windows_visible(&all);
@@ -135,21 +138,15 @@ void ewmh_client_list()
wins.windows[wins.depth++] = c->window; wins.windows[wins.depth++] = c->window;
} }
} }
XChangeProperty(display, root, atoms[_NET_CLIENT_LIST_STACKING], XA_WINDOW, 32, window_set_window_prop(root, atoms[_NET_CLIENT_LIST_STACKING], wins.windows, wins.depth);
PropModeReplace, (unsigned char*)&wins.windows, wins.depth); // hack for now, since we dont track window mapping history
} window_set_window_prop(root, atoms[_NET_CLIENT_LIST], wins.windows, wins.depth);
// update _NET_ACTIVE_WINDOW
void ewmh_active_window(Window w)
{
XChangeProperty(display, root, atoms[_NET_ACTIVE_WINDOW], XA_WINDOW, 32,
PropModeReplace, (unsigned char*)&w, 1);
} }
// build a client struct // build a client struct
client* window_client(Window win) client* window_client(Window win)
{ {
int i; int i, x, y, w, h;
if (win == None) return NULL; if (win == None) return NULL;
client *c = calloc(1, sizeof(client)); client *c = calloc(1, sizeof(client));
@@ -175,7 +172,6 @@ client* window_client(Window win)
c->attr.x, c->attr.y, c->attr.width, c->attr.height)) c->attr.x, c->attr.y, c->attr.width, c->attr.height))
{ c->monitor = i; break; } { c->monitor = i; break; }
int x, y, w, h;
c->spot = SPOT1; c->spot = SPOT1;
spot_xywh(SPOT2, c->monitor, &x, &y, &w, &h); spot_xywh(SPOT2, c->monitor, &x, &y, &w, &h);
@@ -186,21 +182,31 @@ client* window_client(Window win)
if (INTERSECT(x, y, w, h, c->attr.x + c->attr.width/2, c->attr.y+c->attr.height/2, 1, 1)) if (INTERSECT(x, y, w, h, c->attr.x + c->attr.width/2, c->attr.y+c->attr.height/2, 1, 1))
c->spot = SPOT3; c->spot = SPOT3;
if (c->visible)
{
window_get_atom_prop(c->window, atoms[_NET_WM_STATE], c->states, MAX_NET_WM_STATES); window_get_atom_prop(c->window, atoms[_NET_WM_STATE], c->states, MAX_NET_WM_STATES);
c->urgent = client_state(c, atoms[_NET_WM_STATE_DEMANDS_ATTENTION]); c->urgent = client_state(c, atoms[_NET_WM_STATE_DEMANDS_ATTENTION]);
if (c->visible) unsigned long tags = 0;
{ if (window_get_cardinal_prop(c->window, atoms[XOAT_TAGS], &tags, 1))
c->tags = tags;
XWMHints *hints = XGetWMHints(display, c->window); XWMHints *hints = XGetWMHints(display, c->window);
if (hints) if (hints)
{ {
memmove(&c->hints, hints, sizeof(XWMHints)); memmove(&c->hints, hints, sizeof(XWMHints));
XFree(hints); XFree(hints);
} }
long sr; long sr; XGetWMNormalHints(display, c->window, &c->size, &sr);
XGetWMNormalHints(display, c->window, &c->size, &sr);
c->input = c->hints.flags & InputHint && c->hints.input ? 1:0; c->input = c->hints.flags & InputHint && c->hints.input ? 1:0;
c->urgent = c->urgent || c->hints.flags & XUrgencyHint ? 1: 0; c->urgent = c->urgent || c->hints.flags & XUrgencyHint ? 1: 0;
XClassHint chint;
if (XGetClassHint(display, c->window, &chint))
{
c->class = strdup(chint.res_class);
XFree(chint.res_class); XFree(chint.res_name);
}
} }
return c; return c;
} }
@@ -208,7 +214,6 @@ client* window_client(Window win)
return NULL; return NULL;
} }
// check _NET_WM_STATE
int client_state(client *c, Atom state) int client_state(client *c, Atom state)
{ {
int i; int i;
@@ -224,7 +229,6 @@ int client_add_state(client *c, Atom state)
for (i = 0; i < MAX_NET_WM_STATES; i++) for (i = 0; i < MAX_NET_WM_STATES; i++)
{ {
if (c->states[i]) continue; if (c->states[i]) continue;
c->states[i] = state; c->states[i] = state;
window_set_atom_prop(c->window, atoms[_NET_WM_STATE], c->states, i+1); window_set_atom_prop(c->window, atoms[_NET_WM_STATE], c->states, i+1);
return 1; return 1;
@@ -258,7 +262,7 @@ void windows_visible(stack *s)
memmove(s, &inplay, sizeof(stack)); memmove(s, &inplay, sizeof(stack));
return; return;
} }
s->depth = 0; memset(s, 0, sizeof(stack));
unsigned int nwins; int i; Window w1, w2, *wins; unsigned int nwins; int i; Window w1, w2, *wins;
if (XQueryTree(display, root, &w1, &w2, &wins, &nwins) && wins) if (XQueryTree(display, root, &w1, &w2, &wins, &nwins) && wins)
{ {
@@ -294,9 +298,10 @@ int window_message(Window target, Window subject, Atom atom, unsigned long proto
return r; return r;
} }
// release a client struct
void client_free(client *c) void client_free(client *c)
{ {
if (!c) return;
free(c->class);
free(c); free(c);
} }
@@ -314,14 +319,12 @@ int client_protocol(client *c, Atom protocol)
return found; return found;
} }
// friendly close followed by stab in the back
void client_close(client *c) void client_close(client *c)
{ {
if (!client_protocol(c, atoms[WM_DELETE_WINDOW])) if (!client_protocol(c, atoms[WM_DELETE_WINDOW]))
XKillClient(display, c->window); XKillClient(display, c->window);
} }
// move/resize a window
void client_position(client *c, int x, int y, int w, int h) void client_position(client *c, int x, int y, int w, int h)
{ {
if (!c) return; if (!c) return;
@@ -384,7 +387,6 @@ void spot_xywh(int spot, int mon, int *x, int *y, int *w, int *h)
{ {
spot = MAX(SPOT1, MIN(SPOT3, spot)); spot = MAX(SPOT1, MIN(SPOT3, spot));
monitor *m = &monitors[MIN(nmonitors-1, MAX(0, mon))]; monitor *m = &monitors[MIN(nmonitors-1, MAX(0, mon))];
int width_spot1 = (double)m->w / 100 * MIN(90, MAX(10, SPOT1_WIDTH_PCT)); int width_spot1 = (double)m->w / 100 * MIN(90, MAX(10, SPOT1_WIDTH_PCT));
int height_spot2 = (double)m->h / 100 * MIN(90, MAX(10, SPOT2_HEIGHT_PCT)); int height_spot2 = (double)m->h / 100 * MIN(90, MAX(10, SPOT2_HEIGHT_PCT));
@@ -447,12 +449,9 @@ void client_cycle(client *c)
// find a window within a screen "spot" and raise/activate // find a window within a screen "spot" and raise/activate
Window spot_active(int spot, int mon, Window except) Window spot_active(int spot, int mon, Window except)
{ {
int x, y, w, h; int i, x, y, w, h;
spot_xywh(spot, mon, &x, &y, &w, &h); spot_xywh(spot, mon, &x, &y, &w, &h);
stack wins; windows_visible(&wins);
int i; stack wins;
memset(&wins, 0, sizeof(stack));
windows_visible(&wins);
for (i = 0; i < wins.depth; i++) for (i = 0; i < wins.depth; i++)
{ {
@@ -492,10 +491,8 @@ void client_stack(client *c, stack *all, stack *raise)
void client_raise(client *c) void client_raise(client *c)
{ {
int i; stack raise, all, family; int i; stack raise, all, family;
memset(&all, 0, sizeof(stack));
memset(&raise, 0, sizeof(stack)); memset(&raise, 0, sizeof(stack));
memset(&family, 0, sizeof(stack)); memset(&family, 0, sizeof(stack));
windows_visible(&all); windows_visible(&all);
for (i = 0; i < all.depth; i++) for (i = 0; i < all.depth; i++)
@@ -530,9 +527,7 @@ void client_raise(client *c)
void client_lower(client *c) void client_lower(client *c)
{ {
stack lower, all; stack lower, all;
memset(&all, 0, sizeof(stack));
memset(&lower, 0, sizeof(stack)); memset(&lower, 0, sizeof(stack));
windows_visible(&all); windows_visible(&all);
client_stack(c, &all, &lower); client_stack(c, &all, &lower);
@@ -560,18 +555,15 @@ void client_active(client *c)
client_protocol(c, atoms[WM_TAKE_FOCUS]); client_protocol(c, atoms[WM_TAKE_FOCUS]);
XSetInputFocus(display, c->input ? c->window: PointerRoot, RevertToPointerRoot, CurrentTime); XSetInputFocus(display, c->input ? c->window: PointerRoot, RevertToPointerRoot, CurrentTime);
window_set_window_prop(root, atoms[_NET_ACTIVE_WINDOW], &c->window, 1);
client_review(c); client_review(c);
ewmh_active_window(c->window);
} }
// real simple switcher/launcher // real simple switcher/launcher
void find_or_start(char *class) void find_or_start(char *class)
{ {
int i; stack all; int i; client *found = NULL;
memset(&all, 0, sizeof(stack)); stack all; windows_visible(&all);
windows_visible(&all);
client *found = NULL;
for (i = 0; !found && i < all.depth; i++) for (i = 0; !found && i < all.depth; i++)
{ {
@@ -581,7 +573,6 @@ void find_or_start(char *class)
{ {
if (!strcasecmp(chint.res_class, class) || !strcasecmp(chint.res_name, class)) if (!strcasecmp(chint.res_class, class) || !strcasecmp(chint.res_name, class))
found = c; found = c;
XFree(chint.res_class); XFree(chint.res_class);
XFree(chint.res_name); XFree(chint.res_name);
} }
@@ -610,6 +601,40 @@ void client_review(client *c)
XSetWindowBorderWidth(display, c->window, client_state(c, atoms[_NET_WM_STATE_FULLSCREEN]) ? 0: BORDER); XSetWindowBorderWidth(display, c->window, client_state(c, atoms[_NET_WM_STATE_FULLSCREEN]) ? 0: BORDER);
} }
void raise_tag(unsigned long tag)
{
int i; client *c = NULL, *t = NULL;
stack all; windows_visible(&all);
for (i = all.depth-1; i > -1; i--)
{
c = all.clients[i];
if (c && c->manage && c->visible && c->tags & tag)
{
if (c->monitor == current_mon && c->spot == current_spot) t = c;
client_raise(c);
}
}
if (t) client_active(t);
unsigned long desktop = 0;
if (tag & TAG2) desktop = 1;
if (tag & TAG3) desktop = 2;
window_set_cardinal_prop(root, atoms[_NET_CURRENT_DESKTOP], &desktop, 1);
}
void client_set_tags(client *c)
{
unsigned long tags = c->tags;
window_set_cardinal_prop(c->window, atoms[XOAT_TAGS], &tags, 1);
unsigned long desktop = 0xffffffff;
if (tags & TAG3) desktop = 2;
if (tags & TAG2) desktop = 1;
if (tags & TAG1) desktop = 0;
window_set_cardinal_prop(c->window, atoms[_NET_WM_DESKTOP], &desktop, 1);
}
// ------- event handlers -------- // ------- event handlers --------
void create_notify(XCreateWindowEvent *e) void create_notify(XCreateWindowEvent *e)
@@ -694,8 +719,6 @@ void map_notify(XMapEvent *e)
if (c && c->manage) if (c && c->manage)
{ {
client_raise(c); client_raise(c);
if (current)
a = window_client(current); a = window_client(current);
// if no current window, or new window has opened in the // if no current window, or new window has opened in the
@@ -732,7 +755,7 @@ void key_press(XKeyEvent *e)
latest = e->time; latest = e->time;
client *c = window_client(current); client *c = window_client(current);
short act = ACTION_NONE; void *data = NULL; short act = ACTION_NONE; void *data = NULL; int num = 0;
KeySym key = XkbKeycodeToKeysym(display, e->keycode, 0, 0); KeySym key = XkbKeycodeToKeysym(display, e->keycode, 0, 0);
unsigned int state = e->state & ~(LockMask|NumlockMask); unsigned int state = e->state & ~(LockMask|NumlockMask);
unsigned long spot; unsigned long spot;
@@ -743,6 +766,7 @@ void key_press(XKeyEvent *e)
{ {
act = keys[i].act; act = keys[i].act;
data = keys[i].data; data = keys[i].data;
num = keys[i].num;
break; break;
} }
} }
@@ -805,6 +829,16 @@ void key_press(XKeyEvent *e)
client_review(c); client_review(c);
client_spot(c, c->spot, 1); client_spot(c, c->spot, 1);
break; break;
case ACTION_TAG:
c->tags |= (unsigned int)num;
client_set_tags(c);
warnx("tags %d 0x%08lx %s", c->tags, (long)c->window, c->class);
break;
case ACTION_UNTAG:
c->tags &= ~((unsigned int)num);
client_set_tags(c);
warnx("tags %d 0x%08lx %s", c->tags, (long)c->window, c->class);
break;
} }
} }
switch (act) switch (act)
@@ -842,6 +876,9 @@ void key_press(XKeyEvent *e)
if (spot_active(SPOT3, current_mon-1, None)) break; if (spot_active(SPOT3, current_mon-1, None)) break;
} }
break; break;
case ACTION_RAISE_TAG:
raise_tag(num);
break;
} }
client_free(c); client_free(c);
ewmh_client_list(); ewmh_client_list();
@@ -869,6 +906,7 @@ void client_message(XClientMessageEvent *e)
if (c && c->manage) if (c && c->manage)
{ {
warnx("client message 0x%lx 0x%08lx %s", e->message_type, (long)c->window, c->class);
if (e->message_type == atoms[_NET_ACTIVE_WINDOW]) if (e->message_type == atoms[_NET_ACTIVE_WINDOW])
{ {
client_raise(c); client_raise(c);
@@ -880,6 +918,18 @@ void client_message(XClientMessageEvent *e)
client_close(c); client_close(c);
} }
} }
else
if (e->message_type == atoms[XOAT_EXIT])
{
warnx("exit!");
exit(EXIT_SUCCESS);
}
else
if (e->message_type == atoms[XOAT_RESTART])
{
warnx("restart!");
execsh(self);
}
client_free(c); client_free(c);
} }
@@ -905,26 +955,109 @@ void property_notify(XPropertyEvent *e)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int i, j; int i, j; self = argv[0];
stack wins; memset(&wins, 0, sizeof(stack));
signal(SIGCHLD, catch_exit); signal(SIGCHLD, catch_exit);
if(!(display = XOpenDisplay(0))) return 1; if (!(display = XOpenDisplay(0))) return 1;
screen = DefaultScreenOfDisplay(display); screen = DefaultScreenOfDisplay(display);
scr_id = DefaultScreen(display); scr_id = DefaultScreen(display);
root = DefaultRootWindow(display); root = DefaultRootWindow(display);
xerror = XSetErrorHandler(oops); xerror = XSetErrorHandler(oops);
for (i = 0; i < ATOMS; i++) atoms[i] = XInternAtom(display, atom_names[i], False);
// check for restart
if (argc > 1 && !strcmp(argv[1], "restart"))
{
Window cli = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, None, None);
window_message(root, cli, atoms[XOAT_RESTART], 0, SubstructureNotifyMask | SubstructureRedirectMask);
exit(EXIT_SUCCESS);
}
// check for exit
if (argc > 1 && !strcmp(argv[1], "exit"))
{
Window cli = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, None, None);
window_message(root, cli, atoms[XOAT_EXIT], 0, SubstructureNotifyMask | SubstructureRedirectMask);
exit(EXIT_SUCCESS);
}
// default non-multi-head setup // default non-multi-head setup
monitors[0].x = 0; memset(monitors, 0, sizeof(monitors));
monitors[0].y = 0;
monitors[0].w = WidthOfScreen(screen); monitors[0].w = WidthOfScreen(screen);
monitors[0].h = HeightOfScreen(screen); monitors[0].h = HeightOfScreen(screen);
nmonitors = 1;
warnx("screen(%d): %dx%d+%d+%d", scr_id, monitors[0].w, monitors[0].h, monitors[0].x, monitors[0].y);
// detect panel struts
windows_visible(&wins);
inplay.depth = 0;
for (i = 0; i < wins.depth; i++)
{
client *c = wins.clients[i];
if (!c) continue;
unsigned long strut[12]; memset(strut, 0, sizeof(strut));
if (window_get_cardinal_prop(c->window, atoms[_NET_WM_STRUT_PARTIAL], strut, 12)
|| window_get_cardinal_prop(c->window, atoms[_NET_WM_STRUT], strut, 4))
{
struts[LEFT] = MAX(struts[LEFT], strut[LEFT]);
struts[RIGHT] = MAX(struts[RIGHT], strut[RIGHT]);
struts[TOP] = MAX(struts[TOP], strut[TOP]);
struts[BOTTOM] = MAX(struts[BOTTOM], strut[BOTTOM]);
warnx("struts %ld %ld %ld %ld 0x%08lx %s", strut[LEFT], strut[LEFT], strut[LEFT], strut[LEFT], (long)c->window, c->class);
}
client_free(c);
}
// support multi-head.
if (XineramaIsActive(display))
{
XineramaScreenInfo *info = XineramaQueryScreens(display, &nmonitors);
if (info)
{
nmonitors = MIN(nmonitors, MAX_MONITORS);
for (i = 0; i < nmonitors; i++)
{
monitors[i].x = info[i].x_org;
monitors[i].y = info[i].y_org + struts[TOP];
monitors[i].w = info[i].width;
monitors[i].h = info[i].height - struts[TOP] - struts[BOTTOM];
warnx("monitor %d %dx%d+%d+%d", i, monitors[i].w, monitors[i].h, monitors[i].x, monitors[i].y);
}
XFree(info);
}
}
// left struts affect first monitor
monitors[0].x += struts[LEFT];
monitors[0].w -= struts[LEFT];
// right struts affect last monitor
monitors[nmonitors-1].w -= struts[RIGHT];
// dump atoms for debug
for (i = 0; i < ATOMS; i++) warnx("atom 0x%lx %s", (long)atoms[i], atom_names[i]);
// become the window manager
XSelectInput(display, root, StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask); XSelectInput(display, root, StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask);
// ewmh support
unsigned long desktop = 0, desktops = 3, pid = getpid(), viewport[2] = { 0, 0 },
geometry[2] = { WidthOfScreen(screen), HeightOfScreen(screen) };
ewmh = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, 0, 0);
window_set_atom_prop(root, atoms[_NET_SUPPORTED], atoms, ATOMS);
window_set_window_prop(root, atoms[_NET_SUPPORTING_WM_CHECK], &ewmh, 1);
window_set_cardinal_prop(root, atoms[_NET_CURRENT_DESKTOP], &desktop, 1);
window_set_cardinal_prop(root, atoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1);
window_set_cardinal_prop(root, atoms[_NET_DESKTOP_GEOMETRY], geometry, 2);
window_set_cardinal_prop(root, atoms[_NET_DESKTOP_VIEWPORT], viewport, 2);
window_set_cardinal_prop(ewmh, atoms[_NET_WM_PID], &pid, 1);
XChangeProperty(display, ewmh, atoms[_NET_WM_NAME], XA_STRING, 8, PropModeReplace, (const unsigned char*)"xoat", 4);
// figure out NumlockMask // figure out NumlockMask
XModifierKeymap *modmap = XGetModifierMapping(display); XModifierKeymap *modmap = XGetModifierMapping(display);
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
@@ -953,56 +1086,6 @@ int main(int argc, char *argv[])
XGrabButton(display, Button1, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); XGrabButton(display, Button1, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None);
XGrabButton(display, Button3, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); XGrabButton(display, Button3, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None);
for (i = 0; i < ATOMS; i++) atoms[i] = XInternAtom(display, atom_names[i], False);
stack wins;
memset(&wins, 0, sizeof(stack));
// detect panel struts
windows_visible(&wins);
inplay.depth = 0;
for (i = 0; i < wins.depth; i++)
{
client *c = wins.clients[i];
if (c)
{
unsigned long strut[12]; memset(strut, 0, sizeof(strut));
if (window_get_cardinal_prop(c->window, atoms[_NET_WM_STRUT_PARTIAL], strut, 12)
|| window_get_cardinal_prop(c->window, atoms[_NET_WM_STRUT], strut, 4))
{
struts[LEFT] = MAX(struts[LEFT], strut[LEFT]);
struts[RIGHT] = MAX(struts[RIGHT], strut[RIGHT]);
struts[TOP] = MAX(struts[TOP], strut[TOP]);
struts[BOTTOM] = MAX(struts[BOTTOM], strut[BOTTOM]);
}
}
client_free(c);
}
// support multi-head.
if (XineramaIsActive(display))
{
XineramaScreenInfo *info = XineramaQueryScreens(display, &nmonitors);
if (info)
{
nmonitors = MIN(nmonitors, MAX_MONITORS);
for (i = 0; i < nmonitors; i++)
{
monitors[i].x = info[i].x_org;
monitors[i].y = info[i].y_org + struts[TOP];
monitors[i].w = info[i].width;
monitors[i].h = info[i].height - struts[TOP] - struts[BOTTOM];
}
XFree(info);
}
}
// left struts affect first monitor
monitors[0].x += struts[LEFT];
monitors[0].w -= struts[LEFT];
// right struts affect last monitor
monitors[nmonitors-1].w -= struts[RIGHT];
// setup existing managable windows // setup existing managable windows
windows_visible(&wins); windows_visible(&wins);
inplay.depth = 0; inplay.depth = 0;
@@ -1011,15 +1094,19 @@ int main(int argc, char *argv[])
client *c = wins.clients[i]; client *c = wins.clients[i];
if (c && c->manage) if (c && c->manage)
{ {
warnx("window 0x%08lx (%d,%d,%d) %s", (long)c->window, c->tags, c->monitor, c->spot, c->class);
window_listen(c->window); window_listen(c->window);
// activate first one // activate first one
if (!current && c->visible) if (!current && c->visible)
{ {
client_active(c);
client_raise(c); client_raise(c);
client_active(c);
} }
if (c->visible) if (c->visible)
{
client_review(c); client_review(c);
client_set_tags(c);
}
} }
client_free(c); client_free(c);
} }
@@ -1029,8 +1116,7 @@ int main(int argc, char *argv[])
while (inplay.depth) while (inplay.depth)
client_free(inplay.clients[--inplay.depth]); client_free(inplay.clients[--inplay.depth]);
XEvent ev; XEvent ev; XNextEvent(display, &ev);
XNextEvent(display, &ev);
switch (ev.type) switch (ev.type)
{ {

45
xoat.h
View File

@@ -55,6 +55,8 @@ Screen *screen;
int scr_id; int scr_id;
Window root; Window root;
Time latest; Time latest;
char *self;
Window ewmh;
typedef struct { typedef struct {
short x, y, w, h; short x, y, w, h;
@@ -62,7 +64,7 @@ typedef struct {
#define MAX_MONITORS 3 #define MAX_MONITORS 3
monitor monitors[MAX_MONITORS]; monitor monitors[MAX_MONITORS];
int nmonitors; int nmonitors = 1;
#define MAX_NET_WM_STATES 5 #define MAX_NET_WM_STATES 5
@@ -74,6 +76,8 @@ typedef struct {
Window transient_for; Window transient_for;
Atom type, states[MAX_NET_WM_STATES]; Atom type, states[MAX_NET_WM_STATES];
short monitor, spot, visible, trans, manage, input, urgent; short monitor, spot, visible, trans, manage, input, urgent;
unsigned short tags;
char *class;
} client; } client;
#define STACK 64 #define STACK 64
@@ -98,23 +102,36 @@ int struts[4] = { 0, 0, 0, 0 };
#define GENERAL_ATOMS(X) \ #define GENERAL_ATOMS(X) \
X(XOAT_SPOT),\ X(XOAT_SPOT),\
X(XOAT_TAGS),\
X(XOAT_EXIT),\
X(XOAT_RESTART),\
X(_MOTIF_WM_HINTS),\ X(_MOTIF_WM_HINTS),\
X(WM_DELETE_WINDOW),\ X(_NET_SUPPORTED),\
X(WM_TAKE_FOCUS),\ X(_NET_CURRENT_DESKTOP),\
X(_NET_NUMBER_OF_DESKTOPS),\
X(_NET_DESKTOP_GEOMETRY),\
X(_NET_DESKTOP_VIEWPORT),\
X(_NET_ACTIVE_WINDOW),\ X(_NET_ACTIVE_WINDOW),\
X(_NET_CLOSE_WINDOW),\ X(_NET_CLOSE_WINDOW),\
X(_NET_WM_STRUT_PARTIAL),\ X(_NET_CLIENT_LIST_STACKING),\
X(_NET_CLIENT_LIST),\
X(_NET_SUPPORTING_WM_CHECK),\
X(_NET_WM_NAME),\
X(_NET_WM_PID),\
X(_NET_WM_DESKTOP),\
X(_NET_WM_STRUT),\ X(_NET_WM_STRUT),\
X(_NET_WM_STRUT_PARTIAL),\
X(_NET_WM_WINDOW_TYPE),\ X(_NET_WM_WINDOW_TYPE),\
X(_NET_WM_WINDOW_TYPE_DESKTOP),\ X(_NET_WM_WINDOW_TYPE_DESKTOP),\
X(_NET_WM_WINDOW_TYPE_DOCK),\ X(_NET_WM_WINDOW_TYPE_DOCK),\
X(_NET_WM_WINDOW_TYPE_SPLASH),\ X(_NET_WM_WINDOW_TYPE_SPLASH),\
X(_NET_WM_WINDOW_TYPE_NOTIFICATION),\ X(_NET_WM_WINDOW_TYPE_NOTIFICATION),\
X(_NET_WM_WINDOW_TYPE_DIALOG),\ X(_NET_WM_WINDOW_TYPE_DIALOG),\
X(_NET_CLIENT_LIST_STACKING),\
X(_NET_WM_STATE),\ X(_NET_WM_STATE),\
X(_NET_WM_STATE_FULLSCREEN),\ X(_NET_WM_STATE_FULLSCREEN),\
X(_NET_WM_STATE_DEMANDS_ATTENTION),\ X(_NET_WM_STATE_DEMANDS_ATTENTION),\
X(WM_DELETE_WINDOW),\
X(WM_TAKE_FOCUS),\
X(WM_PROTOCOLS) X(WM_PROTOCOLS)
enum { GENERAL_ATOMS(ATOM_ENUM), ATOMS }; enum { GENERAL_ATOMS(ATOM_ENUM), ATOMS };
@@ -141,6 +158,9 @@ enum {
ACTION_FOCUS_MONITOR_INC, ACTION_FOCUS_MONITOR_INC,
ACTION_FOCUS_MONITOR_DEC, ACTION_FOCUS_MONITOR_DEC,
ACTION_FULLSCREEN_TOGGLE, ACTION_FULLSCREEN_TOGGLE,
ACTION_TAG,
ACTION_UNTAG,
ACTION_RAISE_TAG,
ACTIONS ACTIONS
}; };
@@ -148,7 +168,10 @@ typedef struct {
unsigned int mod; unsigned int mod;
KeySym key; KeySym key;
short act; short act;
union {
void *data; void *data;
int num;
};
} binding; } binding;
enum { enum {
@@ -156,9 +179,13 @@ enum {
}; };
enum { enum {
SPOT_CURRENT, SPOT1=1, // large left pane
SPOT_SMART,
SPOT1, // large left pane
SPOT2, // medium top right pane SPOT2, // medium top right pane
SPOT3 // small bottom right pane SPOT3, // small bottom right pane
SPOT_CURRENT,
SPOT_SMART
}; };
#define TAG1 1<<0
#define TAG2 1<<1
#define TAG3 1<<2

27
xoat.md
View File

@@ -68,6 +68,33 @@ Shift-Mod4-x
F1 F1
: Launch urxvt : Launch urxvt
Mod4-F1
: Raise windows in tag 1.
Shift-Mod4-F1
: Place current window in tag 1.
Shift-Control-Mod4-F1
: Remove current window from tag 1.
Mod4-F2
: Raise windows in tag 2.
Shift-Mod4-F2
: Place current window in tag 2.
Shift-Control-Mod4-F2
: Remove current window from tag 2.
Mod4-F3
: Raise windows in tag 3.
Shift-Mod4-F3
: Place current window in tag 3.
Shift-Control-Mod4-F3
: Remove current window from tag 3.
# SEE ALSO # SEE ALSO
**dmenu** (1) **dmenu** (1)