11 #include <gio/gunixsocketaddress.h>
13 #include "broadway-server.h"
15 BroadwayServer *server;
18 static guint32 client_id_count = 1;
22 GSocketConnection *connection;
23 GBufferedInputStream *in;
24 guint32 last_seen_serial;
26 guint disconnect_idle;
30 client_free (BroadwayClient *client)
32 g_assert (client->windows == NULL);
33 g_assert (client->disconnect_idle == 0);
34 clients = g_list_remove (clients, client);
35 g_object_unref (client->connection);
36 g_object_unref (client->in);
41 client_disconnected (BroadwayClient *client)
45 if (client->disconnect_idle != 0)
47 g_source_remove (client->disconnect_idle);
48 client->disconnect_idle = 0;
51 for (l = client->windows; l != NULL; l = l->next)
52 broadway_server_destroy_window (server,
53 GPOINTER_TO_UINT (l->data));
54 g_list_free (client->windows);
55 client->windows = NULL;
57 broadway_server_flush (server);
63 disconnect_idle_cb (BroadwayClient *client)
65 client->disconnect_idle = 0;
66 client_disconnected (client);
67 return G_SOURCE_REMOVE;
71 client_disconnect_in_idle (BroadwayClient *client)
73 if (client->disconnect_idle == 0)
74 client->disconnect_idle =
75 g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)disconnect_idle_cb, client, NULL);
79 send_reply (BroadwayClient *client,
80 BroadwayRequest *request,
85 GOutputStream *output;
87 reply->base.size = size;
88 reply->base.last_serial = client->last_seen_serial;
89 reply->base.in_reply_to = request ? request->base.serial : 0;
90 reply->base.type = type;
92 output = g_io_stream_get_output_stream (G_IO_STREAM (client->connection));
93 if (!g_output_stream_write_all (output, reply, size, NULL, NULL, NULL))
95 g_printerr ("can't write to client");
96 client_disconnect_in_idle (client);
100 static cairo_region_t *
101 region_from_rects (BroadwayRect *rects, int n_rects)
103 cairo_region_t *region;
104 cairo_rectangle_int_t *cairo_rects;
107 cairo_rects = g_new (cairo_rectangle_int_t, n_rects);
108 for (i = 0; i < n_rects; i++)
110 cairo_rects[i].x = rects[i].x;
111 cairo_rects[i].y = rects[i].y;
112 cairo_rects[i].width = rects[i].width;
113 cairo_rects[i].height = rects[i].height;
115 region = cairo_region_create_rectangles (cairo_rects, n_rects);
116 g_free (cairo_rects);
120 static const cairo_user_data_key_t shm_cairo_key;
128 shm_data_unmap (void *_data)
130 ShmSurfaceData *data = _data;
131 munmap (data->data, data->data_size);
136 open_surface (char *name, int width, int height)
138 ShmSurfaceData *data;
139 cairo_surface_t *surface;
144 size = width * height * sizeof (guint32);
146 fd = shm_open(name, O_RDONLY, 0600);
149 perror ("Failed to shm_open");
153 ptr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
159 data = g_new0 (ShmSurfaceData, 1);
162 data->data_size = size;
164 surface = cairo_image_surface_create_for_data ((guchar *)data->data,
167 width * sizeof (guint32));
168 g_assert (surface != NULL);
170 cairo_surface_set_user_data (surface, &shm_cairo_key,
171 data, shm_data_unmap);
177 client_handle_request (BroadwayClient *client,
178 BroadwayRequest *request)
180 BroadwayReplyNewWindow reply_new_window;
181 BroadwayReplySync reply_sync;
182 BroadwayReplyQueryMouse reply_query_mouse;
183 BroadwayReplyGrabPointer reply_grab_pointer;
184 BroadwayReplyUngrabPointer reply_ungrab_pointer;
185 cairo_region_t *area;
186 cairo_surface_t *surface;
188 client->last_seen_serial = request->base.serial;
190 switch (request->base.type)
192 case BROADWAY_REQUEST_NEW_WINDOW:
193 reply_new_window.id =
194 broadway_server_new_window (server,
195 request->new_window.x,
196 request->new_window.y,
197 request->new_window.width,
198 request->new_window.height,
199 request->new_window.is_temp);
201 g_list_prepend (client->windows,
202 GUINT_TO_POINTER (reply_new_window.id));
204 send_reply (client, request, (BroadwayReply *)&reply_new_window, sizeof (reply_new_window),
205 BROADWAY_REPLY_NEW_WINDOW);
207 case BROADWAY_REQUEST_FLUSH:
208 broadway_server_flush (server);
210 case BROADWAY_REQUEST_SYNC:
211 broadway_server_flush (server);
212 send_reply (client, request, (BroadwayReply *)&reply_sync, sizeof (reply_sync),
213 BROADWAY_REPLY_SYNC);
215 case BROADWAY_REQUEST_QUERY_MOUSE:
216 broadway_server_query_mouse (server,
217 &reply_query_mouse.toplevel,
218 &reply_query_mouse.root_x,
219 &reply_query_mouse.root_y,
220 &reply_query_mouse.mask);
221 send_reply (client, request, (BroadwayReply *)&reply_query_mouse, sizeof (reply_query_mouse),
222 BROADWAY_REPLY_QUERY_MOUSE);
224 case BROADWAY_REQUEST_DESTROY_WINDOW:
226 g_list_remove (client->windows,
227 GUINT_TO_POINTER (request->destroy_window.id));
228 broadway_server_destroy_window (server, request->destroy_window.id);
230 case BROADWAY_REQUEST_SHOW_WINDOW:
231 broadway_server_window_show (server, request->show_window.id);
233 case BROADWAY_REQUEST_HIDE_WINDOW:
234 broadway_server_window_hide (server, request->hide_window.id);
236 case BROADWAY_REQUEST_SET_TRANSIENT_FOR:
237 broadway_server_window_set_transient_for (server,
238 request->set_transient_for.id,
239 request->set_transient_for.parent);
241 case BROADWAY_REQUEST_TRANSLATE:
242 area = region_from_rects (request->translate.rects,
243 request->translate.n_rects);
244 broadway_server_window_translate (server,
245 request->translate.id,
247 request->translate.dx,
248 request->translate.dy);
249 cairo_region_destroy (area);
251 case BROADWAY_REQUEST_UPDATE:
252 surface = open_surface (request->update.name,
253 request->update.width,
254 request->update.height);
257 broadway_server_window_update (server,
260 cairo_surface_destroy (surface);
263 case BROADWAY_REQUEST_MOVE_RESIZE:
264 if (!broadway_server_window_move_resize (server,
265 request->move_resize.id,
266 request->move_resize.x,
267 request->move_resize.y,
268 request->move_resize.width,
269 request->move_resize.height))
271 /* TODO: Send configure request */
274 case BROADWAY_REQUEST_GRAB_POINTER:
275 reply_grab_pointer.status =
276 broadway_server_grab_pointer (server,
278 request->grab_pointer.id,
279 request->grab_pointer.owner_events,
280 request->grab_pointer.event_mask,
281 request->grab_pointer.time_);
282 send_reply (client, request, (BroadwayReply *)&reply_grab_pointer, sizeof (reply_grab_pointer),
283 BROADWAY_REPLY_GRAB_POINTER);
285 case BROADWAY_REQUEST_UNGRAB_POINTER:
286 reply_ungrab_pointer.status =
287 broadway_server_ungrab_pointer (server,
288 request->ungrab_pointer.time_);
289 send_reply (client, request, (BroadwayReply *)&reply_ungrab_pointer, sizeof (reply_ungrab_pointer),
290 BROADWAY_REPLY_UNGRAB_POINTER);
293 g_warning ("Unknown request of type %d\n", request->base.type);
298 client_fill_cb (GObject *source_object,
299 GAsyncResult *result,
302 BroadwayClient *client = user_data;
305 res = g_buffered_input_stream_fill_finish (client->in, result, NULL);
310 gsize count, remaining;
312 BroadwayRequest request;
314 buffer = (guint8 *)g_buffered_input_stream_peek_buffer (client->in, &count);
317 while (remaining >= sizeof (guint32))
319 memcpy (&size, buffer, sizeof (guint32));
321 if (size <= remaining)
323 g_assert (size >= sizeof (BroadwayRequestBase));
324 g_assert (size <= sizeof (BroadwayRequest));
326 memcpy (&request, buffer, size);
327 client_handle_request (client, &request);
334 /* This is guaranteed not to block */
335 g_input_stream_skip (G_INPUT_STREAM (client->in), count - remaining, NULL, NULL);
337 g_buffered_input_stream_fill_async (client->in,
341 client_fill_cb, client);
345 client_disconnected (client);
351 incoming_client (GSocketService *service,
352 GSocketConnection *connection,
353 GObject *source_object)
355 BroadwayClient *client;
358 client = g_new0 (BroadwayClient, 1);
359 client->id = client_id_count++;
360 client->connection = g_object_ref (connection);
362 input = g_io_stream_get_input_stream (G_IO_STREAM (client->connection));
363 client->in = (GBufferedInputStream *)g_buffered_input_stream_new (input);
365 clients = g_list_prepend (clients, client);
367 g_buffered_input_stream_fill_async (client->in,
371 client_fill_cb, client);
378 main (int argc, char *argv[])
380 GError *error = NULL;
381 GOptionContext *context;
383 GSocketAddress *address;
384 GSocketService *listener;
388 const GOptionEntry entries[] = {
389 { "port", 'p', 0, G_OPTION_ARG_INT, &http_port, "Httpd port", "PORT" },
393 context = g_option_context_new ("[:DISPLAY] - broadway display daemon");
394 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
395 if (!g_option_context_parse (context, &argc, &argv, &error))
397 g_printerr ("option parsing failed: %s\n", error->message);
405 g_printerr ("Usage broadwayd [:DISPLAY]\n");
408 display = strtol(argv[1]+1, NULL, 10);
411 g_printerr ("Failed to parse display num %s\n", argv[1]);
417 http_port = 8080 + (display - 1);
419 server = broadway_server_new (http_port, &error);
422 g_printerr ("%s\n", error->message);
426 base = g_strdup_printf ("broadway%d.socket", display);
427 path = g_build_filename (g_get_user_runtime_dir (), base, NULL);
429 g_print ("Listening on %s\n", path);
430 address = g_unix_socket_address_new_with_type (path, -1,
431 G_UNIX_SOCKET_ADDRESS_ABSTRACT);
434 listener = g_socket_service_new ();
435 if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener),
437 G_SOCKET_TYPE_STREAM,
438 G_SOCKET_PROTOCOL_DEFAULT,
443 g_printerr ("Can't listen: %s\n", error->message);
446 g_object_unref (address);
448 g_signal_connect (listener, "incoming", G_CALLBACK (incoming_client), NULL);
450 g_socket_service_start (G_SOCKET_SERVICE (listener));
452 loop = g_main_loop_new (NULL, FALSE);
453 g_main_loop_run (loop);
459 get_event_size (int type)
463 case BROADWAY_EVENT_ENTER:
464 case BROADWAY_EVENT_LEAVE:
465 return sizeof (BroadwayInputCrossingMsg);
466 case BROADWAY_EVENT_POINTER_MOVE:
467 return sizeof (BroadwayInputPointerMsg);
468 case BROADWAY_EVENT_BUTTON_PRESS:
469 case BROADWAY_EVENT_BUTTON_RELEASE:
470 return sizeof (BroadwayInputButtonMsg);
471 case BROADWAY_EVENT_SCROLL:
472 return sizeof (BroadwayInputScrollMsg);
473 case BROADWAY_EVENT_KEY_PRESS:
474 case BROADWAY_EVENT_KEY_RELEASE:
475 return sizeof (BroadwayInputKeyMsg);
476 case BROADWAY_EVENT_GRAB_NOTIFY:
477 case BROADWAY_EVENT_UNGRAB_NOTIFY:
478 return sizeof (BroadwayInputGrabReply);
479 case BROADWAY_EVENT_CONFIGURE_NOTIFY:
480 return sizeof (BroadwayInputConfigureNotify);
481 case BROADWAY_EVENT_DELETE_NOTIFY:
482 return sizeof (BroadwayInputDeleteNotify);
483 case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
484 return sizeof (BroadwayInputScreenResizeNotify);
486 g_assert_not_reached ();
492 broadway_events_got_input (BroadwayInputMsg *message,
496 BroadwayReplyEvent reply_event;
499 size = get_event_size (message->base.type);
501 reply_event.msg = *message;
503 for (l = clients; l != NULL; l = l->next)
505 BroadwayClient *client = l->data;
507 if (client_id == -1 ||
508 client->id == client_id)
510 send_reply (client, NULL, (BroadwayReply *)&reply_event,
511 sizeof (BroadwayReplyBase) + size,
512 BROADWAY_REPLY_EVENT);