aboutsummaryrefslogtreecommitdiffstats
path: root/sway/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/config.c')
-rw-r--r--sway/config.c214
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 @@
36struct sway_config *config = NULL; 37struct sway_config *config = NULL;
37 38
38static struct xkb_state *keysym_translation_state_create( 39static 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
50static void keysym_translation_state_destroy( 55static 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;
333cleanup: 357cleanup:
@@ -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
365static 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
341static char *get_config_path(void) { 372static 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
372static bool load_config(const char *path, struct sway_config *config, 414static 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
967static 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
975static 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
985void config_update_font_height(bool recalculate) { 964void 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];