init commit

This commit is contained in:
2024-05-14 12:29:49 +03:00
commit 29ce74f3f6
209 changed files with 17530 additions and 0 deletions

98
wofi/src/config.c Normal file
View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2019-2024 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <map.h>
void config_put(struct map* map, char* line) {
size_t line_size = strlen(line);
char* new_line = calloc(1, line_size + 1);
size_t new_line_count = 0;
for(size_t count = 0; count < line_size; ++count) {
if(line[count] == '\\') {
new_line[new_line_count++] = line[++count];
} else if(line[count] == '#') {
break;
} else {
new_line[new_line_count++] = line[count];
}
}
line = new_line;
char* equals = strchr(line, '=');
if(equals == NULL) {
free(line);
return;
}
*equals = 0;
char* key = equals - 1;
while(*key == ' ') {
--key;
}
*(key + 1) = 0;
char* value = equals + 1;
while(*value == ' ') {
++value;
}
size_t len = strlen(value);
char* end = value + len - 1;
if(*end == '\n' || *end == ' ') {
*end = 0;
}
map_put(map, line, value);
free(line);
}
void config_load(struct map* map, const char* config) {
FILE* file = fopen(config, "r");
char* line = NULL;
size_t size = 0;
while(getline(&line, &size, file) != -1) {
config_put(map, line);
}
free(line);
fclose(file);
}
char* config_get(struct map* config, const char* key, char* def_opt) {
char* opt = map_get(config, key);
if(opt == NULL) {
opt = def_opt;
}
return opt;
}
int config_get_mnemonic(struct map* config, const char* key, char* def_opt, int num_choices, ...) {
char* opt = config_get(config, key, def_opt);
va_list ap;
va_start(ap, num_choices);
int result = 0;
for(int i = 0; i < num_choices; i++) {
char* cmp_str = va_arg(ap, char*);
if(strcmp(opt, cmp_str) == 0) {
result = i;
break;
}
}
va_end(ap);
return result;
}

798
wofi/src/main.c Normal file
View File

