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