mirror of
https://github.com/ivanizag/izapple2.git
synced 2026-10-22 00:02:27 +00:00
Give a2ebiten a mouse, and the review of the shared code
The mouse the frontends share is little more than a scale, so a2ebiten can have one now. Ebitengine has no mouse events, the pointer is polled when the frame is updated, and its position is taken on the picture instead of on the window, so it lands on the same place whatever the size of the window is. The models with a mouse, like desktop, are of some use there now. A pointer outside the window stays on the edge instead of wrapping around, which Ebitengine reports as soon as the pointer leaves the picture. NewMixer is now NewFrontMixer, so that it does not look like the NewMixer of the audio package. The methods that the machine calls are marked as the implementations of izapple2.MouseProvider and izapple2.JoysticksProvider, and the note about SDL_WarpMouseInWindow is gone: it came from the SDL2 mouse and means nothing in a package that knows nothing about SDL.
This commit is contained in:
1 parent
88e59ae78d
commit
1b563d7668
10 files changed
+58
-20
No files matched your search
+10
-4
@@ -1,9 +1,9 @@
|
||||
# The Ebitengine frontend
|
||||
|
||||
`a2ebiten` opens a window with [Ebitengine](https://ebitengine.org/). It has
|
||||
the screen with all its modes, the sound, the diskettes dropped on the window
|
||||
and the same function keys as [a2sdl](frontend_sdl2.md), and it is missing the
|
||||
things around them: no joysticks and no mouse.
|
||||
the screen with all its modes, the sound, the mouse, the diskettes dropped on
|
||||
the window and the same function keys as [a2sdl](frontend_sdl2.md), and it is
|
||||
missing the things around them: no joysticks.
|
||||
|
||||
## Building
|
||||
|
||||
@@ -58,10 +58,16 @@ so the area used is the one the pointer was last seen on. Check with F8 before
|
||||
dragging, or look at the areas shown after the drop to see where the file
|
||||
landed.
|
||||
|
||||
## The mouse
|
||||
|
||||
The models that use a mouse, like `desktop`, work with the pointer on the
|
||||
window. Ebitengine has no mouse events, so the pointer is read on every frame,
|
||||
and its position is taken on the picture and not on the window: it lands on the
|
||||
same place whatever the size of the window is.
|
||||
|
||||
## What is missing
|
||||
|
||||
- **Joysticks and paddles**, and the mouse as a joystick.
|
||||
- **The mouse**, so the models that use it, like `desktop`, are not much use.
|
||||
- **Pasting** from the clipboard.
|
||||
|
||||
The window is 564x384 and can be resized. The picture is generated at a fixed
|
||||
|
||||
@@ -23,7 +23,7 @@ type ebitenAudio struct {
|
||||
|
||||
func newEbitenAudio(a *izapple2.Apple2) *ebitenAudio {
|
||||
return &ebitenAudio{
|
||||
mixer: shared.NewMixer(a),
|
||||
mixer: shared.NewFrontMixer(a),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ type Game struct {
|
||||
view *shared.View
|
||||
keyboard *ebitenKeyboard
|
||||
speaker *ebitenAudio
|
||||
mouse *shared.Mouse
|
||||
dropTargets *ebitenDropTargets
|
||||
fontSource *text.GoTextFaceSource
|
||||
|
||||
@@ -41,6 +42,7 @@ var hudColor = color.RGBA{208, 241, 141, 255} // Yellow
|
||||
func (g *Game) Update() error {
|
||||
g.keyboard.update()
|
||||
g.speaker.update()
|
||||
g.updateMouse()
|
||||
g.dropTargets.update()
|
||||
|
||||
if g.paused != g.a.IsPaused() {
|
||||
@@ -71,6 +73,20 @@ func (g *Game) Update() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
updateMouse reads where the pointer is and whether its button is pressed.
|
||||
Ebitengine has no mouse events, they are polled when the frame is updated.
|
||||
|
||||
The positions are on the virtual screen the game is laid out on, not on the
|
||||
window, so the pointer lands on the same place of the picture whatever the size
|
||||
of the window is.
|
||||
*/
|
||||
func (g *Game) updateMouse() {
|
||||
x, y := ebiten.CursorPosition()
|
||||
g.mouse.SetPosition(x, y, virtualWidth, virtualHeight)
|
||||
g.mouse.SetButton(ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft))
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
if g.image != nil {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
@@ -128,8 +144,10 @@ func ebitenRun(a *izapple2.Apple2) {
|
||||
view: view,
|
||||
keyboard: newEbitenKeyBoard(a, view),
|
||||
speaker: newEbitenAudio(a),
|
||||
mouse: shared.NewMouse(),
|
||||
dropTargets: newEbitenDropTargets(a),
|
||||
}
|
||||
a.SetMouseProvider(game.mouse)
|
||||
|
||||
var err error
|
||||
game.fontSource, err = text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
|
||||
|
||||
@@ -40,7 +40,7 @@ func newAudioOutput(a *izapple2.Apple2) *audioOutput {
|
||||
size := int(samplesPerFrame) + 2
|
||||
|
||||
return &audioOutput{
|
||||
mixer: shared.NewMixer(a),
|
||||
mixer: shared.NewFrontMixer(a),
|
||||
samplesPerFrame: samplesPerFrame,
|
||||
mono: make([]float32, size),
|
||||
stereo: make([]C.int16_t, 2*size),
|
||||
|
||||
@@ -34,7 +34,7 @@ var theSDLAudio atomic.Pointer[sdlAudio]
|
||||
|
||||
func newSDLAudio(a *izapple2.Apple2) *sdlAudio {
|
||||
return &sdlAudio{
|
||||
mixer: shared.NewMixer(a),
|
||||
mixer: shared.NewFrontMixer(a),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ type sdl3Audio struct {
|
||||
|
||||
func newSDL3Audio(a *izapple2.Apple2) *sdl3Audio {
|
||||
return &sdl3Audio{
|
||||
mixer: shared.NewMixer(a),
|
||||
mixer: shared.NewFrontMixer(a),
|
||||
buf: make([]float32, bufferSamples),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"github.com/ivanizag/izapple2/audio"
|
||||
)
|
||||
|
||||
// NewMixer creates the mixer of a machine with every sound source of it
|
||||
// NewFrontMixer creates the mixer of a machine with every sound source of it
|
||||
// attached, ready for the frontend to send the samples to its audio device
|
||||
func NewMixer(a *izapple2.Apple2) *audio.Mixer {
|
||||
func NewFrontMixer(a *izapple2.Apple2) *audio.Mixer {
|
||||
mixer := audio.NewMixer(a.GetClockMhz())
|
||||
for _, source := range a.GetAudioSources() {
|
||||
source.SetAudioSink(mixer.NewSource())
|
||||
|
||||
@@ -21,8 +21,8 @@ func (m *Mouse) SetPosition(x int, y int, width int, height int) {
|
||||
return
|
||||
}
|
||||
|
||||
m.x = uint16(65536 * x / width)
|
||||
m.y = uint16(65536 * y / height)
|
||||
m.x = mousePosition(x, width)
|
||||
m.y = mousePosition(y, height)
|
||||
}
|
||||
|
||||
// SetButton presses or releases the button of the mouse
|
||||
@@ -30,10 +30,16 @@ func (m *Mouse) SetButton(pressed bool) {
|
||||
m.pressed = pressed
|
||||
}
|
||||
|
||||
// mousePosition turns a position on the window into the full range the machine
|
||||
// reads the mouse in, keeping what is outside the window on the edge
|
||||
func mousePosition(position int, size int) uint16 {
|
||||
return uint16(max(min(65536*position/size, 65535), 0))
|
||||
}
|
||||
|
||||
// izapple2.MouseProvider implementation
|
||||
|
||||
// ReadMouse returns the position of the pointer on the window and whether its
|
||||
// button is pressed
|
||||
func (m *Mouse) ReadMouse() (x uint16, y uint16, pressed bool) {
|
||||
return m.x, m.y, m.pressed
|
||||
}
|
||||
|
||||
// TODO: SDL_WarpMouseInWindow
|
||||
@@ -22,4 +22,10 @@ func TestMouse(t *testing.T) {
|
||||
if x, y, _ := m.ReadMouse(); x != 32768 || y != 16384 {
|
||||
t.Errorf("the mouse moved to (%v, %v) on a window without size", x, y)
|
||||
}
|
||||
|
||||
// The pointer outside the window stays on the edge
|
||||
m.SetPosition(-10, 999, 640, 480)
|
||||
if x, y, _ := m.ReadMouse(); x != 0 || y != 65535 {
|
||||
t.Errorf("the mouse is at (%v, %v), expected (0, 65535)", x, y)
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,14 @@ func (p *Paddles) SetMouseButton(button int, pressed bool) {
|
||||
p.mouseButtons[button] = pressed
|
||||
}
|
||||
|
||||
// mouseToPaddle turns a position on the window into the value of a paddle
|
||||
// centered on the center of the window
|
||||
func mouseToPaddle(x int, width int) uint8 {
|
||||
return uint8(max(min(x-(width/2)+paddleCentered, 255), 0))
|
||||
}
|
||||
|
||||
// izapple2.JoysticksProvider implementation
|
||||
|
||||
// ReadButton returns whether a pushbutton is pressed, as the machine reads it
|
||||
func (p *Paddles) ReadButton(i int) bool {
|
||||
switch i {
|
||||
@@ -150,9 +158,3 @@ func (p *Paddles) ReadButton(i int) bool {
|
||||
func (p *Paddles) ReadPaddle(i int) (uint8, bool) {
|
||||
return p.paddle[i], p.hasPaddle[i]
|
||||
}
|
||||
|
||||
// mouseToPaddle turns a position on the window into the value of a paddle
|
||||
// centered on the center of the window
|
||||
func mouseToPaddle(x int, width int) uint8 {
|
||||
return uint8(max(min(x-(width/2)+paddleCentered, 255), 0))
|
||||
}
|
||||
Reference in new issue
Block a user