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 "XLVkGuiViewImpl.h"
24 :
25 : #if LINUX
26 :
27 : #include "linux/XLPlatformLinuxWaylandView.h"
28 : #include "linux/XLPlatformLinuxXcbView.h"
29 : #include "XLTextInputManager.h"
30 : #include "XLVkPlatform.h"
31 : #include "SPPlatformUnistd.h"
32 :
33 : #include <sys/eventfd.h>
34 : #include <poll.h>
35 :
36 : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk::platform {
37 :
38 10 : ViewImpl::ViewImpl() { }
39 :
40 20 : ViewImpl::~ViewImpl() {
41 10 : _view = nullptr;
42 20 : }
43 :
44 10 : bool ViewImpl::init(Application &loop, const core::Device &dev, ViewInfo &&info) {
45 10 : _constraints.density = info.density;
46 :
47 10 : if (!vk::View::init(loop, static_cast<const vk::Device &>(dev), move(info))) {
48 0 : return false;
49 : }
50 :
51 10 : return true;
52 : }
53 :
54 : #if XL_ENABLE_WAYLAND
55 : static VkSurfaceKHR createWindowSurface(xenolith::platform::WaylandView *v, vk::Instance *instance, VkPhysicalDevice dev) {
56 : auto display = v->getDisplay();
57 : auto surface = v->getSurface();
58 :
59 : std::cout << "Create wayland surface for " << (void *)dev << " on " << (void *)display->display << "\n";
60 : auto supports = instance->vkGetPhysicalDeviceWaylandPresentationSupportKHR(dev, 0, display->display);
61 : if (!supports) {
62 : return nullptr;
63 : }
64 :
65 : VkSurfaceKHR ret;
66 : VkWaylandSurfaceCreateInfoKHR info{
67 : VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
68 : nullptr,
69 : 0,
70 : display->display,
71 : surface
72 : };
73 :
74 : if (instance->vkCreateWaylandSurfaceKHR(instance->getInstance(), &info, nullptr, &ret) == VK_SUCCESS) {
75 : VkBool32 pSupported = 0;
76 : instance->vkGetPhysicalDeviceSurfaceSupportKHR(dev, 0, ret, &pSupported);
77 : if (!pSupported) {
78 : instance->vkDestroySurfaceKHR(instance->getInstance(), ret, nullptr);
79 : return nullptr;
80 : }
81 : return ret;
82 : }
83 : return nullptr;
84 : }
85 : #endif
86 :
87 10 : static VkSurfaceKHR createWindowSurface(xenolith::platform::XcbView *v, vk::Instance *instance, VkPhysicalDevice dev) {
88 10 : auto connection = v->getConnection();
89 10 : auto window = v->getWindow();
90 :
91 10 : VkSurfaceKHR surface = VK_NULL_HANDLE;
92 10 : VkXcbSurfaceCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, nullptr, 0, connection, window};
93 10 : if (instance->vkCreateXcbSurfaceKHR(instance->getInstance(), &createInfo, nullptr, &surface) != VK_SUCCESS) {
94 0 : return nullptr;
95 : }
96 10 : return surface;
97 : }
98 :
99 10 : void ViewImpl::threadInit() {
100 10 : thread::ThreadInfo::setThreadInfo(_threadName);
101 10 : _threadId = std::this_thread::get_id();
102 :
103 10 : auto presentMask = _device->getPresentatonMask();
104 :
105 : #if XL_ENABLE_WAYLAND
106 : if (auto wayland = xenolith::platform::WaylandLibrary::getInstance()) {
107 : if ((platform::SurfaceType(presentMask) & platform::SurfaceType::Wayland) != platform::SurfaceType::None) {
108 : auto waylandDisplay = getenv("WAYLAND_DISPLAY");
109 : auto sessionType = getenv("XDG_SESSION_TYPE");
110 :
111 : if (waylandDisplay || strcasecmp("wayland", sessionType) == 0) {
112 : auto view = Rc<xenolith::platform::WaylandView>::alloc(wayland, this, _info.name, _info.bundleId, _info.rect);
113 : if (!view) {
114 : log::error("VkView", "Fail to initialize wayland window");
115 : return;
116 : }
117 :
118 : _view = view;
119 : _surface = Rc<vk::Surface>::create(_instance,
120 : createWindowSurface(view, _instance, _device->getPhysicalDevice()), _view);
121 : if (_surface) {
122 : setFrameInterval(_view->getScreenFrameInterval());
123 : } else {
124 : _view = nullptr;
125 : }
126 : }
127 : }
128 : }
129 : #endif
130 :
131 10 : if (!_view) {
132 : // try X11
133 10 : if (auto xcb = xenolith::platform::XcbLibrary::getInstance()) {
134 10 : if ((platform::SurfaceType(presentMask) & platform::SurfaceType::XCB) != platform::SurfaceType::None) {
135 10 : auto view = Rc<xenolith::platform::XcbView>::alloc(xcb, this, _info.name, _info.bundleId, _info.rect);
136 10 : if (!view) {
137 0 : log::error("VkView", "Fail to initialize xcb window");
138 0 : return;
139 : }
140 :
141 10 : _view = view;
142 10 : _surface = Rc<vk::Surface>::create(_instance,
143 10 : createWindowSurface(view, _instance, _device->getPhysicalDevice()), _view);
144 10 : setFrameInterval(_view->getScreenFrameInterval());
145 10 : }
146 : }
147 : }
148 :
149 10 : if (!_view) {
150 0 : log::error("View", "No available surface type");
151 : }
152 :
153 10 : View::threadInit();
154 : }
155 :
156 10 : void ViewImpl::threadDispose() {
157 10 : View::threadDispose();
158 10 : }
159 :
160 10 : bool ViewImpl::worker() {
161 10 : _eventFd = eventfd(0, EFD_NONBLOCK);
162 10 : auto socket = _view->getSocketFd();
163 :
164 : timespec timeoutMin;
165 10 : timeoutMin.tv_sec = 0;
166 10 : timeoutMin.tv_nsec = 1000 * 250;
167 :
168 : struct pollfd fds[2];
169 :
170 10 : fds[0].fd = _eventFd;
171 10 : fds[0].events = POLLIN;
172 10 : fds[0].revents = 0;
173 :
174 10 : fds[1].fd = socket;
175 10 : fds[1].events = POLLIN;
176 10 : fds[1].revents = 0;
177 :
178 10 : update(false);
179 :
180 434786 : while (_shouldQuit.test_and_set()) {
181 217383 : bool shouldUpdate = false;
182 :
183 217383 : int ret = ::ppoll( fds, 2, &timeoutMin, nullptr);
184 217383 : if (ret > 0) {
185 17024 : if (fds[0].revents != 0) {
186 16797 : uint64_t value = 0;
187 16797 : auto sz = ::read(_eventFd, &value, sizeof(uint64_t));
188 16797 : if (sz == 8 && value) {
189 16797 : shouldUpdate = true;
190 : }
191 16797 : fds[0].revents = 0;
192 : }
193 17024 : if (fds[1].revents != 0) {
194 276 : fds[1].revents = 0;
195 276 : if (!_view->poll(false)) {
196 0 : break;
197 : }
198 : }
199 200359 : } else if (ret == 0) {
200 200359 : shouldUpdate = true;
201 0 : } else if (errno != EINTR) {
202 : // ignore EINTR to allow debugging
203 0 : break;
204 : }
205 :
206 217383 : if (shouldUpdate) {
207 217156 : update(false);
208 : }
209 : }
210 :
211 10 : if (_swapchain) {
212 10 : _swapchain->deprecate(false);
213 : }
214 :
215 10 : ::close(_eventFd);
216 10 : return false;
217 : }
218 :
219 19949 : void ViewImpl::wakeup(std::unique_lock<Mutex> &) {
220 19949 : if (_eventFd >= 0) {
221 19949 : uint64_t value = 1;
222 19949 : ::write(_eventFd, (const void *)&value, sizeof(uint64_t));
223 : }
224 19949 : }
225 :
226 0 : void ViewImpl::updateTextCursor(uint32_t pos, uint32_t len) {
227 :
228 0 : }
229 :
230 0 : void ViewImpl::updateTextInput(WideStringView str, uint32_t pos, uint32_t len, TextInputType) {
231 :
232 0 : }
233 :
234 10 : void ViewImpl::runTextInput(WideStringView str, uint32_t pos, uint32_t len, TextInputType) {
235 10 : performOnThread([this] {
236 10 : _inputEnabled = true;
237 10 : _mainLoop->performOnMainThread([this] () {
238 10 : _director->getTextInputManager()->setInputEnabled(true);
239 10 : }, this);
240 10 : }, this);
241 10 : }
242 :
243 20 : void ViewImpl::cancelTextInput() {
244 20 : performOnThread([this] {
245 0 : _inputEnabled = false;
246 0 : _mainLoop->performOnMainThread([this] () {
247 0 : _director->getTextInputManager()->setInputEnabled(false);
248 0 : }, this);
249 0 : }, this);
250 20 : }
251 :
252 4630 : void ViewImpl::presentWithQueue(vk::DeviceQueue &queue, Rc<ImageStorage> &&image) {
253 4630 : auto &e = _swapchain->getImageInfo().extent;
254 4630 : _view->commit(e.width, e.height);
255 4630 : vk::View::presentWithQueue(queue, move(image));
256 4630 : }
257 :
258 4630 : bool ViewImpl::pollInput(bool frameReady) {
259 4630 : if (!_view->poll(frameReady)) {
260 0 : close();
261 0 : return false;
262 : }
263 4630 : return true;
264 : }
265 :
266 20 : core::SurfaceInfo ViewImpl::getSurfaceOptions() const {
267 20 : core::SurfaceInfo ret = vk::View::getSurfaceOptions();
268 20 : _view->onSurfaceInfo(ret);
269 20 : return ret;
270 0 : }
271 :
272 10 : void ViewImpl::mapWindow() {
273 10 : if (_view) {
274 10 : _view->mapWindow();
275 : }
276 10 : View::mapWindow();
277 10 : }
278 :
279 0 : void ViewImpl::readFromClipboard(Function<void(BytesView, StringView)> &&cb, Ref *ref) {
280 0 : performOnThread([this, cb = move(cb), ref = Rc<Ref>(ref)] () mutable {
281 0 : _view->readFromClipboard([this, cb = move(cb)] (BytesView view, StringView ct) mutable {
282 0 : _mainLoop->performOnMainThread([cb = move(cb), view = view.bytes<Interface>(), ct = ct.str<Interface>()] () {
283 0 : cb(view, ct);
284 0 : }, this);
285 0 : }, ref);
286 0 : }, this);
287 0 : }
288 :
289 0 : void ViewImpl::writeToClipboard(BytesView data, StringView contentType) {
290 0 : performOnThread([this, data = data.bytes<Interface>(), contentType = contentType.str<Interface>()] {
291 0 : _view->writeToClipboard(data, contentType);
292 0 : }, this);
293 0 : }
294 :
295 10 : void ViewImpl::finalize() {
296 10 : _view = nullptr;
297 10 : View::finalize();
298 10 : }
299 :
300 10 : Rc<vk::View> createView(Application &loop, const core::Device &dev, ViewInfo &&info) {
301 20 : return Rc<ViewImpl>::create(loop, dev, move(info));
302 : }
303 :
304 : struct InstanceSurfaceData : Ref {
305 20 : virtual ~InstanceSurfaceData() { }
306 :
307 : SurfaceType surfaceType = SurfaceType::None;
308 : #if XL_ENABLE_WAYLAND
309 : Rc<xenolith::platform::WaylandLibrary> wayland;
310 : #endif
311 : Rc<xenolith::platform::XcbLibrary> xcb;
312 : };
313 :
314 160 : uint32_t checkPresentationSupport(const vk::Instance *instance, VkPhysicalDevice device, uint32_t queueIdx) {
315 160 : InstanceSurfaceData *instanceData = (InstanceSurfaceData *)instance->getUserdata();
316 :
317 160 : uint32_t ret = 0;
318 160 : if ((instanceData->surfaceType & SurfaceType::Wayland) != SurfaceType::None) {
319 : #if XL_ENABLE_WAYLAND
320 : auto display = xenolith::platform::WaylandLibrary::getInstance()->getActiveConnection().display;
321 : std::cout << "Check if " << (void *)device << " [" << queueIdx << "] supports wayland on " << (void *)display << ": ";
322 : auto supports = instance->vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, queueIdx, display);
323 : if (supports) {
324 : ret |= toInt(SurfaceType::Wayland);
325 : std::cout << "yes\n";
326 : } else {
327 : std::cout << "no\n";
328 : }
329 : #endif
330 : }
331 160 : if ((instanceData->surfaceType & SurfaceType::XCB) != SurfaceType::None) {
332 160 : auto conn = xenolith::platform::XcbLibrary::getInstance()->getActiveConnection();
333 160 : auto supports = instance->vkGetPhysicalDeviceXcbPresentationSupportKHR(device, queueIdx, conn.connection, conn.screen->root_visual);
334 160 : if (supports) {
335 100 : ret |= toInt(SurfaceType::XCB);
336 : }
337 : }
338 160 : return ret;
339 : }
340 :
341 10 : bool initInstance(vk::platform::VulkanInstanceData &data, const vk::platform::VulkanInstanceInfo &info) {
342 10 : auto instanceData = Rc<InstanceSurfaceData>::alloc();
343 :
344 10 : SurfaceType osSurfaceType = SurfaceType::None;
345 10 : instanceData->xcb = Rc<xenolith::platform::XcbLibrary>::create();
346 10 : if (instanceData->xcb) {
347 10 : osSurfaceType |= SurfaceType::XCB;
348 : }
349 :
350 : #if XL_ENABLE_WAYLAND
351 : instanceData->wayland = Rc<xenolith::platform::WaylandLibrary>::create();
352 : if (instanceData->wayland) {
353 : osSurfaceType |= SurfaceType::Wayland;
354 : }
355 : #endif
356 :
357 10 : const char *surfaceExt = nullptr;
358 210 : for (auto &extension : info.availableExtensions) {
359 200 : if (strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extension.extensionName) == 0) {
360 10 : surfaceExt = extension.extensionName;
361 10 : data.extensionsToEnable.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
362 380 : } else if (strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, extension.extensionName) == 0
363 190 : && (osSurfaceType & SurfaceType::XCB) != SurfaceType::None) {
364 10 : instanceData->surfaceType |= SurfaceType::XCB;
365 10 : data.extensionsToEnable.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
366 360 : } else if (strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, extension.extensionName) == 0
367 180 : && (osSurfaceType & SurfaceType::Wayland) != SurfaceType::None) {
368 0 : instanceData->surfaceType |= SurfaceType::Wayland;
369 0 : data.extensionsToEnable.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
370 : }
371 : }
372 :
373 10 : if (instanceData->surfaceType != SurfaceType::None && surfaceExt) {
374 10 : data.checkPresentationSupport = checkPresentationSupport;
375 10 : data.userdata = instanceData;
376 10 : return true;
377 : }
378 :
379 0 : return false;
380 10 : }
381 :
382 : }
383 :
384 : #endif
|