From 182ac21866ffe1eb9cc644c03b02bee739704ace Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 31 Mar 2023 02:07:35 +0900 Subject: [PATCH] internal/goglfw: separate the Windows version detection to a new package winver --- internal/goglfw/api_windows.go | 63 ------------ internal/goglfw/wglcontext_windows.go | 9 +- internal/goglfw/win32init_windows.go | 42 +------- internal/goglfw/win32monitor_windows.go | 3 +- internal/goglfw/win32platform_windows.go | 32 +------ internal/goglfw/win32window_windows.go | 45 ++++----- .../graphicsdriver/directx/api_windows.go | 70 -------------- .../directx/graphics_windows.go | 3 +- internal/winver/api_windows.go | 95 +++++++++++++++++++ internal/winver/winver_windows.go | 87 +++++++++++++++++ 10 files changed, 221 insertions(+), 228 deletions(-) create mode 100644 internal/winver/api_windows.go create mode 100644 internal/winver/winver_windows.go diff --git a/internal/goglfw/api_windows.go b/internal/goglfw/api_windows.go index d0af82142..b26ef29f5 100644 --- a/internal/goglfw/api_windows.go +++ b/internal/goglfw/api_windows.go @@ -164,11 +164,6 @@ const ( _TME_LEAVE = 0x00000002 _UNICODE_NOCHAR = 0xffff _USER_DEFAULT_SCREEN_DPI = 96 - _VER_BUILDNUMBER = 0x00000004 - _VER_GREATER_EQUAL = 3 - _VER_MAJORVERSION = 0x00000002 - _VER_MINORVERSION = 0x00000001 - _VER_SERVICEPACKMAJOR = 0x00000020 _VERTSIZE = 6 _VK_ADD = 0x6B _VK_CAPITAL = 0x14 @@ -262,11 +257,6 @@ const ( _WGL_TRANSPARENT_INDEX_VALUE_ARB = 0x203B _WGL_TRANSPARENT_RED_VALUE_ARB = 0x2037 _WGL_TYPE_RGBA_ARB = 0x202B - _WIN32_WINNT_VISTA = 0x0600 - _WIN32_WINNT_WIN7 = 0x0601 - _WIN32_WINNT_WIN8 = 0x0602 - _WIN32_WINNT_WINBLUE = 0x0603 - _WIN32_WINNT_WINXP = 0x0501 _WM_CAPTURECHANGED = 0x0215 _WM_CHAR = 0x0102 _WM_CLOSE = 0x0010 @@ -387,18 +377,11 @@ func _GET_XBUTTON_WPARAM(wParam _WPARAM) uint16 { func _GET_Y_LPARAM(lp _LPARAM) int { return int(_HIWORD(uint32(lp))) } -func _HIBYTE(wValue uint16) byte { - return byte(wValue >> 8) -} func _HIWORD(dwValue uint32) uint16 { return uint16(dwValue >> 16) } -func _LOBYTE(wValue uint16) byte { - return byte(wValue) -} - func _LOWORD(dwValue uint32) uint16 { return uint16(dwValue) } @@ -590,20 +573,6 @@ type _MSG struct { lPrivate uint32 } -type _OSVERSIONINFOEXW struct { - dwOSVersionInfoSize uint32 - dwMajorVersion uint32 - dwMinorVersion uint32 - dwBuildNumber uint32 - dwPlatformId uint32 - szCSDVersion [128]uint16 - wServicePackMajor uint16 - wServicePackMinor uint16 - wSuiteMask uint16 - wProductType byte - wReserved byte -} - type _PIXELFORMATDESCRIPTOR struct { nSize uint16 nVersion uint16 @@ -722,7 +691,6 @@ var ( dwmapi = windows.NewLazySystemDLL("dwmapi.dll") gdi32 = windows.NewLazySystemDLL("gdi32.dll") kernel32 = windows.NewLazySystemDLL("kernel32.dll") - ntdll = windows.NewLazySystemDLL("ntdll.dll") opengl32 = windows.NewLazySystemDLL("opengl32.dll") shcore = windows.NewLazySystemDLL("shcore.dll") shell32 = windows.NewLazySystemDLL("shell32.dll") @@ -749,9 +717,6 @@ var ( procTlsFree = kernel32.NewProc("TlsFree") procTlsGetValue = kernel32.NewProc("TlsGetValue") procTlsSetValue = kernel32.NewProc("TlsSetValue") - procVerSetConditionMask = kernel32.NewProc("VerSetConditionMask") - - procRtlVerifyVersionInfo = ntdll.NewProc("RtlVerifyVersionInfo") procWGLCreateContext = opengl32.NewProc("wglCreateContext") procWGLDeleteContext = opengl32.NewProc("wglDeleteContext") @@ -1522,24 +1487,6 @@ func _ReleaseDC(hWnd windows.HWND, hDC _HDC) int32 { return int32(r) } -func _RtlVerifyVersionInfo(versionInfo *_OSVERSIONINFOEXW, typeMask uint32, conditionMask uint64) int32 { - var r uintptr - if unsafe.Sizeof(uintptr(0)) == unsafe.Sizeof(uint64(0)) { - r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), uintptr(conditionMask)) - } else { - switch runtime.GOARCH { - case "386": - r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), uintptr(conditionMask), uintptr(conditionMask>>32)) - case "arm": - // Adjust the alignment for ARM. - r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), 0, uintptr(conditionMask), uintptr(conditionMask>>32)) - default: - panic(fmt.Sprintf("goglfw: GOARCH=%s is not supported", runtime.GOARCH)) - } - } - return int32(r) -} - func _ScreenToClient(hWnd windows.HWND, lpPoint *_POINT) error { r, _, e := procScreenToClient.Call(uintptr(hWnd), uintptr(unsafe.Pointer(lpPoint))) if int32(r) == 0 && !errors.Is(e, windows.ERROR_SUCCESS) { @@ -1777,16 +1724,6 @@ func _UnregisterDeviceNotification(handle _HDEVNOTIFY) error { return nil } -func _VerSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 { - if unsafe.Sizeof(uintptr(0)) == unsafe.Sizeof(uint64(0)) { - r, _, _ := procVerSetConditionMask.Call(uintptr(conditionMask), uintptr(typeMask), uintptr(condition)) - return uint64(r) - } else { - r1, r2, _ := procVerSetConditionMask.Call(uintptr(conditionMask), uintptr(conditionMask>>32), uintptr(typeMask), uintptr(condition)) - return uint64(r1) | (uint64(r2) << 32) - } -} - func _WaitMessage() error { r, _, e := procWaitMessage.Call() if int32(r) == 0 && !errors.Is(e, windows.ERROR_SUCCESS) { diff --git a/internal/goglfw/wglcontext_windows.go b/internal/goglfw/wglcontext_windows.go index 823e83173..4d913be05 100644 --- a/internal/goglfw/wglcontext_windows.go +++ b/internal/goglfw/wglcontext_windows.go @@ -14,6 +14,7 @@ import ( "golang.org/x/sys/windows" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" + "github.com/hajimehoshi/ebiten/v2/internal/winver" ) func findPixelFormatAttribValue(attribs []int32, values []int32, attrib int32) int32 { @@ -230,9 +231,9 @@ func makeContextCurrentWGL(window *Window) error { } func swapBuffersWGL(window *Window) error { - if window.monitor == nil && _IsWindowsVistaOrGreater() { + if window.monitor == nil && winver.IsWindowsVistaOrGreater() { // DWM Composition is always enabled on Win8+ - enabled := _IsWindows8OrGreater() + enabled := winver.IsWindows8OrGreater() if !enabled { var err error @@ -266,9 +267,9 @@ func swapIntervalWGL(interval int) error { window.context.platform.interval = interval - if window.monitor == nil && _IsWindowsVistaOrGreater() { + if window.monitor == nil && winver.IsWindowsVistaOrGreater() { // DWM Composition is always enabled on Win8+ - enabled := _IsWindows8OrGreater() + enabled := winver.IsWindows8OrGreater() if !enabled { e, err := _DwmIsCompositionEnabled() diff --git a/internal/goglfw/win32init_windows.go b/internal/goglfw/win32init_windows.go index 7abece14f..b7b5a0cb4 100644 --- a/internal/goglfw/win32init_windows.go +++ b/internal/goglfw/win32init_windows.go @@ -12,6 +12,7 @@ import ( "golang.org/x/sys/windows" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" + "github.com/hajimehoshi/ebiten/v2/internal/winver" ) func createKeyTables() { @@ -238,41 +239,6 @@ func createHelperWindow() error { return nil } -func isWindowsVersionOrGreaterWin32(major, minor, sp uint16) bool { - osvi := _OSVERSIONINFOEXW{ - dwMajorVersion: uint32(major), - dwMinorVersion: uint32(minor), - wServicePackMajor: sp, - } - osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi)) - var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_SERVICEPACKMAJOR - cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL) - cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL) - cond = _VerSetConditionMask(cond, _VER_SERVICEPACKMAJOR, _VER_GREATER_EQUAL) - // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the - // latter lies unless the user knew to embed a non-default manifest - // announcing support for Windows 10 via supportedOS GUID - return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0 -} - -func isWindows10BuildOrGreaterWin32(build uint16) bool { - osvi := _OSVERSIONINFOEXW{ - dwMajorVersion: 10, - dwMinorVersion: 0, - dwBuildNumber: uint32(build), - } - osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi)) - var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER - cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL) - cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL) - cond = _VerSetConditionMask(cond, _VER_BUILDNUMBER, _VER_GREATER_EQUAL) - - // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the - // latter lies unless the user knew to embed a non-default manifest - // announcing support for Windows 10 via supportedOS GUID - return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0 -} - func platformInit() error { // Changing the foreground lock timeout was removed from the original code. // See https://github.com/glfw/glfw/commit/58b48a3a00d9c2a5ca10cc23069a71d8773cc7a4 @@ -286,17 +252,17 @@ func platformInit() error { createKeyTables() updateKeyNamesWin32() - if isWindows10CreatorsUpdateOrGreaterWin32() { + if winver.IsWindows10CreatorsUpdateOrGreater() { if !microsoftgdk.IsXbox() { // Ignore the error as SetProcessDpiAwarenessContext returns an error on Steam Deck (#2113). // This seems an issue in Wine and/or Proton, but there is nothing we can do. _ = _SetProcessDpiAwarenessContext(_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) } - } else if _IsWindows8Point1OrGreater() { + } else if winver.IsWindows8Point1OrGreater() { if err := _SetProcessDpiAwareness(_PROCESS_PER_MONITOR_DPI_AWARE); err != nil && !errors.Is(err, handleError(windows.E_ACCESSDENIED)) { return err } - } else if _IsWindowsVistaOrGreater() { + } else if winver.IsWindowsVistaOrGreater() { _SetProcessDPIAware() } diff --git a/internal/goglfw/win32monitor_windows.go b/internal/goglfw/win32monitor_windows.go index af8631a72..7650ff743 100644 --- a/internal/goglfw/win32monitor_windows.go +++ b/internal/goglfw/win32monitor_windows.go @@ -12,6 +12,7 @@ import ( "golang.org/x/sys/windows" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" + "github.com/hajimehoshi/ebiten/v2/internal/winver" ) func monitorCallback(handle _HMONITOR, dc _HDC, rect *_RECT, monitor *Monitor /* _LPARAM */) uintptr /* _BOOL */ { @@ -217,7 +218,7 @@ func (m *Monitor) restoreVideoModeWin32() { func getMonitorContentScaleWin32(handle _HMONITOR) (xscale, yscale float32, err error) { var xdpi, ydpi uint32 - if _IsWindows8Point1OrGreater() { + if winver.IsWindows8Point1OrGreater() { var err error xdpi, ydpi, err = _GetDpiForMonitor(handle, _MDT_EFFECTIVE_DPI) if err != nil { diff --git a/internal/goglfw/win32platform_windows.go b/internal/goglfw/win32platform_windows.go index 37150924b..3aeb763ab 100644 --- a/internal/goglfw/win32platform_windows.go +++ b/internal/goglfw/win32platform_windows.go @@ -5,7 +5,9 @@ package goglfw -import "golang.org/x/sys/windows" +import ( + "golang.org/x/sys/windows" +) const ( _GLFW_WNDCLASSNAME = "GLFW30" @@ -96,31 +98,3 @@ type platformLibraryContextState struct { ARB_create_context_no_error bool ARB_context_flush_control bool } - -func _IsWindowsXPOrGreater() bool { - return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WINXP)), uint16(_LOBYTE(_WIN32_WINNT_WINXP)), 0) -} - -func _IsWindowsVistaOrGreater() bool { - return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_VISTA)), uint16(_LOBYTE(_WIN32_WINNT_VISTA)), 0) -} - -func _IsWindows7OrGreater() bool { - return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WIN7)), uint16(_LOBYTE(_WIN32_WINNT_WIN7)), 0) -} - -func _IsWindows8OrGreater() bool { - return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WIN8)), uint16(_LOBYTE(_WIN32_WINNT_WIN8)), 0) -} - -func _IsWindows8Point1OrGreater() bool { - return isWindowsVersionOrGreaterWin32(uint16(_HIBYTE(_WIN32_WINNT_WINBLUE)), uint16(_LOBYTE(_WIN32_WINNT_WINBLUE)), 0) -} - -func isWindows10AnniversaryUpdateOrGreaterWin32() bool { - return isWindows10BuildOrGreaterWin32(14393) -} - -func isWindows10CreatorsUpdateOrGreaterWin32() bool { - return isWindows10BuildOrGreaterWin32(15063) -} diff --git a/internal/goglfw/win32window_windows.go b/internal/goglfw/win32window_windows.go index 150521389..28fc47228 100644 --- a/internal/goglfw/win32window_windows.go +++ b/internal/goglfw/win32window_windows.go @@ -15,6 +15,7 @@ import ( "golang.org/x/sys/windows" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" + "github.com/hajimehoshi/ebiten/v2/internal/winver" ) func (w *Window) getWindowStyle() uint32 { @@ -137,7 +138,7 @@ func getFullWindowSize(style uint32, exStyle uint32, contentWidth, contentHeight right: int32(contentWidth), bottom: int32(contentHeight), } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, style, false, exStyle, dpi); err != nil { return 0, 0, err } @@ -153,7 +154,7 @@ func (w *Window) applyAspectRatio(edge int, area *_RECT) error { ratio := float32(w.numer) / float32(w.denom) var dpi uint32 = _USER_DEFAULT_SCREEN_DPI - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { dpi = _GetDpiForWindow(w.platform.handle) } @@ -343,7 +344,7 @@ func (w *Window) updateWindowStyles() error { return err } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, style, false, w.getWindowExStyle(), _GetDpiForWindow(w.platform.handle)); err != nil { return err } @@ -368,7 +369,7 @@ func (w *Window) updateWindowStyles() error { } func (w *Window) updateFramebufferTransparency() error { - if !_IsWindowsVistaOrGreater() { + if !winver.IsWindowsVistaOrGreater() { return nil } @@ -382,7 +383,7 @@ func (w *Window) updateFramebufferTransparency() error { } var opaque bool - if !_IsWindows8OrGreater() { + if !winver.IsWindows8OrGreater() { _, opaque, err = _DwmGetColorizationColor() if err != nil { // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). @@ -390,7 +391,7 @@ func (w *Window) updateFramebufferTransparency() error { } } - if _IsWindows8OrGreater() || !opaque { + if winver.IsWindows8OrGreater() || !opaque { region, err := _CreateRectRgn(0, 0, -1, -1) if err != nil { return err @@ -473,7 +474,7 @@ func (w *Window) acquireMonitor() error { // HACK: When mouse trails are enabled the cursor becomes invisible when // the OpenGL ICD switches to page flipping - if _IsWindowsXPOrGreater() { + if winver.IsWindowsXPOrGreater() { if err := _SystemParametersInfoW(_SPI_GETMOUSETRAILS, 0, uintptr(unsafe.Pointer(&_glfw.platformWindow.mouseTrailSize)), 0); err != nil { return err } @@ -504,7 +505,7 @@ func (w *Window) releaseMonitor() error { _SetThreadExecutionState(_ES_CONTINUOUS) // HACK: Restore mouse trail length saved in acquireMonitor - if _IsWindowsXPOrGreater() { + if winver.IsWindowsXPOrGreater() { if err := _SystemParametersInfoW(_SPI_SETMOUSETRAILS, _glfw.platformWindow.mouseTrailSize, 0, 0); err != nil { return err } @@ -546,7 +547,7 @@ func (w *Window) maximizeWindowManually() error { return err } exStyle := uint32(s) - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { dpi := _GetDpiForWindow(w.platform.handle) if err := _AdjustWindowRectExForDpi(&rect, style, false, exStyle, dpi); err != nil { return err @@ -588,7 +589,7 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) // and for a regular window during its initial creation switch uMsg { case _WM_NCCREATE: - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { cs := (*_CREATESTRUCTW)(unsafe.Pointer(lParam)) wndconfig := (*wndconfig)(cs.lpCreateParams) @@ -1062,7 +1063,7 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) break } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { dpi = _GetDpiForWindow(window.platform.handle) } @@ -1122,7 +1123,7 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) } // Adjust the window size to keep the content area size constant - if isWindows10CreatorsUpdateOrGreaterWin32() { + if winver.IsWindows10CreatorsUpdateOrGreater() { var source, target _RECT size := (*_SIZE)(unsafe.Pointer(lParam)) @@ -1146,7 +1147,7 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) // Resize windowed mode windows that either permit rescaling or that // need it to compensate for non-client area scaling - if window.monitor == nil && (window.platform.scaleToMonitor || isWindows10CreatorsUpdateOrGreaterWin32()) { + if window.monitor == nil && (window.platform.scaleToMonitor || winver.IsWindows10CreatorsUpdateOrGreater()) { suggested := (*_RECT)(unsafe.Pointer(lParam)) if err := _SetWindowPos(window.platform.handle, _HWND_TOP, suggested.left, @@ -1244,7 +1245,7 @@ func (w *Window) createNativeWindow(wndconfig *wndconfig, fbconfig *fbconfig) er handleToWindow[w.platform.handle] = w - if !microsoftgdk.IsXbox() && _IsWindows7OrGreater() { + if !microsoftgdk.IsXbox() && winver.IsWindows7OrGreater() { if err := _ChangeWindowMessageFilterEx(w.platform.handle, _WM_DROPFILES, _MSGFLT_ALLOW, nil); err != nil { return err } @@ -1291,7 +1292,7 @@ func (w *Window) createNativeWindow(wndconfig *wndconfig, fbconfig *fbconfig) er return err } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, style, false, exStyle, _GetDpiForWindow(w.platform.handle)); err != nil { return err } @@ -1577,7 +1578,7 @@ func (w *Window) platformSetWindowPos(xpos, ypos int) error { right: int32(xpos), bottom: int32(ypos), } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, w.getWindowStyle(), false, w.getWindowExStyle(), _GetDpiForWindow(w.platform.handle)); err != nil { return err } @@ -1619,7 +1620,7 @@ func (w *Window) platformSetWindowSize(width, height int) error { bottom: int32(height), } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, w.getWindowStyle(), false, w.getWindowExStyle(), _GetDpiForWindow(w.platform.handle)); err != nil { return err } @@ -1688,7 +1689,7 @@ func (w *Window) platformGetWindowFrameSize() (left, top, right, bottom int, err right: int32(width), bottom: int32(height), } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, w.getWindowStyle(), false, w.getWindowExStyle(), _GetDpiForWindow(w.platform.handle)); err != nil { return 0, 0, 0, 0, err } @@ -1770,7 +1771,7 @@ func (w *Window) platformSetWindowMonitor(monitor *Monitor, xpos, ypos, width, h right: int32(xpos + width), bottom: int32(ypos + height), } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, w.getWindowStyle(), false, w.getWindowExStyle(), _GetDpiForWindow(w.platform.handle)); err != nil { return err } @@ -1853,7 +1854,7 @@ func (w *Window) platformSetWindowMonitor(monitor *Monitor, xpos, ypos, width, h right: int32(xpos + width), bottom: int32(ypos + height), } - if isWindows10AnniversaryUpdateOrGreaterWin32() { + if winver.IsWindows10AnniversaryUpdateOrGreater() { if err := _AdjustWindowRectExForDpi(&rect, w.getWindowStyle(), false, w.getWindowExStyle(), _GetDpiForWindow(w.platform.handle)); err != nil { @@ -1926,7 +1927,7 @@ func (w *Window) platformFramebufferTransparent() bool { return false } - if !_IsWindowsVistaOrGreater() { + if !winver.IsWindowsVistaOrGreater() { return false } @@ -1935,7 +1936,7 @@ func (w *Window) platformFramebufferTransparent() bool { return false } - if !_IsWindows8OrGreater() { + if !winver.IsWindows8OrGreater() { // HACK: Disable framebuffer transparency on Windows 7 when the // colorization color is opaque, because otherwise the window // contents is blended additively with the previous frame instead diff --git a/internal/graphicsdriver/directx/api_windows.go b/internal/graphicsdriver/directx/api_windows.go index aa2c4b519..32a9a5a14 100644 --- a/internal/graphicsdriver/directx/api_windows.go +++ b/internal/graphicsdriver/directx/api_windows.go @@ -16,7 +16,6 @@ package directx import ( "fmt" - "runtime" "unsafe" "golang.org/x/sys/windows" @@ -24,13 +23,6 @@ import ( const is64bit = unsafe.Sizeof(uintptr(0)) == 8 -const ( - _VER_BUILDNUMBER = 0x00000004 - _VER_GREATER_EQUAL = 3 - _VER_MAJORVERSION = 0x00000002 - _VER_MINORVERSION = 0x00000001 -) - type handleError windows.Handle func (h handleError) Error() string { @@ -48,33 +40,13 @@ func boolToUintptr(v bool) uintptr { return 0 } -type _OSVERSIONINFOEXW struct { - dwOSVersionInfoSize uint32 - dwMajorVersion uint32 - dwMinorVersion uint32 - dwBuildNumber uint32 - dwPlatformId uint32 - szCSDVersion [128]uint16 - wServicePackMajor uint16 - wServicePackMinor uint16 - wSuiteMask uint16 - wProductType byte - wReserved byte -} - type _PAPPSTATE_CHANGE_ROUTINE func(quiesced bool, context unsafe.Pointer) uintptr var ( // https://github.com/MicrosoftDocs/sdk-api/blob/docs/sdk-api-src/content/appnotify/nf-appnotify-registerappstatechangenotification.md appnotify = windows.NewLazySystemDLL("API-MS-Win-Core-psm-appnotify-l1-1-0.dll") - kernel32 = windows.NewLazySystemDLL("kernel32.dll") - ntdll = windows.NewLazySystemDLL("ntdll.dll") procRegisterAppStateChangeNotification = appnotify.NewProc("RegisterAppStateChangeNotification") - - procVerSetConditionMask = kernel32.NewProc("VerSetConditionMask") - - procRtlVerifyVersionInfo = ntdll.NewProc("RtlVerifyVersionInfo") ) func _RegisterAppStateChangeNotification(routine _PAPPSTATE_CHANGE_ROUTINE, context unsafe.Pointer) (unsafe.Pointer, error) { @@ -86,45 +58,3 @@ func _RegisterAppStateChangeNotification(routine _PAPPSTATE_CHANGE_ROUTINE, cont } return registration, nil } - -func _RtlVerifyVersionInfo(versionInfo *_OSVERSIONINFOEXW, typeMask uint32, conditionMask uint64) int32 { - var r uintptr - if is64bit { - r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), uintptr(conditionMask)) - } else { - switch runtime.GOARCH { - case "386": - r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), uintptr(conditionMask), uintptr(conditionMask>>32)) - case "arm": - // Adjust the alignment for ARM. - r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), 0, uintptr(conditionMask), uintptr(conditionMask>>32)) - default: - panic(fmt.Sprintf("directx: GOARCH=%s is not supported", runtime.GOARCH)) - } - } - return int32(r) -} - -func _VerSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 { - if is64bit { - r, _, _ := procVerSetConditionMask.Call(uintptr(conditionMask), uintptr(typeMask), uintptr(condition)) - return uint64(r) - } else { - r1, r2, _ := procVerSetConditionMask.Call(uintptr(conditionMask), uintptr(conditionMask>>32), uintptr(typeMask), uintptr(condition)) - return uint64(r1) | (uint64(r2) << 32) - } -} - -func isWindows10OrGreaterWin32() bool { - osvi := _OSVERSIONINFOEXW{ - dwMajorVersion: 10, - dwMinorVersion: 0, - dwBuildNumber: 0, - } - osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi)) - var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER - cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL) - cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL) - cond = _VerSetConditionMask(cond, _VER_BUILDNUMBER, _VER_GREATER_EQUAL) - return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0 -} diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index eb86e1dc8..e7fd5edda 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -29,6 +29,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" + "github.com/hajimehoshi/ebiten/v2/internal/winver" ) type stencilMode int @@ -275,7 +276,7 @@ func (g *graphicsInfra) initSwapChain(width, height int, device unsafe.Pointer, // DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL/DISCARD are not supported for older Windows than 10 or DirectX 12. // https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect - if !isWindows10OrGreaterWin32() { + if !winver.IsWindows10OrGreater() { desc.SwapEffect = _DXGI_SWAP_EFFECT_SEQUENTIAL desc.BufferCount = 1 } diff --git a/internal/winver/api_windows.go b/internal/winver/api_windows.go new file mode 100644 index 000000000..3f79605a2 --- /dev/null +++ b/internal/winver/api_windows.go @@ -0,0 +1,95 @@ +// Copyright 2023 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package winver + +import ( + "fmt" + "runtime" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + _VER_BUILDNUMBER = 0x00000004 + _VER_GREATER_EQUAL = 3 + _VER_MAJORVERSION = 0x00000002 + _VER_MINORVERSION = 0x00000001 + _VER_SERVICEPACKMAJOR = 0x00000020 + _WIN32_WINNT_VISTA = 0x0600 + _WIN32_WINNT_WIN7 = 0x0601 + _WIN32_WINNT_WIN8 = 0x0602 + _WIN32_WINNT_WINBLUE = 0x0603 + _WIN32_WINNT_WINXP = 0x0501 +) + +type _OSVERSIONINFOEXW struct { + dwOSVersionInfoSize uint32 + dwMajorVersion uint32 + dwMinorVersion uint32 + dwBuildNumber uint32 + dwPlatformId uint32 + szCSDVersion [128]uint16 + wServicePackMajor uint16 + wServicePackMinor uint16 + wSuiteMask uint16 + wProductType byte + wReserved byte +} + +func _HIBYTE(wValue uint16) byte { + return byte(wValue >> 8) +} + +func _LOBYTE(wValue uint16) byte { + return byte(wValue) +} + +var ( + kernel32 = windows.NewLazySystemDLL("kernel32.dll") + ntdll = windows.NewLazySystemDLL("ntdll.dll") + + procVerSetConditionMask = kernel32.NewProc("VerSetConditionMask") + + procRtlVerifyVersionInfo = ntdll.NewProc("RtlVerifyVersionInfo") +) + +func _RtlVerifyVersionInfo(versionInfo *_OSVERSIONINFOEXW, typeMask uint32, conditionMask uint64) int32 { + var r uintptr + if unsafe.Sizeof(uintptr(0)) == unsafe.Sizeof(uint64(0)) { + r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), uintptr(conditionMask)) + } else { + switch runtime.GOARCH { + case "386": + r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), uintptr(conditionMask), uintptr(conditionMask>>32)) + case "arm": + // Adjust the alignment for ARM. + r, _, _ = procRtlVerifyVersionInfo.Call(uintptr(unsafe.Pointer(versionInfo)), uintptr(typeMask), 0, uintptr(conditionMask), uintptr(conditionMask>>32)) + default: + panic(fmt.Sprintf("goglfw: GOARCH=%s is not supported", runtime.GOARCH)) + } + } + return int32(r) +} + +func _VerSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 { + if unsafe.Sizeof(uintptr(0)) == unsafe.Sizeof(uint64(0)) { + r, _, _ := procVerSetConditionMask.Call(uintptr(conditionMask), uintptr(typeMask), uintptr(condition)) + return uint64(r) + } else { + r1, r2, _ := procVerSetConditionMask.Call(uintptr(conditionMask), uintptr(conditionMask>>32), uintptr(typeMask), uintptr(condition)) + return uint64(r1) | (uint64(r2) << 32) + } +} diff --git a/internal/winver/winver_windows.go b/internal/winver/winver_windows.go new file mode 100644 index 000000000..a9e1de281 --- /dev/null +++ b/internal/winver/winver_windows.go @@ -0,0 +1,87 @@ +// Copyright 2023 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package winver + +import ( + "unsafe" +) + +func isWindowsVersionOrGreater(major, minor, sp uint16) bool { + osvi := _OSVERSIONINFOEXW{ + dwMajorVersion: uint32(major), + dwMinorVersion: uint32(minor), + wServicePackMajor: sp, + } + osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi)) + var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_SERVICEPACKMAJOR + cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL) + cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL) + cond = _VerSetConditionMask(cond, _VER_SERVICEPACKMAJOR, _VER_GREATER_EQUAL) + + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embed a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0 +} + +func isWindows10BuildOrGreater(build uint16) bool { + osvi := _OSVERSIONINFOEXW{ + dwMajorVersion: 10, + dwMinorVersion: 0, + dwBuildNumber: uint32(build), + } + osvi.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osvi)) + var mask uint32 = _VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER + cond := _VerSetConditionMask(0, _VER_MAJORVERSION, _VER_GREATER_EQUAL) + cond = _VerSetConditionMask(cond, _VER_MINORVERSION, _VER_GREATER_EQUAL) + cond = _VerSetConditionMask(cond, _VER_BUILDNUMBER, _VER_GREATER_EQUAL) + + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embed a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return _RtlVerifyVersionInfo(&osvi, mask, cond) == 0 +} + +func IsWindowsXPOrGreater() bool { + return isWindowsVersionOrGreater(uint16(_HIBYTE(_WIN32_WINNT_WINXP)), uint16(_LOBYTE(_WIN32_WINNT_WINXP)), 0) +} + +func IsWindowsVistaOrGreater() bool { + return isWindowsVersionOrGreater(uint16(_HIBYTE(_WIN32_WINNT_VISTA)), uint16(_LOBYTE(_WIN32_WINNT_VISTA)), 0) +} + +func IsWindows7OrGreater() bool { + return isWindowsVersionOrGreater(uint16(_HIBYTE(_WIN32_WINNT_WIN7)), uint16(_LOBYTE(_WIN32_WINNT_WIN7)), 0) +} + +func IsWindows8OrGreater() bool { + return isWindowsVersionOrGreater(uint16(_HIBYTE(_WIN32_WINNT_WIN8)), uint16(_LOBYTE(_WIN32_WINNT_WIN8)), 0) +} + +func IsWindows8Point1OrGreater() bool { + return isWindowsVersionOrGreater(uint16(_HIBYTE(_WIN32_WINNT_WINBLUE)), uint16(_LOBYTE(_WIN32_WINNT_WINBLUE)), 0) +} + +func IsWindows10OrGreater() bool { + return isWindows10BuildOrGreater(0) +} + +func IsWindows10AnniversaryUpdateOrGreater() bool { + return isWindows10BuildOrGreater(14393) +} + +func IsWindows10CreatorsUpdateOrGreater() bool { + return isWindows10BuildOrGreater(15063) +}