Add cursor capture functionality (#1016)

Fixes #1016
This commit is contained in:
Zachary Burkett 2019-12-13 22:30:03 -05:00 committed by Hajime Hoshi
parent ba0e8ef13f
commit ed19d6fae9
8 changed files with 144 additions and 40 deletions

28
cursormodes.go Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2019 The Ebiten 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 ebiten
import "github.com/hajimehoshi/ebiten/internal/driver"
// A CursorModeType represents
// a render and coordinate mode of a mouse cursor.
type CursorModeType int
// Cursor Modes
const (
CursorModeVisible = CursorModeType(driver.CursorModeVisible)
CursorModeHidden = CursorModeType(driver.CursorModeHidden)
CursorModeCaptured = CursorModeType(driver.CursorModeCaptured)
)

View File

@ -0,0 +1,23 @@
// Copyright 2019 The Ebiten 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 driver
type CursorMode int
const (
CursorModeVisible CursorMode = 1 << iota
CursorModeHidden
CursorModeCaptured
)

View File

@ -34,7 +34,7 @@ type UI interface {
RunWithoutMainLoop(width, height int, scale float64, title string, context UIContext, graphics Graphics) <-chan error RunWithoutMainLoop(width, height int, scale float64, title string, context UIContext, graphics Graphics) <-chan error
DeviceScaleFactor() float64 DeviceScaleFactor() float64
IsCursorVisible() bool CursorMode() CursorMode
IsFullscreen() bool IsFullscreen() bool
IsRunnableInBackground() bool IsRunnableInBackground() bool
IsVsyncEnabled() bool IsVsyncEnabled() bool
@ -46,7 +46,7 @@ type UI interface {
WindowPosition() (int, int) WindowPosition() (int, int)
IsScreenTransparent() bool IsScreenTransparent() bool
SetCursorVisible(visible bool) SetCursorMode(mode CursorMode)
SetFullscreen(fullscreen bool) SetFullscreen(fullscreen bool)
SetRunnableInBackground(runnableInBackground bool) SetRunnableInBackground(runnableInBackground bool)
SetScreenScale(scale float64) SetScreenScale(scale float64)

View File

@ -90,6 +90,7 @@ const (
) )
const ( const (
CursorDisabled = 0x00034003
CursorHidden = 0x00034002 CursorHidden = 0x00034002
CursorNormal = 0x00034001 CursorNormal = 0x00034001
NoAPI = 0 NoAPI = 0

View File

@ -58,7 +58,7 @@ type UserInterface struct {
initFullscreenWidthInDP int initFullscreenWidthInDP int
initFullscreenHeightInDP int initFullscreenHeightInDP int
initFullscreen bool initFullscreen bool
initCursorVisible bool initCursorMode driver.CursorMode
initWindowDecorated bool initWindowDecorated bool
initWindowResizable bool initWindowResizable bool
initWindowPositionX int initWindowPositionX int
@ -86,7 +86,7 @@ var (
theUI = &UserInterface{ theUI = &UserInterface{
origPosX: invalidPos, origPosX: invalidPos,
origPosY: invalidPos, origPosY: invalidPos,
initCursorVisible: true, initCursorMode: driver.CursorModeVisible,
initWindowDecorated: true, initWindowDecorated: true,
initWindowPositionX: invalidPos, initWindowPositionX: invalidPos,
initWindowPositionY: invalidPos, initWindowPositionY: invalidPos,
@ -209,16 +209,16 @@ func (u *UserInterface) setInitFullscreen(initFullscreen bool) {
u.m.Unlock() u.m.Unlock()
} }
func (u *UserInterface) isInitCursorVisible() bool { func (u *UserInterface) getInitCursorMode() driver.CursorMode {
u.m.RLock() u.m.RLock()
v := u.initCursorVisible v := u.initCursorMode
u.m.RUnlock() u.m.RUnlock()
return v return v
} }
func (u *UserInterface) setInitCursorVisible(visible bool) { func (u *UserInterface) setInitCursorMode(mode driver.CursorMode) {
u.m.Lock() u.m.Lock()
u.initCursorVisible = visible u.initCursorMode = mode
u.m.Unlock() u.m.Unlock()
} }
@ -487,27 +487,44 @@ func (u *UserInterface) adjustPosition(x, y int) (int, int) {
return x - int(ox/s), y - int(oy/s) return x - int(ox/s), y - int(oy/s)
} }
func (u *UserInterface) IsCursorVisible() bool { func (u *UserInterface) CursorMode() driver.CursorMode {
if !u.isRunning() { if !u.isRunning() {
return u.isInitCursorVisible() return u.getInitCursorMode()
} }
v := false var v driver.CursorMode
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
v = u.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal mode := u.window.GetInputMode(glfw.CursorMode)
switch mode {
case glfw.CursorNormal:
v = driver.CursorModeVisible
case glfw.CursorHidden:
v = driver.CursorModeHidden
case glfw.CursorDisabled:
v = driver.CursorModeCaptured
default:
panic(fmt.Sprintf("invalid cursor mode: %d", mode))
}
return nil return nil
}) })
return v return v
} }
func (u *UserInterface) SetCursorVisible(visible bool) { func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
if !u.isRunning() { if !u.isRunning() {
u.setInitCursorVisible(visible) u.setInitCursorMode(mode)
return return
} }
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
c := glfw.CursorNormal var c int
if !visible { switch mode {
case driver.CursorModeVisible:
c = glfw.CursorNormal
case driver.CursorModeHidden:
c = glfw.CursorHidden c = glfw.CursorHidden
case driver.CursorModeCaptured:
c = glfw.CursorDisabled
default:
panic(fmt.Sprintf("invalid cursor mode: %d", mode))
} }
u.window.SetInputMode(glfw.CursorMode, c) u.window.SetInputMode(glfw.CursorMode, c)
return nil return nil
@ -649,10 +666,12 @@ func (u *UserInterface) createWindow() error {
u.window.SetInputMode(glfw.StickyMouseButtonsMode, glfw.True) u.window.SetInputMode(glfw.StickyMouseButtonsMode, glfw.True)
u.window.SetInputMode(glfw.StickyKeysMode, glfw.True) u.window.SetInputMode(glfw.StickyKeysMode, glfw.True)
// Solve the initial properties of the window.
mode := glfw.CursorNormal mode := glfw.CursorNormal
if !u.isInitCursorVisible() { switch u.getInitCursorMode() {
case driver.CursorModeHidden:
mode = glfw.CursorHidden mode = glfw.CursorHidden
case driver.CursorModeCaptured:
mode = glfw.CursorDisabled
} }
u.window.SetInputMode(glfw.CursorMode, mode) u.window.SetInputMode(glfw.CursorMode, mode)
u.window.SetTitle(u.title) u.window.SetTitle(u.title)

View File

@ -123,12 +123,25 @@ func (u *UserInterface) adjustPosition(x, y int) (int, int) {
return int(float64(x) / s), int(float64(y) / s) return int(float64(x) / s), int(float64(y) / s)
} }
func (u *UserInterface) IsCursorVisible() bool { func (u *UserInterface) CursorMode() driver.CursorMode {
// The initial value is an empty string, so don't compare with "auto" here. if u.IsCursorVisible {
return canvas.Get("style").Get("cursor").String() != "none" return driver.CursorModeVisible
} else {
return driver.CursorModeHidden
}
} }
func (u *UserInterface) SetCursorVisible(visible bool) { func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
var visible bool
switch mode {
case driver.CursorModeVisible:
visible = true
case driver.CursorModeHidden:
visible = false
default:
return
}
if visible { if visible {
canvas.Get("style").Set("cursor", "auto") canvas.Get("style").Set("cursor", "auto")
} else { } else {

View File

@ -383,11 +383,11 @@ func (u *UserInterface) adjustPosition(x, y int) (int, int) {
return int(float64(x)/s - ox/as), int(float64(y)/s - oy/as) return int(float64(x)/s - ox/as), int(float64(y)/s - oy/as)
} }
func (u *UserInterface) IsCursorVisible() bool { func (u *UserInterface) CursorMode() driver.CursorMode {
return false return driver.CursorModeHidden
} }
func (u *UserInterface) SetCursorVisible(visible bool) { func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
// Do nothing // Do nothing
} }

44
run.go
View File

@ -261,26 +261,46 @@ func ScreenScale() float64 {
return uiDriver().ScreenScale() return uiDriver().ScreenScale()
} }
// IsCursorVisible returns a boolean value indicating whether // CursorMode returns the current cursor mode.
// the cursor is visible or not.
// //
// IsCursorVisible always returns false on mobiles. // On browsers, only CursorModeVisible and CursorModeHidden are supported.
// //
// IsCursorVisible is concurrent-safe. // CursorMode returns CursorModeHidden on mobiles.
//
// CursorMode is concurrent-safe.
func CursorMode() CursorModeType {
return CursorModeType(uiDriver().CursorMode())
}
// SetCursorMode sets the render and capture mode of the mouse cursor.
// CursorModeVisible sets the cursor to always be visible.
// CursorModeHidden hides the system cursor when over the window.
// CursorModeCaptured hides the system cursor and locks it to the window.
//
// On browsers, only CursorModeVisible and CursorModeHidden are supported.
//
// SetCursorMode does nothing on mobiles.
//
// SetCursorMode is concurrent-safe.
func SetCursorMode(mode CursorModeType) {
uiDriver().SetCursorMode(driver.CursorMode(mode))
}
// IsCursorVisible is deprecated as of 1.11.0-alpha. Use CursorMode instead.
func IsCursorVisible() bool { func IsCursorVisible() bool {
return uiDriver().IsCursorVisible() return CursorMode() == CursorModeVisible
} }
// SetCursorVisible changes the state of cursor visiblity. // SetCursorVisible is deprecated as of 1.11.0-alpha. Use SetCursorMode instead.
//
// SetCursorVisible does nothing on mobiles.
//
// SetCursorVisible is concurrent-safe.
func SetCursorVisible(visible bool) { func SetCursorVisible(visible bool) {
uiDriver().SetCursorVisible(visible) if visible {
SetCursorMode(CursorModeVisible)
} else {
SetCursorMode(CursorModeHidden)
}
} }
// SetCursorVisibility is deprecated as of 1.6.0-alpha. Use SetCursorVisible instead. // SetCursorVisibility is deprecated as of 1.6.0-alpha. Use SetCursorMode instead.
func SetCursorVisibility(visible bool) { func SetCursorVisibility(visible bool) {
SetCursorVisible(visible) SetCursorVisible(visible)
} }