#include #include #include #include #include #include #include #include #define FONT_HARDCODE "berry" _window_handle_t window; uint8_t *pixbuf; char *termbuf; struct font_info *font; uint32_t width; uint32_t height; uint32_t cols = 50; uint32_t rows = 15; uint32_t cursor_y = 0; uint32_t cursor_x = 0; uint8_t bg_color = 0x10; uint8_t fg_color = 0x07; struct waiting_for_key_record { _task_handle_t task; struct waiting_for_key_record *next; } *first_key_waiting = 0, *last_key_waiting = 0; static void draw_char(uint32_t y, uint32_t x, bool inverted) { //syslogf("drawing 0x%2h%s at %u, %u", termbuf[y * cols + x], inverted ? " inverted" : "", y, x); put_char(font, termbuf[y * cols + x], pixbuf + y * font->space_height * width + x * font->space_width, width, inverted ? fg_color : bg_color, inverted ? bg_color : fg_color); } static void clear() { for (uint32_t i = 0; i < cols * rows; ++i) termbuf[i] = ' '; for (uint32_t i = 0; i < width * height; ++i) pixbuf[i] = bg_color; } static void scroll_fw() { uint32_t i; for (i = 0; i < cols * (rows - 1); ++i) termbuf[i] = termbuf[i + cols]; for (; i < cols * rows; ++i) termbuf[i] = ' '; const uint32_t row_height = font->space_height; for (i = 0; i < width * (height - row_height); ++i) pixbuf[i] = pixbuf[i + width * row_height]; for (; i < width * height; ++i) pixbuf[i] = bg_color; } static void cursor_down() { if (cursor_y == rows - 1) scroll_fw(); else ++cursor_y; } static void cursor_right() { if (cursor_x == cols - 1) { cursor_x = 0; cursor_down(); } else ++cursor_x; } __attribute__ ((pure)) static uint32_t word_len(const char *sz) { const char *const back = sz; while ((*sz != ' ') && (*sz != '\n') && *sz && (*sz != '\t')) ++sz; return sz - back; } #define MIN_TAB 3 #define TAB_STOP 4 static void on_newline() { draw_char(cursor_y, cursor_x, false); cursor_x = 0; cursor_down(); } static void add_char(char ch); static void on_tab() { for (uint32_t i = 0; i < MIN_TAB; ++i) add_char(' '); while (cursor_x % TAB_STOP) add_char(' '); } static void add_char(char ch) { if (ch == '\n') on_newline(); else if (ch == '\t') on_tab(); else { termbuf[cursor_y * cols + cursor_x] = ch; draw_char(cursor_y, cursor_x, false); cursor_right(); } } static void add_sz_no_ww(const char *sz) { while (*sz) add_char(*(sz++)); } static void add_sn_no_ww(const char *sz, uint32_t l) { for (uint32_t i = 0; i < l; ++i) add_char(sz[i]); } static void add_sz_ww(const char *sz) { while (*sz) { if (*sz == ' ') { ++sz; continue; } if (*sz == '\n') { on_newline(); ++sz; continue; } if (*sz == '\t') { on_tab(); ++sz; continue; } if (cursor_x) add_char(' '); const uint32_t len = word_len(sz); if ((len > cols - cursor_x) && (len <= cols)) { cursor_x = 0; cursor_down(); } add_sn_no_ww(sz, len); sz += len; } } char *const sz_empty_backup = ""; char *sz_from_task = 0; uint32_t sz_buf_size = 0; #define BUF_INCREMENTS 1024 static void get_sn(uint32_t len, _task_handle_t from) { if (sz_buf_size <= len) { if (sz_from_task && (sz_from_task != sz_empty_backup)) free_block(sz_from_task); sz_from_task = get_block(sz_buf_size += 1024); if (!sz_from_task) { add_sz_ww("Could not allocate enough memory for sent string. Treating as empty string.\n"); sz_buf_size = 0; sz_from_task = sz_empty_backup; return; } } const uint32_t real_len = try_read_ipc(from, sz_from_task, len); sz_from_task[real_len] = '\0'; } static void set_dimensions(uint32_t new_rows, uint32_t new_cols) { free_block(termbuf); free_block(pixbuf); //from here until _resize, the kernel may draw garbage if it // needs to redraw the window. it won't page fault though. rows = new_rows; cols = new_cols; termbuf = get_block(rows * cols); width = cols * font->space_width; height = cols * font->space_height; pixbuf = get_block(width * height); cursor_y = 0; cursor_x = 0; clear(); _resize_window(window, width, height, pixbuf); } void draw_all() { for (uint32_t y = 0; y < rows; ++y) for (uint32_t x = 0; x < cols; ++x) draw_char(y, x, false); draw_char(cursor_y, cursor_x, true); } //#include void main(const char *cmd) { //syslogf(" this task: 0x%2h", this_task); //syslogf(" stdio task: 0x%2h", stdio_task); //syslogf("calling task: 0x%2h", calling_task); font = get_font(FONT_HARDCODE); if (!font) return; termbuf = get_block(cols * rows); width = cols * font->space_width; height = rows * font->space_height; pixbuf = get_block(width * height); clear(); add_sz_ww("Portland Terminal\n"); window = _new_window(width, height, pixbuf); _paint_window(window); _task_handle_t child_handle = run_command(cmd, this_task); if (!child_handle) { add_sz_ww("Failed to run passed command. Press any key to close.\n"); _paint_window(window); while (1) { struct window_action action; _get_win_action(window, &action); if (action.action_type == NOT_READY) { _wait_for_action(); _yield_task(); } else if (action.action_type == KEY_DOWN) return; } } while (1) { if (first_key_waiting) { struct window_action action; _get_win_action(window, &action); if (action.action_type == KEY_DOWN) { union terminal_response rs = { .as_key = action.as_key }; try_send_ipc(first_key_waiting->task, &rs, sizeof(union terminal_response)); free_block(first_key_waiting); first_key_waiting = first_key_waiting->next; if (!first_key_waiting) last_key_waiting = 0; continue; } } _task_handle_t from = _find_unread_ipc(); if (!from) { if (!_is_task_running(child_handle)) return; _wait_for_action(); _wait_for_any_ipc_sent(); _wait_for_task(child_handle); _yield_task(); continue; } struct terminal_command request; const uint32_t read = try_read_ipc(from, &request, sizeof(struct terminal_command)); if (read != sizeof(struct terminal_command)) { syslogf("received %u / %u bytes of a command from 0x%2x", read, sizeof(struct terminal_command), from); continue; } //syslogf("received full command from 0x%2x", from); switch (request.kind) { case SET_DIMENSIONS: set_dimensions(request.as_coords.y, request.as_coords.x); continue; union terminal_response rs; case GET_DIMENSIONS: rs.as_coords.y = rows; rs.as_coords.x = cols; try_send_ipc(from, &rs, sizeof(union terminal_response)); continue; case PAINT: _paint_window(window); continue; case CLEAR: clear(); cursor_y = 0; cursor_x = 0; draw_char(0, 0, true); continue; case SET_COLOR: fg_color = request.as_color.fg; bg_color = request.as_color.bg; draw_all(); continue; case SET_CURSOR: draw_char(cursor_y, cursor_x, false); cursor_y = request.as_coords.y; cursor_x = request.as_coords.x; draw_char(cursor_y, cursor_x, true); continue; case CURSOR_LEFT: draw_char(cursor_y, cursor_x, false); if (cursor_x) --cursor_x; else if (cursor_y) { cursor_x = cols - 1; --cursor_y; } draw_char(cursor_y, cursor_x, true); continue; case CURSOR_RIGHT: draw_char(cursor_y, cursor_x, false); cursor_right(); draw_char(cursor_y, cursor_x, true); continue; case CURSOR_UP: if (cursor_y) { draw_char(cursor_y, cursor_x, false); --cursor_y; draw_char(cursor_y, cursor_x, true); } continue; case CURSOR_DOWN: draw_char(cursor_y, cursor_x, false); if (cursor_y == rows - 1) scroll_fw(); else ++cursor_y; draw_char(cursor_y, cursor_x, true); continue; case ADD_CHAR: add_char(request.as_char); draw_char(cursor_y, cursor_x, true); continue; case ADD_SN: draw_char(cursor_y, cursor_x, false); get_sn(request.as_uint, from); add_sz_ww(sz_from_task); draw_char(cursor_y, cursor_x, true); continue; case ADD_SN_NO_WORDWRAP: draw_char(cursor_y, cursor_x, false); get_sn(request.as_uint, from); add_sz_no_ww(sz_from_task); draw_char(cursor_y, cursor_x, true); continue; struct waiting_for_key_record *new_record; case GET_KEY: new_record = get_block(sizeof(struct waiting_for_key_record)); if (last_key_waiting) last_key_waiting->next = new_record; else first_key_waiting = new_record; last_key_waiting = new_record; new_record->task = from; new_record->next = 0; continue; default: add_sz_ww("Bad terminal command received, ignoring.\n"); continue; } } }