Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XLPlatformLinuxWaylandView.h"
24 :
25 : #include <linux/input.h>
26 :
27 : #ifndef XL_WAYLAND_LOG
28 : #if XL_WAYLAND_DEBUG
29 : #define XL_WAYLAND_LOG(...) log::debug("Wayland", __VA_ARGS__)
30 : #else
31 : #define XL_WAYLAND_LOG(...)
32 : #endif
33 : #endif
34 :
35 : namespace STAPPLER_VERSIONIZED stappler::xenolith::platform {
36 :
37 : static struct wl_surface_listener s_WaylandSurfaceListener{
38 0 : [] (void *data, wl_surface *surface, wl_output *output) {
39 0 : ((WaylandView *)data)->handleSurfaceEnter(surface, output);
40 0 : },
41 0 : [] (void *data, wl_surface *surface, wl_output *output) {
42 0 : ((WaylandView *)data)->handleSurfaceLeave(surface, output);
43 0 : },
44 : };
45 :
46 : static const wl_callback_listener s_WaylandSurfaceFrameListener{
47 0 : [] (void *data, wl_callback *wl_callback, uint32_t callback_data) {
48 0 : ((WaylandView *)data)->handleSurfaceFrameDone(wl_callback, callback_data);
49 0 : },
50 : };
51 :
52 : static xdg_surface_listener const s_XdgSurfaceListener{
53 0 : [] (void *data, xdg_surface *xdg_surface, uint32_t serial) {
54 0 : ((WaylandView *)data)->handleSurfaceConfigure(xdg_surface, serial);
55 0 : },
56 : };
57 :
58 : static const xdg_toplevel_listener s_XdgToplevelListener{
59 0 : [] (void *data, xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, wl_array *states) {
60 0 : ((WaylandView *)data)->handleToplevelConfigure(xdg_toplevel, width, height, states);
61 0 : },
62 0 : [] (void* data, struct xdg_toplevel* xdg_toplevel) {
63 0 : ((WaylandView *)data)->handleToplevelClose(xdg_toplevel);
64 0 : },
65 0 : [] (void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) {
66 0 : ((WaylandView *)data)->handleToplevelBounds(xdg_toplevel, width, height);
67 0 : }
68 : };
69 :
70 0 : WaylandView::WaylandView(WaylandLibrary *lib, ViewInterface *view, StringView name, StringView bundleName, URect rect) {
71 0 : _display = Rc<WaylandDisplay>::create(lib);
72 :
73 0 : _view = view;
74 0 : _currentExtent = Extent2(rect.width, rect.height);
75 :
76 0 : _surface = _display->createSurface(this);
77 0 : if (_surface) {
78 0 : _display->wayland->wl_surface_set_user_data(_surface, this);
79 0 : _display->wayland->wl_surface_add_listener(_surface, &s_WaylandSurfaceListener, this);
80 :
81 0 : auto region = _display->wayland->wl_compositor_create_region(_display->compositor);
82 0 : _display->wayland->wl_region_add(region, 0, 0, _currentExtent.width, _currentExtent.height);
83 0 : _display->wayland->wl_surface_set_opaque_region(_surface, region);
84 :
85 0 : _xdgSurface = _display->wayland->xdg_wm_base_get_xdg_surface(_display->xdgWmBase, _surface);
86 :
87 0 : _display->wayland->xdg_surface_add_listener(_xdgSurface, &s_XdgSurfaceListener, this);
88 0 : _toplevel = _display->wayland->xdg_surface_get_toplevel(_xdgSurface);
89 0 : _display->wayland->xdg_toplevel_set_title(_toplevel, name.data());
90 0 : _display->wayland->xdg_toplevel_set_app_id(_toplevel, bundleName.data());
91 0 : _display->wayland->xdg_toplevel_add_listener(_toplevel, &s_XdgToplevelListener, this);
92 :
93 0 : if (_clientSizeDecoration) {
94 0 : createDecorations();
95 : }
96 :
97 0 : _display->wayland->wl_surface_commit(_surface);
98 0 : _display->wayland->wl_region_destroy(region);
99 : }
100 :
101 0 : uint32_t rate = 60000;
102 0 : for (auto &it : _display->outputs) {
103 0 : rate = std::max(rate, uint32_t(it->mode.refresh));
104 : }
105 0 : _screenFrameInterval = 1'000'000'000ULL / rate;
106 0 : }
107 :
108 0 : WaylandView::~WaylandView() {
109 0 : _iconMaximized = nullptr;
110 0 : _decors.clear();
111 0 : if (_toplevel) {
112 0 : _display->wayland->xdg_toplevel_destroy(_toplevel);
113 0 : _toplevel = nullptr;
114 : }
115 0 : if (_xdgSurface) {
116 0 : _display->wayland->xdg_surface_destroy(_xdgSurface);
117 0 : _xdgSurface = nullptr;
118 : }
119 0 : if (_surface) {
120 0 : _display->destroySurface(_surface);
121 0 : _surface = nullptr;
122 : }
123 0 : _display = nullptr;
124 0 : }
125 :
126 0 : bool WaylandView::poll(bool frameReady) {
127 0 : if (_shouldClose) {
128 0 : return false;
129 : }
130 :
131 0 : if (_display->seatDirty) {
132 0 : _display->seat->update();
133 : }
134 :
135 0 : if (frameReady && ((_continuousRendering && _state.test(XDG_TOPLEVEL_STATE_ACTIVATED)) || _scheduleNext)) {
136 0 : auto frame = _display->wayland->wl_surface_frame(_surface);
137 0 : _display->wayland->wl_callback_add_listener(frame, &s_WaylandSurfaceFrameListener, this);
138 0 : _display->wayland->wl_surface_commit(_surface);
139 0 : _scheduleNext = false;
140 : }
141 :
142 0 : _display->flush();
143 :
144 0 : if (!_shouldClose) {
145 0 : if (!_keys.empty()) {
146 0 : handleKeyRepeat();
147 : }
148 : }
149 :
150 0 : return !_shouldClose;
151 : }
152 :
153 0 : int WaylandView::getSocketFd() const {
154 0 : return _display->getSocketFd();
155 : }
156 :
157 0 : uint64_t WaylandView::getScreenFrameInterval() const {
158 : // On Wayland, limit on full interval causes vblank miss due Mailbox implementation, so, limit on half-interval
159 : // Mailbox do appropriate sync even without specified frame interval, it's just a little help for engine
160 0 : return _screenFrameInterval /*/ 2*/;
161 : }
162 :
163 0 : void WaylandView::mapWindow() {
164 0 : _display->flush();
165 0 : }
166 :
167 0 : void WaylandView::handleSurfaceEnter(wl_surface *surface, wl_output *output) {
168 0 : if (!_display->wayland->ownsProxy(output)) {
169 0 : return;
170 : }
171 :
172 0 : auto out = (WaylandOutput *)_display->wayland->wl_output_get_user_data(output);
173 0 : if (out) {
174 0 : _activeOutputs.emplace(out);
175 : XL_WAYLAND_LOG("handleSurfaceEnter: output: ", out->description());
176 : }
177 : }
178 :
179 0 : void WaylandView::handleSurfaceLeave(wl_surface *surface, wl_output *output) {
180 0 : if (!_display->wayland->ownsProxy(output)) {
181 0 : return;
182 : }
183 :
184 0 : auto out = (WaylandOutput *)_display->wayland->wl_output_get_user_data(output);
185 0 : if (out) {
186 0 : _activeOutputs.erase(out);
187 : XL_WAYLAND_LOG("handleSurfaceLeave: output: ", out->description());
188 : }
189 : }
190 :
191 0 : void WaylandView::handleSurfaceConfigure(xdg_surface *surface, uint32_t serial) {
192 : XL_WAYLAND_LOG("handleSurfaceConfigure: serial: ", serial);
193 0 : _configureSerial = serial;
194 0 : }
195 :
196 0 : void WaylandView::handleToplevelConfigure(xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, wl_array *states) {
197 0 : StringStream stream;
198 0 : stream << "handleToplevelConfigure: width: " << width << ", height: " << height << ";";
199 :
200 0 : auto oldState = _state;
201 0 : _state.reset();
202 :
203 0 : for (uint32_t *it = (uint32_t *)states->data; (const char *)it < ((const char *) states->data + states->size); ++ it) {
204 0 : _state.set(*it);
205 0 : switch (*it) {
206 0 : case XDG_TOPLEVEL_STATE_MAXIMIZED: stream << " MAXIMIZED;"; break;
207 0 : case XDG_TOPLEVEL_STATE_FULLSCREEN: stream << " FULLSCREEN;"; break;
208 0 : case XDG_TOPLEVEL_STATE_RESIZING: stream << " RESIZING;"; break;
209 0 : case XDG_TOPLEVEL_STATE_ACTIVATED: stream << " ACTIVATED;"; break;
210 0 : case XDG_TOPLEVEL_STATE_TILED_LEFT: stream << " TILED_LEFT;"; break;
211 0 : case XDG_TOPLEVEL_STATE_TILED_RIGHT: stream << " TILED_RIGHT;"; break;
212 0 : case XDG_TOPLEVEL_STATE_TILED_TOP: stream << " TILED_TOP;"; break;
213 0 : case XDG_TOPLEVEL_STATE_TILED_BOTTOM: stream << " TILED_BOTTOM;"; break;
214 : }
215 : }
216 :
217 0 : if (_state.test(XDG_TOPLEVEL_STATE_ACTIVATED) != oldState.test(XDG_TOPLEVEL_STATE_ACTIVATED)) {
218 0 : _view->handleInputEvent(core::InputEventData::BoolEvent(core::InputEventName::FocusGain, _state.test(XDG_TOPLEVEL_STATE_ACTIVATED)));
219 : }
220 :
221 0 : if (width && height) {
222 0 : if (_currentExtent.width != uint32_t(width) || _currentExtent.height != uint32_t(height)) {
223 0 : _currentExtent.width = width;
224 0 : _currentExtent.height = height - DecorOffset - DecorInset;
225 0 : _view->deprecateSwapchain();
226 :
227 0 : stream << "surface: " << _currentExtent.width << " " << _currentExtent.height;
228 : }
229 : }
230 :
231 0 : auto checkVisible = [&, this] (WaylandDecorationName name) {
232 0 : switch (name) {
233 0 : case WaylandDecorationName::RightSide:
234 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
235 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_RIGHT)) { return false; }
236 0 : break;
237 0 : case WaylandDecorationName::TopRigntCorner:
238 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
239 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_TOP) && _state.test(XDG_TOPLEVEL_STATE_TILED_RIGHT)) { return false; }
240 0 : break;
241 0 : case WaylandDecorationName::TopSide:
242 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
243 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_TOP)) { return false; }
244 0 : break;
245 0 : case WaylandDecorationName::TopLeftCorner:
246 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
247 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_TOP) && _state.test(XDG_TOPLEVEL_STATE_TILED_LEFT)) { return false; }
248 0 : break;
249 0 : case WaylandDecorationName::BottomRightCorner:
250 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
251 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_BOTTOM) && _state.test(XDG_TOPLEVEL_STATE_TILED_RIGHT)) { return false; }
252 0 : break;
253 0 : case WaylandDecorationName::BottomSide:
254 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
255 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_BOTTOM)) { return false; }
256 0 : break;
257 0 : case WaylandDecorationName::BottomLeftCorner:
258 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
259 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_BOTTOM) && _state.test(XDG_TOPLEVEL_STATE_TILED_LEFT)) { return false; }
260 0 : break;
261 0 : case WaylandDecorationName::LeftSide:
262 0 : if (_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) { return false; }
263 0 : if (_state.test(XDG_TOPLEVEL_STATE_TILED_LEFT)) { return false; }
264 0 : break;
265 0 : default:
266 0 : break;
267 : }
268 0 : return true;
269 0 : };
270 :
271 0 : for (auto &it : _decors) {
272 0 : it->setActive(_state.test(XDG_TOPLEVEL_STATE_ACTIVATED));
273 0 : it->setVisible(checkVisible(it->name));
274 : }
275 :
276 : XL_WAYLAND_LOG(stream.str());
277 0 : }
278 :
279 0 : void WaylandView::handleToplevelClose(xdg_toplevel *xdg_toplevel) {
280 : XL_WAYLAND_LOG("handleToplevelClose");
281 0 : _shouldClose = true;
282 0 : }
283 :
284 0 : void WaylandView::handleToplevelBounds(xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) {
285 : XL_WAYLAND_LOG("handleToplevelBounds: width: ", width, ", height: ", height);
286 0 : }
287 :
288 0 : void WaylandView::handleSurfaceFrameDone(wl_callback *frame, uint32_t data) {
289 0 : _display->wayland->wl_callback_destroy(frame);
290 0 : }
291 :
292 0 : void WaylandView::handlePointerEnter(wl_fixed_t surface_x, wl_fixed_t surface_y) {
293 0 : if (!_pointerInit || _display->seat->hasPointerFrames) {
294 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::Enter });
295 0 : ev.enter.x = surface_x;
296 0 : ev.enter.y = surface_y;
297 : } else {
298 0 : _view->handleInputEvent(core::InputEventData::BoolEvent(core::InputEventName::PointerEnter, true,
299 0 : Vec2(float(wl_fixed_to_double(surface_x)), float(_currentExtent.height - wl_fixed_to_double(surface_y)))));
300 :
301 0 : _surfaceX = wl_fixed_to_double(surface_x);
302 0 : _surfaceY = wl_fixed_to_double(surface_y);
303 : }
304 :
305 : XL_WAYLAND_LOG("handlePointerEnter: x: ", wl_fixed_to_int(surface_x), ", y: ", wl_fixed_to_int(surface_y));
306 0 : }
307 :
308 0 : void WaylandView::handlePointerLeave() {
309 0 : if (!_pointerInit) {
310 0 : _pointerInit = true;
311 0 : if (!_display->seat->hasPointerFrames) {
312 0 : handlePointerFrame();
313 : }
314 : }
315 :
316 0 : handlePointerFrame(); // drop pending events
317 0 : _view->handleInputEvent(core::InputEventData::BoolEvent(core::InputEventName::PointerEnter, false,
318 0 : Vec2(float(_surfaceX), float(_currentExtent.height - _surfaceY))));
319 0 : }
320 :
321 0 : void WaylandView::handlePointerMotion(uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
322 : // XL_WAYLAND_LOG("handlePointerMotion: x: ", wl_fixed_to_int(surface_x), ", y: ", wl_fixed_to_int(surface_y));
323 :
324 0 : if (!_pointerInit) {
325 0 : _pointerInit = true;
326 0 : if (!_display->seat->hasPointerFrames) {
327 0 : handlePointerFrame();
328 : }
329 : }
330 :
331 0 : if (_display->seat->hasPointerFrames) {
332 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::Motion });
333 0 : ev.motion.time = time;
334 0 : ev.motion.x = surface_x;
335 0 : ev.motion.y = surface_y;
336 : } else {
337 0 : _view->handleInputEvent(core::InputEventData({
338 : maxOf<uint32_t>(),
339 : core::InputEventName::MouseMove,
340 : core::InputMouseButton::None,
341 0 : _activeModifiers,
342 0 : float(wl_fixed_to_double(surface_x)),
343 0 : float(_currentExtent.height - wl_fixed_to_double(surface_y))
344 : }));
345 :
346 0 : _surfaceX = wl_fixed_to_double(surface_x);
347 0 : _surfaceY = wl_fixed_to_double(surface_y);
348 : }
349 0 : }
350 :
351 0 : static core::InputMouseButton getButton(uint32_t button) {
352 0 : switch (button) {
353 0 : case BTN_LEFT: return core::InputMouseButton::MouseLeft; break;
354 0 : case BTN_RIGHT: return core::InputMouseButton::MouseRight; break;
355 0 : case BTN_MIDDLE: return core::InputMouseButton::MouseMiddle; break;
356 0 : default:
357 0 : return core::InputMouseButton(toInt(core::InputMouseButton::Mouse8) + (button - 0x113));
358 : break;
359 : }
360 : return core::InputMouseButton::None;
361 : }
362 :
363 0 : void WaylandView::handlePointerButton(uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
364 0 : if (!_pointerInit) {
365 0 : return;
366 : }
367 :
368 : XL_WAYLAND_LOG("handlePointerButton");
369 0 : if (_display->seat->hasPointerFrames) {
370 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::Button });
371 0 : ev.button.serial = serial;
372 0 : ev.button.time = time;
373 0 : ev.button.button = button;
374 0 : ev.button.state = state;
375 : } else {
376 0 : _view->handleInputEvent(core::InputEventData({
377 : button,
378 0 : ((state == WL_POINTER_BUTTON_STATE_PRESSED) ? core::InputEventName::Begin : core::InputEventName::End),
379 0 : getButton(button),
380 0 : _activeModifiers,
381 0 : float(_surfaceX),
382 0 : float(_currentExtent.height - _surfaceY)
383 : }));
384 : }
385 : }
386 :
387 0 : void WaylandView::handlePointerAxis(uint32_t time, uint32_t axis, wl_fixed_t value) {
388 0 : if (!_pointerInit) {
389 0 : return;
390 : }
391 :
392 0 : if (_display->seat->hasPointerFrames) {
393 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::Axis });
394 0 : ev.axis.time = time;
395 0 : ev.axis.axis = axis;
396 0 : ev.axis.value = value;
397 : } else {
398 0 : core::InputMouseButton btn = core::InputMouseButton::None;
399 0 : auto val = wl_fixed_to_int(value);
400 0 : switch (axis) {
401 0 : case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
402 0 : if (val < 0) {
403 0 : btn = core::InputMouseButton::MouseScrollUp;
404 : } else {
405 0 : btn = core::InputMouseButton::MouseScrollDown;
406 : }
407 0 : break;
408 0 : case WL_POINTER_AXIS_VERTICAL_SCROLL:
409 0 : if (val > 0) {
410 0 : btn = core::InputMouseButton::MouseScrollRight;
411 : } else {
412 0 : btn = core::InputMouseButton::MouseScrollLeft;
413 : }
414 0 : break;
415 : }
416 :
417 0 : core::InputEventData event({
418 0 : toInt(btn),
419 : core::InputEventName::Scroll,
420 : btn,
421 0 : _activeModifiers,
422 0 : float(_surfaceX),
423 0 : float(_currentExtent.height - _surfaceY)
424 0 : });
425 :
426 0 : switch (axis) {
427 0 : case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
428 0 : event.point.valueX = float(wl_fixed_to_double(value));
429 0 : event.point.valueY = 0.0f;
430 0 : break;
431 0 : case WL_POINTER_AXIS_VERTICAL_SCROLL:
432 0 : event.point.valueX = 0.0f;
433 0 : event.point.valueY = -float(wl_fixed_to_double(value));
434 0 : break;
435 : }
436 :
437 0 : _view->handleInputEvent(event);
438 : }
439 : }
440 :
441 0 : void WaylandView::handlePointerAxisSource(uint32_t axis_source) {
442 0 : if (!_pointerInit) {
443 0 : return;
444 : }
445 :
446 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::AxisSource });
447 0 : ev.axisSource.axis_source = axis_source;
448 : }
449 :
450 0 : void WaylandView::handlePointerAxisStop(uint32_t time, uint32_t axis) {
451 0 : if (!_pointerInit) {
452 0 : return;
453 : }
454 :
455 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::AxisStop });
456 0 : ev.axisStop.time = time;
457 0 : ev.axisStop.axis = axis;
458 : }
459 :
460 0 : void WaylandView::handlePointerAxisDiscrete(uint32_t axis, int32_t discrete) {
461 0 : if (!_pointerInit) {
462 0 : return;
463 : }
464 :
465 0 : auto &ev = _pointerEvents.emplace_back(PointerEvent{ PointerEvent::AxisDiscrete });
466 0 : ev.axisDiscrete.axis = axis;
467 0 : ev.axisDiscrete.discrete = discrete;
468 : }
469 :
470 0 : void WaylandView::handlePointerFrame() {
471 0 : if (!_pointerInit || _pointerEvents.empty()) {
472 0 : return;
473 : }
474 :
475 0 : Vector<core::InputEventData> inputEvents;
476 :
477 0 : bool positionChanged = false;
478 0 : double x = 0.0f;
479 0 : double y = 0.0f;
480 :
481 0 : core::InputMouseButton axisBtn = core::InputMouseButton::None;
482 0 : uint32_t axisSource = 0;
483 0 : bool hasAxis = false;
484 0 : double axisX = 0.0f;
485 0 : double axisY = 0.0f;
486 :
487 0 : for (auto &it : _pointerEvents) {
488 0 : switch (it.event) {
489 0 : case PointerEvent::None: break;
490 0 : case PointerEvent::Enter:
491 0 : inputEvents.emplace_back(core::InputEventData::BoolEvent(core::InputEventName::PointerEnter, true,
492 0 : Vec2(float(wl_fixed_to_double(it.enter.x)), float(_currentExtent.height - wl_fixed_to_double(it.enter.y)))));
493 0 : positionChanged = true;
494 0 : x = wl_fixed_to_double(it.enter.x);
495 0 : y = wl_fixed_to_double(it.enter.y);
496 0 : break;
497 0 : case PointerEvent::Leave:
498 0 : break;
499 0 : case PointerEvent::Motion:
500 0 : positionChanged = true;
501 0 : x = wl_fixed_to_double(it.motion.x);
502 0 : y = wl_fixed_to_double(it.motion.y);
503 0 : break;
504 0 : case PointerEvent::Button:
505 0 : break;
506 0 : case PointerEvent::Axis:
507 0 : switch (it.axis.axis) {
508 0 : case WL_POINTER_AXIS_VERTICAL_SCROLL:
509 0 : hasAxis = true;
510 0 : axisY -= wl_fixed_to_double(it.axis.value);
511 0 : if (wl_fixed_to_int(it.axis.value) < 0) {
512 0 : axisBtn = core::InputMouseButton::MouseScrollUp;
513 : } else {
514 0 : axisBtn = core::InputMouseButton::MouseScrollDown;
515 : }
516 0 : break;
517 0 : case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
518 0 : hasAxis = true;
519 0 : axisX += wl_fixed_to_double(it.axis.value);
520 0 : if (wl_fixed_to_int(it.axis.value) > 0) {
521 0 : axisBtn = core::InputMouseButton::MouseScrollRight;
522 : } else {
523 0 : axisBtn = core::InputMouseButton::MouseScrollLeft;
524 : }
525 0 : break;
526 0 : default: break;
527 : }
528 0 : break;
529 0 : case PointerEvent::AxisSource:
530 0 : axisSource = it.axisSource.axis_source;
531 0 : break;
532 0 : case PointerEvent::AxisStop:
533 0 : break;
534 0 : case PointerEvent::AxisDiscrete:
535 0 : break;
536 : }
537 : }
538 :
539 0 : if (positionChanged) {
540 0 : inputEvents.emplace_back(core::InputEventData({
541 : maxOf<uint32_t>(),
542 : core::InputEventName::MouseMove,
543 : core::InputMouseButton::None,
544 0 : _activeModifiers,
545 0 : float(x),
546 0 : float(_currentExtent.height - y)
547 : }));
548 :
549 0 : _surfaceX = x;
550 0 : _surfaceY = y;
551 : }
552 :
553 0 : if (hasAxis) {
554 0 : auto &event = inputEvents.emplace_back(core::InputEventData({
555 : axisSource,
556 : core::InputEventName::Scroll,
557 : axisBtn,
558 0 : _activeModifiers,
559 0 : float(_surfaceX),
560 0 : float(_currentExtent.height - _surfaceY)
561 : }));
562 :
563 0 : event.point.valueX = float(axisX);
564 0 : event.point.valueY = float(axisY);
565 : }
566 :
567 0 : for (auto &it : _pointerEvents) {
568 0 : switch (it.event) {
569 0 : case PointerEvent::None: break;
570 0 : case PointerEvent::Enter: break;
571 0 : case PointerEvent::Leave:
572 0 : inputEvents.emplace_back(core::InputEventData::BoolEvent(core::InputEventName::PointerEnter, false,
573 0 : Vec2(float(_surfaceX), float(_currentExtent.height - _surfaceY))));
574 0 : break;
575 0 : case PointerEvent::Motion: break;
576 0 : case PointerEvent::Button:
577 0 : inputEvents.emplace_back(core::InputEventData({
578 0 : it.button.button,
579 0 : ((it.button.state == WL_POINTER_BUTTON_STATE_PRESSED) ? core::InputEventName::Begin : core::InputEventName::End),
580 0 : getButton(it.button.button),
581 0 : _activeModifiers,
582 0 : float(_surfaceX),
583 0 : float(_currentExtent.height - _surfaceY)
584 : }));
585 0 : break;
586 0 : case PointerEvent::Axis: break;
587 0 : case PointerEvent::AxisSource: break;
588 0 : case PointerEvent::AxisStop: break;
589 0 : case PointerEvent::AxisDiscrete: break;
590 : }
591 : }
592 :
593 0 : if (!inputEvents.empty()) {
594 0 : _view->handleInputEvents(move(inputEvents));
595 : }
596 0 : _pointerEvents.clear();
597 0 : }
598 :
599 0 : void WaylandView::handleKeyboardEnter(Vector<uint32_t> &&keys, uint32_t depressed, uint32_t latched, uint32_t locked) {
600 0 : handleKeyModifiers(depressed, latched, locked);
601 0 : uint32_t n = 1;
602 0 : for (auto &it : keys) {
603 0 : handleKey(n, it, WL_KEYBOARD_KEY_STATE_PRESSED);
604 0 : ++ n;
605 : }
606 0 : }
607 :
608 0 : void WaylandView::handleKeyboardLeave() {
609 0 : Vector<core::InputEventData> events;
610 0 : uint32_t n = 1;
611 0 : for (auto &it : _keys) {
612 0 : core::InputEventData event({
613 : n,
614 : core::InputEventName::KeyCanceled,
615 : core::InputMouseButton::None,
616 0 : _activeModifiers,
617 0 : float(_surfaceX),
618 0 : float(_currentExtent.height - _surfaceY)
619 0 : });
620 :
621 0 : event.key.keycode = _display->seat->translateKey(it.second.scancode);
622 0 : event.key.keysym = it.second.scancode;
623 0 : event.key.keychar = it.second.codepoint;
624 :
625 0 : events.emplace_back(move(event));
626 :
627 0 : ++ n;
628 : }
629 :
630 0 : if (!events.empty()) {
631 0 : _view->handleInputEvents(move(events));
632 : }
633 0 : }
634 :
635 0 : void WaylandView::handleKey(uint32_t time, uint32_t scancode, uint32_t state) {
636 0 : core::InputEventData event({
637 : time,
638 0 : (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? core::InputEventName::KeyPressed : core::InputEventName::KeyReleased,
639 : core::InputMouseButton::None,
640 0 : _activeModifiers,
641 0 : float(_surfaceX),
642 0 : float(_currentExtent.height - _surfaceY)
643 0 : });
644 :
645 0 : event.key.keycode = _display->seat->translateKey(scancode);
646 0 : event.key.compose = core::InputKeyComposeState::Nothing;
647 0 : event.key.keysym = scancode;
648 0 : event.key.keychar = 0;
649 :
650 0 : const xkb_keysym_t *keysyms = nullptr;
651 0 : const xkb_keycode_t keycode = scancode + 8;
652 :
653 0 : if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
654 0 : char32_t codepoint = 0;
655 0 : if (_display->xkb && _view->isInputEnabled()) {
656 0 : if (_display->xkb->xkb_state_key_get_syms(_display->seat->state, keycode, &keysyms) == 1) {
657 0 : const xkb_keysym_t keysym = _display->seat->composeSymbol(keysyms[0], event.key.compose);
658 0 : const uint32_t cp = _display->xkb->xkb_keysym_to_utf32(keysym);
659 0 : if (cp != 0 && keysym != XKB_KEY_NoSymbol) {
660 0 : codepoint = cp;
661 : }
662 : }
663 : }
664 :
665 0 : auto it = _keys.emplace(scancode, KeyData{
666 : scancode,
667 : codepoint,
668 0 : platform::clock(core::ClockType::Monotonic),
669 : false
670 0 : }).first;
671 :
672 0 : if (_display->xkb && _display->xkb->xkb_keymap_key_repeats(
673 0 : _display->xkb->xkb_state_get_keymap(_display->seat->state), keycode)) {
674 0 : it->second.repeats = true;
675 : }
676 : } else {
677 0 : auto it = _keys.find(scancode);
678 0 : if (it == _keys.end()) {
679 0 : return;
680 : }
681 :
682 0 : event.key.keychar = it->second.codepoint;
683 0 : _keys.erase(it);
684 : }
685 :
686 0 : _view->handleInputEvent(move(event));
687 : }
688 :
689 0 : void WaylandView::handleKeyModifiers(uint32_t depressed, uint32_t latched, uint32_t locked) {
690 0 : if (!_display->seat->state) {
691 0 : return;
692 : }
693 :
694 0 : _activeModifiers = core::InputModifier::None;
695 0 : if (_display->xkb->xkb_state_mod_index_is_active(_display->seat->state,
696 0 : _display->seat->keyState.controlIndex, XKB_STATE_MODS_EFFECTIVE) == 1) {
697 0 : _activeModifiers |= core::InputModifier::Ctrl;
698 : }
699 :
700 0 : if (_display->xkb->xkb_state_mod_index_is_active(_display->seat->state,
701 0 : _display->seat->keyState.altIndex, XKB_STATE_MODS_EFFECTIVE) == 1) {
702 0 : _activeModifiers |= core::InputModifier::Alt;
703 : }
704 :
705 0 : if (_display->xkb->xkb_state_mod_index_is_active(_display->seat->state,
706 0 : _display->seat->keyState.shiftIndex, XKB_STATE_MODS_EFFECTIVE) == 1) {
707 0 : _activeModifiers |= core::InputModifier::Shift;
708 : }
709 :
710 0 : if (_display->xkb->xkb_state_mod_index_is_active(_display->seat->state,
711 0 : _display->seat->keyState.superIndex, XKB_STATE_MODS_EFFECTIVE) == 1) {
712 0 : _activeModifiers |= core::InputModifier::Mod4;
713 : }
714 :
715 0 : if (_display->xkb->xkb_state_mod_index_is_active(_display->seat->state,
716 0 : _display->seat->keyState.capsLockIndex, XKB_STATE_MODS_EFFECTIVE) == 1) {
717 0 : _activeModifiers |= core::InputModifier::CapsLock;
718 : }
719 :
720 0 : if (_display->xkb->xkb_state_mod_index_is_active(_display->seat->state,
721 0 : _display->seat->keyState.numLockIndex, XKB_STATE_MODS_EFFECTIVE) == 1) {
722 0 : _activeModifiers |= core::InputModifier::NumLock;
723 : }
724 : }
725 :
726 0 : void WaylandView::handleKeyRepeat() {
727 0 : Vector<core::InputEventData> events;
728 0 : auto spawnRepeatEvent = [&, this] (const KeyData &it) {
729 0 : core::InputEventData event({
730 0 : uint32_t(events.size() + 1),
731 : core::InputEventName::KeyRepeated,
732 : core::InputMouseButton::None,
733 0 : _activeModifiers,
734 0 : float(_surfaceX),
735 0 : float(_currentExtent.height - _surfaceY)
736 0 : });
737 :
738 0 : event.key.keycode = _display->seat->translateKey(it.scancode);
739 0 : event.key.keysym = it.scancode;
740 0 : event.key.keychar = it.codepoint;
741 :
742 0 : events.emplace_back(move(event));
743 0 : };
744 :
745 0 : uint64_t repeatDelay = _display->seat->keyState.keyRepeatDelay;
746 0 : uint64_t repeatInterval = _display->seat->keyState.keyRepeatInterval;
747 0 : auto t = platform::clock(core::ClockType::Monotonic);
748 0 : for (auto &it : _keys) {
749 0 : if (it.second.repeats) {
750 0 : if (!it.second.lastRepeat) {
751 0 : auto dt = t - it.second.time;
752 0 : if (dt > repeatDelay * 1000) {
753 0 : dt -= repeatDelay * 1000;
754 0 : it.second.lastRepeat = t - dt;
755 : }
756 : }
757 0 : if (it.second.lastRepeat) {
758 0 : auto dt = t - it.second.lastRepeat;
759 0 : while (dt > repeatInterval) {
760 0 : spawnRepeatEvent(it.second);
761 0 : dt -= repeatInterval;
762 0 : it.second.lastRepeat += repeatInterval;
763 : }
764 : }
765 : }
766 : }
767 :
768 0 : if (!events.empty()) {
769 0 : _view->handleInputEvents(move(events));
770 : }
771 0 : }
772 :
773 0 : void WaylandView::handleDecorationPress(WaylandDecoration *decor, uint32_t serial, bool released) {
774 0 : auto switchMaximized = [&, this] {
775 0 : if (!_state.test(XDG_TOPLEVEL_STATE_MAXIMIZED)) {
776 0 : _display->wayland->xdg_toplevel_set_maximized(_toplevel);
777 0 : _iconMaximized->setAlternative(true);
778 : } else {
779 0 : _display->wayland->xdg_toplevel_unset_maximized(_toplevel);
780 0 : _iconMaximized->setAlternative(false);
781 : }
782 0 : };
783 0 : switch (decor->name) {
784 0 : case WaylandDecorationName::IconClose:
785 0 : _shouldClose = true;
786 0 : return;
787 : break;
788 0 : case WaylandDecorationName::IconMaximize:
789 0 : switchMaximized();
790 0 : return;
791 : break;
792 0 : case WaylandDecorationName::IconMinimize:
793 0 : _display->wayland->xdg_toplevel_set_minimized(_toplevel);
794 0 : return;
795 0 : default:
796 0 : break;
797 : }
798 0 : uint32_t edges = 0;
799 0 : switch (decor->image) {
800 0 : case WaylandCursorImage::RightSide: edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break;
801 0 : case WaylandCursorImage::TopRigntCorner: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; break;
802 0 : case WaylandCursorImage::TopSide: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; break;
803 0 : case WaylandCursorImage::TopLeftCorner: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; break;
804 0 : case WaylandCursorImage::BottomRightCorner: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; break;
805 0 : case WaylandCursorImage::BottomSide: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; break;
806 0 : case WaylandCursorImage::BottomLeftCorner: edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; break;
807 0 : case WaylandCursorImage::LeftSide: edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break;
808 0 : case WaylandCursorImage::LeftPtr:
809 0 : if (released) {
810 0 : switchMaximized();
811 0 : return;
812 : }
813 0 : break;
814 0 : case WaylandCursorImage::Max:
815 0 : break;
816 : }
817 :
818 0 : if (edges != 0) {
819 0 : _display->wayland->xdg_toplevel_resize(_toplevel, _display->seat->seat, serial, edges);
820 : } else {
821 0 : _display->wayland->xdg_toplevel_move(_toplevel, _display->seat->seat, serial);
822 : }
823 : }
824 :
825 0 : void WaylandView::scheduleFrame() {
826 0 : _scheduleNext = true;
827 0 : }
828 :
829 0 : void WaylandView::onSurfaceInfo(core::SurfaceInfo &info) const {
830 0 : info.currentExtent = _currentExtent;
831 0 : }
832 :
833 0 : void WaylandView::createDecorations() {
834 0 : if (!_display->viewporter || !_clientSizeDecoration) {
835 0 : return;
836 : }
837 :
838 0 : WaylandShm::ShadowBuffers buf;
839 0 : if (!_display->shm->allocateDecorations(&buf, DecorWidth, DecorInset, Color::Grey_100, Color::Grey_200)) {
840 0 : return;
841 : }
842 :
843 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.top), move(buf.topActive), WaylandDecorationName::TopSide));
844 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.bottom), move(buf.bottomActive), WaylandDecorationName::BottomSide));
845 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.left), move(buf.leftActive), WaylandDecorationName::LeftSide));
846 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.right), move(buf.rightActive), WaylandDecorationName::RightSide));
847 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.topLeft), move(buf.topLeftActive), WaylandDecorationName::TopLeftCorner));
848 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.topRight), move(buf.topRightActive), WaylandDecorationName::TopRigntCorner));
849 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.bottomLeft), move(buf.bottomLeftActive), WaylandDecorationName::BottomLeftCorner));
850 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.bottomRight), move(buf.bottomRightActive), WaylandDecorationName::BottomRightCorner));
851 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.headerLeft), move(buf.headerLeftActive), WaylandDecorationName::HeaderLeft));
852 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.headerRight), move(buf.headerRightActive), WaylandDecorationName::HeaderRight));
853 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, Rc<WaylandBuffer>(buf.headerCenter), Rc<WaylandBuffer>(buf.headerCenterActive), WaylandDecorationName::HeaderCenter));
854 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, Rc<WaylandBuffer>(buf.headerCenter), Rc<WaylandBuffer>(buf.headerCenterActive), WaylandDecorationName::HeaderBottom));
855 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.iconClose), move(buf.iconCloseActive), WaylandDecorationName::IconClose));
856 0 : _iconMaximized = _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.iconMaximize), move(buf.iconMaximizeActive), WaylandDecorationName::IconMaximize));
857 0 : _iconMaximized->setAltBuffers(move(buf.iconRestore), move(buf.iconRestoreActive));
858 :
859 0 : _decors.emplace_back(Rc<WaylandDecoration>::create(this, move(buf.iconMinimize), move(buf.iconMinimizeActive), WaylandDecorationName::IconMinimize));
860 0 : }
861 :
862 0 : void WaylandView::commit(uint32_t width, uint32_t height) {
863 0 : bool dirty = _commitedExtent.width != width || _commitedExtent.height != height || _configureSerial != maxOf<uint32_t>();
864 :
865 0 : if (!dirty) {
866 0 : for (auto &it : _decors) {
867 0 : if (it->dirty) {
868 0 : dirty = true;
869 0 : break;
870 : }
871 : }
872 : }
873 :
874 0 : if (!dirty) {
875 0 : return;
876 : }
877 :
878 0 : StringStream stream;
879 0 : stream << "commit: " << width << " " << height << ";";
880 0 : if (_configureSerial != maxOf<uint32_t>()) {
881 0 : _display->wayland->xdg_toplevel_set_min_size(_toplevel, DecorWidth * 2 + IconSize * 3, DecorWidth * 2 + DecorOffset);
882 0 : _display->wayland->xdg_surface_set_window_geometry(_xdgSurface, 0, -DecorInset - DecorOffset,
883 0 : width, height + DecorInset + DecorOffset);
884 :
885 0 : _display->wayland->xdg_surface_ack_configure(_xdgSurface, _configureSerial);
886 0 : stream << " configure: " << _configureSerial << ";";
887 0 : _configureSerial = maxOf<uint32_t>();
888 : }
889 :
890 0 : _commitedExtent.width = width;
891 0 : _commitedExtent.height = height;
892 :
893 0 : auto insetWidth = _commitedExtent.width - DecorInset * 2;
894 0 : auto insetHeight = _commitedExtent.height - DecorInset;
895 0 : auto cornerSize = DecorWidth + DecorInset;
896 :
897 0 : for (auto &it : _decors) {
898 0 : switch (it->name) {
899 0 : case WaylandDecorationName::TopSide:
900 0 : it->setGeometry(DecorInset, - DecorWidth - DecorInset, insetWidth, DecorWidth);
901 0 : break;
902 0 : case WaylandDecorationName::BottomSide:
903 0 : it->setGeometry(DecorInset, _commitedExtent.height, insetWidth, DecorWidth);
904 0 : break;
905 0 : case WaylandDecorationName::LeftSide:
906 0 : it->setGeometry(- DecorWidth, 0, DecorWidth, insetHeight);
907 0 : break;
908 0 : case WaylandDecorationName::RightSide:
909 0 : it->setGeometry(_commitedExtent.width, 0, DecorWidth, insetHeight);
910 0 : break;
911 0 : case WaylandDecorationName::TopLeftCorner:
912 0 : it->setGeometry(- DecorWidth, - DecorWidth - DecorInset, cornerSize, cornerSize);
913 0 : break;
914 0 : case WaylandDecorationName::TopRigntCorner:
915 0 : it->setGeometry(_commitedExtent.width - DecorInset, - DecorWidth - DecorInset, cornerSize, cornerSize);
916 0 : break;
917 0 : case WaylandDecorationName::BottomLeftCorner:
918 0 : it->setGeometry(- DecorWidth, _commitedExtent.height - DecorInset, cornerSize, cornerSize);
919 0 : break;
920 0 : case WaylandDecorationName::BottomRightCorner:
921 0 : it->setGeometry(_commitedExtent.width - DecorInset, _commitedExtent.height - DecorInset, cornerSize, cornerSize);
922 0 : break;
923 0 : case WaylandDecorationName::HeaderLeft:
924 0 : it->setGeometry(0, - DecorInset - DecorOffset, DecorInset, DecorInset);
925 0 : break;
926 0 : case WaylandDecorationName::HeaderRight:
927 0 : it->setGeometry(_commitedExtent.width - DecorInset, - DecorInset - DecorOffset, DecorInset, DecorInset);
928 0 : break;
929 0 : case WaylandDecorationName::HeaderCenter:
930 0 : it->setGeometry(DecorInset, - DecorInset - DecorOffset, _commitedExtent.width - DecorInset * 2, DecorInset);
931 0 : break;
932 0 : case WaylandDecorationName::HeaderBottom:
933 0 : it->setGeometry(0, - DecorOffset, _commitedExtent.width, DecorOffset);
934 0 : break;
935 0 : case WaylandDecorationName::IconClose:
936 0 : it->setGeometry(_commitedExtent.width - (IconSize + 4), -IconSize, IconSize, IconSize);
937 0 : break;
938 0 : case WaylandDecorationName::IconMaximize:
939 0 : it->setGeometry(_commitedExtent.width - (IconSize + 4) * 2, -IconSize, IconSize, IconSize);
940 0 : break;
941 0 : case WaylandDecorationName::IconMinimize:
942 0 : it->setGeometry(_commitedExtent.width - (IconSize + 4) * 3, -IconSize, IconSize, IconSize);
943 0 : break;
944 0 : default:
945 0 : break;
946 : }
947 : }
948 :
949 0 : bool surfacesDirty = false;
950 0 : for (auto &it : _decors) {
951 0 : if (it->commit()) {
952 0 : surfacesDirty = true;
953 : }
954 : }
955 0 : if (surfacesDirty) {
956 0 : stream << " Surfaces Dirty;";
957 : }
958 :
959 : XL_WAYLAND_LOG(stream.str());
960 0 : }
961 :
962 0 : void WaylandView::readFromClipboard(Function<void(BytesView, StringView)> &&, Ref *) {
963 :
964 0 : }
965 :
966 0 : void WaylandView::writeToClipboard(BytesView, StringView contentType) {
967 :
968 0 : }
969 :
970 : }
|