Skip to content

Commit 9969797

Browse files
linesightclaude
andcommitted
feat: implement RenderHandler.GetScreenInfo with HiDPI support
Wires the previously-stubbed RenderHandler_GetScreenInfo through to a Python callback that fills a screen_info_out dict (device_scale_factor, depth, depth_per_component, is_monochrome, rect, available_rect). Asserts device_scale_factor > 0 to prevent compositor divide-by-zero, mirrors rect into available_rect when only one is supplied, and documents the OnPaint buffer-scaling implications for HiDPI displays. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9c2ae77 commit 9969797

3 files changed

Lines changed: 102 additions & 3 deletions

File tree

api/API-index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@
349349
* [GetViewRect](RenderHandler.md#getviewrect)
350350
* [GetScreenRect](RenderHandler.md#getscreenrect)
351351
* [GetScreenPoint](RenderHandler.md#getscreenpoint)
352+
* [GetScreenInfo](RenderHandler.md#getscreeninfo)
352353
* [OnPopupShow](RenderHandler.md#onpopupshow)
353354
* [OnPopupSize](RenderHandler.md#onpopupsize)
354355
* [OnPaint](RenderHandler.md#onpaint)

api/RenderHandler.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Table of contents:
1717
* [GetViewRect](#getviewrect)
1818
* [GetScreenRect](#getscreenrect)
1919
* [GetScreenPoint](#getscreenpoint)
20+
* [GetScreenInfo](#getscreeninfo)
2021
* [OnPopupShow](#onpopupshow)
2122
* [OnPopupSize](#onpopupsize)
2223
* [OnPaint](#onpaint)
@@ -43,7 +44,6 @@ Off-screen rendering examples:
4344

4445
Callbacks available in upstream CEF, but not yet exposed in CEF Python
4546
(see src/include/cef_render_handler.h):
46-
* GetScreenInfo
4747
* OnImeCompositionRangeChanged
4848

4949

@@ -100,6 +100,46 @@ Called to retrieve the translation from view coordinates to actual
100100
screen coordinates. Return true if the screen coordinates were provided.
101101

102102

103+
### GetScreenInfo
104+
105+
| Parameter | Type |
106+
| --- | --- |
107+
| browser | [Browser](Browser.md) |
108+
| screen_info_out | dict |
109+
| __Return__ | bool |
110+
111+
Called to allow the client to fill in the screen info with appropriate
112+
values. Return true if `screen_info_out` was modified, false to let CEF
113+
use defaults.
114+
115+
Fill `screen_info_out` with any of the following keys (all optional):
116+
117+
| Key | Type | Default | Notes |
118+
| --- | --- | --- | --- |
119+
| `device_scale_factor` | float | `1.0` | Ratio between physical and logical pixels. **Must be > 0.** |
120+
| `depth` | int | `24` | Screen depth in bits per pixel. |
121+
| `depth_per_component` | int | `8` | Bits per color component. |
122+
| `is_monochrome` | bool | `False` | True for black-and-white printers. |
123+
| `rect` | list[x,y,width,height] | unset | Display monitor rectangle in DIP. |
124+
| `available_rect` | list[x,y,width,height] | unset | Work-area rectangle in DIP (excludes taskbars). If only `rect` is supplied it is mirrored into `available_rect`. |
125+
126+
If neither `rect` nor `available_rect` is supplied, CEF falls back to
127+
the rectangle returned by `GetViewRect` (popups may be misplaced if
128+
the rectangle ends up empty).
129+
130+
**HiDPI handling:** set `device_scale_factor` to match the host
131+
display (e.g. `2.0` on a Retina/4K screen, `1.5` for 150% fractional
132+
scaling). The factor is applied to the OnPaint buffer: with a view
133+
rect of 800x600 and `device_scale_factor=2.0`, OnPaint receives a
134+
1600x1200 BGRA buffer while `width`/`height` arguments equal the
135+
buffer's pixel dimensions. Always upload the buffer at its actual
136+
pixel size and lay it out at the view's logical size to avoid
137+
blurry output. Call
138+
[Browser.NotifyScreenInfoChanged](Browser.md#notifyscreeninfochanged)
139+
when the device scale factor or display geometry changes (e.g. the
140+
window moved to a different monitor) so CEF re-queries this callback.
141+
142+
103143
### OnPopupShow
104144

105145
| Parameter | Type |

src/handlers/render_handler.pyx

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,66 @@ cdef public cpp_bool RenderHandler_GetScreenInfo(
147147
CefRefPtr[CefBrowser] cefBrowser,
148148
CefScreenInfo& cefScreenInfo
149149
) except * with gil:
150-
# Not yet implemented.
151-
return False
150+
cdef PyBrowser pyBrowser
151+
cdef dict pyScreenInfo = {}
152+
cdef py_bool ret
153+
cdef list pyRect
154+
cdef double deviceScaleFactor
155+
try:
156+
pyBrowser = GetPyBrowser(cefBrowser, "GetScreenInfo")
157+
callback = pyBrowser.GetClientCallback("GetScreenInfo")
158+
if not callback:
159+
return False
160+
ret = callback(browser=pyBrowser, screen_info_out=pyScreenInfo)
161+
if not ret:
162+
return False
163+
# device_scale_factor is the ratio between physical and logical
164+
# pixels. On HiDPI displays this is typically 2.0 (1.25/1.5/1.75
165+
# for fractional scaling). It MUST be > 0 -- a value of 0 will
166+
# divide-by-zero inside Chromium's compositor. Setting this also
167+
# scales the OnPaint buffer: a 800x600 view rect with
168+
# device_scale_factor=2.0 yields a 1600x1200 BGRA buffer.
169+
if "device_scale_factor" in pyScreenInfo:
170+
deviceScaleFactor = float(pyScreenInfo["device_scale_factor"])
171+
assert deviceScaleFactor > 0.0, (
172+
"device_scale_factor must be > 0")
173+
cefScreenInfo.device_scale_factor = <float>deviceScaleFactor
174+
else:
175+
cefScreenInfo.device_scale_factor = 1.0
176+
cefScreenInfo.depth = int(pyScreenInfo.get("depth", 24))
177+
cefScreenInfo.depth_per_component = int(
178+
pyScreenInfo.get("depth_per_component", 8))
179+
cefScreenInfo.is_monochrome = <cpp_bool>bool(
180+
pyScreenInfo.get("is_monochrome", False))
181+
# rect/available_rect are in DIP (logical pixels). Leaving them
182+
# zero-initialized tells CEF to fall back to GetViewRect (see
183+
# cef_render_handler.h:107).
184+
if "rect" in pyScreenInfo:
185+
pyRect = list(pyScreenInfo["rect"])
186+
assert len(pyRect) == 4, "rect must be [x, y, width, height]"
187+
cefScreenInfo.rect.x = int(pyRect[0])
188+
cefScreenInfo.rect.y = int(pyRect[1])
189+
cefScreenInfo.rect.width = int(pyRect[2])
190+
cefScreenInfo.rect.height = int(pyRect[3])
191+
if "available_rect" in pyScreenInfo:
192+
pyRect = list(pyScreenInfo["available_rect"])
193+
assert len(pyRect) == 4, (
194+
"available_rect must be [x, y, width, height]")
195+
cefScreenInfo.available_rect.x = int(pyRect[0])
196+
cefScreenInfo.available_rect.y = int(pyRect[1])
197+
cefScreenInfo.available_rect.width = int(pyRect[2])
198+
cefScreenInfo.available_rect.height = int(pyRect[3])
199+
elif "rect" in pyScreenInfo:
200+
# Mirror rect into available_rect so popups place correctly
201+
# when the caller only supplied one.
202+
cefScreenInfo.available_rect.x = cefScreenInfo.rect.x
203+
cefScreenInfo.available_rect.y = cefScreenInfo.rect.y
204+
cefScreenInfo.available_rect.width = cefScreenInfo.rect.width
205+
cefScreenInfo.available_rect.height = cefScreenInfo.rect.height
206+
return True
207+
except:
208+
(exc_type, exc_value, exc_trace) = sys.exc_info()
209+
sys.excepthook(exc_type, exc_value, exc_trace)
152210

153211
cdef public void RenderHandler_OnPopupShow(
154212
CefRefPtr[CefBrowser] cefBrowser,

0 commit comments

Comments
 (0)