@@ -0,0 +1,798 @@
/*
* Copyright (C) 2019-2020 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <map.h>
#include <wofi.h>
#include <utils.h>
#include <config.h>
#include <wayland-client.h>
#include <gtk/gtk.h>
static const char* nyan_colors[] = {"#FF0000", "#FFA500", "#FFFF00", "#00FF00", "#0000FF", "#FF00FF"};
static size_t nyan_color_l = sizeof(nyan_colors) / sizeof(char*);
static char* CONFIG_LOCATION;
static char* COLORS_LOCATION;
static struct map* config;
static char* config_path;
static char* stylesheet;
static char* color_path;
static uint8_t nyan_shift = 0;
struct option_node {
char* option;
struct wl_list link;
};
static char* get_exec_name(char* path) {
char* slash = strrchr(path, '/');
uint64_t offset;
if(slash == NULL) {
offset = 0;
} else {
offset = (slash - path) + 1;
}
return path + offset;
}
static void print_usage(char** argv) {
printf("%s [options]\n", get_exec_name(argv[0]));
printf("Options:\n");
printf("--help\t\t\t-h\tDisplays this help message\n");
printf("--fork\t\t\t-f\tForks the menu so you can close the terminal\n");
printf("--conf\t\t\t-c\tSelects a config file to use\n");
printf("--style\t\t\t-s\tSelects a stylesheet to use\n");
printf("--color\t\t\t-C\tSelects a colors file to use\n");
printf("--dmenu\t\t\t-d\tRuns in dmenu mode\n");
printf("--show\t\t\t-S\tSpecifies the mode to run in. A list can be found in wofi(7)\n");
printf("--width\t\t\t-W\tSpecifies the surface width\n");
printf("--height\t\t-H\tSpecifies the surface height\n");
printf("--prompt\t\t-p\tPrompt to display\n");
printf("--xoffset\t\t-x\tThe x offset\n");
printf("--yoffset\t\t-y\tThe y offset\n");
printf("--normal-window\t\t-n\tRender to a normal window\n");
printf("--allow-images\t\t-I\tAllows images to be rendered\n");
printf("--allow-markup\t\t-m\tAllows pango markup\n");
printf("--cache-file\t\t-k\tSets the cache file to use\n");
printf("--term\t\t\t-t\tSpecifies the terminal to use when running in a term\n");
printf("--password\t\t-P\tRuns in password mode\n");
printf("--exec-search\t\t-e\tMakes enter always use the search contents not the first result\n");
printf("--hide-scroll\t\t-b\tHides the scroll bars\n");
printf("--matching\t\t-M\tSets the matching method, default is contains\n");
printf("--insensitive\t\t-i\tAllows case insensitive searching\n");
printf("--parse-search\t\t-q\tParses the search text removing image escapes and pango\n");
printf("--version\t\t-v\tPrints the version and then exits\n");
printf("--location\t\t-l\tSets the location\n");
printf("--no-actions\t\t-a\tDisables multiple actions for modes that support it\n");
printf("--define\t\t-D\tSets a config option\n");
printf("--lines\t\t\t-L\tSets the height in number of lines\n");
printf("--columns\t\t-w\tSets the number of columns to display\n");
printf("--sort-order\t\t-O\tSets the sort order\n");
printf("--gtk-dark\t\t-G\tUses the dark variant of the current GTK theme\n");
printf("--search\t\t-Q\tSearch for something immediately on open\n");
printf("--monitor\t\t-o\tSets the monitor to open on\n");
printf("--pre-display-cmd\t-r\tRuns command for the displayed entries, without changing the output. %%s for the real string\n");
exit(0);
}
void wofi_load_css(bool nyan) {
if(access(stylesheet, R_OK) == 0) {
FILE* file = fopen(stylesheet, "r");
fseek(file, 0, SEEK_END);
ssize_t size = ftell(file);
fseek(file, 0, SEEK_SET);
char* data = malloc(size + 1);
if (fread(data, 1, size, file) == 0) {
fprintf(stderr, "failed to read stylesheet data from file\n");
exit(EXIT_FAILURE);
}
fclose(file);
data[size] = 0;
struct wl_list lines;
struct node {
char* line;
struct wl_list link;
};
wl_list_init(&lines);
if(nyan) {
for(ssize_t count = nyan_shift; count < 100 + nyan_shift; ++count) {
size_t i = count % nyan_color_l;
struct node* entry = malloc(sizeof(struct node));
entry->line = strdup(nyan_colors[i]);
wl_list_insert(&lines, &entry->link);
}
nyan_shift = (nyan_shift + 1) % nyan_color_l;
} else {
if(access(color_path, R_OK) == 0) {
file = fopen(color_path, "r");
char* line = NULL;
size_t line_size = 0;
ssize_t line_l = 0;
while((line_l = getline(&line, &line_size, file)) != -1) {
struct node* entry = malloc(sizeof(struct node));
line[line_l - 1] = 0;
entry->line = malloc(line_l + 1);
strcpy(entry->line, line);
wl_list_insert(&lines, &entry->link);
}
fclose(file);
free(line);
}
}
ssize_t count = wl_list_length(&lines) - 1;
if(count > 99) {
fprintf(stderr, "Woah there that's a lot of colors. Try having no more than 100, thanks\n");
exit(1);
}
struct node* node;
wl_list_for_each(node, &lines, link) {
//Do --wofi-color replace
const char* color = node->line;
const char* wofi_color = "--wofi-color";
char count_str[3];
snprintf(count_str, 3, "%zu", count--);
char* needle = utils_concat(2, wofi_color, count_str);
size_t color_len = strlen(color);
size_t needle_len = strlen(needle);
if(color_len > needle_len) {
free(needle);
fprintf(stderr, "What color format is this, try #FFFFFF, kthxbi\n");
continue;
}
char* replace = strstr(data, needle);
while(replace != NULL) {
memcpy(replace, color, color_len);
memset(replace + color_len, ' ', needle_len - color_len);
replace = strstr(data, needle);
}
free(needle);
//Do --wofi-rgb-color replace
if(color_len < 7) {
fprintf(stderr, "What color format is this, try #FFFFFF, kthxbi\n");
continue;
}
const char* wofi_rgb_color = "--wofi-rgb-color";
needle = utils_concat(2, wofi_rgb_color, count_str);
needle_len = strlen(needle);
replace = strstr(data, needle);
while(replace != NULL) {
char r[3];
char g[3];
char b[3];
memcpy(r, color + 1, 2);
memcpy(g, color + 3, 2);
memcpy(b, color + 5, 2);
r[2] = 0;
g[2] = 0;
b[2] = 0;
char rgb[14];
snprintf(rgb, 14, "%ld, %ld, %ld", strtol(r, NULL, 16), strtol(g, NULL, 16), strtol(b, NULL, 16));
size_t rgb_len = strlen(rgb);
memcpy(replace, rgb, rgb_len);
memset(replace + rgb_len, ' ', needle_len - rgb_len);
replace = strstr(data, needle);
}
free(needle);
}
GtkCssProvider* css = gtk_css_provider_new();
gtk_css_provider_load_from_data(css, data, strlen(data), NULL);
free(data);
struct node* tmp;
wl_list_for_each_safe(node, tmp, &lines, link) {
free(node->line);
wl_list_remove(&node->link);
free(node);
}
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
}
static void sig(int32_t signum) {
switch(signum) {
case SIGTERM:
exit(1);
break;
}
}
int main(int argc, char** argv) {
const struct option opts[] = {
{
.name = "help",
.has_arg = no_argument,
.flag = NULL,
.val = 'h'
},
{
.name = "fork",
.has_arg = no_argument,
.flag = NULL,
.val = 'f'
},
{
.name = "conf",
.has_arg = required_argument,
.flag = NULL,
.val = 'c'
},
{
.name = "style",
.has_arg = required_argument,
.flag = NULL,
.val = 's'
},
{
.name = "color",
.has_arg = required_argument,
.flag = NULL,
.val = 'C'
},
{
.name = "dmenu",
.has_arg = no_argument,
.flag = NULL,
.val = 'd'
},
{
.name = "show",
.has_arg = required_argument,
.flag = NULL,
.val = 'S'
},
{
.name = "width",
.has_arg = required_argument,
.flag = NULL,
.val = 'W'
},
{
.name = "height",
.has_arg = required_argument,
.flag = NULL,
.val = 'H'
},
{
.name = "prompt",
.has_arg = required_argument,
.flag = NULL,
.val = 'p'
},
{
.name = "xoffset",
.has_arg = required_argument,
.flag = NULL,
.val = 'x'
},
{
.name = "yoffset",
.has_arg = required_argument,
.flag = NULL,
.val = 'y'
},
{
.name = "normal-window",
.has_arg = no_argument,
.flag = NULL,
.val = 'n'
},
{
.name = "allow-images",
.has_arg = no_argument,
.flag = NULL,
.val = 'I'
},
{
.name = "allow-markup",
.has_arg = no_argument,
.flag = NULL,
.val = 'm'
},
{
.name = "cache-file",
.has_arg = required_argument,
.flag = NULL,
.val = 'k'
},
{
.name = "term",
.has_arg = required_argument,
.flag = NULL,
.val = 't'
},
{
.name = "password",
.has_arg = optional_argument,
.flag = NULL,
.val = 'P'
},
{
.name = "exec-search",
.has_arg = no_argument,
.flag = NULL,
.val = 'e'
},
{
.name = "hide-scroll",
.has_arg = no_argument,
.flag = NULL,
.val = 'b'
},
{
.name = "matching",
.has_arg = required_argument,
.flag = NULL,
.val = 'M'
},
{
.name = "insensitive",
.has_arg = no_argument,
.flag = NULL,
.val = 'i'
},
{
.name = "parse-search",
.has_arg = no_argument,
.flag = NULL,
.val = 'q'
},
{
.name = "version",
.has_arg = no_argument,
.flag = NULL,
.val = 'v'
},
{
.name = "location",
.has_arg = required_argument,
.flag = NULL,
.val = 'l'
},
{
.name = "no-actions",
.has_arg = no_argument,
.flag = NULL,
.val = 'a'
},
{
.name = "define",
.has_arg = required_argument,
.flag = NULL,
.val = 'D'
},
{
.name = "lines",
.has_arg = required_argument,
.flag = NULL,
.val = 'L'
},
{
.name = "columns",
.has_arg = required_argument,
.flag = NULL,
.val = 'w'
},
{
.name = "sort-order",
.has_arg = required_argument,
.flag = NULL,
.val = 'O'
},
{
.name = "gtk-dark",
.has_arg = no_argument,
.flag = NULL,
.val = 'G'
},
{
.name = "search",
.has_arg = required_argument,
.flag = NULL,
.val = 'Q'
},
{
.name = "monitor",
.has_arg = required_argument,
.flag = NULL,
.val = 'o'
},
{
.name = "pre-display-cmd",
.has_arg = required_argument,
.flag = NULL,
.val = 'r'
},
{
.name = NULL,
.has_arg = 0,
.flag = NULL,
.val = 0
}
};
const char* config_str = NULL;
char* style_str = NULL;
char* color_str = NULL;
char* mode = NULL;
char* prompt = NULL;
char* width = NULL;
char* height = NULL;
char* x = NULL;
char* y = NULL;
char* normal_window = NULL;
char* allow_images = NULL;
char* allow_markup = NULL;
char* cache_file = NULL;
char* terminal = NULL;
char* password_char = "false";
char* exec_search = NULL;
char* hide_scroll = NULL;
char* matching = NULL;
char* insensitive = NULL;
char* parse_search = NULL;
char* location = NULL;
char* no_actions = NULL;
char* lines = NULL;
char* columns = NULL;
char* sort_order = NULL;
char* gtk_dark = NULL;
char* search = NULL;
char* monitor = NULL;
char* pre_display_cmd = NULL;
struct wl_list options;
wl_list_init(&options);
struct option_node* node;
int opt;
while((opt = getopt_long(argc, argv, "hfc:s:C:dS:W:H:p:x:y:nImk:t:P::ebM:iqvl:aD:L:w:O:GQ:o:r:", opts, NULL)) != -1) {
switch(opt) {
case 'h':
print_usage(argv);
break;
case 'f':
if(fork() > 0) {
exit(0);
}
fclose(stdout);
fclose(stderr);
fclose(stdin);
break;
case 'c':
config_str = optarg;
break;
case 's':
style_str = optarg;
break;
case 'C':
color_str = optarg;
break;
case 'd':
mode = "dmenu";
break;
case 'S':
mode = optarg;
break;
case 'W':
width = optarg;
break;
case 'H':
height = optarg;
break;
case 'p':
prompt = optarg;
break;
case 'x':
x = optarg;
break;
case 'y':
y = optarg;
break;
case 'n':
normal_window = "true";
break;
case 'I':
allow_images = "true";
break;
case 'm':
allow_markup = "true";
break;
case 'k':
cache_file = optarg;
break;
case 't':
terminal = optarg;
break;
case 'P':
password_char = optarg;
break;
case 'e':
exec_search = "true";
break;
case 'b':
hide_scroll = "true";
break;
case 'M':
matching = optarg;
break;
case 'i':
insensitive = "true";
break;
case 'q':
parse_search = "true";
break;
case 'v':
printf(VERSION"\n");
exit(0);
break;
case 'l':
location = optarg;
break;
case 'a':
no_actions = "true";
break;
case 'D':
node = malloc(sizeof(struct option_node));
node->option = optarg;
wl_list_insert(&options, &node->link);
break;
case 'L':
lines = optarg;
break;
case 'w':
columns = optarg;
break;
case 'O':
sort_order = optarg;
break;
case 'G':
gtk_dark = "true";
break;
case 'Q':
search = optarg;
break;
case 'o':
monitor = optarg;
break;
case 'r':
pre_display_cmd = optarg;
break;
}
}
const char* home_dir = getenv("HOME");
const char* xdg_conf = getenv("XDG_CONFIG_HOME");
if(xdg_conf == NULL) {
CONFIG_LOCATION = utils_concat(2, home_dir, "/.config/wofi");
} else {
CONFIG_LOCATION = utils_concat(2, xdg_conf, "/wofi");
}
const char* xdg_cache = getenv("XDG_CACHE_HOME");
if(xdg_cache == NULL) {
COLORS_LOCATION = utils_concat(2, home_dir, "/.cache/wal/colors");
} else {
COLORS_LOCATION = utils_concat(2, xdg_cache, "/wal/colors");
}
config = map_init();
//Check if --conf was specified
if(config_str == NULL) {
const char* config_f = "/config";
config_path = utils_concat(2, CONFIG_LOCATION, config_f);
} else {
config_path = strdup(config_str);
}
if(access(config_path, R_OK) == 0) {
config_load(config, config_path);
}
free(config_path);
if(style_str == NULL) {
style_str = map_get(config, "style");
}
//Check if --style was specified
if(style_str == NULL) {
style_str = map_get(config, "stylesheet");
if(style_str == NULL) {
const char* style_f = "/style.css";
stylesheet = utils_concat(2, CONFIG_LOCATION, style_f);
} else {
if(style_str[0] == '/') {
stylesheet = strdup(style_str);
} else {
stylesheet = utils_concat(3, CONFIG_LOCATION, "/", style_str);
}
}
} else {
stylesheet = strdup(style_str);
}
if(color_str == NULL) {
color_str = map_get(config, "color");
}
//Check if --color was specified
if(color_str == NULL) {
color_str = map_get(config, "colors");
if(color_str == NULL) {
color_path = strdup(COLORS_LOCATION);
} else {
if(color_str[0] == '/') {
color_path = strdup(color_str);
} else {
color_path = utils_concat(3, CONFIG_LOCATION, "/", color_str);
}
}
} else {
color_path = strdup(color_str);
}
//Check if --gtk-dark was specified
if(gtk_dark == NULL) {
gtk_dark = map_get(config, "gtk_dark");
}
free(COLORS_LOCATION);
struct option_node* tmp;
wl_list_for_each_safe(node, tmp, &options, link) {
config_put(config, node->option);
wl_list_remove(&node->link);
free(node);
}
if(map_get(config, "show") != NULL) {
map_put(config, "mode", map_get(config, "show"));
}
if(strcmp(get_exec_name(argv[0]), "dmenu") == 0) {
map_put(config, "mode", "dmenu");
cache_file = "/dev/null";
} else if(strcmp(get_exec_name(argv[0]), "wofi-askpass") == 0) {
map_put(config, "mode", "dmenu");
cache_file = "/dev/null";
password_char = "*";
prompt = "Password";
} else if(mode != NULL) {
map_put(config, "mode", mode);
} else if(map_get(config, "mode") == NULL) {
fprintf(stderr, "I need a mode, please give me a mode, that's what --show is for\n");
exit(1);
}
map_put(config, "config_dir", CONFIG_LOCATION);
if(width != NULL) {
map_put(config, "width", width);
}
if(height != NULL) {
map_put(config, "height", height);
}
if(prompt != NULL) {
map_put(config, "prompt", prompt);
}
if(map_get(config, "xoffset") != NULL) {
map_put(config, "x", map_get(config, "xoffset"));
}
if(x != NULL) {
map_put(config, "x", x);
}
if(map_get(config, "yoffset") != NULL) {
map_put(config, "y", map_get(config, "yoffset"));
}
if(y != NULL) {
map_put(config, "y", y);
}
if(normal_window != NULL) {
map_put(config, "normal_window", normal_window);
}
if(allow_images != NULL) {
map_put(config, "allow_images", allow_images);
}
if(allow_markup != NULL) {
map_put(config, "allow_markup", allow_markup);
}
if(cache_file != NULL) {
map_put(config, "cache_file", cache_file);
}
if(terminal != NULL) {
map_put(config, "term", terminal);
}
if(map_get(config, "password") != NULL) {
map_put(config, "password_char", map_get(config, "password"));
}
if(password_char == NULL || (password_char != NULL && strcmp(password_char, "false") != 0)) {
if(password_char == NULL) {
password_char = "*";
}
map_put(config, "password_char", password_char);
}
if(exec_search != NULL) {
map_put(config, "exec_search", exec_search);
}
if(hide_scroll != NULL) {
map_put(config, "hide_scroll", hide_scroll);
}
if(matching != NULL) {
map_put(config, "matching", matching);
}
if(insensitive != NULL) {
map_put(config, "insensitive", insensitive);
}
if(parse_search != NULL) {
map_put(config, "parse_search", parse_search);
}
if(location != NULL) {
map_put(config, "location", location);
}
if(no_actions != NULL) {
map_put(config, "no_actions", no_actions);
}
if(lines != NULL) {
map_put(config, "lines", lines);
}
if(columns != NULL) {
map_put(config, "columns", columns);
}
if(sort_order != NULL) {
map_put(config, "sort_order", sort_order);
}
if(search != NULL) {
map_put(config, "search", search);
}
if(monitor != NULL) {
map_put(config, "monitor", monitor);
}
if(pre_display_cmd != NULL) {
map_put(config, "pre_display_cmd", pre_display_cmd);
}
struct sigaction sigact = {0};
sigact.sa_handler = sig;
sigaction(SIGTERM, &sigact, NULL);
gtk_init(&argc, &argv);
if(gtk_dark != NULL && strcmp(gtk_dark, "true") == 0) {
g_object_set(gtk_settings_get_default(),
"gtk-application-prefer-dark-theme", TRUE, NULL);
}
wofi_load_css(false);
wofi_init(config);
gtk_main();
return 0;
}

96
wofi/src/map.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2019-2020 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmodule.h>
struct map {
GTree* tree;
bool mman;
};
static gint compare(gconstpointer p1, gconstpointer p2, gpointer data) {
(void) data;
const char* str1 = p1;
const char* str2 = p2;
return strcmp(str1, str2);
}
struct map* map_init(void) {
struct map* map = malloc(sizeof(struct map));
map->tree = g_tree_new_full(compare, NULL, free, free);
map->mman = true;
return map;
}
struct map* map_init_void(void) {
struct map* map = malloc(sizeof(struct map));
map->tree = g_tree_new_full(compare, NULL, free, NULL);
map->mman = false;
return map;
}
void map_free(struct map* map) {
g_tree_destroy(map->tree);
free(map);
}
static void put(struct map* map, const char* key, void* value) {
char* k = strdup(key);
char* v = value;
if(map->mman && value != NULL) {
v = strdup(value);
}
g_tree_insert(map->tree, k, v);
}
bool map_put(struct map* map, const char* key, char* value) {
if(map->mman) {
put(map, key, value);
return true;
} else {
fprintf(stderr, "This is an unmanaged map please use map_put_void\n");
return false;
}
}
bool map_put_void(struct map* map, const char* key, void* value) {
if(map->mman) {
fprintf(stderr, "This is an managed map please use map_put\n");
return false;
} else {
put(map, key, value);
return true;
}
}
void* map_get(struct map* map, const char* key) {
return g_tree_lookup(map->tree, key);
}
bool map_contains(struct map* map, const char* key) {
return map_get(map, key) != NULL;
}
size_t map_size(struct map* map) {
return g_tree_nnodes(map->tree);
}

412
wofi/src/match.c Normal file
View File

@@ -0,0 +1,412 @@
/*
* Copyright (C) 2019-2022 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <match.h>
#include <string.h>
// leading gap
#define SCORE_GAP_LEADING -0.005
// trailing gap
#define SCORE_GAP_TRAILING -0.005
// gap in the middle
#define SCORE_GAP_INNER -0.01
// we matched the characters consecutively
#define SCORE_MATCH_CONSECUTIVE 1.0
// we got a consecutive match, but insensitive is on
// and we didn't match the case.
#define SCORE_MATCH_NOT_MATCH_CASE 0.9
// we are matching after a slash
#define SCORE_MATCH_SLASH 0.9
// we are matching after a space dash or hyphen
#define SCORE_MATCH_WORD 0.8
// we are matching a camel case letter
#define SCORE_MATCH_CAPITAL 0.7
// we are matching after a dot
#define SCORE_MATCH_DOT 0.6
#define SWAP(x, y, T) \
do { \
T SWAP = x; \
x = y; \
y = SWAP; \
} while(0)
#define max(a, b) (((a) > (b)) ? (a) : (b))
// matching
static bool contains_match(const char* filter, const char* text, bool insensitive) {
if(filter == NULL || strcmp(filter, "") == 0) {
return true;
}
if(text == NULL) {
return false;
}
if(insensitive) {
return strcasestr(text, filter) != NULL;
} else {
return strstr(text, filter) != NULL;
}
}
static char* strcasechr(const char* s,char c, bool insensitive) {
if(insensitive) {
const char accept[3] = {tolower(c), toupper(c), 0};
return strpbrk(s, accept);
} else {
return strchr(s, c);
}
}
static bool fuzzy_match(const char* filter, const char* text, bool insensitive) {
if(filter == NULL || strcmp(filter, "") == 0) {
return true;
}
if(text == NULL) {
return false;
}
// we just check that all the characters (ignoring case) are in the
// search text possibly case insensitively in the correct order
while(*filter != 0) {
char nch = *filter++;
if(!(text = strcasechr(text, nch, insensitive))) {
return false;
}
text++;
}
return true;
}
static bool multi_contains_match(const char* filter, const char* text, bool insensitive) {
if(filter == NULL || strcmp(filter, "") == 0) {
return true;
}
if(text == NULL) {
return false;
}
char new_filter[MAX_MULTI_CONTAINS_FILTER_SIZE];
strncpy(new_filter, filter, sizeof(new_filter));
new_filter[sizeof(new_filter) - 1] = '\0';
char* token;
char* rest = new_filter;
while((token = strtok_r(rest, " ", &rest))) {
if(contains_match(token, text, insensitive) == false) {
return false;
}
}
return true;
}
bool match_for_matching_mode(const char* filter, const char* text,
enum matching_mode matching, bool insensitive) {
bool retval;
switch(matching) {
case MATCHING_MODE_MULTI_CONTAINS:
retval = multi_contains_match(filter, text, insensitive);
break;
case MATCHING_MODE_CONTAINS:
retval = contains_match(filter, text, insensitive);
break;
case MATCHING_MODE_FUZZY:
retval = fuzzy_match(filter, text, insensitive);
break;
default:
return false;
}
return retval;
}
// end matching
// fuzzy matching
static void precompute_bonus(const char* haystack, score_t* match_bonus) {
/* Which positions are beginning of words */
int m = strlen(haystack);
char last_ch = '\0';
for(int i = 0; i < m; i++) {
char ch = haystack[i];
score_t score = 0;
if(isalnum(ch)) {
if(!last_ch || last_ch == '/') {
score = SCORE_MATCH_SLASH;
} else if(last_ch == '-' || last_ch == '_' ||
last_ch == ' ') {
score = SCORE_MATCH_WORD;
} else if(last_ch >= 'a' && last_ch <= 'z' &&
ch >= 'A' && ch <= 'Z') {
/* CamelCase */
score = SCORE_MATCH_CAPITAL;
} else if(last_ch == '.') {
score = SCORE_MATCH_DOT;
}
}
match_bonus[i] = score;
last_ch = ch;
}
}
static inline bool match_with_case(char a, char b, bool insensitive) {
if(insensitive) {
return tolower(a) == tolower(b);
} else {
return a == b;
}
}
static inline void match_row(int row, score_t* curr_D, score_t* curr_M,
const score_t* last_D, const score_t* last_M,
const char* needle, const char* haystack, int n, int m, score_t* match_bonus, bool insensitive) {
int i = row;
score_t prev_score = SCORE_MIN;
score_t gap_score = i == n - 1 ? SCORE_GAP_TRAILING : SCORE_GAP_INNER;
for(int j = 0; j < m; j++) {
if(match_with_case(needle[i], haystack[j], insensitive)) {
score_t score = SCORE_MIN;
if(!i) {
// first line we fill in a row for non-matching
score = (j * SCORE_GAP_LEADING) + match_bonus[j];
} else if(j) { /* i > 0 && j > 0*/
// we definitely match case insensitively already so if
// our character isn't the same then we have a different case
score_t consecutive_bonus = needle[i] == haystack[j] ? SCORE_MATCH_CONSECUTIVE : SCORE_MATCH_NOT_MATCH_CASE;
score = max(last_M[j - 1] + match_bonus[j],
/* consecutive match, doesn't stack
with match_bonus */
last_D[j - 1] + consecutive_bonus);
}
curr_D[j] = score;
curr_M[j] = prev_score = max(score, prev_score + gap_score);
} else {
curr_D[j] = SCORE_MIN;
curr_M[j] = prev_score = prev_score + gap_score;
}
}
}
// Fuzzy matching scoring. Adapted from
// https://github.com/jhawthorn/fzy/blob/master/src/match.c and
// https://github.com/jhawthorn/fzy/blob/master/ALGORITHM.md
// For a fuzzy match string needle being searched for in haystack we provide a
// number score for how well we match.
// We create two matrices of size needle_len (n) by haystack_len (m).
// The first matrix is the score matrix. Each position (i,j) within this matrix
// consists of the score that corresponds to the score that would be generated
// by matching the first i characters of the needle with the first j
// characters of the haystack. Gaps have a fixed penalty for having a gap along
// with a linear penalty for gap size (c.f. gotoh's algorithm).
// matches give a positive score, with a slight weight given to matches after
// certain special characters (i.e. the first character after a `/` will be
// "almost" consecutive but lower than an actual consecutive match).
// Our second matrix is our diagonal matrix where we store the best match
// that ends at a match. This allows us to calculate our gap penalties alongside
// our consecutive match scores.
// In addition, since we only rely on the current, and previous row of the
// matrices and we only want to compute the score, we only store those scores
// and reuse the previous rows (rather than storing the entire (n*m) matrix).
// In addition we've simplified some of the algorithm compared to fzy to
// improve legibility. (Can reimplement lookup tables later if wanted.)
// Also, the reference algorithm does not take into account case sensitivity
// which has been implemented here.
static score_t fuzzy_score(const char* haystack, const char* needle, bool insensitive) {
if(*needle == 0)
return SCORE_MIN;
int n = strlen(needle);
int m = strlen(haystack);
score_t match_bonus[m];
precompute_bonus(haystack, match_bonus);
if(m > MATCH_FUZZY_MAX_LEN || n > m) {
/*
* Unreasonably large candidate: return no score
* If it is a valid match it will still be returned, it will
* just be ranked below any reasonably sized candidates
*/
return SCORE_MIN;
} else if(n == m) {
/* Since this method can only be called with a haystack which
* matches needle. If the lengths of the strings are equal the
* strings themselves must also be equal (ignoring case).
*/
return SCORE_MAX;
}
/*
* D[][] Stores the best score for this position ending with a match.
* M[][] Stores the best possible score at this position.
*/
score_t D[2][MATCH_FUZZY_MAX_LEN], M[2][MATCH_FUZZY_MAX_LEN];
score_t* last_D, *last_M;
score_t* curr_D, *curr_M;
last_D = D[0];
last_M = M[0];
curr_D = D[1];
curr_M = M[1];
for(int i = 0; i < n; i++) {
match_row(i, curr_D, curr_M, last_D, last_M, needle, haystack, n, m, match_bonus, insensitive);
SWAP(curr_D, last_D, score_t *);
SWAP(curr_M, last_M, score_t *);
}
return last_M[m - 1];
}
// end fuzzy matching
// sorting
static int fuzzy_sort(const char* text1, const char* text2, const char* filter, bool insensitive) {
bool match1 = fuzzy_match(filter, text1, insensitive);
bool match2 = fuzzy_match(filter, text2, insensitive);
// both filters match do fuzzy scoring
if(match1 && match2) {
score_t dist1 = fuzzy_score(text1, filter, insensitive);
score_t dist2 = fuzzy_score(text2, filter, insensitive);
if(dist1 == dist2) {
// same same
return 0;
} else if(dist1 > dist2) { // highest score wins.
// text1 goes first
return -1;
} else {
// text2 goes first
return 1;
}
} else if(match1) {
// text1 goes first
return -1;
} else if(match2) {
// text2 goes first
return 1;
} else {
// same same.
return 0;
}
}
// we sort based on how early in the string all the matches are.
// if there are matches for each.
static int multi_contains_sort(const char* text1, const char* text2, const char* filter, bool insensitive) {
// sum of string positions of each match
int t1_count = 0;
int t2_count = 0;
// does this string match with mult-contains
bool t1_match = true;
bool t2_match = true;
char new_filter[MAX_MULTI_CONTAINS_FILTER_SIZE];
strncpy(new_filter, filter, sizeof(new_filter));
new_filter[sizeof(new_filter) - 1] = '\0';
char* token;
char* rest = new_filter;
while((token = strtok_r(rest, " ", &rest))) {
char* str1, *str2;
if(insensitive) {
str1 = strcasestr(text1, token);
str2 = strcasestr(text2, token);
} else {
str1 = strstr(text1, token);
str2 = strstr(text2, token);
}
t1_match = t1_match && str1 != NULL;
t2_match = t2_match && str2 != NULL;
if(str1 != NULL) {
int pos1 = str1 - text1;
t1_count += pos1;
}
if(str2 != NULL) {
int pos2 = str2 - text2;
t2_count += pos2;
}
}
if(t1_match && t2_match) {
// both match
// return the one with the smallest count.
return t1_count - t2_count;
} else if(t1_match) {
return -1;
} else if(t2_match) {
return 1;
} else {
return 0;
}
}
static int contains_sort(const char* text1, const char* text2, const char* filter, bool insensitive) {
char* str1, *str2;
if(insensitive) {
str1 = strcasestr(text1, filter);
str2 = strcasestr(text2, filter);
} else {
str1 = strstr(text1, filter);
str2 = strstr(text2, filter);
}
bool tx1 = str1 == text1;
bool tx2 = str2 == text2;
bool txc1 = str1 != NULL;
bool txc2 = str2 != NULL;
if(tx1 && tx2) {
return 0;
} else if(tx1) {
return -1;
} else if(tx2) {
return 1;
} else if(txc1 && txc2) {
return 0;
} else if(txc1) {
return -1;
} else if(txc2) {
return 1;
} else {
return 0;
}
}
int sort_for_matching_mode(const char* text1, const char* text2, int fallback,
enum matching_mode match_type, const char* filter, bool insensitive) {
int primary = 0;
switch(match_type) {
case MATCHING_MODE_MULTI_CONTAINS:
primary = multi_contains_sort(text1, text2, filter, insensitive);
break;
case MATCHING_MODE_CONTAINS:
primary = contains_sort(text1, text2, filter, insensitive);
break;
case MATCHING_MODE_FUZZY:
primary = fuzzy_sort(text1, text2, filter, insensitive);
break;
default:
return 0;
}
if(primary == 0) {
return fallback;
}
return primary;
}
// end sorting

