zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

ui.go (44073B)


      1 // Copyright 2015 Hajime Hoshi
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 //go:build (darwin || freebsd || linux || windows) && !android && !ios
     16 // +build darwin freebsd linux windows
     17 // +build !android
     18 // +build !ios
     19 
     20 package glfw
     21 
     22 import (
     23 	"fmt"
     24 	"image"
     25 	"os"
     26 	"runtime"
     27 	"sync"
     28 	"sync/atomic"
     29 	"time"
     30 
     31 	"github.com/hajimehoshi/ebiten/v2/internal/devicescale"
     32 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     33 	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
     34 	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
     35 	"github.com/hajimehoshi/ebiten/v2/internal/thread"
     36 )
     37 
     38 func driverCursorModeToGLFWCursorMode(mode driver.CursorMode) int {
     39 	switch mode {
     40 	case driver.CursorModeVisible:
     41 		return glfw.CursorNormal
     42 	case driver.CursorModeHidden:
     43 		return glfw.CursorHidden
     44 	case driver.CursorModeCaptured:
     45 		return glfw.CursorDisabled
     46 	default:
     47 		panic(fmt.Sprintf("glfw: invalid driver.CursorMode: %d", mode))
     48 	}
     49 }
     50 
     51 type UserInterface struct {
     52 	context driver.UIContext
     53 	title   string
     54 	window  *glfw.Window
     55 
     56 	// windowWidth and windowHeight represents a window size.
     57 	// The units are device-dependent pixels.
     58 	windowWidth  int
     59 	windowHeight int
     60 
     61 	// The units are device-independent pixels.
     62 	minWindowWidthInDP  int
     63 	minWindowHeightInDP int
     64 	maxWindowWidthInDP  int
     65 	maxWindowHeightInDP int
     66 
     67 	running              uint32
     68 	toChangeSize         bool
     69 	origPosX             int
     70 	origPosY             int
     71 	runnableOnUnfocused  bool
     72 	fpsMode              driver.FPSMode
     73 	iconImages           []image.Image
     74 	cursorShape          driver.CursorShape
     75 	windowClosingHandled bool
     76 	windowBeingClosed    bool
     77 
     78 	// setSizeCallbackEnabled must be accessed from the main thread.
     79 	setSizeCallbackEnabled bool
     80 
     81 	// err must be accessed from the main thread.
     82 	err error
     83 
     84 	lastDeviceScaleFactor float64
     85 
     86 	// These values are not changed after initialized.
     87 	// TODO: the fullscreen size should be updated when the initial window position is changed?
     88 	initMonitor              *glfw.Monitor
     89 	initFullscreenWidthInDP  int
     90 	initFullscreenHeightInDP int
     91 
     92 	initTitle               string
     93 	initFPSMode             driver.FPSMode
     94 	initFullscreen          bool
     95 	initCursorMode          driver.CursorMode
     96 	initWindowDecorated     bool
     97 	initWindowResizable     bool
     98 	initWindowPositionXInDP int
     99 	initWindowPositionYInDP int
    100 	initWindowWidthInDP     int
    101 	initWindowHeightInDP    int
    102 	initWindowFloating      bool
    103 	initWindowMaximized     bool
    104 	initScreenTransparent   bool
    105 	initFocused             bool
    106 
    107 	fpsModeInited bool
    108 
    109 	input   Input
    110 	iwindow window
    111 
    112 	sizeCallback              glfw.SizeCallback
    113 	closeCallback             glfw.CloseCallback
    114 	framebufferSizeCallback   glfw.FramebufferSizeCallback
    115 	framebufferSizeCallbackCh chan struct{}
    116 
    117 	t thread.Thread
    118 	m sync.RWMutex
    119 }
    120 
    121 const (
    122 	maxInt     = int(^uint(0) >> 1)
    123 	minInt     = -maxInt - 1
    124 	invalidPos = minInt
    125 )
    126 
    127 var (
    128 	theUI = &UserInterface{
    129 		runnableOnUnfocused:     true,
    130 		minWindowWidthInDP:      glfw.DontCare,
    131 		minWindowHeightInDP:     glfw.DontCare,
    132 		maxWindowWidthInDP:      glfw.DontCare,
    133 		maxWindowHeightInDP:     glfw.DontCare,
    134 		origPosX:                invalidPos,
    135 		origPosY:                invalidPos,
    136 		initFPSMode:             driver.FPSModeVsyncOn,
    137 		initCursorMode:          driver.CursorModeVisible,
    138 		initWindowDecorated:     true,
    139 		initWindowPositionXInDP: invalidPos,
    140 		initWindowPositionYInDP: invalidPos,
    141 		initWindowWidthInDP:     640,
    142 		initWindowHeightInDP:    480,
    143 		initFocused:             true,
    144 		fpsMode:                 driver.FPSModeVsyncOn,
    145 	}
    146 )
    147 
    148 func init() {
    149 	theUI.input.ui = theUI
    150 	theUI.iwindow.ui = theUI
    151 }
    152 
    153 func Get() *UserInterface {
    154 	return theUI
    155 }
    156 
    157 func init() {
    158 	hideConsoleWindowOnWindows()
    159 	if err := initialize(); err != nil {
    160 		panic(err)
    161 	}
    162 	glfw.SetMonitorCallback(func(monitor *glfw.Monitor, event glfw.PeripheralEvent) {
    163 		updateMonitors()
    164 	})
    165 	updateMonitors()
    166 }
    167 
    168 var glfwSystemCursors = map[driver.CursorShape]*glfw.Cursor{}
    169 
    170 func initialize() error {
    171 	if err := glfw.Init(); err != nil {
    172 		return err
    173 	}
    174 
    175 	glfw.WindowHint(glfw.Visible, glfw.False)
    176 	glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
    177 
    178 	// Create a window to set the initial monitor.
    179 	w, err := glfw.CreateWindow(16, 16, "", nil, nil)
    180 	if err != nil {
    181 		return err
    182 	}
    183 	if w == nil {
    184 		// This can happen on Windows Remote Desktop (#903).
    185 		panic("glfw: glfw.CreateWindow must not return nil")
    186 	}
    187 	defer w.Destroy()
    188 	initializeWindowAfterCreation(w)
    189 
    190 	m := currentMonitor(w)
    191 	theUI.initMonitor = m
    192 	v := m.GetVideoMode()
    193 	scale := videoModeScale(m)
    194 	theUI.initFullscreenWidthInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Width), scale))
    195 	theUI.initFullscreenHeightInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Height), scale))
    196 
    197 	// Create system cursors. These cursors are destroyed at glfw.Terminate().
    198 	glfwSystemCursors[driver.CursorShapeDefault] = nil
    199 	glfwSystemCursors[driver.CursorShapeText] = glfw.CreateStandardCursor(glfw.IBeamCursor)
    200 	glfwSystemCursors[driver.CursorShapeCrosshair] = glfw.CreateStandardCursor(glfw.CrosshairCursor)
    201 	glfwSystemCursors[driver.CursorShapePointer] = glfw.CreateStandardCursor(glfw.HandCursor)
    202 	glfwSystemCursors[driver.CursorShapeEWResize] = glfw.CreateStandardCursor(glfw.HResizeCursor)
    203 	glfwSystemCursors[driver.CursorShapeNSResize] = glfw.CreateStandardCursor(glfw.VResizeCursor)
    204 
    205 	return nil
    206 }
    207 
    208 type monitor struct {
    209 	m  *glfw.Monitor
    210 	vm *glfw.VidMode
    211 	// Pos of monitor in virtual coords
    212 	x int
    213 	y int
    214 }
    215 
    216 // monitors is the monitor list cache for desktop glfw compile targets.
    217 // populated by 'updateMonitors' which is called on init and every
    218 // monitor config change event.
    219 //
    220 // monitors must be manipulated on the main thread.
    221 var monitors []*monitor
    222 
    223 func updateMonitors() {
    224 	monitors = nil
    225 	ms := glfw.GetMonitors()
    226 	for _, m := range ms {
    227 		x, y := m.GetPos()
    228 		monitors = append(monitors, &monitor{
    229 			m:  m,
    230 			vm: m.GetVideoMode(),
    231 			x:  x,
    232 			y:  y,
    233 		})
    234 	}
    235 	clearVideoModeScaleCache()
    236 	devicescale.ClearCache()
    237 }
    238 
    239 func ensureMonitors() []*monitor {
    240 	if len(monitors) == 0 {
    241 		updateMonitors()
    242 	}
    243 	return monitors
    244 }
    245 
    246 // getMonitorFromPosition returns a monitor for the given window x/y,
    247 // or returns nil if monitor is not found.
    248 //
    249 // getMonitorFromPosition must be called on the main thread.
    250 func getMonitorFromPosition(wx, wy int) *monitor {
    251 	for _, m := range ensureMonitors() {
    252 		// TODO: Fix incorrectness in the cases of https://github.com/glfw/glfw/issues/1961.
    253 		// See also internal/devicescale/impl_desktop.go for a maybe better way of doing this.
    254 		if m.x <= wx && wx < m.x+m.vm.Width && m.y <= wy && wy < m.y+m.vm.Height {
    255 			return m
    256 		}
    257 	}
    258 	return nil
    259 }
    260 
    261 func (u *UserInterface) isRunning() bool {
    262 	return atomic.LoadUint32(&u.running) != 0
    263 }
    264 
    265 func (u *UserInterface) setRunning(running bool) {
    266 	if running {
    267 		atomic.StoreUint32(&u.running, 1)
    268 	} else {
    269 		atomic.StoreUint32(&u.running, 0)
    270 	}
    271 }
    272 
    273 func (u *UserInterface) getWindowSizeLimits() (minw, minh, maxw, maxh int) {
    274 	u.m.RLock()
    275 	defer u.m.RUnlock()
    276 
    277 	minw, minh, maxw, maxh = -1, -1, -1, -1
    278 	if u.minWindowWidthInDP >= 0 {
    279 		minw = int(u.toGLFWPixel(float64(u.minWindowWidthInDP)))
    280 	}
    281 	if u.minWindowHeightInDP >= 0 {
    282 		minh = int(u.toGLFWPixel(float64(u.minWindowHeightInDP)))
    283 	}
    284 	if u.maxWindowWidthInDP >= 0 {
    285 		maxw = int(u.toGLFWPixel(float64(u.maxWindowWidthInDP)))
    286 	}
    287 	if u.maxWindowHeightInDP >= 0 {
    288 		maxh = int(u.toGLFWPixel(float64(u.maxWindowHeightInDP)))
    289 	}
    290 	return
    291 }
    292 
    293 func (u *UserInterface) getWindowSizeLimitsInDP() (minw, minh, maxw, maxh int) {
    294 	u.m.RLock()
    295 	defer u.m.RUnlock()
    296 	return u.minWindowWidthInDP, u.minWindowHeightInDP, u.maxWindowWidthInDP, u.maxWindowHeightInDP
    297 }
    298 
    299 func (u *UserInterface) setWindowSizeLimitsInDP(minw, minh, maxw, maxh int) bool {
    300 	u.m.RLock()
    301 	defer u.m.RUnlock()
    302 	if u.minWindowWidthInDP == minw && u.minWindowHeightInDP == minh && u.maxWindowWidthInDP == maxw && u.maxWindowHeightInDP == maxh {
    303 		return false
    304 	}
    305 	u.minWindowWidthInDP = minw
    306 	u.minWindowHeightInDP = minh
    307 	u.maxWindowWidthInDP = maxw
    308 	u.maxWindowHeightInDP = maxh
    309 	return true
    310 }
    311 
    312 func (u *UserInterface) getInitTitle() string {
    313 	u.m.RLock()
    314 	v := u.initTitle
    315 	u.m.RUnlock()
    316 	return v
    317 }
    318 
    319 func (u *UserInterface) setInitTitle(title string) {
    320 	u.m.RLock()
    321 	u.initTitle = title
    322 	u.m.RUnlock()
    323 }
    324 
    325 func (u *UserInterface) getInitFPSMode() driver.FPSMode {
    326 	u.m.RLock()
    327 	v := u.initFPSMode
    328 	u.m.RUnlock()
    329 	return v
    330 }
    331 
    332 func (u *UserInterface) isInitFullscreen() bool {
    333 	u.m.RLock()
    334 	v := u.initFullscreen
    335 	u.m.RUnlock()
    336 	return v
    337 }
    338 
    339 func (u *UserInterface) setInitFullscreen(initFullscreen bool) {
    340 	u.m.Lock()
    341 	u.initFullscreen = initFullscreen
    342 	u.m.Unlock()
    343 }
    344 
    345 func (u *UserInterface) getInitCursorMode() driver.CursorMode {
    346 	u.m.RLock()
    347 	v := u.initCursorMode
    348 	u.m.RUnlock()
    349 	return v
    350 }
    351 
    352 func (u *UserInterface) setInitCursorMode(mode driver.CursorMode) {
    353 	u.m.Lock()
    354 	u.initCursorMode = mode
    355 	u.m.Unlock()
    356 }
    357 
    358 func (u *UserInterface) getCursorShape() driver.CursorShape {
    359 	u.m.RLock()
    360 	v := u.cursorShape
    361 	u.m.RUnlock()
    362 	return v
    363 }
    364 
    365 func (u *UserInterface) setCursorShape(shape driver.CursorShape) driver.CursorShape {
    366 	u.m.Lock()
    367 	old := u.cursorShape
    368 	u.cursorShape = shape
    369 	u.m.Unlock()
    370 	return old
    371 }
    372 
    373 func (u *UserInterface) isInitWindowDecorated() bool {
    374 	u.m.RLock()
    375 	v := u.initWindowDecorated
    376 	u.m.RUnlock()
    377 	return v
    378 }
    379 
    380 func (u *UserInterface) setInitWindowDecorated(decorated bool) {
    381 	u.m.Lock()
    382 	u.initWindowDecorated = decorated
    383 	u.m.Unlock()
    384 }
    385 
    386 func (u *UserInterface) isRunnableOnUnfocused() bool {
    387 	u.m.RLock()
    388 	v := u.runnableOnUnfocused
    389 	u.m.RUnlock()
    390 	return v
    391 }
    392 
    393 func (u *UserInterface) setRunnableOnUnfocused(runnableOnUnfocused bool) {
    394 	u.m.Lock()
    395 	u.runnableOnUnfocused = runnableOnUnfocused
    396 	u.m.Unlock()
    397 }
    398 
    399 func (u *UserInterface) isInitWindowResizable() bool {
    400 	u.m.RLock()
    401 	v := u.initWindowResizable
    402 	u.m.RUnlock()
    403 	return v
    404 }
    405 
    406 func (u *UserInterface) setInitWindowResizable(resizable bool) {
    407 	u.m.Lock()
    408 	u.initWindowResizable = resizable
    409 	u.m.Unlock()
    410 }
    411 
    412 func (u *UserInterface) isInitScreenTransparent() bool {
    413 	u.m.RLock()
    414 	v := u.initScreenTransparent
    415 	u.m.RUnlock()
    416 	return v
    417 }
    418 
    419 func (u *UserInterface) setInitScreenTransparent(transparent bool) {
    420 	u.m.RLock()
    421 	u.initScreenTransparent = transparent
    422 	u.m.RUnlock()
    423 }
    424 
    425 func (u *UserInterface) getIconImages() []image.Image {
    426 	u.m.RLock()
    427 	i := u.iconImages
    428 	u.m.RUnlock()
    429 	return i
    430 }
    431 
    432 func (u *UserInterface) setIconImages(iconImages []image.Image) {
    433 	u.m.Lock()
    434 	u.iconImages = iconImages
    435 	u.m.Unlock()
    436 }
    437 
    438 func (u *UserInterface) getInitWindowPosition() (int, int) {
    439 	u.m.RLock()
    440 	defer u.m.RUnlock()
    441 	if u.initWindowPositionXInDP != invalidPos && u.initWindowPositionYInDP != invalidPos {
    442 		return u.initWindowPositionXInDP, u.initWindowPositionYInDP
    443 	}
    444 	return invalidPos, invalidPos
    445 }
    446 
    447 func (u *UserInterface) setInitWindowPosition(x, y int) {
    448 	u.m.Lock()
    449 	defer u.m.Unlock()
    450 
    451 	u.initWindowPositionXInDP = x
    452 	u.initWindowPositionYInDP = y
    453 }
    454 
    455 func (u *UserInterface) getInitWindowSize() (int, int) {
    456 	u.m.Lock()
    457 	w, h := u.initWindowWidthInDP, u.initWindowHeightInDP
    458 	u.m.Unlock()
    459 	return w, h
    460 }
    461 
    462 func (u *UserInterface) setInitWindowSize(width, height int) {
    463 	u.m.Lock()
    464 	u.initWindowWidthInDP, u.initWindowHeightInDP = width, height
    465 	u.m.Unlock()
    466 }
    467 
    468 func (u *UserInterface) isInitWindowFloating() bool {
    469 	u.m.Lock()
    470 	f := u.initWindowFloating
    471 	u.m.Unlock()
    472 	return f
    473 }
    474 
    475 func (u *UserInterface) setInitWindowFloating(floating bool) {
    476 	u.m.Lock()
    477 	u.initWindowFloating = floating
    478 	u.m.Unlock()
    479 }
    480 
    481 func (u *UserInterface) isInitWindowMaximized() bool {
    482 	u.m.Lock()
    483 	m := u.initWindowMaximized
    484 	u.m.Unlock()
    485 	return m
    486 }
    487 
    488 func (u *UserInterface) setInitWindowMaximized(maximized bool) {
    489 	u.m.Lock()
    490 	u.initWindowMaximized = maximized
    491 	u.m.Unlock()
    492 }
    493 
    494 func (u *UserInterface) isWindowClosingHandled() bool {
    495 	u.m.Lock()
    496 	v := u.windowClosingHandled
    497 	u.m.Unlock()
    498 	return v
    499 }
    500 
    501 func (u *UserInterface) setWindowClosingHandled(handled bool) {
    502 	u.m.Lock()
    503 	u.windowClosingHandled = handled
    504 	u.m.Unlock()
    505 }
    506 
    507 func (u *UserInterface) isWindowBeingClosed() bool {
    508 	u.m.Lock()
    509 	v := u.windowBeingClosed
    510 	u.m.Unlock()
    511 	return v
    512 }
    513 
    514 func (u *UserInterface) isInitFocused() bool {
    515 	u.m.Lock()
    516 	v := u.initFocused
    517 	u.m.Unlock()
    518 	return v
    519 }
    520 
    521 func (u *UserInterface) setInitFocused(focused bool) {
    522 	u.m.Lock()
    523 	u.initFocused = focused
    524 	u.m.Unlock()
    525 }
    526 
    527 func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
    528 	if !u.isRunning() {
    529 		return u.initFullscreenWidthInDP, u.initFullscreenHeightInDP
    530 	}
    531 
    532 	var w, h int
    533 	_ = u.t.Call(func() error {
    534 		m := currentMonitor(u.window)
    535 		v := m.GetVideoMode()
    536 		s := videoModeScale(m)
    537 		w = int(u.fromGLFWMonitorPixel(float64(v.Width), s))
    538 		h = int(u.fromGLFWMonitorPixel(float64(v.Height), s))
    539 		return nil
    540 	})
    541 	return w, h
    542 }
    543 
    544 // isFullscreen must be called from the main thread.
    545 func (u *UserInterface) isFullscreen() bool {
    546 	if !u.isRunning() {
    547 		panic("glfw: isFullscreen can't be called before the main loop starts")
    548 	}
    549 	return u.window.GetMonitor() != nil || u.isNativeFullscreen()
    550 }
    551 
    552 func (u *UserInterface) IsFullscreen() bool {
    553 	if !u.isRunning() {
    554 		return u.isInitFullscreen()
    555 	}
    556 	b := false
    557 	_ = u.t.Call(func() error {
    558 		b = u.isFullscreen()
    559 		return nil
    560 	})
    561 	return b
    562 }
    563 
    564 func (u *UserInterface) SetFullscreen(fullscreen bool) {
    565 	if !u.isRunning() {
    566 		u.setInitFullscreen(fullscreen)
    567 		return
    568 	}
    569 
    570 	var update bool
    571 	_ = u.t.Call(func() error {
    572 		update = u.isFullscreen() != fullscreen
    573 		return nil
    574 	})
    575 	if !update {
    576 		return
    577 	}
    578 
    579 	_ = u.t.Call(func() error {
    580 		w, h := u.windowWidth, u.windowHeight
    581 		u.setWindowSize(w, h, fullscreen)
    582 		return nil
    583 	})
    584 }
    585 
    586 func (u *UserInterface) IsFocused() bool {
    587 	if !u.isRunning() {
    588 		return false
    589 	}
    590 
    591 	var focused bool
    592 	_ = u.t.Call(func() error {
    593 		focused = u.window.GetAttrib(glfw.Focused) == glfw.True
    594 		return nil
    595 	})
    596 	return focused
    597 }
    598 
    599 func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
    600 	u.setRunnableOnUnfocused(runnableOnUnfocused)
    601 }
    602 
    603 func (u *UserInterface) IsRunnableOnUnfocused() bool {
    604 	return u.isRunnableOnUnfocused()
    605 }
    606 
    607 func (u *UserInterface) SetFPSMode(mode driver.FPSMode) {
    608 	if !u.isRunning() {
    609 		// In general, m is used for locking init* values.
    610 		// m is not used for updating vsync in setWindowSize so far, but
    611 		// it should be OK since any goroutines can't reach here when
    612 		// the game already starts and setWindowSize can be called.
    613 		u.m.Lock()
    614 		u.initFPSMode = mode
    615 		u.m.Unlock()
    616 		return
    617 	}
    618 	_ = u.t.Call(func() error {
    619 		if !u.fpsModeInited {
    620 			u.m.Lock()
    621 			u.initFPSMode = mode
    622 			u.m.Unlock()
    623 			return nil
    624 		}
    625 		u.setFPSMode(mode)
    626 		u.updateVsync()
    627 		return nil
    628 	})
    629 }
    630 
    631 func (u *UserInterface) FPSMode() driver.FPSMode {
    632 	if !u.isRunning() {
    633 		return u.getInitFPSMode()
    634 	}
    635 	var v driver.FPSMode
    636 	_ = u.t.Call(func() error {
    637 		if !u.fpsModeInited {
    638 			v = u.getInitFPSMode()
    639 			return nil
    640 		}
    641 		v = u.fpsMode
    642 		return nil
    643 	})
    644 	return v
    645 }
    646 
    647 func (u *UserInterface) ScheduleFrame() {
    648 	if !u.isRunning() {
    649 		return
    650 	}
    651 	// As the main thread can be blocked, do not check the current FPS mode.
    652 	// PostEmptyEvent is concurrent safe.
    653 	glfw.PostEmptyEvent()
    654 }
    655 
    656 func (u *UserInterface) CursorMode() driver.CursorMode {
    657 	if !u.isRunning() {
    658 		return u.getInitCursorMode()
    659 	}
    660 	var v driver.CursorMode
    661 	_ = u.t.Call(func() error {
    662 		mode := u.window.GetInputMode(glfw.CursorMode)
    663 		switch mode {
    664 		case glfw.CursorNormal:
    665 			v = driver.CursorModeVisible
    666 		case glfw.CursorHidden:
    667 			v = driver.CursorModeHidden
    668 		case glfw.CursorDisabled:
    669 			v = driver.CursorModeCaptured
    670 		default:
    671 			panic(fmt.Sprintf("glfw: invalid GLFW cursor mode: %d", mode))
    672 		}
    673 		return nil
    674 	})
    675 	return v
    676 }
    677 
    678 func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
    679 	if !u.isRunning() {
    680 		u.setInitCursorMode(mode)
    681 		return
    682 	}
    683 	_ = u.t.Call(func() error {
    684 		u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(mode))
    685 		return nil
    686 	})
    687 }
    688 
    689 func (u *UserInterface) CursorShape() driver.CursorShape {
    690 	return u.getCursorShape()
    691 }
    692 
    693 func (u *UserInterface) SetCursorShape(shape driver.CursorShape) {
    694 	old := u.setCursorShape(shape)
    695 	if old == shape {
    696 		return
    697 	}
    698 	if !u.isRunning() {
    699 		return
    700 	}
    701 	_ = u.t.Call(func() error {
    702 		u.setNativeCursor(shape)
    703 		return nil
    704 	})
    705 }
    706 
    707 func (u *UserInterface) DeviceScaleFactor() float64 {
    708 	if !u.isRunning() {
    709 		// TODO: Use the initWindowPosition. This requires to convert the units correctly (#1575).
    710 		return u.deviceScaleFactor()
    711 	}
    712 
    713 	f := 0.0
    714 	_ = u.t.Call(func() error {
    715 		f = u.deviceScaleFactor()
    716 		return nil
    717 	})
    718 	return f
    719 }
    720 
    721 // deviceScaleFactor must be called from the main thread.
    722 func (u *UserInterface) deviceScaleFactor() float64 {
    723 	m := u.initMonitor
    724 	if u.window != nil {
    725 		m = currentMonitor(u.window)
    726 	}
    727 	mx, my := m.GetPos()
    728 	return devicescale.GetAt(mx, my)
    729 }
    730 
    731 func init() {
    732 	// Lock the main thread.
    733 	runtime.LockOSThread()
    734 }
    735 
    736 func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) {
    737 	panic("glfw: RunWithoutMainLoop is not implemented")
    738 }
    739 
    740 // createWindow creates a GLFW window.
    741 //
    742 // createWindow must be called from the main thread.
    743 //
    744 // createWindow does not set the position or size so far.
    745 func (u *UserInterface) createWindow() error {
    746 	if u.window != nil {
    747 		panic("glfw: u.window must not exist at createWindow")
    748 	}
    749 
    750 	// As a start, create a window with temporary size to create OpenGL context thread.
    751 	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
    752 	if err != nil {
    753 		return err
    754 	}
    755 	initializeWindowAfterCreation(window)
    756 
    757 	u.window = window
    758 
    759 	if u.Graphics().IsGL() {
    760 		u.window.MakeContextCurrent()
    761 	}
    762 
    763 	u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(u.getInitCursorMode()))
    764 	u.window.SetCursor(glfwSystemCursors[u.getCursorShape()])
    765 	u.window.SetTitle(u.title)
    766 	// TODO: Set icons
    767 
    768 	u.registerWindowSetSizeCallback()
    769 	u.registerWindowCloseCallback()
    770 
    771 	return nil
    772 }
    773 
    774 // registerWindowSetSizeCallback must be called from the main thread.
    775 func (u *UserInterface) registerWindowSetSizeCallback() {
    776 	if u.sizeCallback == 0 {
    777 		u.sizeCallback = glfw.ToSizeCallback(func(_ *glfw.Window, width, height int) {
    778 			if !u.setSizeCallbackEnabled {
    779 				return
    780 			}
    781 
    782 			u.adjustViewSize()
    783 
    784 			if u.window.GetAttrib(glfw.Resizable) == glfw.False {
    785 				return
    786 			}
    787 			if u.isFullscreen() && !u.isNativeFullscreen() {
    788 				return
    789 			}
    790 
    791 			if err := u.runOnAnotherThreadFromMainThread(func() error {
    792 				// Disable Vsync temporarily. On macOS, getting a next frame can get stuck (#1740).
    793 				u.Graphics().SetVsyncEnabled(false)
    794 
    795 				var outsideWidth, outsideHeight float64
    796 				var outsideSizeChanged bool
    797 
    798 				_ = u.t.Call(func() error {
    799 					if width != 0 || height != 0 {
    800 						u.setWindowSize(width, height, u.isFullscreen())
    801 					}
    802 
    803 					outsideWidth, outsideHeight, outsideSizeChanged = u.updateSize()
    804 					return nil
    805 				})
    806 				if outsideSizeChanged {
    807 					u.context.Layout(outsideWidth, outsideHeight)
    808 				}
    809 				if err := u.context.ForceUpdateFrame(); err != nil {
    810 					return err
    811 				}
    812 				if u.Graphics().IsGL() {
    813 					_ = u.t.Call(func() error {
    814 						u.swapBuffers()
    815 						return nil
    816 					})
    817 				}
    818 				return nil
    819 			}); err != nil {
    820 				u.err = err
    821 			}
    822 		})
    823 	}
    824 	u.window.SetSizeCallback(u.sizeCallback)
    825 }
    826 
    827 // registerWindowCloseCallback must be called from the main thread.
    828 func (u *UserInterface) registerWindowCloseCallback() {
    829 	if u.closeCallback == 0 {
    830 		u.closeCallback = glfw.ToCloseCallback(func(_ *glfw.Window) {
    831 			u.m.Lock()
    832 			u.windowBeingClosed = true
    833 			u.m.Unlock()
    834 
    835 			if !u.isWindowClosingHandled() {
    836 				return
    837 			}
    838 			u.window.SetShouldClose(false)
    839 		})
    840 	}
    841 	u.window.SetCloseCallback(u.closeCallback)
    842 }
    843 
    844 func (u *UserInterface) init() error {
    845 	if u.Graphics().IsGL() {
    846 		glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI)
    847 		glfw.WindowHint(glfw.ContextVersionMajor, 2)
    848 		glfw.WindowHint(glfw.ContextVersionMinor, 1)
    849 	} else {
    850 		glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
    851 	}
    852 
    853 	glfw.WindowHint(glfw.AutoIconify, glfw.False)
    854 
    855 	decorated := glfw.False
    856 	if u.isInitWindowDecorated() {
    857 		decorated = glfw.True
    858 	}
    859 	glfw.WindowHint(glfw.Decorated, decorated)
    860 
    861 	transparent := glfw.False
    862 	if u.isInitScreenTransparent() {
    863 		transparent = glfw.True
    864 	}
    865 	glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
    866 	u.Graphics().SetTransparent(u.isInitScreenTransparent())
    867 
    868 	resizable := glfw.False
    869 	if u.isInitWindowResizable() {
    870 		resizable = glfw.True
    871 	}
    872 	glfw.WindowHint(glfw.Resizable, resizable)
    873 
    874 	floating := glfw.False
    875 	if u.isInitWindowFloating() {
    876 		floating = glfw.True
    877 	}
    878 	glfw.WindowHint(glfw.Floating, floating)
    879 
    880 	focused := glfw.False
    881 	if u.isInitFocused() {
    882 		focused = glfw.True
    883 	}
    884 	glfw.WindowHint(glfw.FocusOnShow, focused)
    885 
    886 	// Set the window visible explicitly or the application freezes on Wayland (#974).
    887 	if os.Getenv("WAYLAND_DISPLAY") != "" {
    888 		glfw.WindowHint(glfw.Visible, glfw.True)
    889 	}
    890 
    891 	if err := u.createWindow(); err != nil {
    892 		return err
    893 	}
    894 
    895 	u.setSizeCallbackEnabled = true
    896 
    897 	setSize := func() {
    898 		ww, wh := u.getInitWindowSize()
    899 		ww = int(u.toGLFWPixel(float64(ww)))
    900 		wh = int(u.toGLFWPixel(float64(wh)))
    901 		u.setWindowSize(ww, wh, u.isFullscreen())
    902 	}
    903 
    904 	// Set the window size and the window position in this order on Linux or other UNIX using X (#1118),
    905 	// but this should be inverted on Windows. This is very tricky, but there is no obvious way to solve
    906 	// this. This doesn't matter on macOS.
    907 	if runtime.GOOS == "windows" {
    908 		u.setWindowPosition(u.getInitWindowPosition())
    909 		setSize()
    910 	} else {
    911 		setSize()
    912 		u.setWindowPosition(u.getInitWindowPosition())
    913 	}
    914 
    915 	u.updateWindowSizeLimits()
    916 
    917 	// Maximizing a window requires a proper size and position. Call Maximize here (#1117).
    918 	if u.isInitWindowMaximized() {
    919 		u.window.Maximize()
    920 	}
    921 
    922 	u.title = u.getInitTitle()
    923 	u.window.SetTitle(u.title)
    924 	u.window.Show()
    925 
    926 	if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
    927 		g.SetWindow(u.nativeWindow())
    928 	}
    929 
    930 	return nil
    931 }
    932 
    933 func (u *UserInterface) updateSize() (float64, float64, bool) {
    934 	ww, wh := u.windowWidth, u.windowHeight
    935 	u.setWindowSize(ww, wh, u.isFullscreen())
    936 
    937 	if !u.toChangeSize {
    938 		return 0, 0, false
    939 	}
    940 	u.toChangeSize = false
    941 
    942 	var w, h float64
    943 	if u.isFullscreen() && !u.isNativeFullscreen() {
    944 		// On Linux, the window size is not reliable just after making the window
    945 		// fullscreened. Use the monitor size.
    946 		// On macOS's native fullscreen, the window's size returns a more precise size
    947 		// reflecting the adjustment of the view size (#1745).
    948 		m := currentMonitor(u.window)
    949 		v := m.GetVideoMode()
    950 		ww, wh := v.Width, v.Height
    951 		s := videoModeScale(m)
    952 		w = u.fromGLFWMonitorPixel(float64(ww), s)
    953 		h = u.fromGLFWMonitorPixel(float64(wh), s)
    954 	} else {
    955 		// Instead of u.windowWidth and u.windowHeight, use the actual window size here.
    956 		// On Windows, the specified size at SetSize and the actual window size might not
    957 		// match (#1163).
    958 		ww, wh := u.window.GetSize()
    959 		w = u.fromGLFWPixel(float64(ww))
    960 		h = u.fromGLFWPixel(float64(wh))
    961 	}
    962 
    963 	return w, h, true
    964 }
    965 
    966 // setFPSMode must be called from the main thread.
    967 func (u *UserInterface) setFPSMode(fpsMode driver.FPSMode) {
    968 	u.fpsMode = fpsMode
    969 	u.fpsModeInited = true
    970 
    971 	sticky := glfw.True
    972 	if fpsMode == driver.FPSModeVsyncOffMinimum {
    973 		sticky = glfw.False
    974 	}
    975 	u.window.SetInputMode(glfw.StickyMouseButtonsMode, sticky)
    976 	u.window.SetInputMode(glfw.StickyKeysMode, sticky)
    977 }
    978 
    979 // update must be called from the main thread.
    980 func (u *UserInterface) update() (float64, float64, bool, error) {
    981 	if u.err != nil {
    982 		return 0, 0, false, u.err
    983 	}
    984 
    985 	if u.window.ShouldClose() {
    986 		return 0, 0, false, driver.RegularTermination
    987 	}
    988 
    989 	if u.isInitFullscreen() {
    990 		w, h := u.window.GetSize()
    991 		u.setWindowSize(w, h, true)
    992 		u.setInitFullscreen(false)
    993 	}
    994 
    995 	// Initialize vsync after SetMonitor is called. See the comment in updateVsync.
    996 	// Calling this inside setWindowSize didn't work (#1363).
    997 	if !u.fpsModeInited {
    998 		u.setFPSMode(u.getInitFPSMode())
    999 	}
   1000 
   1001 	// Call updateVsync even though fpsMode is not updated.
   1002 	// The vsync state might be changed in other places (e.g., the SetSizeCallback).
   1003 	// Also, when toggling to fullscreen, vsync state might be reset unexpectedly (#1787).
   1004 	u.updateVsync()
   1005 
   1006 	outsideWidth, outsideHeight, outsideSizeChanged := u.updateSize()
   1007 
   1008 	if u.fpsMode != driver.FPSModeVsyncOffMinimum {
   1009 		// TODO: Updating the input can be skipped when clock.Update returns 0 (#1367).
   1010 		glfw.PollEvents()
   1011 	} else {
   1012 		glfw.WaitEvents()
   1013 	}
   1014 	u.input.update(u.window, u.context)
   1015 
   1016 	for !u.isRunnableOnUnfocused() && u.window.GetAttrib(glfw.Focused) == 0 && !u.window.ShouldClose() {
   1017 		if err := hooks.SuspendAudio(); err != nil {
   1018 			return 0, 0, false, err
   1019 		}
   1020 		// Wait for an arbitrary period to avoid busy loop.
   1021 		time.Sleep(time.Second / 60)
   1022 		glfw.PollEvents()
   1023 	}
   1024 	if err := hooks.ResumeAudio(); err != nil {
   1025 		return 0, 0, false, err
   1026 	}
   1027 
   1028 	return outsideWidth, outsideHeight, outsideSizeChanged, nil
   1029 }
   1030 
   1031 func (u *UserInterface) loop() error {
   1032 	defer func() {
   1033 		_ = u.t.Call(func() error {
   1034 			glfw.Terminate()
   1035 			return nil
   1036 		})
   1037 	}()
   1038 
   1039 	for {
   1040 		var unfocused bool
   1041 
   1042 		// On Windows, the focusing state might be always false (#987).
   1043 		// On Windows, even if a window is in another workspace, vsync seems to work.
   1044 		// Then let's assume the window is always 'focused' as a workaround.
   1045 		if runtime.GOOS != "windows" {
   1046 			unfocused = u.window.GetAttrib(glfw.Focused) == glfw.False
   1047 		}
   1048 
   1049 		var t1, t2 time.Time
   1050 
   1051 		if unfocused {
   1052 			t1 = time.Now()
   1053 		}
   1054 
   1055 		var outsideWidth, outsideHeight float64
   1056 		var outsideSizeChanged bool
   1057 		if err := u.t.Call(func() error {
   1058 			var err error
   1059 			outsideWidth, outsideHeight, outsideSizeChanged, err = u.update()
   1060 			return err
   1061 		}); err != nil {
   1062 			return err
   1063 		}
   1064 		if outsideSizeChanged {
   1065 			u.context.Layout(outsideWidth, outsideHeight)
   1066 		}
   1067 
   1068 		if err := u.context.UpdateFrame(); err != nil {
   1069 			return err
   1070 		}
   1071 
   1072 		// Create icon images in a different goroutine (#1478).
   1073 		// In the fullscreen mode, SetIcon fails (#1578).
   1074 		if imgs := u.getIconImages(); len(imgs) > 0 && !u.isFullscreen() {
   1075 			u.setIconImages(nil)
   1076 
   1077 			// Convert the icons in the different goroutine, as (*ebiten.Image).At cannot be invoked
   1078 			// from this goroutine. At works only in between BeginFrame and EndFrame.
   1079 			go func() {
   1080 				newImgs := make([]image.Image, len(imgs))
   1081 				for i, img := range imgs {
   1082 					// TODO: If img is not *ebiten.Image, this converting is not necessary.
   1083 					// However, this package cannot refer *ebiten.Image due to the package
   1084 					// dependencies.
   1085 
   1086 					b := img.Bounds()
   1087 					rgba := image.NewRGBA(b)
   1088 					for j := b.Min.Y; j < b.Max.Y; j++ {
   1089 						for i := b.Min.X; i < b.Max.X; i++ {
   1090 							rgba.Set(i, j, img.At(i, j))
   1091 						}
   1092 					}
   1093 					newImgs[i] = rgba
   1094 				}
   1095 
   1096 				_ = u.t.Call(func() error {
   1097 					// In the fullscreen mode, reset the icon images and try again later.
   1098 					if u.isFullscreen() {
   1099 						u.setIconImages(imgs)
   1100 						return nil
   1101 					}
   1102 					u.window.SetIcon(newImgs)
   1103 					return nil
   1104 				})
   1105 			}()
   1106 		}
   1107 
   1108 		// swapBuffers also checks IsGL, so this condition is redundant.
   1109 		// However, (*thread).Call is not good for performance due to channels.
   1110 		// Let's avoid this whenever possible (#1367).
   1111 		if u.Graphics().IsGL() {
   1112 			_ = u.t.Call(func() error {
   1113 				u.swapBuffers()
   1114 				return nil
   1115 			})
   1116 		}
   1117 
   1118 		if unfocused {
   1119 			t2 = time.Now()
   1120 		}
   1121 
   1122 		// When a window is not focused, SwapBuffers might return immediately and CPU might be busy.
   1123 		// Mitigate this by sleeping (#982).
   1124 		if unfocused {
   1125 			d := t2.Sub(t1)
   1126 			const wait = time.Second / 60
   1127 			if d < wait {
   1128 				time.Sleep(wait - d)
   1129 			}
   1130 		}
   1131 	}
   1132 }
   1133 
   1134 // swapBuffers must be called from the main thread.
   1135 func (u *UserInterface) swapBuffers() {
   1136 	if u.Graphics().IsGL() {
   1137 		u.window.SwapBuffers()
   1138 	}
   1139 }
   1140 
   1141 // updateWindowSizeLimits must be called from the main thread.
   1142 func (u *UserInterface) updateWindowSizeLimits() {
   1143 	minw, minh, maxw, maxh := u.getWindowSizeLimitsInDP()
   1144 	if minw < 0 {
   1145 		minw = glfw.DontCare
   1146 	} else {
   1147 		minw = int(u.toGLFWPixel(float64(minw)))
   1148 	}
   1149 	if minh < 0 {
   1150 		minh = glfw.DontCare
   1151 	} else {
   1152 		minh = int(u.toGLFWPixel(float64(minh)))
   1153 	}
   1154 	if maxw < 0 {
   1155 		maxw = glfw.DontCare
   1156 	} else {
   1157 		maxw = int(u.toGLFWPixel(float64(maxw)))
   1158 	}
   1159 	if maxh < 0 {
   1160 		maxh = glfw.DontCare
   1161 	} else {
   1162 		maxh = int(u.toGLFWPixel(float64(maxh)))
   1163 	}
   1164 	u.window.SetSizeLimits(minw, minh, maxw, maxh)
   1165 }
   1166 
   1167 // adjustWindowSizeBasedOnSizeLimitsInDP adjust the size based on the window size limits.
   1168 // width and height are in device-dependent pixels.
   1169 func (u *UserInterface) adjustWindowSizeBasedOnSizeLimits(width, height int) (int, int) {
   1170 	minw, minh, maxw, maxh := u.getWindowSizeLimits()
   1171 	if minw >= 0 && width < minw {
   1172 		width = minw
   1173 	}
   1174 	if minh >= 0 && height < minh {
   1175 		height = minh
   1176 	}
   1177 	if maxw >= 0 && width > maxw {
   1178 		width = maxw
   1179 	}
   1180 	if maxh >= 0 && height > maxh {
   1181 		height = maxh
   1182 	}
   1183 	return width, height
   1184 }
   1185 
   1186 // adjustWindowSizeBasedOnSizeLimitsInDP adjust the size based on the window size limits.
   1187 // width and height are in device-independent pixels.
   1188 func (u *UserInterface) adjustWindowSizeBasedOnSizeLimitsInDP(width, height int) (int, int) {
   1189 	minw, minh, maxw, maxh := u.getWindowSizeLimitsInDP()
   1190 	if minw >= 0 && width < minw {
   1191 		width = minw
   1192 	}
   1193 	if minh >= 0 && height < minh {
   1194 		height = minh
   1195 	}
   1196 	if maxw >= 0 && width > maxw {
   1197 		width = maxw
   1198 	}
   1199 	if maxh >= 0 && height > maxh {
   1200 		height = maxh
   1201 	}
   1202 	return width, height
   1203 }
   1204 
   1205 // setWindowSize must be called from the main thread.
   1206 func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
   1207 	width, height = u.adjustWindowSizeBasedOnSizeLimits(width, height)
   1208 
   1209 	u.Graphics().SetFullscreen(fullscreen)
   1210 
   1211 	if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
   1212 		return
   1213 	}
   1214 
   1215 	if width < 1 {
   1216 		width = 1
   1217 	}
   1218 	if height < 1 {
   1219 		height = 1
   1220 	}
   1221 
   1222 	u.lastDeviceScaleFactor = u.deviceScaleFactor()
   1223 
   1224 	// To make sure the current existing framebuffers are rendered,
   1225 	// swap buffers here before SetSize is called.
   1226 	u.swapBuffers()
   1227 
   1228 	// Disable the callback of SetSize. This callback can be invoked by SetMonitor or SetSize.
   1229 	// ForceUpdateFrame is called from the callback.
   1230 	// While setWindowSize can be called from UpdateFrame,
   1231 	// calling ForceUpdateFrame inside UpdateFrame is illegal (#1505).
   1232 	if u.setSizeCallbackEnabled {
   1233 		u.setSizeCallbackEnabled = false
   1234 		defer func() {
   1235 			u.setSizeCallbackEnabled = true
   1236 		}()
   1237 	}
   1238 
   1239 	windowRecreated := u.setWindowSizeImpl(width, height, fullscreen)
   1240 
   1241 	u.adjustViewSize()
   1242 
   1243 	// As width might be updated, update windowWidth/Height here.
   1244 	u.windowWidth = width
   1245 	u.windowHeight = height
   1246 
   1247 	u.toChangeSize = true
   1248 
   1249 	if windowRecreated {
   1250 		if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
   1251 			g.SetWindow(u.nativeWindow())
   1252 		}
   1253 	}
   1254 }
   1255 
   1256 func (u *UserInterface) setWindowSizeImpl(width, height int, fullscreen bool) bool {
   1257 	var windowRecreated bool
   1258 
   1259 	if fullscreen {
   1260 		if x, y := u.origPos(); x == invalidPos || y == invalidPos {
   1261 			u.setOrigPos(u.window.GetPos())
   1262 		}
   1263 
   1264 		if u.isNativeFullscreenAvailable() {
   1265 			u.setNativeFullscreen(fullscreen)
   1266 		} else {
   1267 			m := currentMonitor(u.window)
   1268 			v := m.GetVideoMode()
   1269 			u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
   1270 
   1271 			// Swapping buffer is necesary to prevent the image lag (#1004).
   1272 			// TODO: This might not work when vsync is disabled.
   1273 			if u.Graphics().IsGL() {
   1274 				glfw.PollEvents()
   1275 				u.swapBuffers()
   1276 			}
   1277 		}
   1278 	} else {
   1279 		// On Windows, giving a too small width doesn't call a callback (#165).
   1280 		// To prevent hanging up, return asap if the width is too small.
   1281 		// 126 is an arbitrary number and I guess this is small enough.
   1282 		minWindowWidth := int(u.toGLFWPixel(126))
   1283 		if u.window.GetAttrib(glfw.Decorated) == glfw.False {
   1284 			minWindowWidth = 1
   1285 		}
   1286 		if width < minWindowWidth {
   1287 			width = minWindowWidth
   1288 		}
   1289 
   1290 		if u.isNativeFullscreenAvailable() && u.isNativeFullscreen() {
   1291 			u.setNativeFullscreen(false)
   1292 		} else if !u.isNativeFullscreenAvailable() && u.window.GetMonitor() != nil {
   1293 			if u.Graphics().IsGL() {
   1294 				// When OpenGL is used, swapping buffer is enough to solve the image-lag
   1295 				// issue (#1004). Rather, recreating window destroys GPU resources.
   1296 				// TODO: This might not work when vsync is disabled.
   1297 				u.window.SetMonitor(nil, 0, 0, width, height, 0)
   1298 				glfw.PollEvents()
   1299 				u.swapBuffers()
   1300 			} else {
   1301 				// Recreate the window since an image lag remains after coming back from
   1302 				// fullscreen (#1004).
   1303 				if u.window != nil {
   1304 					u.window.Destroy()
   1305 					u.window = nil
   1306 				}
   1307 				if err := u.createWindow(); err != nil {
   1308 					// TODO: This should return an error.
   1309 					panic(fmt.Sprintf("glfw: failed to recreate window: %v", err))
   1310 				}
   1311 				// Reset the size limits explicitly.
   1312 				u.updateWindowSizeLimits()
   1313 				u.window.Show()
   1314 				windowRecreated = true
   1315 			}
   1316 		}
   1317 
   1318 		if x, y := u.origPos(); x != invalidPos && y != invalidPos {
   1319 			u.window.SetPos(x, y)
   1320 			// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
   1321 			// work with two or more SetPos.
   1322 			if runtime.GOOS == "darwin" {
   1323 				u.window.SetPos(x+1, y)
   1324 				u.window.SetPos(x, y)
   1325 			}
   1326 			u.setOrigPos(invalidPos, invalidPos)
   1327 		}
   1328 
   1329 		// Set the window size after the position. The order matters.
   1330 		// In the opposite order, the window size might not be correct when going back from fullscreen with multi monitors.
   1331 		oldW, oldH := u.window.GetSize()
   1332 		newW := width
   1333 		newH := height
   1334 		if oldW != newW || oldH != newH {
   1335 			u.framebufferSizeCallbackCh = make(chan struct{}, 1)
   1336 			if u.framebufferSizeCallback == 0 {
   1337 				u.framebufferSizeCallback = glfw.ToFramebufferSizeCallback(func(_ *glfw.Window, _, _ int) {
   1338 					// This callback can be invoked multiple times by one PollEvents in theory (#1618).
   1339 					// Allow the case when the channel is full.
   1340 					select {
   1341 					case u.framebufferSizeCallbackCh <- struct{}{}:
   1342 					default:
   1343 					}
   1344 				})
   1345 			}
   1346 			u.window.SetFramebufferSizeCallback(u.framebufferSizeCallback)
   1347 			u.window.SetSize(newW, newH)
   1348 			// Just after SetSize, GetSize is not reliable especially on Linux/UNIX.
   1349 			// Let's wait for FramebufferSize callback in any cases.
   1350 
   1351 			// Use the timeout as FramebufferSize event might not be fired (#1618).
   1352 			t := time.NewTimer(time.Second)
   1353 			defer t.Stop()
   1354 
   1355 		event:
   1356 			for {
   1357 				glfw.PollEvents()
   1358 				select {
   1359 				case <-u.framebufferSizeCallbackCh:
   1360 					break event
   1361 				case <-t.C:
   1362 					break event
   1363 				default:
   1364 					time.Sleep(time.Millisecond)
   1365 				}
   1366 			}
   1367 			u.window.SetFramebufferSizeCallback(glfw.ToFramebufferSizeCallback(nil))
   1368 			close(u.framebufferSizeCallbackCh)
   1369 			u.framebufferSizeCallbackCh = nil
   1370 		}
   1371 
   1372 		// Window title might be lost on macOS after coming back from fullscreen.
   1373 		u.window.SetTitle(u.title)
   1374 	}
   1375 
   1376 	return windowRecreated
   1377 }
   1378 
   1379 // updateVsync must be called on the main thread.
   1380 func (u *UserInterface) updateVsync() {
   1381 	if u.Graphics().IsGL() {
   1382 		// SwapInterval is affected by the current monitor of the window.
   1383 		// This needs to be called at least after SetMonitor.
   1384 		// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
   1385 		//
   1386 		// TODO: (#405) If triple buffering is needed, SwapInterval(0) should be called,
   1387 		// but is this correct? If glfw.SwapInterval(0) and the driver doesn't support triple
   1388 		// buffering, what will happen?
   1389 		if u.fpsMode == driver.FPSModeVsyncOn {
   1390 			glfw.SwapInterval(1)
   1391 		} else {
   1392 			glfw.SwapInterval(0)
   1393 		}
   1394 	}
   1395 	u.Graphics().SetVsyncEnabled(u.fpsMode == driver.FPSModeVsyncOn)
   1396 }
   1397 
   1398 // currentMonitor returns the current active monitor.
   1399 //
   1400 // The given window might or might not be used to detect the monitor.
   1401 //
   1402 // currentMonitor must be called on the main thread.
   1403 func currentMonitor(window *glfw.Window) *glfw.Monitor {
   1404 	// GetMonitor is available only in fullscreen.
   1405 	if m := window.GetMonitor(); m != nil {
   1406 		return m
   1407 	}
   1408 
   1409 	// Getting a monitor from a window position is not reliable in general (e.g., when a window is put across
   1410 	// multiple monitors, or, before SetWindowPosition is called.).
   1411 	// Get the monitor which the current window belongs to. This requires OS API.
   1412 	if m := currentMonitorByOS(window); m != nil {
   1413 		return m
   1414 	}
   1415 
   1416 	// As the fallback, detect the monitor from the window.
   1417 	if m := getMonitorFromPosition(window.GetPos()); m != nil {
   1418 		return m.m
   1419 	}
   1420 	return glfw.GetPrimaryMonitor()
   1421 }
   1422 
   1423 func (u *UserInterface) SetScreenTransparent(transparent bool) {
   1424 	if !u.isRunning() {
   1425 		u.setInitScreenTransparent(transparent)
   1426 		return
   1427 	}
   1428 	panic("glfw: SetScreenTransparent can't be called after the main loop starts")
   1429 }
   1430 
   1431 func (u *UserInterface) IsScreenTransparent() bool {
   1432 	if !u.isRunning() {
   1433 		return u.isInitScreenTransparent()
   1434 	}
   1435 	val := false
   1436 	_ = u.t.Call(func() error {
   1437 		val = u.window.GetAttrib(glfw.TransparentFramebuffer) == glfw.True
   1438 		return nil
   1439 	})
   1440 	return val
   1441 }
   1442 
   1443 func (u *UserInterface) ResetForFrame() {
   1444 	// The offscreens must be updated every frame (#490).
   1445 	var w, h float64
   1446 	var changed bool
   1447 	_ = u.t.Call(func() error {
   1448 		w, h, changed = u.updateSize()
   1449 		return nil
   1450 	})
   1451 	if changed {
   1452 		u.context.Layout(w, h)
   1453 	}
   1454 	u.input.resetForFrame()
   1455 
   1456 	u.m.Lock()
   1457 	u.windowBeingClosed = false
   1458 	u.m.Unlock()
   1459 }
   1460 
   1461 func (u *UserInterface) MonitorPosition() (int, int) {
   1462 	if !u.isRunning() {
   1463 		return u.monitorPosition()
   1464 	}
   1465 	var mx, my int
   1466 	_ = u.t.Call(func() error {
   1467 		mx, my = u.monitorPosition()
   1468 		return nil
   1469 	})
   1470 	return mx, my
   1471 }
   1472 
   1473 func (u *UserInterface) SetInitFocused(focused bool) {
   1474 	if u.isRunning() {
   1475 		panic("ui: SetInitFocused must be called before the main loop")
   1476 	}
   1477 	u.setInitFocused(focused)
   1478 }
   1479 
   1480 func (u *UserInterface) monitorPosition() (int, int) {
   1481 	// TODO: fromGLFWMonitorPixel might be required.
   1482 	return currentMonitor(u.window).GetPos()
   1483 }
   1484 
   1485 func (u *UserInterface) Input() driver.Input {
   1486 	return &u.input
   1487 }
   1488 
   1489 func (u *UserInterface) Window() driver.Window {
   1490 	return &u.iwindow
   1491 }
   1492 
   1493 // GLFW's functions to manipulate a window can invoke the SetSize callback (#1576, #1585, #1606).
   1494 // As the callback must not be called in the frame (between BeginFrame and EndFrame),
   1495 // disable the callback temporarily.
   1496 
   1497 // maximizeWindow must be called from the main thread.
   1498 func (u *UserInterface) maximizeWindow() {
   1499 	if u.isNativeFullscreen() {
   1500 		return
   1501 	}
   1502 
   1503 	if u.setSizeCallbackEnabled {
   1504 		u.setSizeCallbackEnabled = false
   1505 		defer func() {
   1506 			u.setSizeCallbackEnabled = true
   1507 		}()
   1508 	}
   1509 	u.window.Maximize()
   1510 
   1511 	if !u.isFullscreen() {
   1512 		// On Linux/UNIX, maximizing might not finish even though Maximize returns. Just wait for its finish.
   1513 		// Do not check this in the fullscreen since apparently the condition can never be true.
   1514 		for u.window.GetAttrib(glfw.Maximized) != glfw.True {
   1515 			glfw.PollEvents()
   1516 		}
   1517 
   1518 		// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
   1519 		// Do not call setWindowSize in the fullscreen mode since setWindowSize requires the window size
   1520 		// before the fullscreen, while window.GetSize() returns the desktop screen size in the fullscreen mode.
   1521 		w, h := u.window.GetSize()
   1522 		u.setWindowSize(w, h, u.isFullscreen())
   1523 	}
   1524 }
   1525 
   1526 // iconifyWindow must be called from the main thread.
   1527 func (u *UserInterface) iconifyWindow() {
   1528 	if u.isNativeFullscreen() {
   1529 		return
   1530 	}
   1531 
   1532 	if u.setSizeCallbackEnabled {
   1533 		u.setSizeCallbackEnabled = false
   1534 		defer func() {
   1535 			u.setSizeCallbackEnabled = true
   1536 		}()
   1537 	}
   1538 	u.window.Iconify()
   1539 
   1540 	// On Linux/UNIX, iconifying might not finish even though Iconify returns. Just wait for its finish.
   1541 	for u.window.GetAttrib(glfw.Iconified) != glfw.True {
   1542 		glfw.PollEvents()
   1543 	}
   1544 
   1545 	// After iconifiying, the window is invisible and setWindowSize doesn't have to be called.
   1546 	// Rather, the window size might be (0, 0) and it might be impossible to call setWindowSize (#1585).
   1547 }
   1548 
   1549 // restoreWindow must be called from the main thread.
   1550 func (u *UserInterface) restoreWindow() {
   1551 	if u.setSizeCallbackEnabled {
   1552 		u.setSizeCallbackEnabled = false
   1553 		defer func() {
   1554 			u.setSizeCallbackEnabled = true
   1555 		}()
   1556 	}
   1557 
   1558 	u.window.Restore()
   1559 
   1560 	// On Linux/UNIX, restoring might not finish even though Restore returns (#1608). Just wait for its finish.
   1561 	// On macOS, the restoring state might be the same as the maximized state. Skip this.
   1562 	if runtime.GOOS != "darwin" {
   1563 		for u.window.GetAttrib(glfw.Maximized) == glfw.True || u.window.GetAttrib(glfw.Iconified) == glfw.True {
   1564 			glfw.PollEvents()
   1565 		}
   1566 	}
   1567 
   1568 	// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
   1569 	// Do not call setWindowSize in the fullscreen mode since setWindowSize requires the window size
   1570 	// before the fullscreen, while window.GetSize() returns the desktop screen size in the fullscreen mode.
   1571 	if !u.isFullscreen() {
   1572 		w, h := u.window.GetSize()
   1573 		u.setWindowSize(w, h, u.isFullscreen())
   1574 	}
   1575 }
   1576 
   1577 // setWindowDecorated must be called from the main thread.
   1578 func (u *UserInterface) setWindowDecorated(decorated bool) {
   1579 	if u.setSizeCallbackEnabled {
   1580 		u.setSizeCallbackEnabled = false
   1581 		defer func() {
   1582 			u.setSizeCallbackEnabled = true
   1583 		}()
   1584 	}
   1585 	v := glfw.False
   1586 	if decorated {
   1587 		v = glfw.True
   1588 	}
   1589 	u.window.SetAttrib(glfw.Decorated, v)
   1590 
   1591 	// The title can be lost when the decoration is gone. Recover this.
   1592 	if decorated {
   1593 		u.window.SetTitle(u.title)
   1594 	}
   1595 }
   1596 
   1597 // setWindowFloating must be called from the main thread.
   1598 func (u *UserInterface) setWindowFloating(floating bool) {
   1599 	if u.setSizeCallbackEnabled {
   1600 		u.setSizeCallbackEnabled = false
   1601 		defer func() {
   1602 			u.setSizeCallbackEnabled = true
   1603 		}()
   1604 	}
   1605 	v := glfw.False
   1606 	if floating {
   1607 		v = glfw.True
   1608 	}
   1609 	u.window.SetAttrib(glfw.Floating, v)
   1610 }
   1611 
   1612 // setWindowResizable must be called from the main thread.
   1613 func (u *UserInterface) setWindowResizable(resizable bool) {
   1614 	if u.setSizeCallbackEnabled {
   1615 		u.setSizeCallbackEnabled = false
   1616 		defer func() {
   1617 			u.setSizeCallbackEnabled = true
   1618 		}()
   1619 	}
   1620 
   1621 	v := glfw.False
   1622 	if resizable {
   1623 		v = glfw.True
   1624 	}
   1625 	u.window.SetAttrib(glfw.Resizable, v)
   1626 }
   1627 
   1628 // setWindowPosition must be called from the main thread.
   1629 func (u *UserInterface) setWindowPosition(x, y int) {
   1630 	if u.setSizeCallbackEnabled {
   1631 		u.setSizeCallbackEnabled = false
   1632 		defer func() {
   1633 			u.setSizeCallbackEnabled = true
   1634 		}()
   1635 	}
   1636 
   1637 	mx, my := currentMonitor(u.window).GetPos()
   1638 	xf := u.toGLFWPixel(float64(x))
   1639 	yf := u.toGLFWPixel(float64(y))
   1640 	if x, y := u.adjustWindowPosition(mx+int(xf), my+int(yf)); u.isFullscreen() {
   1641 		u.setOrigPos(x, y)
   1642 	} else {
   1643 		u.window.SetPos(x, y)
   1644 	}
   1645 
   1646 	// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
   1647 	//
   1648 	// There are cases when setWindowSize should be called (#1606) and should not be called (#1609).
   1649 	// For the former, macOS seems enough so far.
   1650 	//
   1651 	// Do not call setWindowSize in the fullscreen mode since setWindowSize requires the window size
   1652 	// before the fullscreen, while window.GetSize() returns the desktop screen size in the fullscreen mode.
   1653 	if !u.isFullscreen() && runtime.GOOS == "darwin" {
   1654 		w, h := u.window.GetSize()
   1655 		u.setWindowSize(w, h, u.isFullscreen())
   1656 	}
   1657 }
   1658 
   1659 // setWindowTitle must be called from the main thread.
   1660 func (u *UserInterface) setWindowTitle(title string) {
   1661 	if u.setSizeCallbackEnabled {
   1662 		u.setSizeCallbackEnabled = false
   1663 		defer func() {
   1664 			u.setSizeCallbackEnabled = true
   1665 		}()
   1666 	}
   1667 
   1668 	u.window.SetTitle(title)
   1669 }
   1670 
   1671 func (u *UserInterface) origPos() (int, int) {
   1672 	// On macOS, the window can be fullscreened without calling an Ebiten function.
   1673 	// Then, an original position might not be available by u.window.GetPos().
   1674 	// Do not rely on the window position.
   1675 	if u.isNativeFullscreenAvailable() {
   1676 		return invalidPos, invalidPos
   1677 	}
   1678 	return u.origPosX, u.origPosY
   1679 }
   1680 
   1681 func (u *UserInterface) setOrigPos(x, y int) {
   1682 	// TODO: The original position should be updated at a 'PosCallback'.
   1683 
   1684 	// On macOS, the window can be fullscreened without calling an Ebiten function.
   1685 	// Then, an original position might not be available by u.window.GetPos().
   1686 	// Do not rely on the window position.
   1687 	if u.isNativeFullscreenAvailable() {
   1688 		return
   1689 	}
   1690 	u.origPosX = x
   1691 	u.origPosY = y
   1692 }