2026-01-27 22:34:24 +01:00
/*
Crafter ® . Graphics
Copyright ( C ) 2025 Catcrafts ®
catcrafts . net
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation ;
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
module ;
# include <assert.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <linux/input-event-codes.h>
# include "../lib/xdg-shell-client-protocol.h"
# include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
# include "../lib/fractional-scale-v1.h"
# include "../lib/viewporter.h"
# include <string.h>
# include <linux/input.h>
# include <sys/mman.h>
# include <wayland-cursor.h>
# include <xkbcommon/xkbcommon.h>
# include <errno.h>
# include <fcntl.h>
# include <print>
# include <wayland-client.h>
# include <wayland-client-protocol.h>
# include <vulkan/vulkan.h>
# include <vulkan/vulkan_wayland.h>
module Crafter . Graphics : Window_vulkan_impl ;
import : Window ;
import : MouseElement ;
import std ;
import : Types ;
import : Shm ;
import : VulkanDevice ;
import : VulkanTransition ;
import Crafter . Event ;
using namespace Crafter ;
void WindowVulkan : : CreateSwapchain ( )
{
// Store the current swap chain handle so we can use it later on to ease up recreation
VkSwapchainKHR oldSwapchain = swapChain ;
// Get physical device surface properties and formats
VkSurfaceCapabilitiesKHR surfCaps ;
VulkanDevice : : CheckVkResult ( vkGetPhysicalDeviceSurfaceCapabilitiesKHR ( VulkanDevice : : physDevice , vulkanSurface , & surfCaps ) ) ;
VkExtent2D swapchainExtent = { } ;
// If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain
if ( surfCaps . currentExtent . width = = ( uint32_t ) - 1 )
{
// If the surface size is undefined, the size is set to the size of the images requested
swapchainExtent . width = width ;
swapchainExtent . height = height ;
}
else
{
// If the surface size is defined, the swap chain size must match
swapchainExtent = surfCaps . currentExtent ;
width = surfCaps . currentExtent . width ;
height = surfCaps . currentExtent . height ;
}
// Select a present mode for the swapchain
uint32_t presentModeCount ;
VulkanDevice : : CheckVkResult ( vkGetPhysicalDeviceSurfacePresentModesKHR ( VulkanDevice : : physDevice , vulkanSurface , & presentModeCount , NULL ) ) ;
assert ( presentModeCount > 0 ) ;
std : : vector < VkPresentModeKHR > presentModes ( presentModeCount ) ;
VulkanDevice : : CheckVkResult ( vkGetPhysicalDeviceSurfacePresentModesKHR ( VulkanDevice : : physDevice , vulkanSurface , & presentModeCount , presentModes . data ( ) ) ) ;
// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
// This mode waits for the vertical blank ("v-sync")
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR ;
// Determine the number of images
uint32_t desiredNumberOfSwapchainImages = surfCaps . minImageCount + 1 ;
if ( ( surfCaps . maxImageCount > 0 ) & & ( desiredNumberOfSwapchainImages > surfCaps . maxImageCount ) )
{
desiredNumberOfSwapchainImages = surfCaps . maxImageCount ;
}
// Find the transformation of the surface
VkSurfaceTransformFlagsKHR preTransform ;
if ( surfCaps . supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR )
{
// We prefer a non-rotated transform
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ;
}
else
{
preTransform = surfCaps . currentTransform ;
}
// Find a supported composite alpha format (not all devices support alpha opaque)
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR ;
// Simply select the first composite alpha format available
std : : vector < VkCompositeAlphaFlagBitsKHR > compositeAlphaFlags = {
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR ,
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR ,
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR ,
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR ,
} ;
for ( auto & compositeAlphaFlag : compositeAlphaFlags ) {
if ( surfCaps . supportedCompositeAlpha & compositeAlphaFlag ) {
compositeAlpha = compositeAlphaFlag ;
break ;
} ;
}
VkSwapchainCreateInfoKHR swapchainCI = { } ;
swapchainCI . sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR ;
swapchainCI . surface = vulkanSurface ;
swapchainCI . minImageCount = desiredNumberOfSwapchainImages ;
swapchainCI . imageFormat = colorFormat ;
swapchainCI . imageColorSpace = colorSpace ;
swapchainCI . imageExtent = { swapchainExtent . width , swapchainExtent . height } ;
swapchainCI . imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ;
swapchainCI . preTransform = ( VkSurfaceTransformFlagBitsKHR ) preTransform ;
swapchainCI . imageArrayLayers = 1 ;
swapchainCI . imageSharingMode = VK_SHARING_MODE_EXCLUSIVE ;
swapchainCI . queueFamilyIndexCount = 0 ;
swapchainCI . presentMode = swapchainPresentMode ;
// Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images
swapchainCI . oldSwapchain = oldSwapchain ;
// Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
swapchainCI . clipped = VK_TRUE ;
swapchainCI . compositeAlpha = compositeAlpha ;
// Enable transfer source on swap chain images if supported
if ( surfCaps . supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT ) {
2026-01-29 02:05:18 +01:00
swapchainCI . imageUsage | = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT ;
2026-01-27 22:34:24 +01:00
}
// Enable transfer destination on swap chain images if supported
if ( surfCaps . supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT ) {
2026-01-29 02:05:18 +01:00
swapchainCI . imageUsage | = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT ;
2026-01-27 22:34:24 +01:00
}
VulkanDevice : : CheckVkResult ( vkCreateSwapchainKHR ( VulkanDevice : : device , & swapchainCI , nullptr , & swapChain ) ) ;
// If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain)
if ( oldSwapchain ! = VK_NULL_HANDLE ) {
for ( auto i = 0 ; i < images . size ( ) ; i + + ) {
vkDestroyImageView ( VulkanDevice : : device , imageViews [ i ] , nullptr ) ;
}
vkDestroySwapchainKHR ( VulkanDevice : : device , oldSwapchain , nullptr ) ;
}
uint32_t imageCount { 0 } ;
VulkanDevice : : CheckVkResult ( vkGetSwapchainImagesKHR ( VulkanDevice : : device , swapChain , & imageCount , nullptr ) ) ;
// Get the swap chain images
images . resize ( imageCount ) ;
VulkanDevice : : CheckVkResult ( vkGetSwapchainImagesKHR ( VulkanDevice : : device , swapChain , & imageCount , images . data ( ) ) ) ;
// Get the swap chain buffers containing the image and imageview
imageViews . resize ( imageCount ) ;
for ( auto i = 0 ; i < images . size ( ) ; i + + )
{
VkImageViewCreateInfo colorAttachmentView = { } ;
colorAttachmentView . sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO ;
colorAttachmentView . pNext = NULL ;
colorAttachmentView . format = colorFormat ;
colorAttachmentView . components = {
VK_COMPONENT_SWIZZLE_R ,
VK_COMPONENT_SWIZZLE_G ,
VK_COMPONENT_SWIZZLE_B ,
VK_COMPONENT_SWIZZLE_A
} ;
colorAttachmentView . subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
colorAttachmentView . subresourceRange . baseMipLevel = 0 ;
colorAttachmentView . subresourceRange . levelCount = 1 ;
colorAttachmentView . subresourceRange . baseArrayLayer = 0 ;
colorAttachmentView . subresourceRange . layerCount = 1 ;
colorAttachmentView . viewType = VK_IMAGE_VIEW_TYPE_2D ;
colorAttachmentView . flags = 0 ;
colorAttachmentView . image = images [ i ] ;
VulkanDevice : : CheckVkResult ( vkCreateImageView ( VulkanDevice : : device , & colorAttachmentView , nullptr , & imageViews [ i ] ) ) ;
}
}
WindowVulkan : : WindowVulkan ( std : : uint32_t width , std : : uint32_t height ) : Window ( width , height ) {
2026-01-28 01:07:41 +01:00
2026-01-27 22:34:24 +01:00
display = wl_display_connect ( NULL ) ;
if ( display = = NULL ) {
std : : cerr < < " failed to create display " < < std : : endl ;
}
wl_registry * registry = wl_display_get_registry ( display ) ;
wl_registry_add_listener ( registry , & registry_listener , this ) ;
if ( wl_display_roundtrip ( display ) = = - 1 ) {
exit ( EXIT_FAILURE ) ;
}
if ( shm = = NULL | | compositor = = NULL | | xdgWmBase = = NULL ) {
std : : cerr < < " no wl_shm, wl_compositor or xdg_wm_base support " < < std : : endl ;
exit ( EXIT_FAILURE ) ;
}
surface = wl_compositor_create_surface ( compositor ) ;
xdgSurface = xdg_wm_base_get_xdg_surface ( xdgWmBase , surface ) ;
xdgToplevel = xdg_surface_get_toplevel ( xdgSurface ) ;
xdg_surface_add_listener ( xdgSurface , & xdg_surface_listener , this ) ;
xdg_toplevel_add_listener ( xdgToplevel , & xdg_toplevel_listener , this ) ;
wl_surface_commit ( surface ) ;
wp_scale = wp_fractional_scale_manager_v1_get_fractional_scale ( fractionalScaleManager , surface ) ;
wp_fractional_scale_v1_add_listener ( wp_scale , & wp_fractional_scale_v1_listener , this ) ;
while ( wl_display_dispatch ( display ) ! = - 1 & & ! configured ) { }
wl_surface_commit ( surface ) ;
zxdg_toplevel_decoration_v1 * decoration = zxdg_decoration_manager_v1_get_toplevel_decoration ( manager , xdgToplevel ) ;
zxdg_toplevel_decoration_v1_set_mode ( decoration , ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE ) ;
wpViewport = wp_viewporter_get_viewport ( wpViewporter , surface ) ;
wp_viewport_set_destination ( wpViewport , std : : ceil ( width / scale ) , std : : ceil ( height / scale ) ) ;
wl_surface_commit ( surface ) ;
VkWaylandSurfaceCreateInfoKHR createInfo = { } ;
createInfo . sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR ;
createInfo . display = display ;
createInfo . surface = surface ;
VulkanDevice : : CheckVkResult ( vkCreateWaylandSurfaceKHR ( VulkanDevice : : instance , & createInfo , NULL , & vulkanSurface ) ) ;
// Get list of supported surface formats
std : : uint32_t formatCount ;
VulkanDevice : : CheckVkResult ( vkGetPhysicalDeviceSurfaceFormatsKHR ( VulkanDevice : : physDevice , vulkanSurface , & formatCount , NULL ) ) ;
assert ( formatCount > 0 ) ;
std : : vector < VkSurfaceFormatKHR > surfaceFormats ( formatCount ) ;
VulkanDevice : : CheckVkResult ( vkGetPhysicalDeviceSurfaceFormatsKHR ( VulkanDevice : : physDevice , vulkanSurface , & formatCount , surfaceFormats . data ( ) ) ) ;
// We want to get a format that best suits our needs, so we try to get one from a set of preferred formats
// Initialize the format to the first one returned by the implementation in case we can't find one of the preffered formats
VkSurfaceFormatKHR selectedFormat = surfaceFormats [ 0 ] ;
std : : vector < VkFormat > preferredImageFormats = {
2026-01-29 19:26:30 +01:00
VK_FORMAT_R8G8B8A8_UNORM ,
2026-01-27 22:34:24 +01:00
} ;
for ( auto & availableFormat : surfaceFormats ) {
if ( std : : find ( preferredImageFormats . begin ( ) , preferredImageFormats . end ( ) , availableFormat . format ) ! = preferredImageFormats . end ( ) ) {
selectedFormat = availableFormat ;
break ;
}
}
colorFormat = selectedFormat . format ;
colorSpace = selectedFormat . colorSpace ;
CreateSwapchain ( ) ;
std : : array < VkAttachmentDescription , 1 > attachments = { } ;
// Color attachment
attachments [ 0 ] . format = colorFormat ;
attachments [ 0 ] . samples = VK_SAMPLE_COUNT_1_BIT ;
attachments [ 0 ] . loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR ;
attachments [ 0 ] . storeOp = VK_ATTACHMENT_STORE_OP_STORE ;
attachments [ 0 ] . stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE ;
attachments [ 0 ] . stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE ;
attachments [ 0 ] . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
attachments [ 0 ] . finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ;
VkAttachmentReference colorReference = { } ;
colorReference . attachment = 0 ;
2026-01-29 02:05:18 +01:00
colorReference . layout = VK_IMAGE_LAYOUT_GENERAL ;
2026-01-27 22:34:24 +01:00
VkSubpassDescription subpassDescription = { } ;
subpassDescription . pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS ;
subpassDescription . colorAttachmentCount = 1 ;
subpassDescription . pColorAttachments = & colorReference ;
subpassDescription . pDepthStencilAttachment = nullptr ;
subpassDescription . inputAttachmentCount = 0 ;
subpassDescription . pInputAttachments = nullptr ;
subpassDescription . preserveAttachmentCount = 0 ;
subpassDescription . pPreserveAttachments = nullptr ;
subpassDescription . pResolveAttachments = nullptr ;
drawCmdBuffers . resize ( images . size ( ) ) ;
VkCommandBufferAllocateInfo cmdBufAllocateInfo { } ;
cmdBufAllocateInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO ;
cmdBufAllocateInfo . commandPool = VulkanDevice : : commandPool ;
cmdBufAllocateInfo . level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ;
cmdBufAllocateInfo . commandBufferCount = static_cast < uint32_t > ( drawCmdBuffers . size ( ) ) ;
VulkanDevice : : CheckVkResult ( vkAllocateCommandBuffers ( VulkanDevice : : device , & cmdBufAllocateInfo , drawCmdBuffers . data ( ) ) ) ;
VkSemaphoreCreateInfo semaphoreCreateInfo { } ;
semaphoreCreateInfo . sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO ;
VulkanDevice : : CheckVkResult ( vkCreateSemaphore ( VulkanDevice : : device , & semaphoreCreateInfo , nullptr , & semaphores . presentComplete ) ) ;
VulkanDevice : : CheckVkResult ( vkCreateSemaphore ( VulkanDevice : : device , & semaphoreCreateInfo , nullptr , & semaphores . renderComplete ) ) ;
// Set up submit info structure
// Semaphores will stay the same during application lifetime
// Command buffer submission info is set by each example
submitInfo . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
submitInfo . pWaitDstStageMask = & submitPipelineStages ;
submitInfo . waitSemaphoreCount = 1 ;
submitInfo . pWaitSemaphores = & semaphores . presentComplete ;
submitInfo . signalSemaphoreCount = 1 ;
submitInfo . pSignalSemaphores = & semaphores . renderComplete ;
submitInfo . pNext = VK_NULL_HANDLE ;
}
WindowVulkan : : WindowVulkan ( std : : uint32_t width , std : : uint32_t height , const std : : string_view title ) : WindowVulkan ( width , height ) {
xdg_toplevel_set_title ( xdgToplevel , title . data ( ) ) ;
}
WindowVulkan : : ~ WindowVulkan ( ) {
xdg_toplevel_destroy ( xdgToplevel ) ;
xdg_surface_destroy ( xdgSurface ) ;
wl_surface_destroy ( surface ) ;
}
void WindowVulkan : : StartSync ( ) {
while ( open & & wl_display_dispatch ( display ) ! = - 1 ) {
}
}
void WindowVulkan : : Render ( ) {
// Acquire the next image from the swap chain
VulkanDevice : : CheckVkResult ( vkAcquireNextImageKHR ( VulkanDevice : : device , swapChain , UINT64_MAX , semaphores . presentComplete , ( VkFence ) nullptr , & currentBuffer ) ) ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VkCommandBufferBeginInfo cmdBufInfo { } ;
cmdBufInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ;
2026-01-29 01:31:17 +01:00
2026-01-27 22:34:24 +01:00
VulkanDevice : : CheckVkResult ( vkBeginCommandBuffer ( drawCmdBuffers [ currentBuffer ] , & cmdBufInfo ) ) ;
VkImageSubresourceRange range { } ;
range . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
range . baseMipLevel = 0 ;
range . levelCount = VK_REMAINING_MIP_LEVELS ;
range . baseArrayLayer = 0 ;
range . layerCount = VK_REMAINING_ARRAY_LAYERS ;
image_layout_transition ( drawCmdBuffers [ currentBuffer ] ,
images [ currentBuffer ] ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ,
0 ,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
VK_IMAGE_LAYOUT_UNDEFINED ,
2026-01-29 02:05:18 +01:00
VK_IMAGE_LAYOUT_GENERAL ,
range
) ;
2026-01-30 00:09:37 +01:00
onRender . Invoke ( ) ;
2026-01-29 02:05:18 +01:00
2026-01-29 01:31:17 +01:00
vkCmdBindPipeline ( drawCmdBuffers [ currentBuffer ] , VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR , rtPipeline ) ;
VkBindDescriptorSetsInfo bindDescriptorSetsInfo {
. sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO ,
. stageFlags = VK_SHADER_STAGE_ALL ,
. layout = rtPipelineLayout ,
. firstSet = 0 ,
. descriptorSetCount = 1 ,
. pDescriptorSets = descriptorsRt . data ( )
} ;
vkCmdBindDescriptorSets2 ( drawCmdBuffers [ currentBuffer ] , & bindDescriptorSetsInfo ) ;
2026-01-29 02:05:18 +01:00
VulkanDevice : : vkCmdTraceRaysKHR ( drawCmdBuffers [ currentBuffer ] , & raygenRegion , & missRegion , & hitRegion , & callableRegion , width , height , 1 ) ;
2026-01-27 22:34:24 +01:00
image_layout_transition ( drawCmdBuffers [ currentBuffer ] ,
images [ currentBuffer ] ,
2026-01-29 02:05:18 +01:00
VK_IMAGE_LAYOUT_GENERAL ,
2026-01-27 22:34:24 +01:00
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
range
) ;
VulkanDevice : : CheckVkResult ( vkEndCommandBuffer ( drawCmdBuffers [ currentBuffer ] ) ) ;
VulkanDevice : : CheckVkResult ( vkQueueSubmit ( VulkanDevice : : queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VkPresentInfoKHR presentInfo = { } ;
presentInfo . sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR ;
presentInfo . pNext = NULL ;
presentInfo . swapchainCount = 1 ;
presentInfo . pSwapchains = & swapChain ;
presentInfo . pImageIndices = & currentBuffer ;
// Check if a wait semaphore has been specified to wait for before presenting the image
if ( semaphores . renderComplete ! = VK_NULL_HANDLE )
{
presentInfo . pWaitSemaphores = & semaphores . renderComplete ;
presentInfo . waitSemaphoreCount = 1 ;
}
VulkanDevice : : CheckVkResult ( vkQueuePresentKHR ( VulkanDevice : : queue , & presentInfo ) ) ;
VulkanDevice : : CheckVkResult ( vkQueueWaitIdle ( VulkanDevice : : queue ) ) ;
}
VkCommandBuffer WindowVulkan : : StartInit ( ) {
VkCommandBufferBeginInfo cmdBufInfo { } ;
cmdBufInfo . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ;
VulkanDevice : : CheckVkResult ( vkBeginCommandBuffer ( drawCmdBuffers [ currentBuffer ] , & cmdBufInfo ) ) ;
return drawCmdBuffers [ currentBuffer ] ;
}
void WindowVulkan : : FinishInit ( ) {
VkSubmitInfo submitInfo { } ;
submitInfo . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VulkanDevice : : CheckVkResult ( vkEndCommandBuffer ( drawCmdBuffers [ currentBuffer ] ) ) ;
VulkanDevice : : CheckVkResult ( vkQueueSubmit ( VulkanDevice : : queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanDevice : : CheckVkResult ( vkQueueWaitIdle ( VulkanDevice : : queue ) ) ;
}
void WindowVulkan : : QueueRender ( ) {
if ( cb = = nullptr ) {
cb = wl_surface_frame ( surface ) ;
wl_callback_add_listener ( cb , & wl_callback_listener , this ) ;
}
}
void WindowVulkan : : StartUpdate ( ) {
lastFrameBegin = std : : chrono : : high_resolution_clock : : now ( ) ;
cb = wl_surface_frame ( surface ) ;
wl_callback_add_listener ( cb , & wl_callback_listener , this ) ;
updating = true ;
}
void WindowVulkan : : StopUpdate ( ) {
updating = false ;
}
void WindowVulkan : : SetTitle ( const std : : string_view title ) {
xdg_toplevel_set_title ( xdgToplevel , title . data ( ) ) ;
}
void WindowVulkan : : Resize ( std : : uint32_t width , std : : uint32_t height ) {
}
void WindowVulkan : : xdg_wm_base_handle_ping ( void * data , xdg_wm_base * xdg_wm_base , std : : uint32_t serial ) {
xdg_wm_base_pong ( xdg_wm_base , serial ) ;
}
# ifdef CRAFTER_TIMING
std : : chrono : : time_point < std : : chrono : : high_resolution_clock > framEnd ;
# endif
void WindowVulkan : : wl_surface_frame_done ( void * data , struct wl_callback * cb , uint32_t time )
{
auto start = std : : chrono : : high_resolution_clock : : now ( ) ;
wl_callback_destroy ( cb ) ;
cb = nullptr ;
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
# ifdef CRAFTER_TIMING
window - > vblank = duration_cast < std : : chrono : : milliseconds > ( start - window - > frameEnd ) ;
# endif
if ( window - > updating ) {
cb = wl_surface_frame ( window - > surface ) ;
wl_callback_add_listener ( cb , & WindowVulkan : : wl_callback_listener , window ) ;
2026-01-30 00:09:37 +01:00
window - > currentFrameTime = { start , start - window - > lastFrameBegin } ;
2026-01-27 22:34:24 +01:00
window - > onUpdate . Invoke ( { start , start - window - > lastFrameBegin } ) ;
# ifdef CRAFTER_TIMING
window - > totalUpdate = std : : chrono : : nanoseconds ( 0 ) ;
window - > updateTimings . clear ( ) ;
for ( const std : : pair < const EventListener < FrameTime > * , std : : chrono : : nanoseconds > & entry : window - > onUpdate . listenerTimes ) {
window - > updateTimings . push_back ( entry ) ;
window - > totalUpdate + = entry . second ;
}
# endif
}
# ifdef CRAFTER_TIMING
auto renderStart = std : : chrono : : high_resolution_clock : : now ( ) ;
window - > renderTimings . clear ( ) ;
# endif
window - > Render ( ) ;
# ifdef CRAFTER_TIMING
auto renderEnd = std : : chrono : : high_resolution_clock : : now ( ) ;
window - > totalRender = renderEnd - renderStart ;
# endif
# ifdef CRAFTER_TIMING
window - > frameEnd = std : : chrono : : high_resolution_clock : : now ( ) ;
window - > frameTimes . push_back ( window - > totalUpdate + window - > totalRender ) ;
// Keep only the last 100 frame times
if ( window - > frameTimes . size ( ) > 100 ) {
window - > frameTimes . erase ( window - > frameTimes . begin ( ) ) ;
}
# endif
window - > lastFrameBegin = start ;
}
void WindowVulkan : : pointer_handle_button ( void * data , wl_pointer * pointer , std : : uint32_t serial , std : : uint32_t time , std : : uint32_t button , std : : uint32_t state ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
if ( button = = BTN_LEFT ) {
if ( state = = WL_POINTER_BUTTON_STATE_PRESSED ) {
window - > mouseLeftHeld = true ;
window - > onMouseLeftClick . Invoke ( window - > currentMousePos ) ;
for ( MouseElement * element : window - > mouseElements ) {
if ( element ) {
if ( window - > currentMousePos . x > = element - > mouseScaled . x & & window - > currentMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > currentMousePos . y > element - > mouseScaled . y & & window - > currentMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) {
element - > onMouseLeftClick . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
}
}
}
} else {
window - > mouseLeftHeld = false ;
window - > onMouseLeftRelease . Invoke ( window - > currentMousePos ) ;
for ( MouseElement * element : window - > mouseElements ) {
if ( element ) {
if ( window - > currentMousePos . x > = element - > mouseScaled . x & & window - > currentMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > currentMousePos . y > element - > mouseScaled . y & & window - > currentMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) {
element - > onMouseLeftRelease . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
}
}
}
}
} else if ( button = = BTN_RIGHT ) {
if ( state = = WL_POINTER_BUTTON_STATE_PRESSED ) {
window - > mouseRightHeld = true ;
window - > onMouseRightClick . Invoke ( window - > currentMousePos ) ;
for ( MouseElement * element : window - > mouseElements ) {
if ( element ) {
if ( window - > currentMousePos . x > = element - > mouseScaled . x & & window - > currentMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > currentMousePos . y > element - > mouseScaled . y & & window - > currentMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) {
element - > onMouseRightClick . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
}
}
}
} else {
window - > mouseRightHeld = true ;
window - > onMouseRightRelease . Invoke ( window - > currentMousePos ) ;
for ( MouseElement * element : window - > mouseElements ) {
if ( element ) {
if ( window - > currentMousePos . x > = element - > mouseScaled . x & & window - > currentMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > currentMousePos . y > element - > mouseScaled . y & & window - > currentMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) {
element - > onMouseRightRelease . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
}
}
}
}
}
window - > mouseElements . erase ( std : : remove ( window - > mouseElements . begin ( ) , window - > mouseElements . end ( ) , static_cast < MouseElement * > ( nullptr ) ) , window - > mouseElements . end ( ) ) ;
window - > mouseElements . insert ( window - > mouseElements . end ( ) , window - > pendingMouseElements . begin ( ) , window - > pendingMouseElements . end ( ) ) ;
window - > pendingMouseElements . clear ( ) ;
}
void WindowVulkan : : PointerListenerHandleMotion ( void * data , wl_pointer * wl_pointer , uint time , wl_fixed_t surface_x , wl_fixed_t surface_y ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
MousePoint pos = { FractionalToMappedBoundless < std : : uint32_t > ( ( wl_fixed_to_double ( surface_x ) * window - > scale ) / window - > width ) , FractionalToMappedBoundless < std : : uint32_t > ( ( wl_fixed_to_double ( surface_y ) * window - > scale ) / window - > height ) } ;
window - > lastMousePos = window - > currentMousePos ;
window - > currentMousePos = pos ;
window - > mouseDelta = { window - > currentMousePos . x - window - > lastMousePos . x , window - > currentMousePos . y - window - > lastMousePos . y } ;
window - > onMouseMove . Invoke ( { window - > lastMousePos , window - > currentMousePos , window - > mouseDelta } ) ;
for ( MouseElement * element : window - > mouseElements ) {
if ( element ) {
if ( window - > currentMousePos . x > = element - > mouseScaled . x & & window - > currentMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > currentMousePos . y > element - > mouseScaled . y & & window - > currentMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) {
element - > onMouseMove . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
if ( ! ( window - > lastMousePos . x > = element - > mouseScaled . x & & window - > lastMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > lastMousePos . y > element - > mouseScaled . y & & window - > lastMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) ) {
element - > onMouseEnter . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
}
} else if ( window - > lastMousePos . x > = element - > mouseScaled . x & & window - > lastMousePos . x < = element - > mouseScaled . x + element - > mouseScaled . width & & window - > lastMousePos . y > element - > mouseScaled . y & & window - > lastMousePos . y < element - > mouseScaled . y + element - > mouseScaled . height ) {
element - > onMouseLeave . Invoke ( { AbsoluteToMappedBoundless ( window - > currentMousePos . x - element - > mouseScaled . x , element - > mouseScaled . width ) , AbsoluteToMappedBoundless ( window - > currentMousePos . y - element - > mouseScaled . y , element - > mouseScaled . height ) } ) ;
}
}
}
window - > mouseElements . erase ( std : : remove ( window - > mouseElements . begin ( ) , window - > mouseElements . end ( ) , static_cast < MouseElement * > ( nullptr ) ) , window - > mouseElements . end ( ) ) ;
}
void WindowVulkan : : PointerListenerHandleEnter ( void * data , wl_pointer * wl_pointer , uint serial , wl_surface * surface , wl_fixed_t surface_x , wl_fixed_t surface_y ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
window - > onMouseEnter . Invoke ( { window - > lastMousePos , window - > currentMousePos , window - > mouseDelta } ) ;
}
void WindowVulkan : : PointerListenerHandleLeave ( void * data , wl_pointer * , std : : uint32_t , wl_surface * ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
window - > onMouseLeave . Invoke ( { window - > lastMousePos , window - > currentMousePos , window - > mouseDelta } ) ;
}
void WindowVulkan : : PointerListenerHandleAxis ( void * , wl_pointer * , std : : uint32_t , std : : uint32_t , wl_fixed_t value ) {
}
void WindowVulkan : : keyboard_keymap ( void * data , wl_keyboard * keyboard , uint32_t format , int fd , uint32_t size ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
if ( format ! = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 ) {
close ( fd ) ;
fprintf ( stderr , " Unsupported keymap format \n " ) ;
return ;
}
void * map = mmap ( NULL , size , PROT_READ , MAP_SHARED , fd , 0 ) ;
if ( map = = MAP_FAILED ) {
close ( fd ) ;
perror ( " mmap " ) ;
return ;
}
window - > xkb_context = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
window - > xkb_keymap = xkb_keymap_new_from_string ( window - > xkb_context , ( const char * ) map , XKB_KEYMAP_FORMAT_TEXT_V1 , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
munmap ( map , size ) ;
close ( fd ) ;
window - > xkb_state = xkb_state_new ( window - > xkb_keymap ) ;
}
void WindowVulkan : : keyboard_enter ( void * data , wl_keyboard * keyboard , uint32_t serial , wl_surface * surface , wl_array * keys ) {
}
void WindowVulkan : : keyboard_leave ( void * data , wl_keyboard * keyboard , uint32_t serial , wl_surface * surface ) {
}
CrafterKeys keysym_to_crafter_key ( xkb_keysym_t sym )
{
switch ( sym )
{
// Alphabet
case XKB_KEY_a : return CrafterKeys : : A ;
case XKB_KEY_b : return CrafterKeys : : B ;
case XKB_KEY_c : return CrafterKeys : : C ;
case XKB_KEY_d : return CrafterKeys : : D ;
case XKB_KEY_e : return CrafterKeys : : E ;
case XKB_KEY_f : return CrafterKeys : : F ;
case XKB_KEY_g : return CrafterKeys : : G ;
case XKB_KEY_h : return CrafterKeys : : H ;
case XKB_KEY_i : return CrafterKeys : : I ;
case XKB_KEY_j : return CrafterKeys : : J ;
case XKB_KEY_k : return CrafterKeys : : K ;
case XKB_KEY_l : return CrafterKeys : : L ;
case XKB_KEY_m : return CrafterKeys : : M ;
case XKB_KEY_n : return CrafterKeys : : N ;
case XKB_KEY_o : return CrafterKeys : : O ;
case XKB_KEY_p : return CrafterKeys : : P ;
case XKB_KEY_q : return CrafterKeys : : Q ;
case XKB_KEY_r : return CrafterKeys : : R ;
case XKB_KEY_s : return CrafterKeys : : S ;
case XKB_KEY_t : return CrafterKeys : : T ;
case XKB_KEY_u : return CrafterKeys : : U ;
case XKB_KEY_v : return CrafterKeys : : V ;
case XKB_KEY_w : return CrafterKeys : : W ;
case XKB_KEY_x : return CrafterKeys : : X ;
case XKB_KEY_y : return CrafterKeys : : Y ;
case XKB_KEY_z : return CrafterKeys : : Z ;
// Numbers
case XKB_KEY_0 : return CrafterKeys : : _0 ;
case XKB_KEY_1 : return CrafterKeys : : _1 ;
case XKB_KEY_2 : return CrafterKeys : : _2 ;
case XKB_KEY_3 : return CrafterKeys : : _3 ;
case XKB_KEY_4 : return CrafterKeys : : _4 ;
case XKB_KEY_5 : return CrafterKeys : : _5 ;
case XKB_KEY_6 : return CrafterKeys : : _6 ;
case XKB_KEY_7 : return CrafterKeys : : _7 ;
case XKB_KEY_8 : return CrafterKeys : : _8 ;
case XKB_KEY_9 : return CrafterKeys : : _9 ;
// Function keys
case XKB_KEY_F1 : return CrafterKeys : : F1 ;
case XKB_KEY_F2 : return CrafterKeys : : F2 ;
case XKB_KEY_F3 : return CrafterKeys : : F3 ;
case XKB_KEY_F4 : return CrafterKeys : : F4 ;
case XKB_KEY_F5 : return CrafterKeys : : F5 ;
case XKB_KEY_F6 : return CrafterKeys : : F6 ;
case XKB_KEY_F7 : return CrafterKeys : : F7 ;
case XKB_KEY_F8 : return CrafterKeys : : F8 ;
case XKB_KEY_F9 : return CrafterKeys : : F9 ;
case XKB_KEY_F10 : return CrafterKeys : : F10 ;
case XKB_KEY_F11 : return CrafterKeys : : F11 ;
case XKB_KEY_F12 : return CrafterKeys : : F12 ;
// Control keys
case XKB_KEY_Escape : return CrafterKeys : : Escape ;
case XKB_KEY_Tab : return CrafterKeys : : Tab ;
case XKB_KEY_Return : return CrafterKeys : : Enter ;
case XKB_KEY_space : return CrafterKeys : : Space ;
case XKB_KEY_BackSpace : return CrafterKeys : : Backspace ;
case XKB_KEY_Delete : return CrafterKeys : : Delete ;
case XKB_KEY_Insert : return CrafterKeys : : Insert ;
case XKB_KEY_Home : return CrafterKeys : : Home ;
case XKB_KEY_End : return CrafterKeys : : End ;
case XKB_KEY_Page_Up : return CrafterKeys : : PageUp ;
case XKB_KEY_Page_Down : return CrafterKeys : : PageDown ;
case XKB_KEY_Caps_Lock : return CrafterKeys : : CapsLock ;
case XKB_KEY_Num_Lock : return CrafterKeys : : NumLock ;
case XKB_KEY_Scroll_Lock : return CrafterKeys : : ScrollLock ;
// Modifiers
case XKB_KEY_Shift_L : return CrafterKeys : : LeftShift ;
case XKB_KEY_Shift_R : return CrafterKeys : : RightShift ;
case XKB_KEY_Control_L : return CrafterKeys : : LeftCtrl ;
case XKB_KEY_Control_R : return CrafterKeys : : RightCtrl ;
case XKB_KEY_Alt_L : return CrafterKeys : : LeftAlt ;
case XKB_KEY_Alt_R : return CrafterKeys : : RightAlt ;
case XKB_KEY_Super_L : return CrafterKeys : : LeftSuper ;
case XKB_KEY_Super_R : return CrafterKeys : : RightSuper ;
// Arrows
case XKB_KEY_Up : return CrafterKeys : : Up ;
case XKB_KEY_Down : return CrafterKeys : : Down ;
case XKB_KEY_Left : return CrafterKeys : : Left ;
case XKB_KEY_Right : return CrafterKeys : : Right ;
// Keypad
case XKB_KEY_KP_0 : return CrafterKeys : : keypad_0 ;
case XKB_KEY_KP_1 : return CrafterKeys : : keypad_1 ;
case XKB_KEY_KP_2 : return CrafterKeys : : keypad_2 ;
case XKB_KEY_KP_3 : return CrafterKeys : : keypad_3 ;
case XKB_KEY_KP_4 : return CrafterKeys : : keypad_4 ;
case XKB_KEY_KP_5 : return CrafterKeys : : keypad_5 ;
case XKB_KEY_KP_6 : return CrafterKeys : : keypad_6 ;
case XKB_KEY_KP_7 : return CrafterKeys : : keypad_7 ;
case XKB_KEY_KP_8 : return CrafterKeys : : keypad_8 ;
case XKB_KEY_KP_9 : return CrafterKeys : : keypad_9 ;
case XKB_KEY_KP_Enter : return CrafterKeys : : keypad_enter ;
case XKB_KEY_KP_Add : return CrafterKeys : : keypad_plus ;
case XKB_KEY_KP_Subtract : return CrafterKeys : : keypad_minus ;
case XKB_KEY_KP_Multiply : return CrafterKeys : : keypad_multiply ;
case XKB_KEY_KP_Divide : return CrafterKeys : : keypad_divide ;
case XKB_KEY_KP_Decimal : return CrafterKeys : : keypad_decimal ;
// Punctuation
case XKB_KEY_grave : return CrafterKeys : : grave ;
case XKB_KEY_minus : return CrafterKeys : : minus ;
case XKB_KEY_equal : return CrafterKeys : : equal ;
case XKB_KEY_bracketleft : return CrafterKeys : : bracket_left ;
case XKB_KEY_bracketright : return CrafterKeys : : bracket_right ;
case XKB_KEY_backslash : return CrafterKeys : : backslash ;
case XKB_KEY_semicolon : return CrafterKeys : : semicolon ;
case XKB_KEY_apostrophe : return CrafterKeys : : quote ;
case XKB_KEY_comma : return CrafterKeys : : comma ;
case XKB_KEY_period : return CrafterKeys : : period ;
case XKB_KEY_slash : return CrafterKeys : : slash ;
default :
return CrafterKeys : : CrafterKeysMax ;
}
}
void WindowVulkan : : keyboard_key ( void * data , wl_keyboard * keyboard , uint32_t serial , uint32_t time , uint32_t key , uint32_t state ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
if ( ! window - > xkb_state ) {
return ;
}
xkb_keycode_t keycode = key + 8 ;
xkb_keysym_t keysym = xkb_state_key_get_one_sym ( window - > xkb_state , keycode ) ;
CrafterKeys crafterKey = keysym_to_crafter_key ( keysym ) ;
if ( state = = WL_KEYBOARD_KEY_STATE_PRESSED ) {
if ( window - > heldkeys [ static_cast < std : : uint8_t > ( crafterKey ) ] ) {
window - > onKeyHold [ static_cast < std : : uint8_t > ( crafterKey ) ] . Invoke ( ) ;
window - > onAnyKeyHold . Invoke ( crafterKey ) ;
} else {
window - > heldkeys [ static_cast < std : : uint8_t > ( crafterKey ) ] = true ;
window - > onKeyDown [ static_cast < std : : uint8_t > ( crafterKey ) ] . Invoke ( ) ;
window - > onAnyKeyDown . Invoke ( crafterKey ) ;
}
} else {
window - > heldkeys [ static_cast < std : : uint8_t > ( crafterKey ) ] = false ;
window - > onKeyUp [ static_cast < std : : uint8_t > ( crafterKey ) ] . Invoke ( ) ;
window - > onAnyKeyUp . Invoke ( crafterKey ) ;
}
}
void WindowVulkan : : keyboard_modifiers ( void * data , wl_keyboard * keyboard , uint32_t serial , uint32_t mods_depressed , uint32_t mods_latched , uint32_t mods_locked , uint32_t group ) {
}
void WindowVulkan : : keyboard_repeat_info ( void * data , wl_keyboard * keyboard , int32_t rate , int32_t delay ) {
}
void WindowVulkan : : seat_handle_capabilities ( void * data , wl_seat * seat , uint32_t capabilities ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
window - > seat = seat ;
if ( capabilities & WL_SEAT_CAPABILITY_POINTER ) {
wl_pointer * pointer = wl_seat_get_pointer ( seat ) ;
wl_pointer_add_listener ( pointer , & pointer_listener , window ) ;
}
if ( capabilities & WL_SEAT_CAPABILITY_KEYBOARD ) {
wl_keyboard * keyboard = wl_seat_get_keyboard ( seat ) ;
wl_keyboard_add_listener ( keyboard , & keyboard_listener , window ) ;
}
}
void WindowVulkan : : handle_global ( void * data , wl_registry * registry , std : : uint32_t name , const char * interface , std : : uint32_t version ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
if ( strcmp ( interface , wl_shm_interface . name ) = = 0 ) {
window - > shm = reinterpret_cast < wl_shm * > ( wl_registry_bind ( registry , name , & wl_shm_interface , 1 ) ) ;
} else if ( strcmp ( interface , wl_seat_interface . name ) = = 0 ) {
wl_seat * seat = reinterpret_cast < wl_seat * > ( wl_registry_bind ( registry , name , & wl_seat_interface , 1 ) ) ;
wl_seat_add_listener ( seat , & seat_listener , window ) ;
} else if ( compositor = = NULL & & strcmp ( interface , wl_compositor_interface . name ) = = 0 ) {
compositor = reinterpret_cast < wl_compositor * > ( wl_registry_bind ( registry , name , & wl_compositor_interface , 3 ) ) ;
} else if ( strcmp ( interface , xdg_wm_base_interface . name ) = = 0 ) {
window - > xdgWmBase = reinterpret_cast < xdg_wm_base * > ( wl_registry_bind ( registry , name , & xdg_wm_base_interface , 1 ) ) ;
xdg_wm_base_add_listener ( window - > xdgWmBase , & xdgWmBaseListener , NULL ) ;
} else if ( strcmp ( interface , zxdg_decoration_manager_v1_interface . name ) = = 0 ) {
window - > manager = reinterpret_cast < zxdg_decoration_manager_v1 * > ( wl_registry_bind ( registry , name , & zxdg_decoration_manager_v1_interface , 1 ) ) ;
} else if ( strcmp ( interface , wp_viewporter_interface . name ) = = 0 ) {
window - > wpViewporter = reinterpret_cast < wp_viewporter * > ( wl_registry_bind ( registry , name , & wp_viewporter_interface , 1 ) ) ;
} else if ( strcmp ( interface , wp_fractional_scale_manager_v1_interface . name ) = = 0 ) {
window - > fractionalScaleManager = reinterpret_cast < wp_fractional_scale_manager_v1 * > ( wl_registry_bind ( registry , name , & wp_fractional_scale_manager_v1_interface , 1 ) ) ;
}
}
void WindowVulkan : : handle_global_remove ( void * data , wl_registry * registry , uint32_t name ) {
}
void WindowVulkan : : xdg_toplevel_configure ( void * , xdg_toplevel * , std : : int32_t , std : : int32_t , wl_array * ) {
}
void WindowVulkan : : xdg_toplevel_handle_close ( void * data , xdg_toplevel * ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
window - > onClose . Invoke ( ) ;
window - > open = false ;
}
void WindowVulkan : : xdg_surface_handle_configure ( void * data , xdg_surface * xdg_surface , std : : uint32_t serial ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
// The compositor configures our surface, acknowledge the configure event
xdg_surface_ack_configure ( xdg_surface , serial ) ;
if ( window - > configured ) {
// If this isn't the first configure event we've received, we already
// have a buffer attached, so no need to do anything. Commit the
// surface to apply the configure acknowledgement.
wl_surface_commit ( window - > surface ) ;
}
window - > configured = true ;
}
void WindowVulkan : : xdg_surface_handle_preferred_scale ( void * data , wp_fractional_scale_v1 * , std : : uint32_t scale ) {
WindowVulkan * window = reinterpret_cast < WindowVulkan * > ( data ) ;
window - > scale = scale / 120.0f ;
}