summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-04-15 16:35:36 +0200
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-04-15 16:35:36 +0200
commit893e4ab58fce2918480501f999313000b6de6249 (patch)
treebd3d588210ef2aa56434fbf94adfc6f333311b84
parent91fe87974e8c0d02b230fb1218905920154f5af5 (diff)
downloadglitch-893e4ab58fce2918480501f999313000b6de6249.tar.gz
Add tiling and floating mode
-rw-r--r--config.def.h5
-rw-r--r--glitch.h20
-rw-r--r--manager.c108
-rw-r--r--widgets.c53
4 files changed, 181 insertions, 5 deletions
diff --git a/config.def.h b/config.def.h
index 67f78d0..a10d0f9 100644
--- a/config.def.h
+++ b/config.def.h
@@ -28,6 +28,10 @@ static const char *mic_active_bg_color = "firebrick";
static const char *mic_muted_bg_color = "#222222";
static const char *mic_active_fg_color = "white";
static const char *mic_muted_fg_color = "white";
+static const char *layout_tile_bg_color = "darkgreen";
+static const char *layout_float_bg_color = "#333333";
+static const char *layout_tile_fg_color = "white";
+static const char *layout_float_fg_color = "white";
static Shortcut shortcuts[] = {
/* Mask KeySym Shell command */
@@ -87,6 +91,7 @@ static Keybinds keybinds[] = {
{ MODKEY | ShiftMask, XK_r, reload, { 0 } },
{ MODKEY, XK_c, center_window, { 0 } },
{ MODKEY, XK_m, toggle_mic_mute, { 0 } },
+ { MODKEY, XK_space, toggle_layout, { 0 } },
{ MODKEY | ShiftMask, XK_q, quit, { 0 } },
{ MODKEY, XK_q, close_window, { 0 } },
};
diff --git a/glitch.h b/glitch.h
index 507b007..b4e416d 100644
--- a/glitch.h
+++ b/glitch.h
@@ -32,6 +32,11 @@ typedef enum {
LOG_ERROR,
} LogLevel;
+typedef enum {
+ LAYOUT_FLOATING,
+ LAYOUT_TILING,
+} LayoutMode;
+
typedef struct {
unsigned long normal_active;
unsigned long normal_inactive;
@@ -77,16 +82,23 @@ typedef struct {
XftColor xft_mic_muted_bg;
XftColor xft_mic_active_fg;
XftColor xft_mic_muted_fg;
-
+ XftColor xft_layout_tile_bg;
+ XftColor xft_layout_float_bg;
+ XftColor xft_layout_tile_fg;
+ XftColor xft_layout_float_fg;
+
unsigned long last_widget_update;
Client *clients;
-
+
int is_cycling;
Window cycle_win;
Window *cycle_clients;
int cycle_count;
int active_cycle_index;
+ // Layout management
+ LayoutMode layout_modes[NUM_DESKTOPS + 1]; // 1-indexed for convenience
+
// PulseAudio
pa_threaded_mainloop *pa_mainloop;
pa_context *pa_ctx;
@@ -172,10 +184,14 @@ void center_window(const Arg *arg);
void widget_desktop_indicator(void);
void widget_datetime(void);
void widget_mic_indicator(void);
+void widget_layout_indicator(void);
void redraw_widgets(void);
void init_audio(void);
void deinit_audio(void);
void toggle_mic_mute(const Arg *arg);
+void apply_tiling_layout(void);
+void toggle_layout(const Arg *arg);
+
#endif // GLITCH_H
diff --git a/manager.c b/manager.c
index f6b46f1..63f7e58 100644
--- a/manager.c
+++ b/manager.c
@@ -253,6 +253,11 @@ void init_window_manager(void) {
XChangeProperty(wm.dpy, wm.root, _NET_CURRENT_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&current_desktop, 1);
log_message(stdout, LOG_DEBUG, "Registering %d desktops", NUM_DESKTOPS);
+ // Initialize layout modes.
+ for (int i = 0; i <= NUM_DESKTOPS; i++) {
+ wm.layout_modes[i] = LAYOUT_FLOATING;
+ }
+
// Initialize colormap early as it's needed for Xft.
wm.cmap = DefaultColormap(wm.dpy, wm.screen);
@@ -308,6 +313,30 @@ void init_window_manager(void) {
XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_mic_muted_fg);
}
+ if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_tile_bg_color, &wm.xft_layout_tile_bg)) {
+ log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to dark green", layout_tile_bg_color);
+ XRenderColor render_color = {0x0000, 0x6400, 0x0000, 0xFFFF};
+ XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_tile_bg);
+ }
+
+ if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_float_bg_color, &wm.xft_layout_float_bg)) {
+ log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to gray", layout_float_bg_color);
+ XRenderColor render_color = {0x3333, 0x3333, 0x3333, 0xFFFF};
+ XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_float_bg);
+ }
+
+ if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_tile_fg_color, &wm.xft_layout_tile_fg)) {
+ log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to white", layout_tile_fg_color);
+ XRenderColor render_color = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+ XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_tile_fg);
+ }
+
+ if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_float_fg_color, &wm.xft_layout_float_fg)) {
+ log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to white", layout_float_fg_color);
+ XRenderColor render_color = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+ XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_float_fg);
+ }
+
wm.running = 1;
scan_windows();
@@ -641,7 +670,8 @@ void handle_map_request(void) {
grab_buttons(window);
add_client(window);
-
+ apply_tiling_layout();
+
XSync(wm.dpy, False);
XSetErrorHandler(old);
@@ -663,6 +693,7 @@ void handle_unmap_notify(void) {
}
log_message(stdout, LOG_DEBUG, "Window 0x%lx unmapped", window);
+ apply_tiling_layout();
update_client_list();
}
@@ -673,6 +704,7 @@ void handle_destroy_notify(void) {
wm.active = None;
}
remove_client(window);
+ apply_tiling_layout();
log_message(stdout, LOG_DEBUG, "Window 0x%lx destroyed", window);
update_client_list();
}
@@ -938,6 +970,7 @@ void goto_desktop(const Arg *arg) {
set_active_window(new_active, CurrentTime);
set_active_border(new_active);
+ apply_tiling_layout();
widget_desktop_indicator();
widget_datetime();
@@ -956,6 +989,7 @@ void send_window_to_desktop(const Arg *arg) {
XUnmapWindow(wm.dpy, wm.active);
wm.active = None;
+ apply_tiling_layout();
widget_desktop_indicator();
widget_datetime();
log_message(stdout, LOG_DEBUG, "Moved window to desktop %d", arg->i);
@@ -1612,3 +1646,75 @@ void reload(const Arg *arg) {
wm.restart = 1;
log_message(stdout, LOG_DEBUG, "Reload window manager");
}
+
+void apply_tiling_layout(void) {
+ if (wm.layout_modes[wm.current_desktop] != LAYOUT_TILING) return;
+
+ int n = 0;
+ for (Client *c = wm.clients; c; c = c->next) {
+ if (window_exists(c->window)) {
+ unsigned long desktop;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *prop = NULL;
+
+ int status = XGetWindowProperty(wm.dpy, c->window, _NET_WM_DESKTOP, 0, 1, False, XA_CARDINAL, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
+ if (status == Success && prop && nitems > 0) {
+ desktop = *(unsigned long *)prop;
+ XFree(prop);
+ if (desktop == wm.current_desktop && !is_sticky(c->window) && !is_always_on_top(c->window) && !has_wm_state(c->window, _NET_WM_STATE_FULLSCREEN)) {
+ n++;
+ }
+ } else if (prop) {
+ XFree(prop);
+ }
+ }
+ }
+
+ if (n == 0) return;
+
+ int screen_width = DisplayWidth(wm.dpy, wm.screen);
+ int screen_height = DisplayHeight(wm.dpy, wm.screen);
+ int mw = (n > 1) ? screen_width / 3 : screen_width;
+ int i = 0;
+
+ for (Client *c = wm.clients; c; c = c->next) {
+ if (window_exists(c->window)) {
+ unsigned long desktop;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *prop = NULL;
+
+ int status = XGetWindowProperty(wm.dpy, c->window, _NET_WM_DESKTOP, 0, 1, False, XA_CARDINAL, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
+ if (status == Success && prop && nitems > 0) {
+ desktop = *(unsigned long *)prop;
+ XFree(prop);
+ if (desktop == wm.current_desktop && !is_sticky(c->window) && !is_always_on_top(c->window) && !has_wm_state(c->window, _NET_WM_STATE_FULLSCREEN)) {
+ if (n == 1) {
+ XMoveResizeWindow(wm.dpy, c->window, 0, 0, screen_width - 2 * border_size, screen_height - 2 * border_size);
+ } else if (i == 0) { // Master
+ XMoveResizeWindow(wm.dpy, c->window, 0, 0, mw - 2 * border_size, screen_height - 2 * border_size);
+ } else { // Stack
+ int h = screen_height / (n - 1);
+ XMoveResizeWindow(wm.dpy, c->window, mw, (i - 1) * h, screen_width - mw - 2 * border_size, h - 2 * border_size);
+ }
+ i++;
+ }
+ } else if (prop) {
+ XFree(prop);
+ }
+ }
+ }
+}
+
+void toggle_layout(const Arg *arg) {
+ (void)arg;
+ wm.layout_modes[wm.current_desktop] = (wm.layout_modes[wm.current_desktop] == LAYOUT_TILING) ? LAYOUT_FLOATING : LAYOUT_TILING;
+ if (wm.layout_modes[wm.current_desktop] == LAYOUT_TILING) {
+ apply_tiling_layout();
+ }
+ redraw_widgets();
+ log_message(stdout, LOG_DEBUG, "Toggled layout for desktop %d to %s", wm.current_desktop, wm.layout_modes[wm.current_desktop] == LAYOUT_TILING ? "TILING" : "FLOATING");
+}
diff --git a/widgets.c b/widgets.c
index 4024bba..79fcdac 100644
--- a/widgets.c
+++ b/widgets.c
@@ -44,13 +44,20 @@ void widget_mic_indicator(void) {
XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents);
int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2;
+ // Layout indicator size
+ LayoutMode mode = wm.layout_modes[wm.current_desktop];
+ const char *layout_buf = (mode == LAYOUT_TILING) ? "T" : "F";
+ XGlyphInfo layout_extents;
+ XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)layout_buf, strlen(layout_buf), &layout_extents);
+ int layout_size_w = (wm.font->height > layout_extents.width ? wm.font->height : layout_extents.width) + padding * 2;
+
const char *buf = "MIC";
XGlyphInfo extents;
XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)buf, strlen(buf), &extents);
int size_w = extents.width + padding * 4;
int size_h = desktop_size;
- int x = screen_width - desktop_size - size_w - 20;
+ int x = screen_width - desktop_size - layout_size_w - size_w - 20;
int y = 10;
XftColor *bg = wm.mic_muted ? &wm.xft_mic_muted_bg : &wm.xft_mic_active_bg;
@@ -66,6 +73,40 @@ void widget_mic_indicator(void) {
XftDrawStringUtf8(wm.xft_draw, fg, wm.font, text_x, text_y, (FcChar8 *)buf, strlen(buf));
}
+void widget_layout_indicator(void) {
+ int screen_width = DisplayWidth(wm.dpy, wm.screen);
+ int padding = 3;
+
+ // Desktop indicator size
+ char desktop_buf[8];
+ snprintf(desktop_buf, sizeof(desktop_buf), "%u", wm.current_desktop);
+ XGlyphInfo desktop_extents;
+ XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents);
+ int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2;
+
+ LayoutMode mode = wm.layout_modes[wm.current_desktop];
+ const char *buf = (mode == LAYOUT_TILING) ? "T" : "F";
+ XGlyphInfo extents;
+ XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)buf, strlen(buf), &extents);
+
+ int size_w = (wm.font->height > extents.width ? wm.font->height : extents.width) + padding * 2;
+ int size_h = desktop_size;
+ int x = screen_width - desktop_size - size_w - 15;
+ int y = 10;
+
+ XftColor *bg = (mode == LAYOUT_TILING) ? &wm.xft_layout_tile_bg : &wm.xft_layout_float_bg;
+ XftColor *fg = (mode == LAYOUT_TILING) ? &wm.xft_layout_tile_fg : &wm.xft_layout_float_fg;
+
+ // Draw the background.
+ XftDrawRect(wm.xft_draw, bg, x, y, size_w, size_h);
+
+ // Center the text.
+ int text_x = x + (size_w - extents.width) / 2 + extents.x;
+ int text_y = y + (size_h - wm.font->ascent - wm.font->descent) / 2 + wm.font->ascent;
+
+ XftDrawStringUtf8(wm.xft_draw, fg, wm.font, text_x, text_y, (FcChar8 *)buf, strlen(buf));
+}
+
void widget_datetime(void) {
int screen_width = DisplayWidth(wm.dpy, wm.screen);
int padding = 3;
@@ -77,13 +118,20 @@ void widget_datetime(void) {
XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents);
int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2;
+ // Layout indicator size
+ LayoutMode mode = wm.layout_modes[wm.current_desktop];
+ const char *layout_buf = (mode == LAYOUT_TILING) ? "T" : "F";
+ XGlyphInfo layout_extents;
+ XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)layout_buf, strlen(layout_buf), &layout_extents);
+ int layout_size_w = (wm.font->height > layout_extents.width ? wm.font->height : layout_extents.width) + padding * 2;
+
// Mic indicator size
const char *mic_buf = "MIC";
XGlyphInfo mic_extents;
XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)mic_buf, strlen(mic_buf), &mic_extents);
int mic_size_w = mic_extents.width + padding * 4;
- int offset_x = desktop_size + mic_size_w + 40;
+ int offset_x = desktop_size + layout_size_w + mic_size_w + 35;
char time_buf[64];
time_t now = time(NULL);
@@ -107,6 +155,7 @@ void widget_datetime(void) {
void redraw_widgets(void) {
widget_desktop_indicator();
+ widget_layout_indicator();
widget_mic_indicator();
widget_datetime();
}