60
wofi/src/property_box.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2019-2020 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <property_box.h>
#include <map.h>
struct _WofiPropertyBox {
GtkBox super;
};
typedef struct {
struct map* properties;
} WofiPropertyBoxPrivate;
G_DEFINE_TYPE_WITH_PRIVATE(WofiPropertyBox, wofi_property_box, GTK_TYPE_BOX)
static void wofi_property_box_init(WofiPropertyBox* box) {
WofiPropertyBoxPrivate* this = wofi_property_box_get_instance_private(box);
this->properties = map_init();
}
static void finalize(GObject* obj) {
WofiPropertyBoxPrivate* this = wofi_property_box_get_instance_private(WOFI_PROPERTY_BOX(obj));
map_free(this->properties);
G_OBJECT_CLASS(wofi_property_box_parent_class)->finalize(obj);
}
static void wofi_property_box_class_init(WofiPropertyBoxClass* class) {
GObjectClass* g_class = G_OBJECT_CLASS(class);
g_class->finalize = finalize;
}
GtkWidget* wofi_property_box_new(GtkOrientation orientation, gint spacing) {
return g_object_new(WOFI_TYPE_PROPERTY_BOX, "orientation", orientation, "spacing", spacing, NULL);
}
void wofi_property_box_add_property(WofiPropertyBox* box, const gchar* key, gchar* value) {
WofiPropertyBoxPrivate* this = wofi_property_box_get_instance_private(box);
map_put(this->properties, key, value);
}
const gchar* wofi_property_box_get_property(WofiPropertyBox* box, const gchar* key) {
WofiPropertyBoxPrivate* this = wofi_property_box_get_instance_private(box);
return map_get(this->properties, key);
}

