mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-04 07:43:11 +08:00
144 lines
3.4 KiB
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
|
|
}
|