2025-04-19 15:46:26 +02:00
module ;
# include <errno.h>
# include <fcntl.h>
# include <linux/input.h>
# include <string>
# include <sys/mman.h>
# include <unistd.h>
# include <wayland-cursor.h>
# include <xkbcommon/xkbcommon.h>
# include <iostream>
# include <vulkan/vulkan.h>
# include <vulkan/vulkan_wayland.h>
# include <wayland-client.h>
# include <cstring>
# include "xdg-shell-client-protocol.h"
# include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
# define _POSIX_C_SOURCE 200809L
# include <errno.h>
# include <fcntl.h>
# include <string.h>
# include <sys/mman.h>
# include <time.h>
# include <unistd.h>
# include <print>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/mman.h>
# include <unistd.h>
# include <wayland-client.h>
# include <wayland-client-protocol.h>
# include <linux/input-event-codes.h>
# include <cmath>
2025-05-04 05:15:31 +02:00
# include <xkbcommon/xkbcommon.h>
# include <cstdio>
2025-04-19 15:46:26 +02:00
module Crafter . Graphics ;
import Crafter . Event ;
using namespace Crafter ;
static void 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 ) ;
}
xdg_wm_base_listener xdgWmBaseListener = {
. ping = xdg_wm_base_handle_ping ,
} ;
void WindowWayland : : pointer_handle_button ( void * data , wl_pointer * pointer , std : : uint32_t serial , std : : uint32_t time , std : : uint32_t button , std : : uint32_t state ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( data ) ;
if ( button = = BTN_LEFT ) {
if ( state = = WL_POINTER_BUTTON_STATE_PRESSED ) {
window - > mouseLeftHeld = true ;
window - > onMouseLeftClick . Invoke ( window - > currentMousePos ) ;
} else {
window - > mouseLeftHeld = false ;
window - > onMouseLeftRelease . Invoke ( window - > currentMousePos ) ;
}
} else if ( button = = BTN_RIGHT ) {
if ( state = = WL_POINTER_BUTTON_STATE_PRESSED ) {
window - > mouseRightHeld = true ;
window - > onMouseRightClick . Invoke ( window - > currentMousePos ) ;
} else {
window - > mouseRightHeld = true ;
window - > onMouseRightRelease . Invoke ( window - > currentMousePos ) ;
}
}
}
void WindowWayland : : PointerListenerHandleMotion ( void * data , wl_pointer * wl_pointer , uint time , wl_fixed_t surface_x , wl_fixed_t surface_y ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( data ) ;
MousePoint pos = { wl_fixed_to_double ( surface_x ) , wl_fixed_to_double ( surface_y ) } ;
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 } ) ;
}
void WindowWayland : : PointerListenerHandleEnter ( void * data , wl_pointer * wl_pointer , uint serial , wl_surface * surface , wl_fixed_t surface_x , wl_fixed_t surface_y ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( data ) ;
window - > onMouseEnter . Invoke ( { window - > lastMousePos , window - > currentMousePos , window - > mouseDelta } ) ;
}
void WindowWayland : : PointerListenerHandleLeave ( void * data , wl_pointer * , std : : uint32_t , wl_surface * ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( data ) ;
window - > onMouseEnter . Invoke ( { window - > lastMousePos , window - > currentMousePos , window - > mouseDelta } ) ;
}
void WindowWayland : : PointerListenerHandleAxis ( void * , wl_pointer * , std : : uint32_t , std : : uint32_t , wl_fixed_t value ) {
std : : cout < < wl_fixed_to_double ( value ) < < std : : endl ;
}
wl_pointer_listener WindowWayland : : pointer_listener = {
. enter = WindowWayland : : PointerListenerHandleEnter ,
. leave = WindowWayland : : PointerListenerHandleLeave ,
. motion = WindowWayland : : PointerListenerHandleMotion ,
. button = WindowWayland : : pointer_handle_button ,
. axis = WindowWayland : : PointerListenerHandleAxis ,
} ;
2025-05-04 05:15:31 +02:00
xkb_keymap * xkb_keymap ;
xkb_context * xkb_context = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
xkb_state * xkb_state ;
void keyboard_keymap ( void * data , wl_keyboard * keyboard , uint32_t format , int fd , uint32_t size ) {
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 ;
}
xkb_context = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
xkb_keymap = xkb_keymap_new_from_string ( xkb_context , ( const char * ) map , XKB_KEYMAP_FORMAT_TEXT_V1 , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
munmap ( map , size ) ;
close ( fd ) ;
xkb_state = xkb_state_new ( xkb_keymap ) ;
}
void keyboard_enter ( void * data , wl_keyboard * keyboard , uint32_t serial , wl_surface * surface , wl_array * keys ) {
}
void keyboard_leave ( void * data , wl_keyboard * keyboard , uint32_t serial , wl_surface * surface ) {
}
void keyboard_key ( void * data , wl_keyboard * keyboard , uint32_t serial , uint32_t time , uint32_t key , uint32_t state ) {
if ( ! xkb_state ) {
return ;
}
WindowWayland * window = reinterpret_cast < WindowWayland * > ( data ) ;
xkb_keycode_t keycode = key + 8 ;
xkb_keysym_t keysym = xkb_state_key_get_one_sym ( xkb_state , keycode ) ;
char utf8 [ 8 ] = { 0 } ;
int len = xkb_keysym_to_utf8 ( keysym , utf8 , sizeof ( utf8 ) ) ;
if ( len ! = 0 ) {
char keypress = utf8 [ 0 ] ;
if ( state = = WL_KEYBOARD_KEY_STATE_PRESSED ) {
if ( window - > heldkeys [ keypress ] ) {
window - > onKeyHold [ keypress ] . Invoke ( ) ;
} else {
window - > onKeyDown [ keypress ] . Invoke ( ) ;
}
} else {
window - > onKeyUp [ keypress ] . Invoke ( ) ;
}
} else {
// // fallback for keys like Return, Escape, etc.
// char name[64];
// if (xkb_keysym_get_name(keysym, name, sizeof(name)) > 0) {
// printf("Key %s pressed (non-printable or multi-char)\n", name);
// }
}
}
void 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 keyboard_repeat_info ( void * data , wl_keyboard * keyboard , int32_t rate , int32_t delay ) {
}
wl_keyboard_listener WindowWayland : : keyboard_listener = {
. keymap = keyboard_keymap ,
. enter = keyboard_enter ,
. leave = keyboard_leave ,
. key = keyboard_key ,
. modifiers = keyboard_modifiers ,
. repeat_info = keyboard_repeat_info ,
} ;
2025-04-19 15:46:26 +02:00
void WindowWayland : : seat_handle_capabilities ( void * data , wl_seat * seat , uint32_t capabilities ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( 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 ) ;
}
2025-05-04 05:15:31 +02:00
if ( capabilities & WL_SEAT_CAPABILITY_KEYBOARD ) {
wl_keyboard * keyboard = wl_seat_get_keyboard ( seat ) ;
wl_keyboard_add_listener ( keyboard , & keyboard_listener , NULL ) ;
}
2025-04-19 15:46:26 +02:00
}
wl_seat_listener WindowWayland : : seat_listener = {
. capabilities = seat_handle_capabilities ,
} ;
void WindowWayland : : handle_global ( void * data , wl_registry * registry , std : : uint32_t name , const char * interface , std : : uint32_t version ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( 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 , 1 ) ) ;
} 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 ) ) ;
}
}
static void handle_global_remove ( void * data , wl_registry * registry , uint32_t name ) {
}
wl_registry_listener WindowWayland : : registry_listener = {
. global = WindowWayland : : handle_global ,
. global_remove = handle_global_remove ,
} ;
static void noop5 ( void * , xdg_toplevel * , std : : int32_t , std : : int32_t , wl_array * ) {
}
void WindowWayland : : xdg_toplevel_handle_close ( void * data , xdg_toplevel * ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( data ) ;
window - > onClose . Invoke ( ) ;
window - > open = false ;
}
xdg_toplevel_listener WindowWayland : : xdg_toplevel_listener = {
. configure = noop5 ,
. close = WindowWayland : : xdg_toplevel_handle_close ,
} ;
void WindowWayland : : xdg_surface_handle_configure ( void * data , xdg_surface * xdg_surface , std : : uint32_t serial ) {
WindowWayland * window = reinterpret_cast < WindowWayland * > ( 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 ;
}
xdg_surface_listener WindowWayland : : xdg_surface_listener = {
. configure = xdg_surface_handle_configure ,
} ;
static void randname ( char * buf ) {
struct timespec ts ;
clock_gettime ( CLOCK_REALTIME , & ts ) ;
long r = ts . tv_nsec ;
for ( int i = 0 ; i < 6 ; + + i ) {
buf [ i ] = ' A ' + ( r & 15 ) + ( r & 16 ) * 2 ;
r > > = 5 ;
}
}
static int anonymous_shm_open ( void ) {
char name [ ] = " /hello-wayland-XXXXXX " ;
int retries = 100 ;
do {
randname ( name + strlen ( name ) - 6 ) ;
- - retries ;
// shm_open guarantees that O_CLOEXEC is set
int fd = shm_open ( name , O_RDWR | O_CREAT | O_EXCL , 0600 ) ;
if ( fd > = 0 ) {
shm_unlink ( name ) ;
return fd ;
}
} while ( retries > 0 & & errno = = EEXIST ) ;
return - 1 ;
}
int create_shm_file ( off_t size ) {
int fd = anonymous_shm_open ( ) ;
if ( fd < 0 ) {
return fd ;
}
if ( ftruncate ( fd , size ) < 0 ) {
close ( fd ) ;
return - 1 ;
}
return fd ;
}
WindowWayland : : WindowWayland ( std : : string name , std : : uint32_t width , std : : uint32_t height ) : Window ( name , width , height ) {
// Connect to the Wayland compositor
display = wl_display_connect ( NULL ) ;
if ( display = = NULL ) {
std : : cerr < < " failed to create display " < < std : : endl ;
}
// Obtain the wl_registry and fetch the list of globals
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 ) ;
}
// Check that all globals we require are available
if ( shm = = NULL | | compositor = = NULL | | xdgWmBase = = NULL ) {
std : : cerr < < " no wl_shm, wl_compositor or xdg_wm_base support " < < std : : endl ;
exit ( EXIT_FAILURE ) ;
}
// Create a wl_surface, a xdg_surface and a xdg_toplevel
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 ) ;
while ( wl_display_dispatch ( display ) ! = - 1 & & ! configured ) {
// This space intentionally left blank
}
// Create a wl_buffer, attach it to the surface and commit the surface
int stride = width * 4 ;
int size = stride * height ;
// Allocate a shared memory file with the right size
int fd = create_shm_file ( size ) ;
if ( fd < 0 ) {
fprintf ( stderr , " creating a buffer file for %d B failed: %m \n " , size ) ;
}
// Map the shared memory file
2025-05-03 06:51:33 +02:00
shm_data = reinterpret_cast < Pixel_RU8_GU8_BU8_AU8 * > ( mmap ( NULL , size , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0 ) ) ;
2025-04-19 15:46:26 +02:00
if ( shm_data = = MAP_FAILED ) {
fprintf ( stderr , " mmap failed: %m \n " ) ;
close ( fd ) ;
}
// Create a wl_buffer from our shared memory file descriptor
wl_shm_pool * pool = wl_shm_create_pool ( shm , fd , size ) ;
buffer = wl_shm_pool_create_buffer ( pool , 0 , width , height , stride , WL_SHM_FORMAT_ARGB8888 ) ;
wl_shm_pool_destroy ( pool ) ;
// Now that we've mapped the file and created the wl_buffer, we no longer
// need to keep file descriptor opened
close ( fd ) ;
if ( buffer = = NULL ) {
exit ( EXIT_FAILURE ) ;
}
wl_surface_attach ( surface , buffer , 0 , 0 ) ;
wl_surface_commit ( surface ) ;
for ( std : : uint32_t x = 0 ; x < 128 ; x + + ) {
for ( std : : uint32_t y = 0 ; y < 128 ; y + + ) {
shm_data [ x * 128 + y ] . r = 0 ;
shm_data [ x * 128 + y ] . g = 0 ;
shm_data [ x * 128 + y ] . b = 0 ;
shm_data [ x * 128 + y ] . a = 255 ;
}
}
zxdg_toplevel_decoration_v1 * decoration = zxdg_decoration_manager_v1_get_toplevel_decoration ( manager , xdgToplevel ) ; // toplevel is from xdg_surface_get_toplevel
zxdg_toplevel_decoration_v1_set_mode ( decoration , ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE ) ;
}
WindowWayland : : ~ WindowWayland ( ) {
xdg_toplevel_destroy ( xdgToplevel ) ;
xdg_surface_destroy ( xdgSurface ) ;
wl_surface_destroy ( surface ) ;
wl_buffer_destroy ( buffer ) ;
}