diff --git a/app/internal/window/os_js.go b/app/internal/window/os_js.go index 588308fe..66772f58 100644 --- a/app/internal/window/os_js.go +++ b/app/internal/window/os_js.go @@ -370,7 +370,7 @@ func (w *window) config() (int, int, float32, config) { w.cnv.Set("width", iw) w.cnv.Set("height", ih) } - const ppdp = 96 * inchPrDp * monitorScale + const ppdp = 96 * inchPrDp return iw, ih, float32(scale), config{ pxPerDp: ppdp * float32(scale), pxPerSp: ppdp * float32(scale), diff --git a/app/internal/window/os_macos.go b/app/internal/window/os_macos.go index 71c39ca7..ed4edb2c 100644 --- a/app/internal/window/os_macos.go +++ b/app/internal/window/os_macos.go @@ -36,7 +36,6 @@ type window struct { view C.CFTypeRef w Callbacks stage system.Stage - ppdp float32 scale float32 } @@ -198,7 +197,7 @@ func (w *window) draw(sync bool) { } width := int(wf*w.scale + .5) height := int(hf*w.scale + .5) - cfg := configFor(w.ppdp, w.scale) + cfg := configFor(w.scale) cfg.now = time.Now() w.setStage(system.StageRunning) w.w.Event(FrameEvent{ @@ -213,20 +212,10 @@ func (w *window) draw(sync bool) { }) } -func getPixelsPerDp(scale float32) float32 { - ppdp := float32(C.gio_getPixelsPerDP()) - ppdp = ppdp * scale * monitorScale - if ppdp < minDensity { - ppdp = minDensity - } - return ppdp / scale -} - -func configFor(ppdp, scale float32) config { - ppdp = ppdp * scale +func configFor(scale float32) config { return config{ - pxPerDp: ppdp, - pxPerSp: ppdp, + pxPerDp: scale, + pxPerSp: scale, } } @@ -258,10 +247,9 @@ func gio_onShow(view C.CFTypeRef) { //export gio_onCreate func gio_onCreate(view C.CFTypeRef) { viewDo(view, func(views viewMap, view C.CFTypeRef) { - scale := float32(C.gio_getBackingScale()) + scale := float32(C.gio_getViewBackingScale(view)) w := &window{ view: view, - ppdp: getPixelsPerDp(scale), scale: scale, } wopts := <-mainWindow.out @@ -283,15 +271,13 @@ func Main() { // TODO: return this error from CreateWindow. panic(errors.New("CreateWindow: failed to create view")) } - scale := float32(C.gio_getBackingScale()) - ppdp := getPixelsPerDp(scale) - cfg := configFor(ppdp, scale) + // Window sizes is in unscaled screen coordinates, not device pixels. + cfg := configFor(1.0) opts := wopts.opts w := cfg.Px(opts.Width) h := cfg.Px(opts.Height) - // Window sizes is on screen coordinates, not device pixels. - w = int(float32(w) / scale) - h = int(float32(h) / scale) + w = int(float32(w)) + h = int(float32(h)) title := C.CString(opts.Title) defer C.free(unsafe.Pointer(title)) C.gio_main(view, title, C.CGFloat(w), C.CGFloat(h)) diff --git a/app/internal/window/os_macos.h b/app/internal/window/os_macos.h index 0e17d34b..201b6d04 100644 --- a/app/internal/window/os_macos.h +++ b/app/internal/window/os_macos.h @@ -12,8 +12,6 @@ __attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef) __attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef); __attribute__ ((visibility ("hidden"))) void gio_setAnimating(CFTypeRef viewRef, BOOL anim); __attribute__ ((visibility ("hidden"))) void gio_updateDisplayLink(CFTypeRef viewRef, CGDirectDisplayID dispID); -__attribute__ ((visibility ("hidden"))) CGFloat gio_getPixelsPerDP(void); -__attribute__ ((visibility ("hidden"))) CGFloat gio_getBackingScale(void); __attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef); #endif diff --git a/app/internal/window/os_macos.m b/app/internal/window/os_macos.m index 8ebfd8f3..35d98c52 100644 --- a/app/internal/window/os_macos.m +++ b/app/internal/window/os_macos.m @@ -57,25 +57,6 @@ CGFloat gio_viewWidth(CFTypeRef viewRef) { return [view bounds].size.width; } -// Points pr. dp. -static CGFloat getPointsPerDP(NSScreen *screen) { - NSDictionary *description = [screen deviceDescription]; - NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - return (25.4/160)*displayPixelSize.width / displayPhysicalSize.width; -} - -// Pixels pr dp. -CGFloat gio_getPixelsPerDP(void) { - NSScreen *screen = [NSScreen mainScreen]; - return getPointsPerDP(screen); -} - -CGFloat gio_getBackingScale() { - NSScreen *screen = [NSScreen mainScreen]; - return [screen backingScaleFactor]; -} - CGFloat gio_getViewBackingScale(CFTypeRef viewRef) { NSView *view = (__bridge NSView *)viewRef; return [view.window backingScaleFactor]; diff --git a/app/internal/window/os_wayland.go b/app/internal/window/os_wayland.go index 2b3e79ad..ed362464 100644 --- a/app/internal/window/os_wayland.go +++ b/app/internal/window/os_wayland.go @@ -187,21 +187,13 @@ func createNativeWindow(opts *Options) (*window, error) { return nil, fmt.Errorf("createNativeWindow: failed to create pipe: %v", err) } - fontScale := detectFontScale() - var ppmm float32 var scale int for _, conf := range outputConfig { - if d, err := conf.ppmm(); err == nil && d > ppmm { - ppmm = d - } if s := conf.scale; s > scale { scale = s } } - ppdp := ppmm * mmPrDp * fontScale - if ppdp < minDensity { - ppdp = minDensity - } + ppdp := detectUIScale() w := &window{ disp: conn.disp, @@ -893,17 +885,6 @@ func gio_onTextInputDeleteSurroundingText(data unsafe.Pointer, im *C.struct_zwp_ func gio_onTextInputDone(data unsafe.Pointer, im *C.struct_zwp_text_input_v3, serial C.uint32_t) { } -// ppmm returns the approximate pixels per millimeter for the output. -func (c *wlOutput) ppmm() (float32, error) { - if c.physWidth == 0 || c.physHeight == 0 { - return 0, errors.New("no physical size data for output") - } - // Because of https://gitlab.gnome.org/GNOME/mutter/issues/369, output dimensions might be undetectably swapped. - // Instead, compute and return sqrt(px²/mm²). - density := float32(math.Sqrt(float64(c.width*c.height) / float64(c.physWidth*c.physHeight))) - return density, nil -} - func (w *window) flushScroll() { var fling f32.Point if w.fling.anim.Active() { @@ -911,7 +892,7 @@ func (w *window) flushScroll() { fling = w.fling.dir.Mul(dist) } // The Wayland reported scroll distance for - // discrete scroll axis is only 10 pixels, where + // discrete scroll axes is only 10 pixels, where // 100 seems more appropriate. const discreteScale = 10 if w.scroll.steps.X != 0 { @@ -1066,17 +1047,16 @@ func (w *window) surface() (*C.struct_wl_surface, int, int) { func (w *window) ShowTextInput(show bool) {} -// detectFontScale reports the system font scale, or monitorScale -// if it fails. -func detectFontScale() float32 { +// detectUIScale reports the system UI scale, or 1.0 if it fails. +func detectUIScale() float32 { // TODO: What about other window environments? out, err := exec.Command("gsettings", "get", "org.gnome.desktop.interface", "text-scaling-factor").Output() if err != nil { - return monitorScale + return 1.0 } scale, err := strconv.ParseFloat(string(bytes.TrimSpace(out)), 32) if err != nil { - return monitorScale + return 1.0 } return float32(scale) } diff --git a/app/internal/window/os_windows.go b/app/internal/window/os_windows.go index b9fcaf89..f594a6c4 100644 --- a/app/internal/window/os_windows.go +++ b/app/internal/window/os_windows.go @@ -79,7 +79,9 @@ const ( _INFINITE = 0xFFFFFFFF - _LOGPIXELSX = 88 + _MDT_EFFECTIVE_DPI = 0 + + _MONITOR_DEFAULTTOPRIMARY = 1 _SIZE_MAXIMIZED = 2 _SIZE_MINIMIZED = 1 @@ -110,6 +112,7 @@ const ( _WM_CANCELMODE = 0x001F _WM_CHAR = 0x0102 _WM_CREATE = 0x0001 + _WM_DPICHANGED = 0x02E0 _WM_DESTROY = 0x0002 _WM_KEYDOWN = 0x0100 _WM_KEYUP = 0x0101 @@ -270,6 +273,9 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr } // The message is processed. return 1 + case _WM_DPICHANGED: + // Let Windows know we're prepared for runtime DPI changes. + return 1 case _WM_KEYDOWN, _WM_SYSKEYDOWN: if n, ok := convertKeyCode(wParam); ok { cmd := key.Event{Name: n} @@ -478,12 +484,9 @@ func convertKeyCode(code uintptr) (rune, bool) { } func configForDC(hdc syscall.Handle) config { - dpi := getDeviceCaps(hdc, _LOGPIXELSX) - ppdp := float32(dpi) * inchPrDp * monitorScale - // Force a minimum density to keep text legible and to handle bogus output geometry. - if ppdp < minDensity { - ppdp = minDensity - } + hmon := monitorFromPoint(point{}, _MONITOR_DEFAULTTOPRIMARY) + dpi := getDpiForMonitor(hmon, _MDT_EFFECTIVE_DPI) + ppdp := float32(dpi) * inchPrDp return config{ pxPerDp: ppdp, pxPerSp: ppdp, @@ -508,6 +511,7 @@ var ( _GetMessageTime = user32.NewProc("GetMessageTime") _KillTimer = user32.NewProc("KillTimer") _LoadCursor = user32.NewProc("LoadCursorW") + _MonitorFromPoint = user32.NewProc("MonitorFromPoint") _MsgWaitForMultipleObjectsEx = user32.NewProc("MsgWaitForMultipleObjectsEx") _PeekMessage = user32.NewProc("PeekMessageW") _PostMessage = user32.NewProc("PostMessageW") @@ -526,8 +530,8 @@ var ( _UnregisterClass = user32.NewProc("UnregisterClassW") _UpdateWindow = user32.NewProc("UpdateWindow") - gdi32 = syscall.NewLazySystemDLL("gdi32") - _GetDeviceCaps = gdi32.NewProc("GetDeviceCaps") + shcore = syscall.NewLazySystemDLL("shcore") + _GetDpiForMonitor = shcore.NewProc("GetDpiForMonitor") ) func getModuleHandle() (syscall.Handle, error) { @@ -596,9 +600,10 @@ func getDC(hwnd syscall.Handle) (syscall.Handle, error) { return syscall.Handle(hdc), nil } -func getDeviceCaps(hdc syscall.Handle, index int32) int { - c, _, _ := _GetDeviceCaps.Call(uintptr(hdc), uintptr(index)) - return int(c) +func getDpiForMonitor(hmonitor syscall.Handle, dpiType uint32) int { + var dpiX, dpiY uintptr + _GetDpiForMonitor.Call(uintptr(hmonitor), uintptr(dpiType), uintptr(unsafe.Pointer(&dpiX)), uintptr(unsafe.Pointer(&dpiY))) + return int(dpiX) } func getKeyState(nVirtKey int32) int16 { @@ -636,6 +641,11 @@ func loadCursor(curID uint16) (syscall.Handle, error) { return syscall.Handle(h), nil } +func monitorFromPoint(pt point, flags uint32) syscall.Handle { + r, _, _ := _MonitorFromPoint.Call(uintptr(pt.x), uintptr(pt.y), uintptr(flags)) + return syscall.Handle(r) +} + func msgWaitForMultipleObjectsEx(nCount uint32, pHandles uintptr, millis, mask, flags uint32) (uint32, error) { r, _, err := _MsgWaitForMultipleObjectsEx.Call(uintptr(nCount), pHandles, uintptr(millis), uintptr(mask), uintptr(flags)) res := uint32(r) diff --git a/app/internal/window/window.go b/app/internal/window/window.go index 4c26ad91..258ebe52 100644 --- a/app/internal/window/window.go +++ b/app/internal/window/window.go @@ -109,13 +109,4 @@ func newWindowRendezvous() *windowRendezvous { const ( inchPrDp = 1.0 / 160 - mmPrDp = 25.4 / 160 - // monitorScale is the extra scale applied to - // monitor outputs to compensate for the extra - // viewing distance compared to phone and tables. - monitorScale = 1.20 - // minDensity is the minimum pixels per dp to - // ensure font and ui legibility on low-dpi - // screens. - minDensity = 1.0 )