Skip to content

Commit e7d14c2

Browse files
committed
fixes gdamore#62 windows console may have FIFO hang
fixes gdamore#63 Initial views API integration
1 parent 4295478 commit e7d14c2

18 files changed

+1605
-14
lines changed

console_win.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func NewConsoleScreen() (Screen, error) {
123123

124124
func (s *cScreen) Init() error {
125125

126-
s.evch = make(chan Event, 2)
126+
s.evch = make(chan Event, 10)
127127
s.quit = make(chan struct{})
128128

129129
if in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0); e != nil {
@@ -196,10 +196,16 @@ func (s *cScreen) Fini() {
196196
syscall.Close(s.out)
197197
}
198198

199-
func (s *cScreen) PostEvent(ev Event) {
199+
func (s *cScreen) PostEventWait(ev Event) {
200+
s.evch <- ev
201+
}
202+
203+
func (s *cScreen) PostEvent(ev Event) error {
200204
select {
201-
case <-s.quit:
202205
case s.evch <- ev:
206+
return nil
207+
default:
208+
return ErrEventQFull
203209
}
204210
}
205211

@@ -977,3 +983,5 @@ func (s *cScreen) CanDisplay(r rune, checkFallbacks bool) bool {
977983
// poorly supported under Windows.)
978984
return true
979985
}
986+
987+
func (s *cScreen) Resize(int, int, int, int) {}

errors.go

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ var (
4242
// encoding was found for it. This problem never occurs if
4343
// the environment is UTF-8 or UTF-16.
4444
ErrNoCharset = errors.New("character set not supported")
45+
46+
// ErrEventQFull indicates that the event queue is full, and
47+
// cannot accept more events.
48+
ErrEventQFull = errors.New("event queue full")
4549
)
4650

4751
// An EventError is an event representing some sort of error, and carries

event.go

+24
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,27 @@ type Event interface {
2424
// When reports the time when the event was generated.
2525
When() time.Time
2626
}
27+
28+
// EventTime is a simple base event class, suitable for easy reuse.
29+
// It can be used to deliver actual timer events as well.
30+
type EventTime struct {
31+
when time.Time
32+
}
33+
34+
func (e *EventTime) When() time.Time {
35+
return e.when
36+
}
37+
38+
func (e *EventTime) SetEventTime(t time.Time) {
39+
e.when = t
40+
}
41+
42+
func (e *EventTime) SetEventNow() {
43+
e.SetEventTime(time.Now())
44+
}
45+
46+
// EventHandler is anything that handles events. If the handler has
47+
// consumed the event, it should return true. False otherwise.
48+
type EventHandler interface {
49+
HandleEvent(Event) bool
50+
}

screen.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,20 @@ type Screen interface {
8080
// Furthermore, this will return nil if the Screen is finalized.
8181
PollEvent() Event
8282

83-
// PostEvent posts an event into the event stream.
84-
PostEvent(Event)
83+
// PostEvent tries to post an event into the event stream. This
84+
// can fail if the event queue is full. In that case, the event
85+
// is dropped, and ErrEventQFull is returned.
86+
PostEvent(ev Event) error
87+
88+
// PostEventWait is like PostEvent, but if the queue is full, it
89+
// blocks until there is space in the queue, making delivery
90+
// reliable. However, it is VERY important that this function
91+
// never be called from within whatever event loop is polling
92+
// with PollEvent(), otherwise a deadlock may arise.
93+
//
94+
// For this reason, when using this function, the use of a
95+
// Goroutine is recommended to ensure no deadlock can occur.
96+
PostEventWait(ev Event)
8597

8698
// EnableMouse enables the mouse. (If your terminal supports it.)
8799
EnableMouse()
@@ -157,6 +169,11 @@ type Screen interface {
157169
// also return true if the terminal can replace the glyph with
158170
// one that is visually indistinguishable from the one requested.
159171
CanDisplay(r rune, checkFallbacks bool) bool
172+
173+
// Resize does nothing, since its generally not possible to
174+
// ask a screen to resize, but it allows the Screen to implement
175+
// the View interface.
176+
Resize(int, int, int, int)
160177
}
161178

162179
// NewScreen returns a default Screen suitable for the user's terminal

sim_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func TestResize(t *testing.T) {
132132
So(sc.Style, ShouldEqual, st)
133133

134134
Convey("Do resize", func() {
135-
s.Resize(30, 10)
135+
s.SetSize(30, 10)
136136
s.Show()
137137
b2, x2, y2 := s.GetContents()
138138
So(b2, ShouldNotEqual, b)

simulation.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ type SimulationScreen interface {
4949
// InjectMouse injects a mouse event.
5050
InjectMouse(x, y int, buttons ButtonMask, mod ModMask)
5151

52-
// Resize resizes the underlying physical screen. It also causes
52+
// SetSize resizes the underlying physical screen. It also causes
5353
// a resize event to be injected during the next Show() or Sync().
5454
// A new physical contents array will be allocated (with data from
5555
// the old copied), so any prior value obtained with GetContents
5656
// won't be used anymore
57-
Resize(width, height int)
57+
SetSize(width, height int)
5858

5959
// GetContents returns screen contents as an array of
6060
// cells, along with the physical width & height. Note that the
61-
// physical contents will be used until the next time Resize()
61+
// physical contents will be used until the next time SetSize()
6262
// is called.
6363
GetContents() (cells []SimCell, width int, height int)
6464

@@ -351,11 +351,16 @@ func (s *simscreen) PollEvent() Event {
351351
}
352352
}
353353

354-
func (s *simscreen) PostEvent(ev Event) {
354+
func (s *simscreen) PostEventWait(ev Event) {
355+
s.evch <- ev
356+
}
357+
358+
func (s *simscreen) PostEvent(ev Event) error {
355359
select {
356360
case s.evch <- ev:
361+
return nil
357362
default:
358-
// drop the event on the floor
363+
return ErrEventQFull
359364
}
360365
}
361366

@@ -459,7 +464,7 @@ func (s *simscreen) CharacterSet() string {
459464
return s.charset
460465
}
461466

462-
func (s *simscreen) Resize(w, h int) {
467+
func (s *simscreen) SetSize(w, h int) {
463468
s.Lock()
464469
newc := make([]SimCell, w*h)
465470
for row := 0; row < h && row < s.physh; row++ {
@@ -519,3 +524,5 @@ func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {
519524
}
520525
return false
521526
}
527+
528+
func (s *simscreen) Resize(int, int, int, int) {}

tscreen.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -654,11 +654,16 @@ func (t *tScreen) buildAcsMap() {
654654
}
655655
}
656656

657-
func (t *tScreen) PostEvent(ev Event) {
657+
func (t *tScreen) PostEventWait(ev Event) {
658+
t.evch <- ev
659+
}
660+
661+
func (t *tScreen) PostEvent(ev Event) error {
658662
select {
659663
case t.evch <- ev:
664+
return nil
660665
default:
661-
// drop the event on the floor
666+
return ErrEventQFull
662667
}
663668
}
664669

@@ -1106,3 +1111,5 @@ func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
11061111
}
11071112
return false
11081113
}
1114+
1115+
func (t *tScreen) Resize(int, int, int, int) {}

views/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## tcell views
2+
3+
This package provides some enhanced functionality on top of base tcell.
4+
In particular, support for logical views, and a few different kinds of
5+
widgets like title bars, and scrollable areas, are provided.
6+
7+
These make up a higher level interface than tcell itself.

views/_demos/hbox.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2015 The Tops'l Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use file except in compliance with the License.
5+
// You may obtain a copy of the license at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"fmt"
19+
"os"
20+
21+
"github.com/gdamore/tcell"
22+
"github.com/gdamore/tcell/views"
23+
)
24+
25+
type MyBox struct {
26+
views.BoxLayout
27+
}
28+
29+
func (m *MyBox) HandleEvent(ev tcell.Event) bool {
30+
switch ev := ev.(type) {
31+
case *tcell.EventKey:
32+
if ev.Key() == tcell.KeyEscape {
33+
views.AppQuit()
34+
return true
35+
}
36+
}
37+
return m.BoxLayout.HandleEvent(ev)
38+
}
39+
40+
func main() {
41+
42+
if e := views.AppInit(); e != nil {
43+
fmt.Fprintln(os.Stderr, e.Error())
44+
os.Exit(1)
45+
}
46+
47+
outer := &MyBox{}
48+
outer.SetOrientation(views.Vertical)
49+
50+
title := views.NewTextBar()
51+
title.SetStyle(tcell.StyleDefault.
52+
Background(tcell.ColorYellow).
53+
Foreground(tcell.ColorBlack))
54+
title.SetCenter("Horizontal Boxes", tcell.StyleDefault)
55+
title.SetLeft("ESC to exit", tcell.StyleDefault.
56+
Background(tcell.ColorBlue).
57+
Foreground(tcell.ColorWhite))
58+
title.SetRight("==>X", tcell.StyleDefault)
59+
60+
box := views.NewBoxLayout(views.Horizontal)
61+
62+
l := views.NewText()
63+
m := views.NewText()
64+
r := views.NewText()
65+
66+
l.SetText("Left (0.0)")
67+
m.SetText("Middle (0.7)")
68+
r.SetText("Right (0.3)")
69+
l.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
70+
Background(tcell.ColorRed))
71+
m.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
72+
Background(tcell.ColorLime))
73+
r.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
74+
Background(tcell.ColorBlue))
75+
l.SetAlignment(views.AlignBegin)
76+
m.SetAlignment(views.AlignMiddle)
77+
r.SetAlignment(views.AlignEnd)
78+
79+
box.AddWidget(l, 0)
80+
box.AddWidget(m, 0.7)
81+
box.AddWidget(r, 0.3)
82+
83+
outer.AddWidget(title, 0)
84+
outer.AddWidget(box, 1)
85+
views.SetApplication(outer)
86+
views.RunApplication()
87+
}

views/_demos/vbox.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright 2015 The Tops'l Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use file except in compliance with the License.
5+
// You may obtain a copy of the license at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"fmt"
19+
"os"
20+
21+
"github.com/gdamore/tcell"
22+
"github.com/gdamore/tcell/views"
23+
)
24+
25+
type MyBox struct {
26+
views.BoxLayout
27+
}
28+
29+
func (m *MyBox) HandleEvent(ev tcell.Event) bool {
30+
switch ev := ev.(type) {
31+
case *tcell.EventKey:
32+
if ev.Key() == tcell.KeyEscape {
33+
views.AppQuit()
34+
return true
35+
}
36+
}
37+
return m.BoxLayout.HandleEvent(ev)
38+
}
39+
40+
func main() {
41+
42+
if e := views.AppInit(); e != nil {
43+
fmt.Fprintln(os.Stderr, e.Error())
44+
os.Exit(1)
45+
}
46+
47+
box := &MyBox{}
48+
box.SetOrientation(views.Vertical)
49+
50+
title := views.NewTextBar()
51+
title.SetStyle(tcell.StyleDefault.
52+
Foreground(tcell.ColorBlack).
53+
Background(tcell.ColorYellow))
54+
title.SetCenter("Vertical Layout", tcell.StyleDefault)
55+
top := views.NewText()
56+
mid := views.NewText()
57+
bot := views.NewText()
58+
59+
top.SetText("Top-Right (0.0)\nLine Two")
60+
mid.SetText("Center (0.7)")
61+
bot.SetText("Bottom-Left (0.3)")
62+
top.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
63+
Background(tcell.ColorRed))
64+
mid.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
65+
Background(tcell.ColorLime))
66+
bot.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
67+
Background(tcell.ColorBlue))
68+
69+
top.SetAlignment(views.VAlignTop | views.HAlignRight)
70+
mid.SetAlignment(views.VAlignCenter | views.HAlignCenter)
71+
bot.SetAlignment(views.VAlignBottom | views.HAlignLeft)
72+
73+
box.AddWidget(title, 0)
74+
box.AddWidget(top, 0)
75+
box.AddWidget(mid, 0.7)
76+
box.AddWidget(bot, 0.3)
77+
78+
views.SetApplication(box)
79+
views.RunApplication()
80+
}

0 commit comments

Comments
 (0)