Compare commits
5 Commits
v0.2.0
...
image-buff
| Author | SHA1 | Date | |
|---|---|---|---|
| bbf067d463 | |||
| 32d95b338b | |||
| ef1e1f4ac2 | |||
| db20ea707d | |||
| 0fb6d8a6b2 |
@@ -11,6 +11,7 @@ type Application struct {
|
|||||||
backend Backend
|
backend Backend
|
||||||
config Config
|
config Config
|
||||||
callbackManager CallbackManager
|
callbackManager CallbackManager
|
||||||
|
imageManager ImageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run initializes the application, starts it, and then returns a channel that
|
// Run initializes the application, starts it, and then returns a channel that
|
||||||
@@ -33,60 +34,42 @@ func (application *Application) Run () (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnQuit registers an event handler to be called just before the application
|
|
||||||
// quits. This can happen when the user closes the application, or the backend
|
|
||||||
// experiences an unrecoverable error.
|
|
||||||
func (application *Application) OnQuit (
|
func (application *Application) OnQuit (
|
||||||
onQuit func (),
|
onQuit func (),
|
||||||
) {
|
) {
|
||||||
application.callbackManager.onQuit = onQuit
|
application.callbackManager.onQuit = onQuit
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPress registers an event handler to be called when a key or mouse button
|
|
||||||
// is pressed.
|
|
||||||
func (application *Application) OnPress (
|
func (application *Application) OnPress (
|
||||||
onPress func (button Button, modifiers Modifiers),
|
onPress func (button Button),
|
||||||
) {
|
) {
|
||||||
application.callbackManager.onPress = onPress
|
application.callbackManager.onPress = onPress
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPress registers an event handler to be called when a key or mouse button
|
|
||||||
// is released.
|
|
||||||
func (application *Application) OnRelease (
|
func (application *Application) OnRelease (
|
||||||
onRelease func (button Button),
|
onRelease func (button Button),
|
||||||
) {
|
) {
|
||||||
application.callbackManager.onRelease = onRelease
|
application.callbackManager.onRelease = onRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnResize registers an event handler to be called when the application window
|
|
||||||
// is resized. After the event handler is called, any updates it makes will
|
|
||||||
// automatically be pushed to the screen.
|
|
||||||
func (application *Application) OnResize (
|
func (application *Application) OnResize (
|
||||||
onResize func (),
|
onResize func (),
|
||||||
) {
|
) {
|
||||||
application.callbackManager.onResize = onResize
|
application.callbackManager.onResize = onResize
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnMouseMove registers an event handler to be called when mouse motion is
|
|
||||||
// detected. The coordinates of the cell that the mouse now hovers over are
|
|
||||||
// given as input.
|
|
||||||
func (application *Application) OnMouseMove (
|
func (application *Application) OnMouseMove (
|
||||||
onMouseMove func (x, y int),
|
onMouseMove func (x, y int),
|
||||||
) {
|
) {
|
||||||
application.callbackManager.onMouseMove = onMouseMove
|
application.callbackManager.onMouseMove = onMouseMove
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnScroll registers an event handler to be called when the user uses the mouse
|
|
||||||
// scroll wheel. Horizontal and vertical amounts are given as input.
|
|
||||||
func (application *Application) OnScroll (
|
func (application *Application) OnScroll (
|
||||||
onScroll func (x, y int),
|
onScroll func (x, y int),
|
||||||
) {
|
) {
|
||||||
application.callbackManager.onScroll = onScroll
|
application.callbackManager.onScroll = onScroll
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStart registers an event handler to be called once when the application
|
|
||||||
// starts, right before the first time updates are pushed to the screen.
|
|
||||||
// Anything done in here will be the first thing to appear on screen.
|
|
||||||
func (application *Application) OnStart (
|
func (application *Application) OnStart (
|
||||||
onStart func (),
|
onStart func (),
|
||||||
) {
|
) {
|
||||||
@@ -138,3 +121,21 @@ func (application *Application) Config () (config *Config) {
|
|||||||
config = &application.config
|
config = &application.config
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddImage adds a new image buffer and returns a pointer to it.
|
||||||
|
func (application *Application) NewImage () (im *ColorImage) {
|
||||||
|
cellWidth, cellHeight := application.backend.CellMetrics()
|
||||||
|
im = &ColorImage {
|
||||||
|
cellWidth: cellWidth,
|
||||||
|
cellHeight: cellHeight,
|
||||||
|
}
|
||||||
|
application.imageManager.Add(im)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the specified image buffer, if the application has it. If the
|
||||||
|
// image was found and removed, removed will be true.
|
||||||
|
func (application *Application) RemoveImage (im *ColorImage) (removed bool) {
|
||||||
|
removed = application.imageManager.Remove(im)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
46
backend.go
46
backend.go
@@ -3,54 +3,18 @@ package stone
|
|||||||
import "image"
|
import "image"
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
// Backend represents a backend for stone. Backends can be registered for use
|
|
||||||
// with the RegisterBackend() function. All of the below methods MUST be thread
|
|
||||||
// safe!
|
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
// Run is the backend's event loop. It must cleanly exit when the user
|
|
||||||
// closes the window, but not before calling the OnQuit event. Run
|
|
||||||
// must call event handlers within its own event loop in a
|
|
||||||
// non-concurrent fashion.
|
|
||||||
//
|
|
||||||
// The OnStart event handler must run after the backend has been fully
|
|
||||||
// initialized, and right before updates are first pushed to the screen.
|
|
||||||
// Whatever the application draws from within this event handler must be
|
|
||||||
// the first thing that appears on-screen.
|
|
||||||
//
|
|
||||||
// The OnResize event handler must run whenever the window is resized.
|
|
||||||
// The backend must push updates to the screen after OnResize has been
|
|
||||||
// run.
|
|
||||||
//
|
|
||||||
// The backend must not push updates to the screen in any other case,
|
|
||||||
// except when its Draw() method is specifically called.
|
|
||||||
//
|
|
||||||
// The OnPress, OnRelease, OnMouseMove, and OnMouseScroll events are to
|
|
||||||
// be called when such events happen. It is reccommended to compress
|
|
||||||
// resize, mouse move, and mouse scroll events whenever possible to
|
|
||||||
// reduce the likelihood of event buildup.
|
|
||||||
Run ()
|
Run ()
|
||||||
|
|
||||||
// SetTitle sets the application title. This will most often be the
|
|
||||||
// window title. This method may not always produce an effect, depending
|
|
||||||
// on the backend.
|
|
||||||
SetTitle (title string) (err error)
|
SetTitle (title string) (err error)
|
||||||
|
|
||||||
// SetIcon takes in a set of images of different sizes and sets the
|
|
||||||
// window's icon to them. This method may not always produce an effect,
|
|
||||||
// depending on the backend.
|
|
||||||
SetIcon (icons []image.Image) (err error)
|
SetIcon (icons []image.Image) (err error)
|
||||||
|
|
||||||
// Draw pushes all updates made to the application's buffer to the
|
|
||||||
// screen.
|
|
||||||
Draw ()
|
Draw ()
|
||||||
|
CellMetrics () (width, height int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackendFactory must completely initialize a backend, and return it. If
|
|
||||||
// anything goes wrong, it must stop, clean up any resources and return an
|
|
||||||
// error so another backend can be chosen.
|
|
||||||
type BackendFactory func (
|
type BackendFactory func (
|
||||||
application *Application,
|
application *Application,
|
||||||
callbackManager *CallbackManager,
|
callbackManager *CallbackManager,
|
||||||
|
imageManager *ImageManager,
|
||||||
) (
|
) (
|
||||||
backend Backend,
|
backend Backend,
|
||||||
err error,
|
err error,
|
||||||
@@ -58,7 +22,6 @@ type BackendFactory func (
|
|||||||
|
|
||||||
var factories []BackendFactory
|
var factories []BackendFactory
|
||||||
|
|
||||||
// RegisterBackend registers a backend factory.
|
|
||||||
func RegisterBackend (factory BackendFactory) {
|
func RegisterBackend (factory BackendFactory) {
|
||||||
factories = append(factories, factory)
|
factories = append(factories, factory)
|
||||||
}
|
}
|
||||||
@@ -66,7 +29,10 @@ func RegisterBackend (factory BackendFactory) {
|
|||||||
func instantiateBackend (application *Application) (backend Backend, err error) {
|
func instantiateBackend (application *Application) (backend Backend, err error) {
|
||||||
// find a suitable backend
|
// find a suitable backend
|
||||||
for _, factory := range factories {
|
for _, factory := range factories {
|
||||||
backend, err = factory(application, &application.callbackManager)
|
backend, err = factory (
|
||||||
|
application,
|
||||||
|
&application.callbackManager,
|
||||||
|
&application.imageManager)
|
||||||
if err == nil && backend != nil { return }
|
if err == nil && backend != nil { return }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,11 +95,12 @@ func (backend *Backend) drawRune (
|
|||||||
destinationRectangle, mask, maskPoint, _, ok := backend.font.face.Glyph (
|
destinationRectangle, mask, maskPoint, _, ok := backend.font.face.Glyph (
|
||||||
fixed.Point26_6 {
|
fixed.Point26_6 {
|
||||||
X: fixed.I(origin.X),
|
X: fixed.I(origin.X),
|
||||||
Y: fixed.I(origin.Y),
|
Y: fixed.I(origin.Y - backend.metrics.descent),
|
||||||
},
|
},
|
||||||
character)
|
character)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
|
println("warning")
|
||||||
strokeRectangle (
|
strokeRectangle (
|
||||||
&image.Uniform {
|
&image.Uniform {
|
||||||
C: backend.config.Color(stone.ColorForeground),
|
C: backend.config.Color(stone.ColorForeground),
|
||||||
@@ -118,70 +119,16 @@ func (backend *Backend) drawRune (
|
|||||||
backend.boundsOfCell(x, y))
|
backend.boundsOfCell(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
// cue a series of pointless optimizations
|
draw.DrawMask (
|
||||||
alphaMask, isAlpha := mask.(*image.Alpha)
|
backend.canvas,
|
||||||
if isAlpha {
|
destinationRectangle,
|
||||||
backend.sprayRuneMaskAlpha (
|
&image.Uniform {
|
||||||
alphaMask, destinationRectangle,
|
C: backend.config.Color(runeColor),
|
||||||
maskPoint, backend.colors[runeColor])
|
},
|
||||||
} else {
|
image.Point { },
|
||||||
backend.sprayRuneMask (
|
mask,
|
||||||
mask, destinationRectangle,
|
maskPoint,
|
||||||
maskPoint, backend.colors[runeColor])
|
draw.Over)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (backend *Backend) sprayRuneMask (
|
|
||||||
mask image.Image,
|
|
||||||
bounds image.Rectangle,
|
|
||||||
maskPoint image.Point,
|
|
||||||
fill xgraphics.BGRA,
|
|
||||||
) {
|
|
||||||
maxX := bounds.Max.X - bounds.Min.X
|
|
||||||
maxY := bounds.Max.Y - bounds.Min.Y
|
|
||||||
|
|
||||||
for y := 0; y < maxY; y ++ {
|
|
||||||
for x := 0; x < maxX; x ++ {
|
|
||||||
_, _, _,
|
|
||||||
alpha := mask.At(x + maskPoint.X, y + maskPoint.Y).RGBA()
|
|
||||||
backend.canvas.SetBGRA (
|
|
||||||
x + bounds.Min.X,
|
|
||||||
y + bounds.Min.Y - backend.metrics.descent,
|
|
||||||
xgraphics.BlendBGRA (
|
|
||||||
backend.colors[stone.ColorBackground],
|
|
||||||
xgraphics.BGRA {
|
|
||||||
R: fill.R,
|
|
||||||
G: fill.G,
|
|
||||||
B: fill.B,
|
|
||||||
A: uint8(alpha >> 8),
|
|
||||||
}))
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (backend *Backend) sprayRuneMaskAlpha (
|
|
||||||
mask *image.Alpha,
|
|
||||||
bounds image.Rectangle,
|
|
||||||
maskPoint image.Point,
|
|
||||||
fill xgraphics.BGRA,
|
|
||||||
) {
|
|
||||||
maxX := bounds.Max.X - bounds.Min.X
|
|
||||||
maxY := bounds.Max.Y - bounds.Min.Y
|
|
||||||
|
|
||||||
for y := 0; y < maxY; y ++ {
|
|
||||||
for x := 0; x < maxX; x ++ {
|
|
||||||
alpha := mask.AlphaAt(x + maskPoint.X, y + maskPoint.Y).A
|
|
||||||
backend.canvas.SetBGRA (
|
|
||||||
x + bounds.Min.X,
|
|
||||||
y + bounds.Min.Y - backend.metrics.descent,
|
|
||||||
xgraphics.BlendBGRA (
|
|
||||||
backend.colors[stone.ColorBackground],
|
|
||||||
xgraphics.BGRA {
|
|
||||||
R: fill.R,
|
|
||||||
G: fill.G,
|
|
||||||
B: fill.B,
|
|
||||||
A: alpha,
|
|
||||||
}))
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillRectangle (
|
func fillRectangle (
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ func (backend *Backend) handleButtonPress (
|
|||||||
backend.compressScrollSum(&sum)
|
backend.compressScrollSum(&sum)
|
||||||
backend.callbackManager.RunScroll(sum.x, sum.y)
|
backend.callbackManager.RunScroll(sum.x, sum.y)
|
||||||
} else {
|
} else {
|
||||||
backend.callbackManager.RunPress (
|
backend.callbackManager.RunPress(stone.Button(buttonEvent.Detail + 127))
|
||||||
stone.Button(buttonEvent.Detail + 127),
|
|
||||||
stone.Modifiers { })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,18 +109,8 @@ func (backend *Backend) handleKeyPress (
|
|||||||
event xevent.KeyPressEvent,
|
event xevent.KeyPressEvent,
|
||||||
) {
|
) {
|
||||||
keyEvent := *event.KeyPressEvent
|
keyEvent := *event.KeyPressEvent
|
||||||
button, num := backend.keycodeToButton(keyEvent.Detail, keyEvent.State)
|
button := backend.keycodeToButton(keyEvent.Detail, keyEvent.State)
|
||||||
backend.callbackManager.RunPress (button, stone.Modifiers {
|
backend.callbackManager.RunPress(button)
|
||||||
Shift:
|
|
||||||
(keyEvent.State & xproto.ModMaskShift) > 0 ||
|
|
||||||
(keyEvent.State & backend.modifierMasks.shiftLock) > 0,
|
|
||||||
Control: (keyEvent.State & xproto.ModMaskControl) > 0,
|
|
||||||
Alt: (keyEvent.State & backend.modifierMasks.alt) > 0,
|
|
||||||
Meta: (keyEvent.State & backend.modifierMasks.meta) > 0,
|
|
||||||
Super: (keyEvent.State & backend.modifierMasks.super) > 0,
|
|
||||||
Hyper: (keyEvent.State & backend.modifierMasks.hyper) > 0,
|
|
||||||
NumberPad: num,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (backend *Backend) handleKeyRelease (
|
func (backend *Backend) handleKeyRelease (
|
||||||
@@ -130,7 +118,7 @@ func (backend *Backend) handleKeyRelease (
|
|||||||
event xevent.KeyReleaseEvent,
|
event xevent.KeyReleaseEvent,
|
||||||
) {
|
) {
|
||||||
keyEvent := *event.KeyReleaseEvent
|
keyEvent := *event.KeyReleaseEvent
|
||||||
button, _ := backend.keycodeToButton(keyEvent.Detail, keyEvent.State)
|
button := backend.keycodeToButton(keyEvent.Detail, keyEvent.State)
|
||||||
backend.callbackManager.RunRelease(button)
|
backend.callbackManager.RunRelease(button)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import "github.com/flopp/go-findfont"
|
|||||||
func factory (
|
func factory (
|
||||||
application *stone.Application,
|
application *stone.Application,
|
||||||
callbackManager *stone.CallbackManager,
|
callbackManager *stone.CallbackManager,
|
||||||
|
imageManager *stone.ImageManager,
|
||||||
) (
|
) (
|
||||||
output stone.Backend,
|
output stone.Backend,
|
||||||
err error,
|
err error,
|
||||||
@@ -29,6 +30,7 @@ func factory (
|
|||||||
application: application,
|
application: application,
|
||||||
config: application.Config(),
|
config: application.Config(),
|
||||||
callbackManager: callbackManager,
|
callbackManager: callbackManager,
|
||||||
|
imageManager: imageManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
// load font
|
// load font
|
||||||
@@ -74,18 +76,7 @@ func factory (
|
|||||||
if err != nil { return }
|
if err != nil { return }
|
||||||
backend.window, err = xwindow.Generate(backend.connection)
|
backend.window, err = xwindow.Generate(backend.connection)
|
||||||
if err != nil { return }
|
if err != nil { return }
|
||||||
|
|
||||||
// get keyboard mapping information
|
|
||||||
keybind.Initialize(backend.connection)
|
keybind.Initialize(backend.connection)
|
||||||
backend.modifierMasks.capsLock = backend.keysymToMask(0xFFE5)
|
|
||||||
backend.modifierMasks.shiftLock = backend.keysymToMask(0xFFE6)
|
|
||||||
backend.modifierMasks.numLock = backend.keysymToMask(0xFF7F)
|
|
||||||
backend.modifierMasks.modeSwitch = backend.keysymToMask(0xFF7E)
|
|
||||||
|
|
||||||
backend.modifierMasks.hyper = backend.keysymToMask(0xffed)
|
|
||||||
backend.modifierMasks.super = backend.keysymToMask(0xffeb)
|
|
||||||
backend.modifierMasks.meta = backend.keysymToMask(0xffe7)
|
|
||||||
backend.modifierMasks.alt = backend.keysymToMask(0xffe9)
|
|
||||||
|
|
||||||
// create the window
|
// create the window
|
||||||
backend.window.Create (
|
backend.window.Create (
|
||||||
@@ -169,38 +160,6 @@ func findAndLoadFont (name string, size float64) (face font.Face) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (backend *Backend) keysymToKeycode (
|
|
||||||
symbol xproto.Keysym,
|
|
||||||
) (
|
|
||||||
code xproto.Keycode,
|
|
||||||
) {
|
|
||||||
mapping := keybind.KeyMapGet(backend.connection)
|
|
||||||
|
|
||||||
for index, testSymbol := range mapping.Keysyms {
|
|
||||||
if testSymbol == symbol {
|
|
||||||
code = xproto.Keycode (
|
|
||||||
index /
|
|
||||||
int(mapping.KeysymsPerKeycode) +
|
|
||||||
int(backend.connection.Setup().MinKeycode))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (backend *Backend) keysymToMask (
|
|
||||||
symbol xproto.Keysym,
|
|
||||||
) (
|
|
||||||
mask uint16,
|
|
||||||
) {
|
|
||||||
mask = keybind.ModGet (
|
|
||||||
backend.connection,
|
|
||||||
backend.keysymToKeycode(symbol))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init registers this backend when the program starts.
|
// init registers this backend when the program starts.
|
||||||
func init () {
|
func init () {
|
||||||
stone.RegisterBackend(factory)
|
stone.RegisterBackend(factory)
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package x
|
package x
|
||||||
|
|
||||||
// TODO: rename this file? lol
|
|
||||||
|
|
||||||
// import "fmt"
|
|
||||||
import "unicode"
|
import "unicode"
|
||||||
import "github.com/jezek/xgb/xproto"
|
import "github.com/jezek/xgb/xproto"
|
||||||
import "github.com/jezek/xgbutil/keybind"
|
import "github.com/jezek/xgbutil/keybind"
|
||||||
@@ -39,15 +36,10 @@ var buttonCodeTable = map[xproto.Keysym] stone.Button {
|
|||||||
0xFFE2: stone.KeyRightShift,
|
0xFFE2: stone.KeyRightShift,
|
||||||
0xFFE3: stone.KeyLeftControl,
|
0xFFE3: stone.KeyLeftControl,
|
||||||
0xFFE4: stone.KeyRightControl,
|
0xFFE4: stone.KeyRightControl,
|
||||||
|
|
||||||
0xFFE7: stone.KeyLeftMeta,
|
|
||||||
0xFFE8: stone.KeyRightMeta,
|
|
||||||
0xFFE9: stone.KeyLeftAlt,
|
0xFFE9: stone.KeyLeftAlt,
|
||||||
0xFFEA: stone.KeyRightAlt,
|
0xFFEA: stone.KeyRightAlt,
|
||||||
0xFFEB: stone.KeyLeftSuper,
|
0xFFEB: stone.KeyLeftSuper,
|
||||||
0xFFEC: stone.KeyRightSuper,
|
0xFFEC: stone.KeyRightSuper,
|
||||||
0xFFED: stone.KeyLeftHyper,
|
|
||||||
0xFFEE: stone.KeyRightHyper,
|
|
||||||
|
|
||||||
0xFFFF: stone.KeyDelete,
|
0xFFFF: stone.KeyDelete,
|
||||||
|
|
||||||
@@ -63,53 +55,6 @@ var buttonCodeTable = map[xproto.Keysym] stone.Button {
|
|||||||
0xFFC7: stone.KeyF10,
|
0xFFC7: stone.KeyF10,
|
||||||
0xFFC8: stone.KeyF11,
|
0xFFC8: stone.KeyF11,
|
||||||
0xFFC9: stone.KeyF12,
|
0xFFC9: stone.KeyF12,
|
||||||
|
|
||||||
// TODO: send this whenever a compose key, dead key, etc is pressed,
|
|
||||||
// and then send the resulting character while witholding the key
|
|
||||||
// presses that were used to compose it. As far as the program is
|
|
||||||
// concerned, a magical key with the final character was pressed and the
|
|
||||||
// KeyDead key is just so that the program might provide some visual
|
|
||||||
// feedback to the user while input is being waited for.
|
|
||||||
0xFF20: stone.KeyDead,
|
|
||||||
}
|
|
||||||
|
|
||||||
var keypadCodeTable = map[xproto.Keysym] stone.Button {
|
|
||||||
0xff80: stone.Button(' '),
|
|
||||||
0xff89: stone.KeyTab,
|
|
||||||
0xff8d: stone.KeyEnter,
|
|
||||||
0xff91: stone.KeyF1,
|
|
||||||
0xff92: stone.KeyF2,
|
|
||||||
0xff93: stone.KeyF3,
|
|
||||||
0xff94: stone.KeyF4,
|
|
||||||
0xff95: stone.KeyHome,
|
|
||||||
0xff96: stone.KeyLeft,
|
|
||||||
0xff97: stone.KeyUp,
|
|
||||||
0xff98: stone.KeyRight,
|
|
||||||
0xff99: stone.KeyDown,
|
|
||||||
0xff9a: stone.KeyPageUp,
|
|
||||||
0xff9b: stone.KeyPageDown,
|
|
||||||
0xff9c: stone.KeyEnd,
|
|
||||||
0xff9d: stone.KeyHome,
|
|
||||||
0xff9e: stone.KeyInsert,
|
|
||||||
0xff9f: stone.KeyDelete,
|
|
||||||
0xffbd: stone.Button('='),
|
|
||||||
0xffaa: stone.Button('*'),
|
|
||||||
0xffab: stone.Button('+'),
|
|
||||||
0xffac: stone.Button(','),
|
|
||||||
0xffad: stone.Button('-'),
|
|
||||||
0xffae: stone.Button('.'),
|
|
||||||
0xffaf: stone.Button('/'),
|
|
||||||
|
|
||||||
0xffb0: stone.Button('0'),
|
|
||||||
0xffb1: stone.Button('1'),
|
|
||||||
0xffb2: stone.Button('2'),
|
|
||||||
0xffb3: stone.Button('3'),
|
|
||||||
0xffb4: stone.Button('4'),
|
|
||||||
0xffb5: stone.Button('5'),
|
|
||||||
0xffb6: stone.Button('6'),
|
|
||||||
0xffb7: stone.Button('7'),
|
|
||||||
0xffb8: stone.Button('8'),
|
|
||||||
0xffb9: stone.Button('9'),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (backend *Backend) keycodeToButton (
|
func (backend *Backend) keycodeToButton (
|
||||||
@@ -117,13 +62,14 @@ func (backend *Backend) keycodeToButton (
|
|||||||
state uint16,
|
state uint16,
|
||||||
) (
|
) (
|
||||||
button stone.Button,
|
button stone.Button,
|
||||||
numberPad bool,
|
|
||||||
) {
|
) {
|
||||||
shift :=
|
// FIXME: also set shift to true if the lock modifier is on and the lock
|
||||||
state & xproto.ModMaskShift > 0 ||
|
// modifier is interpreted as shiftLock
|
||||||
state & backend.modifierMasks.shiftLock > 0
|
shift := state & xproto.ModMaskShift > 0
|
||||||
capsLock := state & backend.modifierMasks.capsLock > 0
|
|
||||||
numLock := state & backend.modifierMasks.numLock > 0
|
// FIXME: only set this to true if the lock modifier is on and the lock
|
||||||
|
// modifier is interpreted as capsLock
|
||||||
|
capsLock := state & xproto.ModMaskLock > 0
|
||||||
|
|
||||||
symbol1 := keybind.KeysymGet(backend.connection, keycode, 0)
|
symbol1 := keybind.KeysymGet(backend.connection, keycode, 0)
|
||||||
symbol2 := keybind.KeysymGet(backend.connection, keycode, 1)
|
symbol2 := keybind.KeysymGet(backend.connection, keycode, 1)
|
||||||
@@ -132,25 +78,13 @@ func (backend *Backend) keycodeToButton (
|
|||||||
|
|
||||||
cased := false
|
cased := false
|
||||||
|
|
||||||
// PARAGRAPH 3
|
// third paragraph
|
||||||
//
|
|
||||||
// A list of KeySyms is associated with each KeyCode. The list is
|
|
||||||
// intended to convey the set of symbols on the corresponding key. If
|
|
||||||
// the list (ignoring trailing NoSymbol entries) is a single KeySym
|
|
||||||
// ``K'', then the list is treated as if it were the list ``K NoSymbol
|
|
||||||
// K NoSymbol''. If the list (ignoring trailing NoSymbol entries) is a
|
|
||||||
// pair of KeySyms ``K1 K2'', then the list is treated as if it were the
|
|
||||||
// list ``K1 K2 K1 K2''. If the list (ignoring trailing NoSymbol
|
|
||||||
// entries) is a triple of KeySyms ``K1 K2 K3'', then the list is
|
|
||||||
// treated as if it were the list ``K1 K2 K3 NoSymbol''. When an
|
|
||||||
// explicit ``void'' element is desired in the list, the value
|
|
||||||
// VoidSymbol can be used.
|
|
||||||
switch {
|
switch {
|
||||||
case symbol2 == 0 && symbol3 == 0 && symbol4 == 0:
|
case symbol2 == 0 && symbol3 == 0 && symbol4 == 0:
|
||||||
symbol3 = symbol1
|
symbol3 = symbol1
|
||||||
case symbol3 == 0 && symbol4 == 0:
|
case symbol3 == 0 && symbol4 == 0:
|
||||||
symbol3 = symbol1
|
symbol3 = symbol1
|
||||||
symbol4 = symbol2
|
symbol2 = symbol2
|
||||||
case symbol4 == 0:
|
case symbol4 == 0:
|
||||||
symbol4 = 0
|
symbol4 = 0
|
||||||
}
|
}
|
||||||
@@ -163,18 +97,7 @@ func (backend *Backend) keycodeToButton (
|
|||||||
// FIXME: we ignore mode switch stuff
|
// FIXME: we ignore mode switch stuff
|
||||||
_ = symbol4Rune
|
_ = symbol4Rune
|
||||||
|
|
||||||
// PARAGRAPH 4
|
// fourth paragraph
|
||||||
//
|
|
||||||
// The first four elements of the list are split into two groups of
|
|
||||||
// KeySyms. Group 1 contains the first and second KeySyms; Group 2
|
|
||||||
// contains the third and fourth KeySyms. Within each group, if the
|
|
||||||
// second element of the group is NoSymbol , then the group should be
|
|
||||||
// treated as if the second element were the same as the first element,
|
|
||||||
// except when the first element is an alphabetic KeySym ``K'' for which
|
|
||||||
// both lowercase and uppercase forms are defined. In that case, the
|
|
||||||
// group should be treated as if the first element were the lowercase
|
|
||||||
// form of ``K'' and the second element were the uppercase form of
|
|
||||||
// ``K.''
|
|
||||||
if symbol2 == 0 {
|
if symbol2 == 0 {
|
||||||
upper := unicode.IsUpper(symbol1Rune)
|
upper := unicode.IsUpper(symbol1Rune)
|
||||||
lower := unicode.IsLower(symbol1Rune)
|
lower := unicode.IsLower(symbol1Rune)
|
||||||
@@ -184,7 +107,6 @@ func (backend *Backend) keycodeToButton (
|
|||||||
cased = true
|
cased = true
|
||||||
} else {
|
} else {
|
||||||
symbol2 = symbol1
|
symbol2 = symbol1
|
||||||
symbol2Rune = symbol1Rune
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if symbol4 == 0 {
|
if symbol4 == 0 {
|
||||||
@@ -196,44 +118,20 @@ func (backend *Backend) keycodeToButton (
|
|||||||
cased = true
|
cased = true
|
||||||
} else {
|
} else {
|
||||||
symbol4 = symbol3
|
symbol4 = symbol3
|
||||||
symbol4Rune = symbol3Rune
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedKeysym xproto.Keysym
|
var selectedKeysym xproto.Keysym
|
||||||
var selectedRune rune
|
var selectedRune rune
|
||||||
|
|
||||||
_, symbol2IsNumPad := keypadCodeTable[symbol2]
|
// big ol list in the middle
|
||||||
|
|
||||||
// "PARAGRAPH" 5
|
|
||||||
//
|
|
||||||
// Within a group, the choice of KeySym is determined by applying the
|
|
||||||
// first rule that is satisfied from the following list:
|
|
||||||
switch {
|
switch {
|
||||||
case numLock && symbol2IsNumPad:
|
// FIXME: take into account numlock
|
||||||
// The numlock modifier is on and the second KeySym is a keypad
|
|
||||||
// KeySym. In this case, if the Shift modifier is on, or if the
|
|
||||||
// Lock modifier is on and is interpreted as ShiftLock, then the
|
|
||||||
// first KeySym is used, otherwise the second KeySym is used.
|
|
||||||
if shift {
|
|
||||||
selectedKeysym = symbol1
|
|
||||||
selectedRune = symbol1Rune
|
|
||||||
} else {
|
|
||||||
selectedKeysym = symbol2
|
|
||||||
selectedRune = symbol2Rune
|
|
||||||
}
|
|
||||||
|
|
||||||
case !shift && !capsLock:
|
case !shift && !capsLock:
|
||||||
// The Shift and Lock modifiers are both off. In this case, the
|
|
||||||
// first KeySym is used.
|
|
||||||
selectedKeysym = symbol1
|
selectedKeysym = symbol1
|
||||||
selectedRune = symbol1Rune
|
selectedRune = symbol1Rune
|
||||||
|
|
||||||
case !shift && capsLock:
|
case !shift && capsLock:
|
||||||
// The Shift modifier is off, and the Lock modifier is on and is
|
|
||||||
// interpreted as CapsLock. In this case, the first KeySym is
|
|
||||||
// used, but if that KeySym is lowercase alphabetic, then the
|
|
||||||
// corresponding uppercase KeySym is used instead.
|
|
||||||
if cased && unicode.IsLower(symbol1Rune) {
|
if cased && unicode.IsLower(symbol1Rune) {
|
||||||
selectedRune = symbol2Rune
|
selectedRune = symbol2Rune
|
||||||
} else {
|
} else {
|
||||||
@@ -242,10 +140,6 @@ func (backend *Backend) keycodeToButton (
|
|||||||
}
|
}
|
||||||
|
|
||||||
case shift && capsLock:
|
case shift && capsLock:
|
||||||
// The Shift modifier is on, and the Lock modifier is on and is
|
|
||||||
// interpreted as CapsLock. In this case, the second KeySym is
|
|
||||||
// used, but if that KeySym is lowercase alphabetic, then the
|
|
||||||
// corresponding uppercase KeySym is used instead.
|
|
||||||
if cased && unicode.IsLower(symbol2Rune) {
|
if cased && unicode.IsLower(symbol2Rune) {
|
||||||
selectedRune = unicode.ToUpper(symbol2Rune)
|
selectedRune = unicode.ToUpper(symbol2Rune)
|
||||||
} else {
|
} else {
|
||||||
@@ -254,24 +148,18 @@ func (backend *Backend) keycodeToButton (
|
|||||||
}
|
}
|
||||||
|
|
||||||
case shift:
|
case shift:
|
||||||
// The Shift modifier is on, or the Lock modifier is on and is
|
|
||||||
// interpreted as ShiftLock, or both. In this case, the second
|
|
||||||
// KeySym is used.
|
|
||||||
selectedKeysym = symbol2
|
selectedKeysym = symbol2
|
||||||
selectedRune = symbol2Rune
|
selectedRune = symbol2Rune
|
||||||
}
|
}
|
||||||
|
|
||||||
// look up in control code table
|
// look up in table
|
||||||
var isControl bool
|
var isControl bool
|
||||||
button, isControl = buttonCodeTable[selectedKeysym]
|
button, isControl = buttonCodeTable[selectedKeysym]
|
||||||
if isControl { return }
|
|
||||||
|
|
||||||
// look up in keypad table
|
// if it wasn't found,
|
||||||
button, numberPad = keypadCodeTable[selectedKeysym]
|
if !isControl {
|
||||||
if numberPad { return }
|
|
||||||
|
|
||||||
// otherwise, use the rune
|
|
||||||
button = stone.Button(selectedRune)
|
button = stone.Button(selectedRune)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type Backend struct {
|
|||||||
application *stone.Application
|
application *stone.Application
|
||||||
config *stone.Config
|
config *stone.Config
|
||||||
callbackManager *stone.CallbackManager
|
callbackManager *stone.CallbackManager
|
||||||
|
imageManager *stone.ImageManager
|
||||||
connection *xgbutil.XUtil
|
connection *xgbutil.XUtil
|
||||||
window *xwindow.Window
|
window *xwindow.Window
|
||||||
canvas *xgraphics.Image
|
canvas *xgraphics.Image
|
||||||
@@ -43,18 +44,6 @@ type Backend struct {
|
|||||||
descent int
|
descent int
|
||||||
}
|
}
|
||||||
|
|
||||||
modifierMasks struct {
|
|
||||||
capsLock uint16
|
|
||||||
shiftLock uint16
|
|
||||||
numLock uint16
|
|
||||||
modeSwitch uint16
|
|
||||||
|
|
||||||
alt uint16
|
|
||||||
meta uint16
|
|
||||||
super uint16
|
|
||||||
hyper uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
windowBoundsClean bool
|
windowBoundsClean bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +89,12 @@ func (backend *Backend) SetIcon (icons []image.Image) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (backend *Backend) CellMetrics () (width, height int) {
|
||||||
|
width = backend.metrics.cellWidth
|
||||||
|
height = backend.metrics.cellHeight
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// calculateWindowSize calculates window bounds based on the internal buffer
|
// calculateWindowSize calculates window bounds based on the internal buffer
|
||||||
// size.
|
// size.
|
||||||
func (backend *Backend) calculateWindowSize () (x, y int) {
|
func (backend *Backend) calculateWindowSize () (x, y int) {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ type Color uint8
|
|||||||
const (
|
const (
|
||||||
ColorBackground Color = 0x0
|
ColorBackground Color = 0x0
|
||||||
ColorForeground Color = 0x1
|
ColorForeground Color = 0x1
|
||||||
ColorDim Color = 0x2
|
ColorRed Color = 0x2
|
||||||
ColorRed Color = 0x3
|
ColorOrange Color = 0x3
|
||||||
ColorYellow Color = 0x4
|
ColorYellow Color = 0x4
|
||||||
ColorGreen Color = 0x5
|
ColorGreen Color = 0x5
|
||||||
ColorBlue Color = 0x6
|
ColorBlue Color = 0x6
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ func (config *Config) load () {
|
|||||||
color.RGBA { R: 0, G: 0, B: 0, A: 0 },
|
color.RGBA { R: 0, G: 0, B: 0, A: 0 },
|
||||||
// foreground
|
// foreground
|
||||||
color.RGBA { R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF },
|
color.RGBA { R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF },
|
||||||
// dim
|
|
||||||
color.RGBA { R: 0x80, G: 0x80, B: 0x80, A: 0xFF },
|
|
||||||
// red
|
// red
|
||||||
color.RGBA { R: 0xFF, G: 0x00, B: 0x00, A: 0xFF },
|
color.RGBA { R: 0xFF, G: 0x00, B: 0x00, A: 0xFF },
|
||||||
|
// orange
|
||||||
|
color.RGBA { R: 0xFF, G: 0x80, B: 0x00, A: 0xFF },
|
||||||
// yellow
|
// yellow
|
||||||
color.RGBA { R: 0xFF, G: 0xFF, B: 0x00, A: 0xFF },
|
color.RGBA { R: 0xFF, G: 0xFF, B: 0x00, A: 0xFF },
|
||||||
// green
|
// green
|
||||||
@@ -154,10 +154,10 @@ func (config *Config) loadFile (path string) {
|
|||||||
config.colors[ColorBackground] = valueColor
|
config.colors[ColorBackground] = valueColor
|
||||||
case "colorForeground":
|
case "colorForeground":
|
||||||
config.colors[ColorForeground] = valueColor
|
config.colors[ColorForeground] = valueColor
|
||||||
case "colorDim":
|
|
||||||
config.colors[ColorDim] = valueColor
|
|
||||||
case "colorRed":
|
case "colorRed":
|
||||||
config.colors[ColorRed] = valueColor
|
config.colors[ColorRed] = valueColor
|
||||||
|
case "colorOrange":
|
||||||
|
config.colors[ColorOrange] = valueColor
|
||||||
case "colorYellow":
|
case "colorYellow":
|
||||||
config.colors[ColorYellow] = valueColor
|
config.colors[ColorYellow] = valueColor
|
||||||
case "colorGreen":
|
case "colorGreen":
|
||||||
|
|||||||
6
event.go
6
event.go
@@ -2,7 +2,7 @@ package stone
|
|||||||
|
|
||||||
type CallbackManager struct {
|
type CallbackManager struct {
|
||||||
onQuit func ()
|
onQuit func ()
|
||||||
onPress func (button Button, modifiers Modifiers)
|
onPress func (button Button)
|
||||||
onRelease func (button Button)
|
onRelease func (button Button)
|
||||||
onResize func ()
|
onResize func ()
|
||||||
onMouseMove func (x, y int)
|
onMouseMove func (x, y int)
|
||||||
@@ -15,9 +15,9 @@ func (manager *CallbackManager) RunQuit () {
|
|||||||
manager.onQuit()
|
manager.onQuit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *CallbackManager) RunPress (button Button, modifiers Modifiers) {
|
func (manager *CallbackManager) RunPress (button Button) {
|
||||||
if manager.onPress == nil { return }
|
if manager.onPress == nil { return }
|
||||||
manager.onPress(button, modifiers)
|
manager.onPress(button)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *CallbackManager) RunRelease (button Button) {
|
func (manager *CallbackManager) RunRelease (button Button) {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func redraw () {
|
|||||||
application.SetRune(0, height - 1, '+')
|
application.SetRune(0, height - 1, '+')
|
||||||
|
|
||||||
for x := 0; x < width; x ++ {
|
for x := 0; x < width; x ++ {
|
||||||
application.SetColor(x, height / 2, stone.Color(x % 5 + 3))
|
application.SetColor(x, height / 2, stone.Color(x % 6 + 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
for x := 1; x < width - 1; x ++ {
|
for x := 1; x < width - 1; x ++ {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func main () {
|
|||||||
if err != nil { panic(err) }
|
if err != nil { panic(err) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func onPress (button stone.Button, modifiers stone.Modifiers) {
|
func onPress (button stone.Button) {
|
||||||
if button == stone.MouseButtonLeft {
|
if button == stone.MouseButtonLeft {
|
||||||
mousePressed = true
|
mousePressed = true
|
||||||
application.SetRune(0, 0, '+')
|
application.SetRune(0, 0, '+')
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
import "fmt"
|
|
||||||
import "image"
|
|
||||||
import _ "image/png"
|
|
||||||
import "git.tebibyte.media/sashakoshka/stone"
|
|
||||||
import _ "git.tebibyte.media/sashakoshka/stone/backends/x"
|
|
||||||
|
|
||||||
var application = &stone.Application { }
|
|
||||||
|
|
||||||
func main () {
|
|
||||||
application.SetTitle("press any key")
|
|
||||||
application.SetSize(8, 1)
|
|
||||||
|
|
||||||
iconFile16, err := os.Open("assets/scaffold16.png")
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
icon16, _, err := image.Decode(iconFile16)
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
iconFile16.Close()
|
|
||||||
iconFile32, err := os.Open("assets/scaffold32.png")
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
icon32, _, err := image.Decode(iconFile32)
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
iconFile16.Close()
|
|
||||||
|
|
||||||
application.SetIcon([]image.Image { icon16, icon32 })
|
|
||||||
|
|
||||||
application.OnPress(onPress)
|
|
||||||
application.OnRelease(onRelease)
|
|
||||||
|
|
||||||
err = application.Run()
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
}
|
|
||||||
|
|
||||||
func onPress (button stone.Button, modifiers stone.Modifiers) {
|
|
||||||
fmt.Printf (
|
|
||||||
"=>>\t0x%X\tsh: %t\tctrl: %t\talt: %t\tm: %t\ts: %t \th: %t\tnumpad: %t\n",
|
|
||||||
button,
|
|
||||||
modifiers.Shift,
|
|
||||||
modifiers.Control,
|
|
||||||
modifiers.Alt,
|
|
||||||
modifiers.Meta,
|
|
||||||
modifiers.Super,
|
|
||||||
modifiers.Hyper,
|
|
||||||
modifiers.NumberPad)
|
|
||||||
}
|
|
||||||
|
|
||||||
func onRelease (button stone.Button) {
|
|
||||||
fmt.Printf("<--\t0x%X\n", button)
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "os"
|
import "os"
|
||||||
import "fmt"
|
|
||||||
import "image"
|
import "image"
|
||||||
import _ "image/png"
|
import _ "image/png"
|
||||||
import "git.tebibyte.media/sashakoshka/stone"
|
import "git.tebibyte.media/sashakoshka/stone"
|
||||||
import _ "git.tebibyte.media/sashakoshka/stone/backends/x"
|
import _ "git.tebibyte.media/sashakoshka/stone/backends/x"
|
||||||
|
|
||||||
var application = &stone.Application { }
|
var application = &stone.Application { }
|
||||||
var caretX = 0
|
var caret = 0
|
||||||
var caretY = 2
|
|
||||||
var page = 1
|
|
||||||
|
|
||||||
func main () {
|
func main () {
|
||||||
application.SetTitle("hellorld")
|
application.SetTitle("hellorld")
|
||||||
application.SetSize(32, 28)
|
application.SetSize(32, 16)
|
||||||
|
|
||||||
iconFile16, err := os.Open("assets/scaffold16.png")
|
iconFile16, err := os.Open("assets/scaffold16.png")
|
||||||
if err != nil { panic(err) }
|
if err != nil { panic(err) }
|
||||||
@@ -28,57 +25,32 @@ func main () {
|
|||||||
iconFile16.Close()
|
iconFile16.Close()
|
||||||
|
|
||||||
application.SetIcon([]image.Image { icon16, icon32 })
|
application.SetIcon([]image.Image { icon16, icon32 })
|
||||||
application.OnStart(redraw)
|
|
||||||
application.OnPress(onPress)
|
|
||||||
application.OnResize(redraw)
|
|
||||||
|
|
||||||
err = application.Run()
|
channel, err := application.Run()
|
||||||
if err != nil { panic(err) }
|
if err != nil { panic(err) }
|
||||||
|
|
||||||
application.Draw()
|
application.Draw()
|
||||||
|
|
||||||
|
for {
|
||||||
|
event := <- channel
|
||||||
|
switch event.(type) {
|
||||||
|
case stone.EventQuit:
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
|
case stone.EventPress:
|
||||||
|
button := event.(stone.EventPress).Button
|
||||||
|
if button.Printable() {
|
||||||
|
application.SetRune(caret, 0, rune(button))
|
||||||
|
caret ++
|
||||||
|
width, _ := application.Size()
|
||||||
|
if caret >= width {
|
||||||
|
caret = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func redraw () {
|
|
||||||
application.Clear()
|
|
||||||
_, height := application.Size()
|
|
||||||
application.SetDot(0, 0)
|
|
||||||
fmt.Fprint(application, "type some text below:")
|
|
||||||
caretX = 0
|
|
||||||
caretY = 2
|
|
||||||
application.SetDot(0, height - 1)
|
|
||||||
fmt.Fprintf(application, "page %d", page)
|
|
||||||
drawCaret()
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawCaret () {
|
|
||||||
application.SetRune(caretX, caretY, '+')
|
|
||||||
application.SetColor(caretX, caretY, stone.ColorDim)
|
|
||||||
}
|
|
||||||
|
|
||||||
func onPress (button stone.Button, modifiers stone.Modifiers) {
|
|
||||||
width, height := application.Size()
|
|
||||||
|
|
||||||
if button == stone.KeyEnter {
|
|
||||||
application.SetRune(caretX, caretY, 0)
|
|
||||||
caretX = 0
|
|
||||||
caretY ++
|
|
||||||
|
|
||||||
} else if button.Printable() {
|
|
||||||
application.SetRune(caretX, caretY, rune(button))
|
|
||||||
application.SetColor(caretX, caretY, stone.ColorForeground)
|
|
||||||
caretX ++
|
|
||||||
|
|
||||||
if caretX >= width {
|
|
||||||
caretX = 0
|
|
||||||
caretY ++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if caretY >= height - 2 {
|
|
||||||
page ++
|
|
||||||
redraw()
|
|
||||||
}
|
|
||||||
|
|
||||||
drawCaret()
|
|
||||||
application.Draw()
|
application.Draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case stone.EventResize:
|
||||||
|
application.Draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
142
image.go
Normal file
142
image.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package stone
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
import "image"
|
||||||
|
import "image/color"
|
||||||
|
|
||||||
|
type ImageManager struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
images []*ColorImage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ImageManager) For (callback func (im *ColorImage)) {
|
||||||
|
manager.lock.RLock()
|
||||||
|
defer manager.lock.RUnlock()
|
||||||
|
|
||||||
|
for _, im := range manager.images {
|
||||||
|
callback(im)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ImageManager) Add (im *ColorImage) {
|
||||||
|
manager.lock.Lock()
|
||||||
|
defer manager.lock.Unlock()
|
||||||
|
|
||||||
|
manager.images = append(manager.images, im)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ImageManager) Size () (size int) {
|
||||||
|
manager.lock.RLock()
|
||||||
|
defer manager.lock.RUnlock()
|
||||||
|
|
||||||
|
size = len(manager.images)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ImageManager) At (index int) (im *ColorImage) {
|
||||||
|
manager.lock.RLock()
|
||||||
|
defer manager.lock.RUnlock()
|
||||||
|
|
||||||
|
if index < 0 || index > len(manager.images) { return }
|
||||||
|
im = manager.images[index]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ImageManager) Remove (im *ColorImage) (removed bool) {
|
||||||
|
manager.lock.Lock()
|
||||||
|
defer manager.lock.Unlock()
|
||||||
|
|
||||||
|
index := 0
|
||||||
|
for manager.images[index] != im && index < len(manager.images) {
|
||||||
|
index ++
|
||||||
|
}
|
||||||
|
|
||||||
|
if index >= len(manager.images) { return }
|
||||||
|
|
||||||
|
manager.images = append (
|
||||||
|
manager.images[:index],
|
||||||
|
manager.images[index + 1:]...)
|
||||||
|
removed = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColorImage struct {
|
||||||
|
x, y int
|
||||||
|
width, height int
|
||||||
|
bufferWidth, bufferHeight int
|
||||||
|
cellWidth, cellHeight int
|
||||||
|
buffer []color.RGBA
|
||||||
|
|
||||||
|
clean bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) Model () (model color.Model) {
|
||||||
|
model = color.RGBAModel
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) Bounds () (bounds image.Rectangle) {
|
||||||
|
bounds.Max.X = im.width
|
||||||
|
bounds.Max.Y = im.height
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) Size () (width, height int) {
|
||||||
|
width = im.width
|
||||||
|
height = im.height
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) SetSize (width, height int) {
|
||||||
|
im.width = width
|
||||||
|
im.height = height
|
||||||
|
im.bufferWidth = im.cellWidth * im.width
|
||||||
|
im.bufferHeight = im.cellHeight * im.height
|
||||||
|
im.buffer = make([]color.RGBA, im.bufferWidth * im.bufferHeight)
|
||||||
|
im.clean = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) At (x, y int) (pixel color.Color) {
|
||||||
|
if im.outOfBounds(x, y) { return }
|
||||||
|
pixel = im.buffer[x + y * im.width]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) AtRGBA (x, y int) (pixel color.RGBA) {
|
||||||
|
if im.outOfBounds(x, y) { return }
|
||||||
|
pixel = im.buffer[x + y * im.width]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) Set (x, y int, pixel color.Color) {
|
||||||
|
if im.outOfBounds(x, y) { return }
|
||||||
|
r, g, b, a := pixel.RGBA()
|
||||||
|
im.buffer[x + y * im.width] = color.RGBA {
|
||||||
|
R: uint8(r >> 8),
|
||||||
|
G: uint8(g >> 8),
|
||||||
|
B: uint8(b >> 8),
|
||||||
|
A: uint8(a >> 8),
|
||||||
|
}
|
||||||
|
|
||||||
|
im.clean = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) SetRGBA (x, y int, pixel color.RGBA) {
|
||||||
|
if im.outOfBounds(x, y) { return }
|
||||||
|
im.buffer[x + y * im.width] = pixel
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) MarkClean () {
|
||||||
|
im.clean = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *ColorImage) outOfBounds (x, y int) (outOfBounds bool) {
|
||||||
|
outOfBounds =
|
||||||
|
x >= im.width ||
|
||||||
|
y >= im.height ||
|
||||||
|
x < 0 ||
|
||||||
|
y < 0
|
||||||
|
return
|
||||||
|
}
|
||||||
29
input.go
29
input.go
@@ -35,12 +35,8 @@ const (
|
|||||||
KeyRightControl Button = 23
|
KeyRightControl Button = 23
|
||||||
KeyLeftAlt Button = 24
|
KeyLeftAlt Button = 24
|
||||||
KeyRightAlt Button = 25
|
KeyRightAlt Button = 25
|
||||||
KeyLeftMeta Button = 26
|
KeyLeftSuper Button = 26
|
||||||
KeyRightMeta Button = 27
|
KeyRightSuper Button = 27
|
||||||
KeyLeftSuper Button = 28
|
|
||||||
KeyRightSuper Button = 29
|
|
||||||
KeyLeftHyper Button = 30
|
|
||||||
KeyRightHyper Button = 31
|
|
||||||
|
|
||||||
KeyDelete Button = 127
|
KeyDelete Button = 127
|
||||||
|
|
||||||
@@ -71,8 +67,6 @@ const (
|
|||||||
KeyF10 Button = 153
|
KeyF10 Button = 153
|
||||||
KeyF11 Button = 154
|
KeyF11 Button = 154
|
||||||
KeyF12 Button = 155
|
KeyF12 Button = 155
|
||||||
|
|
||||||
KeyDead Button = 156
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Printable returns whether or not the character could show up on screen. If
|
// Printable returns whether or not the character could show up on screen. If
|
||||||
@@ -82,22 +76,3 @@ func (button Button) Printable () (printable bool) {
|
|||||||
printable = unicode.IsPrint(rune(button))
|
printable = unicode.IsPrint(rune(button))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modifiers lists what modifier keys are being pressed. This is used in
|
|
||||||
// conjunction with a button code in a button press event. These should be used
|
|
||||||
// instead of attempting to track the state of the modifier keys, because there
|
|
||||||
// is no guarantee that one press event will be coupled with one release event.
|
|
||||||
type Modifiers struct {
|
|
||||||
Shift bool
|
|
||||||
Control bool
|
|
||||||
Alt bool
|
|
||||||
Meta bool
|
|
||||||
Super bool
|
|
||||||
Hyper bool
|
|
||||||
|
|
||||||
// NumberPad does not represent a key, but it behaves like one. If it is
|
|
||||||
// set to true, the button was pressed on the number pad. It is treated
|
|
||||||
// as a modifier key because if you don't care whether a key was pressed
|
|
||||||
// on the number pad or not, you can just ignore this value.
|
|
||||||
NumberPad bool
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user