Skip to content

Commit c87ec69

Browse files
Paul V Cravenclaude
andcommitted
Return get_display_size() in window units with display scaling
In stretch mode, pyglet multiplies a window's size by the screen's scale on Windows and Linux, but get_display_size() returned the screen's size in pixels, so a window that size was scaled up twice and didn't fit on the screen. It now divides by the scale there, rounding down. macOS and the web don't scale windows, so they're unchanged (#2791). center_window() needs the size in pixels, to match the framebuffer and set_location, so it reads the window's screen directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 8cdacd0 commit c87ec69

4 files changed

Lines changed: 83 additions & 4 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Arcade [PyPi Release History](https://pypi.org/project/arcade/#history) page.
66
## Unreleased
77

88
### Fixes
9+
- Fixed a window sized with `arcade.get_display_size()` being bigger than the screen with display scaling, such as Windows set to 125% or 150%. Arcade scales windows up by the display scale, but `get_display_size()` returned the screen's size in pixels, so the window was scaled up twice. It now returns the size in window units on Windows and Linux: at 125%, a 1920x1200 screen gives 1536x960, which fills it exactly. On macOS and the web it's unchanged, as they don't scale windows ([#2791](https://github.com/pythonarcade/arcade/issues/2791)).
910
- GUI: Fixed a gap between `UIFlatButton`'s border and the edge of the button, half the border's width, which also showed on `UIDropdown` ([#2868](https://github.com/pythonarcade/arcade/issues/2868)).
1011
- GUI: Passing a single style such as `UIFlatButton.UIStyle()` as a widget's `style` raised a confusing `TypeError` about attribute names. It now raises a `TypeError` saying a dict of styles for each state is needed, and how to start from the widget's `DEFAULT_STYLE` ([#2817](https://github.com/pythonarcade/arcade/issues/2817)).
1112
- GUI: Fixed `UITextArea`:

‎arcade/application.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from arcade.gl.provider import get_arcade_context, set_provider
3333
from arcade.types import LBWH, Color, Rect, RGBANormalized, RGBOrA255
3434
from arcade.utils import is_raspberry_pi
35-
from arcade.window_commands import get_display_size, set_window
35+
from arcade.window_commands import set_window
3636

3737
if TYPE_CHECKING:
3838
from arcade.camera import Projector
@@ -534,8 +534,9 @@ def set_fullscreen(
534534

535535
def center_window(self) -> None:
536536
"""Center the window on your desktop."""
537-
# Get the display screen using pyglet
538-
screen_width, screen_height = get_display_size()
537+
# The screen's size in pixels, like the framebuffer's size and the
538+
# location. get_display_size() is in window units instead.
539+
screen_width, screen_height = self.screen.width, self.screen.height
539540

540541
window_width, window_height = self.get_framebuffer_size()
541542
# Center the window

‎arcade/window_commands.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,29 @@ def get_display_size(screen_id: int = 0) -> tuple[int, int]:
4646
4747
The size of the primary monitor is returned by default.
4848
49+
The size is in the same units as a :py:class:`~arcade.Window`'s size, so a
50+
window this size fills the screen. With display scaling, such as Windows set
51+
to 150%, that's smaller than the screen's size in pixels, because Arcade
52+
scales windows up to match.
53+
4954
Args:
5055
screen_id: The screen number
5156
Returns:
5257
Tuple containing the width and height of the screen
5358
"""
5459
display = pyglet.display.Display() # type: ignore # pending: pyglet fixes import tricks
5560
screen = display.get_screens()[screen_id]
56-
return screen.width, screen.height
61+
width, height = screen.width, screen.height
62+
# In stretch mode, pyglet multiplies a window's size by the screen's scale
63+
# on Windows and Linux. Screen sizes on macOS are already in the same units
64+
# as window sizes, and the web doesn't scale windows.
65+
if pyglet.options.dpi_scaling == "stretch" and (
66+
pyglet.compat_platform == "win32" or pyglet.compat_platform.startswith("linux")
67+
):
68+
scale = screen.get_scale()
69+
# Round down, so the scaled window isn't bigger than the screen
70+
width, height = int(width / scale), int(height / scale)
71+
return width, height
5772

5873

5974
def get_window() -> Window:
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""get_display_size() is in window units, so a window that size fills the screen (#2791)."""
2+
3+
import pyglet
4+
import pytest
5+
6+
import arcade
7+
8+
9+
class FakeScreen:
10+
def __init__(self, width, height, scale):
11+
self.width = width
12+
self.height = height
13+
self._scale = scale
14+
15+
def get_scale(self):
16+
return self._scale
17+
18+
19+
@pytest.fixture
20+
def fake_screen(monkeypatch):
21+
def use(width, height, scale, dpi_scaling, platform):
22+
screen = FakeScreen(width, height, scale)
23+
24+
class FakeDisplay:
25+
def get_screens(self):
26+
return [screen]
27+
28+
monkeypatch.setattr(pyglet.display, "Display", FakeDisplay)
29+
monkeypatch.setattr(pyglet.options, "dpi_scaling", dpi_scaling)
30+
monkeypatch.setattr(pyglet, "compat_platform", platform)
31+
32+
return use
33+
34+
35+
@pytest.mark.parametrize("platform", ["win32", "linux", "linux-compat"])
36+
@pytest.mark.parametrize(
37+
("width", "height", "scale", "expected"),
38+
[
39+
(1920, 1080, 1.0, (1920, 1080)),
40+
(2560, 1440, 1.25, (2048, 1152)),
41+
(1920, 1080, 1.5, (1280, 720)),
42+
# Rounded down, so the scaled window isn't bigger than the screen
43+
(1366, 768, 1.75, (780, 438)),
44+
],
45+
)
46+
def test_stretch_divides_by_scale(fake_screen, platform, width, height, scale, expected):
47+
fake_screen(width, height, scale, "stretch", platform)
48+
size = arcade.get_display_size()
49+
assert size == expected
50+
assert int(size[0] * scale) <= width
51+
assert int(size[1] * scale) <= height
52+
53+
54+
@pytest.mark.parametrize("platform", ["darwin", "emscripten"])
55+
def test_stretch_on_platforms_that_dont_scale_windows(fake_screen, platform):
56+
fake_screen(1440, 900, 2.0, "stretch", platform)
57+
assert arcade.get_display_size() == (1440, 900)
58+
59+
60+
def test_platform_scaling_returns_the_screen_size(fake_screen):
61+
fake_screen(2560, 1440, 1.25, "platform", "win32")
62+
assert arcade.get_display_size() == (2560, 1440)

0 commit comments

Comments
 (0)