FreeRDP
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 */
38 static 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 
62 static const struct wl_data_offer_listener data_offer_listener = { .offer = data_offer_offer };
63 
64 static 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 
90 static void data_device_selection(void* data, struct wl_data_device* data_device,
91  struct wl_data_offer* data_offer)
92 {
93 }
94 
95 static const struct wl_data_device_listener data_device_listener = {
96  .data_offer = data_device_data_offer, .selection = data_device_selection
97 };
98 
99 /* copy */
100 static void data_source_target_handler(void* data, struct wl_data_source* data_source,
101  const char* mime_type)
102 {
103 }
104 
105 static 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 
112 static 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 
118 static 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 
124 static void UwacRegisterDeviceListener(UwacSeat* s)
125 {
126  wl_data_device_add_listener(s->data_device, &data_device_listener, s);
127 }
128 
129 static 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 
139 UwacReturnCode 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 
168 UwacReturnCode 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 
179 UwacReturnCode 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 
188 static void callback_done(void* data, struct wl_callback* callback, uint32_t serial)
189 {
190  *(uint32_t*)data = serial;
191 }
192 
193 static const struct wl_callback_listener callback_listener = { .done = callback_done };
194 
195 static 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 
210 UwacReturnCode 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 
227 void* 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 
278 fail:
279  free(data);
280  close(pipefd[0]);
281  return NULL;
282 }