wl_data_device interface in the base wayland protocol is the most complex one, in my opinion, wayland.xml explains it fairly well, but every pieces are scattered in the file, here I serialize it together, give you quick run through.

In brief, data device in wayland consists of four interfaces: The first is wl_data_device_manager, it is the global. The second is wl_data_device, can be created from wl_data_device_manager for a given seat, it is used to set selection(ctrl-c ctrl-v) and drag-n-drop. The 3rd is wl_data_source, also created from wl_data_device_manager, this source will be either a selection source or drag-n-drop source. The last is wl_data_offer, which is used for copying data from wl_data_source.

Now we talk about the dynamics of the those 4 objects. Firstly a client creates a data_source (for example you pressed ctrl-C), gives it all the MIME types it offers and mark it as a selection or a drag. Then it is server's responsibility to create wl_data_offer at a proper moment later(either a new surface focused on a keyboard or pointer moves in a surface). Once the data_offer created, server will immediately send all the acceptable MIME types ot the offer, expects one of them accepted by the offer. If it is true, the data_offer need to give a file desriptor for writing data to, the server only need to transfer the call to the data_source and it writes to that fd. After it is done, data_offer is happy then send a wl_data_offer.finish to server can destroy the source. This process roughly covers the selection process. The drag-n-drop is a little more complicated.

The different in drag-n-drop starts with wl_data_offer creation. As we said before, it happens at wl_surface.enter, here we would call wl_data_device.enter for the data_offer. wl_data_device.motion is called on cursor movement. Note that pratically you can drag through the entire surface without releasing, then on the next surface, server will call wl_data_device.leave first, then create another data_offer amid wl_data_device.enter. During this process, there is a side story happening, wl_data_source and wl_data_offer needs to negociating the actions(copy/move) and acceptable MIMEs. When cursor released, data_source is notified with wl_data_source.dnd_performed, finally it would be like in selection, data_offer gives a file destriptor and data_source to write and wl_data_offer.finish is used to finish the transaction.

Overall it is more complex protocol, multiple requests and events are executed in sequence and expected in a given order. This is unlike most interface, where requests/events are more like a single shot.