mirror of
https://github.com/gdamore/tcell.git
synced 2025-04-24 13:48:51 +08:00
created wscreen.go
updated readme
This commit is contained in:
parent
497e90a4fe
commit
edb6434493
54
README-wasm.md
Normal file
54
README-wasm.md
Normal file
@ -0,0 +1,54 @@
|
||||
# WASM for _Tcell_
|
||||
|
||||
You can build _Tcell_ project into a webpage by compiling it slightly differently. This will result in a _Tcell_ project you can embed into another html page, or use as a standalone page.
|
||||
|
||||
## Building your project
|
||||
|
||||
WASM needs special build flags in order to work. You can build it by executing
|
||||
```sh
|
||||
GOOS=js GOARCH=wasm go build -o yourfile.wasm
|
||||
```
|
||||
|
||||
## Additional files
|
||||
|
||||
You also need 5 other files in the same directory as the wasm. Four (`tcell.html`, `tcell.js`, `termstyle.css`, and `beep.wav`) are provided in the `webfiles` directory. The last one, `wasm_exec.js`, can be copied from GOROOT into the current directory by executing
|
||||
```sh
|
||||
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./
|
||||
```
|
||||
|
||||
In `tcell.js`, you also need to change the constant
|
||||
```js
|
||||
const wasmFilePath = "yourfile.wasm"
|
||||
```
|
||||
to the file you outputed to when building.
|
||||
|
||||
## Displaying your project
|
||||
|
||||
### Standalone
|
||||
|
||||
You can see the project (with an white background around the terminal) by serving the directory. You can do this using any framework, including another golang project:
|
||||
|
||||
```golang
|
||||
// server.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Fatal(http.ListenAndServe(":8080",
|
||||
http.FileServer(http.Dir("/path/to/dir/to/serve"))
|
||||
))
|
||||
}
|
||||
```
|
||||
|
||||
To see the webpage with this example, you can type in `localhost:8080/tcell.html` into your browser while `server.go` is running.
|
||||
|
||||
### Embedding
|
||||
It is recomended to use an iframe if you want to embed the app into a webpage:
|
||||
```html
|
||||
<iframe src="tcell.html" title="Tcell app"></iframe>
|
||||
```
|
14
README.md
14
README.md
@ -265,18 +265,8 @@ Modern console applications like ConEmu and the Windows 10 terminal,
|
||||
support all the good features (resize, mouse tracking, etc.)
|
||||
|
||||
### WASM
|
||||
WASM needs special build flags and extra files in the same directory in order to work. You can build it by executing
|
||||
```sh
|
||||
GOOS=js GOARCH=wasm build -o yourfile.wasm
|
||||
```
|
||||
You also need 5 other files. Four (`tcell.html`, `tcell.js`, `termstyle.css`, and `beep.wav`) are provided in the `webfiles` directory. The last one, `wasm_exec.js`, can be copied from GOROOT into the current directory by executing
|
||||
```sh
|
||||
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./
|
||||
```
|
||||
It is recomended use an iframe if you want to embed the app into a webpage:
|
||||
```html
|
||||
<iframe src="tcell.html" title="Tcell app"></iframe>
|
||||
```
|
||||
|
||||
WASM is supported, but needs additional setup detailed in [README-wasm](README-wasm.md).
|
||||
|
||||
### Plan9 and others
|
||||
|
||||
|
1649
tscreen.go
1649
tscreen.go
File diff suppressed because it is too large
Load Diff
1665
tscreen_term.go
1665
tscreen_term.go
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
const wasmFilePath = "main.wasm"
|
||||
const term = document.getElementById("terminal")
|
||||
var width = 80; var height = 24
|
||||
const beepAudio = new Audio("beep.wav");
|
||||
@ -192,6 +193,6 @@ document.addEventListener("paste", e => {
|
||||
});
|
||||
|
||||
const go = new Go();
|
||||
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
|
||||
WebAssembly.instantiateStreaming(fetch(wasmFilePath), go.importObject).then((result) => {
|
||||
go.run(result.instance);
|
||||
});
|
@ -26,26 +26,24 @@ import (
|
||||
)
|
||||
|
||||
func NewTerminfoScreen() (Screen, error) {
|
||||
t := &tScreen{}
|
||||
t := &wScreen{}
|
||||
t.fallback = make(map[rune]string)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
type tScreen struct {
|
||||
type wScreen struct {
|
||||
w, h int
|
||||
style Style
|
||||
cells CellBuffer
|
||||
|
||||
running bool
|
||||
fini bool // dummy var; html doesn't need to get restored or "shut down"
|
||||
clear bool
|
||||
flagsPresent bool
|
||||
pasteEnabled bool
|
||||
mouseFlags MouseFlags
|
||||
|
||||
cursorStyle CursorStyle
|
||||
cx, cy int // dummies so web tScreen can use generic tScreen.Sync
|
||||
|
||||
quit chan struct{}
|
||||
evch chan Event
|
||||
@ -54,14 +52,13 @@ type tScreen struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (t *tScreen) Init() error {
|
||||
func (t *wScreen) Init() error {
|
||||
t.w, t.h = 80, 24 // default for html as of now
|
||||
t.evch = make(chan Event, 10)
|
||||
t.quit = make(chan struct{})
|
||||
|
||||
t.Lock()
|
||||
t.running = true
|
||||
t.cx, t.cy = -1, -1
|
||||
t.style = StyleDefault
|
||||
t.cells.Resize(t.w, t.h)
|
||||
t.Unlock()
|
||||
@ -71,11 +68,48 @@ func (t *tScreen) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tScreen) Fini() {
|
||||
func (t *wScreen) Fini() {
|
||||
close(t.quit)
|
||||
}
|
||||
|
||||
func (t *tScreen) drawCell(x, y int) int {
|
||||
func (t *wScreen) SetStyle(style Style) {
|
||||
t.Lock()
|
||||
t.style = style
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) Clear() {
|
||||
t.Fill(' ', t.style)
|
||||
}
|
||||
|
||||
func (t *wScreen) Fill(r rune, style Style) {
|
||||
t.Lock()
|
||||
t.cells.Fill(r, style)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) {
|
||||
t.Lock()
|
||||
t.cells.SetContent(x, y, mainc, combc, style)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) GetContent(x, y int) (rune, []rune, Style, int) {
|
||||
t.Lock()
|
||||
mainc, combc, style, width := t.cells.GetContent(x, y)
|
||||
t.Unlock()
|
||||
return mainc, combc, style, width
|
||||
}
|
||||
|
||||
func (t *wScreen) SetCell(x, y int, style Style, ch ...rune) {
|
||||
if len(ch) > 0 {
|
||||
t.SetContent(x, y, ch[0], ch[1:], style)
|
||||
} else {
|
||||
t.SetContent(x, y, ' ', nil, style)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *wScreen) drawCell(x, y int) int {
|
||||
mainc, combc, style, width := t.cells.GetContent(x, y)
|
||||
|
||||
if !t.cells.Dirty(x, y) {
|
||||
@ -99,24 +133,35 @@ func (t *tScreen) drawCell(x, y int) int {
|
||||
return width
|
||||
}
|
||||
|
||||
func (t *tScreen) ShowCursor(x, y int) {
|
||||
func (t *wScreen) ShowCursor(x, y int) {
|
||||
t.Lock()
|
||||
js.Global().Call("showCursor", x, y)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) SetCursorStyle(cs CursorStyle) {
|
||||
func (t *wScreen) SetCursorStyle(cs CursorStyle) {
|
||||
t.Lock()
|
||||
js.Global().Call("setCursorStyle", curStyleClasses[cs])
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) clearScreen() {
|
||||
func (t *wScreen) HideCursor() {
|
||||
t.ShowCursor(-1, -1)
|
||||
}
|
||||
|
||||
func (t *wScreen) Show() {
|
||||
t.Lock()
|
||||
t.resize()
|
||||
t.draw()
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) clearScreen() {
|
||||
js.Global().Call("clearScreen", t.style.fg.Hex(), t.style.bg.Hex())
|
||||
t.clear = false
|
||||
}
|
||||
|
||||
func (t *tScreen) draw() {
|
||||
func (t *wScreen) draw() {
|
||||
if t.clear {
|
||||
t.clearScreen()
|
||||
}
|
||||
@ -131,7 +176,24 @@ func (t *tScreen) draw() {
|
||||
js.Global().Call("show")
|
||||
}
|
||||
|
||||
func (t *tScreen) enableMouse(f MouseFlags) {
|
||||
func (t *wScreen) EnableMouse(flags ...MouseFlags) {
|
||||
var f MouseFlags
|
||||
flagsPresent := false
|
||||
for _, flag := range flags {
|
||||
f |= flag
|
||||
flagsPresent = true
|
||||
}
|
||||
if !flagsPresent {
|
||||
f = MouseMotionEvents | MouseDragEvents | MouseButtonEvents
|
||||
}
|
||||
|
||||
t.Lock()
|
||||
t.mouseFlags = f
|
||||
t.enableMouse(f)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) enableMouse(f MouseFlags) {
|
||||
if f&MouseButtonEvents != 0 {
|
||||
js.Global().Set("onMouseClick", js.FuncOf(t.onMouseEvent))
|
||||
} else {
|
||||
@ -145,7 +207,28 @@ func (t *tScreen) enableMouse(f MouseFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tScreen) enablePasting(on bool) {
|
||||
func (t *wScreen) DisableMouse() {
|
||||
t.Lock()
|
||||
t.mouseFlags = 0
|
||||
t.enableMouse(0)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) EnablePaste() {
|
||||
t.Lock()
|
||||
t.pasteEnabled = true
|
||||
t.enablePasting(true)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) DisablePaste() {
|
||||
t.Lock()
|
||||
t.pasteEnabled = false
|
||||
t.enablePasting(false)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) enablePasting(on bool) {
|
||||
if on {
|
||||
js.Global().Set("onPaste", js.FuncOf(t.onPaste))
|
||||
} else {
|
||||
@ -153,15 +236,85 @@ func (t *tScreen) enablePasting(on bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *wScreen) Size() (int, int) {
|
||||
t.Lock()
|
||||
w, h := t.w, t.h
|
||||
t.Unlock()
|
||||
return w, h
|
||||
}
|
||||
|
||||
// resize does nothing, as asking the web window to resize
|
||||
// without a specified width or height will cause no change.
|
||||
func (t *tScreen) resize() {}
|
||||
func (t *wScreen) resize() {}
|
||||
|
||||
func (t *tScreen) Colors() int {
|
||||
func (t *wScreen) Colors() int {
|
||||
return 16777216 // 256 ^ 3
|
||||
}
|
||||
|
||||
func (t *tScreen) onMouseEvent(this js.Value, args []js.Value) interface{} {
|
||||
func (t *wScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) {
|
||||
defer close(ch)
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
return
|
||||
case <-t.quit:
|
||||
return
|
||||
case ev := <-t.evch:
|
||||
select {
|
||||
case <-quit:
|
||||
return
|
||||
case <-t.quit:
|
||||
return
|
||||
case ch <- ev:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *wScreen) PollEvent() Event {
|
||||
select {
|
||||
case <-t.quit:
|
||||
return nil
|
||||
case ev := <-t.evch:
|
||||
return ev
|
||||
}
|
||||
}
|
||||
|
||||
func (t *wScreen) HasPendingEvent() bool {
|
||||
return len(t.evch) > 0
|
||||
}
|
||||
|
||||
func (t *wScreen) PostEventWait(ev Event) {
|
||||
t.evch <- ev
|
||||
}
|
||||
|
||||
func (t *wScreen) PostEvent(ev Event) error {
|
||||
select {
|
||||
case t.evch <- ev:
|
||||
return nil
|
||||
default:
|
||||
return ErrEventQFull
|
||||
}
|
||||
}
|
||||
|
||||
func (t *wScreen) clip(x, y int) (int, int) {
|
||||
w, h := t.cells.Size()
|
||||
if x < 0 {
|
||||
x = 0
|
||||
}
|
||||
if y < 0 {
|
||||
y = 0
|
||||
}
|
||||
if x > w-1 {
|
||||
x = w - 1
|
||||
}
|
||||
if y > h-1 {
|
||||
y = h - 1
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (t *wScreen) onMouseEvent(this js.Value, args []js.Value) interface{} {
|
||||
mod := ModNone
|
||||
button := ButtonNone
|
||||
|
||||
@ -196,7 +349,7 @@ func (t *tScreen) onMouseEvent(this js.Value, args []js.Value) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tScreen) onKeyEvent(this js.Value, args []js.Value) interface{} {
|
||||
func (t *wScreen) onKeyEvent(this js.Value, args []js.Value) interface{} {
|
||||
key := args[0].String()
|
||||
|
||||
// don't accept any modifier keys as their own
|
||||
@ -241,7 +394,7 @@ func (t *tScreen) onKeyEvent(this js.Value, args []js.Value) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tScreen) onPaste(this js.Value, args []js.Value) interface{} {
|
||||
func (t *wScreen) onPaste(this js.Value, args []js.Value) interface{} {
|
||||
t.PostEventWait(NewEventPaste(args[0].Bool()))
|
||||
return nil
|
||||
}
|
||||
@ -250,15 +403,36 @@ func (t *tScreen) onPaste(this js.Value, args []js.Value) interface{} {
|
||||
// happen when javascript calls a function (for example, when
|
||||
// mouse input is disabled, when onMouseEvent() is called from
|
||||
// js, it redirects here and does nothing).
|
||||
func (t *tScreen) unset(this js.Value, args []js.Value) interface{} {
|
||||
func (t *wScreen) unset(this js.Value, args []js.Value) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tScreen) CharacterSet() string {
|
||||
func (t *wScreen) Sync() {
|
||||
t.Lock()
|
||||
t.resize()
|
||||
t.clear = true
|
||||
t.cells.Invalidate()
|
||||
t.draw()
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) CharacterSet() string {
|
||||
return "UTF-8"
|
||||
}
|
||||
|
||||
func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
||||
func (t *wScreen) RegisterRuneFallback(orig rune, fallback string) {
|
||||
t.Lock()
|
||||
t.fallback[orig] = fallback
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) UnregisterRuneFallback(orig rune) {
|
||||
t.Lock()
|
||||
delete(t.fallback, orig)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *wScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
||||
if utf8.ValidRune(r) {
|
||||
return true
|
||||
}
|
||||
@ -271,15 +445,15 @@ func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *tScreen) HasMouse() bool {
|
||||
func (t *wScreen) HasMouse() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *tScreen) HasKey(k Key) bool {
|
||||
func (t *wScreen) HasKey(k Key) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *tScreen) SetSize(w, h int) {
|
||||
func (t *wScreen) SetSize(w, h int) {
|
||||
if w == t.w && h == t.h {
|
||||
return
|
||||
}
|
||||
@ -291,9 +465,11 @@ func (t *tScreen) SetSize(w, h int) {
|
||||
t.PostEvent(NewEventResize(w, h))
|
||||
}
|
||||
|
||||
func (t *wScreen) Resize(int, int, int, int) {}
|
||||
|
||||
// Suspend simply pauses all input and output, and clears the screen.
|
||||
// There isn't a "default terminal" to go back to.
|
||||
func (t *tScreen) Suspend() error {
|
||||
func (t *wScreen) Suspend() error {
|
||||
t.Lock()
|
||||
if !t.running {
|
||||
t.Unlock()
|
||||
@ -307,7 +483,7 @@ func (t *tScreen) Suspend() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tScreen) Resume() error {
|
||||
func (t *wScreen) Resume() error {
|
||||
t.Lock()
|
||||
|
||||
if t.running {
|
||||
@ -324,7 +500,7 @@ func (t *tScreen) Resume() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tScreen) Beep() error {
|
||||
func (t *wScreen) Beep() error {
|
||||
js.Global().Call("beep")
|
||||
return nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user