aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
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";
28static const char *mic_muted_bg_color = "#222222"; 28static const char *mic_muted_bg_color = "#222222";
29static const char *mic_active_fg_color = "white"; 29static const char *mic_active_fg_color = "white";
30static const char *mic_muted_fg_color = "white"; 30static const char *mic_muted_fg_color = "white";
31static const char *layout_tile_bg_color = "darkgreen";
32static const char *layout_float_bg_color = "#333333";
33static const char *layout_tile_fg_color = "white";
34static const char *layout_float_fg_color = "white";
31 35
32static Shortcut shortcuts[] = { 36static Shortcut shortcuts[] = {
33 /* Mask KeySym Shell command */ 37 /* Mask KeySym Shell command */
@@ -87,6 +91,7 @@ static Keybinds keybinds[] = {
87 { MODKEY | ShiftMask, XK_r, reload, { 0 } }, 91 { MODKEY | ShiftMask, XK_r, reload, { 0 } },
88 { MODKEY, XK_c, center_window, { 0 } }, 92 { MODKEY, XK_c, center_window, { 0 } },
89 { MODKEY, XK_m, toggle_mic_mute, { 0 } }, 93 { MODKEY, XK_m, toggle_mic_mute, { 0 } },
94 { MODKEY, XK_space, toggle_layout, { 0 } },
90 { MODKEY | ShiftMask, XK_q, quit, { 0 } }, 95 { MODKEY | ShiftMask, XK_q, quit, { 0 } },
91 { MODKEY, XK_q, close_window, { 0 } }, 96 { MODKEY, XK_q, close_window, { 0 } },
92}; 97};
diff --git a/glitch.h b/glitch.h
index 507b007..b4e416d 100644
--- a/glitch.h
+++ b/glitch.h
@@ -32,6 +32,11 @@ typedef enum {
32 LOG_ERROR, 32 LOG_ERROR,
33} LogLevel; 33} LogLevel;
34 34
35typedef enum {
36 LAYOUT_FLOATING,
37 LAYOUT_TILING,
38} LayoutMode;
39
35typedef struct { 40typedef struct {
36 unsigned long normal_active; 41 unsigned long normal_active;
37 unsigned long normal_inactive; 42 unsigned long normal_inactive;
@@ -77,16 +82,23 @@ typedef struct {
77 XftColor xft_mic_muted_bg; 82 XftColor xft_mic_muted_bg;
78 XftColor xft_mic_active_fg; 83 XftColor xft_mic_active_fg;
79 XftColor xft_mic_muted_fg; 84 XftColor xft_mic_muted_fg;
80 85 XftColor xft_layout_tile_bg;
86 XftColor xft_layout_float_bg;
87 XftColor xft_layout_tile_fg;
88 XftColor xft_layout_float_fg;
89
81 unsigned long last_widget_update; 90 unsigned long last_widget_update;
82 Client *clients; 91 Client *clients;
83 92
84 int is_cycling; 93 int is_cycling;
85 Window cycle_win; 94 Window cycle_win;
86 Window *cycle_clients; 95 Window *cycle_clients;
87 int cycle_count; 96 int cycle_count;
88 int active_cycle_index; 97 int active_cycle_index;
89 98
99 // Layout management
100 LayoutMode layout_modes[NUM_DESKTOPS + 1]; // 1-indexed for convenience
101
90 // PulseAudio 102 // PulseAudio
91 pa_threaded_mainloop *pa_mainloop; 103 pa_threaded_mainloop *pa_mainloop;
92 pa_context *pa_ctx; 104 pa_context *pa_ctx;
@@ -172,10 +184,14 @@ void center_window(const Arg *arg);
172void widget_desktop_indicator(void); 184void widget_desktop_indicator(void);
173void widget_datetime(void); 185void widget_datetime(void);
174void widget_mic_indicator(void); 186void widget_mic_indicator(void);
187void widget_layout_indicator(void);
175void redraw_widgets(void); 188void redraw_widgets(void);
176 189
177void init_audio(void); 190void init_audio(void);
178void deinit_audio(void); 191void deinit_audio(void);
179void toggle_mic_mute(const Arg *arg); 192void toggle_mic_mute(const Arg *arg);
180 193
194void apply_tiling_layout(void);
195void toggle_layout(const Arg *arg);
196
181#endif // GLITCH_H 197#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) {
253 XChangeProperty(wm.dpy, wm.root, _NET_CURRENT_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&current_desktop, 1); 253 XChangeProperty(wm.dpy, wm.root, _NET_CURRENT_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&current_desktop, 1);
254 log_message(stdout, LOG_DEBUG, "Registering %d desktops", NUM_DESKTOPS); 254 log_message(stdout, LOG_DEBUG, "Registering %d desktops", NUM_DESKTOPS);
255 255
256 // Initialize layout modes.
257 for (int i = 0; i <= NUM_DESKTOPS; i++) {
258 wm.layout_modes[i] = LAYOUT_FLOATING;
259 }
260
256 // Initialize colormap early as it's needed for Xft. 261 // Initialize colormap early as it's needed for Xft.
257 wm.cmap = DefaultColormap(wm.dpy, wm.screen); 262 wm.cmap = DefaultColormap(wm.dpy, wm.screen);
258 263
@@ -308,6 +313,30 @@ void init_window_manager(void) {
308 XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_mic_muted_fg); 313 XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_mic_muted_fg);
309 } 314 }
310 315
316 if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_tile_bg_color, &wm.xft_layout_tile_bg)) {
317 log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to dark green", layout_tile_bg_color);
318 XRenderColor render_color = {0x0000, 0x6400, 0x0000, 0xFFFF};
319 XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_tile_bg);
320 }
321
322 if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_float_bg_color, &wm.xft_layout_float_bg)) {
323 log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to gray", layout_float_bg_color);
324 XRenderColor render_color = {0x3333, 0x3333, 0x3333, 0xFFFF};
325 XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_float_bg);
326 }
327
328 if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_tile_fg_color, &wm.xft_layout_tile_fg)) {
329 log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to white", layout_tile_fg_color);
330 XRenderColor render_color = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
331 XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_tile_fg);
332 }
333
334 if (!XftColorAllocName(wm.dpy, visual, wm.cmap, layout_float_fg_color, &wm.xft_layout_float_fg)) {
335 log_message(stdout, LOG_WARNING, "Failed to allocate color %s, falling back to white", layout_float_fg_color);
336 XRenderColor render_color = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
337 XftColorAllocValue(wm.dpy, visual, wm.cmap, &render_color, &wm.xft_layout_float_fg);
338 }
339
311 wm.running = 1; 340 wm.running = 1;
312 341
313 scan_windows(); 342 scan_windows();
@@ -641,7 +670,8 @@ void handle_map_request(void) {
641 grab_buttons(window); 670 grab_buttons(window);
642 671
643 add_client(window); 672 add_client(window);
644 673 apply_tiling_layout();
674
645 XSync(wm.dpy, False); 675 XSync(wm.dpy, False);
646 XSetErrorHandler(old); 676 XSetErrorHandler(old);
647 677
@@ -663,6 +693,7 @@ void handle_unmap_notify(void) {
663 } 693 }
664 694
665 log_message(stdout, LOG_DEBUG, "Window 0x%lx unmapped", window); 695 log_message(stdout, LOG_DEBUG, "Window 0x%lx unmapped", window);
696 apply_tiling_layout();
666 update_client_list(); 697 update_client_list();
667} 698}
668 699
@@ -673,6 +704,7 @@ void handle_destroy_notify(void) {
673 wm.active = None; 704 wm.active = None;
674 } 705 }
675 remove_client(window); 706 remove_client(window);
707 apply_tiling_layout();
676 log_message(stdout, LOG_DEBUG, "Window 0x%lx destroyed", window); 708 log_message(stdout, LOG_DEBUG, "Window 0x%lx destroyed", window);
677 update_client_list(); 709 update_client_list();
678} 710}
@@ -938,6 +970,7 @@ void goto_desktop(const Arg *arg) {
938 970
939 set_active_window(new_active, CurrentTime); 971 set_active_window(new_active, CurrentTime);
940 set_active_border(new_active); 972 set_active_border(new_active);
973 apply_tiling_layout();
941 974
942 widget_desktop_indicator(); 975 widget_desktop_indicator();
943 widget_datetime(); 976 widget_datetime();
@@ -956,6 +989,7 @@ void send_window_to_desktop(const Arg *arg) {
956 XUnmapWindow(wm.dpy, wm.active); 989 XUnmapWindow(wm.dpy, wm.active);
957 990
958 wm.active = None; 991 wm.active = None;
992 apply_tiling_layout();
959 widget_desktop_indicator(); 993 widget_desktop_indicator();
960 widget_datetime(); 994 widget_datetime();
961 log_message(stdout, LOG_DEBUG, "Moved window to desktop %d", arg->i); 995 log_message(stdout, LOG_DEBUG, "Moved window to desktop %d", arg->i);
@@ -1612,3 +1646,75 @@ void reload(const Arg *arg) {
1612 wm.restart = 1; 1646 wm.restart = 1;
1613 log_message(stdout, LOG_DEBUG, "Reload window manager"); 1647 log_message(stdout, LOG_DEBUG, "Reload window manager");
1614} 1648}
1649
1650void apply_tiling_layout(void) {
1651 if (wm.layout_modes[wm.current_desktop] != LAYOUT_TILING) return;
1652
1653 int n = 0;
1654 for (Client *c = wm.clients; c; c = c->next) {
1655 if (window_exists(c->window)) {
1656 unsigned long desktop;
1657 Atom actual_type;
1658 int actual_format;
1659 unsigned long nitems, bytes_after;
1660 unsigned char *prop = NULL;
1661
1662 int status = XGetWindowProperty(wm.dpy, c->window, _NET_WM_DESKTOP, 0, 1, False, XA_CARDINAL, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
1663 if (status == Success && prop && nitems > 0) {
1664 desktop = *(unsigned long *)prop;
1665 XFree(prop);
1666 if (desktop == wm.current_desktop && !is_sticky(c->window) && !is_always_on_top(c->window) && !has_wm_state(c->window, _NET_WM_STATE_FULLSCREEN)) {
1667 n++;
1668 }
1669 } else if (prop) {
1670 XFree(prop);
1671 }
1672 }
1673 }
1674
1675 if (n == 0) return;
1676
1677 int screen_width = DisplayWidth(wm.dpy, wm.screen);
1678 int screen_height = DisplayHeight(wm.dpy, wm.screen);
1679 int mw = (n > 1) ? screen_width / 3 : screen_width;
1680 int i = 0;
1681
1682 for (Client *c = wm.clients; c; c = c->next) {
1683 if (window_exists(c->window)) {
1684 unsigned long desktop;
1685 Atom actual_type;
1686 int actual_format;
1687 unsigned long nitems, bytes_after;
1688 unsigned char *prop = NULL;
1689
1690 int status = XGetWindowProperty(wm.dpy, c->window, _NET_WM_DESKTOP, 0, 1, False, XA_CARDINAL, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
1691 if (status == Success && prop && nitems > 0) {
1692 desktop = *(unsigned long *)prop;
1693 XFree(prop);
1694 if (desktop == wm.current_desktop && !is_sticky(c->window) && !is_always_on_top(c->window) && !has_wm_state(c->window, _NET_WM_STATE_FULLSCREEN)) {
1695 if (n == 1) {
1696 XMoveResizeWindow(wm.dpy, c->window, 0, 0, screen_width - 2 * border_size, screen_height - 2 * border_size);
1697 } else if (i == 0) { // Master
1698 XMoveResizeWindow(wm.dpy, c->window, 0, 0, mw - 2 * border_size, screen_height - 2 * border_size);
1699 } else { // Stack
1700 int h = screen_height / (n - 1);
1701 XMoveResizeWindow(wm.dpy, c->window, mw, (i - 1) * h, screen_width - mw - 2 * border_size, h - 2 * border_size);
1702 }
1703 i++;
1704 }
1705 } else if (prop) {
1706 XFree(prop);
1707 }
1708 }
1709 }
1710}
1711
1712void toggle_layout(const Arg *arg) {
1713 (void)arg;
1714 wm.layout_modes[wm.current_desktop] = (wm.layout_modes[wm.current_desktop] == LAYOUT_TILING) ? LAYOUT_FLOATING : LAYOUT_TILING;
1715 if (wm.layout_modes[wm.current_desktop] == LAYOUT_TILING) {
1716 apply_tiling_layout();
1717 }
1718 redraw_widgets();
1719 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");
1720}
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) {
44 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents); 44 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents);
45 int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2; 45 int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2;
46 46
47 // Layout indicator size
48 LayoutMode mode = wm.layout_modes[wm.current_desktop];
49 const char *layout_buf = (mode == LAYOUT_TILING) ? "T" : "F";
50 XGlyphInfo layout_extents;
51 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)layout_buf, strlen(layout_buf), &layout_extents);
52 int layout_size_w = (wm.font->height > layout_extents.width ? wm.font->height : layout_extents.width) + padding * 2;
53
47 const char *buf = "MIC"; 54 const char *buf = "MIC";
48 XGlyphInfo extents; 55 XGlyphInfo extents;
49 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)buf, strlen(buf), &extents); 56 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)buf, strlen(buf), &extents);
50 57
51 int size_w = extents.width + padding * 4; 58 int size_w = extents.width + padding * 4;
52 int size_h = desktop_size; 59 int size_h = desktop_size;
53 int x = screen_width - desktop_size - size_w - 20; 60 int x = screen_width - desktop_size - layout_size_w - size_w - 20;
54 int y = 10; 61 int y = 10;
55 62
56 XftColor *bg = wm.mic_muted ? &wm.xft_mic_muted_bg : &wm.xft_mic_active_bg; 63 XftColor *bg = wm.mic_muted ? &wm.xft_mic_muted_bg : &wm.xft_mic_active_bg;
@@ -66,6 +73,40 @@ void widget_mic_indicator(void) {
66 XftDrawStringUtf8(wm.xft_draw, fg, wm.font, text_x, text_y, (FcChar8 *)buf, strlen(buf)); 73 XftDrawStringUtf8(wm.xft_draw, fg, wm.font, text_x, text_y, (FcChar8 *)buf, strlen(buf));
67} 74}
68 75
76void widget_layout_indicator(void) {
77 int screen_width = DisplayWidth(wm.dpy, wm.screen);
78 int padding = 3;
79
80 // Desktop indicator size
81 char desktop_buf[8];
82 snprintf(desktop_buf, sizeof(desktop_buf), "%u", wm.current_desktop);
83 XGlyphInfo desktop_extents;
84 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents);
85 int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2;
86
87 LayoutMode mode = wm.layout_modes[wm.current_desktop];
88 const char *buf = (mode == LAYOUT_TILING) ? "T" : "F";
89 XGlyphInfo extents;
90 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)buf, strlen(buf), &extents);
91
92 int size_w = (wm.font->height > extents.width ? wm.font->height : extents.width) + padding * 2;
93 int size_h = desktop_size;
94 int x = screen_width - desktop_size - size_w - 15;
95 int y = 10;
96
97 XftColor *bg = (mode == LAYOUT_TILING) ? &wm.xft_layout_tile_bg : &wm.xft_layout_float_bg;
98 XftColor *fg = (mode == LAYOUT_TILING) ? &wm.xft_layout_tile_fg : &wm.xft_layout_float_fg;
99
100 // Draw the background.
101 XftDrawRect(wm.xft_draw, bg, x, y, size_w, size_h);
102
103 // Center the text.
104 int text_x = x + (size_w - extents.width) / 2 + extents.x;
105 int text_y = y + (size_h - wm.font->ascent - wm.font->descent) / 2 + wm.font->ascent;
106
107 XftDrawStringUtf8(wm.xft_draw, fg, wm.font, text_x, text_y, (FcChar8 *)buf, strlen(buf));
108}
109
69void widget_datetime(void) { 110void widget_datetime(void) {
70 int screen_width = DisplayWidth(wm.dpy, wm.screen); 111 int screen_width = DisplayWidth(wm.dpy, wm.screen);
71 int padding = 3; 112 int padding = 3;
@@ -77,13 +118,20 @@ void widget_datetime(void) {
77 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents); 118 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)desktop_buf, strlen(desktop_buf), &desktop_extents);
78 int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2; 119 int desktop_size = (wm.font->height > desktop_extents.width ? wm.font->height : desktop_extents.width) + padding * 2;
79 120
121 // Layout indicator size
122 LayoutMode mode = wm.layout_modes[wm.current_desktop];
123 const char *layout_buf = (mode == LAYOUT_TILING) ? "T" : "F";
124 XGlyphInfo layout_extents;
125 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)layout_buf, strlen(layout_buf), &layout_extents);
126 int layout_size_w = (wm.font->height > layout_extents.width ? wm.font->height : layout_extents.width) + padding * 2;
127
80 // Mic indicator size 128 // Mic indicator size
81 const char *mic_buf = "MIC"; 129 const char *mic_buf = "MIC";
82 XGlyphInfo mic_extents; 130 XGlyphInfo mic_extents;
83 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)mic_buf, strlen(mic_buf), &mic_extents); 131 XftTextExtentsUtf8(wm.dpy, wm.font, (FcChar8 *)mic_buf, strlen(mic_buf), &mic_extents);
84 int mic_size_w = mic_extents.width + padding * 4; 132 int mic_size_w = mic_extents.width + padding * 4;
85 133
86 int offset_x = desktop_size + mic_size_w + 40; 134 int offset_x = desktop_size + layout_size_w + mic_size_w + 35;
87 135
88 char time_buf[64]; 136 char time_buf[64];
89 time_t now = time(NULL); 137 time_t now = time(NULL);
@@ -107,6 +155,7 @@ void widget_datetime(void) {
107 155
108void redraw_widgets(void) { 156void redraw_widgets(void) {
109 widget_desktop_indicator(); 157 widget_desktop_indicator();
158 widget_layout_indicator();
110 widget_mic_indicator(); 159 widget_mic_indicator();
111 widget_datetime(); 160 widget_datetime();
112} 161}