diff --git a/.gitignore b/.gitignore index 4322790..c3d4a56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,64 +1,76 @@ -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -# Vim files -*.swp -*.taghl -tags - -# Mac OS files -.DS_Store - -# Project specific files -build/ -.cache +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Vim files +*.swp +*.taghl +tags + +# Mac OS files +.DS_Store + +# Project specific files +build/ +.cache + +.vscode/ + +src/compile_commands.json +src/build.ninja +src/meson-info/intro-buildsystem_files.json +src/meson-info/intro-install_plan.json +src/meson-info/intro-installed.json +src/meson-info/intro-targets.json +src/meson-info/intro-tests.json +src/meson-info/meson-info.json +src/meson-logs/meson-log.txt diff --git a/meson.build b/meson.build index 0aa4949..c56a250 100644 --- a/meson.build +++ b/meson.build @@ -116,6 +116,7 @@ common_sources = files( 'src/surface.c', 'src/unicode.c', 'src/xmalloc.c', + 'src/modules.c', ) compgen_sources = files( diff --git a/src/config.c b/src/config.c index 52e14d8..2a25797 100644 --- a/src/config.c +++ b/src/config.c @@ -1,3 +1,11 @@ +#include "config.h" +#include "color.h" +#include "log.h" +#include "nelem.h" +#include "scale.h" +#include "tofi.h" +#include "unicode.h" +#include "xmalloc.h" #include #include #include @@ -5,14 +13,6 @@ #include #include #include -#include "tofi.h" -#include "color.h" -#include "config.h" -#include "log.h" -#include "nelem.h" -#include "scale.h" -#include "unicode.h" -#include "xmalloc.h" /* Maximum number of config file errors before we give up */ #define MAX_ERRORS 5 @@ -21,1147 +21,1164 @@ #define MAX_RECURSION 32 /* Anyone with a 10M config file is doing something very wrong */ -#define MAX_CONFIG_SIZE (10*1024*1024) +#define MAX_CONFIG_SIZE (10 * 1024 * 1024) /* Convenience macros for anchor combinations */ -#define ANCHOR_TOP_LEFT (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT \ - ) -#define ANCHOR_TOP (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT \ - ) -#define ANCHOR_TOP_RIGHT (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT \ - ) -#define ANCHOR_RIGHT (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM \ - ) -#define ANCHOR_BOTTOM_RIGHT (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT \ - ) -#define ANCHOR_BOTTOM (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT \ - ) -#define ANCHOR_BOTTOM_LEFT (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT \ - ) -#define ANCHOR_LEFT (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM \ - ) -#define ANCHOR_CENTER (\ - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT \ - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT \ - ) +#define ANCHOR_TOP_LEFT \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) +#define ANCHOR_TOP \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | \ + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) +#define ANCHOR_TOP_RIGHT \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) +#define ANCHOR_RIGHT \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | \ + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) +#define ANCHOR_BOTTOM_RIGHT \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) +#define ANCHOR_BOTTOM \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | \ + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) +#define ANCHOR_BOTTOM_LEFT \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) +#define ANCHOR_LEFT \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | \ + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) +#define ANCHOR_CENTER \ + (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | \ + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) struct uint32_percent { - uint32_t value; - bool percent; + uint32_t value; + bool percent; }; static char *strip(const char *str); -static bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const char *option, const char *value); +static bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, + const char *option, const char *value); static char *get_config_path(void); -static uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent); +static uint32_t fixup_percentage(uint32_t value, uint32_t base, + bool is_percent); -static uint32_t parse_anchor(const char *filename, size_t lineno, const char *str, bool *err); -static enum cursor_style parse_cursor_style(const char *filename, size_t lineno, const char *str, bool *err); -static enum matching_algorithm parse_matching_algorithm(const char *filename, size_t lineno, const char *str, bool *err); +static uint32_t parse_anchor(const char *filename, size_t lineno, + const char *str, bool *err); +static enum cursor_style parse_cursor_style(const char *filename, size_t lineno, + const char *str, bool *err); +static enum matching_algorithm parse_matching_algorithm(const char *filename, + size_t lineno, + const char *str, + bool *err); -static bool parse_bool(const char *filename, size_t lineno, const char *str, bool *err); -static uint32_t parse_char(const char *filename, size_t lineno, const char *str, bool *err); -static struct color parse_color(const char *filename, size_t lineno, const char *str, bool *err); -static uint32_t parse_uint32(const char *filename, size_t lineno, const char *str, bool *err); -static int32_t parse_int32(const char *filename, size_t lineno, const char *str, bool *err); -static struct uint32_percent parse_uint32_percent(const char *filename, size_t lineno, const char *str, bool *err); -static struct directional parse_directional(const char *filename, size_t lineno, const char *str, bool *err); +static bool parse_bool(const char *filename, size_t lineno, const char *str, + bool *err); +static uint32_t parse_char(const char *filename, size_t lineno, const char *str, + bool *err); +static struct color parse_color(const char *filename, size_t lineno, + const char *str, bool *err); +static uint32_t parse_uint32(const char *filename, size_t lineno, + const char *str, bool *err); +static int32_t parse_int32(const char *filename, size_t lineno, const char *str, + bool *err); +static struct uint32_percent parse_uint32_percent(const char *filename, + size_t lineno, + const char *str, bool *err); +static struct directional parse_directional(const char *filename, size_t lineno, + const char *str, bool *err); /* * Function-like macro. Yuck. */ -#define PARSE_ERROR_NO_ARGS(filename, lineno, fmt) \ - if ((lineno) > 0) {\ - log_error("%s: line %zu: ", (filename), (lineno));\ - log_append_error((fmt)); \ - } else {\ - log_error((fmt)); \ - } +#define PARSE_ERROR_NO_ARGS(filename, lineno, fmt) \ + if ((lineno) > 0) { \ + log_error("%s: line %zu: ", (filename), (lineno)); \ + log_append_error((fmt)); \ + } else { \ + log_error((fmt)); \ + } -#define PARSE_ERROR(filename, lineno, fmt, ...) \ - if ((lineno) > 0) {\ - log_error("%s: line %zu: ", (filename), (lineno));\ - log_append_error((fmt), __VA_ARGS__); \ - } else {\ - log_error((fmt), __VA_ARGS__); \ - } +#define PARSE_ERROR(filename, lineno, fmt, ...) \ + if ((lineno) > 0) { \ + log_error("%s: line %zu: ", (filename), (lineno)); \ + log_append_error((fmt), __VA_ARGS__); \ + } else { \ + log_error((fmt), __VA_ARGS__); \ + } -void config_load(struct tofi *tofi, const char *filename) -{ - char *default_filename = NULL; - if (!filename) { - default_filename = get_config_path(); - if (!default_filename) { - return; - } - filename = default_filename; - } - /* - * Track and limit recursion depth, so we don't overflow the stack if - * a config file loop is created. - */ - static uint8_t recursion_depth = 0; - recursion_depth++; - if (recursion_depth > MAX_RECURSION) { - log_error("Refusing to load %s, recursion too deep (>%u layers).\n", filename, MAX_RECURSION); - recursion_depth--; - return; - } - char *config; - FILE *fp = fopen(filename, "rb"); - if (!fp) { - if (!default_filename || errno != ENOENT) { - log_error("Failed to open config file %s: %s\n", filename, strerror(errno)); - } - goto CLEANUP_FILENAME; - } - if (fseek(fp, 0, SEEK_END)) { - log_error("Failed to seek in config file: %s\n", strerror(errno)); - fclose(fp); - goto CLEANUP_FILENAME; - } - size_t size; - { - long ssize = ftell(fp); - if (ssize < 0) { - log_error("Failed to determine config file size: %s\n", strerror(errno)); - fclose(fp); - goto CLEANUP_FILENAME; - } - if (ssize > MAX_CONFIG_SIZE) { - log_error("Config file too big (> %d MiB)! Are you sure it's a file?\n", MAX_CONFIG_SIZE / 1024 / 1024); - fclose(fp); - goto CLEANUP_FILENAME; - } - size = (size_t)ssize; - } - config = xmalloc(size + 1); - if (!config) { - log_error("Failed to malloc buffer for config file.\n"); - fclose(fp); - goto CLEANUP_FILENAME; - } - rewind(fp); - if (fread(config, 1, size, fp) != size) { - log_error("Failed to read config file: %s\n", strerror(errno)); - fclose(fp); - goto CLEANUP_CONFIG; - } - fclose(fp); - config[size] = '\0'; +void config_load(struct tofi *tofi, const char *filename) { + char *default_filename = NULL; + if (!filename) { + default_filename = get_config_path(); + if (!default_filename) { + return; + } + filename = default_filename; + } + /* + * Track and limit recursion depth, so we don't overflow the stack if + * a config file loop is created. + */ + static uint8_t recursion_depth = 0; + recursion_depth++; + if (recursion_depth > MAX_RECURSION) { + log_error("Refusing to load %s, recursion too deep (>%u layers).\n", + filename, MAX_RECURSION); + recursion_depth--; + return; + } + char *config; + FILE *fp = fopen(filename, "rb"); + if (!fp) { + if (!default_filename || errno != ENOENT) { + log_error("Failed to open config file %s: %s\n", filename, + strerror(errno)); + } + goto CLEANUP_FILENAME; + } + if (fseek(fp, 0, SEEK_END)) { + log_error("Failed to seek in config file: %s\n", strerror(errno)); + fclose(fp); + goto CLEANUP_FILENAME; + } + size_t size; + { + long ssize = ftell(fp); + if (ssize < 0) { + log_error("Failed to determine config file size: %s\n", strerror(errno)); + fclose(fp); + goto CLEANUP_FILENAME; + } + if (ssize > MAX_CONFIG_SIZE) { + log_error("Config file too big (> %d MiB)! Are you sure it's a file?\n", + MAX_CONFIG_SIZE / 1024 / 1024); + fclose(fp); + goto CLEANUP_FILENAME; + } + size = (size_t)ssize; + } + config = xmalloc(size + 1); + if (!config) { + log_error("Failed to malloc buffer for config file.\n"); + fclose(fp); + goto CLEANUP_FILENAME; + } + rewind(fp); + if (fread(config, 1, size, fp) != size) { + log_error("Failed to read config file: %s\n", strerror(errno)); + fclose(fp); + goto CLEANUP_CONFIG; + } + fclose(fp); + config[size] = '\0'; - char *config_copy = xstrdup(config); - if (!config_copy) { - log_error("Failed to malloc second buffer for config file.\n"); - goto CLEANUP_ALL; - } + char *config_copy = xstrdup(config); + if (!config_copy) { + log_error("Failed to malloc second buffer for config file.\n"); + goto CLEANUP_ALL; + } - log_debug("Loading config file %s.\n", filename); + log_debug("Loading config file %s.\n", filename); - char *saveptr1 = NULL; - char *saveptr2 = NULL; + char *saveptr1 = NULL; + char *saveptr2 = NULL; - char *copy_pos = config_copy; - size_t lineno = 1; - size_t num_errs = 0; - for (char *str1 = config; ; str1 = NULL, saveptr2 = NULL) { - if (num_errs > MAX_ERRORS) { - log_error("Too many config file errors (>%u), giving up.\n", MAX_ERRORS); - break; - } - char *line = strtok_r(str1, "\r\n", &saveptr1); - if (!line) { - /* We're done here */ - break; - } - while ((copy_pos - config_copy) < (line - config)) { - if (*copy_pos == '\n') { - lineno++; - } - copy_pos++; - } - { - /* Grab first non-space character on the line. */ - char c = '\0'; - for (char *tmp = line; *tmp != '\0'; tmp++) { - c = *tmp; - if (!isspace(c)) { - break; - } - } - /* - * Comment characters. - * N.B. treating section headers as comments for now. - */ - switch (c) { - case '#': - case ';': - case '[': - continue; - } - } - if (line[0] == '=') { - PARSE_ERROR_NO_ARGS(filename, lineno, "Missing option.\n"); - num_errs++; - continue; - } - char *option = strtok_r(line, "=", &saveptr2); - if (!option) { - char *tmp = strip(line); - PARSE_ERROR(filename, lineno, "Config option \"%s\" missing value.\n", tmp); - num_errs++; - free(tmp); - continue; - } - char *option_stripped = strip(option); - if (!option_stripped) { - PARSE_ERROR_NO_ARGS(filename, lineno, "Missing option.\n"); - num_errs++; - continue; - } - char *value = strtok_r(NULL, "\r\n", &saveptr2); - if (!value) { - PARSE_ERROR(filename, lineno, "Config option \"%s\" missing value.\n", option_stripped); - num_errs++; - free(option_stripped); - continue; - } - char *value_stripped = strip(value); - if (!value_stripped) { - PARSE_ERROR(filename, lineno, "Config option \"%s\" missing value.\n", option_stripped); - num_errs++; - free(option_stripped); - continue; - } - if (!parse_option(tofi, filename, lineno, option_stripped, value_stripped)) { - num_errs++; - } + char *copy_pos = config_copy; + size_t lineno = 1; + size_t num_errs = 0; + for (char *str1 = config;; str1 = NULL, saveptr2 = NULL) { + if (num_errs > MAX_ERRORS) { + log_error("Too many config file errors (>%u), giving up.\n", MAX_ERRORS); + break; + } + char *line = strtok_r(str1, "\r\n", &saveptr1); + if (!line) { + /* We're done here */ + break; + } + while ((copy_pos - config_copy) < (line - config)) { + if (*copy_pos == '\n') { + lineno++; + } + copy_pos++; + } + { + /* Grab first non-space character on the line. */ + char c = '\0'; + for (char *tmp = line; *tmp != '\0'; tmp++) { + c = *tmp; + if (!isspace(c)) { + break; + } + } + /* + * Comment characters. + * N.B. treating section headers as comments for now. + */ + switch (c) { + case '#': + case ';': + case '[': + continue; + } + } + if (line[0] == '=') { + PARSE_ERROR_NO_ARGS(filename, lineno, "Missing option.\n"); + num_errs++; + continue; + } + char *option = strtok_r(line, "=", &saveptr2); + if (!option) { + char *tmp = strip(line); + PARSE_ERROR(filename, lineno, "Config option \"%s\" missing value.\n", + tmp); + num_errs++; + free(tmp); + continue; + } + char *option_stripped = strip(option); + if (!option_stripped) { + PARSE_ERROR_NO_ARGS(filename, lineno, "Missing option.\n"); + num_errs++; + continue; + } + char *value = strtok_r(NULL, "\r\n", &saveptr2); + if (!value) { + PARSE_ERROR(filename, lineno, "Config option \"%s\" missing value.\n", + option_stripped); + num_errs++; + free(option_stripped); + continue; + } + char *value_stripped = strip(value); + if (!value_stripped) { + PARSE_ERROR(filename, lineno, "Config option \"%s\" missing value.\n", + option_stripped); + num_errs++; + free(option_stripped); + continue; + } + if (!parse_option(tofi, filename, lineno, option_stripped, + value_stripped)) { + num_errs++; + } - /* Cleanup */ - free(value_stripped); - free(option_stripped); - } + /* Cleanup */ + free(value_stripped); + free(option_stripped); + } CLEANUP_ALL: - free(config_copy); + free(config_copy); CLEANUP_CONFIG: - free(config); + free(config); CLEANUP_FILENAME: - if (default_filename) { - free(default_filename); - } - recursion_depth--; + if (default_filename) { + free(default_filename); + } + recursion_depth--; } -char *strip(const char *str) -{ - size_t start = 0; - size_t end = strlen(str); - while (start <= end && isspace(str[start])) { - start++; - } - if (start == end) { - return NULL; - } - while (end > start && (isspace(str[end]) || str[end] == '\0')) { - end--; - } - if (end < start) { - return NULL; - } - if (str[start] == '"' && str[end] == '"' && end > start) { - start++; - end--; - } - size_t len = end - start + 1; - char *buf = xcalloc(len + 1, 1); - strncpy(buf, str + start, len); - buf[len] = '\0'; - return buf; +char *strip(const char *str) { + size_t start = 0; + size_t end = strlen(str); + while (start <= end && isspace(str[start])) { + start++; + } + if (start == end) { + return NULL; + } + while (end > start && (isspace(str[end]) || str[end] == '\0')) { + end--; + } + if (end < start) { + return NULL; + } + if (str[start] == '"' && str[end] == '"' && end > start) { + start++; + end--; + } + size_t len = end - start + 1; + char *buf = xcalloc(len + 1, 1); + strncpy(buf, str + start, len); + buf[len] = '\0'; + return buf; } -bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const char *option, const char *value) -{ - bool err = false; - struct uint32_percent percent; - if (strcasecmp(option, "include") == 0) { - if (value[0] == '/') { - config_load(tofi, value); - } else { - char *tmp = xstrdup(filename); - char *dir = dirname(tmp); - size_t len = strlen(dir) + 1 + strlen(value) + 1; - char *config = xcalloc(len, 1); - snprintf(config, len, "%s/%s", dir, value); - config_load(tofi, config); - free(config); - free(tmp); - } - } else if (strcasecmp(option, "anchor") == 0) { - uint32_t val = parse_anchor(filename, lineno, value, &err); - if (!err) { - tofi->anchor = val; - } - } else if (strcasecmp(option, "background-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.background_color = val; - } - } else if (strcasecmp(option, "corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.corner_radius = val; - } - } else if (strcasecmp(option, "output") == 0) { - snprintf(tofi->target_output_name, N_ELEM(tofi->target_output_name), "%s", value); - } else if (strcasecmp(option, "font") == 0) { - if ((strlen(value) > 2) && (value[0] == '~') && (value[1] == '/')) { - snprintf(tofi->window.entry.font_name, N_ELEM(tofi->window.entry.font_name), "%s%s", getenv("HOME"), &(value[1])); - } else { - snprintf(tofi->window.entry.font_name, N_ELEM(tofi->window.entry.font_name), "%s", value); - } - } else if (strcasecmp(option, "font-size") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (val == 0) { - err = true; - PARSE_ERROR(filename, lineno, "Option \"%s\" must be greater than 0.\n", option); - } else { - tofi->window.entry.font_size = val; - } - } else if (strcasecmp(option, "font-features") == 0) { - snprintf(tofi->window.entry.font_features, N_ELEM(tofi->window.entry.font_features), "%s", value); - } else if (strcasecmp(option, "font-variations") == 0) { - snprintf(tofi->window.entry.font_variations, N_ELEM(tofi->window.entry.font_variations), "%s", value); - } else if (strcasecmp(option, "num-results") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.num_results = val; - } - } else if (strcasecmp(option, "outline-width") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.outline_width = val; - } - } else if (strcasecmp(option, "outline-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.outline_color = val; - } - } else if (strcasecmp(option, "text-cursor") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.cursor_theme.show = val; - } - } else if (strcasecmp(option, "text-cursor-style") == 0) { - enum cursor_style val = parse_cursor_style(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.cursor_theme.style = val; - } - } else if (strcasecmp(option, "text-cursor-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.cursor_theme.color = val; - tofi->window.entry.cursor_theme.color_specified = true; - } - } else if (strcasecmp(option, "text-cursor-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.cursor_theme.text_color = val; - tofi->window.entry.cursor_theme.text_color_specified = true; - } - } else if (strcasecmp(option, "text-cursor-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.cursor_theme.corner_radius = val; - } - } else if (strcasecmp(option, "text-cursor-thickness") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.cursor_theme.thickness = val; - tofi->window.entry.cursor_theme.thickness_specified = true; - } - } else if (strcasecmp(option, "prompt-text") == 0) { - snprintf(tofi->window.entry.prompt_text, N_ELEM(tofi->window.entry.prompt_text), "%s", value); - } else if (strcasecmp(option, "prompt-padding") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.prompt_padding = val; - } - } else if (strcasecmp(option, "prompt-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.prompt_theme.foreground_color = val; - tofi->window.entry.prompt_theme.foreground_specified = true; - } - } else if (strcasecmp(option, "prompt-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.prompt_theme.background_color = val; - tofi->window.entry.prompt_theme.background_specified = true; - } - } else if (strcasecmp(option, "prompt-background-padding") == 0) { - struct directional val = parse_directional(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.prompt_theme.padding = val; - tofi->window.entry.prompt_theme.padding_specified = true; - } - } else if (strcasecmp(option, "prompt-background-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.prompt_theme.background_corner_radius = val; - tofi->window.entry.prompt_theme.radius_specified = true; - } - } else if (strcasecmp(option, "placeholder-text") == 0) { - snprintf(tofi->window.entry.placeholder_text, N_ELEM(tofi->window.entry.placeholder_text), "%s", value); - } else if (strcasecmp(option, "placeholder-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.placeholder_theme.foreground_color = val; - tofi->window.entry.placeholder_theme.foreground_specified = true; - } - } else if (strcasecmp(option, "placeholder-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.placeholder_theme.background_color = val; - tofi->window.entry.placeholder_theme.background_specified = true; - } - } else if (strcasecmp(option, "placeholder-background-padding") == 0) { - struct directional val = parse_directional(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.placeholder_theme.padding = val; - tofi->window.entry.placeholder_theme.padding_specified = true; - } - } else if (strcasecmp(option, "placeholder-background-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.placeholder_theme.background_corner_radius = val; - tofi->window.entry.placeholder_theme.radius_specified = true; - } - } else if (strcasecmp(option, "input-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.input_theme.foreground_color = val; - tofi->window.entry.input_theme.foreground_specified = true; - } - } else if (strcasecmp(option, "input-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.input_theme.background_color = val; - tofi->window.entry.input_theme.background_specified = true; - } - } else if (strcasecmp(option, "input-background-padding") == 0) { - struct directional val = parse_directional(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.input_theme.padding = val; - tofi->window.entry.input_theme.padding_specified = true; - } - } else if (strcasecmp(option, "input-background-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.input_theme.background_corner_radius = val; - tofi->window.entry.input_theme.radius_specified = true; - } - } else if (strcasecmp(option, "default-result-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.default_result_theme.foreground_color = val; - tofi->window.entry.default_result_theme.foreground_specified = true; - } - } else if (strcasecmp(option, "default-result-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.default_result_theme.background_color = val; - tofi->window.entry.default_result_theme.background_specified = true; - } - } else if (strcasecmp(option, "default-result-background-padding") == 0) { - struct directional val = parse_directional(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.default_result_theme.padding = val; - tofi->window.entry.default_result_theme.padding_specified = true; - } - } else if (strcasecmp(option, "default-result-background-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.default_result_theme.background_corner_radius = val; - tofi->window.entry.default_result_theme.radius_specified = true; - } - } else if (strcasecmp(option, "alternate-result-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.alternate_result_theme.foreground_color = val; - tofi->window.entry.alternate_result_theme.foreground_specified = true; - } - } else if (strcasecmp(option, "alternate-result-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.alternate_result_theme.background_color = val; - tofi->window.entry.alternate_result_theme.background_specified = true; - } - } else if (strcasecmp(option, "alternate-result-background-padding") == 0) { - struct directional val = parse_directional(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.alternate_result_theme.padding = val; - tofi->window.entry.alternate_result_theme.padding_specified = true; - } - } else if (strcasecmp(option, "alternate-result-background-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.alternate_result_theme.background_corner_radius = val; - tofi->window.entry.alternate_result_theme.radius_specified = true; - } - } else if (strcasecmp(option, "min-input-width") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.input_width = val; - } - } else if (strcasecmp(option, "result-spacing") == 0) { - int32_t val = parse_int32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.result_spacing = val; - } - } else if (strcasecmp(option, "border-width") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.border_width = val; - } - } else if (strcasecmp(option, "border-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.border_color = val; - } - } else if (strcasecmp(option, "text-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.foreground_color = val; - } - } else if (strcasecmp(option, "selection-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.selection_theme.foreground_color = val; - tofi->window.entry.selection_theme.foreground_specified = true; - } - } else if (strcasecmp(option, "selection-match-color") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.selection_highlight_color = val; - } - } else if (strcasecmp(option, "selection-padding") == 0) { - log_warning("The \"selection-padding\" option is deprecated, and will be removed in future. Please switch to \"selection-background-padding\".\n"); - int32_t val = parse_int32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.selection_theme.padding.left = val; - tofi->window.entry.selection_theme.padding.right = val; - tofi->window.entry.selection_theme.padding_specified = true; - } - } else if (strcasecmp(option, "selection-background") == 0) { - struct color val = parse_color(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.selection_theme.background_color = val; - tofi->window.entry.selection_theme.background_specified = true; - } - } else if (strcasecmp(option, "selection-background-padding") == 0) { - struct directional val = parse_directional(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.selection_theme.padding = val; - tofi->window.entry.selection_theme.padding_specified = true; - } - } else if (strcasecmp(option, "selection-background-corner-radius") == 0) { - uint32_t val = parse_uint32(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.selection_theme.background_corner_radius = val; - tofi->window.entry.selection_theme.radius_specified = true; - } - } else if (strcasecmp(option, "exclusive-zone") == 0) { - if (strcmp(value, "-1") == 0) { - tofi->window.exclusive_zone = -1; - } else { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.exclusive_zone = percent.value; - tofi->window.exclusive_zone_is_percent = percent.percent; - } - } - } else if (strcasecmp(option, "width") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.width = percent.value; - tofi->window.width_is_percent = percent.percent; - } - } else if (strcasecmp(option, "height") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.height = percent.value; - tofi->window.height_is_percent = percent.percent; - } - } else if (strcasecmp(option, "margin-top") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.margin_top = percent.value; - tofi->window.margin_top_is_percent = percent.percent; - } - } else if (strcasecmp(option, "margin-bottom") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.margin_bottom = percent.value; - tofi->window.margin_bottom_is_percent = percent.percent; - } - } else if (strcasecmp(option, "margin-left") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.margin_left = percent.value; - tofi->window.margin_left_is_percent = percent.percent; - } - } else if (strcasecmp(option, "margin-right") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.margin_right = percent.value; - tofi->window.margin_right_is_percent = percent.percent; - } - } else if (strcasecmp(option, "padding-top") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.padding_top = percent.value; - tofi->window.entry.padding_top_is_percent = percent.percent; - } - } else if (strcasecmp(option, "padding-bottom") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.padding_bottom = percent.value; - tofi->window.entry.padding_bottom_is_percent = percent.percent; - } - } else if (strcasecmp(option, "padding-left") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.padding_left = percent.value; - tofi->window.entry.padding_left_is_percent = percent.percent; - } - } else if (strcasecmp(option, "padding-right") == 0) { - percent = parse_uint32_percent(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.padding_right = percent.value; - tofi->window.entry.padding_right_is_percent = percent.percent; - } - } else if (strcasecmp(option, "clip-to-padding") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.clip_to_padding = val; - } - } else if (strcasecmp(option, "horizontal") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.horizontal = val; - } - } else if (strcasecmp(option, "hide-cursor") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->hide_cursor = val; - } - } else if (strcasecmp(option, "history") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->use_history = val; - } - } else if (strcasecmp(option, "history-file") == 0) { - snprintf(tofi->history_file, N_ELEM(tofi->history_file), "%s", value); - } else if (strcasecmp(option, "matching-algorithm") == 0) { - enum matching_algorithm val = parse_matching_algorithm(filename, lineno, value, &err); - if (!err) { - tofi->matching_algorithm= val; - } - } else if (strcasecmp(option, "fuzzy-match") == 0) { - log_warning("The \"fuzzy-match\" option is deprecated, and may be removed in future. Please switch to \"matching-algorithm\".\n"); - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - if (val) { - tofi->matching_algorithm = MATCHING_ALGORITHM_FUZZY; - } - } - } else if (strcasecmp(option, "require-match") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->require_match = val; - } - } else if (strcasecmp(option, "auto-accept-single") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->auto_accept_single = val; - } - } else if (strcasecmp(option, "print-index") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->print_index = val; - } - } else if (strcasecmp(option, "hide-input") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.hide_input = val; - } - } else if (strcasecmp(option, "hidden-character") == 0) { - uint32_t ch = parse_char(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.hidden_character_utf8_length = - utf32_to_utf8(ch, tofi->window.entry.hidden_character_utf8); - } - } else if (strcasecmp(option, "physical-keybindings") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->physical_keybindings = val; - } - } else if (strcasecmp(option, "drun-launch") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->drun_launch = val; - } - } else if (strcasecmp(option, "drun-print-exec") == 0) { - log_warning("drun-print-exec is deprecated, as it is now always true.\n" - " This option may be removed in a future version of tofi.\n"); - } else if (strcasecmp(option, "terminal") == 0) { - snprintf(tofi->default_terminal, N_ELEM(tofi->default_terminal), "%s", value); - } else if (strcasecmp(option, "hint-font") == 0) { - bool val = !parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->window.entry.harfbuzz.disable_hinting = val; - } - } else if (strcasecmp(option, "multi-instance") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->multiple_instance = val; - } - } else if (strcasecmp(option, "ascii-input") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->ascii_input = val; - } - } else if (strcasecmp(option, "late-keyboard-init") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->late_keyboard_init = val; - } - } else if (strcasecmp(option, "output") == 0) { - snprintf(tofi->target_output_name, N_ELEM(tofi->target_output_name), "%s", value); - } else if (strcasecmp(option, "scale") == 0) { - bool val = parse_bool(filename, lineno, value, &err); - if (!err) { - tofi->use_scale = val; - } - } else { - PARSE_ERROR(filename, lineno, "Unknown option \"%s\"\n", option); - err = true; - } - return !err; +bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, + const char *option, const char *value) { + bool err = false; + struct uint32_percent percent; + if (strcasecmp(option, "include") == 0) { + if (value[0] == '/') { + config_load(tofi, value); + } else { + char *tmp = xstrdup(filename); + char *dir = dirname(tmp); + size_t len = strlen(dir) + 1 + strlen(value) + 1; + char *config = xcalloc(len, 1); + snprintf(config, len, "%s/%s", dir, value); + config_load(tofi, config); + free(config); + free(tmp); + } + } else if (strcasecmp(option, "anchor") == 0) { + uint32_t val = parse_anchor(filename, lineno, value, &err); + if (!err) { + tofi->anchor = val; + } + } else if (strcasecmp(option, "background-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.background_color = val; + } + } else if (strcasecmp(option, "corner-radius") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.corner_radius = val; + } + } else if (strcasecmp(option, "output") == 0) { + snprintf(tofi->target_output_name, N_ELEM(tofi->target_output_name), "%s", + value); + } else if (strcasecmp(option, "font") == 0) { + if ((strlen(value) > 2) && (value[0] == '~') && (value[1] == '/')) { + snprintf(tofi->window.entry.font_name, + N_ELEM(tofi->window.entry.font_name), "%s%s", getenv("HOME"), + &(value[1])); + } else { + snprintf(tofi->window.entry.font_name, + N_ELEM(tofi->window.entry.font_name), "%s", value); + } + } else if (strcasecmp(option, "font-size") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (val == 0) { + err = true; + PARSE_ERROR(filename, lineno, "Option \"%s\" must be greater than 0.\n", + option); + } else { + tofi->window.entry.font_size = val; + } + } else if (strcasecmp(option, "font-features") == 0) { + snprintf(tofi->window.entry.font_features, + N_ELEM(tofi->window.entry.font_features), "%s", value); + } else if (strcasecmp(option, "font-variations") == 0) { + snprintf(tofi->window.entry.font_variations, + N_ELEM(tofi->window.entry.font_variations), "%s", value); + } else if (strcasecmp(option, "num-results") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.num_results = val; + } + } else if (strcasecmp(option, "outline-width") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.outline_width = val; + } + } else if (strcasecmp(option, "outline-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.outline_color = val; + } + } else if (strcasecmp(option, "text-cursor") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.cursor_theme.show = val; + } + } else if (strcasecmp(option, "text-cursor-style") == 0) { + enum cursor_style val = parse_cursor_style(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.cursor_theme.style = val; + } + } else if (strcasecmp(option, "text-cursor-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.cursor_theme.color = val; + tofi->window.entry.cursor_theme.color_specified = true; + } + } else if (strcasecmp(option, "text-cursor-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.cursor_theme.text_color = val; + tofi->window.entry.cursor_theme.text_color_specified = true; + } + } else if (strcasecmp(option, "text-cursor-corner-radius") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.cursor_theme.corner_radius = val; + } + } else if (strcasecmp(option, "text-cursor-thickness") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.cursor_theme.thickness = val; + tofi->window.entry.cursor_theme.thickness_specified = true; + } + } else if (strcasecmp(option, "prompt-text") == 0) { + snprintf(tofi->window.entry.prompt_text, + N_ELEM(tofi->window.entry.prompt_text), "%s", value); + } else if (strcasecmp(option, "prompt-padding") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.prompt_padding = val; + } + } else if (strcasecmp(option, "prompt-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.prompt_theme.foreground_color = val; + tofi->window.entry.prompt_theme.foreground_specified = true; + } + } else if (strcasecmp(option, "prompt-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.prompt_theme.background_color = val; + tofi->window.entry.prompt_theme.background_specified = true; + } + } else if (strcasecmp(option, "prompt-background-padding") == 0) { + struct directional val = parse_directional(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.prompt_theme.padding = val; + tofi->window.entry.prompt_theme.padding_specified = true; + } + } else if (strcasecmp(option, "prompt-background-corner-radius") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.prompt_theme.background_corner_radius = val; + tofi->window.entry.prompt_theme.radius_specified = true; + } + } else if (strcasecmp(option, "placeholder-text") == 0) { + snprintf(tofi->window.entry.placeholder_text, + N_ELEM(tofi->window.entry.placeholder_text), "%s", value); + } else if (strcasecmp(option, "placeholder-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.placeholder_theme.foreground_color = val; + tofi->window.entry.placeholder_theme.foreground_specified = true; + } + } else if (strcasecmp(option, "placeholder-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.placeholder_theme.background_color = val; + tofi->window.entry.placeholder_theme.background_specified = true; + } + } else if (strcasecmp(option, "placeholder-background-padding") == 0) { + struct directional val = parse_directional(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.placeholder_theme.padding = val; + tofi->window.entry.placeholder_theme.padding_specified = true; + } + } else if (strcasecmp(option, "placeholder-background-corner-radius") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.placeholder_theme.background_corner_radius = val; + tofi->window.entry.placeholder_theme.radius_specified = true; + } + } else if (strcasecmp(option, "input-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.input_theme.foreground_color = val; + tofi->window.entry.input_theme.foreground_specified = true; + } + } else if (strcasecmp(option, "input-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.input_theme.background_color = val; + tofi->window.entry.input_theme.background_specified = true; + } + } else if (strcasecmp(option, "input-background-padding") == 0) { + struct directional val = parse_directional(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.input_theme.padding = val; + tofi->window.entry.input_theme.padding_specified = true; + } + } else if (strcasecmp(option, "input-background-corner-radius") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.input_theme.background_corner_radius = val; + tofi->window.entry.input_theme.radius_specified = true; + } + } else if (strcasecmp(option, "default-result-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.default_result_theme.foreground_color = val; + tofi->window.entry.default_result_theme.foreground_specified = true; + } + } else if (strcasecmp(option, "default-result-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.default_result_theme.background_color = val; + tofi->window.entry.default_result_theme.background_specified = true; + } + } else if (strcasecmp(option, "default-result-background-padding") == 0) { + struct directional val = parse_directional(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.default_result_theme.padding = val; + tofi->window.entry.default_result_theme.padding_specified = true; + } + } else if (strcasecmp(option, "default-result-background-corner-radius") == + 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.default_result_theme.background_corner_radius = val; + tofi->window.entry.default_result_theme.radius_specified = true; + } + } else if (strcasecmp(option, "alternate-result-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.alternate_result_theme.foreground_color = val; + tofi->window.entry.alternate_result_theme.foreground_specified = true; + } + } else if (strcasecmp(option, "alternate-result-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.alternate_result_theme.background_color = val; + tofi->window.entry.alternate_result_theme.background_specified = true; + } + } else if (strcasecmp(option, "alternate-result-background-padding") == 0) { + struct directional val = parse_directional(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.alternate_result_theme.padding = val; + tofi->window.entry.alternate_result_theme.padding_specified = true; + } + } else if (strcasecmp(option, "alternate-result-background-corner-radius") == + 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.alternate_result_theme.background_corner_radius = val; + tofi->window.entry.alternate_result_theme.radius_specified = true; + } + } else if (strcasecmp(option, "min-input-width") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.input_width = val; + } + } else if (strcasecmp(option, "result-spacing") == 0) { + int32_t val = parse_int32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.result_spacing = val; + } + } else if (strcasecmp(option, "border-width") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.border_width = val; + } + } else if (strcasecmp(option, "border-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.border_color = val; + } + } else if (strcasecmp(option, "text-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.foreground_color = val; + } + } else if (strcasecmp(option, "selection-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.selection_theme.foreground_color = val; + tofi->window.entry.selection_theme.foreground_specified = true; + } + } else if (strcasecmp(option, "selection-match-color") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.selection_highlight_color = val; + } + } else if (strcasecmp(option, "selection-padding") == 0) { + log_warning( + "The \"selection-padding\" option is deprecated, and will be removed " + "in future. Please switch to \"selection-background-padding\".\n"); + int32_t val = parse_int32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.selection_theme.padding.left = val; + tofi->window.entry.selection_theme.padding.right = val; + tofi->window.entry.selection_theme.padding_specified = true; + } + } else if (strcasecmp(option, "selection-background") == 0) { + struct color val = parse_color(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.selection_theme.background_color = val; + tofi->window.entry.selection_theme.background_specified = true; + } + } else if (strcasecmp(option, "selection-background-padding") == 0) { + struct directional val = parse_directional(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.selection_theme.padding = val; + tofi->window.entry.selection_theme.padding_specified = true; + } + } else if (strcasecmp(option, "selection-background-corner-radius") == 0) { + uint32_t val = parse_uint32(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.selection_theme.background_corner_radius = val; + tofi->window.entry.selection_theme.radius_specified = true; + } + } else if (strcasecmp(option, "exclusive-zone") == 0) { + if (strcmp(value, "-1") == 0) { + tofi->window.exclusive_zone = -1; + } else { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.exclusive_zone = percent.value; + tofi->window.exclusive_zone_is_percent = percent.percent; + } + } + } else if (strcasecmp(option, "width") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.width = percent.value; + tofi->window.width_is_percent = percent.percent; + } + } else if (strcasecmp(option, "height") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.height = percent.value; + tofi->window.height_is_percent = percent.percent; + } + } else if (strcasecmp(option, "margin-top") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.margin_top = percent.value; + tofi->window.margin_top_is_percent = percent.percent; + } + } else if (strcasecmp(option, "margin-bottom") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.margin_bottom = percent.value; + tofi->window.margin_bottom_is_percent = percent.percent; + } + } else if (strcasecmp(option, "margin-left") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.margin_left = percent.value; + tofi->window.margin_left_is_percent = percent.percent; + } + } else if (strcasecmp(option, "margin-right") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.margin_right = percent.value; + tofi->window.margin_right_is_percent = percent.percent; + } + } else if (strcasecmp(option, "padding-top") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.padding_top = percent.value; + tofi->window.entry.padding_top_is_percent = percent.percent; + } + } else if (strcasecmp(option, "padding-bottom") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.padding_bottom = percent.value; + tofi->window.entry.padding_bottom_is_percent = percent.percent; + } + } else if (strcasecmp(option, "padding-left") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.padding_left = percent.value; + tofi->window.entry.padding_left_is_percent = percent.percent; + } + } else if (strcasecmp(option, "padding-right") == 0) { + percent = parse_uint32_percent(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.padding_right = percent.value; + tofi->window.entry.padding_right_is_percent = percent.percent; + } + } else if (strcasecmp(option, "clip-to-padding") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.clip_to_padding = val; + } + } else if (strcasecmp(option, "horizontal") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.horizontal = val; + } + } else if (strcasecmp(option, "hide-cursor") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->hide_cursor = val; + } + } else if (strcasecmp(option, "history") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->use_history = val; + } + } else if (strcasecmp(option, "history-file") == 0) { + snprintf(tofi->history_file, N_ELEM(tofi->history_file), "%s", value); + } else if (strcasecmp(option, "matching-algorithm") == 0) { + enum matching_algorithm val = + parse_matching_algorithm(filename, lineno, value, &err); + if (!err) { + tofi->matching_algorithm = val; + } + } else if (strcasecmp(option, "fuzzy-match") == 0) { + log_warning("The \"fuzzy-match\" option is deprecated, and may be removed " + "in future. Please switch to \"matching-algorithm\".\n"); + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + if (val) { + tofi->matching_algorithm = MATCHING_ALGORITHM_FUZZY; + } + } + } else if (strcasecmp(option, "require-match") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->require_match = val; + } + } else if (strcasecmp(option, "auto-accept-single") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->auto_accept_single = val; + } + } else if (strcasecmp(option, "print-index") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->print_index = val; + } + } else if (strcasecmp(option, "hide-input") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.hide_input = val; + } + } else if (strcasecmp(option, "hidden-character") == 0) { + uint32_t ch = parse_char(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.hidden_character_utf8_length = + utf32_to_utf8(ch, tofi->window.entry.hidden_character_utf8); + } + } else if (strcasecmp(option, "physical-keybindings") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->physical_keybindings = val; + } + } else if (strcasecmp(option, "drun-launch") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->drun_launch = val; + } + } else if (strcasecmp(option, "drun-print-exec") == 0) { + log_warning( + "drun-print-exec is deprecated, as it is now always true.\n" + " This option may be removed in a future version of tofi.\n"); + } else if (strcasecmp(option, "terminal") == 0) { + snprintf(tofi->default_terminal, N_ELEM(tofi->default_terminal), "%s", + value); + } else if (strcasecmp(option, "hint-font") == 0) { + bool val = !parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->window.entry.harfbuzz.disable_hinting = val; + } + } else if (strcasecmp(option, "multi-instance") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->multiple_instance = val; + } + } else if (strcasecmp(option, "ascii-input") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->ascii_input = val; + } + } else if (strcasecmp(option, "late-keyboard-init") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->late_keyboard_init = val; + } + } else if (strcasecmp(option, "output") == 0) { + snprintf(tofi->target_output_name, N_ELEM(tofi->target_output_name), "%s", + value); + } else if (strcasecmp(option, "scale") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->use_scale = val; + } + } else if (strcasecmp(option, "module_math") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->module_math = val; + } + } else if (strcasecmp(option, "module_search") == 0) { + bool val = parse_bool(filename, lineno, value, &err); + if (!err) { + tofi->module_search = val; + } + } else { + PARSE_ERROR(filename, lineno, "Unknown option \"%s\"\n", option); + err = true; + } + return !err; } -bool config_apply(struct tofi *tofi, const char *option, const char *value) -{ - return parse_option(tofi, "", 0, option, value); +bool config_apply(struct tofi *tofi, const char *option, const char *value) { + return parse_option(tofi, "", 0, option, value); } -uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent) -{ - if (is_percent) { - return value * base / 100; - } - return value; +uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent) { + if (is_percent) { + return value * base / 100; + } + return value; } -void config_fixup_values(struct tofi *tofi) -{ - uint32_t base_width = tofi->output_width; - uint32_t base_height = tofi->output_height; - uint32_t scale; - if (tofi->window.fractional_scale != 0) { - scale = tofi->window.fractional_scale; - } else { - scale = tofi->window.scale * 120; - } +void config_fixup_values(struct tofi *tofi) { + uint32_t base_width = tofi->output_width; + uint32_t base_height = tofi->output_height; + uint32_t scale; + if (tofi->window.fractional_scale != 0) { + scale = tofi->window.fractional_scale; + } else { + scale = tofi->window.scale * 120; + } - /* - * If we're going to be scaling these values in Cairo, - * we need to apply the inverse scale here. - */ - if (tofi->use_scale) { - base_width = scale_apply_inverse(base_width, scale); - base_height = scale_apply_inverse(base_height, scale); - } + /* + * If we're going to be scaling these values in Cairo, + * we need to apply the inverse scale here. + */ + if (tofi->use_scale) { + base_width = scale_apply_inverse(base_width, scale); + base_height = scale_apply_inverse(base_height, scale); + } - tofi->window.margin_top = fixup_percentage( - tofi->window.margin_top, - base_height, - tofi->window.margin_top_is_percent); - tofi->window.margin_bottom = fixup_percentage( - tofi->window.margin_bottom, - base_height, - tofi->window.margin_bottom_is_percent); - tofi->window.margin_left = fixup_percentage( - tofi->window.margin_left, - base_width, - tofi->window.margin_left_is_percent); - tofi->window.margin_right = fixup_percentage( - tofi->window.margin_right, - base_width, - tofi->window.margin_right_is_percent); - tofi->window.entry.padding_top = fixup_percentage( - tofi->window.entry.padding_top, - base_height, - tofi->window.entry.padding_top_is_percent); - tofi->window.entry.padding_bottom = fixup_percentage( - tofi->window.entry.padding_bottom, - base_height, - tofi->window.entry.padding_bottom_is_percent); - tofi->window.entry.padding_left = fixup_percentage( - tofi->window.entry.padding_left, - base_width, - tofi->window.entry.padding_left_is_percent); - tofi->window.entry.padding_right = fixup_percentage( - tofi->window.entry.padding_right, - base_width, - tofi->window.entry.padding_right_is_percent); + tofi->window.margin_top = fixup_percentage( + tofi->window.margin_top, base_height, tofi->window.margin_top_is_percent); + tofi->window.margin_bottom = + fixup_percentage(tofi->window.margin_bottom, base_height, + tofi->window.margin_bottom_is_percent); + tofi->window.margin_left = + fixup_percentage(tofi->window.margin_left, base_width, + tofi->window.margin_left_is_percent); + tofi->window.margin_right = + fixup_percentage(tofi->window.margin_right, base_width, + tofi->window.margin_right_is_percent); + tofi->window.entry.padding_top = + fixup_percentage(tofi->window.entry.padding_top, base_height, + tofi->window.entry.padding_top_is_percent); + tofi->window.entry.padding_bottom = + fixup_percentage(tofi->window.entry.padding_bottom, base_height, + tofi->window.entry.padding_bottom_is_percent); + tofi->window.entry.padding_left = + fixup_percentage(tofi->window.entry.padding_left, base_width, + tofi->window.entry.padding_left_is_percent); + tofi->window.entry.padding_right = + fixup_percentage(tofi->window.entry.padding_right, base_width, + tofi->window.entry.padding_right_is_percent); - /* - * Window width and height are a little special. We're only going to be - * using them to specify sizes to Wayland, which always wants scaled - * pixels, so always scale them here (unless we've directly specified a - * scaled size). - */ - tofi->window.width = fixup_percentage( - tofi->window.width, - tofi->output_width, - tofi->window.width_is_percent); - tofi->window.height = fixup_percentage( - tofi->window.height, - tofi->output_height, - tofi->window.height_is_percent); - if (tofi->window.width_is_percent || !tofi->use_scale) { - tofi->window.width = scale_apply_inverse(tofi->window.width, scale); - } - if (tofi->window.height_is_percent || !tofi->use_scale) { - tofi->window.height = scale_apply_inverse(tofi->window.height, scale); - } + /* + * Window width and height are a little special. We're only going to be + * using them to specify sizes to Wayland, which always wants scaled + * pixels, so always scale them here (unless we've directly specified a + * scaled size). + */ + tofi->window.width = fixup_percentage(tofi->window.width, tofi->output_width, + tofi->window.width_is_percent); + tofi->window.height = fixup_percentage( + tofi->window.height, tofi->output_height, tofi->window.height_is_percent); + if (tofi->window.width_is_percent || !tofi->use_scale) { + tofi->window.width = scale_apply_inverse(tofi->window.width, scale); + } + if (tofi->window.height_is_percent || !tofi->use_scale) { + tofi->window.height = scale_apply_inverse(tofi->window.height, scale); + } - /* Don't attempt percentage handling if exclusive_zone is set to -1. */ - if (tofi->window.exclusive_zone > 0) { - /* Exclusive zone base depends on anchor. */ - switch (tofi->anchor) { - case ANCHOR_TOP: - case ANCHOR_BOTTOM: - tofi->window.exclusive_zone = fixup_percentage( - tofi->window.exclusive_zone, - base_height, - tofi->window.exclusive_zone_is_percent); - break; - case ANCHOR_LEFT: - case ANCHOR_RIGHT: - tofi->window.exclusive_zone = fixup_percentage( - tofi->window.exclusive_zone, - base_width, - tofi->window.exclusive_zone_is_percent); - break; - default: - /* - * Exclusive zone >0 is meaningless for other - * anchor positions. - */ - tofi->window.exclusive_zone = - MIN(tofi->window.exclusive_zone, 0); - break; - } - } + /* Don't attempt percentage handling if exclusive_zone is set to -1. */ + if (tofi->window.exclusive_zone > 0) { + /* Exclusive zone base depends on anchor. */ + switch (tofi->anchor) { + case ANCHOR_TOP: + case ANCHOR_BOTTOM: + tofi->window.exclusive_zone = + fixup_percentage(tofi->window.exclusive_zone, base_height, + tofi->window.exclusive_zone_is_percent); + break; + case ANCHOR_LEFT: + case ANCHOR_RIGHT: + tofi->window.exclusive_zone = + fixup_percentage(tofi->window.exclusive_zone, base_width, + tofi->window.exclusive_zone_is_percent); + break; + default: + /* + * Exclusive zone >0 is meaningless for other + * anchor positions. + */ + tofi->window.exclusive_zone = MIN(tofi->window.exclusive_zone, 0); + break; + } + } } -char *get_config_path() -{ - char *base_dir = getenv("XDG_CONFIG_HOME"); - char *ext = ""; - size_t len = strlen("/tofi/config") + 1; - if (!base_dir) { - base_dir = getenv("HOME"); - ext = "/.config"; - if (!base_dir) { - log_error("Couldn't find XDG_CONFIG_HOME or HOME envvars\n"); - return NULL; - } - } - len += strlen(base_dir) + strlen(ext) + 2; - char *name = xcalloc(len, sizeof(*name)); - snprintf(name, len, "%s%s%s", base_dir, ext, "/tofi/config"); - return name; +char *get_config_path() { + char *base_dir = getenv("XDG_CONFIG_HOME"); + char *ext = ""; + size_t len = strlen("/tofi/config") + 1; + if (!base_dir) { + base_dir = getenv("HOME"); + ext = "/.config"; + if (!base_dir) { + log_error("Couldn't find XDG_CONFIG_HOME or HOME envvars\n"); + return NULL; + } + } + len += strlen(base_dir) + strlen(ext) + 2; + char *name = xcalloc(len, sizeof(*name)); + snprintf(name, len, "%s%s%s", base_dir, ext, "/tofi/config"); + return name; } -bool parse_bool(const char *filename, size_t lineno, const char *str, bool *err) -{ - if (strcasecmp(str, "true") == 0) { - return true; - } else if (strcasecmp(str, "false") == 0) { - return false; - } - PARSE_ERROR(filename, lineno, "Invalid boolean value \"%s\".\n", str); - if (err) { - *err = true; - } - return false; +bool parse_bool(const char *filename, size_t lineno, const char *str, + bool *err) { + if (strcasecmp(str, "true") == 0) { + return true; + } else if (strcasecmp(str, "false") == 0) { + return false; + } + PARSE_ERROR(filename, lineno, "Invalid boolean value \"%s\".\n", str); + if (err) { + *err = true; + } + return false; } -uint32_t parse_char(const char *filename, size_t lineno, const char *str, bool *err) -{ - uint32_t ch = U'\0'; - if (*str == '\0') { - return ch; - } - if (!utf8_validate(str)) { - PARSE_ERROR(filename, lineno, "Invalid UTF-8 string \"%s\".\n", str); - if (err) { - *err = true; - } - return ch; - } - char *tmp = utf8_compose(str); - ch = utf8_to_utf32_validate(tmp); - if (ch == (uint32_t)-2 || ch == (uint32_t)-1 || *utf8_next_char(tmp) != '\0') { - PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as a character.\n", str); - if (err) { - *err = true; - } - } - free(tmp); +uint32_t parse_char(const char *filename, size_t lineno, const char *str, + bool *err) { + uint32_t ch = U'\0'; + if (*str == '\0') { + return ch; + } + if (!utf8_validate(str)) { + PARSE_ERROR(filename, lineno, "Invalid UTF-8 string \"%s\".\n", str); + if (err) { + *err = true; + } + return ch; + } + char *tmp = utf8_compose(str); + ch = utf8_to_utf32_validate(tmp); + if (ch == (uint32_t)-2 || ch == (uint32_t)-1 || + *utf8_next_char(tmp) != '\0') { + PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as a character.\n", + str); + if (err) { + *err = true; + } + } + free(tmp); - return ch; + return ch; } -uint32_t parse_anchor(const char *filename, size_t lineno, const char *str, bool *err) -{ - if(strcasecmp(str, "top-left") == 0) { - return ANCHOR_TOP_LEFT; - } - if (strcasecmp(str, "top") == 0) { - return ANCHOR_TOP; - } - if (strcasecmp(str, "top-right") == 0) { - return ANCHOR_TOP_RIGHT; - } - if (strcasecmp(str, "right") == 0) { - return ANCHOR_RIGHT; - } - if (strcasecmp(str, "bottom-right") == 0) { - return ANCHOR_BOTTOM_RIGHT; - } - if (strcasecmp(str, "bottom") == 0) { - return ANCHOR_BOTTOM; - } - if (strcasecmp(str, "bottom-left") == 0) { - return ANCHOR_BOTTOM_LEFT; - } - if (strcasecmp(str, "left") == 0) { - return ANCHOR_LEFT; - } - if (strcasecmp(str, "center") == 0) { - return ANCHOR_CENTER; - } - PARSE_ERROR(filename, lineno, "Invalid anchor \"%s\".\n", str); - if (err) { - *err = true; - } - return 0; +uint32_t parse_anchor(const char *filename, size_t lineno, const char *str, + bool *err) { + if (strcasecmp(str, "top-left") == 0) { + return ANCHOR_TOP_LEFT; + } + if (strcasecmp(str, "top") == 0) { + return ANCHOR_TOP; + } + if (strcasecmp(str, "top-right") == 0) { + return ANCHOR_TOP_RIGHT; + } + if (strcasecmp(str, "right") == 0) { + return ANCHOR_RIGHT; + } + if (strcasecmp(str, "bottom-right") == 0) { + return ANCHOR_BOTTOM_RIGHT; + } + if (strcasecmp(str, "bottom") == 0) { + return ANCHOR_BOTTOM; + } + if (strcasecmp(str, "bottom-left") == 0) { + return ANCHOR_BOTTOM_LEFT; + } + if (strcasecmp(str, "left") == 0) { + return ANCHOR_LEFT; + } + if (strcasecmp(str, "center") == 0) { + return ANCHOR_CENTER; + } + PARSE_ERROR(filename, lineno, "Invalid anchor \"%s\".\n", str); + if (err) { + *err = true; + } + return 0; } -enum cursor_style parse_cursor_style(const char *filename, size_t lineno, const char *str, bool *err) -{ - if(strcasecmp(str, "bar") == 0) { - return CURSOR_STYLE_BAR; - } - if(strcasecmp(str, "block") == 0) { - return CURSOR_STYLE_BLOCK; - } - if(strcasecmp(str, "underscore") == 0) { - return CURSOR_STYLE_UNDERSCORE; - } - PARSE_ERROR(filename, lineno, "Invalid cursor style \"%s\".\n", str); - if (err) { - *err = true; - } - return 0; +enum cursor_style parse_cursor_style(const char *filename, size_t lineno, + const char *str, bool *err) { + if (strcasecmp(str, "bar") == 0) { + return CURSOR_STYLE_BAR; + } + if (strcasecmp(str, "block") == 0) { + return CURSOR_STYLE_BLOCK; + } + if (strcasecmp(str, "underscore") == 0) { + return CURSOR_STYLE_UNDERSCORE; + } + PARSE_ERROR(filename, lineno, "Invalid cursor style \"%s\".\n", str); + if (err) { + *err = true; + } + return 0; } -enum matching_algorithm parse_matching_algorithm(const char *filename, size_t lineno, const char *str, bool *err) -{ - if(strcasecmp(str, "normal") == 0) { - return MATCHING_ALGORITHM_NORMAL; - } - if(strcasecmp(str, "fuzzy") == 0) { - return MATCHING_ALGORITHM_FUZZY; - } - if(strcasecmp(str, "prefix") == 0) { - return MATCHING_ALGORITHM_PREFIX; - } - PARSE_ERROR(filename, lineno, "Invalid matching algorithm \"%s\".\n", str); - if (err) { - *err = true; - } - return 0; +enum matching_algorithm parse_matching_algorithm(const char *filename, + size_t lineno, const char *str, + bool *err) { + if (strcasecmp(str, "normal") == 0) { + return MATCHING_ALGORITHM_NORMAL; + } + if (strcasecmp(str, "fuzzy") == 0) { + return MATCHING_ALGORITHM_FUZZY; + } + if (strcasecmp(str, "prefix") == 0) { + return MATCHING_ALGORITHM_PREFIX; + } + PARSE_ERROR(filename, lineno, "Invalid matching algorithm \"%s\".\n", str); + if (err) { + *err = true; + } + return 0; } -struct color parse_color(const char *filename, size_t lineno, const char *str, bool *err) -{ - struct color color = hex_to_color(str); - if (color.r == -1) { - PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as a color.\n", str); - if (err) { - *err = true; - } - } - return color; +struct color parse_color(const char *filename, size_t lineno, const char *str, + bool *err) { + struct color color = hex_to_color(str); + if (color.r == -1) { + PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as a color.\n", str); + if (err) { + *err = true; + } + } + return color; } -uint32_t parse_uint32(const char *filename, size_t lineno, const char *str, bool *err) -{ - errno = 0; - char *endptr; - int64_t ret = strtoull(str, &endptr, 0); - if (endptr == str || *endptr != '\0') { - PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as unsigned int.\n", str); - if (err) { - *err = true; - } - } else if (errno || ret < 0 || ret > UINT32_MAX) { - PARSE_ERROR(filename, lineno, "Unsigned int value \"%s\" out of range.\n", str); - if (err) { - *err = true; - } - } - return ret; +uint32_t parse_uint32(const char *filename, size_t lineno, const char *str, + bool *err) { + errno = 0; + char *endptr; + int64_t ret = strtoull(str, &endptr, 0); + if (endptr == str || *endptr != '\0') { + PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as unsigned int.\n", + str); + if (err) { + *err = true; + } + } else if (errno || ret < 0 || ret > UINT32_MAX) { + PARSE_ERROR(filename, lineno, "Unsigned int value \"%s\" out of range.\n", + str); + if (err) { + *err = true; + } + } + return ret; } -int32_t parse_int32(const char *filename, size_t lineno, const char *str, bool *err) -{ - errno = 0; - char *endptr; - int64_t ret = strtoll(str, &endptr, 0); - if (endptr == str || *endptr != '\0') { - PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as int.\n", str); - if (err) { - *err = true; - } - } else if (errno || ret < INT32_MIN || ret > INT32_MAX) { - PARSE_ERROR(filename, lineno, "Int value \"%s\" out of range.\n", str); - if (err) { - *err = true; - } - } - return ret; +int32_t parse_int32(const char *filename, size_t lineno, const char *str, + bool *err) { + errno = 0; + char *endptr; + int64_t ret = strtoll(str, &endptr, 0); + if (endptr == str || *endptr != '\0') { + PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as int.\n", str); + if (err) { + *err = true; + } + } else if (errno || ret < INT32_MIN || ret > INT32_MAX) { + PARSE_ERROR(filename, lineno, "Int value \"%s\" out of range.\n", str); + if (err) { + *err = true; + } + } + return ret; } -struct uint32_percent parse_uint32_percent(const char *filename, size_t lineno, const char *str, bool *err) -{ - errno = 0; - char *endptr; - int64_t val = strtoull(str, &endptr, 0); - bool percent = false; - if (endptr == str || (*endptr != '\0' && *endptr != '%')) { - PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as unsigned int.\n", str); - if (err) { - *err = true; - } - } else if (errno || val < 0 || val > UINT32_MAX) { - PARSE_ERROR(filename, lineno, "Unsigned int value \"%s\" out of range.\n", str); - if (err) { - *err = true; - } - } - if (!err || !*err) { - if (*endptr == '%') { - percent = true; - } - } - return (struct uint32_percent){ val, percent }; +struct uint32_percent parse_uint32_percent(const char *filename, size_t lineno, + const char *str, bool *err) { + errno = 0; + char *endptr; + int64_t val = strtoull(str, &endptr, 0); + bool percent = false; + if (endptr == str || (*endptr != '\0' && *endptr != '%')) { + PARSE_ERROR(filename, lineno, "Failed to parse \"%s\" as unsigned int.\n", + str); + if (err) { + *err = true; + } + } else if (errno || val < 0 || val > UINT32_MAX) { + PARSE_ERROR(filename, lineno, "Unsigned int value \"%s\" out of range.\n", + str); + if (err) { + *err = true; + } + } + if (!err || !*err) { + if (*endptr == '%') { + percent = true; + } + } + return (struct uint32_percent){val, percent}; } -struct directional parse_directional(const char *filename, size_t lineno, const char *str, bool *err) -{ - /* One extra value to act as a sentinel for too many values in str. */ - int32_t values[5]; - char *saveptr = NULL; - char *tmp = xstrdup(str); - char *val = strtok_r(tmp, ",", &saveptr); +struct directional parse_directional(const char *filename, size_t lineno, + const char *str, bool *err) { + /* One extra value to act as a sentinel for too many values in str. */ + int32_t values[5]; + char *saveptr = NULL; + char *tmp = xstrdup(str); + char *val = strtok_r(tmp, ",", &saveptr); - size_t n; + size_t n; - for (n = 0; n < N_ELEM(values) && val != NULL; n++) { - values[n] = parse_int32(filename, lineno, val, err); - if (err && *err) { - break; - } - val = strtok_r(NULL, ",", &saveptr); - } - free(tmp); + for (n = 0; n < N_ELEM(values) && val != NULL; n++) { + values[n] = parse_int32(filename, lineno, val, err); + if (err && *err) { + break; + } + val = strtok_r(NULL, ",", &saveptr); + } + free(tmp); - struct directional ret = {0}; - if (err && *err) { - return ret; - } + struct directional ret = {0}; + if (err && *err) { + return ret; + } - switch (n) { - case 0: - break; - case 1: - ret = (struct directional) { - .top = values[0], - .right = values[0], - .bottom = values[0], - .left = values[0], - }; - break; - case 2: - ret = (struct directional) { - .top = values[0], - .right = values[1], - .bottom = values[0], - .left = values[1], - }; - break; - case 3: - ret = (struct directional) { - .top = values[0], - .right = values[1], - .bottom = values[2], - .left = values[1], - }; - break; - case 4: - ret = (struct directional) { - .top = values[0], - .right = values[1], - .bottom = values[2], - .left = values[3], - }; - break; - default: - PARSE_ERROR(filename, lineno, "Too many values in \"%s\" for directional.\n", str); - if (err) { - *err = true; - } - break; - }; + switch (n) { + case 0: + break; + case 1: + ret = (struct directional){ + .top = values[0], + .right = values[0], + .bottom = values[0], + .left = values[0], + }; + break; + case 2: + ret = (struct directional){ + .top = values[0], + .right = values[1], + .bottom = values[0], + .left = values[1], + }; + break; + case 3: + ret = (struct directional){ + .top = values[0], + .right = values[1], + .bottom = values[2], + .left = values[1], + }; + break; + case 4: + ret = (struct directional){ + .top = values[0], + .right = values[1], + .bottom = values[2], + .left = values[3], + }; + break; + default: + PARSE_ERROR(filename, lineno, + "Too many values in \"%s\" for directional.\n", str); + if (err) { + *err = true; + } + break; + }; - return ret; + return ret; } diff --git a/src/desktop_vec.c b/src/desktop_vec.c index 1fd0b41..c6c6d38 100644 --- a/src/desktop_vec.c +++ b/src/desktop_vec.c @@ -1,261 +1,260 @@ -#include -#include #include "desktop_vec.h" -#include "matching.h" #include "log.h" +#include "matching.h" +#include "modules.h" #include "string_vec.h" #include "unicode.h" #include "xmalloc.h" +#include +#include +#include +#include +#include // incluced String for qalc +#include +#include -static bool match_current_desktop(char * const *desktop_list, gsize length); +static bool match_current_desktop(char *const *desktop_list, gsize length); [[nodiscard("memory leaked")]] -struct desktop_vec desktop_vec_create(void) -{ - struct desktop_vec vec = { - .count = 0, - .size = 128, - .buf = xcalloc(128, sizeof(*vec.buf)), - }; - return vec; +struct desktop_vec desktop_vec_create(void) { + struct desktop_vec vec = { + .count = 0, + .size = 128, + .buf = xcalloc(128, sizeof(*vec.buf)), + }; + return vec; } -void desktop_vec_destroy(struct desktop_vec *restrict vec) -{ - for (size_t i = 0; i < vec->count; i++) { - free(vec->buf[i].id); - free(vec->buf[i].name); - free(vec->buf[i].path); - free(vec->buf[i].keywords); - } - free(vec->buf); +void desktop_vec_destroy(struct desktop_vec *restrict vec) { + for (size_t i = 0; i < vec->count; i++) { + free(vec->buf[i].id); + free(vec->buf[i].name); + free(vec->buf[i].path); + free(vec->buf[i].keywords); + } + free(vec->buf); } -void desktop_vec_add( - struct desktop_vec *restrict vec, - const char *restrict id, - const char *restrict name, - const char *restrict path, - const char *restrict keywords) -{ - if (vec->count == vec->size) { - vec->size *= 2; - vec->buf = xrealloc(vec->buf, vec->size * sizeof(vec->buf[0])); - } - vec->buf[vec->count].id = xstrdup(id); - vec->buf[vec->count].name = utf8_normalize(name); - if (vec->buf[vec->count].name == NULL) { - vec->buf[vec->count].name = xstrdup(name); - } - vec->buf[vec->count].path = xstrdup(path); - vec->buf[vec->count].keywords = xstrdup(keywords); - vec->buf[vec->count].search_score = 0; - vec->buf[vec->count].history_score = 0; - vec->count++; +void desktop_vec_add(struct desktop_vec *restrict vec, const char *restrict id, + const char *restrict name, const char *restrict path, + const char *restrict keywords) { + if (vec->count == vec->size) { + vec->size *= 2; + vec->buf = xrealloc(vec->buf, vec->size * sizeof(vec->buf[0])); + } + vec->buf[vec->count].id = xstrdup(id); + vec->buf[vec->count].name = utf8_normalize(name); + if (vec->buf[vec->count].name == NULL) { + vec->buf[vec->count].name = xstrdup(name); + } + vec->buf[vec->count].path = xstrdup(path); + vec->buf[vec->count].keywords = xstrdup(keywords); + vec->buf[vec->count].search_score = 0; + vec->buf[vec->count].history_score = 0; + vec->count++; } -void desktop_vec_add_file(struct desktop_vec *vec, const char *id, const char *path) -{ - GKeyFile *file = g_key_file_new(); - if (!g_key_file_load_from_file(file, path, G_KEY_FILE_NONE, NULL)) { - log_error("Failed to open %s.\n", path); - return; - } +void desktop_vec_add_file(struct desktop_vec *vec, const char *id, + const char *path) { + GKeyFile *file = g_key_file_new(); + if (!g_key_file_load_from_file(file, path, G_KEY_FILE_NONE, NULL)) { + log_error("Failed to open %s.\n", path); + return; + } - const char *group = "Desktop Entry"; + const char *group = "Desktop Entry"; - if (g_key_file_get_boolean(file, group, "Hidden", NULL) - || g_key_file_get_boolean(file, group, "NoDisplay", NULL)) { - goto cleanup_file; - } + if (g_key_file_get_boolean(file, group, "Hidden", NULL) || + g_key_file_get_boolean(file, group, "NoDisplay", NULL)) { + goto cleanup_file; + } - char *name = g_key_file_get_locale_string(file, group, "Name", NULL, NULL); - if (name == NULL) { - log_error("%s: No name found.\n", path); - goto cleanup_file; - } + char *name = g_key_file_get_locale_string(file, group, "Name", NULL, NULL); + if (name == NULL) { + log_error("%s: No name found.\n", path); + goto cleanup_file; + } - /* - * This is really a list rather than a string, but for the purposes of - * matching against user input it's easier to just keep it as a string. - */ - char *keywords = g_key_file_get_locale_string(file, group, "Keywords", NULL, NULL); - if (keywords == NULL) { - keywords = xmalloc(1); - *keywords = '\0'; - } + /* + * This is really a list rather than a string, but for the purposes of + * matching against user input it's easier to just keep it as a string. + */ + char *keywords = + g_key_file_get_locale_string(file, group, "Keywords", NULL, NULL); + if (keywords == NULL) { + keywords = xmalloc(1); + *keywords = '\0'; + } - gsize length; - gchar **list = g_key_file_get_string_list(file, group, "OnlyShowIn", &length, NULL); - if (list) { - bool match = match_current_desktop(list, length); - g_strfreev(list); - list = NULL; - if (!match) { - goto cleanup_all; - } - } + gsize length; + gchar **list = + g_key_file_get_string_list(file, group, "OnlyShowIn", &length, NULL); + if (list) { + bool match = match_current_desktop(list, length); + g_strfreev(list); + list = NULL; + if (!match) { + goto cleanup_all; + } + } - list = g_key_file_get_string_list(file, group, "NotShowIn", &length, NULL); - if (list) { - bool match = match_current_desktop(list, length); - g_strfreev(list); - list = NULL; - if (match) { - goto cleanup_all; - } - } + list = g_key_file_get_string_list(file, group, "NotShowIn", &length, NULL); + if (list) { + bool match = match_current_desktop(list, length); + g_strfreev(list); + list = NULL; + if (match) { + goto cleanup_all; + } + } - desktop_vec_add(vec, id, name, path, keywords); + desktop_vec_add(vec, id, name, path, keywords); cleanup_all: - free(keywords); - free(name); + free(keywords); + free(name); cleanup_file: - g_key_file_unref(file); + g_key_file_unref(file); } -static int cmpdesktopp(const void *restrict a, const void *restrict b) -{ - struct desktop_entry *restrict d1 = (struct desktop_entry *)a; - struct desktop_entry *restrict d2 = (struct desktop_entry *)b; - return strcmp(d1->name, d2->name); +static int cmpdesktopp(const void *restrict a, const void *restrict b) { + struct desktop_entry *restrict d1 = (struct desktop_entry *)a; + struct desktop_entry *restrict d2 = (struct desktop_entry *)b; + return strcmp(d1->name, d2->name); } -static int cmpscorep(const void *restrict a, const void *restrict b) -{ - struct scored_string *restrict str1 = (struct scored_string *)a; - struct scored_string *restrict str2 = (struct scored_string *)b; +static int cmpscorep(const void *restrict a, const void *restrict b) { + struct scored_string *restrict str1 = (struct scored_string *)a; + struct scored_string *restrict str2 = (struct scored_string *)b; - int hist_diff = str2->history_score - str1->history_score; - int search_diff = str2->search_score - str1->search_score; - return hist_diff + search_diff; + int hist_diff = str2->history_score - str1->history_score; + int search_diff = str2->search_score - str1->search_score; + return hist_diff + search_diff; } -void desktop_vec_sort(struct desktop_vec *restrict vec) -{ - qsort(vec->buf, vec->count, sizeof(vec->buf[0]), cmpdesktopp); +void desktop_vec_sort(struct desktop_vec *restrict vec) { + qsort(vec->buf, vec->count, sizeof(vec->buf[0]), cmpdesktopp); } -struct desktop_entry *desktop_vec_find_sorted(struct desktop_vec *restrict vec, const char *name) -{ - /* - * Explicitly cast away const-ness, as even though we won't modify the - * name, the compiler rightly complains that we might. - */ - struct desktop_entry tmp = { .name = (char *)name }; - return bsearch(&tmp, vec->buf, vec->count, sizeof(vec->buf[0]), cmpdesktopp); +struct desktop_entry *desktop_vec_find_sorted(struct desktop_vec *restrict vec, + const char *name) { + /* + * Explicitly cast away const-ness, as even though we won't modify the + * name, the compiler rightly complains that we might. + */ + struct desktop_entry tmp = {.name = (char *)name}; + return bsearch(&tmp, vec->buf, vec->count, sizeof(vec->buf[0]), cmpdesktopp); } -struct string_ref_vec desktop_vec_filter( - const struct desktop_vec *restrict vec, - const char *restrict substr, - enum matching_algorithm algorithm) -{ - struct string_ref_vec filt = string_ref_vec_create(); - for (size_t i = 0; i < vec->count; i++) { - int32_t search_score; - search_score = match_words(algorithm, substr, vec->buf[i].name); - if (search_score != INT32_MIN) { - string_ref_vec_add(&filt, vec->buf[i].name); - /* Store the score of the match for later sorting. */ - filt.buf[filt.count - 1].search_score = search_score; - filt.buf[filt.count - 1].history_score = vec->buf[i].history_score; - } else { - /* If we didn't match the name, check the keywords. */ - search_score = match_words(algorithm, substr, vec->buf[i].keywords); - if (search_score != INT32_MIN) { - string_ref_vec_add(&filt, vec->buf[i].name); - /* - * Arbitrary score addition to make name - * matches preferred over keyword matches. - */ - filt.buf[filt.count - 1].search_score = search_score - 20; - filt.buf[filt.count - 1].history_score = vec->buf[i].history_score; - } - } - } - /* - * Sort the results by this search_score. This moves matches at the beginnings - * of words to the front of the result list. - */ - qsort(filt.buf, filt.count, sizeof(filt.buf[0]), cmpscorep); - return filt; +struct string_ref_vec desktop_vec_filter(struct tofi *tofi, + const struct desktop_vec *restrict vec, + const char *restrict substr, + enum matching_algorithm algorithm) { + struct string_ref_vec filt = string_ref_vec_create(); + for (size_t i = 0; i < vec->count; i++) { + int32_t search_score; + search_score = match_words(algorithm, substr, vec->buf[i].name); + if (search_score != INT32_MIN) { + string_ref_vec_add(&filt, vec->buf[i].name); + /* Store the score of the match for later sorting. */ + filt.buf[filt.count - 1].search_score = search_score; + filt.buf[filt.count - 1].history_score = vec->buf[i].history_score; + } else { + /* If we didn't match the name, check the keywords. */ + search_score = match_words(algorithm, substr, vec->buf[i].keywords); + if (search_score != INT32_MIN) { + string_ref_vec_add(&filt, vec->buf[i].name); + /* + * Arbitrary score addition to make name + * matches preferred over keyword matches. + */ + filt.buf[filt.count - 1].search_score = search_score - 20; + filt.buf[filt.count - 1].history_score = vec->buf[i].history_score; + } + } + } + /* + * Sort the results by this search_score. This moves matches at the beginnings + * of words to the front of the result list. + */ + qsort(filt.buf, filt.count, sizeof(filt.buf[0]), cmpscorep); + // use input to calculate if no match is found + if (filt.count == 0) { + add_module_suggestions(tofi, substr, &filt); + } + return filt; } -struct desktop_vec desktop_vec_load(FILE *file) -{ - struct desktop_vec vec = desktop_vec_create(); - if (file == NULL) { - return vec; - } +struct desktop_vec desktop_vec_load(FILE *file) { + struct desktop_vec vec = desktop_vec_create(); + if (file == NULL) { + return vec; + } - ssize_t bytes_read; - char *line = NULL; - size_t len; - while ((bytes_read = getline(&line, &len, file)) != -1) { - if (line[bytes_read - 1] == '\n') { - line[bytes_read - 1] = '\0'; - } - char *id = line; - size_t sublen = strlen(line); - char *name = &line[sublen + 1]; - sublen = strlen(name); - char *path = &name[sublen + 1]; - sublen = strlen(path); - char *keywords = &path[sublen + 1]; - desktop_vec_add(&vec, id, name, path, keywords); - } - free(line); + ssize_t bytes_read; + char *line = NULL; + size_t len; + while ((bytes_read = getline(&line, &len, file)) != -1) { + if (line[bytes_read - 1] == '\n') { + line[bytes_read - 1] = '\0'; + } + char *id = line; + size_t sublen = strlen(line); + char *name = &line[sublen + 1]; + sublen = strlen(name); + char *path = &name[sublen + 1]; + sublen = strlen(path); + char *keywords = &path[sublen + 1]; + desktop_vec_add(&vec, id, name, path, keywords); + } + free(line); - return vec; + return vec; } -void desktop_vec_save(struct desktop_vec *restrict vec, FILE *restrict file) -{ - /* - * Using null bytes for field separators is a bit odd, but it makes - * parsing very quick and easy. - */ - for (size_t i = 0; i < vec->count; i++) { - fputs(vec->buf[i].id, file); - fputc('\0', file); - fputs(vec->buf[i].name, file); - fputc('\0', file); - fputs(vec->buf[i].path, file); - fputc('\0', file); - fputs(vec->buf[i].keywords, file); - fputc('\n', file); - } +void desktop_vec_save(struct desktop_vec *restrict vec, FILE *restrict file) { + /* + * Using null bytes for field separators is a bit odd, but it makes + * parsing very quick and easy. + */ + for (size_t i = 0; i < vec->count; i++) { + fputs(vec->buf[i].id, file); + fputc('\0', file); + fputs(vec->buf[i].name, file); + fputc('\0', file); + fputs(vec->buf[i].path, file); + fputc('\0', file); + fputs(vec->buf[i].keywords, file); + fputc('\n', file); + } } -bool match_current_desktop(char * const *desktop_list, gsize length) -{ - const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP"); - if (xdg_current_desktop == NULL) { - return false; - } +bool match_current_desktop(char *const *desktop_list, gsize length) { + const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP"); + if (xdg_current_desktop == NULL) { + return false; + } - struct string_vec desktops = string_vec_create(); + struct string_vec desktops = string_vec_create(); - char *saveptr = NULL; - char *tmp = xstrdup(xdg_current_desktop); - char *desktop = strtok_r(tmp, ":", &saveptr); - while (desktop != NULL) { - string_vec_add(&desktops, desktop); - desktop = strtok_r(NULL, ":", &saveptr); - } + char *saveptr = NULL; + char *tmp = xstrdup(xdg_current_desktop); + char *desktop = strtok_r(tmp, ":", &saveptr); + while (desktop != NULL) { + string_vec_add(&desktops, desktop); + desktop = strtok_r(NULL, ":", &saveptr); + } - string_vec_sort(&desktops); - for (gsize i = 0; i < length; i++) { - if (string_vec_find_sorted(&desktops, desktop_list[i])) { - return true; - } - } + string_vec_sort(&desktops); + for (gsize i = 0; i < length; i++) { + if (string_vec_find_sorted(&desktops, desktop_list[i])) { + return true; + } + } - string_vec_destroy(&desktops); - free(tmp); - return false; + string_vec_destroy(&desktops); + free(tmp); + return false; } /* @@ -318,9 +317,9 @@ bool match_current_desktop(char * const *desktop_list, gsize length) // if (xdg_current_desktop == NULL) { // return false; // } -// +// // struct string_vec desktops = string_vec_create(); -// +// // char *saveptr = NULL; // char *tmp = xstrdup(xdg_current_desktop); // char *desktop = strtok_r(tmp, ":", &saveptr); @@ -329,7 +328,7 @@ bool match_current_desktop(char * const *desktop_list, gsize length) // desktop = strtok_r(NULL, ":", &saveptr); // } // free(tmp); -// +// // /* // * Technically this will fail if the desktop list contains an escaped // * \;, but I don't know of any desktops with semicolons in their names. @@ -344,33 +343,33 @@ bool match_current_desktop(char * const *desktop_list, gsize length) // desktop = strtok_r(NULL, ";", &saveptr); // } // free(tmp); -// +// // string_vec_destroy(&desktops); // return false; // } -// -// static void desktop_vec_add_file2(struct desktop_vec *desktop, const char *id, const char *path) +// +// static void desktop_vec_add_file2(struct desktop_vec *desktop, const char +// *id, const char *path) // { // FILE *file = fopen(path, "rb"); // if (!file) { // log_error("Failed to open %s.\n", path); // return; // } -// +// // char *line = NULL; // size_t len; // bool found = false; // while(getline(&line, &len, file) > 0) { -// if (!strncmp(line, "[Desktop Entry]", strlen("[Desktop Entry]"))) { -// found = true; -// break; +// if (!strncmp(line, "[Desktop Entry]", strlen("[Desktop +// Entry]"))) { found = true; break; // } // } // if (!found) { // log_error("%s: No [Desktop Entry] section found.\n", path); // goto cleanup_file; // } -// +// // /* Please forgive the macro usage. */ // #define OPTION(key) (!strncmp(line, (key), strlen((key)))) // char *name = NULL; @@ -423,9 +422,9 @@ bool match_current_desktop(char * const *desktop_list, gsize length) // log_error("%s: Malformed name key.\n", path); // goto cleanup_file; // } -// +// // desktop_vec_add(desktop, id, name, path); -// +// // cleanup_name: // free(name); // cleanup_file: diff --git a/src/desktop_vec.h b/src/desktop_vec.h index cdc0d6c..9c2c703 100644 --- a/src/desktop_vec.h +++ b/src/desktop_vec.h @@ -1,47 +1,48 @@ #ifndef DESKTOP_VEC_H #define DESKTOP_VEC_H +#include "matching.h" #include #include -#include #include -#include "matching.h" +#include struct desktop_entry { - char *id; - char *name; - char *path; - char *keywords; - uint32_t search_score; - uint32_t history_score; + char *id; + char *name; + char *path; + char *keywords; + uint32_t search_score; + uint32_t history_score; }; struct desktop_vec { - size_t count; - size_t size; - struct desktop_entry *buf; + size_t count; + size_t size; + struct desktop_entry *buf; }; [[nodiscard("memory leaked")]] struct desktop_vec desktop_vec_create(void); void desktop_vec_destroy(struct desktop_vec *restrict vec); -void desktop_vec_add( - struct desktop_vec *restrict vec, - const char *restrict id, - const char *restrict name, - const char *restrict path, - const char *restrict keywords); -void desktop_vec_add_file(struct desktop_vec *desktop, const char *id, const char *path); +void desktop_vec_add(struct desktop_vec *restrict vec, const char *restrict id, + const char *restrict name, const char *restrict path, + const char *restrict keywords); +void desktop_vec_add_file(struct desktop_vec *desktop, const char *id, + const char *path); void desktop_vec_sort(struct desktop_vec *restrict vec); -struct desktop_entry *desktop_vec_find_sorted(struct desktop_vec *restrict vec, const char *name); -struct string_ref_vec desktop_vec_filter( - const struct desktop_vec *restrict vec, - const char *restrict substr, - enum matching_algorithm algorithm); +struct desktop_entry *desktop_vec_find_sorted(struct desktop_vec *restrict vec, + const char *name); + +struct tofi; + +struct string_ref_vec desktop_vec_filter(struct tofi *tofi, + const struct desktop_vec *restrict vec, + const char *restrict substr, + enum matching_algorithm algorithm); struct desktop_vec desktop_vec_load(FILE *file); void desktop_vec_save(struct desktop_vec *restrict vec, FILE *restrict file); - #endif /* DESKTOP_VEC_H */ diff --git a/src/input.c b/src/input.c index 8b8f0a7..3e1aba4 100644 --- a/src/input.c +++ b/src/input.c @@ -1,17 +1,17 @@ -#include -#include -#include -#include #include "input.h" #include "log.h" #include "nelem.h" #include "tofi.h" #include "unicode.h" - +#include +#include +#include +#include static uint32_t keysym_to_key(xkb_keysym_t sym); static void add_character(struct tofi *tofi, xkb_keycode_t keycode); -static void delete_character(struct tofi *tofi); +static void delete_prev_character(struct tofi *tofi); +static void delete_next_character(struct tofi *tofi); static void delete_word(struct tofi *tofi); static void clear_input(struct tofi *tofi); static void paste(struct tofi *tofi); @@ -23,400 +23,409 @@ static void next_cursor_or_result(struct tofi *tofi); static void previous_cursor_or_result(struct tofi *tofi); static void reset_selection(struct tofi *tofi); -void input_handle_keypress(struct tofi *tofi, xkb_keycode_t keycode) -{ - if (tofi->xkb_state == NULL) { - return; - } - - bool ctrl = xkb_state_mod_name_is_active( - tofi->xkb_state, - XKB_MOD_NAME_CTRL, - XKB_STATE_MODS_EFFECTIVE); - bool alt = xkb_state_mod_name_is_active( - tofi->xkb_state, - XKB_MOD_NAME_ALT, - XKB_STATE_MODS_EFFECTIVE); - bool shift = xkb_state_mod_name_is_active( - tofi->xkb_state, - XKB_MOD_NAME_SHIFT, - XKB_STATE_MODS_EFFECTIVE); - - uint32_t ch = xkb_state_key_get_utf32(tofi->xkb_state, keycode); - - /* - * Use physical key code for shortcuts by default, ignoring layout - * changes. Linux keycodes are 8 less than XKB keycodes. - */ - uint32_t key = keycode - 8; - if (!tofi->physical_keybindings) { - xkb_keysym_t sym = xkb_state_key_get_one_sym(tofi->xkb_state, keycode); - key = keysym_to_key(sym); - } - - /* - * Alt does not affect which character is selected, so we have to check - * for it explicitly. - */ - if (utf32_isprint(ch) && !ctrl && !alt) { - add_character(tofi, keycode); - } else if ((key == KEY_BACKSPACE || key == KEY_W) && ctrl) { - delete_word(tofi); - } else if (key == KEY_BACKSPACE - || (key == KEY_H && ctrl)) { - delete_character(tofi); - } else if (key == KEY_U && ctrl) { - clear_input(tofi); - } else if (key == KEY_V && ctrl) { - paste(tofi); - } else if (key == KEY_LEFT) { - previous_cursor_or_result(tofi); - } else if (key == KEY_RIGHT) { - next_cursor_or_result(tofi); - } else if (key == KEY_UP - || key == KEY_LEFT - || (key == KEY_TAB && shift) - || (key == KEY_H && alt) - || ((key == KEY_K || key == KEY_P || key == KEY_B) && (ctrl || alt))) { - select_previous_result(tofi); - } else if (key == KEY_DOWN - || key == KEY_RIGHT - || key == KEY_TAB - || (key == KEY_L && alt) - || ((key == KEY_J || key == KEY_N || key == KEY_F) && (ctrl || alt))) { - select_next_result(tofi); - } else if (key == KEY_HOME) { - reset_selection(tofi); - } else if (key == KEY_PAGEUP) { - select_previous_page(tofi); - } else if (key == KEY_PAGEDOWN) { - select_next_page(tofi); - } else if (key == KEY_ESC - || ((key == KEY_C || key == KEY_LEFTBRACE || key == KEY_G) && ctrl)) { - tofi->closed = true; - return; - } else if (key == KEY_ENTER - || key == KEY_KPENTER - || (key == KEY_M && ctrl)) { - tofi->submit = true; - return; - } - - if (tofi->auto_accept_single && tofi->window.entry.results.count == 1) { - tofi->submit = true; - } - - tofi->window.surface.redraw = true; +void input_handle_keypress(struct tofi *tofi, xkb_keycode_t keycode) { + if (tofi->xkb_state == NULL) { + return; + } + + bool ctrl = xkb_state_mod_name_is_active(tofi->xkb_state, XKB_MOD_NAME_CTRL, + XKB_STATE_MODS_EFFECTIVE); + bool alt = xkb_state_mod_name_is_active(tofi->xkb_state, XKB_MOD_NAME_ALT, + XKB_STATE_MODS_EFFECTIVE); + bool shift = xkb_state_mod_name_is_active(tofi->xkb_state, XKB_MOD_NAME_SHIFT, + XKB_STATE_MODS_EFFECTIVE); + + uint32_t ch = xkb_state_key_get_utf32(tofi->xkb_state, keycode); + + /* + * Use physical key code for shortcuts by default, ignoring layout + * changes. Linux keycodes are 8 less than XKB keycodes. + */ + uint32_t key = keycode - 8; + if (!tofi->physical_keybindings) { + xkb_keysym_t sym = xkb_state_key_get_one_sym(tofi->xkb_state, keycode); + key = keysym_to_key(sym); + } + + /* + * Alt does not affect which character is selected, so we have to check + * for it explicitly. + */ + if (utf32_isprint(ch) && !ctrl && !alt) { + add_character(tofi, keycode); + } else if ((key == KEY_BACKSPACE || key == KEY_W) && ctrl) { + delete_word(tofi); + } else if (key == KEY_BACKSPACE || (key == KEY_H && ctrl)) { + delete_prev_character(tofi); + } else if (key == KEY_DELETE) { + delete_next_character(tofi); + } else if (key == KEY_U && ctrl) { + clear_input(tofi); + } else if (key == KEY_V && ctrl) { + paste(tofi); + } else if (key == KEY_LEFT) { + previous_cursor_or_result(tofi); + } else if (key == KEY_RIGHT) { + next_cursor_or_result(tofi); + } else if (key == KEY_UP || key == KEY_LEFT || (key == KEY_TAB && shift) || + (key == KEY_H && alt) || + ((key == KEY_K || key == KEY_P || key == KEY_B) && + (ctrl || alt))) { + select_previous_result(tofi); + } else if (key == KEY_DOWN || key == KEY_RIGHT || key == KEY_TAB || + (key == KEY_L && alt) || + ((key == KEY_J || key == KEY_N || key == KEY_F) && + (ctrl || alt))) { + select_next_result(tofi); + } else if (key == KEY_HOME) { + reset_selection(tofi); + } else if (key == KEY_PAGEUP) { + select_previous_page(tofi); + } else if (key == KEY_PAGEDOWN) { + select_next_page(tofi); + } else if (key == KEY_ESC || + ((key == KEY_C || key == KEY_LEFTBRACE || key == KEY_G) && ctrl)) { + tofi->closed = true; + return; + } else if (key == KEY_ENTER || key == KEY_KPENTER || (key == KEY_M && ctrl)) { + tofi->submit = true; + return; + } + + if (tofi->auto_accept_single && tofi->window.entry.results.count == 1) { + tofi->submit = true; + } + + tofi->window.surface.redraw = true; +} + +static uint32_t keysym_to_key(xkb_keysym_t sym) { + switch (sym) { + case XKB_KEY_BackSpace: + return KEY_BACKSPACE; + case XKB_KEY_Delete: + return KEY_DELETE; + case XKB_KEY_w: + return KEY_W; + case XKB_KEY_u: + return KEY_U; + case XKB_KEY_v: + return KEY_V; + case XKB_KEY_Left: + return KEY_LEFT; + case XKB_KEY_Right: + return KEY_RIGHT; + case XKB_KEY_Up: + return KEY_UP; + case XKB_KEY_ISO_Left_Tab: + return KEY_TAB; + case XKB_KEY_h: + return KEY_H; + case XKB_KEY_k: + return KEY_K; + case XKB_KEY_p: + return KEY_P; + case XKB_KEY_Down: + return KEY_DOWN; + case XKB_KEY_Tab: + return KEY_TAB; + case XKB_KEY_l: + return KEY_L; + case XKB_KEY_j: + return KEY_J; + case XKB_KEY_n: + return KEY_N; + case XKB_KEY_Home: + return KEY_HOME; + case XKB_KEY_Page_Up: + return KEY_PAGEUP; + case XKB_KEY_Page_Down: + return KEY_PAGEDOWN; + case XKB_KEY_Escape: + return KEY_ESC; + case XKB_KEY_c: + return KEY_C; + case XKB_KEY_bracketleft: + return KEY_LEFTBRACE; + case XKB_KEY_Return: + return KEY_ENTER; + case XKB_KEY_KP_Enter: + return KEY_KPENTER; + case XKB_KEY_m: + return KEY_M; + } + return (uint32_t)-1; } -static uint32_t keysym_to_key(xkb_keysym_t sym) -{ - switch (sym) { - case XKB_KEY_BackSpace: - return KEY_BACKSPACE; - case XKB_KEY_w: - return KEY_W; - case XKB_KEY_u: - return KEY_U; - case XKB_KEY_v: - return KEY_V; - case XKB_KEY_Left: - return KEY_LEFT; - case XKB_KEY_Right: - return KEY_RIGHT; - case XKB_KEY_Up: - return KEY_UP; - case XKB_KEY_ISO_Left_Tab: - return KEY_TAB; - case XKB_KEY_h: - return KEY_H; - case XKB_KEY_k: - return KEY_K; - case XKB_KEY_p: - return KEY_P; - case XKB_KEY_Down: - return KEY_DOWN; - case XKB_KEY_Tab: - return KEY_TAB; - case XKB_KEY_l: - return KEY_L; - case XKB_KEY_j: - return KEY_J; - case XKB_KEY_n: - return KEY_N; - case XKB_KEY_Home: - return KEY_HOME; - case XKB_KEY_Page_Up: - return KEY_PAGEUP; - case XKB_KEY_Page_Down: - return KEY_PAGEDOWN; - case XKB_KEY_Escape: - return KEY_ESC; - case XKB_KEY_c: - return KEY_C; - case XKB_KEY_bracketleft: - return KEY_LEFTBRACE; - case XKB_KEY_Return: - return KEY_ENTER; - case XKB_KEY_KP_Enter: - return KEY_KPENTER; - case XKB_KEY_m: - return KEY_M; - } - return (uint32_t)-1; +void reset_selection(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + entry->selection = 0; + entry->first_result = 0; } -void reset_selection(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - entry->selection = 0; - entry->first_result = 0; +void add_character(struct tofi *tofi, xkb_keycode_t keycode) { + struct entry *entry = &tofi->window.entry; + + if (entry->input_utf32_length >= N_ELEM(entry->input_utf32) - 1) { + /* No more room for input */ + return; + } + + char buf[5]; /* 4 UTF-8 bytes plus null terminator. */ + int len = xkb_state_key_get_utf8(tofi->xkb_state, keycode, buf, sizeof(buf)); + if (entry->cursor_position == entry->input_utf32_length) { + entry->input_utf32[entry->input_utf32_length] = utf8_to_utf32(buf); + entry->input_utf32_length++; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + memcpy(&entry->input_utf8[entry->input_utf8_length], buf, N_ELEM(buf)); + entry->input_utf8_length += len; + + if (entry->mode == TOFI_MODE_DRUN) { + struct string_ref_vec results = desktop_vec_filter( + tofi, &entry->apps, entry->input_utf8, tofi->matching_algorithm); + string_ref_vec_destroy(&entry->results); + entry->results = results; + } else { + struct string_ref_vec tmp = entry->results; + entry->results = string_ref_vec_filter(&entry->results, entry->input_utf8, + tofi->matching_algorithm); + string_ref_vec_destroy(&tmp); + } + + reset_selection(tofi); + } else { + for (size_t i = entry->input_utf32_length; i > entry->cursor_position; + i--) { + entry->input_utf32[i] = entry->input_utf32[i - 1]; + } + entry->input_utf32[entry->cursor_position] = utf8_to_utf32(buf); + entry->input_utf32_length++; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + + input_refresh_results(tofi); + } + + entry->cursor_position++; } -void add_character(struct tofi *tofi, xkb_keycode_t keycode) -{ - struct entry *entry = &tofi->window.entry; - - if (entry->input_utf32_length >= N_ELEM(entry->input_utf32) - 1) { - /* No more room for input */ - return; - } - - char buf[5]; /* 4 UTF-8 bytes plus null terminator. */ - int len = xkb_state_key_get_utf8( - tofi->xkb_state, - keycode, - buf, - sizeof(buf)); - if (entry->cursor_position == entry->input_utf32_length) { - entry->input_utf32[entry->input_utf32_length] = utf8_to_utf32(buf); - entry->input_utf32_length++; - entry->input_utf32[entry->input_utf32_length] = U'\0'; - memcpy(&entry->input_utf8[entry->input_utf8_length], - buf, - N_ELEM(buf)); - entry->input_utf8_length += len; - - if (entry->mode == TOFI_MODE_DRUN) { - struct string_ref_vec results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->matching_algorithm); - string_ref_vec_destroy(&entry->results); - entry->results = results; - } else { - struct string_ref_vec tmp = entry->results; - entry->results = string_ref_vec_filter(&entry->results, entry->input_utf8, tofi->matching_algorithm); - string_ref_vec_destroy(&tmp); - } - - reset_selection(tofi); - } else { - for (size_t i = entry->input_utf32_length; i > entry->cursor_position; i--) { - entry->input_utf32[i] = entry->input_utf32[i - 1]; - } - entry->input_utf32[entry->cursor_position] = utf8_to_utf32(buf); - entry->input_utf32_length++; - entry->input_utf32[entry->input_utf32_length] = U'\0'; - - input_refresh_results(tofi); - } - - entry->cursor_position++; +void input_refresh_results(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + size_t bytes_written = 0; + for (size_t i = 0; i < entry->input_utf32_length; i++) { + bytes_written += + utf32_to_utf8(entry->input_utf32[i], &entry->input_utf8[bytes_written]); + } + entry->input_utf8[bytes_written] = '\0'; + entry->input_utf8_length = bytes_written; + string_ref_vec_destroy(&entry->results); + if (entry->mode == TOFI_MODE_DRUN) { + entry->results = desktop_vec_filter(tofi, &entry->apps, entry->input_utf8, + tofi->matching_algorithm); + } else { + entry->results = string_ref_vec_filter(&entry->commands, entry->input_utf8, + tofi->matching_algorithm); + } + + reset_selection(tofi); } -void input_refresh_results(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - size_t bytes_written = 0; - for (size_t i = 0; i < entry->input_utf32_length; i++) { - bytes_written += utf32_to_utf8( - entry->input_utf32[i], - &entry->input_utf8[bytes_written]); - } - entry->input_utf8[bytes_written] = '\0'; - entry->input_utf8_length = bytes_written; - string_ref_vec_destroy(&entry->results); - if (entry->mode == TOFI_MODE_DRUN) { - entry->results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->matching_algorithm); - } else { - entry->results = string_ref_vec_filter(&entry->commands, entry->input_utf8, tofi->matching_algorithm); - } - - reset_selection(tofi); +void delete_prev_character(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + if (entry->input_utf32_length == 0) { + /* No input to delete. */ + return; + } + + if (entry->cursor_position == 0) { + return; + } else if (entry->cursor_position == entry->input_utf32_length) { + entry->cursor_position--; + entry->input_utf32_length--; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + } else { + for (size_t i = entry->cursor_position - 1; + i < entry->input_utf32_length - 1; i++) { + entry->input_utf32[i] = entry->input_utf32[i + 1]; + } + entry->cursor_position--; + entry->input_utf32_length--; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + } + + input_refresh_results(tofi); } -void delete_character(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - if (entry->input_utf32_length == 0) { - /* No input to delete. */ - return; - } - - if (entry->cursor_position == 0) { - return; - } else if (entry->cursor_position == entry->input_utf32_length) { - entry->cursor_position--; - entry->input_utf32_length--; - entry->input_utf32[entry->input_utf32_length] = U'\0'; - } else { - for (size_t i = entry->cursor_position - 1; i < entry->input_utf32_length - 1; i++) { - entry->input_utf32[i] = entry->input_utf32[i + 1]; - } - entry->cursor_position--; - entry->input_utf32_length--; - entry->input_utf32[entry->input_utf32_length] = U'\0'; - } - - input_refresh_results(tofi); +void delete_next_character(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + if (entry->input_utf32_length == 0) { + /* No input to delete. */ + return; + } + + if (entry->cursor_position == entry->input_utf32_length) { + return; + } else if (entry->cursor_position == entry->input_utf32_length) { + // entry->cursor_position--; + entry->input_utf32_length++; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + } else { + for (size_t i = entry->cursor_position + 1; + i < entry->input_utf32_length - 1; i++) { + entry->input_utf32[i] = entry->input_utf32[i + 1]; + } + // entry->cursor_position--; + entry->input_utf32_length--; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + } + + input_refresh_results(tofi); } -void delete_word(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - if (entry->cursor_position == 0) { - /* No input to delete. */ - return; - } - - uint32_t new_cursor_pos = entry->cursor_position; - while (new_cursor_pos > 0 && utf32_isspace(entry->input_utf32[new_cursor_pos - 1])) { - new_cursor_pos--; - } - while (new_cursor_pos > 0 && !utf32_isspace(entry->input_utf32[new_cursor_pos - 1])) { - new_cursor_pos--; - } - uint32_t new_length = entry->input_utf32_length - (entry->cursor_position - new_cursor_pos); - for (size_t i = 0; i < new_length; i++) { - entry->input_utf32[new_cursor_pos + i] = entry->input_utf32[entry->cursor_position + i]; - } - entry->input_utf32_length = new_length; - entry->input_utf32[entry->input_utf32_length] = U'\0'; - - entry->cursor_position = new_cursor_pos; - input_refresh_results(tofi); +void delete_word(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + if (entry->cursor_position == 0) { + /* No input to delete. */ + return; + } + + uint32_t new_cursor_pos = entry->cursor_position; + while (new_cursor_pos > 0 && + utf32_isspace(entry->input_utf32[new_cursor_pos - 1])) { + new_cursor_pos--; + } + while (new_cursor_pos > 0 && + !utf32_isspace(entry->input_utf32[new_cursor_pos - 1])) { + new_cursor_pos--; + } + uint32_t new_length = + entry->input_utf32_length - (entry->cursor_position - new_cursor_pos); + for (size_t i = 0; i < new_length; i++) { + entry->input_utf32[new_cursor_pos + i] = + entry->input_utf32[entry->cursor_position + i]; + } + entry->input_utf32_length = new_length; + entry->input_utf32[entry->input_utf32_length] = U'\0'; + + entry->cursor_position = new_cursor_pos; + input_refresh_results(tofi); } -void clear_input(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; +void clear_input(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; - entry->cursor_position = 0; - entry->input_utf32_length = 0; - entry->input_utf32[0] = U'\0'; + entry->cursor_position = 0; + entry->input_utf32_length = 0; + entry->input_utf32[0] = U'\0'; - input_refresh_results(tofi); + input_refresh_results(tofi); } -void paste(struct tofi *tofi) -{ - if (tofi->clipboard.wl_data_offer == NULL || tofi->clipboard.mime_type == NULL) { - return; - } - - /* - * Create a pipe, and give the write end to the compositor to give to - * the clipboard manager. - */ - errno = 0; - int fildes[2]; - if (pipe2(fildes, O_CLOEXEC | O_NONBLOCK) == -1) { - log_error("Failed to open pipe for clipboard: %s\n", strerror(errno)); - return; - } - wl_data_offer_receive(tofi->clipboard.wl_data_offer, tofi->clipboard.mime_type, fildes[1]); - close(fildes[1]); - - /* Keep the read end for reading in the main loop. */ - tofi->clipboard.fd = fildes[0]; +void paste(struct tofi *tofi) { + if (tofi->clipboard.wl_data_offer == NULL || + tofi->clipboard.mime_type == NULL) { + return; + } + + /* + * Create a pipe, and give the write end to the compositor to give to + * the clipboard manager. + */ + errno = 0; + int fildes[2]; + if (pipe2(fildes, O_CLOEXEC | O_NONBLOCK) == -1) { + log_error("Failed to open pipe for clipboard: %s\n", strerror(errno)); + return; + } + wl_data_offer_receive(tofi->clipboard.wl_data_offer, + tofi->clipboard.mime_type, fildes[1]); + close(fildes[1]); + + /* Keep the read end for reading in the main loop. */ + tofi->clipboard.fd = fildes[0]; } -void select_previous_result(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; +void select_previous_result(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; - if (entry->selection > 0) { - entry->selection--; - return; - } + if (entry->selection > 0) { + entry->selection--; + return; + } - uint32_t nsel = MAX(MIN(entry->num_results_drawn, entry->results.count), 1); + uint32_t nsel = MAX(MIN(entry->num_results_drawn, entry->results.count), 1); - if (entry->first_result > nsel) { - entry->first_result -= entry->last_num_results_drawn; - entry->selection = entry->last_num_results_drawn - 1; - } else if (entry->first_result > 0) { - entry->selection = entry->first_result - 1; - entry->first_result = 0; - } + if (entry->first_result > nsel) { + entry->first_result -= entry->last_num_results_drawn; + entry->selection = entry->last_num_results_drawn - 1; + } else if (entry->first_result > 0) { + entry->selection = entry->first_result - 1; + entry->first_result = 0; + } } -void select_next_result(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - uint32_t nsel = MAX(MIN(entry->num_results_drawn, entry->results.count), 1); - - entry->selection++; - if (entry->selection >= nsel) { - entry->selection -= nsel; - if (entry->results.count > 0) { - entry->first_result += nsel; - entry->first_result %= entry->results.count; - } else { - entry->first_result = 0; - } - entry->last_num_results_drawn = entry->num_results_drawn; - } +void select_next_result(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + uint32_t nsel = MAX(MIN(entry->num_results_drawn, entry->results.count), 1); + + entry->selection++; + if (entry->selection >= nsel) { + entry->selection -= nsel; + if (entry->results.count > 0) { + entry->first_result += nsel; + entry->first_result %= entry->results.count; + } else { + entry->first_result = 0; + } + entry->last_num_results_drawn = entry->num_results_drawn; + } } -void previous_cursor_or_result(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - if (entry->cursor_theme.show - && entry->selection == 0 - && entry->cursor_position > 0) { - entry->cursor_position--; - } else { - select_previous_result(tofi); - } +void previous_cursor_or_result(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + if (entry->cursor_theme.show && entry->selection == 0 && + entry->cursor_position > 0) { + entry->cursor_position--; + } else { + select_previous_result(tofi); + } } -void next_cursor_or_result(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; +void next_cursor_or_result(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; - if (entry->cursor_theme.show - && entry->cursor_position < entry->input_utf32_length) { - entry->cursor_position++; - } else { - select_next_result(tofi); - } + if (entry->cursor_theme.show && + entry->cursor_position < entry->input_utf32_length) { + entry->cursor_position++; + } else { + select_next_result(tofi); + } } -void select_previous_page(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - if (entry->first_result >= entry->last_num_results_drawn) { - entry->first_result -= entry->last_num_results_drawn; - } else { - entry->first_result = 0; - } - entry->selection = 0; - entry->last_num_results_drawn = entry->num_results_drawn; +void select_previous_page(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + if (entry->first_result >= entry->last_num_results_drawn) { + entry->first_result -= entry->last_num_results_drawn; + } else { + entry->first_result = 0; + } + entry->selection = 0; + entry->last_num_results_drawn = entry->num_results_drawn; } -void select_next_page(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; +void select_next_page(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; - entry->first_result += entry->num_results_drawn; - if (entry->first_result >= entry->results.count) { - entry->first_result = 0; - } - entry->selection = 0; - entry->last_num_results_drawn = entry->num_results_drawn; + entry->first_result += entry->num_results_drawn; + if (entry->first_result >= entry->results.count) { + entry->first_result = 0; + } + entry->selection = 0; + entry->last_num_results_drawn = entry->num_results_drawn; } diff --git a/src/main.c b/src/main.c index e91838f..171472e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,20 @@ +#include "compgen.h" +#include "config.h" +#include "drun.h" +#include "entry.h" +#include "input.h" +#include "lock.h" +#include "log.h" +#include "modules.h" +#include "nelem.h" +#include "scale.h" +#include "shm.h" +#include "src/clipboard.h" +#include "string_vec.h" +#include "tofi.h" +#include "unicode.h" +#include "viewporter.h" +#include "xmalloc.h" #include #include #include @@ -13,22 +30,6 @@ #include #include #include -#include "tofi.h" -#include "compgen.h" -#include "drun.h" -#include "config.h" -#include "entry.h" -#include "input.h" -#include "log.h" -#include "nelem.h" -#include "lock.h" -#include "scale.h" -#include "shm.h" -#include "string_vec.h" -#include "string_vec.h" -#include "unicode.h" -#include "viewporter.h" -#include "xmalloc.h" #undef MAX #undef MIN @@ -39,1890 +40,1620 @@ static const char *mime_type_text_plain = "text/plain"; static const char *mime_type_text_plain_utf8 = "text/plain;charset=utf-8"; static uint32_t gettime_ms() { - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); - uint32_t ms = t.tv_sec * 1000; - ms += t.tv_nsec / 1000000; - return ms; + uint32_t ms = t.tv_sec * 1000; + ms += t.tv_nsec / 1000000; + return ms; } - /* Read all of stdin into a buffer. */ static char *read_stdin(bool normalize) { - const size_t block_size = BUFSIZ; - size_t num_blocks = 1; - size_t buf_size = block_size; - - char *buf = xmalloc(buf_size); - for (size_t block = 0; ; block++) { - if (block == num_blocks) { - num_blocks *= 2; - buf = xrealloc(buf, num_blocks * block_size); - } - size_t bytes_read = fread( - &buf[block * block_size], - 1, - block_size, - stdin); - if (bytes_read != block_size) { - if (!feof(stdin) && ferror(stdin)) { - log_error("Error reading stdin.\n"); - } - buf[block * block_size + bytes_read] = '\0'; - break; - } - } - if (normalize) { - if (utf8_validate(buf)) { - char *tmp = utf8_normalize(buf); - free(buf); - buf = tmp; - } else { - log_error("Invalid UTF-8 in stdin.\n"); - } - } - return buf; + const size_t block_size = BUFSIZ; + size_t num_blocks = 1; + size_t buf_size = block_size; + + char *buf = xmalloc(buf_size); + for (size_t block = 0;; block++) { + if (block == num_blocks) { + num_blocks *= 2; + buf = xrealloc(buf, num_blocks * block_size); + } + size_t bytes_read = fread(&buf[block * block_size], 1, block_size, stdin); + if (bytes_read != block_size) { + if (!feof(stdin) && ferror(stdin)) { + log_error("Error reading stdin.\n"); + } + buf[block * block_size + bytes_read] = '\0'; + break; + } + } + if (normalize) { + if (utf8_validate(buf)) { + char *tmp = utf8_normalize(buf); + free(buf); + buf = tmp; + } else { + log_error("Invalid UTF-8 in stdin.\n"); + } + } + return buf; } -static void zwlr_layer_surface_configure( - void *data, - struct zwlr_layer_surface_v1 *zwlr_layer_surface, - uint32_t serial, - uint32_t width, - uint32_t height) -{ - struct tofi *tofi = data; - if (width == 0 || height == 0) { - /* Compositor is deferring to us, so don't do anything. */ - log_debug("Layer surface configure with no width or height.\n"); - return; - } - log_debug("Layer surface configure, %u x %u.\n", width, height); - - /* - * Resize the main window. - * We want actual pixel width / height, so we have to scale the - * values provided by Wayland. - */ - if (tofi->window.fractional_scale != 0) { - tofi->window.surface.width = scale_apply(width, tofi->window.fractional_scale); - tofi->window.surface.height = scale_apply(height, tofi->window.fractional_scale); - } else { - tofi->window.surface.width = width * tofi->window.scale; - tofi->window.surface.height = height * tofi->window.scale; - } - - zwlr_layer_surface_v1_ack_configure( - tofi->window.zwlr_layer_surface, - serial); +static void +zwlr_layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *zwlr_layer_surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct tofi *tofi = data; + if (width == 0 || height == 0) { + /* Compositor is deferring to us, so don't do anything. */ + log_debug("Layer surface configure with no width or height.\n"); + return; + } + log_debug("Layer surface configure, %u x %u.\n", width, height); + + /* + * Resize the main window. + * We want actual pixel width / height, so we have to scale the + * values provided by Wayland. + */ + if (tofi->window.fractional_scale != 0) { + tofi->window.surface.width = + scale_apply(width, tofi->window.fractional_scale); + tofi->window.surface.height = + scale_apply(height, tofi->window.fractional_scale); + } else { + tofi->window.surface.width = width * tofi->window.scale; + tofi->window.surface.height = height * tofi->window.scale; + } + + zwlr_layer_surface_v1_ack_configure(tofi->window.zwlr_layer_surface, serial); } -static void zwlr_layer_surface_close( - void *data, - struct zwlr_layer_surface_v1 *zwlr_layer_surface) -{ - struct tofi *tofi = data; - tofi->closed = true; - log_debug("Layer surface close.\n"); +static void +zwlr_layer_surface_close(void *data, + struct zwlr_layer_surface_v1 *zwlr_layer_surface) { + struct tofi *tofi = data; + tofi->closed = true; + log_debug("Layer surface close.\n"); } -static const struct zwlr_layer_surface_v1_listener zwlr_layer_surface_listener = { - .configure = zwlr_layer_surface_configure, - .closed = zwlr_layer_surface_close -}; - -static void wl_keyboard_keymap( - void *data, - struct wl_keyboard *wl_keyboard, - uint32_t format, - int32_t fd, - uint32_t size) -{ - struct tofi *tofi = data; - assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); - - char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - assert(map_shm != MAP_FAILED); - - if (tofi->late_keyboard_init) { - log_debug("Delaying keyboard configuration.\n"); - tofi->xkb_keymap_string = xstrdup(map_shm); - } else { - log_debug("Configuring keyboard.\n"); - struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( - tofi->xkb_context, - map_shm, - XKB_KEYMAP_FORMAT_TEXT_V1, - XKB_KEYMAP_COMPILE_NO_FLAGS); - munmap(map_shm, size); - close(fd); - - struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); - xkb_keymap_unref(tofi->xkb_keymap); - xkb_state_unref(tofi->xkb_state); - tofi->xkb_keymap = xkb_keymap; - tofi->xkb_state = xkb_state; - log_debug("Keyboard configured.\n"); - } - munmap(map_shm, size); - close(fd); +static const struct zwlr_layer_surface_v1_listener zwlr_layer_surface_listener = + {.configure = zwlr_layer_surface_configure, + .closed = zwlr_layer_surface_close}; + +static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, uint32_t size) { + struct tofi *tofi = data; + assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); + + char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + assert(map_shm != MAP_FAILED); + + if (tofi->late_keyboard_init) { + log_debug("Delaying keyboard configuration.\n"); + tofi->xkb_keymap_string = xstrdup(map_shm); + } else { + log_debug("Configuring keyboard.\n"); + struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( + tofi->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_shm, size); + close(fd); + + struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); + xkb_keymap_unref(tofi->xkb_keymap); + xkb_state_unref(tofi->xkb_state); + tofi->xkb_keymap = xkb_keymap; + tofi->xkb_state = xkb_state; + log_debug("Keyboard configured.\n"); + } + munmap(map_shm, size); + close(fd); } -static void wl_keyboard_enter( - void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - struct wl_surface *surface, - struct wl_array *keys) -{ - /* Deliberately left blank */ +static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface, + struct wl_array *keys) { + /* Deliberately left blank */ } -static void wl_keyboard_leave( - void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - struct wl_surface *surface) -{ - /* Deliberately left blank */ +static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface) { + /* Deliberately left blank */ } -static void wl_keyboard_key( - void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - uint32_t time, - uint32_t key, - uint32_t state) -{ - struct tofi *tofi = data; - - /* - * If this wasn't a keypress (i.e. was a key release), just update key - * repeat info and return. - */ - uint32_t keycode = key + 8; - if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { - if (keycode == tofi->repeat.keycode) { - tofi->repeat.active = false; - } else { - tofi->repeat.next = gettime_ms() + tofi->repeat.delay; - } - return; - } - - /* A rate of 0 disables key repeat */ - if (xkb_keymap_key_repeats(tofi->xkb_keymap, keycode) && tofi->repeat.rate != 0) { - tofi->repeat.active = true; - tofi->repeat.keycode = keycode; - tofi->repeat.next = gettime_ms() + tofi->repeat.delay; - } - input_handle_keypress(tofi, keycode); +static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) { + struct tofi *tofi = data; + + /* + * If this wasn't a keypress (i.e. was a key release), just update key + * repeat info and return. + */ + uint32_t keycode = key + 8; + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { + if (keycode == tofi->repeat.keycode) { + tofi->repeat.active = false; + } else { + tofi->repeat.next = gettime_ms() + tofi->repeat.delay; + } + return; + } + + /* A rate of 0 disables key repeat */ + if (xkb_keymap_key_repeats(tofi->xkb_keymap, keycode) && + tofi->repeat.rate != 0) { + tofi->repeat.active = true; + tofi->repeat.keycode = keycode; + tofi->repeat.next = gettime_ms() + tofi->repeat.delay; + } + input_handle_keypress(tofi, keycode); } -static void wl_keyboard_modifiers( - void *data, - struct wl_keyboard *wl_keyboard, - uint32_t serial, - uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, - uint32_t group) -{ - struct tofi *tofi = data; - if (tofi->xkb_state == NULL) { - return; - } - xkb_state_update_mask( - tofi->xkb_state, - mods_depressed, - mods_latched, - mods_locked, - 0, - 0, - group); +static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) { + struct tofi *tofi = data; + if (tofi->xkb_state == NULL) { + return; + } + xkb_state_update_mask(tofi->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); } -static void wl_keyboard_repeat_info( - void *data, - struct wl_keyboard *wl_keyboard, - int32_t rate, - int32_t delay) -{ - struct tofi *tofi = data; - tofi->repeat.rate = rate; - tofi->repeat.delay = delay; - if (rate > 0) { - log_debug("Key repeat every %u ms after %u ms.\n", - 1000 / rate, - delay); - } else { - log_debug("Key repeat disabled.\n"); - } +static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) { + struct tofi *tofi = data; + tofi->repeat.rate = rate; + tofi->repeat.delay = delay; + if (rate > 0) { + log_debug("Key repeat every %u ms after %u ms.\n", 1000 / rate, delay); + } else { + log_debug("Key repeat disabled.\n"); + } } static const struct wl_keyboard_listener wl_keyboard_listener = { - .keymap = wl_keyboard_keymap, - .enter = wl_keyboard_enter, - .leave = wl_keyboard_leave, - .key = wl_keyboard_key, - .modifiers = wl_keyboard_modifiers, - .repeat_info = wl_keyboard_repeat_info, + .keymap = wl_keyboard_keymap, + .enter = wl_keyboard_enter, + .leave = wl_keyboard_leave, + .key = wl_keyboard_key, + .modifiers = wl_keyboard_modifiers, + .repeat_info = wl_keyboard_repeat_info, }; -static void wl_pointer_enter( - void *data, - struct wl_pointer *pointer, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - struct tofi *tofi = data; - if (tofi->hide_cursor) { - /* Hide the cursor by setting its surface to NULL. */ - wl_pointer_set_cursor(tofi->wl_pointer, serial, NULL, 0, 0); - } +static void wl_pointer_enter(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct tofi *tofi = data; + if (tofi->hide_cursor) { + /* Hide the cursor by setting its surface to NULL. */ + wl_pointer_set_cursor(tofi->wl_pointer, serial, NULL, 0, 0); + } } -static void wl_pointer_leave( - void *data, - struct wl_pointer *pointer, - uint32_t serial, - struct wl_surface *surface) -{ - /* Deliberately left blank */ +static void wl_pointer_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) { + /* Deliberately left blank */ } -static void wl_pointer_motion( - void *data, - struct wl_pointer *pointer, - uint32_t time, - wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - /* Deliberately left blank */ +static void wl_pointer_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t surface_x, + wl_fixed_t surface_y) { + /* Deliberately left blank */ } -static void wl_pointer_button( - void *data, - struct wl_pointer *pointer, - uint32_t serial, - uint32_t time, - uint32_t button, - enum wl_pointer_button_state state) -{ - /* Deliberately left blank */ +static void wl_pointer_button(void *data, struct wl_pointer *pointer, + uint32_t serial, uint32_t time, uint32_t button, + enum wl_pointer_button_state state) { + /* Deliberately left blank */ } -static void wl_pointer_axis( - void *data, - struct wl_pointer *pointer, - uint32_t time, - enum wl_pointer_axis axis, - wl_fixed_t value) -{ - /* Deliberately left blank */ +static void wl_pointer_axis(void *data, struct wl_pointer *pointer, + uint32_t time, enum wl_pointer_axis axis, + wl_fixed_t value) { + /* Deliberately left blank */ } -static void wl_pointer_frame(void *data, struct wl_pointer *pointer) -{ - /* Deliberately left blank */ +static void wl_pointer_frame(void *data, struct wl_pointer *pointer) { + /* Deliberately left blank */ } -static void wl_pointer_axis_source( - void *data, - struct wl_pointer *pointer, - enum wl_pointer_axis_source axis_source) -{ - /* Deliberately left blank */ +static void wl_pointer_axis_source(void *data, struct wl_pointer *pointer, + enum wl_pointer_axis_source axis_source) { + /* Deliberately left blank */ } -static void wl_pointer_axis_stop( - void *data, - struct wl_pointer *pointer, - uint32_t time, - enum wl_pointer_axis axis) -{ - /* Deliberately left blank */ +static void wl_pointer_axis_stop(void *data, struct wl_pointer *pointer, + uint32_t time, enum wl_pointer_axis axis) { + /* Deliberately left blank */ } -static void wl_pointer_axis_discrete( - void *data, - struct wl_pointer *pointer, - enum wl_pointer_axis axis, - int32_t discrete) -{ - /* Deliberately left blank */ +static void wl_pointer_axis_discrete(void *data, struct wl_pointer *pointer, + enum wl_pointer_axis axis, + int32_t discrete) { + /* Deliberately left blank */ } static const struct wl_pointer_listener wl_pointer_listener = { - .enter = wl_pointer_enter, - .leave = wl_pointer_leave, - .motion = wl_pointer_motion, - .button = wl_pointer_button, - .axis = wl_pointer_axis, - .frame = wl_pointer_frame, - .axis_source = wl_pointer_axis_source, - .axis_stop = wl_pointer_axis_stop, - .axis_discrete = wl_pointer_axis_discrete -}; - -static void wl_seat_capabilities( - void *data, - struct wl_seat *wl_seat, - uint32_t capabilities) -{ - struct tofi *tofi = data; - - bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; - bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; - - if (have_keyboard && tofi->wl_keyboard == NULL) { - tofi->wl_keyboard = wl_seat_get_keyboard(tofi->wl_seat); - wl_keyboard_add_listener( - tofi->wl_keyboard, - &wl_keyboard_listener, - tofi); - log_debug("Got keyboard from seat.\n"); - } else if (!have_keyboard && tofi->wl_keyboard != NULL) { - wl_keyboard_release(tofi->wl_keyboard); - tofi->wl_keyboard = NULL; - log_debug("Released keyboard.\n"); - } - - if (have_pointer && tofi->wl_pointer == NULL) { - tofi->wl_pointer = wl_seat_get_pointer(tofi->wl_seat); - wl_pointer_add_listener( - tofi->wl_pointer, - &wl_pointer_listener, - tofi); - log_debug("Got pointer from seat.\n"); - } else if (!have_pointer && tofi->wl_pointer != NULL) { - wl_pointer_release(tofi->wl_pointer); - tofi->wl_pointer = NULL; - log_debug("Released pointer.\n"); - } + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete}; + +static void wl_seat_capabilities(void *data, struct wl_seat *wl_seat, + uint32_t capabilities) { + struct tofi *tofi = data; + + bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; + bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; + + if (have_keyboard && tofi->wl_keyboard == NULL) { + tofi->wl_keyboard = wl_seat_get_keyboard(tofi->wl_seat); + wl_keyboard_add_listener(tofi->wl_keyboard, &wl_keyboard_listener, tofi); + log_debug("Got keyboard from seat.\n"); + } else if (!have_keyboard && tofi->wl_keyboard != NULL) { + wl_keyboard_release(tofi->wl_keyboard); + tofi->wl_keyboard = NULL; + log_debug("Released keyboard.\n"); + } + + if (have_pointer && tofi->wl_pointer == NULL) { + tofi->wl_pointer = wl_seat_get_pointer(tofi->wl_seat); + wl_pointer_add_listener(tofi->wl_pointer, &wl_pointer_listener, tofi); + log_debug("Got pointer from seat.\n"); + } else if (!have_pointer && tofi->wl_pointer != NULL) { + wl_pointer_release(tofi->wl_pointer); + tofi->wl_pointer = NULL; + log_debug("Released pointer.\n"); + } } -static void wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name) -{ - /* Deliberately left blank */ +static void wl_seat_name(void *data, struct wl_seat *wl_seat, + const char *name) { + /* Deliberately left blank */ } static const struct wl_seat_listener wl_seat_listener = { - .capabilities = wl_seat_capabilities, - .name = wl_seat_name, + .capabilities = wl_seat_capabilities, + .name = wl_seat_name, }; -static void wl_data_offer_offer( - void *data, - struct wl_data_offer *wl_data_offer, - const char *mime_type) -{ - struct clipboard *clipboard = data; - - /* Only accept plain text, and prefer utf-8. */ - if (!strcmp(mime_type, mime_type_text_plain)) { - if (clipboard->mime_type != NULL) { - clipboard->mime_type = mime_type_text_plain; - } - } else if (!strcmp(mime_type, mime_type_text_plain_utf8)) { - clipboard->mime_type = mime_type_text_plain_utf8; - } +static void wl_data_offer_offer(void *data, struct wl_data_offer *wl_data_offer, + const char *mime_type) { + struct clipboard *clipboard = data; + + /* Only accept plain text, and prefer utf-8. */ + if (!strcmp(mime_type, mime_type_text_plain)) { + if (clipboard->mime_type != NULL) { + clipboard->mime_type = mime_type_text_plain; + } + } else if (!strcmp(mime_type, mime_type_text_plain_utf8)) { + clipboard->mime_type = mime_type_text_plain_utf8; + } } -static void wl_data_offer_source_actions( - void *data, - struct wl_data_offer *wl_data_offer, - uint32_t source_actions) -{ - /* Deliberately left blank */ +static void wl_data_offer_source_actions(void *data, + struct wl_data_offer *wl_data_offer, + uint32_t source_actions) { + /* Deliberately left blank */ } -static void wl_data_offer_action( - void *data, - struct wl_data_offer *wl_data_offer, - uint32_t action) -{ - /* Deliberately left blank */ +static void wl_data_offer_action(void *data, + struct wl_data_offer *wl_data_offer, + uint32_t action) { + /* Deliberately left blank */ } static const struct wl_data_offer_listener wl_data_offer_listener = { - .offer = wl_data_offer_offer, - .source_actions = wl_data_offer_source_actions, - .action = wl_data_offer_action -}; - -static void wl_data_device_data_offer( - void *data, - struct wl_data_device *wl_data_device, - struct wl_data_offer *wl_data_offer) -{ - struct clipboard *clipboard = data; - clipboard_reset(clipboard); - clipboard->wl_data_offer = wl_data_offer; - wl_data_offer_add_listener( - wl_data_offer, - &wl_data_offer_listener, - clipboard); + .offer = wl_data_offer_offer, + .source_actions = wl_data_offer_source_actions, + .action = wl_data_offer_action}; + +static void wl_data_device_data_offer(void *data, + struct wl_data_device *wl_data_device, + struct wl_data_offer *wl_data_offer) { + struct clipboard *clipboard = data; + clipboard_reset(clipboard); + clipboard->wl_data_offer = wl_data_offer; + wl_data_offer_add_listener(wl_data_offer, &wl_data_offer_listener, clipboard); } -static void wl_data_device_enter( - void *data, - struct wl_data_device *wl_data_device, - uint32_t serial, - struct wl_surface *wl_surface, - int32_t x, - int32_t y, - struct wl_data_offer *wl_data_offer) -{ - /* Drag-and-drop is just ignored for now. */ - wl_data_offer_accept( - wl_data_offer, - serial, - NULL); - wl_data_offer_set_actions( - wl_data_offer, - WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, - WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); +static void wl_data_device_enter(void *data, + struct wl_data_device *wl_data_device, + uint32_t serial, struct wl_surface *wl_surface, + int32_t x, int32_t y, + struct wl_data_offer *wl_data_offer) { + /* Drag-and-drop is just ignored for now. */ + wl_data_offer_accept(wl_data_offer, serial, NULL); + wl_data_offer_set_actions(wl_data_offer, + WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, + WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); } -static void wl_data_device_leave( - void *data, - struct wl_data_device *wl_data_device) -{ - /* Deliberately left blank */ +static void wl_data_device_leave(void *data, + struct wl_data_device *wl_data_device) { + /* Deliberately left blank */ } -static void wl_data_device_motion( - void *data, - struct wl_data_device *wl_data_device, - uint32_t time, - int32_t x, - int32_t y) -{ - /* Deliberately left blank */ +static void wl_data_device_motion(void *data, + struct wl_data_device *wl_data_device, + uint32_t time, int32_t x, int32_t y) { + /* Deliberately left blank */ } -static void wl_data_device_drop( - void *data, - struct wl_data_device *wl_data_device) -{ - /* Deliberately left blank */ +static void wl_data_device_drop(void *data, + struct wl_data_device *wl_data_device) { + /* Deliberately left blank */ } -static void wl_data_device_selection( - void *data, - struct wl_data_device *wl_data_device, - struct wl_data_offer *wl_data_offer) -{ - struct clipboard *clipboard = data; - if (wl_data_offer == NULL) { - clipboard_reset(clipboard); - } +static void wl_data_device_selection(void *data, + struct wl_data_device *wl_data_device, + struct wl_data_offer *wl_data_offer) { + struct clipboard *clipboard = data; + if (wl_data_offer == NULL) { + clipboard_reset(clipboard); + } } static const struct wl_data_device_listener wl_data_device_listener = { - .data_offer = wl_data_device_data_offer, - .enter = wl_data_device_enter, - .leave = wl_data_device_leave, - .motion = wl_data_device_motion, - .drop = wl_data_device_drop, - .selection = wl_data_device_selection -}; - -static void output_geometry( - void *data, - struct wl_output *wl_output, - int32_t x, - int32_t y, - int32_t physical_width, - int32_t physical_height, - int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ - struct tofi *tofi = data; - struct output_list_element *el; - wl_list_for_each(el, &tofi->output_list, link) { - if (el->wl_output == wl_output) { - el->transform = transform; - } - } + .data_offer = wl_data_device_data_offer, + .enter = wl_data_device_enter, + .leave = wl_data_device_leave, + .motion = wl_data_device_motion, + .drop = wl_data_device_drop, + .selection = wl_data_device_selection}; + +static void output_geometry(void *data, struct wl_output *wl_output, int32_t x, + int32_t y, int32_t physical_width, + int32_t physical_height, int32_t subpixel, + const char *make, const char *model, + int32_t transform) { + struct tofi *tofi = data; + struct output_list_element *el; + wl_list_for_each(el, &tofi->output_list, link) { + if (el->wl_output == wl_output) { + el->transform = transform; + } + } } -static void output_mode( - void *data, - struct wl_output *wl_output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ - struct tofi *tofi = data; - struct output_list_element *el; - wl_list_for_each(el, &tofi->output_list, link) { - if (el->wl_output == wl_output) { - if (flags & WL_OUTPUT_MODE_CURRENT) { - el->width = width; - el->height = height; - } - } - } +static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) { + struct tofi *tofi = data; + struct output_list_element *el; + wl_list_for_each(el, &tofi->output_list, link) { + if (el->wl_output == wl_output) { + if (flags & WL_OUTPUT_MODE_CURRENT) { + el->width = width; + el->height = height; + } + } + } } -static void output_scale( - void *data, - struct wl_output *wl_output, - int32_t factor) -{ - struct tofi *tofi = data; - struct output_list_element *el; - wl_list_for_each(el, &tofi->output_list, link) { - if (el->wl_output == wl_output) { - el->scale = factor; - } - } +static void output_scale(void *data, struct wl_output *wl_output, + int32_t factor) { + struct tofi *tofi = data; + struct output_list_element *el; + wl_list_for_each(el, &tofi->output_list, link) { + if (el->wl_output == wl_output) { + el->scale = factor; + } + } } -static void output_name( - void *data, - struct wl_output *wl_output, - const char *name) -{ - struct tofi *tofi = data; - struct output_list_element *el; - wl_list_for_each(el, &tofi->output_list, link) { - if (el->wl_output == wl_output) { - el->name = xstrdup(name); - } - } +static void output_name(void *data, struct wl_output *wl_output, + const char *name) { + struct tofi *tofi = data; + struct output_list_element *el; + wl_list_for_each(el, &tofi->output_list, link) { + if (el->wl_output == wl_output) { + el->name = xstrdup(name); + } + } } -static void output_description( - void *data, - struct wl_output *wl_output, - const char *description) -{ - /* Deliberately left blank */ +static void output_description(void *data, struct wl_output *wl_output, + const char *description) { + /* Deliberately left blank */ } -static void output_done(void *data, struct wl_output *wl_output) -{ - log_debug("Output configuration done.\n"); +static void output_done(void *data, struct wl_output *wl_output) { + log_debug("Output configuration done.\n"); } static const struct wl_output_listener wl_output_listener = { - .geometry = output_geometry, - .mode = output_mode, - .done = output_done, - .scale = output_scale, + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, #ifndef NO_WL_OUTPUT_NAME - .name = output_name, - .description = output_description, + .name = output_name, + .description = output_description, #endif }; -static void registry_global( - void *data, - struct wl_registry *wl_registry, - uint32_t name, - const char *interface, - uint32_t version) -{ - struct tofi *tofi = data; - //log_debug("Registry %u: %s v%u.\n", name, interface, version); - if (!strcmp(interface, wl_compositor_interface.name)) { - tofi->wl_compositor = wl_registry_bind( - wl_registry, - name, - &wl_compositor_interface, - 4); - log_debug("Bound to compositor %u.\n", name); - } else if (!strcmp(interface, wl_seat_interface.name)) { - tofi->wl_seat = wl_registry_bind( - wl_registry, - name, - &wl_seat_interface, - 7); - wl_seat_add_listener( - tofi->wl_seat, - &wl_seat_listener, - tofi); - log_debug("Bound to seat %u.\n", name); - } else if (!strcmp(interface, wl_output_interface.name)) { - struct output_list_element *el = xmalloc(sizeof(*el)); - if (version < 4) { - el->name = xstrdup(""); - log_warning("Using an outdated compositor, " - "output selection will not work.\n"); - } else { - version = 4; - } - el->wl_output = wl_registry_bind( - wl_registry, - name, - &wl_output_interface, - version); - wl_output_add_listener( - el->wl_output, - &wl_output_listener, - tofi); - wl_list_insert(&tofi->output_list, &el->link); - log_debug("Bound to output %u.\n", name); - } else if (!strcmp(interface, wl_shm_interface.name)) { - tofi->wl_shm = wl_registry_bind( - wl_registry, - name, - &wl_shm_interface, - 1); - log_debug("Bound to shm %u.\n", name); - } else if (!strcmp(interface, wl_data_device_manager_interface.name)) { - tofi->wl_data_device_manager = wl_registry_bind( - wl_registry, - name, - &wl_data_device_manager_interface, - 3); - log_debug("Bound to data device manager %u.\n", name); - } else if (!strcmp(interface, zwlr_layer_shell_v1_interface.name)) { - if (version < 3) { - log_warning("Using an outdated compositor, " - "screen anchoring may not work.\n"); - } else { - version = 3; - } - tofi->zwlr_layer_shell = wl_registry_bind( - wl_registry, - name, - &zwlr_layer_shell_v1_interface, - version); - log_debug("Bound to zwlr_layer_shell_v1 %u.\n", name); - } else if (!strcmp(interface, wp_viewporter_interface.name)) { - tofi->wp_viewporter = wl_registry_bind( - wl_registry, - name, - &wp_viewporter_interface, - 1); - log_debug("Bound to wp_viewporter %u.\n", name); - } else if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name)) { - tofi->wp_fractional_scale_manager = wl_registry_bind( - wl_registry, - name, - &wp_fractional_scale_manager_v1_interface, - 1); - log_debug("Bound to wp_fractional_scale_manager_v1 %u.\n", name); - } +static void registry_global(void *data, struct wl_registry *wl_registry, + uint32_t name, const char *interface, + uint32_t version) { + struct tofi *tofi = data; + // log_debug("Registry %u: %s v%u.\n", name, interface, version); + if (!strcmp(interface, wl_compositor_interface.name)) { + tofi->wl_compositor = + wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); + log_debug("Bound to compositor %u.\n", name); + } else if (!strcmp(interface, wl_seat_interface.name)) { + tofi->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 7); + wl_seat_add_listener(tofi->wl_seat, &wl_seat_listener, tofi); + log_debug("Bound to seat %u.\n", name); + } else if (!strcmp(interface, wl_output_interface.name)) { + struct output_list_element *el = xmalloc(sizeof(*el)); + if (version < 4) { + el->name = xstrdup(""); + log_warning("Using an outdated compositor, " + "output selection will not work.\n"); + } else { + version = 4; + } + el->wl_output = + wl_registry_bind(wl_registry, name, &wl_output_interface, version); + wl_output_add_listener(el->wl_output, &wl_output_listener, tofi); + wl_list_insert(&tofi->output_list, &el->link); + log_debug("Bound to output %u.\n", name); + } else if (!strcmp(interface, wl_shm_interface.name)) { + tofi->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); + log_debug("Bound to shm %u.\n", name); + } else if (!strcmp(interface, wl_data_device_manager_interface.name)) { + tofi->wl_data_device_manager = wl_registry_bind( + wl_registry, name, &wl_data_device_manager_interface, 3); + log_debug("Bound to data device manager %u.\n", name); + } else if (!strcmp(interface, zwlr_layer_shell_v1_interface.name)) { + if (version < 3) { + log_warning("Using an outdated compositor, " + "screen anchoring may not work.\n"); + } else { + version = 3; + } + tofi->zwlr_layer_shell = wl_registry_bind( + wl_registry, name, &zwlr_layer_shell_v1_interface, version); + log_debug("Bound to zwlr_layer_shell_v1 %u.\n", name); + } else if (!strcmp(interface, wp_viewporter_interface.name)) { + tofi->wp_viewporter = + wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1); + log_debug("Bound to wp_viewporter %u.\n", name); + } else if (!strcmp(interface, + wp_fractional_scale_manager_v1_interface.name)) { + tofi->wp_fractional_scale_manager = wl_registry_bind( + wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1); + log_debug("Bound to wp_fractional_scale_manager_v1 %u.\n", name); + } } -static void registry_global_remove( - void *data, - struct wl_registry *wl_registry, - uint32_t name) -{ - /* Deliberately left blank */ +static void registry_global_remove(void *data, struct wl_registry *wl_registry, + uint32_t name) { + /* Deliberately left blank */ } static const struct wl_registry_listener wl_registry_listener = { - .global = registry_global, - .global_remove = registry_global_remove, + .global = registry_global, + .global_remove = registry_global_remove, }; -static void surface_enter( - void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - log_debug("Surface entered output.\n"); +static void surface_enter(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { + log_debug("Surface entered output.\n"); } -static void surface_leave( - void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - /* Deliberately left blank */ +static void surface_leave(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { + /* Deliberately left blank */ } static const struct wl_surface_listener wl_surface_listener = { - .enter = surface_enter, - .leave = surface_leave -}; + .enter = surface_enter, .leave = surface_leave}; /* * These "dummy_*" functions are callbacks just for the dummy surface used to * select the default output if there's more than one. */ static void dummy_layer_surface_configure( - void *data, - struct zwlr_layer_surface_v1 *zwlr_layer_surface, - uint32_t serial, - uint32_t width, - uint32_t height) -{ - zwlr_layer_surface_v1_ack_configure( - zwlr_layer_surface, - serial); + void *data, struct zwlr_layer_surface_v1 *zwlr_layer_surface, + uint32_t serial, uint32_t width, uint32_t height) { + zwlr_layer_surface_v1_ack_configure(zwlr_layer_surface, serial); } -static void dummy_layer_surface_close( - void *data, - struct zwlr_layer_surface_v1 *zwlr_layer_surface) -{ -} +static void +dummy_layer_surface_close(void *data, + struct zwlr_layer_surface_v1 *zwlr_layer_surface) {} -static const struct zwlr_layer_surface_v1_listener dummy_layer_surface_listener = { - .configure = dummy_layer_surface_configure, - .closed = dummy_layer_surface_close -}; +static const struct zwlr_layer_surface_v1_listener + dummy_layer_surface_listener = {.configure = dummy_layer_surface_configure, + .closed = dummy_layer_surface_close}; static void dummy_fractional_scale_preferred_scale( - void *data, - struct wp_fractional_scale_v1 *wp_fractional_scale, - uint32_t scale) -{ - struct tofi *tofi = data; - tofi->window.fractional_scale = scale; + void *data, struct wp_fractional_scale_v1 *wp_fractional_scale, + uint32_t scale) { + struct tofi *tofi = data; + tofi->window.fractional_scale = scale; } -static const struct wp_fractional_scale_v1_listener dummy_fractional_scale_listener = { - .preferred_scale = dummy_fractional_scale_preferred_scale -}; - -static void dummy_surface_enter( - void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct tofi *tofi = data; - struct output_list_element *el; - wl_list_for_each(el, &tofi->output_list, link) { - if (el->wl_output == wl_output) { - tofi->default_output = el; - break; - } - } +static const struct wp_fractional_scale_v1_listener + dummy_fractional_scale_listener = { + .preferred_scale = dummy_fractional_scale_preferred_scale}; + +static void dummy_surface_enter(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { + struct tofi *tofi = data; + struct output_list_element *el; + wl_list_for_each(el, &tofi->output_list, link) { + if (el->wl_output == wl_output) { + tofi->default_output = el; + break; + } + } } -static void dummy_surface_leave( - void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - /* Deliberately left blank */ +static void dummy_surface_leave(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { + /* Deliberately left blank */ } static const struct wl_surface_listener dummy_surface_listener = { - .enter = dummy_surface_enter, - .leave = dummy_surface_leave -}; - - -static void usage(bool err) -{ - fprintf(err ? stderr : stdout, "%s", -"Usage: tofi [options]\n" -"\n" -"Basic options:\n" -" -h, --help Print this message and exit.\n" -" -c, --config Specify a config file.\n" -" --prompt-text Prompt text.\n" -" --width Width of the window.\n" -" --height Height of the window.\n" -" --output Name of output to display window on.\n" -" --anchor Location on screen to anchor window.\n" -" --horizontal List results horizontally.\n" -"\n" -"All options listed in \"man 5 tofi\" are also accpted in the form \"--key=value\".\n" - ); + .enter = dummy_surface_enter, .leave = dummy_surface_leave}; + +static void usage(bool err) { + fprintf( + err ? stderr : stdout, "%s", + "Usage: tofi [options]\n" + "\n" + "Basic options:\n" + " -h, --help Print this message and exit.\n" + " -c, --config Specify a config file.\n" + " --prompt-text Prompt text.\n" + " --width Width of the window.\n" + " --height Height of the window.\n" + " --output Name of output to display window " + "on.\n" + " --anchor Location on screen to anchor " + "window.\n" + " --horizontal List results horizontally.\n" + "\n" + "All options listed in \"man 5 tofi\" are also accpted in the form " + "\"--key=value\".\n"); } /* Option parsing with getopt. */ const struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"config", required_argument, NULL, 'c'}, - {"include", required_argument, NULL, 0}, - {"anchor", required_argument, NULL, 0}, - {"exclusive-zone", required_argument, NULL, 0}, - {"background-color", required_argument, NULL, 0}, - {"corner-radius", required_argument, NULL, 0}, - {"font", required_argument, NULL, 0}, - {"font-size", required_argument, NULL, 0}, - {"font-features", required_argument, NULL, 0}, - {"font-variations", required_argument, NULL, 0}, - {"num-results", required_argument, NULL, 0}, - {"selection-color", required_argument, NULL, 0}, - {"selection-match-color", required_argument, NULL, 0}, - {"selection-padding", required_argument, NULL, 0}, - {"selection-background", required_argument, NULL, 0}, - {"selection-background-padding", required_argument, NULL, 0}, - {"selection-background-corner-radius", required_argument, NULL, 0}, - {"outline-width", required_argument, NULL, 0}, - {"outline-color", required_argument, NULL, 0}, - {"text-cursor", required_argument, NULL, 0}, - {"text-cursor-style", required_argument, NULL, 0}, - {"text-cursor-color", required_argument, NULL, 0}, - {"text-cursor-background", required_argument, NULL, 0}, - {"text-cursor-corner-radius", required_argument, NULL, 0}, - {"text-cursor-thickness", required_argument, NULL, 0}, - {"prompt-text", required_argument, NULL, 0}, - {"prompt-padding", required_argument, NULL, 0}, - {"prompt-color", required_argument, NULL, 0}, - {"prompt-background", required_argument, NULL, 0}, - {"prompt-background-padding", required_argument, NULL, 0}, - {"prompt-background-corner-radius", required_argument, NULL, 0}, - {"placeholder-text", required_argument, NULL, 0}, - {"placeholder-color", required_argument, NULL, 0}, - {"placeholder-background", required_argument, NULL, 0}, - {"placeholder-background-padding", required_argument, NULL, 0}, - {"placeholder-background-corner-radius", required_argument, NULL, 0}, - {"input-color", required_argument, NULL, 0}, - {"input-background", required_argument, NULL, 0}, - {"input-background-padding", required_argument, NULL, 0}, - {"input-background-corner-radius", required_argument, NULL, 0}, - {"default-result-color", required_argument, NULL, 0}, - {"default-result-background", required_argument, NULL, 0}, - {"default-result-background-padding", required_argument, NULL, 0}, - {"default-result-background-corner-radius", required_argument, NULL, 0}, - {"alternate-result-color", required_argument, NULL, 0}, - {"alternate-result-background", required_argument, NULL, 0}, - {"alternate-result-background-padding", required_argument, NULL, 0}, - {"alternate-result-background-corner-radius", required_argument, NULL, 0}, - {"result-spacing", required_argument, NULL, 0}, - {"min-input-width", required_argument, NULL, 0}, - {"border-width", required_argument, NULL, 0}, - {"border-color", required_argument, NULL, 0}, - {"text-color", required_argument, NULL, 0}, - {"width", required_argument, NULL, 0}, - {"height", required_argument, NULL, 0}, - {"margin-top", required_argument, NULL, 0}, - {"margin-bottom", required_argument, NULL, 0}, - {"margin-left", required_argument, NULL, 0}, - {"margin-right", required_argument, NULL, 0}, - {"padding-top", required_argument, NULL, 0}, - {"padding-bottom", required_argument, NULL, 0}, - {"padding-left", required_argument, NULL, 0}, - {"padding-right", required_argument, NULL, 0}, - {"clip-to-padding", required_argument, NULL, 0}, - {"horizontal", required_argument, NULL, 0}, - {"hide-cursor", required_argument, NULL, 0}, - {"history", required_argument, NULL, 0}, - {"history-file", required_argument, NULL, 0}, - {"fuzzy-match", required_argument, NULL, 0}, - {"matching-algorithm", required_argument, NULL, 0}, - {"require-match", required_argument, NULL, 0}, - {"auto-accept-single", required_argument, NULL, 0}, - {"print-index", required_argument, NULL, 0}, - {"hide-input", required_argument, NULL, 0}, - {"hidden-character", required_argument, NULL, 0}, - {"physical-keybindings", required_argument, NULL, 0}, - {"drun-launch", required_argument, NULL, 0}, - {"drun-print-exec", required_argument, NULL, 0}, - {"terminal", required_argument, NULL, 0}, - {"hint-font", required_argument, NULL, 0}, - {"multi-instance", required_argument, NULL, 0}, - {"ascii-input", required_argument, NULL, 0}, - {"output", required_argument, NULL, 0}, - {"scale", required_argument, NULL, 0}, - {"late-keyboard-init", optional_argument, NULL, 'k'}, - {NULL, 0, NULL, 0} -}; + {"help", no_argument, NULL, 'h'}, + {"config", required_argument, NULL, 'c'}, + {"include", required_argument, NULL, 0}, + {"anchor", required_argument, NULL, 0}, + {"exclusive-zone", required_argument, NULL, 0}, + {"background-color", required_argument, NULL, 0}, + {"corner-radius", required_argument, NULL, 0}, + {"font", required_argument, NULL, 0}, + {"font-size", required_argument, NULL, 0}, + {"font-features", required_argument, NULL, 0}, + {"font-variations", required_argument, NULL, 0}, + {"num-results", required_argument, NULL, 0}, + {"selection-color", required_argument, NULL, 0}, + {"selection-match-color", required_argument, NULL, 0}, + {"selection-padding", required_argument, NULL, 0}, + {"selection-background", required_argument, NULL, 0}, + {"selection-background-padding", required_argument, NULL, 0}, + {"selection-background-corner-radius", required_argument, NULL, 0}, + {"outline-width", required_argument, NULL, 0}, + {"outline-color", required_argument, NULL, 0}, + {"text-cursor", required_argument, NULL, 0}, + {"text-cursor-style", required_argument, NULL, 0}, + {"text-cursor-color", required_argument, NULL, 0}, + {"text-cursor-background", required_argument, NULL, 0}, + {"text-cursor-corner-radius", required_argument, NULL, 0}, + {"text-cursor-thickness", required_argument, NULL, 0}, + {"prompt-text", required_argument, NULL, 0}, + {"prompt-padding", required_argument, NULL, 0}, + {"prompt-color", required_argument, NULL, 0}, + {"prompt-background", required_argument, NULL, 0}, + {"prompt-background-padding", required_argument, NULL, 0}, + {"prompt-background-corner-radius", required_argument, NULL, 0}, + {"placeholder-text", required_argument, NULL, 0}, + {"placeholder-color", required_argument, NULL, 0}, + {"placeholder-background", required_argument, NULL, 0}, + {"placeholder-background-padding", required_argument, NULL, 0}, + {"placeholder-background-corner-radius", required_argument, NULL, 0}, + {"input-color", required_argument, NULL, 0}, + {"input-background", required_argument, NULL, 0}, + {"input-background-padding", required_argument, NULL, 0}, + {"input-background-corner-radius", required_argument, NULL, 0}, + {"default-result-color", required_argument, NULL, 0}, + {"default-result-background", required_argument, NULL, 0}, + {"default-result-background-padding", required_argument, NULL, 0}, + {"default-result-background-corner-radius", required_argument, NULL, 0}, + {"alternate-result-color", required_argument, NULL, 0}, + {"alternate-result-background", required_argument, NULL, 0}, + {"alternate-result-background-padding", required_argument, NULL, 0}, + {"alternate-result-background-corner-radius", required_argument, NULL, 0}, + {"result-spacing", required_argument, NULL, 0}, + {"min-input-width", required_argument, NULL, 0}, + {"border-width", required_argument, NULL, 0}, + {"border-color", required_argument, NULL, 0}, + {"text-color", required_argument, NULL, 0}, + {"width", required_argument, NULL, 0}, + {"height", required_argument, NULL, 0}, + {"margin-top", required_argument, NULL, 0}, + {"margin-bottom", required_argument, NULL, 0}, + {"margin-left", required_argument, NULL, 0}, + {"margin-right", required_argument, NULL, 0}, + {"padding-top", required_argument, NULL, 0}, + {"padding-bottom", required_argument, NULL, 0}, + {"padding-left", required_argument, NULL, 0}, + {"padding-right", required_argument, NULL, 0}, + {"clip-to-padding", required_argument, NULL, 0}, + {"horizontal", required_argument, NULL, 0}, + {"hide-cursor", required_argument, NULL, 0}, + {"history", required_argument, NULL, 0}, + {"history-file", required_argument, NULL, 0}, + {"fuzzy-match", required_argument, NULL, 0}, + {"matching-algorithm", required_argument, NULL, 0}, + {"require-match", required_argument, NULL, 0}, + {"auto-accept-single", required_argument, NULL, 0}, + {"print-index", required_argument, NULL, 0}, + {"hide-input", required_argument, NULL, 0}, + {"hidden-character", required_argument, NULL, 0}, + {"physical-keybindings", required_argument, NULL, 0}, + {"drun-launch", required_argument, NULL, 0}, + {"drun-print-exec", required_argument, NULL, 0}, + {"terminal", required_argument, NULL, 0}, + {"hint-font", required_argument, NULL, 0}, + {"multi-instance", required_argument, NULL, 0}, + {"ascii-input", required_argument, NULL, 0}, + {"output", required_argument, NULL, 0}, + {"scale", required_argument, NULL, 0}, + {"late-keyboard-init", optional_argument, NULL, 'k'}, + {NULL, 0, NULL, 0}}; const char *short_options = ":hc:"; -static void parse_args(struct tofi *tofi, int argc, char *argv[]) -{ - - bool load_default_config = true; - int option_index = 0; - - /* Handle errors ourselves. */ - opterr = 0; - - /* First pass, just check for config file, help, and errors. */ - optind = 1; - int opt = getopt_long(argc, argv, short_options, long_options, &option_index); - while (opt != -1) { - if (opt == 'h') { - usage(false); - exit(EXIT_SUCCESS); - } else if (opt == 'c') { - config_load(tofi, optarg); - load_default_config = false; - } else if (opt == ':') { - log_error("Option %s requires an argument.\n", argv[optind - 1]); - usage(true); - exit(EXIT_FAILURE); - } else if (opt == '?') { - if (optopt) { - log_error("Unknown option -%c.\n", optopt); - } else { - log_error("Unknown option %s.\n", argv[optind - 1]); - } - usage(true); - exit(EXIT_FAILURE); - } - opt = getopt_long(argc, argv, short_options, long_options, &option_index); - } - if (load_default_config) { - config_load(tofi, NULL); - } - - /* Second pass, parse everything else. */ - optind = 1; - opt = getopt_long(argc, argv, short_options, long_options, &option_index); - while (opt != -1) { - if (opt == 0) { - if (!config_apply(tofi, long_options[option_index].name, optarg)) { - exit(EXIT_FAILURE); - } - } else if (opt == 'k') { - /* - * Backwards compatibility for --late-keyboard-init not - * taking an argument. - */ - if (optarg) { - if (!config_apply(tofi, long_options[option_index].name, optarg)) { - exit(EXIT_FAILURE); - } - } else { - tofi->late_keyboard_init = true; - } - } - opt = getopt_long(argc, argv, short_options, long_options, &option_index); - } - - if (optind < argc) { - log_error("Unexpected non-option argument '%s'.\n", argv[optind]); - usage(true); - exit(EXIT_FAILURE); - } +static void parse_args(struct tofi *tofi, int argc, char *argv[]) { + + bool load_default_config = true; + int option_index = 0; + + /* Handle errors ourselves. */ + opterr = 0; + + /* First pass, just check for config file, help, and errors. */ + optind = 1; + int opt = getopt_long(argc, argv, short_options, long_options, &option_index); + while (opt != -1) { + if (opt == 'h') { + usage(false); + exit(EXIT_SUCCESS); + } else if (opt == 'c') { + config_load(tofi, optarg); + load_default_config = false; + } else if (opt == ':') { + log_error("Option %s requires an argument.\n", argv[optind - 1]); + usage(true); + exit(EXIT_FAILURE); + } else if (opt == '?') { + if (optopt) { + log_error("Unknown option -%c.\n", optopt); + } else { + log_error("Unknown option %s.\n", argv[optind - 1]); + } + usage(true); + exit(EXIT_FAILURE); + } + opt = getopt_long(argc, argv, short_options, long_options, &option_index); + } + if (load_default_config) { + config_load(tofi, NULL); + } + + /* Second pass, parse everything else. */ + optind = 1; + opt = getopt_long(argc, argv, short_options, long_options, &option_index); + while (opt != -1) { + if (opt == 0) { + if (!config_apply(tofi, long_options[option_index].name, optarg)) { + exit(EXIT_FAILURE); + } + } else if (opt == 'k') { + /* + * Backwards compatibility for --late-keyboard-init not + * taking an argument. + */ + if (optarg) { + if (!config_apply(tofi, long_options[option_index].name, optarg)) { + exit(EXIT_FAILURE); + } + } else { + tofi->late_keyboard_init = true; + } + } + opt = getopt_long(argc, argv, short_options, long_options, &option_index); + } + + if (optind < argc) { + log_error("Unexpected non-option argument '%s'.\n", argv[optind]); + usage(true); + exit(EXIT_FAILURE); + } } -static bool do_submit(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - uint32_t selection = entry->selection + entry->first_result; - char *res = entry->results.buf[selection].string; - - if (tofi->window.entry.results.count == 0) { - /* Always require a match in drun mode. */ - if (tofi->require_match || entry->mode == TOFI_MODE_DRUN) { - return false; - } else { - printf("%s\n", entry->input_utf8); - return true; - } - } - - if (entry->mode == TOFI_MODE_DRUN) { - /* - * At this point, the list of apps is history sorted rather - * than alphabetically sorted, so we can't use - * desktop_vec_find_sorted(). - */ - struct desktop_entry *app = NULL; - for (size_t i = 0; i < entry->apps.count; i++) { - if (!strcmp(res, entry->apps.buf[i].name)) { - app = &entry->apps.buf[i]; - break; - } - } - if (app == NULL) { - log_error("Couldn't find application file! This shouldn't happen.\n"); - return false; - } - char *path = app->path; - if (tofi->drun_launch) { - drun_launch(path); - } else { - drun_print(path, tofi->default_terminal); - } - } else { - if (entry->mode == TOFI_MODE_PLAIN && tofi->print_index) { - for (size_t i = 0; i < entry->commands.count; i++) { - if (res == entry->commands.buf[i].string) { - printf("%zu\n", i + 1); - break; - } - } - } else { - printf("%s\n", res); - } - } - if (tofi->use_history) { - history_add( - &entry->history, - entry->results.buf[selection].string); - if (tofi->history_file[0] == 0) { - history_save_default_file(&entry->history, entry->mode == TOFI_MODE_DRUN); - } else { - history_save(&entry->history, tofi->history_file); - } - } - return true; +static bool do_submit(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + uint32_t selection = entry->selection + entry->first_result; + char *res = entry->results.buf[selection].string; + + if (tofi->window.entry.results.count == 0) { + if (tofi->require_match) { + return false; + } else { + // return result + printf(res); + return true; + } + } + + if (entry->mode == TOFI_MODE_DRUN) { + /* + * At this point, the list of apps is history sorted rather + * than alphabetically sorted, so we can't use + * desktop_vec_find_sorted(). + */ + struct desktop_entry *app = NULL; + for (size_t i = 0; i < entry->apps.count; i++) { + if (!strcmp(res, entry->apps.buf[i].name)) { + app = &entry->apps.buf[i]; + break; + } + } + if (app == NULL) { + if (execute_module(res, entry->input_utf8)) + return true; + log_error("Couldn't find application file! This shouldn't happen.\n"); + return false; + } + char *path = app->path; + if (tofi->drun_launch) { + drun_launch(path); + } else { + drun_print(path, tofi->default_terminal); + } + } else { + if (entry->mode == TOFI_MODE_PLAIN && tofi->print_index) { + for (size_t i = 0; i < entry->commands.count; i++) { + if (res == entry->commands.buf[i].string) { + printf("%zu\n", i + 1); + break; + } + } + } else { + printf("%s\n", res); + } + } + if (tofi->use_history) { + history_add(&entry->history, entry->results.buf[selection].string); + if (tofi->history_file[0] == 0) { + history_save_default_file(&entry->history, entry->mode == TOFI_MODE_DRUN); + } else { + history_save(&entry->history, tofi->history_file); + } + } + return true; } -static void read_clipboard(struct tofi *tofi) -{ - struct entry *entry = &tofi->window.entry; - - /* Make a copy of any text after the cursor. */ - uint32_t *end_text = NULL; - size_t end_text_length = entry->input_utf32_length - entry->cursor_position; - if (end_text_length > 0) { - end_text = xcalloc(end_text_length, sizeof(*entry->input_utf32)); - memcpy(end_text, - &entry->input_utf32[entry->cursor_position], - end_text_length * sizeof(*entry->input_utf32)); - } - /* Buffer for 4 UTF-8 bytes plus a null terminator. */ - char buffer[5]; - memset(buffer, 0, N_ELEM(buffer)); - errno = 0; - bool eof = false; - while (entry->cursor_position < N_ELEM(entry->input_utf32)) { - for (size_t i = 0; i < 4; i++) { - /* - * Read input 1 byte at a time. This is slow, but easy, - * and speed of pasting shouldn't really matter. - */ - int res = read(tofi->clipboard.fd, &buffer[i], 1); - if (res == 0) { - eof = true; - break; - } else if (res == -1) { - if (errno == EAGAIN) { - /* - * There was no more data to be read, - * but EOF hasn't been reached yet. - * - * This could mean more than a pipe's - * capacity (64k) of data was sent, in - * which case we'd potentially skip - * a character, but we should hit the - * input length limit long before that. - */ - input_refresh_results(tofi); - tofi->window.surface.redraw = true; - return; - } - log_error("Failed to read clipboard: %s\n", strerror(errno)); - clipboard_finish_paste(&tofi->clipboard); - return; - } - uint32_t unichar = utf8_to_utf32_validate(buffer); - if (unichar == (uint32_t)-2) { - /* The current character isn't complete yet. */ - continue; - } else if (unichar == (uint32_t)-1) { - log_error("Invalid UTF-8 character in clipboard: %s\n", buffer); - break; - } else { - entry->input_utf32[entry->cursor_position] = unichar; - entry->cursor_position++; - break; - } - } - memset(buffer, 0, N_ELEM(buffer)); - if (eof) { - break; - } - } - entry->input_utf32_length = entry->cursor_position; - - /* If there was any text after the cursor, re-insert it now. */ - if (end_text != NULL) { - for (size_t i = 0; i < end_text_length; i++) { - if (entry->input_utf32_length == N_ELEM(entry->input_utf32)) { - break; - } - entry->input_utf32[entry->input_utf32_length] = end_text[i]; - entry->input_utf32_length++; - } - free(end_text); - } - entry->input_utf32[MIN(entry->input_utf32_length, N_ELEM(entry->input_utf32) - 1)] = U'\0'; - - clipboard_finish_paste(&tofi->clipboard); - - input_refresh_results(tofi); - tofi->window.surface.redraw = true; +static void read_clipboard(struct tofi *tofi) { + struct entry *entry = &tofi->window.entry; + + /* Make a copy of any text after the cursor. */ + uint32_t *end_text = NULL; + size_t end_text_length = entry->input_utf32_length - entry->cursor_position; + if (end_text_length > 0) { + end_text = xcalloc(end_text_length, sizeof(*entry->input_utf32)); + memcpy(end_text, &entry->input_utf32[entry->cursor_position], + end_text_length * sizeof(*entry->input_utf32)); + } + /* Buffer for 4 UTF-8 bytes plus a null terminator. */ + char buffer[5]; + memset(buffer, 0, N_ELEM(buffer)); + errno = 0; + bool eof = false; + while (entry->cursor_position < N_ELEM(entry->input_utf32)) { + for (size_t i = 0; i < 4; i++) { + /* + * Read input 1 byte at a time. This is slow, but easy, + * and speed of pasting shouldn't really matter. + */ + int res = read(tofi->clipboard.fd, &buffer[i], 1); + if (res == 0) { + eof = true; + break; + } else if (res == -1) { + if (errno == EAGAIN) { + /* + * There was no more data to be read, + * but EOF hasn't been reached yet. + * + * This could mean more than a pipe's + * capacity (64k) of data was sent, in + * which case we'd potentially skip + * a character, but we should hit the + * input length limit long before that. + */ + input_refresh_results(tofi); + tofi->window.surface.redraw = true; + return; + } + log_error("Failed to read clipboard: %s\n", strerror(errno)); + clipboard_finish_paste(&tofi->clipboard); + return; + } + uint32_t unichar = utf8_to_utf32_validate(buffer); + if (unichar == (uint32_t)-2) { + /* The current character isn't complete yet. */ + continue; + } else if (unichar == (uint32_t)-1) { + log_error("Invalid UTF-8 character in clipboard: %s\n", buffer); + break; + } else { + entry->input_utf32[entry->cursor_position] = unichar; + entry->cursor_position++; + break; + } + } + memset(buffer, 0, N_ELEM(buffer)); + if (eof) { + break; + } + } + entry->input_utf32_length = entry->cursor_position; + + /* If there was any text after the cursor, re-insert it now. */ + if (end_text != NULL) { + for (size_t i = 0; i < end_text_length; i++) { + if (entry->input_utf32_length == N_ELEM(entry->input_utf32)) { + break; + } + entry->input_utf32[entry->input_utf32_length] = end_text[i]; + entry->input_utf32_length++; + } + free(end_text); + } + entry->input_utf32[MIN(entry->input_utf32_length, + N_ELEM(entry->input_utf32) - 1)] = U'\0'; + + clipboard_finish_paste(&tofi->clipboard); + + input_refresh_results(tofi); + tofi->window.surface.redraw = true; } -int main(int argc, char *argv[]) -{ - /* Call log_debug to initialise the timers we use for perf checking. */ - log_debug("This is tofi.\n"); - - /* - * Set the locale to the user's default, so we can deal with non-ASCII - * characters. - */ - setlocale(LC_ALL, ""); - - /* Default options. */ - struct tofi tofi = { - .window = { - .scale = 1, - .width = 1280, - .height = 720, - .exclusive_zone = -1, - .entry = { - .font_name = "Sans", - .font_size = 24, - .prompt_text = "run: ", - .hidden_character_utf8 = u8"*", - .padding_top = 8, - .padding_bottom = 8, - .padding_left = 8, - .padding_right = 8, - .clip_to_padding = true, - .border_width = 12, - .outline_width = 4, - .background_color = {0.106f, 0.114f, 0.118f, 1.0f}, - .foreground_color = {1.0f, 1.0f, 1.0f, 1.0f}, - .border_color = {0.976f, 0.149f, 0.447f, 1.0f}, - .outline_color = {0.031f, 0.031f, 0.0f, 1.0f}, - .placeholder_theme.foreground_color = {1.0f, 1.0f, 1.0f, 0.66f}, - .placeholder_theme.foreground_specified = true, - .selection_theme.foreground_color = {0.976f, 0.149f, 0.447f, 1.0f}, - .selection_theme.foreground_specified = true, - .cursor_theme.thickness = 2 - } - }, - .anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, - .use_history = true, - .require_match = true, - .use_scale = true, - .physical_keybindings = true, - }; - wl_list_init(&tofi.output_list); - if (getenv("TERMINAL") != NULL) { - snprintf( - tofi.default_terminal, - N_ELEM(tofi.default_terminal), - "%s", - getenv("TERMINAL")); - } - - parse_args(&tofi, argc, argv); - log_debug("Config done.\n"); - - if (!tofi.multiple_instance && lock_check()) { - log_error("Another instance of tofi is already running.\n"); - exit(EXIT_FAILURE); - } - - /* - * Initial Wayland & XKB setup. - * The first thing to do is connect a listener to the global registry, - * so that we can bind to the various global objects and start talking - * to Wayland. - */ - - log_debug("Connecting to Wayland display.\n"); - tofi.wl_display = wl_display_connect(NULL); - if (tofi.wl_display == NULL) { - log_error("Couldn't connect to Wayland display.\n"); - exit(EXIT_FAILURE); - } - tofi.wl_registry = wl_display_get_registry(tofi.wl_display); - if (!tofi.late_keyboard_init) { - log_debug("Creating xkb context.\n"); - tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (tofi.xkb_context == NULL) { - log_error("Couldn't create an XKB context.\n"); - exit(EXIT_FAILURE); - } - } - wl_registry_add_listener( - tofi.wl_registry, - &wl_registry_listener, - &tofi); - - /* - * After this first roundtrip, the only thing that should have happened - * is our registry_global() function being called and setting up the - * various global object bindings. - */ - log_debug("First roundtrip start.\n"); - log_indent(); - wl_display_roundtrip(tofi.wl_display); - log_unindent(); - log_debug("First roundtrip done.\n"); - - /* - * The next roundtrip causes the listeners we set up in - * registry_global() to be called. Notably, the output should be - * configured, telling us the scale factor and size. - */ - log_debug("Second roundtrip start.\n"); - log_indent(); - wl_display_roundtrip(tofi.wl_display); - log_unindent(); - log_debug("Second roundtrip done.\n"); - - { - /* - * Determine the output we're going to appear on, and get its - * fractional scale if supported. - * - * This seems like an ugly solution, but as far as I know - * there's no way to determine the default output other than to - * call get_layer_surface with NULL as the output and see which - * output our surface turns up on. - * - * Additionally, determining fractional scale factors can - * currently only be done by attaching a wp_fractional_scale to - * a surface and displaying it. - * - * Here we set up a single pixel surface, perform the required - * two roundtrips, then tear it down. tofi.default_output - * should then contain the output our surface was assigned to, - * and tofi.window.fractional_scale should have the scale - * factor. - */ - log_debug("Determining output.\n"); - log_indent(); - struct surface surface = { - .width = 1, - .height = 1 - }; - surface.wl_surface = - wl_compositor_create_surface(tofi.wl_compositor); - wl_surface_add_listener( - surface.wl_surface, - &dummy_surface_listener, - &tofi); - - struct wp_fractional_scale_v1 *wp_fractional_scale = NULL; - if (tofi.wp_fractional_scale_manager != NULL) { - wp_fractional_scale = - wp_fractional_scale_manager_v1_get_fractional_scale( - tofi.wp_fractional_scale_manager, - surface.wl_surface); - wp_fractional_scale_v1_add_listener( - wp_fractional_scale, - &dummy_fractional_scale_listener, - &tofi); - } - - /* - * If we have a desired output, make sure we appear on it so we - * can determine the correct fractional scale. - */ - struct wl_output *wl_output = NULL; - if (tofi.target_output_name[0] != '\0') { - struct output_list_element *el; - wl_list_for_each(el, &tofi.output_list, link) { - if (!strcmp(tofi.target_output_name, el->name)) { - wl_output = el->wl_output; - break; - } - } - } - - struct zwlr_layer_surface_v1 *zwlr_layer_surface = - zwlr_layer_shell_v1_get_layer_surface( - tofi.zwlr_layer_shell, - surface.wl_surface, - wl_output, - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, - "dummy"); - /* - * Workaround for Hyprland, where if this is not set the dummy - * surface never enters an output for some reason. - */ - zwlr_layer_surface_v1_set_keyboard_interactivity( - zwlr_layer_surface, - ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE - ); - zwlr_layer_surface_v1_add_listener( - zwlr_layer_surface, - &dummy_layer_surface_listener, - &tofi); - zwlr_layer_surface_v1_set_size( - zwlr_layer_surface, - 1, - 1); - wl_surface_commit(surface.wl_surface); - log_debug("First dummy roundtrip start.\n"); - log_indent(); - wl_display_roundtrip(tofi.wl_display); - log_unindent(); - log_debug("First dummy roundtrip done.\n"); - log_debug("Initialising dummy surface.\n"); - log_indent(); - surface_init(&surface, tofi.wl_shm); - surface_draw(&surface); - log_unindent(); - log_debug("Dummy surface initialised.\n"); - log_debug("Second dummy roundtrip start.\n"); - log_indent(); - wl_display_roundtrip(tofi.wl_display); - log_unindent(); - log_debug("Second dummy roundtrip done.\n"); - surface_destroy(&surface); - zwlr_layer_surface_v1_destroy(zwlr_layer_surface); - if (wp_fractional_scale != NULL) { - wp_fractional_scale_v1_destroy(wp_fractional_scale); - } - wl_surface_destroy(surface.wl_surface); - - /* - * Walk through our output list and select the one we want if - * the user's asked for a specific one, otherwise just get the - * default one. - */ - bool found_target = false; - struct output_list_element *head; - head = wl_container_of(tofi.output_list.next, head, link); - - struct output_list_element *el; - struct output_list_element *tmp; - if (tofi.target_output_name[0] != 0) { - log_debug("Looking for output %s.\n", tofi.target_output_name); - } else if (tofi.default_output != NULL) { - snprintf( - tofi.target_output_name, - N_ELEM(tofi.target_output_name), - "%s", - tofi.default_output->name); - /* We don't need this anymore. */ - tofi.default_output = NULL; - } - wl_list_for_each_reverse_safe(el, tmp, &tofi.output_list, link) { - if (!strcmp(tofi.target_output_name, el->name)) { - found_target = true; - continue; - } - /* - * If we've already found the output we're looking for - * or this isn't the first output in the list, remove - * it. - */ - if (found_target || el != head) { - wl_list_remove(&el->link); - wl_output_release(el->wl_output); - free(el->name); - free(el); - } - } - - /* - * The only output left should either be the one we want, or - * the first that was advertised. - */ - el = wl_container_of(tofi.output_list.next, el, link); - - /* - * If we're rotated 90 degrees, we need to swap width and - * height to calculate percentages. - */ - switch (el->transform) { - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - tofi.output_width = el->height; - tofi.output_height = el->width; - break; - default: - tofi.output_width = el->width; - tofi.output_height = el->height; - } - tofi.window.scale = el->scale; - tofi.window.transform = el->transform; - log_unindent(); - log_debug("Selected output %s.\n", el->name); - } - - /* - * We can now scale values and calculate any percentages, as we know - * the output size and scale. - */ - config_fixup_values(&tofi); - - /* - * If we were invoked as tofi-run, generate the command list. - * If we were invoked as tofi-drun, generate the desktop app list. - * Otherwise, just read standard input. - */ - if (strstr(argv[0], "-run")) { - log_debug("Generating command list.\n"); - log_indent(); - tofi.window.entry.mode = TOFI_MODE_RUN; - tofi.window.entry.command_buffer = compgen_cached(); - struct string_ref_vec commands = string_ref_vec_from_buffer(tofi.window.entry.command_buffer); - if (tofi.use_history) { - if (tofi.history_file[0] == 0) { - tofi.window.entry.history = history_load_default_file(false); - } else { - tofi.window.entry.history = history_load(tofi.history_file); - } - tofi.window.entry.commands = compgen_history_sort(&commands, &tofi.window.entry.history); - string_ref_vec_destroy(&commands); - } else { - tofi.window.entry.commands = commands; - } - log_unindent(); - log_debug("Command list generated.\n"); - } else if (strstr(argv[0], "-drun")) { - log_debug("Generating desktop app list.\n"); - log_indent(); - tofi.window.entry.mode = TOFI_MODE_DRUN; - struct desktop_vec apps = drun_generate_cached(); - if (tofi.use_history) { - if (tofi.history_file[0] == 0) { - tofi.window.entry.history = history_load_default_file(true); - } else { - tofi.window.entry.history = history_load(tofi.history_file); - } - drun_history_sort(&apps, &tofi.window.entry.history); - } - struct string_ref_vec commands = string_ref_vec_create(); - for (size_t i = 0; i < apps.count; i++) { - string_ref_vec_add(&commands, apps.buf[i].name); - } - tofi.window.entry.commands = commands; - tofi.window.entry.apps = apps; - log_unindent(); - log_debug("App list generated.\n"); - } else { - log_debug("Reading stdin.\n"); - char *buf = read_stdin(!tofi.ascii_input); - tofi.window.entry.command_buffer = buf; - tofi.window.entry.commands = string_ref_vec_from_buffer(buf); - if (tofi.use_history) { - if (tofi.history_file[0] == 0) { - tofi.use_history = false; - } else { - tofi.window.entry.history = history_load(tofi.history_file); - string_ref_vec_history_sort(&tofi.window.entry.commands, &tofi.window.entry.history); - } - } - log_debug("Result list generated.\n"); - } - tofi.window.entry.results = string_ref_vec_copy(&tofi.window.entry.commands); - - if (tofi.auto_accept_single && tofi.window.entry.results.count == 1) { - log_debug("Only one result, exiting.\n"); - do_submit(&tofi); - return EXIT_SUCCESS; - } - - /* - * Next, we create the Wayland surface, which takes on the - * layer shell role. - */ - log_debug("Creating main window surface.\n"); - tofi.window.surface.wl_surface = - wl_compositor_create_surface(tofi.wl_compositor); - wl_surface_add_listener( - tofi.window.surface.wl_surface, - &wl_surface_listener, - &tofi); - if (tofi.window.width == 0 || tofi.window.height == 0) { - /* - * Workaround for compatibility with legacy behaviour. - * - * Before the fractional_scale protocol was released, there was - * no way for a client to know whether a fractional scale - * factor had been set, meaning percentage-based dimensions - * were incorrect. As a workaround for full-size windows, we - * allowed specifying 0 for the width / height, which caused - * zwlr_layer_shell to tell us the correct size to use. - * - * To make fractional scaling work, we have to use - * wp_viewporter, and no longer need to set the buffer scale. - * However, viewporter doesn't allow specifying 0 for - * destination width or height. As a workaround, if 0 size is - * set, don't use viewporter, warn the user and set the buffer - * scale here. - */ - log_warning("Width or height set to 0, disabling fractional scaling support.\n"); - log_warning("If your compositor supports the fractional scale protocol, percentages are preferred.\n"); - tofi.window.fractional_scale = 0; - wl_surface_set_buffer_scale( - tofi.window.surface.wl_surface, - tofi.window.scale); - } else if (tofi.wp_viewporter == NULL) { - /* - * We also could be running on a Wayland compositor which - * doesn't support wp_viewporter, in which case we need to use - * the old scaling method. - */ - log_warning("Using an outdated compositor, " - "fractional scaling will not work properly.\n"); - tofi.window.fractional_scale = 0; - wl_surface_set_buffer_scale( - tofi.window.surface.wl_surface, - tofi.window.scale); - } - - /* Grab the first (and only remaining) output from our list. */ - struct wl_output *wl_output; - { - struct output_list_element *el; - el = wl_container_of(tofi.output_list.next, el, link); - wl_output = el->wl_output; - } - - tofi.window.zwlr_layer_surface = zwlr_layer_shell_v1_get_layer_surface( - tofi.zwlr_layer_shell, - tofi.window.surface.wl_surface, - wl_output, - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - "launcher"); - zwlr_layer_surface_v1_set_keyboard_interactivity( - tofi.window.zwlr_layer_surface, - ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE); - zwlr_layer_surface_v1_add_listener( - tofi.window.zwlr_layer_surface, - &zwlr_layer_surface_listener, - &tofi); - zwlr_layer_surface_v1_set_anchor( - tofi.window.zwlr_layer_surface, - tofi.anchor); - zwlr_layer_surface_v1_set_exclusive_zone( - tofi.window.zwlr_layer_surface, - tofi.window.exclusive_zone); - zwlr_layer_surface_v1_set_margin( - tofi.window.zwlr_layer_surface, - tofi.window.margin_top, - tofi.window.margin_right, - tofi.window.margin_bottom, - tofi.window.margin_left); - /* - * No matter whether we're scaling via Cairo or not, we're presenting a - * scaled buffer to Wayland, so scale the window size here if we - * haven't already done so. - */ - zwlr_layer_surface_v1_set_size( - tofi.window.zwlr_layer_surface, - tofi.window.width, - tofi.window.height); - - /* - * Set up a viewport for our surface, necessary for fractional scaling. - */ - if (tofi.wp_viewporter != NULL) { - tofi.window.wp_viewport = wp_viewporter_get_viewport( - tofi.wp_viewporter, - tofi.window.surface.wl_surface); - if (tofi.window.width > 0 && tofi.window.height > 0) { - wp_viewport_set_destination( - tofi.window.wp_viewport, - tofi.window.width, - tofi.window.height); - } - } - - /* Commit the surface to finalise setup. */ - wl_surface_commit(tofi.window.surface.wl_surface); - - /* - * Create a data device and setup a listener for data offers. This is - * required for clipboard support. - */ - tofi.wl_data_device = wl_data_device_manager_get_data_device( - tofi.wl_data_device_manager, - tofi.wl_seat); - wl_data_device_add_listener( - tofi.wl_data_device, - &wl_data_device_listener, - &tofi.clipboard); - - /* - * Now that we've done all our Wayland-related setup, we do another - * roundtrip. This should cause the layer surface window to be - * configured, after which we're ready to start drawing to the screen. - */ - log_debug("Third roundtrip start.\n"); - log_indent(); - wl_display_roundtrip(tofi.wl_display); - log_unindent(); - log_debug("Third roundtrip done.\n"); - - - /* - * Create the various structures for our window surface. This needs to - * be done before calling entry_init as that performs some initial - * drawing, and surface_init allocates the buffers we'll be drawing to. - */ - log_debug("Initialising window surface.\n"); - log_indent(); - surface_init(&tofi.window.surface, tofi.wl_shm); - log_unindent(); - log_debug("Window surface initialised.\n"); - - /* - * Initialise the structures for rendering the entry. - * Cairo needs to know the size of the surface it's creating, and - * there's no way to resize it aside from tearing everything down and - * starting again, so we make sure to do this after we've determined - * our output's scale factor. This stops us being able to change the - * scale factor after startup, but this is just a launcher, which - * shouldn't be moving between outputs while running. - */ - log_debug("Initialising renderer.\n"); - log_indent(); - { - /* - * No matter how we're scaling (with fractions, integers or not - * at all), we pass a fractional scale factor (the numerator of - * a fraction with denominator 120) to our setup function for - * ease. - */ - uint32_t scale = 120; - if (tofi.use_scale) { - if (tofi.window.fractional_scale != 0) { - scale = tofi.window.fractional_scale; - } else { - scale = tofi.window.scale * 120; - } - } - entry_init( - &tofi.window.entry, - tofi.window.surface.shm_pool_data, - tofi.window.surface.width, - tofi.window.surface.height, - scale); - } - log_unindent(); - log_debug("Renderer initialised.\n"); - - /* Perform an initial render. */ - surface_draw(&tofi.window.surface); - - /* - * entry_init() left the second of the two buffers we use for - * double-buffering unpainted to lower startup time, as described - * there. Here, we flush our first, finished buffer to the screen, then - * copy over the image to the second buffer before we need to use it in - * the main loop. This ensures we paint to the screen as quickly as - * possible after startup. - */ - wl_display_roundtrip(tofi.wl_display); - log_debug("Initialising second buffer.\n"); - memcpy( - cairo_image_surface_get_data(tofi.window.entry.cairo[1].surface), - cairo_image_surface_get_data(tofi.window.entry.cairo[0].surface), - tofi.window.surface.width * tofi.window.surface.height * sizeof(uint32_t) - ); - log_debug("Second buffer initialised.\n"); - - /* We've just rendered, so we don't need to do it again right now. */ - tofi.window.surface.redraw = false; - - /* If we delayed keyboard initialisation, do it now */ - if (tofi.late_keyboard_init) { - log_debug("Creating xkb context.\n"); - tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (tofi.xkb_context == NULL) { - log_error("Couldn't create an XKB context.\n"); - exit(EXIT_FAILURE); - } - log_debug("Configuring keyboard.\n"); - struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( - tofi.xkb_context, - tofi.xkb_keymap_string, - XKB_KEYMAP_FORMAT_TEXT_V1, - XKB_KEYMAP_COMPILE_NO_FLAGS); - - struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); - xkb_keymap_unref(tofi.xkb_keymap); - xkb_state_unref(tofi.xkb_state); - tofi.xkb_keymap = xkb_keymap; - tofi.xkb_state = xkb_state; - free(tofi.xkb_keymap_string); - tofi.late_keyboard_init = false; - log_debug("Keyboard configured.\n"); - } - - /* - * Main event loop. - * See the wl_display(3) man page for an explanation of the - * order of the various functions called here. - */ - while (!tofi.closed) { - struct pollfd pollfds[2] = {{0}, {0}}; - pollfds[0].fd = wl_display_get_fd(tofi.wl_display); - - /* Make sure we're ready to receive events on the main queue. */ - while (wl_display_prepare_read(tofi.wl_display) != 0) { - wl_display_dispatch_pending(tofi.wl_display); - } - - /* Make sure all our requests have been sent to the server. */ - while (wl_display_flush(tofi.wl_display) != 0) { - pollfds[0].events = POLLOUT; - poll(&pollfds[0], 1, -1); - } - - /* - * Set time to wait for poll() to -1 (unlimited), unless - * there's some key repeating going on. - */ - int timeout = -1; - if (tofi.repeat.active) { - int64_t wait = (int64_t)tofi.repeat.next - (int64_t)gettime_ms(); - if (wait >= 0) { - timeout = wait; - } else { - timeout = 0; - } - } - - pollfds[0].events = POLLIN | POLLPRI; - int res; - if (tofi.clipboard.fd == 0) { - res = poll(&pollfds[0], 1, timeout); - } else { - /* - * We're trying to paste from the clipboard, which is - * done by reading from a pipe, so poll that file - * descriptor as well. - */ - pollfds[1].fd = tofi.clipboard.fd; - pollfds[1].events = POLLIN | POLLPRI; - res = poll(pollfds, 2, timeout); - } - if (res == 0) { - /* - * No events to process and no error - we presumably - * have a key repeat to handle. - */ - wl_display_cancel_read(tofi.wl_display); - if (tofi.repeat.active) { - int64_t wait = (int64_t)tofi.repeat.next - (int64_t)gettime_ms(); - if (wait <= 0) { - input_handle_keypress(&tofi, tofi.repeat.keycode); - tofi.repeat.next += 1000 / tofi.repeat.rate; - } - } - } else if (res < 0) { - /* There was an error polling the display. */ - wl_display_cancel_read(tofi.wl_display); - } else { - if (pollfds[0].revents & (POLLIN | POLLPRI)) { - /* Events to read, so put them on the queue. */ - wl_display_read_events(tofi.wl_display); - } else { - /* - * No events to read - we were woken up to - * handle clipboard data. - */ - wl_display_cancel_read(tofi.wl_display); - } - if (pollfds[1].revents & (POLLIN | POLLPRI)) { - /* Read clipboard data. */ - if (tofi.clipboard.fd > 0) { - read_clipboard(&tofi); - } - } - if (pollfds[1].revents & POLLHUP) { - /* - * The other end of the clipboard pipe has - * closed, cleanup. - */ - clipboard_finish_paste(&tofi.clipboard); - } - } - - /* Handle any events we read. */ - wl_display_dispatch_pending(tofi.wl_display); - - if (tofi.window.surface.redraw) { - entry_update(&tofi.window.entry); - surface_draw(&tofi.window.surface); - tofi.window.surface.redraw = false; - } - if (tofi.submit) { - tofi.submit = false; - if (do_submit(&tofi)) { - break; - } - } - - } - - log_debug("Window closed, performing cleanup.\n"); +int main(int argc, char *argv[]) { + /* Call log_debug to initialise the timers we use for perf checking. */ + log_debug("This is tofi.\n"); + + /* + * Set the locale to the user's default, so we can deal with non-ASCII + * characters. + */ + setlocale(LC_ALL, ""); + + /* Default options. */ + struct tofi tofi = { + .window = {.scale = 1, + .width = 1280, + .height = 720, + .exclusive_zone = -1, + .entry = {.font_name = "Sans", + .font_size = 24, + .prompt_text = "run: ", + .hidden_character_utf8 = u8"*", + .padding_top = 8, + .padding_bottom = 8, + .padding_left = 8, + .padding_right = 8, + .clip_to_padding = true, + .border_width = 12, + .outline_width = 4, + .background_color = {0.106f, 0.114f, 0.118f, 1.0f}, + .foreground_color = {1.0f, 1.0f, 1.0f, 1.0f}, + .border_color = {0.976f, 0.149f, 0.447f, 1.0f}, + .outline_color = {0.031f, 0.031f, 0.0f, 1.0f}, + .placeholder_theme.foreground_color = {1.0f, 1.0f, + 1.0f, 0.66f}, + .placeholder_theme.foreground_specified = true, + .selection_theme.foreground_color = {0.976f, 0.149f, + 0.447f, 1.0f}, + .selection_theme.foreground_specified = true, + .cursor_theme.thickness = 2}}, + .anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, + .use_history = true, + .require_match = true, + .use_scale = true, + .physical_keybindings = true, + }; + wl_list_init(&tofi.output_list); + if (getenv("TERMINAL") != NULL) { + snprintf(tofi.default_terminal, N_ELEM(tofi.default_terminal), "%s", + getenv("TERMINAL")); + } + + parse_args(&tofi, argc, argv); + log_debug("Config done.\n"); + + if (!tofi.multiple_instance && lock_check()) { + log_error("Another instance of tofi is already running.\n"); + exit(EXIT_FAILURE); + } + + /* + * Initial Wayland & XKB setup. + * The first thing to do is connect a listener to the global registry, + * so that we can bind to the various global objects and start talking + * to Wayland. + */ + + log_debug("Connecting to Wayland display.\n"); + tofi.wl_display = wl_display_connect(NULL); + if (tofi.wl_display == NULL) { + log_error("Couldn't connect to Wayland display.\n"); + exit(EXIT_FAILURE); + } + tofi.wl_registry = wl_display_get_registry(tofi.wl_display); + if (!tofi.late_keyboard_init) { + log_debug("Creating xkb context.\n"); + tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (tofi.xkb_context == NULL) { + log_error("Couldn't create an XKB context.\n"); + exit(EXIT_FAILURE); + } + } + wl_registry_add_listener(tofi.wl_registry, &wl_registry_listener, &tofi); + + /* + * After this first roundtrip, the only thing that should have happened + * is our registry_global() function being called and setting up the + * various global object bindings. + */ + log_debug("First roundtrip start.\n"); + log_indent(); + wl_display_roundtrip(tofi.wl_display); + log_unindent(); + log_debug("First roundtrip done.\n"); + + /* + * The next roundtrip causes the listeners we set up in + * registry_global() to be called. Notably, the output should be + * configured, telling us the scale factor and size. + */ + log_debug("Second roundtrip start.\n"); + log_indent(); + wl_display_roundtrip(tofi.wl_display); + log_unindent(); + log_debug("Second roundtrip done.\n"); + + { + /* + * Determine the output we're going to appear on, and get its + * fractional scale if supported. + * + * This seems like an ugly solution, but as far as I know + * there's no way to determine the default output other than to + * call get_layer_surface with NULL as the output and see which + * output our surface turns up on. + * + * Additionally, determining fractional scale factors can + * currently only be done by attaching a wp_fractional_scale to + * a surface and displaying it. + * + * Here we set up a single pixel surface, perform the required + * two roundtrips, then tear it down. tofi.default_output + * should then contain the output our surface was assigned to, + * and tofi.window.fractional_scale should have the scale + * factor. + */ + log_debug("Determining output.\n"); + log_indent(); + struct surface surface = {.width = 1, .height = 1}; + surface.wl_surface = wl_compositor_create_surface(tofi.wl_compositor); + wl_surface_add_listener(surface.wl_surface, &dummy_surface_listener, &tofi); + + struct wp_fractional_scale_v1 *wp_fractional_scale = NULL; + if (tofi.wp_fractional_scale_manager != NULL) { + wp_fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale( + tofi.wp_fractional_scale_manager, surface.wl_surface); + wp_fractional_scale_v1_add_listener( + wp_fractional_scale, &dummy_fractional_scale_listener, &tofi); + } + + /* + * If we have a desired output, make sure we appear on it so we + * can determine the correct fractional scale. + */ + struct wl_output *wl_output = NULL; + if (tofi.target_output_name[0] != '\0') { + struct output_list_element *el; + wl_list_for_each(el, &tofi.output_list, link) { + if (!strcmp(tofi.target_output_name, el->name)) { + wl_output = el->wl_output; + break; + } + } + } + + struct zwlr_layer_surface_v1 *zwlr_layer_surface = + zwlr_layer_shell_v1_get_layer_surface( + tofi.zwlr_layer_shell, surface.wl_surface, wl_output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "dummy"); + /* + * Workaround for Hyprland, where if this is not set the dummy + * surface never enters an output for some reason. + */ + zwlr_layer_surface_v1_set_keyboard_interactivity( + zwlr_layer_surface, + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE); + zwlr_layer_surface_v1_add_listener(zwlr_layer_surface, + &dummy_layer_surface_listener, &tofi); + zwlr_layer_surface_v1_set_size(zwlr_layer_surface, 1, 1); + wl_surface_commit(surface.wl_surface); + log_debug("First dummy roundtrip start.\n"); + log_indent(); + wl_display_roundtrip(tofi.wl_display); + log_unindent(); + log_debug("First dummy roundtrip done.\n"); + log_debug("Initialising dummy surface.\n"); + log_indent(); + surface_init(&surface, tofi.wl_shm); + surface_draw(&surface); + log_unindent(); + log_debug("Dummy surface initialised.\n"); + log_debug("Second dummy roundtrip start.\n"); + log_indent(); + wl_display_roundtrip(tofi.wl_display); + log_unindent(); + log_debug("Second dummy roundtrip done.\n"); + surface_destroy(&surface); + zwlr_layer_surface_v1_destroy(zwlr_layer_surface); + if (wp_fractional_scale != NULL) { + wp_fractional_scale_v1_destroy(wp_fractional_scale); + } + wl_surface_destroy(surface.wl_surface); + + /* + * Walk through our output list and select the one we want if + * the user's asked for a specific one, otherwise just get the + * default one. + */ + bool found_target = false; + struct output_list_element *head; + head = wl_container_of(tofi.output_list.next, head, link); + + struct output_list_element *el; + struct output_list_element *tmp; + if (tofi.target_output_name[0] != 0) { + log_debug("Looking for output %s.\n", tofi.target_output_name); + } else if (tofi.default_output != NULL) { + snprintf(tofi.target_output_name, N_ELEM(tofi.target_output_name), "%s", + tofi.default_output->name); + /* We don't need this anymore. */ + tofi.default_output = NULL; + } + wl_list_for_each_reverse_safe(el, tmp, &tofi.output_list, link) { + if (!strcmp(tofi.target_output_name, el->name)) { + found_target = true; + continue; + } + /* + * If we've already found the output we're looking for + * or this isn't the first output in the list, remove + * it. + */ + if (found_target || el != head) { + wl_list_remove(&el->link); + wl_output_release(el->wl_output); + free(el->name); + free(el); + } + } + + /* + * The only output left should either be the one we want, or + * the first that was advertised. + */ + el = wl_container_of(tofi.output_list.next, el, link); + + /* + * If we're rotated 90 degrees, we need to swap width and + * height to calculate percentages. + */ + switch (el->transform) { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + tofi.output_width = el->height; + tofi.output_height = el->width; + break; + default: + tofi.output_width = el->width; + tofi.output_height = el->height; + } + tofi.window.scale = el->scale; + tofi.window.transform = el->transform; + log_unindent(); + log_debug("Selected output %s.\n", el->name); + } + + /* + * We can now scale values and calculate any percentages, as we know + * the output size and scale. + */ + config_fixup_values(&tofi); + + /* + * If we were invoked as tofi-run, generate the command list. + * If we were invoked as tofi-drun, generate the desktop app list. + * Otherwise, just read standard input. + */ + if (strstr(argv[0], "-run")) { + log_debug("Generating command list.\n"); + log_indent(); + tofi.window.entry.mode = TOFI_MODE_RUN; + tofi.window.entry.command_buffer = compgen_cached(); + struct string_ref_vec commands = + string_ref_vec_from_buffer(tofi.window.entry.command_buffer); + if (tofi.use_history) { + if (tofi.history_file[0] == 0) { + tofi.window.entry.history = history_load_default_file(false); + } else { + tofi.window.entry.history = history_load(tofi.history_file); + } + tofi.window.entry.commands = + compgen_history_sort(&commands, &tofi.window.entry.history); + string_ref_vec_destroy(&commands); + } else { + tofi.window.entry.commands = commands; + } + log_unindent(); + log_debug("Command list generated.\n"); + } else if (strstr(argv[0], "-drun")) { + log_debug("Generating desktop app list.\n"); + log_indent(); + tofi.window.entry.mode = TOFI_MODE_DRUN; + struct desktop_vec apps = drun_generate_cached(); + if (tofi.use_history) { + if (tofi.history_file[0] == 0) { + tofi.window.entry.history = history_load_default_file(true); + } else { + tofi.window.entry.history = history_load(tofi.history_file); + } + drun_history_sort(&apps, &tofi.window.entry.history); + } + struct string_ref_vec commands = string_ref_vec_create(); + for (size_t i = 0; i < apps.count; i++) { + string_ref_vec_add(&commands, apps.buf[i].name); + } + tofi.window.entry.commands = commands; + tofi.window.entry.apps = apps; + log_unindent(); + log_debug("App list generated.\n"); + } else { + log_debug("Reading stdin.\n"); + char *buf = read_stdin(!tofi.ascii_input); + tofi.window.entry.command_buffer = buf; + tofi.window.entry.commands = string_ref_vec_from_buffer(buf); + if (tofi.use_history) { + if (tofi.history_file[0] == 0) { + tofi.use_history = false; + } else { + tofi.window.entry.history = history_load(tofi.history_file); + string_ref_vec_history_sort(&tofi.window.entry.commands, + &tofi.window.entry.history); + } + } + log_debug("Result list generated.\n"); + } + tofi.window.entry.results = string_ref_vec_copy(&tofi.window.entry.commands); + + if (tofi.auto_accept_single && tofi.window.entry.results.count == 1) { + log_debug("Only one result, exiting.\n"); + do_submit(&tofi); + return EXIT_SUCCESS; + } + + /* + * Next, we create the Wayland surface, which takes on the + * layer shell role. + */ + log_debug("Creating main window surface.\n"); + tofi.window.surface.wl_surface = + wl_compositor_create_surface(tofi.wl_compositor); + wl_surface_add_listener(tofi.window.surface.wl_surface, &wl_surface_listener, + &tofi); + if (tofi.window.width == 0 || tofi.window.height == 0) { + /* + * Workaround for compatibility with legacy behaviour. + * + * Before the fractional_scale protocol was released, there was + * no way for a client to know whether a fractional scale + * factor had been set, meaning percentage-based dimensions + * were incorrect. As a workaround for full-size windows, we + * allowed specifying 0 for the width / height, which caused + * zwlr_layer_shell to tell us the correct size to use. + * + * To make fractional scaling work, we have to use + * wp_viewporter, and no longer need to set the buffer scale. + * However, viewporter doesn't allow specifying 0 for + * destination width or height. As a workaround, if 0 size is + * set, don't use viewporter, warn the user and set the buffer + * scale here. + */ + log_warning( + "Width or height set to 0, disabling fractional scaling support.\n"); + log_warning("If your compositor supports the fractional scale protocol, " + "percentages are preferred.\n"); + tofi.window.fractional_scale = 0; + wl_surface_set_buffer_scale(tofi.window.surface.wl_surface, + tofi.window.scale); + } else if (tofi.wp_viewporter == NULL) { + /* + * We also could be running on a Wayland compositor which + * doesn't support wp_viewporter, in which case we need to use + * the old scaling method. + */ + log_warning("Using an outdated compositor, " + "fractional scaling will not work properly.\n"); + tofi.window.fractional_scale = 0; + wl_surface_set_buffer_scale(tofi.window.surface.wl_surface, + tofi.window.scale); + } + + /* Grab the first (and only remaining) output from our list. */ + struct wl_output *wl_output; + { + struct output_list_element *el; + el = wl_container_of(tofi.output_list.next, el, link); + wl_output = el->wl_output; + } + + tofi.window.zwlr_layer_surface = zwlr_layer_shell_v1_get_layer_surface( + tofi.zwlr_layer_shell, tofi.window.surface.wl_surface, wl_output, + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "launcher"); + zwlr_layer_surface_v1_set_keyboard_interactivity( + tofi.window.zwlr_layer_surface, + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE); + zwlr_layer_surface_v1_add_listener(tofi.window.zwlr_layer_surface, + &zwlr_layer_surface_listener, &tofi); + zwlr_layer_surface_v1_set_anchor(tofi.window.zwlr_layer_surface, tofi.anchor); + zwlr_layer_surface_v1_set_exclusive_zone(tofi.window.zwlr_layer_surface, + tofi.window.exclusive_zone); + zwlr_layer_surface_v1_set_margin( + tofi.window.zwlr_layer_surface, tofi.window.margin_top, + tofi.window.margin_right, tofi.window.margin_bottom, + tofi.window.margin_left); + /* + * No matter whether we're scaling via Cairo or not, we're presenting a + * scaled buffer to Wayland, so scale the window size here if we + * haven't already done so. + */ + zwlr_layer_surface_v1_set_size(tofi.window.zwlr_layer_surface, + tofi.window.width, tofi.window.height); + + /* + * Set up a viewport for our surface, necessary for fractional scaling. + */ + if (tofi.wp_viewporter != NULL) { + tofi.window.wp_viewport = wp_viewporter_get_viewport( + tofi.wp_viewporter, tofi.window.surface.wl_surface); + if (tofi.window.width > 0 && tofi.window.height > 0) { + wp_viewport_set_destination(tofi.window.wp_viewport, tofi.window.width, + tofi.window.height); + } + } + + /* Commit the surface to finalise setup. */ + wl_surface_commit(tofi.window.surface.wl_surface); + + /* + * Create a data device and setup a listener for data offers. This is + * required for clipboard support. + */ + tofi.wl_data_device = wl_data_device_manager_get_data_device( + tofi.wl_data_device_manager, tofi.wl_seat); + wl_data_device_add_listener(tofi.wl_data_device, &wl_data_device_listener, + &tofi.clipboard); + + /* + * Now that we've done all our Wayland-related setup, we do another + * roundtrip. This should cause the layer surface window to be + * configured, after which we're ready to start drawing to the screen. + */ + log_debug("Third roundtrip start.\n"); + log_indent(); + wl_display_roundtrip(tofi.wl_display); + log_unindent(); + log_debug("Third roundtrip done.\n"); + + /* + * Create the various structures for our window surface. This needs to + * be done before calling entry_init as that performs some initial + * drawing, and surface_init allocates the buffers we'll be drawing to. + */ + log_debug("Initialising window surface.\n"); + log_indent(); + surface_init(&tofi.window.surface, tofi.wl_shm); + log_unindent(); + log_debug("Window surface initialised.\n"); + + /* + * Initialise the structures for rendering the entry. + * Cairo needs to know the size of the surface it's creating, and + * there's no way to resize it aside from tearing everything down and + * starting again, so we make sure to do this after we've determined + * our output's scale factor. This stops us being able to change the + * scale factor after startup, but this is just a launcher, which + * shouldn't be moving between outputs while running. + */ + log_debug("Initialising renderer.\n"); + log_indent(); + { + /* + * No matter how we're scaling (with fractions, integers or not + * at all), we pass a fractional scale factor (the numerator of + * a fraction with denominator 120) to our setup function for + * ease. + */ + uint32_t scale = 120; + if (tofi.use_scale) { + if (tofi.window.fractional_scale != 0) { + scale = tofi.window.fractional_scale; + } else { + scale = tofi.window.scale * 120; + } + } + entry_init(&tofi.window.entry, tofi.window.surface.shm_pool_data, + tofi.window.surface.width, tofi.window.surface.height, scale); + } + log_unindent(); + log_debug("Renderer initialised.\n"); + + /* Perform an initial render. */ + surface_draw(&tofi.window.surface); + + /* + * entry_init() left the second of the two buffers we use for + * double-buffering unpainted to lower startup time, as described + * there. Here, we flush our first, finished buffer to the screen, then + * copy over the image to the second buffer before we need to use it in + * the main loop. This ensures we paint to the screen as quickly as + * possible after startup. + */ + wl_display_roundtrip(tofi.wl_display); + log_debug("Initialising second buffer.\n"); + memcpy(cairo_image_surface_get_data(tofi.window.entry.cairo[1].surface), + cairo_image_surface_get_data(tofi.window.entry.cairo[0].surface), + tofi.window.surface.width * tofi.window.surface.height * + sizeof(uint32_t)); + log_debug("Second buffer initialised.\n"); + + /* We've just rendered, so we don't need to do it again right now. */ + tofi.window.surface.redraw = false; + + /* If we delayed keyboard initialisation, do it now */ + if (tofi.late_keyboard_init) { + log_debug("Creating xkb context.\n"); + tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (tofi.xkb_context == NULL) { + log_error("Couldn't create an XKB context.\n"); + exit(EXIT_FAILURE); + } + log_debug("Configuring keyboard.\n"); + struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( + tofi.xkb_context, tofi.xkb_keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); + xkb_keymap_unref(tofi.xkb_keymap); + xkb_state_unref(tofi.xkb_state); + tofi.xkb_keymap = xkb_keymap; + tofi.xkb_state = xkb_state; + free(tofi.xkb_keymap_string); + tofi.late_keyboard_init = false; + log_debug("Keyboard configured.\n"); + } + + /* + * Main event loop. + * See the wl_display(3) man page for an explanation of the + * order of the various functions called here. + */ + while (!tofi.closed) { + struct pollfd pollfds[2] = {{0}, {0}}; + pollfds[0].fd = wl_display_get_fd(tofi.wl_display); + + /* Make sure we're ready to receive events on the main queue. */ + while (wl_display_prepare_read(tofi.wl_display) != 0) { + wl_display_dispatch_pending(tofi.wl_display); + } + + /* Make sure all our requests have been sent to the server. */ + while (wl_display_flush(tofi.wl_display) != 0) { + pollfds[0].events = POLLOUT; + poll(&pollfds[0], 1, -1); + } + + /* + * Set time to wait for poll() to -1 (unlimited), unless + * there's some key repeating going on. + */ + int timeout = -1; + if (tofi.repeat.active) { + int64_t wait = (int64_t)tofi.repeat.next - (int64_t)gettime_ms(); + if (wait >= 0) { + timeout = wait; + } else { + timeout = 0; + } + } + + pollfds[0].events = POLLIN | POLLPRI; + int res; + if (tofi.clipboard.fd == 0) { + res = poll(&pollfds[0], 1, timeout); + } else { + /* + * We're trying to paste from the clipboard, which is + * done by reading from a pipe, so poll that file + * descriptor as well. + */ + pollfds[1].fd = tofi.clipboard.fd; + pollfds[1].events = POLLIN | POLLPRI; + res = poll(pollfds, 2, timeout); + } + if (res == 0) { + /* + * No events to process and no error - we presumably + * have a key repeat to handle. + */ + wl_display_cancel_read(tofi.wl_display); + if (tofi.repeat.active) { + int64_t wait = (int64_t)tofi.repeat.next - (int64_t)gettime_ms(); + if (wait <= 0) { + input_handle_keypress(&tofi, tofi.repeat.keycode); + tofi.repeat.next += 1000 / tofi.repeat.rate; + } + } + } else if (res < 0) { + /* There was an error polling the display. */ + wl_display_cancel_read(tofi.wl_display); + } else { + if (pollfds[0].revents & (POLLIN | POLLPRI)) { + /* Events to read, so put them on the queue. */ + wl_display_read_events(tofi.wl_display); + } else { + /* + * No events to read - we were woken up to + * handle clipboard data. + */ + wl_display_cancel_read(tofi.wl_display); + } + if (pollfds[1].revents & (POLLIN | POLLPRI)) { + /* Read clipboard data. */ + if (tofi.clipboard.fd > 0) { + read_clipboard(&tofi); + } + } + if (pollfds[1].revents & POLLHUP) { + /* + * The other end of the clipboard pipe has + * closed, cleanup. + */ + clipboard_finish_paste(&tofi.clipboard); + } + } + + /* Handle any events we read. */ + wl_display_dispatch_pending(tofi.wl_display); + + if (tofi.window.surface.redraw) { + entry_update(&tofi.window.entry); + surface_draw(&tofi.window.surface); + tofi.window.surface.redraw = false; + } + if (tofi.submit) { + tofi.submit = false; + if (do_submit(&tofi)) { + break; + } + } + } + + log_debug("Window closed, performing cleanup.\n"); #ifdef DEBUG - /* - * For debug builds, try to cleanup as much as possible, to make using - * e.g. Valgrind easier. There's still a few unavoidable leaks though, - * mostly from Pango, and Cairo holds onto quite a bit of cached data - * (without leaking it) - */ - surface_destroy(&tofi.window.surface); - entry_destroy(&tofi.window.entry); - if (tofi.window.wp_viewport != NULL) { - wp_viewport_destroy(tofi.window.wp_viewport); - } - zwlr_layer_surface_v1_destroy(tofi.window.zwlr_layer_surface); - wl_surface_destroy(tofi.window.surface.wl_surface); - if (tofi.wl_keyboard != NULL) { - wl_keyboard_release(tofi.wl_keyboard); - } - if (tofi.wl_pointer != NULL) { - wl_pointer_release(tofi.wl_pointer); - } - wl_compositor_destroy(tofi.wl_compositor); - if (tofi.clipboard.wl_data_offer != NULL) { - wl_data_offer_destroy(tofi.clipboard.wl_data_offer); - } - wl_data_device_release(tofi.wl_data_device); - wl_data_device_manager_destroy(tofi.wl_data_device_manager); - wl_seat_release(tofi.wl_seat); - { - struct output_list_element *el; - struct output_list_element *tmp; - wl_list_for_each_safe(el, tmp, &tofi.output_list, link) { - wl_list_remove(&el->link); - wl_output_release(el->wl_output); - free(el->name); - free(el); - } - } - wl_shm_destroy(tofi.wl_shm); - if (tofi.wp_fractional_scale_manager != NULL) { - wp_fractional_scale_manager_v1_destroy(tofi.wp_fractional_scale_manager); - } - if (tofi.wp_viewporter != NULL) { - wp_viewporter_destroy(tofi.wp_viewporter); - } - zwlr_layer_shell_v1_destroy(tofi.zwlr_layer_shell); - xkb_state_unref(tofi.xkb_state); - xkb_keymap_unref(tofi.xkb_keymap); - xkb_context_unref(tofi.xkb_context); - wl_registry_destroy(tofi.wl_registry); - if (tofi.window.entry.mode == TOFI_MODE_DRUN) { - desktop_vec_destroy(&tofi.window.entry.apps); - } - if (tofi.window.entry.command_buffer != NULL) { - free(tofi.window.entry.command_buffer); - } - string_ref_vec_destroy(&tofi.window.entry.commands); - string_ref_vec_destroy(&tofi.window.entry.results); - if (tofi.use_history) { - history_destroy(&tofi.window.entry.history); - } + /* + * For debug builds, try to cleanup as much as possible, to make using + * e.g. Valgrind easier. There's still a few unavoidable leaks though, + * mostly from Pango, and Cairo holds onto quite a bit of cached data + * (without leaking it) + */ + surface_destroy(&tofi.window.surface); + entry_destroy(&tofi.window.entry); + if (tofi.window.wp_viewport != NULL) { + wp_viewport_destroy(tofi.window.wp_viewport); + } + zwlr_layer_surface_v1_destroy(tofi.window.zwlr_layer_surface); + wl_surface_destroy(tofi.window.surface.wl_surface); + if (tofi.wl_keyboard != NULL) { + wl_keyboard_release(tofi.wl_keyboard); + } + if (tofi.wl_pointer != NULL) { + wl_pointer_release(tofi.wl_pointer); + } + wl_compositor_destroy(tofi.wl_compositor); + if (tofi.clipboard.wl_data_offer != NULL) { + wl_data_offer_destroy(tofi.clipboard.wl_data_offer); + } + wl_data_device_release(tofi.wl_data_device); + wl_data_device_manager_destroy(tofi.wl_data_device_manager); + wl_seat_release(tofi.wl_seat); + { + struct output_list_element *el; + struct output_list_element *tmp; + wl_list_for_each_safe(el, tmp, &tofi.output_list, link) { + wl_list_remove(&el->link); + wl_output_release(el->wl_output); + free(el->name); + free(el); + } + } + wl_shm_destroy(tofi.wl_shm); + if (tofi.wp_fractional_scale_manager != NULL) { + wp_fractional_scale_manager_v1_destroy(tofi.wp_fractional_scale_manager); + } + if (tofi.wp_viewporter != NULL) { + wp_viewporter_destroy(tofi.wp_viewporter); + } + zwlr_layer_shell_v1_destroy(tofi.zwlr_layer_shell); + xkb_state_unref(tofi.xkb_state); + xkb_keymap_unref(tofi.xkb_keymap); + xkb_context_unref(tofi.xkb_context); + wl_registry_destroy(tofi.wl_registry); + if (tofi.window.entry.mode == TOFI_MODE_DRUN) { + desktop_vec_destroy(&tofi.window.entry.apps); + } + if (tofi.window.entry.command_buffer != NULL) { + free(tofi.window.entry.command_buffer); + } + string_ref_vec_destroy(&tofi.window.entry.commands); + string_ref_vec_destroy(&tofi.window.entry.results); + if (tofi.use_history) { + history_destroy(&tofi.window.entry.history); + } #endif - /* - * For release builds, skip straight to display disconnection and quit. - */ - wl_display_roundtrip(tofi.wl_display); - wl_display_disconnect(tofi.wl_display); - - log_debug("Finished, exiting.\n"); - if (tofi.closed) { - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + /* + * For release builds, skip straight to display disconnection and quit. + */ + wl_display_roundtrip(tofi.wl_display); + wl_display_disconnect(tofi.wl_display); + + log_debug("Finished, exiting.\n"); + if (tofi.closed) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/src/meson-info/intro-benchmarks.json b/src/meson-info/intro-benchmarks.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/src/meson-info/intro-benchmarks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/meson-info/intro-buildoptions.json b/src/meson-info/intro-buildoptions.json new file mode 100644 index 0000000..80c3373 --- /dev/null +++ b/src/meson-info/intro-buildoptions.json @@ -0,0 +1,662 @@ +[ + { + "name": "build.cmake_prefix_path", + "value": [], + "section": "core", + "machine": "build", + "type": "array", + "description": "List of additional prefixes for cmake to search" + }, + { + "name": "build.pkg_config_path", + "value": [], + "section": "core", + "machine": "build", + "type": "array", + "description": "List of additional paths for pkg-config to search" + }, + { + "name": "auto_features", + "value": "auto", + "section": "core", + "machine": "any", + "choices": [ + "enabled", + "disabled", + "auto" + ], + "type": "combo", + "description": "Override value of all 'auto' features" + }, + { + "name": "backend", + "value": "ninja", + "section": "core", + "machine": "any", + "choices": [ + "ninja", + "vs", + "vs2010", + "vs2012", + "vs2013", + "vs2015", + "vs2017", + "vs2019", + "vs2022", + "xcode", + "none" + ], + "type": "combo", + "description": "Backend to use" + }, + { + "name": "buildtype", + "value": "debugoptimized", + "section": "core", + "machine": "any", + "choices": [ + "plain", + "debug", + "debugoptimized", + "release", + "minsize", + "custom" + ], + "type": "combo", + "description": "Build type to use" + }, + { + "name": "cmake_prefix_path", + "value": [], + "section": "core", + "machine": "host", + "type": "array", + "description": "List of additional prefixes for cmake to search" + }, + { + "name": "debug", + "value": true, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Enable debug symbols and other information" + }, + { + "name": "default_both_libraries", + "value": "shared", + "section": "core", + "machine": "any", + "choices": [ + "shared", + "static", + "auto" + ], + "type": "combo", + "description": "Default library type for both_libraries" + }, + { + "name": "default_library", + "value": "shared", + "section": "core", + "machine": "any", + "choices": [ + "shared", + "static", + "both" + ], + "type": "combo", + "description": "Default library type" + }, + { + "name": "force_fallback_for", + "value": [], + "section": "core", + "machine": "any", + "type": "array", + "description": "Force fallback for those subprojects" + }, + { + "name": "genvslite", + "value": "vs2022", + "section": "core", + "machine": "any", + "choices": [ + "vs2022" + ], + "type": "combo", + "description": "Setup multiple buildtype-suffixed ninja-backend build directories, and a [builddir]_vs containing a Visual Studio meta-backend with multiple configurations that calls into them" + }, + { + "name": "install_umask", + "value": 18, + "section": "core", + "machine": "any", + "type": "integer", + "description": "Default umask to apply on permissions of installed files" + }, + { + "name": "layout", + "value": "mirror", + "section": "core", + "machine": "any", + "choices": [ + "mirror", + "flat" + ], + "type": "combo", + "description": "Build directory layout" + }, + { + "name": "optimization", + "value": "2", + "section": "core", + "machine": "any", + "choices": [ + "plain", + "0", + "g", + "1", + "2", + "3", + "s" + ], + "type": "combo", + "description": "Optimization level" + }, + { + "name": "pkg_config_path", + "value": [], + "section": "core", + "machine": "host", + "type": "array", + "description": "List of additional paths for pkg-config to search" + }, + { + "name": "pkgconfig.relocatable", + "value": false, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Generate pkgconfig files as relocatable" + }, + { + "name": "prefer_static", + "value": false, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Whether to try static linking before shared linking" + }, + { + "name": "python.allow_limited_api", + "value": true, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Whether to allow use of the Python Limited API" + }, + { + "name": "python.bytecompile", + "value": 0, + "section": "core", + "machine": "any", + "type": "integer", + "description": "Whether to compile bytecode" + }, + { + "name": "python.install_env", + "value": "prefix", + "section": "core", + "machine": "any", + "choices": [ + "auto", + "prefix", + "system", + "venv" + ], + "type": "combo", + "description": "Which python environment to install to" + }, + { + "name": "python.platlibdir", + "value": "", + "section": "core", + "machine": "any", + "type": "string", + "description": "Directory for site-specific, platform-specific files." + }, + { + "name": "python.purelibdir", + "value": "", + "section": "core", + "machine": "any", + "type": "string", + "description": "Directory for site-specific, non-platform-specific files." + }, + { + "name": "strip", + "value": false, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Strip targets on install" + }, + { + "name": "unity", + "value": "off", + "section": "core", + "machine": "any", + "choices": [ + "on", + "off", + "subprojects" + ], + "type": "combo", + "description": "Unity build" + }, + { + "name": "unity_size", + "value": 4, + "section": "core", + "machine": "any", + "type": "integer", + "description": "Unity block size" + }, + { + "name": "vsenv", + "value": false, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Activate Visual Studio environment" + }, + { + "name": "warning_level", + "value": "3", + "section": "core", + "machine": "any", + "choices": [ + "0", + "1", + "2", + "3", + "everything" + ], + "type": "combo", + "description": "Compiler warning level to use" + }, + { + "name": "werror", + "value": false, + "section": "core", + "machine": "any", + "type": "boolean", + "description": "Treat warnings as errors" + }, + { + "name": "wrap_mode", + "value": "default", + "section": "core", + "machine": "any", + "choices": [ + "default", + "nofallback", + "nodownload", + "forcefallback", + "nopromote" + ], + "type": "combo", + "description": "Wrap mode" + }, + { + "name": "backend_max_links", + "value": 0, + "section": "backend", + "machine": "any", + "type": "integer", + "description": "Maximum number of linker processes to run or 0 for no limit" + }, + { + "name": "b_asneeded", + "value": true, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Use -Wl,--as-needed when linking" + }, + { + "name": "b_colorout", + "value": "always", + "section": "base", + "machine": "any", + "choices": [ + "auto", + "always", + "never" + ], + "type": "combo", + "description": "Use colored output" + }, + { + "name": "b_coverage", + "value": false, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Enable coverage tracking." + }, + { + "name": "b_lto", + "value": true, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Use link time optimization" + }, + { + "name": "b_lto_threads", + "value": -1, + "section": "base", + "machine": "any", + "type": "integer", + "description": "Use multiple threads for Link Time Optimization" + }, + { + "name": "b_lundef", + "value": true, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Use -Wl,--no-undefined when linking" + }, + { + "name": "b_ndebug", + "value": "false", + "section": "base", + "machine": "any", + "choices": [ + "true", + "false", + "if-release" + ], + "type": "combo", + "description": "Disable asserts" + }, + { + "name": "b_pch", + "value": true, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Use precompiled headers" + }, + { + "name": "b_pgo", + "value": "off", + "section": "base", + "machine": "any", + "choices": [ + "off", + "generate", + "use" + ], + "type": "combo", + "description": "Use profile guided optimization" + }, + { + "name": "b_pie", + "value": true, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Build executables as position independent" + }, + { + "name": "b_sanitize", + "value": "none", + "section": "base", + "machine": "any", + "choices": [ + "none", + "address", + "thread", + "undefined", + "memory", + "leak", + "address,undefined" + ], + "type": "combo", + "description": "Code sanitizer to use" + }, + { + "name": "b_staticpic", + "value": true, + "section": "base", + "machine": "any", + "type": "boolean", + "description": "Build static libraries as position independent" + }, + { + "name": "build.c_args", + "value": [], + "section": "compiler", + "machine": "build", + "type": "array", + "description": "Extra arguments passed to the c compiler" + }, + { + "name": "build.c_link_args", + "value": [], + "section": "compiler", + "machine": "build", + "type": "array", + "description": "Extra arguments passed to the c linker" + }, + { + "name": "build.c_std", + "value": "c2x", + "section": "compiler", + "machine": "build", + "choices": [ + "none", + "c89", + "c99", + "c11", + "c17", + "c18", + "c2x", + "c23", + "gnu89", + "gnu99", + "gnu11", + "gnu17", + "gnu18", + "gnu2x", + "gnu23" + ], + "type": "combo", + "description": "C language standard to use" + }, + { + "name": "c_args", + "value": [], + "section": "compiler", + "machine": "host", + "type": "array", + "description": "Extra arguments passed to the c compiler" + }, + { + "name": "c_link_args", + "value": [], + "section": "compiler", + "machine": "host", + "type": "array", + "description": "Extra arguments passed to the c linker" + }, + { + "name": "c_std", + "value": "c2x", + "section": "compiler", + "machine": "host", + "choices": [ + "none", + "c89", + "c99", + "c11", + "c17", + "c18", + "c2x", + "c23", + "gnu89", + "gnu99", + "gnu11", + "gnu17", + "gnu18", + "gnu2x", + "gnu23" + ], + "type": "combo", + "description": "C language standard to use" + }, + { + "name": "bindir", + "value": "bin", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Executable directory" + }, + { + "name": "datadir", + "value": "share", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Data file directory" + }, + { + "name": "includedir", + "value": "include", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Header file directory" + }, + { + "name": "infodir", + "value": "share/info", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Info page directory" + }, + { + "name": "libdir", + "value": "lib", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Library directory" + }, + { + "name": "libexecdir", + "value": "libexec", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Library executable directory" + }, + { + "name": "licensedir", + "value": "", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Licenses directory" + }, + { + "name": "localedir", + "value": "share/locale", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Locale data directory" + }, + { + "name": "localstatedir", + "value": "/var", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Localstate data directory" + }, + { + "name": "mandir", + "value": "share/man", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Manual page directory" + }, + { + "name": "prefix", + "value": "/usr", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Installation prefix" + }, + { + "name": "sbindir", + "value": "sbin", + "section": "directory", + "machine": "any", + "type": "string", + "description": "System executable directory" + }, + { + "name": "sharedstatedir", + "value": "/var/lib", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Architecture-independent data directory" + }, + { + "name": "sysconfdir", + "value": "/etc", + "section": "directory", + "machine": "any", + "type": "string", + "description": "Sysconf data directory" + }, + { + "name": "man-pages", + "value": "auto", + "section": "user", + "machine": "any", + "choices": [ + "enabled", + "disabled", + "auto" + ], + "type": "combo", + "description": "Install man pages." + }, + { + "name": "errorlogs", + "value": true, + "section": "test", + "machine": "any", + "type": "boolean", + "description": "Whether to print the logs from failing tests" + }, + { + "name": "stdsplit", + "value": true, + "section": "test", + "machine": "any", + "type": "boolean", + "description": "Split stdout and stderr in test logs" + } +] \ No newline at end of file diff --git a/src/meson-info/intro-compilers.json b/src/meson-info/intro-compilers.json new file mode 100644 index 0000000..8843f45 --- /dev/null +++ b/src/meson-info/intro-compilers.json @@ -0,0 +1,38 @@ +{ + "host": { + "c": { + "id": "gcc", + "exelist": [ + "cc" + ], + "linker_exelist": [ + "cc" + ], + "file_suffixes": [ + "c" + ], + "default_suffix": "c", + "version": "14.2.1", + "full_version": "cc (GCC) 14.2.1 20240910", + "linker_id": "ld.bfd" + } + }, + "build": { + "c": { + "id": "gcc", + "exelist": [ + "cc" + ], + "linker_exelist": [ + "cc" + ], + "file_suffixes": [ + "c" + ], + "default_suffix": "c", + "version": "14.2.1", + "full_version": "cc (GCC) 14.2.1 20240910", + "linker_id": "ld.bfd" + } + } +} \ No newline at end of file diff --git a/src/meson-info/intro-dependencies.json b/src/meson-info/intro-dependencies.json new file mode 100644 index 0000000..21d13f7 --- /dev/null +++ b/src/meson-info/intro-dependencies.json @@ -0,0 +1,262 @@ +[ + { + "name": "freetype2", + "type": "pkgconfig", + "version": "26.2.20", + "compile_args": [ + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/sysprof-6", + "-pthread" + ], + "link_args": [ + "/usr/lib/libfreetype.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "freetype" + ] + }, + { + "name": "harfbuzz", + "type": "pkgconfig", + "version": "10.2.0", + "compile_args": [ + "-I/usr/include/harfbuzz", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/sysprof-6", + "-pthread" + ], + "link_args": [ + "/usr/lib/libharfbuzz.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "harfbuzz" + ] + }, + { + "name": "cairo", + "type": "pkgconfig", + "version": "1.18.2", + "compile_args": [ + "-I/usr/include/cairo", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/sysprof-6", + "-pthread", + "-I/usr/include/pixman-1" + ], + "link_args": [ + "/usr/lib/libcairo.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "cairo" + ] + }, + { + "name": "pangocairo", + "type": "pkgconfig", + "version": "1.56.1", + "compile_args": [ + "-I/usr/include/pango-1.0", + "-I/usr/include/cairo", + "-I/usr/include/pixman-1", + "-I/usr/include/libmount", + "-I/usr/include/blkid", + "-I/usr/include/fribidi", + "-I/usr/include/harfbuzz", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/sysprof-6", + "-pthread" + ], + "link_args": [ + "/usr/lib/libpangocairo-1.0.so", + "/usr/lib/libpango-1.0.so", + "/usr/lib/libgobject-2.0.so", + "/usr/lib/libglib-2.0.so", + "/usr/lib/libharfbuzz.so", + "/usr/lib/libcairo.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "pangocairo" + ] + }, + { + "name": "wayland-client", + "type": "pkgconfig", + "version": "1.23.1", + "compile_args": [], + "link_args": [ + "/usr/lib/libwayland-client.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "wayland_client" + ] + }, + { + "name": "wayland-protocols", + "type": "pkgconfig", + "version": "1.39", + "compile_args": [], + "link_args": [], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "wayland_protocols" + ] + }, + { + "name": "wayland-scanner", + "type": "pkgconfig", + "version": "1.23.1", + "compile_args": [], + "link_args": [], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "wayland_scanner_dep" + ] + }, + { + "name": "xkbcommon", + "type": "pkgconfig", + "version": "1.7.0", + "compile_args": [], + "link_args": [ + "/usr/lib/libxkbcommon.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "xkbcommon" + ] + }, + { + "name": "glib-2.0", + "type": "pkgconfig", + "version": "2.82.4", + "compile_args": [ + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/sysprof-6", + "-pthread" + ], + "link_args": [ + "/usr/lib/libglib-2.0.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "glib" + ] + }, + { + "name": "gio-unix-2.0", + "type": "pkgconfig", + "version": "2.82.4", + "compile_args": [ + "-I/usr/include/gio-unix-2.0", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/libmount", + "-I/usr/include/blkid", + "-I/usr/include/sysprof-6", + "-pthread" + ], + "link_args": [ + "/usr/lib/libgio-2.0.so", + "/usr/lib/libgobject-2.0.so", + "/usr/lib/libglib-2.0.so" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "gio_unix" + ] + }, + { + "name": "rt", + "type": "library", + "version": "unknown", + "compile_args": [], + "link_args": [ + "-lrt" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "librt" + ] + }, + { + "name": "m", + "type": "library", + "version": "unknown", + "compile_args": [], + "link_args": [ + "-lm" + ], + "include_directories": [], + "sources": [], + "extra_files": [], + "dependencies": [], + "depends": [], + "meson_variables": [ + "libm" + ] + } +] \ No newline at end of file diff --git a/src/meson-info/intro-machines.json b/src/meson-info/intro-machines.json new file mode 100644 index 0000000..117e511 --- /dev/null +++ b/src/meson-info/intro-machines.json @@ -0,0 +1,35 @@ +{ + "host": { + "system": "linux", + "cpu_family": "x86_64", + "cpu": "x86_64", + "endian": "little", + "kernel": "linux", + "subsystem": "linux", + "is_64_bit": true, + "exe_suffix": "", + "object_suffix": "o" + }, + "build": { + "system": "linux", + "cpu_family": "x86_64", + "cpu": "x86_64", + "endian": "little", + "kernel": "linux", + "subsystem": "linux", + "is_64_bit": true, + "exe_suffix": "", + "object_suffix": "o" + }, + "target": { + "system": "linux", + "cpu_family": "x86_64", + "cpu": "x86_64", + "endian": "little", + "kernel": "linux", + "subsystem": "linux", + "is_64_bit": true, + "exe_suffix": "", + "object_suffix": "o" + } +} \ No newline at end of file diff --git a/src/meson-info/intro-projectinfo.json b/src/meson-info/intro-projectinfo.json new file mode 100644 index 0000000..da1ac50 --- /dev/null +++ b/src/meson-info/intro-projectinfo.json @@ -0,0 +1,10 @@ +{ + "version": "0.9.1", + "descriptive_name": "tofi", + "license": [ + "MIT" + ], + "license_files": [], + "subproject_dir": "subprojects", + "subprojects": [] +} \ No newline at end of file diff --git a/src/meson-private/build.dat b/src/meson-private/build.dat new file mode 100644 index 0000000..fd73f38 Binary files /dev/null and b/src/meson-private/build.dat differ diff --git a/src/meson-private/cleantrees.dat b/src/meson-private/cleantrees.dat new file mode 100644 index 0000000..d3a30dd Binary files /dev/null and b/src/meson-private/cleantrees.dat differ diff --git a/src/meson-private/cmd_line.txt b/src/meson-private/cmd_line.txt new file mode 100644 index 0000000..6e871cd --- /dev/null +++ b/src/meson-private/cmd_line.txt @@ -0,0 +1,4 @@ +[options] + +[properties] + diff --git a/src/meson-private/coredata.dat b/src/meson-private/coredata.dat new file mode 100644 index 0000000..060b174 Binary files /dev/null and b/src/meson-private/coredata.dat differ diff --git a/src/meson-private/install.dat b/src/meson-private/install.dat new file mode 100644 index 0000000..eb01065 Binary files /dev/null and b/src/meson-private/install.dat differ diff --git a/src/meson-private/meson.lock b/src/meson-private/meson.lock new file mode 100644 index 0000000..e69de29 diff --git a/src/meson-private/meson_benchmark_setup.dat b/src/meson-private/meson_benchmark_setup.dat new file mode 100644 index 0000000..92c3c88 --- /dev/null +++ b/src/meson-private/meson_benchmark_setup.dat @@ -0,0 +1 @@ +€]”. \ No newline at end of file diff --git a/src/meson-private/meson_test_setup.dat b/src/meson-private/meson_test_setup.dat new file mode 100644 index 0000000..4f2f768 Binary files /dev/null and b/src/meson-private/meson_test_setup.dat differ diff --git a/src/meson-private/sanitycheckc.c b/src/meson-private/sanitycheckc.c new file mode 100644 index 0000000..a27020e --- /dev/null +++ b/src/meson-private/sanitycheckc.c @@ -0,0 +1 @@ +int main(void) { int class=0; return class; } diff --git a/src/modules.c b/src/modules.c new file mode 100644 index 0000000..5f5afc5 --- /dev/null +++ b/src/modules.c @@ -0,0 +1,79 @@ +#include "modules.h" +#include "string_vec.h" +#include "unicode.h" +#include +#include +#include + +void do_math(const char *restrict query, struct string_ref_vec *filt) { + char result[128] = "qalc -t -m 1000 \""; + result[sizeof(result) - 1] = '\0'; // Ensure null termination + strncat(result, query, sizeof(result) - 1); + strncat(result, "\"", sizeof(result) - 1); + // execute command + FILE *fp = popen(result, "r"); + // extract String from FILE + fgets(result, 50, fp); + pclose(fp); + // print result + if (result != NULL) { + // printf(result); + char math_result[128] = "="; + strncat(math_result, result, sizeof(math_result) - 1); + math_result[strlen(math_result) - 1] = '\0'; + string_ref_vec_add(filt, utf8_normalize(math_result)); + } +} +bool math_result(char *suggestion, char *query) { + // notification + + char notfy[128] = "notify-send \""; + strncat(notfy, query, sizeof(notfy) - 1); + strncat(notfy, "\" ", sizeof(notfy) - 1); + strncat(notfy, suggestion, sizeof(notfy) - 1); + strncat(notfy, "\"", sizeof(notfy) - 1); + system(notfy); + // copy + char copy[128] = "wl-copy \""; + strncat(copy, suggestion, sizeof(suggestion) - 1); + strncat(copy, "\"", sizeof(notfy) - 1); + system(copy); + + return true; +} + +void do_search(const char *restrict query, struct string_ref_vec *filt) { + char search[128] = "? search"; + string_ref_vec_add(filt, utf8_normalize(search)); +} + +bool module_search_run(char *query) { + char command[128] = "firefox --search \""; + strncat(command, query, sizeof(command) - 1); + strncat(command, "\"", sizeof(command) - 1); + system(command); + return true; +} + +bool execute_module(char *suggestion, char *query) { + char prefix = suggestion[0]; + memmove(suggestion, suggestion + 1, strlen(suggestion)); + switch (prefix) { + case '=': + return math_result(suggestion, query); + case '?': + return module_search_run(query); + // add more modules + default: + return false; + } +} + +void add_module_suggestions(struct tofi *tofi, const char *restrict query, + struct string_ref_vec *filt) { + if (tofi->module_math) + do_math(query, filt); + if (tofi->module_search) + do_search(query, filt); + // add more modules +} diff --git a/src/modules.h b/src/modules.h new file mode 100644 index 0000000..21ee8ad --- /dev/null +++ b/src/modules.h @@ -0,0 +1,10 @@ +#ifndef MODULES_H +#define MODULES_H + +#include "tofi.h" + +bool execute_module(char *suggestion, char *query); +void add_module_suggestions(struct tofi *tofi, const char *restrict query, + struct string_ref_vec *filt); + +#endif /* MKDIRP_H */ diff --git a/src/tofi.h b/src/tofi.h index dae2c1d..10eabb0 100644 --- a/src/tofi.h +++ b/src/tofi.h @@ -1,112 +1,114 @@ #ifndef TOFI_H #define TOFI_H -#include -#include -#include -#include #include "clipboard.h" #include "color.h" #include "entry.h" +#include "fractional-scale-v1.h" #include "matching.h" #include "surface.h" #include "wlr-layer-shell-unstable-v1.h" -#include "fractional-scale-v1.h" +#include +#include +#include +#include #define MAX_OUTPUT_NAME_LEN 256 #define MAX_TERMINAL_NAME_LEN 256 #define MAX_HISTORY_FILE_NAME_LEN 256 struct output_list_element { - struct wl_list link; - struct wl_output *wl_output; - char *name; - uint32_t width; - uint32_t height; - int32_t scale; - int32_t transform; + struct wl_list link; + struct wl_output *wl_output; + char *name; + uint32_t width; + uint32_t height; + int32_t scale; + int32_t transform; }; struct tofi { - /* Wayland globals */ - struct wl_display *wl_display; - struct wl_registry *wl_registry; - struct wl_compositor *wl_compositor; - struct wl_seat *wl_seat; - struct wl_shm *wl_shm; - struct wl_data_device_manager *wl_data_device_manager; - struct wl_data_device *wl_data_device; - struct wp_viewporter *wp_viewporter; - struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager; - struct zwlr_layer_shell_v1 *zwlr_layer_shell; - struct wl_list output_list; - struct output_list_element *default_output; + /* Wayland globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct wl_seat *wl_seat; + struct wl_shm *wl_shm; + struct wl_data_device_manager *wl_data_device_manager; + struct wl_data_device *wl_data_device; + struct wp_viewporter *wp_viewporter; + struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager; + struct zwlr_layer_shell_v1 *zwlr_layer_shell; + struct wl_list output_list; + struct output_list_element *default_output; - /* Wayland objects */ - struct wl_keyboard *wl_keyboard; - struct wl_pointer *wl_pointer; + /* Wayland objects */ + struct wl_keyboard *wl_keyboard; + struct wl_pointer *wl_pointer; - /* Keyboard objects */ - char *xkb_keymap_string; - struct xkb_state *xkb_state; - struct xkb_context *xkb_context; - struct xkb_keymap *xkb_keymap; + /* Keyboard objects */ + char *xkb_keymap_string; + struct xkb_state *xkb_state; + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; - /* State */ - bool submit; - bool closed; - int32_t output_width; - int32_t output_height; - struct clipboard clipboard; - struct { - struct surface surface; - struct wp_viewport *wp_viewport; - struct zwlr_layer_surface_v1 *zwlr_layer_surface; - struct entry entry; - uint32_t width; - uint32_t height; - uint32_t scale; - uint32_t fractional_scale; - int32_t transform; - int32_t exclusive_zone; - int32_t margin_top; - int32_t margin_bottom; - int32_t margin_left; - int32_t margin_right; - bool width_is_percent; - bool height_is_percent; - bool exclusive_zone_is_percent; - bool margin_top_is_percent; - bool margin_bottom_is_percent; - bool margin_left_is_percent; - bool margin_right_is_percent; - } window; - struct { - uint32_t rate; - uint32_t delay; - uint32_t keycode; - uint32_t next; - bool active; - } repeat; + /* State */ + bool submit; + bool closed; + int32_t output_width; + int32_t output_height; + struct clipboard clipboard; + struct { + struct surface surface; + struct wp_viewport *wp_viewport; + struct zwlr_layer_surface_v1 *zwlr_layer_surface; + struct entry entry; + uint32_t width; + uint32_t height; + uint32_t scale; + uint32_t fractional_scale; + int32_t transform; + int32_t exclusive_zone; + int32_t margin_top; + int32_t margin_bottom; + int32_t margin_left; + int32_t margin_right; + bool width_is_percent; + bool height_is_percent; + bool exclusive_zone_is_percent; + bool margin_top_is_percent; + bool margin_bottom_is_percent; + bool margin_left_is_percent; + bool margin_right_is_percent; + } window; + struct { + uint32_t rate; + uint32_t delay; + uint32_t keycode; + uint32_t next; + bool active; + } repeat; - /* Options */ - uint32_t anchor; - enum matching_algorithm matching_algorithm; - bool ascii_input; - bool hide_cursor; - bool use_history; - bool use_scale; - bool late_keyboard_init; - bool drun_launch; - bool drun_print_exec; - bool require_match; - bool auto_accept_single; - bool print_index; - bool multiple_instance; - bool physical_keybindings; - char target_output_name[MAX_OUTPUT_NAME_LEN]; - char default_terminal[MAX_TERMINAL_NAME_LEN]; - char history_file[MAX_HISTORY_FILE_NAME_LEN]; + /* Options */ + uint32_t anchor; + enum matching_algorithm matching_algorithm; + bool ascii_input; + bool hide_cursor; + bool use_history; + bool use_scale; + bool late_keyboard_init; + bool drun_launch; + bool drun_print_exec; + bool require_match; + bool auto_accept_single; + bool print_index; + bool multiple_instance; + bool physical_keybindings; + bool module_math; + bool module_search; + char target_output_name[MAX_OUTPUT_NAME_LEN]; + char default_terminal[MAX_TERMINAL_NAME_LEN]; + char history_file[MAX_HISTORY_FILE_NAME_LEN]; }; #endif /* TOFI_H */