Add dev mode via env variable XDGCTL_DEV=n

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-26 12:42:35 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-26 12:42:35 +0100
Commit 201e742a1a3c368aa697627efb4514756bfdb16b (patch)
-rw-r--r-- main.c 109
1 files changed, 87 insertions, 22 deletions
diff --git a/main.c b/main.c
...
33
	int col; // 0 for category, 1 for app
33
	int col; // 0 for category, 1 for app
34
	char message[512];
34
	char message[512];
35
	GList *cached_apps;
35
	GList *cached_apps;
  
36
	int is_dev_mode;
  
37
	int dev_count;
36
} State;
38
} State;
37
  
39
  
38
GList *get_apps_for_category(int category_idx) {
40
int get_category_count(State *state) {
  
41
	if (state->is_dev_mode) {
  
42
		return state->dev_count;
  
43
	}
  
44
	int count = 0;
  
45
	while (categories[count].name)
  
46
		count++;
  
47
	return count;
  
48
}
  
49
  
  
50
const char *get_category_name(State *state, int idx) {
  
51
	if (state->is_dev_mode) {
  
52
		static char mock_name[64];
  
53
		snprintf(mock_name, sizeof(mock_name), "Dev Category %d", idx + 1);
  
54
		return mock_name;
  
55
	}
  
56
	return categories[idx].name;
  
57
}
  
58
  
  
59
GList *get_apps_for_category(State *state, int category_idx) {
39
	GList *apps = NULL;
60
	GList *apps = NULL;
40
	for (int m = 0; categories[category_idx].mimetypes[m] != NULL; ++m) {
61
	for (int m = 0; categories[category_idx].mimetypes[m] != NULL; ++m) {
41
		GList *type_apps = g_app_info_get_all_for_type(categories[category_idx].mimetypes[m]);
62
		GList *type_apps = g_app_info_get_all_for_type(categories[category_idx].mimetypes[m]);
...
58
}
79
}
59
  
80
  
60
void update_cached_apps(State *state) {
81
void update_cached_apps(State *state) {
61
	if (state->cached_apps) {
82
	if (state->cached_apps && !state->is_dev_mode) {
62
		g_list_free_full(state->cached_apps, g_object_unref);
83
		g_list_free_full(state->cached_apps, g_object_unref);
  
84
	} else if (state->cached_apps && state->is_dev_mode) {
  
85
		g_list_free_full(state->cached_apps, g_free);
63
	}
86
	}
64
	state->cached_apps = get_apps_for_category(state->category_idx);
87
	state->cached_apps = NULL;
  
88
  
  
89
	if (state->is_dev_mode) {
  
90
		for (int i = 0; i < state->dev_count; ++i) {
  
91
			char *mock_app = g_strdup_printf("Dev Application %d.%d", state->category_idx + 1, i + 1);
  
92
			state->cached_apps = g_list_append(state->cached_apps, mock_app);
  
93
		}
  
94
	} else {
  
95
		state->cached_apps = get_apps_for_category(state, state->category_idx);
  
96
	}
65
	state->app_idx = 0;
97
	state->app_idx = 0;
66
}
98
}
67
  
99
  
...
72
}
104
}
73
  
105
  
74
void draw_categories(State *state) {
106
void draw_categories(State *state) {
75
	for (int i = 0; categories[i].name != NULL; ++i) {
107
	int count = get_category_count(state);
  
108
	for (int i = 0; i < count; ++i) {
76
		uint16_t fg = COLOR_DEFAULT;
109
		uint16_t fg = COLOR_DEFAULT;
77
		uint16_t bg = TB_DEFAULT;
110
		uint16_t bg = TB_DEFAULT;
78
		if (state->col == 0 && state->category_idx == i) {
111
		if (state->col == 0 && state->category_idx == i) {
...
81
			fg = COLOR_SELECTED;
114
			fg = COLOR_SELECTED;
82
			bg = COLOR_DEFAULT;
115
			bg = COLOR_DEFAULT;
83
		}
116
		}
84
		tb_print(X_OFF_CATEGORIES, Y_OFF_START + i, fg, bg, categories[i].name);
117
		tb_print(X_OFF_CATEGORIES, Y_OFF_START + i, fg, bg, get_category_name(state, i));
85
	}
118
	}
86
}
119
}
87
  
120
  
