diff options
author | Benji Dial <benji6283@gmail.com> | 2021-02-16 20:38:53 -0500 |
---|---|---|
committer | Benji Dial <benji6283@gmail.com> | 2021-02-16 20:38:53 -0500 |
commit | 47513bd32c256c4f35e3a8ced7d9fd7e15903530 (patch) | |
tree | cafdf75d52a954814726e07445063c41bb6599f9 /src/user | |
parent | bd7facc4b5f53481dc85a15ba123361b2758655b (diff) | |
download | portland-os-47513bd32c256c4f35e3a8ced7d9fd7e15903530.tar.gz |
terminal application with ipc, shift+pause state dumper, hello world for terminal, meminfo popup program
Diffstat (limited to 'src/user')
25 files changed, 883 insertions, 509 deletions
diff --git a/src/user/hello/hello.asm b/src/user/hello/hello.asm new file mode 100644 index 0000000..cc0645a --- /dev/null +++ b/src/user/hello/hello.asm @@ -0,0 +1,28 @@ +bits 32 + +global _entry + +section .text +_entry: + mov eax, 0x05 + mov ebx, esi + mov ecx, data.len + mov edx, data + int 0x30 + + int 0x38 + +section .rodata +data: + dd 0xb + dd .str_len + dd 0 + +.str: + db "Hello, world!", 0x0a +.str_len equ $ - .str + + dd 0x02 + dd 0 + dd 0 +.len equ $ - data
\ No newline at end of file diff --git a/src/user/highway/cmds.c b/src/user/highway/cmds.c index 0420ae1..53198df 100644 --- a/src/user/highway/cmds.c +++ b/src/user/highway/cmds.c @@ -1,4 +1,4 @@ -#include <terminal/terminal.h> +#include <libterm/terminal.h> #include <knob/file.h> diff --git a/src/user/highway/line.c b/src/user/highway/line.c index bf171e5..cefdf92 100644 --- a/src/user/highway/line.c +++ b/src/user/highway/line.c @@ -1,4 +1,4 @@ -#include <terminal/terminal.h> +#include <libterm/terminal.h> #include <knob/block.h> #include <knob/task.h> @@ -20,7 +20,7 @@ void ensure_color() { const struct no_null_sn *fg = get_var((struct no_null_sn){.data = "_color_fg", .length = 9}); const struct no_null_sn *bg = get_var((struct no_null_sn){.data = "_color_bg", .length = 9}); if (fg && bg) - set_color( + term_set_color( (hex_to_int(fg->data[0]) << 4) | hex_to_int(fg->data[1]), (hex_to_int(bg->data[0]) << 4) | hex_to_int(bg->data[1]) ); @@ -32,6 +32,7 @@ static void line_replace(const char *from) { while (*fi) { if (ti == line + LINE_SIZE) { term_add_sz("Line too long.\n"); + term_paint(); line[0] = '\0'; return; } @@ -49,11 +50,13 @@ static void line_replace(const char *from) { term_addf("Unterminated variable at\"%10s...\".\n", fi); else term_addf("Unterminated variable at \"%s\".\n", fi); + term_paint(); line[0] = '\0'; return; } if (ti + (var_end - var_start) >= line + LINE_SIZE) { term_add_sz("Line too long.\n"); + term_paint(); line[0] = '\0'; return; } @@ -87,16 +90,17 @@ void run_line(const char *original_line) { else if (blockequ(line, "echo ", 5)) { term_add_sz(space + 1); term_add_char('\n'); + term_paint(); } else if (blockequ(line, "vars", 5)) dump_vars(); - else if (blockequ(line, "quit", 5)) { - del_term(active_term); + else if (blockequ(line, "quit", 5)) __pcrt_quit(); + else if (blockequ(line, "clear", 6)) { + term_clear(); + term_paint(); } - else if (blockequ(line, "clear", 6)) - clear_term(); - else if (blockequ(line, "help", 5)) + else if (blockequ(line, "help", 5)) { term_add_sz("Highway is a command shell for Portland OS. It includes variable support and a couple of pseudo-commands. Variables are addressed by surrounding with \"$\". The following list shows each of the pseudo-commands.\n\n" " source FILE\t" "run each command in FILE\n" " clear\t\t\t" "clear the screen\n" @@ -105,10 +109,13 @@ void run_line(const char *original_line) { " vars\t\t\t" "dump variables\n" " quit\t\t\t" "exit highway\n" " help\t\t\t" "show this\n"); + term_paint(); + } else if (!try_run_command_blocking(line, stdio_task)) { const struct no_null_sn *path = get_var((struct no_null_sn){.data = "_path", .length = 5}); if (!path->length) { term_add_sz("Could not run command.\n"); + term_paint(); return; } for (uint16_t to_i = LINE_SIZE - 1; to_i >= path->length; --to_i) @@ -116,14 +123,17 @@ void run_line(const char *original_line) { blockcpy(line, path->data, path->length); if (!try_run_command_blocking(line, stdio_task)) { term_add_sz("Could not run command.\n"); + term_paint(); return; } else { + _yield_task(); + term_add_char('\n'); ensure_color(); - if (active_term->cursor_x) - term_newline(); } } - else + else { + _yield_task(); ensure_color(); + } }
\ No newline at end of file diff --git a/src/user/highway/main.c b/src/user/highway/main.c index 60d5a69..c9ceee2 100644 --- a/src/user/highway/main.c +++ b/src/user/highway/main.c @@ -1,32 +1,21 @@ -#include <terminal/terminal.h> -#include <terminal/readline.h> +#include <libterm/terminal.h> +#include <libterm/readline.h> -#include <libfont/fonts.h> - -#include <knob/format.h> -#include <knob/heap.h> #include <knob/task.h> #include "cmds.h" #include "line.h" -#define FONT_NAME "berry" - void main(const char *arg) { - struct font_info *f = get_font(FONT_NAME); - - if (!f) - return; - - active_term = make_term(f, 50, 18); - if (!active_term) - return; +//syslogf(" this task: 0x%2h", this_task); +//syslogf(" stdio task: 0x%2h", stdio_task); +//syslogf("calling task: 0x%2h", calling_task); source(*arg ? arg : "user/default.rc"); ensure_color(); term_add_sz("Portland Highway\nType \"help\" for help.\n"); - paint_term(); + term_paint(); char cmd_buf[128]; while (1) { diff --git a/src/user/highway/vars.c b/src/user/highway/vars.c index 5f56621..82062c4 100644 --- a/src/user/highway/vars.c +++ b/src/user/highway/vars.c @@ -1,4 +1,4 @@ -#include <terminal/terminal.h> +#include <libterm/terminal.h> #include <knob/format.h> #include <knob/block.h> @@ -77,6 +77,7 @@ void del_var(struct no_null_sn name) { void dump_vars() { for (struct var_dict_node *node = var_dict_start; node; node = node->next) { - term_addf_no_ww("$%ns$ = %ns\n", node->name.length, node->name.data, node->value.length, node->value.data); + term_addf_no_ww("$%ns$\t= %ns\n", node->name.length, node->name.data, node->value.length, node->value.data); + term_paint(); } }
\ No newline at end of file diff --git a/src/user/include/knob/ipc.h b/src/user/include/knob/ipc.h index 3eab562..335959d 100644 --- a/src/user/include/knob/ipc.h +++ b/src/user/include/knob/ipc.h @@ -5,7 +5,7 @@ //blocking, returns early if other process is dead. //return value is number of bytes written. -uint32_t try_send_ipc(_task_handle_t to, void *buffer, uint32_t size); +uint32_t try_send_ipc(_task_handle_t to, const void *buffer, uint32_t size); //blocking, returns early if other process is dead. //return value is number of bytes read. diff --git a/src/user/include/libterm/command.h b/src/user/include/libterm/command.h new file mode 100644 index 0000000..7587306 --- /dev/null +++ b/src/user/include/libterm/command.h @@ -0,0 +1,68 @@ +#ifndef LIBTERM_COMMAND_H +#define LIBTERM_COMMAND_H + +#include <libterm/terminal.h> + +#include <knob/ipc.h> + +#include <keypack.h> + +#include <stdbool.h> +#include <stdint.h> + +//set to stdio task by default +extern _task_handle_t term_task; + +struct terminal_command { + enum { + SET_DIMENSIONS, + GET_DIMENSIONS, + PAINT, + CLEAR, + SET_COLOR, + SET_CURSOR, + CURSOR_LEFT, + CURSOR_RIGHT, + CURSOR_UP, + CURSOR_DOWN, + ADD_CHAR, + ADD_SN, + ADD_SN_NO_WORDWRAP, + GET_KEY + } kind; + + union { + struct { + uint32_t y; + uint32_t x; + } as_coords; + struct { + uint8_t fg; + uint8_t bg; + } as_color; + char as_char; + uint32_t as_uint; + }; +} __attribute__ ((__packed__)); + +union terminal_response { + struct { + uint32_t y; + uint32_t x; + } as_coords; + struct key_packet as_key; +} __attribute__ ((__packed__)); + +//returns false if terminal has died +static inline bool try_send_command(struct terminal_command *cmd) { + return try_send_ipc(term_task, cmd, sizeof(struct terminal_command)) + == sizeof(struct terminal_command); +} + +//returns false if terminal has died +static inline bool try_get_response(union terminal_response *rs) { + return try_read_ipc(term_task, rs, sizeof(union terminal_response)) + == sizeof(union terminal_response); +} + +#endif
\ No newline at end of file diff --git a/src/user/include/terminal/readline.h b/src/user/include/libterm/readline.h index 9046610..b1d5b0a 100644 --- a/src/user/include/terminal/readline.h +++ b/src/user/include/libterm/readline.h @@ -1,5 +1,5 @@ -#ifndef TERMINAL_READLINE_H -#define TERMINAL_READLINE_H +#ifndef LIBTERM_READLINE_H +#define LIBTERM_READLINE_H #include <stdint.h> diff --git a/src/user/include/libterm/terminal.h b/src/user/include/libterm/terminal.h new file mode 100644 index 0000000..19bd517 --- /dev/null +++ b/src/user/include/libterm/terminal.h @@ -0,0 +1,35 @@ +#ifndef LIBTERM_TERMINAL_H +#define LIBTERM_TERMINAL_H + +#include <pland/syscall.h> + +#include <stdarg.h> +#include <stdint.h> + +void term_set_dimensions(uint32_t width, uint32_t height); +void term_get_dimensions(uint32_t *width, uint32_t *height); + +void term_paint(); +void term_clear(); + +void term_set_color(uint8_t fg, uint8_t bg); +void term_set_cursor(uint32_t new_y, uint32_t new_x); + +void term_cursor_left(); +void term_cursor_right(); +void term_cursor_up(); +void term_cursor_down(); + +void term_add_char(char ch); +void term_add_sn_no_ww(const char *s, uint32_t n); +void term_add_sz_no_ww(const char *sz); +void term_add_sz(const char *sz); + +void term_addf_no_ww_v(const char *fmt, va_list args); +void term_addf_no_ww(const char *fmt, ...); +void term_addf_v(const char *fmt, va_list args); +void term_addf(const char *fmt, ...); + +struct key_packet term_get_key_blocking(); + +#endif
\ No newline at end of file diff --git a/src/user/include/pland/pcrt.h b/src/user/include/pland/pcrt.h index 42158cd..795738e 100644 --- a/src/user/include/pland/pcrt.h +++ b/src/user/include/pland/pcrt.h @@ -17,5 +17,6 @@ void __pcrt_quit() __attribute__ ((noreturn)); extern _task_handle_t calling_task; extern _task_handle_t stdio_task; +extern _task_handle_t this_task; #endif diff --git a/src/user/include/pland/syscall.h b/src/user/include/pland/syscall.h index 6d327d5..6870865 100644 --- a/src/user/include/pland/syscall.h +++ b/src/user/include/pland/syscall.h @@ -39,8 +39,8 @@ enum _scn { _SCN_PAINT_WINDOW, _SCN_GET_WIN_ACTION, _SCN_WAIT_FOR_ACTION, - _SCN_WAIT_IPC_SEND, - _SCN_WAIT_FOR_ANY_IPC, + _SCN_WAIT_IPC_SENT, + _SCN_WAIT_ANY_IPC_SENT, _SCN_FIND_UNREAD_IPC, _SCN_WAIT_IPC_READ, _SCN_IS_TASK_RUNNING @@ -137,7 +137,7 @@ static inline uint32_t _ipc_send(_task_handle_t handle, uint32_t count, const vo } static inline uint32_t _ipc_read(_task_handle_t handle, uint32_t count, void *buffer) { - return _sc3(_SCN_IPC_SEND, handle, count, (uint32_t)buffer); + return _sc3(_SCN_IPC_READ, handle, count, (uint32_t)buffer); } static inline void *_allocate_ram(uint32_t pages) { @@ -184,11 +184,11 @@ static inline void _delete_window(_window_handle_t window) { _sc1(_SCN_DELETE_WINDOW, (uint32_t)window); } -static inline void _resize_window(_window_handle_t window, uint16_t width, uint16_t height) { - _sc3(_SCN_RESIZE_WINDOW, (uint32_t)window, width, height); +static inline void _resize_window(_window_handle_t window, uint16_t width, uint16_t height, const void *pixel_buffer) { + _sc4(_SCN_RESIZE_WINDOW, (uint32_t)window, width, height, (uint32_t)pixel_buffer); } -static inline void _reassign_pixbuf(_window_handle_t window, void *pixel_buffer) { +static inline void _reassign_pixbuf(_window_handle_t window, const void *pixel_buffer) { _sc2(_SCN_REASSIGN_PIXBUF, (uint32_t)window, (uint32_t)pixel_buffer); } @@ -204,16 +204,16 @@ static inline void _wait_for_action() { _sc0(_SCN_WAIT_FOR_ACTION); } -static inline void _wait_ipc_send(_task_handle_t sending_task) { - _sc1(_SCN_WAIT_IPC_SEND, sending_task); +static inline void _wait_ipc_sent(_task_handle_t sending_task) { + _sc1(_SCN_WAIT_IPC_SENT, sending_task); } static inline void _system_log(const char *sz) { _sc1(_SCN_SYSTEM_LOG, (uint32_t)sz); } -static inline void _wait_for_any_ipc() { - _sc0(_SCN_WAIT_FOR_ANY_IPC); +static inline void _wait_for_any_ipc_sent() { + _sc0(_SCN_WAIT_ANY_IPC_SENT); } static inline _task_handle_t _find_unread_ipc() { diff --git a/src/user/include/popups/popup.h b/src/user/include/popups/popup.h index 1a3c531..9a39997 100644 --- a/src/user/include/popups/popup.h +++ b/src/user/include/popups/popup.h @@ -11,8 +11,7 @@ struct popup { struct key_packet quit_as; //terminated by one with .key_id == 0 - struct key_packet *quit_binds; - bool free_quit_binds; + const struct key_packet *quit_binds; }; void handle_actions(struct popup *p); diff --git a/src/user/include/terminal/terminal.h b/src/user/include/terminal/terminal.h deleted file mode 100644 index 1782173..0000000 --- a/src/user/include/terminal/terminal.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef TERMINAL_TERMINAL_H -#define TERMINAL_TERMINAL_H - -#include <libfont/fonts.h> - -#include <pland/syscall.h> - -#include <stdarg.h> -#include <stdint.h> - -struct terminal { - _window_handle_t window; - uint8_t *pixbuf; - uint32_t window_width; - uint32_t window_height; - - struct font_info *font; - - uint32_t cols; - uint32_t rows; - char *charbuf; - - uint32_t cursor_y; - uint32_t cursor_x; - - uint8_t fg; - uint8_t bg; -}; - -struct terminal *make_term(struct font_info *font, uint32_t cols, uint32_t rows); -void del_term(struct terminal *term); - -extern struct terminal *active_term; - -void paint_term(); - -void set_color(uint8_t fg, uint8_t bg); -void clear_term(); - -void move_cursor(uint32_t new_y, uint32_t new_x); - -void cursor_left(); -void cursor_right(); -void cursor_up(); -void cursor_down(); - -void term_newline(); -void term_add_char(char ch); -void term_add_sz_no_ww(const char *sz); -void term_add_sz(const char *sz); - -void term_addf_no_ww_v(const char *fmt, va_list args); -void term_addf_no_ww(const char *fmt, ...); -void term_addf_v(const char *fmt, va_list args); -void term_addf(const char *fmt, ...); - -#endif
\ No newline at end of file diff --git a/src/user/knob/format.c b/src/user/knob/format.c index 54d50ef..c5d2447 100644 --- a/src/user/knob/format.c +++ b/src/user/knob/format.c @@ -81,6 +81,8 @@ static const char *get_format(const char *from, struct format_spec *format_out) return from + 1; } +//char debug[] = "-- format_v: fmt = \" \"..."; + //allocates new memory char *format_v(const char *fmt, va_list args) { buf = get_block(FORMAT_BUF_INIT_SIZE); @@ -90,6 +92,11 @@ char *format_v(const char *fmt, va_list args) { buf_i = buf; while (*fmt) { + //debug[20] = *fmt; + //debug[21] = fmt[1]; + //debug[22] = fmt[2]; + //_system_log(debug); + if (*fmt != '%') { ensure(1); *(buf_i++) = *(fmt++); @@ -133,11 +140,15 @@ char *format_v(const char *fmt, va_list args) { case UNSIGNED_DECIMAL: k = va_arg(args, uint32_t); if (!form.len) { - uint32_t n = 10; - ++form.len; - while (k >= n) { + if (k >= 1000000000) + form.len = 10; + else { + uint32_t n = 10; ++form.len; - n *= 10; + while (k >= n) { + ++form.len; + n *= 10; + } } } ensure(form.len); diff --git a/src/user/knob/ipc.c b/src/user/knob/ipc.c index dbf1a22..5ca4bb4 100644 --- a/src/user/knob/ipc.c +++ b/src/user/knob/ipc.c @@ -2,10 +2,12 @@ //blocking, returns early if other process is dead. //return value is number of bytes written. -uint32_t try_send_ipc(_task_handle_t to, void *buffer, uint32_t size) { +uint32_t try_send_ipc(_task_handle_t to, const void *buffer, uint32_t size) { const uint32_t size_backup = size; while (size) { + //syslogf("_ipc_send(0x%2h, 0x%h, %u)", to, buffer, size); uint32_t res = _ipc_send(to, size, buffer); + //syslogf("=> %u", res); if (!res) { _wait_ipc_read(to); _yield_task(); @@ -17,16 +19,19 @@ uint32_t try_send_ipc(_task_handle_t to, void *buffer, uint32_t size) { buffer += res; } } + return size_backup; } //blocking, returns early if other process is dead. //return value is number of bytes read. -uint32_t read_ipc(_task_handle_t from, void *buffer, uint32_t size) { +uint32_t try_read_ipc(_task_handle_t from, void *buffer, uint32_t size) { const uint32_t size_backup = size; while (size) { + //syslogf("_ipc_read(0x%2h, 0x%h, %u)", from, buffer, size); uint32_t res = _ipc_read(from, size, buffer); + //syslogf("=> %u", res); if (!res) { - _wait_ipc_send(from); + _wait_ipc_sent(from); _yield_task(); } else if (res == -1) @@ -36,6 +41,7 @@ uint32_t read_ipc(_task_handle_t from, void *buffer, uint32_t size) { buffer += res; } } + return size_backup; } void flush_ipc(_task_handle_t from) { diff --git a/src/user/knob/task.c b/src/user/knob/task.c index 3bf3e85..9a49386 100644 --- a/src/user/knob/task.c +++ b/src/user/knob/task.c @@ -1,9 +1,12 @@ -#include <stdbool.h> #include <pland/syscall.h> #include <knob/file.h> #include <knob/heap.h> #include <knob/block.h> +#include <stdbool.h> + +#include <knob/format.h> + _task_handle_t run_command(const char *path, _task_handle_t stdio_task) { uint8_t dn; path = remove_prefix(path, &dn); @@ -14,9 +17,9 @@ _task_handle_t run_command(const char *path, _task_handle_t stdio_task) { blockcpy(new_path, path, ptr - path); new_path[ptr - path] = '\0'; - bool succeded = _start_task(dn, new_path, ptr + 1, stdio_task); + _task_handle_t handle = _start_task(dn, new_path, ptr + 1, stdio_task); free_block(new_path); - return succeded; + return handle; } return _start_task(dn, path, "", stdio_task); @@ -24,11 +27,12 @@ _task_handle_t run_command(const char *path, _task_handle_t stdio_task) { bool try_run_command_blocking(const char *path, _task_handle_t stdio_task) { _task_handle_t handle = run_command(path, stdio_task); - if (!handle) + if (!handle) { return false; - while (_is_task_running(handle)) { + } + do { _wait_for_task(handle); _yield_task(); - } + } while (_is_task_running(handle)); return true; }
\ No newline at end of file diff --git a/src/user/libterm/readline.c b/src/user/libterm/readline.c new file mode 100644 index 0000000..b37801d --- /dev/null +++ b/src/user/libterm/readline.c @@ -0,0 +1,106 @@ +#include <libterm/terminal.h> + +#include <knob/block.h> +#include <knob/key.h> + +#include <stdint.h> + +//returns length of string without null terminator +//max_length doesn't include null terminator +uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { + term_add_sz(prompt); + term_add_char(' '); + term_paint(); + + uint32_t l = 0; + uint32_t c = 0; + + while (1) { + struct key_packet kp = term_get_key_blocking(); + switch (kp.key_id) { + case KEY_LEFT_ARROW: + if (c) { + --c; + term_cursor_left(); + term_paint(); + } + continue; + case KEY_RIGHT_ARROW: + if (c != l) { + ++c; + term_cursor_right(); + term_paint(); + } + continue; + case KEY_HOME: + while (c) { + --c; + term_cursor_left(); + } + term_paint(); + continue; + case KEY_END: + while (c != l) { + ++c; + term_cursor_right(); + } + term_paint(); + continue; + case KEY_DELETE: + if (c != l) { + ++c; + term_cursor_right(); + } + case KEY_BSPACE: + if (!c) + continue; + --c; + --l; + for (uint32_t i = c; i < l; ++i) + sz[i] = sz[i + 1]; + term_cursor_left(); + term_add_sn_no_ww(sz + c, l - c); + term_add_char(' '); + for (uint32_t i = l + 1; i > c; --i) + term_cursor_left(); + term_paint(); + continue; + case KEY_ENTER: + while (c != l) { + ++c; + term_cursor_right(); + } + sz[l] = '\0'; + term_add_char('\n'); + term_paint(); + return l; + default: + if (l == max_length) + continue; + char ch = key_to_char(kp); + if (!ch) + continue; + if (c == l) { + ++l; + term_add_char(sz[c++] = ch); + term_paint(); + continue; + } + if (!(kp.modifiers & INSERT)) { + term_add_char(sz[c++] = ch); + term_paint(); + continue; + } + for (uint32_t i = l; i > c; --i) + sz[i] = sz[i - 1]; + sz[c] = ch; + ++l; + term_add_sn_no_ww(sz + c, l - c); + ++c; + for (uint32_t i = l; i > c; --i) + term_cursor_left(); + term_paint(); + continue; + } + } +}
\ No newline at end of file diff --git a/src/user/libterm/terminal.c b/src/user/libterm/terminal.c new file mode 100644 index 0000000..387d63f --- /dev/null +++ b/src/user/libterm/terminal.c @@ -0,0 +1,179 @@ +#include <libterm/command.h> + +#include <knob/format.h> +#include <knob/block.h> +#include <knob/ipc.h> + +_task_handle_t term_task; + +void term_set_dimensions(uint32_t width, uint32_t height) { + struct terminal_command cmd = { + .kind = SET_DIMENSIONS, + .as_coords = { + .x = width, + .y = height + } + }; + + try_send_command(&cmd); +} + +void term_get_dimensions(uint32_t *width, uint32_t *height) { + struct terminal_command cmd = { + .kind = GET_DIMENSIONS + }; + + if (try_send_command(&cmd)) { + union terminal_response rs; + if (try_get_response(&rs)) { + *width = rs.as_coords.x; + *height = rs.as_coords.y; + } + } +} + +void term_paint() { + struct terminal_command cmd = { + .kind = PAINT + }; + + try_send_command(&cmd); +} + +void term_clear() { + struct terminal_command cmd = { + .kind = CLEAR + }; + + try_send_command(&cmd); +} + +void term_set_color(uint8_t fg, uint8_t bg) { + struct terminal_command cmd = { + .kind = SET_COLOR, + .as_color = { + .fg = fg, + .bg = bg + } + }; + + try_send_command(&cmd); +} + +void term_set_cursor(uint32_t new_y, uint32_t new_x) { + struct terminal_command cmd = { + .kind = SET_CURSOR, + .as_coords = { + .y = new_y, + .x = new_x + } + }; + + try_send_command(&cmd); +} + +void term_cursor_left() { + struct terminal_command cmd = { + .kind = CURSOR_LEFT + }; + + try_send_command(&cmd); +} + +void term_cursor_right() { + struct terminal_command cmd = { + .kind = CURSOR_RIGHT + }; + + try_send_command(&cmd); +} + +void term_cursor_up() { + struct terminal_command cmd = { + .kind = CURSOR_UP + }; + + try_send_command(&cmd); +} + +void term_cursor_down() { + struct terminal_command cmd = { + .kind = CURSOR_DOWN + }; + + try_send_command(&cmd); +} + +void term_add_char(char ch) { + struct terminal_command cmd = { + .kind = ADD_CHAR, + .as_char = ch + }; + + try_send_command(&cmd); +} + +void term_add_sn_no_ww(const char *s, uint32_t n) { + struct terminal_command cmd = { + .kind = ADD_SN_NO_WORDWRAP, + .as_uint = n + }; + + if (try_send_command(&cmd)) + try_send_ipc(term_task, s, n); +} + +void term_add_sz_no_ww(const char *sz) { + term_add_sn_no_ww(sz, strlen(sz)); +} + +void term_add_sz(const char *sz) { + const uint32_t len = strlen(sz); + + struct terminal_command cmd = { + .kind = ADD_SN, + .as_uint = len + }; + + if (try_send_command(&cmd)) + try_send_ipc(term_task, sz, len); +} + +void term_addf_no_ww_v(const char *fmt, va_list args) { + char *const msg = format_v(fmt, args); + term_add_sz_no_ww(msg); + free_block(msg); +} + +void term_addf_no_ww(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + term_addf_no_ww_v(fmt, args); + va_end(args); +} + +void term_addf_v(const char *fmt, va_list args) { + char *const msg = format_v(fmt, args); + term_add_sz(msg); + free_block(msg); +} + +void term_addf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + term_addf_v(fmt, args); + va_end(args); +} + +struct key_packet term_get_key_blocking() { + struct terminal_command cmd = { + .kind = GET_KEY + }; + + try_send_command(&cmd); + + union terminal_response rs; + try_get_response(&rs); + + return rs.as_key; +} diff --git a/src/user/libterm/termtask.c b/src/user/libterm/termtask.c new file mode 100644 index 0000000..cc853a6 --- /dev/null +++ b/src/user/libterm/termtask.c @@ -0,0 +1,9 @@ +#include <libterm/command.h> + +#include <pland/pcrt.h> + +void set_term_task_to_stdio() { + term_task = stdio_task; +} + +BEFORE_MAIN(set_term_task_to_stdio);
\ No newline at end of file diff --git a/src/user/popups/info.c b/src/user/popups/info.c index 81a09e9..58903c8 100644 --- a/src/user/popups/info.c +++ b/src/user/popups/info.c @@ -43,7 +43,6 @@ void info_popup(struct popup *into, const char *msg, uint8_t fg, uint8_t bg) { into->has_quit = false; into->quit_binds = (struct key_packet *)info_quits; - into->free_quit_binds = false; const uint32_t pitch = info_font->space_width * w + 2 * PADDING; const uint32_t height = info_font->space_height * h + 2 * PADDING; diff --git a/src/user/popups/popup.c b/src/user/popups/popup.c index d214f81..9cdccb4 100644 --- a/src/user/popups/popup.c +++ b/src/user/popups/popup.c @@ -30,8 +30,6 @@ void handle_actions(struct popup *p) { void delete_popup(struct popup *p) { _delete_window(p->handle); free_block(p->pixbuf); - if (p->free_quit_binds) - free_block(p->quit_binds); } void make_modal(struct popup *p) { diff --git a/src/user/runtimes/c/pcrt.asm b/src/user/runtimes/c/pcrt.asm index 85c898c..0f06236 100644 --- a/src/user/runtimes/c/pcrt.asm +++ b/src/user/runtimes/c/pcrt.asm @@ -4,6 +4,7 @@ global __pcrt_entry global __pcrt_quit global calling_task global stdio_task +global this_task extern main extern __pcrt_before_main_start @@ -13,8 +14,9 @@ extern __pcrt_before_quit_end section .text __pcrt_entry: - mov dword [calling_task], esi - mov dword [stdio_task], edi + mov dword [calling_task], edi + mov dword [stdio_task], esi + mov dword [this_task], ecx push edx mov ebx, __pcrt_before_main_start @@ -42,4 +44,5 @@ __pcrt_quit: section .bss calling_task resd 1 -stdio_task resd 1
\ No newline at end of file +stdio_task resd 1 +this_task resd 1
\ No newline at end of file diff --git a/src/user/terminal/main.c b/src/user/terminal/main.c new file mode 100644 index 0000000..15ca6a1 --- /dev/null +++ b/src/user/terminal/main.c @@ -0,0 +1,373 @@ +#include <libterm/command.h> + +#include <libfont/fonts.h> + +#include <knob/format.h> +#include <knob/heap.h> +#include <knob/task.h> +#include <knob/ipc.h> + +#include <pland/syscall.h> +#include <pland/pcrt.h> + +#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 <knob/format.h> + +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; + } + } +}
\ No newline at end of file diff --git a/src/user/terminal/readline.c b/src/user/terminal/readline.c deleted file mode 100644 index 37ef54b..0000000 --- a/src/user/terminal/readline.c +++ /dev/null @@ -1,104 +0,0 @@ -#include <terminal/terminal.h> - -#include <knob/format.h> -#include <knob/heap.h> -#include <knob/key.h> - -#include <pland/syscall.h> - -#include <stdint.h> - -//returns length of string without null terminator -//max_length doesn't include null terminator -uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { - uint32_t i = 0; - uint32_t l = 0; - - term_add_sz_no_ww(prompt); - paint_term(); - - const _window_handle_t handle = active_term->window; - struct window_action action; - while (1) { - _get_win_action(handle, &action); - switch (action.action_type) { - case NOT_READY: - _wait_for_action(); - _yield_task(); - continue; - case KEY_DOWN: - //;char *const debug_msg = format("got key 0x%2x, 0x%3x", action.as_key.key_id, action.as_key.modifiers); - // _system_log(debug_msg); - // free_block(debug_msg); - switch (action.as_key.key_id) { - case KEY_DELETE: - if (i != l) { - cursor_right(); - ++i; - } - case KEY_BSPACE: - if (!i) - continue; - --l; - --i; - for (uint8_t j = i; j < l; ++j) - sz[j] = sz[j + 1]; - sz[l] = '\0'; - cursor_left(); - uint32_t cursor_backup_x = active_term->cursor_x; - uint32_t cursor_backup_y = active_term->cursor_y; - term_add_sz_no_ww(sz + i); - term_add_char(' '); - move_cursor(cursor_backup_y, cursor_backup_x); - paint_term(); - continue; - case KEY_ENTER: - term_newline(); - paint_term(); - sz[l] = '\0'; - return l; - case KEY_HOME: - case KEY_UP_ARROW: - for (; i; --i) - cursor_left(); - paint_term(); - continue; - case KEY_END: - case KEY_DOWN_ARROW: - for (; i != l; ++i) - cursor_right(); - paint_term(); - continue; - case KEY_LEFT_ARROW: - if (i) { - cursor_left(); - paint_term(); - --i; - } - continue; - case KEY_RIGHT_ARROW: - if (i != l) { - cursor_right(); - paint_term(); - ++i; - } - continue; - default: - if (i == max_length) - continue; - char ch = key_to_char(action.as_key); - if (ch) { - term_add_char(ch); - paint_term(); - sz[i] = ch; - if (i == l) - ++l; - ++i; - } - continue; - } - default: - continue; - } - } -}
\ No newline at end of file diff --git a/src/user/terminal/terminal.c b/src/user/terminal/terminal.c deleted file mode 100644 index 7961fd5..0000000 --- a/src/user/terminal/terminal.c +++ /dev/null @@ -1,284 +0,0 @@ -#include <terminal/terminal.h> - -#include <libfont/fonts.h> - -#include <knob/format.h> -#include <knob/heap.h> - -struct term_list_entry { - struct terminal term; - struct term_list_entry *next; - struct term_list_entry *prev; -}; - -struct term_list_entry *last_term = 0; -struct terminal *active_term = 0; - -struct terminal *make_term(struct font_info *font, uint32_t cols, uint32_t rows) { - if (!font) - return 0; - - struct term_list_entry *next_entry = get_block(sizeof(struct term_list_entry)); - if (!next_entry) - return 0; - - char *const cb = get_block(cols * rows); - - if (!cb) { - free_block(next_entry); - return 0; - } - - const uint32_t w = cols * font->space_width; - const uint32_t h = rows * font->space_height; - - uint8_t *const pb = get_block(w * h); - if (!pb) { - free_block(next_entry); - free_block(cb); - return 0; - } - - _window_handle_t win = _new_window(w, h, pb); - if (!win) { - free_block(next_entry); - free_block(cb); - free_block(pb); - return 0; - } - - for (char *i = cb; i < cb + cols * rows; ++i) - *i = ' '; - for (uint8_t *i = pb; i < pb + w * h; ++i) - *i = 0x10; - - next_entry->term.window = win; - next_entry->term.pixbuf = pb; - next_entry->term.window_width = w; - next_entry->term.window_height = h; - - next_entry->term.font = font; - - next_entry->term.cols = cols; - next_entry->term.rows = rows; - next_entry->term.charbuf = cb; - - next_entry->term.cursor_y = 0; - next_entry->term.cursor_x = 0; - - next_entry->term.fg = 0x0f; - next_entry->term.bg = 0x10; - - next_entry->prev = last_term; - next_entry->next = 0; - - if (last_term) - last_term->next = next_entry; - last_term = next_entry; - - return (struct terminal *)next_entry; -} - -void del_term(struct terminal *term) { - _delete_window(term->window); - free_block(term->pixbuf); - free_block(term->charbuf); - - free_block(term);//coincides with the term_list_entry - if (active_term == term) - active_term = 0; - - for (struct term_list_entry *i = last_term; i; i = i->prev) - if (i == (struct term_list_entry *)term) { - if (i->prev) - i->prev->next = i->next; - if (i->next) - i->next->prev = i->prev; - if (i == last_term) - last_term = i->prev; - return; - } -} - -static void draw_char(uint32_t y, uint32_t x, bool inverted) { - put_char(active_term->font, active_term->charbuf[y * active_term->cols + x], active_term->pixbuf + (y * active_term->cols * active_term->font->space_height + x) * active_term->font->space_width, active_term->window_width, inverted ? active_term->fg : active_term->bg, inverted ? active_term->bg : active_term->fg); -} - -static void draw_cursor() { - draw_char(active_term->cursor_y, active_term->cursor_x, true); -} - -void paint_term() { - _paint_window(active_term->window); -} - -void move_cursor(uint32_t new_y, uint32_t new_x) { - draw_char(active_term->cursor_y, active_term->cursor_x, false); - active_term->cursor_y = new_y; - active_term->cursor_x = new_x; - draw_cursor(); -} - -static void redraw_term() { - for (uint32_t y = 0; y < active_term->rows; ++y) - for (uint32_t x = 0; x < active_term->cols; ++x) - draw_char(y, x, false); - draw_cursor(); -} - -void set_color(uint8_t fg, uint8_t bg) { - active_term->fg = fg; - active_term->bg = bg; - redraw_term(); -} - -void clear_term() { - for (char *i = active_term->charbuf, *const e = i + active_term->cols * active_term->rows; i != e; ++i) - *i = ' '; - for (uint8_t *i = active_term->pixbuf, *const e = i + active_term->window_width * active_term->window_height; i != e; ++i) - *i = active_term->bg; - move_cursor(0, 0); -} - -void cursor_up() { - draw_char(active_term->cursor_y, active_term->cursor_x, false); - if (active_term->cursor_y) - --active_term->cursor_y; - else - //eventually, maybe scroll back through a longer terminal buffer - active_term->cursor_x = 0; - draw_cursor(); -} - -void cursor_left() { - draw_char(active_term->cursor_y, active_term->cursor_x, false); - if (active_term->cursor_x) { - --active_term->cursor_x; - draw_cursor(); - } - else { - active_term->cursor_x = active_term->cols - 1; - cursor_up(); - } -} - -void cursor_down() { - draw_char(active_term->cursor_y, active_term->cursor_x, false); - - const uint32_t rows = active_term->rows; - - if (++active_term->cursor_y == rows) { - --active_term->cursor_y; - - char *const cb = active_term->charbuf; - uint8_t *const pb = active_term->pixbuf; - const uint32_t cols = active_term->cols; - const uint32_t fw = active_term->font->space_width; - const uint32_t fh = active_term->font->space_height; - - char *to; - for (to = cb; to < cb + (rows - 1) * cols; ++to) - *to = *(to + cols); - for (; to < cb + rows * cols; ++to) - *to = ' '; - uint8_t *pto; - for (pto = pb; pto < pb + cols * fw * (rows - 1) * fh; ++pto) - *pto = *(pto + cols * fw * fh); - for (; pto < pb + cols * fw * rows * fh; ++pto) - *pto = active_term->bg; - } - - draw_cursor(); -} - -void cursor_right() { - draw_char(active_term->cursor_y, active_term->cursor_x, false); - - if (++active_term->cursor_x == active_term->cols) - term_newline(); - else - draw_cursor(); -} - -void term_newline() { - draw_char(active_term->cursor_y, active_term->cursor_x, false); - active_term->cursor_x = 0; - cursor_down(); -} - -void term_add_char(char ch) { - if (ch == '\n') { - term_newline(); - return; - } -//char *const debug_msg = format("charbuf[%u] = '%c'", active_term->cursor_y * active_term->cols + active_term->cursor_x, ch); -//_system_log(debug_msg); -//free_block(debug_msg); - active_term->charbuf[active_term->cursor_y * active_term->cols + active_term->cursor_x] = ch; - cursor_right(); -} - -void term_add_sz_no_ww(const char *sz) { - while (*sz) - term_add_char(*(sz++)); -} - -#define MIN_TAB 3 -#define TAB_STEP 4 - -void term_add_sz(const char *sz) { - //TODO: special hyphen handling - const char *word = sz; - while (1) - if (!*sz || (*sz == ' ') || (*sz == '\n') || (*sz == '\t')) { - const uint32_t len = sz - word; - if ((active_term->cursor_x + len > active_term->cols) && (len <= active_term->cols) && active_term->cols) - term_newline(); - for (const char *i = word; i < sz; ++i) - term_add_char(*i); - while ((*sz == ' ') || (*sz == '\n') || (*sz == '\t')) { - if (*sz == '\n') - term_newline(); - else if (*sz == '\t') { - for (uint8_t i = 0; i < MIN_TAB; ++i) - cursor_right(); - while (active_term->cursor_x % TAB_STEP) - cursor_right(); - } - ++sz; - } - if (!*sz) - return; - if (active_term->cursor_x) - term_add_char(' '); - word = sz; - } - else - ++sz; -} - -void term_addf_no_ww_v(const char *fmt, va_list args) { - char *const msg = format_v(fmt, args); - term_add_sz_no_ww(msg); - free_block(msg); -} - -void term_addf_no_ww(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - term_addf_no_ww_v(fmt, args); - va_end(args); -} - -void term_addf_v(const char *fmt, va_list args) { - char *const msg = format_v(fmt, args); - term_add_sz(msg); - free_block(msg); -} - -void term_addf(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - term_addf_v(fmt, args); - va_end(args); -}
\ No newline at end of file |