2024-06-18 18:45:05 +02:00
# include <aquamarine/backend/Wayland.hpp>
# include <wayland.hpp>
# include <xdg-shell.hpp>
2024-06-19 22:40:23 +02:00
# include "Shared.hpp"
2024-06-22 16:50:30 +02:00
# include "FormatUtils.hpp"
2024-06-19 22:40:23 +02:00
# include <string.h>
# include <xf86drm.h>
# include <gbm.h>
# include <fcntl.h>
2024-06-22 16:50:30 +02:00
# include <sys/mman.h>
2024-07-06 17:51:46 +02:00
# include <unistd.h>
2024-06-18 18:45:05 +02:00
using namespace Aquamarine ;
using namespace Hyprutils : : Memory ;
using namespace Hyprutils : : Math ;
# define SP CSharedPointer
2024-06-22 16:50:30 +02:00
static std : : pair < int , std : : string > openExclusiveShm ( ) {
// Only absolute paths can be shared across different shm_open() calls
srand ( time ( nullptr ) ) ;
std : : string name = std : : format ( " /aq{:x} " , rand ( ) % RAND_MAX ) ;
for ( size_t i = 0 ; i < 69 ; + + i ) {
int fd = shm_open ( name . c_str ( ) , O_RDWR | O_CREAT | O_EXCL , 0600 ) ;
if ( fd > = 0 )
return { fd , name } ;
}
return { - 1 , " " } ;
}
static int allocateSHMFile ( size_t len ) {
auto [ fd , name ] = openExclusiveShm ( ) ;
if ( fd < 0 )
return - 1 ;
shm_unlink ( name . c_str ( ) ) ;
int ret ;
do {
ret = ftruncate ( fd , len ) ;
} while ( ret < 0 & & errno = = EINTR ) ;
if ( ret < 0 ) {
close ( fd ) ;
return - 1 ;
}
return fd ;
}
wl_shm_format shmFormatFromDRM ( uint32_t drmFormat ) {
switch ( drmFormat ) {
case DRM_FORMAT_XRGB8888 : return WL_SHM_FORMAT_XRGB8888 ;
case DRM_FORMAT_ARGB8888 : return WL_SHM_FORMAT_ARGB8888 ;
default : return ( wl_shm_format ) drmFormat ;
}
return ( wl_shm_format ) drmFormat ;
}
2024-06-18 18:45:05 +02:00
Aquamarine : : CWaylandBackend : : ~ CWaylandBackend ( ) {
2024-06-19 22:40:23 +02:00
if ( drmState . fd > = 0 )
close ( drmState . fd ) ;
2024-06-18 18:45:05 +02:00
}
2024-06-19 22:40:23 +02:00
2024-06-18 18:45:05 +02:00
eBackendType Aquamarine : : CWaylandBackend : : type ( ) {
return AQ_BACKEND_WAYLAND ;
}
2024-06-19 22:40:23 +02:00
Aquamarine : : CWaylandBackend : : CWaylandBackend ( SP < CBackend > backend_ ) : backend ( backend_ ) {
2024-06-18 18:45:05 +02:00
;
}
bool Aquamarine : : CWaylandBackend : : start ( ) {
backend - > log ( AQ_LOG_DEBUG , " Starting the Wayland backend! " ) ;
waylandState . display = wl_display_connect ( nullptr ) ;
if ( ! waylandState . display ) {
backend - > log ( AQ_LOG_ERROR , " Wayland backend cannot start: wl_display_connect failed (is a wayland compositor running?) " ) ;
return false ;
}
2024-09-25 12:45:04 +02:00
auto XDGCURRENTDESKTOP = getenv ( " XDG_CURRENT_DESKTOP " ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Connected to a wayland compositor: {} " , ( XDGCURRENTDESKTOP ? XDGCURRENTDESKTOP : " unknown (XDG_CURRENT_DEKSTOP unset?) " ) ) ) ;
2024-06-20 19:24:43 +02:00
waylandState . registry = makeShared < CCWlRegistry > ( ( wl_proxy * ) wl_display_get_registry ( waylandState . display ) ) ;
2024-06-18 18:45:05 +02:00
backend - > log ( AQ_LOG_DEBUG , std : : format ( " Got registry at 0x{:x} " , ( uintptr_t ) waylandState . registry - > resource ( ) ) ) ;
2024-06-20 19:24:43 +02:00
waylandState . registry - > setGlobal ( [ this ] ( CCWlRegistry * r , uint32_t id , const char * name , uint32_t version ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " | received global: {} (version {}) with id {} " , name , version , id ) ) ) ;
2024-06-18 18:45:05 +02:00
const std : : string NAME = name ;
if ( NAME = = " wl_seat " ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " > binding to global: {} (version {}) with id {} " , name , 9 , id ) ) ) ;
2024-06-20 19:24:43 +02:00
waylandState . seat = makeShared < CCWlSeat > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) waylandState . registry - > resource ( ) , id , & wl_seat_interface , 9 ) ) ;
2024-06-18 18:45:05 +02:00
initSeat ( ) ;
} else if ( NAME = = " xdg_wm_base " ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " > binding to global: {} (version {}) with id {} " , name , 6 , id ) ) ) ;
2024-06-20 19:24:43 +02:00
waylandState . xdg = makeShared < CCXdgWmBase > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) waylandState . registry - > resource ( ) , id , & xdg_wm_base_interface , 6 ) ) ;
2024-06-18 18:45:05 +02:00
initShell ( ) ;
} else if ( NAME = = " wl_compositor " ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " > binding to global: {} (version {}) with id {} " , name , 6 , id ) ) ) ;
2024-06-20 19:24:43 +02:00
waylandState . compositor = makeShared < CCWlCompositor > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) waylandState . registry - > resource ( ) , id , & wl_compositor_interface , 6 ) ) ;
2024-06-22 16:50:30 +02:00
} else if ( NAME = = " wl_shm " ) {
2024-07-02 14:42:24 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " > binding to global: {} (version {}) with id {} " , name , 1 , id ) ) ) ;
2024-06-22 16:50:30 +02:00
waylandState . shm = makeShared < CCWlShm > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) waylandState . registry - > resource ( ) , id , & wl_shm_interface , 1 ) ) ;
2024-06-19 22:40:23 +02:00
} else if ( NAME = = " zwp_linux_dmabuf_v1 " ) {
2024-07-21 16:22:23 +02:00
TRACE ( backend - > log ( AQ_LOG_TRACE , std : : format ( " > binding to global: {} (version {}) with id {} " , name , 4 , id ) ) ) ;
2024-06-19 22:40:23 +02:00
waylandState . dmabuf =
2024-07-21 16:22:23 +02:00
makeShared < CCZwpLinuxDmabufV1 > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) waylandState . registry - > resource ( ) , id , & zwp_linux_dmabuf_v1_interface , 4 ) ) ;
2024-06-19 22:40:23 +02:00
if ( ! initDmabuf ( ) ) {
backend - > log ( AQ_LOG_ERROR , " Wayland backend cannot start: zwp_linux_dmabuf_v1 init failed " ) ;
waylandState . dmabufFailed = true ;
}
2024-06-18 18:45:05 +02:00
}
} ) ;
2024-06-20 19:24:43 +02:00
waylandState . registry - > setGlobalRemove ( [ this ] ( CCWlRegistry * r , uint32_t id ) { backend - > log ( AQ_LOG_DEBUG , std : : format ( " Global {} removed " , id ) ) ; } ) ;
2024-06-18 18:45:05 +02:00
wl_display_roundtrip ( waylandState . display ) ;
2024-06-22 16:50:30 +02:00
if ( ! waylandState . xdg | | ! waylandState . compositor | | ! waylandState . seat | | ! waylandState . dmabuf | | waylandState . dmabufFailed | | ! waylandState . shm ) {
2024-06-18 18:45:05 +02:00
backend - > log ( AQ_LOG_ERROR , " Wayland backend cannot start: Missing protocols " ) ;
return false ;
}
dispatchEvents ( ) ;
2024-07-01 14:33:05 +02:00
createOutput ( ) ;
2024-06-18 18:45:05 +02:00
return true ;
}
2024-06-19 22:40:23 +02:00
int Aquamarine : : CWaylandBackend : : drmFD ( ) {
return drmState . fd ;
}
2024-07-01 14:52:50 +02:00
bool Aquamarine : : CWaylandBackend : : createOutput ( const std : : string & szName ) {
auto o = outputs . emplace_back ( SP < CWaylandOutput > ( new CWaylandOutput ( szName . empty ( ) ? std : : format ( " WAYLAND-{} " , + + lastOutputID ) : szName , self ) ) ) ;
2024-06-20 19:24:43 +02:00
o - > self = o ;
2024-07-01 14:59:16 +02:00
if ( backend - > ready )
2024-07-09 14:10:52 +02:00
o - > swapchain = CSwapchain : : create ( backend - > primaryAllocator , self . lock ( ) ) ;
2024-06-21 15:49:28 +02:00
idleCallbacks . emplace_back ( [ this , o ] ( ) { backend - > events . newOutput . emit ( SP < IOutput > ( o ) ) ; } ) ;
2024-07-01 14:52:50 +02:00
return true ;
2024-06-18 18:45:05 +02:00
}
2024-06-27 00:07:59 +02:00
std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > Aquamarine : : CWaylandBackend : : pollFDs ( ) {
2024-06-18 18:45:05 +02:00
if ( ! waylandState . display )
2024-06-27 00:07:59 +02:00
return { } ;
2024-06-18 18:45:05 +02:00
2024-06-27 00:07:59 +02:00
return { makeShared < SPollFD > ( wl_display_get_fd ( waylandState . display ) , [ this ] ( ) { dispatchEvents ( ) ; } ) } ;
2024-06-18 18:45:05 +02:00
}
bool Aquamarine : : CWaylandBackend : : dispatchEvents ( ) {
wl_display_flush ( waylandState . display ) ;
if ( wl_display_prepare_read ( waylandState . display ) = = 0 ) {
wl_display_read_events ( waylandState . display ) ;
wl_display_dispatch_pending ( waylandState . display ) ;
} else
wl_display_dispatch ( waylandState . display ) ;
int ret = 0 ;
do {
ret = wl_display_dispatch_pending ( waylandState . display ) ;
wl_display_flush ( waylandState . display ) ;
} while ( ret > 0 ) ;
2024-06-20 19:24:43 +02:00
// dispatch frames
2024-06-21 15:49:28 +02:00
if ( backend - > ready ) {
2024-08-27 20:04:26 +02:00
for ( auto const & f : idleCallbacks ) {
2024-06-21 15:49:28 +02:00
f ( ) ;
}
idleCallbacks . clear ( ) ;
2024-06-20 19:24:43 +02:00
}
return true ;
}
uint32_t Aquamarine : : CWaylandBackend : : capabilities ( ) {
return AQ_BACKEND_CAPABILITY_POINTER ;
}
bool Aquamarine : : CWaylandBackend : : setCursor ( Hyprutils : : Memory : : CSharedPointer < IBuffer > buffer , const Hyprutils : : Math : : Vector2D & hotspot ) {
// TODO:
2024-06-18 18:45:05 +02:00
return true ;
}
2024-06-20 19:24:43 +02:00
void Aquamarine : : CWaylandBackend : : onReady ( ) {
2024-08-27 20:04:26 +02:00
for ( auto const & o : outputs ) {
2024-07-09 14:10:52 +02:00
o - > swapchain = CSwapchain : : create ( backend - > primaryAllocator , self . lock ( ) ) ;
2024-06-20 19:24:43 +02:00
if ( ! o - > swapchain ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {} failed: swapchain creation failed " , o - > name ) ) ;
continue ;
}
}
}
Aquamarine : : CWaylandKeyboard : : CWaylandKeyboard ( SP < CCWlKeyboard > keyboard_ , Hyprutils : : Memory : : CWeakPointer < CWaylandBackend > backend_ ) : keyboard ( keyboard_ ) , backend ( backend_ ) {
2024-06-18 18:45:05 +02:00
if ( ! keyboard - > resource ( ) )
return ;
backend - > backend - > log ( AQ_LOG_DEBUG , " New wayland keyboard wl_keyboard " ) ;
2024-06-20 19:24:43 +02:00
keyboard - > setKey ( [ this ] ( CCWlKeyboard * r , uint32_t serial , uint32_t timeMs , uint32_t key , wl_keyboard_key_state state ) {
2024-06-18 18:45:05 +02:00
events . key . emit ( SKeyEvent {
. timeMs = timeMs ,
. key = key ,
. pressed = state = = WL_KEYBOARD_KEY_STATE_PRESSED ,
} ) ;
} ) ;
2024-06-20 19:24:43 +02:00
keyboard - > setModifiers ( [ this ] ( CCWlKeyboard * r , uint32_t serial , uint32_t depressed , uint32_t latched , uint32_t locked , uint32_t group ) {
2024-06-19 22:40:23 +02:00
events . modifiers . emit ( SModifiersEvent {
2024-06-18 18:45:05 +02:00
. depressed = depressed ,
. latched = latched ,
. locked = locked ,
. group = group ,
} ) ;
} ) ;
}
Aquamarine : : CWaylandKeyboard : : ~ CWaylandKeyboard ( ) {
;
}
const std : : string & Aquamarine : : CWaylandKeyboard : : getName ( ) {
return name ;
}
2024-06-20 19:24:43 +02:00
Aquamarine : : CWaylandPointer : : CWaylandPointer ( SP < CCWlPointer > pointer_ , Hyprutils : : Memory : : CWeakPointer < CWaylandBackend > backend_ ) : pointer ( pointer_ ) , backend ( backend_ ) {
2024-06-18 18:45:05 +02:00
if ( ! pointer - > resource ( ) )
return ;
backend - > backend - > log ( AQ_LOG_DEBUG , " New wayland pointer wl_pointer " ) ;
2024-06-20 19:24:43 +02:00
pointer - > setMotion ( [ this ] ( CCWlPointer * r , uint32_t serial , wl_fixed_t x , wl_fixed_t y ) {
2024-06-22 17:31:01 +02:00
const auto STATE = backend - > focusedOutput - > state - > state ( ) ;
if ( ! backend - > focusedOutput | | ( ! STATE . mode & & ! STATE . customMode ) )
2024-06-18 18:45:05 +02:00
return ;
2024-06-22 17:31:01 +02:00
const Vector2D size = STATE . customMode ? STATE . customMode - > pixelSize : STATE . mode - > pixelSize ;
2024-06-19 22:40:23 +02:00
2024-06-20 19:24:43 +02:00
Vector2D local = { wl_fixed_to_double ( x ) , wl_fixed_to_double ( y ) } ;
local = local / size ;
2024-06-18 18:45:05 +02:00
2024-06-21 15:49:28 +02:00
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
2024-06-18 18:45:05 +02:00
events . warp . emit ( SWarpEvent {
2024-06-21 15:49:28 +02:00
. timeMs = ( uint32_t ) ( now . tv_sec * 1000 + now . tv_nsec / 1000000 ) ,
2024-06-18 18:45:05 +02:00
. absolute = local ,
} ) ;
} ) ;
2024-06-20 19:24:43 +02:00
pointer - > setEnter ( [ this ] ( CCWlPointer * r , uint32_t serial , wl_proxy * surface , wl_fixed_t x , wl_fixed_t y ) {
2024-06-18 18:45:05 +02:00
backend - > lastEnterSerial = serial ;
2024-08-27 20:04:26 +02:00
for ( auto const & o : backend - > outputs ) {
2024-06-18 18:45:05 +02:00
if ( o - > waylandState . surface - > resource ( ) ! = surface )
continue ;
backend - > focusedOutput = o ;
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " [wayland] focus changed: {} " , o - > name ) ) ;
2024-06-22 16:50:30 +02:00
o - > onEnter ( pointer , serial ) ;
2024-06-18 18:45:05 +02:00
break ;
}
} ) ;
2024-06-22 16:50:30 +02:00
pointer - > setLeave ( [ this ] ( CCWlPointer * r , uint32_t serial , wl_proxy * surface ) {
2024-08-27 20:04:26 +02:00
for ( auto const & o : backend - > outputs ) {
2024-06-22 16:50:30 +02:00
if ( o - > waylandState . surface - > resource ( ) ! = surface )
continue ;
o - > cursorState . serial = 0 ;
}
} ) ;
2024-06-20 19:24:43 +02:00
pointer - > setButton ( [ this ] ( CCWlPointer * r , uint32_t serial , uint32_t timeMs , uint32_t button , wl_pointer_button_state state ) {
2024-06-18 18:45:05 +02:00
events . button . emit ( SButtonEvent {
. timeMs = timeMs ,
. button = button ,
. pressed = state = = WL_POINTER_BUTTON_STATE_PRESSED ,
} ) ;
} ) ;
2024-06-20 19:24:43 +02:00
pointer - > setAxis ( [ this ] ( CCWlPointer * r , uint32_t timeMs , wl_pointer_axis axis , wl_fixed_t value ) {
2024-06-18 18:45:05 +02:00
events . axis . emit ( SAxisEvent {
. timeMs = timeMs ,
. axis = axis = = WL_POINTER_AXIS_HORIZONTAL_SCROLL ? AQ_POINTER_AXIS_HORIZONTAL : AQ_POINTER_AXIS_VERTICAL ,
2024-06-21 15:49:28 +02:00
. delta = wl_fixed_to_double ( value ) ,
2024-06-18 18:45:05 +02:00
} ) ;
} ) ;
2024-06-20 19:24:43 +02:00
pointer - > setFrame ( [ this ] ( CCWlPointer * r ) { events . frame . emit ( ) ; } ) ;
2024-06-18 18:45:05 +02:00
}
Aquamarine : : CWaylandPointer : : ~ CWaylandPointer ( ) {
;
}
const std : : string & Aquamarine : : CWaylandPointer : : getName ( ) {
return name ;
}
void Aquamarine : : CWaylandBackend : : initSeat ( ) {
2024-06-20 19:24:43 +02:00
waylandState . seat - > setCapabilities ( [ this ] ( CCWlSeat * r , wl_seat_capability cap ) {
2024-06-18 18:45:05 +02:00
const bool HAS_KEYBOARD = ( ( uint32_t ) cap ) & WL_SEAT_CAPABILITY_KEYBOARD ;
const bool HAS_POINTER = ( ( uint32_t ) cap ) & WL_SEAT_CAPABILITY_POINTER ;
2024-06-19 22:40:23 +02:00
if ( HAS_KEYBOARD & & keyboards . empty ( ) ) {
2024-06-20 19:24:43 +02:00
auto k = keyboards . emplace_back ( makeShared < CWaylandKeyboard > ( makeShared < CCWlKeyboard > ( waylandState . seat - > sendGetKeyboard ( ) ) , self ) ) ;
2024-06-21 15:49:28 +02:00
idleCallbacks . emplace_back ( [ this , k ] ( ) { backend - > events . newKeyboard . emit ( SP < IKeyboard > ( k ) ) ; } ) ;
2024-06-19 22:40:23 +02:00
} else if ( ! HAS_KEYBOARD & & ! keyboards . empty ( ) )
2024-06-18 18:45:05 +02:00
keyboards . clear ( ) ;
2024-06-19 22:40:23 +02:00
if ( HAS_POINTER & & pointers . empty ( ) ) {
2024-06-20 19:24:43 +02:00
auto p = pointers . emplace_back ( makeShared < CWaylandPointer > ( makeShared < CCWlPointer > ( waylandState . seat - > sendGetPointer ( ) ) , self ) ) ;
2024-06-21 15:49:28 +02:00
idleCallbacks . emplace_back ( [ this , p ] ( ) { backend - > events . newPointer . emit ( SP < IPointer > ( p ) ) ; } ) ;
2024-06-19 22:40:23 +02:00
} else if ( ! HAS_POINTER & & ! pointers . empty ( ) )
pointers . clear ( ) ;
2024-06-18 18:45:05 +02:00
} ) ;
}
void Aquamarine : : CWaylandBackend : : initShell ( ) {
2024-06-20 19:24:43 +02:00
waylandState . xdg - > setPing ( [ ] ( CCXdgWmBase * r , uint32_t serial ) { r - > sendPong ( serial ) ; } ) ;
2024-06-18 18:45:05 +02:00
}
2024-06-19 22:40:23 +02:00
bool Aquamarine : : CWaylandBackend : : initDmabuf ( ) {
2024-06-20 19:24:43 +02:00
waylandState . dmabufFeedback = makeShared < CCZwpLinuxDmabufFeedbackV1 > ( waylandState . dmabuf - > sendGetDefaultFeedback ( ) ) ;
2024-06-19 22:40:23 +02:00
if ( ! waylandState . dmabufFeedback ) {
backend - > log ( AQ_LOG_ERROR , " initDmabuf: failed to get default feedback " ) ;
return false ;
}
2024-06-20 19:24:43 +02:00
waylandState . dmabufFeedback - > setDone ( [ this ] ( CCZwpLinuxDmabufFeedbackV1 * r ) {
2024-06-19 22:40:23 +02:00
// no-op
backend - > log ( AQ_LOG_DEBUG , " zwp_linux_dmabuf_v1: Got done " ) ;
} ) ;
2024-06-20 19:24:43 +02:00
waylandState . dmabufFeedback - > setMainDevice ( [ this ] ( CCZwpLinuxDmabufFeedbackV1 * r , wl_array * deviceArr ) {
2024-06-19 22:40:23 +02:00
backend - > log ( AQ_LOG_DEBUG , " zwp_linux_dmabuf_v1: Got main device " ) ;
dev_t device ;
ASSERT ( deviceArr - > size = = sizeof ( device ) ) ;
memcpy ( & device , deviceArr - > data , sizeof ( device ) ) ;
drmDevice * drmDev ;
if ( drmGetDeviceFromDevId ( device , /* flags */ 0 , & drmDev ) ! = 0 ) {
backend - > log ( AQ_LOG_ERROR , " zwp_linux_dmabuf_v1: drmGetDeviceFromDevId failed " ) ;
return ;
}
const char * name = nullptr ;
if ( drmDev - > available_nodes & ( 1 < < DRM_NODE_RENDER ) )
name = drmDev - > nodes [ DRM_NODE_RENDER ] ;
else {
// Likely a split display/render setup. Pick the primary node and hope
// Mesa will open the right render node under-the-hood.
ASSERT ( drmDev - > available_nodes & ( 1 < < DRM_NODE_PRIMARY ) ) ;
name = drmDev - > nodes [ DRM_NODE_PRIMARY ] ;
backend - > log ( AQ_LOG_WARNING , " zwp_linux_dmabuf_v1: DRM device has no render node, using primary. " ) ;
}
if ( ! name ) {
backend - > log ( AQ_LOG_ERROR , " zwp_linux_dmabuf_v1: no node name " ) ;
return ;
}
drmState . nodeName = name ;
drmFreeDevice ( & drmDev ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " zwp_linux_dmabuf_v1: Got node {} " , drmState . nodeName ) ) ;
} ) ;
2024-06-22 16:50:30 +02:00
waylandState . dmabufFeedback - > setFormatTable ( [ this ] ( CCZwpLinuxDmabufFeedbackV1 * r , int32_t fd , uint32_t size ) {
# pragma pack(push, 1)
struct wlDrmFormatMarshalled {
uint32_t drmFormat ;
char pad [ 4 ] ;
uint64_t modifier ;
} ;
# pragma pack(pop)
static_assert ( sizeof ( wlDrmFormatMarshalled ) = = 16 ) ;
auto formatTable = mmap ( nullptr , size , PROT_READ , MAP_PRIVATE , fd , 0 ) ;
if ( formatTable = = MAP_FAILED ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " zwp_linux_dmabuf_v1: Failed to mmap the format table " ) ) ;
return ;
}
const auto FORMATS = ( wlDrmFormatMarshalled * ) formatTable ;
for ( size_t i = 0 ; i < size / 16 ; + + i ) {
auto & fmt = FORMATS [ i ] ;
auto modName = drmGetFormatModifierName ( fmt . modifier ) ;
backend - > log ( AQ_LOG_DEBUG , std : : format ( " zwp_linux_dmabuf_v1: Got format {} with modifier {} " , fourccToName ( fmt . drmFormat ) , modName ? modName : " UNKNOWN " ) ) ;
free ( modName ) ;
auto it = std : : find_if ( dmabufFormats . begin ( ) , dmabufFormats . end ( ) , [ & fmt ] ( const auto & e ) { return e . drmFormat = = fmt . drmFormat ; } ) ;
if ( it = = dmabufFormats . end ( ) ) {
dmabufFormats . emplace_back ( SDRMFormat { . drmFormat = fmt . drmFormat , . modifiers = { fmt . modifier } } ) ;
continue ;
}
it - > modifiers . emplace_back ( fmt . modifier ) ;
}
munmap ( formatTable , size ) ;
} ) ;
2024-06-19 22:40:23 +02:00
wl_display_roundtrip ( waylandState . display ) ;
if ( ! drmState . nodeName . empty ( ) ) {
drmState . fd = open ( drmState . nodeName . c_str ( ) , O_RDWR | O_NONBLOCK | O_CLOEXEC ) ;
if ( drmState . fd < 0 ) {
backend - > log ( AQ_LOG_ERROR , std : : format ( " zwp_linux_dmabuf_v1: Failed to open node {} " , drmState . nodeName ) ) ;
return false ;
}
backend - > log ( AQ_LOG_DEBUG , std : : format ( " zwp_linux_dmabuf_v1: opened node {} with fd {} " , drmState . nodeName , drmState . fd ) ) ;
}
return true ;
}
2024-06-22 16:50:30 +02:00
std : : vector < SDRMFormat > Aquamarine : : CWaylandBackend : : getRenderFormats ( ) {
return dmabufFormats ;
}
std : : vector < SDRMFormat > Aquamarine : : CWaylandBackend : : getCursorFormats ( ) {
return dmabufFormats ;
}
2024-07-09 14:10:52 +02:00
SP < IAllocator > Aquamarine : : CWaylandBackend : : preferredAllocator ( ) {
return backend - > primaryAllocator ;
}
2024-11-11 14:04:23 +01:00
std : : vector < SP < IAllocator > > Aquamarine : : CWaylandBackend : : getAllocators ( ) {
return { backend - > primaryAllocator } ;
}
2024-06-20 19:24:43 +02:00
Aquamarine : : CWaylandOutput : : CWaylandOutput ( const std : : string & name_ , Hyprutils : : Memory : : CWeakPointer < CWaylandBackend > backend_ ) : backend ( backend_ ) {
name = name_ ;
2024-06-18 18:45:05 +02:00
2024-06-20 19:24:43 +02:00
waylandState . surface = makeShared < CCWlSurface > ( backend - > waylandState . compositor - > sendCreateSurface ( ) ) ;
2024-06-18 18:45:05 +02:00
if ( ! waylandState . surface - > resource ( ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {} failed: no surface given. Errno: {} " , name , errno ) ) ;
return ;
}
2024-06-20 19:24:43 +02:00
waylandState . xdgSurface = makeShared < CCXdgSurface > ( backend - > waylandState . xdg - > sendGetXdgSurface ( waylandState . surface - > resource ( ) ) ) ;
2024-06-18 18:45:05 +02:00
if ( ! waylandState . xdgSurface - > resource ( ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {} failed: no xdgSurface given. Errno: {} " , name , errno ) ) ;
return ;
}
2024-06-20 19:24:43 +02:00
waylandState . xdgSurface - > setConfigure ( [ this ] ( CCXdgSurface * r , uint32_t serial ) {
2024-06-18 18:45:05 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " Output {}: configure surface with {} " , name , serial ) ) ;
r - > sendAckConfigure ( serial ) ;
} ) ;
2024-06-20 19:24:43 +02:00
waylandState . xdgToplevel = makeShared < CCXdgToplevel > ( waylandState . xdgSurface - > sendGetToplevel ( ) ) ;
2024-06-18 18:45:05 +02:00
if ( ! waylandState . xdgToplevel - > resource ( ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {} failed: no xdgToplevel given. Errno: {} " , name , errno ) ) ;
return ;
}
waylandState . xdgToplevel - > setWmCapabilities (
2024-06-20 19:24:43 +02:00
[ this ] ( CCXdgToplevel * r , wl_array * arr ) { backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " Output {}: wm_capabilities received " , name ) ) ; } ) ;
2024-06-19 22:40:23 +02:00
2024-06-20 19:24:43 +02:00
waylandState . xdgToplevel - > setConfigure ( [ this ] ( CCXdgToplevel * r , int32_t w , int32_t h , wl_array * arr ) {
2024-06-18 18:45:05 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " Output {}: configure toplevel with {}x{} " , name , w , h ) ) ;
2024-08-31 14:02:08 +02:00
if ( w = = 0 | | h = = 0 ) {
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " Output {}: w/h is 0, sending default hardcoded 1280x720 " , name ) ) ;
w = 1280 ;
h = 720 ;
}
2024-06-18 18:45:05 +02:00
events . state . emit ( SStateEvent { . size = { w , h } } ) ;
2024-06-19 22:40:23 +02:00
sendFrameAndSetCallback ( ) ;
2024-06-18 18:45:05 +02:00
} ) ;
2024-07-01 14:33:05 +02:00
waylandState . xdgToplevel - > setClose ( [ this ] ( CCXdgToplevel * r ) { destroy ( ) ; } ) ;
2024-07-01 13:06:35 +02:00
2024-07-17 11:40:45 +02:00
waylandState . xdgToplevel - > sendSetTitle ( std : : format ( " aquamarine - {} " , name ) . c_str ( ) ) ;
waylandState . xdgToplevel - > sendSetAppId ( " aquamarine " ) ;
2024-06-20 19:24:43 +02:00
auto inputRegion = makeShared < CCWlRegion > ( backend - > waylandState . compositor - > sendCreateRegion ( ) ) ;
2024-06-19 22:40:23 +02:00
inputRegion - > sendAdd ( 0 , 0 , INT32_MAX , INT32_MAX ) ;
waylandState . surface - > sendSetInputRegion ( inputRegion . get ( ) ) ;
2024-06-18 18:45:05 +02:00
waylandState . surface - > sendAttach ( nullptr , 0 , 0 ) ;
waylandState . surface - > sendCommit ( ) ;
2024-06-19 22:40:23 +02:00
inputRegion - > sendDestroy ( ) ;
2024-06-18 18:45:05 +02:00
backend - > backend - > log ( AQ_LOG_DEBUG , std : : format ( " Output {}: initialized " , name ) ) ;
}
Aquamarine : : CWaylandOutput : : ~ CWaylandOutput ( ) {
2024-07-01 14:33:05 +02:00
backend - > idleCallbacks . clear ( ) ; // FIXME: mega hack to avoid a UAF in frame events
2024-07-25 23:14:05 +02:00
events . destroy . emit ( ) ;
2024-06-18 18:45:05 +02:00
if ( waylandState . xdgToplevel )
waylandState . xdgToplevel - > sendDestroy ( ) ;
if ( waylandState . xdgSurface )
waylandState . xdgSurface - > sendDestroy ( ) ;
if ( waylandState . surface )
waylandState . surface - > sendDestroy ( ) ;
}
2024-07-14 11:06:13 +02:00
std : : vector < SDRMFormat > Aquamarine : : CWaylandOutput : : getRenderFormats ( ) {
// TODO
// this is technically wrong because this returns the format table formats
// the actually supported formats are given by tranche formats
return backend - > getRenderFormats ( ) ;
}
2024-07-01 14:33:05 +02:00
bool Aquamarine : : CWaylandOutput : : destroy ( ) {
events . destroy . emit ( ) ;
waylandState . surface - > sendAttach ( nullptr , 0 , 0 ) ;
waylandState . surface - > sendCommit ( ) ;
waylandState . frameCallback . reset ( ) ;
std : : erase ( backend - > outputs , self . lock ( ) ) ;
return true ;
}
2024-06-20 19:24:43 +02:00
bool Aquamarine : : CWaylandOutput : : test ( ) {
return true ; // TODO:
}
2024-06-19 22:40:23 +02:00
bool Aquamarine : : CWaylandOutput : : commit ( ) {
Vector2D pixelSize = { } ;
uint32_t refreshRate = 0 ;
2024-06-22 17:31:01 +02:00
if ( state - > internalState . customMode )
pixelSize = state - > internalState . customMode - > pixelSize ;
else if ( state - > internalState . mode )
pixelSize = state - > internalState . mode - > pixelSize ;
2024-06-19 22:40:23 +02:00
else {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: pending state rejected: invalid mode " , name ) ) ;
return false ;
}
2024-06-22 17:31:01 +02:00
uint32_t format = state - > internalState . drmFormat ;
2024-06-19 22:40:23 +02:00
if ( format = = DRM_FORMAT_INVALID ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: pending state rejected: invalid format " , name ) ) ;
return false ;
}
2024-06-20 19:24:43 +02:00
if ( ! swapchain ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: no swapchain, lying because it will soon be here " , name ) ) ;
return true ;
}
2024-06-19 22:40:23 +02:00
if ( ! swapchain - > reconfigure ( SSwapchainOptions { . length = 2 , . size = pixelSize , . format = format } ) ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: pending state rejected: swapchain failed reconfiguring " , name ) ) ;
return false ;
}
2024-06-22 17:31:01 +02:00
if ( ! state - > internalState . buffer ) {
// if the consumer explicitly committed a null buffer, that's a violation.
if ( state - > internalState . committed & COutputState : : AQ_OUTPUT_STATE_BUFFER ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: pending state rejected: no buffer " , name ) ) ;
return false ;
}
events . commit . emit ( ) ;
state - > onCommit ( ) ;
return true ;
2024-06-19 22:40:23 +02:00
}
2024-06-22 17:31:01 +02:00
auto wlBuffer = wlBufferFromBuffer ( state - > internalState . buffer ) ;
2024-06-19 22:40:23 +02:00
if ( ! wlBuffer ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: pending state rejected: no wlBuffer?? " , name ) ) ;
return false ;
}
if ( wlBuffer - > pendingRelease )
backend - > backend - > log ( AQ_LOG_WARNING , std : : format ( " Output {}: pending state has a non-released buffer?? " , name ) ) ;
wlBuffer - > pendingRelease = true ;
waylandState . surface - > sendAttach ( wlBuffer - > waylandState . buffer . get ( ) , 0 , 0 ) ;
waylandState . surface - > sendDamageBuffer ( 0 , 0 , INT32_MAX , INT32_MAX ) ;
waylandState . surface - > sendCommit ( ) ;
2024-06-21 15:49:28 +02:00
readyForFrameCallback = true ;
2024-06-21 18:37:09 +02:00
events . commit . emit ( ) ;
2024-06-22 17:31:01 +02:00
state - > onCommit ( ) ;
2024-07-05 14:24:10 +02:00
needsFrame = false ;
2024-06-22 17:31:01 +02:00
2024-06-19 22:40:23 +02:00
return true ;
}
2024-06-20 19:24:43 +02:00
SP < IBackendImplementation > Aquamarine : : CWaylandOutput : : getBackend ( ) {
return SP < IBackendImplementation > ( backend . lock ( ) ) ;
}
2024-06-19 22:40:23 +02:00
SP < CWaylandBuffer > Aquamarine : : CWaylandOutput : : wlBufferFromBuffer ( SP < IBuffer > buffer ) {
std : : erase_if ( backendState . buffers , [ this ] ( const auto & el ) { return el . first . expired ( ) | | ! swapchain - > contains ( el . first . lock ( ) ) ; } ) ;
2024-08-27 20:04:26 +02:00
for ( auto const & [ k , v ] : backendState . buffers ) {
2024-06-19 22:40:23 +02:00
if ( k ! = buffer )
continue ;
return v ;
}
// create a new one
auto wlBuffer = makeShared < CWaylandBuffer > ( buffer , backend ) ;
if ( ! wlBuffer - > good ( ) )
return nullptr ;
backendState . buffers . emplace_back ( std : : make_pair < > ( buffer , wlBuffer ) ) ;
return wlBuffer ;
}
void Aquamarine : : CWaylandOutput : : sendFrameAndSetCallback ( ) {
events . frame . emit ( ) ;
2024-06-21 15:49:28 +02:00
frameScheduled = false ;
if ( waylandState . frameCallback | | ! readyForFrameCallback )
2024-06-19 22:40:23 +02:00
return ;
2024-06-20 19:24:43 +02:00
waylandState . frameCallback = makeShared < CCWlCallback > ( waylandState . surface - > sendFrame ( ) ) ;
2024-06-21 15:49:28 +02:00
waylandState . frameCallback - > setDone ( [ this ] ( CCWlCallback * r , uint32_t ms ) { onFrameDone ( ) ; } ) ;
}
void Aquamarine : : CWaylandOutput : : onFrameDone ( ) {
waylandState . frameCallback . reset ( ) ;
readyForFrameCallback = false ;
2024-07-11 20:54:23 +02:00
// FIXME: this is wrong, but otherwise we get bugs.
// thanks @phonetic112
scheduleFrame ( AQ_SCHEDULE_NEEDS_FRAME ) ;
2024-06-21 15:49:28 +02:00
if ( frameScheduledWhileWaiting )
sendFrameAndSetCallback ( ) ;
else
2024-06-19 22:40:23 +02:00
events . frame . emit ( ) ;
2024-06-21 15:49:28 +02:00
frameScheduledWhileWaiting = false ;
2024-06-19 22:40:23 +02:00
}
2024-06-20 19:24:43 +02:00
bool Aquamarine : : CWaylandOutput : : setCursor ( Hyprutils : : Memory : : CSharedPointer < IBuffer > buffer , const Hyprutils : : Math : : Vector2D & hotspot ) {
2024-06-22 16:50:30 +02:00
if ( ! cursorState . cursorSurface )
cursorState . cursorSurface = makeShared < CCWlSurface > ( backend - > waylandState . compositor - > sendCreateSurface ( ) ) ;
if ( ! cursorState . cursorSurface ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: Failed to create a wl_surface for the cursor " , name ) ) ;
return false ;
}
if ( ! buffer ) {
cursorState . cursorBuffer . reset ( ) ;
cursorState . cursorWlBuffer . reset ( ) ;
2024-08-26 10:30:29 +02:00
if ( ! backend - > pointers . empty ( ) )
backend - > pointers . at ( 0 ) - > pointer - > sendSetCursor ( cursorState . serial , nullptr , cursorState . hotspot . x , cursorState . hotspot . y ) ;
2024-06-22 16:50:30 +02:00
return true ;
}
cursorState . cursorBuffer = buffer ;
cursorState . hotspot = hotspot ;
if ( buffer - > shm ( ) . success ) {
auto attrs = buffer - > shm ( ) ;
auto [ pixelData , fmt , bufLen ] = buffer - > beginDataPtr ( 0 ) ;
int fd = allocateSHMFile ( bufLen ) ;
if ( fd < 0 ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: Failed to allocate a shm file " , name ) ) ;
return false ;
}
void * data = mmap ( nullptr , bufLen , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0 ) ;
if ( data = = MAP_FAILED ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: Failed to mmap the cursor pixel data " , name ) ) ;
close ( fd ) ;
return false ;
}
memcpy ( data , pixelData , bufLen ) ;
munmap ( data , bufLen ) ;
auto pool = makeShared < CCWlShmPool > ( backend - > waylandState . shm - > sendCreatePool ( fd , bufLen ) ) ;
if ( ! pool ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: Failed to submit a wl_shm pool " , name ) ) ;
close ( fd ) ;
return false ;
}
cursorState . cursorWlBuffer = makeShared < CCWlBuffer > ( pool - > sendCreateBuffer ( 0 , attrs . size . x , attrs . size . y , attrs . stride , shmFormatFromDRM ( attrs . format ) ) ) ;
pool . reset ( ) ;
close ( fd ) ;
} else if ( auto attrs = buffer - > dmabuf ( ) ; attrs . success ) {
auto params = makeShared < CCZwpLinuxBufferParamsV1 > ( backend - > waylandState . dmabuf - > sendCreateParams ( ) ) ;
for ( int i = 0 ; i < attrs . planes ; + + i ) {
params - > sendAdd ( attrs . fds . at ( i ) , i , attrs . offsets . at ( i ) , attrs . strides . at ( i ) , attrs . modifier > > 32 , attrs . modifier & 0xFFFFFFFF ) ;
}
cursorState . cursorWlBuffer = makeShared < CCWlBuffer > ( params - > sendCreateImmed ( attrs . size . x , attrs . size . y , attrs . format , ( zwpLinuxBufferParamsV1Flags ) 0 ) ) ;
} else {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: Failed to create a buffer for cursor: No known attrs (tried dmabuf / shm) " , name ) ) ;
return false ;
}
if ( ! cursorState . cursorWlBuffer ) {
backend - > backend - > log ( AQ_LOG_ERROR , std : : format ( " Output {}: Failed to create a buffer for cursor " , name ) ) ;
return false ;
}
cursorState . cursorSurface - > sendSetBufferScale ( 1 ) ;
cursorState . cursorSurface - > sendSetBufferTransform ( WL_OUTPUT_TRANSFORM_NORMAL ) ;
cursorState . cursorSurface - > sendAttach ( cursorState . cursorWlBuffer . get ( ) , 0 , 0 ) ;
cursorState . cursorSurface - > sendDamage ( 0 , 0 , INT32_MAX , INT32_MAX ) ;
cursorState . cursorSurface - > sendCommit ( ) ;
// this may fail if we are not in focus
if ( ! backend - > pointers . empty ( ) & & cursorState . serial )
backend - > pointers . at ( 0 ) - > pointer - > sendSetCursor ( cursorState . serial , cursorState . cursorSurface . get ( ) , cursorState . hotspot . x , cursorState . hotspot . y ) ;
2024-06-20 19:24:43 +02:00
return true ;
}
2024-08-21 22:59:51 +02:00
void Aquamarine : : CWaylandOutput : : moveCursor ( const Hyprutils : : Math : : Vector2D & coord , bool skipSchedule ) {
2024-06-20 19:24:43 +02:00
return ;
}
2024-06-22 16:50:30 +02:00
void Aquamarine : : CWaylandOutput : : onEnter ( SP < CCWlPointer > pointer , uint32_t serial ) {
cursorState . serial = serial ;
if ( ! cursorState . cursorSurface )
return ;
pointer - > sendSetCursor ( serial , cursorState . cursorSurface . get ( ) , cursorState . hotspot . x , cursorState . hotspot . y ) ;
}
2024-06-26 19:26:38 +02:00
Hyprutils : : Math : : Vector2D Aquamarine : : CWaylandOutput : : cursorPlaneSize ( ) {
2024-06-22 16:50:30 +02:00
return { - 1 , - 1 } ; // no limit
}
2024-07-07 18:05:05 +02:00
void Aquamarine : : CWaylandOutput : : scheduleFrame ( const scheduleFrameReason reason ) {
TRACE ( backend - > backend - > log ( AQ_LOG_TRACE ,
std : : format ( " CWaylandOutput::scheduleFrame: reason {}, needsFrame {}, frameScheduled {} " , ( uint32_t ) reason , needsFrame , frameScheduled ) ) ) ;
2024-07-06 11:26:42 +02:00
needsFrame = true ;
2024-06-21 15:49:28 +02:00
if ( frameScheduled )
2024-06-20 19:24:43 +02:00
return ;
2024-06-21 15:49:28 +02:00
frameScheduled = true ;
if ( waylandState . frameCallback )
frameScheduledWhileWaiting = true ;
else
backend - > idleCallbacks . emplace_back ( [ this ] ( ) { sendFrameAndSetCallback ( ) ; } ) ;
2024-06-20 19:24:43 +02:00
}
2024-06-19 22:40:23 +02:00
Aquamarine : : CWaylandBuffer : : CWaylandBuffer ( SP < IBuffer > buffer_ , Hyprutils : : Memory : : CWeakPointer < CWaylandBackend > backend_ ) : buffer ( buffer_ ) , backend ( backend_ ) {
2024-06-20 19:24:43 +02:00
auto params = makeShared < CCZwpLinuxBufferParamsV1 > ( backend - > waylandState . dmabuf - > sendCreateParams ( ) ) ;
2024-06-19 22:40:23 +02:00
if ( ! params ) {
backend - > backend - > log ( AQ_LOG_ERROR , " WaylandBuffer: failed to query params " ) ;
return ;
}
auto attrs = buffer - > dmabuf ( ) ;
for ( size_t i = 0 ; i < attrs . planes ; + + i ) {
params - > sendAdd ( attrs . fds . at ( i ) , i , attrs . offsets . at ( i ) , attrs . strides . at ( i ) , attrs . modifier > > 32 , attrs . modifier & 0xFFFFFFFF ) ;
}
2024-06-20 19:24:43 +02:00
waylandState . buffer = makeShared < CCWlBuffer > ( params - > sendCreateImmed ( attrs . size . x , attrs . size . y , attrs . format , ( zwpLinuxBufferParamsV1Flags ) 0 ) ) ;
2024-06-19 22:40:23 +02:00
2024-06-20 19:24:43 +02:00
waylandState . buffer - > setRelease ( [ this ] ( CCWlBuffer * r ) { pendingRelease = false ; } ) ;
2024-06-19 22:40:23 +02:00
params - > sendDestroy ( ) ;
}
Aquamarine : : CWaylandBuffer : : ~ CWaylandBuffer ( ) {
if ( waylandState . buffer & & waylandState . buffer - > resource ( ) )
waylandState . buffer - > sendDestroy ( ) ;
}
bool Aquamarine : : CWaylandBuffer : : good ( ) {
return waylandState . buffer & & waylandState . buffer - > resource ( ) ;
}