#include #include #include #include #include #include #include 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; mainloop_fd_t app; char rd_c_buf; char wr_c_buf; } 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 term_on_rd_c(mainloop_fd_t *fd); void term_app_on_error(mainloop_fd_t *fd); 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); // setup communication with app term.app.fd = 2; term.app.on_error = term_app_on_error; term.app.data = &term; mainloop_expect(&term.app, &term.rd_c_buf, 1, term_on_rd_c); 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; if (t->fb) { 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] = ' '; if (t->fb) 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--; if (t->fb) { 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--; } else { nl--; nc = t->w - 1; } 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); mainloop_rm_fd(&c->app); mainloop_add_fd(&c->app); } 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; keyboard_press(c->kb, p->arg); } 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); c->wr_c_buf = 0; if (k.flags & KBD_CHAR) { c->wr_c_buf = k.chr; } else { if (k.key == KBD_CODE_RETURN) c->wr_c_buf = '\n'; if (k.key == KBD_CODE_TAB) c->wr_c_buf = '\t'; if (k.key == KBD_CODE_BKSP) c->wr_c_buf = '\b'; } mainloop_nonblocking_write(&c->app, &c->wr_c_buf, 1, false); } void c_unknown_msg(gip_handler_t *s, gip_msg_header *p) { // TODO } void c_fd_error(gip_handler_t *s) { // TODO } void term_on_rd_c(mainloop_fd_t *fd) { term_t *t = (term_t*)fd->data; term_putc(t, t->rd_c_buf); } void term_app_on_error(mainloop_fd_t *fd) { // 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 :*/