8 #include <gio/gunixsocketaddress.h>
10 #include "gdkbroadway-server.h"
12 GdkBroadwayServer *server;
15 static guint32 client_id_count = 1;
19 GSocketConnection *connection;
20 GBufferedInputStream *in;
21 guint32 last_seen_serial;
26 client_free (BroadwayClient *client)
28 clients = g_list_remove (clients, client);
29 g_object_unref (client->connection);
30 g_object_unref (client->in);
35 client_disconnected (BroadwayClient *client)
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 */
43 send_reply (BroadwayClient *client,
44 BroadwayRequest *request,
49 GOutputStream *output;
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;
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))
59 g_printerr ("can't write to client");
60 client_disconnected (client);
61 /* TODO: Make sure we don't access client after this */
65 static cairo_region_t *
66 region_from_rects (BroadwayRect *rects, int n_rects)
68 cairo_region_t *region;
69 cairo_rectangle_int_t *cairo_rects;
72 cairo_rects = g_new (cairo_rectangle_int_t, n_rects);
73 for (i = 0; i < n_rects; i++)
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;
80 region = cairo_region_create_rectangles (cairo_rects, n_rects);
85 static const cairo_user_data_key_t shm_cairo_key;
93 shm_data_unmap (void *_data)
95 ShmSurfaceData *data = _data;
96 munmap (data->data, data->data_size);
101 open_surface (char *name, int width, int height)
103 ShmSurfaceData *data;
104 cairo_surface_t *surface;
109 /* TODO: Cache this */
111 size = width * height * sizeof (guint32);
113 fd = shm_open(name, O_RDONLY, 0600);
116 perror ("Failed to shm_open");
120 ptr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
126 data = g_new0 (ShmSurfaceData, 1);
129 data->data_size = size;
131 surface = cairo_image_surface_create_for_data ((guchar *)data->data,
134 width * sizeof (guint32));
135 g_assert (surface != NULL);
137 cairo_surface_set_user_data (surface, &shm_cairo_key,
138 data, shm_data_unmap);
144 client_handle_request (BroadwayClient *client,
145 BroadwayRequest *request)
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;
155 client->last_seen_serial = request->base.serial;
157 switch (request->base.type)
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);
168 send_reply (client, request, (BroadwayReply *)&reply_new_window, sizeof (reply_new_window),
169 BROADWAY_REPLY_NEW_WINDOW);
171 case BROADWAY_REQUEST_FLUSH:
172 _gdk_broadway_server_flush (server);
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);
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);
188 case BROADWAY_REQUEST_DESTROY_WINDOW:
189 _gdk_broadway_server_destroy_window (server, request->destroy_window.id);
191 case BROADWAY_REQUEST_SHOW_WINDOW:
192 _gdk_broadway_server_window_show (server, request->show_window.id);
194 case BROADWAY_REQUEST_HIDE_WINDOW:
195 _gdk_broadway_server_window_hide (server, request->hide_window.id);
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);
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,
208 request->translate.dx,
209 request->translate.dy);
210 cairo_region_destroy (area);
212 case BROADWAY_REQUEST_UPDATE:
213 surface = open_surface (request->update.name,
214 request->update.width,
215 request->update.height);
218 _gdk_broadway_server_window_update (server,
221 cairo_surface_destroy (surface);
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))
232 /* TODO: Send configure request */
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);
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);
253 g_warning ("Unknown request of type %d\n", request->base.type);
258 client_fill_cb (GObject *source_object,
259 GAsyncResult *result,
262 BroadwayClient *client = user_data;
265 res = g_buffered_input_stream_fill_finish (client->in, result, NULL);
270 gsize count, remaining;
272 BroadwayRequest request;
274 buffer = (guint8 *)g_buffered_input_stream_peek_buffer (client->in, &count);
277 while (remaining >= sizeof (guint32))
279 memcpy (&size, buffer, sizeof (guint32));
281 if (size <= remaining)
283 g_assert (size >= sizeof (BroadwayRequestBase));
284 g_assert (size <= sizeof (BroadwayRequest));
286 memcpy (&request, buffer, size);
287 client_handle_request (client, &request);
294 /* This is guaranteed not to block */
295 g_input_stream_skip (G_INPUT_STREAM (client->in), count - remaining, NULL, NULL);
297 g_buffered_input_stream_fill_async (client->in,
301 client_fill_cb, client);
305 client_disconnected (client);
311 incoming_client (GSocketService *service,
312 GSocketConnection *connection,
313 GObject *source_object)
315 BroadwayClient *client;
318 client = g_new0 (BroadwayClient, 1);
319 client->id = client_id_count++;
320 client->connection = g_object_ref (connection);
322 input = g_io_stream_get_input_stream (G_IO_STREAM (client->connection));
323 client->in = (GBufferedInputStream *)g_buffered_input_stream_new (input);
325 clients = g_list_prepend (clients, client);
327 g_buffered_input_stream_fill_async (client->in,
331 client_fill_cb, client);
337 main (int argc, char *argv[])
341 GSocketAddress *address;
342 GSocketService *listener;
346 server = _gdk_broadway_server_new (8080, &error);
349 g_printerr ("%s\n", error->message);
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);
359 listener = g_socket_service_new ();
360 if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener),
362 G_SOCKET_TYPE_STREAM,
363 G_SOCKET_PROTOCOL_DEFAULT,
368 g_printerr ("Can't listen: %s\n", error->message);
371 g_object_unref (address);
373 g_signal_connect (listener, "incoming", G_CALLBACK (incoming_client), NULL);
375 g_socket_service_start (G_SOCKET_SERVICE (listener));
377 loop = g_main_loop_new (NULL, FALSE);
378 g_main_loop_run (loop);
384 get_event_size (int type)
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);
411 g_assert_not_reached ();
417 _gdk_broadway_events_got_input (BroadwayInputMsg *message)
420 BroadwayReplyEvent reply_event;
423 size = get_event_size (message->base.type);
425 reply_event.msg = *message;
428 Don't send to all clients
431 for (l = clients; l != NULL; l = l->next)
433 BroadwayClient *client = l->data;
435 send_reply (client, NULL, (BroadwayReply *)&reply_event,
436 sizeof (BroadwayReplyBase) + size,
437 BROADWAY_REPLY_EVENT);