~sircmpwn/scurvy

f388085be6a6c0b9c1345c66ab8a82ebf8a371cc — Drew DeVault 4 years ago 44cef8c
Add config parsing and improved color support
M CMakeLists.txt => CMakeLists.txt +4 -0
@@ 20,6 20,7 @@ list(INSERT CMAKE_MODULE_PATH 0
	${CMAKE_CURRENT_SOURCE_DIR}/CMake
)

find_package(Libvterm REQUIRED)
find_package(Cairo REQUIRED)
find_package(Pango REQUIRED)



@@ 30,8 31,11 @@ add_executable(scurvy
    src/main.c
    src/ini.c
    src/colors.c
    src/config.c
    src/log.c
)

target_link_libraries(scurvy
    scurvy-wayland
    ${LIBVTERM_LIBRARIES}
)

M include/colors.h => include/colors.h +1 -1
@@ 1,7 1,7 @@
#ifndef _SCURVY_COLORS_H
#define _SCURVY_COLORS_H

#include <stdint.h>
#include <stdbool.h>

bool color_parse(const char *str, uint32_t *val);


A include/config.h => include/config.h +16 -0
@@ 0,0 1,16 @@
#ifndef _SCURVY_CONFIG_H
#define _SCURVY_CONFIG_H
#include <stdint.h>
#include <stdbool.h>

struct scurvy_config {
	char *font;
	char *url;
	char *term;
};

extern struct scurvy_config *config;

bool load_scurvy_config(const char *file);

#endif

A include/log.h => include/log.h +24 -0
@@ 0,0 1,24 @@
#ifndef _SCURVY_LOG_H
#define _SCURVY_LOG_H

#include <stdarg.h>

enum log_level {
	L_ERROR = 1,
	L_INFO = 2,
	L_DEBUG = 3
};

void init_log(enum log_level level);

void _scurvy_log(const char *filename, int line, enum log_level level,
		const char* format, ...) __attribute__((format(printf,4,5)));
