LCOV - code coverage report
Current view: top level - xenolith/backend/vkgui/platform/linux - XLVkGuiViewImpl.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 139 176 79.0 %
Date: 2024-05-12 00:16:13 Functions: 23 33 69.7 %

          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

Generated by: LCOV version 1.14