2015-01-01 17:20:20 +01:00
|
|
|
// Copyright 2015 Hajime Hoshi
|
2014-12-24 03:04:10 +01:00
|
|
|
//
|
|
|
|
// 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.
|
2014-12-09 15:16:04 +01:00
|
|
|
|
2021-06-10 18:03:35 +02:00
|
|
|
//go:build (darwin || freebsd || linux || windows) && !android && !ios
|
2017-05-11 12:09:13 +02:00
|
|
|
// +build darwin freebsd linux windows
|
2016-05-18 20:17:50 +02:00
|
|
|
// +build !android
|
2016-06-15 17:49:22 +02:00
|
|
|
// +build !ios
|
2015-01-02 07:20:05 +01:00
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
package glfw
|
2014-12-05 18:26:02 +01:00
|
|
|
|
|
|
|
import (
|
2019-12-07 11:28:15 +01:00
|
|
|
"fmt"
|
2017-09-22 21:12:02 +02:00
|
|
|
"image"
|
2019-11-17 11:58:41 +01:00
|
|
|
"os"
|
2014-12-14 10:57:29 +01:00
|
|
|
"runtime"
|
2016-09-03 14:14:06 +02:00
|
|
|
"sync"
|
2021-02-10 18:38:12 +01:00
|
|
|
"sync/atomic"
|
2015-01-07 17:32:37 +01:00
|
|
|
"time"
|
2015-06-20 18:33:28 +02:00
|
|
|
|
2020-10-03 19:35:13 +02:00
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/devicescale"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/hooks"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/thread"
|
2014-12-05 18:26:02 +01:00
|
|
|
)
|
|
|
|
|
2021-04-14 18:59:31 +02:00
|
|
|
func driverCursorModeToGLFWCursorMode(mode driver.CursorMode) int {
|
|
|
|
switch mode {
|
|
|
|
case driver.CursorModeVisible:
|
|
|
|
return glfw.CursorNormal
|
|
|
|
case driver.CursorModeHidden:
|
|
|
|
return glfw.CursorHidden
|
|
|
|
case driver.CursorModeCaptured:
|
|
|
|
return glfw.CursorDisabled
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("glfw: invalid driver.CursorMode: %d", mode))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
type UserInterface struct {
|
2020-04-02 17:06:42 +02:00
|
|
|
context driver.UIContext
|
|
|
|
title string
|
|
|
|
window *glfw.Window
|
2019-12-09 18:37:10 +01:00
|
|
|
|
|
|
|
// windowWidth and windowHeight represents a window size.
|
2021-04-16 21:43:20 +02:00
|
|
|
// The units are device-dependent pixels.
|
|
|
|
windowWidth int
|
|
|
|
windowHeight int
|
|
|
|
|
|
|
|
// The units are device-independent pixels.
|
|
|
|
minWindowWidthInDP int
|
|
|
|
minWindowHeightInDP int
|
|
|
|
maxWindowWidthInDP int
|
|
|
|
maxWindowHeightInDP int
|
2018-01-02 16:23:18 +01:00
|
|
|
|
2021-02-10 18:38:12 +01:00
|
|
|
running uint32
|
2020-03-20 16:34:12 +01:00
|
|
|
toChangeSize bool
|
|
|
|
origPosX int
|
|
|
|
origPosY int
|
|
|
|
runnableOnUnfocused bool
|
|
|
|
vsync bool
|
2021-02-10 19:45:35 +01:00
|
|
|
iconImages []image.Image
|
2021-04-11 07:21:38 +02:00
|
|
|
cursorShape driver.CursorShape
|
2018-01-02 16:23:18 +01:00
|
|
|
|
2021-04-18 15:07:26 +02:00
|
|
|
// setSizeCallbackEnabled must be accessed from the main thread.
|
|
|
|
setSizeCallbackEnabled bool
|
|
|
|
|
2021-01-19 18:21:51 +01:00
|
|
|
// err must be accessed from the main thread.
|
|
|
|
err error
|
|
|
|
|
2019-12-08 12:24:20 +01:00
|
|
|
lastDeviceScaleFactor float64
|
2018-08-05 14:19:22 +02:00
|
|
|
|
2021-02-10 20:00:37 +01:00
|
|
|
// These values are not changed after initialized.
|
|
|
|
// TODO: the fullscreen size should be updated when the initial window position is changed?
|
2019-12-08 16:11:18 +01:00
|
|
|
initMonitor *glfw.Monitor
|
|
|
|
initFullscreenWidthInDP int
|
|
|
|
initFullscreenHeightInDP int
|
2021-02-10 20:00:37 +01:00
|
|
|
|
|
|
|
initTitle string
|
|
|
|
initVsync bool
|
|
|
|
initFullscreen bool
|
|
|
|
initCursorMode driver.CursorMode
|
|
|
|
initWindowDecorated bool
|
|
|
|
initWindowResizable bool
|
|
|
|
initWindowPositionXInDP int
|
|
|
|
initWindowPositionYInDP int
|
|
|
|
initWindowWidthInDP int
|
|
|
|
initWindowHeightInDP int
|
|
|
|
initWindowFloating bool
|
|
|
|
initWindowMaximized bool
|
|
|
|
initScreenTransparent bool
|
|
|
|
initFocused bool
|
2018-01-02 16:23:18 +01:00
|
|
|
|
2020-06-14 04:50:58 +02:00
|
|
|
vsyncInited bool
|
|
|
|
|
2020-01-03 09:32:46 +01:00
|
|
|
input Input
|
|
|
|
iwindow window
|
2019-03-30 13:50:32 +01:00
|
|
|
|
2021-06-13 14:43:23 +02:00
|
|
|
sizeCallback glfw.SizeCallback
|
|
|
|
framebufferSizeCallback glfw.FramebufferSizeCallback
|
|
|
|
framebufferSizeCallbackCh chan struct{}
|
|
|
|
|
2020-10-20 19:55:17 +02:00
|
|
|
t thread.Thread
|
2019-08-18 18:01:43 +02:00
|
|
|
m sync.RWMutex
|
2016-03-24 16:38:30 +01:00
|
|
|
}
|
|
|
|
|
2019-02-24 15:31:26 +01:00
|
|
|
const (
|
|
|
|
maxInt = int(^uint(0) >> 1)
|
|
|
|
minInt = -maxInt - 1
|
2019-12-22 04:46:57 +01:00
|
|
|
invalidPos = minInt
|
2019-02-24 15:31:26 +01:00
|
|
|
)
|
|
|
|
|
2017-07-30 13:26:40 +02:00
|
|
|
var (
|
2019-04-07 03:42:55 +02:00
|
|
|
theUI = &UserInterface{
|
2020-10-06 17:30:46 +02:00
|
|
|
runnableOnUnfocused: true,
|
2021-04-16 21:43:20 +02:00
|
|
|
minWindowWidthInDP: glfw.DontCare,
|
|
|
|
minWindowHeightInDP: glfw.DontCare,
|
|
|
|
maxWindowWidthInDP: glfw.DontCare,
|
|
|
|
maxWindowHeightInDP: glfw.DontCare,
|
2019-12-22 04:46:57 +01:00
|
|
|
origPosX: invalidPos,
|
|
|
|
origPosY: invalidPos,
|
2020-06-14 04:50:58 +02:00
|
|
|
initVsync: true,
|
2019-12-21 09:34:58 +01:00
|
|
|
initCursorMode: driver.CursorModeVisible,
|
|
|
|
initWindowDecorated: true,
|
2019-12-22 04:46:57 +01:00
|
|
|
initWindowPositionXInDP: invalidPos,
|
|
|
|
initWindowPositionYInDP: invalidPos,
|
|
|
|
initWindowWidthInDP: 640,
|
|
|
|
initWindowHeightInDP: 480,
|
2020-02-09 15:03:03 +01:00
|
|
|
initFocused: true,
|
|
|
|
vsync: true,
|
2017-07-30 13:26:40 +02:00
|
|
|
}
|
|
|
|
)
|
2016-03-24 16:38:30 +01:00
|
|
|
|
2019-04-07 12:27:30 +02:00
|
|
|
func init() {
|
|
|
|
theUI.input.ui = theUI
|
2019-12-24 16:05:56 +01:00
|
|
|
theUI.iwindow.ui = theUI
|
2019-04-07 12:27:30 +02:00
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func Get() *UserInterface {
|
|
|
|
return theUI
|
|
|
|
}
|
|
|
|
|
2016-07-23 13:25:52 +02:00
|
|
|
func init() {
|
2018-12-18 17:01:16 +01:00
|
|
|
hideConsoleWindowOnWindows()
|
2018-05-04 16:55:23 +02:00
|
|
|
if err := initialize(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-11-25 15:13:44 +01:00
|
|
|
glfw.SetMonitorCallback(func(monitor *glfw.Monitor, event glfw.PeripheralEvent) {
|
2018-11-07 12:54:37 +01:00
|
|
|
cacheMonitors()
|
|
|
|
})
|
|
|
|
cacheMonitors()
|
2016-07-23 13:25:52 +02:00
|
|
|
}
|
|
|
|
|
2021-04-11 07:21:38 +02:00
|
|
|
var glfwSystemCursors = map[driver.CursorShape]*glfw.Cursor{}
|
|
|
|
|
2016-07-23 13:25:52 +02:00
|
|
|
func initialize() error {
|
2016-05-07 15:27:10 +02:00
|
|
|
if err := glfw.Init(); err != nil {
|
2016-07-23 13:25:52 +02:00
|
|
|
return err
|
2014-12-05 18:26:02 +01:00
|
|
|
}
|
2021-04-11 07:21:38 +02:00
|
|
|
|
2014-12-17 12:03:26 +01:00
|
|
|
glfw.WindowHint(glfw.Visible, glfw.False)
|
2020-07-05 08:00:56 +02:00
|
|
|
glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
|
2017-09-13 20:37:38 +02:00
|
|
|
|
2018-12-18 17:01:16 +01:00
|
|
|
// Create a window to set the initial monitor.
|
|
|
|
w, err := glfw.CreateWindow(16, 16, "", nil, nil)
|
2014-12-07 20:22:50 +01:00
|
|
|
if err != nil {
|
2016-07-23 13:25:52 +02:00
|
|
|
return err
|
2014-12-07 20:22:50 +01:00
|
|
|
}
|
2019-07-21 16:41:51 +02:00
|
|
|
if w == nil {
|
|
|
|
// This can happen on Windows Remote Desktop (#903).
|
2019-11-30 19:34:09 +01:00
|
|
|
panic("glfw: glfw.CreateWindow must not return nil")
|
2019-07-21 16:41:51 +02:00
|
|
|
}
|
2021-02-10 19:45:35 +01:00
|
|
|
defer w.Destroy()
|
2019-07-21 16:41:51 +02:00
|
|
|
|
2021-02-10 19:45:35 +01:00
|
|
|
m := currentMonitor(w)
|
|
|
|
theUI.initMonitor = m
|
|
|
|
v := m.GetVideoMode()
|
|
|
|
scale := devicescale.GetAt(currentMonitor(w).GetPos())
|
|
|
|
theUI.initFullscreenWidthInDP = int(fromGLFWMonitorPixel(float64(v.Width), scale))
|
|
|
|
theUI.initFullscreenHeightInDP = int(fromGLFWMonitorPixel(float64(v.Height), scale))
|
2017-08-12 08:39:41 +02:00
|
|
|
|
2021-04-11 07:21:38 +02:00
|
|
|
// Create system cursors. These cursors are destroyed at glfw.Terminate().
|
|
|
|
glfwSystemCursors[driver.CursorShapeDefault] = nil
|
|
|
|
glfwSystemCursors[driver.CursorShapeText] = glfw.CreateStandardCursor(glfw.IBeamCursor)
|
|
|
|
glfwSystemCursors[driver.CursorShapeCrosshair] = glfw.CreateStandardCursor(glfw.CrosshairCursor)
|
|
|
|
glfwSystemCursors[driver.CursorShapePointer] = glfw.CreateStandardCursor(glfw.HandCursor)
|
2021-05-02 07:50:50 +02:00
|
|
|
glfwSystemCursors[driver.CursorShapeEWResize] = glfw.CreateStandardCursor(glfw.HResizeCursor)
|
|
|
|
glfwSystemCursors[driver.CursorShapeNSResize] = glfw.CreateStandardCursor(glfw.VResizeCursor)
|
2021-04-11 07:21:38 +02:00
|
|
|
|
2016-07-23 13:25:52 +02:00
|
|
|
return nil
|
2014-12-17 12:03:26 +01:00
|
|
|
}
|
2014-12-10 15:52:37 +01:00
|
|
|
|
2018-11-07 12:54:37 +01:00
|
|
|
type cachedMonitor struct {
|
|
|
|
m *glfw.Monitor
|
|
|
|
vm *glfw.VidMode
|
|
|
|
// Pos of monitor in virtual coords
|
|
|
|
x int
|
|
|
|
y int
|
|
|
|
}
|
|
|
|
|
|
|
|
// monitors is the monitor list cache for desktop glfw compile targets.
|
|
|
|
// populated by 'cacheMonitors' which is called on init and every
|
|
|
|
// monitor config change event.
|
2018-11-14 17:08:36 +01:00
|
|
|
//
|
|
|
|
// monitors must be manipulated on the main thread.
|
2018-11-07 12:54:37 +01:00
|
|
|
var monitors []*cachedMonitor
|
|
|
|
|
|
|
|
func cacheMonitors() {
|
2018-11-14 17:08:36 +01:00
|
|
|
monitors = nil
|
2018-11-07 12:54:37 +01:00
|
|
|
ms := glfw.GetMonitors()
|
|
|
|
for _, m := range ms {
|
|
|
|
x, y := m.GetPos()
|
|
|
|
monitors = append(monitors, &cachedMonitor{
|
|
|
|
m: m,
|
|
|
|
vm: m.GetVideoMode(),
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-23 20:05:38 +02:00
|
|
|
// getCachedMonitor returns a monitor for the given window x/y,
|
|
|
|
// or returns nil if monitor is not found.
|
2018-11-14 17:08:36 +01:00
|
|
|
//
|
|
|
|
// getCachedMonitor must be called on the main thread.
|
2020-08-23 20:05:38 +02:00
|
|
|
func getCachedMonitor(wx, wy int) *cachedMonitor {
|
2018-11-07 12:54:37 +01:00
|
|
|
for _, m := range monitors {
|
2020-03-28 15:54:55 +01:00
|
|
|
if m.x <= wx && wx < m.x+m.vm.Width && m.y <= wy && wy < m.y+m.vm.Height {
|
2020-08-23 20:05:38 +02:00
|
|
|
return m
|
2018-11-07 12:54:37 +01:00
|
|
|
}
|
|
|
|
}
|
2020-08-23 20:05:38 +02:00
|
|
|
return nil
|
2018-11-07 12:54:37 +01:00
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) isRunning() bool {
|
2021-02-10 18:38:12 +01:00
|
|
|
return atomic.LoadUint32(&u.running) != 0
|
2016-09-03 14:14:06 +02:00
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) setRunning(running bool) {
|
2021-02-10 18:38:12 +01:00
|
|
|
if running {
|
|
|
|
atomic.StoreUint32(&u.running, 1)
|
|
|
|
} else {
|
|
|
|
atomic.StoreUint32(&u.running, 0)
|
|
|
|
}
|
2017-08-02 17:19:04 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 17:56:35 +02:00
|
|
|
func (u *UserInterface) getWindowSizeLimits() (minw, minh, maxw, maxh int) {
|
|
|
|
u.m.RLock()
|
|
|
|
defer u.m.RUnlock()
|
2021-04-20 04:04:13 +02:00
|
|
|
|
|
|
|
minw, minh, maxw, maxh = -1, -1, -1, -1
|
|
|
|
if u.minWindowWidthInDP >= 0 {
|
|
|
|
minw = int(u.toGLFWPixel(float64(u.minWindowWidthInDP)))
|
|
|
|
}
|
|
|
|
if u.minWindowHeightInDP >= 0 {
|
|
|
|
minh = int(u.toGLFWPixel(float64(u.minWindowHeightInDP)))
|
|
|
|
}
|
|
|
|
if u.maxWindowWidthInDP >= 0 {
|
|
|
|
maxw = int(u.toGLFWPixel(float64(u.maxWindowWidthInDP)))
|
|
|
|
}
|
|
|
|
if u.maxWindowHeightInDP >= 0 {
|
|
|
|
maxh = int(u.toGLFWPixel(float64(u.maxWindowHeightInDP)))
|
|
|
|
}
|
|
|
|
return
|
2021-04-17 17:56:35 +02:00
|
|
|
}
|
|
|
|
|
2021-04-16 21:43:20 +02:00
|
|
|
func (u *UserInterface) getWindowSizeLimitsInDP() (minw, minh, maxw, maxh int) {
|
2021-04-16 19:27:04 +02:00
|
|
|
u.m.RLock()
|
|
|
|
defer u.m.RUnlock()
|
2021-04-16 21:43:20 +02:00
|
|
|
return u.minWindowWidthInDP, u.minWindowHeightInDP, u.maxWindowWidthInDP, u.maxWindowHeightInDP
|
2021-04-16 19:27:04 +02:00
|
|
|
}
|
|
|
|
|
2021-04-16 21:43:20 +02:00
|
|
|
func (u *UserInterface) setWindowSizeLimitsInDP(minw, minh, maxw, maxh int) bool {
|
2021-04-16 19:27:04 +02:00
|
|
|
u.m.RLock()
|
|
|
|
defer u.m.RUnlock()
|
2021-04-16 21:43:20 +02:00
|
|
|
if u.minWindowWidthInDP == minw && u.minWindowHeightInDP == minh && u.maxWindowWidthInDP == maxw && u.maxWindowHeightInDP == maxh {
|
2021-04-16 19:27:04 +02:00
|
|
|
return false
|
|
|
|
}
|
2021-04-16 21:43:20 +02:00
|
|
|
u.minWindowWidthInDP = minw
|
|
|
|
u.minWindowHeightInDP = minh
|
|
|
|
u.maxWindowWidthInDP = maxw
|
|
|
|
u.maxWindowHeightInDP = maxh
|
2021-04-16 19:27:04 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-12-21 17:41:51 +01:00
|
|
|
func (u *UserInterface) getInitTitle() string {
|
|
|
|
u.m.RLock()
|
|
|
|
v := u.initTitle
|
|
|
|
u.m.RUnlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setInitTitle(title string) {
|
|
|
|
u.m.RLock()
|
|
|
|
u.initTitle = title
|
|
|
|
u.m.RUnlock()
|
|
|
|
}
|
|
|
|
|
2020-06-14 04:50:58 +02:00
|
|
|
func (u *UserInterface) isInitVsyncEnabled() bool {
|
|
|
|
u.m.RLock()
|
|
|
|
v := u.initVsync
|
|
|
|
u.m.RUnlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) isInitFullscreen() bool {
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RLock()
|
2017-08-02 17:19:04 +02:00
|
|
|
v := u.initFullscreen
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RUnlock()
|
2017-08-02 17:19:04 +02:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) setInitFullscreen(initFullscreen bool) {
|
2017-08-02 17:19:04 +02:00
|
|
|
u.m.Lock()
|
|
|
|
u.initFullscreen = initFullscreen
|
|
|
|
u.m.Unlock()
|
2016-09-03 14:14:06 +02:00
|
|
|
}
|
|
|
|
|
2019-12-14 04:30:03 +01:00
|
|
|
func (u *UserInterface) getInitCursorMode() driver.CursorMode {
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RLock()
|
2019-12-14 04:30:03 +01:00
|
|
|
v := u.initCursorMode
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RUnlock()
|
2017-08-12 08:39:41 +02:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-12-14 04:30:03 +01:00
|
|
|
func (u *UserInterface) setInitCursorMode(mode driver.CursorMode) {
|
2017-08-12 08:39:41 +02:00
|
|
|
u.m.Lock()
|
2019-12-14 04:30:03 +01:00
|
|
|
u.initCursorMode = mode
|
2017-08-12 08:39:41 +02:00
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2021-04-11 07:21:38 +02:00
|
|
|
func (u *UserInterface) getCursorShape() driver.CursorShape {
|
|
|
|
u.m.RLock()
|
|
|
|
v := u.cursorShape
|
|
|
|
u.m.RUnlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setCursorShape(shape driver.CursorShape) driver.CursorShape {
|
|
|
|
u.m.Lock()
|
|
|
|
old := u.cursorShape
|
|
|
|
u.cursorShape = shape
|
|
|
|
u.m.Unlock()
|
|
|
|
return old
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) isInitWindowDecorated() bool {
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RLock()
|
2017-09-13 20:37:38 +02:00
|
|
|
v := u.initWindowDecorated
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RUnlock()
|
2017-09-13 20:37:38 +02:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) setInitWindowDecorated(decorated bool) {
|
2017-09-13 20:37:38 +02:00
|
|
|
u.m.Lock()
|
|
|
|
u.initWindowDecorated = decorated
|
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2020-03-20 16:34:12 +01:00
|
|
|
func (u *UserInterface) isRunnableOnUnfocused() bool {
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RLock()
|
2020-03-20 16:34:12 +01:00
|
|
|
v := u.runnableOnUnfocused
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RUnlock()
|
2017-08-02 16:37:50 +02:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2020-03-20 16:34:12 +01:00
|
|
|
func (u *UserInterface) setRunnableOnUnfocused(runnableOnUnfocused bool) {
|
2017-08-02 16:37:50 +02:00
|
|
|
u.m.Lock()
|
2020-03-20 16:34:12 +01:00
|
|
|
u.runnableOnUnfocused = runnableOnUnfocused
|
2017-08-02 16:37:50 +02:00
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) isInitWindowResizable() bool {
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RLock()
|
2019-01-06 16:21:59 +01:00
|
|
|
v := u.initWindowResizable
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RUnlock()
|
2019-01-06 16:21:59 +01:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) setInitWindowResizable(resizable bool) {
|
2019-01-06 16:21:59 +01:00
|
|
|
u.m.Lock()
|
|
|
|
u.initWindowResizable = resizable
|
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-11-30 16:07:41 +01:00
|
|
|
func (u *UserInterface) isInitScreenTransparent() bool {
|
|
|
|
u.m.RLock()
|
|
|
|
v := u.initScreenTransparent
|
|
|
|
u.m.RUnlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setInitScreenTransparent(transparent bool) {
|
|
|
|
u.m.RLock()
|
|
|
|
u.initScreenTransparent = transparent
|
|
|
|
u.m.RUnlock()
|
|
|
|
}
|
|
|
|
|
2021-02-07 13:16:09 +01:00
|
|
|
func (u *UserInterface) getIconImages() []image.Image {
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RLock()
|
2021-02-07 13:16:09 +01:00
|
|
|
i := u.iconImages
|
2019-08-18 18:01:43 +02:00
|
|
|
u.m.RUnlock()
|
2017-09-22 21:12:02 +02:00
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2021-02-07 13:16:09 +01:00
|
|
|
func (u *UserInterface) setIconImages(iconImages []image.Image) {
|
2017-09-22 21:12:02 +02:00
|
|
|
u.m.Lock()
|
2021-02-07 13:16:09 +01:00
|
|
|
u.iconImages = iconImages
|
2017-09-22 21:12:02 +02:00
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-11-30 16:21:11 +01:00
|
|
|
func (u *UserInterface) getInitWindowPosition() (int, int) {
|
2019-11-30 16:11:46 +01:00
|
|
|
u.m.RLock()
|
|
|
|
defer u.m.RUnlock()
|
2019-12-22 04:46:57 +01:00
|
|
|
if u.initWindowPositionXInDP != invalidPos && u.initWindowPositionYInDP != invalidPos {
|
2019-12-21 09:34:58 +01:00
|
|
|
return u.initWindowPositionXInDP, u.initWindowPositionYInDP
|
2019-11-30 16:11:46 +01:00
|
|
|
}
|
2019-12-22 04:46:57 +01:00
|
|
|
return invalidPos, invalidPos
|
2019-11-30 16:11:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setInitWindowPosition(x, y int) {
|
|
|
|
u.m.Lock()
|
|
|
|
defer u.m.Unlock()
|
|
|
|
|
2019-12-21 09:34:58 +01:00
|
|
|
u.initWindowPositionXInDP = x
|
|
|
|
u.initWindowPositionYInDP = y
|
2019-11-30 16:11:46 +01:00
|
|
|
}
|
|
|
|
|
2019-12-16 02:52:53 +01:00
|
|
|
func (u *UserInterface) getInitWindowSize() (int, int) {
|
|
|
|
u.m.Lock()
|
|
|
|
w, h := u.initWindowWidthInDP, u.initWindowHeightInDP
|
|
|
|
u.m.Unlock()
|
|
|
|
return w, h
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setInitWindowSize(width, height int) {
|
|
|
|
u.m.Lock()
|
|
|
|
u.initWindowWidthInDP, u.initWindowHeightInDP = width, height
|
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2020-03-20 13:46:23 +01:00
|
|
|
func (u *UserInterface) isInitWindowFloating() bool {
|
|
|
|
u.m.Lock()
|
|
|
|
f := u.initWindowFloating
|
|
|
|
u.m.Unlock()
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setInitWindowFloating(floating bool) {
|
|
|
|
u.m.Lock()
|
|
|
|
u.initWindowFloating = floating
|
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2020-03-21 11:26:28 +01:00
|
|
|
func (u *UserInterface) isInitWindowMaximized() bool {
|
|
|
|
u.m.Lock()
|
2021-06-13 16:25:24 +02:00
|
|
|
m := u.initWindowMaximized
|
2020-03-21 11:26:28 +01:00
|
|
|
u.m.Unlock()
|
2021-06-13 16:25:24 +02:00
|
|
|
return m
|
2020-03-21 11:26:28 +01:00
|
|
|
}
|
|
|
|
|
2021-06-13 16:25:24 +02:00
|
|
|
func (u *UserInterface) setInitWindowMaximized(maximized bool) {
|
2020-03-21 11:26:28 +01:00
|
|
|
u.m.Lock()
|
2021-06-13 16:25:24 +02:00
|
|
|
u.initWindowMaximized = maximized
|
2020-08-23 19:55:31 +02:00
|
|
|
u.m.Unlock()
|
2020-02-09 15:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) isInitFocused() bool {
|
|
|
|
u.m.Lock()
|
|
|
|
v := u.initFocused
|
|
|
|
u.m.Unlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) setInitFocused(focused bool) {
|
|
|
|
u.m.Lock()
|
|
|
|
u.initFocused = focused
|
2020-03-21 11:26:28 +01:00
|
|
|
u.m.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
2018-12-18 17:17:59 +01:00
|
|
|
if !u.isRunning() {
|
2019-12-08 16:11:18 +01:00
|
|
|
return u.initFullscreenWidthInDP, u.initFullscreenHeightInDP
|
2018-12-18 17:17:59 +01:00
|
|
|
}
|
|
|
|
|
2019-12-08 13:49:42 +01:00
|
|
|
var w, h int
|
2019-06-05 17:19:12 +02:00
|
|
|
_ = u.t.Call(func() error {
|
2020-08-23 20:27:38 +02:00
|
|
|
v := currentMonitor(u.window).GetVideoMode()
|
2021-02-10 19:45:35 +01:00
|
|
|
s := u.deviceScaleFactor()
|
|
|
|
w = int(fromGLFWMonitorPixel(float64(v.Width), s))
|
|
|
|
h = int(fromGLFWMonitorPixel(float64(v.Height), s))
|
2018-12-18 17:17:59 +01:00
|
|
|
return nil
|
|
|
|
})
|
2019-12-08 13:49:42 +01:00
|
|
|
return w, h
|
2018-05-04 09:09:55 +02:00
|
|
|
}
|
|
|
|
|
2019-02-07 09:19:24 +01:00
|
|
|
// isFullscreen must be called from the main thread.
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) isFullscreen() bool {
|
2017-08-12 11:31:16 +02:00
|
|
|
if !u.isRunning() {
|
2019-11-30 19:34:09 +01:00
|
|
|
panic("glfw: isFullscreen can't be called before the main loop starts")
|
2017-08-12 11:31:16 +02:00
|
|
|
}
|
|
|
|
return u.window.GetMonitor() != nil
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) IsFullscreen() bool {
|
2017-06-29 17:35:34 +02:00
|
|
|
if !u.isRunning() {
|
2017-08-02 17:19:04 +02:00
|
|
|
return u.isInitFullscreen()
|
2017-06-29 17:35:34 +02:00
|
|
|
}
|
2018-02-24 10:10:39 +01:00
|
|
|
b := false
|
2019-06-05 17:19:12 +02:00
|
|
|
_ = u.t.Call(func() error {
|
2019-02-07 09:19:24 +01:00
|
|
|
b = u.isFullscreen()
|
2018-02-24 10:10:39 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return b
|
2017-06-29 17:35:34 +02:00
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
2017-08-02 18:07:04 +02:00
|
|
|
if !u.isRunning() {
|
|
|
|
u.setInitFullscreen(fullscreen)
|
|
|
|
return
|
|
|
|
}
|
2019-12-09 18:37:10 +01:00
|
|
|
|
|
|
|
var update bool
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
update = u.isFullscreen() != fullscreen
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if !update {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = u.t.Call(func() error {
|
2021-04-18 10:35:46 +02:00
|
|
|
if u.isNativeFullscreen() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
w, h := u.windowWidth, u.windowHeight
|
|
|
|
u.setWindowSize(w, h, fullscreen)
|
2019-12-09 18:37:10 +01:00
|
|
|
return nil
|
|
|
|
})
|
2017-08-02 18:07:04 +02:00
|
|
|
}
|
|
|
|
|
2020-03-20 16:08:40 +01:00
|
|
|
func (u *UserInterface) IsFocused() bool {
|
2020-01-16 02:47:23 +01:00
|
|
|
if !u.isRunning() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-03-20 16:08:40 +01:00
|
|
|
var focused bool
|
2020-01-16 02:47:23 +01:00
|
|
|
_ = u.t.Call(func() error {
|
2020-03-20 16:08:40 +01:00
|
|
|
focused = u.window.GetAttrib(glfw.Focused) == glfw.True
|
2020-01-16 02:47:23 +01:00
|
|
|
return nil
|
|
|
|
})
|
2020-03-20 16:08:40 +01:00
|
|
|
return focused
|
2020-01-16 02:47:23 +01:00
|
|
|
}
|
|
|
|
|
2020-03-20 16:34:12 +01:00
|
|
|
func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
|
|
|
|
u.setRunnableOnUnfocused(runnableOnUnfocused)
|
2017-08-02 16:37:50 +02:00
|
|
|
}
|
|
|
|
|
2020-03-20 16:34:12 +01:00
|
|
|
func (u *UserInterface) IsRunnableOnUnfocused() bool {
|
|
|
|
return u.isRunnableOnUnfocused()
|
2017-08-02 16:37:50 +02:00
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) SetVsyncEnabled(enabled bool) {
|
2018-07-14 13:50:09 +02:00
|
|
|
if !u.isRunning() {
|
2018-07-16 19:37:22 +02:00
|
|
|
// In general, m is used for locking init* values.
|
2019-12-09 18:37:10 +01:00
|
|
|
// m is not used for updating vsync in setWindowSize so far, but
|
2018-07-16 19:37:22 +02:00
|
|
|
// it should be OK since any goroutines can't reach here when
|
2019-12-09 18:37:10 +01:00
|
|
|
// the game already starts and setWindowSize can be called.
|
2019-08-18 18:47:55 +02:00
|
|
|
u.m.Lock()
|
2020-06-14 04:50:58 +02:00
|
|
|
u.initVsync = enabled
|
2019-08-18 18:47:55 +02:00
|
|
|
u.m.Unlock()
|
2018-07-14 13:50:09 +02:00
|
|
|
return
|
|
|
|
}
|
2019-12-09 18:37:10 +01:00
|
|
|
_ = u.t.Call(func() error {
|
2020-06-14 04:50:58 +02:00
|
|
|
if !u.vsyncInited {
|
|
|
|
u.m.Lock()
|
|
|
|
u.initVsync = enabled
|
|
|
|
u.m.Unlock()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
u.vsync = enabled
|
|
|
|
u.updateVsync()
|
2019-12-09 18:37:10 +01:00
|
|
|
return nil
|
|
|
|
})
|
2018-07-14 13:50:09 +02:00
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) IsVsyncEnabled() bool {
|
2020-06-14 04:50:58 +02:00
|
|
|
if !u.isRunning() {
|
|
|
|
return u.isInitVsyncEnabled()
|
|
|
|
}
|
|
|
|
var v bool
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
if !u.vsyncInited {
|
|
|
|
v = u.isInitVsyncEnabled()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
v = u.vsync
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return v
|
2018-07-14 13:50:09 +02:00
|
|
|
}
|
|
|
|
|
2019-12-14 04:30:03 +01:00
|
|
|
func (u *UserInterface) CursorMode() driver.CursorMode {
|
2017-08-12 08:39:41 +02:00
|
|
|
if !u.isRunning() {
|
2019-12-14 04:30:03 +01:00
|
|
|
return u.getInitCursorMode()
|
2017-08-12 08:39:41 +02:00
|
|
|
}
|
2019-12-14 04:30:03 +01:00
|
|
|
var v driver.CursorMode
|
2019-06-05 17:19:12 +02:00
|
|
|
_ = u.t.Call(func() error {
|
2019-12-14 04:30:03 +01:00
|
|
|
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:
|
2021-04-14 18:59:31 +02:00
|
|
|
panic(fmt.Sprintf("glfw: invalid GLFW cursor mode: %d", mode))
|
2019-12-14 04:30:03 +01:00
|
|
|
}
|
2017-08-12 08:39:41 +02:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2019-12-14 04:30:03 +01:00
|
|
|
func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
|
2017-08-12 08:39:41 +02:00
|
|
|
if !u.isRunning() {
|
2019-12-14 04:30:03 +01:00
|
|
|
u.setInitCursorMode(mode)
|
2017-08-12 08:39:41 +02:00
|
|
|
return
|
|
|
|
}
|
2019-06-05 17:19:12 +02:00
|
|
|
_ = u.t.Call(func() error {
|
2021-04-14 18:59:31 +02:00
|
|
|
u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(mode))
|
2017-08-12 08:39:41 +02:00
|
|
|
return nil
|
|
|
|
})
|
2016-09-03 10:17:54 +02:00
|
|
|
}
|
|
|
|
|
2021-04-11 07:21:38 +02:00
|
|
|
func (u *UserInterface) CursorShape() driver.CursorShape {
|
|
|
|
return u.getCursorShape()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) SetCursorShape(shape driver.CursorShape) {
|
|
|
|
old := u.setCursorShape(shape)
|
|
|
|
if old == shape {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !u.isRunning() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_ = u.t.Call(func() error {
|
2021-05-02 07:50:50 +02:00
|
|
|
u.setNativeCursor(shape)
|
2021-04-11 07:21:38 +02:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) DeviceScaleFactor() float64 {
|
2018-10-07 18:11:13 +02:00
|
|
|
if !u.isRunning() {
|
2021-04-17 11:17:24 +02:00
|
|
|
// TODO: Use the initWindowPosition. This requires to convert the units correctly (#1575).
|
2018-12-18 17:01:16 +01:00
|
|
|
return devicescale.GetAt(u.initMonitor.GetPos())
|
2018-10-07 18:11:13 +02:00
|
|
|
}
|
|
|
|
|
2019-12-08 12:24:20 +01:00
|
|
|
f := 0.0
|
2019-06-05 17:19:12 +02:00
|
|
|
_ = u.t.Call(func() error {
|
2019-12-08 12:24:20 +01:00
|
|
|
f = u.deviceScaleFactor()
|
2018-10-07 18:11:13 +02:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2019-12-08 12:24:20 +01:00
|
|
|
// deviceScaleFactor must be called from the main thread.
|
|
|
|
func (u *UserInterface) deviceScaleFactor() float64 {
|
2021-04-16 21:24:13 +02:00
|
|
|
m := u.initMonitor
|
|
|
|
if u.window != nil {
|
|
|
|
m = currentMonitor(u.window)
|
|
|
|
}
|
|
|
|
return devicescale.GetAt(m.GetPos())
|
2019-12-08 12:24:20 +01:00
|
|
|
}
|
|
|
|
|
2019-06-05 17:05:53 +02:00
|
|
|
func init() {
|
|
|
|
// Lock the main thread.
|
|
|
|
runtime.LockOSThread()
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:19:43 +01:00
|
|
|
func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) {
|
2019-04-10 03:44:02 +02:00
|
|
|
panic("glfw: RunWithoutMainLoop is not implemented")
|
|
|
|
}
|
|
|
|
|
2019-12-07 11:28:15 +01:00
|
|
|
// createWindow creates a GLFW window.
|
|
|
|
//
|
|
|
|
// createWindow must be called from the main thread.
|
|
|
|
//
|
|
|
|
// createWindow does not set the position or size so far.
|
|
|
|
func (u *UserInterface) createWindow() error {
|
|
|
|
if u.window != nil {
|
|
|
|
panic("glfw: u.window must not exist at createWindow")
|
|
|
|
}
|
|
|
|
|
|
|
|
// As a start, create a window with temporary size to create OpenGL context thread.
|
|
|
|
window, err := glfw.CreateWindow(16, 16, "", nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
u.window = window
|
|
|
|
|
2020-01-03 09:32:46 +01:00
|
|
|
if u.Graphics().IsGL() {
|
2019-12-07 11:28:15 +01:00
|
|
|
u.window.MakeContextCurrent()
|
|
|
|
}
|
|
|
|
|
|
|
|
u.window.SetInputMode(glfw.StickyMouseButtonsMode, glfw.True)
|
|
|
|
u.window.SetInputMode(glfw.StickyKeysMode, glfw.True)
|
2021-04-14 18:59:31 +02:00
|
|
|
u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(u.getInitCursorMode()))
|
2021-04-11 07:21:38 +02:00
|
|
|
u.window.SetCursor(glfwSystemCursors[u.getCursorShape()])
|
2019-12-07 11:28:15 +01:00
|
|
|
u.window.SetTitle(u.title)
|
|
|
|
// TODO: Set icons
|
|
|
|
|
2021-04-18 18:28:56 +02:00
|
|
|
u.registerWindowSetSizeCallback()
|
|
|
|
|
2021-03-05 17:39:20 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// registerWindowSetSizeCallback must be called from the main thread.
|
|
|
|
func (u *UserInterface) registerWindowSetSizeCallback() {
|
2021-06-13 14:43:23 +02:00
|
|
|
if u.sizeCallback == 0 {
|
|
|
|
u.sizeCallback = glfw.ToSizeCallback(func(_ *glfw.Window, width, height int) {
|
|
|
|
if !u.setSizeCallbackEnabled {
|
|
|
|
return
|
|
|
|
}
|
2021-01-19 18:21:51 +01:00
|
|
|
|
2021-06-13 14:43:23 +02:00
|
|
|
if u.window.GetAttrib(glfw.Resizable) == glfw.False {
|
|
|
|
return
|
2021-01-19 18:21:51 +01:00
|
|
|
}
|
2021-06-13 14:43:23 +02:00
|
|
|
if u.isFullscreen() {
|
|
|
|
return
|
2021-01-19 18:21:51 +01:00
|
|
|
}
|
2021-06-13 14:43:23 +02:00
|
|
|
|
|
|
|
if err := u.runOnAnotherThreadFromMainThread(func() error {
|
|
|
|
var outsideWidth, outsideHeight float64
|
|
|
|
var outsideSizeChanged bool
|
|
|
|
|
2021-01-19 18:21:51 +01:00
|
|
|
_ = u.t.Call(func() error {
|
2021-06-13 14:43:23 +02:00
|
|
|
if width != 0 || height != 0 {
|
|
|
|
u.setWindowSize(width, height, u.isFullscreen())
|
|
|
|
}
|
|
|
|
|
|
|
|
outsideWidth, outsideHeight, outsideSizeChanged = u.updateSize()
|
2021-01-19 18:21:51 +01:00
|
|
|
return nil
|
|
|
|
})
|
2021-06-13 14:43:23 +02:00
|
|
|
if outsideSizeChanged {
|
|
|
|
u.context.Layout(outsideWidth, outsideHeight)
|
|
|
|
}
|
|
|
|
if err := u.context.ForceUpdate(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if u.Graphics().IsGL() {
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
u.swapBuffers()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}); err != nil {
|
|
|
|
u.err = err
|
2021-01-19 18:21:51 +01:00
|
|
|
}
|
2021-06-13 14:43:23 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
u.window.SetSizeCallback(u.sizeCallback)
|
2019-12-07 11:28:15 +01:00
|
|
|
}
|
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
func (u *UserInterface) init() error {
|
|
|
|
if u.Graphics().IsGL() {
|
|
|
|
glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI)
|
|
|
|
glfw.WindowHint(glfw.ContextVersionMajor, 2)
|
|
|
|
glfw.WindowHint(glfw.ContextVersionMinor, 1)
|
|
|
|
} else {
|
|
|
|
glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
|
|
|
|
}
|
2018-12-18 17:01:16 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
decorated := glfw.False
|
|
|
|
if u.isInitWindowDecorated() {
|
|
|
|
decorated = glfw.True
|
|
|
|
}
|
|
|
|
glfw.WindowHint(glfw.Decorated, decorated)
|
2019-11-30 16:07:41 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
transparent := glfw.False
|
|
|
|
if u.isInitScreenTransparent() {
|
|
|
|
transparent = glfw.True
|
|
|
|
}
|
|
|
|
glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
|
|
|
|
u.Graphics().SetTransparent(u.isInitScreenTransparent())
|
2019-01-06 16:21:59 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
resizable := glfw.False
|
|
|
|
if u.isInitWindowResizable() {
|
|
|
|
resizable = glfw.True
|
|
|
|
}
|
|
|
|
glfw.WindowHint(glfw.Resizable, resizable)
|
2020-03-20 13:46:23 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
floating := glfw.False
|
|
|
|
if u.isInitWindowFloating() {
|
|
|
|
floating = glfw.True
|
|
|
|
}
|
|
|
|
glfw.WindowHint(glfw.Floating, floating)
|
2020-02-09 15:03:03 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
focused := glfw.False
|
|
|
|
if u.isInitFocused() {
|
|
|
|
focused = glfw.True
|
|
|
|
}
|
|
|
|
glfw.WindowHint(glfw.FocusOnShow, focused)
|
2019-11-10 11:26:46 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
// Set the window visible explicitly or the application freezes on Wayland (#974).
|
|
|
|
if os.Getenv("WAYLAND_DISPLAY") != "" {
|
|
|
|
glfw.WindowHint(glfw.Visible, glfw.True)
|
|
|
|
}
|
2018-12-17 19:07:13 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
if err := u.createWindow(); err != nil {
|
2019-12-07 11:28:15 +01:00
|
|
|
return err
|
|
|
|
}
|
2021-04-20 16:01:02 +02:00
|
|
|
u.setSizeCallbackEnabled = true
|
2019-12-07 11:28:15 +01:00
|
|
|
|
2020-03-28 16:50:15 +01:00
|
|
|
setSize := func() {
|
|
|
|
ww, wh := u.getInitWindowSize()
|
2020-09-18 17:21:08 +02:00
|
|
|
ww = int(u.toGLFWPixel(float64(ww)))
|
|
|
|
wh = int(u.toGLFWPixel(float64(wh)))
|
2020-06-14 04:50:58 +02:00
|
|
|
u.setWindowSize(ww, wh, u.isFullscreen())
|
2020-03-28 16:50:15 +01:00
|
|
|
}
|
|
|
|
|
2020-03-28 17:33:23 +01:00
|
|
|
// Set the window size and the window position in this order on Linux or other UNIX using X (#1118),
|
2020-10-16 21:21:07 +02:00
|
|
|
// but this should be inverted on Windows. This is very tricky, but there is no obvious way to solve
|
|
|
|
// this. This doesn't matter on macOS.
|
2020-03-28 16:50:15 +01:00
|
|
|
if runtime.GOOS == "windows" {
|
2021-04-20 11:16:41 +02:00
|
|
|
u.setWindowPosition(u.getInitWindowPosition())
|
2020-03-28 16:50:15 +01:00
|
|
|
setSize()
|
|
|
|
} else {
|
|
|
|
setSize()
|
2021-04-20 11:16:41 +02:00
|
|
|
u.setWindowPosition(u.getInitWindowPosition())
|
2020-03-28 16:50:15 +01:00
|
|
|
}
|
2019-12-07 11:28:15 +01:00
|
|
|
|
2021-04-16 19:27:04 +02:00
|
|
|
u.updateWindowSizeLimits()
|
|
|
|
|
2020-03-28 14:33:58 +01:00
|
|
|
// Maximizing a window requires a proper size and position. Call Maximize here (#1117).
|
|
|
|
if u.isInitWindowMaximized() {
|
2020-10-16 21:21:07 +02:00
|
|
|
u.window.Maximize()
|
2020-03-28 14:33:58 +01:00
|
|
|
}
|
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
u.title = u.getInitTitle()
|
|
|
|
u.window.SetTitle(u.title)
|
|
|
|
u.window.Show()
|
2018-12-28 06:08:44 +01:00
|
|
|
|
2020-09-03 17:43:51 +02:00
|
|
|
if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
|
2020-10-16 21:21:07 +02:00
|
|
|
g.SetWindow(u.nativeWindow())
|
2020-01-03 10:53:37 +01:00
|
|
|
}
|
2020-10-16 21:21:07 +02:00
|
|
|
|
|
|
|
return nil
|
2016-02-25 19:09:23 +01:00
|
|
|
}
|
|
|
|
|
2020-10-16 22:53:15 +02:00
|
|
|
func (u *UserInterface) updateSize() (float64, float64, bool) {
|
|
|
|
ww, wh := u.windowWidth, u.windowHeight
|
|
|
|
u.setWindowSize(ww, wh, u.isFullscreen())
|
2019-12-07 11:28:15 +01:00
|
|
|
|
2020-10-16 22:53:15 +02:00
|
|
|
if !u.toChangeSize {
|
|
|
|
return 0, 0, false
|
|
|
|
}
|
|
|
|
u.toChangeSize = false
|
2018-08-05 14:19:22 +02:00
|
|
|
|
2020-10-16 22:53:15 +02:00
|
|
|
var w, h float64
|
|
|
|
if u.isFullscreen() {
|
|
|
|
v := currentMonitor(u.window).GetVideoMode()
|
|
|
|
ww, wh := v.Width, v.Height
|
2021-02-10 19:45:35 +01:00
|
|
|
s := u.deviceScaleFactor()
|
|
|
|
w = fromGLFWMonitorPixel(float64(ww), s)
|
|
|
|
h = fromGLFWMonitorPixel(float64(wh), s)
|
2020-10-16 22:53:15 +02:00
|
|
|
} else {
|
|
|
|
// Instead of u.windowWidth and u.windowHeight, use the actual window size here.
|
|
|
|
// On Windows, the specified size at SetSize and the actual window size might not
|
|
|
|
// match (#1163).
|
|
|
|
ww, wh := u.window.GetSize()
|
|
|
|
w = u.fromGLFWPixel(float64(ww))
|
|
|
|
h = u.fromGLFWPixel(float64(wh))
|
2018-02-01 18:08:03 +01:00
|
|
|
}
|
2020-10-16 22:53:15 +02:00
|
|
|
// On Linux/UNIX, further adjusting is required (#1307).
|
|
|
|
w = u.toFramebufferPixel(w)
|
|
|
|
h = u.toFramebufferPixel(h)
|
|
|
|
|
|
|
|
return w, h, true
|
2018-02-01 18:08:03 +01:00
|
|
|
}
|
|
|
|
|
2020-10-16 23:01:05 +02:00
|
|
|
// update must be called from the main thread.
|
|
|
|
func (u *UserInterface) update() (float64, float64, bool, error) {
|
2021-01-19 18:21:51 +01:00
|
|
|
if u.err != nil {
|
|
|
|
return 0, 0, false, u.err
|
|
|
|
}
|
|
|
|
|
2020-10-17 08:09:12 +02:00
|
|
|
if u.window.ShouldClose() {
|
2020-10-16 23:01:05 +02:00
|
|
|
return 0, 0, false, driver.RegularTermination
|
|
|
|
}
|
2020-10-16 21:37:38 +02:00
|
|
|
|
2020-10-16 23:01:05 +02:00
|
|
|
if u.isInitFullscreen() {
|
|
|
|
w, h := u.window.GetSize()
|
|
|
|
u.setWindowSize(w, h, true)
|
|
|
|
u.setInitFullscreen(false)
|
|
|
|
}
|
2020-10-16 22:31:21 +02:00
|
|
|
|
2020-10-16 23:01:05 +02:00
|
|
|
// Initialize vsync after SetMonitor is called. See the comment in updateVsync.
|
|
|
|
// Calling this inside setWindowSize didn't work (#1363).
|
|
|
|
if !u.vsyncInited {
|
|
|
|
u.vsync = u.isInitVsyncEnabled()
|
|
|
|
u.updateVsync()
|
|
|
|
u.vsyncInited = true
|
2020-10-16 21:37:38 +02:00
|
|
|
}
|
2020-09-22 11:40:52 +02:00
|
|
|
|
2020-10-16 23:01:05 +02:00
|
|
|
outsideWidth, outsideHeight, outsideSizeChanged := u.updateSize()
|
2020-10-16 22:53:15 +02:00
|
|
|
|
2020-10-17 10:45:29 +02:00
|
|
|
// TODO: Updating the input can be skipped when clock.Update returns 0 (#1367).
|
2020-10-16 23:01:05 +02:00
|
|
|
glfw.PollEvents()
|
|
|
|
u.input.update(u.window, u.context)
|
2016-05-18 04:56:43 +02:00
|
|
|
|
2020-10-16 23:01:05 +02:00
|
|
|
for !u.isRunnableOnUnfocused() && u.window.GetAttrib(glfw.Focused) == 0 && !u.window.ShouldClose() {
|
2021-05-04 15:24:31 +02:00
|
|
|
if err := hooks.SuspendAudio(); err != nil {
|
|
|
|
return 0, 0, false, err
|
|
|
|
}
|
2020-10-16 23:01:05 +02:00
|
|
|
// Wait for an arbitrary period to avoid busy loop.
|
|
|
|
time.Sleep(time.Second / 60)
|
2019-03-31 12:51:53 +02:00
|
|
|
glfw.PollEvents()
|
2020-10-16 23:01:05 +02:00
|
|
|
}
|
2021-05-04 15:24:31 +02:00
|
|
|
if err := hooks.ResumeAudio(); err != nil {
|
|
|
|
return 0, 0, false, err
|
|
|
|
}
|
2019-01-06 16:21:59 +01:00
|
|
|
|
2020-10-16 23:01:05 +02:00
|
|
|
return outsideWidth, outsideHeight, outsideSizeChanged, nil
|
2015-01-07 17:32:37 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 17:06:42 +02:00
|
|
|
func (u *UserInterface) loop() error {
|
2016-09-01 18:07:41 +02:00
|
|
|
defer func() {
|
2019-06-05 17:19:12 +02:00
|
|
|
_ = u.t.Call(func() error {
|
2016-09-01 18:07:41 +02:00
|
|
|
glfw.Terminate()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}()
|
2021-02-07 13:16:09 +01:00
|
|
|
|
2016-08-31 19:38:47 +02:00
|
|
|
for {
|
2019-11-17 06:10:35 +01:00
|
|
|
var unfocused bool
|
|
|
|
|
|
|
|
// On Windows, the focusing state might be always false (#987).
|
|
|
|
// On Windows, even if a window is in another workspace, vsync seems to work.
|
2020-01-01 15:59:56 +01:00
|
|
|
// Then let's assume the window is always 'focused' as a workaround.
|
2019-11-17 06:10:35 +01:00
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
unfocused = u.window.GetAttrib(glfw.Focused) == glfw.False
|
|
|
|
}
|
2019-11-14 05:07:10 +01:00
|
|
|
|
|
|
|
var t1, t2 time.Time
|
|
|
|
|
|
|
|
if unfocused {
|
|
|
|
t1 = time.Now()
|
|
|
|
}
|
2020-10-16 23:01:05 +02:00
|
|
|
|
|
|
|
var outsideWidth, outsideHeight float64
|
|
|
|
var outsideSizeChanged bool
|
|
|
|
if err := u.t.Call(func() error {
|
|
|
|
var err error
|
|
|
|
outsideWidth, outsideHeight, outsideSizeChanged, err = u.update()
|
2016-08-31 19:38:47 +02:00
|
|
|
return err
|
2020-10-16 23:01:05 +02:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if outsideSizeChanged {
|
|
|
|
u.context.Layout(outsideWidth, outsideHeight)
|
2016-08-31 19:38:47 +02:00
|
|
|
}
|
2020-10-16 23:01:05 +02:00
|
|
|
|
2020-10-16 22:31:21 +02:00
|
|
|
if err := u.context.Update(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-16 23:08:13 +02:00
|
|
|
|
2021-04-18 15:13:34 +02:00
|
|
|
// Create icon images in a different goroutine (#1478).
|
2021-04-21 15:50:00 +02:00
|
|
|
// In the fullscreen mode, SetIcon fails (#1578).
|
2021-04-24 17:57:06 +02:00
|
|
|
if imgs := u.getIconImages(); len(imgs) > 0 && !u.isFullscreen() {
|
|
|
|
u.setIconImages(imgs[:0])
|
2021-02-07 13:16:09 +01:00
|
|
|
|
|
|
|
// Convert the icons in the different goroutine, as (*ebiten.Image).At cannot be invoked
|
|
|
|
// from this goroutine. At works only in between BeginFrame and EndFrame.
|
|
|
|
go func() {
|
|
|
|
newImgs := make([]image.Image, len(imgs))
|
|
|
|
for i, img := range imgs {
|
|
|
|
// TODO: If img is not *ebiten.Image, this converting is not necessary.
|
|
|
|
// However, this package cannot refer *ebiten.Image due to the package
|
|
|
|
// dependencies.
|
|
|
|
|
|
|
|
b := img.Bounds()
|
|
|
|
rgba := image.NewRGBA(b)
|
|
|
|
for j := b.Min.Y; j < b.Max.Y; j++ {
|
|
|
|
for i := b.Min.X; i < b.Max.X; i++ {
|
|
|
|
rgba.Set(i, j, img.At(i, j))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newImgs[i] = rgba
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = u.t.Call(func() error {
|
2021-04-21 15:50:00 +02:00
|
|
|
// In the fullscreen mode, reset the icon images and try again later.
|
2021-04-18 15:13:34 +02:00
|
|
|
if u.isFullscreen() {
|
|
|
|
u.setIconImages(imgs)
|
|
|
|
return nil
|
|
|
|
}
|
2021-02-07 13:16:09 +01:00
|
|
|
u.window.SetIcon(newImgs)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2020-10-16 23:08:13 +02:00
|
|
|
// swapBuffers also checks IsGL, so this condition is redundant.
|
|
|
|
// However, (*thread).Call is not good for performance due to channels.
|
|
|
|
// Let's avoid this whenever possible (#1367).
|
|
|
|
if u.Graphics().IsGL() {
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
u.swapBuffers()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
2020-10-16 22:31:21 +02:00
|
|
|
|
2019-11-14 05:07:10 +01:00
|
|
|
if unfocused {
|
|
|
|
t2 = time.Now()
|
|
|
|
}
|
|
|
|
|
|
|
|
// When a window is not focused, SwapBuffers might return immediately and CPU might be busy.
|
|
|
|
// Mitigate this by sleeping (#982).
|
|
|
|
if unfocused {
|
|
|
|
d := t2.Sub(t1)
|
|
|
|
const wait = time.Second / 60
|
|
|
|
if d < wait {
|
|
|
|
time.Sleep(wait - d)
|
|
|
|
}
|
|
|
|
}
|
2016-07-23 14:01:30 +02:00
|
|
|
}
|
2016-03-26 09:24:40 +01:00
|
|
|
}
|
|
|
|
|
2018-02-24 16:00:59 +01:00
|
|
|
// swapBuffers must be called from the main thread.
|
2019-04-07 03:42:55 +02:00
|
|
|
func (u *UserInterface) swapBuffers() {
|
2020-01-03 09:32:46 +01:00
|
|
|
if u.Graphics().IsGL() {
|
2018-11-12 16:00:10 +01:00
|
|
|
u.window.SwapBuffers()
|
|
|
|
}
|
2014-12-14 10:57:29 +01:00
|
|
|
}
|
2015-02-09 02:25:00 +01:00
|
|
|
|
2021-04-16 19:27:04 +02:00
|
|
|
// updateWindowSizeLimits must be called from the main thread.
|
|
|
|
func (u *UserInterface) updateWindowSizeLimits() {
|
2021-04-16 21:43:20 +02:00
|
|
|
minw, minh, maxw, maxh := u.getWindowSizeLimitsInDP()
|
2021-04-16 19:27:04 +02:00
|
|
|
if minw < 0 {
|
|
|
|
minw = glfw.DontCare
|
2021-04-16 21:43:20 +02:00
|
|
|
} else {
|
|
|
|
minw = int(u.toGLFWPixel(float64(minw)))
|
2021-04-16 19:27:04 +02:00
|
|
|
}
|
|
|
|
if minh < 0 {
|
|
|
|
minh = glfw.DontCare
|
2021-04-16 21:43:20 +02:00
|
|
|
} else {
|
|
|
|
minh = int(u.toGLFWPixel(float64(minh)))
|
2021-04-16 19:27:04 +02:00
|
|
|
}
|
|
|
|
if maxw < 0 {
|
|
|
|
maxw = glfw.DontCare
|
2021-04-16 21:43:20 +02:00
|
|
|
} else {
|
|
|
|
maxw = int(u.toGLFWPixel(float64(maxw)))
|
2021-04-16 19:27:04 +02:00
|
|
|
}
|
|
|
|
if maxh < 0 {
|
|
|
|
maxh = glfw.DontCare
|
2021-04-16 21:43:20 +02:00
|
|
|
} else {
|
|
|
|
maxh = int(u.toGLFWPixel(float64(maxh)))
|
2021-04-16 19:27:04 +02:00
|
|
|
}
|
|
|
|
u.window.SetSizeLimits(minw, minh, maxw, maxh)
|
|
|
|
}
|
|
|
|
|
2021-04-17 17:56:35 +02:00
|
|
|
// adjustWindowSizeBasedOnSizeLimitsInDP adjust the size based on the window size limits.
|
|
|
|
// width and height are in device-dependent pixels.
|
|
|
|
func (u *UserInterface) adjustWindowSizeBasedOnSizeLimits(width, height int) (int, int) {
|
|
|
|
minw, minh, maxw, maxh := u.getWindowSizeLimits()
|
|
|
|
if minw >= 0 && width < minw {
|
|
|
|
width = minw
|
|
|
|
}
|
|
|
|
if minh >= 0 && height < minh {
|
|
|
|
height = minh
|
|
|
|
}
|
|
|
|
if maxw >= 0 && width > maxw {
|
|
|
|
width = maxw
|
|
|
|
}
|
|
|
|
if maxh >= 0 && height > maxh {
|
|
|
|
height = maxh
|
|
|
|
}
|
|
|
|
return width, height
|
|
|
|
}
|
|
|
|
|
2021-04-16 21:43:20 +02:00
|
|
|
// adjustWindowSizeBasedOnSizeLimitsInDP adjust the size based on the window size limits.
|
|
|
|
// width and height are in device-independent pixels.
|
|
|
|
func (u *UserInterface) adjustWindowSizeBasedOnSizeLimitsInDP(width, height int) (int, int) {
|
|
|
|
minw, minh, maxw, maxh := u.getWindowSizeLimitsInDP()
|
2021-04-16 19:27:04 +02:00
|
|
|
if minw >= 0 && width < minw {
|
|
|
|
width = minw
|
|
|
|
}
|
|
|
|
if minh >= 0 && height < minh {
|
|
|
|
height = minh
|
|
|
|
}
|
|
|
|
if maxw >= 0 && width > maxw {
|
|
|
|
width = maxw
|
|
|
|
}
|
|
|
|
if maxh >= 0 && height > maxh {
|
|
|
|
height = maxh
|
|
|
|
}
|
|
|
|
return width, height
|
|
|
|
}
|
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
// setWindowSize must be called from the main thread.
|
2020-06-14 04:50:58 +02:00
|
|
|
func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
2021-04-17 17:56:35 +02:00
|
|
|
width, height = u.adjustWindowSizeBasedOnSizeLimits(width, height)
|
2021-04-16 19:27:04 +02:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
|
|
|
|
return
|
|
|
|
}
|
2016-03-22 03:56:40 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
if width < 1 {
|
|
|
|
width = 1
|
|
|
|
}
|
|
|
|
if height < 1 {
|
|
|
|
height = 1
|
|
|
|
}
|
2017-10-19 20:25:21 +02:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
u.lastDeviceScaleFactor = u.deviceScaleFactor()
|
2017-10-19 20:25:21 +02:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
// To make sure the current existing framebuffers are rendered,
|
|
|
|
// swap buffers here before SetSize is called.
|
|
|
|
u.swapBuffers()
|
2019-12-07 11:28:15 +01:00
|
|
|
|
2021-04-18 15:07:26 +02:00
|
|
|
// Disable the callback of SetSize. This callback can be invoked by SetMonitor or SetSize.
|
2021-02-23 08:52:09 +01:00
|
|
|
// ForceUpdate is called from the callback.
|
|
|
|
// While setWindowSize can be called from Update, calling ForceUpdate inside Update is illegal (#1505).
|
2021-04-18 15:07:26 +02:00
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
2021-04-17 16:57:17 +02:00
|
|
|
}
|
2021-02-23 08:52:09 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
var windowRecreated bool
|
2019-12-07 11:28:15 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
if fullscreen {
|
|
|
|
if u.origPosX == invalidPos || u.origPosY == invalidPos {
|
|
|
|
u.origPosX, u.origPosY = u.window.GetPos()
|
|
|
|
}
|
|
|
|
m := currentMonitor(u.window)
|
|
|
|
v := m.GetVideoMode()
|
|
|
|
u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
|
|
|
|
|
|
|
|
// Swapping buffer is necesary to prevent the image lag (#1004).
|
|
|
|
// TODO: This might not work when vsync is disabled.
|
|
|
|
if u.Graphics().IsGL() {
|
|
|
|
glfw.PollEvents()
|
|
|
|
u.swapBuffers()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// On Windows, giving a too small width doesn't call a callback (#165).
|
|
|
|
// To prevent hanging up, return asap if the width is too small.
|
|
|
|
// 126 is an arbitrary number and I guess this is small enough.
|
|
|
|
minWindowWidth := int(u.toGLFWPixel(126))
|
|
|
|
if u.window.GetAttrib(glfw.Decorated) == glfw.False {
|
|
|
|
minWindowWidth = 1
|
|
|
|
}
|
|
|
|
if width < minWindowWidth {
|
|
|
|
width = minWindowWidth
|
|
|
|
}
|
2019-12-07 11:28:15 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
if u.window.GetMonitor() != nil {
|
2020-01-03 09:32:46 +01:00
|
|
|
if u.Graphics().IsGL() {
|
2020-10-16 21:21:07 +02:00
|
|
|
// When OpenGL is used, swapping buffer is enough to solve the image-lag
|
|
|
|
// issue (#1004). Rather, recreating window destroys GPU resources.
|
|
|
|
// TODO: This might not work when vsync is disabled.
|
|
|
|
u.window.SetMonitor(nil, 0, 0, width, height, 0)
|
2017-10-19 20:25:21 +02:00
|
|
|
glfw.PollEvents()
|
2019-12-07 11:28:15 +01:00
|
|
|
u.swapBuffers()
|
2020-10-16 21:21:07 +02:00
|
|
|
} else {
|
|
|
|
// Recreate the window since an image lag remains after coming back from
|
|
|
|
// fullscreen (#1004).
|
|
|
|
if u.window != nil {
|
|
|
|
u.window.Destroy()
|
|
|
|
u.window = nil
|
|
|
|
}
|
|
|
|
if err := u.createWindow(); err != nil {
|
|
|
|
// TODO: This should return an error.
|
|
|
|
panic(fmt.Sprintf("glfw: failed to recreate window: %v", err))
|
2017-10-19 20:25:21 +02:00
|
|
|
}
|
2021-04-16 19:27:04 +02:00
|
|
|
// Reset the size limits explicitly.
|
|
|
|
u.updateWindowSizeLimits()
|
2020-10-16 21:21:07 +02:00
|
|
|
u.window.Show()
|
|
|
|
windowRecreated = true
|
2017-06-29 17:35:34 +02:00
|
|
|
}
|
2020-10-16 21:21:07 +02:00
|
|
|
}
|
2018-10-05 20:53:34 +02:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
if u.origPosX != invalidPos && u.origPosY != invalidPos {
|
|
|
|
x := u.origPosX
|
|
|
|
y := u.origPosY
|
|
|
|
u.window.SetPos(x, y)
|
|
|
|
// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
|
|
|
|
// work with two or more SetPos.
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
u.window.SetPos(x+1, y)
|
2019-12-09 18:37:10 +01:00
|
|
|
u.window.SetPos(x, y)
|
2019-12-08 17:06:25 +01:00
|
|
|
}
|
2020-10-16 21:21:07 +02:00
|
|
|
u.origPosX = invalidPos
|
|
|
|
u.origPosY = invalidPos
|
|
|
|
}
|
2019-12-08 17:06:25 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
// Set the window size after the position. The order matters.
|
|
|
|
// In the opposite order, the window size might not be correct when going back from fullscreen with multi monitors.
|
|
|
|
oldW, oldH := u.window.GetSize()
|
|
|
|
newW := width
|
|
|
|
newH := height
|
|
|
|
if oldW != newW || oldH != newH {
|
2021-06-13 14:43:23 +02:00
|
|
|
u.framebufferSizeCallbackCh = make(chan struct{}, 1)
|
|
|
|
if u.framebufferSizeCallback == 0 {
|
|
|
|
u.framebufferSizeCallback = glfw.ToFramebufferSizeCallback(func(_ *glfw.Window, _, _ int) {
|
|
|
|
// This callback can be invoked multiple times by one PollEvents in theory (#1618).
|
|
|
|
// Allow the case when the channel is full.
|
|
|
|
select {
|
|
|
|
case u.framebufferSizeCallbackCh <- struct{}{}:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
u.window.SetFramebufferSizeCallback(u.framebufferSizeCallback)
|
2020-10-16 21:21:07 +02:00
|
|
|
u.window.SetSize(newW, newH)
|
2021-04-22 20:28:09 +02:00
|
|
|
// Just after SetSize, GetSize is not reliable especially on Linux/UNIX.
|
2021-04-22 20:02:06 +02:00
|
|
|
// Let's wait for FramebufferSize callback in any cases.
|
|
|
|
|
|
|
|
// Use the timeout as FramebufferSize event might not be fired (#1618).
|
|
|
|
t := time.NewTimer(time.Second)
|
|
|
|
defer t.Stop()
|
|
|
|
|
|
|
|
event:
|
|
|
|
for {
|
|
|
|
glfw.PollEvents()
|
|
|
|
select {
|
2021-06-13 14:43:23 +02:00
|
|
|
case <-u.framebufferSizeCallbackCh:
|
2021-04-22 20:02:06 +02:00
|
|
|
break event
|
|
|
|
case <-t.C:
|
|
|
|
break event
|
|
|
|
default:
|
2021-04-23 22:18:44 +02:00
|
|
|
time.Sleep(time.Millisecond)
|
2019-12-07 11:28:15 +01:00
|
|
|
}
|
|
|
|
}
|
2021-06-13 14:43:23 +02:00
|
|
|
u.window.SetFramebufferSizeCallback(glfw.ToFramebufferSizeCallback(nil))
|
|
|
|
close(u.framebufferSizeCallbackCh)
|
|
|
|
u.framebufferSizeCallbackCh = nil
|
2019-12-07 11:28:15 +01:00
|
|
|
}
|
2018-07-14 13:50:09 +02:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
// Window title might be lost on macOS after coming back from fullscreen.
|
|
|
|
u.window.SetTitle(u.title)
|
|
|
|
}
|
2019-12-09 18:37:10 +01:00
|
|
|
|
2020-10-16 21:21:07 +02:00
|
|
|
// As width might be updated, update windowWidth/Height here.
|
|
|
|
u.windowWidth = width
|
|
|
|
u.windowHeight = height
|
|
|
|
|
|
|
|
u.toChangeSize = true
|
2019-12-07 11:28:15 +01:00
|
|
|
|
|
|
|
if windowRecreated {
|
2020-09-03 17:43:51 +02:00
|
|
|
if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
|
2020-01-03 10:53:37 +01:00
|
|
|
g.SetWindow(u.nativeWindow())
|
|
|
|
}
|
2019-12-07 11:28:15 +01:00
|
|
|
}
|
2015-02-09 02:25:00 +01:00
|
|
|
}
|
2018-10-05 16:59:30 +02:00
|
|
|
|
2020-06-14 04:50:58 +02:00
|
|
|
// updateVsync must be called on the main thread.
|
|
|
|
func (u *UserInterface) updateVsync() {
|
|
|
|
if u.Graphics().IsGL() {
|
|
|
|
// SwapInterval is affected by the current monitor of the window.
|
|
|
|
// This needs to be called at least after SetMonitor.
|
|
|
|
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
|
|
|
|
//
|
|
|
|
// TODO: (#405) If triple buffering is needed, SwapInterval(0) should be called,
|
|
|
|
// but is this correct? If glfw.SwapInterval(0) and the driver doesn't support triple
|
|
|
|
// buffering, what will happen?
|
|
|
|
if u.vsync {
|
|
|
|
glfw.SwapInterval(1)
|
|
|
|
} else {
|
|
|
|
glfw.SwapInterval(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
u.Graphics().SetVsyncEnabled(u.vsync)
|
|
|
|
}
|
|
|
|
|
2020-08-23 20:27:38 +02:00
|
|
|
// currentMonitor returns the current active monitor.
|
|
|
|
//
|
|
|
|
// The given window might or might not be used to detect the monitor.
|
2018-10-05 16:59:30 +02:00
|
|
|
//
|
|
|
|
// currentMonitor must be called on the main thread.
|
2020-08-23 20:27:38 +02:00
|
|
|
func currentMonitor(window *glfw.Window) *glfw.Monitor {
|
2021-04-21 15:50:00 +02:00
|
|
|
// GetMonitor is available only in fullscreen.
|
2020-08-23 20:27:38 +02:00
|
|
|
if m := window.GetMonitor(); m != nil {
|
2020-08-23 16:37:58 +02:00
|
|
|
return m
|
2018-10-05 16:59:30 +02:00
|
|
|
}
|
2020-08-23 16:37:58 +02:00
|
|
|
|
|
|
|
// Getting a monitor from a window position is not reliable in general (e.g., when a window is put across
|
2020-08-23 17:57:48 +02:00
|
|
|
// multiple monitors, or, before SetWindowPosition is called.).
|
2018-10-06 15:42:28 +02:00
|
|
|
// Get the monitor which the current window belongs to. This requires OS API.
|
2020-08-23 20:31:46 +02:00
|
|
|
if m := currentMonitorByOS(window); m != nil {
|
2020-08-23 20:27:38 +02:00
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// As the fallback, detect the monitor from the window.
|
|
|
|
if m := getCachedMonitor(window.GetPos()); m != nil {
|
|
|
|
return m.m
|
|
|
|
}
|
|
|
|
return glfw.GetPrimaryMonitor()
|
2018-10-05 16:59:30 +02:00
|
|
|
}
|
2019-04-07 11:28:50 +02:00
|
|
|
|
2019-11-30 16:07:41 +01:00
|
|
|
func (u *UserInterface) SetScreenTransparent(transparent bool) {
|
|
|
|
if !u.isRunning() {
|
|
|
|
u.setInitScreenTransparent(transparent)
|
|
|
|
return
|
|
|
|
}
|
2019-11-30 19:34:09 +01:00
|
|
|
panic("glfw: SetScreenTransparent can't be called after the main loop starts")
|
2019-11-30 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) IsScreenTransparent() bool {
|
|
|
|
if !u.isRunning() {
|
|
|
|
return u.isInitScreenTransparent()
|
|
|
|
}
|
|
|
|
val := false
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
val = u.window.GetAttrib(glfw.TransparentFramebuffer) == glfw.True
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2020-04-02 17:06:42 +02:00
|
|
|
func (u *UserInterface) ResetForFrame() {
|
|
|
|
// The offscreens must be updated every frame (#490).
|
2020-10-16 22:53:15 +02:00
|
|
|
var w, h float64
|
|
|
|
var changed bool
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
w, h, changed = u.updateSize()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if changed {
|
|
|
|
u.context.Layout(w, h)
|
|
|
|
}
|
2020-04-02 17:06:42 +02:00
|
|
|
u.input.resetForFrame()
|
|
|
|
}
|
|
|
|
|
2020-02-09 15:03:03 +01:00
|
|
|
func (u *UserInterface) MonitorPosition() (int, int) {
|
|
|
|
if !u.isRunning() {
|
|
|
|
return u.monitorPosition()
|
|
|
|
}
|
|
|
|
var mx, my int
|
|
|
|
_ = u.t.Call(func() error {
|
|
|
|
mx, my = u.monitorPosition()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return mx, my
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) SetInitFocused(focused bool) {
|
|
|
|
if u.isRunning() {
|
|
|
|
panic("ui: SetInitFocused must be called before the main loop")
|
|
|
|
}
|
|
|
|
u.setInitFocused(focused)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *UserInterface) monitorPosition() (int, int) {
|
2020-09-18 17:56:39 +02:00
|
|
|
// TODO: fromGLFWMonitorPixel might be required.
|
2020-08-23 20:27:38 +02:00
|
|
|
return currentMonitor(u.window).GetPos()
|
2020-02-09 15:03:03 +01:00
|
|
|
}
|
|
|
|
|
2019-04-07 11:28:50 +02:00
|
|
|
func (u *UserInterface) Input() driver.Input {
|
2019-04-07 11:55:56 +02:00
|
|
|
return &u.input
|
2019-04-07 11:28:50 +02:00
|
|
|
}
|
2019-12-24 16:05:56 +01:00
|
|
|
|
|
|
|
func (u *UserInterface) Window() driver.Window {
|
|
|
|
return &u.iwindow
|
|
|
|
}
|
2021-04-17 16:57:17 +02:00
|
|
|
|
2021-04-20 11:16:41 +02:00
|
|
|
// GLFW's functions to manipulate a window can invoke the SetSize callback (#1576, #1585, #1606).
|
|
|
|
// As the callback must not be called in the frame (between BeginFrame and EndFrame),
|
|
|
|
// disable the callback temporarily.
|
|
|
|
|
|
|
|
// maximizeWindow must be called from the main thread.
|
|
|
|
func (u *UserInterface) maximizeWindow() {
|
2021-04-18 15:07:26 +02:00
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
2021-04-17 16:57:17 +02:00
|
|
|
}
|
|
|
|
u.window.Maximize()
|
|
|
|
|
2021-04-20 13:09:04 +02:00
|
|
|
if !u.isFullscreen() {
|
2021-04-21 15:25:10 +02:00
|
|
|
// On Linux/UNIX, maximizing might not finish even though Maximize returns. Just wait for its finish.
|
2021-04-21 15:50:00 +02:00
|
|
|
// Do not check this in the fullscreen since apparently the condition can never be true.
|
2021-04-21 15:25:10 +02:00
|
|
|
for u.window.GetAttrib(glfw.Maximized) != glfw.True {
|
|
|
|
glfw.PollEvents()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
|
2021-04-21 15:50:00 +02:00
|
|
|
// Do not call setWindowSize in the fullscreen mode since setWindowSize requires the window size
|
|
|
|
// before the fullscreen, while window.GetSize() returns the desktop screen size in the fullscreen mode.
|
2021-04-20 13:09:04 +02:00
|
|
|
w, h := u.window.GetSize()
|
|
|
|
u.setWindowSize(w, h, u.isFullscreen())
|
|
|
|
}
|
2021-04-17 16:57:17 +02:00
|
|
|
}
|
2021-04-17 17:10:37 +02:00
|
|
|
|
2021-04-20 11:16:41 +02:00
|
|
|
// iconifyWindow must be called from the main thread.
|
|
|
|
func (u *UserInterface) iconifyWindow() {
|
2021-04-18 15:07:26 +02:00
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
2021-04-17 18:03:06 +02:00
|
|
|
}
|
|
|
|
u.window.Iconify()
|
|
|
|
|
2021-04-20 17:52:04 +02:00
|
|
|
// On Linux/UNIX, iconifying might not finish even though Iconify returns. Just wait for its finish.
|
|
|
|
for u.window.GetAttrib(glfw.Iconified) != glfw.True {
|
|
|
|
glfw.PollEvents()
|
|
|
|
}
|
|
|
|
|
2021-04-18 14:06:02 +02:00
|
|
|
// After iconifiying, the window is invisible and setWindowSize doesn't have to be called.
|
|
|
|
// Rather, the window size might be (0, 0) and it might be impossible to call setWindowSize (#1585).
|
2021-04-17 18:03:06 +02:00
|
|
|
}
|
|
|
|
|
2021-04-20 11:16:41 +02:00
|
|
|
// restoreWindow must be called from the main thread.
|
|
|
|
func (u *UserInterface) restoreWindow() {
|
2021-04-18 15:07:26 +02:00
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
2021-04-17 17:10:37 +02:00
|
|
|
}
|
2021-04-20 17:52:04 +02:00
|
|
|
|
2021-04-17 17:10:37 +02:00
|
|
|
u.window.Restore()
|
|
|
|
|
2021-04-20 17:52:04 +02:00
|
|
|
// On Linux/UNIX, restoring might not finish even though Restore returns (#1608). Just wait for its finish.
|
|
|
|
// On macOS, the restoring state might be the same as the maximized state. Skip this.
|
|
|
|
if runtime.GOOS != "darwin" {
|
|
|
|
for u.window.GetAttrib(glfw.Maximized) == glfw.True || u.window.GetAttrib(glfw.Iconified) == glfw.True {
|
|
|
|
glfw.PollEvents()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 15:07:26 +02:00
|
|
|
// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
|
2021-04-21 15:50:00 +02:00
|
|
|
// Do not call setWindowSize in the fullscreen mode since setWindowSize requires the window size
|
|
|
|
// before the fullscreen, while window.GetSize() returns the desktop screen size in the fullscreen mode.
|
2021-04-20 13:09:04 +02:00
|
|
|
if !u.isFullscreen() {
|
|
|
|
w, h := u.window.GetSize()
|
|
|
|
u.setWindowSize(w, h, u.isFullscreen())
|
|
|
|
}
|
2021-04-17 17:10:37 +02:00
|
|
|
}
|
2021-04-18 14:23:59 +02:00
|
|
|
|
2021-04-20 11:16:41 +02:00
|
|
|
// setWindowDecorated must be called from the main thread.
|
|
|
|
func (u *UserInterface) setWindowDecorated(decorated bool) {
|
2021-04-18 15:07:26 +02:00
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
2021-04-18 14:23:59 +02:00
|
|
|
}
|
|
|
|
v := glfw.False
|
|
|
|
if decorated {
|
|
|
|
v = glfw.True
|
|
|
|
}
|
|
|
|
u.window.SetAttrib(glfw.Decorated, v)
|
|
|
|
|
2021-04-20 11:16:41 +02:00
|
|
|
// The title can be lost when the decoration is gone. Recover this.
|
|
|
|
if decorated {
|
|
|
|
u.window.SetTitle(u.title)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// setWindowFloating must be called from the main thread.
|
|
|
|
func (u *UserInterface) setWindowFloating(floating bool) {
|
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
v := glfw.False
|
|
|
|
if floating {
|
|
|
|
v = glfw.True
|
|
|
|
}
|
|
|
|
u.window.SetAttrib(glfw.Floating, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// setWindowResizable must be called from the main thread.
|
|
|
|
func (u *UserInterface) setWindowResizable(resizable bool) {
|
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
v := glfw.False
|
|
|
|
if resizable {
|
|
|
|
v = glfw.True
|
|
|
|
}
|
|
|
|
u.window.SetAttrib(glfw.Resizable, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// setWindowPosition must be called from the main thread.
|
|
|
|
func (u *UserInterface) setWindowPosition(x, y int) {
|
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
mx, my := currentMonitor(u.window).GetPos()
|
|
|
|
xf := u.toGLFWPixel(float64(x))
|
|
|
|
yf := u.toGLFWPixel(float64(y))
|
|
|
|
if x, y := u.adjustWindowPosition(mx+int(xf), my+int(yf)); u.isFullscreen() {
|
|
|
|
u.origPosX, u.origPosY = x, y
|
|
|
|
} else {
|
|
|
|
u.window.SetPos(x, y)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
|
2021-04-20 16:38:34 +02:00
|
|
|
//
|
|
|
|
// There are cases when setWindowSize should be called (#1606) and should not be called (#1609).
|
|
|
|
// For the former, macOS seems enough so far.
|
|
|
|
//
|
2021-04-21 15:50:00 +02:00
|
|
|
// Do not call setWindowSize in the fullscreen mode since setWindowSize requires the window size
|
|
|
|
// before the fullscreen, while window.GetSize() returns the desktop screen size in the fullscreen mode.
|
2021-04-20 16:38:34 +02:00
|
|
|
if !u.isFullscreen() && runtime.GOOS == "darwin" {
|
2021-04-20 13:09:04 +02:00
|
|
|
w, h := u.window.GetSize()
|
|
|
|
u.setWindowSize(w, h, u.isFullscreen())
|
|
|
|
}
|
2021-04-20 11:16:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// setWindowTitle must be called from the main thread.
|
|
|
|
func (u *UserInterface) setWindowTitle(title string) {
|
|
|
|
if u.setSizeCallbackEnabled {
|
|
|
|
u.setSizeCallbackEnabled = false
|
|
|
|
defer func() {
|
|
|
|
u.setSizeCallbackEnabled = true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
u.window.SetTitle(title)
|
2021-04-18 14:23:59 +02:00
|
|
|
}
|