#define scurvy_log(VERBOSITY, FMT, ...) \
	_scurvy_log(__FILE__, __LINE__, VERBOSITY, FMT, ##__VA_ARGS__)

void _scurvy_vlog(const char *filename, int line, enum log_level level,
		const char* format, va_list ap);
#define scurvy_vlog(VERBOSITY, FMT, ARGS) \
	_scurvy_vlog(__FILE__, __LINE__, VERBOSITY, FMT, ARGS)

#endif

A include/term.h => include/term.h +7 -0
@@ 0,0 1,7 @@
#ifndef _SCURVY_TERM_H
#define _SCURVY_TERM_H
#include <vterm.h>

extern VTerm *vterm;

#endif

M scurvy.conf => scurvy.conf +11 -13
@@ 12,16 12,14 @@ term=xterm

[colors]
#
# Configure colors using CSS specifications
# Supports color names, #HEXHEX, or #HEXHEXAA for alpha
# TODO: rgb, hsl, rgba, hsla
foreground="white"
background="black"
color0="black"
color1="red"
color2="green"
color3="yellow"
color4="blue"
color5="darkmagenta"
color6="cyan"
color7="white"
# Supports CSS color names, #RRGGBB, #RRGGBBAA, #RGB, or #RGBA
foreground=#e5e5e5
background=#000
color0=#000
color1=#A00
color2=#0A0
color3=#A50
color4=#00A
color5=#A0A
color6=#0AA
color7=#AAA

M src/colors.c => src/colors.c +17 -14
@@ 177,30 177,33 @@ static bool get_named_color(const char *name, uint32_t *color) {

static bool parse_hex_color(const char *str, uint32_t *val) {
	int len = strlen(str);
	if (len != 7 && len != 9) {
		return false;
	}
	char *end;
	*val = strtoul(str + 1, &end, 16);
	if (*end) {
		return false;
	}
	if (len == 7) {
	if (len == 9) { // #RRGGBBAA
		return true;
	} else if (len == 7) { // #RRGGBB
		*val = (*val << 8) | 0xFF;
		return true;
	} else if (len == 4) { // #RGB
		*val = ((*val & 0xF) | (*val & 0xF) << 4)
			| ((*val & 0xF0) | (*val & 0xF0) << 4) << 4
			| ((*val & 0xF00) | (*val & 0xF00) << 4) << 8;
		*val = (*val << 8) | 0xFF;
		return true;
	} else if (len == 5) { // #RGBA
		*val = ((*val & 0xF) | (*val & 0xF) << 4)
			| ((*val & 0xF0) | (*val & 0xF0) << 4) << 4
			| ((*val & 0xF00) | (*val & 0xF00) << 4) << 8
			| ((*val & 0xF000) | (*val & 0xF000) << 4) << 12;
	}
	return true;
	return false;
}

bool color_parse(const char *str, uint32_t *val) {
	if (strncmp(str, "rgba(", 5) == 0) {
		return false; // TODO
	} else if (strncmp(str, "rgb(", 4) == 0) {
		return false; // TODO
	} else if (strncmp(str, "hsla(", 5) == 0) {
		return false; // TODO
	} else if (strncmp(str, "hsl(", 4) == 0) {
		return false; // TODO
	} else if (*str == '#') {
	if (*str == '#') {
		return parse_hex_color(str, val);
	}
	return get_named_color(str, val);

A src/config.c => src/config.c +170 -0
@@ 0,0 1,170 @@
#define _POSIX_C_SOURCE 200809L
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wordexp.h>
#include <vterm.h>
#include "log.h"
#include "ini.h"
#include "colors.h"
#include "config.h"
#include "term.h"

struct scurvy_config *config = NULL;

static bool file_exists(const char *path) {
	return access(path, R_OK) != -1;
}

static char *get_config_path(const char **config_paths, int len) {
	if (!getenv("XDG_CONFIG_HOME")) {
		char *home = getenv("HOME");
		char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
		strcpy(config_home, home);
		strcat(config_home, "/.config");
		setenv("XDG_CONFIG_HOME", config_home, 1);
		free(config_home);
	}

	wordexp_t p;
	char *path;

	int i;
	for (i = 0; i < len; ++i) {
		if (wordexp(config_paths[i], &p, 0) == 0) {
			path = strdup(p.we_wordv[0]);
			wordfree(&p);
			if (file_exists(path)) {
				return path;
			}
		}
	}

	return NULL; // Not reached
}

static bool open_config(const char *path, FILE **f) {
	struct stat sb;
	if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
		return false;
	}

	if (path == NULL) {
		scurvy_log(L_ERROR, "Unable to find a config file!");
		return false;
	}

	*f = fopen(path, "r");
	return *f != NULL;
}

static void set_color(const char *key, const char *value) {
	VTermState *state = vterm_obtain_state(vterm);
	if (strncmp(key, "color", 5) == 0) {
		char *end;
		int index = strtol(key + 5, &end, 10);
		if (index < 0 || index > 255) {
			scurvy_log(L_ERROR, "Invalid color configuration '%s'", key);
			return;
		}
		uint32_t color;
		if (!color_parse(value, &color)) {
			scurvy_log(L_ERROR, "Invalid color specification '%s'", value);
			return;
		}
		VTermColor col = {
			color >> 8 & 0xFF,
			color >> 16 & 0xFF,
			color >> 24 & 0xFF,
		};
		vterm_state_set_palette_color(state, index, &col);
	}
}

int handle_config_option(void *_config, const char *section,
		const char *key, const char *value) {
	struct scurvy_config *config = _config;
	struct { const char *section; const char *key; char **string; } strings[] = {
		{ "scurvy", "font", &config->font },
		{ "scurvy", "url", &config->url },
		{ "scurvy", "term", &config->term },
	};

	if (strcmp(section, "colors") == 0) {
		set_color(key, value);
		return 1;
	}

	for (size_t i = 0; i < sizeof(strings) / (sizeof(void *) * 3); ++i) {
		if (strcmp(strings[i].section, section) == 0
				&& strcmp(strings[i].key, key) == 0) {
			if(*strings[i].string) free(*strings[i].string);
			*strings[i].string = strdup(value);
			return 1;
		}
	}

	scurvy_log(L_ERROR, "Unknown config option [%s]%s", section, key);
	return 1;
}

static bool load_config(const char *path, struct scurvy_config *config) {
	FILE *f;
	if (!open_config(path, &f)) {
		scurvy_log(L_ERROR, "Unable to open %s for reading", path);
		return false;
	}

	scurvy_log(L_DEBUG, "Loading config from %s", path);

	int ini = ini_parse_file(f, handle_config_option, config);
	if (ini != 0) {
		scurvy_log(L_ERROR, "Configuration parsing error on line %d", ini);
	}

	fclose(f);
	return ini == 0;
}

static void config_defaults() {
	config->font = strdup("monospace 10");
	config->url = strdup("xdg-open");
	config->term = strdup("xterm");
}

void free_config(struct scurvy_config *config) {
	free(config->font);
	free(config->url);
	free(config->term);
}

bool load_scurvy_config(const char *file) {
	static const char *config_paths[] = {
		"$XDG_CONFIG_HOME/scurvy.conf",
	};

	char *path;
	if (file != NULL) {
		path = strdup(file);
	} else {
		path = get_config_path(config_paths, 3);
	}

	struct scurvy_config *old_config = config;
	config = calloc(1, sizeof(struct scurvy_config));

	config_defaults(config);

	bool success = load_config(path, config);

	if (old_config) {
		free_config(old_config);
	}

	free(path);
	return success;
}

A src/log.c => src/log.c +69 -0
@@ 0,0 1,69 @@
#define _POSIX_C_SOURCE 200809L

#include <libgen.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "log.h"

int colored = 1;
enum log_level loglevel_default = L_ERROR;
enum log_level l = L_INFO;

static const char *level_colors[] = {
	[L_ERROR ] = "\x1B[1;31m",
	[L_INFO  ] = "\x1B[1;34m",
	[L_DEBUG ] = "\x1B[1;30m",
};

void init_log(enum log_level level) {
	if (level != L_DEBUG) {
		// command "debuglog" needs to know the user specified log level when
		// turning off debug logging.
		loglevel_default = level;
	}
	l = level;
}

void set_log_level(enum log_level level) {
	l = level;
}

void reset_log_level(void) {
	l = loglevel_default;
}

void _scurvy_vlog(const char *filename, int line, enum log_level level,
		const char* format, va_list ap) {
	if (level <= l) {
		unsigned int c = level;
		if (c > sizeof(level_colors) / sizeof(char *)) {
			c = sizeof(level_colors) / sizeof(char *) - 1;
		}

		if (colored && isatty(STDERR_FILENO)) {
			fprintf(stderr, "%s", level_colors[c]);
		}

		char *file = strdup(filename);
		fprintf(stderr, "[%s:%d] ", basename(file), line);
		free(file);
		vfprintf(stderr, format, ap);

		if (colored && isatty(STDERR_FILENO)) {
			fprintf(stderr, "\x1B[0m");
		}
		fprintf(stderr, "\n");
	}
}

void _scurvy_log(const char *filename, int line, enum log_level level,
		const char* format, ...) {
	va_list args;
	va_start(args, format);
	_scurvy_vlog(filename, line, level, format, args);
	va_end(args);
}

M src/main.c => src/main.c +16 -1
@@ 1,5 1,20 @@
#include <stdlib.h>
#include <stdint.h>
#include "config.h"
#include "log.h"
#include "term.h"

struct VTerm *vterm;

int wayland_main();

int main(int argc, char **argv) {
	return wayland_main();
	init_log(L_DEBUG);
	vterm = vterm_new(80, 24);
	if (!load_scurvy_config(NULL)) {
		return 1;
	}
	int status = wayland_main();
	vterm_free(vterm);
	return status;
}

M src/wayland/main.c => src/wayland/main.c +12 -0
@@ 1,3 1,4 @@
#include <xkbcommon/xkbcommon.h>
#include <wayland-client.h>
#include <cairo/cairo.h>
#include <stdbool.h>


@@ 16,9 17,20 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
struct registry *registry;
struct window *win;

static void window_resize(struct window *_win) {
	// TODO
}

static void keyboard_event(enum wl_keyboard_key_state state,
		xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
	// TODO
}

int wayland_main(void) {
	registry = registry_poll();
	registry->input->notify = keyboard_event;
	win = window_setup(registry, 640, 480, 1, true);
	win->notify_resize = window_resize;
	while (wl_display_dispatch(registry->display) != -1) {
		if (window_prerender(win) && win->cairo) {
			cairo_set_source_u32(win->cairo, 0xFF0000FF);