-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
-
314
-
315
-
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
-
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
-
334
-
335
-
336
-
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
-
435
/* Functions related to the Configuration */
#include <stdlib.h>
#include <string.h>
#include <gdk/gdk.h>
#include <glib.h>
#include "config.h"
#include "utils.h"
static gchar *parse_greeter_string(GKeyFile *keyfile, const char *group_name,
const char *key_name, const gchar *fallback);
static gint parse_greeter_integer(GKeyFile *keyfile, const char *group_name,
const char *key_name, const gint fallback);
static gboolean parse_greeter_boolean(GKeyFile *keyfile, const char *group_name,
const char *key_name, const gboolean fallback);
static GdkRGBA *parse_greeter_color_key(GKeyFile *keyfile, const char *key_name, const char *default_str);
static guint parse_greeter_hotkey_keyval(GKeyFile *keyfile, const char *key_name, const char default_char);
static gunichar *parse_greeter_password_char(GKeyFile *keyfile);
static gfloat parse_greeter_password_alignment(GKeyFile *keyfile);
static gboolean is_rtl_keymap_layout(void);
gboolean input_string_equals(gchar *input_str, const gchar * const fixed_str);
/* Initialize the configuration, sourcing the greeter's configuration file */
Config *initialize_config(void)
{
Config *config = malloc(sizeof(Config));
if (config == NULL) {
g_error("Could not allocate memory for Config");
}
// Load the key-value file
GKeyFile *keyfile = g_key_file_new();
GError *keyerror = NULL;
gboolean keyfile_loaded = g_key_file_load_from_file(
keyfile, CONFIG_FILE, G_KEY_FILE_NONE, &keyerror);
if (!keyfile_loaded) {
if (keyerror != NULL) {
g_error("Could not load configuration file: %s", keyerror->message);
free(keyerror);
} else {
g_error("Could not load configuration file.");
}
}
// Parse values from the keyfile into a Config.
config->login_user =
g_strchomp(g_key_file_get_string(keyfile, "greeter", "user", NULL));
if (strcmp(config->login_user, "CHANGE_ME") == 0) {
g_message("User configuration value is unchanged.");
}
config->show_password_label =
parse_greeter_boolean(keyfile, "greeter", "show-password-label", TRUE);
config->password_label_text = parse_greeter_string(
keyfile, "greeter", "password-label-text", "Password:");
config->invalid_password_text = parse_greeter_string(
keyfile, "greeter", "invalid-password-text", "Invalid Password");
config->show_input_cursor =
parse_greeter_boolean(keyfile, "greeter", "show-input-cursor", TRUE);
config->password_alignment = parse_greeter_password_alignment(keyfile);
config->password_input_width = parse_greeter_integer(
keyfile, "greeter", "password-input-width", -1);
config->show_image_on_all_monitors = parse_greeter_boolean(
keyfile, "greeter", "show-image-on-all-monitors", FALSE);
config->show_sys_info = parse_greeter_boolean(
keyfile, "greeter", "show-sys-info", FALSE);
// Parse Hotkey Settings
config->suspend_key = parse_greeter_hotkey_keyval(keyfile, "suspend-key", 'u');
config->hibernate_key = parse_greeter_hotkey_keyval(keyfile, "hibernate-key", 'h');
config->restart_key = parse_greeter_hotkey_keyval(keyfile, "restart-key", 'r');
config->shutdown_key = parse_greeter_hotkey_keyval(keyfile, "shutdown-key", 's');
config->session_key = parse_greeter_hotkey_keyval(keyfile, "session-key", 'e');
gchar *mod_key =
g_key_file_get_string(keyfile, "greeter-hotkeys", "mod-key", NULL);
if (mod_key == NULL) {
config->mod_bit = GDK_SUPER_MASK;
} else {
g_strchomp(mod_key);
if (strcmp(mod_key, "control") == 0) {
config->mod_bit = GDK_CONTROL_MASK;
} else if (strcmp(mod_key, "alt") == 0) {
config->mod_bit = GDK_MOD1_MASK;
} else if (strcmp(mod_key, "meta") == 0) {
config->mod_bit = GDK_SUPER_MASK;
} else {
g_error("Invalid mod-key configuration value: '%s'\n", mod_key);
}
}
// Parse Theme Settings
// Font
config->font =
parse_greeter_string(keyfile, "greeter-theme", "font", "Sans");
config->font_size =
parse_greeter_string(keyfile, "greeter-theme", "font-size", "1em");
config->font_weight =
parse_greeter_string(keyfile, "greeter-theme", "font-weight", "bold");
config->font_style =
parse_greeter_string(keyfile, "greeter-theme", "font-style", "normal");
config->text_color =
parse_greeter_color_key(keyfile, "text-color", "#080800");
config->error_color =
parse_greeter_color_key(keyfile, "error-color", "#F8F8F0");
// Background
config->background_image =
g_key_file_get_string(keyfile, "greeter-theme", "background-image", NULL);
if (config->background_image == NULL || strcmp(config->background_image, "") == 0) {
config->background_image = (gchar *) "\"\"";
}
config->background_color =
parse_greeter_color_key(keyfile, "background-color", "#1B1D1E");
config->background_image_size =
parse_greeter_string(keyfile, "greeter-theme", "background-image-size", "auto");
// Window
config->window_color =
parse_greeter_color_key(keyfile, "window-color", "#F92672");
config->border_color =
parse_greeter_color_key(keyfile, "border-color", "#080800");
config->border_width = parse_greeter_string(
keyfile, "greeter-theme", "border-width", "2px");
// Password
config->password_char =
parse_greeter_password_char(keyfile);
config->password_color =
parse_greeter_color_key(keyfile, "password-color", "#F8F8F0");
config->password_background_color =
parse_greeter_color_key(keyfile, "password-background-color", "#1B1D1E");
gchar *temp_password_border_color = g_key_file_get_string(
keyfile, "greeter-theme", "password-border-color", NULL);
if (temp_password_border_color == NULL) {
config->password_border_color = config->border_color;
} else {
free(temp_password_border_color);
config->password_border_color =
parse_greeter_color_key(keyfile, "password-border-color", "#080800");
}
config->password_border_width = parse_greeter_string(
keyfile, "greeter-theme", "password-border-width", config->border_width);
config->password_border_radius = parse_greeter_string(
keyfile, "greeter-theme", "password-border-radius", "0.341125em");
// System Info
gchar *temp_sys_info_color = g_key_file_get_string(
keyfile, "greeter-theme", "sys-info-color", NULL);
if (temp_sys_info_color == NULL) {
config->sys_info_color = config->text_color;
} else {
config->sys_info_color = parse_greeter_color_key(
keyfile, "sys-info-color", "#080800");
}
config->sys_info_font = parse_greeter_string(keyfile, "greeter-theme", "sys-info-font", config->font);
config->sys_info_font_size =
parse_greeter_string(keyfile, "greeter-theme", "sys-info-font-size", config->font_size);
config->sys_info_margin =
parse_greeter_string(keyfile, "greeter-theme", "sys-info-margin", "-5px -5px -5px");
gint layout_spacing =
parse_greeter_integer(keyfile, "greeter-theme", "layout-space", 15);
if (layout_spacing < 0) {
config->layout_spacing = (guint) (-1 * layout_spacing);
} else {
config->layout_spacing = (guint) layout_spacing;
}
config->x_pos = g_key_file_get_double(keyfile, "greeter-theme", "x-pos", NULL);
config->y_pos = g_key_file_get_double(keyfile, "greeter-theme", "y-pos", NULL);
g_key_file_free(keyfile);
return config;
}
/* Cleanup any memory allocated for the Config */
void destroy_config(Config *config)
{
free(config->login_user);
free(config->font);
free(config->font_size);
free(config->font_weight);
free(config->font_style);
free(config->text_color);
free(config->error_color);
free(config->background_image);
free(config->background_color);
free(config->background_image_size);
free(config->window_color);
free(config->border_color);
free(config->border_width);
free(config->password_label_text);
free(config->invalid_password_text);
free(config->password_char);
free(config->password_color);
free(config->password_background_color);
free(config->password_border_color);
free(config->password_border_width);
free(config->password_border_radius);
free(config->sys_info_color);
free(config->sys_info_font_size);
free(config->sys_info_margin);
free(config);
}
/* Parse a string from the config file, returning a copy of the fallback value
* if the key is not present in the group.
*/
static gchar *parse_greeter_string(GKeyFile *keyfile, const char *group_name,
const char *key_name, const gchar *fallback)
{
gchar *parsed_string = g_key_file_get_string(keyfile, group_name, key_name, NULL);
if (parsed_string == NULL) {
g_warning("Could not find value for %s.%s - falling back to '%s'",
group_name, key_name, fallback);
return g_strdup(fallback);
} else {
return parsed_string;
}
}
/* Parse an integer from the config file, returning the fallback value if the
* key is not present in the group, or if the value is not an integer.
*/
static gint parse_greeter_integer(GKeyFile *keyfile, const char *group_name,
const char *key_name, const gint fallback)
{
GError *parse_error = NULL;
gint parse_result = g_key_file_get_integer(
keyfile, group_name, key_name, &parse_error);
if (parse_error != NULL) {
if (parse_error->code == G_KEY_FILE_ERROR_INVALID_VALUE) {
// Read the value as a string so we can log it
gchar *value = g_key_file_get_string(
keyfile, group_name, key_name, NULL);
g_warning("Invalid integer for %s.%s: `%s`",
group_name, key_name, value);
free(value);
}
g_error_free(parse_error);
return fallback;
}
return parse_result;
}
/* Parse a boolean from the config file, returning the fallback value if the
* key is not present in the group or cannot be parsed as a boolean.
*/
static gboolean parse_greeter_boolean(GKeyFile *keyfile, const char *group_name,
const char *key_name, const gboolean fallback)
{
GError *parse_error = NULL;
gboolean parse_result = g_key_file_get_boolean(
keyfile, group_name, key_name, &parse_error);
if (!parse_result && parse_error != NULL) {
if (parse_error->code == G_KEY_FILE_ERROR_INVALID_VALUE) {
// Read the value as a string so we can log it
gchar *value = g_key_file_get_string(
keyfile, group_name, key_name, NULL);
g_warning("Invalid boolean for %s.%s: `%s`\n",
group_name, key_name, value);
g_free(value);
}
g_error_free(parse_error);
return fallback;
}
return parse_result;
}
/* Parse a greeter-colors group key into a newly-allocated GdkRGBA value */
static GdkRGBA *parse_greeter_color_key(GKeyFile *keyfile, const char *key_name, const char *default_str)
{
gchar *color_string = g_key_file_get_string(
keyfile, "greeter-theme", key_name, NULL);
if (color_string == NULL) {
g_warning("Could not find value for %s.%s - falling back to '%s'",
"greeter-theme", key_name, default_str);
}
GdkRGBA *default_color = malloc(sizeof(GdkRGBA));
gboolean default_was_parsed = gdk_rgba_parse(default_color, default_str);
if (!default_was_parsed) {
g_critical("Could not parse the default '%s' setting: %s", key_name, default_str);
}
if (color_string != NULL) {
if (strstr(color_string, "#") != NULL) {
// Remove quotations from hex color strings
remove_char(color_string, '"');
remove_char(color_string, '\'');
}
GdkRGBA *color = malloc(sizeof(GdkRGBA));
gboolean color_was_parsed = gdk_rgba_parse(color, color_string);
if (color_was_parsed) {
free(default_color);
return color;
}
g_warning("Could not parse the '%s' setting: %s - falling back to default of %s",
key_name, color_string, default_str);
free(color);
}
return default_color;
}
/* Parse a greeter-hotkeys key into the GDKkeyval of it's first character */
static guint parse_greeter_hotkey_keyval(GKeyFile *keyfile, const char *key_name, const char default_char)
{
gchar *key = g_key_file_get_string(
keyfile, "greeter-hotkeys", key_name, NULL);
guint32 key_code;
if (key == NULL) {
key_code = (guint32) default_char;
} else if (strcmp(key, "") == 0) {
g_warning("Configuration contains empty key for '%s' - falling back to default of %c\n", key_name, default_char);
key_code = (guint32) default_char;
} else {
key_code = (guint32) key[0];
}
return gdk_unicode_to_keyval(key_code);
}
/* Parse the password masking character that should be displayed when typing
* into the password input.
*
* We first attempt to parse a literal -1 or 0, where -1 means to use the
* default character & 0 means to display no characters when typing a password.
*
* If that parsing fails, we attempt to parse the field as a string & use the
* first character of the string. If the string is empty or parsing fails, we
* fall back to the default characer.
*
* Since the related gtk_entry function takes a unsigned int, we use pointers,
* where NULL means "use the default" & all other values indicate an argument
* to the `gtk_entry_set_invisible_char` function.
*
*/
static gunichar *parse_greeter_password_char(GKeyFile *keyfile)
{
const char *const group_name = "greeter-theme";
const char *const key_name = "password-character";
GError *parse_error = NULL;
// Attempt the int parsing
const gint int_result = g_key_file_get_integer(
keyfile, group_name, key_name, &parse_error);
// Matches -1
if (int_result == -1) {
return NULL;
}
gunichar *result = malloc(sizeof(gunichar));
// Matches 0
if (int_result == 0 && parse_error == NULL) {
*result = 0;
return result;
} else if (parse_error != NULL) {
g_error_free(parse_error);
}
// Atempt the string parsing
gchar *str_result = g_key_file_get_string(
keyfile, group_name, key_name, NULL);
// Invalid or 0-length string
if (str_result == NULL || strlen(str_result) == 0) {
if (str_result != NULL) {
free(str_result);
}
free(result);
return NULL;
}
// Convert to unicode code points
gunichar *unicode_str = g_utf8_to_ucs4(str_result, -1, NULL, NULL, NULL);
free(str_result);
if (unicode_str == NULL) {
free(result);
return NULL;
}
*result = unicode_str[0];
g_free(unicode_str);
return result;
}
/* Parse the password input alignment, properly handling RTL layouts.
*
* Note that the gboolean returned by this function is meant to be used with
* the `gtk_entry_set_alignment` function.
*/
static gfloat parse_greeter_password_alignment(GKeyFile *keyfile)
{
gfloat alignment;
gchar *password_alignment_text = parse_greeter_string(
keyfile, "greeter", "password-alignment", "right");
gboolean is_rtl = is_rtl_keymap_layout();
if (input_string_equals(password_alignment_text, "left")) {
alignment = is_rtl ? 1 : 0;
} else if (input_string_equals(password_alignment_text, "center")) {
alignment = 0.5;
} else {
alignment = is_rtl ? 0 : 1;
}
free(password_alignment_text);
return alignment;
}
/* Determine if the default Display's Keymap is in the Right-to-Left direction
*/
static gboolean is_rtl_keymap_layout(void)
{
GdkDisplay *display = gdk_display_get_default();
if (display == NULL) {
return FALSE;
}
GdkKeymap *keymap = gdk_keymap_get_for_display(display);
PangoDirection text_direction = gdk_keymap_get_direction(keymap);
return text_direction == PANGO_DIRECTION_RTL;
}
/* Take a string from the config file, trim any whitespace & check it's
* equality with a fixed string.
*/
gboolean input_string_equals(gchar *input_str, const gchar * const fixed_str) {
return strcmp(g_strchomp(input_str), fixed_str) == 0;
}