Не получает WM_DPICHANGED в приложении SDL - PullRequest
1 голос
/ 17 марта 2019

Я пытаюсь написать приложение с поддержкой DPI для Windows 10 с SDL2, используя привязки PySDL2.На данный момент меня не волнует поддержка любых других операционных систем или версий Windows, более старых, чем Anniversary Update.Проблема в том, что я не получаю сообщение WM_DPICHANGED , в котором мне нужно изменить размер окна при перетаскивании между мониторами с различными коэффициентами масштабирования.

Я использовал Windows API, чтобы установить целоеобрабатывать как per-monitor-dpi -ware (v2), что, кажется, работает в основном правильно.Содержимое окна никогда не масштабируется, я могу правильно опросить текущее значение DPI дисплея, а не-клиентские области (например, строка заголовка) масштабируются Windows, как они должны.Другие родные сообщения Windows также поступают, как видно с WM_MOUSEMOVE.

Версии: Windows 10 1809, Python 3.7.2, SDL 2.0.9 все 64-битные, PySDL2 0.9.6, если вы хотите запустить этолокально вам нужно поместить SDL2.dll в подкаталог «/ native» вашего рабочего каталога или соответствующим образом изменить строку 3

import sys
import os
os.environ["PYSDL2_DLL_PATH"] = os.path.abspath(os.getcwd() + "/native")
import sdl2.ext
import ctypes

# https://docs.microsoft.com/de-de/windows/desktop/hidpi/wm-dpichanged
WM_DPICHANGED = 0x02E0
WM_MOUSEMOVE = 0x0200
# https://docs.microsoft.com/de-de/windows/desktop/hidpi/dpi-awareness-context
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4


def run():
    # Set the DPI Awareness Context to DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (-4), which means we need to do all
    # DPI scaling by ourselves, EXCEPT for win32 popups and non client areas (like the title bar)
    # This function was introduced with the Windows 10 Anniversary Update, so we should probably have fallbacks for
    # other operating system versions
    # https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiawarenesscontext
    if not ctypes.windll.user32.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2):
        print("Unable to set DPI awareness")
    sdl2.ext.init()
    # We want to receive native WM_DPICHANGED messages, so we need to tell SDL to pass them through
    sdl2.SDL_EventState(sdl2.SDL_SYSWMEVENT, sdl2.SDL_ENABLE)
    window_width = 640
    window_height = 480
    window = sdl2.ext.Window("λ³", (window_width, window_height), flags=sdl2.SDL_WINDOW_OPENGL | sdl2.SDL_WINDOW_ALLOW_HIGHDPI)
    sdl2_window = window.window
    sdl2.SDL_SetWindowResizable(sdl2_window, True)

    window.show()

    running = True
    scaling_percentage = 100

    while running:
        sdl2_events = sdl2.ext.get_events()
        for sdl2_event in sdl2_events:
            if sdl2_event.type == sdl2.SDL_QUIT:
                running = False
                break
            elif sdl2_event.type == sdl2.SDL_SYSWMEVENT:
                message_pointer = sdl2_event.syswm.msg
                # Workaround because the default pointer type that is returned has no definition for the structure
                message_pointer_usable = ctypes.cast(message_pointer, ctypes.POINTER(sdl2.syswm.SDL_SysWMmsg))
                sys_wm_msg = message_pointer_usable.contents
                windows_msg = message_pointer_usable.contents.msg.win

                # On 32 bit Windows both W and L are 32 bit, on 64 bit Windows both are 64 bit
                # When values are stored in the 'high' and 'low' 'word' of W or L, we still mean the first 16 bit or
                # 16 bit after that, regardless of the platform
                l_high = (windows_msg.lParam & 0xFFFF0000) >> 16
                l_low = windows_msg.lParam & 0xFFFF
                w_high = (windows_msg.wParam & 0xFFFF0000) >> 16
                w_low = windows_msg.wParam & 0xFFFF

                if windows_msg.msg == WM_DPICHANGED:
                    print("This never gets called")
                if windows_msg.msg == WM_MOUSEMOVE:
                    # This works
                    print(f"x {l_low}, y {l_high}")

        display_index = sdl2.SDL_GetWindowDisplayIndex(sdl2_window)
        diagonal_dpi = ctypes.c_float()
        horizontal_dpi = ctypes.c_float()
        vertical_dpi = ctypes.c_float()

        # This also works and reports the percentage correctly when dragging between monitors
        sdl2.SDL_GetDisplayDPI(display_index, ctypes.byref(diagonal_dpi), ctypes.byref(horizontal_dpi), ctypes.byref(vertical_dpi))
        new_scaling_percentage = int(diagonal_dpi.value/96*100)
        if new_scaling_percentage != scaling_percentage:
            scaling_percentage = new_scaling_percentage
            print(f"Display scale changed to {scaling_percentage}%")

        sdl2.SDL_Delay(10)

    sdl2.ext.quit()
    return 0


if __name__ == "__main__":
    sys.exit(run())
...