5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-04 07:43:11 +08:00
wails/v3/pkg/w32/screen.go

144 lines
3.4 KiB
Go

//go:build windows
package w32
import (
"fmt"
"syscall"
"unsafe"
)
type Screen struct {
MONITORINFOEX
HMonitor uintptr
Name string
IsPrimary bool
IsCurrent bool
ScaleFactor float32
Rotation float32
}
type DISPLAY_DEVICE struct {
cb uint32
DeviceName [32]uint16
DeviceString [128]uint16
StateFlags uint32
DeviceID [128]uint16
DeviceKey [128]uint16
}
func getMonitorName(deviceName string) (string, error) {
var device DISPLAY_DEVICE
device.cb = uint32(unsafe.Sizeof(device))
i := uint32(0)
for {
res, _, _ := procEnumDisplayDevices.Call(uintptr(unsafe.Pointer(MustStringToUTF16Ptr(deviceName))), uintptr(i), uintptr(unsafe.Pointer(&device)), 0)
if res == 0 {
break
}
if device.StateFlags&0x1 != 0 {
return syscall.UTF16ToString(device.DeviceString[:]), nil
}
i++
}
return "", fmt.Errorf("monitor name not found for device: %s", deviceName)
}
// I'm not convinced this works properly
func GetRotationForMonitor(displayName [32]uint16) (float32, error) {
var devMode DEVMODE
devMode.DmSize = uint16(unsafe.Sizeof(devMode))
resp, _, _ := procEnumDisplaySettings.Call(uintptr(unsafe.Pointer(&displayName[0])), ENUM_CURRENT_SETTINGS, uintptr(unsafe.Pointer(&devMode)))
if resp == 0 {
return 0, fmt.Errorf("EnumDisplaySettings failed")
}
if (devMode.DmFields & DM_DISPLAYORIENTATION) == 0 {
return 0, fmt.Errorf("DM_DISPLAYORIENTATION not set")
}
switch devMode.DmOrientation {
case DMDO_DEFAULT:
return 0, nil
case DMDO_90:
return 90, nil
case DMDO_180:
return 180, nil
case DMDO_270:
return 270, nil
}
return -1, nil
}
func GetAllScreens() ([]*Screen, error) {
var result []*Screen
var errMessage string
// Get cursor position to determine the current monitor
var cursor POINT
ret, _, _ := procGetCursorPos.Call(uintptr(unsafe.Pointer(&cursor)))
if ret == 0 {
return nil, fmt.Errorf("GetCursorPos failed")
}
// Enumerate the monitors
enumFunc := func(hMonitor uintptr, hdc uintptr, lprcMonitor *RECT, lParam uintptr) uintptr {
monitor := MONITORINFOEX{
MONITORINFO: MONITORINFO{
CbSize: uint32(unsafe.Sizeof(MONITORINFOEX{})),
},
SzDevice: [32]uint16{},
}
ret, _, _ := procGetMonitorInfo.Call(hMonitor, uintptr(unsafe.Pointer(&monitor)))
if ret == 0 {
errMessage = "GetMonitorInfo failed"
return 0 // Stop enumeration
}
screen := &Screen{
MONITORINFOEX: monitor,
HMonitor: hMonitor,
IsPrimary: monitor.DwFlags == MONITORINFOF_PRIMARY,
IsCurrent: rectContainsPoint(monitor.RcMonitor, cursor),
}
// Get monitor name
name, err := getMonitorName(syscall.UTF16ToString(monitor.SzDevice[:]))
if err == nil {
screen.Name = name
}
// Get DPI for monitor
var dpiX, dpiY uint
ret = GetDPIForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)
if ret != S_OK {
errMessage = "GetDpiForMonitor failed"
return 0 // Stop enumeration
}
// Convert to scale factor
screen.ScaleFactor = float32(dpiX) / 96.0
// Get rotation of monitor
rot, err := GetRotationForMonitor(monitor.SzDevice)
if err == nil {
screen.Rotation = rot
}
result = append(result, screen)
return 1 // Continue enumeration
}
ret, _, _ = procEnumDisplayMonitors.Call(0, 0, syscall.NewCallback(enumFunc), 0)
if ret == 0 {
return nil, fmt.Errorf("EnumDisplayMonitors failed: %s", errMessage)
}
return result, nil
}
func rectContainsPoint(r RECT, p POINT) bool {
return p.X >= r.Left && p.X < r.Right && p.Y >= r.Top && p.Y < r.Bottom
}