1
0
mirror of https://github.com/gdamore/tcell.git synced 2025-04-24 13:48:51 +08:00

Implement Beep() API

Add a Beep() method to the Screen interface. On *nix systems, this
writes the bell character (0x07) to the tty. On Windows, we call the
MessageBeep syscall.

Fixes: #2
This commit is contained in:
Ben Burwell 2019-07-22 21:49:28 -04:00 committed by Garrett D'Amore
parent 96a065d8ff
commit 8ec73b6fa6
10 changed files with 165 additions and 3 deletions

73
_demos/beep.go Normal file
View File

@ -0,0 +1,73 @@
// Copyright 2019 The TCell 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.
// beep makes a beep every second until you press ESC
package main
import (
"fmt"
"os"
"time"
"github.com/gdamore/tcell"
)
func main() {
tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
s, e := tcell.NewScreen()
if e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
}
if e = s.Init(); e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
}
s.SetStyle(tcell.StyleDefault)
s.Clear()
quit := make(chan struct{})
go func() {
for {
ev := s.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
switch ev.Key() {
case tcell.KeyEscape, tcell.KeyEnter, tcell.KeyCtrlC:
close(quit)
return
case tcell.KeyCtrlL:
s.Sync()
}
case *tcell.EventResize:
s.Sync()
}
}
}()
beep(s, quit)
s.Fini()
}
func beep(s tcell.Screen, quit <-chan struct{}) {
t := time.NewTicker(time.Second)
for {
select {
case <-quit:
return
case <-t.C:
s.Beep()
}
}
}

View File

@ -90,10 +90,13 @@ var winColors = map[Color]Color{
ColorWhite: ColorWhite,
}
var k32 = syscall.NewLazyDLL("kernel32.dll")
var (
k32 = syscall.NewLazyDLL("kernel32.dll")
u32 = syscall.NewLazyDLL("user32.dll")
)
// We have to bring in the kernel32.dll directly, so we can get access to some
// system calls that the core Go API lacks.
// We have to bring in the kernel32 and user32 DLLs directly, so we can get
// access to some system calls that the core Go API lacks.
//
// Note that Windows appends some functions with W to indicate that wide
// characters (Unicode) are in use. The documentation refers to them
@ -114,6 +117,7 @@ var (
procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo")
procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize")
procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute")
procMessageBeep = u32.NewProc("MessageBeep")
)
const (
@ -1031,3 +1035,16 @@ func (s *cScreen) HasKey(k Key) bool {
return valid[k]
}
func (s *cScreen) Beep() error {
// A simple beep. If the sound card is not available, the sound is generated
// using the speaker.
//
// Reference:
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebeep
const simpleBeep = 0xffffffff
if rv, _, err := procMessageBeep.Call(simpleBeep); rv == 0 {
return err
}
return nil
}

View File

@ -193,6 +193,10 @@ type Screen interface {
// menus, displayed hot-keys, etc. Note that KeyRune (literal
// runes) is always true.
HasKey(Key) bool
// Beep attempts to sound an OS-dependent audible alert and returns an error
// when unsuccessful.
Beep() error
}
// NewScreen returns a default Screen suitable for the user's terminal

View File

@ -109,3 +109,44 @@ func TestResize(t *testing.T) {
t.Errorf("Incorrect cell content after resize: %v", cell2)
}
}
func TestBeep(t *testing.T) {
s := mkTestScreen(t, "")
defer s.Fini()
b0, x0, y0 := s.GetContents()
if err := s.Beep(); err != nil {
t.Errorf("could not beep: %v", err)
}
s.Show()
b1, x1, y1 := s.GetContents()
if x0 != x1 {
t.Fatalf("screen width changed unexpectedly from %d to %d", x0, x1)
}
if y0 != y1 {
t.Fatalf("screen height changed unexpectedly from %d to %d", y0, y1)
}
if len(b0) != len(b1) {
t.Fatalf("screen size changed unexpectedly (had %d cells, has %d cells)",
len(b0), len(b1))
}
for i := 0; i < len(b0); i++ {
cell0 := b0[i]
cell1 := b1[i]
if len(cell0.Runes) != len(cell1.Runes) {
t.Errorf("incorrect cell content: had %d runes, has %d runes",
len(cell0.Runes), len(cell1.Runes))
continue
}
for j := 0; j < len(cell0.Runes); j++ {
r0 := cell0.Runes[j]
r1 := cell1.Runes[j]
if r0 != r1 {
t.Errorf("incorrect content: cell %d rune %d changed from %v to %v",
i, j, r0, r1)
}
}
}
}

View File

@ -506,3 +506,7 @@ func (s *simscreen) Resize(int, int, int, int) {}
func (s *simscreen) HasKey(Key) bool {
return true
}
func (s *simscreen) Beep() error {
return nil
}

View File

@ -114,3 +114,8 @@ func (t *tScreen) getWinSize() (int, int, error) {
}
return int(dim[1]), int(dim[0]), nil
}
func (t *tScreen) Beep() error {
t.writeString(string(byte(7)))
return nil
}

View File

@ -135,3 +135,8 @@ func (t *tScreen) getWinSize() (int, int, error) {
}
return int(dim[1]), int(dim[0]), nil
}
func (t *tScreen) Beep() error {
t.writeString(string(byte(7)))
return nil
}

View File

@ -140,3 +140,8 @@ func (t *tScreen) getWinSize() (int, int, error) {
}
return cols, rows, nil
}
func (t *tScreen) Beep() error {
t.writeString(string(byte(7)))
return nil
}

View File

@ -30,3 +30,7 @@ func (t *tScreen) termioFini() {
func (t *tScreen) getWinSize() (int, int, error) {
return 0, 0, ErrNoScreen
}
func (t *tScreen) Beep() error {
return ErrNoScreen
}

View File

@ -37,4 +37,8 @@ func (t *tScreen) getCharset() string {
return "UTF-16LE"
}
func (t *tScreen) Beep() error {
return ErrNoScreen
}
type termiosPrivate struct{}