diff --git a/console_win.go b/console_win.go index 7c57160..a901a12 100644 --- a/console_win.go +++ b/console_win.go @@ -343,6 +343,26 @@ func (s *cScreen) PostEvent(ev Event) error { } } +func (s *cScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { + defer close(ch) + for { + select { + case <-quit: + return + case <-s.stopQ: + return + case ev := <-s.evch: + select { + case <-quit: + return + case <-s.stopQ: + return + case ch <- ev: + } + } + } +} + func (s *cScreen) PollEvent() Event { select { case <-s.stopQ: diff --git a/event_test.go b/event_test.go index f8ede20..7908d26 100644 --- a/event_test.go +++ b/event_test.go @@ -68,3 +68,42 @@ func TestMouseEvents(t *testing.T) { t.Errorf("Modifiers should be control") } } + +func TestChannelMouseEvents(t *testing.T) { + + s := mkTestScreen(t, "") + defer s.Fini() + + s.EnableMouse() + s.InjectMouse(4, 9, Button1, ModCtrl) + evch := make(chan Event) + quit := make(chan struct{}) + em := new(EventMouse) + go s.ChannelEvents(evch, quit) + +loop: + for { + select { + case ev := <-evch: + if evm, ok := ev.(*EventMouse); ok { + em = evm + close(quit) + break loop + } + continue + case <-time.After(time.Second): + close(quit) + break loop + } + } + + if x, y := em.Position(); x != 4 || y != 9 { + t.Errorf("Mouse position wrong (%v, %v)", x, y) + } + if em.Buttons() != Button1 { + t.Errorf("Should be Button1") + } + if em.Modifiers() != ModCtrl { + t.Errorf("Modifiers should be control") + } +} diff --git a/screen.go b/screen.go index 2d9a9ad..cbe505f 100644 --- a/screen.go +++ b/screen.go @@ -78,6 +78,17 @@ type Screen interface { // response to a call to Clear or Flush. Size() (int, int) + // ChannelEvents is an infinite loop that waits for an event and + // channels it into the user provided channel ch. Closing the + // quit channel and calling the Fini method are cancellation + // signals. When a cancellation signal is received the method + // returns after closing ch. + // + // This method should be used as a goroutine. + // + // NOTE: PollEvent should not be called while this method is running. + ChannelEvents(ch chan<- Event, quit <-chan struct{}) + // PollEvent waits for events to arrive. Main application loops // must spin on this to prevent the application from stalling. // Furthermore, this will return nil if the Screen is finalized. diff --git a/simulation.go b/simulation.go index 468aeeb..451460b 100644 --- a/simulation.go +++ b/simulation.go @@ -351,6 +351,26 @@ func (s *simscreen) Colors() int { return 256 } +func (s *simscreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { + defer close(ch) + for { + select { + case <-quit: + return + case <-s.quit: + return + case ev := <-s.evch: + select { + case <-quit: + return + case <-s.quit: + return + case ch <- ev: + } + } + } +} + func (s *simscreen) PollEvent() Event { select { case <-s.quit: @@ -531,4 +551,4 @@ func (s *simscreen) Suspend() error { func (s *simscreen) Resume() error { return nil -} \ No newline at end of file +} diff --git a/tscreen.go b/tscreen.go index 49aacf1..ebd61a2 100644 --- a/tscreen.go +++ b/tscreen.go @@ -638,7 +638,7 @@ func (t *tScreen) drawCell(x, y int) int { t.TPuts(ti.TGoto(x-1, y)) t.TPuts(ti.InsertChar) t.cy = y - t.cx = x-1 + t.cx = x - 1 t.cells.SetDirty(x-1, y, true) _ = t.drawCell(x-1, y) t.TPuts(t.ti.TGoto(0, 0)) @@ -941,6 +941,26 @@ func (t *tScreen) nColors() int { return t.ti.Colors } +func (t *tScreen) 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 *tScreen) PollEvent() Event { select { case <-t.quit: