~sircmpwn/scurvy

0946e861653245b931dfcd747e6f1566aaaca9ae — Drew DeVault 4 years ago f388085
Add child process, more colors
M CMakeLists.txt => CMakeLists.txt +8 -1
@@ 25,7 25,11 @@ find_package(Cairo REQUIRED)
find_package(Pango REQUIRED)

add_subdirectory(src/wayland)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/include
    ${PANGO_INCLUDE_DIRS}
    ${CAIRO_INCLUDE_DIRS}
)

add_executable(scurvy
    src/main.c


@@ 33,6 37,9 @@ add_executable(scurvy
    src/colors.c
    src/config.c
    src/log.c
    src/pango.c
    src/child.c
    src/term.c
)

target_link_libraries(scurvy

A include/child.h => include/child.h +14 -0
@@ 0,0 1,14 @@
#ifndef _SCURVY_CHILD_H
#define _SCURVY_CHILD_H
#include <sys/types.h>

struct scurvy_child {
	pid_t pid;
	int fd;
};

struct scurvy_child *child_spawn(char **argv);
void child_free(struct scurvy_child *child);
bool child_read_pty(struct scurvy_child *child);

#endif

M include/config.h => include/config.h +1 -0
@@ 4,6 4,7 @@
#include <stdbool.h>

struct scurvy_config {
	uint32_t background;
	char *font;
	char *url;
	char *term;

A include/pango.h => include/pango.h +13 -0
@@ 0,0 1,13 @@
#ifndef _SCURVY_PANGO_H
#define _SCURVY_PANGO_H
#include <cairo/cairo.h>
#include <pango/pangocairo.h>

PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
		int32_t scale, bool markup);
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
		int32_t scale, bool markup, const char *fmt, ...);
void pango_printf(cairo_t *cairo, const char *font,
		int32_t scale, bool markup, const char *fmt, ...);

#endif

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

extern VTerm *vterm;

void set_term_size(cairo_t *cairo, int width, int height);

#endif

A src/child.c => src/child.c +136 -0
@@ 0,0 1,136 @@
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <libtsm.h>
#include <termbox.h>
#include <signal.h>
#include <stdbool.h>
#include <errno.h>
#include <wordexp.h>
#include <termios.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <pty.h>
#include <vterm.h>
#include "term.h"
#include "log.h"
#include "child.h"

static void init_child_pty(struct scurvy_child *child) {
	if (grantpt(child->fd) < 0) {
		close(child->fd);
		goto fail;
	}
	if (unlockpt(child->fd) < 0) {
		close(child->fd);
		goto fail;
	}
	char *slave_name = ptsname(child->fd);
	if (!slave_name) {
		close(child->fd);
		goto fail;
	}
	int slave = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY);
	if (slave < 0) {
		close(child->fd);
		goto fail;
	}
	pid_t pid = setsid();
	if (pid < 0) {
		close(child->fd);
		goto fail_slave;
	}
	close(child->fd);
	struct termios attr;
	struct winsize ws;
	if (tcgetattr(slave, &attr) < 0) {
		goto fail_slave;
	}
	attr.c_cc[VERASE] = 010;
	if (tcsetattr(slave, TCSANOW, &attr) < 0) {
		goto fail_slave;
	}
	memset(&ws, 0, sizeof(ws));
	ws.ws_col = 80;
	ws.ws_row = 24;
	if (ioctl(slave, TIOCSWINSZ, &ws) < 0) {
		goto fail_slave;
	}
	dup2(slave, STDIN_FILENO);
	dup2(slave, STDOUT_FILENO);
	dup2(slave, STDERR_FILENO);
	ioctl(slave, TIOCSCTTY, NULL);
	close(slave);
	setenv("TERM", "xterm-256color", 1);
	return;
fail_slave:
	close(slave);
fail:
	// TODO: bubble up error
	exit(1);
}

static void init_child(struct scurvy_child *child, char **argv) {
	// Clear signals
	sigset_t sigset;
	sigemptyset(&sigset);
	if (sigprocmask(SIG_SETMASK, &sigset, NULL) < 0) {
		exit(1);
	}
	for (int i = 0; i < SIGUNUSED; ++i) {
		signal(i, SIG_DFL);
	}
	init_child_pty(child);
	execvp(argv[0], argv);
	exit(1);
}

struct scurvy_child *child_spawn(char **argv) {
	struct scurvy_child *child = calloc(sizeof(struct scurvy_child), 1);
	if (!child) {
		return NULL;
	}
	child->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
	child->pid = fork();
	if (child->pid < 0) {
		scurvy_log(L_ERROR, "fork() failed (%d)", errno);
	} else if (!child->pid) {
		init_child(child, argv);
	}
	return child;
}

bool child_read_pty(struct scurvy_child *child) {
	static char buf[4096];
	int r = read(child->fd, &buf, sizeof(buf));
	if (r == -1) {
		if (errno != EAGAIN) {
			scurvy_log(L_DEBUG, "pty read error %d", errno);
			// TODO: Abort
			return false;
		}
	}
	if (r > 0) {
		//vterm_input_write(vterm, buf, r);
		return true;
	}
	return false;
}

void child_free(struct scurvy_child *child) {
	if (!child) {
		return;
	}
	close(child->fd);
	free(child);
}

M src/config.c => src/config.c +25 -12
@@ 62,8 62,21 @@ static bool open_config(const char *path, FILE **f) {
	return *f != NULL;
}

