diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-04-15 16:35:36 +0200 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-04-15 16:35:36 +0200 |
| commit | 893e4ab58fce2918480501f999313000b6de6249 (patch) | |
| tree | bd3d588210ef2aa56434fbf94adfc6f333311b84 | |
| parent | 91fe87974e8c0d02b230fb1218905920154f5af5 (diff) | |
| download | glitch-893e4ab58fce2918480501f999313000b6de6249.tar.gz | |
Add tiling and floating mode
| -rw-r--r-- | config.def.h | 5 | ||||
| -rw-r--r-- | glitch.h | 20 | ||||
| -rw-r--r-- | manager.c | 108 | ||||
| -rw-r--r-- | widgets.c | 53 |
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 } }, }; @@ -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 @@ -253,6 +253,11 @@ void init_window_manager(void) { XChangeProperty(wm.dpy, wm.root, _NET_CURRENT_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)¤t_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"); +} @@ -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(); } |
