aboutsummaryrefslogblamecommitdiff
path: root/src/sysbin/terminal/main.c
blob: 9ba7afe678e672b11dc9c5f9d6fefd200e11244f (plain) (tree)




























































































































































































































































                                                                                                        
#include <string.h>
#include <malloc.h>
#include <user_region.h>
#include <debug.h>

#include <gip.h>
#include <draw.h>
#include <keyboard.h>

typedef char tchar_t;

typedef struct {
	fb_info_t mode;
	fd_t fd;
	fb_t *fb;

	uint32_t sv_features, cl_features;

	font_t *font;
	int cw, ch;	// size of a character
	color_t fg, bg;

	keyboard_t *kb;

	int w, h;
	tchar_t* scr_chars;
	int csrl, csrc;
	bool csr_visible;

} term_t;

void c_buffer_info(gip_handler_t *s, gip_msg_header *p, gip_buffer_info_msg *m);
void c_key_down(gip_handler_t *s, gip_msg_header *p);
void c_key_up(gip_handler_t *s, gip_msg_header *p);
void c_unknown_msg(gip_handler_t *s, gip_msg_header *p);
void c_fd_error(gip_handler_t *s);

void c_async_initiate(gip_handler_t *s, gip_msg_header *p, void* msgdata, void* hdata);

gip_handler_callbacks_t term_gip_cb = {
	.reset = 0,
	.initiate = 0,
	.ok = 0,
	.failure = 0,
	.enable_features = 0,
	.disable_features = 0,
	.query_mode = 0,
	.set_mode = 0,
	.buffer_info = c_buffer_info,
	.mode_info = 0,
	.buffer_damage = 0,
	.key_down = c_key_down,
	.key_up = c_key_up,
	.unknown_msg = c_unknown_msg,
	.fd_error = c_fd_error,
};


int main(int argc, char **argv) {
	dbg_print("[terminal] Starting up.\n");

	term_t term;
	memset(&term, 0, sizeof(term));

	term.kb = init_keyboard();
	ASSERT(term.kb != 0);

	term.font = g_load_font("default");
	ASSERT(term.font != 0);
	term.cw = g_text_width(term.font, "#");
	term.ch = g_text_height(term.font, "#");

	gip_handler_t *h = new_gip_handler(&term_gip_cb, &term);
	ASSERT(h != 0);

	h->mainloop_item.fd = 1;
	mainloop_add_fd(&h->mainloop_item);

	gip_msg_header reset_msg = { .code = GIPC_RESET, .arg = 0 };
	gip_cmd(h, &reset_msg, 0, c_async_initiate, 0);

	mainloop_run();

	return 0;
}

// Terminal features

void term_draw_c(term_t *t, int l, int c) {
	color_t bg = t->bg;
	if (l == t->csrl && c == t->csrc) bg = g_color_rgb(t->fb, 0, 100, 0);

	char ss[2];
	ss[0] = t->scr_chars[l * t->w + c];
	ss[1] = 0;

	g_fillrect(t->fb, c * t->cw, l * t->ch, t->cw, t->ch, bg);
	g_write(t->fb, c * t->cw, l * t->ch, ss, t->font, t->fg);
}

void term_move_cursor(term_t *t, int l, int c) {
	int l0 = t->csrl, c0 = t->csrc;
	t->csrl = l;
	t->csrc = c;
	term_draw_c(t, l0, c0);
	term_draw_c(t, l, c);
}

void term_clear_screen(term_t *t) {
	t->csr_visible = true;

	for (int i = 0; i < t->w * t->h; i++) t->scr_chars[i] = ' ';

	g_fillrect(t->fb, 0, 0, t->mode.width, t->mode.height, t->bg);

	term_move_cursor(t, 0, 0);
}

void term_scroll1(term_t *t) {
	for (int i = 0; i < t->w * (t->h - 1); i++)
		t->scr_chars[i] = t->scr_chars[i + t->w];
	for (int i = 0; i < t->w; i++) {
		t->scr_chars[i + (t->h - 1) * t->w] = ' ';
	}
	t->csrl--;

	g_scroll_up(t->fb, t->ch);
	g_fillrect(t->fb, 0, t->ch * (t->h - 1), t->cw * t->w, t->ch, t->bg);
}

