1 #include "config.h"
2 
3 #ifdef HAVE_LINUX_MEMFD_H
4 #include <linux/memfd.h>
5 #endif
6 
7 #ifdef HAVE_SYS_MMAN_H
8 #include <sys/mman.h>
9 #endif
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 
13 #include "gdkbroadway-server.h"
14 
15 #include "gdkprivate-broadway.h"
16 #include "gdk-private.h"
17 
18 #include <gdk/gdktextureprivate.h>
19 
20 #include <glib.h>
21 #include <glib/gprintf.h>
22 #include <gio/gunixsocketaddress.h>
23 #include <gio/gunixfdmessage.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #elif defined (G_OS_WIN32)
30 #include <io.h>
31 #define ftruncate _chsize_s
32 #endif
33 #include <sys/types.h>
34 #ifdef G_OS_WIN32
35 #include <windows.h>
36 #endif
37 #include "gdkintl.h"
38 
39 typedef struct BroadwayInput BroadwayInput;
40 
41 struct _GdkBroadwayServer {
42   GObject parent_instance;
43   GdkDisplay *display;
44 
45   guint32 next_serial;
46   guint32 next_texture_id;
47   GSocketConnection *connection;
48 
49   guint32 recv_buffer_size;
50   guint8 recv_buffer[1024];
51 
52   guint process_input_idle;
53   GList *incoming;
54 };
55 
56 struct _GdkBroadwayServerClass
57 {
58   GObjectClass parent_class;
59 };
60 
61 static gboolean input_available_cb (gpointer stream, gpointer user_data);
62 
63 static GType gdk_broadway_server_get_type (void);
64 
G_DEFINE_TYPE(GdkBroadwayServer,gdk_broadway_server,G_TYPE_OBJECT)65 G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT)
66 
67 static void
68 gdk_broadway_server_init (GdkBroadwayServer *server)
69 {
70   server->next_serial = 1;
71   server->next_texture_id = 1;
72 }
73 
74 static void
gdk_broadway_server_finalize(GObject * object)75 gdk_broadway_server_finalize (GObject *object)
76 {
77   G_OBJECT_CLASS (gdk_broadway_server_parent_class)->finalize (object);
78 }
79 
80 static void
gdk_broadway_server_class_init(GdkBroadwayServerClass * class)81 gdk_broadway_server_class_init (GdkBroadwayServerClass * class)
82 {
83   GObjectClass *object_class = G_OBJECT_CLASS (class);
84 
85   object_class->finalize = gdk_broadway_server_finalize;
86 }
87 
88 gboolean
_gdk_broadway_server_lookahead_event(GdkBroadwayServer * server,const char * types)89 _gdk_broadway_server_lookahead_event (GdkBroadwayServer  *server,
90                                       const char         *types)
91 {
92   return FALSE;
93 }
94 
95 gulong
_gdk_broadway_server_get_next_serial(GdkBroadwayServer * server)96 _gdk_broadway_server_get_next_serial (GdkBroadwayServer *server)
97 {
98   return (gulong)server->next_serial;
99 }
100 
101 GdkBroadwayServer *
_gdk_broadway_server_new(GdkDisplay * display,const char * display_name,GError ** error)102 _gdk_broadway_server_new (GdkDisplay *display,
103                           const char *display_name,
104                           GError **error)
105 {
106   GdkBroadwayServer *server;
107   GSocketClient *client;
108   GSocketConnection *connection;
109   GSocketAddress *address;
110   GPollableInputStream *pollable;
111   GInputStream *in;
112   GSource *source;
113   char *local_socket_type = NULL;
114   int port;
115 
116   if (display_name == NULL)
117     display_name = ":0";
118 
119   if (display_name[0] == ':' && g_ascii_isdigit(display_name[1]))
120     {
121       char *path, *basename;
122 
123       port = strtol (display_name + strlen (":"), NULL, 10);
124       basename = g_strdup_printf ("broadway%d.socket", port + 1);
125       path = g_build_filename (g_get_user_runtime_dir (), basename, NULL);
126       g_free (basename);
127 
128       address = g_unix_socket_address_new_with_type (path, -1,
129                                                      G_UNIX_SOCKET_ADDRESS_PATH);
130       g_free (path);
131     }
132   else
133     {
134       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
135                    _("Broadway display type not supported: %s"), display_name);
136       return NULL;
137     }
138 
139   g_free (local_socket_type);
140 
141   client = g_socket_client_new ();
142 
143   connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (address), NULL, error);
144 
145   g_object_unref (address);
146   g_object_unref (client);
147 
148   if (connection == NULL)
149     return NULL;
150 
151   server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL);
152   server->connection = connection;
153   server->display = display;
154 
155   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
156   pollable = G_POLLABLE_INPUT_STREAM (in);
157 
158   source = g_pollable_input_stream_create_source (pollable, NULL);
159   g_source_attach (source, NULL);
160   g_source_set_callback (source, (GSourceFunc)input_available_cb, server, NULL);
161 
162   return server;
163 }
164 
165 static guint32
gdk_broadway_server_send_message_with_size(GdkBroadwayServer * server,BroadwayRequestBase * base,gsize size,guint32 type,int fd)166 gdk_broadway_server_send_message_with_size (GdkBroadwayServer *server, BroadwayRequestBase *base,
167                                             gsize size, guint32 type, int fd)
168 {
169   GOutputStream *out;
170   gsize written;
171   guchar *buf;
172 
173   base->size = size;
174   base->type = type;
175   base->serial = server->next_serial++;
176 
177   buf = (guchar *)base;
178 
179   if (fd != -1)
180     {
181       GUnixFDList *fd_list = g_unix_fd_list_new_from_array (&fd, 1);
182       GSocketControlMessage *control_message = g_unix_fd_message_new_with_fd_list (fd_list);
183       GSocket *socket = g_socket_connection_get_socket (server->connection);
184       GOutputVector vector;
185       gssize bytes_written;
186 
187       vector.buffer = buf;
188       vector.size = size;
189 
190       bytes_written = g_socket_send_message (socket,
191                                              NULL, /* address */
192                                              &vector,
193                                              1,
194                                              &control_message, 1,
195                                              G_SOCKET_MSG_NONE,
196                                              NULL,
197                                              NULL);
198 
199       if (bytes_written <= 0)
200         {
201           g_printerr ("Unable to write to server\n");
202           exit (1);
203         }
204 
205       buf += bytes_written;
206       size -= bytes_written;
207 
208       g_object_unref (control_message);
209       g_object_unref (fd_list);
210     }
211 
212   if (size > 0)
213     {
214       out = g_io_stream_get_output_stream (G_IO_STREAM (server->connection));
215       if (!g_output_stream_write_all (out, buf, size, &written, NULL, NULL))
216         {
217           g_printerr ("Unable to write to server\n");
218           exit (1);
219         }
220 
221       g_assert (written == size);
222     }
223 
224 
225   return base->serial;
226 }
227 
228 #define gdk_broadway_server_send_message(_server, _msg, _type) \
229   gdk_broadway_server_send_message_with_size(_server, (BroadwayRequestBase *)&_msg, sizeof (_msg), _type, -1)
230 
231 #define gdk_broadway_server_send_fd_message(_server, _msg, _type, _fd)      \
232   gdk_broadway_server_send_message_with_size(_server, (BroadwayRequestBase *)&_msg, sizeof (_msg), _type, _fd)
233 
234 static void
parse_all_input(GdkBroadwayServer * server)235 parse_all_input (GdkBroadwayServer *server)
236 {
237   guint8 *p, *end;
238   guint32 size;
239   BroadwayReply *reply;
240 
241   p = server->recv_buffer;
242   end = p + server->recv_buffer_size;
243 
244   while (p + sizeof (guint32) <= end)
245     {
246       memcpy (&size, p, sizeof (guint32));
247       if (p + size > end)
248         break;
249 
250       reply = g_memdup2 (p, size);
251       p += size;
252 
253       server->incoming = g_list_append (server->incoming, reply);
254     }
255 
256   if (p < end)
257     memmove (server->recv_buffer, p, end - p);
258   server->recv_buffer_size = end - p;
259 }
260 
261 static void
read_some_input_blocking(GdkBroadwayServer * server)262 read_some_input_blocking (GdkBroadwayServer *server)
263 {
264   GInputStream *in;
265   gssize res;
266 
267   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
268 
269   g_assert (server->recv_buffer_size < sizeof (server->recv_buffer));
270   res = g_input_stream_read (in, &server->recv_buffer[server->recv_buffer_size],
271                              sizeof (server->recv_buffer) - server->recv_buffer_size,
272                              NULL, NULL);
273 
274   if (res <= 0)
275     {
276       g_printerr ("Unable to read from broadway server\n");
277       exit (1);
278     }
279 
280   server->recv_buffer_size += res;
281 }
282 
283 static void
read_some_input_nonblocking(GdkBroadwayServer * server)284 read_some_input_nonblocking (GdkBroadwayServer *server)
285 {
286   GInputStream *in;
287   GPollableInputStream *pollable;
288   gssize res;
289   GError *error;
290 
291   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
292   pollable = G_POLLABLE_INPUT_STREAM (in);
293 
294   g_assert (server->recv_buffer_size < sizeof (server->recv_buffer));
295   error = NULL;
296   res = g_pollable_input_stream_read_nonblocking (pollable, &server->recv_buffer[server->recv_buffer_size],
297                                                   sizeof (server->recv_buffer) - server->recv_buffer_size,
298                                                   NULL, &error);
299 
300   if (res < 0 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
301     {
302       g_error_free (error);
303       res = 0;
304     }
305   else if (res <= 0)
306     {
307       g_printerr ("Unable to read from broadway server: %s\n", error ? error->message : "eof");
308       exit (1);
309     }
310 
311   server->recv_buffer_size += res;
312 }
313 
314 static BroadwayReply *
find_response_by_serial(GdkBroadwayServer * server,guint32 serial)315 find_response_by_serial (GdkBroadwayServer *server, guint32 serial)
316 {
317   GList *l;
318 
319   for (l = server->incoming; l != NULL; l = l->next)
320     {
321       BroadwayReply *reply = l->data;
322 
323       if (reply->base.in_reply_to == serial)
324         return reply;
325     }
326 
327   return NULL;
328 }
329 
330 static void
process_input_messages(GdkBroadwayServer * server)331 process_input_messages (GdkBroadwayServer *server)
332 {
333   BroadwayReply *reply;
334 
335   if (server->process_input_idle != 0)
336     {
337       g_source_remove (server->process_input_idle);
338       server->process_input_idle = 0;
339     }
340 
341   while (server->incoming)
342     {
343       reply = server->incoming->data;
344       server->incoming =
345         g_list_delete_link (server->incoming,
346                             server->incoming);
347 
348       if (reply->base.type == BROADWAY_REPLY_EVENT)
349         _gdk_broadway_events_got_input (server->display, &reply->event.msg);
350       else
351         g_warning ("Unhandled reply type %d", reply->base.type);
352       g_free (reply);
353     }
354 }
355 
356 static gboolean
process_input_idle_cb(GdkBroadwayServer * server)357 process_input_idle_cb (GdkBroadwayServer *server)
358 {
359   server->process_input_idle = 0;
360   process_input_messages (server);
361   return G_SOURCE_REMOVE;
362 }
363 
364 static void
queue_process_input_at_idle(GdkBroadwayServer * server)365 queue_process_input_at_idle (GdkBroadwayServer *server)
366 {
367   if (server->process_input_idle == 0)
368     server->process_input_idle =
369       g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL);
370 }
371 
372 static gboolean
input_available_cb(gpointer stream,gpointer user_data)373 input_available_cb (gpointer stream, gpointer user_data)
374 {
375   GdkBroadwayServer *server = user_data;
376 
377   read_some_input_nonblocking (server);
378   parse_all_input (server);
379 
380   process_input_messages (server);
381 
382   return G_SOURCE_CONTINUE;
383 }
384 
385 static BroadwayReply *
gdk_broadway_server_wait_for_reply(GdkBroadwayServer * server,guint32 serial)386 gdk_broadway_server_wait_for_reply (GdkBroadwayServer *server,
387                                     guint32 serial)
388 {
389   BroadwayReply *reply;
390 
391   while (TRUE)
392     {
393       reply = find_response_by_serial (server, serial);
394       if (reply)
395         {
396           server->incoming = g_list_remove (server->incoming, reply);
397           break;
398         }
399 
400       read_some_input_blocking (server);
401       parse_all_input (server);
402     }
403 
404   queue_process_input_at_idle (server);
405   return reply;
406 }
407 
408 void
_gdk_broadway_server_flush(GdkBroadwayServer * server)409 _gdk_broadway_server_flush (GdkBroadwayServer *server)
410 {
411   BroadwayRequestFlush msg;
412 
413   gdk_broadway_server_send_message (server, msg, BROADWAY_REQUEST_FLUSH);
414 }
415 
416 void
_gdk_broadway_server_sync(GdkBroadwayServer * server)417 _gdk_broadway_server_sync (GdkBroadwayServer *server)
418 {
419   BroadwayRequestSync msg;
420   guint32 serial;
421   BroadwayReply *reply;
422 
423   serial = gdk_broadway_server_send_message (server, msg,
424                                              BROADWAY_REQUEST_SYNC);
425   reply = gdk_broadway_server_wait_for_reply (server, serial);
426 
427   g_assert (reply->base.type == BROADWAY_REPLY_SYNC);
428 
429   g_free (reply);
430 
431   return;
432 }
433 
434 void
_gdk_broadway_server_roundtrip(GdkBroadwayServer * server,gint32 id,guint32 tag)435 _gdk_broadway_server_roundtrip (GdkBroadwayServer *server,
436                                 gint32            id,
437                                 guint32           tag)
438 {
439   BroadwayRequestRoundtrip msg;
440 
441   msg.id = id;
442   msg.tag = tag;
443   gdk_broadway_server_send_message (server, msg,
444                                     BROADWAY_REQUEST_ROUNDTRIP);
445 }
446 
447 void
_gdk_broadway_server_query_mouse(GdkBroadwayServer * server,guint32 * surface,gint32 * root_x,gint32 * root_y,guint32 * mask)448 _gdk_broadway_server_query_mouse (GdkBroadwayServer *server,
449                                   guint32            *surface,
450                                   gint32             *root_x,
451                                   gint32             *root_y,
452                                   guint32            *mask)
453 {
454   BroadwayRequestQueryMouse msg;
455   guint32 serial;
456   BroadwayReply *reply;
457 
458   serial = gdk_broadway_server_send_message (server, msg,
459                                              BROADWAY_REQUEST_QUERY_MOUSE);
460   reply = gdk_broadway_server_wait_for_reply (server, serial);
461 
462   g_assert (reply->base.type == BROADWAY_REPLY_QUERY_MOUSE);
463 
464   if (surface)
465     *surface = reply->query_mouse.surface;
466   if (root_x)
467     *root_x = reply->query_mouse.root_x;
468   if (root_y)
469     *root_y = reply->query_mouse.root_y;
470   if (mask)
471     *mask = reply->query_mouse.mask;
472 
473   g_free (reply);
474 }
475 
476 guint32
_gdk_broadway_server_new_surface(GdkBroadwayServer * server,int x,int y,int width,int height)477 _gdk_broadway_server_new_surface (GdkBroadwayServer *server,
478                                  int x,
479                                  int y,
480                                  int width,
481                                  int height)
482 {
483   BroadwayRequestNewSurface msg;
484   guint32 serial, id;
485   BroadwayReply *reply;
486 
487   msg.x = x;
488   msg.y = y;
489   msg.width = width;
490   msg.height = height;
491   serial = gdk_broadway_server_send_message (server, msg,
492                                              BROADWAY_REQUEST_NEW_SURFACE);
493   reply = gdk_broadway_server_wait_for_reply (server, serial);
494 
495   g_assert (reply->base.type == BROADWAY_REPLY_NEW_SURFACE);
496 
497   id = reply->new_surface.id;
498 
499   g_free (reply);
500 
501   return id;
502 }
503 
504 void
_gdk_broadway_server_destroy_surface(GdkBroadwayServer * server,int id)505 _gdk_broadway_server_destroy_surface (GdkBroadwayServer *server,
506                                      int id)
507 {
508   BroadwayRequestDestroySurface msg;
509 
510   msg.id = id;
511   gdk_broadway_server_send_message (server, msg,
512                                     BROADWAY_REQUEST_DESTROY_SURFACE);
513 }
514 
515 gboolean
_gdk_broadway_server_surface_show(GdkBroadwayServer * server,int id)516 _gdk_broadway_server_surface_show (GdkBroadwayServer *server,
517                                   int id)
518 {
519   BroadwayRequestShowSurface msg;
520 
521   msg.id = id;
522   gdk_broadway_server_send_message (server, msg,
523                                     BROADWAY_REQUEST_SHOW_SURFACE);
524 
525   return TRUE;
526 }
527 
528 gboolean
_gdk_broadway_server_surface_hide(GdkBroadwayServer * server,int id)529 _gdk_broadway_server_surface_hide (GdkBroadwayServer *server,
530                                   int id)
531 {
532   BroadwayRequestHideSurface msg;
533 
534   msg.id = id;
535   gdk_broadway_server_send_message (server, msg,
536                                     BROADWAY_REQUEST_HIDE_SURFACE);
537 
538   return TRUE;
539 }
540 
541 void
_gdk_broadway_server_surface_focus(GdkBroadwayServer * server,int id)542 _gdk_broadway_server_surface_focus (GdkBroadwayServer *server,
543                                    int id)
544 {
545   BroadwayRequestFocusSurface msg;
546 
547   msg.id = id;
548   gdk_broadway_server_send_message (server, msg,
549                                     BROADWAY_REQUEST_FOCUS_SURFACE);
550 }
551 
552 void
_gdk_broadway_server_surface_set_transient_for(GdkBroadwayServer * server,int id,int parent)553 _gdk_broadway_server_surface_set_transient_for (GdkBroadwayServer *server,
554                                                int id, int parent)
555 {
556   BroadwayRequestSetTransientFor msg;
557 
558   msg.id = id;
559   msg.parent = parent;
560   gdk_broadway_server_send_message (server, msg,
561                                     BROADWAY_REQUEST_SET_TRANSIENT_FOR);
562 }
563 
564 static int
open_shared_memory(void)565 open_shared_memory (void)
566 {
567   static gboolean force_shm_open = FALSE;
568   int ret = -1;
569 
570 #if !defined (__NR_memfd_create)
571   force_shm_open = TRUE;
572 #endif
573 
574   do
575     {
576 #if defined (__NR_memfd_create)
577       if (!force_shm_open)
578         {
579           ret = syscall (__NR_memfd_create, "gdk-broadway", MFD_CLOEXEC);
580 
581           /* fall back to shm_open until debian stops shipping 3.16 kernel
582            * See bug 766341
583            */
584           if (ret < 0 && errno == ENOSYS)
585             force_shm_open = TRUE;
586         }
587 #endif
588 
589       if (force_shm_open)
590         {
591           char name[NAME_MAX - 1] = "";
592 
593           sprintf (name, "/gdk-broadway-%x", g_random_int ());
594 
595           ret = shm_open (name, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
596 
597           if (ret >= 0)
598             shm_unlink (name);
599           else if (errno == EEXIST)
600             continue;
601         }
602     }
603   while (ret < 0 && errno == EINTR);
604 
605   if (ret < 0)
606     g_critical (G_STRLOC ": creating shared memory file (using %s) failed: %m",
607                 force_shm_open? "shm_open" : "memfd_create");
608 
609   return ret;
610 }
611 
612 typedef struct {
613   int fd;
614   gsize size;
615 } PngData;
616 
617 static cairo_status_t
write_png_cb(void * closure,const guchar * data,unsigned int length)618 write_png_cb (void         *closure,
619               const guchar *data,
620               unsigned int  length)
621 {
622   PngData *png_data = closure;
623   int fd = png_data->fd;
624 
625   while (length)
626     {
627       gssize ret = write (fd, data, length);
628 
629       if (ret <= 0)
630         return CAIRO_STATUS_WRITE_ERROR;
631 
632       png_data->size += ret;
633       length -= ret;
634       data += ret;
635     }
636 
637   return CAIRO_STATUS_SUCCESS;
638 }
639 
640 guint32
gdk_broadway_server_upload_texture(GdkBroadwayServer * server,GdkTexture * texture)641 gdk_broadway_server_upload_texture (GdkBroadwayServer *server,
642                                     GdkTexture        *texture)
643 {
644   guint32 id;
645   cairo_surface_t *surface = gdk_texture_download_surface (texture);
646   BroadwayRequestUploadTexture msg;
647   PngData data;
648 
649   id = server->next_texture_id++;
650 
651   data.fd = open_shared_memory ();
652   data.size = 0;
653   cairo_surface_write_to_png_stream (surface, write_png_cb, &data);
654 
655   msg.id = id;
656   msg.offset = 0;
657   msg.size = data.size;
658 
659   /* This passes ownership of fd */
660   gdk_broadway_server_send_fd_message (server, msg,
661                                        BROADWAY_REQUEST_UPLOAD_TEXTURE, data.fd);
662 
663   return id;
664 }
665 
666 
667 void
gdk_broadway_server_release_texture(GdkBroadwayServer * server,guint32 id)668 gdk_broadway_server_release_texture (GdkBroadwayServer *server,
669                                      guint32            id)
670 {
671   BroadwayRequestReleaseTexture msg;
672 
673   msg.id = id;
674 
675   gdk_broadway_server_send_message (server, msg,
676                                     BROADWAY_REQUEST_RELEASE_TEXTURE);
677 }
678 
679 void
gdk_broadway_server_surface_set_nodes(GdkBroadwayServer * server,guint32 id,GArray * nodes)680 gdk_broadway_server_surface_set_nodes (GdkBroadwayServer *server,
681                                       guint32 id,
682                                       GArray *nodes)
683 {
684   gsize size = sizeof(BroadwayRequestSetNodes) + sizeof(guint32) * (nodes->len - 1);
685   BroadwayRequestSetNodes *msg = g_alloca (size);
686   int i;
687 
688   for (i = 0; i < nodes->len; i++)
689     msg->data[i] = g_array_index (nodes, guint32, i);
690 
691   msg->id = id;
692   gdk_broadway_server_send_message_with_size (server, (BroadwayRequestBase *) msg, size, BROADWAY_REQUEST_SET_NODES, -1);
693 }
694 
695 gboolean
_gdk_broadway_server_surface_move_resize(GdkBroadwayServer * server,int id,gboolean with_move,int x,int y,int width,int height)696 _gdk_broadway_server_surface_move_resize (GdkBroadwayServer *server,
697                                          int id,
698                                          gboolean with_move,
699                                          int x,
700                                          int y,
701                                          int width,
702                                          int height)
703 {
704   BroadwayRequestMoveResize msg;
705 
706   msg.id = id;
707   msg.with_move = with_move;
708   msg.x = x;
709   msg.y = y;
710   msg.width = width;
711   msg.height = height;
712 
713   gdk_broadway_server_send_message (server, msg,
714                                     BROADWAY_REQUEST_MOVE_RESIZE);
715 
716   return TRUE;
717 }
718 
719 GdkGrabStatus
_gdk_broadway_server_grab_pointer(GdkBroadwayServer * server,int id,gboolean owner_events,guint32 event_mask,guint32 time_)720 _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server,
721                                    int id,
722                                    gboolean owner_events,
723                                    guint32 event_mask,
724                                    guint32 time_)
725 {
726   BroadwayRequestGrabPointer msg;
727   guint32 serial, status;
728   BroadwayReply *reply;
729 
730   msg.id = id;
731   msg.owner_events = owner_events;
732   msg.event_mask = event_mask;
733   msg.time_ = time_;
734 
735   serial = gdk_broadway_server_send_message (server, msg,
736                                              BROADWAY_REQUEST_GRAB_POINTER);
737   reply = gdk_broadway_server_wait_for_reply (server, serial);
738 
739   g_assert (reply->base.type == BROADWAY_REPLY_GRAB_POINTER);
740 
741   status = reply->grab_pointer.status;
742 
743   g_free (reply);
744 
745   return status;
746 }
747 
748 guint32
_gdk_broadway_server_ungrab_pointer(GdkBroadwayServer * server,guint32 time_)749 _gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server,
750                                      guint32    time_)
751 {
752   BroadwayRequestUngrabPointer msg;
753   guint32 serial, status;
754   BroadwayReply *reply;
755 
756   msg.time_ = time_;
757 
758   serial = gdk_broadway_server_send_message (server, msg,
759                                              BROADWAY_REQUEST_UNGRAB_POINTER);
760   reply = gdk_broadway_server_wait_for_reply (server, serial);
761 
762   g_assert (reply->base.type == BROADWAY_REPLY_UNGRAB_POINTER);
763 
764   status = reply->ungrab_pointer.status;
765 
766   g_free (reply);
767 
768   return status;
769 }
770 
771 void
_gdk_broadway_server_set_show_keyboard(GdkBroadwayServer * server,gboolean show)772 _gdk_broadway_server_set_show_keyboard (GdkBroadwayServer *server,
773                                         gboolean show)
774 {
775   BroadwayRequestSetShowKeyboard msg;
776 
777   msg.show_keyboard = show;
778   gdk_broadway_server_send_message (server, msg,
779                                     BROADWAY_REQUEST_SET_SHOW_KEYBOARD);
780 }
781