1package cef
2
3import (
4 "sync"
5
6 purecef "github.com/bnema/purego-cef/cef"
7 cef2gtk "github.com/bnema/purego-cef2gtk"
8
9 "github.com/bnema/dumber/internal/logging"
10)
11
12type popupSurface interface {
13 SetPopupVisible(bool)
14 SetPopupRect(purecef.Rect)
15}
16
17type dumberRenderHandler struct {
18 wv *WebView
19 main purecef.RenderHandler
20 popup purecef.RenderHandler
21 popupSurface popupSurface
22}
23
24var _ purecef.RenderHandler = (*dumberRenderHandler)(nil)
25
26func newDumberRenderHandler(wv *WebView) purecef.RenderHandler {
27 if wv == nil || wv.viewBridge == nil {
28 return nil
29 }
30 h := &dumberRenderHandler{wv: wv}
31
32 var unsupportedPaintOnce sync.Once
33 h.main = wv.viewBridge.RenderHandler(cef2gtk.Hooks{
34 OnUnsupportedPaint: func() {
35 unsupportedPaintOnce.Do(func() {
36 if wv.ctx != nil {
37 logging.FromContext(wv.ctx).Warn().Msg("cef: unsupported CPU paint from accelerated bridge")
38 }
39 })
40 },
41 OnError: func(err error) {
42 if wv.ctx != nil {
43 logging.FromContext(wv.ctx).Warn().Err(err).Msg("cef: accelerated render bridge error")
44 }
45 },
46 OnTextSelectionChanged: func(selectedText string, _ *purecef.Range) {
47 handleRenderTextSelectionChanged(wv, selectedText)
48 },
49 })
50
51 if wv.popupSurface != nil {
52 var popupUnsupportedPaintOnce sync.Once
53 h.popupSurface = wv.popupSurface
54 h.popup = wv.popupSurface.RenderHandler(cef2gtk.Hooks{
55 OnUnsupportedPaint: func() {
56 popupUnsupportedPaintOnce.Do(func() {
57 if wv.ctx != nil {
58 logging.FromContext(wv.ctx).Warn().Msg("cef: unsupported CPU paint from popup surface")
59 }
60 })
61 },
62 OnError: func(err error) {
63 if wv.ctx != nil {
64 logging.FromContext(wv.ctx).Warn().Err(err).Msg("cef: popup surface render error")
65 }
66 },
67 })
68 }
69
70 return h
71}
72
73func (h *dumberRenderHandler) renderTargetForPaint(elementType purecef.PaintElementType) purecef.RenderHandler {
74 if h == nil {
75 return nil
76 }
77 if elementType == purecef.PaintElementTypePetPopup && h.popup != nil {
78 return h.popup
79 }
80 return h.main
81}
82
83func (h *dumberRenderHandler) GetAccessibilityHandler() purecef.AccessibilityHandler {
84 if h == nil || h.main == nil {
85 return nil
86 }
87 return h.main.GetAccessibilityHandler()
88}
89
90func (h *dumberRenderHandler) GetRootScreenRect(browser purecef.Browser, rect *purecef.Rect) int32 {
91 if h == nil || h.main == nil {
92 return 0
93 }
94 return h.main.GetRootScreenRect(browser, rect)
95}
96
97func (h *dumberRenderHandler) GetViewRect(browser purecef.Browser, rect *purecef.Rect) {
98 if h == nil || h.main == nil {
99 if rect != nil {
100 rect.X, rect.Y, rect.Width, rect.Height = 0, 0, 1, 1
101 }
102 return
103 }
104 h.main.GetViewRect(browser, rect)
105}
106
107func (h *dumberRenderHandler) GetScreenPoint(browser purecef.Browser, viewX, viewY int32, screenX, screenY *int32) int32 {
108 if h == nil || h.main == nil {
109 return 0
110 }
111 return h.main.GetScreenPoint(browser, viewX, viewY, screenX, screenY)
112}
113
114func (h *dumberRenderHandler) GetScreenInfo(browser purecef.Browser, info *purecef.ScreenInfo) int32 {
115 if h == nil || h.main == nil {
116 return 0
117 }
118 return h.main.GetScreenInfo(browser, info)
119}
120
121func (h *dumberRenderHandler) OnPopupShow(browser purecef.Browser, show int32) {
122 if h != nil && h.popupSurface != nil {
123 visible := show != 0
124 if h.wv != nil {
125 h.wv.runOnGTKSync(func() {
126 h.popupSurface.SetPopupVisible(visible)
127 })
128 } else {
129 h.popupSurface.SetPopupVisible(visible)
130 }
131 }
132 if h != nil && h.popup != nil {
133 h.popup.OnPopupShow(browser, show)
134 }
135}
136
137func (h *dumberRenderHandler) OnPopupSize(browser purecef.Browser, rect *purecef.Rect) {
138 if h != nil && h.popupSurface != nil && rect != nil {
139 popupRect := *rect
140 if h.wv != nil {
141 popupRect = popupRectForWidget(h.wv, popupRect)
142 h.wv.runOnGTKSync(func() {
143 h.popupSurface.SetPopupRect(popupRect)
144 })
145 } else {
146 h.popupSurface.SetPopupRect(popupRect)
147 }
148 }
149 if h != nil && h.popup != nil {
150 h.popup.OnPopupSize(browser, rect)
151 }
152}
153
154func popupRectForWidget(wv *WebView, rect purecef.Rect) purecef.Rect {
155 if wv == nil {
156 return rect
157 }
158 // Only convert when the OSR backing-scale compatibility path is active.
159 // A monitor scale above 1 is not enough by itself because CEF already reports
160 // logical popup coordinates when backing-scale mode is off.
161 if wv.osrBackingScaleFactor() <= 1 {
162 return rect
163 }
164 scale := wv.viewBridgeScale()
165 return purecef.Rect{
166 X: deviceToLogicalCoord(rect.X, scale),
167 Y: deviceToLogicalCoord(rect.Y, scale),
168 Width: deviceToLogicalSize(rect.Width, scale),
169 Height: deviceToLogicalSize(rect.Height, scale),
170 }
171}
172
173func (h *dumberRenderHandler) OnPaint(
174 browser purecef.Browser,
175 elementType purecef.PaintElementType,
176 dirtyRects []purecef.Rect,
177 buffer []byte,
178 width, height int32,
179) {
180 if delegate := h.renderTargetForPaint(elementType); delegate != nil {
181 delegate.OnPaint(browser, elementType, dirtyRects, buffer, width, height)
182 }
183}
184
185func (h *dumberRenderHandler) OnAcceleratedPaint(
186 browser purecef.Browser,
187 elementType purecef.PaintElementType,
188 dirtyRects []purecef.Rect,
189 info *purecef.AcceleratedPaintInfo,
190) {
191 if delegate := h.renderTargetForPaint(elementType); delegate != nil {
192 delegate.OnAcceleratedPaint(browser, elementType, dirtyRects, info)
193 }
194}
195
196func (h *dumberRenderHandler) GetTouchHandleSize(browser purecef.Browser, orientation purecef.HorizontalAlignment, size *purecef.Size) {
197 if h == nil || h.main == nil {
198 return
199 }
200 h.main.GetTouchHandleSize(browser, orientation, size)
201}
202
203func (h *dumberRenderHandler) OnTouchHandleStateChanged(browser purecef.Browser, state *purecef.TouchHandleState) {
204 if h == nil || h.main == nil {
205 return
206 }
207 h.main.OnTouchHandleStateChanged(browser, state)
208}
209
210func (h *dumberRenderHandler) StartDragging(
211 browser purecef.Browser,
212 dragData purecef.DragData,
213 allowedOps purecef.DragOperationsMask,
214 x, y int32,
215) int32 {
216 if h == nil || h.main == nil {
217 return 0
218 }
219 return h.main.StartDragging(browser, dragData, allowedOps, x, y)
220}
221
222func (h *dumberRenderHandler) UpdateDragCursor(browser purecef.Browser, operation purecef.DragOperationsMask) {
223 if h == nil || h.main == nil {
224 return
225 }
226 h.main.UpdateDragCursor(browser, operation)
227}
228
229func (h *dumberRenderHandler) OnScrollOffsetChanged(browser purecef.Browser, x, y float64) {
230 if h == nil || h.main == nil {
231 return
232 }
233 h.main.OnScrollOffsetChanged(browser, x, y)
234}
235
236func (h *dumberRenderHandler) OnImeCompositionRangeChanged(
237 browser purecef.Browser,
238 selectedRange *purecef.Range,
239 characterBounds []purecef.Rect,
240) {
241 if h == nil || h.main == nil {
242 return
243 }
244 h.main.OnImeCompositionRangeChanged(browser, selectedRange, characterBounds)
245}
246
247func (h *dumberRenderHandler) OnTextSelectionChanged(browser purecef.Browser, selectedText string, selectedRange *purecef.Range) {
248 if h == nil || h.main == nil {
249 return
250 }
251 h.main.OnTextSelectionChanged(browser, selectedText, selectedRange)
252}
253
254func (h *dumberRenderHandler) OnVirtualKeyboardRequested(browser purecef.Browser, mode purecef.TextInputMode) {
255 if h == nil || h.main == nil {
256 return
257 }
258 h.main.OnVirtualKeyboardRequested(browser, mode)
259}
260
261func handleRenderTextSelectionChanged(wv *WebView, selectedText string) {
262 if wv == nil {
263 return
264 }
265 previous, changed := wv.setSelectedText(selectedText)
266 if !changed {
267 return
268 }
269 if wv.ctx != nil && selectedText == "" {
270 if previous != "" {
271 logging.FromContext(wv.ctx).Debug().
272 Int("prev_text_len", len(previous)).
273 Msg("cef: text selection cleared")
274 }
275 } else if wv.ctx != nil {
276 logging.FromContext(wv.ctx).Debug().
277 Int("text_len", len(selectedText)).
278 Msg("cef: text selection changed")
279 }
280 if wv.engine != nil {
281 wv.scheduleSelectionUpdate(selectedText)
282 }
283}