119
wofi/src/utils.c Normal file
View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2019-2020 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <utils.h>
#include <libgen.h>
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
time_t utils_get_time_millis(void) {
struct timeval time;
gettimeofday(&time, NULL);
return (time.tv_sec * 1000) + (time.tv_usec / 1000);
}
void utils_sleep_millis(time_t millis) {
struct timespec time;
time.tv_sec = millis / 1000;
time.tv_nsec = (millis % 1000) * pow(1000, 2);
nanosleep(&time, NULL);
}
char* utils_concat(size_t arg_count, ...) {
va_list args;
va_start(args, arg_count);
size_t buf_s = 1;
for(size_t count = 0; count < arg_count; ++count) {
buf_s += strlen(va_arg(args, char*));
}
va_end(args);
va_start(args, arg_count);
char* buffer = malloc(buf_s);
strcpy(buffer, va_arg(args, char*));
for(size_t count = 0; count < arg_count - 1; ++count) {
strcat(buffer, va_arg(args, char*));
}
va_end(args);
return buffer;
}
size_t utils_min(size_t n1, size_t n2) {
return n1 < n2 ? n1 : n2;
}
size_t utils_max(size_t n1, size_t n2) {
return n1 > n2 ? n1 : n2;
}
size_t utils_min3(size_t n1, size_t n2, size_t n3) {
if(n1 < n2 && n1 < n3) {
return n1;
} else if(n2 < n1 && n2 < n3) {
return n2;
} else {
return n3;
}
}
size_t utils_distance(const char* haystack, const char* needle) {
size_t str1_len = strlen(haystack);
size_t str2_len = strlen(needle);
size_t arr[str1_len + 1][str2_len + 1];
arr[0][0] = 0;
for(size_t count = 1; count <= str1_len; ++count) {
arr[count][0] = count;
}
for(size_t count = 1; count <= str2_len; ++count) {
arr[0][count] = count;
}
uint8_t cost;
for(size_t c1 = 1; c1 <= str1_len; ++c1) {
for(size_t c2 = 1; c2 <= str2_len; ++c2) {
if(haystack[c1 - 1] == needle[c2 - 1]) {
cost = 0;
} else {
cost = 1;
}
arr[c1][c2] = utils_min3(arr[c1 - 1][c2] + 1, arr[c1][c2 - 1] + 1, arr[c1 - 1][c2 - 1] + cost);
}
}
if(strstr(haystack, needle) != NULL) {
arr[str1_len][str2_len] -= str2_len;
}
return arr[str1_len][str2_len];
}
void utils_mkdir(char* path, mode_t mode) {
if(access(path, F_OK) != 0) {
char* tmp = strdup(path);
utils_mkdir(dirname(tmp), mode);
mkdir(path, mode);
free(tmp);
}
}

