mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-12 21:50:26 +01:00
Multiple updates
- Use viewport as render target for terminal: Terminal now only draws cells which changed since the last _draw() call. A viewport is used with clear mode set to NEVER to cache previous draw calls. The terminal node and viewport are wrapped by a GDScript Terminal node which takes care of resizing the viewport scene, and forcing the terminal to redraw all cells when necessary (i.e. on resize or theme change). Adds update_mode to terminal interface which can be set to one of: - DISABLED: terminal will never be drawn - AUTO: terminal will only draw the cells that changed, but automatically redraw the full screen when necessary (for example, when the size or theme changed). - ALL: terminal will always draw every cell on every update. This is the most reliable but least performant option. - ALL_NEXT_FRAME: Will use update_mode ALL for the next _draw() call, then change update_mode back to AUTO. - Upgraded libtsm: Includes changes from Fredrik Wikstrom (salass00)'s fork of libtsm. - Don't require theme to be set. Terminal will use default fonts/colors if no theme is set.
This commit is contained in:
parent
3dd89ec0a7
commit
d2f073d7ae
13 changed files with 312 additions and 178 deletions
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
const struct Terminal::cell Terminal::empty_cell = {{0, 0, 0, 0, 0}, {}};
|
|
||||||
|
|
||||||
std::map<std::pair<int64_t, int64_t>, int> Terminal::_key_list = {};
|
std::map<std::pair<int64_t, int64_t>, int> Terminal::_key_list = {};
|
||||||
void Terminal::_populate_key_list() {
|
void Terminal::_populate_key_list() {
|
||||||
if (!_key_list.empty())
|
if (!_key_list.empty())
|
||||||
|
@ -243,29 +241,27 @@ static void write_cb(struct tsm_vte *vte, const char *u8, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int text_draw_cb(struct tsm_screen *con, uint64_t id, const uint32_t *ch,
|
static int text_draw_cb(struct tsm_screen *con, uint64_t id, const uint32_t *ch,
|
||||||
size_t len, unsigned int width, unsigned int posx,
|
size_t len, unsigned int width, unsigned int col,
|
||||||
unsigned int posy, const struct tsm_screen_attr *attr,
|
unsigned int row, const struct tsm_screen_attr *attr,
|
||||||
tsm_age_t age, void *data) {
|
tsm_age_t age, void *data) {
|
||||||
|
|
||||||
Terminal *terminal = static_cast<Terminal *>(data);
|
Terminal *terminal = static_cast<Terminal *>(data);
|
||||||
|
|
||||||
if (age <= terminal->framebuffer_age)
|
if (terminal->update_mode == Terminal::UpdateMode::AUTO && age != 0 &&
|
||||||
|
age <= terminal->framebuffer_age)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
std::pair<Color, Color> color_pair = terminal->get_cell_colors(attr);
|
||||||
|
terminal->draw_background(row, col, color_pair.first);
|
||||||
|
|
||||||
size_t ulen;
|
size_t ulen;
|
||||||
char buf[5] = {0};
|
char buf[5] = {0};
|
||||||
|
|
||||||
if (len > 0) {
|
if (len < 1) // No foreground to draw.
|
||||||
char *utf8 = tsm_ucs4_to_utf8_alloc(ch, len, &ulen);
|
return 0;
|
||||||
memcpy(terminal->cells[posy][posx].ch, utf8, ulen);
|
|
||||||
} else {
|
|
||||||
terminal->cells[posy][posx] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&terminal->cells[posy][posx].attr, attr, sizeof(tsm_screen_attr));
|
char *utf8 = tsm_ucs4_to_utf8_alloc(ch, len, &ulen);
|
||||||
|
memcpy(buf, utf8, ulen);
|
||||||
if (!terminal->sleep)
|
terminal->draw_foreground(row, col, buf, attr, color_pair.second);
|
||||||
terminal->update();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -273,15 +269,19 @@ static int text_draw_cb(struct tsm_screen *con, uint64_t id, const uint32_t *ch,
|
||||||
void Terminal::_register_methods() {
|
void Terminal::_register_methods() {
|
||||||
register_method("_init", &Terminal::_init);
|
register_method("_init", &Terminal::_init);
|
||||||
register_method("_ready", &Terminal::_ready);
|
register_method("_ready", &Terminal::_ready);
|
||||||
|
register_method("_notification", &Terminal::_notification);
|
||||||
register_method("_gui_input", &Terminal::_gui_input);
|
register_method("_gui_input", &Terminal::_gui_input);
|
||||||
register_method("_draw", &Terminal::_draw);
|
register_method("_draw", &Terminal::_draw);
|
||||||
register_method("_notification", &Terminal::_notification);
|
|
||||||
|
|
||||||
register_method("write", &Terminal::write);
|
register_method("write", &Terminal::write);
|
||||||
register_method("update_size", &Terminal::update_size);
|
|
||||||
|
register_method("_update_theme", &Terminal::update_theme);
|
||||||
|
register_method("_update_size", &Terminal::update_theme);
|
||||||
|
|
||||||
register_property<Terminal, int>("rows", &Terminal::rows, 24);
|
register_property<Terminal, int>("rows", &Terminal::rows, 24);
|
||||||
register_property<Terminal, int>("cols", &Terminal::cols, 80);
|
register_property<Terminal, int>("cols", &Terminal::cols, 80);
|
||||||
|
register_property<Terminal, int>("update_mode", &Terminal::update_mode,
|
||||||
|
UpdateMode::AUTO);
|
||||||
|
|
||||||
register_signal<Terminal>("data_sent", "data",
|
register_signal<Terminal>("data_sent", "data",
|
||||||
GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
|
GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
|
||||||
|
@ -296,7 +296,8 @@ Terminal::Terminal() {}
|
||||||
Terminal::~Terminal() {}
|
Terminal::~Terminal() {}
|
||||||
|
|
||||||
void Terminal::_init() {
|
void Terminal::_init() {
|
||||||
sleep = true;
|
framebuffer_age = 0;
|
||||||
|
update_mode = UpdateMode::AUTO;
|
||||||
|
|
||||||
if (tsm_screen_new(&screen, NULL, NULL)) {
|
if (tsm_screen_new(&screen, NULL, NULL)) {
|
||||||
ERR_PRINT("Error creating new tsm screen");
|
ERR_PRINT("Error creating new tsm screen");
|
||||||
|
@ -307,14 +308,9 @@ void Terminal::_init() {
|
||||||
if (tsm_vte_new(&vte, screen, write_cb, this, NULL, NULL)) {
|
if (tsm_vte_new(&vte, screen, write_cb, this, NULL, NULL)) {
|
||||||
ERR_PRINT("Error creating new tsm vte");
|
ERR_PRINT("Error creating new tsm vte");
|
||||||
}
|
}
|
||||||
|
|
||||||
update_theme();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::_ready() {
|
void Terminal::_ready() { update_theme(); }
|
||||||
update_size();
|
|
||||||
connect("resized", this, "update_size");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Terminal::_notification(int what) {
|
void Terminal::_notification(int what) {
|
||||||
switch (what) {
|
switch (what) {
|
||||||
|
@ -322,7 +318,7 @@ void Terminal::_notification(int what) {
|
||||||
update_size();
|
update_size();
|
||||||
break;
|
break;
|
||||||
case NOTIFICATION_THEME_CHANGED:
|
case NOTIFICATION_THEME_CHANGED:
|
||||||
update_theme();
|
// update_theme();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,42 +352,32 @@ void Terminal::_gui_input(Variant event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::_draw() {
|
void Terminal::_draw() {
|
||||||
if (sleep)
|
if (update_mode == UpdateMode::DISABLED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Draw the full terminal rect background */
|
if ((update_mode > UpdateMode::AUTO) || framebuffer_age == 0) {
|
||||||
Color background_color = palette[TSM_COLOR_BACKGROUND];
|
/* Draw the full terminal rect background */
|
||||||
|
// Draw the rectangle slightly larger, so it fills the entire viewport.
|
||||||
draw_rect(Rect2(Vector2(0, 0), get_rect().size), background_color);
|
Color background_color = palette[TSM_COLOR_BACKGROUND];
|
||||||
|
draw_rect(Rect2(Vector2(-4, -4), get_rect().size + Vector2(8, 8)),
|
||||||
for (int row = 0; row < rows; row++) {
|
background_color);
|
||||||
for (int col = 0; col < cols; col++) {
|
|
||||||
/* Draw cell background and foreground */
|
|
||||||
std::pair<Color, Color> color_pair = get_cell_colors(row, col);
|
|
||||||
draw_background(row, col, color_pair.first);
|
|
||||||
draw_foreground(row, col, color_pair.second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this);
|
||||||
|
|
||||||
|
if (update_mode == UpdateMode::ALL_NEXT_FRAME)
|
||||||
|
update_mode = UpdateMode::AUTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::update_theme() {
|
void Terminal::update_theme() {
|
||||||
/* Generate color palette based on theme */
|
/* Generate color palette based on theme */
|
||||||
|
|
||||||
// Converts a color from the Control's theme to one that can
|
|
||||||
// be used in a tsm color palette.
|
|
||||||
auto set_pallete_color = [this](tsm_vte_color color, String theme_color,
|
auto set_pallete_color = [this](tsm_vte_color color, String theme_color,
|
||||||
int default_r, int default_g,
|
Color default_color) -> void {
|
||||||
int default_b) -> void {
|
|
||||||
Color c;
|
Color c;
|
||||||
|
|
||||||
if (has_color(theme_color, "Terminal")) {
|
c = has_color(theme_color, "Terminal") ? get_color(theme_color, "Terminal")
|
||||||
c = get_color(theme_color, "Terminal");
|
: default_color;
|
||||||
} else {
|
|
||||||
int r = default_r;
|
|
||||||
int g = default_g;
|
|
||||||
int b = default_b;
|
|
||||||
c = Color((float)r / 255.0, (float)g / 255.0, (float)b / 255.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
color_palette[color][0] = c.get_r8();
|
color_palette[color][0] = c.get_r8();
|
||||||
color_palette[color][1] = c.get_g8();
|
color_palette[color][1] = c.get_g8();
|
||||||
|
@ -400,25 +386,30 @@ void Terminal::update_theme() {
|
||||||
palette[color] = c;
|
palette[color] = c;
|
||||||
};
|
};
|
||||||
|
|
||||||
set_pallete_color(TSM_COLOR_BLACK, "Black", 0, 0, 0);
|
set_pallete_color(TSM_COLOR_BLACK, "Black", Color(0, 0, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_RED, "Red", 205, 0, 0);
|
set_pallete_color(TSM_COLOR_RED, "Red", Color(0.501961, 0, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_GREEN, "Green", 0, 205, 0);
|
set_pallete_color(TSM_COLOR_GREEN, "Green", Color(0, 0.501961, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_YELLOW, "Yellow", 205, 205, 0);
|
set_pallete_color(TSM_COLOR_YELLOW, "Yellow",
|
||||||
set_pallete_color(TSM_COLOR_BLUE, "Blue", 0, 0, 238);
|
Color(0.501961, 0.501961, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_MAGENTA, "Magenta", 205, 0, 205);
|
set_pallete_color(TSM_COLOR_BLUE, "Blue", Color(0, 0, 0.501961, 1));
|
||||||
set_pallete_color(TSM_COLOR_CYAN, "Cyan", 0, 205, 205);
|
set_pallete_color(TSM_COLOR_MAGENTA, "Magenta",
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_GREY, "Light Grey", 229, 229, 229);
|
Color(0.501961, 0, 0.501961, 1));
|
||||||
set_pallete_color(TSM_COLOR_DARK_GREY, "Dark Grey", 127, 127, 127);
|
set_pallete_color(TSM_COLOR_CYAN, "Cyan", Color(0, 0.501961, 0.501961, 1));
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_RED, "Light Red", 255, 0, 0);
|
set_pallete_color(TSM_COLOR_DARK_GREY, "Dark Grey",
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_GREEN, "Light Green", 0, 255, 0);
|
Color(0.501961, 0.501961, 0.501961, 1));
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_YELLOW, "Light Yellow", 255, 255, 0);
|
set_pallete_color(TSM_COLOR_LIGHT_GREY, "Light Grey",
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_BLUE, "Light Blue", 0, 0, 255);
|
Color(0.752941, 0.752941, 0.752941, 1));
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_MAGENTA, "Light Magenta", 255, 0, 255);
|
set_pallete_color(TSM_COLOR_LIGHT_RED, "Light Red", Color(1, 0, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_LIGHT_CYAN, "Light Cyan", 0, 255, 255);
|
set_pallete_color(TSM_COLOR_LIGHT_GREEN, "Light Green", Color(0, 1, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_WHITE, "White", 255, 255, 255);
|
set_pallete_color(TSM_COLOR_LIGHT_YELLOW, "Light Yellow", Color(1, 1, 0, 1));
|
||||||
|
set_pallete_color(TSM_COLOR_LIGHT_BLUE, "Light Blue", Color(0, 0, 1, 1));
|
||||||
|
set_pallete_color(TSM_COLOR_LIGHT_MAGENTA, "Light Magenta",
|
||||||
|
Color(1, 0, 1, 1));
|
||||||
|
set_pallete_color(TSM_COLOR_LIGHT_CYAN, "Light Cyan", Color(0, 1, 1, 1));
|
||||||
|
set_pallete_color(TSM_COLOR_WHITE, "White", Color(1, 1, 1, 1));
|
||||||
|
|
||||||
set_pallete_color(TSM_COLOR_BACKGROUND, "Background", 255, 255, 255);
|
set_pallete_color(TSM_COLOR_BACKGROUND, "Background", Color(0, 0, 0, 1));
|
||||||
set_pallete_color(TSM_COLOR_FOREGROUND, "Foreground", 0, 0, 0);
|
set_pallete_color(TSM_COLOR_FOREGROUND, "Foreground", Color(1, 1, 1, 1));
|
||||||
|
|
||||||
if (tsm_vte_set_custom_palette(vte, color_palette)) {
|
if (tsm_vte_set_custom_palette(vte, color_palette)) {
|
||||||
ERR_PRINT("Error setting custom palette");
|
ERR_PRINT("Error setting custom palette");
|
||||||
|
@ -442,9 +433,8 @@ void Terminal::update_theme() {
|
||||||
fontmap.insert(std::pair<String, Ref<Font>>(font_style, fontref));
|
fontmap.insert(std::pair<String, Ref<Font>>(font_style, fontref));
|
||||||
};
|
};
|
||||||
|
|
||||||
set_font(
|
set_font("Bold Italic", "res://addons/godot_xterm/themes/fonts/cousine/"
|
||||||
"Bold Italic",
|
"cousine_bold_italic.tres");
|
||||||
"res://addons/godot_xterm/themes/fonts/cousine/cousine_bold_italic.tres");
|
|
||||||
set_font("Bold",
|
set_font("Bold",
|
||||||
"res://addons/godot_xterm/themes/fonts/cousine/cousine_bold.tres");
|
"res://addons/godot_xterm/themes/fonts/cousine/cousine_bold.tres");
|
||||||
set_font("Italic",
|
set_font("Italic",
|
||||||
|
@ -452,32 +442,28 @@ void Terminal::update_theme() {
|
||||||
set_font(
|
set_font(
|
||||||
"Regular",
|
"Regular",
|
||||||
"res://addons/godot_xterm/themes/fonts/cousine/cousine_regular.tres");
|
"res://addons/godot_xterm/themes/fonts/cousine/cousine_regular.tres");
|
||||||
|
|
||||||
|
update_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::draw_background(int row, int col, Color bgcolor) {
|
void Terminal::draw_background(int row, int col, Color bgcolor) {
|
||||||
|
|
||||||
/* Draw the background */
|
/* Draw the background */
|
||||||
Vector2 background_pos = Vector2(col * cell_size.x, row * cell_size.y);
|
Vector2 background_pos = Vector2(col * cell_size.x, row * cell_size.y);
|
||||||
Rect2 background_rect = Rect2(background_pos, cell_size);
|
Rect2 background_rect = Rect2(background_pos, cell_size);
|
||||||
draw_rect(background_rect, bgcolor);
|
draw_rect(background_rect, bgcolor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::draw_foreground(int row, int col, Color fgcolor) {
|
void Terminal::draw_foreground(int row, int col, char *ch,
|
||||||
|
const tsm_screen_attr *attr, Color fgcolor) {
|
||||||
struct cell cell = cells[row][col];
|
|
||||||
|
|
||||||
if (cell.ch == nullptr)
|
|
||||||
return; // No foreground to draw
|
|
||||||
|
|
||||||
/* Set the font */
|
/* Set the font */
|
||||||
|
|
||||||
Ref<Font> fontref = get_font("");
|
Ref<Font> fontref = get_font("");
|
||||||
|
|
||||||
if (cell.attr.bold && cell.attr.italic) {
|
if (attr->bold && attr->italic) {
|
||||||
fontref = fontmap["Bold Italic"];
|
fontref = fontmap["Bold Italic"];
|
||||||
} else if (cell.attr.bold) {
|
} else if (attr->bold) {
|
||||||
fontref = fontmap["Bold"];
|
fontref = fontmap["Bold"];
|
||||||
} else if (cell.attr.italic) {
|
} else if (attr->italic) {
|
||||||
fontref = fontmap["Italic"];
|
fontref = fontmap["Italic"];
|
||||||
} else {
|
} else {
|
||||||
fontref = fontmap["Regular"];
|
fontref = fontmap["Regular"];
|
||||||
|
@ -485,65 +471,61 @@ void Terminal::draw_foreground(int row, int col, Color fgcolor) {
|
||||||
|
|
||||||
/* Draw the foreground */
|
/* Draw the foreground */
|
||||||
|
|
||||||
if (cell.attr.blink)
|
if (attr->blink)
|
||||||
; // TODO: Handle blink
|
; // TODO: Handle blink
|
||||||
|
|
||||||
int font_height = fontref.ptr()->get_height();
|
int font_height = fontref.ptr()->get_height();
|
||||||
Vector2 foreground_pos =
|
Vector2 foreground_pos =
|
||||||
Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25);
|
Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25);
|
||||||
draw_string(fontref, foreground_pos, cell.ch, fgcolor);
|
draw_string(fontref, foreground_pos, ch, fgcolor);
|
||||||
|
|
||||||
if (cell.attr.underline)
|
if (attr->underline)
|
||||||
draw_string(fontref, foreground_pos, "_", fgcolor);
|
draw_string(fontref, foreground_pos, "_", fgcolor);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Color, Color> Terminal::get_cell_colors(int row, int col) {
|
std::pair<Color, Color> Terminal::get_cell_colors(const tsm_screen_attr *attr) {
|
||||||
struct cell cell = cells[row][col];
|
|
||||||
Color fgcol, bgcol;
|
Color fgcol, bgcol;
|
||||||
float fr = 0, fg = 0, fb = 0, br = 1, bg = 1, bb = 1;
|
float fr = 0, fg = 0, fb = 0, br = 1, bg = 1, bb = 1;
|
||||||
|
|
||||||
/* Get foreground color */
|
/* Get foreground color */
|
||||||
|
|
||||||
if (cell.attr.fccode && palette.count(cell.attr.fccode)) {
|
if (attr->fccode && palette.count(attr->fccode)) {
|
||||||
fgcol = palette[cell.attr.fccode];
|
fgcol = palette[attr->fccode];
|
||||||
} else {
|
} else {
|
||||||
fr = (float)cell.attr.fr / 255.0;
|
fr = (float)attr->fr / 255.0;
|
||||||
fg = (float)cell.attr.fg / 255.0;
|
fg = (float)attr->fg / 255.0;
|
||||||
fb = (float)cell.attr.fb / 255.0;
|
fb = (float)attr->fb / 255.0;
|
||||||
fgcol = Color(fr, fg, fb);
|
fgcol = Color(fr, fg, fb);
|
||||||
|
|
||||||
if (cell.attr.fccode != -1) {
|
if (attr->fccode != -1) {
|
||||||
palette.insert(
|
palette.insert(std::pair<int, Color>(attr->fccode, Color(fr, fg, fb)));
|
||||||
std::pair<int, Color>(cell.attr.fccode, Color(fr, fg, fb)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get background color */
|
/* Get background color */
|
||||||
|
|
||||||
if (cell.attr.bccode && palette.count(cell.attr.bccode)) {
|
if (attr->bccode && palette.count(attr->bccode)) {
|
||||||
bgcol = palette[cell.attr.bccode];
|
bgcol = palette[attr->bccode];
|
||||||
} else {
|
} else {
|
||||||
br = (float)cell.attr.br / 255.0;
|
br = (float)attr->br / 255.0;
|
||||||
bg = (float)cell.attr.bg / 255.0;
|
bg = (float)attr->bg / 255.0;
|
||||||
bb = (float)cell.attr.bb / 255.0;
|
bb = (float)attr->bb / 255.0;
|
||||||
bgcol = Color(br, bg, bb);
|
bgcol = Color(br, bg, bb);
|
||||||
|
|
||||||
if (cell.attr.bccode != -1) {
|
if (attr->bccode != -1) {
|
||||||
palette.insert(
|
palette.insert(std::pair<int, Color>(attr->bccode, Color(br, bg, bb)));
|
||||||
std::pair<int, Color>(cell.attr.bccode, Color(br, bg, bb)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell.attr.inverse)
|
if (attr->inverse)
|
||||||
std::swap(bgcol, fgcol);
|
std::swap(bgcol, fgcol);
|
||||||
|
|
||||||
return std::make_pair(bgcol, fgcol);
|
return std::make_pair(bgcol, fgcol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recalculates the cell_size and number of cols/rows based on font size and the
|
|
||||||
// Control's rect_size
|
|
||||||
void Terminal::update_size() {
|
void Terminal::update_size() {
|
||||||
sleep = true;
|
// Recalculates the cell_size and number of cols/rows based on font size and
|
||||||
|
// the Control's rect_size.
|
||||||
|
|
||||||
Ref<Font> fontref = fontmap.count("Regular")
|
Ref<Font> fontref = fontmap.count("Regular")
|
||||||
? fontmap["Regular"]
|
? fontmap["Regular"]
|
||||||
|
@ -557,33 +539,12 @@ void Terminal::update_size() {
|
||||||
|
|
||||||
emit_signal("size_changed", Vector2(cols, rows));
|
emit_signal("size_changed", Vector2(cols, rows));
|
||||||
|
|
||||||
Cells new_cells = {};
|
|
||||||
|
|
||||||
for (int x = 0; x < rows; x++) {
|
|
||||||
Row row(cols);
|
|
||||||
|
|
||||||
for (int y = 0; y < cols; y++) {
|
|
||||||
if (x < cells.size() && y < cells[x].size()) {
|
|
||||||
row[y] = cells[x][y];
|
|
||||||
} else {
|
|
||||||
row[y] = empty_cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_cells.push_back(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
cells = new_cells;
|
|
||||||
|
|
||||||
tsm_screen_resize(screen, cols, rows);
|
tsm_screen_resize(screen, cols, rows);
|
||||||
|
|
||||||
sleep = false;
|
|
||||||
framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this);
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::write(Variant data) {
|
void Terminal::write(Variant data) {
|
||||||
|
|
||||||
const char *u8;
|
const char *u8;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
|
@ -606,5 +567,6 @@ void Terminal::write(Variant data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tsm_vte_input(vte, u8, len);
|
tsm_vte_input(vte, u8, len);
|
||||||
framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this);
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,18 +14,6 @@ class Terminal : public Control {
|
||||||
GODOT_CLASS(Terminal, Control)
|
GODOT_CLASS(Terminal, Control)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct cell {
|
|
||||||
char ch[5];
|
|
||||||
struct tsm_screen_attr attr;
|
|
||||||
};
|
|
||||||
static const struct cell empty_cell;
|
|
||||||
|
|
||||||
public:
|
|
||||||
typedef std::vector<std::vector<struct cell>> Cells;
|
|
||||||
typedef std::vector<struct cell> Row;
|
|
||||||
|
|
||||||
Cells cells;
|
|
||||||
|
|
||||||
Ref<InputEventKey> input_event_key;
|
Ref<InputEventKey> input_event_key;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -44,11 +32,13 @@ private:
|
||||||
std::map<String, Ref<Font>> fontmap = {};
|
std::map<String, Ref<Font>> fontmap = {};
|
||||||
|
|
||||||
void update_size();
|
void update_size();
|
||||||
|
|
||||||
void update_theme();
|
void update_theme();
|
||||||
std::pair<Color, Color> get_cell_colors(int row, int col);
|
|
||||||
|
public:
|
||||||
|
std::pair<Color, Color> get_cell_colors(const tsm_screen_attr *attr);
|
||||||
void draw_background(int row, int col, Color bgcol);
|
void draw_background(int row, int col, Color bgcol);
|
||||||
void draw_foreground(int row, int col, Color fgcol);
|
void draw_foreground(int row, int col, char *ch, const tsm_screen_attr *attr,
|
||||||
|
Color fgcol);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void _register_methods();
|
static void _register_methods();
|
||||||
|
@ -64,10 +54,16 @@ public:
|
||||||
|
|
||||||
void write(Variant data);
|
void write(Variant data);
|
||||||
|
|
||||||
|
enum UpdateMode {
|
||||||
|
DISABLED,
|
||||||
|
AUTO,
|
||||||
|
ALL,
|
||||||
|
ALL_NEXT_FRAME,
|
||||||
|
};
|
||||||
|
|
||||||
int rows;
|
int rows;
|
||||||
int cols;
|
int cols;
|
||||||
|
int update_mode;
|
||||||
bool sleep;
|
|
||||||
|
|
||||||
uint8_t color_palette[TSM_COLOR_NUM][3];
|
uint8_t color_palette[TSM_COLOR_NUM][3];
|
||||||
|
|
||||||
|
|
2
addons/godot_xterm/native/thirdparty/libtsm
vendored
2
addons/godot_xterm/native/thirdparty/libtsm
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 1f3ae2bc36236136b8ed328b0c64ed954beac33f
|
Subproject commit 1fd9cef6f2ddef2609d587ea3a5a8eeadb22a2e4
|
99
addons/godot_xterm/nodes/terminal/terminal.gd
Normal file
99
addons/godot_xterm/nodes/terminal/terminal.gd
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
const DefaultTheme = preload("../../themes/default.tres")
|
||||||
|
|
||||||
|
signal data_sent(data)
|
||||||
|
signal key_pressed(data, event)
|
||||||
|
signal size_changed(new_size)
|
||||||
|
|
||||||
|
enum UpdateMode {
|
||||||
|
DISABLED,
|
||||||
|
AUTO,
|
||||||
|
ALL,
|
||||||
|
ALL_NEXT_FRAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
export (UpdateMode) var update_mode = UpdateMode.AUTO setget set_update_mode
|
||||||
|
|
||||||
|
var rows: int setget , get_rows # TODO: Show in inspector.
|
||||||
|
var cols: int setget , get_cols # TODO: Show in inspector.
|
||||||
|
|
||||||
|
var _viewport: Viewport = preload("./viewport.tscn").instance()
|
||||||
|
var _native_terminal: Control = _viewport.get_node("Terminal")
|
||||||
|
var _screen := TextureRect.new()
|
||||||
|
var _visibility_notifier := VisibilityNotifier2D.new()
|
||||||
|
|
||||||
|
|
||||||
|
func set_update_mode(value):
|
||||||
|
update_mode = value
|
||||||
|
_native_terminal.update_mode = value
|
||||||
|
|
||||||
|
|
||||||
|
func get_rows() -> int:
|
||||||
|
return _native_terminal.rows
|
||||||
|
|
||||||
|
|
||||||
|
func get_cols() -> int:
|
||||||
|
return _native_terminal.cols
|
||||||
|
|
||||||
|
|
||||||
|
func write(data) -> void:
|
||||||
|
assert(data is String or data is PoolByteArray)
|
||||||
|
_native_terminal.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
if theme:
|
||||||
|
_native_terminal.theme = theme
|
||||||
|
|
||||||
|
_native_terminal.update_mode = update_mode
|
||||||
|
_native_terminal.connect("data_sent", self, "_on_data_sent")
|
||||||
|
_native_terminal.connect("key_pressed", self, "_on_key_pressed")
|
||||||
|
_native_terminal.connect("size_changed", self, "_on_size_changed")
|
||||||
|
|
||||||
|
_viewport.size = rect_size
|
||||||
|
_viewport.render_target_update_mode = Viewport.UPDATE_ALWAYS
|
||||||
|
|
||||||
|
_screen.set_anchors_preset(PRESET_WIDE)
|
||||||
|
_screen.texture = _viewport.get_texture()
|
||||||
|
|
||||||
|
_visibility_notifier.connect("screen_entered", self, "_refresh")
|
||||||
|
|
||||||
|
add_child(_viewport)
|
||||||
|
add_child(_screen)
|
||||||
|
add_child(_visibility_notifier)
|
||||||
|
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh():
|
||||||
|
if update_mode == UpdateMode.AUTO:
|
||||||
|
_native_terminal.update_mode = UpdateMode.ALL_NEXT_FRAME
|
||||||
|
|
||||||
|
|
||||||
|
func _gui_input(event):
|
||||||
|
_native_terminal._gui_input(event)
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what: int) -> void:
|
||||||
|
match what:
|
||||||
|
NOTIFICATION_RESIZED:
|
||||||
|
_viewport.size = rect_size
|
||||||
|
_visibility_notifier.rect = get_rect()
|
||||||
|
_refresh()
|
||||||
|
NOTIFICATION_THEME_CHANGED:
|
||||||
|
_native_terminal.theme = theme
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_data_sent(data: PoolByteArray):
|
||||||
|
emit_signal("data_sent", data)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_key_pressed(data: String, event: InputEventKey):
|
||||||
|
emit_signal("key_pressed", data, event)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_size_changed(new_size: Vector2):
|
||||||
|
emit_signal("size_changed", new_size)
|
2
addons/godot_xterm/nodes/terminal/viewport.gd
Normal file
2
addons/godot_xterm/nodes/terminal/viewport.gd
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
tool
|
||||||
|
extends Viewport
|
32
addons/godot_xterm/nodes/terminal/viewport.tscn
Normal file
32
addons/godot_xterm/nodes/terminal/viewport.tscn
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=1]
|
||||||
|
|
||||||
|
[sub_resource type="GDScript" id=1]
|
||||||
|
script/source = "tool
|
||||||
|
extends Viewport
|
||||||
|
"
|
||||||
|
|
||||||
|
[node name="Viewport" type="Viewport"]
|
||||||
|
size = Vector2( 2, 2 )
|
||||||
|
own_world = true
|
||||||
|
transparent_bg = true
|
||||||
|
handle_input_locally = false
|
||||||
|
hdr = false
|
||||||
|
usage = 0
|
||||||
|
render_target_v_flip = true
|
||||||
|
render_target_clear_mode = 1
|
||||||
|
gui_snap_controls_to_pixels = false
|
||||||
|
script = SubResource( 1 )
|
||||||
|
|
||||||
|
[node name="Terminal" type="Control" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
margin_right = -2.0
|
||||||
|
margin_bottom = -2.0
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
rows = 2
|
||||||
|
cols = 1
|
|
@ -1,20 +1,16 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://addons/godot_xterm/themes/default_dark.tres" type="Theme" id=1]
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gd" type="Script" id=1]
|
||||||
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=4]
|
|
||||||
[ext_resource path="res://examples/asciicast/example.cast" type="Animation" id=6]
|
[ext_resource path="res://examples/asciicast/example.cast" type="Animation" id=6]
|
||||||
|
|
||||||
[node name="Terminal" type="Control"]
|
[node name="Terminal" type="Control"]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
focus_mode = 2
|
focus_mode = 1
|
||||||
theme = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
script = ExtResource( 4 )
|
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
rows = 31
|
|
||||||
cols = 102
|
|
||||||
|
|
||||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||||
autoplay = "example"
|
autoplay = "example"
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=1]
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gd" type="Script" id=1]
|
||||||
[ext_resource path="res://examples/menu/menu.gd" type="Script" id=2]
|
[ext_resource path="res://examples/menu/menu.gd" type="Script" id=2]
|
||||||
[ext_resource path="res://addons/godot_xterm/themes/default_dark.tres" type="Theme" id=3]
|
|
||||||
|
|
||||||
[node name="Menu" type="Control"]
|
[node name="Menu" type="Control"]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
|
@ -15,15 +14,11 @@ __meta__ = {
|
||||||
[node name="Terminal" type="Control" parent="."]
|
[node name="Terminal" type="Control" parent="."]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
focus_mode = 2
|
rect_pivot_offset = Vector2( -731.582, 67.4799 )
|
||||||
size_flags_horizontal = 3
|
focus_mode = 1
|
||||||
size_flags_vertical = 3
|
|
||||||
theme = ExtResource( 3 )
|
|
||||||
script = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
rows = 31
|
|
||||||
cols = 102
|
|
||||||
|
|
||||||
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]
|
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=1]
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gd" type="Script" id=1]
|
||||||
[ext_resource path="res://addons/godot_xterm/nodes/pseudoterminal/pseudoterminal.gdns" type="Script" id=2]
|
[ext_resource path="res://addons/godot_xterm/nodes/pseudoterminal/pseudoterminal.gdns" type="Script" id=2]
|
||||||
[ext_resource path="res://addons/godot_xterm/themes/default_dark.tres" type="Theme" id=3]
|
|
||||||
|
|
||||||
[node name="Terminal" type="Control"]
|
[node name="Terminal" type="Control"]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
focus_mode = 2
|
focus_mode = 1
|
||||||
theme = ExtResource( 3 )
|
|
||||||
script = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
rows = 31
|
|
||||||
cols = 102
|
|
||||||
|
|
||||||
[node name="Pseudoterminal" type="Node" parent="."]
|
[node name="Pseudoterminal" type="Node" parent="."]
|
||||||
script = ExtResource( 2 )
|
script = ExtResource( 2 )
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://addons/godot_xterm/themes/default_dark.tres" type="Theme" id=1]
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gd" type="Script" id=2]
|
||||||
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=2]
|
|
||||||
[ext_resource path="res://examples/web_console/web_console.gd" type="Script" id=3]
|
[ext_resource path="res://examples/web_console/web_console.gd" type="Script" id=3]
|
||||||
|
|
||||||
[node name="WebConsole" type="Control"]
|
[node name="WebConsole" type="Control"]
|
||||||
|
@ -16,12 +15,9 @@ __meta__ = {
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
focus_mode = 2
|
focus_mode = 2
|
||||||
theme = ExtResource( 1 )
|
|
||||||
script = ExtResource( 2 )
|
script = ExtResource( 2 )
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
rows = 31
|
|
||||||
cols = 102
|
|
||||||
|
|
||||||
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]
|
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]
|
||||||
|
|
10
test/scenes/basic.gd
Normal file
10
test/scenes/basic.gd
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
onready var terminal = $Terminal
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
print("terminal size; rows %d; cols %d;" % [terminal.rows, terminal.cols])
|
||||||
|
terminal.write("h")
|
||||||
|
yield(get_tree().create_timer(1), "timeout")
|
||||||
|
terminal.write(" i")
|
29
test/scenes/basic.tscn
Normal file
29
test/scenes/basic.tscn
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gd" type="Script" id=2]
|
||||||
|
[ext_resource path="res://test/scenes/basic.gd" type="Script" id=3]
|
||||||
|
|
||||||
|
[node name="BasicNewTerm" type="Control"]
|
||||||
|
margin_right = 43.0
|
||||||
|
margin_bottom = 43.0
|
||||||
|
script = ExtResource( 3 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="ColorRect" type="ColorRect" parent="."]
|
||||||
|
margin_right = 40.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
color = Color( 0, 1, 0, 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Terminal" type="Control" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
focus_mode = 1
|
||||||
|
script = ExtResource( 2 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
21
test/scenes/basic_fullscreen.tscn
Normal file
21
test/scenes/basic_fullscreen.tscn
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gd" type="Script" id=1]
|
||||||
|
[ext_resource path="res://test/scenes/basic.gd" type="Script" id=2]
|
||||||
|
|
||||||
|
[node name="BasicNewTerm" type="Control"]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
script = ExtResource( 2 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Terminal" type="Control" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
focus_mode = 1
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
Loading…
Reference in a new issue