ebiten/internal/goglfw/monitor.go
2023-08-01 23:57:05 +09:00

277 lines
6.0 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || windows
package goglfw
import (
"sort"
)
func abs(x int) uint {
if x < 0 {
return uint(-x)
}
return uint(x)
}
func (v *VidMode) equals(other *VidMode) bool {
if v.RedBits+v.GreenBits+v.BlueBits != other.RedBits+other.GreenBits+other.BlueBits {
return false
}
if v.Width != other.Width {
return false
}
if v.Height != other.Height {
return false
}
if v.RefreshRate != other.RefreshRate {
return false
}
return true
}
func (m *Monitor) refreshVideoModes() error {
m.modes = m.modes[:0]
modes, err := m.platformAppendVideoModes(m.modes)
if err != nil {
return err
}
sort.Slice(modes, func(i, j int) bool {
a := modes[i]
b := modes[j]
abpp := a.RedBits + a.GreenBits + a.BlueBits
bbpp := b.RedBits + b.GreenBits + b.BlueBits
if abpp != bbpp {
return abpp < bbpp
}
aarea := a.Width * a.Height
barea := b.Width * b.Height
if aarea != barea {
return aarea < barea
}
if a.Width != b.Width {
return a.Width < b.Width
}
return a.RefreshRate < b.RefreshRate
})
m.modes = modes
return nil
}
func inputMonitor(monitor *Monitor, action PeripheralEvent, placement int) error {
switch action {
case Connected:
switch placement {
case _GLFW_INSERT_FIRST:
_glfw.monitors = append(_glfw.monitors, nil)
copy(_glfw.monitors[1:], _glfw.monitors)
_glfw.monitors[0] = monitor
case _GLFW_INSERT_LAST:
_glfw.monitors = append(_glfw.monitors, monitor)
}
case Disconnected:
for _, window := range _glfw.windows {
if window.monitor == monitor {
width, height, err := window.platformGetWindowSize()
if err != nil {
return err
}
if err := window.platformSetWindowMonitor(nil, 0, 0, width, height, 0); err != nil {
return err
}
xoff, yoff, _, _, err := window.platformGetWindowFrameSize()
if err != nil {
return err
}
if err := window.platformSetWindowPos(xoff, yoff); err != nil {
return err
}
}
}
for i, m := range _glfw.monitors {
if m == monitor {
copy(_glfw.monitors[i:], _glfw.monitors[i+1:])
_glfw.monitors[len(_glfw.monitors)-1] = nil
_glfw.monitors = _glfw.monitors[:len(_glfw.monitors)-1]
break
}
}
}
if _glfw.callbacks.monitor != nil {
_glfw.callbacks.monitor(monitor, action)
}
return nil
}
func (m *Monitor) inputMonitorWindow(window *Window) {
m.window = window
}
func (m *Monitor) chooseVideoMode(desired *VidMode) (*VidMode, error) {
if err := m.refreshVideoModes(); err != nil {
return nil, err
}
// math.MaxUint was added at Go 1.17. See https://github.com/golang/go/issues/28538
const (
intSize = 32 << (^uint(0) >> 63)
maxUint = 1<<intSize - 1
)
var (
leastColorDiff uint = maxUint
leastSizeDiff uint = maxUint
leastRateDiff uint = maxUint
)
var closest *VidMode
for _, v := range m.modes {
var colorDiff uint
if desired.RedBits != DontCare {
colorDiff += abs(v.RedBits - desired.RedBits)
}
if desired.GreenBits != DontCare {
colorDiff += abs(v.GreenBits - desired.GreenBits)
}
if desired.BlueBits != DontCare {
colorDiff += abs(v.BlueBits - desired.BlueBits)
}
sizeDiff := abs((v.Width-desired.Width)*(v.Width-desired.Width) +
(v.Height-desired.Height)*(v.Height-desired.Height))
var rateDiff uint
if desired.RefreshRate != DontCare {
rateDiff = abs(v.RefreshRate - desired.RefreshRate)
} else {
rateDiff = maxUint - uint(v.RefreshRate)
}
if colorDiff < leastColorDiff ||
colorDiff == leastColorDiff && sizeDiff < leastSizeDiff ||
colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff {
closest = v
leastColorDiff = colorDiff
leastSizeDiff = sizeDiff
leastRateDiff = rateDiff
}
}
return closest, nil
}
func splitBPP(bpp int) (red, green, blue int) {
// We assume that by 32 the user really meant 24
if bpp == 32 {
bpp = 24
}
// Convert "bits per pixel" to red, green & blue sizes
red = bpp / 3
green = bpp / 3
blue = bpp / 3
delta := bpp - (red * 3)
if delta >= 1 {
green++
}
if delta == 2 {
red++
}
return
}
// GLFW public APIs
func GetMonitors() ([]*Monitor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return _glfw.monitors, nil
}
func GetPrimaryMonitor() (*Monitor, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
if len(_glfw.monitors) == 0 {
return nil, nil
}
return _glfw.monitors[0], nil
}
func (m *Monitor) GetPos() (xpos, ypos int, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
xpos, ypos, ok := m.platformGetMonitorPos()
if !ok {
return 0, 0, nil
}
return xpos, ypos, nil
}
func (m *Monitor) GetWorkarea() (xpos, ypos, width, height int, err error) {
if !_glfw.initialized {
return 0, 0, 0, 0, NotInitialized
}
xpos, ypos, width, height = m.platformGetMonitorWorkarea()
return
}
// GetPhysicalSize is not implemented.
func (m *Monitor) GetContentScale() (xscale, yscale float32, err error) {
if !_glfw.initialized {
return 0, 0, NotInitialized
}
xscale, yscale, err = m.platformGetMonitorContentScale()
return
}
func (m *Monitor) GetName() (string, error) {
if !_glfw.initialized {
return "", NotInitialized
}
return m.name, nil
}
// SetUserPointer is not implemented.
// GetUserPointer is not implemented.
func SetMonitorCallback(cbfun MonitorCallback) (MonitorCallback, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
old := _glfw.callbacks.monitor
_glfw.callbacks.monitor = cbfun
return old, nil
}
func (m *Monitor) GetVideoModes() ([]*VidMode, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return m.modes, nil
}
func (m *Monitor) GetVideoMode() (*VidMode, error) {
if !_glfw.initialized {
return nil, NotInitialized
}
return m.platformGetVideoMode(), nil
}
// SetGamma is not implemented.
// GetGammaRamp is not implemented.
// SetGammaRamp is not implemented.