mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 19:58:54 +01:00
b359985e97
Many of the structs found in internal.h (internal_windows.go) contain defines such as GLFW_WIN32_WINDOW_STATE which gets replaced with a struct defined in win32platform_windows.go (win32_platform.h). Originally, these structs where directly placed inside of internal_windows.go. However, to make it easier to add macOS and Linux these cannot be in this file. This commit separates the windows specific structs into the respective windows file and updates the field to be named state. Updates #2546
358 lines
9.0 KiB
Go
358 lines
9.0 KiB
Go
// SPDX-License-Identifier: Zlib
|
|
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
|
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy
|
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
|
|
|
package goglfw
|
|
|
|
import (
|
|
"errors"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
|
|
)
|
|
|
|
func monitorCallback(handle _HMONITOR, dc _HDC, rect *_RECT, monitor *Monitor /* _LPARAM */) uintptr /* _BOOL */ {
|
|
if mi, ok := _GetMonitorInfoW_Ex(handle); ok {
|
|
if windows.UTF16ToString(mi.szDevice[:]) == monitor.platform.adapterName {
|
|
monitor.platform.handle = handle
|
|
}
|
|
}
|
|
return 1
|
|
}
|
|
|
|
var monitorCallbackPtr = windows.NewCallbackCDecl(monitorCallback)
|
|
|
|
func createMonitor(adapter *_DISPLAY_DEVICEW, display *_DISPLAY_DEVICEW) (*Monitor, error) {
|
|
var name string
|
|
if display != nil {
|
|
name = windows.UTF16ToString(display.DeviceString[:])
|
|
} else {
|
|
name = windows.UTF16ToString(adapter.DeviceString[:])
|
|
}
|
|
if name == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
adapterDeviceName := windows.UTF16ToString(adapter.DeviceName[:])
|
|
dm, ok := _EnumDisplaySettingsW(adapterDeviceName, _ENUM_CURRENT_SETTINGS)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
|
|
monitor := &Monitor{
|
|
name: name,
|
|
}
|
|
|
|
if adapter.StateFlags&_DISPLAY_DEVICE_MODESPRUNED != 0 {
|
|
monitor.platform.modesPruned = true
|
|
}
|
|
|
|
monitor.platform.adapterName = adapterDeviceName
|
|
if display != nil {
|
|
monitor.platform.displayName = windows.UTF16ToString(display.DeviceName[:])
|
|
}
|
|
|
|
rect := _RECT{
|
|
left: dm.dmPosition.x,
|
|
top: dm.dmPosition.y,
|
|
right: dm.dmPosition.x + int32(dm.dmPelsWidth),
|
|
bottom: dm.dmPosition.y + int32(dm.dmPelsHeight),
|
|
}
|
|
if err := _EnumDisplayMonitors(0, &rect, monitorCallbackPtr, _LPARAM(unsafe.Pointer(monitor))); err != nil {
|
|
return nil, err
|
|
}
|
|
return monitor, nil
|
|
}
|
|
|
|
func pollMonitorsWin32() error {
|
|
if microsoftgdk.IsXbox() {
|
|
return nil
|
|
}
|
|
|
|
disconnected := make([]*Monitor, len(_glfw.monitors))
|
|
copy(disconnected, _glfw.monitors)
|
|
|
|
adapterLoop:
|
|
for adapterIndex := uint32(0); ; adapterIndex++ {
|
|
adapter, ok := _EnumDisplayDevicesW("", adapterIndex, 0)
|
|
if !ok {
|
|
break
|
|
}
|
|
if adapter.StateFlags&_DISPLAY_DEVICE_ACTIVE == 0 {
|
|
continue
|
|
}
|
|
|
|
typ := _GLFW_INSERT_LAST
|
|
if adapter.StateFlags&_DISPLAY_DEVICE_PRIMARY_DEVICE != 0 {
|
|
typ = _GLFW_INSERT_FIRST
|
|
}
|
|
|
|
var found bool
|
|
displayLoop:
|
|
for displayIndex := uint32(0); ; displayIndex++ {
|
|
display, ok := _EnumDisplayDevicesW(windows.UTF16ToString(adapter.DeviceName[:]), displayIndex, 0)
|
|
if !ok {
|
|
break
|
|
}
|
|
found = true
|
|
if display.StateFlags&_DISPLAY_DEVICE_ACTIVE == 0 {
|
|
continue
|
|
}
|
|
|
|
for i, monitor := range disconnected {
|
|
if monitor != nil && monitor.platform.displayName == windows.UTF16ToString(display.DeviceName[:]) {
|
|
disconnected[i] = nil
|
|
err := _EnumDisplayMonitors(0, nil, monitorCallbackPtr, _LPARAM(unsafe.Pointer(_glfw.monitors[i])))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
continue displayLoop
|
|
}
|
|
}
|
|
|
|
monitor, err := createMonitor(&adapter, &display)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if monitor == nil {
|
|
return nil
|
|
}
|
|
|
|
if err := inputMonitor(monitor, Connected, typ); err != nil {
|
|
return err
|
|
}
|
|
typ = _GLFW_INSERT_LAST
|
|
}
|
|
|
|
// HACK: If an active adapter does not have any display devices
|
|
// (as sometimes happens), add it directly as a monitor
|
|
if !found {
|
|
for i, monitor := range disconnected {
|
|
if monitor != nil && monitor.platform.displayName == windows.UTF16ToString(adapter.DeviceName[:]) {
|
|
disconnected[i] = nil
|
|
continue adapterLoop
|
|
}
|
|
}
|
|
|
|
monitor, err := createMonitor(&adapter, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if monitor == nil {
|
|
return nil
|
|
}
|
|
|
|
if err := inputMonitor(monitor, Connected, typ); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, monitor := range disconnected {
|
|
if monitor != nil {
|
|
if err := inputMonitor(monitor, Disconnected, 0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Monitor) setVideoModeWin32(desired *VidMode) error {
|
|
best, err := m.chooseVideoMode(desired)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current := m.platformGetVideoMode()
|
|
if best.equals(current) {
|
|
return nil
|
|
}
|
|
|
|
dm := _DEVMODEW{
|
|
dmFields: _DM_PELSWIDTH | _DM_PELSHEIGHT | _DM_BITSPERPEL | _DM_DISPLAYFREQUENCY,
|
|
dmPelsWidth: uint32(best.Width),
|
|
dmPelsHeight: uint32(best.Height),
|
|
dmBitsPerPel: uint32(best.RedBits + best.GreenBits + best.BlueBits),
|
|
dmDisplayFrequency: uint32(best.RefreshRate),
|
|
}
|
|
dm.dmSize = uint16(unsafe.Sizeof(dm))
|
|
if dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24 {
|
|
dm.dmBitsPerPel = 32
|
|
}
|
|
switch _ChangeDisplaySettingsExW(m.platform.adapterName, &dm, 0, _CDS_FULLSCREEN, nil) {
|
|
case _DISP_CHANGE_SUCCESSFUL:
|
|
m.platform.modeChanged = true
|
|
return nil
|
|
case _DISP_CHANGE_BADDUALVIEW:
|
|
return errors.New("goglfw: the system uses DualView at Monitor.setVideoModeWin32")
|
|
case _DISP_CHANGE_BADFLAGS:
|
|
return errors.New("goglfw: invalid flags at Monitor.setVideoModeWin32")
|
|
case _DISP_CHANGE_BADMODE:
|
|
return errors.New("goglfw: graphics mode not supported at Monitor.setVideoModeWin32")
|
|
case _DISP_CHANGE_BADPARAM:
|
|
return errors.New("goglfw: invalid parameter at Monitor.setVideoModeWin32")
|
|
case _DISP_CHANGE_FAILED:
|
|
return errors.New("goglfw: graphics mode failed at Monitor.setVideoModeWin32")
|
|
case _DISP_CHANGE_NOTUPDATED:
|
|
return errors.New("goglfw: failed to write to registry at Monitor.setVideoModeWin32")
|
|
case _DISP_CHANGE_RESTART:
|
|
return errors.New("goglfw: computer restart required at Monitor.setVideoModeWin32")
|
|
default:
|
|
return errors.New("goglfw: unknown error at Monitor.setVideoModeWin32")
|
|
}
|
|
}
|
|
|
|
func (m *Monitor) restoreVideoModeWin32() {
|
|
if m.platform.modeChanged {
|
|
_ChangeDisplaySettingsExW(m.platform.adapterName, nil, 0, _CDS_FULLSCREEN, nil)
|
|
m.platform.modeChanged = false
|
|
}
|
|
}
|
|
|
|
func getMonitorContentScaleWin32(handle _HMONITOR) (xscale, yscale float32, err error) {
|
|
var xdpi, ydpi uint32
|
|
|
|
if _IsWindows8Point1OrGreater() {
|
|
var err error
|
|
xdpi, ydpi, err = _GetDpiForMonitor(handle, _MDT_EFFECTIVE_DPI)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
} else {
|
|
dc, err := _GetDC(0)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
defer _ReleaseDC(0, dc)
|
|
|
|
xdpi = uint32(_GetDeviceCaps(dc, _LOGPIXELSX))
|
|
ydpi = uint32(_GetDeviceCaps(dc, _LOGPIXELSY))
|
|
}
|
|
|
|
xscale = float32(xdpi) / _USER_DEFAULT_SCREEN_DPI
|
|
yscale = float32(ydpi) / _USER_DEFAULT_SCREEN_DPI
|
|
return
|
|
}
|
|
|
|
func (m *Monitor) platformGetMonitorPos() (xpos, ypos int, ok bool) {
|
|
if microsoftgdk.IsXbox() {
|
|
return 0, 0, true
|
|
}
|
|
|
|
dm, ok := _EnumDisplaySettingsExW(m.platform.adapterName, _ENUM_CURRENT_SETTINGS, _EDS_ROTATEDMODE)
|
|
if !ok {
|
|
return 0, 0, false
|
|
}
|
|
return int(dm.dmPosition.x), int(dm.dmPosition.y), true
|
|
}
|
|
|
|
func (m *Monitor) platformGetMonitorContentScale() (xscale, yscale float32, err error) {
|
|
if microsoftgdk.IsXbox() {
|
|
return 1, 1, nil
|
|
}
|
|
|
|
return getMonitorContentScaleWin32(m.platform.handle)
|
|
}
|
|
|
|
func (m *Monitor) platformGetMonitorWorkarea() (xpos, ypos, width, height int) {
|
|
if microsoftgdk.IsXbox() {
|
|
w, h := microsoftgdk.MonitorResolution()
|
|
return 0, 0, w, h
|
|
}
|
|
|
|
mi, ok := _GetMonitorInfoW(m.platform.handle)
|
|
if !ok {
|
|
return 0, 0, 0, 0
|
|
}
|
|
xpos = int(mi.rcWork.left)
|
|
ypos = int(mi.rcWork.top)
|
|
width = int(mi.rcWork.right - mi.rcWork.left)
|
|
height = int(mi.rcWork.bottom - mi.rcWork.top)
|
|
return
|
|
}
|
|
|
|
func (m *Monitor) platformAppendVideoModes(monitors []*VidMode) ([]*VidMode, error) {
|
|
origLen := len(monitors)
|
|
loop:
|
|
for modeIndex := uint32(0); ; modeIndex++ {
|
|
dm, ok := _EnumDisplaySettingsW(m.platform.adapterName, modeIndex)
|
|
if !ok {
|
|
break
|
|
}
|
|
|
|
// Skip modes with less than 15 BPP
|
|
if dm.dmBitsPerPel < 15 {
|
|
continue
|
|
}
|
|
|
|
r, g, b := splitBPP(int(dm.dmBitsPerPel))
|
|
mode := &VidMode{
|
|
Width: int(dm.dmPelsWidth),
|
|
Height: int(dm.dmPelsHeight),
|
|
RefreshRate: int(dm.dmDisplayFrequency),
|
|
RedBits: r,
|
|
GreenBits: g,
|
|
BlueBits: b,
|
|
}
|
|
|
|
// Skip duplicate modes
|
|
for _, m := range monitors[origLen:] {
|
|
if m.equals(mode) {
|
|
continue loop
|
|
}
|
|
}
|
|
|
|
if m.platform.modesPruned {
|
|
// Skip modes not supported by the connected displays
|
|
if _ChangeDisplaySettingsExW(m.platform.adapterName, &dm, 0, _CDS_TEST, nil) != _DISP_CHANGE_SUCCESSFUL {
|
|
continue
|
|
}
|
|
}
|
|
|
|
monitors = append(monitors, mode)
|
|
}
|
|
|
|
if len(monitors) == origLen {
|
|
// HACK: Report the current mode if no valid modes were found
|
|
monitors = append(monitors, m.platformGetVideoMode())
|
|
}
|
|
|
|
return monitors, nil
|
|
}
|
|
|
|
func (m *Monitor) platformGetVideoMode() *VidMode {
|
|
if microsoftgdk.IsXbox() {
|
|
return m.modes[0]
|
|
}
|
|
|
|
dm, _ := _EnumDisplaySettingsW(m.platform.adapterName, _ENUM_CURRENT_SETTINGS)
|
|
r, g, b := splitBPP(int(dm.dmBitsPerPel))
|
|
return &VidMode{
|
|
Width: int(dm.dmPelsWidth),
|
|
Height: int(dm.dmPelsHeight),
|
|
RefreshRate: int(dm.dmDisplayFrequency),
|
|
RedBits: r,
|
|
GreenBits: g,
|
|
BlueBits: b,
|
|
}
|
|
}
|
|
|
|
func (m *Monitor) in32Adapter() (string, error) {
|
|
if !_glfw.initialized {
|
|
return "", NotInitialized
|
|
}
|
|
return m.platform.adapterName, nil
|
|
}
|
|
|
|
func (m *Monitor) win32Monitor() (string, error) {
|
|
if !_glfw.initialized {
|
|
return "", NotInitialized
|
|
}
|
|
return m.platform.displayName, nil
|
|
}
|