First commit
This commit is contained in:
76
src/config.c
Normal file
76
src/config.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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>
|
||||
|
||||
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) {
|
||||
char* hash = strchr(line, '#');
|
||||
if(hash != NULL) {
|
||||
if(hash == line || *(hash - 1) != '\\') {
|
||||
*hash = 0;
|
||||
}
|
||||
}
|
||||
char* backslash = strchr(line, '\\');
|
||||
size_t backslash_count = 0;
|
||||
while(backslash != NULL) {
|
||||
++backslash_count;
|
||||
backslash = strchr(backslash + 1, '\\');
|
||||
}
|
||||
char* new_line = calloc(1, size - backslash_count);
|
||||
size_t line_size = strlen(line);
|
||||
size_t new_line_count = 0;
|
||||
for(size_t count = 0; count < line_size; ++count) {
|
||||
if(line[count] == '\\') {
|
||||
continue;
|
||||
}
|
||||
new_line[new_line_count++] = line[count];
|
||||
}
|
||||
free(line);
|
||||
line = new_line;
|
||||
char* equals = strchr(line, '=');
|
||||
if(equals == NULL) {
|
||||
continue;
|
||||
}
|
||||
*equals = 0;
|
||||
char* key = equals - 1;
|
||||
while(*key == ' ') {
|
||||
--key;
|
||||
}
|
||||
*(key + 1) = 0;
|
||||
char* value = equals + 1;
|
||||
while(*value == ' ') {
|
||||
++value;
|
||||
}
|
||||
size_t len = strlen(value);
|
||||
*(value + len - 1) = 0;
|
||||
map_put(map, line, value);
|
||||
}
|
||||
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;
|
||||
}
|
393
src/main.c
Normal file
393
src/main.c
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 <wofi.h>
|
||||
#include <utils.h>
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
|
||||
static char* CONFIG_LOCATION;
|
||||
static char* COLORS_LOCATION;
|
||||
static struct map* config;
|
||||
static char* config_path;
|
||||
static char* stylesheet;
|
||||
static char* color_path;
|
||||
|
||||
static void print_usage(char** argv) {
|
||||
char* slash = strrchr(argv[0], '/');
|
||||
uint64_t offset;
|
||||
if(slash == NULL) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = (slash - argv[0]) + 1;
|
||||
}
|
||||
printf("%s [options]\n", argv[0] + offset);
|
||||
printf("Options:\n");
|
||||
printf("--help\t\t-h\tDisplays this help message\n");
|
||||
printf("--fork\t\t-f\tForks the menu so you can close the terminal\n");
|
||||
printf("--conf\t\t-c\tSelects a config file to use\n");
|
||||
printf("--style\t\t-s\tSelects a stylesheet to use\n");
|
||||
printf("--color\t\t-C\tSelects a colors file to use\n");
|
||||
printf("--dmenu\t\t-d\tRuns in dmenu mode\n");
|
||||
printf("--show\t\t-S\tSpecifies the mode to run in\n");
|
||||
printf("--width\t\t-W\tSpecifies the surface width\n");
|
||||
printf("--height\t-H\tSpecifies the surface height\n");
|
||||
printf("\t\t-p\tPrompt to display\n");
|
||||
printf("--xoffset\t-x\tThe x offset\n");
|
||||
printf("--yoffset\t-y\tThe y offset\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void load_css() {
|
||||
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);
|
||||
fread(data, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
data[size] = 0;
|
||||
struct wl_list lines;
|
||||
struct node {
|
||||
char* line;
|
||||
struct wl_list link;
|
||||
};
|
||||
wl_list_init(&lines);
|
||||
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 99, 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, "%lu", 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 = "xoffset",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'x'
|
||||
},
|
||||
{
|
||||
.name = "yoffset",
|
||||
.has_arg = required_argument,
|
||||
.flag = NULL,
|
||||
.val = 'y'
|
||||
},
|
||||
{
|
||||
.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 opt;
|
||||
while((opt = getopt_long(argc, argv, "hfc:s:C:dS:W:H:p:x:y:", 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
free(COLORS_LOCATION);
|
||||
|
||||
if(mode == NULL) {
|
||||
fprintf(stderr, "I need a mode, please give me a mode, that's what --show is for\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(mode != NULL) {
|
||||
map_put(config, "mode", mode);
|
||||
}
|
||||
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(x != NULL) {
|
||||
map_put(config, "x", x);
|
||||
}
|
||||
if(y != NULL) {
|
||||
map_put(config, "y", y);
|
||||
}
|
||||
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
load_css();
|
||||
|
||||
wofi_init(config);
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
130
src/map.c
Normal file
130
src/map.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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>
|
||||
|
||||
struct map {
|
||||
char* key;
|
||||
void* value;
|
||||
size_t size;
|
||||
bool mman;
|
||||
struct map* head, *left, *right;
|
||||
};
|
||||
|
||||
struct map* map_init() {
|
||||
struct map* map = calloc(1, sizeof(struct map));
|
||||
map->head = map;
|
||||
map->mman = true;
|
||||
return map;
|
||||
}
|
||||
|
||||
struct map* map_init_void() {
|
||||
struct map* map = map_init();
|
||||
map->mman = false;
|
||||
return map;
|
||||
}
|
||||
|
||||
void map_free(struct map* map) {
|
||||
if(map->left != NULL) {
|
||||
map_free(map->left);
|
||||
}
|
||||
if(map->right != NULL) {
|
||||
map_free(map->right);
|
||||
}
|
||||
if(map->key != NULL) {
|
||||
free(map->key);
|
||||
}
|
||||
if(map->value != NULL && map->head->mman) {
|
||||
free(map->value);
|
||||
}
|
||||
free(map);
|
||||
}
|
||||
|
||||
static void put(struct map* map, const char* key, void* value) {
|
||||
if(map->key == NULL) {
|
||||
map->key = strdup(key);
|
||||
if(value != NULL && map->head->mman) {
|
||||
map->value = strdup(value);
|
||||
} else {
|
||||
map->value = value;
|
||||
}
|
||||
++map->head->size;
|
||||
} else if(strcmp(key, map->key) < 0) {
|
||||
if(map->left == NULL) {
|
||||
map->left = map_init();
|
||||
map->left->head = map->head;
|
||||
}
|
||||
put(map->left, key, value);
|
||||
} else if(strcmp(key, map->key) > 0) {
|
||||
if(map->right == NULL) {
|
||||
map->right = map_init();
|
||||
map->right->head = map->head;
|
||||
}
|
||||
put(map->right, key, value);
|
||||
} else {
|
||||
if(map->value != NULL && map->head->mman) {
|
||||
free(map->value);
|
||||
}
|
||||
if(value != NULL && map->head->mman) {
|
||||
map->value = strdup(value);
|
||||
} else {
|
||||
map->value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool map_put(struct map* map, const char* key, char* value) {
|
||||
if(map->head->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->head->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) {
|
||||
if(map->key == NULL) {
|
||||
return NULL;
|
||||
} else if(strcmp(key, map->key) < 0) {
|
||||
if(map->left == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return map_get(map->left, key);
|
||||
} else if(strcmp(key, map->key) > 0) {
|
||||
if(map->right == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return map_get(map->right, key);
|
||||
} else {
|
||||
return map->value;
|
||||
}
|
||||
}
|
||||
|
||||
size_t map_size(struct map* map) {
|
||||
return map->size;
|
||||
}
|
59
src/utils.c
Normal file
59
src/utils.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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>
|
||||
|
||||
time_t utils_get_time_millis() {
|
||||
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_split(char* str, const char chr) {
|
||||
char* split = strchr(str, chr);
|
||||
size_t count = 1;
|
||||
for(; split != NULL; ++count) {
|
||||
*split = 0;
|
||||
split = strchr(split + 1, chr);
|
||||
}
|
||||
return count;
|
||||
}
|
213
src/wofi.c
Normal file
213
src/wofi.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 <wofi.h>
|
||||
|
||||
static uint64_t width, height;
|
||||
static int64_t x, y;
|
||||
static struct zwlr_layer_shell_v1* shell;
|
||||
static GtkWidget* window;
|
||||
static const gchar* filter;
|
||||
|
||||
static void nop() {}
|
||||
|
||||
static void add_interface(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
|
||||
(void) data;
|
||||
if(strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version);
|
||||
}
|
||||
}
|
||||
|
||||
static void config_surface(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t _width, uint32_t _height) {
|
||||
(void) data;
|
||||
(void) _width;
|
||||
(void) _height;
|
||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||
zwlr_layer_surface_v1_set_size(surface, width, height);
|
||||
zwlr_layer_surface_v1_set_keyboard_interactivity(surface, true);
|
||||
if(x >= 0 && y >= 0) {
|
||||
zwlr_layer_surface_v1_set_margin(surface, y, 0, 0, x);
|
||||
zwlr_layer_surface_v1_set_anchor(surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
|
||||
}
|
||||
gtk_window_set_default_size(GTK_WINDOW(window), width, height);
|
||||
gtk_window_resize(GTK_WINDOW(window), width, height);
|
||||
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
|
||||
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
|
||||
gtk_widget_show_all(window);
|
||||
}
|
||||
|
||||
static void get_input(GtkSearchEntry* entry, gpointer data) {
|
||||
GtkListBox* box = data;
|
||||
filter = gtk_entry_get_text(GTK_ENTRY(entry));
|
||||
gtk_list_box_invalidate_filter(box);
|
||||
}
|
||||
|
||||
static void do_run(GtkWidget* box) {
|
||||
char* path = strdup(getenv("PATH"));
|
||||
char* original_path = path;
|
||||
size_t colon_count = utils_split(path, ':');
|
||||
for(size_t count = 0; count < colon_count; ++count) {
|
||||
DIR* dir = opendir(path);
|
||||
if(dir == NULL) {
|
||||
continue;
|
||||
}
|
||||
struct dirent* entry;
|
||||
while((entry = readdir(dir)) != NULL) {
|
||||
if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
char* full_path = utils_concat(3, path, "/", entry->d_name);
|
||||
struct stat info;
|
||||
stat(full_path, &info);
|
||||
if(access(full_path, X_OK) == 0 && S_ISREG(info.st_mode)) {
|
||||
GtkWidget* label = gtk_label_new(entry->d_name);
|
||||
gtk_label_set_xalign(GTK_LABEL(label), 0);
|
||||
gtk_container_add(GTK_CONTAINER(box), label);
|
||||
}
|
||||
free(full_path);
|
||||
}
|
||||
path += strlen(path) + 1;
|
||||
}
|
||||
free(original_path);
|
||||
}
|
||||
|
||||
static void do_dmenu(GtkWidget* box) {
|
||||
char* line;
|
||||
size_t size = 0;
|
||||
while(getline(&line, &size, stdin) != -1) {
|
||||
char* lf = strchr(line, '\n');
|
||||
if(lf != NULL) {
|
||||
*lf = 0;
|
||||
}
|
||||
GtkWidget* label = gtk_label_new(line);
|
||||
gtk_label_set_xalign(GTK_LABEL(label), 0);
|
||||
gtk_container_add(GTK_CONTAINER(box), label);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
|
||||
static void execute_action(char* mode, const gchar* cmd) {
|
||||
if(strcmp(mode, "run") == 0) {
|
||||
execlp(cmd, cmd, NULL);
|
||||
fprintf(stderr, "%s cannot be executed\n", cmd);
|
||||
exit(errno);
|
||||
} else if(strcmp(mode, "dmenu") == 0) {
|
||||
printf("%s\n", cmd);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_item(GtkListBox* box, GtkListBoxRow* row, gpointer data) {
|
||||
(void) box;
|
||||
char* mode = data;
|
||||
GtkWidget* label = gtk_bin_get_child(GTK_BIN(row));
|
||||
execute_action(mode, gtk_label_get_text(GTK_LABEL(label)));
|
||||
}
|
||||
|
||||
static void activate_search(GtkEntry* entry, gpointer data) {
|
||||
char* mode = data;
|
||||
execute_action(mode, gtk_entry_get_text(entry));
|
||||
}
|
||||
|
||||
static gboolean do_filter(GtkListBoxRow* row, gpointer data) {
|
||||
(void) data;
|
||||
GtkWidget* label = gtk_bin_get_child(GTK_BIN(row));
|
||||
const gchar* text = gtk_label_get_text(GTK_LABEL(label));
|
||||
if(filter == NULL || strcmp(filter, "") == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
if(strstr(text, filter) != NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean escape(GtkWidget* widget, GdkEvent* event, gpointer data) {
|
||||
(void) widget;
|
||||
(void) event;
|
||||
(void) data;
|
||||
guint code;
|
||||
gdk_event_get_keyval(event, &code);
|
||||
if(code == GDK_KEY_Escape) {
|
||||
exit(0);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void wofi_init(struct map* config) {
|
||||
width = strtol(config_get(config, "width", "1000"), NULL, 10);
|
||||
height = strtol(config_get(config, "height", "400"), NULL, 10);
|
||||
x = strtol(config_get(config, "x", "-1"), NULL, 10);
|
||||
y = strtol(config_get(config, "y", "-1"), NULL, 10);
|
||||
char* mode = map_get(config, "mode");
|
||||
char* prompt = config_get(config, "prompt", mode);
|
||||
GdkDisplay* disp = gdk_display_get_default();
|
||||
struct wl_display* wl = gdk_wayland_display_get_wl_display(disp);
|
||||
struct wl_registry* registry = wl_display_get_registry(wl);
|
||||
struct wl_registry_listener listener = {
|
||||
.global = add_interface,
|
||||
.global_remove = nop
|
||||
};
|
||||
wl_registry_add_listener(registry, &listener, NULL);
|
||||
wl_display_roundtrip(wl);
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_realize(window);
|
||||
gtk_widget_set_name(window, "window");
|
||||
GdkWindow* gdk_win = gtk_widget_get_window(window);
|
||||
gdk_wayland_window_set_use_custom_surface(gdk_win);
|
||||
struct wl_surface* wl_surface = gdk_wayland_window_get_wl_surface(gdk_win);
|
||||
struct zwlr_layer_surface_v1* surface = zwlr_layer_shell_v1_get_layer_surface(shell, wl_surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "wofi");
|
||||
struct zwlr_layer_surface_v1_listener* surface_listener = malloc(sizeof(struct zwlr_layer_surface_v1_listener));
|
||||
surface_listener->configure = config_surface;
|
||||
surface_listener->closed = nop;
|
||||
zwlr_layer_surface_v1_add_listener(surface, surface_listener, NULL);
|
||||
wl_surface_commit(wl_surface);
|
||||
wl_display_roundtrip(wl);
|
||||
|
||||
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_widget_set_name(box, "outer-box");
|
||||
gtk_container_add(GTK_CONTAINER(window), box);
|
||||
GtkWidget* entry = gtk_search_entry_new();
|
||||
gtk_widget_set_name(entry, "input");
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(entry), prompt);
|
||||
gtk_container_add(GTK_CONTAINER(box), entry);
|
||||
|
||||
GtkWidget* scroll = gtk_scrolled_window_new(NULL, NULL);
|
||||
gtk_widget_set_name(scroll, "scroll");
|
||||
gtk_container_add(GTK_CONTAINER(box), scroll);
|
||||
gtk_widget_set_size_request(scroll, width, height);
|
||||
|
||||
GtkWidget* inner_box = gtk_list_box_new();
|
||||
gtk_widget_set_name(inner_box, "inner-box");
|
||||
gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(inner_box), FALSE);
|
||||
gtk_container_add(GTK_CONTAINER(scroll), inner_box);
|
||||
|
||||
gtk_list_box_set_filter_func(GTK_LIST_BOX(inner_box), do_filter, NULL, NULL);
|
||||
|
||||
g_signal_connect(entry, "search-changed", G_CALLBACK(get_input), inner_box);
|
||||
g_signal_connect(inner_box, "row-activated", G_CALLBACK(activate_item), mode);
|
||||
g_signal_connect(entry, "activate", G_CALLBACK(activate_search), mode);
|
||||
g_signal_connect(window, "key-press-event", G_CALLBACK(escape), NULL);
|
||||
|
||||
if(strcmp(mode, "run") == 0) {
|
||||
do_run(inner_box);
|
||||
} else if(strcmp(mode, "dmenu") == 0) {
|
||||
do_dmenu(inner_box);
|
||||
}
|
||||
gtk_widget_grab_focus(entry);
|
||||
}
|
Reference in New Issue
Block a user