void term_putc(term_t *t, int c) {
	int nl = t->csrl, nc = t->csrc;
	if (c == '\n') {
		nl++; nc = 0;
	} else if (c == '\b') {
		if (nc > 0) {
			nc--;
			t->scr_chars[nl * t->w + nc] = ' ';
		}
	} else if (c == '\t') {
		while (nc % 4 != 0) nc++;
	} else if (c >= ' ' && c < 128) {
		t->scr_chars[nl * t->w + nc] = c;
		nc++;
	}
	if (nc >= t->w) {
		nc = 0;
		nl++;
	}
	if (nl >= t->h) {
		term_scroll1(t);
		nl--;
	}
	term_move_cursor(t, nl, nc);
}

void term_puts(term_t *t, char* s) {
	while ((*s) != 0) {
		term_putc(t, *s);
		s++;
	}
}

// GIP client connection

void c_buffer_info(gip_handler_t *s, gip_msg_header *p, gip_buffer_info_msg *m) {
	dbg_printf("[terminal] Got buffer info msg\n");

	term_t *c = (term_t*)s->data;

	if (c->fb != 0) {
		g_delete_fb(c->fb);
		c->fb = 0;
	}
	if (c->fd != 0) {
		close(c->fd);
		c->fd = 0;
	}

	c->fd = use_token(&m->tok);
	if (c->fd != 0) {
		memcpy(&c->mode, &m->geom, sizeof(fb_info_t));

		dbg_printf("[terminal] Got buffer on FD %d, %dx%dx%d\n",
			c->fd, c->mode.width, c->mode.height, c->mode.bpp);

		if (c->scr_chars != 0) {
			free(c->scr_chars);
			c->scr_chars = 0;
		}
		c->w = c->mode.width / c->cw;
		c->h = c->mode.height / c->ch;
		c->scr_chars = (tchar_t*)malloc(c->w * c->h * sizeof(tchar_t));
		ASSERT(c->scr_chars != 0);

		c->fb = g_fb_from_file(c->fd, &m->geom);
		if (c->fb != 0) {
			c->bg = g_color_rgb(c->fb, 0, 0, 0);
			c->fg = g_color_rgb(c->fb, 200, 200, 200);
			term_clear_screen(c);
		} else {
			dbg_printf("[terminal] Could not open framebuffer file %d\n", c->fd);
		}

	}
}

void c_key_down(gip_handler_t *s, gip_msg_header *p) {
	term_t *c = (term_t*)s->data;

	key_t k = keyboard_press(c->kb, p->arg);

	if (k.flags & KBD_CHAR)
		dbg_printf("D %x %c\n", k.flags, k.key);
	else
		dbg_printf("D %x %d\n", k.flags, k.key);
}

void c_key_up(gip_handler_t *s, gip_msg_header *p) {
	term_t *c = (term_t*)s->data;

	key_t k = keyboard_release(c->kb, p->arg);

	if (k.flags & KBD_CHAR)
		dbg_printf("U %x %c\n", k.flags, k.key);
	else
		dbg_printf("U %x %d\n", k.flags, k.key);

	if (k.flags & KBD_CHAR) {
		term_putc(c, k.chr);
	} else {
		if (k.key == KBD_CODE_RETURN) term_putc(c, '\n');
		if (k.key == KBD_CODE_TAB) term_putc(c, '\t');
		if (k.key == KBD_CODE_BKSP) term_putc(c, '\b');
	}
}

void c_unknown_msg(gip_handler_t *s, gip_msg_header *p) {
	// TODO
}

void c_fd_error(gip_handler_t *s) {
	// TODO
}

void c_async_initiate(gip_handler_t *s, gip_msg_header *p, void* msgdata, void* hdata) {
	term_t *c = (term_t*)s->data;
	c->sv_features = p->arg;

	dbg_printf("[temrinal] Got initiate reply to reset request (features 0x%p).\n", c->sv_features);
}

/* vim: set ts=4 sw=4 tw=0 noet :*/