Merge in sorting of launcher items with glib functions

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-04-15 18:51:07 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-04-15 18:51:07 +0200
Commit 9b0d35aa6c6fb995f6a15906a43fd50b3a58f822 (patch)
-rw-r--r-- README.md 10
-rw-r--r-- glitch.h 1
-rw-r--r-- launcher.c 53
3 files changed, 61 insertions, 3 deletions
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/glitch.h b/glitch.h
...
58
typedef struct {
58
typedef struct {
59
	char *name;
59
	char *name;
60
	char *exec;
60
	char *exec;
  
61
	int usage;
61
} LauncherItem;
62
} LauncherItem;
62
  
63
  
63
typedef struct {
64
typedef struct {
...
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;
...