2025-05-07 19:21:51 +02: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 as published by the Free Software Foundation ; either
version 3.0 of the License , or ( at your option ) any later version .
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
*/
2025-04-19 15:46:26 +02:00
module ;
# include <errno.h>
# include <fcntl.h>
# include <linux/input.h>
# include <unistd.h>
# include <wayland-cursor.h>
# include <vulkan/vulkan.h>
# include <vulkan/vulkan_wayland.h>
# include <wayland-client.h>
# include <stdio.h>
# include <stdlib.h>
# include <sys/mman.h>
# include <unistd.h>
# include <wayland-client.h>
# include <wayland-client-protocol.h>
# include <linux/input-event-codes.h>
2025-05-04 05:15:31 +02:00
# include <xkbcommon/xkbcommon.h>
2025-04-19 15:46:26 +02:00
2025-11-16 15:32:11 +01:00
module Crafter . Graphics : WindowWaylandWayland ;
import : WindowWaylandWayland ;
import std ;
2025-04-19 15:46:26 +02:00
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 ) ;
2025-06-13 23:59:36 +02:00
for ( UiElement & element : window - > elements ) {
ScaleData data = window - > ScaleElement ( element ) ;
if ( window - > currentMousePos . x > = data . x & & window - > currentMousePos . x < = data . x + data . width & & window - > currentMousePos . y > data . y & & window - > currentMousePos . y < data . y + data . height ) {
element . onMouseLeftClick . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
}
}
2025-04-19 15:46:26 +02:00
} else {
window - > mouseLeftHeld = false ;
window - > onMouseLeftRelease . Invoke ( window - > currentMousePos ) ;
2025-06-13 23:59:36 +02:00
for ( UiElement & element : window - > elements ) {
ScaleData data = window - > ScaleElement ( element ) ;
if ( window - > currentMousePos . x > = data . x & & window - > currentMousePos . x < = data . x + data . width & & window - > currentMousePos . y > data . y & & window - > currentMousePos . y < data . y + data . height ) {
element . onMouseLeftRelease . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
}
}
2025-04-19 15:46:26 +02:00
}
} else if ( button = = BTN_RIGHT ) {
if ( state = = WL_POINTER_BUTTON_STATE_PRESSED ) {
window - > mouseRightHeld = true ;
2025-06-13 23:59:36 +02:00
window - > onMouseRightClick . Invoke ( window - > currentMousePos ) ;
for ( UiElement & element : window - > elements ) {
ScaleData data = window - > ScaleElement ( element ) ;
if ( window - > currentMousePos . x > = data . x & & window - > currentMousePos . x < = data . x + data . width & & window - > currentMousePos . y > data . y & & window - > currentMousePos . y < data . y + data . height ) {
element . onMouseRightClick . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
}
}
2025-04-19 15:46:26 +02:00
} else {
window - > mouseRightHeld = true ;
2025-06-13 23:59:36 +02:00
window - > onMouseRightRelease . Invoke ( window - > currentMousePos ) ;
for ( UiElement & element : window - > elements ) {
ScaleData data = window - > ScaleElement ( element ) ;
if ( window - > currentMousePos . x > = data . x & & window - > currentMousePos . x < = data . x + data . width & & window - > currentMousePos . y > data . y & & window - > currentMousePos . y < data . y + data . height ) {
element . onMouseRightRelease . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
}
}
2025-04-19 15:46:26 +02:00
}
}
}
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 } ) ;
2025-06-13 23:59:36 +02:00
for ( UiElement & element : window - > elements ) {
ScaleData data = window - > ScaleElement ( element ) ;
if ( window - > currentMousePos . x > = data . x & & window - > currentMousePos . x < = data . x + data . width & & window - > currentMousePos . y > data . y & & window - > currentMousePos . y < data . y + data . height ) {
element . onMouseMove . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
if ( ! ( window - > lastMousePos . x > = data . x & & window - > lastMousePos . x < = data . x + data . width & & window - > lastMousePos . y > data . y & & window - > lastMousePos . y < data . y + data . height ) ) {
element . onMouseEnter . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
}
} else if ( window - > lastMousePos . x > = data . x & & window - > lastMousePos . x < = data . x + data . width & & window - > lastMousePos . y > data . y & & window - > lastMousePos . y < data . y + data . height ) {
element . onMouseLeave . Invoke ( { window - > currentMousePos . x - data . x , window - > currentMousePos . y - data . y } ) ;
}
}
2025-04-19 15:46:26 +02:00
}
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 ( ) ;
2025-06-13 23:59:36 +02:00
window - > onAnyKeyHold . Invoke ( keypress ) ;
2025-05-04 05:15:31 +02:00
} else {
2025-05-05 06:00:35 +02:00
window - > heldkeys [ keypress ] = true ;
2025-05-04 05:15:31 +02:00
window - > onKeyDown [ keypress ] . Invoke ( ) ;
2025-06-13 23:59:36 +02:00
window - > onAnyKeyDown . Invoke ( keypress ) ;
2025-05-04 05:15:31 +02:00
}
} else {
2025-05-05 06:00:35 +02:00
window - > heldkeys [ keypress ] = false ;
2025-05-04 05:15:31 +02:00
window - > onKeyUp [ keypress ] . Invoke ( ) ;
2025-06-13 23:59:36 +02:00
window - > onAnyKeyUp . Invoke ( keypress ) ;
2025-05-04 05:15:31 +02:00
}
} 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 ) ;
2025-05-05 06:00:35 +02:00
wl_keyboard_add_listener ( keyboard , & keyboard_listener , window ) ;
2025-05-04 05:15:31 +02:00
}
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 ,
} ;
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
}
wl_surface_commit ( surface ) ;
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 ) ;
}