2025-12-09 20:11:01 -06:00
# include <Mw/Milsko.h>
2026-03-19 17:32:36 -07:00
# include <sys/poll.h>
2026-03-19 19:43:57 -07:00
# include <sys/mman.h>
2025-12-09 20:11:01 -06:00
# include "../../external/stb_ds.h"
/* TODO: find out what FreeBSD and such wants us to include */
# ifdef __FreeBSD__
/* XXX: Untested (nishi) */
# include <evdev/input.h>
# else
# include <linux/input.h>
# include <linux/input-event-codes.h>
# endif
2026-03-24 17:38:21 -07:00
wayland_call_table_t wl_call_tbl ;
2026-03-24 23:24:20 -07:00
MwBool MwWaylandAlwaysRender = MwFALSE ;
2026-03-24 17:38:21 -07:00
2026-04-23 10:45:29 -07:00
static pthread_mutex_t destroyedWidgetsTableMutex ;
2026-04-26 20:01:03 -07:00
/* So Wayland, bless its soul; it keeps using callbacks LONG after they should not only be destroyed but the widget doesn't even exist anymore. Naturally, this causes use after free. so we fight fire with fire in the worst code i've ever written: by storing the freed pointers here, we disallow wayland from ever using them again. if something else is created that takes this slot, we remove it from the table. */
static MwLL * destroyedWidgetsTable ;
2026-04-23 10:45:29 -07:00
static MwBool is_destroyed ( MwLL self ) {
int i ;
pthread_mutex_lock ( & destroyedWidgetsTableMutex ) ;
for ( i = 0 ; i < arrlen ( destroyedWidgetsTable ) ; i + + ) {
if ( self = = destroyedWidgetsTable [ i ] ) {
pthread_mutex_unlock ( & destroyedWidgetsTableMutex ) ;
return MwTRUE ;
}
}
pthread_mutex_unlock ( & destroyedWidgetsTableMutex ) ;
return MwFALSE ;
}
static void undestroy ( MwLL self ) {
int i ;
pthread_mutex_lock ( & destroyedWidgetsTableMutex ) ;
for ( i = 0 ; i < arrlen ( destroyedWidgetsTable ) ; i + + ) {
if ( self = = destroyedWidgetsTable [ i ] ) {
arrdel ( destroyedWidgetsTable , i ) ;
break ;
}
}
pthread_mutex_unlock ( & destroyedWidgetsTableMutex ) ;
return ;
}
2026-04-23 10:57:05 -07:00
# define WIDGET_CHECK(handle) \
if ( ! handle - > wayland . valid ) { \
2026-04-23 14:47:51 -07:00
/*printf("[WARNING] Operation on invalid widget at line %d\n", __LINE__);*/ \
2026-04-23 10:57:05 -07:00
return ; \
}
2026-04-23 10:45:29 -07:00
/* Standard procedure before event callbacks in Wayland */
2026-01-04 11:40:34 -07:00
# define WAYLAND_EVENT_OP_START(self) \
2026-04-23 10:57:05 -07:00
if ( is_destroyed ( self ) ) { \
2026-04-23 14:47:51 -07:00
/*printf("[WARNING] Operation on invalid widget at line %d\n", __LINE__);*/ \
2026-04-23 10:57:05 -07:00
return ; \
2026-04-23 12:31:53 -07:00
}
2026-01-04 11:40:34 -07:00
/* Footer for WAYLAND_EVENT_OP_START */
2026-04-23 12:31:53 -07:00
# define WAYLAND_EVENT_OP_END(self)
2026-01-04 11:40:34 -07:00
2025-12-18 13:15:03 -07:00
/* Setup the framebuffer with the saved width/height */
static void framebuffer_setup ( struct _MwLLWayland * wayland ) ;
/* Destroy the framebuffer */
static void framebuffer_destroy ( struct _MwLLWayland * handle ) ;
2025-12-17 19:41:05 -07:00
2026-03-19 19:43:57 -07:00
/* Setup the framebuffer with the saved width/height */
static void backbuffer_setup ( struct _MwLLWayland * wayland ) ;
/* Destroy the backbuffer */
static void backbuffer_destroy ( struct _MwLLWayland * handle ) ;
2025-12-18 23:32:51 -07:00
static void buffer_destroy ( struct _MwLLWaylandShmBuffer * buffer ) ;
2025-12-18 11:26:51 -07:00
static void region_setup ( MwLL handle ) ;
static void region_invalidate ( MwLL handle ) ;
2026-03-26 17:48:22 -07:00
static void update_buffer ( MwLL self , struct _MwLLWaylandShmBuffer * buffer ) ;
2026-04-26 13:23:33 -07:00
static void setup_clipboard ( MwLL self , struct wl_seat * wl_seat ) ;
2026-04-26 13:44:11 -07:00
static void setup_zwp_clipboard ( MwLL self , struct wl_seat * wl_seat ) ;
static int event_loop ( MwLL handle ) ;
2026-04-26 13:23:33 -07:00
2025-12-09 20:11:01 -06:00
/* Get the registered interface from r, or NULL if it doesn't currently have it. */
# define WAYLAND_GET_INTERFACE(handle, inter) shget(handle.wl_protocol_map, inter##_interface.name)
/* `wl_registry.global` callback */
static void new_protocol ( void * data , struct wl_registry * registry ,
MwU32 name , const char * interface ,
MwU32 version ) {
2026-01-04 11:40:34 -07:00
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) version ;
( void ) registry ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-09 20:11:01 -06:00
2026-01-04 11:40:34 -07:00
wayland_protocol_callback_table_t * cb = shget ( self - > wayland . wl_protocol_setup_map , interface ) ;
2025-12-18 23:32:51 -07:00
if ( cb ! = NULL ) {
2026-04-23 14:02:33 -07:00
shput ( self - > wayland . wl_protocol_map , interface , cb - > setup ( name , data ) ) ;
2026-03-26 17:48:22 -07:00
/* we don't care for adding this protocol, we just use it to know if the compositor will let us have transparent surfaces */
} else if ( strcmp ( interface , " wp_alpha_modifier_v1 " ) = = 0 ) {
self - > common . supports_transparency = MwTRUE ;
2025-12-09 20:11:01 -06:00
} else {
2026-03-26 17:48:22 -07:00
// printf("unknown interface %s\n", interface);
2025-12-09 20:11:01 -06:00
}
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `wl_registry.global_remove` callback */
static void protocol_removed ( void * data ,
struct wl_registry * registry ,
MwU32 name ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) registry ;
( void ) name ;
2025-12-18 23:32:51 -07:00
printf ( " %d destroyed \n " , name ) ;
2025-12-09 20:11:01 -06:00
} ;
2025-12-30 17:12:12 -07:00
/* Flush Wayland events */
static void wl_flush ( MwLL handle ) {
struct pollfd pfd ;
int rc ;
pfd . fd = wl_display_get_fd ( handle - > wayland . display ) ;
pfd . events = POLLIN ;
while ( MwTRUE ) {
rc = wl_display_flush ( handle - > wayland . display ) ;
if ( errno ! = EAGAIN | | rc ! = - 1 ) {
break ;
}
if ( poll ( & pfd , 1 , - 1 ) = = - 1 ) {
break ;
}
}
}
2026-03-27 15:14:04 -07:00
/* Recursively dispatch a resize event to a widget and its children */
2026-03-24 14:43:38 -07:00
static void recursive_dispatch_resize ( MwLL handle ) {
2026-03-24 13:31:49 -07:00
MwWidget h = ( MwWidget ) handle - > common . user ;
if ( h ) {
int i ;
for ( i = 0 ; i < arrlen ( h - > children ) ; i + + ) {
MwDispatch ( h - > children [ i ] , resize ) ;
if ( arrlen ( h - > children [ i ] - > children ) > 0 ) {
2026-03-24 14:43:38 -07:00
recursive_dispatch_resize ( h - > children [ i ] - > lowlevel ) ;
2026-03-24 13:31:49 -07:00
}
}
}
} ;
2026-03-27 17:15:59 -07:00
/* Recursively dispatch a move event to a widget and its children */
2026-04-17 01:36:34 +09:00
static void recursive_dispatch_move ( MwLL handle , MwMouse * p ) {
2026-03-27 15:14:04 -07:00
MwWidget h = ( MwWidget ) handle - > common . user ;
if ( h ) {
int i ;
for ( i = 0 ; i < arrlen ( h - > children ) ; i + + ) {
2026-03-27 17:15:59 -07:00
MwLLDispatch ( h - > children [ i ] - > lowlevel , move , p ) ;
2026-03-27 15:14:04 -07:00
if ( arrlen ( h - > children [ i ] - > children ) > 0 ) {
2026-03-27 17:15:59 -07:00
recursive_dispatch_move ( h - > children [ i ] - > lowlevel , p ) ;
2026-03-27 15:14:04 -07:00
}
}
}
} ;
2026-04-24 09:09:37 +09:00
static void recursive_render ( MwLL handle ) {
int i ;
WIDGET_CHECK ( handle ) ;
for ( i = 0 ; i < arrlen ( ( ( MwWidget ) handle - > common . user ) - > children ) ; i + + ) recursive_render ( ( ( MwWidget ) handle - > common . user ) - > children [ i ] - > lowlevel ) ;
MwLLForceRender ( handle ) ;
}
2025-12-30 17:12:12 -07:00
static void wl_offer ( void * data ,
struct wl_data_offer * wl_data_offer ,
const char * mime_type ) {
wl_clipboard_device_context_t * self = data ;
wl_data_offer_accept ( wl_data_offer , self - > ll - > wayland . clipboard_serial , mime_type ) ;
2025-12-18 17:46:23 -07:00
} ;
2025-12-30 17:12:12 -07:00
struct wl_data_offer_listener offer_listener = {
. offer = wl_offer ,
2025-12-18 17:46:23 -07:00
} ;
2025-12-30 17:12:12 -07:00
static void wl_data_device_data_offer ( void * data ,
struct wl_data_device * wl_data_device ,
struct wl_data_offer * offer ) {
wl_clipboard_device_context_t * self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_data_device ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
wl_data_offer_add_listener ( offer , & offer_listener , data ) ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
self - > offer . wl = offer ;
} ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
static void wl_data_device_enter ( void * data ,
struct wl_data_device * wl_data_device ,
uint32_t serial ,
struct wl_surface * surface ,
wl_fixed_t x ,
wl_fixed_t y ,
struct wl_data_offer * id ) {
2026-01-04 11:40:34 -07:00
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_data_device ;
( void ) surface ;
( void ) x ;
( void ) y ;
( void ) id ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-30 17:12:12 -07:00
self - > wayland . clipboard_serial = serial ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-30 17:12:12 -07:00
} ;
static void wl_data_device_leave ( void * data ,
struct wl_data_device * wl_data_device ) {
2026-01-04 11:40:34 -07:00
MwLL self = data ;
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-30 17:12:12 -07:00
wl_data_device_destroy ( wl_data_device ) ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-30 17:12:12 -07:00
} ;
static void wl_data_device_motion ( void * data ,
struct wl_data_device * wl_data_device ,
uint32_t time ,
wl_fixed_t x ,
wl_fixed_t y ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) wl_data_device ;
( void ) time ;
( void ) x ;
( void ) y ;
2025-12-30 17:12:12 -07:00
} ;
static void wl_data_device_drop ( void * data ,
struct wl_data_device * wl_data_device ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) wl_data_device ;
2025-12-30 17:12:12 -07:00
} ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
static void wl_data_device_selection ( void * data ,
struct wl_data_device * wl_data_device ,
struct wl_data_offer * id ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) wl_data_device ;
( void ) id ;
2025-12-18 17:46:23 -07:00
} ;
2025-12-30 17:12:12 -07:00
struct wl_data_device_listener wl_data_device_listener = {
. data_offer = wl_data_device_data_offer ,
. enter = wl_data_device_enter ,
. leave = wl_data_device_leave ,
. motion = wl_data_device_motion ,
. drop = wl_data_device_drop ,
. selection = wl_data_device_selection ,
2025-12-18 17:46:23 -07:00
} ;
2025-12-30 17:12:12 -07:00
static void zwp_primary_selection_device_v1_data_offer ( void * data ,
struct zwp_primary_selection_device_v1 * zwp_primary_selection_device_v1 ,
struct zwp_primary_selection_offer_v1 * offer ) {
wl_clipboard_device_context_t * self = data ;
2026-03-24 17:38:21 -07:00
( void ) zwp_primary_selection_device_v1 ;
2025-12-30 17:12:12 -07:00
self - > offer . zwp = offer ;
} ;
static void zwp_primary_selection_device_v1_selection ( void * data ,
struct zwp_primary_selection_device_v1 * zwp_primary_selection_device_v1 ,
struct zwp_primary_selection_offer_v1 * id ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) zwp_primary_selection_device_v1 ;
( void ) id ;
2025-12-30 17:12:12 -07:00
} ;
struct zwp_primary_selection_device_v1_listener zwp_primary_selection_device_v1_listener = {
. data_offer = zwp_primary_selection_device_v1_data_offer ,
. selection = zwp_primary_selection_device_v1_selection ,
} ;
2026-04-15 10:58:46 -07:00
static void wl_clipboard_read ( wl_clipboard_device_context_t * ctx , int clipboard_type ) {
2025-12-30 17:12:12 -07:00
int fds [ 2 ] ;
fd_set set ;
char * buf = NULL ;
struct timeval timeout ;
int rc = 0 ;
if ( pipe ( fds ) ! = 0 ) {
return ;
}
FD_ZERO ( & set ) ;
FD_SET ( fds [ 0 ] , & set ) ;
timeout . tv_sec = 0 ;
timeout . tv_usec = 100 ;
2026-04-26 13:23:33 -07:00
if ( clipboard_type = = MwCLIPBOARD_PRIMARY & & ctx - > offer . zwp ) {
2025-12-30 17:12:12 -07:00
zwp_primary_selection_offer_v1_receive ( ctx - > offer . zwp , " text/plain " , fds [ 1 ] ) ;
2026-03-23 15:45:48 -07:00
} else if ( ctx - > offer . wl ) {
2025-12-30 17:12:12 -07:00
wl_data_offer_receive ( ctx - > offer . wl , " text/plain " , fds [ 1 ] ) ;
}
close ( fds [ 1 ] ) ;
wl_flush ( ctx - > ll ) ;
wl_display_roundtrip ( ctx - > ll - > wayland . display ) ;
while ( MwTRUE ) {
rc = select ( fds [ 0 ] + 1 , & set , NULL , NULL , & timeout ) ;
if ( rc < = 0 ) {
break ;
} else {
char b ;
ssize_t n = read ( fds [ 0 ] , & b , sizeof ( b ) ) ;
if ( n < = 0 ) {
break ;
}
arrpush ( buf , b ) ;
}
}
arrpush ( buf , 0 ) ;
close ( fds [ 0 ] ) ;
MwLLDispatch ( ctx - > ll , clipboard , buf ) ;
arrfree ( buf ) ;
2026-03-23 12:05:32 -07:00
ctx - > ll - > wayland . events_pending = 1 ;
2025-12-30 17:12:12 -07:00
}
static void wl_data_source_listener_target ( void * data ,
struct wl_data_source * wl_data_source ,
const char * mime_type ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) wl_data_source ;
( void ) mime_type ;
2025-12-30 17:12:12 -07:00
} ;
static void wl_data_source_listener_send ( void * data ,
struct wl_data_source * wl_data_source ,
const char * mime_type ,
int32_t fd ) {
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_data_source ;
( void ) mime_type ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-30 17:12:12 -07:00
if ( self - > wayland . clipboard_buffer ! = NULL ) {
write ( fd , self - > wayland . clipboard_buffer , strlen ( self - > wayland . clipboard_buffer ) ) ;
close ( fd ) ;
}
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-30 17:12:12 -07:00
} ;
static void wl_data_source_listener_cancelled ( void * data ,
struct wl_data_source * wl_data_source ) ;
struct wl_data_source_listener wl_data_source_listener = {
. target = wl_data_source_listener_target ,
. send = wl_data_source_listener_send ,
. cancelled = wl_data_source_listener_cancelled ,
} ;
static void wl_data_source_listener_cancelled ( void * data ,
struct wl_data_source * wl_data_source ) {
MwLL self = data ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2026-04-26 20:01:03 -07:00
if ( wl_data_source )
wl_data_source_destroy ( wl_data_source ) ;
2025-12-30 17:12:12 -07:00
self - > wayland . clipboard_source . wl = wl_data_device_manager_create_data_source ( self - > wayland . clipboard_manager . wl ) ;
wl_data_source_offer ( self - > wayland . clipboard_source . wl , " text/plain " ) ;
wl_data_source_offer ( self - > wayland . clipboard_source . wl , " text/plain;charset=utf-8 " ) ;
wl_data_source_offer ( self - > wayland . clipboard_source . wl , " TEXT " ) ;
wl_data_source_offer ( self - > wayland . clipboard_source . wl , " STRING " ) ;
wl_data_source_offer ( self - > wayland . clipboard_source . wl , " UTF8_STRING " ) ;
wl_data_source_add_listener ( self - > wayland . clipboard_source . wl , & wl_data_source_listener , self ) ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-30 17:12:12 -07:00
} ;
static void zwp_primary_selection_source_v1_send ( void * data ,
struct zwp_primary_selection_source_v1 * wl_data_source ,
const char * mime_type ,
int32_t fd ) {
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_data_source ;
( void ) mime_type ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-30 17:12:12 -07:00
if ( self - > wayland . clipboard_buffer ! = NULL ) {
write ( fd , self - > wayland . clipboard_buffer , strlen ( self - > wayland . clipboard_buffer ) ) ;
close ( fd ) ;
}
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-30 17:12:12 -07:00
} ;
static void zwp_primary_selection_source_v1_cancelled ( void * data ,
struct zwp_primary_selection_source_v1 * wl_data_source ) {
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_data_source ;
2025-12-30 17:12:12 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-30 17:12:12 -07:00
zwp_primary_selection_source_v1_destroy ( self - > wayland . clipboard_source . zwp ) ;
self - > wayland . clipboard_source . zwp = zwp_primary_selection_device_manager_v1_create_source ( self - > wayland . clipboard_manager . zwp ) ;
zwp_primary_selection_source_v1_offer ( self - > wayland . clipboard_source . zwp , " text/plain " ) ;
zwp_primary_selection_source_v1_offer ( self - > wayland . clipboard_source . zwp , " text/plain;charset=utf-8 " ) ;
zwp_primary_selection_source_v1_offer ( self - > wayland . clipboard_source . zwp , " TEXT " ) ;
zwp_primary_selection_source_v1_offer ( self - > wayland . clipboard_source . zwp , " STRING " ) ;
zwp_primary_selection_source_v1_offer ( self - > wayland . clipboard_source . zwp , " UTF8_STRING " ) ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-30 17:12:12 -07:00
} ;
struct zwp_primary_selection_source_v1_listener zwp_primary_selection_source_v1_listener = {
. send = zwp_primary_selection_source_v1_send ,
. cancelled = zwp_primary_selection_source_v1_cancelled ,
} ;
/* wl_data_device_manager setup function */
static wayland_protocol_t * wl_data_device_manager_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland - > clipboard_manager . wl = wl_registry_bind ( wayland - > registry , name , & wl_data_device_manager_interface , 2 ) ;
wayland - > clipboard_source . wl = wl_data_device_manager_create_data_source ( wayland - > clipboard_manager . wl ) ;
wl_data_source_offer ( wayland - > clipboard_source . wl , " text/plain " ) ;
wl_data_source_offer ( wayland - > clipboard_source . wl , " text/plain;charset=utf-8 " ) ;
wl_data_source_offer ( wayland - > clipboard_source . wl , " TEXT " ) ;
wl_data_source_offer ( wayland - > clipboard_source . wl , " STRING " ) ;
wl_data_source_offer ( wayland - > clipboard_source . wl , " UTF8_STRING " ) ;
wl_data_source_add_listener ( wayland - > clipboard_source . wl , & wl_data_source_listener , wayland ) ;
2026-04-26 13:44:11 -07:00
while ( ! wayland - > pointer_seat ) {
if ( wl_display_roundtrip ( wayland - > display ) = = - 1 ) {
printf ( " roundtrip failed: %d \n " , wl_display_get_error ( wayland - > display ) ) ;
raise ( SIGTRAP ) ;
return NULL ;
}
}
2026-04-26 13:23:33 -07:00
if ( wayland - > pointer_seat ) {
setup_clipboard ( ( MwLL ) wayland , wayland - > pointer_seat ) ;
}
2025-12-30 17:12:12 -07:00
return NULL ;
}
static void wl_data_device_manager_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
2026-04-15 10:58:46 -07:00
if ( wayland - > clipboard_manager . wl ! = NULL ) {
2026-01-02 14:19:30 -07:00
wl_data_device_manager_destroy ( wayland - > clipboard_manager . wl ) ;
wayland - > clipboard_manager . wl = NULL ;
}
2026-04-26 20:01:03 -07:00
if ( wayland - > clipboard_source . wl ) {
// wl_data_source_destroy(wayland->clipboard_source.wl);
wayland - > clipboard_source . wl = NULL ;
}
2025-12-30 17:12:12 -07:00
}
2025-12-18 17:46:23 -07:00
/* zwp_primary_selection_device_manager_v1 setup function */
static wayland_protocol_t * zwp_primary_selection_device_manager_v1_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
2025-12-30 17:12:12 -07:00
wayland - > clipboard_manager . zwp = wl_registry_bind ( wayland - > registry , name , & zwp_primary_selection_device_manager_v1_interface , 1 ) ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
wayland - > clipboard_source . zwp = zwp_primary_selection_device_manager_v1_create_source ( wayland - > clipboard_manager . zwp ) ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
zwp_primary_selection_source_v1_offer ( wayland - > clipboard_source . zwp , " text/plain " ) ;
zwp_primary_selection_source_v1_offer ( wayland - > clipboard_source . zwp , " text/plain;charset=utf-8 " ) ;
zwp_primary_selection_source_v1_offer ( wayland - > clipboard_source . zwp , " TEXT " ) ;
zwp_primary_selection_source_v1_offer ( wayland - > clipboard_source . zwp , " STRING " ) ;
zwp_primary_selection_source_v1_offer ( wayland - > clipboard_source . zwp , " UTF8_STRING " ) ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
zwp_primary_selection_source_v1_add_listener ( wayland - > clipboard_source . zwp , & zwp_primary_selection_source_v1_listener , wayland ) ;
2025-12-18 17:46:23 -07:00
2025-12-30 17:12:12 -07:00
wayland - > supports_zwp = MwTRUE ;
2025-12-18 17:46:23 -07:00
2026-04-26 13:44:11 -07:00
while ( ! wayland - > pointer_seat ) {
if ( wl_display_roundtrip ( wayland - > display ) = = - 1 ) {
printf ( " roundtrip failed: %d \n " , wl_display_get_error ( wayland - > display ) ) ;
raise ( SIGTRAP ) ;
return NULL ;
}
}
if ( wayland - > pointer_seat ) {
setup_zwp_clipboard ( ( MwLL ) wayland , wayland - > pointer_seat ) ;
}
2025-12-30 17:12:12 -07:00
return NULL ;
2025-12-18 17:46:23 -07:00
}
2025-12-18 23:32:51 -07:00
static void zwp_primary_selection_device_manager_v1_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
( void ) data ;
2025-12-18 23:32:51 -07:00
}
2026-03-24 23:44:30 -07:00
/* zwp_primary_selection_device_manager_v1 setup function */
static wayland_protocol_t * zwp_pointer_constraints_v1_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland - > pointer_constraints = wl_registry_bind ( wayland - > registry , name , & zwp_pointer_constraints_v1_interface , 1 ) ;
return NULL ;
}
static void zwp_pointer_constraints_v1_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
( void ) wayland ;
( void ) data ;
}
2026-04-23 11:28:38 -07:00
static struct wl_surface * curSurface = NULL ;
2026-03-24 22:40:36 -07:00
2025-12-09 20:11:01 -06:00
/* `wl_pointer.enter` callback */
static void pointer_enter ( void * data , struct wl_pointer * wl_pointer , MwU32 serial ,
struct wl_surface * surface , wl_fixed_t surface_x ,
2025-12-18 11:26:51 -07:00
wl_fixed_t surface_y ) {
2026-04-23 13:32:05 -07:00
MwLL self = data ;
MwLL topmost_parent = self ;
2026-03-24 17:38:21 -07:00
( void ) surface_x ;
( void ) surface_y ;
2026-04-23 14:47:51 -07:00
( void ) wl_pointer ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2026-04-23 13:32:05 -07:00
while ( topmost_parent - > wayland . parent ) topmost_parent = topmost_parent - > wayland . parent ;
2026-03-24 22:40:36 -07:00
curSurface = surface ;
2026-01-03 18:27:34 -07:00
2025-12-18 13:15:03 -07:00
self - > wayland . pointer_serial = serial ;
2026-04-23 13:32:05 -07:00
wl_pointer_set_cursor ( self - > wayland . pointer , self - > wayland . pointer_serial , topmost_parent - > wayland . cursor . surface , 0 , 0 ) ;
2025-12-18 13:15:03 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-18 11:26:51 -07:00
} ;
2025-12-09 20:11:01 -06:00
/* `wl_pointer.leave` callback */
static void pointer_leave ( void * data , struct wl_pointer * wl_pointer , MwU32 serial ,
2025-12-18 11:26:51 -07:00
struct wl_surface * surface ) {
2026-03-27 15:14:04 -07:00
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_pointer ;
( void ) serial ;
( void ) surface ;
2026-03-27 15:14:04 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
curSurface = NULL ;
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-18 11:26:51 -07:00
} ;
2025-12-09 20:11:01 -06:00
2026-04-17 01:36:34 +09:00
static void xdg_borderless_step ( MwLL self , MwMouse p , MwU32 serial ) {
2026-04-20 14:49:20 +09:00
if ( self - > wayland . type = = MwLL_WAYLAND_TOPLEVEL & & self - > wayland . backbuffer . surface = = curSurface ) {
2026-03-23 15:45:48 -07:00
if ( p . point . y > = ( self - > wayland . wh + CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM ) - 5 ) {
if ( p . point . x > = ( self - > wayland . ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT ) - 5 ) {
2026-03-20 19:28:18 -07:00
xdg_toplevel_resize ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial , XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT ) ;
} else {
xdg_toplevel_resize ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial , XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM ) ;
}
} else if ( p . point . y < = 5 ) {
2026-03-23 15:45:48 -07:00
if ( p . point . x > = ( self - > wayland . ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT ) - 5 ) {
2026-03-20 19:28:18 -07:00
xdg_toplevel_resize ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial , XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT ) ;
} else {
xdg_toplevel_resize ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial , XDG_TOPLEVEL_RESIZE_EDGE_TOP ) ;
}
2026-03-23 15:45:48 -07:00
} else if ( p . point . x > = ( self - > wayland . ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT ) - 5 ) {
2026-03-20 19:28:18 -07:00
xdg_toplevel_resize ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial , XDG_TOPLEVEL_RESIZE_EDGE_RIGHT ) ;
} else if ( p . point . x < = 5 ) {
xdg_toplevel_resize ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial , XDG_TOPLEVEL_RESIZE_EDGE_LEFT ) ;
} else if ( p . point . y < = CSD_BORDER_FRAME_TOP ) {
xdg_toplevel_move ( self - > wayland . toplevel - > xdg_top_level , self - > wayland . pointer_seat , serial ) ;
}
} ;
}
2026-03-23 12:05:32 -07:00
static MwLL currentlyHeldWidget = NULL ;
2026-03-27 17:15:59 -07:00
static void relative_pointer_motion ( void * data ,
struct zwp_relative_pointer_v1 * pointer ,
uint32_t timeHi ,
uint32_t timeLo ,
wl_fixed_t dx ,
wl_fixed_t dy ,
wl_fixed_t dxUnaccel ,
wl_fixed_t dyUnaccel ) {
2026-04-17 01:36:34 +09:00
MwLL self = data ;
MwMouse p ;
2026-03-27 17:15:59 -07:00
2026-04-16 13:22:21 -07:00
( void ) pointer ;
( void ) timeHi ;
( void ) timeLo ;
( void ) dx ;
( void ) dy ;
2026-03-27 17:15:59 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
p . point . x = wl_fixed_to_int ( dxUnaccel ) ;
p . point . y = wl_fixed_to_int ( dyUnaccel ) ;
recursive_dispatch_move ( self , & p ) ;
2026-03-27 18:06:17 -07:00
if ( self - > wayland . locked_pointer ) zwp_locked_pointer_v1_set_cursor_position_hint ( self - > wayland . locked_pointer , 0 , CSD_BORDER_FRAME_TOP ) ;
2026-03-27 17:15:59 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
}
struct zwp_relative_pointer_v1_listener relative_pointer_listener =
{
relative_pointer_motion } ;
/* zwp_primary_selection_device_manager_v1 setup function */
static wayland_protocol_t * zwp_relative_pointer_manager_v1_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland - > relative_pointer_manager = wl_registry_bind ( wayland - > registry , name , & zwp_relative_pointer_manager_v1_interface , 1 ) ;
return NULL ;
}
static void zwp_relative_pointer_manager_v1_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
( void ) wayland ;
( void ) data ;
}
2025-12-09 20:11:01 -06:00
/* `wl_pointer.motion` callback */
static void pointer_motion ( void * data , struct wl_pointer * wl_pointer , MwU32 time ,
wl_fixed_t surface_x , wl_fixed_t surface_y ) {
2026-04-17 01:36:34 +09:00
MwLL self = data ;
MwLL topmost_parent = self ;
MwMouse p ;
2026-04-23 13:32:05 -07:00
MwBool inArea = MwFALSE ;
2026-03-24 17:38:21 -07:00
( void ) time ;
( void ) wl_pointer ;
2025-12-09 20:11:01 -06:00
2026-03-27 18:06:17 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
while ( topmost_parent - > wayland . parent ) topmost_parent = topmost_parent - > wayland . parent ;
2026-01-03 18:27:34 -07:00
2026-03-17 20:28:58 -07:00
self - > wayland . cur_mouse_pos . x = wl_fixed_to_int ( surface_x ) ;
self - > wayland . cur_mouse_pos . y = wl_fixed_to_int ( surface_y ) ;
2026-03-27 18:06:17 -07:00
p . point = self - > wayland . cur_mouse_pos ;
2025-12-09 20:11:01 -06:00
MwLLDispatch ( self , move , & p ) ;
2025-12-30 22:26:19 -07:00
2026-03-23 12:05:32 -07:00
if ( currentlyHeldWidget ) {
MwLLDispatch ( currentlyHeldWidget , down , & p ) ;
2026-03-17 20:28:58 -07:00
}
2026-03-23 12:05:32 -07:00
2026-04-23 13:32:05 -07:00
if ( self - > wayland . framebuffer . surface ) {
inArea | = self - > wayland . framebuffer . surface = = curSurface ;
}
if ( self - > wayland . backbuffer . surface ) {
inArea | = self - > wayland . backbuffer . surface = = curSurface ;
}
if ( inArea ) {
wl_pointer_set_cursor ( self - > wayland . pointer , self - > wayland . pointer_serial , self - > wayland . cursor . surface , 0 , 0 ) ;
}
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `wl_pointer.button` callback */
static void pointer_button ( void * data , struct wl_pointer * wl_pointer , MwU32 serial , MwU32 time , MwU32 button , MwU32 state ) {
2026-04-17 01:36:34 +09:00
MwLL self = data ;
MwMouse p ;
MwBool inArea = MwFALSE ;
2026-01-03 18:27:34 -07:00
2026-03-24 17:38:21 -07:00
( void ) wl_pointer ;
( void ) serial ;
( void ) time ;
2026-03-17 20:28:58 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2026-01-03 18:27:34 -07:00
2025-12-09 20:11:01 -06:00
p . point = self - > wayland . cur_mouse_pos ;
2026-03-17 20:28:58 -07:00
2026-03-27 15:14:04 -07:00
if ( self - > wayland . framebuffer . surface ) {
inArea | = self - > wayland . framebuffer . surface = = curSurface ;
}
if ( self - > wayland . backbuffer . surface ) {
inArea | = self - > wayland . backbuffer . surface = = curSurface ;
}
if ( inArea ) {
2025-12-09 20:11:01 -06:00
switch ( button ) {
case BTN_LEFT :
2026-04-17 01:36:34 +09:00
p . button = MwMOUSE_LEFT ;
2025-12-09 20:11:01 -06:00
break ;
case BTN_MIDDLE :
2026-04-17 01:36:34 +09:00
p . button = MwMOUSE_MIDDLE ;
2025-12-09 20:11:01 -06:00
break ;
case BTN_RIGHT :
2026-04-17 01:36:34 +09:00
p . button = MwMOUSE_RIGHT ;
2025-12-09 20:11:01 -06:00
break ;
}
2026-03-20 19:28:18 -07:00
self - > wayland . held_down = state = = WL_POINTER_BUTTON_STATE_PRESSED ;
2025-12-09 20:11:01 -06:00
switch ( state ) {
case WL_POINTER_BUTTON_STATE_PRESSED :
MwLLDispatch ( self , down , & p ) ;
2026-03-23 12:05:32 -07:00
currentlyHeldWidget = self ;
2026-03-20 19:28:18 -07:00
2025-12-09 20:11:01 -06:00
break ;
case WL_POINTER_BUTTON_STATE_RELEASED :
2026-03-23 12:05:32 -07:00
if ( currentlyHeldWidget ! = NULL ) {
MwLLDispatch ( currentlyHeldWidget , up , & p ) ;
currentlyHeldWidget = NULL ;
2026-01-12 18:50:33 -07:00
} else {
MwLLDispatch ( self , up , & p ) ;
2026-01-12 18:18:05 -07:00
}
2025-12-09 20:11:01 -06:00
break ;
}
}
2025-12-17 19:14:54 -07:00
2026-03-20 19:28:18 -07:00
if ( ! self - > wayland . has_decorations ) {
xdg_borderless_step ( self , p , serial ) ;
}
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `wl_pointer.axis` callback */
static void pointer_axis ( void * data , struct wl_pointer * wl_pointer , MwU32 time ,
2026-03-24 17:38:21 -07:00
MwU32 axis , wl_fixed_t value ) {
( void ) data ;
( void ) wl_pointer ;
( void ) time ;
( void ) axis ;
( void ) value ;
} ;
2025-12-09 20:11:01 -06:00
struct wl_pointer_listener pointer_listener = {
. enter = pointer_enter ,
. leave = pointer_leave ,
. motion = pointer_motion ,
. button = pointer_button ,
. axis = pointer_axis ,
} ;
/* `wl_keyboard.keymap` callback */
static void keyboard_keymap ( void * data ,
struct wl_keyboard * wl_keyboard ,
MwU32 format ,
int32_t fd ,
MwU32 size ) {
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_keyboard ;
2026-01-04 11:40:34 -07:00
2026-04-20 14:49:20 +09:00
if ( self - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2025-12-09 20:11:01 -06:00
assert ( format = = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 ) ;
2026-04-17 13:12:22 -07:00
char * map_shm = ( char * ) mmap ( NULL , size , PROT_READ , MAP_SHARED , fd , 0 ) ;
2025-12-09 20:11:01 -06:00
assert ( map_shm ! = MAP_FAILED ) ;
2026-04-17 13:12:22 -07:00
struct xkb_keymap * xkb_keymap = xkb_keymap_new_from_string (
2026-03-27 15:14:04 -07:00
self - > wayland . xkb_context , map_shm , XKB_KEYMAP_FORMAT_TEXT_V1 ,
2025-12-09 20:11:01 -06:00
XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
munmap ( map_shm , size ) ;
close ( fd ) ;
2026-04-17 13:12:22 -07:00
struct xkb_state * xkb_state = xkb_state_new ( xkb_keymap ) ;
2025-12-09 20:11:01 -06:00
2026-03-27 15:14:04 -07:00
self - > wayland . xkb_keymap = xkb_keymap ;
self - > wayland . xkb_state = xkb_state ;
} else {
self - > wayland . xkb_keymap = self - > wayland . parent - > wayland . xkb_keymap ;
self - > wayland . xkb_state = self - > wayland . parent - > wayland . xkb_state ;
2025-12-09 20:11:01 -06:00
}
} ;
/* `wl_keyboard.enter` callback */
static void keyboard_enter ( void * data ,
struct wl_keyboard * wl_keyboard ,
MwU32 serial ,
struct wl_surface * surface ,
struct wl_array * keys ) {
2026-01-04 11:40:34 -07:00
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_keyboard ;
( void ) surface ;
( void ) keys ;
2026-01-03 18:27:34 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2026-01-03 18:27:34 -07:00
2025-12-30 17:12:12 -07:00
self - > wayland . keyboard_serial = serial ;
2025-12-09 20:11:01 -06:00
MwLLDispatch ( self , focus_in , NULL ) ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `wl_keyboard.leave` callback */
static void keyboard_leave ( void * data ,
struct wl_keyboard * wl_keyboard ,
MwU32 serial ,
struct wl_surface * surface ) {
2026-01-04 11:40:34 -07:00
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_keyboard ;
( void ) serial ;
( void ) surface ;
2026-01-03 18:27:34 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2026-01-03 18:27:34 -07:00
2025-12-09 20:11:01 -06:00
MwLLDispatch ( self , focus_out , NULL ) ;
2026-01-03 18:27:34 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `wl_keyboard.key` callback */
static void keyboard_key ( void * data ,
struct wl_keyboard * wl_keyboard ,
MwU32 serial ,
MwU32 time ,
MwU32 key ,
MwU32 state ) {
2026-04-16 13:22:21 -07:00
MwLL self = data ;
MwBool inArea = 0 ;
2026-03-27 15:14:04 -07:00
2026-03-24 17:38:21 -07:00
( void ) wl_keyboard ;
( void ) serial ;
( void ) time ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2026-01-03 18:27:34 -07:00
2026-03-27 15:14:04 -07:00
if ( self - > wayland . framebuffer . surface ) {
inArea | = self - > wayland . framebuffer . surface = = curSurface ;
}
2026-03-27 18:06:17 -07:00
if ( self - > wayland . backbuffer . surface ) {
inArea | = self - > wayland . backbuffer . surface = = curSurface ;
}
2026-03-27 15:14:04 -07:00
if ( inArea ) {
xkb_layout_index_t layout ;
MwU32 levels ;
2026-04-16 13:22:21 -07:00
xkb_level_index_t level = 0 ;
2026-03-27 15:14:04 -07:00
const xkb_keysym_t * syms_out ;
MwU32 keycode = key + 8 ;
MwU64 syms_num ;
int i ;
if ( ! self - > wayland . xkb_keymap ) {
2026-04-23 10:45:29 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
return ;
}
2026-03-27 15:14:04 -07:00
layout = xkb_state_key_get_layout ( self - > wayland . xkb_state , keycode ) ;
levels =
xkb_keymap_num_levels_for_key ( self - > wayland . xkb_keymap , keycode , layout ) ;
if ( ( ( ( self - > wayland . mod_state & 1 ) = = 1 ) | | ( ( self - > wayland . mod_state & 2 ) = = 2 ) ) & & levels > = 2 ) {
level = 1 ;
} else if ( levels > = 1 ) {
level = 0 ;
}
syms_num = xkb_keymap_key_get_syms_by_level ( self - > wayland . xkb_keymap , keycode , layout , level , & syms_out ) ;
2025-12-09 20:11:01 -06:00
if ( syms_out = = NULL ) {
2026-04-23 10:45:29 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
return ;
}
for ( i = 0 ; i < syms_num ; i + + ) {
2026-04-16 13:22:21 -07:00
int sym = syms_out [ i ] ;
int key = - 1 ;
2026-03-27 15:14:04 -07:00
2025-12-09 20:11:01 -06:00
switch ( sym ) {
case XKB_KEY_BackSpace :
2026-04-20 00:18:04 +09:00
key = MwKEY_BACKSPACE ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Left :
2026-04-20 00:18:04 +09:00
key = MwKEY_LEFT ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Right :
2026-04-20 00:18:04 +09:00
key = MwKEY_RIGHT ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Up :
2026-04-20 00:18:04 +09:00
key = MwKEY_UP ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Down :
2026-04-20 00:18:04 +09:00
key = MwKEY_DOWN ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Return :
2026-04-20 00:18:04 +09:00
key = MwKEY_ENTER ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Escape :
2026-04-20 00:18:04 +09:00
key = MwKEY_ESCAPE ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Shift_L :
2026-04-20 00:18:04 +09:00
key = MwKEY_LEFTSHIFT ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Shift_R :
2026-04-20 00:18:04 +09:00
key = MwKEY_RIGHTSHIFT ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Alt_L :
case XKB_KEY_Alt_R :
2026-04-20 00:18:04 +09:00
key = MwKEY_ALT ;
2025-12-09 20:11:01 -06:00
break ;
case XKB_KEY_Control_L :
case XKB_KEY_Control_R :
2026-04-20 00:18:04 +09:00
key = MwKEY_CONTROL ;
2025-12-09 20:11:01 -06:00
break ;
default :
2026-03-31 12:36:31 -07:00
if ( MwStringIsKeyUTF8 ( sym ) ) {
2026-03-27 15:14:04 -07:00
key = sym ;
}
2025-12-09 20:11:01 -06:00
}
2026-03-27 15:14:04 -07:00
2025-12-18 11:36:04 -07:00
if ( ( self - > wayland . mod_state & 4 ) = = 4 ) {
2026-04-20 00:23:04 +09:00
key | = MwKEY_CONTROL_FLAG ;
2025-12-18 11:36:04 -07:00
}
if ( ( self - > wayland . mod_state & 8 ) = = 8 ) {
2026-04-20 00:23:04 +09:00
key | = MwKEY_ALT_FLAG ;
2025-12-18 11:36:04 -07:00
}
2025-12-30 17:12:12 -07:00
2026-03-27 15:14:04 -07:00
if ( key ! = - 1 ) {
if ( state = = WL_KEYBOARD_KEY_STATE_PRESSED ) {
MwLLDispatch ( self , key , & key ) ;
} else {
MwLLDispatch ( self , key_released , & key ) ;
}
2025-12-09 20:11:01 -06:00
}
}
}
2025-12-30 22:26:19 -07:00
2026-03-24 23:24:20 -07:00
if ( ! MwWaylandAlwaysRender ) {
2025-12-30 22:26:19 -07:00
MwLLDispatch ( self , draw , NULL ) ;
}
2026-01-03 18:27:34 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `wl_keyboard.modifiers` callback */
static void keyboard_modifiers ( void * data ,
struct wl_keyboard * wl_keyboard ,
MwU32 serial ,
MwU32 mods_depressed ,
MwU32 mods_latched ,
MwU32 mods_locked ,
2025-12-18 11:36:04 -07:00
MwU32 group ) {
2026-01-04 11:40:34 -07:00
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) wl_keyboard ;
( void ) serial ;
( void ) group ;
( void ) mods_latched ;
2026-01-03 18:27:34 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-18 11:36:04 -07:00
self - > wayland . mod_state = 0 ;
self - > wayland . mod_state | = mods_depressed ;
self - > wayland . mod_state | = mods_locked ;
2026-01-03 18:27:34 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-18 11:36:04 -07:00
} ;
2025-12-09 20:11:01 -06:00
2026-04-26 13:44:11 -07:00
static void setup_zwp_clipboard ( MwLL self , struct wl_seat * wl_seat ) {
2026-04-26 13:23:33 -07:00
wl_clipboard_device_context_t * device_ctx_zwp = NULL ;
2026-04-26 13:44:11 -07:00
device_ctx_zwp = malloc ( sizeof ( wl_clipboard_device_context_t ) ) ;
memset ( device_ctx_zwp , 0 , sizeof ( wl_clipboard_device_context_t ) ) ;
device_ctx_zwp - > device . zwp = zwp_primary_selection_device_manager_v1_get_device ( self - > wayland . clipboard_manager . zwp , wl_seat ) ;
device_ctx_zwp - > ll = self ;
zwp_primary_selection_device_v1_add_listener ( device_ctx_zwp - > device . zwp , & zwp_primary_selection_device_v1_listener , device_ctx_zwp ) ;
arrpush ( self - > wayland . clipboard_devices_zwp , device_ctx_zwp ) ;
}
static void setup_clipboard ( MwLL self , struct wl_seat * wl_seat ) {
wl_clipboard_device_context_t * device_ctx_wl = malloc ( sizeof ( wl_clipboard_device_context_t ) ) ;
2026-04-26 13:23:33 -07:00
memset ( device_ctx_wl , 0 , sizeof ( wl_clipboard_device_context_t ) ) ;
device_ctx_wl - > device . wl = wl_data_device_manager_get_data_device ( self - > wayland . clipboard_manager . wl , wl_seat ) ;
device_ctx_wl - > ll = self ;
wl_data_device_add_listener ( device_ctx_wl - > device . wl , & wl_data_device_listener , device_ctx_wl ) ;
arrpush ( self - > wayland . clipboard_devices_wl , device_ctx_wl ) ;
} ;
2025-12-09 20:11:01 -06:00
struct wl_keyboard_listener keyboard_listener = {
. keymap = keyboard_keymap ,
. enter = keyboard_enter ,
. leave = keyboard_leave ,
. key = keyboard_key ,
. modifiers = keyboard_modifiers ,
} ;
/* `wl_seat.name` callback */
static void
2026-03-24 17:38:21 -07:00
wl_seat_name ( void * data , struct wl_seat * wl_seat , const char * name ) {
( void ) data ;
( void ) wl_seat ;
( void ) name ;
} ;
2025-12-09 20:11:01 -06:00
/* `wl_seat.capabilities` callback */
static void wl_seat_capabilities ( void * data , struct wl_seat * wl_seat ,
MwU32 capabilities ) {
2026-04-26 13:23:33 -07:00
MwLL self = data ;
2025-12-30 17:12:12 -07:00
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-09 20:11:01 -06:00
if ( capabilities & WL_SEAT_CAPABILITY_KEYBOARD ) {
2025-12-18 23:32:51 -07:00
self - > wayland . keyboard = wl_seat_get_keyboard ( wl_seat ) ;
wl_keyboard_add_listener ( self - > wayland . keyboard , & keyboard_listener , data ) ;
2025-12-09 20:11:01 -06:00
}
if ( capabilities & WL_SEAT_CAPABILITY_POINTER ) {
2026-03-20 19:28:18 -07:00
self - > wayland . pointer_seat = wl_seat ;
self - > wayland . pointer = wl_seat_get_pointer ( wl_seat ) ;
2025-12-18 13:15:03 -07:00
wl_pointer_add_listener ( self - > wayland . pointer , & pointer_listener , data ) ;
2025-12-09 20:11:01 -06:00
}
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
2025-12-18 13:43:45 -07:00
static void output_geometry ( void * data ,
struct wl_output * wl_output ,
int32_t x ,
int32_t y ,
int32_t physical_width ,
int32_t physical_height ,
int32_t subpixel ,
const char * make ,
const char * model ,
2026-03-24 17:38:21 -07:00
int32_t transform ) {
( void ) data ;
( void ) wl_output ;
( void ) x ;
( void ) y ;
( void ) physical_width ;
( void ) physical_height ;
( void ) subpixel ;
( void ) make ;
( void ) model ;
( void ) transform ;
} ;
2025-12-18 13:43:45 -07:00
static void output_mode ( void * data ,
struct wl_output * wl_output ,
uint32_t flags ,
int32_t width ,
int32_t height ,
int32_t refresh ) {
2026-03-24 17:38:21 -07:00
MwLL self = data ;
( void ) wl_output ;
( void ) flags ;
( void ) refresh ;
2025-12-18 13:43:45 -07:00
self - > wayland . mw = width ;
self - > wayland . mh = height ;
} ;
struct wl_output_listener output_listener = {
. geometry = output_geometry ,
. mode = output_mode ,
} ;
2025-12-09 20:11:01 -06:00
/* `xdg_wm_base.ping` callback */
void xdg_wm_base_ping ( void * data ,
struct xdg_wm_base * xdg_wm_base ,
MwU32 serial ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
2025-12-09 20:11:01 -06:00
xdg_wm_base_pong ( xdg_wm_base , serial ) ;
} ;
/* wl_seat setup function */
static wayland_protocol_t * wl_seat_setup ( MwU32 name , MwLL ll ) {
wayland_protocol_t * proto = malloc ( sizeof ( wayland_protocol_t ) ) ;
proto - > listener = malloc ( sizeof ( struct wl_seat_listener ) ) ;
( ( struct wl_seat_listener * ) proto - > listener ) - > name = wl_seat_name ;
( ( struct wl_seat_listener * ) proto - > listener ) - > capabilities = wl_seat_capabilities ;
2026-01-12 18:18:05 -07:00
wl_seat_add_listener ( wl_registry_bind ( ll - > wayland . registry , name , & wl_seat_interface , 1 ) , proto - > listener , ll ) ;
2025-12-09 20:11:01 -06:00
return proto ;
}
2025-12-18 23:32:51 -07:00
static void wl_seat_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
2025-12-18 23:32:51 -07:00
free ( data - > listener ) ;
}
2025-12-18 13:43:45 -07:00
/* wl_output setup function */
static wayland_protocol_t * wl_output_setup ( MwU32 name , MwLL ll ) {
ll - > wayland . output = wl_registry_bind ( ll - > wayland . registry , name , & wl_output_interface , 1 ) ;
wl_output_add_listener ( ll - > wayland . output , & output_listener , ll ) ;
return NULL ;
}
2025-12-18 23:32:51 -07:00
static void wl_output_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
( void ) data ;
2025-12-18 23:32:51 -07:00
}
2025-12-09 20:11:01 -06:00
/* wl_compositor setup function */
static wayland_protocol_t * wl_compositor_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland - > compositor = wl_registry_bind ( wayland - > registry , name , & wl_compositor_interface , 1 ) ;
return NULL ;
}
2025-12-18 23:32:51 -07:00
static void wl_compositor_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
( void ) data ;
2025-12-18 23:32:51 -07:00
}
2025-12-09 20:11:01 -06:00
/* wl_subcompositor setup function */
static wayland_protocol_t * wl_subcompositor_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
2026-04-20 14:49:20 +09:00
if ( wayland - > type = = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-19 19:43:57 -07:00
wayland - > toplevel - > scompositor = wl_registry_bind ( wayland - > registry , name , & wl_subcompositor_interface , 1 ) ;
} else {
wayland - > sublevel - > subcompositor = wl_registry_bind ( wayland - > registry , name , & wl_subcompositor_interface , 1 ) ;
}
2025-12-09 20:11:01 -06:00
return NULL ;
}
2025-12-18 23:32:51 -07:00
static void wl_subcompositor_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
2025-12-18 23:32:51 -07:00
wl_subcompositor_destroy ( wayland - > sublevel - > subcompositor ) ;
}
2025-12-09 20:11:01 -06:00
/* xdg_wm_base setup function */
static wayland_protocol_t * xdg_wm_base_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland_protocol_t * proto = malloc ( sizeof ( wayland_protocol_t ) ) ;
proto - > listener = malloc ( sizeof ( struct xdg_wm_base_listener ) ) ;
( ( struct xdg_wm_base_listener * ) proto - > listener ) - > ping = xdg_wm_base_ping ;
proto - > context = wl_registry_bind ( wayland - > registry , name , & xdg_wm_base_interface , 1 ) ;
xdg_wm_base_add_listener ( proto - > context , proto - > listener , wayland ) ;
return proto ;
}
2025-12-18 23:32:51 -07:00
static void xdg_wm_base_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
2025-12-18 23:32:51 -07:00
free ( data - > listener ) ;
}
2026-03-19 19:43:57 -07:00
/* xdg_wm_base setup function */
static wayland_protocol_t * wp_viewporter_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland_protocol_t * proto = malloc ( sizeof ( wayland_protocol_t ) ) ;
proto - > context = wl_registry_bind ( wayland - > registry , name , & wp_viewporter_interface , 1 ) ;
return proto ;
}
static void wp_viewporter_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
( void ) data ;
2026-03-19 19:43:57 -07:00
}
2025-12-09 20:11:01 -06:00
/* the two decoration manager constructs */
typedef struct zxdg_decoration_manager_v1_context {
struct zxdg_decoration_manager_v1 * manager ;
struct zxdg_toplevel_decoration_v1 * decoration ;
} zxdg_decoration_manager_v1_context_t ;
/* zxdg_decoration_manager_v1 setup function */
static wayland_protocol_t * zxdg_decoration_manager_v1_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland_protocol_t * proto = malloc ( sizeof ( wayland_protocol_t ) ) ;
zxdg_decoration_manager_v1_context_t * ctx = malloc ( sizeof ( zxdg_decoration_manager_v1_context_t ) ) ;
proto - > listener = NULL ;
ctx - > manager = wl_registry_bind ( wayland - > registry , name , & zxdg_decoration_manager_v1_interface , 1 ) ;
;
proto - > context = ctx ;
return proto ;
}
2025-12-18 23:32:51 -07:00
static void zxdg_decoration_manager_v1_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
( void ) data ;
2025-12-18 23:32:51 -07:00
}
2026-03-20 20:23:02 -07:00
/* Standard Wayland event loop. */
static int event_loop ( MwLL handle ) {
struct pollfd fd ;
struct timespec timeout ;
struct _MwLLWayland * wayland = & handle - > wayland ;
timeout . tv_nsec = 1 ;
timeout . tv_sec = 0 ;
if ( wayland - > display = = NULL ) {
return 0 ;
}
fd . fd = wl_display_get_fd ( wayland - > display ) ;
fd . events = POLLIN ;
/* If an error other than EAGAIN happens, we have likely been disconnected from the Wayland session */
while ( wl_display_flush ( wayland - > display ) = = - 1 ) {
if ( errno ! = EAGAIN ) {
return MwFALSE ;
}
while ( poll ( & fd , 1 , - 1 ) = = - 1 ) {
if ( errno ! = EINTR & & errno ! = EAGAIN ) {
wl_display_cancel_read ( wayland - > display ) ;
MwLLDispatch ( handle , close , NULL ) ;
return 0 ;
}
}
}
wl_display_prepare_read ( handle - > wayland . display ) ;
/* Condition where no events are being sent. */
if ( ! poll ( & fd , 1 , timeout . tv_nsec ) ) {
wl_display_cancel_read ( wayland - > display ) ;
2026-03-26 17:48:22 -07:00
update_buffer ( handle , & wayland - > framebuffer ) ;
2026-04-20 14:49:20 +09:00
if ( wayland - > type = = MwLL_WAYLAND_TOPLEVEL )
2026-03-26 17:48:22 -07:00
update_buffer ( handle , & wayland - > backbuffer ) ;
2026-03-20 20:23:02 -07:00
return 0 ;
}
wl_display_read_events ( wayland - > display ) ;
if ( wl_display_dispatch_pending ( wayland - > display ) < 0 ) {
wl_display_cancel_read ( wayland - > display ) ;
}
return 1 ;
}
/* make the fuck sure that we're configurd */
static void hang_until_configured ( MwLL handle ) {
while ( ! handle - > wayland . configured ) {
if ( wl_display_roundtrip ( handle - > wayland . display ) = = - 1 ) {
2026-04-23 14:02:33 -07:00
printf ( " roundtrip failed: %d \n " , wl_display_get_error ( handle - > wayland . display ) ) ;
2026-03-20 20:23:02 -07:00
raise ( SIGTRAP ) ;
return ;
}
event_loop ( handle ) ;
}
}
2026-03-19 16:35:56 -07:00
/* `xdg_toplevel.configure` callback */
2025-12-09 20:11:01 -06:00
static void xdg_toplevel_configure ( void * data ,
struct xdg_toplevel * xdg_toplevel ,
MwI32 width , MwI32 height ,
struct wl_array * states ) {
2026-03-24 17:38:21 -07:00
MwLL self = data ;
( void ) states ;
( void ) xdg_toplevel ;
2025-12-09 20:11:01 -06:00
2025-12-30 17:12:12 -07:00
/* Yes, somehow this can get called before */
if ( ! self - > wayland . configured ) {
return ;
}
2025-12-09 20:11:01 -06:00
if ( width = = 0 | | height = = 0 ) {
width = self - > wayland . ww ;
height = self - > wayland . wh ;
/* if it's still 0 then bail */
if ( width = = 0 | | height = = 0 ) {
return ;
}
}
2025-12-18 11:26:51 -07:00
region_invalidate ( self ) ;
2025-12-09 20:11:01 -06:00
self - > wayland . ww = width ;
self - > wayland . wh = height ;
2026-03-19 16:35:56 -07:00
2026-04-19 21:12:39 -07:00
if ( self - > wayland . resizing = = 0 ) {
xdg_surface_set_window_geometry ( self - > wayland . toplevel - > xdg_surface , 0 , 0 , self - > wayland . ww , self - > wayland . wh ) ;
2025-12-09 20:11:01 -06:00
2026-04-19 21:12:39 -07:00
backbuffer_destroy ( & self - > wayland ) ;
backbuffer_setup ( & self - > wayland ) ;
framebuffer_destroy ( & self - > wayland ) ;
framebuffer_setup ( & self - > wayland ) ;
2026-03-24 22:40:36 -07:00
2026-04-19 21:12:39 -07:00
region_setup ( self ) ;
MwLLDispatch ( self , resize , NULL ) ;
2025-12-17 19:14:54 -07:00
2026-04-19 21:12:39 -07:00
recursive_dispatch_resize ( self ) ;
2026-03-25 00:31:23 -07:00
2026-04-19 21:12:39 -07:00
if ( self - > wayland . pointer_constrained ) {
zwp_locked_pointer_v1_set_cursor_position_hint ( self - > wayland . locked_pointer , 0 , CSD_BORDER_FRAME_TOP ) ;
wl_surface_commit ( self - > wayland . framebuffer . surface ) ;
}
self - > wayland . resizing = 1 ;
2026-03-25 00:31:23 -07:00
}
2025-12-09 20:11:01 -06:00
} ;
/* `xdg_surface.close` callback */
static void xdg_toplevel_close (
void * data , struct xdg_toplevel * xdg_toplevel ) {
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) xdg_toplevel ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_START ( self ) ;
2025-12-09 20:11:01 -06:00
MwLLDispatch ( self , close , NULL ) ;
2026-01-04 11:40:34 -07:00
WAYLAND_EVENT_OP_END ( self ) ;
2025-12-09 20:11:01 -06:00
} ;
/* `xdg_surface.configure` callback */
static void xdg_surface_configure (
void * data , struct xdg_surface * xdg_surface , MwU32 serial ) {
MwLL self = data ;
xdg_surface_ack_configure ( xdg_surface , serial ) ;
2026-03-27 15:14:04 -07:00
MwLLDispatch ( self , draw , NULL ) ;
2025-12-09 20:11:01 -06:00
if ( self - > wayland . configured ) {
2026-03-26 17:48:22 -07:00
update_buffer ( self , & self - > wayland . framebuffer ) ;
update_buffer ( self , & self - > wayland . backbuffer ) ;
2025-12-09 20:11:01 -06:00
}
self - > wayland . configured = MwTRUE ;
}
2025-12-17 19:14:54 -07:00
/* wl_shm setup function */
static wayland_protocol_t * wl_shm_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
2025-12-18 13:15:03 -07:00
wayland - > framebuffer . shm = wl_registry_bind ( wayland - > registry , name , & wl_shm_interface , 1 ) ;
2026-03-19 19:43:57 -07:00
wayland - > backbuffer . shm = wl_registry_bind ( wayland - > registry , name , & wl_shm_interface , 1 ) ;
2025-12-18 13:15:03 -07:00
wayland - > cursor . shm = wl_registry_bind ( wayland - > registry , name , & wl_shm_interface , 1 ) ;
2025-12-17 19:41:05 -07:00
return NULL ;
}
2025-12-18 23:32:51 -07:00
static void wl_shm_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
( void ) data ;
2025-12-18 23:32:51 -07:00
}
2026-03-25 00:31:23 -07:00
static void update_buffer ( MwLL self , struct _MwLLWaylandShmBuffer * buffer ) {
2026-03-26 16:54:40 -07:00
memcpy ( buffer - > buf , buffer - > buf_back , buffer - > buf_size ) ;
2026-04-08 12:36:18 -07:00
// Yes this is needed every time, it's how we fix weston.
2026-04-23 12:31:53 -07:00
if ( self - > wayland . configured )
wl_surface_attach ( buffer - > surface , buffer - > shm_buffer , 0 , 0 ) ;
2026-04-12 06:44:35 +09:00
wl_surface_commit ( buffer - > surface ) ;
2025-12-17 19:41:05 -07:00
}
2025-12-18 13:15:03 -07:00
static void buffer_setup ( struct _MwLLWaylandShmBuffer * buffer , MwU32 width , MwU32 height ) {
2026-04-17 13:12:22 -07:00
int stride = width * 4 ;
char temp_name [ ] = " /tmp/milsko-wl-shm-XXXXXX " ;
char temp_name_back [ ] = " /tmp/milsko-wl-shm-back-XXXXXX " ;
2025-12-17 19:14:54 -07:00
2025-12-18 13:15:03 -07:00
buffer - > buf_size = width * height * 4 ;
2025-12-17 19:14:54 -07:00
2026-04-17 13:12:22 -07:00
buffer - > fd = mkstemp ( temp_name ) ;
buffer - > fd_back = mkstemp ( temp_name_back ) ;
2025-12-17 19:14:54 -07:00
unlink ( temp_name ) ;
2026-04-17 13:12:22 -07:00
unlink ( temp_name_back ) ;
2025-12-17 19:14:54 -07:00
2025-12-18 13:15:03 -07:00
if ( posix_fallocate ( buffer - > fd , 0 , buffer - > buf_size ) ! = 0 ) {
2025-12-17 19:14:54 -07:00
printf ( " failure setting up wl_shm: could not fallocate. %s. \n " , strerror ( errno ) ) ;
2025-12-18 13:15:03 -07:00
close ( buffer - > fd ) ;
2025-12-17 19:41:05 -07:00
return ;
2025-12-17 19:14:54 -07:00
}
2026-04-17 13:12:22 -07:00
if ( posix_fallocate ( buffer - > fd_back , 0 , buffer - > buf_size ) ! = 0 ) {
printf ( " failure setting up wl_shm: could not fallocate. %s. \n " , strerror ( errno ) ) ;
close ( buffer - > fd_back ) ;
return ;
}
2025-12-18 13:15:03 -07:00
if ( ftruncate ( buffer - > fd , buffer - > buf_size ) ! = 0 ) {
2025-12-17 19:14:54 -07:00
printf ( " failure setting up wl_shm: could not truncate. %s. \n " , strerror ( errno ) ) ;
2025-12-18 13:15:03 -07:00
close ( buffer - > fd ) ;
2025-12-17 19:41:05 -07:00
return ;
2025-12-17 19:14:54 -07:00
}
2026-04-17 13:12:22 -07:00
if ( ftruncate ( buffer - > fd_back , buffer - > buf_size ) ! = 0 ) {
printf ( " failure setting up wl_shm: could not truncate. %s. \n " , strerror ( errno ) ) ;
close ( buffer - > fd_back ) ;
return ;
}
2025-12-17 19:14:54 -07:00
2026-03-26 16:54:40 -07:00
buffer - > buf = mmap ( NULL , buffer - > buf_size , PROT_WRITE , MAP_SHARED , buffer - > fd , 0 ) ;
2026-04-17 13:12:22 -07:00
buffer - > buf_back = mmap ( NULL , buffer - > buf_size , PROT_WRITE , MAP_SHARED , buffer - > fd_back , 0 ) ;
2025-12-17 19:14:54 -07:00
2025-12-18 13:15:03 -07:00
fsync ( buffer - > fd ) ;
2026-04-17 13:12:22 -07:00
fsync ( buffer - > fd_back ) ;
2025-12-17 19:14:54 -07:00
2025-12-18 13:15:03 -07:00
if ( ! ( buffer - > shm_pool = wl_shm_create_pool ( buffer - > shm , buffer - > fd , buffer - > buf_size ) ) ) {
2025-12-17 19:14:54 -07:00
printf ( " failure setting up wl_shm: could not create pool. \n " ) ;
}
2026-04-17 13:12:22 -07:00
if ( ! ( buffer - > shm_pool_back = wl_shm_create_pool ( buffer - > shm , buffer - > fd_back , buffer - > buf_size ) ) ) {
printf ( " failure setting up wl_shm: could not create pool. \n " ) ;
}
buffer - > shm_buffer = wl_shm_pool_create_buffer ( buffer - > shm_pool , 0 , width , height , stride , WL_SHM_FORMAT_ARGB8888 ) ;
buffer - > shm_buffer_back = wl_shm_pool_create_buffer ( buffer - > shm_pool_back , 0 , width , height , stride , WL_SHM_FORMAT_ARGB8888 ) ;
buffer - > setup = MwTRUE ;
2025-12-18 13:15:03 -07:00
}
2025-12-17 19:14:54 -07:00
2025-12-18 13:15:03 -07:00
static void buffer_destroy ( struct _MwLLWaylandShmBuffer * buffer ) {
2026-04-17 13:12:22 -07:00
if ( ! buffer - > setup ) {
return ;
}
2026-04-23 14:02:33 -07:00
if ( buffer - > buf ) munmap ( buffer - > buf , buffer - > buf_size ) ;
if ( buffer - > buf_back ) munmap ( buffer - > buf_back , buffer - > buf_size ) ;
2026-03-27 17:15:59 -07:00
if ( buffer - > shm_buffer ) wl_buffer_destroy ( buffer - > shm_buffer ) ;
2026-04-17 13:12:22 -07:00
if ( buffer - > shm_buffer_back ) wl_buffer_destroy ( buffer - > shm_buffer_back ) ;
2026-03-27 17:15:59 -07:00
if ( buffer - > shm_pool ) wl_shm_pool_destroy ( buffer - > shm_pool ) ;
if ( buffer - > shm_pool_back ) wl_shm_pool_destroy ( buffer - > shm_pool_back ) ;
2026-04-17 13:12:22 -07:00
close ( buffer - > fd ) ;
close ( buffer - > fd_back ) ;
2025-12-18 13:15:03 -07:00
buffer - > setup = MwFALSE ;
}
2025-12-17 19:14:54 -07:00
2025-12-18 13:15:03 -07:00
static void framebuffer_setup ( struct _MwLLWayland * wayland ) {
buffer_setup ( & wayland - > framebuffer , wayland - > ww , wayland - > wh ) ;
2025-12-17 19:14:54 -07:00
2026-03-26 16:54:40 -07:00
wayland - > front_cs = cairo_image_surface_create_for_data ( wayland - > framebuffer . buf_back , CAIRO_FORMAT_ARGB32 , wayland - > ww , wayland - > wh , 4 * wayland - > ww ) ;
2026-03-19 19:43:57 -07:00
wayland - > front_cairo = cairo_create ( wayland - > front_cs ) ;
2025-12-17 19:14:54 -07:00
2026-03-26 16:54:40 -07:00
memset ( wayland - > framebuffer . buf_back , 0 , wayland - > framebuffer . buf_size ) ;
2026-04-14 13:45:47 -07:00
if ( wayland - > configured )
wl_surface_attach ( wayland - > framebuffer . surface , wayland - > framebuffer . shm_buffer , 0 , 0 ) ;
2026-03-26 21:36:57 -07:00
wl_surface_commit ( wayland - > framebuffer . surface ) ;
2026-03-20 20:23:02 -07:00
hang_until_configured ( ( MwLL ) wayland ) ;
2026-03-25 00:31:23 -07:00
update_buffer ( ( MwLL ) wayland , & wayland - > framebuffer ) ;
2025-12-18 13:15:03 -07:00
} ;
static void framebuffer_destroy ( struct _MwLLWayland * wayland ) {
buffer_destroy ( & wayland - > framebuffer ) ;
2026-03-19 19:43:57 -07:00
cairo_destroy ( wayland - > front_cairo ) ;
cairo_surface_destroy ( wayland - > front_cs ) ;
} ;
static void backbuffer_setup ( struct _MwLLWayland * wayland ) {
2026-04-20 14:49:20 +09:00
if ( wayland - > type ! = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-19 19:43:57 -07:00
return ;
}
2026-03-23 15:32:28 -07:00
MwU32 w = wayland - > ww ;
MwU32 h = wayland - > wh ;
if ( ! wayland - > has_decorations ) {
w + = ( CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT ) ;
h + = ( CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM ) ;
}
buffer_setup ( & wayland - > backbuffer , w , h ) ;
2026-03-19 19:43:57 -07:00
2026-03-26 16:54:40 -07:00
wayland - > back_cs = cairo_image_surface_create_for_data ( wayland - > backbuffer . buf_back , CAIRO_FORMAT_ARGB32 , w , h , 4 * w ) ;
2026-03-19 19:43:57 -07:00
wayland - > back_cairo = cairo_create ( wayland - > back_cs ) ;
2026-03-26 16:54:40 -07:00
memset ( wayland - > backbuffer . buf_back , 255 , wayland - > backbuffer . buf_size ) ;
2026-04-14 13:45:47 -07:00
if ( wayland - > configured )
wl_surface_attach ( wayland - > backbuffer . surface , wayland - > backbuffer . shm_buffer , 0 , 0 ) ;
2026-03-26 21:36:57 -07:00
wl_surface_commit ( wayland - > backbuffer . surface ) ;
2026-03-20 20:23:02 -07:00
hang_until_configured ( ( MwLL ) wayland ) ;
2026-03-25 00:31:23 -07:00
update_buffer ( ( MwLL ) wayland , & wayland - > backbuffer ) ;
2026-03-19 19:43:57 -07:00
} ;
static void backbuffer_destroy ( struct _MwLLWayland * wayland ) {
buffer_destroy ( & wayland - > backbuffer ) ;
cairo_destroy ( wayland - > back_cairo ) ;
cairo_surface_destroy ( wayland - > back_cs ) ;
2025-12-18 13:15:03 -07:00
} ;
2025-12-17 19:14:54 -07:00
2025-12-18 11:26:51 -07:00
static void region_invalidate ( MwLL handle ) {
if ( ! handle - > wayland . configured ) {
return ;
}
2026-03-17 20:28:58 -07:00
wl_region_subtract ( handle - > wayland . region , 0 , 0 , handle - > wayland . ww , handle - > wayland . wh ) ;
2025-12-18 11:26:51 -07:00
}
static void region_setup ( MwLL handle ) {
2026-03-19 15:57:20 -07:00
MwLL parent = handle - > wayland . parent ;
2026-04-19 18:50:33 -07:00
int width = ( handle - > wayland . clipping_rect . width = = 0 ) ? handle - > wayland . ww : handle - > wayland . clipping_rect . width ;
int height = ( handle - > wayland . clipping_rect . height = = 0 ) ? handle - > wayland . wh : handle - > wayland . clipping_rect . height ;
2026-03-19 16:01:32 -07:00
if ( ! handle - > wayland . configured ) {
return ;
}
2026-04-19 18:50:33 -07:00
// wl_region_add(handle->wayland.o_region, 0, 0, width, height);
2026-03-17 20:28:58 -07:00
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_POPUP ) {
2026-03-19 15:57:20 -07:00
wl_region_add ( handle - > wayland . region , 0 , 0 , width + abs ( handle - > wayland . x ) , height + abs ( handle - > wayland . y ) ) ;
2026-03-17 20:28:58 -07:00
} else {
2026-03-19 15:57:20 -07:00
if ( parent ) {
if ( width - handle - > wayland . x > parent - > wayland . ww ) {
width = parent - > wayland . ww - handle - > wayland . x ;
}
if ( height - handle - > wayland . y > parent - > wayland . wh ) {
height = parent - > wayland . wh - handle - > wayland . y ;
}
2026-03-23 15:45:48 -07:00
} else {
if ( ! handle - > wayland . has_decorations ) {
width + = CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT ;
height + = CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM ;
}
2026-03-19 15:57:20 -07:00
}
wl_region_add ( handle - > wayland . region , 0 , 0 , width , height ) ;
2026-03-17 20:28:58 -07:00
}
2026-03-23 15:45:48 -07:00
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-23 14:28:49 -07:00
wl_surface_set_opaque_region ( handle - > wayland . backbuffer . surface , handle - > wayland . o_region ) ;
2026-03-19 19:43:57 -07:00
wl_surface_set_input_region ( handle - > wayland . backbuffer . surface , handle - > wayland . region ) ;
}
2026-03-23 14:28:49 -07:00
wl_surface_set_opaque_region ( handle - > wayland . framebuffer . surface , handle - > wayland . o_region ) ;
2026-03-20 19:28:18 -07:00
wl_surface_set_input_region ( handle - > wayland . framebuffer . surface , handle - > wayland . region ) ;
2025-12-18 11:26:51 -07:00
}
2025-12-18 18:21:57 -07:00
static wayland_protocol_t * xdg_toplevel_icon_manager_v1_setup ( MwU32 name , struct _MwLLWayland * wayland ) {
wayland_protocol_t * proto = malloc ( sizeof ( wayland_protocol_t ) ) ;
proto - > context = wl_registry_bind ( wayland - > registry , name , & xdg_toplevel_icon_manager_v1_interface , 1 ) ;
proto - > listener = NULL ;
return proto ;
}
2025-12-18 23:32:51 -07:00
static void xdg_toplevel_icon_manager_v1_interface_destroy ( struct _MwLLWayland * wayland , wayland_protocol_t * data ) {
2026-03-24 17:38:21 -07:00
( void ) wayland ;
2025-12-18 23:32:51 -07:00
free ( data - > listener ) ;
}
2025-12-09 20:11:01 -06:00
/* Function for setting up the callbacks/structs that will be registered upon the relevant interfaces being found. */
static void setup_callbacks ( struct _MwLLWayland * wayland ) {
/* Convience macro for adding the interface functions to the setup map */
# define WL_INTERFACE(interface) \
2025-12-18 23:32:51 -07:00
do { \
wayland_protocol_callback_table_t * cb = malloc ( sizeof ( wayland_protocol_callback_table_t ) ) ; \
cb - > setup = ( wl_setup_func * ) interface # # _setup ; \
cb - > destroy = ( wl_destroy_func * ) interface # # _interface_destroy ; \
shput ( wayland - > wl_protocol_setup_map , interface # # _interface . name , cb ) ; \
} while ( 0 ) ;
2025-12-09 20:11:01 -06:00
wayland - > registry_listener . global = new_protocol ;
wayland - > registry_listener . global_remove = protocol_removed ;
wayland - > wl_protocol_setup_map = NULL ;
wayland - > wl_protocol_map = NULL ;
2026-04-23 14:02:33 -07:00
sh_new_arena ( wayland - > wl_protocol_map ) ;
sh_new_arena ( wayland - > wl_protocol_setup_map ) ;
2025-12-17 19:14:54 -07:00
WL_INTERFACE ( wl_shm ) ;
2025-12-09 20:11:01 -06:00
WL_INTERFACE ( wl_compositor ) ;
WL_INTERFACE ( wl_seat ) ;
2025-12-18 13:43:45 -07:00
WL_INTERFACE ( wl_output ) ;
2025-12-30 17:12:12 -07:00
WL_INTERFACE ( wl_data_device_manager ) ;
2025-12-18 17:46:23 -07:00
WL_INTERFACE ( zwp_primary_selection_device_manager_v1 ) ;
2026-03-24 23:44:30 -07:00
WL_INTERFACE ( zwp_pointer_constraints_v1 ) ;
2026-03-27 17:15:59 -07:00
WL_INTERFACE ( zwp_relative_pointer_manager_v1 ) ;
2026-03-27 15:14:04 -07:00
WL_INTERFACE ( xdg_wm_base ) ;
2026-04-20 14:49:20 +09:00
if ( wayland - > type = = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-19 19:43:57 -07:00
WL_INTERFACE ( wp_viewporter ) ;
2025-12-09 20:11:01 -06:00
WL_INTERFACE ( zxdg_decoration_manager_v1 ) ;
2025-12-18 18:21:57 -07:00
WL_INTERFACE ( xdg_toplevel_icon_manager_v1 ) ;
2026-03-19 19:43:57 -07:00
WL_INTERFACE ( wl_subcompositor ) ;
2026-04-20 14:49:20 +09:00
} else if ( wayland - > type = = MwLL_WAYLAND_POPUP ) {
2025-12-17 19:14:54 -07:00
} else {
WL_INTERFACE ( wl_subcompositor ) ;
2025-12-09 20:11:01 -06:00
}
# undef WL_INTERFACE
}
/* Toplevel setup function */
static void setup_toplevel ( MwLL r , int x , int y ) {
2026-04-20 14:49:20 +09:00
r - > wayland . type = MwLL_WAYLAND_TOPLEVEL ;
2025-12-17 19:14:54 -07:00
r - > wayland . toplevel = malloc ( sizeof ( struct _MwLLWaylandTopLevel ) ) ;
2026-03-17 20:28:58 -07:00
r - > wayland . x = x ;
r - > wayland . y = y ;
2025-12-09 20:11:01 -06:00
setup_callbacks ( & r - > wayland ) ;
/* Connect to the Wayland compositor */
r - > wayland . display = wl_display_connect ( NULL ) ;
if ( r - > wayland . display = = NULL ) {
printf ( " wayland: failed to create display \n " ) ;
raise ( SIGTRAP ) ;
return ;
}
/* Do a roundtrip to ensure all interfaces are setup. */
r - > wayland . registry = wl_display_get_registry ( r - > wayland . display ) ;
wl_registry_add_listener ( r - > wayland . registry , & r - > wayland . registry_listener , r ) ;
if ( wl_display_roundtrip ( r - > wayland . display ) = = - 1 ) {
printf ( " roundtrip failed \n " ) ;
raise ( SIGTRAP ) ;
return ;
}
2026-03-23 14:28:49 -07:00
/* check now if we have decorations so that everything else can act accordingly */
if ( shget ( r - > wayland . wl_protocol_map , zxdg_decoration_manager_v1_interface . name ) ! = NULL ) {
r - > wayland . has_decorations = MwTRUE ;
}
2026-03-27 15:14:04 -07:00
r - > wayland . xkb_context = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
2025-12-09 20:11:01 -06:00
/* Create a wl_surface, a xdg_surface and a xdg_toplevel */
2025-12-18 13:15:03 -07:00
r - > wayland . framebuffer . surface = wl_compositor_create_surface ( r - > wayland . compositor ) ;
2026-03-19 19:43:57 -07:00
r - > wayland . backbuffer . surface = wl_compositor_create_surface ( r - > wayland . compositor ) ;
r - > wayland . toplevel - > ssurface = wl_subcompositor_get_subsurface ( r - > wayland . toplevel - > scompositor , r - > wayland . framebuffer . surface , r - > wayland . backbuffer . surface ) ;
2026-03-20 19:28:18 -07:00
wl_subsurface_set_desync ( r - > wayland . toplevel - > ssurface ) ;
2025-12-18 13:43:45 -07:00
2025-12-09 20:11:01 -06:00
r - > wayland . toplevel - > xdg_surface =
2026-03-19 19:43:57 -07:00
xdg_wm_base_get_xdg_surface ( WAYLAND_GET_INTERFACE ( r - > wayland , xdg_wm_base ) - > context , r - > wayland . backbuffer . surface ) ;
2025-12-09 20:11:01 -06:00
r - > wayland . toplevel - > xdg_top_level = xdg_surface_get_toplevel ( r - > wayland . toplevel - > xdg_surface ) ;
/* setup mandatory listeners */
r - > wayland . toplevel - > xdg_surface_listener . configure = xdg_surface_configure ;
r - > wayland . toplevel - > xdg_toplevel_listener . configure = xdg_toplevel_configure ;
r - > wayland . toplevel - > xdg_toplevel_listener . close = xdg_toplevel_close ;
xdg_surface_add_listener ( r - > wayland . toplevel - > xdg_surface , & r - > wayland . toplevel - > xdg_surface_listener , r ) ;
xdg_toplevel_add_listener ( r - > wayland . toplevel - > xdg_top_level , & r - > wayland . toplevel - > xdg_toplevel_listener ,
r ) ;
xdg_toplevel_set_title ( r - > wayland . toplevel - > xdg_top_level , " Milsko App " ) ;
xdg_toplevel_set_app_id ( r - > wayland . toplevel - > xdg_top_level , " MilskoWaylandApp " ) ;
/* Perform the initial commit and wait for the first configure event */
2026-03-19 19:43:57 -07:00
wl_surface_commit ( r - > wayland . backbuffer . surface ) ;
2025-12-18 13:15:03 -07:00
wl_surface_commit ( r - > wayland . framebuffer . surface ) ;
2025-12-30 17:12:12 -07:00
while ( ! r - > wayland . configured ) {
2026-01-12 18:18:05 -07:00
event_loop ( r ) ;
2025-12-30 17:12:12 -07:00
}
2025-12-09 20:11:01 -06:00
/* setup decorations if we can */
2026-03-23 14:28:49 -07:00
if ( r - > wayland . has_decorations ) {
2025-12-09 20:11:01 -06:00
zxdg_decoration_manager_v1_context_t * dec = WAYLAND_GET_INTERFACE ( r - > wayland , zxdg_decoration_manager_v1 ) - > context ;
dec - > decoration = zxdg_decoration_manager_v1_get_toplevel_decoration (
dec - > manager , r - > wayland . toplevel - > xdg_top_level ) ;
zxdg_toplevel_decoration_v1_set_mode (
dec - > decoration , ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE ) ;
} else {
2026-03-20 19:28:18 -07:00
wl_subsurface_set_position ( r - > wayland . toplevel - > ssurface , CSD_BORDER_FRAME_LEFT , CSD_BORDER_FRAME_TOP ) ;
2025-12-09 20:11:01 -06:00
}
2025-12-18 23:32:51 -07:00
}
/* Toplevel destroy function */
static void destroy_toplevel ( MwLL r ) {
if ( shget ( r - > wayland . wl_protocol_map , zxdg_decoration_manager_v1_interface . name ) ! = NULL ) {
zxdg_decoration_manager_v1_context_t * dec = WAYLAND_GET_INTERFACE ( r - > wayland , zxdg_decoration_manager_v1 ) - > context ;
zxdg_decoration_manager_v1_destroy ( dec - > manager ) ;
}
2025-12-09 20:11:01 -06:00
2026-01-02 14:19:30 -07:00
wl_surface_destroy ( r - > wayland . framebuffer . surface ) ;
2025-12-18 23:32:51 -07:00
wl_compositor_destroy ( r - > wayland . compositor ) ;
xdg_surface_destroy ( r - > wayland . toplevel - > xdg_surface ) ;
xdg_toplevel_destroy ( r - > wayland . toplevel - > xdg_top_level ) ;
2026-04-17 13:12:22 -07:00
xkb_keymap_unref ( r - > wayland . xkb_keymap ) ;
2026-03-27 15:14:04 -07:00
xkb_state_unref ( r - > wayland . xkb_state ) ;
2026-01-12 18:18:05 -07:00
2026-03-27 15:14:04 -07:00
xkb_context_unref ( r - > wayland . xkb_context ) ;
2025-12-18 23:32:51 -07:00
2026-01-12 18:18:05 -07:00
free ( r - > wayland . toplevel ) ;
2025-12-18 23:32:51 -07:00
wl_registry_destroy ( r - > wayland . registry ) ;
wl_display_disconnect ( r - > wayland . display ) ;
2026-03-19 16:35:56 -07:00
r - > wayland . configured = MwFALSE ;
2025-12-09 20:11:01 -06:00
}
/* Sublevel setup function */
static void setup_sublevel ( MwLL parent , MwLL r , int x , int y ) {
2025-12-17 19:14:54 -07:00
struct wl_compositor * compositor = parent - > wayland . compositor ;
2025-12-18 13:15:03 -07:00
struct wl_surface * parent_surface = parent - > wayland . framebuffer . surface ;
2025-12-17 19:14:54 -07:00
2025-12-18 23:32:51 -07:00
r - > wayland . sublevel = malloc ( sizeof ( struct _MwLLWaylandSublevel ) ) ;
r - > wayland . sublevel - > parent = parent ;
2026-04-20 14:49:20 +09:00
r - > wayland . type = MwLL_WAYLAND_SUBLEVEL ;
2025-12-09 20:11:01 -06:00
2026-04-17 13:12:22 -07:00
r - > wayland . display = parent - > wayland . display ;
2025-12-09 20:11:01 -06:00
setup_callbacks ( & r - > wayland ) ;
2025-12-17 19:14:54 -07:00
r - > wayland . registry = wl_display_get_registry ( parent - > wayland . display ) ;
r - > wayland . display = parent - > wayland . display ;
2025-12-09 20:11:01 -06:00
2026-04-20 14:49:20 +09:00
if ( parent - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-27 17:15:59 -07:00
r - > wayland . sublevel - > xdg_surface = parent - > wayland . toplevel - > xdg_surface ;
} else {
r - > wayland . sublevel - > xdg_surface = parent - > wayland . sublevel - > xdg_surface ;
}
2025-12-09 20:11:01 -06:00
wl_registry_add_listener ( r - > wayland . registry , & r - > wayland . registry_listener , r ) ;
if ( wl_display_roundtrip ( r - > wayland . display ) = = - 1 ) {
2026-04-23 14:02:33 -07:00
printf ( " roundtrip failed: %d \n " , wl_display_get_error ( r - > wayland . display ) ) ;
2025-12-09 20:11:01 -06:00
raise ( SIGTRAP ) ;
return ;
}
2025-12-30 17:12:12 -07:00
r - > wayland . framebuffer . surface = wl_compositor_create_surface ( compositor ) ;
2026-03-26 23:32:24 -07:00
2025-12-18 13:15:03 -07:00
r - > wayland . sublevel - > subsurface = wl_subcompositor_get_subsurface ( r - > wayland . sublevel - > subcompositor , r - > wayland . framebuffer . surface , parent_surface ) ;
2025-12-09 20:11:01 -06:00
2026-03-18 23:01:39 -07:00
wl_subsurface_set_desync ( r - > wayland . sublevel - > subsurface ) ;
2025-12-17 19:14:54 -07:00
wl_subsurface_set_position ( r - > wayland . sublevel - > subsurface , x , y ) ;
2025-12-09 20:11:01 -06:00
2026-03-19 15:57:20 -07:00
if ( parent ) {
wl_subsurface_place_above ( r - > wayland . sublevel - > subsurface , parent - > wayland . framebuffer . surface ) ;
}
2026-03-27 15:14:04 -07:00
r - > wayland . xkb_keymap = parent - > wayland . xkb_keymap ;
r - > wayland . xkb_state = parent - > wayland . xkb_state ;
2025-12-17 19:14:54 -07:00
r - > wayland . configured = MwTRUE ;
2025-12-09 20:11:01 -06:00
}
2025-12-18 23:32:51 -07:00
/* Sublevel setup function */
static void destroy_sublevel ( MwLL r ) {
2026-04-17 13:12:22 -07:00
backbuffer_destroy ( & r - > wayland ) ;
framebuffer_destroy ( & r - > wayland ) ;
2025-12-18 23:32:51 -07:00
2026-04-17 13:12:22 -07:00
wl_subsurface_destroy ( r - > wayland . sublevel - > subsurface ) ;
2026-04-16 18:36:42 -07:00
2025-12-18 23:32:51 -07:00
free ( r - > wayland . sublevel ) ;
2026-03-19 16:35:56 -07:00
r - > wayland . configured = MwFALSE ;
2025-12-18 23:32:51 -07:00
}
static void popup_configure ( void * data ,
struct xdg_popup * xdg_popup ,
int32_t x ,
int32_t y ,
int32_t width ,
int32_t height ) {
MwLL self = data ;
2026-03-24 17:38:21 -07:00
( void ) xdg_popup ;
2025-12-18 23:32:51 -07:00
self - > wayland . x = x ;
self - > wayland . y = y ;
self - > wayland . ww = width ;
self - > wayland . wh = height ;
} ;
static void popup_done ( void * data ,
struct xdg_popup * xdg_popup ) {
2026-03-24 17:38:21 -07:00
( void ) data ;
( void ) xdg_popup ;
2025-12-18 23:32:51 -07:00
} ;
struct xdg_popup_listener popup_listener = {
. configure = popup_configure ,
. popup_done = popup_done ,
} ;
/* Popup setup function */
2026-03-24 23:08:43 -07:00
static void setup_popup ( MwLL r , int x , int y , MwLL parent ) {
2025-12-18 23:32:51 -07:00
struct xdg_surface * xdg_surface ;
MwLL topmost_parent = r - > wayland . parent ;
2026-04-20 14:49:20 +09:00
r - > wayland . type = MwLL_WAYLAND_POPUP ;
2026-03-24 23:08:43 -07:00
r - > wayland . x = x ;
r - > wayland . y = y ;
r - > wayland . popup = malloc ( sizeof ( struct _MwLLWaylandPopup ) ) ;
2026-01-02 14:19:30 -07:00
2026-04-20 14:49:20 +09:00
while ( topmost_parent - > wayland . type ! = MwLL_WAYLAND_TOPLEVEL ) {
2025-12-18 23:32:51 -07:00
topmost_parent = topmost_parent - > wayland . parent ;
}
2026-03-20 19:42:30 -07:00
if ( ! topmost_parent - > wayland . has_decorations ) {
r - > wayland . x + = ceil ( ( float ) CSD_BORDER_FRAME_LEFT / 2. ) ;
r - > wayland . y + = ceil ( ( float ) CSD_BORDER_FRAME_TOP / 2. ) ;
}
2025-12-18 23:32:51 -07:00
setup_callbacks ( & r - > wayland ) ;
2026-03-24 23:08:43 -07:00
/* Connect to the Wayland compositor */
r - > wayland . display = parent - > wayland . display ;
2025-12-18 23:32:51 -07:00
/* Do a roundtrip to ensure all interfaces are setup. */
r - > wayland . registry = wl_display_get_registry ( r - > wayland . display ) ;
wl_registry_add_listener ( r - > wayland . registry , & r - > wayland . registry_listener , r ) ;
if ( wl_display_roundtrip ( r - > wayland . display ) = = - 1 ) {
2026-04-23 14:02:33 -07:00
printf ( " roundtrip failed: %d \n " , wl_display_get_error ( r - > wayland . display ) ) ;
2025-12-18 23:32:51 -07:00
raise ( SIGTRAP ) ;
return ;
}
2026-03-24 23:08:43 -07:00
/* check now if we have decorations so that everything else can act accordingly */
if ( shget ( r - > wayland . wl_protocol_map , zxdg_decoration_manager_v1_interface . name ) ! = NULL ) {
r - > wayland . has_decorations = MwTRUE ;
}
r - > wayland . popup - > xdg_wm_base = WAYLAND_GET_INTERFACE ( r - > wayland , xdg_wm_base ) - > context ;
2025-12-18 23:32:51 -07:00
r - > wayland . popup - > xdg_positioner = xdg_wm_base_create_positioner ( r - > wayland . popup - > xdg_wm_base ) ;
xdg_positioner_set_size ( r - > wayland . popup - > xdg_positioner , r - > wayland . ww , r - > wayland . wh ) ;
xdg_positioner_set_anchor_rect (
2026-03-17 20:28:58 -07:00
r - > wayland . popup - > xdg_positioner ,
2026-03-20 19:42:30 -07:00
r - > wayland . x , r - > wayland . y , r - > wayland . ww , r - > wayland . wh ) ;
2025-12-18 23:32:51 -07:00
2026-03-27 15:14:04 -07:00
xdg_surface = topmost_parent - > wayland . toplevel - > xdg_surface ;
r - > wayland . xkb_keymap = topmost_parent - > wayland . xkb_keymap ;
r - > wayland . xkb_state = topmost_parent - > wayland . xkb_state ;
2025-12-18 23:32:51 -07:00
2026-01-02 14:19:30 -07:00
r - > wayland . framebuffer . surface = wl_compositor_create_surface ( r - > wayland . compositor ) ;
2025-12-18 23:32:51 -07:00
r - > wayland . popup - > xdg_surface =
2026-03-24 23:08:43 -07:00
xdg_wm_base_get_xdg_surface ( WAYLAND_GET_INTERFACE ( r - > wayland , xdg_wm_base ) - > context , r - > wayland . framebuffer . surface ) ;
2025-12-18 23:32:51 -07:00
r - > wayland . popup - > xdg_popup = xdg_surface_get_popup (
r - > wayland . popup - > xdg_surface ,
xdg_surface ,
r - > wayland . popup - > xdg_positioner ) ;
xdg_popup_add_listener ( r - > wayland . popup - > xdg_popup , & popup_listener , r ) ;
r - > wayland . popup - > xdg_surface_listener . configure = xdg_surface_configure ;
xdg_surface_add_listener ( r - > wayland . popup - > xdg_surface , & r - > wayland . popup - > xdg_surface_listener , r ) ;
/* Perform the initial commit and wait for the first configure event */
wl_surface_commit ( r - > wayland . framebuffer . surface ) ;
2026-03-24 23:08:43 -07:00
while ( ! r - > wayland . configured ) {
event_loop ( r ) ;
}
2025-12-18 23:32:51 -07:00
}
2025-12-30 17:12:12 -07:00
2025-12-18 23:32:51 -07:00
/* Popup destroy function */
static void destroy_popup ( MwLL r ) {
xdg_popup_destroy ( r - > wayland . popup - > xdg_popup ) ;
xdg_surface_destroy ( r - > wayland . popup - > xdg_surface ) ;
xdg_positioner_destroy ( r - > wayland . popup - > xdg_positioner ) ;
wl_surface_destroy ( r - > wayland . framebuffer . surface ) ;
wl_registry_destroy ( r - > wayland . registry ) ;
2026-03-19 16:35:56 -07:00
r - > wayland . configured = MwFALSE ;
}
2026-03-19 19:46:58 -07:00
static void clip ( MwLL handle ) {
2026-04-19 18:50:33 -07:00
int i ;
2026-04-23 22:45:34 +09:00
int x , y , cx , cy , mx , my ;
2026-04-19 18:50:33 -07:00
MwLL toplevel = handle - > wayland . parent ;
MwLL * ws = NULL ;
2026-04-20 12:36:01 +09:00
arrput ( ws , handle ) ;
2026-04-19 18:50:33 -07:00
while ( toplevel ) {
2026-04-20 12:36:01 +09:00
arrput ( ws , toplevel ) ;
2026-04-24 12:53:27 +09:00
if ( ! toplevel - > wayland . parent | | toplevel - > wayland . is_toplevel ) {
2026-04-19 18:50:33 -07:00
break ;
}
toplevel = toplevel - > wayland . parent ;
}
if ( toplevel ) {
# define MIN(a, b) ((a) < (b) ? (a) : (b))
# define MAX(a, b) ((a) > (b) ? (a) : (b))
2026-04-24 02:02:44 +09:00
/* i seriously have no fucking idea how this works, do not ask me how this really works (nishi) */
2026-04-19 18:50:33 -07:00
x = 0 ;
y = 0 ;
cx = 0 ;
cy = 0 ;
2026-04-23 22:45:34 +09:00
mx = toplevel - > wayland . ww ;
my = toplevel - > wayland . wh ;
2026-04-20 15:37:50 +09:00
// ws is traversed result
// ws[ws.length - 1] is ignored bc it's toplevel
2026-04-19 18:50:33 -07:00
for ( i = arrlen ( ws ) - 2 ; i > = 0 ; i - - ) {
2026-04-24 02:02:44 +09:00
int j ;
int l = 0 ;
2026-04-19 18:50:33 -07:00
x + = ws [ i ] - > wayland . x ;
y + = ws [ i ] - > wayland . y ;
2026-04-23 22:45:34 +09:00
2026-04-24 02:23:25 +09:00
for ( j = i - 1 ; j > = 0 ; j - - ) {
2026-04-24 02:02:44 +09:00
if ( ws [ j ] - > wayland . x > cx ) l = 1 ;
if ( ws [ j ] - > wayland . y > cy ) l = 1 ;
if ( l ) break ;
}
2026-04-24 02:23:25 +09:00
if ( ! l ) {
2026-04-24 02:02:44 +09:00
cx = MAX ( cx , ws [ i ] - > wayland . x ) ;
cy = MAX ( cy , ws [ i ] - > wayland . y ) ;
}
2026-04-23 23:27:44 +09:00
2026-04-23 22:45:34 +09:00
mx = MIN ( mx , x + ws [ i ] - > wayland . ww ) ;
my = MIN ( my , y + ws [ i ] - > wayland . wh ) ;
2026-04-19 18:50:33 -07:00
}
2026-04-20 14:44:53 +09:00
2026-04-24 01:43:38 +09:00
if ( mx < cx ) mx = cx ;
if ( my < cy ) my = cy ;
2026-04-20 12:36:01 +09:00
arrfree ( ws ) ;
2026-04-24 01:43:38 +09:00
if ( mx < cx ) mx = cx ;
if ( my < cy ) my = cy ;
2026-04-23 22:45:34 +09:00
handle - > wayland . clipping_rect . x = cx - x ;
handle - > wayland . clipping_rect . y = cy - y ;
handle - > wayland . clipping_rect . width = mx - cx ;
handle - > wayland . clipping_rect . height = my - cy ;
2026-04-19 18:50:33 -07:00
region_setup ( handle ) ;
2026-04-20 12:36:01 +09:00
2026-03-19 19:46:58 -07:00
cairo_reset_clip ( handle - > wayland . front_cairo ) ;
2026-04-24 12:53:27 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_SUBLEVEL & & ! handle - > wayland . is_toplevel ) {
2026-04-23 22:45:34 +09:00
cairo_rectangle ( handle - > wayland . front_cairo , cx - x , cy - y , mx - cx , my - cy ) ;
2026-04-20 14:49:20 +09:00
cairo_clip ( handle - > wayland . front_cairo ) ;
}
2026-03-20 19:28:18 -07:00
}
2026-03-20 09:59:07 +09:00
}
2026-03-19 16:35:56 -07:00
static void wl_logger ( const char * fmt , va_list args ) {
vprintf ( fmt , args ) ;
raise ( SIGTRAP ) ;
2025-12-18 23:32:51 -07:00
}
2026-03-24 22:40:36 -07:00
static void widget_setup ( MwLL r , MwLL parent , int x , int y , int width , int height , enum _MwLLWaylandType ty ) {
2026-01-04 13:06:36 -07:00
/* Wayland does not report global coordinates ever. Compositors are not even expected to have knowledge of this.
*/
2026-04-17 01:36:34 +09:00
r - > common . coordinate_type = MwCOORDINATE_LOCAL ;
2026-01-04 13:06:36 -07:00
2025-12-09 20:11:01 -06:00
r - > common . type = MwLLBackendWayland ;
2025-12-17 19:14:54 -07:00
if ( width < 2 ) width = 2 ;
if ( height < 2 ) height = 2 ;
2025-12-09 20:11:01 -06:00
if ( x = = MwDEFAULT ) {
x = 0 ;
}
if ( y = = MwDEFAULT ) {
y = 0 ;
}
2025-12-18 23:32:51 -07:00
r - > wayland . ww = width ;
r - > wayland . wh = height ;
r - > wayland . x = x ;
r - > wayland . y = y ;
r - > wayland . parent = parent ;
2026-04-23 10:57:05 -07:00
if ( is_destroyed ( parent ) ) {
r - > wayland . valid = MwFALSE ;
return ;
} else {
r - > wayland . valid = MwTRUE ;
}
2025-12-09 20:11:01 -06:00
2026-04-20 14:49:20 +09:00
if ( ty = = MwLL_WAYLAND_UNKNOWN ) {
2026-03-24 22:40:36 -07:00
if ( parent = = NULL ) {
setup_toplevel ( r , x , y ) ;
} else {
setup_sublevel ( parent , r , x , y ) ;
}
2025-12-09 20:11:01 -06:00
} else {
2026-03-24 22:40:36 -07:00
switch ( ty ) {
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_UNKNOWN :
2026-03-24 22:40:36 -07:00
break ;
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_TOPLEVEL :
2026-03-24 22:40:36 -07:00
setup_toplevel ( r , x , y ) ;
break ;
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_SUBLEVEL :
2026-03-24 22:40:36 -07:00
setup_sublevel ( parent , r , x , y ) ;
break ;
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_POPUP :
2026-03-24 23:08:43 -07:00
setup_popup ( r , x , y , parent ) ;
2026-03-24 22:40:36 -07:00
break ;
}
2025-12-09 20:11:01 -06:00
}
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( r ) ;
2026-04-23 10:45:29 -07:00
2025-12-18 13:15:03 -07:00
framebuffer_setup ( & r - > wayland ) ;
2026-03-19 19:43:57 -07:00
backbuffer_setup ( & r - > wayland ) ;
2025-12-17 19:14:54 -07:00
2026-03-19 15:57:20 -07:00
r - > wayland . region = wl_compositor_create_region ( r - > wayland . compositor ) ;
r - > wayland . o_region = wl_compositor_create_region ( r - > wayland . compositor ) ;
2025-12-18 11:26:51 -07:00
region_setup ( r ) ;
2025-12-17 19:14:54 -07:00
MwLLForceRender ( r ) ;
2026-01-12 18:50:33 -07:00
if ( parent ! = NULL ) {
MwLLForceRender ( parent ) ;
}
2026-03-26 17:48:22 -07:00
if ( ! r - > wayland . has_decorations ) {
MwLLBeginDraw ( r ) ;
MwLLEndDraw ( r ) ;
}
2026-03-24 22:40:36 -07:00
}
2026-04-15 12:25:51 -07:00
2026-04-16 23:40:29 +09:00
# ifdef USE_DBUS
2026-04-16 23:05:41 +02:00
static void dark_theme_listener ( MwLL handle , MwU32 new_value ) {
2026-04-17 09:22:30 +02:00
handle - > wayland . dark_theme = ( new_value = = 1 ) ? 1 : 0 ;
2026-04-16 23:05:41 +02:00
2026-04-17 09:22:30 +02:00
MwLLDispatch ( handle , dark_theme , & handle - > wayland . dark_theme ) ;
2026-04-16 23:05:41 +02:00
}
2026-04-15 15:21:16 -07:00
static void detect_dark_theme ( MwLL handle ) {
2026-04-17 09:22:30 +02:00
MwU32 value = 0 ;
2026-04-16 23:05:41 +02:00
2026-04-15 16:12:38 -07:00
MwLLDBusPortalGet ( & wl_call_tbl . dbus , & handle - > wayland . dbus , " org.freedesktop.portal.Settings " , " org.freedesktop.appearance " , " color-scheme " , & value ) ;
2026-04-17 09:22:30 +02:00
handle - > wayland . dark_theme = ( value = = 1 ) ? 1 : 0 ;
2026-04-15 15:21:16 -07:00
2026-04-16 23:05:41 +02:00
MwLLDBusPortalWatch ( & wl_call_tbl . dbus , & handle - > wayland . dbus , " org.freedesktop.portal.Settings " ) ;
2026-04-15 12:25:51 -07:00
2026-04-17 09:22:30 +02:00
MwLLDispatch ( handle , dark_theme , & handle - > wayland . dark_theme ) ;
2026-04-15 12:25:51 -07:00
}
2026-04-16 23:40:29 +09:00
# endif
2026-04-15 12:25:51 -07:00
2026-03-24 22:40:36 -07:00
static MwLL MwLLCreateImpl ( MwLL parent , int x , int y , int width , int height ) {
MwLL r ;
r = malloc ( sizeof ( * r ) ) ;
memset ( r , 0 , sizeof ( * r ) ) ;
MwLLCreateCommon ( r ) ;
2026-04-23 10:45:29 -07:00
if ( is_destroyed ( r ) ) {
undestroy ( r ) ;
}
2026-04-24 12:53:27 +09:00
r - > wayland . is_toplevel = parent = = NULL ;
2026-04-20 14:49:20 +09:00
widget_setup ( r , parent , x , y , width , height , MwLL_WAYLAND_UNKNOWN ) ;
2025-12-17 19:14:54 -07:00
2026-04-15 16:12:38 -07:00
# ifdef USE_DBUS
if ( ! parent & & wl_call_tbl . has_dbus ) {
wl_call_tbl . has_dbus = MwLLDBusNewContext ( & wl_call_tbl . dbus , & r - > wayland . dbus ) ;
2026-04-15 13:00:59 -07:00
r - > wayland . dark_theme_detection = MwTRUE ;
2026-04-17 09:22:30 +02:00
r - > wayland . dark_theme = - 1 ;
2026-04-15 12:25:51 -07:00
}
2026-04-15 16:12:38 -07:00
# endif
2026-04-15 12:25:51 -07:00
2025-12-09 20:11:01 -06:00
return r ;
}
static void MwLLDestroyImpl ( MwLL handle ) {
2026-04-23 14:47:51 -07:00
int i ;
struct timeval tv ;
int select_ret ;
2026-01-03 18:27:34 -07:00
2026-04-15 13:58:50 -07:00
event_loop ( handle ) ;
2026-04-17 13:12:22 -07:00
// wl_display_cancel_read(handle->wayland.display);
2026-04-15 13:58:50 -07:00
wl_flush ( handle ) ;
2026-04-23 10:45:29 -07:00
handle - > wayland . cancelEvent = MwTRUE ;
2025-12-18 23:32:51 -07:00
2025-12-09 20:11:01 -06:00
MwLLDestroyCommon ( handle ) ;
2026-01-12 18:18:05 -07:00
tv . tv_sec = 0 ;
tv . tv_usec = 1000 ;
do {
select_ret = select ( 1 , NULL , NULL , NULL , & tv ) ;
} while ( ( select_ret = = - 1 ) & & ( errno = = EINTR ) ) ;
pthread_mutex_destroy ( & handle - > wayland . eventsMutex ) ;
2026-04-16 23:40:29 +09:00
# ifdef USE_DBUS
2026-04-15 16:12:38 -07:00
if ( ! wl_call_tbl . has_dbus ) {
MwLLDBusFreeContext ( & wl_call_tbl . dbus , & handle - > wayland . dbus ) ;
2026-04-15 13:58:50 -07:00
}
2026-04-16 23:40:29 +09:00
# endif
2026-04-15 13:58:50 -07:00
2026-04-23 10:45:29 -07:00
if ( handle - > wayland . valid ) {
buffer_destroy ( & handle - > wayland . cursor ) ;
wl_region_destroy ( handle - > wayland . region ) ;
2025-12-17 19:14:54 -07:00
2026-04-23 10:45:29 -07:00
if ( handle - > wayland . supports_zwp ) {
zwp_primary_selection_source_v1_destroy ( handle - > wayland . clipboard_source . zwp ) ;
} else {
wl_data_source_destroy ( handle - > wayland . clipboard_source . wl ) ;
}
if ( handle - > wayland . icon ! = NULL ) {
buffer_destroy ( handle - > wayland . icon ) ;
wl_surface_destroy ( handle - > wayland . icon - > surface ) ;
}
2025-12-18 18:21:57 -07:00
2026-04-23 10:45:29 -07:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
destroy_toplevel ( handle ) ;
} else if ( handle - > wayland . type = = MwLL_WAYLAND_SUBLEVEL ) {
destroy_sublevel ( handle ) ;
} else if ( handle - > wayland . type = = MwLL_WAYLAND_POPUP ) {
destroy_popup ( handle ) ;
}
2025-12-18 23:32:51 -07:00
}
for ( i = 0 ; i < shlen ( handle - > wayland . wl_protocol_setup_map ) ; i + + ) {
void * ctx = shget ( handle - > wayland . wl_protocol_map , handle - > wayland . wl_protocol_setup_map [ i ] . key ) ;
2026-01-12 18:18:05 -07:00
if ( ctx ! = NULL ) {
handle - > wayland . wl_protocol_setup_map [ i ] . value - > destroy ( & handle - > wayland , ctx ) ;
}
2026-01-02 14:19:30 -07:00
shdel ( handle - > wayland . wl_protocol_map , handle - > wayland . wl_protocol_setup_map [ i ] . value ) ;
2025-12-18 23:32:51 -07:00
}
shfree ( handle - > wayland . wl_protocol_map ) ;
shfree ( handle - > wayland . wl_protocol_setup_map ) ;
2026-04-16 18:36:42 -07:00
wl_flush ( handle ) ;
2026-03-24 23:08:43 -07:00
2026-04-23 10:45:29 -07:00
pthread_mutex_lock ( & destroyedWidgetsTableMutex ) ;
arrput ( destroyedWidgetsTable , handle ) ;
pthread_mutex_unlock ( & destroyedWidgetsTableMutex ) ;
2026-04-17 13:12:22 -07:00
2026-04-26 20:01:03 -07:00
free ( handle ) ;
2026-03-24 23:08:43 -07:00
if ( currentlyHeldWidget = = handle ) {
currentlyHeldWidget = NULL ;
}
2025-12-09 20:11:01 -06:00
}
static void MwLLGetXYWHImpl ( MwLL handle , int * x , int * y , unsigned int * w , unsigned int * h ) {
2026-04-23 10:45:29 -07:00
if ( ! handle - > wayland . valid ) {
* x = 0 ;
* y = 0 ;
* w = 1 ;
* h = 1 ;
return ;
}
2025-12-09 20:11:01 -06:00
* x = handle - > wayland . x ;
* y = handle - > wayland . y ;
* w = handle - > wayland . ww ;
* h = handle - > wayland . wh ;
}
static void MwLLSetXYImpl ( MwLL handle , int x , int y ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-01-04 13:01:44 -07:00
region_invalidate ( handle ) ;
handle - > wayland . x = x ;
handle - > wayland . y = y ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_SUBLEVEL ) {
2025-12-17 19:14:54 -07:00
wl_subsurface_set_position ( handle - > wayland . sublevel - > subsurface , x , y ) ;
2025-12-09 20:11:01 -06:00
}
2026-01-04 13:01:44 -07:00
region_setup ( handle ) ;
2025-12-18 11:26:51 -07:00
2026-04-20 16:22:06 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_SUBLEVEL ) recursive_render ( handle ) ;
2026-04-20 12:36:01 +09:00
2025-12-17 19:14:54 -07:00
MwLLDispatch ( handle , draw , NULL ) ;
2025-12-09 20:11:01 -06:00
}
2026-04-23 14:39:38 -07:00
static void actually_set_wh ( MwLL handle ) {
2025-12-18 11:26:51 -07:00
region_invalidate ( handle ) ;
2025-12-30 17:12:12 -07:00
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL & & handle - > wayland . configured ) {
2025-12-09 20:11:01 -06:00
xdg_surface_set_window_geometry ( handle - > wayland . toplevel - > xdg_surface , 0 , 0 , handle - > wayland . ww , handle - > wayland . wh ) ;
}
2025-12-18 11:26:51 -07:00
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_SUBLEVEL & & handle - > wayland . configured & & ! handle - > wayland . dispatching_resize ) {
2026-03-27 15:14:04 -07:00
handle - > wayland . dispatching_resize = MwTRUE ;
MwLLDispatch ( handle , resize , NULL ) ;
handle - > wayland . dispatching_resize = MwFALSE ;
}
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_POPUP ) {
2026-03-19 19:43:57 -07:00
destroy_popup ( handle ) ;
wl_flush ( handle ) ;
2026-03-24 23:08:43 -07:00
setup_popup ( handle , handle - > wayland . x , handle - > wayland . y , handle - > wayland . parent ) ;
2026-03-17 20:28:58 -07:00
}
2025-12-18 11:26:51 -07:00
region_setup ( handle ) ;
2026-04-20 16:22:06 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_SUBLEVEL ) recursive_render ( handle ) ;
2025-12-18 11:26:51 -07:00
2025-12-18 13:15:03 -07:00
framebuffer_destroy ( & handle - > wayland ) ;
framebuffer_setup ( & handle - > wayland ) ;
2026-03-19 19:43:57 -07:00
backbuffer_destroy ( & handle - > wayland ) ;
backbuffer_setup ( & handle - > wayland ) ;
2026-01-12 18:48:22 -07:00
MwLLDispatch ( handle , draw , NULL ) ;
2026-04-23 14:39:38 -07:00
}
static void MwLLSetWHImpl ( MwLL handle , int w , int h ) {
WIDGET_CHECK ( handle ) ;
2026-04-26 12:47:22 -07:00
if ( handle - > wayland . ww = = w & & handle - > wayland . wh = = h ) {
return ;
}
2026-04-23 14:39:38 -07:00
/* Prevent an integer underflow when the w/h is too low */
if ( ( w < 2 | | h < 2 ) ) {
handle - > wayland . ww = 2 ;
handle - > wayland . wh = 2 ;
} else {
handle - > wayland . ww = w ;
handle - > wayland . wh = h ;
}
if ( ! handle - > wayland . setting_wh ) {
handle - > wayland . setting_wh = 1 ;
}
2026-03-23 15:45:48 -07:00
handle - > wayland . events_pending = 1 ;
2025-12-09 20:11:01 -06:00
}
2026-03-19 16:05:44 -07:00
static void MwLLBeginDrawImpl ( MwLL handle ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-20 12:36:01 +09:00
cairo_save ( handle - > wayland . front_cairo ) ;
cairo_set_source_rgba ( handle - > wayland . front_cairo , 0 , 0 , 0 , 0 ) ;
cairo_set_operator ( handle - > wayland . front_cairo , CAIRO_OPERATOR_SOURCE ) ;
cairo_rectangle ( handle - > wayland . front_cairo , 0 , 0 , handle - > wayland . ww , handle - > wayland . wh ) ;
cairo_fill ( handle - > wayland . front_cairo ) ;
cairo_restore ( handle - > wayland . front_cairo ) ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type ! = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-20 19:28:18 -07:00
handle - > wayland . selected_cairo = handle - > wayland . front_cairo ;
2026-03-19 19:43:57 -07:00
return ;
}
2026-03-23 15:45:48 -07:00
2026-03-19 19:43:57 -07:00
if ( ! handle - > wayland . has_decorations ) {
2026-03-24 17:38:21 -07:00
int x ;
2026-03-23 15:32:28 -07:00
MwU32 w = handle - > wayland . ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT ;
MwU32 h = handle - > wayland . wh + CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM ;
2026-03-20 19:28:18 -07:00
cairo_set_line_width ( handle - > wayland . back_cairo , 4.0 ) ;
2026-03-23 15:32:28 -07:00
for ( x = 0 ; x < w ; x + + ) {
float placeholder = ( float ) x / ( float ) w ;
2026-03-20 19:28:18 -07:00
cairo_set_source_rgb ( handle - > wayland . back_cairo , placeholder , 0.25 , 0.25 ) ;
2026-03-23 15:32:28 -07:00
cairo_move_to ( handle - > wayland . back_cairo , w - x , 0 ) ;
cairo_line_to ( handle - > wayland . back_cairo , w - x , CSD_BORDER_FRAME_TOP ) ;
2026-03-20 19:28:18 -07:00
cairo_stroke ( handle - > wayland . back_cairo ) ;
cairo_move_to ( handle - > wayland . back_cairo , x , CSD_BORDER_FRAME_TOP ) ;
2026-03-23 15:32:28 -07:00
cairo_line_to ( handle - > wayland . back_cairo , x , h ) ;
2026-03-20 19:28:18 -07:00
cairo_stroke ( handle - > wayland . back_cairo ) ;
}
cairo_set_source_rgba ( handle - > wayland . back_cairo , 1 , 1 , 1 , 0.5 ) ;
cairo_move_to ( handle - > wayland . back_cairo , 0 , 0 ) ;
2026-03-23 15:32:28 -07:00
cairo_line_to ( handle - > wayland . back_cairo , 0 , h ) ;
2026-03-20 19:28:18 -07:00
cairo_stroke ( handle - > wayland . back_cairo ) ;
cairo_move_to ( handle - > wayland . back_cairo , 0 , 0 ) ;
2026-03-23 15:32:28 -07:00
cairo_line_to ( handle - > wayland . back_cairo , w , 0 ) ;
2026-03-20 19:28:18 -07:00
cairo_stroke ( handle - > wayland . back_cairo ) ;
cairo_set_source_rgba ( handle - > wayland . back_cairo , 0 , 0 , 0 , 0.5 ) ;
2026-03-23 15:32:28 -07:00
cairo_move_to ( handle - > wayland . back_cairo , 0 , h ) ;
cairo_line_to ( handle - > wayland . back_cairo , w , h ) ;
2026-03-20 19:28:18 -07:00
cairo_stroke ( handle - > wayland . back_cairo ) ;
2026-03-23 15:32:28 -07:00
cairo_move_to ( handle - > wayland . back_cairo , w , 0 ) ;
cairo_line_to ( handle - > wayland . back_cairo , w , h ) ;
2026-03-19 19:43:57 -07:00
cairo_stroke ( handle - > wayland . back_cairo ) ;
2026-03-20 19:28:18 -07:00
if ( strlen ( handle - > wayland . title ) ! = 0 ) {
int y , x ;
int i = 0 , sx = 0 , sy = 0 ;
int tw , th ;
unsigned char * px ;
MwRect r ;
MwLLPixmap p ;
tw = strlen ( handle - > wayland . title ) * 7 ;
th = 14 ;
px = malloc ( tw * th * 4 ) ;
assert ( px ) ;
memset ( px , 0 , tw * th * 4 ) ;
handle - > wayland . selected_cairo = handle - > wayland . back_cairo ;
while ( handle - > wayland . title [ i ] ) {
int out ;
i + = MwUTF8ToUTF32 ( handle - > wayland . title + i , & out ) ;
if ( out > 0xff ) {
out = 0 ;
}
for ( y = 0 ; y < 14 ; y + + ) {
for ( x = 0 ; x < 7 ; x + + ) {
unsigned char * ppx = & px [ ( ( sy + y ) * tw + sx + x ) * 4 ] ;
int col = MwFontData [ out ] . data [ y ] & ( 1 < < ( ( 7 - 1 ) - x ) ) ? 255 : 0 ;
ppx [ 0 ] = col ;
ppx [ 1 ] = col ;
ppx [ 2 ] = col ;
ppx [ 3 ] = col ;
}
}
sx + = 7 ;
}
p = MwLLCreatePixmap ( handle , px , tw , th ) ;
r . x = 5 ;
r . y = 5 ;
r . width = tw ;
r . height = th ;
MwLLDrawPixmap ( handle , & r , p ) ;
MwLLDestroyPixmap ( p ) ;
free ( px ) ;
}
2026-03-25 00:31:23 -07:00
update_buffer ( handle , & handle - > wayland . backbuffer ) ;
2026-03-19 19:43:57 -07:00
}
2026-03-20 19:28:18 -07:00
handle - > wayland . selected_cairo = handle - > wayland . front_cairo ;
2026-03-19 16:05:44 -07:00
}
static void MwLLEndDrawImpl ( MwLL handle ) {
2026-03-19 19:43:57 -07:00
if ( handle - > wayland . configured ) {
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-25 00:31:23 -07:00
update_buffer ( handle , & handle - > wayland . backbuffer ) ;
2026-03-19 19:43:57 -07:00
}
2026-03-25 00:31:23 -07:00
update_buffer ( handle , & handle - > wayland . framebuffer ) ;
2026-03-19 19:43:57 -07:00
}
2026-03-19 16:05:44 -07:00
}
2025-12-09 20:11:01 -06:00
static void MwLLPolygonImpl ( MwLL handle , MwPoint * points , int points_count , MwLLColor color ) {
2025-12-17 19:14:54 -07:00
int i ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-03-20 09:59:07 +09:00
clip ( handle ) ;
2026-03-26 17:48:22 -07:00
cairo_set_source_rgba ( handle - > wayland . front_cairo , color - > common . red / 255.0 , color - > common . green / 255.0 , color - > common . blue / 255.0 , 1.0 ) ;
2026-03-19 19:43:57 -07:00
cairo_new_path ( handle - > wayland . front_cairo ) ;
2025-12-09 20:11:01 -06:00
for ( i = 0 ; i < points_count ; i + + ) {
2025-12-17 19:14:54 -07:00
if ( i = = 0 ) {
2026-03-19 19:43:57 -07:00
cairo_move_to ( handle - > wayland . front_cairo , points [ i ] . x , points [ i ] . y ) ;
2025-12-17 19:14:54 -07:00
} else {
2026-03-19 19:43:57 -07:00
cairo_line_to ( handle - > wayland . front_cairo , points [ i ] . x , points [ i ] . y ) ;
2025-12-17 19:14:54 -07:00
}
2025-12-09 20:11:01 -06:00
}
2026-03-19 19:43:57 -07:00
cairo_close_path ( handle - > wayland . front_cairo ) ;
2026-03-19 15:57:20 -07:00
2026-04-14 23:49:04 -07:00
cairo_set_operator ( handle - > wayland . front_cairo , CAIRO_OPERATOR_SOURCE ) ;
2026-03-19 19:43:57 -07:00
cairo_fill ( handle - > wayland . front_cairo ) ;
2026-04-14 23:49:04 -07:00
cairo_set_operator ( handle - > wayland . front_cairo , CAIRO_OPERATOR_OVER ) ;
2025-12-30 22:26:19 -07:00
2026-03-18 19:24:51 -07:00
handle - > wayland . events_pending = 1 ;
2025-12-09 20:11:01 -06:00
}
static void MwLLLineImpl ( MwLL handle , MwPoint * points , MwLLColor color ) {
2025-12-17 19:14:54 -07:00
int i ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2025-12-09 20:11:01 -06:00
2026-03-26 14:40:21 -07:00
clip ( handle ) ;
cairo_set_antialias ( handle - > wayland . front_cairo , CAIRO_ANTIALIAS_NONE ) ;
2026-03-19 19:43:57 -07:00
cairo_set_line_cap ( handle - > wayland . front_cairo , CAIRO_LINE_CAP_SQUARE ) ;
2026-03-26 17:48:22 -07:00
cairo_set_source_rgba ( handle - > wayland . front_cairo , color - > common . red / 255.0 , color - > common . green / 255.0 , color - > common . blue / 255.0 , 1.0 ) ;
2026-03-19 19:43:57 -07:00
cairo_new_path ( handle - > wayland . front_cairo ) ;
2025-12-09 20:11:01 -06:00
for ( i = 0 ; i < 2 ; i + + ) {
2025-12-17 19:14:54 -07:00
if ( i = = 0 ) {
2026-03-19 19:43:57 -07:00
cairo_move_to ( handle - > wayland . front_cairo , points [ i ] . x , points [ i ] . y ) ;
2025-12-17 19:14:54 -07:00
} else {
2026-03-19 19:43:57 -07:00
cairo_line_to ( handle - > wayland . front_cairo , points [ i ] . x , points [ i ] . y ) ;
2025-12-17 19:14:54 -07:00
}
2025-12-09 20:11:01 -06:00
}
2026-03-19 19:43:57 -07:00
cairo_close_path ( handle - > wayland . front_cairo ) ;
cairo_stroke ( handle - > wayland . front_cairo ) ;
2025-12-30 22:26:19 -07:00
2026-03-18 19:24:51 -07:00
handle - > wayland . events_pending = 1 ;
2025-12-09 20:11:01 -06:00
}
static MwLLColor MwLLAllocColorImpl ( MwLL handle , int r , int g , int b ) {
MwLLColor c = malloc ( sizeof ( * c ) ) ;
2026-03-24 17:38:21 -07:00
( void ) handle ;
2025-12-09 20:11:01 -06:00
c - > common . red = r ;
c - > common . blue = b ;
c - > common . green = g ;
return c ;
}
static void MwLLColorUpdateImpl ( MwLL handle , MwLLColor c , int r , int g , int b ) {
2026-03-24 17:38:21 -07:00
( void ) handle ;
2025-12-09 20:11:01 -06:00
c - > common . red = r ;
c - > common . blue = b ;
c - > common . green = g ;
}
static void MwLLFreeColorImpl ( MwLLColor color ) {
free ( color ) ;
}
2025-12-30 19:15:33 -07:00
static int MwLLPendingImpl ( MwLL handle ) {
2026-03-23 12:05:32 -07:00
struct timespec timeout = {
2026-03-24 14:52:53 -07:00
. tv_nsec = 1 ,
2026-03-23 12:05:32 -07:00
. tv_sec = 0 ,
} ;
struct pollfd fd = {
. fd = wl_display_get_fd ( handle - > wayland . display ) ,
. events = POLLOUT ,
} ;
int pending = 0 ;
2026-04-23 11:04:47 -07:00
if ( is_destroyed ( handle ) | | ! handle - > wayland . valid ) {
2026-04-23 10:45:29 -07:00
return 0 ;
}
2026-03-19 17:32:36 -07:00
2026-04-19 21:12:39 -07:00
handle - > wayland . resizing = 0 ;
2026-04-23 14:39:38 -07:00
if ( handle - > wayland . setting_wh ) {
actually_set_wh ( handle ) ;
handle - > wayland . setting_wh = 0 ;
}
2026-04-16 23:40:29 +09:00
# ifdef USE_DBUS
2026-04-16 23:05:41 +02:00
if ( wl_call_tbl . has_dbus ) {
if ( handle - > wayland . dark_theme_detection ) {
handle - > wayland . dark_theme_detection = MwFALSE ;
2026-04-15 13:00:59 -07:00
detect_dark_theme ( handle ) ;
2026-04-15 13:58:50 -07:00
MwLLDispatch ( handle , draw , NULL ) ;
2026-04-16 23:05:41 +02:00
} else {
MwLLDBusPortalPoll ( & wl_call_tbl . dbus , & handle - > wayland . dbus , handle , " org.freedesktop.portal.Settings " , " org.freedesktop.appearance " , " color-scheme " , & dark_theme_listener ) ;
2026-04-15 13:00:59 -07:00
}
2026-04-15 12:25:51 -07:00
}
2026-04-16 23:05:41 +02:00
# endif
2026-04-15 12:25:51 -07:00
2026-03-23 14:28:49 -07:00
if ( ! handle - > wayland . did_initial_resize ) {
2026-03-24 14:43:38 -07:00
recursive_dispatch_resize ( handle ) ;
2026-03-23 14:28:49 -07:00
handle - > wayland . did_initial_resize = 1 ;
}
2026-03-25 00:43:35 -07:00
if ( ! MwWaylandAlwaysRender ) {
wl_display_prepare_read ( handle - > wayland . display ) ;
if ( ! poll ( & fd , 1 , timeout . tv_nsec ) ) {
wl_display_cancel_read ( handle - > wayland . display ) ;
} else {
wl_display_read_events ( handle - > wayland . display ) ;
if ( ( pending = wl_display_dispatch_pending ( handle - > wayland . display ) ) < 0 ) {
wl_display_cancel_read ( handle - > wayland . display ) ;
}
}
2026-03-19 17:32:36 -07:00
} else {
if ( ( pending = wl_display_dispatch_pending ( handle - > wayland . display ) ) < 0 ) {
wl_display_cancel_read ( handle - > wayland . display ) ;
}
}
2026-03-22 16:32:36 -07:00
2026-03-24 23:24:20 -07:00
if ( MwWaylandAlwaysRender ) {
2026-03-23 12:05:32 -07:00
handle - > wayland . did_event_loop_early = MwTRUE ;
event_loop ( handle ) ;
2026-03-22 16:32:36 -07:00
return pending ;
}
2026-03-25 00:31:23 -07:00
update_buffer ( handle , & handle - > wayland . framebuffer ) ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-25 00:31:23 -07:00
update_buffer ( handle , & handle - > wayland . backbuffer ) ;
2026-03-19 19:43:57 -07:00
}
2026-03-17 17:43:22 -07:00
2026-03-23 12:05:32 -07:00
return handle - > wayland . force_render | | handle - > wayland . events_pending | | pending ;
2025-12-09 20:11:01 -06:00
}
static void MwLLNextEventImpl ( MwLL handle ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-03-24 23:24:20 -07:00
if ( ! MwWaylandAlwaysRender ) {
2026-03-17 17:43:22 -07:00
if ( handle - > wayland . did_event_loop_early ) {
handle - > wayland . did_event_loop_early = MwFALSE ;
} else {
event_loop ( handle ) ;
}
2025-12-30 22:44:04 -07:00
}
2026-03-23 12:05:32 -07:00
2025-12-30 22:38:01 -07:00
if ( handle - > wayland . force_render ) {
2026-03-24 14:52:53 -07:00
MwLLDispatch ( handle , draw , NULL ) ;
2026-03-25 00:31:23 -07:00
if ( handle - > wayland . configured ) update_buffer ( handle , & handle - > wayland . framebuffer ) ;
2025-12-30 22:38:01 -07:00
handle - > wayland . force_render = 0 ;
2025-12-18 23:50:03 -07:00
}
2026-03-23 12:05:32 -07:00
if ( handle - > wayland . events_pending ) {
handle - > wayland . events_pending = 0 ;
}
2025-12-09 20:11:01 -06:00
}
static void MwLLSetTitleImpl ( MwLL handle , const char * title ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2025-12-09 20:11:01 -06:00
xdg_toplevel_set_title ( handle - > wayland . toplevel - > xdg_top_level , title ) ;
}
2026-03-19 19:43:57 -07:00
if ( ! handle - > wayland . has_decorations ) {
strncpy ( handle - > wayland . title , title , 255 ) ;
2026-03-26 17:48:22 -07:00
MwLLBeginDraw ( handle ) ;
MwLLEndDraw ( handle ) ;
2026-03-19 19:43:57 -07:00
}
2025-12-09 20:11:01 -06:00
}
static MwLLPixmap MwLLCreatePixmapImpl ( MwLL handle , unsigned char * data , int width , int height ) {
MwLLPixmap r = malloc ( sizeof ( * r ) ) ;
2026-03-24 17:38:21 -07:00
( void ) handle ;
2025-12-09 20:11:01 -06:00
2025-12-17 19:14:54 -07:00
r - > common . width = width ;
r - > common . height = height ;
r - > common . raw = malloc ( 4 * width * height ) ;
memcpy ( r - > common . raw , data , 4 * width * height ) ;
2025-12-09 20:11:01 -06:00
2025-12-17 19:14:54 -07:00
r - > wayland . cs = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32 , width , height ) ;
2025-12-09 20:11:01 -06:00
MwLLPixmapUpdate ( r ) ;
return r ;
}
static void MwLLPixmapUpdateImpl ( MwLLPixmap r ) {
2025-12-17 19:14:54 -07:00
int i ;
unsigned char * d ;
cairo_surface_flush ( r - > wayland . cs ) ;
d = cairo_image_surface_get_data ( r - > wayland . cs ) ;
for ( i = 0 ; i < r - > common . width * r - > common . height * 4 ; i + = 4 ) {
MwU32 * p = ( MwU32 * ) & d [ i ] ;
* p < < = 8 ;
* p | = r - > common . raw [ i + 3 ] ;
* p < < = 8 ;
* p | = r - > common . raw [ i + 0 ] ;
* p < < = 8 ;
* p | = r - > common . raw [ i + 1 ] ;
* p < < = 8 ;
* p | = r - > common . raw [ i + 2 ] ;
}
cairo_surface_mark_dirty ( r - > wayland . cs ) ;
2025-12-09 20:11:01 -06:00
}
static void MwLLDestroyPixmapImpl ( MwLLPixmap pixmap ) {
2025-12-17 19:14:54 -07:00
cairo_surface_destroy ( pixmap - > wayland . cs ) ;
free ( pixmap - > common . raw ) ;
2025-12-09 20:11:01 -06:00
free ( pixmap ) ;
}
static void MwLLDrawPixmapImpl ( MwLL handle , MwRect * rect , MwLLPixmap pixmap ) {
2025-12-17 19:14:54 -07:00
cairo_t * c ;
cairo_surface_t * cs ;
2026-03-22 16:32:36 -07:00
cairo_t * selected_cairo = handle - > wayland . selected_cairo ? handle - > wayland . selected_cairo : handle - > wayland . front_cairo ;
2026-04-26 20:01:03 -07:00
if ( rect - > width < = 0 | | rect - > height < = 0 | | pixmap - > common . width < = 0 | | pixmap - > common . height < = 0 ) return ;
if ( rect - > width > = INT16_MAX ) rect - > width = INT16_MAX ;
if ( rect - > height > = INT16_MAX ) rect - > height = INT16_MAX ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2025-12-09 20:11:01 -06:00
2025-12-17 19:14:54 -07:00
cs = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32 , rect - > width , rect - > height ) ;
c = cairo_create ( cs ) ;
2026-03-20 09:59:07 +09:00
clip ( handle ) ;
2026-03-19 17:05:08 -07:00
2025-12-17 19:14:54 -07:00
cairo_scale ( c , ( double ) rect - > width / pixmap - > common . width , ( double ) rect - > height / pixmap - > common . height ) ;
2026-03-19 15:57:20 -07:00
2025-12-17 19:14:54 -07:00
cairo_set_source_surface ( c , pixmap - > wayland . cs , 0 , 0 ) ;
2025-12-18 12:24:06 +09:00
cairo_pattern_set_filter ( cairo_get_source ( c ) , CAIRO_FILTER_NEAREST ) ;
2026-03-19 15:57:20 -07:00
2026-04-14 23:49:04 -07:00
cairo_set_operator ( handle - > wayland . front_cairo , CAIRO_OPERATOR_OVER ) ;
2025-12-17 19:14:54 -07:00
cairo_paint ( c ) ;
2026-03-22 16:15:50 -07:00
cairo_set_source_surface ( selected_cairo , cs , rect - > x , rect - > y ) ;
cairo_paint ( selected_cairo ) ;
2025-12-09 20:11:01 -06:00
2025-12-17 19:14:54 -07:00
cairo_destroy ( c ) ;
cairo_surface_destroy ( cs ) ;
2026-03-17 17:43:22 -07:00
MwLLForceRender ( handle ) ;
2026-03-20 19:42:30 -07:00
wl_surface_damage ( handle - > wayland . framebuffer . surface , 0 , 0 , handle - > wayland . ww , handle - > wayland . wh ) ;
2025-12-09 20:11:01 -06:00
}
static void MwLLSetIconImpl ( MwLL handle , MwLLPixmap pixmap ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2025-12-18 18:21:57 -07:00
if ( WAYLAND_GET_INTERFACE ( handle - > wayland , xdg_toplevel_icon_manager_v1 ) ! = NULL ) {
struct xdg_toplevel_icon_manager_v1 * icon_manager = WAYLAND_GET_INTERFACE ( handle - > wayland , xdg_toplevel_icon_manager_v1 ) - > context ;
struct xdg_toplevel_icon_v1 * icon = xdg_toplevel_icon_manager_v1_create_icon ( icon_manager ) ;
MwU64 size = pixmap - > common . width * pixmap - > common . height * 4 ;
int i = 0 ;
if ( handle - > wayland . icon = = NULL ) {
2026-01-02 14:19:30 -07:00
handle - > wayland . icon = malloc ( sizeof ( struct _MwLLWaylandShmBuffer ) ) ;
memset ( handle - > wayland . icon , 0 , sizeof ( struct _MwLLWaylandShmBuffer ) ) ;
2025-12-18 18:21:57 -07:00
handle - > wayland . icon - > shm = handle - > wayland . framebuffer . shm ;
}
if ( handle - > wayland . icon - > setup ) {
buffer_destroy ( handle - > wayland . icon ) ;
wl_surface_destroy ( handle - > wayland . icon - > surface ) ;
}
handle - > wayland . icon - > surface = wl_compositor_create_surface ( handle - > wayland . compositor ) ;
2026-04-14 13:45:47 -07:00
buffer_setup ( handle - > wayland . icon , pixmap - > common . width , pixmap - > common . height ) ;
2026-03-26 21:36:57 -07:00
wl_surface_attach ( handle - > wayland . icon - > surface , handle - > wayland . icon - > shm_buffer , 0 , 0 ) ;
wl_surface_commit ( handle - > wayland . icon - > surface ) ;
2025-12-18 18:21:57 -07:00
for ( ; i < size ; i + = 2 ) {
2026-03-26 16:54:40 -07:00
handle - > wayland . icon - > buf_back [ i ] = pixmap - > common . raw [ i + 2 ] ;
handle - > wayland . icon - > buf_back [ i + 1 ] = pixmap - > common . raw [ i + 1 ] ;
handle - > wayland . icon - > buf_back [ i + 2 ] = pixmap - > common . raw [ i + 0 ] ;
handle - > wayland . icon - > buf_back [ i + 3 ] = 255 ;
2025-12-18 18:21:57 -07:00
}
2026-03-25 00:31:23 -07:00
if ( handle - > wayland . configured ) update_buffer ( handle , handle - > wayland . icon ) ;
2025-12-18 18:21:57 -07:00
2026-04-17 13:12:22 -07:00
xdg_toplevel_icon_v1_add_buffer ( icon , handle - > wayland . icon - > shm_buffer_back , 1 ) ;
2025-12-18 18:21:57 -07:00
xdg_toplevel_icon_manager_v1_set_icon ( icon_manager , handle - > wayland . toplevel - > xdg_top_level , icon ) ;
}
}
2025-12-09 20:11:01 -06:00
}
static void MwLLForceRenderImpl ( MwLL handle ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2025-12-18 13:15:03 -07:00
wl_surface_damage ( handle - > wayland . framebuffer . surface , 0 , 0 , handle - > wayland . ww , handle - > wayland . wh ) ;
2025-12-09 20:11:01 -06:00
2025-12-30 22:26:19 -07:00
handle - > wayland . force_render = MwTRUE ;
2026-03-23 12:05:32 -07:00
if ( handle - > wayland . parent ) {
handle - > wayland . parent - > wayland . force_render = MwTRUE ;
}
2025-12-09 20:11:01 -06:00
}
static void MwLLSetCursorImpl ( MwLL handle , MwCursor * image , MwCursor * mask ) {
2026-03-23 12:35:29 -07:00
int x , y , xs , ys ;
2025-12-18 13:15:03 -07:00
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-23 10:45:29 -07:00
2025-12-18 13:15:03 -07:00
if ( handle - > wayland . cursor . setup ) {
buffer_destroy ( & handle - > wayland . cursor ) ;
wl_surface_destroy ( handle - > wayland . cursor . surface ) ;
}
2026-04-24 01:43:38 +09:00
buffer_setup ( & handle - > wayland . cursor , mask - > width , mask - > height ) ;
2026-03-26 16:54:40 -07:00
memset ( handle - > wayland . cursor . buf_back , 0 , handle - > wayland . cursor . buf_size ) ;
2025-12-18 13:15:03 -07:00
2026-03-23 12:35:29 -07:00
xs = - mask - > x + image - > x ;
2026-03-24 04:56:08 +09:00
ys = mask - > height + mask - > y ;
ys = image - > height + image - > y - ys ;
2026-03-23 12:35:29 -07:00
2026-04-24 01:43:38 +09:00
if ( ys < 0 ) ys = - ys ;
2025-12-18 13:15:03 -07:00
for ( y = 0 ; y < mask - > height ; y + + ) {
unsigned int d = mask - > data [ y ] ;
for ( x = mask - > width - 1 ; x > = 0 ; x - - ) {
2026-04-24 01:43:38 +09:00
int idx = ( ( y * mask - > width ) + x ) * 4 ;
2025-12-18 13:15:03 -07:00
if ( d & 1 ) {
2026-03-26 16:54:40 -07:00
handle - > wayland . cursor . buf_back [ idx + 3 ] = 255 ;
2025-12-18 13:15:03 -07:00
} ;
d = d > > 1 ;
}
}
for ( y = 0 ; y < image - > height ; y + + ) {
unsigned int d = image - > data [ y ] ;
for ( x = image - > width - 1 ; x > = 0 ; x - - ) {
int px = 0 ;
2026-04-24 01:43:38 +09:00
int idx = ( ( ( y + ys ) * mask - > width ) + ( x + xs ) ) * 4 ;
2025-12-18 13:15:03 -07:00
if ( d & 1 ) {
px = 255 ;
} ;
2026-03-26 16:54:40 -07:00
handle - > wayland . cursor . buf_back [ idx ] = px ;
handle - > wayland . cursor . buf_back [ idx + 1 ] = px ;
handle - > wayland . cursor . buf_back [ idx + 2 ] = px ;
d = d > > 1 ;
2025-12-18 13:15:03 -07:00
}
}
handle - > wayland . cursor . surface = wl_compositor_create_surface ( handle - > wayland . compositor ) ;
2026-03-26 21:36:57 -07:00
wl_surface_attach ( handle - > wayland . cursor . surface , handle - > wayland . cursor . shm_buffer , 0 , 0 ) ;
2025-12-18 13:15:03 -07:00
wl_surface_commit ( handle - > wayland . cursor . surface ) ;
2026-03-26 21:36:57 -07:00
update_buffer ( handle , & handle - > wayland . cursor ) ;
2025-12-09 20:11:01 -06:00
}
static void MwLLDetachImpl ( MwLL handle , MwPoint * point ) {
2026-03-26 21:36:57 -07:00
MwLL p = handle - > wayland . parent ;
int x = 0 , y = 0 ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-03-26 21:36:57 -07:00
while ( p ! = NULL ) {
x + = p - > wayland . x ;
y + = p - > wayland . y ;
p = p - > wayland . parent ;
}
2026-03-23 12:05:32 -07:00
handle - > wayland . detatching = MwTRUE ;
handle - > wayland . detach_point = * point ;
2026-03-26 21:36:57 -07:00
handle - > wayland . detach_point . x + = x ;
handle - > wayland . detach_point . y + = y ;
2025-12-09 20:11:01 -06:00
}
static void MwLLShowImpl ( MwLL handle , int show ) {
2026-04-14 13:45:47 -07:00
if ( ! handle - > wayland . configured ) {
return ;
}
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-03-26 23:32:24 -07:00
/* Some guy on a mailing list said that "abusing" wl_surface_attach for this purpose is bad? This is documented behavior so please I beg of you let me know if there's a compositor that actually has a problem with this. */
if ( handle - > wayland . framebuffer . surface ) {
wl_surface_attach ( handle - > wayland . framebuffer . surface , show ? handle - > wayland . framebuffer . shm_buffer : NULL , 0 , 0 ) ;
wl_surface_commit ( handle - > wayland . framebuffer . surface ) ;
}
if ( handle - > wayland . backbuffer . surface ) {
wl_surface_attach ( handle - > wayland . backbuffer . surface , show ? handle - > wayland . backbuffer . shm_buffer : NULL , 0 , 0 ) ;
wl_surface_commit ( handle - > wayland . backbuffer . surface ) ;
2026-03-19 17:05:08 -07:00
}
2025-12-09 20:11:01 -06:00
}
static void MwLLMakePopupImpl ( MwLL handle , MwLL parent ) {
2026-03-24 17:38:21 -07:00
( void ) handle ;
( void ) parent ;
2026-04-17 13:14:41 -07:00
/* Wayland doesn't have "popups" in the Milsko sense per se. xdg_popup is closer to ToolWindow and as such is what we use there. So just like the Mac backend, this is just left alone. */
2025-12-09 20:11:01 -06:00
}
static void MwLLSetSizeHintsImpl ( MwLL handle , int minx , int miny , int maxx , int maxy ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2025-12-09 20:11:01 -06:00
xdg_toplevel_set_min_size ( handle - > wayland . toplevel - > xdg_top_level , minx , miny ) ;
xdg_toplevel_set_max_size ( handle - > wayland . toplevel - > xdg_top_level , maxx , maxy ) ;
}
}
static void MwLLMakeBorderlessImpl ( MwLL handle , int toggle ) {
2026-03-24 17:38:21 -07:00
( void ) toggle ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-20 14:49:20 +09:00
if ( handle - > wayland . type = = MwLL_WAYLAND_TOPLEVEL ) {
2025-12-09 20:11:01 -06:00
if ( WAYLAND_GET_INTERFACE ( handle - > wayland , zxdg_decoration_manager_v1 ) ! = NULL ) {
zxdg_decoration_manager_v1_context_t * dec = WAYLAND_GET_INTERFACE ( handle - > wayland , zxdg_decoration_manager_v1 ) - > context ;
dec - > decoration = zxdg_decoration_manager_v1_get_toplevel_decoration (
dec - > manager , handle - > wayland . toplevel - > xdg_top_level ) ;
zxdg_toplevel_decoration_v1_set_mode (
dec - > decoration , ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE ) ;
}
}
}
static void MwLLFocusImpl ( MwLL handle ) {
2026-03-24 17:38:21 -07:00
( void ) handle ;
2025-12-18 19:59:40 -07:00
printf ( " [WARNING] MwLLFocus not supported on Wayland \n " ) ;
2025-12-09 20:11:01 -06:00
}
static void MwLLGrabPointerImpl ( MwLL handle , int toggle ) {
2026-03-27 17:15:59 -07:00
MwLL topmost_parent = handle ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-03-27 17:15:59 -07:00
while ( topmost_parent - > wayland . parent ) topmost_parent = topmost_parent - > wayland . parent ;
if ( handle - > wayland . pointer_constraints & & handle - > wayland . relative_pointer_manager ) {
if ( toggle ) {
topmost_parent - > wayland . locked_pointer = zwp_pointer_constraints_v1_lock_pointer ( topmost_parent - > wayland . pointer_constraints , topmost_parent - > wayland . backbuffer . surface , topmost_parent - > wayland . pointer , topmost_parent - > wayland . region , ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT ) ;
wl_surface_commit ( topmost_parent - > wayland . backbuffer . surface ) ;
2026-03-27 18:06:17 -07:00
zwp_locked_pointer_v1_set_cursor_position_hint ( topmost_parent - > wayland . locked_pointer , 0 , CSD_BORDER_FRAME_TOP ) ;
2026-03-27 17:15:59 -07:00
handle - > wayland . relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer ( handle - > wayland . relative_pointer_manager , handle - > wayland . pointer ) ;
zwp_relative_pointer_v1_add_listener ( handle - > wayland . relative_pointer , & relative_pointer_listener , topmost_parent ) ;
} else {
if ( topmost_parent - > wayland . locked_pointer ) {
zwp_locked_pointer_v1_destroy ( topmost_parent - > wayland . locked_pointer ) ;
topmost_parent - > wayland . locked_pointer = NULL ;
}
if ( topmost_parent - > wayland . relative_pointer ) {
zwp_relative_pointer_v1_destroy ( topmost_parent - > wayland . relative_pointer ) ;
topmost_parent - > wayland . relative_pointer = NULL ;
}
}
2026-03-25 00:31:23 -07:00
handle - > wayland . do_lock_pointer = toggle ;
2026-03-24 23:44:30 -07:00
} else {
2026-03-27 17:15:59 -07:00
printf ( " [WARNING] MwLLGrabPointer not supported in Wayland session, zwp_pointer_constraints_v1 and relative_pointer_manager_v1 are required. \n " ) ;
2026-03-24 23:44:30 -07:00
}
2025-12-09 20:11:01 -06:00
}
2026-04-15 10:58:46 -07:00
static void MwLLSetClipboardImpl ( MwLL handle , const char * text , int clipboard_type ) {
int i ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-15 10:58:46 -07:00
2025-12-30 17:12:12 -07:00
if ( handle - > wayland . clipboard_buffer ! = NULL ) {
free ( handle - > wayland . clipboard_buffer ) ;
}
handle - > wayland . clipboard_buffer = malloc ( strlen ( text ) ) ;
strcpy ( handle - > wayland . clipboard_buffer , text ) ;
2025-12-18 17:46:23 -07:00
2026-04-17 01:36:34 +09:00
if ( clipboard_type = = MwCLIPBOARD_PRIMARY ) {
2026-04-15 10:58:46 -07:00
if ( handle - > wayland . supports_zwp ) {
for ( i = 0 ; i < arrlen ( handle - > wayland . clipboard_devices_zwp ) ; i + + ) {
wl_clipboard_device_context_t * device = handle - > wayland . clipboard_devices_zwp [ i ] ;
zwp_primary_selection_device_v1_set_selection ( device - > device . zwp , handle - > wayland . clipboard_source . zwp , handle - > wayland . keyboard_serial ) ;
}
} else {
printf ( " [WARNING] Primary clipboard requested for MwLLSetClipboard, but this Wayland compositor doesn't support it. \n " ) ;
2025-12-18 17:46:23 -07:00
}
2025-12-30 17:12:12 -07:00
} else {
2026-04-15 10:58:46 -07:00
for ( i = 0 ; i < arrlen ( handle - > wayland . clipboard_devices_wl ) ; i + + ) {
wl_clipboard_device_context_t * device = handle - > wayland . clipboard_devices_wl [ i ] ;
2025-12-30 17:12:12 -07:00
wl_data_device_set_selection ( device - > device . wl , handle - > wayland . clipboard_source . wl , handle - > wayland . keyboard_serial ) ;
2025-12-18 17:46:23 -07:00
}
}
2025-12-30 17:12:12 -07:00
}
2026-04-15 10:58:46 -07:00
static void MwLLGetClipboardImpl ( MwLL handle , int clipboard_type ) {
2026-03-26 21:58:47 -07:00
int i ;
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-17 01:36:34 +09:00
if ( clipboard_type = = MwCLIPBOARD_PRIMARY ) {
2026-04-15 10:58:46 -07:00
if ( handle - > wayland . supports_zwp ) {
for ( i = 0 ; i < arrlen ( handle - > wayland . clipboard_devices_zwp ) ; i + + ) {
wl_clipboard_read (
handle - > wayland . clipboard_devices_zwp [ i ] , clipboard_type ) ;
}
} else {
printf ( " [WARNING] Primary clipboard requested for MwLLSetClipboard, but this Wayland compositor doesn't support it. \n " ) ;
}
} else {
for ( i = 0 ; i < arrlen ( handle - > wayland . clipboard_devices_wl ) ; i + + ) {
wl_clipboard_read (
handle - > wayland . clipboard_devices_wl [ i ] , clipboard_type ) ;
}
2026-03-26 21:58:47 -07:00
}
2025-12-30 17:12:12 -07:00
return ;
2025-12-09 20:11:01 -06:00
}
static void MwLLMakeToolWindowImpl ( MwLL handle ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-23 10:45:29 -07:00
2026-04-20 14:49:20 +09:00
handle - > wayland . type_to_be = MwLL_WAYLAND_POPUP ;
2025-12-09 20:11:01 -06:00
}
static void MwLLGetCursorCoordImpl ( MwLL handle , MwPoint * point ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-23 10:45:29 -07:00
2025-12-18 13:16:16 -07:00
* point = handle - > wayland . cur_mouse_pos ;
2025-12-09 20:11:01 -06:00
}
static void MwLLGetScreenSizeImpl ( MwLL handle , MwRect * rect ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-23 10:45:29 -07:00
2025-12-18 13:43:45 -07:00
rect - > x = 0 ;
rect - > y = 0 ;
rect - > width = handle - > wayland . mw ;
rect - > height = handle - > wayland . mh ;
2025-12-09 20:11:01 -06:00
}
static void MwLLBeginStateChangeImpl ( MwLL handle ) {
2026-03-27 15:14:04 -07:00
( void ) handle ;
/* no-op */
2025-12-09 20:11:01 -06:00
}
static void MwLLEndStateChangeImpl ( MwLL handle ) {
2026-04-23 10:57:05 -07:00
WIDGET_CHECK ( handle ) ;
2026-04-23 10:45:29 -07:00
2026-03-23 12:05:32 -07:00
if ( handle - > wayland . detatching ) {
2026-03-24 23:08:43 -07:00
MwLL topmost_parent = handle - > wayland . parent ;
2026-04-20 14:49:20 +09:00
while ( topmost_parent - > wayland . type ! = MwLL_WAYLAND_TOPLEVEL ) {
2026-03-24 23:08:43 -07:00
topmost_parent = topmost_parent - > wayland . parent ;
}
2026-03-23 12:05:32 -07:00
switch ( handle - > wayland . type ) {
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_UNKNOWN :
2026-03-24 22:40:36 -07:00
break ;
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_TOPLEVEL :
2026-03-23 12:05:32 -07:00
destroy_toplevel ( handle ) ;
break ;
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_SUBLEVEL :
2026-03-23 12:05:32 -07:00
destroy_sublevel ( handle ) ;
break ;
2026-04-20 14:49:20 +09:00
case MwLL_WAYLAND_POPUP :
2026-03-24 22:40:36 -07:00
destroy_popup ( handle ) ;
break ;
2026-03-23 12:05:32 -07:00
}
wl_flush ( handle ) ;
2026-03-24 22:40:36 -07:00
widget_setup ( handle , handle - > wayland . parent , handle - > wayland . detach_point . x , handle - > wayland . detach_point . y , handle - > wayland . ww , handle - > wayland . wh , handle - > wayland . type_to_be ) ;
2026-03-24 18:01:55 -07:00
2026-04-24 12:53:27 +09:00
handle - > wayland . is_toplevel = MwTRUE ;
2026-04-15 13:00:59 -07:00
if ( ! handle - > wayland . parent )
handle - > wayland . dark_theme_detection = MwTRUE ;
2026-03-24 23:08:43 -07:00
if ( handle - > common . user ) {
MwWidget w = handle - > common . user ;
int i ;
for ( i = 0 ; i < arrlen ( w - > children ) ; i + + ) {
2026-04-20 14:49:20 +09:00
if ( w - > children [ i ] - > lowlevel - > wayland . type = = MwLL_WAYLAND_SUBLEVEL ) {
2026-03-27 15:14:04 -07:00
MwLL child = w - > children [ i ] - > lowlevel ;
child - > wayland . sublevel - > xdg_surface = handle - > wayland . popup - > xdg_surface ;
2026-03-24 23:08:43 -07:00
wl_subsurface_destroy ( child - > wayland . sublevel - > subsurface ) ;
wl_flush ( handle ) ;
child - > wayland . sublevel - > subsurface = wl_subcompositor_get_subsurface ( child - > wayland . sublevel - > subcompositor , child - > wayland . framebuffer . surface , handle - > wayland . framebuffer . surface ) ;
wl_subsurface_set_desync ( child - > wayland . sublevel - > subsurface ) ;
wl_subsurface_set_position ( child - > wayland . sublevel - > subsurface , child - > wayland . x , child - > wayland . y ) ;
wl_subsurface_place_above ( child - > wayland . sublevel - > subsurface , handle - > wayland . framebuffer . surface ) ;
MwLLEndStateChangeImpl ( w - > children [ i ] - > lowlevel ) ;
currentlyHeldWidget = NULL ;
}
}
}
2026-03-24 18:01:55 -07:00
2026-04-24 09:09:37 +09:00
recursive_render ( handle ) ;
2026-03-24 18:01:55 -07:00
handle - > wayland . events_pending = 1 ;
2026-03-23 12:05:32 -07:00
handle - > wayland . detatching = MwFALSE ;
}
2025-12-09 20:11:01 -06:00
}
2026-04-16 23:40:29 +09:00
static void MwLLSetDarkThemeImpl ( MwLL handle , int toggle ) {
2026-04-16 09:35:53 +09:00
( void ) handle ;
( void ) toggle ;
}
2025-12-09 20:11:01 -06:00
static int MwLLWaylandCallInitImpl ( void ) {
# ifdef __linux__
2026-04-15 00:29:16 -05:00
MwBool loadWayland = MwFALSE ;
2026-04-08 11:59:05 -07:00
if ( getenv ( " MILSKO_BACKEND " ) ) {
loadWayland | =
( strcmp ( getenv ( " MILSKO_BACKEND " ) , " wayland " ) = = 0 ) ;
2026-04-13 11:28:54 -07:00
} else if ( getenv ( " WAYLAND_DISPLAY " ) ) {
2026-04-08 11:59:05 -07:00
loadWayland | = ( getenv ( " WAYLAND_DISPLAY " ) ! = NULL ) ;
}
if ( loadWayland ) {
2026-03-24 17:38:21 -07:00
if ( wayland_load_funcs ( ) ) {
return 1 ;
}
wl_log_set_handler_client ( wl_logger ) ;
return 0 ;
2025-12-09 20:11:01 -06:00
}
# endif
2026-03-24 17:38:21 -07:00
return 1 ;
2025-12-09 20:11:01 -06:00
}
# include "call.c"
CALL ( Wayland ) ;