FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
uwac-clipboard.c
1/*
2 * Copyright © 2018 Armin Novak <armin.novak@thincast.com>
3 * Copyright © 2018 Thincast Technologies GmbH
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23#include "uwac-priv.h"
24#include "uwac-utils.h"
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <assert.h>
30#include <time.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <sys/mman.h>
34#include <sys/timerfd.h>
35#include <sys/epoll.h>
36
37/* paste */
38static void data_offer_offer(void* data, struct wl_data_offer* data_offer,
39 const char* offered_mime_type)
40{
41 UwacSeat* seat = (UwacSeat*)data;
42
43 assert(seat);
44 if (!seat->ignore_announcement)
45 {
46 UwacClipboardEvent* event =
47 (UwacClipboardEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_CLIPBOARD_OFFER);
48
49 if (!event)
50 {
51 assert(uwacErrorHandler(seat->display, UWAC_ERROR_INTERNAL,
52 "failed to allocate a clipboard event\n"));
53 }
54 else
55 {
56 event->seat = seat;
57 (void)snprintf(event->mime, sizeof(event->mime), "%s", offered_mime_type);
58 }
59 }
60}
61
62static const struct wl_data_offer_listener data_offer_listener = { .offer = data_offer_offer };
63
64static void data_device_data_offer(void* data, struct wl_data_device* data_device,
65 struct wl_data_offer* data_offer)
66{
67 UwacSeat* seat = (UwacSeat*)data;
68
69 assert(seat);
70 if (!seat->ignore_announcement)
71 {
72 UwacClipboardEvent* event =
73 (UwacClipboardEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_CLIPBOARD_SELECT);
74
75 if (!event)
76 {
77 assert(uwacErrorHandler(seat->display, UWAC_ERROR_INTERNAL,
78 "failed to allocate a close event\n"));
79 }
80 else
81 event->seat = seat;
82
83 wl_data_offer_add_listener(data_offer, &data_offer_listener, data);
84 seat->offer = data_offer;
85 }
86 else
87 seat->offer = NULL;
88}
89
90static void data_device_selection(void* data, struct wl_data_device* data_device,
91 struct wl_data_offer* data_offer)
92{
93}
94
95static const struct wl_data_device_listener data_device_listener = {
96 .data_offer = data_device_data_offer, .selection = data_device_selection
97};
98
99/* copy */
100static void data_source_target_handler(void* data, struct wl_data_source* data_source,
101 const char* mime_type)
102{
103}
104
105static void data_source_send_handler(void* data, struct wl_data_source* data_source,
106 const char* mime_type, int fd)
107{
108 UwacSeat* seat = (UwacSeat*)data;
109 seat->transfer_data(seat, seat->data_context, mime_type, fd);
110}
111
112static void data_source_cancelled_handler(void* data, struct wl_data_source* data_source)
113{
114 UwacSeat* seat = (UwacSeat*)data;
115 seat->cancel_data(seat, seat->data_context);
116}
117
118static const struct wl_data_source_listener data_source_listener = {
119 .target = data_source_target_handler,
120 .send = data_source_send_handler,
121 .cancelled = data_source_cancelled_handler
122};
123
124static void UwacRegisterDeviceListener(UwacSeat* s)
125{
126 wl_data_device_add_listener(s->data_device, &data_device_listener, s);
127}
128
129static UwacReturnCode UwacCreateDataSource(UwacSeat* s)
130{
131 if (!s)
132 return UWAC_ERROR_INTERNAL;
133
134 s->data_source = wl_data_device_manager_create_data_source(s->display->data_device_manager);
135 wl_data_source_add_listener(s->data_source, &data_source_listener, s);
136 return UWAC_SUCCESS;
137}
138
139UwacReturnCode UwacSeatRegisterClipboard(UwacSeat* s)
140{
141 UwacClipboardEvent* event = NULL;
142
143 if (!s)
144 return UWAC_ERROR_INTERNAL;
145
146 if (!s->display->data_device_manager || !s->data_device)
147 return UWAC_NOT_ENOUGH_RESOURCES;
148
149 UwacRegisterDeviceListener(s);
150
151 UwacReturnCode rc = UwacCreateDataSource(s);
152
153 if (rc != UWAC_SUCCESS)
154 return rc;
155 event = (UwacClipboardEvent*)UwacDisplayNewEvent(s->display, UWAC_EVENT_CLIPBOARD_AVAILABLE);
156
157 if (!event)
158 {
159 assert(uwacErrorHandler(s->display, UWAC_ERROR_INTERNAL,
160 "failed to allocate a clipboard event\n"));
161 return UWAC_ERROR_INTERNAL;
162 }
163
164 event->seat = s;
165 return UWAC_SUCCESS;
166}
167
168UwacReturnCode UwacClipboardOfferDestroy(UwacSeat* seat)
169{
170 if (!seat)
171 return UWAC_ERROR_INTERNAL;
172
173 if (seat->data_source)
174 wl_data_source_destroy(seat->data_source);
175
176 return UwacCreateDataSource(seat);
177}
178
179UwacReturnCode UwacClipboardOfferCreate(UwacSeat* seat, const char* mime)
180{
181 if (!seat || !mime)
182 return UWAC_ERROR_INTERNAL;
183
184 wl_data_source_offer(seat->data_source, mime);
185 return UWAC_SUCCESS;
186}
187
188static void callback_done(void* data, struct wl_callback* callback, uint32_t serial)
189{
190 *(uint32_t*)data = serial;
191}
192
193static const struct wl_callback_listener callback_listener = { .done = callback_done };
194
195static uint32_t get_serial(UwacSeat* s)
196{
197 struct wl_callback* callback = NULL;
198 uint32_t serial = 0;
199 callback = wl_display_sync(s->display->display);
200 wl_callback_add_listener(callback, &callback_listener, &serial);
201
202 while (serial == 0)
203 {
204 wl_display_dispatch(s->display->display);
205 }
206
207 return serial;
208}
209
210UwacReturnCode UwacClipboardOfferAnnounce(UwacSeat* seat, void* context,
211 UwacDataTransferHandler transfer,
212 UwacCancelDataTransferHandler cancel)
213{
214 if (!seat)
215 return UWAC_ERROR_INTERNAL;
216
217 seat->data_context = context;
218 seat->transfer_data = transfer;
219 seat->cancel_data = cancel;
220 seat->ignore_announcement = true;
221 wl_data_device_set_selection(seat->data_device, seat->data_source, get_serial(seat));
222 wl_display_roundtrip(seat->display->display);
223 seat->ignore_announcement = false;
224 return UWAC_SUCCESS;
225}
226
227void* UwacClipboardDataGet(UwacSeat* seat, const char* mime, size_t* size)
228{
229 ssize_t r = 0;
230 size_t alloc = 0;
231 size_t pos = 0;
232 char* data = NULL;
233 int pipefd[2] = { 0 };
234
235 if (!seat || !mime || !size || !seat->offer)
236 return NULL;
237
238 *size = 0;
239 if (pipe(pipefd) != 0)
240 return NULL;
241
242 wl_data_offer_receive(seat->offer, mime, pipefd[1]);
243 close(pipefd[1]);
244 wl_display_roundtrip(seat->display->display);
245 wl_display_flush(seat->display->display);
246
247 do
248 {
249 if (alloc >= SIZE_MAX - 1024)
250 goto fail;
251
252 alloc += 1024;
253 void* tmp = xrealloc(data, alloc);
254 if (!tmp)
255 goto fail;
256
257 data = tmp;
258
259 if (pos >= alloc)
260 goto fail;
261
262 r = read(pipefd[0], &data[pos], alloc - pos);
263 if (r > 0)
264 pos += r;
265 if (r < 0)
266 goto fail;
267 } while (r > 0);
268
269 close(pipefd[0]);
270
271 if (alloc > 0)
272 {
273 data[pos] = '\0';
274 *size = pos + 1;
275 }
276 return data;
277
278fail:
279 free(data);
280 close(pipefd[0]);
281 return NULL;
282}