mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-09 20:47:26 +01:00
parent
a113687d56
commit
cb56906e14
67
examples/setcursorposition/main.go
Normal file
67
examples/setcursorposition/main.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
const (
|
||||
screenWidth = 640
|
||||
screenHeight = 480
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
x, y := ebiten.CursorPosition()
|
||||
if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
|
||||
x--
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) {
|
||||
x++
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) {
|
||||
y--
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) {
|
||||
y++
|
||||
}
|
||||
ebiten.SetCursorPosition(x, y)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
x, y := ebiten.CursorPosition()
|
||||
ebitenutil.DebugPrint(screen, fmt.Sprintf("Cursor Position: (%d, %d)\nPress arrow keys or WASD keys.", x, y))
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return screenWidth, screenHeight
|
||||
}
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||
ebiten.SetWindowTitle("SetCursorPosition (Ebitengine Demo)")
|
||||
ebiten.SetCursorPosition(screenWidth/2, screenHeight/2)
|
||||
if err := ebiten.RunGame(&Game{}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
20
input.go
20
input.go
@ -74,17 +74,27 @@ func KeyName(key Key) string {
|
||||
return ui.Get().KeyName(ui.Key(key))
|
||||
}
|
||||
|
||||
// CursorPosition returns a position of a mouse cursor relative to the game screen (window). The cursor position is
|
||||
// 'logical' position and this considers the scale of the screen.
|
||||
// CursorPosition returns a position of a mouse cursor relative to the game screen (window).
|
||||
// The cursor position is 'logical' position and this considers the scale of the screen.
|
||||
//
|
||||
// CursorPosition returns (0, 0) before the main loop on desktops and browsers.
|
||||
// CursorPosition returns (0, 0) or the position set by SetCursorPosition before the main loop on desktops and browsers.
|
||||
//
|
||||
// CursorPosition always returns (0, 0) on mobile native applications.
|
||||
//
|
||||
// CursorPosition is concurrent-safe.
|
||||
func CursorPosition() (x, y int) {
|
||||
cx, cy := theInputState.cursorPosition()
|
||||
return int(cx), int(cy)
|
||||
// For the cursor position, theInputState is not used since the cursor position can be updated by SetCursorPosition.
|
||||
return ui.Get().CursorPosition()
|
||||
}
|
||||
|
||||
// SetCursorPosition sets the cursor position.
|
||||
// The cursor position is 'logical' position and this considers the scale of the screen.
|
||||
//
|
||||
// SetCursorPosition works only on desktops. SetCursorPosition does nothing otherwise.
|
||||
//
|
||||
// SetCursorPosition is concurrent-safe.
|
||||
func SetCursorPosition(x, y int) {
|
||||
ui.Get().SetCursorPosition(x, y)
|
||||
}
|
||||
|
||||
// Wheel returns x and y offsets of the mouse wheel or touchpad scroll.
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/file"
|
||||
@ -72,6 +73,8 @@ type userInterfaceImpl struct {
|
||||
initMonitor *Monitor
|
||||
initFullscreen bool
|
||||
initCursorMode CursorMode
|
||||
initCursorX int
|
||||
initCursorY int
|
||||
initWindowDecorated bool
|
||||
initWindowPositionXInDIP int
|
||||
initWindowPositionYInDIP int
|
||||
@ -84,7 +87,7 @@ type userInterfaceImpl struct {
|
||||
initUnfocused bool
|
||||
|
||||
// bufferOnceSwapped must be accessed from the main thread.
|
||||
bufferOnceSwapped bool
|
||||
bufferOnceSwapped atomic.Bool
|
||||
|
||||
origWindowPosX int
|
||||
origWindowPosY int
|
||||
@ -133,6 +136,8 @@ func (u *UserInterface) init() error {
|
||||
maxWindowWidthInDIP: glfw.DontCare,
|
||||
maxWindowHeightInDIP: glfw.DontCare,
|
||||
initCursorMode: CursorModeVisible,
|
||||
initCursorX: invalidPos,
|
||||
initCursorY: invalidPos,
|
||||
initWindowDecorated: true,
|
||||
initWindowPositionXInDIP: invalidPos,
|
||||
initWindowPositionYInDIP: invalidPos,
|
||||
@ -409,6 +414,19 @@ func (u *UserInterface) setInitCursorMode(mode CursorMode) {
|
||||
u.m.Unlock()
|
||||
}
|
||||
|
||||
func (u *UserInterface) setInitCursorPosition(x, y int) {
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
u.initCursorX = x
|
||||
u.initCursorY = y
|
||||
}
|
||||
|
||||
func (u *UserInterface) getInitCursorPosition() (int, int) {
|
||||
u.m.RLock()
|
||||
defer u.m.RUnlock()
|
||||
return u.initCursorX, u.initCursorY
|
||||
}
|
||||
|
||||
func (u *UserInterface) getCursorShape() CursorShape {
|
||||
u.m.RLock()
|
||||
v := u.cursorShape
|
||||
@ -1292,14 +1310,22 @@ func (u *UserInterface) update() (float64, float64, error) {
|
||||
}
|
||||
|
||||
// On macOS, one swapping buffers seems required before entering fullscreen (#2599).
|
||||
if u.isInitFullscreen() && (u.bufferOnceSwapped || runtime.GOOS != "darwin") {
|
||||
if u.isInitFullscreen() && (u.bufferOnceSwapped.Load() || runtime.GOOS != "darwin") {
|
||||
if err := u.setFullscreen(true); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
u.setInitFullscreen(false)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && u.bufferOnceSwapped {
|
||||
if !u.bufferOnceSwapped.Load() {
|
||||
if x, y := u.getInitCursorPosition(); x != invalidPos && y != invalidPos {
|
||||
if err := u.setCursorPosition(x, y); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && u.bufferOnceSwapped.Load() {
|
||||
var err error
|
||||
u.darwinInitOnce.Do(func() {
|
||||
// On macOS, window decoration should be initialized once after buffers are swapped (#2600).
|
||||
@ -1316,7 +1342,7 @@ func (u *UserInterface) update() (float64, float64, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if u.bufferOnceSwapped {
|
||||
if u.bufferOnceSwapped.Load() {
|
||||
var err error
|
||||
u.showWindowOnce.Do(func() {
|
||||
// Show the window after first buffer swap to avoid flash of white especially on Windows.
|
||||
@ -1390,7 +1416,7 @@ func (u *UserInterface) update() (float64, float64, error) {
|
||||
|
||||
// If isRunnableOnUnfocused is false and the window is not focused, wait here.
|
||||
// For the first update, skip this check as the window might not be seen yet in some environments like ChromeOS (#3091).
|
||||
for !u.isRunnableOnUnfocused() && u.bufferOnceSwapped {
|
||||
for !u.isRunnableOnUnfocused() && u.bufferOnceSwapped.Load() {
|
||||
// In the initial state on macOS, the window is not shown (#2620).
|
||||
visible, err := u.window.GetAttrib(glfw.Visible)
|
||||
if err != nil {
|
||||
@ -1493,9 +1519,7 @@ func (u *UserInterface) updateGame() error {
|
||||
}
|
||||
|
||||
u.bufferOnceSwappedOnce.Do(func() {
|
||||
u.mainThread.Call(func() {
|
||||
u.bufferOnceSwapped = true
|
||||
})
|
||||
u.bufferOnceSwapped.Store(true)
|
||||
})
|
||||
|
||||
if unfocused {
|
||||
@ -2178,3 +2202,51 @@ func (u *UserInterface) RunOnMainThread(f func()) {
|
||||
func dipToNativePixels(x float64, scale float64) float64 {
|
||||
return dipToGLFWPixel(x, scale)
|
||||
}
|
||||
|
||||
func (u *UserInterface) CursorPosition() (x, y int) {
|
||||
if !u.isRunning() || !u.bufferOnceSwapped.Load() {
|
||||
x, y := u.getInitCursorPosition()
|
||||
if x == invalidPos || y == invalidPos {
|
||||
return 0, 0
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
return int(u.inputState.CursorX), int(u.inputState.CursorY)
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetCursorPosition(x, y int) {
|
||||
if !u.isRunning() {
|
||||
u.setInitCursorPosition(x, y)
|
||||
return
|
||||
}
|
||||
u.mainThread.Call(func() {
|
||||
if err := u.setCursorPosition(x, y); err != nil {
|
||||
u.setError(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// setCursorPosition must be called from the main thread.
|
||||
func (u *UserInterface) setCursorPosition(x, y int) error {
|
||||
m, err := u.currentMonitor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := m.DeviceScaleFactor()
|
||||
cx, cy := u.context.logicalPositionToClientPosition(float64(x), float64(y), s)
|
||||
gx := dipToGLFWPixel(cx, s)
|
||||
gy := dipToGLFWPixel(cy, s)
|
||||
u.window.SetCursorPos(gx, gy)
|
||||
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
u.inputState.CursorX = float64(x)
|
||||
u.inputState.CursorY = float64(y)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -864,3 +864,13 @@ func IsScreenTransparentAvailable() bool {
|
||||
func dipToNativePixels(x float64, scale float64) float64 {
|
||||
return x
|
||||
}
|
||||
|
||||
func (u *UserInterface) CursorPosition() (x, y int) {
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
return int(u.inputState.CursorX), int(u.inputState.CursorY)
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetCursorPosition(x, y int) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
@ -337,3 +337,13 @@ func (u *UserInterface) UsesStrictContextRestoration() bool {
|
||||
func IsScreenTransparentAvailable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserInterface) CursorPosition() (x, y int) {
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
return int(u.inputState.CursorX), int(u.inputState.CursorY)
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetCursorPosition(x, y int) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
@ -187,3 +187,13 @@ func IsScreenTransparentAvailable() bool {
|
||||
func dipToNativePixels(x float64, scale float64) float64 {
|
||||
return x
|
||||
}
|
||||
|
||||
func (u *UserInterface) CursorPosition() (x, y int) {
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
return int(u.inputState.CursorX), int(u.inputState.CursorY)
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetCursorPosition(x, y int) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
@ -180,3 +180,13 @@ func IsScreenTransparentAvailable() bool {
|
||||
func dipToNativePixels(x float64, scale float64) float64 {
|
||||
return x
|
||||
}
|
||||
|
||||
func (u *UserInterface) CursorPosition() (x, y int) {
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
return int(u.inputState.CursorX), int(u.inputState.CursorY)
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetCursorPosition(x, y int) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user