#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
#include "macro.h"
#include "term-internal.h"
#include "util.h"
+#include "utf8.h"
int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data) {
_cleanup_(term_screen_unrefp) term_screen *screen = NULL;
screen->cmd_fn_data = cmd_fn_data;
screen->flags = TERM_FLAG_7BIT_MODE;
screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
- screen->gl = &screen->g0;
- screen->gr = &screen->g1;
screen->g0 = &term_unicode_lower;
screen->g1 = &term_unicode_upper;
screen->g2 = &term_unicode_lower;
screen->g3 = &term_unicode_upper;
-
- screen->saved.cursor_x = 0;
- screen->saved.cursor_y = 0;
- screen->saved.attr = screen->attr;
- screen->saved.gl = screen->gl;
- screen->saved.gr = screen->gr;
+ screen->state.gl = &screen->g0;
+ screen->state.gr = &screen->g1;
+ screen->saved = screen->state;
+ screen->saved_alt = screen->saved;
r = term_page_new(&screen->page_main);
if (r < 0)
static inline void screen_age_cursor(term_screen *screen) {
term_cell *cell;
- cell = term_page_get_cell(screen->page, screen->cursor_x, screen->cursor_y);
+ cell = term_page_get_cell(screen->page, screen->state.cursor_x, screen->state.cursor_y);
if (cell)
cell->age = screen->age;
}
x = screen_clamp_x(screen, x);
y = screen_clamp_y(screen, y);
- if (x == screen->cursor_x && y == screen->cursor_y)
+ if (x == screen->state.cursor_x && y == screen->state.cursor_y)
return;
if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
screen_age_cursor(screen);
- screen->cursor_x = x;
- screen->cursor_y = y;
+ screen->state.cursor_x = x;
+ screen->state.cursor_y = y;
if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
screen_age_cursor(screen);
}
static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned int y) {
- if (screen->flags & TERM_FLAG_ORIGIN_MODE) {
+ if (screen->state.origin_mode) {
x = screen_clamp_x(screen, x);
y = screen_clamp_x(screen, y) + screen->page->scroll_idx;
}
static void screen_cursor_left(term_screen *screen, unsigned int num) {
- if (num > screen->cursor_x)
- num = screen->cursor_x;
+ if (num > screen->state.cursor_x)
+ num = screen->state.cursor_x;
- screen_cursor_set(screen, screen->cursor_x - num, screen->cursor_y);
+ screen_cursor_set(screen, screen->state.cursor_x - num, screen->state.cursor_y);
}
static void screen_cursor_left_tab(term_screen *screen, unsigned int num) {
unsigned int i;
- i = screen->cursor_x;
+ i = screen->state.cursor_x;
while (i > 0 && num > 0) {
if (screen_tab_is_set(screen, --i))
--num;
}
- screen_cursor_set(screen, i, screen->cursor_y);
+ screen_cursor_set(screen, i, screen->state.cursor_y);
}
static void screen_cursor_right(term_screen *screen, unsigned int num) {
if (num > screen->page->width)
num = screen->page->width;
- screen_cursor_set(screen, screen->cursor_x + num, screen->cursor_y);
+ screen_cursor_set(screen, screen->state.cursor_x + num, screen->state.cursor_y);
}
static void screen_cursor_right_tab(term_screen *screen, unsigned int num) {
unsigned int i;
- i = screen->cursor_x;
+ i = screen->state.cursor_x;
while (i + 1 < screen->page->width && num > 0) {
if (screen_tab_is_set(screen, ++i))
--num;
}
- screen_cursor_set(screen, i, screen->cursor_y);
+ screen_cursor_set(screen, i, screen->state.cursor_y);
}
static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) {
unsigned int max;
- if (screen->cursor_y < screen->page->scroll_idx) {
- if (num > screen->cursor_y)
- num = screen->cursor_y;
+ if (screen->state.cursor_y < screen->page->scroll_idx) {
+ if (num > screen->state.cursor_y)
+ num = screen->state.cursor_y;
- screen_cursor_set(screen, screen->cursor_x, screen->cursor_y - num);
+ screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
} else {
- max = screen->cursor_y - screen->page->scroll_idx;
+ max = screen->state.cursor_y - screen->page->scroll_idx;
if (num > max) {
if (num < 1)
return;
screen_age_cursor(screen);
if (scroll)
- term_page_scroll_down(screen->page, num - max, &screen->attr, screen->age, NULL);
+ term_page_scroll_down(screen->page, num - max, &screen->state.attr, screen->age, NULL);
- screen->cursor_y = screen->page->scroll_idx;
+ screen->state.cursor_y = screen->page->scroll_idx;
if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
screen_age_cursor(screen);
} else {
- screen_cursor_set(screen, screen->cursor_x, screen->cursor_y - num);
+ screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
}
}
}
static void screen_cursor_down(term_screen *screen, unsigned int num, bool scroll) {
unsigned int max;
- if (screen->cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) {
+ if (screen->state.cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) {
if (num > screen->page->height)
num = screen->page->height;
- screen_cursor_set(screen, screen->cursor_x, screen->cursor_y - num);
+ screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
} else {
- max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->cursor_y;
+ max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->state.cursor_y;
if (num > max) {
if (num < 1)
return;
screen_age_cursor(screen);
if (scroll)
- term_page_scroll_up(screen->page, num - max, &screen->attr, screen->age, screen->history);
+ term_page_scroll_up(screen->page, num - max, &screen->state.attr, screen->age, screen->history);
- screen->cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1;
+ screen->state.cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1;
if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
screen_age_cursor(screen);
} else {
- screen_cursor_set(screen, screen->cursor_x, screen->cursor_y + num);
+ screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y + num);
}
}
}
+static void screen_save_state(term_screen *screen, term_state *where) {
+ *where = screen->state;
+}
+
+static void screen_restore_state(term_screen *screen, term_state *from) {
+ screen_cursor_set(screen, from->cursor_x, from->cursor_y);
+ screen->state = *from;
+}
+
+static void screen_reset_page(term_screen *screen, term_page *page) {
+ term_page_set_scroll_region(page, 0, page->height);
+ term_page_erase(page, 0, 0, page->width, page->height, &screen->state.attr, screen->age, false);
+}
+
+static void screen_change_alt(term_screen *screen, bool set) {
+ if (set) {
+ screen->page = screen->page_alt;
+ screen->history = NULL;
+ } else {
+ screen->page = screen->page_main;
+ screen->history = screen->history_main;
+ }
+
+ screen->page->age = screen->age;
+}
+
static inline void set_reset(term_screen *screen, unsigned int flag, bool set) {
if (set)
screen->flags |= flag;
screen->flags &= ~flag;
}
-static void screen_mode_change(term_screen *screen, unsigned int mode, bool dec, bool set) {
+static void screen_mode_change_ansi(term_screen *screen, unsigned mode, bool set) {
+ switch (mode) {
+ case 20:
+ /*
+ * LNM: line-feed/new-line mode
+ * TODO
+ */
+ set_reset(screen, TERM_FLAG_NEWLINE_MODE, set);
+
+ break;
+ default:
+ log_debug("terminal: failed to %s unknown ANSI mode %u", set ? "set" : "unset", mode);
+ }
+}
+
+static void screen_mode_change_dec(term_screen *screen, unsigned int mode, bool set) {
switch (mode) {
case 1:
- if (dec) {
- /*
- * DECCKM: cursor-keys
- * TODO
- */
- set_reset(screen, TERM_FLAG_CURSOR_KEYS, set);
- }
+ /*
+ * DECCKM: cursor-keys
+ * TODO
+ */
+ set_reset(screen, TERM_FLAG_CURSOR_KEYS, set);
break;
case 6:
- if (dec) {
- /*
- * DECOM: origin-mode
- * TODO
- */
- set_reset(screen, TERM_FLAG_ORIGIN_MODE, set);
- }
+ /*
+ * DECOM: origin-mode
+ * TODO
+ */
+ screen->state.origin_mode = set;
break;
case 7:
- if (dec) {
- /*
- * DECAWN: auto-wrap mode
- * TODO
- */
- set_reset(screen, TERM_FLAG_AUTO_WRAP, set);
- }
+ /*
+ * DECAWN: auto-wrap mode
+ * TODO
+ */
+ screen->state.auto_wrap = set;
break;
- case 20:
- if (!dec) {
- /*
- * LNM: line-feed/new-line mode
- * TODO
- */
- set_reset(screen, TERM_FLAG_NEWLINE_MODE, set);
- }
+ case 25:
+ /*
+ * DECTCEM: text-cursor-enable
+ * TODO
+ */
+ set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set);
+ screen_age_cursor(screen);
break;
- case 25:
- if (dec) {
- /*
- * DECTCEM: text-cursor-enable
- * TODO
- */
- set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set);
- }
+ case 47:
+ /*
+ * XTERM-ASB: alternate-screen-buffer
+ * This enables/disables the alternate screen-buffer.
+ * It effectively saves the current page content and
+ * allows you to restore it when changing to the
+ * original screen-buffer again.
+ */
+ screen_change_alt(screen, set);
break;
+ case 1047:
+ /*
+ * XTERM-ASBPE: alternate-screen-buffer-post-erase
+ * This is the same as XTERM-ASB but erases the
+ * alternate screen-buffer before switching back to the
+ * original buffer. Use it to discard any data on the
+ * alternate screen buffer when done.
+ */
+ if (!set)
+ screen_reset_page(screen, screen->page_alt);
+
+ screen_change_alt(screen, set);
+
+ break;
+ case 1048:
+ /*
+ * XTERM-ASBCS: alternate-screen-buffer-cursor-state
+ * This has the same effect as DECSC/DECRC, but uses a
+ * separate state buffer. It is usually used in
+ * combination with alternate screen buffers to provide
+ * stacked state storage.
+ */
+ if (set)
+ screen_save_state(screen, &screen->saved_alt);
+ else
+ screen_restore_state(screen, &screen->saved_alt);
+
+ break;
+ case 1049:
+ /*
+ * XTERM-ASBX: alternate-screen-buffer-extended
+ * This combines XTERM-ASBPE and XTERM-ASBCS somewhat.
+ * When enabling, state is saved, alternate screen
+ * buffer is activated and cleared.
+ * When disabled, alternate screen buffer is cleared,
+ * then normal screen buffer is enabled and state is
+ * restored.
+ */
+ if (set)
+ screen_save_state(screen, &screen->saved_alt);
+
+ screen_reset_page(screen, screen->page_alt);
+ screen_change_alt(screen, set);
+
+ if (!set)
+ screen_restore_state(screen, &screen->saved_alt);
+
+ break;
+ default:
+ log_debug("terminal: failed to %s unknown DEC mode %u", set ? "set" : "unset", mode);
}
}
* identity. */
switch (val) {
case 33 ... 126:
- if (screen->glt) {
- nval = (**screen->glt)[val - 32];
- screen->glt = NULL;
+ if (screen->state.glt) {
+ nval = (**screen->state.glt)[val - 32];
+ screen->state.glt = NULL;
} else {
- nval = (**screen->gl)[val - 32];
+ nval = (**screen->state.gl)[val - 32];
}
break;
case 160 ... 255:
- if (screen->grt) {
- nval = (**screen->grt)[val - 160];
- screen->grt = NULL;
+ if (screen->state.grt) {
+ nval = (**screen->state.grt)[val - 160];
+ screen->state.grt = NULL;
} else {
- nval = (**screen->gr)[val - 160];
+ nval = (**screen->state.gr)[val - 160];
}
break;
}
static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) {
term_char_t ch = TERM_CHAR_NULL;
- uint32_t c;
- if (screen->cursor_x + 1 == screen->page->width
+ if (screen->state.cursor_x + 1 == screen->page->width
&& screen->flags & TERM_FLAG_PENDING_WRAP
- && screen->flags & TERM_FLAG_AUTO_WRAP) {
+ && screen->state.auto_wrap) {
screen_cursor_down(screen, 1, true);
- screen_cursor_set(screen, 0, screen->cursor_y);
+ screen_cursor_set(screen, 0, screen->state.cursor_y);
}
screen_cursor_clear_wrap(screen);
- c = screen_map(screen, seq->terminator);
- ch = term_char_merge(ch, screen_map(screen, c));
- term_page_write(screen->page, screen->cursor_x, screen->cursor_y, ch, 1, &screen->attr, screen->age, false);
+ ch = term_char_merge(ch, screen_map(screen, seq->terminator));
+ term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false);
- if (screen->cursor_x + 1 == screen->page->width)
+ if (screen->state.cursor_x + 1 == screen->page->width)
screen->flags |= TERM_FLAG_PENDING_WRAP;
else
screen_cursor_right(screen, 1);
pos = seq->args[0];
screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, pos - 1, screen->cursor_y);
+ screen_cursor_set(screen, pos - 1, screen->state.cursor_y);
return 0;
}
*/
screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, 0, screen->cursor_y);
+ screen_cursor_set(screen, 0, screen->state.cursor_y);
return 0;
}
num = seq->args[0];
screen_cursor_clear_wrap(screen);
- term_page_delete_cells(screen->page, screen->cursor_x, screen->cursor_y, num, &screen->attr, screen->age);
+ term_page_delete_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
return 0;
}
* state for the main display and the status line.
*/
- screen->attr = screen->saved.attr;
- screen->gl = screen->saved.gl;
- screen->gr = screen->saved.gr;
- screen->glt = screen->saved.glt;
- screen->grt = screen->saved.grt;
- set_reset(screen, TERM_FLAG_AUTO_WRAP, screen->flags & TERM_FLAG_AUTO_WRAP);
- set_reset(screen, TERM_FLAG_ORIGIN_MODE, screen->flags & TERM_FLAG_ORIGIN_MODE);
- screen_cursor_set(screen, screen->saved.cursor_x, screen->saved.cursor_y);
+ screen_restore_state(screen, &screen->saved);
return 0;
}
* * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent
*/
- screen->saved.cursor_x = screen->cursor_x;
- screen->saved.cursor_y = screen->cursor_y;
- screen->saved.attr = screen->attr;
- screen->saved.gl = screen->gl;
- screen->saved.gr = screen->gr;
- screen->saved.glt = screen->glt;
- screen->saved.grt = screen->grt;
- screen->saved.flags = screen->flags & (TERM_FLAG_AUTO_WRAP
- | TERM_FLAG_ORIGIN_MODE);
+ screen_save_state(screen, &screen->saved);
return 0;
}
switch (mode) {
case 0:
case 2:
- screen->attr.protect = 0;
+ screen->state.attr.protect = 0;
break;
case 1:
- screen->attr.protect = 1;
+ screen->state.attr.protect = 1;
break;
}
switch (mode) {
case 0:
term_page_erase(screen->page,
- screen->cursor_x, screen->cursor_y,
+ screen->state.cursor_x, screen->state.cursor_y,
screen->page->width, screen->page->height,
- &screen->attr, screen->age, true);
+ &screen->state.attr, screen->age, true);
break;
case 1:
term_page_erase(screen->page,
0, 0,
- screen->cursor_x, screen->cursor_y,
- &screen->attr, screen->age, true);
+ screen->state.cursor_x, screen->state.cursor_y,
+ &screen->state.attr, screen->age, true);
break;
case 2:
term_page_erase(screen->page,
0, 0,
screen->page->width, screen->page->height,
- &screen->attr, screen->age, true);
+ &screen->state.attr, screen->age, true);
break;
}
switch (mode) {
case 0:
term_page_erase(screen->page,
- screen->cursor_x, screen->cursor_y,
- screen->page->width, screen->cursor_y,
- &screen->attr, screen->age, true);
+ screen->state.cursor_x, screen->state.cursor_y,
+ screen->page->width, screen->state.cursor_y,
+ &screen->state.attr, screen->age, true);
break;
case 1:
term_page_erase(screen->page,
- 0, screen->cursor_y,
- screen->cursor_x, screen->cursor_y,
- &screen->attr, screen->age, true);
+ 0, screen->state.cursor_y,
+ screen->state.cursor_x, screen->state.cursor_y,
+ &screen->state.attr, screen->age, true);
break;
case 2:
term_page_erase(screen->page,
- 0, screen->cursor_y,
- screen->page->width, screen->cursor_y,
- &screen->attr, screen->age, true);
+ 0, screen->state.cursor_y,
+ screen->page->width, screen->state.cursor_y,
+ &screen->state.attr, screen->age, true);
break;
}
bottom = screen->page->height;
}
- term_page_set_scroll_region(screen->page_main, top - 1, bottom - top + 1);
- term_page_set_scroll_region(screen->page_alt, top - 1, bottom - top + 1);
+ term_page_set_scroll_region(screen->page, top - 1, bottom - top + 1);
screen_cursor_clear_wrap(screen);
screen_cursor_set(screen, 0, 0);
if (seq->args[0] > 0)
num = seq->args[0];
- term_page_delete_lines(screen->page, screen->cursor_y, num, &screen->attr, screen->age);
+ term_page_delete_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
return 0;
}
num = seq->args[0];
term_page_erase(screen->page,
- screen->cursor_x, screen->cursor_y,
- screen->cursor_x + num, screen->cursor_y,
- &screen->attr, screen->age, false);
+ screen->state.cursor_x, screen->state.cursor_y,
+ screen->state.cursor_x + num, screen->state.cursor_y,
+ &screen->state.attr, screen->age, false);
return 0;
}
switch (mode) {
case 0:
term_page_erase(screen->page,
- screen->cursor_x, screen->cursor_y,
+ screen->state.cursor_x, screen->state.cursor_y,
screen->page->width, screen->page->height,
- &screen->attr, screen->age, false);
+ &screen->state.attr, screen->age, false);
break;
case 1:
term_page_erase(screen->page,
0, 0,
- screen->cursor_x, screen->cursor_y,
- &screen->attr, screen->age, false);
+ screen->state.cursor_x, screen->state.cursor_y,
+ &screen->state.attr, screen->age, false);
break;
case 2:
term_page_erase(screen->page,
0, 0,
screen->page->width, screen->page->height,
- &screen->attr, screen->age, false);
+ &screen->state.attr, screen->age, false);
break;
}
switch (mode) {
case 0:
term_page_erase(screen->page,
- screen->cursor_x, screen->cursor_y,
- screen->page->width, screen->cursor_y,
- &screen->attr, screen->age, false);
+ screen->state.cursor_x, screen->state.cursor_y,
+ screen->page->width, screen->state.cursor_y,
+ &screen->state.attr, screen->age, false);
break;
case 1:
term_page_erase(screen->page,
- 0, screen->cursor_y,
- screen->cursor_x, screen->cursor_y,
- &screen->attr, screen->age, false);
+ 0, screen->state.cursor_y,
+ screen->state.cursor_x, screen->state.cursor_y,
+ &screen->state.attr, screen->age, false);
break;
case 2:
term_page_erase(screen->page,
- 0, screen->cursor_y,
- screen->page->width, screen->cursor_y,
- &screen->attr, screen->age, false);
+ 0, screen->state.cursor_y,
+ screen->page->width, screen->state.cursor_y,
+ &screen->state.attr, screen->age, false);
break;
}
num = seq->args[0];
screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, num - 1, screen->cursor_y);
+ screen_cursor_set(screen, num - 1, screen->state.cursor_y);
return 0;
}
unsigned int pos;
- pos = screen->cursor_x;
+ pos = screen->state.cursor_x;
if (screen->page->width > 0)
screen->tabs[pos / 8] |= 1U << (pos % 8);
num = seq->args[0];
screen_cursor_clear_wrap(screen);
- term_page_insert_cells(screen->page, screen->cursor_x, screen->cursor_y, num, &screen->attr, screen->age);
+ term_page_insert_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
return 0;
}
num = seq->args[0];
screen_cursor_clear_wrap(screen);
- term_page_insert_lines(screen->page, screen->cursor_y, num, &screen->attr, screen->age);
+ term_page_insert_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
return 0;
}
screen_cursor_down(screen, 1, true);
if (screen->flags & TERM_FLAG_NEWLINE_MODE)
- screen_cursor_left(screen, screen->cursor_x);
+ screen_cursor_left(screen, screen->state.cursor_x);
return 0;
}
* Map G1 into GR.
*/
- screen->gr = &screen->g1;
+ screen->state.gr = &screen->g1;
return 0;
}
* Map G2 into GL.
*/
- screen->gl = &screen->g2;
+ screen->state.gl = &screen->g2;
return 0;
}
* Map G2 into GR.
*/
- screen->gr = &screen->g2;
+ screen->state.gr = &screen->g2;
return 0;
}
* Map G3 into GL.
*/
- screen->gl = &screen->g3;
+ screen->state.gl = &screen->g3;
return 0;
}
* Map G3 into GR.
*/
- screen->gr = &screen->g3;
+ screen->state.gr = &screen->g3;
return 0;
}
screen_cursor_clear_wrap(screen);
screen_cursor_down(screen, 1, true);
- screen_cursor_set(screen, 0, screen->cursor_y);
+ screen_cursor_set(screen, 0, screen->state.cursor_y);
return 0;
}
unsigned int i;
for (i = 0; i < seq->n_args; ++i)
- screen_mode_change(screen, seq->args[i], false, false);
+ screen_mode_change_ansi(screen, seq->args[i], false);
return 0;
}
unsigned int i;
for (i = 0; i < seq->n_args; ++i)
- screen_mode_change(screen, seq->args[i], true, false);
+ screen_mode_change_dec(screen, seq->args[i], false);
return 0;
}
if (seq->args[0] > 0)
num = seq->args[0];
- term_page_scroll_down(screen->page, num, &screen->attr, screen->age, NULL);
+ term_page_scroll_down(screen->page, num, &screen->state.attr, screen->age, NULL);
return 0;
}
int v;
if (seq->n_args < 1) {
- zero(screen->attr);
+ zero(screen->state.attr);
return 0;
}
v = seq->args[i];
switch (v) {
case 1:
- screen->attr.bold = 1;
+ screen->state.attr.bold = 1;
break;
case 3:
- screen->attr.italic = 1;
+ screen->state.attr.italic = 1;
break;
case 4:
- screen->attr.underline = 1;
+ screen->state.attr.underline = 1;
break;
case 5:
- screen->attr.blink = 1;
+ screen->state.attr.blink = 1;
break;
case 7:
- screen->attr.inverse = 1;
+ screen->state.attr.inverse = 1;
break;
case 8:
- screen->attr.hidden = 1;
+ screen->state.attr.hidden = 1;
break;
case 22:
- screen->attr.bold = 0;
+ screen->state.attr.bold = 0;
break;
case 23:
- screen->attr.italic = 0;
+ screen->state.attr.italic = 0;
break;
case 24:
- screen->attr.underline = 0;
+ screen->state.attr.underline = 0;
break;
case 25:
- screen->attr.blink = 0;
+ screen->state.attr.blink = 0;
break;
case 27:
- screen->attr.inverse = 0;
+ screen->state.attr.inverse = 0;
break;
case 28:
- screen->attr.hidden = 0;
+ screen->state.attr.hidden = 0;
break;
case 30 ... 37:
- screen->attr.fg.ccode = v - 30 + TERM_CCODE_BLACK;
+ screen->state.attr.fg.ccode = v - 30 + TERM_CCODE_BLACK;
break;
case 39:
- screen->attr.fg.ccode = 0;
+ screen->state.attr.fg.ccode = 0;
break;
case 40 ... 47:
- screen->attr.bg.ccode = v - 40 + TERM_CCODE_BLACK;
+ screen->state.attr.bg.ccode = v - 40 + TERM_CCODE_BLACK;
break;
case 49:
- screen->attr.bg.ccode = 0;
+ screen->state.attr.bg.ccode = 0;
break;
case 90 ... 97:
- screen->attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK;
+ screen->state.attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK;
break;
case 100 ... 107:
- screen->attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK;
+ screen->state.attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK;
break;
case 38:
/* fallthrough */
case 48:
if (v == 38)
- dst = &screen->attr.fg;
+ dst = &screen->state.attr.fg;
else
- dst = &screen->attr.bg;
+ dst = &screen->state.attr.bg;
++i;
if (i >= seq->n_args)
if (i >= seq->n_args || seq->args[i] < 0)
break;
+ dst->ccode = TERM_CCODE_256;
code = seq->args[i];
- if (code < 16) {
- dst->ccode = code;
- } else if (code < 232) {
- static const uint8_t bval[] = {
- 0x00, 0x5f, 0x87,
- 0xaf, 0xd7, 0xff,
- };
-
- dst->ccode = TERM_CCODE_256;
- dst->c256 = code;
- code -= 16;
- dst->blue = bval[code % 6];
- code /= 6;
- dst->green = bval[code % 6];
- code /= 6;
- dst->red = bval[code % 6];
- } else if (code < 256) {
- dst->ccode = TERM_CCODE_256;
- dst->c256 = code;
- code = (code - 232) * 10 + 8;
- dst->red = code;
- dst->green = code;
- dst->blue = code;
- }
+ dst->c256 = code < 256 ? code : 0;
break;
}
case -1:
/* fallthrough */
case 0:
- zero(screen->attr);
+ zero(screen->state.attr);
break;
}
}
* Map G0 into GL.
*/
- screen->gl = &screen->g0;
+ screen->state.gl = &screen->g0;
return 0;
}
unsigned int i;
for (i = 0; i < seq->n_args; ++i)
- screen_mode_change(screen, seq->args[i], false, true);
+ screen_mode_change_ansi(screen, seq->args[i], true);
return 0;
}
unsigned int i;
for (i = 0; i < seq->n_args; ++i)
- screen_mode_change(screen, seq->args[i], true, true);
+ screen_mode_change_dec(screen, seq->args[i], true);
return 0;
}
* Map G1 into GL.
*/
- screen->gl = &screen->g1;
+ screen->state.gl = &screen->g1;
return 0;
}
* Temporarily map G2 into GL for the next graphics character.
*/
- screen->glt = &screen->g2;
+ screen->state.glt = &screen->g2;
return 0;
}
* Temporarily map G3 into GL for the next graphics character
*/
- screen->glt = &screen->g3;
+ screen->state.glt = &screen->g3;
return 0;
}
if (seq->args[0] > 0)
num = seq->args[0];
- term_page_scroll_up(screen->page, num, &screen->attr, screen->age, screen->history);
+ term_page_scroll_up(screen->page, num, &screen->state.attr, screen->age, screen->history);
return 0;
}
switch (mode) {
case 0:
- pos = screen->cursor_x;
+ pos = screen->state.cursor_x;
if (screen->page->width > 0)
screen->tabs[pos / 8] &= ~(1U << (pos % 8));
break;
case 3:
if (screen->page->width > 0)
- memset(screen->tabs, 0, (screen->page->width + 7) / 8);
+ memzero(screen->tabs, (screen->page->width + 7) / 8);
break;
}
pos = seq->args[0];
screen_cursor_clear_wrap(screen);
- screen_cursor_set_rel(screen, screen->cursor_x, pos - 1);
+ screen_cursor_set_rel(screen, screen->state.cursor_x, pos - 1);
return 0;
}
return screen->page->height;
}
+uint64_t term_screen_get_age(term_screen *screen) {
+ assert_return(screen, 0);
+
+ return screen->age;
+}
+
int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) {
uint32_t *ucs4_str;
size_t i, j, ucs4_len;
assert_return(screen, -EINVAL);
+ ++screen->age;
+
/* Feed bytes into utf8 decoder and handle parsed ucs4 chars. We always
* treat data as UTF-8, but the parser makes sure to fall back to raw
* 8bit mode if the stream is not valid UTF-8. This should be more than
return 0;
}
-int term_screen_feed_keyboard(term_screen *screen, uint32_t keysym, uint32_t ascii, uint32_t ucs4, unsigned int mods) {
+static char *screen_map_key(term_screen *screen,
+ char *p,
+ const uint32_t *keysyms,
+ size_t n_syms,
+ uint32_t ascii,
+ const uint32_t *ucs4,
+ unsigned int mods) {
+ char ch, ch2, ch_mods;
+ uint32_t v;
+ size_t i;
+
+ /* TODO: All these key-mappings need to be verified. Public information
+ * on those mappings is pretty scarce and every emulator seems to do it
+ * slightly differently.
+ * A lot of mappings are also missing. */
+
+ if (n_syms < 1)
+ return p;
+
+ if (n_syms == 1)
+ v = keysyms[0];
+ else
+ v = XKB_KEY_NoSymbol;
+
+ /* In some mappings, the modifiers are encoded as CSI parameters. The
+ * encoding is rather arbitrary, but seems to work. */
+ ch_mods = 0;
+ switch (mods & (TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT | TERM_KBDMOD_CTRL)) {
+ case TERM_KBDMOD_SHIFT:
+ ch_mods = '2';
+ break;
+ case TERM_KBDMOD_ALT:
+ ch_mods = '3';
+ break;
+ case TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
+ ch_mods = '4';
+ break;
+ case TERM_KBDMOD_CTRL:
+ ch_mods = '5';
+ break;
+ case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT:
+ ch_mods = '6';
+ break;
+ case TERM_KBDMOD_CTRL | TERM_KBDMOD_ALT:
+ ch_mods = '7';
+ break;
+ case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
+ ch_mods = '8';
+ break;
+ }
+
+ /* A user might actually use multiple layouts for keyboard
+ * input. @keysyms[0] contains the actual keysym that the user
+ * used. But if this keysym is not in the ascii range, the
+ * input handler does check all other layouts that the user
+ * specified whether one of them maps the key to some ASCII
+ * keysym and provides this via @ascii. We always use the real
+ * keysym except when handling CTRL+<XY> shortcuts we use the
+ * ascii keysym. This is for compatibility to xterm et. al. so
+ * ctrl+c always works regardless of the currently active
+ * keyboard layout. But if no ascii-sym is found, we still use
+ * the real keysym. */
+ if (ascii == XKB_KEY_NoSymbol)
+ ascii = v;
+
+ /* map CTRL+<ascii> */
+ if (mods & TERM_KBDMOD_CTRL) {
+ switch (ascii) {
+ case 0x60 ... 0x7e:
+ /* Right hand side is mapped to the left and then
+ * treated equally. Fall through to left-hand side.. */
+ ascii -= 0x20;
+ case 0x20 ... 0x5f:
+ /* Printable ASCII is mapped 1-1 in XKB and in
+ * combination with CTRL bit 7 is flipped. This
+ * is equivalent to the caret-notation. */
+ *p++ = ascii ^ 0x40;
+ return p;
+ }
+ }
+
+ /* map cursor keys */
+ ch = 0;
+ switch (v) {
+ case XKB_KEY_Up:
+ ch = 'A';
+ break;
+ case XKB_KEY_Down:
+ ch = 'B';
+ break;
+ case XKB_KEY_Right:
+ ch = 'C';
+ break;
+ case XKB_KEY_Left:
+ ch = 'D';
+ break;
+ case XKB_KEY_Home:
+ ch = 'H';
+ break;
+ case XKB_KEY_End:
+ ch = 'F';
+ break;
+ }
+ if (ch) {
+ *p++ = 0x1b;
+ if (screen->flags & TERM_FLAG_CURSOR_KEYS)
+ *p++ = 'O';
+ else
+ *p++ = '[';
+ if (ch_mods) {
+ *p++ = '1';
+ *p++ = ';';
+ *p++ = ch_mods;
+ }
+ *p++ = ch;
+ return p;
+ }
+
+ /* map action keys */
+ ch = 0;
+ switch (v) {
+ case XKB_KEY_Find:
+ ch = '1';
+ break;
+ case XKB_KEY_Insert:
+ ch = '2';
+ break;
+ case XKB_KEY_Delete:
+ ch = '3';
+ break;
+ case XKB_KEY_Select:
+ ch = '4';
+ break;
+ case XKB_KEY_Page_Up:
+ ch = '5';
+ break;
+ case XKB_KEY_Page_Down:
+ ch = '6';
+ break;
+ }
+ if (ch) {
+ *p++ = 0x1b;
+ *p++ = '[';
+ *p++ = ch;
+ if (ch_mods) {
+ *p++ = ';';
+ *p++ = ch_mods;
+ }
+ *p++ = '~';
+ return p;
+ }
+
+ /* map lower function keys */
+ ch = 0;
+ switch (v) {
+ case XKB_KEY_F1:
+ ch = 'P';
+ break;
+ case XKB_KEY_F2:
+ ch = 'Q';
+ break;
+ case XKB_KEY_F3:
+ ch = 'R';
+ break;
+ case XKB_KEY_F4:
+ ch = 'S';
+ break;
+ }
+ if (ch) {
+ if (ch_mods) {
+ *p++ = 0x1b;
+ *p++ = '[';
+ *p++ = '1';
+ *p++ = ';';
+ *p++ = ch_mods;
+ *p++ = ch;
+ } else {
+ *p++ = 0x1b;
+ *p++ = 'O';
+ *p++ = ch;
+ }
+
+ return p;
+ }
+
+ /* map upper function keys */
+ ch = 0;
+ ch2 = 0;
+ switch (v) {
+ case XKB_KEY_F5:
+ ch = '1';
+ ch2 = '5';
+ break;
+ case XKB_KEY_F6:
+ ch = '1';
+ ch2 = '7';
+ break;
+ case XKB_KEY_F7:
+ ch = '1';
+ ch2 = '8';
+ break;
+ case XKB_KEY_F8:
+ ch = '1';
+ ch2 = '9';
+ break;
+ case XKB_KEY_F9:
+ ch = '2';
+ ch2 = '0';
+ break;
+ case XKB_KEY_F10:
+ ch = '2';
+ ch2 = '1';
+ break;
+ case XKB_KEY_F11:
+ ch = '2';
+ ch2 = '2';
+ break;
+ case XKB_KEY_F12:
+ ch = '2';
+ ch2 = '3';
+ break;
+ }
+ if (ch) {
+ *p++ = 0x1b;
+ *p++ = '[';
+ *p++ = ch;
+ if (ch2)
+ *p++ = ch2;
+ if (ch_mods) {
+ *p++ = ';';
+ *p++ = ch_mods;
+ }
+ *p++ = '~';
+ return p;
+ }
+
+ /* map special keys */
+ switch (v) {
+ case 0xff08: /* XKB_KEY_BackSpace */
+ case 0xff09: /* XKB_KEY_Tab */
+ case 0xff0a: /* XKB_KEY_Linefeed */
+ case 0xff0b: /* XKB_KEY_Clear */
+ case 0xff15: /* XKB_KEY_Sys_Req */
+ case 0xff1b: /* XKB_KEY_Escape */
+ case 0xffff: /* XKB_KEY_Delete */
+ *p++ = v - 0xff00;
+ return p;
+ case 0xff13: /* XKB_KEY_Pause */
+ /* TODO: What should we do with this key?
+ * Sending XOFF is awful as there is no simple
+ * way on modern keyboards to send XON again.
+ * If someone wants this, we can re-eanble
+ * optionally. */
+ return p;
+ case 0xff14: /* XKB_KEY_Scroll_Lock */
+ /* TODO: What should we do on scroll-lock?
+ * Sending 0x14 is what the specs say but it is
+ * not used today the way most users would
+ * expect so we disable it. If someone wants
+ * this, we can re-enable it (optionally). */
+ return p;
+ case XKB_KEY_Return:
+ *p++ = 0x0d;
+ if (screen->flags & TERM_FLAG_NEWLINE_MODE)
+ *p++ = 0x0a;
+ return p;
+ case XKB_KEY_ISO_Left_Tab:
+ *p++ = 0x09;
+ return p;
+ }
+
+ /* map unicode keys */
+ for (i = 0; i < n_syms; ++i)
+ p += utf8_encode_unichar(p, ucs4[i]);
+
+ return p;
+}
+
+int term_screen_feed_keyboard(term_screen *screen,
+ const uint32_t *keysyms,
+ size_t n_syms,
+ uint32_t ascii,
+ const uint32_t *ucs4,
+ unsigned int mods) {
+ _cleanup_free_ char *dyn = NULL;
+ static const size_t padding = 1;
+ char buf[128], *start, *p;
+
assert_return(screen, -EINVAL);
- /* TODO */
+ /* allocate buffer if too small */
+ start = buf;
+ if (4 * n_syms + padding > sizeof(buf)) {
+ dyn = malloc(4 * n_syms + padding);
+ if (!dyn)
+ return -ENOMEM;
+
+ start = dyn;
+ }
+
+ /* reserve prefix space */
+ start += padding;
+ p = start;
- return 0;
+ p = screen_map_key(screen, p, keysyms, n_syms, ascii, ucs4, mods);
+ if (!p || p - start < 1)
+ return 0;
+
+ /* The ALT modifier causes ESC to be prepended to any key-stroke. We
+ * already accounted for that buffer space above, so simply prepend it
+ * here.
+ * TODO: is altSendsEscape a suitable default? What are the semantics
+ * exactly? Is it used in C0/C1 conversion? Is it prepended if there
+ * already is an escape character? */
+ if (mods & TERM_KBDMOD_ALT && *start != 0x1b)
+ *--start = 0x1b;
+
+ /* turn C0 into C1 */
+ if (!(screen->flags & TERM_FLAG_7BIT_MODE) && p - start >= 2)
+ if (start[0] == 0x1b && start[1] >= 0x40 && start[1] <= 0x5f)
+ *++start ^= 0x40;
+
+ return screen_write(screen, start, p - start);
}
int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) {
assert_return(screen, -EINVAL);
- r = term_page_reserve(screen->page_main, x, y, &screen->attr, screen->age);
+ r = term_page_reserve(screen->page_main, x, y, &screen->state.attr, screen->age);
if (r < 0)
return r;
- r = term_page_reserve(screen->page_alt, x, y, &screen->attr, screen->age);
+ r = term_page_reserve(screen->page_alt, x, y, &screen->state.attr, screen->age);
if (r < 0)
return r;
for (i = (screen->page->width + 7) / 8 * 8; i < x; i += 8)
screen->tabs[i / 8] = 0x1;
- term_page_resize(screen->page_main, x, y, &screen->attr, screen->age, screen->history);
- term_page_resize(screen->page_alt, x, y, &screen->attr, screen->age, NULL);
+ term_page_resize(screen->page_main, x, y, &screen->state.attr, screen->age, screen->history);
+ term_page_resize(screen->page_alt, x, y, &screen->state.attr, screen->age, NULL);
- screen->cursor_x = screen_clamp_x(screen, screen->cursor_x);
- screen->cursor_y = screen_clamp_x(screen, screen->cursor_y);
+ screen->state.cursor_x = screen_clamp_x(screen, screen->state.cursor_x);
+ screen->state.cursor_y = screen_clamp_x(screen, screen->state.cursor_y);
screen_cursor_clear_wrap(screen);
return 0;
assert(screen);
- screen->gl = &screen->g0;
- screen->gr = &screen->g1;
- screen->glt = NULL;
- screen->grt = NULL;
screen->g0 = &term_unicode_lower;
screen->g1 = &term_unicode_upper;
screen->g2 = &term_unicode_lower;
screen->g3 = &term_unicode_upper;
+ screen->state.attr = screen->default_attr;
+ screen->state.gl = &screen->g0;
+ screen->state.gr = &screen->g1;
+ screen->state.glt = NULL;
+ screen->state.grt = NULL;
+ screen->state.auto_wrap = 0;
+ screen->state.origin_mode = 0;
+
+ screen->saved = screen->state;
+ screen->saved.cursor_x = 0;
+ screen->saved.cursor_y = 0;
+ screen->saved_alt = screen->saved;
screen->page = screen->page_main;
screen->history = screen->history_main;
screen->flags = TERM_FLAG_7BIT_MODE;
screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
- screen->attr = screen->default_attr;
-
- screen->saved.cursor_x = 0;
- screen->saved.cursor_y = 0;
- screen->saved.attr = screen->attr;
- screen->saved.gl = screen->gl;
- screen->saved.gr = screen->gr;
- screen->saved.glt = NULL;
- screen->saved.grt = NULL;
- screen->flags = 0;
for (i = 0; i < screen->page->width; i += 8)
screen->tabs[i / 8] = 0x1;
term_screen_soft_reset(screen);
zero(screen->utf8);
- screen->cursor_x = 0;
- screen->cursor_y = 0;
- term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->attr, screen->age, false);
- term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->attr, screen->age, false);
+ screen->state.cursor_x = 0;
+ screen->state.cursor_y = 0;
+ term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
+ term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
}
int term_screen_set_answerback(term_screen *screen, const char *answerback) {
line_age = MAX(line->age, page->age);
for (i = 0; i < page->width; ++i) {
+ term_attr attr;
+
cell = &line->cells[i];
cell_age = MAX(cell->age, line_age);
* renderers can assume ch_width is set properpy. */
cw = MAX(cell->cwidth, 1U);
+ attr = cell->attr;
+ if (i == screen->state.cursor_x && j == screen->state.cursor_y &&
+ !(screen->flags & TERM_FLAG_HIDE_CURSOR))
+ attr.inverse ^= 1;
+
r = draw_fn(screen,
userdata,
i,
j,
- &cell->attr,
+ &attr,
ch_str,
ch_n,
cw);
}
if (fb_age)
- *fb_age = screen->age++;
+ *fb_age = screen->age;
return 0;
}