mirror of
https://github.com/gdamore/tcell.git
synced 2025-04-24 13:48:51 +08:00
added webfiles directory
updated README.md organized functions into original order
This commit is contained in:
parent
49cc9d21db
commit
b86f5127ac
18
README.md
18
README.md
@ -264,7 +264,21 @@ Windows console mode applications are supported.
|
|||||||
Modern console applications like ConEmu and the Windows 10 terminal,
|
Modern console applications like ConEmu and the Windows 10 terminal,
|
||||||
support all the good features (resize, mouse tracking, etc.)
|
support all the good features (resize, mouse tracking, etc.)
|
||||||
|
|
||||||
### Plan9, WASM, and others
|
### 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>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plan9 and others
|
||||||
|
|
||||||
These platforms won't work, but compilation stubs are supplied
|
These platforms won't work, but compilation stubs are supplied
|
||||||
for folks that want to include parts of this in software for those
|
for folks that want to include parts of this in software for those
|
||||||
@ -279,4 +293,4 @@ please let me know. PRs are especially welcome.
|
|||||||
_Tcell_ is absolutely free, but if you want to obtain commercial, professional support, there are options.
|
_Tcell_ is absolutely free, but if you want to obtain commercial, professional support, there are options.
|
||||||
|
|
||||||
- [TideLift](https://tidelift.com/) subscriptions include support for _Tcell_, as well as many other open source packages.
|
- [TideLift](https://tidelift.com/) subscriptions include support for _Tcell_, as well as many other open source packages.
|
||||||
- [Staysail Systems Inc.](mailto:info@staysail.tech) offers direct support, and custom development around _Tcell_ on an hourly basis.
|
- [Staysail Systems Inc.](mailto:info@staysail.tech) offers direct support, and custom development around _Tcell_ on an hourly basis.
|
62
tscreen.go
62
tscreen.go
@ -35,18 +35,6 @@ func (t *tScreen) SetStyle(style Style) {
|
|||||||
|
|
||||||
func (t *tScreen) Clear() {
|
func (t *tScreen) Clear() {
|
||||||
t.Fill(' ', t.style)
|
t.Fill(' ', t.style)
|
||||||
t.Lock()
|
|
||||||
t.clear = true
|
|
||||||
w, h := t.cells.Size()
|
|
||||||
// because we are going to clear (see t.clear) in the next cycle,
|
|
||||||
// let's also unmark the dirty bit so that we don't waste cycles
|
|
||||||
// drawing things that are already dealt with via the clear escape sequence.
|
|
||||||
for row := 0; row < h; row++ {
|
|
||||||
for col := 0; col < w; col++ {
|
|
||||||
t.cells.SetDirty(col, row, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) Fill(r rune, style Style) {
|
func (t *tScreen) Fill(r rune, style Style) {
|
||||||
@ -57,30 +45,6 @@ func (t *tScreen) Fill(r rune, style Style) {
|
|||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) 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 *tScreen) DisableMouse() {
|
|
||||||
t.Lock()
|
|
||||||
t.mouseFlags = 0
|
|
||||||
t.enableMouse(0)
|
|
||||||
t.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) {
|
func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
if !t.fini {
|
if !t.fini {
|
||||||
@ -117,6 +81,30 @@ func (t *tScreen) Show() {
|
|||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) 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 *tScreen) DisableMouse() {
|
||||||
|
t.Lock()
|
||||||
|
t.mouseFlags = 0
|
||||||
|
t.enableMouse(0)
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) EnablePaste() {
|
func (t *tScreen) EnablePaste() {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
t.pasteEnabled = true
|
t.pasteEnabled = true
|
||||||
@ -225,3 +213,5 @@ func (t *tScreen) UnregisterRuneFallback(orig rune) {
|
|||||||
delete(t.fallback, orig)
|
delete(t.fallback, orig)
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) Resize(int, int, int, int) {}
|
||||||
|
228
tscreen_term.go
228
tscreen_term.go
@ -544,6 +544,36 @@ func (t *tScreen) finish() {
|
|||||||
t.finalize()
|
t.finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) encodeRune(r rune, buf []byte) []byte {
|
||||||
|
|
||||||
|
nb := make([]byte, 6)
|
||||||
|
ob := make([]byte, 6)
|
||||||
|
num := utf8.EncodeRune(ob, r)
|
||||||
|
ob = ob[:num]
|
||||||
|
dst := 0
|
||||||
|
var err error
|
||||||
|
if enc := t.encoder; enc != nil {
|
||||||
|
enc.Reset()
|
||||||
|
dst, _, err = enc.Transform(nb, ob, true)
|
||||||
|
}
|
||||||
|
if err != nil || dst == 0 || nb[0] == '\x1a' {
|
||||||
|
// Combining characters are elided
|
||||||
|
if len(buf) == 0 {
|
||||||
|
if acs, ok := t.acs[r]; ok {
|
||||||
|
buf = append(buf, []byte(acs)...)
|
||||||
|
} else if fb, ok := t.fallback[r]; ok {
|
||||||
|
buf = append(buf, []byte(fb)...)
|
||||||
|
} else {
|
||||||
|
buf = append(buf, '?')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf = append(buf, nb[:dst]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) sendFgBg(fg Color, bg Color, attr AttrMask) AttrMask {
|
func (t *tScreen) sendFgBg(fg Color, bg Color, attr AttrMask) AttrMask {
|
||||||
ti := t.ti
|
ti := t.ti
|
||||||
if ti.Colors == 0 {
|
if ti.Colors == 0 {
|
||||||
@ -625,47 +655,6 @@ func (t *tScreen) sendFgBg(fg Color, bg Color, attr AttrMask) AttrMask {
|
|||||||
return attr
|
return attr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) draw() {
|
|
||||||
// clobber cursor position, because we're going to change it all
|
|
||||||
t.cx = -1
|
|
||||||
t.cy = -1
|
|
||||||
// make no style assumptions
|
|
||||||
t.curstyle = styleInvalid
|
|
||||||
|
|
||||||
t.buf.Reset()
|
|
||||||
t.buffering = true
|
|
||||||
defer func() {
|
|
||||||
t.buffering = false
|
|
||||||
}()
|
|
||||||
|
|
||||||
// hide the cursor while we move stuff around
|
|
||||||
t.hideCursor()
|
|
||||||
|
|
||||||
if t.clear {
|
|
||||||
t.clearScreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
for y := 0; y < t.h; y++ {
|
|
||||||
for x := 0; x < t.w; x++ {
|
|
||||||
width := t.drawCell(x, y)
|
|
||||||
if width > 1 {
|
|
||||||
if x+1 < t.w {
|
|
||||||
// this is necessary so that if we ever
|
|
||||||
// go back to drawing that cell, we
|
|
||||||
// actually will *draw* it.
|
|
||||||
t.cells.SetDirty(x+1, y, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x += width - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore the cursor
|
|
||||||
t.showCursor()
|
|
||||||
|
|
||||||
_, _ = t.buf.WriteTo(t.tty)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) drawCell(x, y int) int {
|
func (t *tScreen) drawCell(x, y int) int {
|
||||||
|
|
||||||
ti := t.ti
|
ti := t.ti
|
||||||
@ -780,6 +769,19 @@ func (t *tScreen) drawCell(x, y int) int {
|
|||||||
return width
|
return width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) ShowCursor(x, y int) {
|
||||||
|
t.Lock()
|
||||||
|
t.cursorx = x
|
||||||
|
t.cursory = y
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) SetCursorStyle(cs CursorStyle) {
|
||||||
|
t.Lock()
|
||||||
|
t.cursorStyle = cs
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) showCursor() {
|
func (t *tScreen) showCursor() {
|
||||||
|
|
||||||
x, y := t.cursorx, t.cursory
|
x, y := t.cursorx, t.cursory
|
||||||
@ -799,22 +801,6 @@ func (t *tScreen) showCursor() {
|
|||||||
t.cy = y
|
t.cy = y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) resize() {
|
|
||||||
if w, h, e := t.tty.WindowSize(); e == nil {
|
|
||||||
if w != t.w || h != t.h {
|
|
||||||
t.cx = -1
|
|
||||||
t.cy = -1
|
|
||||||
|
|
||||||
t.cells.Resize(w, h)
|
|
||||||
t.cells.Invalidate()
|
|
||||||
t.h = h
|
|
||||||
t.w = w
|
|
||||||
ev := NewEventResize(w, h)
|
|
||||||
_ = t.PostEvent(ev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeString sends a string to the terminal. The string is sent as-is and
|
// writeString sends a string to the terminal. The string is sent as-is and
|
||||||
// this function does not expand inline padding indications (of the form
|
// this function does not expand inline padding indications (of the form
|
||||||
// $<[delay]> where [delay] is msec). In order to have these expanded, use
|
// $<[delay]> where [delay] is msec). In order to have these expanded, use
|
||||||
@ -858,6 +844,47 @@ func (t *tScreen) hideCursor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) draw() {
|
||||||
|
// clobber cursor position, because we're going to change it all
|
||||||
|
t.cx = -1
|
||||||
|
t.cy = -1
|
||||||
|
// make no style assumptions
|
||||||
|
t.curstyle = styleInvalid
|
||||||
|
|
||||||
|
t.buf.Reset()
|
||||||
|
t.buffering = true
|
||||||
|
defer func() {
|
||||||
|
t.buffering = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
// hide the cursor while we move stuff around
|
||||||
|
t.hideCursor()
|
||||||
|
|
||||||
|
if t.clear {
|
||||||
|
t.clearScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y < t.h; y++ {
|
||||||
|
for x := 0; x < t.w; x++ {
|
||||||
|
width := t.drawCell(x, y)
|
||||||
|
if width > 1 {
|
||||||
|
if x+1 < t.w {
|
||||||
|
// this is necessary so that if we ever
|
||||||
|
// go back to drawing that cell, we
|
||||||
|
// actually will *draw* it.
|
||||||
|
t.cells.SetDirty(x+1, y, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x += width - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore the cursor
|
||||||
|
t.showCursor()
|
||||||
|
|
||||||
|
_, _ = t.buf.WriteTo(t.tty)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) enableMouse(f MouseFlags) {
|
func (t *tScreen) enableMouse(f MouseFlags) {
|
||||||
// Rather than using terminfo to find mouse escape sequences, we rely on the fact that
|
// Rather than using terminfo to find mouse escape sequences, we rely on the fact that
|
||||||
// pretty much *every* terminal that supports mouse tracking follows the
|
// pretty much *every* terminal that supports mouse tracking follows the
|
||||||
@ -893,6 +920,22 @@ func (t *tScreen) enablePasting(on bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) resize() {
|
||||||
|
if w, h, e := t.tty.WindowSize(); e == nil {
|
||||||
|
if w != t.w || h != t.h {
|
||||||
|
t.cx = -1
|
||||||
|
t.cy = -1
|
||||||
|
|
||||||
|
t.cells.Resize(w, h)
|
||||||
|
t.cells.Invalidate()
|
||||||
|
t.h = h
|
||||||
|
t.w = w
|
||||||
|
ev := NewEventResize(w, h)
|
||||||
|
_ = t.PostEvent(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) Colors() int {
|
func (t *tScreen) Colors() int {
|
||||||
// this doesn't change, no need for lock
|
// this doesn't change, no need for lock
|
||||||
if t.truecolor {
|
if t.truecolor {
|
||||||
@ -972,36 +1015,6 @@ func (t *tScreen) buildAcsMap() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) encodeRune(r rune, buf []byte) []byte {
|
|
||||||
|
|
||||||
nb := make([]byte, 6)
|
|
||||||
ob := make([]byte, 6)
|
|
||||||
num := utf8.EncodeRune(ob, r)
|
|
||||||
ob = ob[:num]
|
|
||||||
dst := 0
|
|
||||||
var err error
|
|
||||||
if enc := t.encoder; enc != nil {
|
|
||||||
enc.Reset()
|
|
||||||
dst, _, err = enc.Transform(nb, ob, true)
|
|
||||||
}
|
|
||||||
if err != nil || dst == 0 || nb[0] == '\x1a' {
|
|
||||||
// Combining characters are elided
|
|
||||||
if len(buf) == 0 {
|
|
||||||
if acs, ok := t.acs[r]; ok {
|
|
||||||
buf = append(buf, []byte(acs)...)
|
|
||||||
} else if fb, ok := t.fallback[r]; ok {
|
|
||||||
buf = append(buf, []byte(fb)...)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, '?')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf = append(buf, nb[:dst]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildMouseEvent returns an event based on the supplied coordinates and button
|
// buildMouseEvent returns an event based on the supplied coordinates and button
|
||||||
// state. Note that the screen's mouse button state is updated based on the
|
// state. Note that the screen's mouse button state is updated based on the
|
||||||
// input to this function (i.e. it mutates the receiver).
|
// input to this function (i.e. it mutates the receiver).
|
||||||
@ -1483,17 +1496,8 @@ func (t *tScreen) inputLoop(stopQ chan struct{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) ShowCursor(x, y int) {
|
func (t *tScreen) CharacterSet() string {
|
||||||
t.Lock()
|
return t.charset
|
||||||
t.cursorx = x
|
|
||||||
t.cursory = y
|
|
||||||
t.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) SetCursorStyle(cs CursorStyle) {
|
|
||||||
t.Lock()
|
|
||||||
t.cursorStyle = cs
|
|
||||||
t.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
||||||
@ -1523,6 +1527,17 @@ func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) HasMouse() bool {
|
||||||
|
return len(t.mouse) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) HasKey(k Key) bool {
|
||||||
|
if k == KeyRune {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return t.keyexist[k]
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) SetSize(w, h int) {
|
func (t *tScreen) SetSize(w, h int) {
|
||||||
if t.setWinSize != "" {
|
if t.setWinSize != "" {
|
||||||
t.TPuts(t.ti.TParm(t.setWinSize, w, h))
|
t.TPuts(t.ti.TParm(t.setWinSize, w, h))
|
||||||
@ -1540,17 +1555,6 @@ func (t *tScreen) Resume() error {
|
|||||||
return t.engage()
|
return t.engage()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) CharacterSet() string {
|
|
||||||
return t.charset
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) HasKey(k Key) bool {
|
|
||||||
if k == KeyRune {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return t.keyexist[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
// engage is used to place the terminal in raw mode and establish screen size, etc.
|
// engage is used to place the terminal in raw mode and establish screen size, etc.
|
||||||
// Think of this is as tcell "engaging" the clutch, as it's going to be driving the
|
// Think of this is as tcell "engaging" the clutch, as it's going to be driving the
|
||||||
// terminal interface.
|
// terminal interface.
|
||||||
@ -1633,12 +1637,6 @@ func (t *tScreen) disengage() {
|
|||||||
_ = t.tty.Stop()
|
_ = t.tty.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) HasMouse() bool {
|
|
||||||
return len(t.mouse) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) Resize(int, int, int, int) {}
|
|
||||||
|
|
||||||
// Beep emits a beep to the terminal.
|
// Beep emits a beep to the terminal.
|
||||||
func (t *tScreen) Beep() error {
|
func (t *tScreen) Beep() error {
|
||||||
t.writeString(string(byte(7)))
|
t.writeString(string(byte(7)))
|
||||||
|
340
tscreen_web.go
340
tscreen_web.go
@ -12,7 +12,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewTerminfoScreen() (Screen, error) {
|
func NewTerminfoScreen() (Screen, error) {
|
||||||
return &tScreen{}, nil
|
t := &tScreen{}
|
||||||
|
t.fallback = make(map[rune]string)
|
||||||
|
|
||||||
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type tScreen struct {
|
type tScreen struct {
|
||||||
@ -38,8 +41,7 @@ type tScreen struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) Init() error {
|
func (t *tScreen) Init() error {
|
||||||
t.w, t.h = 80, 24
|
t.w, t.h = 80, 24 // default for html as of now
|
||||||
t.fallback = make(map[rune]string)
|
|
||||||
t.evch = make(chan Event, 10)
|
t.evch = make(chan Event, 10)
|
||||||
t.quit = make(chan struct{})
|
t.quit = make(chan struct{})
|
||||||
|
|
||||||
@ -59,6 +61,127 @@ func (t *tScreen) Fini() {
|
|||||||
close(t.quit)
|
close(t.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) drawCell(x, y int) int {
|
||||||
|
mainc, combc, style, width := t.cells.GetContent(x, y)
|
||||||
|
|
||||||
|
if !t.cells.Dirty(x, y) {
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
if style == StyleDefault {
|
||||||
|
style = t.style
|
||||||
|
}
|
||||||
|
|
||||||
|
fg, bg := style.fg.Hex(), style.bg.Hex()
|
||||||
|
|
||||||
|
var combcarr []interface{} = make([]interface{}, len(combc))
|
||||||
|
for i, c := range combc {
|
||||||
|
combcarr[i] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
t.cells.SetDirty(x, y, false)
|
||||||
|
js.Global().Call("drawCell", x, y, mainc, combcarr, fg, bg, int(style.attrs))
|
||||||
|
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) ShowCursor(x, y int) {
|
||||||
|
t.Lock()
|
||||||
|
js.Global().Call("showCursor", x, y)
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) SetCursorStyle(cs CursorStyle) {
|
||||||
|
t.Lock()
|
||||||
|
js.Global().Call("setCursorStyle", curStyleClasses[cs])
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) clearScreen() {
|
||||||
|
js.Global().Call("clearScreen", t.style.fg.Hex(), t.style.bg.Hex())
|
||||||
|
t.clear = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) draw() {
|
||||||
|
if t.clear {
|
||||||
|
t.clearScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y < t.h; y++ {
|
||||||
|
for x := 0; x < t.w; x++ {
|
||||||
|
width := t.drawCell(x, y)
|
||||||
|
x += width - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
js.Global().Call("show")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) enableMouse(f MouseFlags) {
|
||||||
|
if f&MouseButtonEvents != 0 {
|
||||||
|
js.Global().Set("onMouseClick", js.FuncOf(t.onMouseEvent))
|
||||||
|
} else {
|
||||||
|
js.Global().Set("onMouseClick", js.FuncOf(t.unset))
|
||||||
|
}
|
||||||
|
|
||||||
|
if f&MouseDragEvents != 0 || f&MouseMotionEvents != 0 {
|
||||||
|
js.Global().Set("onMouseMove", js.FuncOf(t.onMouseEvent))
|
||||||
|
} else {
|
||||||
|
js.Global().Set("onMouseMove", js.FuncOf(t.unset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) enablePasting(on bool) {
|
||||||
|
if on {
|
||||||
|
js.Global().Set("onPaste", js.FuncOf(t.onPaste))
|
||||||
|
} else {
|
||||||
|
js.Global().Set("onPaste", js.FuncOf(t.unset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *tScreen) Colors() int {
|
||||||
|
return 16777216 // 256 ^ 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) onMouseEvent(this js.Value, args []js.Value) interface{} {
|
||||||
|
mod := ModNone
|
||||||
|
button := ButtonNone
|
||||||
|
|
||||||
|
switch args[2].Int() {
|
||||||
|
case 0:
|
||||||
|
if t.mouseFlags&MouseMotionEvents == 0 {
|
||||||
|
// don't want this event! is a mouse motion event, but user has asked not.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
button = ButtonNone
|
||||||
|
case 1:
|
||||||
|
button = Button1
|
||||||
|
case 2:
|
||||||
|
button = Button3 // Note we prefer to treat right as button 2
|
||||||
|
case 3:
|
||||||
|
button = Button2 // And the middle button as button 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[3].Bool() { // mod shift
|
||||||
|
mod |= ModShift
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[4].Bool() { // mod alt
|
||||||
|
mod |= ModAlt
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[5].Bool() { // mod ctrl
|
||||||
|
mod |= ModCtrl
|
||||||
|
}
|
||||||
|
|
||||||
|
t.PostEventWait(NewEventMouse(args[0].Int(), args[1].Int(), button, mod))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tScreen) onKeyEvent(this js.Value, args []js.Value) interface{} {
|
func (t *tScreen) onKeyEvent(this js.Value, args []js.Value) interface{} {
|
||||||
key := args[0].String()
|
key := args[0].String()
|
||||||
|
|
||||||
@ -109,128 +232,36 @@ func (t *tScreen) onPaste(this js.Value, args []js.Value) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) enableMouse(f MouseFlags) {
|
// unset is a dummy function for js when we want nothing to
|
||||||
if f&MouseButtonEvents != 0 {
|
// happen when javascript calls a function (for example, when
|
||||||
js.Global().Set("onMouseClick", js.FuncOf(t.onMouseEvent))
|
// mouse input is disabled, when onMouseEvent() is called from
|
||||||
} else {
|
// js, it redirects here and does nothing).
|
||||||
js.Global().Set("onMouseClick", js.FuncOf(t.unset))
|
|
||||||
}
|
|
||||||
|
|
||||||
if f&MouseDragEvents != 0 || f&MouseMotionEvents != 0 {
|
|
||||||
js.Global().Set("onMouseMove", js.FuncOf(t.onMouseEvent))
|
|
||||||
} else {
|
|
||||||
js.Global().Set("onMouseMove", js.FuncOf(t.unset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) onMouseEvent(this js.Value, args []js.Value) interface{} {
|
|
||||||
mod := ModNone
|
|
||||||
button := ButtonNone
|
|
||||||
|
|
||||||
switch args[2].Int() {
|
|
||||||
case 0:
|
|
||||||
if t.mouseFlags&MouseMotionEvents == 0 {
|
|
||||||
// don't want this event! is a mouse motion event, but user has asked not.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
button = ButtonNone
|
|
||||||
case 1:
|
|
||||||
button = Button1
|
|
||||||
case 2:
|
|
||||||
button = Button3 // Note we prefer to treat right as button 2
|
|
||||||
case 3:
|
|
||||||
button = Button2 // And the middle button as button 3
|
|
||||||
}
|
|
||||||
|
|
||||||
if args[3].Bool() { // mod shift
|
|
||||||
mod |= ModShift
|
|
||||||
}
|
|
||||||
|
|
||||||
if args[4].Bool() { // mod alt
|
|
||||||
mod |= ModAlt
|
|
||||||
}
|
|
||||||
|
|
||||||
if args[5].Bool() { // mod ctrl
|
|
||||||
mod |= ModCtrl
|
|
||||||
}
|
|
||||||
|
|
||||||
t.PostEventWait(NewEventMouse(args[0].Int(), args[1].Int(), button, mod))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) unset(this js.Value, args []js.Value) interface{} {
|
func (t *tScreen) unset(this js.Value, args []js.Value) interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) Colors() int {
|
|
||||||
return 16777216 // 256 ^ 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 *tScreen) draw() {
|
|
||||||
if t.clear {
|
|
||||||
t.clearScreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
for y := 0; y < t.h; y++ {
|
|
||||||
for x := 0; x < t.w; x++ {
|
|
||||||
width := t.drawCell(x, y)
|
|
||||||
x += width - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
js.Global().Call("show")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) clearScreen() {
|
|
||||||
js.Global().Call("clearScreen", t.style.fg.Hex(), t.style.bg.Hex())
|
|
||||||
t.clear = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) drawCell(x, y int) int {
|
|
||||||
mainc, combc, style, width := t.cells.GetContent(x, y)
|
|
||||||
|
|
||||||
if !t.cells.Dirty(x, y) {
|
|
||||||
return width
|
|
||||||
}
|
|
||||||
|
|
||||||
if style == StyleDefault {
|
|
||||||
style = t.style
|
|
||||||
}
|
|
||||||
|
|
||||||
fg, bg := style.fg.Hex(), style.bg.Hex()
|
|
||||||
|
|
||||||
var combcarr []interface{} = make([]interface{}, len(combc))
|
|
||||||
for i, c := range combc {
|
|
||||||
combcarr[i] = c
|
|
||||||
}
|
|
||||||
|
|
||||||
t.cells.SetDirty(x, y, false)
|
|
||||||
js.Global().Call("drawCell", x, y, mainc, combcarr, fg, bg, int(style.attrs))
|
|
||||||
|
|
||||||
return width
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) ShowCursor(x, y int) {
|
|
||||||
t.Lock()
|
|
||||||
js.Global().Call("showCursor", x, y)
|
|
||||||
t.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) SetCursorStyle(cs CursorStyle) {
|
|
||||||
t.Lock()
|
|
||||||
js.Global().Call("setCursorStyle", curStyleClasses[cs])
|
|
||||||
t.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) CharacterSet() string {
|
func (t *tScreen) CharacterSet() string {
|
||||||
return "UTF-8"
|
return "UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
||||||
|
if utf8.ValidRune(r) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !checkFallbacks {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := t.fallback[r]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) HasMouse() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tScreen) HasKey(k Key) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +278,7 @@ func (t *tScreen) SetSize(w, h int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suspend simply pauses all input and output, and clears the screen.
|
// 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 *tScreen) Suspend() error {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
if !t.running {
|
if !t.running {
|
||||||
@ -278,26 +310,6 @@ func (t *tScreen) Resume() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) HasKey(k Key) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) enablePasting(on bool) {
|
|
||||||
if on {
|
|
||||||
js.Global().Set("onPaste", js.FuncOf(t.onPaste))
|
|
||||||
} else {
|
|
||||||
js.Global().Set("onPaste", js.FuncOf(t.unset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) HasMouse() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) Resize(x int, y int, w int, h int) {
|
|
||||||
t.SetSize(w, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tScreen) Beep() error {
|
func (t *tScreen) Beep() error {
|
||||||
js.Global().Call("beep")
|
js.Global().Call("beep")
|
||||||
return nil
|
return nil
|
||||||
@ -396,34 +408,34 @@ var WebKeyNames = map[string]Key{
|
|||||||
"F62": KeyF62,
|
"F62": KeyF62,
|
||||||
"F63": KeyF63,
|
"F63": KeyF63,
|
||||||
"F64": KeyF64,
|
"F64": KeyF64,
|
||||||
"Ctrl-a": KeyCtrlA, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-a": KeyCtrlA, // not reported by HTML- need to do special check
|
||||||
"Ctrl-b": KeyCtrlB, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-b": KeyCtrlB, // not reported by HTML- need to do special check
|
||||||
"Ctrl-c": KeyCtrlC, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-c": KeyCtrlC, // not reported by HTML- need to do special check
|
||||||
"Ctrl-d": KeyCtrlD, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-d": KeyCtrlD, // not reported by HTML- need to do special check
|
||||||
"Ctrl-e": KeyCtrlE, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-e": KeyCtrlE, // not reported by HTML- need to do special check
|
||||||
"Ctrl-f": KeyCtrlF, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-f": KeyCtrlF, // not reported by HTML- need to do special check
|
||||||
"Ctrl-g": KeyCtrlG, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-g": KeyCtrlG, // not reported by HTML- need to do special check
|
||||||
"Ctrl-j": KeyCtrlJ, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-j": KeyCtrlJ, // not reported by HTML- need to do special check
|
||||||
"Ctrl-k": KeyCtrlK, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-k": KeyCtrlK, // not reported by HTML- need to do special check
|
||||||
"Ctrl-l": KeyCtrlL, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-l": KeyCtrlL, // not reported by HTML- need to do special check
|
||||||
"Ctrl-n": KeyCtrlN, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-n": KeyCtrlN, // not reported by HTML- need to do special check
|
||||||
"Ctrl-o": KeyCtrlO, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-o": KeyCtrlO, // not reported by HTML- need to do special check
|
||||||
"Ctrl-p": KeyCtrlP, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-p": KeyCtrlP, // not reported by HTML- need to do special check
|
||||||
"Ctrl-q": KeyCtrlQ, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-q": KeyCtrlQ, // not reported by HTML- need to do special check
|
||||||
"Ctrl-r": KeyCtrlR, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-r": KeyCtrlR, // not reported by HTML- need to do special check
|
||||||
"Ctrl-s": KeyCtrlS, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-s": KeyCtrlS, // not reported by HTML- need to do special check
|
||||||
"Ctrl-t": KeyCtrlT, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-t": KeyCtrlT, // not reported by HTML- need to do special check
|
||||||
"Ctrl-u": KeyCtrlU, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-u": KeyCtrlU, // not reported by HTML- need to do special check
|
||||||
"Ctrl-v": KeyCtrlV, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-v": KeyCtrlV, // not reported by HTML- need to do special check
|
||||||
"Ctrl-w": KeyCtrlW, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-w": KeyCtrlW, // not reported by HTML- need to do special check
|
||||||
"Ctrl-x": KeyCtrlX, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-x": KeyCtrlX, // not reported by HTML- need to do special check
|
||||||
"Ctrl-y": KeyCtrlY, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-y": KeyCtrlY, // not reported by HTML- need to do special check
|
||||||
"Ctrl-z": KeyCtrlZ, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-z": KeyCtrlZ, // not reported by HTML- need to do special check
|
||||||
"Ctrl- ": KeyCtrlSpace, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl- ": KeyCtrlSpace, // not reported by HTML- need to do special check
|
||||||
"Ctrl-_": KeyCtrlUnderscore, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-_": KeyCtrlUnderscore, // not reported by HTML- need to do special check
|
||||||
"Ctrl-]": KeyCtrlRightSq, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-]": KeyCtrlRightSq, // not reported by HTML- need to do special check
|
||||||
"Ctrl-\\": KeyCtrlBackslash, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-\\": KeyCtrlBackslash, // not reported by HTML- need to do special check
|
||||||
"Ctrl-^": KeyCtrlCarat, // not supported by HTML- reported as key + modifier ctrl
|
"Ctrl-^": KeyCtrlCarat, // not reported by HTML- need to do special check
|
||||||
}
|
}
|
||||||
|
|
||||||
var curStyleClasses = map[CursorStyle]string{
|
var curStyleClasses = map[CursorStyle]string{
|
||||||
|
BIN
webfiles/beep.wav
Normal file
BIN
webfiles/beep.wav
Normal file
Binary file not shown.
13
webfiles/tcell.html
Normal file
13
webfiles/tcell.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>Tcell</title>
|
||||||
|
<script src="wasm_exec.js"></script>
|
||||||
|
<link rel="stylesheet" href="termstyle.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="terminal"></pre>
|
||||||
|
<script src="tcell.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
183
webfiles/tcell.js
Normal file
183
webfiles/tcell.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
const term = document.getElementById("terminal")
|
||||||
|
var width = 80; var height = 24
|
||||||
|
const beepAudio = new Audio("beep.wav");
|
||||||
|
|
||||||
|
var cx = -1; var cy = -1
|
||||||
|
var cursorClass = "cursor-blinking-block"
|
||||||
|
|
||||||
|
var content // {data: row[height], dirty: bool}
|
||||||
|
// row = {data: element[width], previous: span}
|
||||||
|
// dirty/[previous being null] indicates if previous (or entire terminal) needs to be recaclulated.
|
||||||
|
// dirty is true/null if terminal/previous need to be re-calculated/shown
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
resize(width, height) // intialize content
|
||||||
|
show() // then show the screen
|
||||||
|
}
|
||||||
|
|
||||||
|
function resize(w, h) {
|
||||||
|
|
||||||
|
width = w
|
||||||
|
height = h
|
||||||
|
content = {data: new Array(height), dirty: true}
|
||||||
|
for (let i = 0; i < height; i++) {
|
||||||
|
content.data[i] = {data: new Array(width), previous: null}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearScreen(fg, bg) {
|
||||||
|
if (fg) { term.style.color = intToHex(fg) }
|
||||||
|
if (bg) { term.style.backgroundColor = intToHex(bg) }
|
||||||
|
|
||||||
|
content.dirty = true
|
||||||
|
for (let i = 0; i < height; i++) {
|
||||||
|
content.data[i].previous = null // we set the row to be recacluated later
|
||||||
|
for (let j = 0; j < width; j++) {
|
||||||
|
content.data[i].data[j] = document.createTextNode(" ") // set the entire row to spaces.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCell(x, y, mainc, combc, fg, bg, attrs) {
|
||||||
|
var combString = String.fromCharCode(mainc)
|
||||||
|
combc.forEach(char => {combString += String.fromCharCode(char)});
|
||||||
|
|
||||||
|
var span = document.createElement("span")
|
||||||
|
var use = false
|
||||||
|
|
||||||
|
if (fg) { span.style.color = intToHex(fg); use = true }
|
||||||
|
if (bg) { span.style.backgroundColor = intToHex(bg); use = true }
|
||||||
|
|
||||||
|
if (attrs != 0) {
|
||||||
|
use = true
|
||||||
|
if ((attrs & 1) != 0) { span.classList.add("bold") }
|
||||||
|
if ((attrs & (1<<1)) != 0) { span.classList.add("blink") }
|
||||||
|
if ((attrs & (1<<2)) != 0) { span.classList.add("reverse") }
|
||||||
|
if ((attrs & (1<<3)) != 0) { span.classList.add("underline") }
|
||||||
|
if ((attrs & (1<<4)) != 0) { span.classList.add("dim") }
|
||||||
|
if ((attrs & (1<<5)) != 0) { span.classList.add("italic") }
|
||||||
|
if ((attrs & (1<<6)) != 0) { span.classList.add("strikethrough") }
|
||||||
|
}
|
||||||
|
|
||||||
|
var textnode = document.createTextNode(combString)
|
||||||
|
span.appendChild(textnode)
|
||||||
|
|
||||||
|
content.dirty = true // invalidate terminal- new cell
|
||||||
|
content.data[y].previous = null // invalidate row- new row
|
||||||
|
content.data[y].data[x] = use ? span : textnode
|
||||||
|
}
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
if (!content.dirty) {
|
||||||
|
return // no new draws; no need to update
|
||||||
|
}
|
||||||
|
|
||||||
|
displayCursor()
|
||||||
|
|
||||||
|
term.innerHTML = ""
|
||||||
|
content.data.forEach(row => {
|
||||||
|
if (row.previous == null) {
|
||||||
|
row.previous = document.createElement("span")
|
||||||
|
row.data.forEach(c => {
|
||||||
|
row.previous.appendChild(c)
|
||||||
|
})
|
||||||
|
row.previous.appendChild(document.createTextNode("\n"))
|
||||||
|
}
|
||||||
|
term.appendChild(row.previous)
|
||||||
|
})
|
||||||
|
|
||||||
|
content.dirty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCursor(x, y) {
|
||||||
|
content.dirty = true
|
||||||
|
|
||||||
|
if (!(cx < 0 || cy < 0)) { // if original position is a valid cursor position
|
||||||
|
content.data[cy].previous = null;
|
||||||
|
if (content.data[cy].data[cx].classList) {
|
||||||
|
content.data[cy].data[cx].classList.remove(cursorClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx = x
|
||||||
|
cy = y
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayCursor() {
|
||||||
|
content.dirty = true
|
||||||
|
|
||||||
|
if (!(cx < 0 || cy < 0)) { // if new position is a valid cursor position
|
||||||
|
content.data[cy].previous = null;
|
||||||
|
|
||||||
|
if (!content.data[cy].data[cx].classList) {
|
||||||
|
var span = document.createElement("span")
|
||||||
|
span.appendChild(content.data[cy].data[cx])
|
||||||
|
content.data[cy].data[cx] = span
|
||||||
|
}
|
||||||
|
|
||||||
|
content.data[cy].data[cx].classList.add(cursorClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCursorStyle(newClass) {
|
||||||
|
if (newClass == cursorClass) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cx < 0 || cy < 0)) {
|
||||||
|
// mark cursor row as dirty; new class has been applied to (cx, cy)
|
||||||
|
content.dirty = true
|
||||||
|
content.data[cy].previous = null
|
||||||
|
|
||||||
|
if (content.data[cy].data[cx].classList) {
|
||||||
|
content.data[cy].data[cx].classList.remove(cursorClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding the new class will be dealt with when displayCursor() is called
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorClass = newClass
|
||||||
|
}
|
||||||
|
|
||||||
|
function beep() {
|
||||||
|
beepAudio.currentTime = 0;
|
||||||
|
beepAudio.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
function intToHex(n) {
|
||||||
|
return "#" + n.toString(16).padStart(6, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
let fontwidth = term.clientWidth / width
|
||||||
|
let fontheight = term.clientHeight / height
|
||||||
|
|
||||||
|
document.addEventListener("keydown", e => {
|
||||||
|
onKeyEvent(e.key, e.shiftKey, e.altKey, e.ctrlKey, e.metaKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
term.addEventListener("click", e => {
|
||||||
|
onMouseClick(Math.min((e.offsetX / fontwidth) | 0, width-1), Math.min((e.offsetY / fontheight) | 0, height-1), e.which, e.shiftKey, e.altKey, e.ctrlKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
term.addEventListener("mousemove", e => {
|
||||||
|
onMouseMove(Math.min((e.offsetX / fontwidth) | 0, width-1), Math.min((e.offsetY / fontheight) | 0, height-1), e.which, e.shiftKey, e.altKey, e.ctrlKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("paste", e => {
|
||||||
|
e.preventDefault();
|
||||||
|
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
||||||
|
onPaste(true)
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
onKeyEvent(text.charAt(i), false, false, false, false)
|
||||||
|
}
|
||||||
|
onPaste(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
const go = new Go();
|
||||||
|
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
|
||||||
|
go.run(result.instance);
|
||||||
|
});
|
67
webfiles/termstyle.css
Normal file
67
webfiles/termstyle.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#terminal {
|
||||||
|
background-color: black;
|
||||||
|
color: green;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
/* Copy paste! */
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style attributes */
|
||||||
|
|
||||||
|
.bold { font-weight: bold; }
|
||||||
|
|
||||||
|
.blink { animation: blinker 1s step-start infinite; }
|
||||||
|
|
||||||
|
.reverse { unicode-bidi: bidi-override; direction: rtl; }
|
||||||
|
|
||||||
|
.underline { text-decoration: underline; }
|
||||||
|
|
||||||
|
.dim { filter: brightness(50) }
|
||||||
|
|
||||||
|
.italic { font-style: italic; }
|
||||||
|
|
||||||
|
.strikethrough { text-decoration: line-through; }
|
||||||
|
|
||||||
|
/* Cursor styles */
|
||||||
|
|
||||||
|
.cursor-steady-block { background-color: lightgrey !important; }
|
||||||
|
.cursor-blinking-block { animation: blinking-block 1s step-start infinite !important; }
|
||||||
|
@keyframes blinking-block { 50% { background-color: lightgrey; } }
|
||||||
|
|
||||||
|
.cursor-steady-underline { text-decoration: underline lightgrey !important; }
|
||||||
|
.cursor-blinking-underline { animation: blinking-underline 1s step-start infinite !important; }
|
||||||
|
@keyframes blinking-underline { 50% { text-decoration: underline lightgrey; } }
|
||||||
|
|
||||||
|
.cursor-steady-bar { margin-left: -2px; }
|
||||||
|
.cursor-steady-bar:before {
|
||||||
|
content: ' ';
|
||||||
|
width: 2px;
|
||||||
|
background-color: lightgrey !important;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.cursor-blinking-bar { margin-left: -2px; }
|
||||||
|
.cursor-blinking-bar:before {
|
||||||
|
content: ' ';
|
||||||
|
width: 2px;
|
||||||
|
background-color: lightgrey !important;
|
||||||
|
display: inline-block;
|
||||||
|
animation: blinker 1s step-start infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* General animations */
|
||||||
|
|
||||||
|
@keyframes blinker {
|
||||||
|
50% { opacity: 0; }
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user