mirror of
https://github.com/lihop/godot-xterm.git
synced 2025-05-04 20:24:23 +02:00
Add/update more files
This commit is contained in:
parent
3307231b65
commit
0769592a1b
44 changed files with 4188 additions and 362 deletions
127
addons/godot_xterm/buffer/attribute_data.gd
Normal file
127
addons/godot_xterm/buffer/attribute_data.gd
Normal file
|
@ -0,0 +1,127 @@
|
|||
# Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const Constants = preload("res://addons/godot_xterm/buffer/constants.gd")
|
||||
const Attributes = Constants.Attributes
|
||||
const FgFlags = Constants.FgFlags
|
||||
const BgFlags = Constants.BgFlags
|
||||
const UnderlineStyle = Constants.UnderlineStyle
|
||||
|
||||
var fg = 0
|
||||
var bg = 0
|
||||
var extended = ExtendedAttrs.new()
|
||||
|
||||
|
||||
# flags
|
||||
func is_inverse() -> int:
|
||||
return fg & FgFlags.INVERSE
|
||||
func is_bold() -> int:
|
||||
return fg & FgFlags.BOLD
|
||||
func is_underline() -> int:
|
||||
return fg & FgFlags.UNDERLINE
|
||||
func is_blink() -> int:
|
||||
return fg & FgFlags.BLINK
|
||||
func is_invisible() -> int:
|
||||
return fg & FgFlags.INVISIBLE
|
||||
func is_italic() -> int:
|
||||
return fg & BgFlags.ITALIC
|
||||
func is_dim() -> int:
|
||||
return fg & BgFlags.DIM
|
||||
|
||||
|
||||
# color modes
|
||||
func get_fg_color_mode() -> int:
|
||||
return fg & Attributes.CM_MASK
|
||||
func get_bg_color_mode() -> int:
|
||||
return bg & Attributes.CM_MASK
|
||||
func is_fg_rgb() -> bool:
|
||||
return (fg & Attributes.CM_MASK) == Attributes.CM_RGB
|
||||
func is_bg_rgb() -> bool:
|
||||
return (bg & Attributes.CM_MASK) == Attributes.CM_RGB
|
||||
func is_fg_palette() -> bool:
|
||||
return (fg & Attributes.CM_MASK) == Attributes.CM_P16 or (fg & Attributes.CM_MASK) == Attributes.CM_P256
|
||||
func is_bg_palette() -> bool:
|
||||
return (bg & Attributes.CM_MASK) == Attributes.CM_P16 or (bg & Attributes.CM_MASK) == Attributes.CM_P256
|
||||
func is_fg_default() -> bool:
|
||||
return (fg & Attributes.CM_MASK) == 0
|
||||
func is_bg_default() -> bool:
|
||||
return (bg & Attributes.CM_MASK) == 0
|
||||
func is_attribute_default() -> bool:
|
||||
return fg == 0 && bg == 0
|
||||
|
||||
|
||||
func get_fg_color() -> int:
|
||||
match fg & Attributes.CM_MASK:
|
||||
Attributes.CM_P16, Attributes.CM_P256:
|
||||
return fg & Attributes.PCOLOR_MASK
|
||||
Attributes.CM_RGB:
|
||||
return fg & Attributes.RGB_MASK
|
||||
_:
|
||||
return -1
|
||||
|
||||
|
||||
func has_extended_attrs() -> int:
|
||||
return bg & BgFlags.HAS_EXTENDED
|
||||
|
||||
|
||||
func get_underline_color() -> int:
|
||||
if bg & BgFlags.HAS_EXTENDED and ~extended.underline_color:
|
||||
match extended.underline_color & Attributes.CM_MASK:
|
||||
Attributes.CM_P16, Attributes.CM_P256:
|
||||
return extended.underline_color & Attributes.PCOLOR_MASK
|
||||
Attributes.CM_RGB:
|
||||
return extended.underline_color & Attributes.RGB_MASK
|
||||
_:
|
||||
return get_fg_color()
|
||||
else:
|
||||
return get_fg_color()
|
||||
|
||||
|
||||
func get_underline_color_mode() -> int:
|
||||
if bg & BgFlags.HAS_EXTENDED and ~extended.underline_color:
|
||||
return extended.underline_color & Attributes.CM_MASK
|
||||
else:
|
||||
return get_fg_color_mode()
|
||||
|
||||
|
||||
func is_underline_color_rgb() -> bool:
|
||||
if bg & BgFlags.HAS_EXTENDED and ~extended.underline_color:
|
||||
return extended.underline_color & Attributes.CM_MASK == Attributes.CM_RGB
|
||||
else:
|
||||
return is_fg_rgb()
|
||||
|
||||
|
||||
func is_underline_color_palette() -> bool:
|
||||
if bg & BgFlags.HAS_EXTENDED and ~extended.underline_color:
|
||||
return extended.underline_color & Attributes.CM_MASK == Attributes.CM_P16 \
|
||||
or extended.underline_color & Attributes.CM_MASK == Attributes.CM_P256
|
||||
else:
|
||||
return is_fg_palette()
|
||||
|
||||
|
||||
func is_underline_color_default() -> bool:
|
||||
if bg & BgFlags.HAS_EXTENDED and ~extended.underline_color:
|
||||
return extended.underline_color & Attributes.CM_MASK == 0
|
||||
else:
|
||||
return is_fg_default()
|
||||
|
||||
|
||||
func get_underline_style():
|
||||
if fg & FgFlags.UNDERLINE:
|
||||
return extended.underline_style if bg & BgFlags.HAS_EXTENDED else UnderlineStyle.SINGLE
|
||||
else:
|
||||
return UnderlineStyle.NONE
|
||||
|
||||
|
||||
class ExtendedAttrs:
|
||||
|
||||
|
||||
var underline_style = UnderlineStyle.NONE
|
||||
var underline_color: int = -1
|
||||
|
||||
|
||||
func _init():
|
||||
underline_style
|
141
addons/godot_xterm/buffer/buffer.gd
Normal file
141
addons/godot_xterm/buffer/buffer.gd
Normal file
|
@ -0,0 +1,141 @@
|
|||
# Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const BufferLine = preload("res://addons/godot_xterm/buffer/buffer_line.gd")
|
||||
const CellData = preload("res://addons/godot_xterm/buffer/cell_data.gd")
|
||||
const Charsets = preload("res://addons/godot_xterm/data/charsets.gd")
|
||||
const Constants = preload("res://addons/godot_xterm/buffer/constants.gd")
|
||||
const CircularList = preload("res://addons/godot_xterm/circular_list.gd")
|
||||
const AttributeData = preload("res://addons/godot_xterm/buffer/attribute_data.gd")
|
||||
|
||||
|
||||
const MAX_BUFFER_SIZE = 4294967295 # 2^32 - 1
|
||||
|
||||
var lines
|
||||
var ydisp: int = 0
|
||||
var ybase: int = 0
|
||||
var y: int = 0
|
||||
var x: int = 0
|
||||
var scroll_bottom: int
|
||||
var scroll_top: int
|
||||
var tabs = {}
|
||||
var saved_y: int = 0
|
||||
var saved_x: int = 0
|
||||
var saved_cur_attr_data = AttributeData.new()
|
||||
var saved_charset = Charsets.DEFAULT_CHARSET
|
||||
var markers: Array = []
|
||||
|
||||
var _null_cell = CellData.from_char_data([0, Constants.NULL_CELL_CHAR,
|
||||
Constants.NULL_CELL_WIDTH, Constants.NULL_CELL_CODE])
|
||||
var _whitespace_cell = CellData.from_char_data([0, Constants.WHITESPACE_CELL_CHAR,
|
||||
Constants.WHITESPACE_CELL_WIDTH, Constants.WHITESPACE_CELL_CODE])
|
||||
var _cols: int
|
||||
var _rows: int
|
||||
var _has_scrollback
|
||||
var _options_service
|
||||
var _buffer_service
|
||||
|
||||
|
||||
func _init(has_scrollback: bool, options_service, buffer_service):
|
||||
_has_scrollback = has_scrollback
|
||||
_options_service = options_service
|
||||
_buffer_service = buffer_service
|
||||
_cols = buffer_service.cols
|
||||
_rows = buffer_service.rows
|
||||
lines = CircularList.new(_get_correct_buffer_length(_rows))
|
||||
scroll_top = 0
|
||||
scroll_bottom = _rows - 1
|
||||
setup_tab_stops()
|
||||
|
||||
|
||||
func get_null_cell(attr = null):
|
||||
if attr:
|
||||
_null_cell.fg = attr.fg
|
||||
_null_cell.bg = attr.bg
|
||||
_null_cell.extended = attr.extended
|
||||
else:
|
||||
_null_cell.fg = 0
|
||||
_null_cell.bg = 0
|
||||
_null_cell.extended = AttributeData.ExtendedAttrs.new()
|
||||
return _null_cell
|
||||
|
||||
|
||||
func get_blank_line(attr, is_wrapped: bool = false):
|
||||
return BufferLine.new(_buffer_service.cols, get_null_cell(attr), is_wrapped)
|
||||
|
||||
|
||||
func _get_correct_buffer_length(rows: int) -> int:
|
||||
if not _has_scrollback:
|
||||
return rows
|
||||
else:
|
||||
var correct_buffer_length = rows + _options_service.options.scrollback
|
||||
return correct_buffer_length if correct_buffer_length < MAX_BUFFER_SIZE else MAX_BUFFER_SIZE
|
||||
|
||||
|
||||
# Fills the viewport with blank lines.
|
||||
func fill_viewport_rows(fill_attr = null) -> void:
|
||||
if lines.length == 0:
|
||||
if not fill_attr:
|
||||
fill_attr = AttributeData.new()
|
||||
var i = _rows
|
||||
while i:
|
||||
lines.push(get_blank_line(fill_attr))
|
||||
i -= 1
|
||||
|
||||
|
||||
|
||||
# Clears the buffer to it's initial state, discarding all previous data.
|
||||
func clear() -> void:
|
||||
ydisp = 0
|
||||
ybase = 0
|
||||
y = 0
|
||||
x = 0
|
||||
lines = CircularList.new(_get_correct_buffer_length(_rows))
|
||||
scroll_top = 0
|
||||
scroll_bottom = _rows - 1
|
||||
setup_tab_stops()
|
||||
|
||||
|
||||
func get_wrapped_range_for_line(y: int) -> Dictionary:
|
||||
var first = y
|
||||
var last = y
|
||||
# Scan upwards for wrapped lines
|
||||
while first > 0 and lines.get_el(first).is_wrapped:
|
||||
first -= 1
|
||||
# Scan downwards for wrapped lines
|
||||
while last + 1 < lines.length and lines.get_el(last + 1).is_wrapped:
|
||||
last += 1
|
||||
return {"first": first, "last": last}
|
||||
|
||||
|
||||
func setup_tab_stops(i = null) -> void:
|
||||
if i == null:
|
||||
return
|
||||
|
||||
if not tabs.get(i):
|
||||
i = prev_stop(i)
|
||||
else:
|
||||
tabs = {}
|
||||
i = 0
|
||||
|
||||
while i < _cols:
|
||||
tabs[i] = true
|
||||
i += _options_service.options.tab_stop_width
|
||||
|
||||
|
||||
func prev_stop(x: int) -> int:
|
||||
if x == null:
|
||||
x = self.x
|
||||
|
||||
while not tabs.get(x - 1, false) and x - 1 > 0:
|
||||
x - 1
|
||||
|
||||
return _cols - 1 if x > _cols else 0 if x < 0 else x
|
||||
|
||||
|
||||
|
||||
|
||||
|
247
addons/godot_xterm/buffer/buffer_line.gd
Normal file
247
addons/godot_xterm/buffer/buffer_line.gd
Normal file
|
@ -0,0 +1,247 @@
|
|||
# Copyright (c) 2018 The xterm.js authors. All rights reserved
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const AttributeData = preload("res://addons/godot_xterm/buffer/attribute_data.gd")
|
||||
const CellData = preload("res://addons/godot_xterm/buffer/cell_data.gd")
|
||||
const Constants = preload("res://addons/godot_xterm/buffer/constants.gd")
|
||||
const Content = Constants.Content
|
||||
const BgFlags = Constants.BgFlags
|
||||
|
||||
const CELL_SIZE = 3
|
||||
|
||||
enum Cell {
|
||||
CONTENT
|
||||
FG
|
||||
BG
|
||||
}
|
||||
|
||||
var _data: Array
|
||||
var _combined: Dictionary = {}
|
||||
var _extended_attrs: Dictionary = {}
|
||||
|
||||
var length: int
|
||||
var is_wrapped
|
||||
|
||||
func _init(cols: int, fill_cell_data = null, is_wrapped: bool = false):
|
||||
self.is_wrapped = is_wrapped
|
||||
_data = []
|
||||
_data.resize(cols * CELL_SIZE)
|
||||
var cell = fill_cell_data if fill_cell_data \
|
||||
else CellData.from_char_data([0, Constants.NULL_CELL_CHAR, Constants.NULL_CELL_WIDTH, Constants.NULL_CELL_CODE])
|
||||
for i in range(cols):
|
||||
set_cell(i, cell)
|
||||
length = cols
|
||||
|
||||
|
||||
func get_cell(index: int):
|
||||
return _data[index * CELL_SIZE + Cell.CONTENT]
|
||||
|
||||
|
||||
func get_width(index: int) -> int:
|
||||
return _data[index * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT
|
||||
|
||||
|
||||
func has_content(index: int) -> int:
|
||||
return _data[index * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK
|
||||
|
||||
|
||||
# Get codepoint of the cell.
|
||||
# To be in line with `code` in CharData this either returns
|
||||
# a single UTF32 codepoint or the last codepoint of a combined string.
|
||||
func get_codepoint(index: int) -> int:
|
||||
var content = _data[index * CELL_SIZE + Cell.CONTENT]
|
||||
if content & Content.IS_COMBINED_MASK:
|
||||
return _combined[index].ord_at(_combined[index].length() - 1)
|
||||
else:
|
||||
return content & Content.CODEPOINT_MASK
|
||||
|
||||
|
||||
func load_cell(index: int, cell):
|
||||
var start_index = index * CELL_SIZE
|
||||
cell.content = _data[start_index + Cell.CONTENT]
|
||||
cell.fg = _data[start_index + Cell.FG]
|
||||
cell.bg = _data[start_index + Cell.BG]
|
||||
if cell.content and cell.content & Content.IS_COMBINED_MASK:
|
||||
cell.combined_data = _combined[index]
|
||||
if cell.bg & BgFlags.HAS_EXTENDED:
|
||||
cell.extended = _extended_attrs[index]
|
||||
return cell
|
||||
|
||||
|
||||
func set_cell(index: int, cell) -> void:
|
||||
if cell.content & Content.IS_COMBINED_MASK:
|
||||
_combined[index] = cell.combined_data
|
||||
if cell.bg & BgFlags.HAS_EXTENDED:
|
||||
_extended_attrs[index] = cell.extended
|
||||
_data[index * CELL_SIZE + Cell.CONTENT] = cell.content
|
||||
_data[index * CELL_SIZE + Cell.FG] = cell.fg
|
||||
_data[index * CELL_SIZE + Cell.BG] = cell.bg
|
||||
|
||||
|
||||
func set_cell_from_codepoint(index: int, codepoint: int, width: int, fg: int, bg: int, e_attrs) -> void:
|
||||
if bg & BgFlags.HAS_EXTENDED:
|
||||
_extended_attrs[index] = e_attrs
|
||||
_data[index * CELL_SIZE + Cell.CONTENT] = codepoint | (width << Content.WIDTH_SHIFT)
|
||||
_data[index * CELL_SIZE + Cell.FG] = fg
|
||||
_data[index * CELL_SIZE + Cell.BG] = bg
|
||||
|
||||
|
||||
# Add a codepoint to a cell from input handler
|
||||
# During input stage combining chars with a width of 0 follow and stack
|
||||
# onto a leading char. Since we already set the attrs
|
||||
# by the previous `set_data_from_code_pont` call, we can omit it here.
|
||||
func add_codepoint_to_cell(index: int, codepoint: int) -> void:
|
||||
var content = _data[index * CELL_SIZE + Cell.CONTENT]
|
||||
if content & Content.IS_COMBINED_MASK:
|
||||
# we already have a combined string, simply add
|
||||
_combined[index] += char(codepoint)
|
||||
else:
|
||||
if content & Content.CODEPOINT_MASK:
|
||||
# normal case for combining chars:
|
||||
# - move current leading char + new one into combined string
|
||||
# - set combined flag
|
||||
_combined[index] = char(content & Content.CODEPOINT_MASK) + char(codepoint)
|
||||
content &= ~Content.CODEPOINT_MASK # set codepoint in buffer to 0
|
||||
content |= Content.IS_COMBINED_MASK
|
||||
else:
|
||||
# should not happen - we actually have no data in the cell yet
|
||||
# simply set the data in the cell buffer with a width of 1
|
||||
content = codepoint | (1 << Content.WIDTH_SHIFT)
|
||||
_data[index * CELL_SIZE + Cell.CONTENT] = content
|
||||
|
||||
|
||||
func insert_cells(pos: int, n: int, fill_cell_data, erase_attr = null) -> void:
|
||||
pos %= length
|
||||
|
||||
# handle fullwidth at pos: reset cell one to the left if pos is second cell of a wide char
|
||||
var fg = erase_attr.fg if erase_attr and erase_attr.fg else 0
|
||||
var bg = erase_attr.bg if erase_attr and erase_attr.bg else 0
|
||||
var extended = erase_attr.extended if erase_attr and erase_attr.extended else AttributeData.ExtendedAttrs.new()
|
||||
if pos and get_width(pos - 1) == 2:
|
||||
set_cell_from_codepoint(pos - 1, 0, 1, fg, bg, extended)
|
||||
|
||||
if n < length - pos:
|
||||
var cell = CellData.new()
|
||||
var i = length - pos - n - 1
|
||||
while i >= 0:
|
||||
set_cell(pos + n + i, load_cell(pos + i, cell))
|
||||
i -= 1
|
||||
for i in range(n):
|
||||
set_cell(pos + i, fill_cell_data)
|
||||
else:
|
||||
for i in range(pos, length):
|
||||
set_cell(i, fill_cell_data)
|
||||
|
||||
# handle fullwidth at line end: reset last cell if it is first cell of a wide char
|
||||
if get_width(length - 1) == 2:
|
||||
set_cell_from_codepoint(length - 1, 0, 1, fg, bg, extended)
|
||||
|
||||
|
||||
func delete_cells(pos: int, n: int, fill_cell_data, erase_attr = null) -> void:
|
||||
pos %= length
|
||||
if n < length - pos:
|
||||
var cell = CellData.new()
|
||||
for i in range(length - pos - n):
|
||||
set_cell(pos + i, load_cell(pos + n + i, cell))
|
||||
for i in range(length - n, length):
|
||||
set_cell(i, fill_cell_data)
|
||||
else:
|
||||
for i in range(pos, length):
|
||||
set_cell(i, fill_cell_data)
|
||||
|
||||
# handle fullwidth at pos:
|
||||
# - reset pos-1 if wide char
|
||||
# - reset pos if width==0 (previous second cell of a wide char)
|
||||
var fg = erase_attr.fg if erase_attr and erase_attr.fg else 0
|
||||
var bg = erase_attr.bg if erase_attr and erase_attr.bg else 0
|
||||
var extended = erase_attr.extended if erase_attr and erase_attr.extended else AttributeData.ExtendedAttrs.new()
|
||||
if pos and get_width(pos - 1) == 2:
|
||||
set_cell_from_codepoint(pos - 1, 0, 1, fg, bg, extended)
|
||||
if get_width(pos) == 0 and not has_content(pos):
|
||||
set_cell_from_codepoint(pos, 0, 1, fg, bg, extended)
|
||||
|
||||
|
||||
func replace_cells(start: int, end: int, fill_cell_data, erase_attr = null) -> void:
|
||||
var fg = erase_attr.fg if erase_attr and erase_attr.fg else 0
|
||||
var bg = erase_attr.bg if erase_attr and erase_attr.bg else 0
|
||||
var extended = erase_attr.extended if erase_attr and erase_attr.extended else AttributeData.ExtendedAttrs.new()
|
||||
|
||||
# handle fullwidth at start: reset cell one to left if start is second cell of a wide char
|
||||
if start and get_width(start - 1) == 2:
|
||||
set_cell_from_codepoint(start - 1, 0, 1, fg, bg, extended)
|
||||
# handle fullwidth at last cell + 1: reset to empty cell if it is second part of a wide char
|
||||
if end < length and get_width(end - 1) == 2:
|
||||
set_cell_from_codepoint(end, 0, 1, fg, bg, extended)
|
||||
|
||||
while start < end and start < length:
|
||||
set_cell(start, fill_cell_data)
|
||||
start += 1
|
||||
|
||||
|
||||
func resize(cols: int, fill_cell_data) -> void:
|
||||
if cols == length:
|
||||
return
|
||||
if cols > length:
|
||||
var data = []
|
||||
if length:
|
||||
if cols * CELL_SIZE < _data.size():
|
||||
data = _data.slice(0, cols * CELL_SIZE - 1)
|
||||
else:
|
||||
data = _data.duplicate()
|
||||
data.resize(cols * CELL_SIZE)
|
||||
_data = data
|
||||
var i = length
|
||||
while i < cols:
|
||||
set_cell(i, fill_cell_data)
|
||||
i += 1
|
||||
else:
|
||||
if cols:
|
||||
var data = []
|
||||
data = _data.slice(0, cols * CELL_SIZE - 1)
|
||||
data.resize(cols * CELL_SIZE)
|
||||
_data = data
|
||||
# Remove any cut off combined data, FIXME: repeat this for extended attrs
|
||||
for key in _combined.keys():
|
||||
if key as int > cols:
|
||||
_combined.erase(key)
|
||||
else:
|
||||
_data = []
|
||||
_combined = {}
|
||||
length = cols
|
||||
|
||||
|
||||
# Fill a line with `fill_cell_data`.
|
||||
func fill(fill_cell_data) -> void:
|
||||
_combined = {}
|
||||
_extended_attrs = {}
|
||||
for i in range(length):
|
||||
set_cell(i, fill_cell_data)
|
||||
|
||||
|
||||
func get_trimmed_length() -> int:
|
||||
for i in range(length - 1, 0, -1):
|
||||
if _data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK:
|
||||
return i + (_data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT)
|
||||
return 0
|
||||
|
||||
|
||||
func translate_to_string(trim_right: bool = false, start_col: int = 0, end_col: int = -1) -> String:
|
||||
if end_col == -1:
|
||||
end_col = length
|
||||
if trim_right:
|
||||
end_col = min(end_col, get_trimmed_length())
|
||||
var result = ""
|
||||
while start_col < end_col:
|
||||
var content = _data[start_col * CELL_SIZE + Cell.CONTENT]
|
||||
var cp = content & Content.CODEPOINT_MASK
|
||||
if content & Content.IS_COMBINED_MASK:
|
||||
result += _combined[start_col]
|
||||
elif cp:
|
||||
result += char(cp)
|
||||
else:
|
||||
result += Constants.WHITESPACE_CELL_CHAR
|
||||
start_col += max(content >> Content.WIDTH_SHIFT, 1) # always advance by 1
|
||||
return result
|
55
addons/godot_xterm/buffer/buffer_set.gd
Normal file
55
addons/godot_xterm/buffer/buffer_set.gd
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
const Buffer = preload("res://addons/godot_xterm/buffer/buffer.gd")
|
||||
|
||||
signal buffer_activated(active_buffer, inactive_buffer)
|
||||
|
||||
var normal
|
||||
var alt
|
||||
var active
|
||||
|
||||
|
||||
func _init(options_service, buffer_service):
|
||||
normal = Buffer.new(true, options_service, buffer_service)
|
||||
normal.fill_viewport_rows()
|
||||
|
||||
# The alt buffer should never have scrollback.
|
||||
# See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
alt = Buffer.new(false, options_service, buffer_service)
|
||||
active = normal
|
||||
|
||||
# TODO
|
||||
#setup_tab_stops()
|
||||
|
||||
|
||||
# Sets the normal Bufer of the BufferSet as its currently active Buffer.
|
||||
func activate_normal_buffer() -> void:
|
||||
if active == normal:
|
||||
return
|
||||
|
||||
normal.x = alt.x
|
||||
normal.y = alt.y
|
||||
|
||||
# The alt buffer should always be cleared when we switch to the normal
|
||||
# buffer. This frees up memory since the alt buffer should always be new
|
||||
# when activated.
|
||||
alt.clear()
|
||||
active = normal
|
||||
emit_signal("buffer_activated", normal, alt)
|
||||
|
||||
|
||||
# Sets the alt Buffer of the BufferSet as its currently active Buffer.
|
||||
func activate_alt_buffer(fill_attr = null) -> void:
|
||||
if active == alt:
|
||||
return
|
||||
|
||||
# Since the alt buffer is always cleared when the normal buffer is
|
||||
# activated, we want to fill it when switching to it.
|
||||
alt.fill_viewport_rows(fill_attr)
|
||||
alt.x = normal.x
|
||||
alt.y = normal.y
|
||||
active = alt
|
||||
emit_signal("buffer_activated", alt, normal)
|
68
addons/godot_xterm/buffer/cell_data.gd
Normal file
68
addons/godot_xterm/buffer/cell_data.gd
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends "res://addons/godot_xterm/buffer/attribute_data.gd"
|
||||
# CellData - represents a single cell in the terminal buffer.
|
||||
|
||||
|
||||
const Decoder = preload("res://addons/godot_xterm/input/text_decoder.gd")
|
||||
|
||||
const Content = Constants.Content
|
||||
|
||||
# Helper to create CellData from CharData
|
||||
static func from_char_data(value):
|
||||
# Workaround as per: https://github.com/godotengine/godot/issues/19345#issuecomment-471218401
|
||||
var char_data = load("res://addons/godot_xterm/buffer/cell_data.gd").new()
|
||||
char_data.set_from_char_data(value)
|
||||
return char_data
|
||||
|
||||
|
||||
# Primitives from terminal buffer
|
||||
var content = 0
|
||||
var combined_data = ''
|
||||
|
||||
|
||||
# Whether cell contains a combined string
|
||||
func is_combined() -> int:
|
||||
return content & Content.IS_COMBINED_MASK
|
||||
|
||||
|
||||
func get_width() -> int:
|
||||
return content >> Content.WIDTH_SHIFT
|
||||
|
||||
|
||||
func get_chars() -> String:
|
||||
if content & Content.IS_COMBINED_MASK:
|
||||
return combined_data
|
||||
elif content & Content.CODEPOINT_MASK:
|
||||
return char(content & Content.CODEPOINT_MASK)
|
||||
else:
|
||||
return Constants.NULL_CELL_CHAR
|
||||
|
||||
func get_code() -> int:
|
||||
if is_combined():
|
||||
return combined_data.ord_at(combined_data.length() - 1)
|
||||
else:
|
||||
return content & Content.CODEPOINT_MASK
|
||||
|
||||
|
||||
func set_from_char_data(value) -> void:
|
||||
var attr: int = value[Constants.CHAR_DATA_ATTR_INDEX]
|
||||
var character: String = value[Constants.CHAR_DATA_CHAR_INDEX]
|
||||
var width: int = value[Constants.CHAR_DATA_WIDTH_INDEX]
|
||||
var code: int = value[Constants.CHAR_DATA_CODE_INDEX]
|
||||
|
||||
fg = attr
|
||||
bg = 0
|
||||
# combined strings need special treatment. Javascript uses utf16 for strings
|
||||
# whereas Godot uses utf8, therefore we don't need any of the special
|
||||
# handling of surrogates in the original xterm.js code.
|
||||
if character.length() >= 2:
|
||||
combined_data = character
|
||||
content = Content.IS_COMBINED_MASK | (width << Content.WIDTH_SHIFT)
|
||||
else:
|
||||
content = (character.ord_at(0) if character.length() else 0) | (width << Content.WIDTH_SHIFT)
|
||||
|
||||
|
||||
func get_as_char_data():
|
||||
return [fg, get_chars(), get_width(), get_code()]
|
93
addons/godot_xterm/buffer/constants.gd
Normal file
93
addons/godot_xterm/buffer/constants.gd
Normal file
|
@ -0,0 +1,93 @@
|
|||
# Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const DEFAULT_COLOR = 256
|
||||
const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0)
|
||||
|
||||
const CHAR_DATA_ATTR_INDEX = 0
|
||||
const CHAR_DATA_CHAR_INDEX = 1
|
||||
const CHAR_DATA_WIDTH_INDEX = 2
|
||||
const CHAR_DATA_CODE_INDEX = 3
|
||||
|
||||
# Null cell - a real empty cell (containing nothing).
|
||||
# Note that code should always be 0 for a null cell as
|
||||
# several test condition of the buffer line rely on this.
|
||||
const NULL_CELL_CHAR = ''
|
||||
const NULL_CELL_WIDTH = 1
|
||||
const NULL_CELL_CODE = 0
|
||||
|
||||
|
||||
# Whitespace cell.
|
||||
# This is meant as a replacement for empty cells when needed
|
||||
# during rendering lines to preserve correct alignment.
|
||||
const WHITESPACE_CELL_CHAR = ' '
|
||||
const WHITESPACE_CELL_WIDTH = 1
|
||||
const WHITESPACE_CELL_CODE = 32
|
||||
|
||||
|
||||
# Bitmasks for accessing data in `content`.
|
||||
enum Content {
|
||||
CODEPOINT_MASK = 0x1FFFFF
|
||||
IS_COMBINED_MASK = 0x200000
|
||||
HAS_CONTENT_MASK = 0x3FFFFF
|
||||
WIDTH_MASK = 0xC00000
|
||||
WIDTH_SHIFT = 22
|
||||
}
|
||||
|
||||
|
||||
enum Attributes {
|
||||
# bit 1..8 blue in RGB, color in P256 and P16
|
||||
BLUE_MASK = 0xFF
|
||||
BLUE_SHIFT = 0
|
||||
PCOLOR_MASK = 0xFF
|
||||
PCOLOR_SHIFT = 0
|
||||
|
||||
# bit 9..16 green in RGB
|
||||
GREEN_MASK = 0xFF00
|
||||
GREEN_SHIFT = 8
|
||||
|
||||
# bit 17..24 red in RGB
|
||||
RED_MASK = 0xFF0000
|
||||
RED_SHIFT = 16
|
||||
|
||||
# bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3)
|
||||
CM_MASK = 0x3000000
|
||||
CM_DEFAULT = 0
|
||||
CM_P16 = 0x1000000
|
||||
CM_P256 = 0x2000000
|
||||
CM_RGB = 0x3000000
|
||||
|
||||
# bit 1..24 RGB room
|
||||
RGB_MASK = 0xFFFFFF
|
||||
}
|
||||
|
||||
|
||||
enum FgFlags {
|
||||
# bit 27..31 (32th bit unused)
|
||||
INVERSE = 0x4000000
|
||||
BOLD = 0x8000000
|
||||
UNDERLINE = 0x10000000
|
||||
BLINK = 0x20000000
|
||||
INVISIBLE = 0x40000000
|
||||
}
|
||||
|
||||
|
||||
enum BgFlags {
|
||||
# bit 27..32 (upper 3 unused)
|
||||
ITALIC = 0x4000000
|
||||
DIM = 0x8000000
|
||||
HAS_EXTENDED = 0x10000000
|
||||
}
|
||||
|
||||
|
||||
enum UnderlineStyle {
|
||||
NONE
|
||||
SINGLE
|
||||
DOUBLE
|
||||
CURLY
|
||||
DOTTED
|
||||
DASHED
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue