Add HTML5 support

This commit is contained in:
Leroy Hopson 2021-06-07 13:53:43 +07:00 committed by Leroy Hopson
parent fbb23661d3
commit bb8d40df58
18 changed files with 284 additions and 31 deletions

View file

@ -11,17 +11,21 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
platform: [ linux, osx, windows ]
platform: [ linux, javascript, osx, windows ]
target: [ release, debug ]
bits: [ 64, 32 ]
include:
- platform: linux
os: ubuntu-latest
- platform: javascript
os: ubuntu-latest
- platform: osx
os: macos-latest
- platform: windows
os: windows-latest
exclude:
- platform: javascript
bits: 64 # Currently only wasm32 is supported.
- platform: osx
bits: 32 # Only 64-bit supported on macOS.
- platform: windows
@ -45,12 +49,28 @@ jobs:
with:
path: addons/godot_xterm/native/external/godot-cpp
key: godot-cpp-${{ matrix.platform }}-${{ matrix.target }}-${{ matrix.bits }}-${{ env.GODOT_CPP_COMMIT_HASH }}
- name: Cache emscripten
if: ${{ matrix.platform == 'javascript' }}
uses: actions/cache@v2
env:
cache-name: cache-emscripten
with:
path: addons/godot_xterm/native/.emcache
key: emsdk-cache-${{ matrix.target }}
# Ubuntu-specific steps.
- name: Install ubuntu build dependencies
if: ${{ matrix.os == 'ubuntu-latest' }}
run: sudo apt-get install -y scons gcc-multilib g++-multilib
# JavaScript-specific steps.
- name: Install javascript build dependencies
if: ${{ matrix.platform == 'javascript' }}
uses: mymindstorm/setup-emsdk@v9
with:
version: 2.0.10
actions-cache-folder: emsdk-cache-${{ matrix.target }}
# MacOS-specific steps.
- name: Install additional macos build dependencies
if: ${{ matrix.os == 'macos-latest' }}
@ -80,6 +100,7 @@ jobs:
name: libgodot-xterm-${{ matrix.target }}
path: |
addons/godot_xterm/native/bin/*.so
addons/godot_xterm/native/bin/*.wasm
addons/godot_xterm/native/bin/*.dylib
addons/godot_xterm/native/bin/*.dll

4
.gitignore vendored
View file

@ -2,6 +2,7 @@
.import/
export.cfg
export_presets.cfg
export/
# Mono-specific ignores
.mono/
@ -9,3 +10,6 @@ data_*/
# Python-specific ignores
.venv
# Emscripten-specific ignores
.emcache/

View file

@ -7,10 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- HTML5 Support.
- Theme colors now support transparency.
- More themes: base16_dark, base16_light, default_dark, default_light, soft_black, solarized, solarized_black, solarized_white.
### Changed
- Supported Godot version -> 3.3.2.
- Set a default theme if no theme property has been set.
### Fixed

View file