80
wofi/src/utils_g.c Normal file
View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2020 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <utils_g.h>
#include <wofi_api.h>
GdkPixbuf* utils_g_resize_pixbuf(GdkPixbuf* pixbuf, uint64_t image_size, GdkInterpType interp) {
int width = gdk_pixbuf_get_width(pixbuf);
int height = gdk_pixbuf_get_height(pixbuf);
if(height > width) {
float percent = (float) image_size / height;
GdkPixbuf* tmp = gdk_pixbuf_scale_simple(pixbuf, width * percent, image_size, interp);
g_object_unref(pixbuf);
return tmp;
} else {
float percent = (float) image_size / width;
GdkPixbuf* tmp = gdk_pixbuf_scale_simple(pixbuf, image_size, height * percent, interp);
g_object_unref(pixbuf);
return tmp;
}
}
GdkPixbuf* utils_g_pixbuf_from_base64(char* base64) {
char* str = strdup(base64);
char* original_str = str;
if(strncmp(str, "data:", sizeof("data:") - 1) == 0) {
str += sizeof("data:") - 1;
}
GError* err = NULL;
GdkPixbufLoader* loader;
if(strncmp(str, "image/", sizeof("image/") - 1) == 0) {
char* mime = strchr(str, ';');
*mime = 0;
loader = gdk_pixbuf_loader_new_with_mime_type(str, &err);
if(err != NULL) {
goto fail;
}
str = mime + 1;
str = strchr(str, ',') + 1;
} else {
loader = gdk_pixbuf_loader_new();
}
gsize data_l;
guchar* data = g_base64_decode(str, &data_l);
gdk_pixbuf_loader_write(loader, data, data_l, &err);
if(err != NULL) {
g_free(data);
goto fail;
}
g_free(data);
free(original_str);
return gdk_pixbuf_loader_get_pixbuf(loader);
fail:
free(str);
fprintf(stderr, "Error loading base64 %s\n", err->message);
return NULL;
}

