diff
Makefile | 5 +
config.mk | 1 +
extras/bar.c | 155 +++++++++++++++++++++++++-
extras/bar.h | 5 +
extras/ewmh/ewmh.c | 6 +
extras/ewmh/ewmh.h | 1 +
extras/tile/masterstack.c | 257 +++++++++++++++++++++++++++++++++++++++++++
extras/tile/masterstack.h | 12 ++
wm.c | 274 ++++++++++++++++++++++++++++++++++++++++++----
wmc.c | 57 ++++++++--
10 files changed, 740 insertions(+), 33 deletions(-)
diff --git a/Makefile b/Makefile
index 8cafd27..6305c32 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,11 @@ WM_EXTRAS += extras/bar.c
CPPFLAGS += -DBAR
endif
+ifeq ($(MASTERSTACK),1)
+WM_EXTRAS += extras/tile/masterstack.c
+CPPFLAGS += -DMASTERSTACK
+endif
+
all: wm wmc
wm: wm.c
diff --git a/config.mk b/config.mk
index 908d91d..d2cfe1e 100644
--- a/config.mk
+++ b/config.mk
@@ -2,6 +2,7 @@
EWMH=1
XINERAMA=1
BAR=1
+MASTERSTACK=1
# uncomment on obsd and other systems which use the X11R6 path.
#CPPFLAGS += -I/usr/X11R6/include
diff --git a/extras/bar.c b/extras/bar.c
index e67898c..20a9743 100644
--- a/extras/bar.c
+++ b/extras/bar.c
@@ -1,4 +1,5 @@
#include <X11/Xlib.h>
+#include <X11/Xatom.h>
#include <X11/Xutil.h>
#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
@@ -7,7 +8,7 @@
#include <stdio.h>
#include <string.h>
-#define bar_height 18
+#define default_bar_height 18
#define bar_padding 6
#define max_status_len 512
#define max_title_len 512
@@ -29,8 +30,12 @@ static GC bar_gc;
static XFontStruct *bar_font;
static int bar_screen;
static int bar_selected_tag;
+static int bar_selected_tags[max_monitors];
static int bar_tag_count;
static int bar_monitor_count;
+static int bar_height;
+static int bar_show_empty = 1;
+static int bar_workspace_usage[max_monitors][max_tags];
static int bar_margin_x;
static int bar_margin_y;
static int bar_is_bottom;
@@ -45,8 +50,29 @@ static char bar_title[max_title_len];
static struct bar_monitor bar_monitors[max_monitors];
void bar_reconfigure(void);
+void bar_draw(void);
static unsigned long bar_alloc_color(const char *color_name, unsigned long fallback);
+static XFontStruct *
+bar_load_font(const char *font_name)
+{
+ XFontStruct *font;
+ char family[256];
+ int i;
+
+ if (bar_display == NULL || font_name == NULL || font_name[0] == '\0')
+ return NULL;
+ font = XLoadQueryFont(bar_display, font_name);
+ if (font != NULL)
+ return font;
+ for (i = 0; font_name[i] != '\0' && font_name[i] != ':' && i < (int)sizeof(family) - 1; i++)
+ family[i] = font_name[i];
+ family[i] = '\0';
+ if (i > 0)
+ return XLoadQueryFont(bar_display, family);
+ return NULL;
+}
+
static int
bar_text_width(const char *text)
{
@@ -55,6 +81,23 @@ bar_text_width(const char *text)
return XTextWidth(bar_font, text, (int)strlen(text));
}
+static int
+bar_text_baseline(void)
+{
+ int text_height;
+ int top_pad;
+
+ if (bar_font == NULL)
+ return bar_height - 5;
+ text_height = bar_font->ascent + bar_font->descent;
+ if (text_height < 1)
+ return bar_height - 5;
+ top_pad = (bar_height - text_height) / 2;
+ if (top_pad < 0)
+ top_pad = 0;
+ return top_pad + bar_font->ascent;
+}
+
static void
bar_draw_rect(Window window, unsigned long color, int x, int y, int width, int height)
{
@@ -125,9 +168,18 @@ bar_load_monitors(void)
static void
bar_create_windows(void)
{
+ Atom net_wm_window_type;
+ Atom net_wm_window_type_dock;
XSetWindowAttributes attrs;
int i, window_x, window_y, window_width;
+ XClassHint class_hint;
+ char class_name[] = "wmbar";
+ char class_class[] = "wmbar";
+ net_wm_window_type = XInternAtom(bar_display, "_NET_WM_WINDOW_TYPE", False);
+ net_wm_window_type_dock = XInternAtom(bar_display, "_NET_WM_WINDOW_TYPE_DOCK", False);
+ class_hint.res_name = class_name;
+ class_hint.res_class = class_class;
attrs.override_redirect = True;
attrs.background_pixel = bar_bg_color;
attrs.event_mask = ExposureMask;
@@ -147,6 +199,9 @@ bar_create_windows(void)
DefaultDepth(bar_display, bar_screen), CopyFromParent,
DefaultVisual(bar_display, bar_screen),
CWOverrideRedirect | CWBackPixel | CWEventMask, &attrs);
+ XSetClassHint(bar_display, bar_monitors[i].window, &class_hint);
+ XChangeProperty(bar_display, bar_monitors[i].window, net_wm_window_type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *)&net_wm_window_type_dock, 1);
XMapRaised(bar_display, bar_monitors[i].window);
}
}
@@ -157,6 +212,7 @@ bar_init(Display *display, Window root_window)
bar_display = display;
bar_root = root_window;
bar_screen = DefaultScreen(display);
+ bar_height = default_bar_height;
bar_font = XLoadQueryFont(display, "fixed");
if (bar_font == NULL)
return 0;
@@ -189,10 +245,62 @@ bar_set_tags(const int *tags, int count, int selected_tag)
bar_tag_count = count > max_tags ? max_tags : count;
bar_selected_tag = selected_tag;
+ for (i = 0; i < max_monitors; i++)
+ bar_selected_tags[i] = selected_tag;
for (i = 0; i < bar_tag_count; i++)
bar_tags[i] = tags[i];
}
+void
+bar_set_selected_tags(const int *selected_tags, int count)
+{
+ int i;
+ int max_count;
+
+ if (selected_tags == NULL || count < 1)
+ return;
+ max_count = count;
+ if (max_count > max_monitors)
+ max_count = max_monitors;
+ for (i = 0; i < max_count; i++)
+ bar_selected_tags[i] = selected_tags[i];
+ for (; i < max_monitors; i++)
+ bar_selected_tags[i] = bar_selected_tags[0];
+}
+
+void
+bar_set_show_empty(int show_empty)
+{
+ bar_show_empty = show_empty ? 1 : 0;
+ bar_draw();
+}
+
+void
+bar_set_workspace_usage(const int *usage, int monitor_count, int workspace_count)
+{
+ int m, w;
+ int mc, wc;
+
+ if (usage == NULL)
+ return;
+ mc = monitor_count;
+ wc = workspace_count;
+ if (mc < 1)
+ mc = 1;
+ if (mc > max_monitors)
+ mc = max_monitors;
+ if (wc < 1)
+ wc = 1;
+ if (wc > max_tags)
+ wc = max_tags;
+ for (m = 0; m < max_monitors; m++)
+ for (w = 0; w < max_tags; w++)
+ bar_workspace_usage[m][w] = 0;
+ for (m = 0; m < mc; m++)
+ for (w = 0; w < wc; w++)
+ bar_workspace_usage[m][w] = usage[m * wc + w] ? 1 : 0;
+}
+
void
bar_set_title(const char *title)
{
@@ -206,35 +314,45 @@ bar_draw(void)
{
int m, x, i, tw, tagw, title_x, status_x, bar_width;
char tag_text[8];
+ int baseline;
if (bar_display == NULL || bar_monitor_count < 1)
return;
+ baseline = bar_text_baseline();
for (m = 0; m < bar_monitor_count; m++) {
+ int selected_tag;
+
if (bar_monitors[m].window == None)
continue;
+ selected_tag = bar_selected_tags[m];
bar_width = bar_monitors[m].bar_width;
bar_draw_rect(bar_monitors[m].window, bar_bg_color, 0, 0, bar_width, bar_height);
x = 0;
for (i = 0; i < bar_tag_count; i++) {
+ int show_tag;
+
+ show_tag = bar_show_empty || i == selected_tag || bar_workspace_usage[m][i];
+ if (!show_tag)
+ continue;
snprintf(tag_text, sizeof(tag_text), "%d", bar_tags[i]);
tw = bar_text_width(tag_text);
tagw = tw + (bar_padding * 2);
- if (i == bar_selected_tag) {
+ if (i == selected_tag) {
bar_draw_rect(bar_monitors[m].window, bar_sel_bg_color, x, 0, tagw, bar_height);
- bar_draw_text(bar_monitors[m].window, bar_sel_accent_color, x + bar_padding, bar_height - 5, tag_text);
+ bar_draw_text(bar_monitors[m].window, bar_sel_accent_color, x + bar_padding, baseline, tag_text);
} else {
- bar_draw_text(bar_monitors[m].window, bar_fg_color, x + bar_padding, bar_height - 5, tag_text);
+ bar_draw_text(bar_monitors[m].window, bar_fg_color, x + bar_padding, baseline, tag_text);
}
x += tagw + bar_padding;
}
status_x = bar_width - bar_padding - bar_text_width(bar_status);
if (status_x < x + bar_padding)
status_x = x + bar_padding;
- bar_draw_text(bar_monitors[m].window, bar_fg_color, status_x, bar_height - 5, bar_status);
+ bar_draw_text(bar_monitors[m].window, bar_fg_color, status_x, baseline, bar_status);
title_x = x + bar_padding;
tw = bar_text_width(bar_title);
if (title_x + tw < status_x - bar_padding)
- bar_draw_text(bar_monitors[m].window, bar_sel_fg_color, title_x, bar_height - 5, bar_title);
+ bar_draw_text(bar_monitors[m].window, bar_sel_fg_color, title_x, baseline, bar_title);
}
XFlush(bar_display);
}
@@ -262,6 +380,31 @@ bar_set_bottom(int is_bottom)
bar_reconfigure();
}
+void
+bar_set_height(int height)
+{
+ if (height < 8)
+ height = 8;
+ bar_height = height;
+ bar_reconfigure();
+}
+
+void
+bar_set_font(const char *font_name)
+{
+ XFontStruct *new_font;
+
+ new_font = bar_load_font(font_name);
+ if (new_font == NULL)
+ return;
+ if (bar_font != NULL)
+ XFreeFont(bar_display, bar_font);
+ bar_font = new_font;
+ if (bar_gc != None)
+ XSetFont(bar_display, bar_gc, bar_font->fid);
+ bar_draw();
+}
+
static unsigned long
bar_alloc_color(const char *color_name, unsigned long fallback)
{
diff --git a/extras/bar.h b/extras/bar.h
index f12da4b..664a20e 100644
--- a/extras/bar.h
+++ b/extras/bar.h
@@ -6,9 +6,14 @@
int bar_init(Display *display, Window root_window);
void bar_set_status(const char *status);
void bar_set_tags(const int *tags, int count, int selected_tag);
+void bar_set_selected_tags(const int *selected_tags, int count);
void bar_set_title(const char *title);
void bar_set_margins(int margin_x, int margin_y);
void bar_set_bottom(int is_bottom);
+void bar_set_height(int height);
+void bar_set_font(const char *font_name);
+void bar_set_show_empty(int show_empty);
+void bar_set_workspace_usage(const int *usage, int monitor_count, int workspace_count);
void bar_set_colors(const char *fg, const char *bg, const char *sel_fg,
const char *sel_bg, const char *sel_accent);
void bar_reconfigure(void);
diff --git a/extras/ewmh/ewmh.c b/extras/ewmh/ewmh.c
index 8bc8b78..45e23bd 100644
--- a/extras/ewmh/ewmh.c
+++ b/extras/ewmh/ewmh.c
@@ -90,3 +90,9 @@ ewmh_sync(Display *display, Window root_window,
PropModeReplace, (unsigned char *)&desktop, 1);
}
}
+
+int
+ewmh_is_internal_window(Window window)
+{
+ return window == wm_check_window;
+}
diff --git a/extras/ewmh/ewmh.h b/extras/ewmh/ewmh.h
index a307b91..85f176a 100644
--- a/extras/ewmh/ewmh.h
+++ b/extras/ewmh/ewmh.h
@@ -8,5 +8,6 @@ void ewmh_set_current_desktop(Display *display, Window root_window, int current_
void ewmh_set_active_window(Display *display, Window root_window, Window window);
void ewmh_sync(Display *display, Window root_window,
Window *windows, int *window_workspace, int window_count);
+int ewmh_is_internal_window(Window window);
#endif
diff --git a/extras/tile/masterstack.c b/extras/tile/masterstack.c
new file mode 100644
index 0000000..0eaa2bd
--- /dev/null
+++ b/extras/tile/masterstack.c
@@ -0,0 +1,257 @@
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "masterstack.h"
+#ifdef XINERAMA
+#include "../xinerama.h"
+#endif
+
+#define max_workspace_windows 4096
+#define max_monitors 16
+#define master_factor_num 3
+#define master_factor_den 5
+
+static int inner_gap = 0;
+static int outer_gap = 0;
+static int top_gap = 0;
+
+void
+tile_masterstack_set_gaps(int inner, int outer)
+{
+ if (inner < 0)
+ inner = inner_gap;
+ if (outer < 0)
+ outer = outer_gap;
+ if (inner < 0)
+ inner = 0;
+ if (outer < 0)
+ outer = 0;
+ inner_gap = inner;
+ outer_gap = outer;
+}
+
+void
+tile_masterstack_set_top_gap(int top)
+{
+ if (top < 0)
+ top = 0;
+ top_gap = top;
+}
+
+static int
+is_normal_window_type(Display *display, Window window)
+{
+ Atom net_wm_window_type;
+ Atom net_wm_window_type_normal;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *prop_data;
+ int i;
+
+ net_wm_window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
+ net_wm_window_type_normal = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ prop_data = NULL;
+ if (XGetWindowProperty(display, window, net_wm_window_type, 0, 8, False, XA_ATOM,
+ &actual_type, &actual_format, &nitems, &bytes_after, &prop_data) != Success)
+ return 1;
+ if (prop_data == NULL)
+ return 1;
+ if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1) {
+ XFree(prop_data);
+ return 1;
+ }
+ for (i = 0; i < (int)nitems; i++) {
+ Atom wt;
+
+ wt = ((Atom *)prop_data)[i];
+ if (wt == net_wm_window_type_normal) {
+ XFree(prop_data);
+ return 1;
+ }
+ }
+ XFree(prop_data);
+ return 0;
+}
+
+static int
+find_window_index(Window *windows, int window_count, Window window)
+{
+ int i;
+
+ for (i = 0; i < window_count; i++)
+ if (windows[i] == window)
+ return i;
+ return -1;
+}
+
+static int
+is_tile_candidate(Display *display, Window root_window, Window window,
+ Window *windows, int *window_workspace, int *window_floating,
+ int window_count, int current_workspace)
+{
+ XWindowAttributes window_attrs;
+ int idx;
+
+ if (window == None || window == PointerRoot || window == root_window)
+ return 0;
+ if (!XGetWindowAttributes(display, window, &window_attrs))
+ return 0;
+ if (window_attrs.override_redirect)
+ return 0;
+ if (window_attrs.map_state != IsViewable)
+ return 0;
+ if (!is_normal_window_type(display, window))
+ return 0;
+ idx = find_window_index(windows, window_count, window);
+ if (idx < 0)
+ return 0;
+ if (window_floating[idx])
+ return 0;
+ if (window_workspace[idx] != current_workspace)
+ return 0;
+ return 1;
+}
+
+static void
+apply_masterstack_region(Display *display, Window *region_windows, int region_count,
+ int x, int y, int width, int height)
+{
+ int i;
+ int area_x, area_y, area_w, area_h;
+
+ if (region_count < 1)
+ return;
+ area_x = x + outer_gap;
+ area_y = y + outer_gap + top_gap;
+ area_w = width - (outer_gap * 2);
+ area_h = height - (outer_gap * 2) - top_gap;
+ if (area_w < 1)
+ area_w = 1;
+ if (area_h < 1)
+ area_h = 1;
+ if (region_count == 1) {
+ XMoveResizeWindow(display, region_windows[0], area_x, area_y,
+ (unsigned int)area_w, (unsigned int)area_h);
+ return;
+ }
+
+ {
+ int master_width, stack_width, stack_count, stack_y;
+ int avail_w, avail_h, total_vgap;
+ int stack_h_each, stack_h_extra;
+
+ avail_w = area_w - inner_gap;
+ if (avail_w < 2)
+ avail_w = area_w;
+ master_width = (avail_w * master_factor_num) / master_factor_den;
+ if (master_width < 1)
+ master_width = 1;
+ if (master_width >= avail_w)
+ master_width = avail_w - 1;
+ if (master_width < 1)
+ master_width = avail_w;
+ stack_width = avail_w - master_width;
+ if (stack_width < 1)
+ stack_width = 1;
+ if (master_width < 1)
+ master_width = 1;
+ XMoveResizeWindow(display, region_windows[0], area_x, area_y,
+ (unsigned int)master_width, (unsigned int)area_h);
+ stack_count = region_count - 1;
+ if (stack_count < 1)
+ return;
+
+ total_vgap = inner_gap * (stack_count - 1);
+ avail_h = area_h - total_vgap;
+ if (avail_h < stack_count)
+ avail_h = stack_count;
+ stack_h_each = avail_h / stack_count;
+ stack_h_extra = avail_h % stack_count;
+ if (stack_h_each < 1)
+ stack_h_each = 1;
+ stack_y = area_y;
+ for (i = 1; i < region_count; i++) {
+ int h;
+
+ h = stack_h_each;
+ if ((i - 1) < stack_h_extra)
+ h++;
+ XMoveResizeWindow(display, region_windows[i], area_x + master_width + inner_gap, stack_y,
+ (unsigned int)stack_width, (unsigned int)h);
+ stack_y += h;
+ if (i < region_count - 1)
+ stack_y += inner_gap;
+ }
+ }
+}
+
+void
+tile_masterstack_apply(Display *display, Window root_window,
+ Window *windows, int *window_workspace, int *window_floating,
+ int window_count, int current_workspace)
+{
+ XWindowAttributes root_attrs;
+ Window tiled[max_workspace_windows];
+ int tiled_count;
+ int i;
+ int monitor_count;
+ int monitor_x[max_monitors], monitor_y[max_monitors];
+ int monitor_w[max_monitors], monitor_h[max_monitors];
+
+ if (!XGetWindowAttributes(display, root_window, &root_attrs))
+ return;
+
+ tiled_count = 0;
+ for (i = 0; i < window_count && tiled_count < max_workspace_windows; i++) {
+ if (is_tile_candidate(display, root_window, windows[i],
+ windows, window_workspace, window_floating, window_count, current_workspace))
+ tiled[tiled_count++] = windows[i];
+ }
+ if (tiled_count < 1)
+ return;
+
+ monitor_count = 1;
+ monitor_x[0] = 0;
+ monitor_y[0] = 0;
+ monitor_w[0] = root_attrs.width;
+ monitor_h[0] = root_attrs.height;
+
+#ifdef XINERAMA
+ monitor_count = xinerama_init(display, root_window);
+ if (monitor_count < 1)
+ monitor_count = 1;
+ if (monitor_count > max_monitors)
+ monitor_count = max_monitors;
+ for (i = 0; i < monitor_count; i++) {
+ if (!xinerama_monitor_geometry(i, &monitor_x[i], &monitor_y[i],
+ &monitor_w[i], &monitor_h[i])) {
+ monitor_x[i] = monitor_x[0];
+ monitor_y[i] = monitor_y[0];
+ monitor_w[i] = monitor_w[0];
+ monitor_h[i] = monitor_h[0];
+ }
+ }
+#endif
+
+ for (i = 0; i < monitor_count; i++) {
+ Window mon_windows[max_workspace_windows];
+ int mon_count;
+ int k;
+
+ mon_count = 0;
+ for (k = 0; k < tiled_count; k++) {
+ int mon;
+
+#ifdef XINERAMA
+ mon = xinerama_monitor_for_window(display, root_window, tiled[k]);
+#else
+ mon = 0;
+#endif
+ if (mon == i)
+ mon_windows[mon_count++] = tiled[k];
+ }
+ apply_masterstack_region(display, mon_windows, mon_count,
+ monitor_x[i], monitor_y[i], monitor_w[i], monitor_h[i]);
+ }
+}
diff --git a/extras/tile/masterstack.h b/extras/tile/masterstack.h
new file mode 100644
index 0000000..6fa5729
--- /dev/null
+++ b/extras/tile/masterstack.h
@@ -0,0 +1,12 @@
+#ifndef MASTERSTACK_H
+#define MASTERSTACK_H
+
+#include <X11/Xlib.h>
+
+void tile_masterstack_apply(Display *display, Window root_window,
+ Window *windows, int *window_workspace, int *window_floating,
+ int window_count, int current_workspace);
+void tile_masterstack_set_gaps(int inner_gap, int outer_gap);
+void tile_masterstack_set_top_gap(int top_gap);
+
+#endif
diff --git a/wm.c b/wm.c
index 4b98a9f..3afc661 100644
--- a/wm.c
+++ b/wm.c
@@ -7,6 +7,9 @@
#ifdef XINERAMA
#include "extras/xinerama.h"
#endif
+#ifdef MASTERSTACK
+#include "extras/tile/masterstack.h"
+#endif
#ifdef BAR
#include "extras/bar.h"
#endif
@@ -26,20 +29,97 @@
#define cmd_bar_sf 10
#define cmd_bar_sb 11
#define cmd_bar_sa 12
+#define cmd_gap_inner 13
+#define cmd_gap_outer 14
+#define cmd_gap_top 15
+#define cmd_tile_focused 16
+#define cmd_bar_height 17
+#define cmd_bar_font 18
+#define cmd_bar_showempty 19
#define num_workspaces 9
#define modifier_variants 4
+#define max_monitors 16
static Window windows[max_windows];
-static int window_workspace[max_windows], window_count, current_workspace;
+static int window_workspace[max_windows], window_floating[max_windows], window_count;
+static int current_workspace[max_monitors], monitor_count;
static Atom net_wm_desktop_atom;
#ifdef BAR
static Atom wm_bar_margin_x_atom, wm_bar_margin_y_atom, wm_bar_bottom_atom;
static Atom wm_bar_nf_atom, wm_bar_nb_atom, wm_bar_sf_atom, wm_bar_sb_atom, wm_bar_sa_atom;
+static Atom wm_bar_height_atom, wm_bar_font_atom;
+static Atom wm_bar_showempty_atom;
static Atom net_wm_name_atom, utf8_string_atom;
#endif
+#ifdef MASTERSTACK
+static Atom wm_gap_inner_atom, wm_gap_outer_atom, wm_gap_top_atom;
+#endif
static int is_root_child(Display *display, Window root_window, Window window);
static void select_window_events(Display *display, Window window);
+static int
+monitor_for_window(Display *display, Window root_window, Window window)
+{
+#ifdef XINERAMA
+ int mon;
+
+ mon = xinerama_monitor_for_window(display, root_window, window);
+ if (mon < 0 || mon >= monitor_count)
+ return 0;
+ return mon;
+#else
+ (void)display;
+ (void)root_window;
+ (void)window;
+ return 0;
+#endif
+}
+
+static int
+active_monitor(Display *display, Window root_window)
+{
+ Window root_return, child_return;
+ int root_x, root_y, window_x, window_y;
+ unsigned int mask;
+
+ if (!XQueryPointer(display, root_window, &root_return, &child_return,
+ &root_x, &root_y, &window_x, &window_y, &mask))
+ return 0;
+#ifdef XINERAMA
+ {
+ int mon;
+
+ mon = xinerama_monitor_for_point(root_x, root_y);
+ if (mon < 0 || mon >= monitor_count)
+ return 0;
+ return mon;
+ }
+#else
+ (void)root_x;
+ (void)root_y;
+ return 0;
+#endif
+}
+
+#ifdef MASTERSTACK
+static void
+apply_layout(Display *display, Window root_window)
+{
+ int mon;
+
+ mon = active_monitor(display, root_window);
+ tile_masterstack_apply(display, root_window, windows, window_workspace,
+ window_floating, window_count, current_workspace[mon]);
+}
+#else
+static void
+apply_layout(Display *display, Window root_window)
+{
+ (void)display;
+ (void)root_window;
+}
+#endif
+
static int
find_window_index(Window window)
{
@@ -139,10 +219,34 @@ static void
refresh_bar(Display *display)
{
static int tags[num_workspaces] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ int mon_tags[max_monitors];
+ int usage[max_monitors * num_workspaces];
Window focused_window;
int revert_to;
+ int i, j;
- bar_set_tags(tags, num_workspaces, current_workspace);
+ for (i = 0; i < max_monitors * num_workspaces; i++)
+ usage[i] = 0;
+ for (i = 0; i < window_count; i++) {
+ int mon;
+ int ws;
+
+ mon = monitor_for_window(display, DefaultRootWindow(display), windows[i]);
+ ws = window_workspace[i];
+ if (mon >= 0 && mon < max_monitors && ws >= 0 && ws < num_workspaces)
+ usage[mon * num_workspaces + ws] = 1;
+ }
+ for (i = 0; i < monitor_count && i < max_monitors; i++)
+ mon_tags[i] = current_workspace[i];
+ if (i < 1) {
+ mon_tags[0] = 0;
+ i = 1;
+ }
+ for (j = i; j < max_monitors; j++)
+ mon_tags[j] = mon_tags[0];
+ bar_set_tags(tags, num_workspaces, mon_tags[0]);
+ bar_set_selected_tags(mon_tags, i);
+ bar_set_workspace_usage(usage, monitor_count, num_workspaces);
XGetInputFocus(display, &focused_window, &revert_to);
set_bar_title_from_window(display, focused_window);
bar_set_status("");
@@ -172,7 +276,8 @@ get_window_index(Window window)
if (window == None || window_count >= max_windows)
return -1;
windows[window_count] = window;
- window_workspace[window_count] = current_workspace;
+ window_workspace[window_count] = 0;
+ window_floating[window_count] = 0;
return window_count++;
}
@@ -205,7 +310,7 @@ get_window_desktop(Display *display, Window window)
return (int)desktop;
}
-#ifdef BAR
+#if defined(BAR) || defined(MASTERSTACK)
static int
read_root_int_property(Display *display, Window root_window, Atom atom, int default_value)
{
@@ -239,7 +344,9 @@ write_root_int_property(Display *display, Window root_window, Atom atom, int val
XChangeProperty(display, root_window, atom, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&card, 1);
}
+#endif
+#ifdef BAR
static char *
read_root_string_property(Display *display, Window root_window, Atom atom)
{
@@ -292,6 +399,7 @@ remove_window(Window window)
for (; i < window_count - 1; i++) {
windows[i] = windows[i + 1];
window_workspace[i] = window_workspace[i + 1];
+ window_floating[i] = window_floating[i + 1];
}
window_count--;
return;
@@ -333,6 +441,8 @@ is_managed_window(Display *display, Window root_window, Window window)
{
XWindowAttributes window_attrs;
+ if (ewmh_is_internal_window(window))
+ return 0;
if (!is_root_child(display, root_window, window))
return 0;
get_window_attrs(display, window, &window_attrs);
@@ -378,18 +488,23 @@ is_root_child(Display *display, Window root_window, Window window)
}
static void
-show_workspace(Display *display, Window root_window)
+show_workspace(Display *display, Window root_window, int target_monitor)
{
Window root_return, parent, *children;
int i, j, window_index;
if (XQueryTree(display, root_window, &root_return, &parent, &children, (unsigned int *)&j)) {
for (i = 0; i < j; i++) {
+ int mon;
+
if (!is_managed_window(display, root_window, children[i]))
continue;
+ mon = monitor_for_window(display, root_window, children[i]);
+ if (mon != target_monitor)
+ continue;
window_index = get_window_index(children[i]);
if (window_index >= 0) {
- if (window_workspace[window_index] == current_workspace)
+ if (window_workspace[window_index] == current_workspace[target_monitor])
XMapWindow(display, children[i]);
else
XUnmapWindow(display, children[i]);
@@ -417,6 +532,9 @@ main(int argc, char **argv)
is_dragging = 0;
drag_window = None;
(void)argc;
+ monitor_count = 1;
+ for (i = 0; i < max_monitors; i++)
+ current_workspace[i] = 0;
if ((display = XOpenDisplay(NULL)) == NULL)
return 1;
XSetErrorHandler(x_error_handler);
@@ -433,13 +551,25 @@ main(int argc, char **argv)
wm_bar_sf_atom = XInternAtom(display, "_WM_BAR_SF", False);
wm_bar_sb_atom = XInternAtom(display, "_WM_BAR_SB", False);
wm_bar_sa_atom = XInternAtom(display, "_WM_BAR_SA", False);
+ wm_bar_height_atom = XInternAtom(display, "_WM_BAR_HEIGHT", False);
+ wm_bar_font_atom = XInternAtom(display, "_WM_BAR_FONT", False);
+ wm_bar_showempty_atom = XInternAtom(display, "_WM_BAR_SHOWEMPTY", False);
net_wm_name_atom = XInternAtom(display, "_NET_WM_NAME", False);
utf8_string_atom = XInternAtom(display, "UTF8_STRING", False);
+#endif
+#ifdef MASTERSTACK
+ wm_gap_inner_atom = XInternAtom(display, "_WM_GAP_INNER", False);
+ wm_gap_outer_atom = XInternAtom(display, "_WM_GAP_OUTER", False);
+ wm_gap_top_atom = XInternAtom(display, "_WM_GAP_TOP", False);
#endif
XSelectInput(display, root_window, SubstructureNotifyMask | StructureNotifyMask);
track_windows(display, root_window);
#ifdef XINERAMA
- xinerama_init(display, root_window);
+ monitor_count = xinerama_init(display, root_window);
+ if (monitor_count < 1)
+ monitor_count = 1;
+ if (monitor_count > max_monitors)
+ monitor_count = max_monitors;
#endif
ewmh_init(display, root_window);
ewmh_sync(display, root_window, windows, window_workspace, window_count);
@@ -463,8 +593,26 @@ main(int argc, char **argv)
bar_set_margins(read_root_int_property(display, root_window, wm_bar_margin_x_atom, 0),
read_root_int_property(display, root_window, wm_bar_margin_y_atom, 0));
bar_set_bottom(read_root_int_property(display, root_window, wm_bar_bottom_atom, 0) ? 1 : 0);
+ bar_set_height(read_root_int_property(display, root_window, wm_bar_height_atom, 18));
+ bar_set_show_empty(read_root_int_property(display, root_window, wm_bar_showempty_atom, 1));
+ {
+ char *font_name;
+
+ font_name = read_root_string_property(display, root_window, wm_bar_font_atom);
+ if (font_name != NULL) {
+ bar_set_font(font_name);
+ free(font_name);
+ }
+ }
refresh_bar(display);
#endif
+#ifdef MASTERSTACK
+ tile_masterstack_set_gaps(
+ read_root_int_property(display, root_window, wm_gap_inner_atom, 0),
+ read_root_int_property(display, root_window, wm_gap_outer_atom, 0));
+ tile_masterstack_set_top_gap(
+ read_root_int_property(display, root_window, wm_gap_top_atom, 0));
+#endif
grab_button_variants(display, 1, 0, root_window, GrabModeSync);
grab_button_variants(display, 1, mod_key, root_window, GrabModeAsync);
@@ -474,20 +622,36 @@ main(int argc, char **argv)
XNextEvent(display, &event);
if (event.type == ClientMessage && event.xclient.message_type == control_atom) {
int command;
-#ifdef BAR
+#if defined(BAR) || defined(MASTERSTACK)
int value;
#endif
command = (int)event.xclient.data.l[0];
i = (int)event.xclient.data.l[1];
-#ifdef BAR
+#if defined(BAR) || defined(MASTERSTACK)
value = (int)event.xclient.data.l[1];
+#endif
+#ifdef BAR
if (command == cmd_bar_position) {
bar_set_bottom(value ? 1 : 0);
write_root_int_property(display, root_window, wm_bar_bottom_atom, value ? 1 : 0);
refresh_bar(display);
continue;
+ } else if (command == cmd_bar_height) {
+ bar_set_height(value);
+ write_root_int_property(display, root_window, wm_bar_height_atom, value);
+ refresh_bar(display);
+ continue;
+ } else if (command == cmd_bar_showempty) {
+ int show_empty;
+
+ show_empty = read_root_int_property(display, root_window, wm_bar_showempty_atom, 1);
+ show_empty = show_empty ? 0 : 1;
+ bar_set_show_empty(show_empty);
+ write_root_int_property(display, root_window, wm_bar_showempty_atom, show_empty);
+ refresh_bar(display);
+ continue;
} else if (command == cmd_bar_margin_x) {
bar_set_margins(value, -1);
write_root_int_property(display, root_window, wm_bar_margin_x_atom, value);
@@ -498,7 +662,7 @@ main(int argc, char **argv)
write_root_int_property(display, root_window, wm_bar_margin_y_atom, value);
refresh_bar(display);
continue;
- } else if (command == cmd_bar_nf || command == cmd_bar_nb ||
+ } else if (command == cmd_bar_font || command == cmd_bar_nf || command == cmd_bar_nb ||
command == cmd_bar_sf || command == cmd_bar_sb || command == cmd_bar_sa) {
Atom source_atom;
Atom actual_type;
@@ -513,7 +677,10 @@ main(int argc, char **argv)
&prop_data) == Success &&
prop_data != NULL && actual_type == XA_STRING &&
actual_format == 8 && nitems > 0) {
- if (command == cmd_bar_nf) {
+ if (command == cmd_bar_font) {
+ bar_set_font((char *)prop_data);
+ write_root_string_property(display, root_window, wm_bar_font_atom, (char *)prop_data);
+ } else if (command == cmd_bar_nf) {
bar_set_colors((char *)prop_data, NULL, NULL, NULL, NULL);
write_root_string_property(display, root_window, wm_bar_nf_atom, (char *)prop_data);
} else if (command == cmd_bar_nb) {
@@ -536,12 +703,47 @@ main(int argc, char **argv)
XDeleteProperty(display, root_window, source_atom);
continue;
}
+#endif
+#ifdef MASTERSTACK
+ if (command == cmd_gap_inner) {
+ tile_masterstack_set_gaps(value, -1);
+ write_root_int_property(display, root_window, wm_gap_inner_atom, value);
+ apply_layout(display, root_window);
+ continue;
+ } else if (command == cmd_gap_outer) {
+ tile_masterstack_set_gaps(-1, value);
+ write_root_int_property(display, root_window, wm_gap_outer_atom, value);
+ apply_layout(display, root_window);
+ continue;
+ } else if (command == cmd_gap_top) {
+ tile_masterstack_set_top_gap(value);
+ write_root_int_property(display, root_window, wm_gap_top_atom, value);
+ apply_layout(display, root_window);
+ continue;
+ }
#endif
if (command == cmd_kill) {
XGetInputFocus(display, &focused_window, &j);
if (!is_root_child(display, root_window, focused_window))
continue;
XKillClient(display, focused_window);
+ } else if (command == cmd_tile_focused) {
+ XGetInputFocus(display, &focused_window, &j);
+ if (!is_root_child(display, root_window, focused_window))
+ continue;
+ j = get_window_index(focused_window);
+ if (j >= 0) {
+ int mon;
+
+ mon = monitor_for_window(display, root_window, focused_window);
+ window_floating[j] = 0;
+ window_workspace[j] = current_workspace[mon];
+ apply_layout(display, root_window);
+ ewmh_sync(display, root_window, windows, window_workspace, window_count);
+#ifdef BAR
+ refresh_bar(display);
+#endif
+ }
} else if (command == cmd_reload) {
#ifdef BAR
bar_cleanup();
@@ -550,11 +752,17 @@ main(int argc, char **argv)
execvp(argv[0], argv);
return 1;
} else if (i >= 0 && i < num_workspaces) {
- if (command == cmd_workspace && i != current_workspace) {
+ if (command == cmd_workspace) {
+ int mon;
+
+ mon = active_monitor(display, root_window);
+ if (i == current_workspace[mon])
+ continue;
track_windows(display, root_window);
- current_workspace = i;
- show_workspace(display, root_window);
- ewmh_set_current_desktop(display, root_window, current_workspace);
+ current_workspace[mon] = i;
+ show_workspace(display, root_window, mon);
+ apply_layout(display, root_window);
+ ewmh_set_current_desktop(display, root_window, current_workspace[mon]);
ewmh_sync(display, root_window, windows, window_workspace, window_count);
#ifdef BAR
refresh_bar(display);
@@ -566,9 +774,14 @@ main(int argc, char **argv)
ewmh_set_active_window(display, root_window, focused_window);
j = get_window_index(focused_window);
if (j >= 0) {
+ int mon;
+
+ mon = monitor_for_window(display, root_window, focused_window);
window_workspace[j] = i;
- if (i != current_workspace)
+ window_floating[j] = 0;
+ if (i != current_workspace[mon])
XUnmapWindow(display, focused_window);
+ apply_layout(display, root_window);
ewmh_sync(display, root_window, windows, window_workspace, window_count);
#ifdef BAR
refresh_bar(display);
@@ -578,6 +791,7 @@ main(int argc, char **argv)
}
} else if (event.type == DestroyNotify) {
remove_window(event.xdestroywindow.window);
+ apply_layout(display, root_window);
ewmh_sync(display, root_window, windows, window_workspace, window_count);
#ifdef BAR
refresh_bar(display);
@@ -589,9 +803,18 @@ main(int argc, char **argv)
if (!is_managed_window(display, root_window, mapped_window))
continue;
if (find_window_index(mapped_window) < 0) {
+ int mon;
+
place_window_at_pointer(display, root_window, mapped_window);
select_window_events(display, mapped_window);
get_window_index(mapped_window);
+ mon = monitor_for_window(display, root_window, mapped_window);
+ j = find_window_index(mapped_window);
+ if (j >= 0) {
+ window_workspace[j] = current_workspace[mon];
+ window_floating[j] = 0;
+ }
+ apply_layout(display, root_window);
ewmh_sync(display, root_window, windows, window_workspace, window_count);
#ifdef BAR
refresh_bar(display);
@@ -607,8 +830,17 @@ main(int argc, char **argv)
ewmh_set_active_window(display, root_window, drag_window);
XSetInputFocus(display, drag_window, RevertToPointerRoot, CurrentTime);
j = get_window_index(drag_window);
- if (j >= 0)
- window_workspace[j] = current_workspace;
+ if (j >= 0) {
+ int mon;
+
+ mon = monitor_for_window(display, root_window, drag_window);
+ window_workspace[j] = current_workspace[mon];
+ if ((event.xbutton.state & mod_key) &&
+ (event.xbutton.button == 1 || event.xbutton.button == 3)) {
+ window_floating[j] = 1;
+ apply_layout(display, root_window);
+ }
+ }
ewmh_sync(display, root_window, windows, window_workspace, window_count);
#ifdef BAR
refresh_bar(display);
@@ -673,7 +905,11 @@ main(int argc, char **argv)
} else if (event.type == ConfigureNotify &&
event.xconfigure.window == root_window) {
#ifdef XINERAMA
- xinerama_init(display, root_window);
+ monitor_count = xinerama_init(display, root_window);
+ if (monitor_count < 1)
+ monitor_count = 1;
+ if (monitor_count > max_monitors)
+ monitor_count = max_monitors;
#endif
#ifdef BAR
bar_reconfigure();
diff --git a/wmc.c b/wmc.c
index 415def1..916d0a8 100644
--- a/wmc.c
+++ b/wmc.c
@@ -1,6 +1,7 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
+#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -17,6 +18,13 @@
#define cmd_bar_sf 10
#define cmd_bar_sb 11
#define cmd_bar_sa 12
+#define cmd_gap_inner 13
+#define cmd_gap_outer 14
+#define cmd_gap_top 15
+#define cmd_tile_focused 16
+#define cmd_bar_height 17
+#define cmd_bar_font 18
+#define cmd_bar_showempty 19
int
main(int argc, char **argv)
@@ -33,6 +41,8 @@ main(int argc, char **argv)
value = 0;
if (argc == 2 && !strcmp(argv[1], "kill"))
command = cmd_kill;
+ else if (argc == 2 && !strcmp(argv[1], "tile"))
+ command = cmd_tile_focused;
else if (argc == 2 && !strcmp(argv[1], "reload"))
command = cmd_reload;
else if (argc == 3 && !strcmp(argv[1], "bar")) {
@@ -42,11 +52,23 @@ main(int argc, char **argv)
} else if (!strcmp(argv[2], "bottom")) {
command = cmd_bar_position;
value = 1;
+ } else if (!strcmp(argv[2], "showempty")) {
+ command = cmd_bar_showempty;
+ value = 0;
} else {
- fprintf(stderr, "usage: wmc bar top|bottom\n");
+ fprintf(stderr, "usage: wmc bar top|bottom|showempty\n");
return 1;
}
} else if (argc == 4 && !strcmp(argv[1], "bar")) {
+ if (!strcmp(argv[2], "height")) {
+ value = (int)strtol(argv[3], &end_ptr, 10);
+ if (*end_ptr != '\0' || value < 1)
+ return 1;
+ command = cmd_bar_height;
+ } else if (!strcmp(argv[2], "font")) {
+ command = cmd_bar_font;
+ value = 0;
+ } else {
if (!strcmp(argv[2], "nf"))
command = cmd_bar_nf;
else if (!strcmp(argv[2], "nb"))
@@ -69,10 +91,26 @@ main(int argc, char **argv)
command = cmd_bar_margin_y;
else {
fprintf(stderr, "usage: wmc bar mx <px> | wmc bar my <px> | "
+ "wmc bar height <px> | wmc bar font <xfont> | "
"wmc bar nf|nb|sf|sb|sa <color>\n");
return 1;
}
}
+ }
+ } else if (argc == 4 && !strcmp(argv[1], "gap")) {
+ value = (int)strtol(argv[3], &end_ptr, 10);
+ if (*end_ptr != '\0' || value < 0)
+ return 1;
+ if (!strcmp(argv[2], "inner"))
+ command = cmd_gap_inner;
+ else if (!strcmp(argv[2], "outer"))
+ command = cmd_gap_outer;
+ else if (!strcmp(argv[2], "top"))
+ command = cmd_gap_top;
+ else {
+ fprintf(stderr, "usage: wmc gap inner <px> | wmc gap outer <px> | wmc gap top <px>\n");
+ return 1;
+ }
}
else if (argc == 2)
workspace = (int)strtol(argv[1], &end_ptr, 10);
@@ -83,15 +121,19 @@ main(int argc, char **argv)
workspace = (int)strtol(argv[2], &end_ptr, 10);
else {
fprintf(stderr, "usage: wmc [ws] 1-9 | wmc move 1-9 | wmc kill | wmc reload | "
- "wmc bar top|bottom | wmc bar mx <px> | wmc bar my <px> | "
- "wmc bar nf|nb|sf|sb|sa <color>\n");
+ "wmc tile | "
+ "wmc bar top|bottom|showempty | wmc bar mx <px> | wmc bar my <px> | "
+ "wmc bar height <px> | wmc bar font <xfont> | "
+ "wmc bar nf|nb|sf|sb|sa <color> | wmc gap inner|outer|top <px>\n");
return 1;
}
if ((command == cmd_workspace || command == cmd_move) &&
(*end_ptr != '\0' || workspace < 1 || workspace > 9))
return 1;
- if (command == cmd_bar_position || command == cmd_bar_margin_x ||
- command == cmd_bar_margin_y)
+ if (command == cmd_bar_position || command == cmd_bar_showempty || command == cmd_bar_margin_x ||
+ command == cmd_bar_margin_y || command == cmd_bar_height ||
+ command == cmd_gap_inner ||
+ command == cmd_gap_outer || command == cmd_gap_top)
workspace = value;
if ((display = XOpenDisplay(NULL)) == NULL)
return 1;
@@ -105,7 +147,7 @@ main(int argc, char **argv)
event.xclient.data.l[0] = command;
if (command == cmd_workspace || command == cmd_move)
event.xclient.data.l[1] = workspace > 0 ? workspace - 1 : 0;
- else if (command >= cmd_bar_nf && command <= cmd_bar_sa) {
+ else if (command == cmd_bar_font || (command >= cmd_bar_nf && command <= cmd_bar_sa)) {
Atom payload_atom;
char atom_name[64];
static unsigned long seq;
@@ -121,6 +163,5 @@ main(int argc, char **argv)
event.xclient.data.l[1] = workspace;
XSendEvent(display, root_window, False, SubstructureNotifyMask, &event);
XFlush(display);
- XCloseDisplay(display);
- return 0;
+ _exit(0);
}