static void set_color(const char *key, const char *value) {
static void set_color(struct scurvy_config *config,
		const char *key, const char *value) {
	VTermState *state = vterm_obtain_state(vterm);
	VTermColor fg, bg;
	vterm_state_get_default_colors(state, &fg, &bg);
	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,
	};
	if (strncmp(key, "color", 5) == 0) {
		char *end;
		int index = strtol(key + 5, &end, 10);


@@ 71,17 84,17 @@ static void set_color(const char *key, const char *value) {
			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);
		scurvy_log(L_DEBUG, "Set color%d to %08X", index, color);
	} else if (strcmp(key, "foreground") == 0) {
		vterm_state_set_default_colors(state, &col, &bg);
		scurvy_log(L_DEBUG, "Set foreground to %08X", color);
	} else if (strcmp(key, "background") == 0) {
		vterm_state_set_default_colors(state, &fg, &col);
		config->background = color;
		scurvy_log(L_DEBUG, "Set background to %08X", color);
	} else {
		scurvy_log(L_ERROR, "Invalid color configuration '%s'", key);
	}
}



@@ 95,7 108,7 @@ int handle_config_option(void *_config, const char *section,
	};

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


M src/main.c => src/main.c +7 -4
@@ 3,18 3,21 @@
#include "config.h"
#include "log.h"
#include "term.h"
#include "child.h"

struct VTerm *vterm;

int wayland_main();
int wayland_main(struct scurvy_child *);

int main(int argc, char **argv) {
	init_log(L_DEBUG);
	vterm = vterm_new(80, 24);
	vterm_set_utf8(vterm, 1);
	if (!load_scurvy_config(NULL)) {
		return 1;
	}
	int status = wayland_main();
	char *_argv[] = { "/usr/bin/bash", NULL };
	struct scurvy_child *child = child_spawn(_argv);
	int status = wayland_main(child);
	child_free(child);
	vterm_free(vterm);
	return status;
}

A src/pango.c => src/pango.c +73 -0
@@ 0,0 1,73 @@
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "log.h"

PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
		int32_t scale, bool markup) {
	PangoLayout *layout = pango_cairo_create_layout(cairo);
	PangoAttrList *attrs;
	if (markup) {
		char *buf;
		pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL);
		pango_layout_set_markup(layout, buf, -1);
		free(buf);
	} else {
		attrs = pango_attr_list_new();
		pango_layout_set_text(layout, text, -1);
	}
	pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
	PangoFontDescription *desc = pango_font_description_from_string(font);
	pango_layout_set_font_description(layout, desc);
	pango_layout_set_single_paragraph_mode(layout, 1);
	pango_layout_set_attributes(layout, attrs);
	pango_attr_list_unref(attrs);
	pango_font_description_free(desc);
	return layout;
}

void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
		int32_t scale, bool markup, const char *fmt, ...) {
	char *buf = malloc(2048);

	va_list args;
	va_start(args, fmt);
	if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
		strcpy(buf, "[buffer overflow]");
	}
	va_end(args);

	PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
	pango_cairo_update_layout(cairo, layout);

	pango_layout_get_pixel_size(layout, width, height);

	g_object_unref(layout);

	free(buf);
}

void pango_printf(cairo_t *cairo, const char *font,
		int32_t scale, bool markup, const char *fmt, ...) {
	char *buf = malloc(2048);

	va_list args;
	va_start(args, fmt);
	if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
		strcpy(buf, "[buffer overflow]");
	}
	va_end(args);

	PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
	pango_cairo_update_layout(cairo, layout);

	pango_cairo_show_layout(cairo, layout);

	g_object_unref(layout);

	free(buf);
}

A src/term.c => src/term.c +13 -0
@@ 0,0 1,13 @@
#include <cairo/cairo.h>
#include <vterm.h>
#include "term.h"
#include "pango.h"
#include "config.h"

struct VTerm *vterm;

void set_term_size(cairo_t *cairo, int width, int height) {
	int w, h;
	get_text_size(cairo, config->font, &w, &h, 1, false, "Hello world");
	vterm_set_size(vterm, height / h, width / w);
}

M src/wayland/main.c => src/wayland/main.c +7 -3
@@ 5,6 5,9 @@
#include <stdint.h>
#include "wayland/window.h"
#include "wayland/registry.h"
#include "config.h"
#include "child.h"
#include "term.h"

void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
	cairo_set_source_rgba(cairo,


@@ 18,7 21,7 @@ struct registry *registry;
struct window *win;

static void window_resize(struct window *_win) {
	// TODO
	set_term_size(win->cairo, win->width, win->height);
}

static void keyboard_event(enum wl_keyboard_key_state state,


@@ 26,15 29,16 @@ static void keyboard_event(enum wl_keyboard_key_state state,
	// TODO
}

int wayland_main(void) {
int wayland_main(struct scurvy_child *child) {
	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);
			cairo_set_source_u32(win->cairo, config->background);
			cairo_paint(win->cairo);
			child_read_pty(child);
			window_render(win);
			wl_display_flush(registry->display);
		}

M src/wayland/window.c => src/wayland/window.c +1 -0
@@ 89,6 89,7 @@ void window_make_shell(struct window *window) {
	window->shell_surface = wl_shell_get_shell_surface(window->registry->shell, window->surface);
	wl_shell_surface_add_listener(window->shell_surface, &surface_listener, window);
	wl_shell_surface_set_toplevel(window->shell_surface);
	wl_shell_surface_set_title(window->shell_surface, "scurvy");
}

struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,