twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

ui.go (27384B)


      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 // +build darwin freebsd linux windows
     16 // +build !android
     17 // +build !ios
     18 
     19 package glfw
     20 
     21 import (
     22 	"fmt"
     23 	"image"
     24 	"os"
     25 	"runtime"
     26 	"sync"
     27 	"time"
     28 
     29 	"github.com/hajimehoshi/ebiten/v2/internal/devicescale"
     30 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     31 	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
     32 	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
     33 	"github.com/hajimehoshi/ebiten/v2/internal/thread"
     34 )
     35 
     36 type UserInterface struct {
     37 	context driver.UIContext
     38 	title   string
     39 	window  *glfw.Window
     40 
     41 	// windowWidth and windowHeight represents a window size.
     42 	// The unit is device-dependent pixels.
     43 	windowWidth  int
     44 	windowHeight int
     45 
     46 	running             bool
     47 	toChangeSize        bool
     48 	origPosX            int
     49 	origPosY            int
     50 	runnableOnUnfocused bool
     51 	vsync               bool
     52 
     53 	lastDeviceScaleFactor float64
     54 
     55 	initMonitor              *glfw.Monitor
     56 	initTitle                string
     57 	initVsync                bool
     58 	initFullscreenWidthInDP  int
     59 	initFullscreenHeightInDP int
     60 	initFullscreen           bool
     61 	initCursorMode           driver.CursorMode
     62 	initWindowDecorated      bool
     63 	initWindowResizable      bool
     64 	initWindowPositionXInDP  int
     65 	initWindowPositionYInDP  int
     66 	initWindowWidthInDP      int
     67 	initWindowHeightInDP     int
     68 	initWindowFloating       bool
     69 	initWindowMaximized      bool
     70 	initScreenTransparent    bool
     71 	initIconImages           []image.Image
     72 	initFocused              bool
     73 
     74 	vsyncInited bool
     75 
     76 	reqWidth  int
     77 	reqHeight int
     78 
     79 	input   Input
     80 	iwindow window
     81 
     82 	t *thread.Thread
     83 	m sync.RWMutex
     84 }
     85 
     86 const (
     87 	maxInt     = int(^uint(0) >> 1)
     88 	minInt     = -maxInt - 1
     89 	invalidPos = minInt
     90 )
     91 
     92 var (
     93 	theUI = &UserInterface{
     94 		runnableOnUnfocused:     true,
     95 		origPosX:                invalidPos,
     96 		origPosY:                invalidPos,
     97 		initVsync:               true,
     98 		initCursorMode:          driver.CursorModeVisible,
     99 		initWindowDecorated:     true,
    100 		initWindowPositionXInDP: invalidPos,
    101 		initWindowPositionYInDP: invalidPos,
    102 		initWindowWidthInDP:     640,
    103 		initWindowHeightInDP:    480,
    104 		initFocused:             true,
    105 		vsync:                   true,
    106 	}
    107 )
    108 
    109 func init() {
    110 	theUI.input.ui = theUI
    111 	theUI.iwindow.ui = theUI
    112 }
    113 
    114 func Get() *UserInterface {
    115 	return theUI
    116 }
    117 
    118 func init() {
    119 	hideConsoleWindowOnWindows()
    120 	if err := initialize(); err != nil {
    121 		panic(err)
    122 	}
    123 	glfw.SetMonitorCallback(func(monitor *glfw.Monitor, event glfw.PeripheralEvent) {
    124 		cacheMonitors()
    125 	})
    126 	cacheMonitors()
    127 }
    128 
    129 func initialize() error {
    130 	if err := glfw.Init(); err != nil {
    131 		return err
    132 	}
    133 	glfw.WindowHint(glfw.Visible, glfw.False)
    134 	glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
    135 
    136 	// Create a window to set the initial monitor.
    137 	w, err := glfw.CreateWindow(16, 16, "", nil, nil)
    138 	if err != nil {
    139 		return err
    140 	}
    141 	if w == nil {
    142 		// This can happen on Windows Remote Desktop (#903).
    143 		panic("glfw: glfw.CreateWindow must not return nil")
    144 	}
    145 
    146 	// Create a window and set it: this affects fromGLFWMonitorPixel and deviceScaleFactor.
    147 	theUI.window = w
    148 	theUI.initMonitor = currentMonitor(w)
    149 	v := theUI.initMonitor.GetVideoMode()
    150 	theUI.initFullscreenWidthInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Width)))
    151 	theUI.initFullscreenHeightInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Height)))
    152 	theUI.window.Destroy()
    153 	theUI.window = nil
    154 
    155 	return nil
    156 }
    157 
    158 type cachedMonitor struct {
    159 	m  *glfw.Monitor
    160 	vm *glfw.VidMode
    161 	// Pos of monitor in virtual coords
    162 	x int
    163 	y int
    164 }
    165 
    166 // monitors is the monitor list cache for desktop glfw compile targets.
    167 // populated by 'cacheMonitors' which is called on init and every
    168 // monitor config change event.
    169 //
    170 // monitors must be manipulated on the main thread.
    171 var monitors []*cachedMonitor
    172 
    173 func cacheMonitors() {
    174 	monitors = nil
    175 	ms := glfw.GetMonitors()
    176 	for _, m := range ms {
    177 		x, y := m.GetPos()
    178 		monitors = append(monitors, &cachedMonitor{
    179 			m:  m,
    180 			vm: m.GetVideoMode(),
    181 			x:  x,
    182 			y:  y,
    183 		})
    184 	}
    185 }
    186 
    187 // getCachedMonitor returns a monitor for the given window x/y,
    188 // or returns nil if monitor is not found.
    189 //
    190 // getCachedMonitor must be called on the main thread.
    191 func getCachedMonitor(wx, wy int) *cachedMonitor {
    192 	for _, m := range monitors {
    193 		if m.x <= wx && wx < m.x+m.vm.Width && m.y <= wy && wy < m.y+m.vm.Height {
    194 			return m
    195 		}
    196 	}
    197 	return nil
    198 }
    199 
    200 func (u *UserInterface) isRunning() bool {
    201 	u.m.RLock()
    202 	v := u.running
    203 	u.m.RUnlock()
    204 	return v
    205 }
    206 
    207 func (u *UserInterface) setRunning(running bool) {
    208 	u.m.Lock()
    209 	u.running = running
    210 	u.m.Unlock()
    211 }
    212 
    213 func (u *UserInterface) getInitTitle() string {
    214 	u.m.RLock()
    215 	v := u.initTitle
    216 	u.m.RUnlock()
    217 	return v
    218 }
    219 
    220 func (u *UserInterface) setInitTitle(title string) {
    221 	u.m.RLock()
    222 	u.initTitle = title
    223 	u.m.RUnlock()
    224 }
    225 
    226 func (u *UserInterface) isInitVsyncEnabled() bool {
    227 	u.m.RLock()
    228 	v := u.initVsync
    229 	u.m.RUnlock()
    230 	return v
    231 }
    232 
    233 func (u *UserInterface) isInitFullscreen() bool {
    234 	u.m.RLock()
    235 	v := u.initFullscreen
    236 	u.m.RUnlock()
    237 	return v
    238 }
    239 
    240 func (u *UserInterface) setInitFullscreen(initFullscreen bool) {
    241 	u.m.Lock()
    242 	u.initFullscreen = initFullscreen
    243 	u.m.Unlock()
    244 }
    245 
    246 func (u *UserInterface) getInitCursorMode() driver.CursorMode {
    247 	u.m.RLock()
    248 	v := u.initCursorMode
    249 	u.m.RUnlock()
    250 	return v
    251 }
    252 
    253 func (u *UserInterface) setInitCursorMode(mode driver.CursorMode) {
    254 	u.m.Lock()
    255 	u.initCursorMode = mode
    256 	u.m.Unlock()
    257 }
    258 
    259 func (u *UserInterface) isInitWindowDecorated() bool {
    260 	u.m.RLock()
    261 	v := u.initWindowDecorated
    262 	u.m.RUnlock()
    263 	return v
    264 }
    265 
    266 func (u *UserInterface) setInitWindowDecorated(decorated bool) {
    267 	u.m.Lock()
    268 	u.initWindowDecorated = decorated
    269 	u.m.Unlock()
    270 }
    271 
    272 func (u *UserInterface) isRunnableOnUnfocused() bool {
    273 	u.m.RLock()
    274 	v := u.runnableOnUnfocused
    275 	u.m.RUnlock()
    276 	return v
    277 }
    278 
    279 func (u *UserInterface) setRunnableOnUnfocused(runnableOnUnfocused bool) {
    280 	u.m.Lock()
    281 	u.runnableOnUnfocused = runnableOnUnfocused
    282 	u.m.Unlock()
    283 }
    284 
    285 func (u *UserInterface) isInitWindowResizable() bool {
    286 	u.m.RLock()
    287 	v := u.initWindowResizable
    288 	u.m.RUnlock()
    289 	return v
    290 }
    291 
    292 func (u *UserInterface) setInitWindowResizable(resizable bool) {
    293 	u.m.Lock()
    294 	u.initWindowResizable = resizable
    295 	u.m.Unlock()
    296 }
    297 
    298 func (u *UserInterface) isInitScreenTransparent() bool {
    299 	u.m.RLock()
    300 	v := u.initScreenTransparent
    301 	u.m.RUnlock()
    302 	return v
    303 }
    304 
    305 func (u *UserInterface) setInitScreenTransparent(transparent bool) {
    306 	u.m.RLock()
    307 	u.initScreenTransparent = transparent
    308 	u.m.RUnlock()
    309 }
    310 
    311 func (u *UserInterface) getInitIconImages() []image.Image {
    312 	u.m.RLock()
    313 	i := u.initIconImages
    314 	u.m.RUnlock()
    315 	return i
    316 }
    317 
    318 func (u *UserInterface) setInitIconImages(iconImages []image.Image) {
    319 	u.m.Lock()
    320 	u.initIconImages = iconImages
    321 	u.m.Unlock()
    322 }
    323 
    324 func (u *UserInterface) getInitWindowPosition() (int, int) {
    325 	u.m.RLock()
    326 	defer u.m.RUnlock()
    327 	if u.initWindowPositionXInDP != invalidPos && u.initWindowPositionYInDP != invalidPos {
    328 		return u.initWindowPositionXInDP, u.initWindowPositionYInDP
    329 	}
    330 	return invalidPos, invalidPos
    331 }
    332 
    333 func (u *UserInterface) setInitWindowPosition(x, y int) {
    334 	u.m.Lock()
    335 	defer u.m.Unlock()
    336 
    337 	u.initWindowPositionXInDP = x
    338 	u.initWindowPositionYInDP = y
    339 }
    340 
    341 func (u *UserInterface) getInitWindowSize() (int, int) {
    342 	u.m.Lock()
    343 	w, h := u.initWindowWidthInDP, u.initWindowHeightInDP
    344 	u.m.Unlock()
    345 	return w, h
    346 }
    347 
    348 func (u *UserInterface) setInitWindowSize(width, height int) {
    349 	u.m.Lock()
    350 	u.initWindowWidthInDP, u.initWindowHeightInDP = width, height
    351 	u.m.Unlock()
    352 }
    353 
    354 func (u *UserInterface) isInitWindowFloating() bool {
    355 	u.m.Lock()
    356 	f := u.initWindowFloating
    357 	u.m.Unlock()
    358 	return f
    359 }
    360 
    361 func (u *UserInterface) setInitWindowFloating(floating bool) {
    362 	u.m.Lock()
    363 	u.initWindowFloating = floating
    364 	u.m.Unlock()
    365 }
    366 
    367 func (u *UserInterface) isInitWindowMaximized() bool {
    368 	u.m.Lock()
    369 	f := u.initWindowMaximized
    370 	u.m.Unlock()
    371 	return f
    372 }
    373 
    374 func (u *UserInterface) setInitWindowMaximized(floating bool) {
    375 	u.m.Lock()
    376 	u.initWindowMaximized = floating
    377 	u.m.Unlock()
    378 }
    379 
    380 func (u *UserInterface) isInitFocused() bool {
    381 	u.m.Lock()
    382 	v := u.initFocused
    383 	u.m.Unlock()
    384 	return v
    385 }
    386 
    387 func (u *UserInterface) setInitFocused(focused bool) {
    388 	u.m.Lock()
    389 	u.initFocused = focused
    390 	u.m.Unlock()
    391 }
    392 
    393 func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
    394 	if !u.isRunning() {
    395 		return u.initFullscreenWidthInDP, u.initFullscreenHeightInDP
    396 	}
    397 
    398 	var w, h int
    399 	_ = u.t.Call(func() error {
    400 		v := currentMonitor(u.window).GetVideoMode()
    401 		w = int(u.fromGLFWMonitorPixel(float64(v.Width)))
    402 		h = int(u.fromGLFWMonitorPixel(float64(v.Height)))
    403 		return nil
    404 	})
    405 	return w, h
    406 }
    407 
    408 // isFullscreen must be called from the main thread.
    409 func (u *UserInterface) isFullscreen() bool {
    410 	if !u.isRunning() {
    411 		panic("glfw: isFullscreen can't be called before the main loop starts")
    412 	}
    413 	return u.window.GetMonitor() != nil
    414 }
    415 
    416 func (u *UserInterface) IsFullscreen() bool {
    417 	if !u.isRunning() {
    418 		return u.isInitFullscreen()
    419 	}
    420 	b := false
    421 	_ = u.t.Call(func() error {
    422 		b = u.isFullscreen()
    423 		return nil
    424 	})
    425 	return b
    426 }
    427 
    428 func (u *UserInterface) SetFullscreen(fullscreen bool) {
    429 	if !u.isRunning() {
    430 		u.setInitFullscreen(fullscreen)
    431 		return
    432 	}
    433 
    434 	var update bool
    435 	_ = u.t.Call(func() error {
    436 		update = u.isFullscreen() != fullscreen
    437 		return nil
    438 	})
    439 	if !update {
    440 		return
    441 	}
    442 
    443 	var w, h int
    444 	_ = u.t.Call(func() error {
    445 		w, h = u.windowWidth, u.windowHeight
    446 		return nil
    447 	})
    448 	u.setWindowSize(w, h, fullscreen)
    449 }
    450 
    451 func (u *UserInterface) IsFocused() bool {
    452 	if !u.isRunning() {
    453 		return false
    454 	}
    455 
    456 	var focused bool
    457 	_ = u.t.Call(func() error {
    458 		focused = u.window.GetAttrib(glfw.Focused) == glfw.True
    459 		return nil
    460 	})
    461 	return focused
    462 }
    463 
    464 func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
    465 	u.setRunnableOnUnfocused(runnableOnUnfocused)
    466 }
    467 
    468 func (u *UserInterface) IsRunnableOnUnfocused() bool {
    469 	return u.isRunnableOnUnfocused()
    470 }
    471 
    472 func (u *UserInterface) SetVsyncEnabled(enabled bool) {
    473 	if !u.isRunning() {
    474 		// In general, m is used for locking init* values.
    475 		// m is not used for updating vsync in setWindowSize so far, but
    476 		// it should be OK since any goroutines can't reach here when
    477 		// the game already starts and setWindowSize can be called.
    478 		u.m.Lock()
    479 		u.initVsync = enabled
    480 		u.m.Unlock()
    481 		return
    482 	}
    483 	_ = u.t.Call(func() error {
    484 		if !u.vsyncInited {
    485 			u.m.Lock()
    486 			u.initVsync = enabled
    487 			u.m.Unlock()
    488 			return nil
    489 		}
    490 		u.vsync = enabled
    491 		u.updateVsync()
    492 		return nil
    493 	})
    494 }
    495 
    496 func (u *UserInterface) IsVsyncEnabled() bool {
    497 	if !u.isRunning() {
    498 		return u.isInitVsyncEnabled()
    499 	}
    500 	var v bool
    501 	_ = u.t.Call(func() error {
    502 		if !u.vsyncInited {
    503 			v = u.isInitVsyncEnabled()
    504 			return nil
    505 		}
    506 		v = u.vsync
    507 		return nil
    508 	})
    509 	return v
    510 }
    511 
    512 func (u *UserInterface) CursorMode() driver.CursorMode {
    513 	if !u.isRunning() {
    514 		return u.getInitCursorMode()
    515 	}
    516 	var v driver.CursorMode
    517 	_ = u.t.Call(func() error {
    518 		mode := u.window.GetInputMode(glfw.CursorMode)
    519 		switch mode {
    520 		case glfw.CursorNormal:
    521 			v = driver.CursorModeVisible
    522 		case glfw.CursorHidden:
    523 			v = driver.CursorModeHidden
    524 		case glfw.CursorDisabled:
    525 			v = driver.CursorModeCaptured
    526 		default:
    527 			panic(fmt.Sprintf("invalid cursor mode: %d", mode))
    528 		}
    529 		return nil
    530 	})
    531 	return v
    532 }
    533 
    534 func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
    535 	if !u.isRunning() {
    536 		u.setInitCursorMode(mode)
    537 		return
    538 	}
    539 	_ = u.t.Call(func() error {
    540 		var c int
    541 		switch mode {
    542 		case driver.CursorModeVisible:
    543 			c = glfw.CursorNormal
    544 		case driver.CursorModeHidden:
    545 			c = glfw.CursorHidden
    546 		case driver.CursorModeCaptured:
    547 			c = glfw.CursorDisabled
    548 		default:
    549 			panic(fmt.Sprintf("invalid cursor mode: %d", mode))
    550 		}
    551 		u.window.SetInputMode(glfw.CursorMode, c)
    552 		return nil
    553 	})
    554 }
    555 
    556 func (u *UserInterface) DeviceScaleFactor() float64 {
    557 	if !u.isRunning() {
    558 		return devicescale.GetAt(u.initMonitor.GetPos())
    559 	}
    560 
    561 	f := 0.0
    562 	_ = u.t.Call(func() error {
    563 		f = u.deviceScaleFactor()
    564 		return nil
    565 	})
    566 	return f
    567 }
    568 
    569 // deviceScaleFactor must be called from the main thread.
    570 func (u *UserInterface) deviceScaleFactor() float64 {
    571 	// Before calling SetWindowPosition, the window's position is not reliable.
    572 	if u.iwindow.setPositionCalled {
    573 		// Avoid calling monitor.GetPos if we have the monitor position cached already.
    574 		if cm := getCachedMonitor(u.window.GetPos()); cm != nil {
    575 			return devicescale.GetAt(cm.x, cm.y)
    576 		}
    577 	}
    578 	return devicescale.GetAt(currentMonitor(u.window).GetPos())
    579 }
    580 
    581 func init() {
    582 	// Lock the main thread.
    583 	runtime.LockOSThread()
    584 }
    585 
    586 func (u *UserInterface) Run(uicontext driver.UIContext) error {
    587 	u.context = uicontext
    588 
    589 	// Initialize the main thread first so the thread is available at u.run (#809).
    590 	u.t = thread.New()
    591 	u.Graphics().SetThread(u.t)
    592 
    593 	ch := make(chan error, 1)
    594 	go func() {
    595 		defer func() {
    596 			_ = u.t.Call(func() error {
    597 				return thread.BreakLoop
    598 			})
    599 		}()
    600 
    601 		defer close(ch)
    602 		if err := u.run(); err != nil {
    603 			ch <- err
    604 		}
    605 	}()
    606 
    607 	u.setRunning(true)
    608 	u.t.Loop()
    609 	u.setRunning(false)
    610 	return <-ch
    611 }
    612 
    613 func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) {
    614 	panic("glfw: RunWithoutMainLoop is not implemented")
    615 }
    616 
    617 // createWindow creates a GLFW window.
    618 //
    619 // createWindow must be called from the main thread.
    620 //
    621 // createWindow does not set the position or size so far.
    622 func (u *UserInterface) createWindow() error {
    623 	if u.window != nil {
    624 		panic("glfw: u.window must not exist at createWindow")
    625 	}
    626 
    627 	// As a start, create a window with temporary size to create OpenGL context thread.
    628 	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
    629 	if err != nil {
    630 		return err
    631 	}
    632 	u.window = window
    633 
    634 	if u.Graphics().IsGL() {
    635 		u.window.MakeContextCurrent()
    636 	}
    637 
    638 	u.window.SetInputMode(glfw.StickyMouseButtonsMode, glfw.True)
    639 	u.window.SetInputMode(glfw.StickyKeysMode, glfw.True)
    640 
    641 	mode := glfw.CursorNormal
    642 	switch u.getInitCursorMode() {
    643 	case driver.CursorModeHidden:
    644 		mode = glfw.CursorHidden
    645 	case driver.CursorModeCaptured:
    646 		mode = glfw.CursorDisabled
    647 	}
    648 	u.window.SetInputMode(glfw.CursorMode, mode)
    649 	u.window.SetTitle(u.title)
    650 	// TODO: Set icons
    651 
    652 	u.window.SetSizeCallback(func(_ *glfw.Window, width, height int) {
    653 		if u.window.GetAttrib(glfw.Resizable) == glfw.False {
    654 			return
    655 		}
    656 		if u.isFullscreen() {
    657 			return
    658 		}
    659 		u.reqWidth = width
    660 		u.reqHeight = height
    661 	})
    662 
    663 	return nil
    664 }
    665 
    666 func (u *UserInterface) run() error {
    667 	if err := u.t.Call(func() error {
    668 		if u.Graphics().IsGL() {
    669 			glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI)
    670 			glfw.WindowHint(glfw.ContextVersionMajor, 2)
    671 			glfw.WindowHint(glfw.ContextVersionMinor, 1)
    672 		} else {
    673 			glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
    674 		}
    675 
    676 		decorated := glfw.False
    677 		if u.isInitWindowDecorated() {
    678 			decorated = glfw.True
    679 		}
    680 		glfw.WindowHint(glfw.Decorated, decorated)
    681 
    682 		transparent := glfw.False
    683 		if u.isInitScreenTransparent() {
    684 			transparent = glfw.True
    685 		}
    686 		glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
    687 		u.Graphics().SetTransparent(u.isInitScreenTransparent())
    688 
    689 		resizable := glfw.False
    690 		if u.isInitWindowResizable() {
    691 			resizable = glfw.True
    692 		}
    693 		glfw.WindowHint(glfw.Resizable, resizable)
    694 
    695 		floating := glfw.False
    696 		if u.isInitWindowFloating() {
    697 			floating = glfw.True
    698 		}
    699 		glfw.WindowHint(glfw.Floating, floating)
    700 
    701 		focused := glfw.False
    702 		if u.isInitFocused() {
    703 			focused = glfw.True
    704 		}
    705 		glfw.WindowHint(glfw.FocusOnShow, focused)
    706 
    707 		// Set the window visible explicitly or the application freezes on Wayland (#974).
    708 		if os.Getenv("WAYLAND_DISPLAY") != "" {
    709 			glfw.WindowHint(glfw.Visible, glfw.True)
    710 		}
    711 
    712 		if err := u.createWindow(); err != nil {
    713 			return err
    714 		}
    715 
    716 		if i := u.getInitIconImages(); i != nil {
    717 			u.window.SetIcon(i)
    718 		}
    719 		return nil
    720 	}); err != nil {
    721 		return err
    722 	}
    723 
    724 	setPosition := func() {
    725 		u.iwindow.SetPosition(u.getInitWindowPosition())
    726 	}
    727 	setSize := func() {
    728 		ww, wh := u.getInitWindowSize()
    729 		ww = int(u.toGLFWPixel(float64(ww)))
    730 		wh = int(u.toGLFWPixel(float64(wh)))
    731 		u.setWindowSize(ww, wh, u.isFullscreen())
    732 	}
    733 
    734 	// Set the window size and the window position in this order on Linux or other UNIX using X (#1118),
    735 	// but this should be inverted on Windows. This is very tricky, but there is no obvious way to solve this.
    736 	// This doesn't matter on macOS.
    737 	if runtime.GOOS == "windows" {
    738 		setPosition()
    739 		setSize()
    740 	} else {
    741 		setSize()
    742 		setPosition()
    743 	}
    744 
    745 	// Maximizing a window requires a proper size and position. Call Maximize here (#1117).
    746 	if u.isInitWindowMaximized() {
    747 		_ = u.t.Call(func() error {
    748 			u.window.Maximize()
    749 			return nil
    750 		})
    751 	}
    752 
    753 	_ = u.t.Call(func() error {
    754 		u.title = u.getInitTitle()
    755 		u.window.SetTitle(u.title)
    756 		u.window.Show()
    757 		return nil
    758 	})
    759 
    760 	var w uintptr
    761 	_ = u.t.Call(func() error {
    762 		w = u.nativeWindow()
    763 		return nil
    764 	})
    765 	if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
    766 		g.SetWindow(w)
    767 	}
    768 	return u.loop()
    769 }
    770 
    771 func (u *UserInterface) updateSize() {
    772 	var w, h int
    773 	_ = u.t.Call(func() error {
    774 		w, h = u.windowWidth, u.windowHeight
    775 		return nil
    776 	})
    777 	u.setWindowSize(w, h, u.isFullscreen())
    778 
    779 	sizeChanged := false
    780 	_ = u.t.Call(func() error {
    781 		if !u.toChangeSize {
    782 			return nil
    783 		}
    784 
    785 		u.toChangeSize = false
    786 		sizeChanged = true
    787 		return nil
    788 	})
    789 	if sizeChanged {
    790 		var w, h float64
    791 		_ = u.t.Call(func() error {
    792 			if u.isFullscreen() {
    793 				v := currentMonitor(u.window).GetVideoMode()
    794 				ww, wh := v.Width, v.Height
    795 				w = u.fromGLFWMonitorPixel(float64(ww))
    796 				h = u.fromGLFWMonitorPixel(float64(wh))
    797 			} else {
    798 				// Instead of u.windowWidth and u.windowHeight, use the actual window size here.
    799 				// On Windows, the specified size at SetSize and the actual window size might not
    800 				// match (#1163).
    801 				ww, wh := u.window.GetSize()
    802 				w = u.fromGLFWPixel(float64(ww))
    803 				h = u.fromGLFWPixel(float64(wh))
    804 			}
    805 			// On Linux/UNIX, further adjusting is required (#1307).
    806 			w = u.toFramebufferPixel(w)
    807 			h = u.toFramebufferPixel(h)
    808 			return nil
    809 		})
    810 		u.context.Layout(w, h)
    811 	}
    812 }
    813 
    814 func (u *UserInterface) update() error {
    815 	shouldClose := false
    816 	_ = u.t.Call(func() error {
    817 		shouldClose = u.window.ShouldClose()
    818 		return nil
    819 	})
    820 	if shouldClose {
    821 		return driver.RegularTermination
    822 	}
    823 
    824 	if u.isInitFullscreen() {
    825 		var w, h int
    826 		_ = u.t.Call(func() error {
    827 			w, h = u.window.GetSize()
    828 			return nil
    829 		})
    830 		u.setWindowSize(w, h, true)
    831 		u.setInitFullscreen(false)
    832 	}
    833 
    834 	// Initialize vsync after SetMonitor is called. See the comment in updateVsync.
    835 	// Calling this inside setWindowSize didn't work (#1363).
    836 	_ = u.t.Call(func() error {
    837 		if !u.vsyncInited {
    838 			u.vsync = u.isInitVsyncEnabled()
    839 			u.updateVsync()
    840 			u.vsyncInited = true
    841 		}
    842 		return nil
    843 	})
    844 
    845 	// This call is needed for initialization.
    846 	u.updateSize()
    847 
    848 	_ = u.t.Call(func() error {
    849 		glfw.PollEvents()
    850 		return nil
    851 	})
    852 	u.input.update(u.window, u.context)
    853 	_ = u.t.Call(func() error {
    854 		defer hooks.ResumeAudio()
    855 
    856 		for !u.isRunnableOnUnfocused() && u.window.GetAttrib(glfw.Focused) == 0 {
    857 			hooks.SuspendAudio()
    858 			// Wait for an arbitrary period to avoid busy loop.
    859 			time.Sleep(time.Second / 60)
    860 			glfw.PollEvents()
    861 			if u.window.ShouldClose() {
    862 				return nil
    863 			}
    864 		}
    865 		return nil
    866 	})
    867 	if err := u.context.Update(); err != nil {
    868 		return err
    869 	}
    870 	if err := u.context.Draw(); err != nil {
    871 		return err
    872 	}
    873 
    874 	// Update the screen size when the window is resizable.
    875 	var w, h int
    876 	_ = u.t.Call(func() error {
    877 		w, h = u.reqWidth, u.reqHeight
    878 		return nil
    879 	})
    880 	if w != 0 || h != 0 {
    881 		u.setWindowSize(w, h, u.isFullscreen())
    882 	}
    883 	_ = u.t.Call(func() error {
    884 		u.reqWidth = 0
    885 		u.reqHeight = 0
    886 		return nil
    887 	})
    888 	return nil
    889 }
    890 
    891 func (u *UserInterface) loop() error {
    892 	defer func() {
    893 		_ = u.t.Call(func() error {
    894 			glfw.Terminate()
    895 			return nil
    896 		})
    897 	}()
    898 	for {
    899 		var unfocused bool
    900 
    901 		// On Windows, the focusing state might be always false (#987).
    902 		// On Windows, even if a window is in another workspace, vsync seems to work.
    903 		// Then let's assume the window is always 'focused' as a workaround.
    904 		if runtime.GOOS != "windows" {
    905 			unfocused = u.window.GetAttrib(glfw.Focused) == glfw.False
    906 		}
    907 
    908 		var t1, t2 time.Time
    909 
    910 		if unfocused {
    911 			t1 = time.Now()
    912 		}
    913 		if err := u.update(); err != nil {
    914 			return err
    915 		}
    916 
    917 		_ = u.t.Call(func() error {
    918 			u.swapBuffers()
    919 			return nil
    920 		})
    921 		if unfocused {
    922 			t2 = time.Now()
    923 		}
    924 
    925 		// When a window is not focused, SwapBuffers might return immediately and CPU might be busy.
    926 		// Mitigate this by sleeping (#982).
    927 		if unfocused {
    928 			d := t2.Sub(t1)
    929 			const wait = time.Second / 60
    930 			if d < wait {
    931 				time.Sleep(wait - d)
    932 			}
    933 		}
    934 	}
    935 }
    936 
    937 // swapBuffers must be called from the main thread.
    938 func (u *UserInterface) swapBuffers() {
    939 	if u.Graphics().IsGL() {
    940 		u.window.SwapBuffers()
    941 	}
    942 }
    943 
    944 func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
    945 	windowRecreated := false
    946 
    947 	_ = u.t.Call(func() error {
    948 		if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
    949 			return nil
    950 		}
    951 
    952 		if width < 1 {
    953 			width = 1
    954 		}
    955 		if height < 1 {
    956 			height = 1
    957 		}
    958 
    959 		u.lastDeviceScaleFactor = u.deviceScaleFactor()
    960 
    961 		// To make sure the current existing framebuffers are rendered,
    962 		// swap buffers here before SetSize is called.
    963 		u.swapBuffers()
    964 
    965 		if fullscreen {
    966 			if u.origPosX == invalidPos || u.origPosY == invalidPos {
    967 				u.origPosX, u.origPosY = u.window.GetPos()
    968 			}
    969 			m := currentMonitor(u.window)
    970 			v := m.GetVideoMode()
    971 			u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
    972 
    973 			// Swapping buffer is necesary to prevent the image lag (#1004).
    974 			// TODO: This might not work when vsync is disabled.
    975 			if u.Graphics().IsGL() {
    976 				glfw.PollEvents()
    977 				u.swapBuffers()
    978 			}
    979 		} else {
    980 			// On Windows, giving a too small width doesn't call a callback (#165).
    981 			// To prevent hanging up, return asap if the width is too small.
    982 			// 126 is an arbitrary number and I guess this is small enough.
    983 			minWindowWidth := int(u.toGLFWPixel(126))
    984 			if u.window.GetAttrib(glfw.Decorated) == glfw.False {
    985 				minWindowWidth = 1
    986 			}
    987 			if width < minWindowWidth {
    988 				width = minWindowWidth
    989 			}
    990 
    991 			if u.window.GetMonitor() != nil {
    992 				if u.Graphics().IsGL() {
    993 					// When OpenGL is used, swapping buffer is enough to solve the image-lag
    994 					// issue (#1004). Rather, recreating window destroys GPU resources.
    995 					// TODO: This might not work when vsync is disabled.
    996 					u.window.SetMonitor(nil, 0, 0, width, height, 0)
    997 					glfw.PollEvents()
    998 					u.swapBuffers()
    999 				} else {
   1000 					// Recreate the window since an image lag remains after coming back from
   1001 					// fullscreen (#1004).
   1002 					if u.window != nil {
   1003 						u.window.Destroy()
   1004 						u.window = nil
   1005 					}
   1006 					if err := u.createWindow(); err != nil {
   1007 						// TODO: This should return an error.
   1008 						panic(fmt.Sprintf("glfw: failed to recreate window: %v", err))
   1009 					}
   1010 					u.window.Show()
   1011 					windowRecreated = true
   1012 				}
   1013 			}
   1014 
   1015 			if u.origPosX != invalidPos && u.origPosY != invalidPos {
   1016 				x := u.origPosX
   1017 				y := u.origPosY
   1018 				u.window.SetPos(x, y)
   1019 				// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
   1020 				// work with two or more SetPos.
   1021 				if runtime.GOOS == "darwin" {
   1022 					u.window.SetPos(x+1, y)
   1023 					u.window.SetPos(x, y)
   1024 				}
   1025 				u.origPosX = invalidPos
   1026 				u.origPosY = invalidPos
   1027 			}
   1028 
   1029 			// Set the window size after the position. The order matters.
   1030 			// In the opposite order, the window size might not be correct when going back from fullscreen with multi monitors.
   1031 			oldW, oldH := u.window.GetSize()
   1032 			newW := width
   1033 			newH := height
   1034 			if oldW != newW || oldH != newH {
   1035 				ch := make(chan struct{})
   1036 				u.window.SetFramebufferSizeCallback(func(_ *glfw.Window, _, _ int) {
   1037 					u.window.SetFramebufferSizeCallback(nil)
   1038 					close(ch)
   1039 				})
   1040 				u.window.SetSize(newW, newH)
   1041 			event:
   1042 				for {
   1043 					glfw.PollEvents()
   1044 					select {
   1045 					case <-ch:
   1046 						break event
   1047 					default:
   1048 					}
   1049 				}
   1050 			}
   1051 
   1052 			// Window title might be lost on macOS after coming back from fullscreen.
   1053 			u.window.SetTitle(u.title)
   1054 		}
   1055 
   1056 		// As width might be updated, update windowWidth/Height here.
   1057 		u.windowWidth = width
   1058 		u.windowHeight = height
   1059 
   1060 		u.toChangeSize = true
   1061 		return nil
   1062 	})
   1063 
   1064 	if windowRecreated {
   1065 		if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
   1066 			g.SetWindow(u.nativeWindow())
   1067 		}
   1068 	}
   1069 }
   1070 
   1071 // updateVsync must be called on the main thread.
   1072 func (u *UserInterface) updateVsync() {
   1073 	if u.Graphics().IsGL() {
   1074 		// SwapInterval is affected by the current monitor of the window.
   1075 		// This needs to be called at least after SetMonitor.
   1076 		// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
   1077 		//
   1078 		// TODO: (#405) If triple buffering is needed, SwapInterval(0) should be called,
   1079 		// but is this correct? If glfw.SwapInterval(0) and the driver doesn't support triple
   1080 		// buffering, what will happen?
   1081 		if u.vsync {
   1082 			glfw.SwapInterval(1)
   1083 		} else {
   1084 			glfw.SwapInterval(0)
   1085 		}
   1086 	}
   1087 	u.Graphics().SetVsyncEnabled(u.vsync)
   1088 }
   1089 
   1090 // currentMonitor returns the current active monitor.
   1091 //
   1092 // The given window might or might not be used to detect the monitor.
   1093 //
   1094 // currentMonitor must be called on the main thread.
   1095 func currentMonitor(window *glfw.Window) *glfw.Monitor {
   1096 	// GetMonitor is available only on fullscreen.
   1097 	if m := window.GetMonitor(); m != nil {
   1098 		return m
   1099 	}
   1100 
   1101 	// Getting a monitor from a window position is not reliable in general (e.g., when a window is put across
   1102 	// multiple monitors, or, before SetWindowPosition is called.).
   1103 	// Get the monitor which the current window belongs to. This requires OS API.
   1104 	if m := currentMonitorByOS(window); m != nil {
   1105 		return m
   1106 	}
   1107 
   1108 	// As the fallback, detect the monitor from the window.
   1109 	if m := getCachedMonitor(window.GetPos()); m != nil {
   1110 		return m.m
   1111 	}
   1112 	return glfw.GetPrimaryMonitor()
   1113 }
   1114 
   1115 func (u *UserInterface) SetScreenTransparent(transparent bool) {
   1116 	if !u.isRunning() {
   1117 		u.setInitScreenTransparent(transparent)
   1118 		return
   1119 	}
   1120 	panic("glfw: SetScreenTransparent can't be called after the main loop starts")
   1121 }
   1122 
   1123 func (u *UserInterface) IsScreenTransparent() bool {
   1124 	if !u.isRunning() {
   1125 		return u.isInitScreenTransparent()
   1126 	}
   1127 	val := false
   1128 	_ = u.t.Call(func() error {
   1129 		val = u.window.GetAttrib(glfw.TransparentFramebuffer) == glfw.True
   1130 		return nil
   1131 	})
   1132 	return val
   1133 }
   1134 
   1135 func (u *UserInterface) ResetForFrame() {
   1136 	// The offscreens must be updated every frame (#490).
   1137 	u.updateSize()
   1138 	u.input.resetForFrame()
   1139 }
   1140 
   1141 func (u *UserInterface) MonitorPosition() (int, int) {
   1142 	if !u.isRunning() {
   1143 		return u.monitorPosition()
   1144 	}
   1145 	var mx, my int
   1146 	_ = u.t.Call(func() error {
   1147 		mx, my = u.monitorPosition()
   1148 		return nil
   1149 	})
   1150 	return mx, my
   1151 }
   1152 
   1153 func (u *UserInterface) SetInitFocused(focused bool) {
   1154 	if u.isRunning() {
   1155 		panic("ui: SetInitFocused must be called before the main loop")
   1156 	}
   1157 	u.setInitFocused(focused)
   1158 }
   1159 
   1160 func (u *UserInterface) monitorPosition() (int, int) {
   1161 	// TODO: fromGLFWMonitorPixel might be required.
   1162 	return currentMonitor(u.window).GetPos()
   1163 }
   1164 
   1165 func (u *UserInterface) Input() driver.Input {
   1166 	return &u.input
   1167 }
   1168 
   1169 func (u *UserInterface) Window() driver.Window {
   1170 	return &u.iwindow
   1171 }