88
void draw_apps_list(State *state) {
121
void draw_apps_list(State *state) {
  
122
	if (state->is_dev_mode) {
  
123
		int i = 0;
  
124
		for (GList *l = state->cached_apps; l != NULL; l = l->next, ++i) {
  
125
			char *app_name = (char *)l->data;
  
126
			uint16_t fg = COLOR_DEFAULT;
  
127
			uint16_t bg = TB_DEFAULT;
  
128
			if (state->col == 1 && state->app_idx == i) {
  
129
				bg = COLOR_SELECTED;
  
130
			}
  
131
			char name[256];
  
132
			snprintf(name, sizeof(name), "  %s", app_name);
  
133
			tb_print(X_OFF_APPS, Y_OFF_START + i, fg, bg, name);
  
134
		}
  
135
		return;
  
136
	}
89
	GList *defaults = NULL;
137
	GList *defaults = NULL;
90
	for (int m = 0; categories[state->category_idx].mimetypes[m] != NULL; ++m) {
138
	for (int m = 0; categories[state->category_idx].mimetypes[m] != NULL; ++m) {
91
		GAppInfo *d = g_app_info_get_default_for_type(categories[state->category_idx].mimetypes[m], FALSE);
139
		GAppInfo *d = g_app_info_get_default_for_type(categories[state->category_idx].mimetypes[m], FALSE);
...
158
		return 1;
206
		return 1;
159
	}
207
	}
160
  
208
  
161
	State state = {0, 0, 0, {0}, NULL};
209
	State state = {0, 0, 0, {0}, NULL, 0, 0};
  
210
	char *dev_env = getenv("XDGCTL_DEV");
  
211
	if (dev_env) {
  
212
		state.is_dev_mode = 1;
  
213
		state.dev_count = atoi(dev_env);
  
214
		if (state.dev_count <= 0)
  
215
			state.dev_count = 10;
  
216
	}
  
217
  
162
	update_cached_apps(&state);
218
	update_cached_apps(&state);
163
  
219
  
164
	struct tb_event ev;
220
	struct tb_event ev;
...
186
				}
242
				}
187
			} else if (ev.key == TB_KEY_ARROW_DOWN) {
243
			} else if (ev.key == TB_KEY_ARROW_DOWN) {
188
				if (state.col == 0) {
244
				if (state.col == 0) {
189
					int count = 0;
245
					int count = get_category_count(&state);
190
					while (categories[count].name)
  
191
						count++;
  
192
					if (state.category_idx < count - 1) {
246
					if (state.category_idx < count - 1) {
193
						state.category_idx++;
247
						state.category_idx++;
194
						update_cached_apps(&state);
248
						update_cached_apps(&state);
...
212
				}
266
				}
213
			} else if (ev.key == TB_KEY_ENTER) {
267
			} else if (ev.key == TB_KEY_ENTER) {
214
				if (state.col == 1) {
268
				if (state.col == 1) {
215
					GAppInfo *selected_app = (GAppInfo *)g_list_nth_data(state.cached_apps, state.app_idx);
269
					if (state.is_dev_mode) {
216
					if (selected_app) {
270
						char *selected_app = (char *)g_list_nth_data(state.cached_apps, state.app_idx);
217
						snprintf(state.message, sizeof(state.message),
271
						if (selected_app) {
218
								 "%s is now default %s",
272
							snprintf(state.message, sizeof(state.message),
219
								 g_app_info_get_name(selected_app), categories[state.category_idx].name);
273
									 "Mock: %s is now default for %s",
220
						for (int m = 0; categories[state.category_idx].mimetypes[m] != NULL; ++m) {
274
									 selected_app, get_category_name(&state, state.category_idx));
221
							GError *error = NULL;
275
						}
222
							g_app_info_set_as_default_for_type(selected_app, categories[state.category_idx].mimetypes[m], &error);
276
					} else {
223
							if (error) {
277
						GAppInfo *selected_app = (GAppInfo *)g_list_nth_data(state.cached_apps, state.app_idx);
224
								snprintf(state.message, sizeof(state.message), "Failed to set default application");
278
						if (selected_app) {
225
								g_error_free(error);
279
							snprintf(state.message, sizeof(state.message),
226
								break;
280
									 "%s is now default %s",
  
281
									 g_app_info_get_name(selected_app), categories[state.category_idx].name);
  
282
							for (int m = 0; categories[state.category_idx].mimetypes[m] != NULL; ++m) {
  
283
								GError *error = NULL;
  
284
								g_app_info_set_as_default_for_type(selected_app, categories[state.category_idx].mimetypes[m], &error);
  
285
								if (error) {
  
286
									snprintf(state.message, sizeof(state.message), "Failed to set default application");
  
287
									g_error_free(error);
  
288
									break;
  
289
								}
227
							}
290
							}
228
						}
291
						}
229
					}
292
					}
...
232
		}
295
		}
233
	}
296
	}
234
  
297
  
235
	if (state.cached_apps) {
298
	if (state.cached_apps && !state.is_dev_mode) {
236
		g_list_free_full(state.cached_apps, g_object_unref);
299
		g_list_free_full(state.cached_apps, g_object_unref);
  
300
	} else if (state.cached_apps && state.is_dev_mode) {
  
301
		g_list_free_full(state.cached_apps, g_free);
237
	}
302
	}
238
	tb_shutdown();
303
	tb_shutdown();
239
	return 0;
304
	return 0;
...