148
wofi/src/widget_builder.c Normal file
View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2020-2022 Scoopta
* This file is part of Wofi
* Wofi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wofi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wofi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <widget_builder.h>
#include <wofi.h>
#include <utils.h>
struct widget_builder* wofi_widget_builder_init(struct mode* mode, size_t actions) {
struct widget_builder* builder = calloc(actions, sizeof(struct widget_builder));
for(size_t count = 0; count < actions; ++count) {
builder[count].mode = mode;
builder[count].box = WOFI_PROPERTY_BOX(wofi_property_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
if(count == 0) {
builder->actions = actions;
}
}
return builder;
}
void wofi_widget_builder_set_search_text(struct widget_builder* builder, char* search_text) {
wofi_property_box_add_property(builder->box, "filter", search_text);
}
void wofi_widget_builder_set_action(struct widget_builder* builder, char* action) {
wofi_property_box_add_property(builder->box, "action", action);
}
static void va_to_list(struct wl_list* classes, va_list args) {
char* arg;
while((arg = va_arg(args, char*)) != NULL) {
struct css_class* class = malloc(sizeof(struct css_class));
class->class = arg;
wl_list_insert(classes, &class->link);
}
}
void wofi_widget_builder_insert_text(struct widget_builder* builder, const char* text, ...) {
struct wl_list classes;
wl_list_init(&classes);
va_list args;
va_start(args, text);
va_to_list(&classes, args);
va_end(args);
wofi_widget_builder_insert_text_with_list(builder, text, &classes);
struct css_class* node, *tmp;
wl_list_for_each_safe(node, tmp, &classes, link) {
free(node);
}
}
void wofi_widget_builder_insert_text_with_list(struct widget_builder* builder, const char* text, struct wl_list* classes) {
GtkWidget* label = gtk_label_new(text);
gtk_container_add(GTK_CONTAINER(builder->box), label);
gtk_widget_set_name(label, "text");
GtkStyleContext* ctx = gtk_widget_get_style_context(label);
struct css_class* node;
wl_list_for_each(node, classes, link) {
char* tmp = utils_concat(3, builder->mode->name, "-", node->class);
gtk_style_context_add_class(ctx, tmp);
free(tmp);
}
}
void wofi_widget_builder_insert_image(struct widget_builder* builder, GdkPixbuf* pixbuf, ...) {
struct wl_list classes;
wl_list_init(&classes);
va_list args;
va_start(args, pixbuf);
va_to_list(&classes, args);
va_end(args);
wofi_widget_builder_insert_image_with_list(builder, pixbuf, &classes);
struct css_class* node, *tmp;
wl_list_for_each_safe(node, tmp, &classes, link) {
free(node);
}
}
void wofi_widget_builder_insert_image_with_list(struct widget_builder* builder, GdkPixbuf* pixbuf, struct wl_list* classes) {
GtkWidget* img = gtk_image_new();
cairo_surface_t* surface = gdk_cairo_surface_create_from_pixbuf(pixbuf, wofi_get_window_scale(), gtk_widget_get_window(img));
gtk_image_set_from_surface(GTK_IMAGE(img), surface);
cairo_surface_destroy(surface);
gtk_container_add(GTK_CONTAINER(builder->box), img);
gtk_widget_set_name(img, "img");
GtkStyleContext* ctx = gtk_widget_get_style_context(img);
struct css_class* node;
wl_list_for_each(node, classes, link) {
char* tmp = utils_concat(3, builder->mode->name, "-", node->class);
gtk_style_context_add_class(ctx, tmp);
free(tmp);
}
}
struct widget_builder* wofi_widget_builder_get_idx(struct widget_builder* builder, size_t idx) {
return builder + idx;
}
struct widget* wofi_widget_builder_get_widget(struct widget_builder* builder) {
if(builder->actions == 0) {
fprintf(stderr, "%s: This is not the 0 index of the widget_builder array\n", builder->mode->name);
return NULL;
}
if(builder->widget == NULL) {
builder->widget = malloc(sizeof(struct widget));
builder->widget->builder = builder;
builder->widget->action_count = builder->actions;
}
for(size_t count = 0; count < builder->actions; ++count) {
}
return builder->widget;
}
void wofi_widget_builder_free(struct widget_builder* builder) {
if(builder->widget != NULL) {
free(builder->widget);
}
free(builder);
}

2093
wofi/src/wofi.c Normal file

File diff suppressed because it is too large Load Diff