Skip to content

Commit

Permalink
fixes #37 Improve docs & fix golint
Browse files Browse the repository at this point in the history
fixes #38 Broke wide characters in last update
fixes #39 Clean up logic for encodings, enhance fallback options
fixes #36 Support more mouse buttons on Windows.
  • Loading branch information
gdamore committed Oct 8, 2015
1 parent 02eef72 commit 7322e40
Show file tree
Hide file tree
Showing 26 changed files with 845 additions and 443 deletions.
15 changes: 12 additions & 3 deletions _demos/mouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,23 @@ import (

"github.com/gdamore/tcell"
"github.com/gdamore/tcell/encoding"

"github.com/mattn/go-runewidth"
)

var defStyle tcell.Style

func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) {
for _, c := range str {
s.SetContent(x, y, c, nil, style)
x++
var comb []rune
w := runewidth.RuneWidth(c)
if w == 0 {
comb = []rune{c}
c = ' '
w = 1
}
s.SetContent(x, y, c, comb, style)
x += w
}
}

Expand Down Expand Up @@ -81,7 +90,7 @@ func drawSelect(s tcell.Screen, x1, y1, x2, y2 int, sel bool) {
}
style = style.Reverse(sel)
s.SetContent(col, row, mainc, combc, style)
col += width-1
col += width - 1
}
}
}
Expand Down
91 changes: 91 additions & 0 deletions ascii.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2015 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.

package tcell

import (
"unicode/utf8"

"golang.org/x/text/encoding"
"golang.org/x/text/transform"
)

type ascii struct{ transform.NopResetter }
type asciiDecoder struct{ transform.NopResetter }
type asciiEncoder struct{ transform.NopResetter }

// ASCII represents an basic 7-bit ASCII scheme. It decodes directly to UTF-8
// without change, as all ASCII values are legal UTF-8. It encodes any UTF-8
// runes outside of ASCII to 0x1A, the ASCII substitution character.
var ASCII encoding.Encoding = ascii{}

func (ascii) NewDecoder() transform.Transformer {
return asciiDecoder{}
}

func (ascii) NewEncoder() transform.Transformer {
return asciiEncoder{}
}

func (asciiDecoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
var e error
var ndst, nsrc int
for _, c := range src {
if ndst >= len(dst) {
e = transform.ErrShortDst
break
}
dst[ndst] = c
ndst++
nsrc++
}
return ndst, nsrc, e
}

func (asciiEncoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
var e error
var ndst, nsrc int
var sz int
for nsrc < len(src) {
if ndst >= len(dst) {
e = transform.ErrShortDst
break
}
r := rune(src[nsrc])
if r < utf8.RuneSelf {
dst[ndst] = uint8(r)
nsrc++
ndst++
continue
}

// No valid runes beyond ASCII. However, we need to consume
// the full rune, and report incomplete runes properly.

// Attempt to decode a multibyte rune
r, sz = utf8.DecodeRune(src[nsrc:])
if sz == 1 {
// If its inconclusive due to insufficient data in
// in the source, report it
if !atEOF && !utf8.FullRune(src[nsrc:]) {
e = transform.ErrShortSrc
break
}
}
nsrc += sz
dst[ndst] = encoding.ASCIISub
ndst++
}
return ndst, nsrc, e
}
37 changes: 22 additions & 15 deletions cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,6 @@ import (
"github.com/mattn/go-runewidth"
)

// Cell represents a single character cell. This is primarily intended for
// use by Screen implementors.
type Cell struct {
currMain rune
currComb []rune
currStyle Style
lastMain rune
lastStyle Style
lastComb []rune
width int
}

type cell struct {
currMain rune
currComb []rune
Expand All @@ -40,12 +28,20 @@ type cell struct {
width int
}

// CellBuffer represents a two dimensional array of character cells.
// This is primarily intended for use by Screen implementors; it
// contains much of the common code they need. To create one, just
// declare a variable of its type; no explicit initialization is necessary.
//
// CellBuffer is not thread safe.
type CellBuffer struct {
w int
h int
cells []cell
}

// SetContent sets the contents (primary rune, combining runes,
// and style) for a cell at a given location.
func (cb *CellBuffer) SetContent(x int, y int,
mainc rune, combc []rune, style Style) {

Expand All @@ -65,14 +61,17 @@ func (cb *CellBuffer) SetContent(x int, y int,

if c.currMain != mainc {
c.width = runewidth.RuneWidth(mainc)
c.width = 1
}
c.currMain = mainc
c.currComb = combc
c.currStyle = style
}
}

// GetContent returns the contents of a character cell, including the
// primary rune, any combining character runes (which will usually be
// nil), the style, and the display width in cells. (The width can be
// either 1, normally, or 2 for East Asian full-width characters.)
func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) {
var mainc rune
var combc []rune
Expand All @@ -89,16 +88,22 @@ func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) {
return mainc, combc, style, width
}

