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
DeviceScaleFactor() float64
IsCursorVisible() bool
CursorMode() CursorMode
IsFullscreen() bool
IsRunnableInBackground() bool
IsVsyncEnabled() bool
@ -46,7 +46,7 @@ type UI interface {
WindowPosition() (int, int)
IsScreenTransparent() bool
SetCursorVisible(visible bool)
SetCursorMode(mode CursorMode)
SetFullscreen(fullscreen bool)
SetRunnableInBackground(runnableInBackground bool)
SetScreenScale(scale float64)

View File

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

View File

@ -58,7 +58,7 @@ type UserInterface struct {
initFullscreenWidthInDP int
initFullscreenHeightInDP int
initFullscreen bool
initCursorVisible bool
initCursorMode driver.CursorMode
initWindowDecorated bool
initWindowResizable bool
initWindowPositionX int
@ -86,7 +86,7 @@ var (
theUI = &UserInterface{
origPosX: invalidPos,
origPosY: invalidPos,
initCursorVisible: true,
initCursorMode: driver.CursorModeVisible,
initWindowDecorated: true,
initWindowPositionX: invalidPos,
initWindowPositionY: invalidPos,
@ -209,16 +209,16 @@ func (u *UserInterface) setInitFullscreen(initFullscreen bool) {
u.m.Unlock()
}
func (u *UserInterface) isInitCursorVisible() bool {
func (u *UserInterface) getInitCursorMode() driver.CursorMode {
u.m.RLock()
v := u.initCursorVisible
v := u.initCursorMode
u.m.RUnlock()
return v
}
func (u *UserInterface) setInitCursorVisible(visible bool) {
func (u *UserInterface) setInitCursorMode(mode driver.CursorMode) {
u.m.Lock()
u.initCursorVisible = visible
u.initCursorMode = mode
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)
}
func (u *UserInterface) IsCursorVisible() bool {
func (u *UserInterface) CursorMode() driver.CursorMode {
if !u.isRunning() {
return u.isInitCursorVisible()
return u.getInitCursorMode()
}
v := false
var v driver.CursorMode
_ = 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 v
}
func (u *UserInterface) SetCursorVisible(visible bool) {
func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
if !u.isRunning() {
u.setInitCursorVisible(visible)
u.setInitCursorMode(mode)
return
}
_ = u.t.Call(func() error {
c := glfw.CursorNormal
if !visible {
var c int
switch mode {
case driver.CursorModeVisible:
c = glfw.CursorNormal
case driver.CursorModeHidden:
c = glfw.CursorHidden
case driver.CursorModeCaptured:
c = glfw.CursorDisabled
default:
panic(fmt.Sprintf("invalid cursor mode: %d", mode))
}
u.window.SetInputMode(glfw.CursorMode, c)
return nil
@ -649,10 +666,12 @@ func (u *UserInterface) createWindow() error {
u.window.SetInputMode(glfw.StickyMouseButtonsMode, glfw.True)
u.window.SetInputMode(glfw.StickyKeysMode, glfw.True)
// Solve the initial properties of the window.
mode := glfw.CursorNormal
if !u.isInitCursorVisible() {
switch u.getInitCursorMode() {
case driver.CursorModeHidden:
mode = glfw.CursorHidden
case driver.CursorModeCaptured:
mode = glfw.CursorDisabled
}
u.window.SetInputMode(glfw.CursorMode, mode)
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)
}
func (u *UserInterface) IsCursorVisible() bool {
// The initial value is an empty string, so don't compare with "auto" here.
return canvas.Get("style").Get("cursor").String() != "none"
func (u *UserInterface) CursorMode() driver.CursorMode {
if u.IsCursorVisible {
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 {
canvas.Get("style").Set("cursor", "auto")
} 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)
}
func (u *UserInterface) IsCursorVisible() bool {
return false
func (u *UserInterface) CursorMode() driver.CursorMode {
return driver.CursorModeHidden
}
func (u *UserInterface) SetCursorVisible(visible bool) {
func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
// Do nothing
}

44
run.go
View File

@ -261,26 +261,46 @@ func ScreenScale() float64 {
return uiDriver().ScreenScale()
}
// IsCursorVisible returns a boolean value indicating whether
// the cursor is visible or not.
// CursorMode returns the current cursor mode.
//
// 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 {
return uiDriver().IsCursorVisible()
return CursorMode() == CursorModeVisible
}
// SetCursorVisible changes the state of cursor visiblity.
//
// SetCursorVisible does nothing on mobiles.
//
// SetCursorVisible is concurrent-safe.
// SetCursorVisible is deprecated as of 1.11.0-alpha. Use SetCursorMode instead.
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) {
SetCursorVisible(visible)
}