]> Pileus Git - ~andy/gtk/blob - gdk/broadway/gdkbroadway-server.c
x11: Use GDK error trapping code
[~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   char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
508
509   return chars[c % sizeof (chars)];
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       name[o++] = 'b';
524       name[o++] = 'd';
525       name[o++] = 'w';
526       name[o++] = '-';
527       for (i = 0; i < 32/4 - 1; i++)
528         {
529           r = g_random_int ();
530           name[o++] = make_valid_fs_char ((r >> 0) & 0xff);
531           name[o++] = make_valid_fs_char ((r >> 8) & 0xff);
532           name[o++] = make_valid_fs_char ((r >> 16) & 0xff);
533           name[o++] = make_valid_fs_char ((r >> 24) & 0xff);
534         }
535       name[o++] = 0;
536   
537       fd = shm_open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
538       if (fd >= 0)
539         return fd;
540
541       if (errno != EEXIST)
542         {
543           g_printerr ("Unable to allocate shared mem for window");
544           exit (1);
545         }
546     }
547
548 }
549
550 static const cairo_user_data_key_t gdk_broadway_shm_cairo_key;
551
552 typedef struct {
553   char name[34];
554   void *data;
555   gsize data_size;
556 } BroadwayShmSurfaceData;
557
558 static void
559 shm_data_destroy (void *_data)
560 {
561   BroadwayShmSurfaceData *data = _data;
562
563   munmap (data->data, data->data_size);
564   shm_unlink (data->name);
565   g_free (data);
566 }
567
568 cairo_surface_t *
569 _gdk_broadway_server_create_surface (int                 width,
570                                      int                 height)
571 {
572   BroadwayShmSurfaceData *data;
573   cairo_surface_t *surface;
574   int res;
575   int fd;
576
577   data = g_new (BroadwayShmSurfaceData, 1);
578   data->data_size = width * height * sizeof (guint32);
579   
580   fd = create_random_shm (data->name);
581
582   res = ftruncate (fd, data->data_size);
583   g_assert (res != -1);
584
585   data->data = mmap(0, data->data_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 
586   (void) close(fd);
587
588   surface = cairo_image_surface_create_for_data ((guchar *)data->data,
589                                                  CAIRO_FORMAT_RGB24, width, height, width * sizeof (guint32));
590   g_assert (surface != NULL);
591   
592   cairo_surface_set_user_data (surface, &gdk_broadway_shm_cairo_key,
593                                data, shm_data_destroy);
594
595   return surface;
596 }
597
598 void
599 _gdk_broadway_server_window_update (GdkBroadwayServer *server,
600                                     gint id,
601                                     cairo_surface_t *surface)
602 {
603   BroadwayRequestUpdate msg;
604   BroadwayShmSurfaceData *data;
605
606   if (surface == NULL)
607     return;
608
609   data = cairo_surface_get_user_data (surface, &gdk_broadway_shm_cairo_key);
610   g_assert (data != NULL);
611
612   msg.id = id;
613   memcpy (msg.name, data->name, 34);
614   msg.width = cairo_image_surface_get_width (surface);
615   msg.height = cairo_image_surface_get_height (surface);
616
617   gdk_broadway_server_send_message (server, msg,
618                                     BROADWAY_REQUEST_UPDATE);
619 }
620
621 gboolean
622 _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
623                                          gint id,
624                                          gboolean with_move,
625                                          int x,
626                                          int y,
627                                          int width,
628                                          int height)
629 {
630   BroadwayRequestMoveResize msg;
631
632   msg.id = id;
633   msg.with_move = with_move;
634   msg.x = x;
635   msg.y = y;
636   msg.width = width;
637   msg.height = height;
638
639   gdk_broadway_server_send_message (server, msg,
640                                     BROADWAY_REQUEST_MOVE_RESIZE);
641
642   return TRUE;
643 }
644
645 GdkGrabStatus
646 _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server,
647                                    gint id,
648                                    gboolean owner_events,
649                                    guint32 event_mask,
650                                    guint32 time_)
651 {
652   BroadwayRequestGrabPointer msg;
653   guint32 serial, status;
654   BroadwayReply *reply;
655
656   msg.id = id;
657   msg.owner_events = owner_events;
658   msg.event_mask = event_mask;
659   msg.time_ = time_;
660
661   serial = gdk_broadway_server_send_message (server, msg,
662                                              BROADWAY_REQUEST_GRAB_POINTER);
663   reply = gdk_broadway_server_wait_for_reply (server, serial);
664
665   g_assert (reply->base.type == BROADWAY_REPLY_GRAB_POINTER);
666
667   status = reply->grab_pointer.status;
668
669   g_free (reply);
670
671   return status;
672 }
673
674 guint32
675 _gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server,
676                                      guint32    time_)
677 {
678   BroadwayRequestUngrabPointer msg;
679   guint32 serial, status;
680   BroadwayReply *reply;
681
682   msg.time_ = time_;
683
684   serial = gdk_broadway_server_send_message (server, msg,
685                                              BROADWAY_REQUEST_UNGRAB_POINTER);
686   reply = gdk_broadway_server_wait_for_reply (server, serial);
687
688   g_assert (reply->base.type == BROADWAY_REPLY_UNGRAB_POINTER);
689
690   status = reply->ungrab_pointer.status;
691
692   g_free (reply);
693
694   return status;
695 }