@ -5,51 +5,26 @@
# include <unistd.h>
# include <wlr/types/wlr_data_control_v1.h>
# include <wlr/types/wlr_data_device.h>
# include <wlr/types/wlr_primary_selection.h>
# include <wlr/util/log.h>
# include "util/signal.h"
# include "wlr-data-control-unstable-v1-protocol.h"
# define DATA_CONTROL_MANAGER_VERSION 1
# define DATA_CONTROL_MANAGER_VERSION 2
struct client_data_source {
struct wlr_data_source source ;
struct data_control_source {
struct wl_resource * resource ;
struct wl_array mime_types ;
bool finalized ;
} ;
static const struct wlr_data_source_impl client_source_impl ;
static struct client_data_source *
client_data_source_from_source ( struct wlr_data_source * wlr_source ) {
assert ( wlr_source - > impl = = & client_source_impl ) ;
return ( struct client_data_source * ) wlr_source ;
}
static void client_source_send ( struct wlr_data_source * wlr_source ,
const char * mime_type , int fd ) {
struct client_data_source * source =
client_data_source_from_source ( wlr_source ) ;
zwlr_data_control_source_v1_send_send ( source - > resource , mime_type , fd ) ;
close ( fd ) ;
}
static void client_source_destroy ( struct wlr_data_source * wlr_source ) {
struct client_data_source * source =
client_data_source_from_source ( wlr_source ) ;
zwlr_data_control_source_v1_send_cancelled ( source - > resource ) ;
// Make the resource inert
wl_resource_set_user_data ( source - > resource , NULL ) ;
free ( source ) ;
}
static const struct wlr_data_source_impl client_source_impl = {
. send = client_source_send ,
. destroy = client_source_destroy ,
// Only one of these is non-NULL.
struct wlr_data_source * active_source ;
struct wlr_primary_selection_source * active_primary_source ;
} ;
static const struct zwlr_data_control_source_v1_interface source_impl ;
static struct client_ data_source * source_from_resource (
static struct data_control_source * source_from_resource (
struct wl_resource * resource ) {
assert ( wl_resource_instance_of ( resource ,
& zwlr_data_control_source_v1_interface , & source_impl ) ) ;
@ -58,7 +33,7 @@ static struct client_data_source *source_from_resource(
static void source_handle_offer ( struct wl_client * client ,
struct wl_resource * resource , const char * mime_type ) {
struct client_ data_source * source = source_from_resource ( resource ) ;
struct data_control _source * source = source_from_resource ( resource ) ;
if ( source = = NULL ) {
return ;
}
@ -66,12 +41,13 @@ static void source_handle_offer(struct wl_client *client,
if ( source - > finalized ) {
wl_resource_post_error ( resource ,
ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER ,
" cannot mutate offer after set_selection " ) ;
" cannot mutate offer after set_selection or "
" set_primary_selection " ) ;
return ;
}
const char * * mime_type_ptr ;
wl_array_for_each ( mime_type_ptr , & source - > source. mime_types) {
wl_array_for_each ( mime_type_ptr , & source - > mime_types) {
if ( strcmp ( * mime_type_ptr , mime_type ) = = 0 ) {
wlr_log ( WLR_DEBUG , " Ignoring duplicate MIME type offer %s " ,
mime_type ) ;
@ -85,7 +61,7 @@ static void source_handle_offer(struct wl_client *client,
return ;
}
char * * p = wl_array_add ( & source - > source. mime_types, sizeof ( char * ) ) ;
char * * p = wl_array_add ( & source - > mime_types, sizeof ( char * ) ) ;
if ( p = = NULL ) {
free ( dup_mime_type ) ;
wl_resource_post_no_memory ( resource ) ;
@ -105,18 +81,156 @@ static const struct zwlr_data_control_source_v1_interface source_impl = {
. destroy = source_handle_destroy ,
} ;
static void data_control_source_destroy ( struct data_control_source * source ) {
if ( source = = NULL ) {
return ;
}
char * * p ;
wl_array_for_each ( p , & source - > mime_types ) {
free ( * p ) ;
}
wl_array_release ( & source - > mime_types ) ;
// Prevent destructors below from calling this recursively.
wl_resource_set_user_data ( source - > resource , NULL ) ;
if ( source - > active_source ! = NULL ) {
wlr_data_source_destroy ( source - > active_source ) ;
} else if ( source - > active_primary_source ! = NULL ) {
wlr_primary_selection_source_destroy (
source - > active_primary_source ) ;
}
free ( source ) ;
}
static void source_handle_resource_destroy ( struct wl_resource * resource ) {
struct client_data_source * source = source_from_resource ( resource ) ;
struct data_control_source * source = source_from_resource ( resource ) ;
data_control_source_destroy ( source ) ;
}
struct client_data_source {
struct wlr_data_source source ;
struct wl_resource * resource ;
} ;
static const struct wlr_data_source_impl client_source_impl ;
static struct client_data_source *
client_data_source_from_source ( struct wlr_data_source * wlr_source ) {
assert ( wlr_source - > impl = = & client_source_impl ) ;
return ( struct client_data_source * ) wlr_source ;
}
static void client_source_send ( struct wlr_data_source * wlr_source ,
const char * mime_type , int fd ) {
struct client_data_source * source =
client_data_source_from_source ( wlr_source ) ;
zwlr_data_control_source_v1_send_send ( source - > resource , mime_type , fd ) ;
close ( fd ) ;
}
static void client_source_destroy ( struct wlr_data_source * wlr_source ) {
struct client_data_source * client_source =
client_data_source_from_source ( wlr_source ) ;
struct data_control_source * source =
source_from_resource ( client_source - > resource ) ;
free ( client_source ) ;
if ( source = = NULL ) {
return ;
}
wlr_data_source_destroy ( & source - > source ) ;
source - > active_source = NULL ;
zwlr_data_control_source_v1_send_cancelled ( source - > resource ) ;
data_control_source_destroy ( source ) ;
}
static const struct wlr_data_source_impl client_source_impl = {
. send = client_source_send ,
. destroy = client_source_destroy ,
} ;
struct client_primary_selection_source {
struct wlr_primary_selection_source source ;
struct wl_resource * resource ;
} ;
static const struct wlr_primary_selection_source_impl
client_primary_selection_source_impl ;
static struct client_primary_selection_source *
client_primary_selection_source_from_source (
struct wlr_primary_selection_source * wlr_source ) {
assert ( wlr_source - > impl = = & client_primary_selection_source_impl ) ;
return ( struct client_primary_selection_source * ) wlr_source ;
}
static void client_primary_selection_source_send (
struct wlr_primary_selection_source * wlr_source ,
const char * mime_type , int fd ) {
struct client_primary_selection_source * source =
client_primary_selection_source_from_source ( wlr_source ) ;
zwlr_data_control_source_v1_send_send ( source - > resource , mime_type , fd ) ;
close ( fd ) ;
}
static void client_primary_selection_source_destroy (
struct wlr_primary_selection_source * wlr_source ) {
struct client_primary_selection_source * client_source =
client_primary_selection_source_from_source ( wlr_source ) ;
struct data_control_source * source =
source_from_resource ( client_source - > resource ) ;
free ( client_source ) ;
if ( source = = NULL ) {
return ;
}
source - > active_primary_source = NULL ;
zwlr_data_control_source_v1_send_cancelled ( source - > resource ) ;
data_control_source_destroy ( source ) ;
}
static const struct wlr_primary_selection_source_impl
client_primary_selection_source_impl = {
. send = client_primary_selection_source_send ,
. destroy = client_primary_selection_source_destroy ,
} ;
struct data_offer {
struct wl_resource * resource ;
struct wlr_data_control_device_v1 * device ;
bool is_primary ;
} ;
static void data_offer_destroy ( struct data_offer * offer ) {
if ( offer = = NULL ) {
return ;
}
struct wlr_data_control_device_v1 * device = offer - > device ;
if ( device ! = NULL ) {
if ( offer - > is_primary ) {
device - > primary_selection_offer_resource = NULL ;
} else {
device - > selection_offer_resource = NULL ;
}
}
wl_resource_set_user_data ( offer - > resource , NULL ) ;
free ( offer ) ;
}
static const struct zwlr_data_control_offer_v1_interface offer_impl ;
static struct wlr_data_control_device_v1 * control_from_offer_resource (
static struct data_offer * data_offer _from_offer_resource(
struct wl_resource * resource ) {
assert ( wl_resource_instance_of ( resource ,
& zwlr_data_control_offer_v1_interface , & offer_impl ) ) ;
@ -125,12 +239,33 @@ static struct wlr_data_control_device_v1 *control_from_offer_resource(
static void offer_handle_receive ( struct wl_client * client ,
struct wl_resource * resource , const char * mime_type , int fd ) {
struct wlr_data_control_device_v1 * device = control_from_offer_resource ( resource ) ;
if ( device = = NULL | | device - > seat - > selection_source = = NULL ) {
struct data_offer * offer = data_offer_from_offer_resource ( resource ) ;
if ( offer = = NULL ) {
close ( fd ) ;
return ;
}
struct wlr_data_control_device_v1 * device = offer - > device ;
if ( device = = NULL ) {
close ( fd ) ;
return ;
}
if ( offer - > is_primary ) {
if ( device - > seat - > primary_selection_source = = NULL ) {
close ( fd ) ;
return ;
}
wlr_primary_selection_source_send (
device - > seat - > primary_selection_source ,
mime_type , fd ) ;
} else {
if ( device - > seat - > selection_source = = NULL ) {
close ( fd ) ;
return ;
}
wlr_data_source_send ( device - > seat - > selection_source , mime_type , fd ) ;
}
}
static void offer_handle_destroy ( struct wl_client * client ,
@ -144,28 +279,39 @@ static const struct zwlr_data_control_offer_v1_interface offer_impl = {
} ;
static void offer_handle_resource_destroy ( struct wl_resource * resource ) {
struct wlr_data_control_device_v1 * device = control_from_offer_resource ( resource ) ;
if ( device ! = NULL ) {
device - > selection_offer_resource = NULL ;
}
struct data_offer * offer = data_offer_from_offer_resource ( resource ) ;
data_offer_destroy ( offer ) ;
}
static struct wl_resource * create_offer ( struct wlr_data_control_device_v1 * device ,
struct wl r_data_source * source ) {
struct wl _array * mime_types , bool is_primary ) {
struct wl_client * client = wl_resource_get_client ( device - > resource ) ;
struct data_offer * offer = calloc ( 1 , sizeof ( struct data_offer ) ) ;
if ( offer = = NULL ) {
wl_client_post_no_memory ( client ) ;
return NULL ;
}
offer - > device = device ;
offer - > is_primary = is_primary ;
uint32_t version = wl_resource_get_version ( device - > resource ) ;
struct wl_resource * resource = wl_resource_create ( client ,
& zwlr_data_control_offer_v1_interface , version , 0 ) ;
if ( resource = = NULL ) {
return NULL ;
}
wl_resource_set_implementation ( resource , & offer_impl , device ,
offer - > resource = resource ;
wl_resource_set_implementation ( resource , & offer_impl , offer ,
offer_handle_resource_destroy ) ;
zwlr_data_control_device_v1_send_data_offer ( device - > resource , resource ) ;
char * * p ;
wl_array_for_each ( p , & source - > mime_types ) {
wl_array_for_each ( p , mime_types ) {
zwlr_data_control_offer_v1_send_offer ( resource , * p ) ;
}
@ -191,16 +337,103 @@ static void control_handle_set_selection(struct wl_client *client,
return ;
}
struct client_ data_source * source = NULL ;
struct data_control _source * source = NULL ;
if ( source_resource ! = NULL ) {
source = source_from_resource ( source_resource ) ;
}
struct wlr_data_source * wlr_source = source ? & source - > source : NULL ;
if ( source = = NULL ) {
wlr_seat_request_set_selection ( device - > seat , NULL ,
wl_display_next_serial ( device - > seat - > display ) ) ;
return ;
}
if ( source - > active_source ! = NULL | |
source - > active_primary_source ! = NULL ) {
wl_resource_post_error ( control_resource ,
ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE ,
" cannot use a data source in set_selection or "
" set_primary_selection more than once " ) ;
return ;
}
struct client_data_source * client_source =
calloc ( 1 , sizeof ( struct client_data_source ) ) ;
if ( client_source = = NULL ) {
wl_client_post_no_memory ( client ) ;
return ;
}
client_source - > resource = source_resource ;
struct wlr_data_source * wlr_source = & client_source - > source ;
wlr_data_source_init ( wlr_source , & client_source_impl ) ;
source - > active_source = wlr_source ;
wl_array_release ( & wlr_source - > mime_types ) ;
wlr_source - > mime_types = source - > mime_types ;
wl_array_init ( & source - > mime_types ) ;
source - > finalized = true ;
wlr_seat_request_set_selection ( device - > seat , wlr_source ,
wl_display_next_serial ( device - > seat - > display ) ) ;
}
static void control_handle_set_primary_selection ( struct wl_client * client ,
struct wl_resource * control_resource ,
struct wl_resource * source_resource ) {
struct wlr_data_control_device_v1 * device =
control_from_resource ( control_resource ) ;
if ( device = = NULL ) {
return ;
}
struct data_control_source * source = NULL ;
if ( source_resource ! = NULL ) {
source = source_from_resource ( source_resource ) ;
}
if ( source = = NULL ) {
wlr_seat_request_set_primary_selection ( device - > seat , NULL ,
wl_display_next_serial ( device - > seat - > display ) ) ;
return ;
}
if ( source - > active_source ! = NULL | |
source - > active_primary_source ! = NULL ) {
wl_resource_post_error ( control_resource ,
ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE ,
" cannot use a data source in set_selection or "
" set_primary_selection more than once " ) ;
return ;
}
struct client_primary_selection_source * client_source =
calloc ( 1 , sizeof ( struct client_primary_selection_source ) ) ;
if ( client_source = = NULL ) {
wl_client_post_no_memory ( client ) ;
return ;
}
client_source - > resource = source_resource ;
struct wlr_primary_selection_source * wlr_source = & client_source - > source ;
wlr_primary_selection_source_init ( wlr_source , & client_primary_selection_source_impl ) ;
source - > active_primary_source = wlr_source ;
wl_array_release ( & wlr_source - > mime_types ) ;
wlr_source - > mime_types = source - > mime_types ;
wl_array_init ( & source - > mime_types ) ;
source - > finalized = true ;
wlr_seat_request_set_primary_selection ( device - > seat , wlr_source ,
wl_display_next_serial ( device - > seat - > display ) ) ;
}
static void control_handle_destroy ( struct wl_client * client ,
struct wl_resource * control_resource ) {
wl_resource_destroy ( control_resource ) ;
@ -208,6 +441,7 @@ static void control_handle_destroy(struct wl_client *client,
static const struct zwlr_data_control_device_v1_interface control_impl = {
. set_selection = control_handle_set_selection ,
. set_primary_selection = control_handle_set_primary_selection ,
. destroy = control_handle_destroy ,
} ;
@ -216,12 +450,15 @@ static void control_send_selection(struct wlr_data_control_device_v1 *device) {
if ( device - > selection_offer_resource ! = NULL ) {
// Make the offer inert
wl_resource_set_user_data ( device - > selection_offer_resource , NULL ) ;
struct data_offer * offer = data_offer_from_offer_resource (
device - > selection_offer_resource ) ;
data_offer_destroy ( offer ) ;
}
device - > selection_offer_resource = NULL ;
if ( source ! = NULL ) {
device - > selection_offer_resource = create_offer ( device , source ) ;
device - > selection_offer_resource =
create_offer ( device , & source - > mime_types , false ) ;
if ( device - > selection_offer_resource = = NULL ) {
wl_resource_post_no_memory ( device - > resource ) ;
return ;
@ -232,6 +469,37 @@ static void control_send_selection(struct wlr_data_control_device_v1 *device) {
device - > selection_offer_resource ) ;
}
static void control_send_primary_selection (
struct wlr_data_control_device_v1 * device ) {
uint32_t version = wl_resource_get_version ( device - > resource ) ;
if ( version < ZWLR_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION ) {
return ;
}
struct wlr_primary_selection_source * source =
device - > seat - > primary_selection_source ;
if ( device - > primary_selection_offer_resource ! = NULL ) {
// Make the offer inert
struct data_offer * offer = data_offer_from_offer_resource (
device - > primary_selection_offer_resource ) ;
data_offer_destroy ( offer ) ;
}
device - > primary_selection_offer_resource = NULL ;
if ( source ! = NULL ) {
device - > primary_selection_offer_resource =
create_offer ( device , & source - > mime_types , true ) ;
if ( device - > primary_selection_offer_resource = = NULL ) {
wl_resource_post_no_memory ( device - > resource ) ;
return ;
}
}
zwlr_data_control_device_v1_send_primary_selection ( device - > resource ,
device - > primary_selection_offer_resource ) ;
}
static void control_handle_resource_destroy ( struct wl_resource * resource ) {
struct wlr_data_control_device_v1 * device = control_from_resource ( resource ) ;
wlr_data_control_device_v1_destroy ( device ) ;
@ -251,6 +519,14 @@ static void control_handle_seat_set_selection(struct wl_listener *listener,
control_send_selection ( device ) ;
}
static void control_handle_seat_set_primary_selection (
struct wl_listener * listener ,
void * data ) {
struct wlr_data_control_device_v1 * device =
wl_container_of ( listener , device , seat_set_primary_selection ) ;
control_send_primary_selection ( device ) ;
}
void wlr_data_control_device_v1_destroy ( struct wlr_data_control_device_v1 * device ) {
if ( device = = NULL ) {
return ;
@ -259,10 +535,18 @@ void wlr_data_control_device_v1_destroy(struct wlr_data_control_device_v1 *devic
// Make the resources inert
wl_resource_set_user_data ( device - > resource , NULL ) ;
if ( device - > selection_offer_resource ! = NULL ) {
wl_resource_set_user_data ( device - > selection_offer_resource , NULL ) ;
struct data_offer * offer = data_offer_from_offer_resource (
device - > selection_offer_resource ) ;
data_offer_destroy ( offer ) ;
}
if ( device - > primary_selection_offer_resource ! = NULL ) {
struct data_offer * offer = data_offer_from_offer_resource (
device - > primary_selection_offer_resource ) ;
data_offer_destroy ( offer ) ;
}
wl_list_remove ( & device - > seat_destroy . link ) ;
wl_list_remove ( & device - > seat_set_selection . link ) ;
wl_list_remove ( & device - > seat_set_primary_selection . link ) ;
wl_list_remove ( & device - > link ) ;
free ( device ) ;
}
@ -279,19 +563,21 @@ static struct wlr_data_control_manager_v1 *manager_from_resource(
static void manager_handle_create_data_source ( struct wl_client * client ,
struct wl_resource * manager_resource , uint32_t id ) {
struct client_ data_source * source =
calloc ( 1 , sizeof ( struct client_ data_source) ) ;
struct data_control _source * source =
calloc ( 1 , sizeof ( struct data_control _source) ) ;
if ( source = = NULL ) {
wl_resource_post_no_memory ( manager_resource ) ;
return ;
}
wlr_data_source_init ( & source - > source , & client_source_impl ) ;
wl_array_init ( & source - > mime_types ) ;
uint32_t version = wl_resource_get_version ( manager_resource ) ;
source - > resource = wl_resource_create ( client ,
& zwlr_data_control_source_v1_interface , version , id ) ;
if ( source - > resource = = NULL ) {
wl_resource_post_no_memory ( manager_resource ) ;
wl_array_release ( & source - > mime_types ) ;
free ( source ) ;
return ;
}
@ -335,6 +621,11 @@ static void manager_handle_get_data_device(struct wl_client *client,
wl_signal_add ( & device - > seat - > events . set_selection ,
& device - > seat_set_selection ) ;
device - > seat_set_primary_selection . notify =
control_handle_seat_set_primary_selection ;
wl_signal_add ( & device - > seat - > events . set_primary_selection ,
& device - > seat_set_primary_selection ) ;
wl_list_insert ( & manager - > devices , & device - > link ) ;
wlr_signal_emit_safe ( & manager - > events . new_device , device ) ;
@ -343,6 +634,7 @@ static void manager_handle_get_data_device(struct wl_client *client,
device = control_from_resource ( resource ) ;
if ( device ! = NULL ) {
control_send_selection ( device ) ;
control_send_primary_selection ( device ) ;
}
}
@ -375,6 +667,10 @@ static void manager_bind(struct wl_client *client, void *data, uint32_t version,
manager_handle_resource_destroy ) ;
wl_list_insert ( & manager - > resources , wl_resource_get_link ( resource ) ) ;
if ( version > = ZWLR_DATA_CONTROL_MANAGER_V1_PRIMARY_SELECTION_SINCE_VERSION ) {
zwlr_data_control_manager_v1_send_primary_selection ( resource ) ;
}
}
static void handle_display_destroy ( struct wl_listener * listener , void * data ) {
@ -396,8 +692,8 @@ struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create(
wl_signal_init ( & manager - > events . new_device ) ;
manager - > global = wl_global_create ( display ,
& zwlr_data_control_manager_v1_interface , DATA_CONTROL_MANAGER_VERSION ,
manager, manager_bind ) ;
& zwlr_data_control_manager_v1_interface ,
DATA_CONTROL_MANAGER_VERSION, manager, manager_bind ) ;
if ( manager - > global = = NULL ) {
free ( manager ) ;
return NULL ;