Skip to content

Commit

Permalink
fixes zyedidia#62 windows console may have FIFO hang
Browse files Browse the repository at this point in the history
fixes zyedidia#63 Initial views API integration
  • Loading branch information
gdamore committed Oct 30, 2015
1 parent 4295478 commit e7d14c2
Show file tree
Hide file tree
Showing 18 changed files with 1,605 additions and 14 deletions.
14 changes: 11 additions & 3 deletions console_win.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func NewConsoleScreen() (Screen, error) {

func (s *cScreen) Init() error {

s.evch = make(chan Event, 2)
s.evch = make(chan Event, 10)
s.quit = make(chan struct{})

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

func (s *cScreen) PostEvent(ev Event) {
func (s *cScreen) PostEventWait(ev Event) {
s.evch <- ev
}

func (s *cScreen) PostEvent(ev Event) error {
select {
case <-s.quit:
case s.evch <- ev:
return nil
default:
return ErrEventQFull
}
}

Expand Down Expand Up @@ -977,3 +983,5 @@ func (s *cScreen) CanDisplay(r rune, checkFallbacks bool) bool {
// poorly supported under Windows.)
return true
}

func (s *cScreen) Resize(int, int, int, int) {}
4 changes: 4 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ var (
// encoding was found for it. This problem never occurs if
// the environment is UTF-8 or UTF-16.
ErrNoCharset = errors.New("character set not supported")

// ErrEventQFull indicates that the event queue is full, and
// cannot accept more events.
ErrEventQFull = errors.New("event queue full")
)

// An EventError is an event representing some sort of error, and carries
Expand Down
24 changes: 24 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,27 @@ type Event interface {
// When reports the time when the event was generated.
When() time.Time
}

// EventTime is a simple base event class, suitable for easy reuse.
// It can be used to deliver actual timer events as well.
type EventTime struct {
when time.Time
}

func (e *EventTime) When() time.Time {
return e.when
}

func (e *EventTime) SetEventTime(t time.Time) {
e.when = t
}

func (e *EventTime) SetEventNow() {
e.SetEventTime(time.Now())
}

// EventHandler is anything that handles events. If the handler has
// consumed the event, it should return true. False otherwise.
type EventHandler interface {
HandleEvent(Event) bool
}
21 changes: 19 additions & 2 deletions screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,20 @@ type Screen interface {
// Furthermore, this will return nil if the Screen is finalized.
PollEvent() Event

// PostEvent posts an event into the event stream.
PostEvent(Event)
// PostEvent tries to post an event into the event stream. This
// can fail if the event queue is full. In that case, the event
// is dropped, and ErrEventQFull is returned.
PostEvent(ev Event) error

// PostEventWait is like PostEvent, but if the queue is full, it
// blocks until there is space in the queue, making delivery
// reliable. However, it is VERY important that this function
// never be called from within whatever event loop is polling
// with PollEvent(), otherwise a deadlock may arise.
//
// For this reason, when using this function, the use of a
// Goroutine is recommended to ensure no deadlock can occur.
PostEventWait(ev Event)

// EnableMouse enables the mouse. (If your terminal supports it.)
EnableMouse()
Expand Down Expand Up @@ -157,6 +169,11 @@ type Screen interface {
// also return true if the terminal can replace the glyph with
// one that is visually indistinguishable from the one requested.
CanDisplay(r rune, checkFallbacks bool) bool

// Resize does nothing, since its generally not possible to
// ask a screen to resize, but it allows the Screen to implement
// the View interface.
Resize(int, int, int, int)
}

// NewScreen returns a default Screen suitable for the user's terminal
Expand Down
2 changes: 1 addition & 1 deletion sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func TestResize(t *testing.T) {
So(sc.Style, ShouldEqual, st)

Convey("Do resize", func() {
s.Resize(30, 10)
s.SetSize(30, 10)
s.Show()
b2, x2, y2 := s.GetContents()
So(b2, ShouldNotEqual, b)
Expand Down
19 changes: 13 additions & 6 deletions simulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ type SimulationScreen interface {
// InjectMouse injects a mouse event.
InjectMouse(x, y int, buttons ButtonMask, mod ModMask)

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

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

Expand Down Expand Up @@ -351,11 +351,16 @@ func (s *simscreen) PollEvent() Event {
}
}

func (s *simscreen) PostEvent(ev Event) {
func (s *simscreen) PostEventWait(ev Event) {
s.evch <- ev
}

func (s *simscreen) PostEvent(ev Event) error {
select {
case s.evch <- ev:
return nil
default:
// drop the event on the floor
return ErrEventQFull
}
}

Expand Down Expand Up @@ -459,7 +464,7 @@ func (s *simscreen) CharacterSet() string {
return s.charset
}

func (s *simscreen) Resize(w, h int) {
func (s *simscreen) SetSize(w, h int) {
s.Lock()
newc := make([]SimCell, w*h)
for row := 0; row < h && row < s.physh; row++ {
Expand Down Expand Up @@ -519,3 +524,5 @@ func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {
}
return false
}

func (s *simscreen) Resize(int, int, int, int) {}
11 changes: 9 additions & 2 deletions tscreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,11 +654,16 @@ func (t *tScreen) buildAcsMap() {
}
}

func (t *tScreen) PostEvent(ev Event) {
func (t *tScreen) PostEventWait(ev Event) {
t.evch <- ev
}

func (t *tScreen) PostEvent(ev Event) error {
select {
case t.evch <- ev:
return nil
default:
// drop the event on the floor
return ErrEventQFull
}
}

Expand Down Expand Up @@ -1106,3 +1111,5 @@ func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
}
return false
}

func (t *tScreen) Resize(int, int, int, int) {}
7 changes: 7 additions & 0 deletions views/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## tcell views

This package provides some enhanced functionality on top of base tcell.
In particular, support for logical views, and a few different kinds of
widgets like title bars, and scrollable areas, are provided.

These make up a higher level interface than tcell itself.
87 changes: 87 additions & 0 deletions views/_demos/hbox.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2015 The Tops'l Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"fmt"
"os"

"github.com/gdamore/tcell"
"github.com/gdamore/tcell/views"
)

type MyBox struct {
views.BoxLayout
}

func (m *MyBox) HandleEvent(ev tcell.Event) bool {
switch ev := ev.(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
views.AppQuit()
return true
}
}
return m.BoxLayout.HandleEvent(ev)
}

func main() {

if e := views.AppInit(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}

outer := &MyBox{}
outer.SetOrientation(views.Vertical)

title := views.NewTextBar()
title.SetStyle(tcell.StyleDefault.
Background(tcell.ColorYellow).
Foreground(tcell.ColorBlack))
title.SetCenter("Horizontal Boxes", tcell.StyleDefault)
title.SetLeft("ESC to exit", tcell.StyleDefault.
Background(tcell.ColorBlue).
Foreground(tcell.ColorWhite))
title.SetRight("==>X", tcell.StyleDefault)

box := views.NewBoxLayout(views.Horizontal)

l := views.NewText()
m := views.NewText()
r := views.NewText()

l.SetText("Left (0.0)")
m.SetText("Middle (0.7)")
r.SetText("Right (0.3)")
l.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
Background(tcell.ColorRed))
m.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
Background(tcell.ColorLime))
r.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
Background(tcell.ColorBlue))
l.SetAlignment(views.AlignBegin)
m.SetAlignment(views.AlignMiddle)
r.SetAlignment(views.AlignEnd)

box.AddWidget(l, 0)
box.AddWidget(m, 0.7)
box.AddWidget(r, 0.3)

outer.AddWidget(title, 0)
outer.AddWidget(box, 1)
views.SetApplication(outer)
views.RunApplication()
}
80 changes: 80 additions & 0 deletions views/_demos/vbox.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2015 The Tops'l Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"fmt"
"os"

"github.com/gdamore/tcell"
"github.com/gdamore/tcell/views"
)

type MyBox struct {
views.BoxLayout
}

func (m *MyBox) HandleEvent(ev tcell.Event) bool {
switch ev := ev.(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
views.AppQuit()
return true
}
}
return m.BoxLayout.HandleEvent(ev)
}

func main() {

if e := views.AppInit(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}

box := &MyBox{}
box.SetOrientation(views.Vertical)

title := views.NewTextBar()
title.SetStyle(tcell.StyleDefault.
Foreground(tcell.ColorBlack).
Background(tcell.ColorYellow))
title.SetCenter("Vertical Layout", tcell.StyleDefault)
top := views.NewText()
mid := views.NewText()
bot := views.NewText()

top.SetText("Top-Right (0.0)\nLine Two")
mid.SetText("Center (0.7)")
bot.SetText("Bottom-Left (0.3)")
top.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
Background(tcell.ColorRed))
mid.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
Background(tcell.ColorLime))
bot.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).
Background(tcell.ColorBlue))

top.SetAlignment(views.VAlignTop | views.HAlignRight)
mid.SetAlignment(views.VAlignCenter | views.HAlignCenter)
bot.SetAlignment(views.VAlignBottom | views.HAlignLeft)

box.AddWidget(title, 0)
box.AddWidget(top, 0)
box.AddWidget(mid, 0.7)
box.AddWidget(bot, 0.3)

views.SetApplication(box)
views.RunApplication()
}
Loading

0 comments on commit e7d14c2

Please sign in to comment.