diff options
Diffstat (limited to 'sway/config.c')
-rw-r--r-- | sway/config.c | 214 |
1 files changed, 97 insertions, 117 deletions
diff --git a/sway/config.c b/sway/config.c index 6e665434..f9131e0f 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #undef _POSIX_C_SOURCE | ||
1 | #define _XOPEN_SOURCE 700 // for realpath | 2 | #define _XOPEN_SOURCE 700 // for realpath |
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
@@ -26,7 +27,7 @@ | |||
26 | #include "sway/tree/arrange.h" | 27 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/root.h" | 28 | #include "sway/tree/root.h" |
28 | #include "sway/tree/workspace.h" | 29 | #include "sway/tree/workspace.h" |
29 | #include "cairo.h" | 30 | #include "cairo_util.h" |
30 | #include "pango.h" | 31 | #include "pango.h" |
31 | #include "stringop.h" | 32 | #include "stringop.h" |
32 | #include "list.h" | 33 | #include "list.h" |
@@ -36,19 +37,26 @@ | |||
36 | struct sway_config *config = NULL; | 37 | struct sway_config *config = NULL; |
37 | 38 | ||
38 | static struct xkb_state *keysym_translation_state_create( | 39 | static struct xkb_state *keysym_translation_state_create( |
39 | struct xkb_rule_names rules) { | 40 | struct xkb_rule_names rules, uint32_t context_flags) { |
40 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 41 | struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); |
41 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( | 42 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( |
42 | context, | 43 | context, |
43 | &rules, | 44 | &rules, |
44 | XKB_KEYMAP_COMPILE_NO_FLAGS); | 45 | XKB_KEYMAP_COMPILE_NO_FLAGS); |
45 | |||
46 | xkb_context_unref(context); | 46 | xkb_context_unref(context); |
47 | if (xkb_keymap == NULL) { | ||
48 | sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); | ||
49 | return NULL; | ||
50 | } | ||
51 | |||
47 | return xkb_state_new(xkb_keymap); | 52 | return xkb_state_new(xkb_keymap); |
48 | } | 53 | } |
49 | 54 | ||
50 | static void keysym_translation_state_destroy( | 55 | static void keysym_translation_state_destroy( |
51 | struct xkb_state *state) { | 56 | struct xkb_state *state) { |
57 | if (state == NULL) { | ||
58 | return; | ||
59 | } | ||
52 | xkb_keymap_unref(xkb_state_get_keymap(state)); | 60 | xkb_keymap_unref(xkb_state_get_keymap(state)); |
53 | xkb_state_unref(state); | 61 | xkb_state_unref(state); |
54 | } | 62 | } |
@@ -82,6 +90,12 @@ static void free_mode(struct sway_mode *mode) { | |||
82 | } | 90 | } |
83 | list_free(mode->switch_bindings); | 91 | list_free(mode->switch_bindings); |
84 | } | 92 | } |
93 | if (mode->gesture_bindings) { | ||
94 | for (int i = 0; i < mode->gesture_bindings->length; i++) { | ||
95 | free_gesture_binding(mode->gesture_bindings->items[i]); | ||
96 | } | ||
97 | list_free(mode->gesture_bindings); | ||
98 | } | ||
85 | free(mode); | 99 | free(mode); |
86 | } | 100 | } |
87 | 101 | ||
@@ -222,6 +236,7 @@ static void config_defaults(struct sway_config *config) { | |||
222 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 236 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
223 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | 237 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; |
224 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; | 238 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; |
239 | if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; | ||
225 | list_add(config->modes, config->current_mode); | 240 | list_add(config->modes, config->current_mode); |
226 | 241 | ||
227 | config->floating_mod = 0; | 242 | config->floating_mod = 0; |
@@ -236,7 +251,7 @@ static void config_defaults(struct sway_config *config) { | |||
236 | config->default_layout = L_NONE; | 251 | config->default_layout = L_NONE; |
237 | config->default_orientation = L_NONE; | 252 | config->default_orientation = L_NONE; |
238 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 253 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
239 | config->font_height = 17; // height of monospace 10 | 254 | config->font_description = pango_font_description_from_string(config->font); |
240 | config->urgent_timeout = 500; | 255 | config->urgent_timeout = 500; |
241 | config->focus_on_window_activation = FOWA_URGENT; | 256 | config->focus_on_window_activation = FOWA_URGENT; |
242 | config->popup_during_fullscreen = POPUP_SMART; | 257 | config->popup_during_fullscreen = POPUP_SMART; |
@@ -266,8 +281,9 @@ static void config_defaults(struct sway_config *config) { | |||
266 | config->title_align = ALIGN_LEFT; | 281 | config->title_align = ALIGN_LEFT; |
267 | config->tiling_drag = true; | 282 | config->tiling_drag = true; |
268 | config->tiling_drag_threshold = 9; | 283 | config->tiling_drag_threshold = 9; |
284 | config->primary_selection = true; | ||
269 | 285 | ||
270 | config->smart_gaps = false; | 286 | config->smart_gaps = SMART_GAPS_OFF; |
271 | config->gaps_inner = 0; | 287 | config->gaps_inner = 0; |
272 | config->gaps_outer.top = 0; | 288 | config->gaps_outer.top = 0; |
273 | config->gaps_outer.right = 0; | 289 | config->gaps_outer.right = 0; |
@@ -291,6 +307,8 @@ static void config_defaults(struct sway_config *config) { | |||
291 | config->hide_edge_borders_smart = ESMART_OFF; | 307 | config->hide_edge_borders_smart = ESMART_OFF; |
292 | config->hide_lone_tab = false; | 308 | config->hide_lone_tab = false; |
293 | 309 | ||
310 | config->has_focused_tab_title = false; | ||
311 | |||
294 | // border colors | 312 | // border colors |
295 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); | 313 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); |
296 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); | 314 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); |
@@ -326,8 +344,14 @@ static void config_defaults(struct sway_config *config) { | |||
326 | 344 | ||
327 | // The keysym to keycode translation | 345 | // The keysym to keycode translation |
328 | struct xkb_rule_names rules = {0}; | 346 | struct xkb_rule_names rules = {0}; |
329 | config->keysym_translation_state = | 347 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
330 | keysym_translation_state_create(rules); | 348 | if (config->keysym_translation_state == NULL) { |
349 | config->keysym_translation_state = keysym_translation_state_create(rules, | ||
350 | XKB_CONTEXT_NO_ENVIRONMENT_NAMES); | ||
351 | } | ||
352 | if (config->keysym_translation_state == NULL) { | ||
353 | goto cleanup; | ||
354 | } | ||
331 | 355 | ||
332 | return; | 356 | return; |
333 | cleanup: | 357 | cleanup: |
@@ -338,35 +362,53 @@ static bool file_exists(const char *path) { | |||
338 | return path && access(path, R_OK) != -1; | 362 | return path && access(path, R_OK) != -1; |
339 | } | 363 | } |
340 | 364 | ||
365 | static char *config_path(const char *prefix, const char *config_folder) { | ||
366 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { | ||
367 | return NULL; | ||
368 | } | ||
369 | return format_str("%s/%s/config", prefix, config_folder); | ||
370 | } | ||
371 | |||
341 | static char *get_config_path(void) { | 372 | static char *get_config_path(void) { |
342 | static const char *config_paths[] = { | 373 | char *path = NULL; |
343 | "$HOME/.sway/config", | 374 | const char *home = getenv("HOME"); |
344 | "$XDG_CONFIG_HOME/sway/config", | 375 | char *config_home_fallback = NULL; |
345 | "$HOME/.i3/config", | 376 | |
346 | "$XDG_CONFIG_HOME/i3/config", | 377 | const char *config_home = getenv("XDG_CONFIG_HOME"); |
347 | SYSCONFDIR "/sway/config", | 378 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { |
348 | SYSCONFDIR "/i3/config", | 379 | config_home_fallback = format_str("%s/.config", home); |
380 | config_home = config_home_fallback; | ||
381 | } | ||
382 | |||
383 | struct config_path { | ||
384 | const char *prefix; | ||
385 | const char *config_folder; | ||
349 | }; | 386 | }; |
350 | 387 | ||
351 | char *config_home = getenv("XDG_CONFIG_HOME"); | 388 | struct config_path config_paths[] = { |
352 | if (!config_home || !*config_home) { | 389 | { .prefix = home, .config_folder = ".sway"}, |
353 | config_paths[1] = "$HOME/.config/sway/config"; | 390 | { .prefix = config_home, .config_folder = "sway"}, |
354 | config_paths[3] = "$HOME/.config/i3/config"; | 391 | { .prefix = home, .config_folder = ".i3"}, |
355 | } | 392 | { .prefix = config_home, .config_folder = "i3"}, |
393 | { .prefix = SYSCONFDIR, .config_folder = "sway"}, | ||
394 | { .prefix = SYSCONFDIR, .config_folder = "i3"} | ||
395 | }; | ||
356 | 396 | ||
357 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | 397 | size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]); |
358 | wordexp_t p; | 398 | for (size_t i = 0; i < num_config_paths; i++) { |
359 | if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { | 399 | path = config_path(config_paths[i].prefix, config_paths[i].config_folder); |
360 | char *path = strdup(p.we_wordv[0]); | 400 | if (!path) { |
361 | wordfree(&p); | 401 | continue; |
362 | if (file_exists(path)) { | 402 | } |
363 | return path; | 403 | if (file_exists(path)) { |
364 | } | 404 | break; |
365 | free(path); | ||
366 | } | 405 | } |
406 | free(path); | ||
407 | path = NULL; | ||
367 | } | 408 | } |
368 | 409 | ||
369 | return NULL; | 410 | free(config_home_fallback); |
411 | return path; | ||
370 | } | 412 | } |
371 | 413 | ||
372 | static bool load_config(const char *path, struct sway_config *config, | 414 | static bool load_config(const char *path, struct sway_config *config, |
@@ -438,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
438 | old_config->xwayland ? "enabled" : "disabled"); | 480 | old_config->xwayland ? "enabled" : "disabled"); |
439 | config->xwayland = old_config->xwayland; | 481 | config->xwayland = old_config->xwayland; |
440 | 482 | ||
483 | // primary_selection can only be enabled/disabled at launch | ||
484 | sway_log(SWAY_DEBUG, "primary_selection will remain %s", | ||
485 | old_config->primary_selection ? "enabled" : "disabled"); | ||
486 | config->primary_selection = old_config->primary_selection; | ||
487 | |||
441 | if (!config->validating) { | 488 | if (!config->validating) { |
442 | if (old_config->swaybg_client != NULL) { | 489 | if (old_config->swaybg_client != NULL) { |
443 | wl_client_destroy(old_config->swaybg_client); | 490 | wl_client_destroy(old_config->swaybg_client); |
@@ -457,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
457 | 504 | ||
458 | config->reading = true; | 505 | config->reading = true; |
459 | 506 | ||
460 | // Read security configs | 507 | bool success = load_config(path, config, &config->swaynag_config_errors); |
461 | // TODO: Security | ||
462 | bool success = true; | ||
463 | /* | ||
464 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | ||
465 | if (!dir) { | ||
466 | sway_log(SWAY_ERROR, | ||
467 | "%s does not exist, sway will have no security configuration" | ||
468 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
469 | } else { | ||
470 | list_t *secconfigs = create_list(); | ||
471 | char *base = SYSCONFDIR "/sway/security.d/"; | ||
472 | struct dirent *ent = readdir(dir); | ||
473 | struct stat s; | ||
474 | while (ent != NULL) { | ||
475 | char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); | ||
476 | strcpy(_path, base); | ||
477 | strcat(_path, ent->d_name); | ||
478 | lstat(_path, &s); | ||
479 | if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { | ||
480 | list_add(secconfigs, _path); | ||
481 | } | ||
482 | else { | ||
483 | free(_path); | ||
484 | } | ||
485 | ent = readdir(dir); | ||
486 | } | ||
487 | closedir(dir); | ||
488 | |||
489 | list_qsort(secconfigs, qstrcmp); | ||
490 | for (int i = 0; i < secconfigs->length; ++i) { | ||
491 | char *_path = secconfigs->items[i]; | ||
492 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || | ||
493 | (((s.st_mode & 0777) != 0644) && | ||
494 | (s.st_mode & 0777) != 0444)) { | ||
495 | sway_log(SWAY_ERROR, | ||
496 | "Refusing to load %s - it must be owned by root " | ||
497 | "and mode 644 or 444", _path); | ||
498 | success = false; | ||
499 | } else { | ||
500 | success = success && load_config(_path, config); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | list_free_items_and_destroy(secconfigs); | ||
505 | } | ||
506 | */ | ||
507 | |||
508 | success = success && load_config(path, config, | ||
509 | &config->swaynag_config_errors); | ||
510 | 508 | ||
511 | if (validating) { | 509 | if (validating) { |
512 | free_config(config); | 510 | free_config(config); |
@@ -514,6 +512,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
514 | return success; | 512 | return success; |
515 | } | 513 | } |
516 | 514 | ||
515 | // Only really necessary if not explicitly `font` is set in the config. | ||
516 | config_update_font_height(); | ||
517 | |||
517 | if (is_active && !validating) { | 518 | if (is_active && !validating) { |
518 | input_manager_verify_fallback_seat(); | 519 | input_manager_verify_fallback_seat(); |
519 | 520 | ||
@@ -531,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
531 | } | 532 | } |
532 | sway_switch_retrigger_bindings_for_all(); | 533 | sway_switch_retrigger_bindings_for_all(); |
533 | 534 | ||
534 | reset_outputs(); | 535 | apply_all_output_configs(); |
535 | spawn_swaybg(); | 536 | spawn_swaybg(); |
536 | 537 | ||
537 | config->reloading = false; | 538 | config->reloading = false; |
@@ -884,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) { | |||
884 | if (config->reading && !config->validating) { | 885 | if (config->reading && !config->validating) { |
885 | va_list args; | 886 | va_list args; |
886 | va_start(args, fmt); | 887 | va_start(args, fmt); |
887 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 888 | char *str = vformat_str(fmt, args); |
888 | va_end(args); | 889 | va_end(args); |
889 | 890 | if (str == NULL) { | |
890 | char *temp = malloc(length + 1); | ||
891 | if (!temp) { | ||
892 | sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); | ||
893 | return; | 891 | return; |
894 | } | 892 | } |
895 | 893 | ||
896 | va_start(args, fmt); | ||
897 | vsnprintf(temp, length, fmt, args); | ||
898 | va_end(args); | ||
899 | |||
900 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, | 894 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, |
901 | "Warning on line %i (%s) '%s': %s", | 895 | "Warning on line %i (%s) '%s': %s", |
902 | config->current_config_line_number, config->current_config_path, | 896 | config->current_config_line_number, config->current_config_path, |
903 | config->current_config_line, temp); | 897 | config->current_config_line, str); |
898 | |||
899 | free(str); | ||
904 | } | 900 | } |
905 | } | 901 | } |
906 | 902 | ||
@@ -940,7 +936,7 @@ char *do_var_replacement(char *str) { | |||
940 | int offset = find - str; | 936 | int offset = find - str; |
941 | strncpy(newptr, str, offset); | 937 | strncpy(newptr, str, offset); |
942 | newptr += offset; | 938 | newptr += offset; |
943 | strncpy(newptr, var->value, vvlen); | 939 | memcpy(newptr, var->value, vvlen); |
944 | newptr += vvlen; | 940 | newptr += vvlen; |
945 | strcpy(newptr, find + vnlen); | 941 | strcpy(newptr, find + vnlen); |
946 | free(str); | 942 | free(str); |
@@ -964,31 +960,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { | |||
964 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 960 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
965 | } | 961 | } |
966 | 962 | ||
967 | static void find_font_height_iterator(struct sway_container *con, void *data) { | ||
968 | size_t amount_below_baseline = con->title_height - con->title_baseline; | ||
969 | size_t extended_height = config->font_baseline + amount_below_baseline; | ||
970 | if (extended_height > config->font_height) { | ||
971 | config->font_height = extended_height; | ||
972 | } | ||
973 | } | ||
974 | |||
975 | static void find_baseline_iterator(struct sway_container *con, void *data) { | ||
976 | bool *recalculate = data; | ||
977 | if (*recalculate) { | ||
978 | container_calculate_title_height(con); | ||
979 | } | ||
980 | if (con->title_baseline > config->font_baseline) { | ||
981 | config->font_baseline = con->title_baseline; | ||
982 | } | ||
983 | } | ||
984 | 963 | ||
985 | void config_update_font_height(bool recalculate) { | 964 | void config_update_font_height(void) { |
986 | size_t prev_max_height = config->font_height; | 965 | int prev_max_height = config->font_height; |
987 | config->font_height = 0; | ||
988 | config->font_baseline = 0; | ||
989 | 966 | ||
990 | root_for_each_container(find_baseline_iterator, &recalculate); | 967 | get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); |
991 | root_for_each_container(find_font_height_iterator, NULL); | ||
992 | 968 | ||
993 | if (config->font_height != prev_max_height) { | 969 | if (config->font_height != prev_max_height) { |
994 | arrange_root(); | 970 | arrange_root(); |
@@ -1022,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) { | |||
1022 | 998 | ||
1023 | struct xkb_rule_names rules = {0}; | 999 | struct xkb_rule_names rules = {0}; |
1024 | input_config_fill_rule_names(input_config, &rules); | 1000 | input_config_fill_rule_names(input_config, &rules); |
1025 | config->keysym_translation_state = | 1001 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
1026 | keysym_translation_state_create(rules); | 1002 | if (config->keysym_translation_state == NULL) { |
1003 | sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state " | ||
1004 | "for device '%s'", input_config->identifier); | ||
1005 | return; | ||
1006 | } | ||
1027 | 1007 | ||
1028 | for (int i = 0; i < config->modes->length; ++i) { | 1008 | for (int i = 0; i < config->modes->length; ++i) { |
1029 | struct sway_mode *mode = config->modes->items[i]; | 1009 | struct sway_mode *mode = config->modes->items[i]; |