@ -2,7 +2,7 @@
# GodotXterm
![Godot Version](https://img.shields.io/badge/godot-3.2+-blue.svg)
![Godot Version](https://img.shields.io/badge/godot-3.3+-blue.svg)
![License](https://img.shields.io/badge/license-MIT-green.svg)
[![Build Status](https://github.com/lihop/godot-xterm/workflows/build/badge.svg?branch=master)](https://github.com/lihop/godot-xterm/actions?query=branch%3Amaster)
@ -16,6 +16,7 @@ Terminal emulator for Godot using GDNative and [libtsm](https://github.com/Aetf/
### Confirmed:
- Linux 64-bit (primarily developed/tested on this platform)
- HTML5 (also tested on this platform)
- Linux 32-bit
- MacOS 64-bit
- Windows 64-bit
@ -23,6 +24,14 @@ Terminal emulator for Godot using GDNative and [libtsm](https://github.com/Aetf/
### Planned/untested:
- Windows 32-bit
## Demo
- An online demo of an HTML5 export of this repo (using the GDNative export type, available since Godot 3.3) can be found [here](https://demo.godot-xterm.nix.nz).
Make sure you click the screen at least once after loading.
- A Linux, MacOS, and Windows demo, which uses GodotXterm in conjunction with [Godot Python](https://github.com/touilleMan/godot-python) can be downloaded [here](https://lihop.itch.io/xterminate).
## Setup
@ -36,6 +45,7 @@ Precompiled binaries can be downloaded from the GitHub releases page. Download t
After extracting the zip file, the directory should contain the following:
- `libgodot-xterm.linux.64.so`
- `libgodot-xterm.linux.32.so`
- `libgodot-xterm.javascript.32.wasm`
- `libgodot-xterm.osx.64.dylib`
- `libgodot-xterm.windows.64.dll`
@ -44,6 +54,7 @@ This plugin follows the standard format of a GDNative plugin as shown in [GDNati
Therefore, referring to the following documentation on compiling a GDNative plugin and compiling the Godot engine itself may be useful:
- [GDNative C++ Example: Compiling the plugin](https://docs.godotengine.org/en/stable/tutorials/plugins/gdnative/gdnative-cpp-example.html#compiling-the-plugin).
- [Compiling for X11 (Linux, \*BSD)](https://docs.godotengine.org/en/stable/development/compiling/compiling_for_x11.html)
- [Compiling for the Web](https://docs.godotengine.org/en/3.3/development/compiling/compiling_for_web.html)
- [Compiling for macOS](https://docs.godotengine.org/en/stable/development/compiling/compiling_for_osx.html)
- [Compiling for Windows](https://docs.godotengine.org/en/stable/development/compiling/compiling_for_windows.html)
@ -53,6 +64,7 @@ The main dependencies are:
- [Git](https://git-scm.com/) (to clone git submodules)
- [SCons](https://scons.org/) (a software construction tool)
- A C/C++ compiler (i.e. [gcc](https://gcc.gnu.org/), [llvm](https://llvm.org/), [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus/))
- [Emscripten](https://emscripten.org/) version 2.0.10 (for HTML5 build)
**Note**: If you are cross-compiling, then you will likely need other dependencies than those listed below.
@ -80,6 +92,12 @@ choco install python3 && python -m pip install scons
#### Steps
The [build.sh] script in `addons/godot_xterm/native` is provided for convenience and can be used on Linux to perform the steps below using the default scons options. On NixOS it will use the `shell.nix` file in the same directory to bring in all dependencies to the build environment.
A Dockerfile is also provided to build for the javascript platform.
It can be run (after cloning Git submodules) with:
```
UID_GID="$(id -u):$(id -g)" docker-compose run javascript-build
```
The 'wasm' binary will be placed in the `bin` directory alongside others.
##### 1. Clone Git Submodules
This step only needs to be performed once and will clone the git repos this plugin depends on to `addons/godot_xterm/native/external`.
@ -95,7 +113,7 @@ This step only needs to be performed once per platform/target/bits combination y
cd addons/godot_xterm/native/external/godot-cpp
scons platform=<platform> target=<target> bits=<bits> generate_bindings=yes
```
Where `<platform>` is one of `linux`, `osx` or `windows`, `<target>` is one of `release` or `debug` and `<bits>` is one of `32` or `64`.
Where `<platform>` is one of `linux`, `javascript`, `osx` or `windows`, `<target>` is one of `release` or `debug` and `<bits>` is one of `32` or `64`.
The `generate_bindings=yes` option is essential.
##### 3. Build GodotXterm
@ -147,7 +165,7 @@ There are three example scenes included in this project which you can study to l
If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license.
You are also implicitly verifying that all code is your original work, or unoriginal work which is published under a compatible license or waiver.
Copyright (c) 2020 [The GodotXterm authors](https://github.com/lihop/godot-xterm/graphs/contributors) (MIT License)<br>
Copyright (c) 2020-2021 Leroy Hopson and [Contributors](https://github.com/lihop/godot-xterm/graphs/contributors) (MIT License)<br>
The fonts used in this project are published under a seperate license.
See the various license files in the [subdirectories](addons/godot_xterm/themes/fonts/) for each font.

View file

@ -46,7 +46,7 @@ opts.Add(EnumVariable(
'platform',
'Target platform',
host_platform,
allowed_values=('linux', 'osx', 'windows'),
allowed_values=('linux', 'javascript', 'osx', 'windows'),
ignorecase=2
))
opts.Add(EnumVariable(
@ -76,6 +76,7 @@ if env['platform'] == 'linux':
env['CC'] = 'gcc'
env['CXX'] = 'g++'
env['LIBSUFFIX'] = '.a'
env.Append(CCFLAGS=['-fPIC', '-Wwrite-strings'])
env.Append(LINKFLAGS=["-Wl,-R'$$ORIGIN'"])
@ -92,6 +93,28 @@ if env['platform'] == 'linux':
env.Append(CCFLAGS=['-m32'])
env.Append(LINKFLAGS=['-m32'])
# Compile for HTML5.
elif env['platform'] == 'javascript':
env.Append(CPPDEFINES=['PLATFORM_JS'])
env['bits'] = '32'
env['CC'] = 'emcc'
env['CXX'] = 'em++'
env['AR'] = 'emar'
env['RANLIB'] = 'emranlib'
env.Append(CPPFLAGS=['-s', 'SIDE_MODULE=1'])
env.Append(LINKFLAGS=['-s', 'SIDE_MODULE=1'])
env['SHOBJSUFFIX'] = '.bc'
env['SHLIBSUFFIX'] = '.wasm'
env['OBJPREFIX'] = ''
env['OBJSUFFIX'] = '.bc'
env['PROGPREFIX'] = ''
env['PROGSUFFIX'] = ''
env['LIBSUFFIX'] = '.bc'
env['LIBPREFIXES'] = ['$LIBPREFIX']
env['LIBSUFFIXES'] = ['$LIBSUFFIX']
env.Replace(SHLINKFLAGS='$LINKFLAGS')
env.Replace(SHLINKFLAGS='$LINKFLAGS')
# Compile for OSX.
elif env['platform'] == 'osx':
@ -99,6 +122,7 @@ elif env['platform'] == 'osx':
env['CC'] = 'clang'
env['CXX'] = 'clang++'
env['LIBSUFFIX'] = '.a'
if env['bits'] == '32':
raise ValueError(
@ -121,6 +145,7 @@ elif env['platform'] == 'osx':
# Compile for Windows.
elif env['platform'] == 'windows':
env.Append(CPPDEFINES=['PLATFORM_WINDOWS'])
env['LIBSUFFIX'] = '.lib'
# On Windows using MSVC.
if host_platform == 'windows':
@ -202,33 +227,42 @@ env.Append(LIBPATH=[
'external/godot-cpp/bin/',
'external/libtsm/build/bin/',
])
env.Append(LIBS=[
'libgodot-cpp.{}.{}.{}'.format(
env.File('external/godot-cpp/bin/libgodot-cpp.{}.{}.{}{}'.format(
env['platform'],
env['target'],
env['bits']
),
'libtsm.{}.{}.{}'.format(
'wasm' if env['platform'] == 'javascript' else env['bits'],
env['LIBSUFFIX'],
)),
env.File('external/libtsm/build/bin/libtsm.{}.{}.{}{}'.format(
env['platform'],
env['target'],
env['bits']
),
env['bits'],
env['LIBSUFFIX'],
)),
])
sources = []
sources.append('src/libgodotxtermnative.cpp')
sources.append('src/terminal.cpp')
# Psuedoterminal not supported on windows yet.
if env['platform'] != 'windows':
# Psuedoterminal not supported on windows (yet) or HTML5.
if env['platform'] != 'windows' and env['platform'] != 'javascript':
sources.append('src/pseudoterminal.cpp')
env.Append(LIBS=['util'])
if env['platform'] == 'linux':
libsuffix = "a"
suffix = "so"
elif env['platform'] == 'javascript':
libsuffix = "bc"
suffix = "wasm"
elif env['platform'] == 'windows':
libsuffix = "lib"
suffix = "dll"
elif env['platform'] == 'osx':
libsuffix = "a"
suffix = "dylib"
library = env.SharedLibrary(

View file

@ -33,3 +33,7 @@ scons generate_bindings=yes -j$(nproc)
# Build libgodot-xterm.
cd ${NATIVE_DIR}
scons -j$(nproc)
echo "To Build libgodot-xterm for javascript platform inside a docker container, run:"
echo 'UID_GID="$(id -u):$(id -g)" docker-compose run javascript-build'

View file

@ -0,0 +1,6 @@
services:
javascript-build:
build: misc/docker
user: ${UID_GID}
volumes:
- .:/godot

@ -1 +1 @@
Subproject commit b79a3b98b28e18bf981e2e2bd0b60faa23ef85f2
Subproject commit b8eebe5eb61f1b22d2d829cccef264138b106cdb

View file

@ -7,18 +7,18 @@ reloadable=true
[entry]
Server.64="res://addons/godot_xterm/native/bin/libgodot-xterm.linux.64.so"
X11.64="res://addons/godot_xterm/native/bin/libgodot-xterm.linux.64.so"
X11.32="res://addons/godot_xterm/native/bin/libgodot-xterm.linux.32.so"
HTML5.wasm32="res://addons/godot_xterm/native/bin/libgodot-xterm.javascript.32.wasm"
OSX.64="res://addons/godot_xterm/native/bin/libgodot-xterm.osx.64.dylib"
Windows.64="res://addons/godot_xterm/native/bin/libgodot-xterm.windows.64.dll"
Windows.32="res://addons/godot_xterm/native/bin/libgodot-xterm.windows.32.dll"
OSX.64="res://addons/godot_xterm/native/bin/libgodot-xterm.osx.64.dylib"
X11.64="res://addons/godot_xterm/native/bin/libgodot-xterm.linux.64.so"
X11.32="res://addons/godot_xterm/native/bin/libgodot-xterm.linux.32.so"
[dependencies]
Server.64=[ ]
X11.64=[ ]
X11.32=[ ]
HTML5.wasm32=[ ]
OSX.64=[ ]
Windows.64=[ ]
Windows.32=[ ]
OSX.64=[ ]
X11.64=[ ]
X11.32=[ ]

View file

View file

@ -0,0 +1,12 @@
FROM ponders/godot-cpp-ci
RUN git clone https://github.com/emscripten-core/emsdk.git
WORKDIR emsdk
RUN ./emsdk install 2.0.10
RUN ./emsdk activate 2.0.10
RUN chmod -R 777 /emsdk
WORKDIR /godot
RUN apt-get update && apt-get install -y vim
COPY ./build.sh /build.sh
RUN chmod +x /build.sh
CMD /build.sh

View file

@ -0,0 +1,7 @@
#!/bin/bash
source /emsdk/emsdk_env.sh
export EM_CACHE=/godot/.emcache
cd /godot/external/godot-cpp
scons platform=javascript -j$(nproc)
cd /godot
scons platform=javascript -j$(nproc)

View file

@ -11,6 +11,35 @@ const CURSOR_LEFT = "\u001b[D"
const DEFAULT_FOREGROUND_COLOR = "\u001b[0m"
class ANSIColor:
extends Object
# Using ANSIColor constants, rather than Color will respect the
# colors of the selected terminal theme. Whereas Color will set
# the exact color specified regardless of theme.
const black = { fg = 30, bg = 40 }
const red = {fg = 31, bg = 41 }
const green = {fg = 32, bg = 42}
const yellow = {fg = 33, bg = 43}
const blue = {fg = 34, bg = 44}
const magenta = {fg = 35, bg = 45}
const cyan = {fg = 36, bg = 46}
const white = {fg = 37, bg = 47}
const bright_black = {fg = 90, bg = 100}
const gray = bright_black
const grey = bright_black
const bright_red = {fg = 91, bg = 101}
const bright_green = {fg = 92, bg = 102}
const bright_yellow = {fg = 93, bg = 103}
const bright_blue = {fg = 94, bg = 104}
const bright_magenta = {fg = 95, bg = 105}
const bright_cyan = {fg = 96, bg = 106}
const bright_white = {fg = 97, bg = 107}
func _init():
assert(false, "ANSIColor is an abstract class. You should only use the color constants (e.g. ANSIColor.black).")
var terminal
@ -44,14 +73,22 @@ func cup(row: int = 0, col: int = 0) -> void:
terminal.write("\u001b[%d;%dH" % [row, col])
func setaf(color: Color) -> void:
var fg = "\u001b[38;2;%d;%d;%dm" % [color.r8, color.g8, color.b8]
terminal.write(fg)
func setaf(color) -> void:
if color is Color:
terminal.write("\u001b[38;2;%d;%d;%dm" % [color.r8, color.g8, color.b8])
elif "fg" in color and color.fg is int:
terminal.write("\u001b[%dm" % color.fg)
else:
push_error("Invalid color: %s" % color)
func setab(color: Color) -> void:
var bg = "\u001b[48;2;%d;%d;%dm" % [color.r8, color.g8, color.b8]
terminal.write(bg)
func setab(color) -> void:
if color is Color:
terminal.write("\u001b[48;2;%d;%d;%dm" % [color.r8, color.g8, color.b8])
elif "bg" in color and color.bg is int:
terminal.write("\u001b[%dm" % color.bg)
else:
push_error("Invalid color: %s" % color)
func rev() -> void:

View file

@ -18,7 +18,7 @@ const TITLE_WIDTH = 42
var menu_items := [
{"name": "Asciicast", "scene": preload("../asciicast/asciicast.tscn")},
{"name": "Terminal", "scene": preload("../terminal/terminal.tscn")},
{"name": "Terminal", "scene": preload("../web_console/web_console.tscn") if OS.has_feature("JavaScript") else preload("../terminal/terminal.tscn")},
{"name": "Exit"}
]
@ -140,15 +140,19 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
"Terminal not Supported on Windows"
)
var scene = item.scene.instance()
var pty = scene.get_node("Pseudoterminal")
var pty = scene if OS.has_feature("JavaScript") else scene.get_node("Pseudoterminal")
get_tree().get_root().add_child(scene)
visible = false
scene.grab_focus()
print("await the exit!")
yield(pty, "exited")
print("a got here")
visible = true
$Terminal.grab_focus()
scene.queue_free()
"Exit":
if OS.has_feature("JavaScript"):
JavaScript.eval("window.location.reload()")
get_tree().quit()

View file

@ -18,6 +18,7 @@ cols = 102
[node name="Pseudoterminal" type="Node" parent="."]
script = ExtResource( 2 )
[connection signal="data_sent" from="." to="Pseudoterminal" method="write"]
[connection signal="size_changed" from="." to="Pseudoterminal" method="resize"]
[connection signal="data_sent" from="Pseudoterminal" to="." method="write"]

View file

@ -0,0 +1,76 @@
extends Node
signal exited(status)
var line := ""
var _tput: TPut
onready var terminal = $Terminal
onready var _has_js: bool = OS.has_feature("JavaScript")
func prompt(prompt: String):
_tput.setaf(TPut.ANSIColor.bright_green)
terminal.write(prompt)
_tput.sgr0()
func _ready():
_tput = TPut.new(terminal)
_tput.setaf(TPut.ANSIColor.bright_cyan)
terminal.write("*** Web Console ***\r\n\r\n")
_tput.setaf(TPut.ANSIColor.yellow)
terminal.write("Input will be passed to Godot's JavaScript.eval() function.\r\n")
terminal.write("Some things you could try:\r\n\r\n")
terminal.write(">> 1 + 1\r\n")
terminal.write(">> window.location\r\n")
terminal.write(">> alert('Hello')\r\n\r\n")
_tput.setaf(TPut.ANSIColor.bright_cyan)
terminal.write("Enter 'q', 'quit', or 'exit' to return to the previous menu.\r\n\r\n")
_tput.sgr0()
prompt(">> ")
terminal.grab_focus()
func grab_focus():
terminal.grab_focus()
func _on_Terminal_key_pressed(_data, event: InputEventKey):
if not event:
return
# For some reason, data String is malformed on HTML5, so only use event.unicode.
var data = char(event.unicode)
match event.scancode:
KEY_ENTER:
terminal.write("\r\n")
if line == "q" or line == "quit" or line == "exit":
return emit_signal("exited", 0)
if not _has_js:
var msg := "WebConsole only available in HTML5 build."
push_error(msg)
_tput.setaf(TPut.ANSIColor.red)
terminal.write(msg)
_tput.sgr0()
prompt("\r\n>> ")
else:
var json = JavaScript.eval("JSON.stringify(%s)" % line, true)
_tput.setaf(TPut.ANSIColor.magenta)
terminal.write(str(json))
_tput.sgr0()
line = ""
#_tput.srg0()
prompt("\r\n>> ")
KEY_BACKSPACE:
if line.length() > 0:
terminal.write("\b \b")
line = line.substr(0, line.length() - 1)
_:
line += data
terminal.write(data)

View file

@ -0,0 +1,27 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/godot_xterm/themes/default_dark.theme" type="Theme" id=1]
[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]
[node name="WebConsole" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 3 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Terminal" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
focus_mode = 2
theme = ExtResource( 1 )
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
rows = 31
cols = 102
[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"]

View file

@ -30,7 +30,7 @@ window/vsync/use_vsync=false
[editor_plugins]
enabled=PoolStringArray( "godot_xterm" )
enabled=PoolStringArray( "res://addons/godot_xterm/plugin.cfg" )
[rendering]