// Size returns the (width, height) in cells of the buffer.
func (cb *CellBuffer) Size() (int, int) {
return cb.w, cb.h
}

// Invalidate marks all characters within the buffer as dirty.
func (cb *CellBuffer) Invalidate() {
for i := range cb.cells {
cb.cells[i].lastMain = rune(0)
}
}

// Dirty checks if a character at the given location needs an
// to be refreshed on the physical display. This returns true
// if the cell content is different since the last time it was
// marked clean.
func (cb *CellBuffer) Dirty(x, y int) bool {
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
c := &cb.cells[(y*cb.w)+x]
Expand All @@ -123,6 +128,9 @@ func (cb *CellBuffer) Dirty(x, y int) bool {
return false
}

// SetDirty is normally used to indicate that a cell has
// been displayed (in which case dirty is false), or to manually
// force a cell to be marked dirty.
func (cb *CellBuffer) SetDirty(x, y int, dirty bool) {
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
c := &cb.cells[(y*cb.w)+x]
Expand Down Expand Up @@ -167,8 +175,7 @@ func (cb *CellBuffer) Resize(w, h int) {

// Fill fills the entire cell buffer array with the specified character
// and style. Normally choose ' ' to clear the screen. This API doesn't
// support combining characters. (Why would you want to fill with combining
// characters?!?)
// support combining characters.
func (cb *CellBuffer) Fill(r rune, style Style) {
for i := range cb.cells {
c := &cb.cells[i]
Expand Down
6 changes: 3 additions & 3 deletions console_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package tcell

import "errors"

// NewConsoleScreen returns a console based screen. This platform
// doesn't have support for any, so it returns nil and a suitable error.
func NewConsoleScreen() (Screen, error) {
return nil, errors.New("no platform specific console support")
return nil, ErrNoScreen
}
28 changes: 21 additions & 7 deletions console_win.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,17 @@ type cScreen struct {
sync.Mutex
}

// all Windows systems are little endian
var k32 = syscall.NewLazyDLL("kernel32.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.
//
// Note that Windows appends some functions with W to indicate that wide
// characters (Unicode) are in use. The documentation refers to them
// without this suffix, as the resolution is made via preprocessor.
// We have to bring in the kernel32.dll directly, so we can get access to some
// system calls that the core Go API lacks.

var (
procReadConsoleInput = k32.NewProc("ReadConsoleInputW")
procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo")
Expand All @@ -68,9 +73,9 @@ var (
procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute")
)

// We have to bring in the kernel32.dll directly, so we can get access to some
// system calls that the core Go API lacks.

// NewConsoleScreen returns a Screen for the Windows console associated
// with the current process. The Screen makes use of the Windows Console
// API to display content and read events.
func NewConsoleScreen() (Screen, error) {
return &cScreen{}, nil
}
Expand Down Expand Up @@ -218,8 +223,8 @@ func (s *cScreen) doCursor() {
}
}

func (c *cScreen) HideCursor() {
c.ShowCursor(-1, -1)
func (s *cScreen) HideCursor() {
s.ShowCursor(-1, -1)
}

type charInfo struct {
Expand Down Expand Up @@ -511,6 +516,15 @@ func (s *cScreen) getConsoleInput() error {
if mrec.btns&0x10 != 0 {
btns |= Button5
}
if mrec.btns&0x20 != 0 {
btns |= Button6
}
if mrec.btns&0x40 != 0 {
btns |= Button7
}
if mrec.btns&0x80 != 0 {
btns |= Button8
}

if mrec.flags&mouseVWheeled != 0 {
if mrec.btns&0x80000000 == 0 {
Expand Down Expand Up @@ -701,7 +715,7 @@ func (s *cScreen) draw() {
continue
}
}
if x > s.w - width {
if x > s.w-width {
mainc = ' '
combc = nil
width = 1
Expand Down
Loading

0 comments on commit 7322e40

Please sign in to comment.