|
diff --git a/README.md b/README.md
|
| ... |
| 18 |
- **Window Centering**: Center windows on screen |
18 |
- **Window Centering**: Center windows on screen |
| 19 |
- **Audio Control**: Toggle microphone mute with on-screen status |
19 |
- **Audio Control**: Toggle microphone mute with on-screen status |
| 20 |
- **Tiling Layout**: Toggle between floating and tiling layouts per desktop |
20 |
- **Tiling Layout**: Toggle between floating and tiling layouts per desktop |
|
|
21 |
- **Application Launcher**: Integrated .desktop-aware launcher with usage-based sorting |
| 21 |
- **On-screen Indicators**: Live status for desktop, layout, mic, and clock |
22 |
- **On-screen Indicators**: Live status for desktop, layout, mic, and clock |
| 22 |
- **Live Reload**: Reload configuration without restart |
23 |
- **Live Reload**: Reload configuration without restart |
| 23 |
|
24 |
|
| ... |
| 26 |
- Built on X11/Xlib for low-level window management |
27 |
- Built on X11/Xlib for low-level window management |
| 27 |
- Uses EWMH (Extended Window Manager Hints) for fullscreen and state functionality |
28 |
- Uses EWMH (Extended Window Manager Hints) for fullscreen and state functionality |
| 28 |
- Integrated PulseAudio support for real-time microphone status tracking |
29 |
- Integrated PulseAudio support for real-time microphone status tracking |
|
|
30 |
- Uses GIO/GLib for standard-compliant .desktop file parsing and application discovery |
|
|
31 |
- Persistent usage tracking for applications to provide "most used" sorting |
| 29 |
- Xft-based on-screen widgets for system status (Clock, Mic, Layout, Desktop) |
32 |
- Xft-based on-screen widgets for system status (Clock, Mic, Layout, Desktop) |
| 30 |
- Maintains state for maximized windows to enable toggle behavior |
33 |
- Maintains state for maximized windows to enable toggle behavior |
| 31 |
- Implements proper X11 event handling and window attribute management |
34 |
- Implements proper X11 event handling and window attribute management |
| ... |
| 38 |
- X11 and Freetype development libraries |
41 |
- X11 and Freetype development libraries |
| 39 |
- libXft development library |
42 |
- libXft development library |
| 40 |
- PulseAudio development library |
43 |
- PulseAudio development library |
|
|
44 |
- GLib/GIO development libraries |
| 41 |
|
45 |
|
| 42 |
### Installing Dependencies |
46 |
### Installing Dependencies |
| 43 |
|
47 |
|
| 44 |
**Void Linux:** |
48 |
**Void Linux:** |
| 45 |
|
49 |
|
| 46 |
```sh |
50 |
```sh |
| 47 |
sudo xbps-install libX11-devel freetype-devel libXft-devel pulseaudio-devel pkg-config |
51 |
sudo xbps-install libX11-devel freetype-devel libXft-devel pulseaudio-devel glib-devel pkg-config |
| 48 |
``` |
52 |
``` |
| 49 |
|
53 |
|
| 50 |
## Compilation |
54 |
## Compilation |
| ... |
| 182 |
{ MODKEY, XK_f, toggle_fullscreen, { 0 } }, |
186 |
{ MODKEY, XK_f, toggle_fullscreen, { 0 } }, |
| 183 |
{ MODKEY, XK_q, close_window, { 0 } }, |
187 |
{ MODKEY, XK_q, close_window, { 0 } }, |
| 184 |
{ MODKEY, XK_m, toggle_mic_mute, { 0 } }, |
188 |
{ MODKEY, XK_m, toggle_mic_mute, { 0 } }, |
|
|
189 |
{ MODKEY, XK_p, toggle_launcher, { 0 } }, |
| 185 |
{ MODKEY, XK_space, toggle_layout, { 0 } }, |
190 |
{ MODKEY, XK_space, toggle_layout, { 0 } }, |
| 186 |
{ MODKEY | ShiftMask, XK_q, quit, { 0 } }, |
191 |
{ MODKEY | ShiftMask, XK_q, quit, { 0 } }, |
| 187 |
{ MODKEY | ShiftMask, XK_r, reload, { 0 } }, |
192 |
{ MODKEY | ShiftMask, XK_r, reload, { 0 } }, |
| ... |
| 211 |
|
216 |
|
| 212 |
Defined in `shortcuts[]` array: |
217 |
Defined in `shortcuts[]` array: |
| 213 |
- `Mod+Return`: Terminal (st) |
218 |
- `Mod+Return`: Terminal (st) |
| 214 |
- `Mod+p`: Application launcher (rofi) |
219 |
- `Mod+p`: Internal application launcher |
| 215 |
- `Mod+w`: Browser (brave) |
220 |
- `Mod+w`: Browser (brave) |
| 216 |
- `Mod+e`: File Manager (thunar) |
221 |
- `Mod+e`: File Manager (thunar) |
| 217 |
- `Mod+s`: Screen magnifier (xmagnify) |
222 |
- `Mod+s`: Screen magnifier (xmagnify) |
| ... |
| 243 |
| `window_hmaximize` | Maximize | None | Toggle horizontal maximize | |
248 |
| `window_hmaximize` | Maximize | None | Toggle horizontal maximize | |
| 244 |
| `window_vmaximize` | Maximize | None | Toggle vertical maximize | |
249 |
| `window_vmaximize` | Maximize | None | Toggle vertical maximize | |
| 245 |
| `toggle_mic_mute` | Audio | None | Toggle microphone mute state | |
250 |
| `toggle_mic_mute` | Audio | None | Toggle microphone mute state | |
|
|
251 |
| `toggle_launcher` | Control | None | Toggle integrated app launcher | |
| 246 |
| `toggle_layout` | Layout | None | Toggle between floating/tiling | |
252 |
| `toggle_layout` | Layout | None | Toggle between floating/tiling | |
| 247 |
| `reload` | System | None | Reload configuration/restart WM | |
253 |
| `reload` | System | None | Reload configuration/restart WM | |
|
diff --git a/launcher.c b/launcher.c
|
| ... |
| 10 |
#include <X11/Xutil.h> |
10 |
#include <X11/Xutil.h> |
| 11 |
#include <X11/keysym.h> |
11 |
#include <X11/keysym.h> |
| 12 |
|
12 |
|
|
|
13 |
#include <sys/stat.h> |
| 13 |
#include <gio/gio.h> |
14 |
#include <gio/gio.h> |
| 14 |
|
15 |
|
| 15 |
#include "glitch.h" |
16 |
#include "glitch.h" |
| ... |
| 19 |
|
20 |
|
| 20 |
static void launcher_filter(void); |
21 |
static void launcher_filter(void); |
| 21 |
|
22 |
|
|
|
23 |
static int compare_launcher_items(const void *a, const void *b) { |
|
|
24 |
const LauncherItem *ia = (const LauncherItem *)a; |
|
|
25 |
const LauncherItem *ib = (const LauncherItem *)b; |
|
|
26 |
if (ib->usage != ia->usage) |
|
|
27 |
return ib->usage - ia->usage; |
|
|
28 |
return strcasecmp(ia->name, ib->name); |
|
|
29 |
} |
|
|
30 |
|
|
|
31 |
static void load_usage(void) { |
|
|
32 |
char path[1024]; |
|
|
33 |
char *home = getenv("HOME"); |
|
|
34 |
if (!home) return; |
|
|
35 |
snprintf(path, sizeof(path), "%s/.cache/glitch/usage.db", home); |
|
|
36 |
|
|
|
37 |
GKeyFile *kf = g_key_file_new(); |
|
|
38 |
if (g_key_file_load_from_file(kf, path, G_KEY_FILE_NONE, NULL)) { |
|
|
39 |
for (int i = 0; i < wm.launcher_items_count; i++) { |
|
|
40 |
wm.launcher_items[i].usage = g_key_file_get_integer(kf, "Usage", wm.launcher_items[i].exec, NULL); |
|
|
41 |
} |
|
|
42 |
} |
|
|
43 |
g_key_file_free(kf); |
|
|
44 |
} |
|
|
45 |
|
|
|
46 |
static void record_usage(const char *exec) { |
|
|
47 |
char path[1024]; |
|
|
48 |
char *home = getenv("HOME"); |
|
|
49 |
if (!home) return; |
|
|
50 |
snprintf(path, sizeof(path), "%s/.cache/glitch", home); |
|
|
51 |
mkdir(path, 0755); |
|
|
52 |
snprintf(path, sizeof(path), "%s/.cache/glitch/usage.db", home); |
|
|
53 |
|
|
|
54 |
GKeyFile *kf = g_key_file_new(); |
|
|
55 |
g_key_file_load_from_file(kf, path, G_KEY_FILE_NONE, NULL); |
|
|
56 |
|
|
|
57 |
int count = g_key_file_get_integer(kf, "Usage", exec, NULL); |
|
|
58 |
g_key_file_set_integer(kf, "Usage", exec, count + 1); |
|
|
59 |
|
|
|
60 |
g_key_file_save_to_file(kf, path, NULL); |
|
|
61 |
g_key_file_free(kf); |
|
|
62 |
} |
|
|
63 |
|
| 22 |
static void load_applications(void) { |
64 |
static void load_applications(void) { |
| 23 |
if (wm.launcher_items) { |
65 |
if (wm.launcher_items) { |
| 24 |
for (int i = 0; i < wm.launcher_items_count; i++) { |
66 |
for (int i = 0; i < wm.launcher_items_count; i++) { |
| ... |
| 47 |
|
89 |
|
| 48 |
if (name && exec) { |
90 |
if (name && exec) { |
| 49 |
wm.launcher_items[wm.launcher_items_count].name = strdup(name); |
91 |
wm.launcher_items[wm.launcher_items_count].name = strdup(name); |
|
|
92 |
wm.launcher_items[wm.launcher_items_count].usage = 0; |
| 50 |
|
93 |
|
| 51 |
char *e = strdup(exec); |
94 |
char *e = strdup(exec); |
|
|
95 |
|
| 52 |
char *percent = strchr(e, '%'); |
96 |
char *percent = strchr(e, '%'); |
| 53 |
if (percent) *percent = '\0'; |
97 |
if (percent) *percent = '\0'; |
| 54 |
|
98 |
|
| ... |
| 66 |
g_object_unref(app); |
110 |
g_object_unref(app); |
| 67 |
} |
111 |
} |
| 68 |
g_list_free(apps); |
112 |
g_list_free(apps); |
|
|
113 |
|
|
|
114 |
load_usage(); |
|
|
115 |
qsort(wm.launcher_items, wm.launcher_items_count, sizeof(LauncherItem), compare_launcher_items); |
| 69 |
} |
116 |
} |
| 70 |
|
117 |
|
| 71 |
void toggle_launcher(const Arg *arg) { |
118 |
void toggle_launcher(const Arg *arg) { |
| ... |
| 77 |
return; |
124 |
return; |
| 78 |
} |
125 |
} |
| 79 |
|
126 |
|
| 80 |
if (!wm.launcher_items) { |
127 |
if (wm.launcher_items) { |
|
|
128 |
load_usage(); |
|
|
129 |
qsort(wm.launcher_items, wm.launcher_items_count, sizeof(LauncherItem), compare_launcher_items); |
|
|
130 |
} else { |
| 81 |
load_applications(); |
131 |
load_applications(); |
| 82 |
} |
132 |
} |
| 83 |
|
133 |
|
| ... |
| 143 |
} |
193 |
} |
| 144 |
} else if (keysym == XK_Return) { |
194 |
} else if (keysym == XK_Return) { |
| 145 |
if (wm.launcher_filtered_count > 0 && wm.launcher_selected < wm.launcher_filtered_count) { |
195 |
if (wm.launcher_filtered_count > 0 && wm.launcher_selected < wm.launcher_filtered_count) { |
|
|
196 |
record_usage(wm.launcher_filtered[wm.launcher_selected]->exec); |
| 146 |
execute_shortcut(wm.launcher_filtered[wm.launcher_selected]->exec); |
197 |
execute_shortcut(wm.launcher_filtered[wm.launcher_selected]->exec); |
| 147 |
toggle_launcher(NULL); |
198 |
toggle_launcher(NULL); |
| 148 |
return; |
199 |
return; |
| ... |