]> Pileus Git - ~andy/gtk/blob - gdk/broadway/broadway-server.c
broadway: Initial version of separate broadway server
[~andy/gtk] / gdk / broadway / broadway-server.c
1 #include <string.h>
2 #include <sys/mman.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5
6 #include <glib.h>
7 #include <gio/gio.h>
8 #include <gio/gunixsocketaddress.h>
9
10 #include "gdkbroadway-server.h"
11
12 GdkBroadwayServer *server;
13 GList *clients;
14
15 static guint32 client_id_count = 1;
16
17 typedef struct  {
18   guint32 id;
19   GSocketConnection *connection;
20   GBufferedInputStream *in;
21   guint32 last_seen_serial;
22   GList *windows;
23 } BroadwayClient;
24
25 static void
26 client_free (BroadwayClient *client)
27 {
28   clients = g_list_remove (clients, client);
29   g_object_unref (client->connection);
30   g_object_unref (client->in);
31   g_free (client);
32 }
33
34 static void
35 client_disconnected (BroadwayClient *client)
36 {
37   g_print ("client %d disconnected\n", client->id);
38   /* TODO: destroy client windows, also maybe do this in an idle, at least in some cases like on an i/o error */
39   client_free (client);
40 }
41
42 static void
43 send_reply (BroadwayClient *client,
44             BroadwayRequest *request,
45             BroadwayReply *reply,
46             gsize size,
47             guint32 type)
48 {
49   GOutputStream *output;
50
51   reply->base.size = size;
52   reply->base.last_serial = client->last_seen_serial;
53   reply->base.in_reply_to = request ? request->base.serial : 0;
54   reply->base.type = type;
55
56   output = g_io_stream_get_output_stream (G_IO_STREAM (client->connection));
57   if (!g_output_stream_write_all (output, reply, size, NULL, NULL, NULL))
58     {
59       g_printerr ("can't write to client");
60       client_disconnected (client);
61       /* TODO: Make sure we don't access client after this */
62     }
63 }
64
65 static cairo_region_t *
66 region_from_rects (BroadwayRect *rects, int n_rects)
67 {
68   cairo_region_t *region;
69   cairo_rectangle_int_t *cairo_rects;
70   int i;
71   
72   cairo_rects = g_new (cairo_rectangle_int_t, n_rects);
73   for (i = 0; i < n_rects; i++)
74     {
75       cairo_rects[i].x = rects[i].x;
76       cairo_rects[i].y = rects[i].y;
77       cairo_rects[i].width = rects[i].width;
78       cairo_rects[i].height = rects[i].height;
79     }
80   region = cairo_region_create_rectangles (cairo_rects, n_rects);
81   g_free (cairo_rects);
82   return region;
83 }
84
85 static const cairo_user_data_key_t shm_cairo_key;
86
87 typedef struct {
88   void *data;
89   gsize data_size;
90 } ShmSurfaceData;
91
92 static void
93 shm_data_unmap (void *_data)
94 {
95   ShmSurfaceData *data = _data;
96   munmap (data->data, data->data_size);
97   g_free (data);
98 }
99
100 cairo_surface_t *
101 open_surface (char *name, int width, int height)
102 {
103   ShmSurfaceData *data;
104   cairo_surface_t *surface;
105   gsize size;
106   void *ptr;
107   int fd;
108
109   /* TODO: Cache this */
110   
111   size = width * height * sizeof (guint32);
112
113   fd = shm_open(name, O_RDONLY, 0600);
114   if (fd == -1)
115     {
116       perror ("Failed to shm_open");
117       return NULL;
118     }
119
120   ptr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); 
121   (void) close(fd);
122
123   if (ptr == NULL)
124     return NULL;
125
126   data = g_new0 (ShmSurfaceData, 1);
127
128   data->data = ptr;
129   data->data_size = size;
130
131   surface = cairo_image_surface_create_for_data ((guchar *)data->data,
132                                                  CAIRO_FORMAT_RGB24,
133                                                  width, height,
134                                                  width * sizeof (guint32));
135   g_assert (surface != NULL);
136   
137   cairo_surface_set_user_data (surface, &shm_cairo_key,
138                                data, shm_data_unmap);
139
140   return surface;
141 }
142
143 static void
144 client_handle_request (BroadwayClient *client,
145                        BroadwayRequest *request)
146 {
147   BroadwayReplyNewWindow reply_new_window;
148   BroadwayReplySync reply_sync;
149   BroadwayReplyQueryMouse reply_query_mouse;
150   BroadwayReplyGrabPointer reply_grab_pointer;
151   BroadwayReplyUngrabPointer reply_ungrab_pointer;
152   cairo_region_t *area;
153   cairo_surface_t *surface;
154
155   client->last_seen_serial = request->base.serial;
156
157   switch (request->base.type)
158     {
159     case BROADWAY_REQUEST_NEW_WINDOW:
160       reply_new_window.id =
161         _gdk_broadway_server_new_window (server,
162                                          request->new_window.x,
163                                          request->new_window.y,
164                                          request->new_window.width,
165                                          request->new_window.height,
166                                          request->new_window.is_temp);
167       
168       send_reply (client, request, (BroadwayReply *)&reply_new_window, sizeof (reply_new_window),
169                   BROADWAY_REPLY_NEW_WINDOW);
170       break;
171     case BROADWAY_REQUEST_FLUSH:
172       _gdk_broadway_server_flush (server);
173       break;
174     case BROADWAY_REQUEST_SYNC:
175       _gdk_broadway_server_flush (server);
176       send_reply (client, request, (BroadwayReply *)&reply_sync, sizeof (reply_sync),
177                   BROADWAY_REPLY_SYNC);
178       break;
179     case BROADWAY_REQUEST_QUERY_MOUSE:
180       _gdk_broadway_server_query_mouse (server,
181                                         &reply_query_mouse.toplevel,
182                                         &reply_query_mouse.root_x,
183                                         &reply_query_mouse.root_y,
184                                         &reply_query_mouse.mask);
185       send_reply (client, request, (BroadwayReply *)&reply_query_mouse, sizeof (reply_query_mouse),
186                   BROADWAY_REPLY_QUERY_MOUSE);
187       break;
188     case BROADWAY_REQUEST_DESTROY_WINDOW:
189       _gdk_broadway_server_destroy_window (server, request->destroy_window.id);
190       break;
191     case BROADWAY_REQUEST_SHOW_WINDOW:
192       _gdk_broadway_server_window_show (server, request->show_window.id);
193       break;
194     case BROADWAY_REQUEST_HIDE_WINDOW:
195       _gdk_broadway_server_window_hide (server, request->hide_window.id);
196       break;
197     case BROADWAY_REQUEST_SET_TRANSIENT_FOR:
198       _gdk_broadway_server_window_set_transient_for (server,
199                                                      request->set_transient_for.id,
200                                                      request->set_transient_for.parent);
201       break;
202     case BROADWAY_REQUEST_TRANSLATE:
203       area = region_from_rects (request->translate.rects,
204                                 request->translate.n_rects);
205       _gdk_broadway_server_window_translate (server,
206                                              request->translate.id,
207                                              area,
208                                              request->translate.dx,
209                                              request->translate.dy);
210       cairo_region_destroy (area);
211       break;
212     case BROADWAY_REQUEST_UPDATE:
213       surface = open_surface (request->update.name,
214                               request->update.width,
215                               request->update.height);
216       if (surface != NULL)
217         {
218           _gdk_broadway_server_window_update (server,
219                                               request->update.id,
220                                               surface);
221           cairo_surface_destroy (surface);
222         }
223       break;
224     case BROADWAY_REQUEST_MOVE_RESIZE:
225       if (!_gdk_broadway_server_window_move_resize (server,
226                                                     request->move_resize.id,
227                                                     request->move_resize.x,
228                                                     request->move_resize.y,
229                                                     request->move_resize.width,
230                                                     request->move_resize.height))
231         {
232           /* TODO: Send configure request */
233         }
234       break;
235     case BROADWAY_REQUEST_GRAB_POINTER:
236       reply_grab_pointer.status =
237         _gdk_broadway_server_grab_pointer (server,
238                                            request->grab_pointer.id,
239                                            request->grab_pointer.owner_events,
240                                            request->grab_pointer.event_mask,
241                                            request->grab_pointer.time_);
242       send_reply (client, request, (BroadwayReply *)&reply_grab_pointer, sizeof (reply_grab_pointer),
243                   BROADWAY_REPLY_GRAB_POINTER);
244       break;
245     case BROADWAY_REQUEST_UNGRAB_POINTER:
246       reply_ungrab_pointer.status =
247         _gdk_broadway_server_ungrab_pointer (server,
248                                              request->ungrab_pointer.time_);
249       send_reply (client, request, (BroadwayReply *)&reply_ungrab_pointer, sizeof (reply_ungrab_pointer),
250                   BROADWAY_REPLY_UNGRAB_POINTER);
251       break;
252     default:
253       g_warning ("Unknown request of type %d\n", request->base.type);
254     }
255 }
256
257 static void
258 client_fill_cb (GObject *source_object,
259                 GAsyncResult *result,
260                 gpointer user_data)
261 {
262   BroadwayClient *client = user_data;
263   gssize res;
264
265   res = g_buffered_input_stream_fill_finish (client->in, result, NULL);
266   
267   if (res > 0)
268     {
269       guint32 size;
270       gsize count, remaining;
271       guint8 *buffer;
272       BroadwayRequest request;
273
274       buffer = (guint8 *)g_buffered_input_stream_peek_buffer (client->in, &count);
275
276       remaining = count;
277       while (remaining >= sizeof (guint32))
278         {
279           memcpy (&size, buffer, sizeof (guint32));
280           
281           if (size <= remaining)
282             {
283               g_assert (size >= sizeof (BroadwayRequestBase));
284               g_assert (size <= sizeof (BroadwayRequest));
285
286               memcpy (&request, buffer, size);
287               client_handle_request (client, &request);
288
289               remaining -= size;
290               buffer += size;
291             }
292         }
293       
294       /* This is guaranteed not to block */
295       g_input_stream_skip (G_INPUT_STREAM (client->in), count - remaining, NULL, NULL);
296       
297       g_buffered_input_stream_fill_async (client->in,
298                                           4*1024,
299                                           0,
300                                           NULL,
301                                           client_fill_cb, client);
302     }
303   else
304     {
305       client_disconnected (client);
306     }
307 }
308
309
310 static gboolean
311 incoming_client (GSocketService    *service,
312                  GSocketConnection *connection,
313                  GObject           *source_object)
314 {
315   BroadwayClient *client;
316   GInputStream *input;
317
318   client = g_new0 (BroadwayClient, 1);
319   client->id = client_id_count++;
320   client->connection = g_object_ref (connection);
321   
322   input = g_io_stream_get_input_stream (G_IO_STREAM (client->connection));
323   client->in = (GBufferedInputStream *)g_buffered_input_stream_new (input);
324
325   clients = g_list_prepend (clients, client);
326
327   g_buffered_input_stream_fill_async (client->in,
328                                       4*1024,
329                                       0,
330                                       NULL,
331                                       client_fill_cb, client);
332   
333   return TRUE;
334 }
335
336 int
337 main (int argc, char *argv[])
338 {
339   GError *error;
340   GMainLoop *loop;
341   GSocketAddress *address;
342   GSocketService *listener;
343   char *path;
344
345   error = NULL;
346   server = _gdk_broadway_server_new (8080, &error);
347   if (server == NULL)
348     {
349       g_printerr ("%s\n", error->message);
350       return 1;
351     }
352
353   path = g_build_filename (g_get_user_runtime_dir (), "broadway1.socket", NULL);
354   g_print ("Listening on %s\n", path);
355   address = g_unix_socket_address_new_with_type (path, -1,
356                                                  G_UNIX_SOCKET_ADDRESS_ABSTRACT);
357   g_free (path);
358
359   listener = g_socket_service_new ();
360   if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener),
361                                       address,
362                                       G_SOCKET_TYPE_STREAM,
363                                       G_SOCKET_PROTOCOL_DEFAULT,
364                                       G_OBJECT (server),
365                                       NULL,
366                                       &error))
367     {
368       g_printerr ("Can't listen: %s\n", error->message);
369       return 1;
370     }
371   g_object_unref (address);
372
373   g_signal_connect (listener, "incoming", G_CALLBACK (incoming_client), NULL);
374
375   g_socket_service_start (G_SOCKET_SERVICE (listener));
376
377   loop = g_main_loop_new (NULL, FALSE);
378   g_main_loop_run (loop);
379   
380   return 0;
381 }
382
383 static gsize
384 get_event_size (int type)
385 {
386   switch (type)
387     {
388     case BROADWAY_EVENT_ENTER:
389     case BROADWAY_EVENT_LEAVE:
390       return sizeof (BroadwayInputCrossingMsg);
391     case BROADWAY_EVENT_POINTER_MOVE:
392       return sizeof (BroadwayInputPointerMsg);
393     case BROADWAY_EVENT_BUTTON_PRESS:
394     case BROADWAY_EVENT_BUTTON_RELEASE:
395       return sizeof (BroadwayInputButtonMsg);
396     case BROADWAY_EVENT_SCROLL:
397       return sizeof (BroadwayInputScrollMsg);
398     case BROADWAY_EVENT_KEY_PRESS:
399     case BROADWAY_EVENT_KEY_RELEASE:
400       return  sizeof (BroadwayInputKeyMsg);
401     case BROADWAY_EVENT_GRAB_NOTIFY:
402     case BROADWAY_EVENT_UNGRAB_NOTIFY:
403       return sizeof (BroadwayInputGrabReply);
404     case BROADWAY_EVENT_CONFIGURE_NOTIFY:
405       return  sizeof (BroadwayInputConfigureNotify);
406     case BROADWAY_EVENT_DELETE_NOTIFY:
407       return sizeof (BroadwayInputDeleteNotify);
408     case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
409       return sizeof (BroadwayInputScreenResizeNotify);
410     default:
411       g_assert_not_reached ();
412     }
413   return 0;
414 }
415
416 void
417 _gdk_broadway_events_got_input (BroadwayInputMsg *message)
418 {
419   GList *l;
420   BroadwayReplyEvent reply_event;
421   gsize size;
422
423   size = get_event_size (message->base.type);
424
425   reply_event.msg = *message;
426
427   /* TODO:
428      Don't send to all clients
429      Rewrite serials, etc
430   */
431   for (l = clients; l != NULL; l = l->next)
432     {
433       BroadwayClient *client = l->data;
434
435       send_reply (client, NULL, (BroadwayReply *)&reply_event,
436                   sizeof (BroadwayReplyBase) + size,
437                   BROADWAY_REPLY_EVENT);
438     }
439 }