1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package tcell
16
17 import (
18 "sync"
19 "unicode/utf8"
20
21 "golang.org/x/text/transform"
22 )
23
24
25
26 func NewSimulationScreen(charset string) SimulationScreen {
27 if charset == "" {
28 charset = "UTF-8"
29 }
30 s := &simscreen{charset: charset}
31 return s
32 }
33
34
35
36
37 type SimulationScreen interface {
38
39
40
41
42
43 InjectKeyBytes(buf []byte) bool
44
45
46
47 InjectKey(key Key, r rune, mod ModMask)
48
49
50 InjectMouse(x, y int, buttons ButtonMask, mod ModMask)
51
52
53
54
55
56 GetContents() (cells []SimCell, width int, height int)
57
58
59 GetCursor() (x int, y int, visible bool)
60
61 Screen
62 }
63
64
65
66 type SimCell struct {
67
68
69 Bytes []byte
70
71
72 Style Style
73
74
75 Runes []rune
76 }
77
78 type simscreen struct {
79 physw int
80 physh int
81 fini bool
82 style Style
83 evch chan Event
84 quit chan struct{}
85
86 front []SimCell
87 back CellBuffer
88 clear bool
89 cursorx int
90 cursory int
91 cursorvis bool
92 mouse bool
93 paste bool
94 charset string
95 encoder transform.Transformer
96 decoder transform.Transformer
97 fillchar rune
98 fillstyle Style
99 fallback map[rune]string
100
101 sync.Mutex
102 }
103
104 func (s *simscreen) Init() error {
105 s.evch = make(chan Event, 10)
106 s.quit = make(chan struct{})
107 s.fillchar = 'X'
108 s.fillstyle = StyleDefault
109 s.mouse = false
110 s.physw = 80
111 s.physh = 25
112 s.cursorx = -1
113 s.cursory = -1
114 s.style = StyleDefault
115
116 if enc := GetEncoding(s.charset); enc != nil {
117 s.encoder = enc.NewEncoder()
118 s.decoder = enc.NewDecoder()
119 } else {
120 return ErrNoCharset
121 }
122
123 s.front = make([]SimCell, s.physw*s.physh)
124 s.back.Resize(80, 25)
125
126
127 s.fallback = make(map[rune]string)
128 for k, v := range RuneFallbacks {
129 s.fallback[k] = v
130 }
131 return nil
132 }
133
134 func (s *simscreen) Fini() {
135 s.Lock()
136 s.fini = true
137 s.back.Resize(0, 0)
138 s.Unlock()
139 if s.quit != nil {
140 close(s.quit)
141 }
142 s.physw = 0
143 s.physh = 0
144 s.front = nil
145 }
146
147 func (s *simscreen) SetStyle(style Style) {
148 s.Lock()
149 s.style = style
150 s.Unlock()
151 }
152
153 func (s *simscreen) Clear() {
154 s.Fill(' ', s.style)
155 }
156
157 func (s *simscreen) Fill(r rune, style Style) {
158 s.Lock()
159 s.back.Fill(r, style)
160 s.Unlock()
161 }
162
163 func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) {
164
165 if len(ch) > 0 {
166 s.SetContent(x, y, ch[0], ch[1:], style)
167 } else {
168 s.SetContent(x, y, ' ', nil, style)
169 }
170 }
171
172 func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) {
173
174 s.Lock()
175 s.back.SetContent(x, y, mainc, combc, st)
176 s.Unlock()
177 }
178
179 func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) {
180 var mainc rune
181 var combc []rune
182 var style Style
183 var width int
184 s.Lock()
185 mainc, combc, style, width = s.back.GetContent(x, y)
186 s.Unlock()
187 return mainc, combc, style, width
188 }
189
190 func (s *simscreen) drawCell(x, y int) int {
191
192 mainc, combc, style, width := s.back.GetContent(x, y)
193 if !s.back.Dirty(x, y) {
194 return width
195 }
196 if x >= s.physw || y >= s.physh || x < 0 || y < 0 {
197 return width
198 }
199 simc := &s.front[(y*s.physw)+x]
200
201 if style == StyleDefault {
202 style = s.style
203 }
204 simc.Style = style
205 simc.Runes = append([]rune{mainc}, combc...)
206
207
208
209
210
211 simc.Bytes = nil
212
213 if x > s.physw-width {
214 simc.Runes = []rune{' '}
215 simc.Bytes = []byte{' '}
216 return width
217 }
218
219 lbuf := make([]byte, 12)
220 ubuf := make([]byte, 12)
221 nout := 0
222
223 for _, r := range simc.Runes {
224
225 l := utf8.EncodeRune(ubuf, r)
226
227 nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true)
228
229 if nout == 0 || lbuf[0] == '\x1a' {
230
231
232
233 if subst, ok := s.fallback[r]; ok {
234 simc.Bytes = append(simc.Bytes,
235 []byte(subst)...)
236
237 } else if r >= ' ' && r <= '~' {
238 simc.Bytes = append(simc.Bytes, byte(r))
239
240 } else if simc.Bytes == nil {
241 simc.Bytes = append(simc.Bytes, '?')
242 }
243 } else {
244 simc.Bytes = append(simc.Bytes, lbuf[:nout]...)
245 }
246 }
247 s.back.SetDirty(x, y, false)
248 return width
249 }
250
251 func (s *simscreen) ShowCursor(x, y int) {
252 s.Lock()
253 s.cursorx, s.cursory = x, y
254 s.showCursor()
255 s.Unlock()
256 }
257
258 func (s *simscreen) HideCursor() {
259 s.ShowCursor(-1, -1)
260 }
261
262 func (s *simscreen) showCursor() {
263
264 x, y := s.cursorx, s.cursory
265 if x < 0 || y < 0 || x >= s.physw || y >= s.physh {
266 s.cursorvis = false
267 } else {
268 s.cursorvis = true
269 }
270 }
271
272 func (s *simscreen) hideCursor() {
273
274 s.cursorvis = false
275 }
276
277 func (s *simscreen) SetCursorStyle(CursorStyle) {}
278
279 func (s *simscreen) Show() {
280 s.Lock()
281 s.resize()
282 s.draw()
283 s.Unlock()
284 }
285
286 func (s *simscreen) clearScreen() {
287
288 for i := range s.front {
289 s.front[i].Style = s.fillstyle
290 s.front[i].Runes = []rune{s.fillchar}
291 s.front[i].Bytes = []byte{byte(s.fillchar)}
292 }
293 s.clear = false
294 }
295
296 func (s *simscreen) draw() {
297 s.hideCursor()
298 if s.clear {
299 s.clearScreen()
300 }
301
302 w, h := s.back.Size()
303 for y := 0; y < h; y++ {
304 for x := 0; x < w; x++ {
305 width := s.drawCell(x, y)
306 x += width - 1
307 }
308 }
309 s.showCursor()
310 }
311
312 func (s *simscreen) EnableMouse(...MouseFlags) {
313 s.mouse = true
314 }
315
316 func (s *simscreen) DisableMouse() {
317 s.mouse = false
318 }
319
320 func (s *simscreen) EnablePaste() {
321 s.paste = true
322 }
323
324 func (s *simscreen) DisablePaste() {
325 s.paste = false
326 }
327
328 func (s *simscreen) Size() (int, int) {
329 s.Lock()
330 w, h := s.back.Size()
331 s.Unlock()
332 return w, h
333 }
334
335 func (s *simscreen) resize() {
336 w, h := s.physw, s.physh
337 ow, oh := s.back.Size()
338 if w != ow || h != oh {
339 s.back.Resize(w, h)
340 ev := NewEventResize(w, h)
341 s.PostEvent(ev)
342 }
343 }
344
345 func (s *simscreen) Colors() int {
346 return 256
347 }
348
349 func (s *simscreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) {
350 defer close(ch)
351 for {
352 select {
353 case <-quit:
354 return
355 case <-s.quit:
356 return
357 case ev := <-s.evch:
358 select {
359 case <-quit:
360 return
361 case <-s.quit:
362 return
363 case ch <- ev:
364 }
365 }
366 }
367 }
368
369 func (s *simscreen) PollEvent() Event {
370 select {
371 case <-s.quit:
372 return nil
373 case ev := <-s.evch:
374 return ev
375 }
376 }
377
378 func (s *simscreen) HasPendingEvent() bool {
379 return len(s.evch) > 0
380 }
381
382 func (s *simscreen) PostEventWait(ev Event) {
383 s.evch <- ev
384 }
385
386 func (s *simscreen) PostEvent(ev Event) error {
387 select {
388 case s.evch <- ev:
389 return nil
390 default:
391 return ErrEventQFull
392 }
393 }
394
395 func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) {
396 ev := NewEventMouse(x, y, buttons, mod)
397 s.PostEvent(ev)
398 }
399
400 func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) {
401 ev := NewEventKey(key, r, mod)
402 s.PostEvent(ev)
403 }
404
405 func (s *simscreen) InjectKeyBytes(b []byte) bool {
406 failed := false
407
408 outer:
409 for len(b) > 0 {
410 if b[0] >= ' ' && b[0] <= 0x7F {
411
412 ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
413 s.PostEvent(ev)
414 b = b[1:]
415 continue
416 }
417
418 if b[0] < 0x80 {
419 mod := ModNone
420
421 if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ {
422 mod = ModCtrl
423 }
424 ev := NewEventKey(Key(b[0]), 0, mod)
425 s.PostEvent(ev)
426 b = b[1:]
427 continue
428 }
429
430 utfb := make([]byte, len(b)*4)
431 for l := 1; l < len(b); l++ {
432 s.decoder.Reset()
433 nout, nin, _ := s.decoder.Transform(utfb, b[:l], true)
434
435 if nout != 0 {
436 r, _ := utf8.DecodeRune(utfb[:nout])
437 if r != utf8.RuneError {
438 ev := NewEventKey(KeyRune, r, ModNone)
439 s.PostEvent(ev)
440 }
441 b = b[nin:]
442 continue outer
443 }
444 }
445 failed = true
446 b = b[1:]
447 continue
448 }
449
450 return !failed
451 }
452
453 func (s *simscreen) Sync() {
454 s.Lock()
455 s.clear = true
456 s.resize()
457 s.back.Invalidate()
458 s.draw()
459 s.Unlock()
460 }
461
462 func (s *simscreen) CharacterSet() string {
463 return s.charset
464 }
465
466 func (s *simscreen) SetSize(w, h int) {
467 s.Lock()
468 newc := make([]SimCell, w*h)
469 for row := 0; row < h && row < s.physh; row++ {
470 for col := 0; col < w && col < s.physw; col++ {
471 newc[(row*w)+col] = s.front[(row*s.physw)+col]
472 }
473 }
474 s.cursorx, s.cursory = -1, -1
475 s.physw, s.physh = w, h
476 s.front = newc
477 s.back.Resize(w, h)
478 s.Unlock()
479 }
480
481 func (s *simscreen) GetContents() ([]SimCell, int, int) {
482 s.Lock()
483 cells, w, h := s.front, s.physw, s.physh
484 s.Unlock()
485 return cells, w, h
486 }
487
488 func (s *simscreen) GetCursor() (int, int, bool) {
489 s.Lock()
490 x, y, vis := s.cursorx, s.cursory, s.cursorvis
491 s.Unlock()
492 return x, y, vis
493 }
494
495 func (s *simscreen) RegisterRuneFallback(r rune, subst string) {
496 s.Lock()
497 s.fallback[r] = subst
498 s.Unlock()
499 }
500
501 func (s *simscreen) UnregisterRuneFallback(r rune) {
502 s.Lock()
503 delete(s.fallback, r)
504 s.Unlock()
505 }
506
507 func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {
508
509 if enc := s.encoder; enc != nil {
510 nb := make([]byte, 6)
511 ob := make([]byte, 6)
512 num := utf8.EncodeRune(ob, r)
513
514 enc.Reset()
515 dst, _, err := enc.Transform(nb, ob[:num], true)
516 if dst != 0 && err == nil && nb[0] != '\x1A' {
517 return true
518 }
519 }
520 if !checkFallbacks {
521 return false
522 }
523 if _, ok := s.fallback[r]; ok {
524 return true
525 }
526 return false
527 }
528
529 func (s *simscreen) HasMouse() bool {
530 return false
531 }
532
533 func (s *simscreen) Resize(int, int, int, int) {}
534
535 func (s *simscreen) HasKey(Key) bool {
536 return true
537 }
538
539 func (s *simscreen) Beep() error {
540 return nil
541 }
542
543 func (s *simscreen) Suspend() error {
544 return nil
545 }
546
547 func (s *simscreen) Resume() error {
548 return nil
549 }
550
View as plain text