]> Pileus Git - ~andy/gtk/blob - gdk/broadway/gdkbroadway-server.c
broadway: Fix gdk_broadway_server_wait_for_reply
[~andy/gtk] / gdk / broadway / gdkbroadway-server.c
1 #include <sys/mman.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4
5 #include "gdkbroadway-server.h"
6
7 #include "gdkprivate-broadway.h"
8
9 #include <glib.h>
10 #include <glib/gprintf.h>
11 #include <gio/gunixsocketaddress.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <netinet/tcp.h>
20
21 typedef struct BroadwayInput BroadwayInput;
22
23 struct _GdkBroadwayServer {
24   GObject parent_instance;
25
26   guint32 next_serial;
27   GSocketConnection *connection;
28
29   guint32 recv_buffer_size;
30   guint8 recv_buffer[1024];
31
32   guint process_input_idle;
33   GList *incomming;
34   
35 };
36
37 struct _GdkBroadwayServerClass
38 {
39   GObjectClass parent_class;
40 };
41
42 static gboolean input_available_cb (gpointer stream, gpointer user_data);
43
44 G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT)
45
46 static void
47 gdk_broadway_server_init (GdkBroadwayServer *server)
48 {
49   server->next_serial = 1;
50 }
51
52 static void
53 gdk_broadway_server_finalize (GObject *object)
54 {
55   G_OBJECT_CLASS (gdk_broadway_server_parent_class)->finalize (object);
56 }
57
58 static void
59 gdk_broadway_server_class_init (GdkBroadwayServerClass * class)
60 {
61   GObjectClass *object_class = G_OBJECT_CLASS (class);
62
63   object_class->finalize = gdk_broadway_server_finalize;
64 }
65
66 gboolean
67 _gdk_broadway_server_lookahead_event (GdkBroadwayServer  *server,
68                                       const char         *types)
69 {
70
71   return FALSE;
72 }
73
74 gulong
75 _gdk_broadway_server_get_next_serial (GdkBroadwayServer *server)
76 {
77   return (gulong)server->next_serial;
78 }
79
80 GdkBroadwayServer *
81 _gdk_broadway_server_new (int port, GError **error)
82 {
83   GdkBroadwayServer *server;
84   char *basename;
85   GSocketClient *client;
86   GSocketConnection *connection;
87   GSocketAddress *address;
88   GPollableInputStream *pollable;
89   GInputStream *in;
90   GSource *source;
91   char *path;
92
93   basename = g_strdup_printf ("broadway%d.socket", port);
94   path = g_build_filename (g_get_user_runtime_dir (), basename, NULL);
95   g_free (basename);
96
97   address = g_unix_socket_address_new_with_type (path, -1,
98                                                  G_UNIX_SOCKET_ADDRESS_ABSTRACT);
99   g_free (path);
100
101   client = g_socket_client_new ();
102
103   error = NULL;
104   connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (address), NULL, error);
105   
106   g_object_unref (address);
107   g_object_unref (client);
108
109   if (connection == NULL)
110     return NULL;
111
112   server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL);
113   server->connection = connection;
114
115   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
116   pollable = G_POLLABLE_INPUT_STREAM (in);
117
118   source = g_pollable_input_stream_create_source (pollable, NULL);
119   g_source_attach (source, NULL);
120   g_source_set_callback (source, (GSourceFunc)input_available_cb, server, NULL);
121
122   return server;
123 }
124
125 guint32
126 _gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server)
127 {
128   return 0;
129 }
130
131 static guint32
132 gdk_broadway_server_send_message_with_size (GdkBroadwayServer *server, BroadwayRequestBase *base,
133                                             gsize size, guint32 type)
134 {
135   GOutputStream *out;
136   gsize written;
137
138   base->size = size;
139   base->type = type;
140   base->serial = server->next_serial++;
141
142   out = g_io_stream_get_output_stream (G_IO_STREAM (server->connection));
143
144   if (!g_output_stream_write_all (out, base, size, &written, NULL, NULL))
145     {
146       g_printerr ("Unable to write to server\n");
147       exit (1);
148     }
149
150   g_assert (written == size);
151
152   return base->serial;
153 }
154
155 #define gdk_broadway_server_send_message(_server, _msg, _type) \
156   gdk_broadway_server_send_message_with_size(_server, (BroadwayRequestBase *)&_msg, sizeof (_msg), _type)
157
158 static void
159 parse_all_input (GdkBroadwayServer *server)
160 {
161   guint8 *p, *end;
162   guint32 size;
163   BroadwayReply *reply;
164
165   p = server->recv_buffer;
166   end = p + server->recv_buffer_size;
167
168   while (p + sizeof (guint32) <= end)
169     {
170       memcpy (&size, p, sizeof (guint32));
171       if (p + size > end)
172         break;
173
174       reply = g_memdup (p, size);
175       p += size;
176
177       server->incomming = g_list_append (server->incomming, reply);
178     }
179   
180   if (p < end)
181     memmove (server->recv_buffer, p, end - p);
182   server->recv_buffer_size = end - p;
183 }
184
185 static void
186 read_some_input_blocking (GdkBroadwayServer *server)
187 {
188   GInputStream *in;
189   gssize res;
190
191   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
192
193   g_assert (server->recv_buffer_size < sizeof (server->recv_buffer));
194   res = g_input_stream_read (in, &server->recv_buffer[server->recv_buffer_size],
195                              sizeof (server->recv_buffer) - server->recv_buffer_size,
196                              NULL, NULL);
197
198   if (res <= 0)
199     {
200       g_printerr ("Unable to read from broadway server\n");
201       exit (1);
202     }
203
204   server->recv_buffer_size += res;
205 }
206
207 static void
208 read_some_input_nonblocking (GdkBroadwayServer *server)
209 {
210   GInputStream *in;
211   GPollableInputStream *pollable;
212   gssize res;
213   GError *error;
214
215   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
216   pollable = G_POLLABLE_INPUT_STREAM (in);
217
218   g_assert (server->recv_buffer_size < sizeof (server->recv_buffer));
219   error = NULL;
220   res = g_pollable_input_stream_read_nonblocking (pollable, &server->recv_buffer[server->recv_buffer_size],
221                                                   sizeof (server->recv_buffer) - server->recv_buffer_size,
222                                                   NULL, &error);
223
224   if (res < 0 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
225     {
226       g_error_free (error);
227       res = 0;
228     }
229   else if (res <= 0)
230     {
231       g_printerr ("Unable to read from broadway server: %s\n", error ? error->message : "eof");
232       exit (1);
233     }
234
235   server->recv_buffer_size += res;
236 }
237
238 static BroadwayReply *
239 find_response_by_serial (GdkBroadwayServer *server, guint32 serial)
240 {
241   GList *l;
242
243   for (l = server->incomming; l != NULL; l = l->next)
244     {
245       BroadwayReply *reply = l->data;
246
247       if (reply->base.in_reply_to == serial)
248         return reply;
249     }
250
251   return NULL;
252 }
253
254 static void
255 process_input_messages (GdkBroadwayServer *server)
256 {
257   BroadwayReply *reply;
258
259   if (server->process_input_idle != 0)
260     {
261       g_source_remove (server->process_input_idle);
262       server->process_input_idle = 0;
263     }
264   
265   while (server->incomming)
266     {
267       reply = server->incomming->data;
268       server->incomming =
269         g_list_delete_link (server->incomming,
270                             server->incomming);
271
272       if (reply->base.type == BROADWAY_REPLY_EVENT)
273         _gdk_broadway_events_got_input (&reply->event.msg);
274       else
275         g_warning ("Unhandled reply type %d\n", reply->base.type);
276       g_free (reply);
277     }
278 }
279
280 static gboolean
281 process_input_idle_cb (GdkBroadwayServer *server)
282 {
283   server->process_input_idle = 0;
284   process_input_messages (server);
285   return G_SOURCE_REMOVE;
286 }
287
288 static void
289 queue_process_input_at_idle (GdkBroadwayServer *server)
290 {
291   if (server->process_input_idle == 0)
292     server->process_input_idle =
293       g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL);
294 }
295
296 static gboolean
297 input_available_cb (gpointer stream, gpointer user_data)
298 {
299   GdkBroadwayServer *server = user_data;
300
301   read_some_input_nonblocking (server);
302   parse_all_input (server);
303
304   process_input_messages (server);
305
306   return G_SOURCE_CONTINUE;
307 }
308
309 static BroadwayReply *
310 gdk_broadway_server_wait_for_reply (GdkBroadwayServer *server,
311                                     guint32 serial)
312 {
313   BroadwayReply *reply;
314
315   while (TRUE)
316     {
317       reply = find_response_by_serial (server, serial);
318       if (reply)
319         {
320           server->incomming = g_list_remove (server->incomming, reply);
321           break;
322         }
323
324       read_some_input_blocking (server);
325       parse_all_input (server);
326     }
327
328   queue_process_input_at_idle (server);
329   return reply;
330 }
331
332 void
333 _gdk_broadway_server_flush (GdkBroadwayServer *server)
334 {
335   BroadwayRequestFlush msg;
336
337   gdk_broadway_server_send_message(server, msg, BROADWAY_REQUEST_FLUSH);
338 }
339
340 void
341 _gdk_broadway_server_sync (GdkBroadwayServer *server)
342 {
343   BroadwayRequestSync msg;
344   guint32 serial;
345   BroadwayReply *reply;
346
347   serial = gdk_broadway_server_send_message (server, msg,
348                                              BROADWAY_REQUEST_SYNC);
349   reply = gdk_broadway_server_wait_for_reply (server, serial);
350
351   g_assert (reply->base.type == BROADWAY_REPLY_SYNC);
352
353   g_free (reply);
354
355   return;
356 }
357
358 void
359 _gdk_broadway_server_query_mouse (GdkBroadwayServer *server,
360                                   guint32            *toplevel,
361                                   gint32             *root_x,
362                                   gint32             *root_y,
363                                   guint32            *mask)
364 {
365   BroadwayRequestQueryMouse msg;
366   guint32 serial;
367   BroadwayReply *reply;
368
369   serial = gdk_broadway_server_send_message (server, msg,
370                                              BROADWAY_REQUEST_QUERY_MOUSE);
371   reply = gdk_broadway_server_wait_for_reply (server, serial);
372
373   g_assert (reply->base.type == BROADWAY_REPLY_QUERY_MOUSE);
374
375   if (toplevel)
376     *toplevel = reply->query_mouse.toplevel;
377   if (root_x)
378     *root_x = reply->query_mouse.root_x;
379   if (root_y)
380     *root_y = reply->query_mouse.root_y;
381   if (mask)
382     *mask = reply->query_mouse.mask;
383   
384   g_free (reply);
385 }
386
387 guint32
388 _gdk_broadway_server_new_window (GdkBroadwayServer *server,
389                                  int x,
390                                  int y,
391                                  int width,
392                                  int height,
393                                  gboolean is_temp)
394 {
395   BroadwayRequestNewWindow msg;
396   guint32 serial, id;
397   BroadwayReply *reply;
398
399   msg.x = x;
400   msg.y = y;
401   msg.width = width;
402   msg.height = height;
403   msg.is_temp = is_temp;
404
405   serial = gdk_broadway_server_send_message (server, msg,
406                                              BROADWAY_REQUEST_NEW_WINDOW);
407   reply = gdk_broadway_server_wait_for_reply (server, serial);
408
409   g_assert (reply->base.type == BROADWAY_REPLY_NEW_WINDOW);
410
411   id = reply->new_window.id;
412   
413   g_free (reply);
414
415   return id;
416 }
417
418 void
419 _gdk_broadway_server_destroy_window (GdkBroadwayServer *server,
420                                      gint id)
421 {
422   BroadwayRequestDestroyWindow msg;
423
424   msg.id = id;
425   gdk_broadway_server_send_message (server, msg,
426                                     BROADWAY_REQUEST_DESTROY_WINDOW);
427 }
428
429 gboolean
430 _gdk_broadway_server_window_show (GdkBroadwayServer *server,
431                                   gint id)
432 {
433   BroadwayRequestShowWindow msg;
434
435   msg.id = id;
436   gdk_broadway_server_send_message (server, msg,
437                                     BROADWAY_REQUEST_SHOW_WINDOW);
438   
439   return TRUE;
440 }
441
442 gboolean
443 _gdk_broadway_server_window_hide (GdkBroadwayServer *server,
444                                   gint id)
445 {
446   BroadwayRequestHideWindow msg;
447
448   msg.id = id;
449   gdk_broadway_server_send_message (server, msg,
450                                     BROADWAY_REQUEST_HIDE_WINDOW);
451   
452   return TRUE;
453 }
454
455 void
456 _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server,
457                                                gint id, gint parent)
458 {
459   BroadwayRequestSetTransientFor msg;
460
461   msg.id = id;
462   msg.parent = parent;
463   gdk_broadway_server_send_message (server, msg,
464                                     BROADWAY_REQUEST_SET_TRANSIENT_FOR);
465 }
466
467 gboolean
468 _gdk_broadway_server_window_translate (GdkBroadwayServer *server,
469                                        gint id,
470                                        cairo_region_t *area,
471                                        gint            dx,
472                                        gint            dy)
473 {
474   BroadwayRequestTranslate *msg;
475   cairo_rectangle_int_t rect;
476   int i, n_rects;
477   gsize msg_size;
478
479   n_rects = cairo_region_num_rectangles (area);
480
481   msg_size = sizeof (BroadwayRequestTranslate) + (n_rects-1) * sizeof (BroadwayRect);
482   msg = g_malloc (msg_size);
483
484   msg->id = id;
485   msg->dx = dx;
486   msg->dy = dy;
487   msg->n_rects = n_rects;
488   
489   for (i = 0; i < n_rects; i++)
490     {
491       cairo_region_get_rectangle (area, i, &rect);
492       msg->rects[i].x = rect.x;
493       msg->rects[i].y = rect.y;
494       msg->rects[i].width = rect.width;
495       msg->rects[i].height = rect.height;
496     }
497
498   gdk_broadway_server_send_message_with_size (server, (BroadwayRequestBase *)msg, msg_size,
499                                               BROADWAY_REQUEST_TRANSLATE);
500   g_free (msg);
501   return TRUE;
502 }
503
504 static char
505 make_valid_fs_char (char c)
506 {
507   if (c == 0 || c == '/')
508     return c + 1;
509   return c;
510 }
511
512 /* name must have at least space for 34 bytes */
513 static int
514 create_random_shm (char *name)
515 {
516   guint32 r;
517   int i, o, fd;
518
519   while (TRUE)
520     {
521       o = 0;
522       name[o++] = '/';
523       for (i = 0; i < 32/4; i++)
524         {
525           r = g_random_int ();
526           name[o++] = make_valid_fs_char ((r >> 0) & 0xff);
527           name[o++] = make_valid_fs_char ((r >> 8) & 0xff);
528           name[o++] = make_valid_fs_char ((r >> 16) & 0xff);
529           name[o++] = make_valid_fs_char ((r >> 24) & 0xff);
530         }
531       name[o++] = 0;
532   
533       fd = shm_open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
534       if (fd >= 0)
535         return fd;
536
537       if (errno != EEXIST)
538         {
539           g_printerr ("Unable to allocate shared mem for window");
540           exit (1);
541         }
542     }
543
544 }
545
546 static const cairo_user_data_key_t gdk_broadway_shm_cairo_key;
547
548 typedef struct {
549   char name[34];
550   void *data;
551   gsize data_size;
552 } BroadwayShmSurfaceData;
553
554 static void
555 shm_data_destroy (void *_data)
556 {
557   BroadwayShmSurfaceData *data = _data;
558
559   munmap (data->data, data->data_size);
560   shm_unlink (data->name);
561   g_free (data);
562 }
563
564 cairo_surface_t *
565 _gdk_broadway_server_create_surface (int                 width,
566                                      int                 height)
567 {
568   BroadwayShmSurfaceData *data;
569   cairo_surface_t *surface;
570   int res;
571   int fd;
572
573   data = g_new (BroadwayShmSurfaceData, 1);
574   data->data_size = width * height * sizeof (guint32);
575   
576   fd = create_random_shm (data->name);
577
578   res = ftruncate (fd, data->data_size);
579   g_assert (res != -1);
580
581   data->data = mmap(0, data->data_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 
582   (void) close(fd);
583
584   surface = cairo_image_surface_create_for_data ((guchar *)data->data,
585                                                  CAIRO_FORMAT_RGB24, width, height, width * sizeof (guint32));
586   g_assert (surface != NULL);
587   
588   cairo_surface_set_user_data (surface, &gdk_broadway_shm_cairo_key,
589                                data, shm_data_destroy);
590
591   return surface;
592 }
593
594 void
595 _gdk_broadway_server_window_update (GdkBroadwayServer *server,
596                                     gint id,
597                                     cairo_surface_t *surface)
598 {
599   BroadwayRequestUpdate msg;
600   BroadwayShmSurfaceData *data;
601
602   if (surface == NULL)
603     return;
604
605   data = cairo_surface_get_user_data (surface, &gdk_broadway_shm_cairo_key);
606   g_assert (data != NULL);
607
608   msg.id = id;
609   memcpy (msg.name, data->name, 34);
610   msg.width = cairo_image_surface_get_width (surface);
611   msg.height = cairo_image_surface_get_height (surface);
612   
613   gdk_broadway_server_send_message (server, msg, 
614                                     BROADWAY_REQUEST_UPDATE);
615 }
616
617 gboolean
618 _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
619                                          gint id,
620                                          int x,
621                                          int y,
622                                          int width,
623                                          int height)
624 {
625   BroadwayRequestMoveResize msg;
626
627   msg.id = id;
628   msg.x = x;
629   msg.y = y;
630   msg.width = width;
631   msg.height = height;
632
633   gdk_broadway_server_send_message (server, msg,
634                                     BROADWAY_REQUEST_MOVE_RESIZE);
635
636   return TRUE;
637 }
638
639 GdkGrabStatus
640 _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server,
641                                    gint id,
642                                    gboolean owner_events,
643                                    guint32 event_mask,
644                                    guint32 time_)
645 {
646   BroadwayRequestGrabPointer msg;
647   guint32 serial, status;
648   BroadwayReply *reply;
649
650   msg.id = id;
651   msg.owner_events = owner_events;
652   msg.event_mask = event_mask;
653   msg.time_ = time_;
654
655   serial = gdk_broadway_server_send_message (server, msg,
656                                              BROADWAY_REQUEST_GRAB_POINTER);
657   reply = gdk_broadway_server_wait_for_reply (server, serial);
658
659   g_assert (reply->base.type == BROADWAY_REPLY_GRAB_POINTER);
660
661   status = reply->grab_pointer.status;
662   
663   g_free (reply);
664
665   return status;
666 }
667
668 guint32
669 _gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server,
670                                      guint32    time_)
671 {
672   BroadwayRequestUngrabPointer msg;
673   guint32 serial, status;
674   BroadwayReply *reply;
675
676   msg.time_ = time_;
677
678   serial = gdk_broadway_server_send_message (server, msg,
679                                              BROADWAY_REQUEST_UNGRAB_POINTER);
680   reply = gdk_broadway_server_wait_for_reply (server, serial);
681
682   g_assert (reply->base.type == BROADWAY_REPLY_UNGRAB_POINTER);
683
684   status = reply->ungrab_pointer.status;
685   
686   g_free (reply);
687
688   return status;
689 }