@ -4,6 +4,8 @@
# include <limits.h>
# include <stdint.h>
# include <stdlib.h>
# include <sys/mman.h>
# include <sys/types.h>
# include <unistd.h>
# include <drm_fourcc.h>
@ -30,6 +32,21 @@
# include "tablet-unstable-v2-client-protocol.h"
# include "relative-pointer-unstable-v1-client-protocol.h"
struct wlr_wl_linux_dmabuf_feedback_v1 {
struct wlr_wl_backend * backend ;
dev_t main_device_id ;
struct wlr_wl_linux_dmabuf_v1_table_entry * format_table ;
size_t format_table_size ;
dev_t tranche_target_device_id ;
} ;
struct wlr_wl_linux_dmabuf_v1_table_entry {
uint32_t format ;
uint32_t pad ; /* unused */
uint64_t modifier ;
} ;
struct wlr_wl_backend * get_wl_backend_from_backend ( struct wlr_backend * backend ) {
assert ( wlr_backend_is_wl ( backend ) ) ;
return ( struct wlr_wl_backend * ) backend ;
@ -108,6 +125,119 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
. modifier = linux_dmabuf_v1_handle_modifier ,
} ;
static void linux_dmabuf_feedback_v1_handle_done ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback ) {
// This space is intentionally left blank
}
static void linux_dmabuf_feedback_v1_handle_format_table ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback , int fd , uint32_t size ) {
struct wlr_wl_linux_dmabuf_feedback_v1 * feedback_data = data ;
feedback_data - > format_table = NULL ;
void * table_data = mmap ( NULL , size , PROT_READ , MAP_PRIVATE , fd , 0 ) ;
if ( table_data = = MAP_FAILED ) {
wlr_log_errno ( WLR_ERROR , " failed to mmap DMA-BUF format table " ) ;
} else {
feedback_data - > format_table = table_data ;
feedback_data - > format_table_size = size ;
}
close ( fd ) ;
}
static void linux_dmabuf_feedback_v1_handle_main_device ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback ,
struct wl_array * dev_id_arr ) {
struct wlr_wl_linux_dmabuf_feedback_v1 * feedback_data = data ;
dev_t dev_id ;
assert ( dev_id_arr - > size = = sizeof ( dev_id ) ) ;
memcpy ( & dev_id , dev_id_arr - > data , sizeof ( dev_id ) ) ;
feedback_data - > main_device_id = dev_id ;
drmDevice * device = NULL ;
if ( drmGetDeviceFromDevId ( dev_id , 0 , & device ) ! = 0 ) {
wlr_log_errno ( WLR_ERROR , " drmGetDeviceFromDevId failed " ) ;
return ;
}
const char * name = NULL ;
if ( device - > available_nodes & ( 1 < < DRM_NODE_RENDER ) ) {
name = device - > 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 ( device - > available_nodes & ( 1 < < DRM_NODE_PRIMARY ) ) ;
name = device - > nodes [ DRM_NODE_PRIMARY ] ;
wlr_log ( WLR_DEBUG , " DRM device %s has no render node, "
" falling back to primary node " , name ) ;
}
feedback_data - > backend - > drm_render_name = strdup ( name ) ;
drmFreeDevice ( & device ) ;
}
static void linux_dmabuf_feedback_v1_handle_tranche_done ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback ) {
struct wlr_wl_linux_dmabuf_feedback_v1 * feedback_data = data ;
feedback_data - > tranche_target_device_id = 0 ;
}
static void linux_dmabuf_feedback_v1_handle_tranche_target_device ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback ,
struct wl_array * dev_id_arr ) {
struct wlr_wl_linux_dmabuf_feedback_v1 * feedback_data = data ;
dev_t dev_id ;
assert ( dev_id_arr - > size = = sizeof ( dev_id ) ) ;
memcpy ( & dev_id , dev_id_arr - > data , sizeof ( dev_id ) ) ;
feedback_data - > tranche_target_device_id = dev_id ;
}
static void linux_dmabuf_feedback_v1_handle_tranche_formats ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback ,
struct wl_array * indices_arr ) {
struct wlr_wl_linux_dmabuf_feedback_v1 * feedback_data = data ;
if ( feedback_data - > format_table = = NULL ) {
return ;
}
if ( feedback_data - > tranche_target_device_id ! = feedback_data - > main_device_id ) {
return ;
}
size_t table_cap = feedback_data - > format_table_size /
sizeof ( struct wlr_wl_linux_dmabuf_v1_table_entry ) ;
uint16_t * index_ptr ;
wl_array_for_each ( index_ptr , indices_arr ) {
assert ( * index_ptr < table_cap ) ;
const struct wlr_wl_linux_dmabuf_v1_table_entry * entry =
& feedback_data - > format_table [ * index_ptr ] ;
wlr_drm_format_set_add ( & feedback_data - > backend - > linux_dmabuf_v1_formats ,
entry - > format , entry - > modifier ) ;
}
}
static void linux_dmabuf_feedback_v1_handle_tranche_flags ( void * data ,
struct zwp_linux_dmabuf_feedback_v1 * feedback , uint32_t flags ) {
// TODO: handle SCANOUT flag
}
static const struct zwp_linux_dmabuf_feedback_v1_listener
linux_dmabuf_feedback_v1_listener = {
. done = linux_dmabuf_feedback_v1_handle_done ,
. format_table = linux_dmabuf_feedback_v1_handle_format_table ,
. main_device = linux_dmabuf_feedback_v1_handle_main_device ,
. tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done ,
. tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device ,
. tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats ,
. tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags ,
} ;
static bool device_has_name ( const drmDevice * device , const char * name ) {
for ( size_t i = 0 ; i < DRM_NODE_MAX ; i + + ) {
if ( ! ( device - > available_nodes & ( 1 < < i ) ) ) {
@ -172,8 +302,6 @@ static char *get_render_name(const char *name) {
static void legacy_drm_handle_device ( void * data , struct wl_drm * drm ,
const char * name ) {
struct wlr_wl_backend * wl = data ;
// TODO: get FD from linux-dmabuf hints instead
wl - > drm_render_name = get_render_name ( name ) ;
}
@ -245,7 +373,7 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if ( strcmp ( iface , zwp_linux_dmabuf_v1_interface . name ) = = 0 & &
version > = 3 ) {
wl - > zwp_linux_dmabuf_v1 = wl_registry_bind ( registry , name ,
& zwp_linux_dmabuf_v1_interface , 3 ) ;
& zwp_linux_dmabuf_v1_interface , version > = 4 ? 4 : version ) ;
zwp_linux_dmabuf_v1_add_listener ( wl - > zwp_linux_dmabuf_v1 ,
& linux_dmabuf_v1_listener , wl ) ;
} else if ( strcmp ( iface , zwp_relative_pointer_manager_v1_interface . name ) = = 0 ) {
@ -428,10 +556,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
wlr_log_errno ( WLR_ERROR , " Could not obtain reference to remote registry " ) ;
goto error_display ;
}
wl_registry_add_listener ( wl - > registry , & registry_listener , wl ) ;
wl_display_roundtrip ( wl - > remote_display ) ; // get globals
wl_display_roundtrip ( wl - > remote_display ) ; // get linux-dmabuf formats
if ( ! wl - > compositor ) {
wlr_log ( WLR_ERROR ,
@ -444,6 +571,35 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
goto error_registry ;
}
struct zwp_linux_dmabuf_feedback_v1 * linux_dmabuf_feedback_v1 = NULL ;
struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { . backend = wl } ;
if ( wl - > zwp_linux_dmabuf_v1 ! = NULL & &
zwp_linux_dmabuf_v1_get_version ( wl - > zwp_linux_dmabuf_v1 ) > =
ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION ) {
linux_dmabuf_feedback_v1 =
zwp_linux_dmabuf_v1_get_default_feedback ( wl - > zwp_linux_dmabuf_v1 ) ;
if ( linux_dmabuf_feedback_v1 = = NULL ) {
wlr_log ( WLR_ERROR , " Allocation failed " ) ;
goto error_registry ;
}
zwp_linux_dmabuf_feedback_v1_add_listener ( linux_dmabuf_feedback_v1 ,
& linux_dmabuf_feedback_v1_listener , & feedback_data ) ;
if ( wl - > legacy_drm ! = NULL ) {
wl_drm_destroy ( wl - > legacy_drm ) ;
wl - > legacy_drm = NULL ;
}
}
wl_display_roundtrip ( wl - > remote_display ) ; // get linux-dmabuf formats
if ( feedback_data . format_table ! = NULL ) {
munmap ( feedback_data . format_table , feedback_data . format_table_size ) ;
}
if ( linux_dmabuf_feedback_v1 ! = NULL ) {
zwp_linux_dmabuf_feedback_v1_destroy ( linux_dmabuf_feedback_v1 ) ;
}
struct wl_event_loop * loop = wl_display_get_event_loop ( wl - > local_display ) ;
int fd = wl_display_get_fd ( wl - > remote_display ) ;
wl - > remote_display_src = wl_event_loop_add_fd ( loop , fd , WL_EVENT_READABLE ,