8 #include <gio/gunixsocketaddress.h>
10 #include "gdkbroadway-server.h"
13 * Cache surfaces that are opened via shm_open inbetween updates.
14 * Send configure event when a window is moved and no client
15 * Rewrite events (only to one client, per-client serials, etc)
16 * _gdk_broadway_server_has_client is always FALSE, so resize don't work.
19 GdkBroadwayServer *server;
22 static guint32 client_id_count = 1;
26 GSocketConnection *connection;
27 GBufferedInputStream *in;
28 guint32 last_seen_serial;
30 guint disconnect_idle;
34 client_free (BroadwayClient *client)
36 g_assert (client->windows == NULL);
37 g_assert (client->disconnect_idle == 0);
38 clients = g_list_remove (clients, client);
39 g_object_unref (client->connection);
40 g_object_unref (client->in);
45 client_disconnected (BroadwayClient *client)
49 if (client->disconnect_idle != 0)
51 g_source_remove (client->disconnect_idle);
52 client->disconnect_idle = 0;
55 for (l = client->windows; l != NULL; l = l->next)
56 _gdk_broadway_server_destroy_window (server,
57 GPOINTER_TO_UINT (l->data));
58 g_list_free (client->windows);
59 client->windows = NULL;
61 _gdk_broadway_server_flush (server);
67 disconnect_idle_cb (BroadwayClient *client)
69 client->disconnect_idle = 0;
70 client_disconnected (client);
71 return G_SOURCE_REMOVE;
75 client_disconnect_in_idle (BroadwayClient *client)
77 if (client->disconnect_idle == 0)
78 client->disconnect_idle =
79 g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)disconnect_idle_cb, client, NULL);
83 send_reply (BroadwayClient *client,
84 BroadwayRequest *request,
89 GOutputStream *output;
91 reply->base.size = size;
92 reply->base.last_serial = client->last_seen_serial;
93 reply->base.in_reply_to = request ? request->base.serial : 0;
94 reply->base.type = type;
96 output = g_io_stream_get_output_stream (G_IO_STREAM (client->connection));
97 if (!g_output_stream_write_all (output, reply, size, NULL, NULL, NULL))
99 g_printerr ("can't write to client");
100 client_disconnect_in_idle (client);
104 static cairo_region_t *
105 region_from_rects (BroadwayRect *rects, int n_rects)
107 cairo_region_t *region;
108 cairo_rectangle_int_t *cairo_rects;
111 cairo_rects = g_new (cairo_rectangle_int_t, n_rects);
112 for (i = 0; i < n_rects; i++)
114 cairo_rects[i].x = rects[i].x;
115 cairo_rects[i].y = rects[i].y;
116 cairo_rects[i].width = rects[i].width;
117 cairo_rects[i].height = rects[i].height;
119 region = cairo_region_create_rectangles (cairo_rects, n_rects);
120 g_free (cairo_rects);
124 static const cairo_user_data_key_t shm_cairo_key;
132 shm_data_unmap (void *_data)
134 ShmSurfaceData *data = _data;
135 munmap (data->data, data->data_size);
140 open_surface (char *name, int width, int height)
142 ShmSurfaceData *data;
143 cairo_surface_t *surface;
148 size = width * height * sizeof (guint32);
150 fd = shm_open(name, O_RDONLY, 0600);
153 perror ("Failed to shm_open");
157 ptr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
163 data = g_new0 (ShmSurfaceData, 1);
166 data->data_size = size;
168 surface = cairo_image_surface_create_for_data ((guchar *)data->data,
171 width * sizeof (guint32));
172 g_assert (surface != NULL);
174 cairo_surface_set_user_data (surface, &shm_cairo_key,
175 data, shm_data_unmap);
181 client_handle_request (BroadwayClient *client,
182 BroadwayRequest *request)
184 BroadwayReplyNewWindow reply_new_window;
185 BroadwayReplySync reply_sync;
186 BroadwayReplyQueryMouse reply_query_mouse;
187 BroadwayReplyGrabPointer reply_grab_pointer;
188 BroadwayReplyUngrabPointer reply_ungrab_pointer;
189 cairo_region_t *area;
190 cairo_surface_t *surface;
192 client->last_seen_serial = request->base.serial;
194 switch (request->base.type)
196 case BROADWAY_REQUEST_NEW_WINDOW:
197 reply_new_window.id =
198 _gdk_broadway_server_new_window (server,
199 request->new_window.x,
200 request->new_window.y,
201 request->new_window.width,
202 request->new_window.height,
203 request->new_window.is_temp);
205 g_list_prepend (client->windows,
206 GUINT_TO_POINTER (reply_new_window.id));
208 send_reply (client, request, (BroadwayReply *)&reply_new_window, sizeof (reply_new_window),
209 BROADWAY_REPLY_NEW_WINDOW);
211 case BROADWAY_REQUEST_FLUSH:
212 _gdk_broadway_server_flush (server);
214 case BROADWAY_REQUEST_SYNC:
215 _gdk_broadway_server_flush (server);
216 send_reply (client, request, (BroadwayReply *)&reply_sync, sizeof (reply_sync),
217 BROADWAY_REPLY_SYNC);
219 case BROADWAY_REQUEST_QUERY_MOUSE:
220 _gdk_broadway_server_query_mouse (server,
221 &reply_query_mouse.toplevel,
222 &reply_query_mouse.root_x,
223 &reply_query_mouse.root_y,
224 &reply_query_mouse.mask);
225 send_reply (client, request, (BroadwayReply *)&reply_query_mouse, sizeof (reply_query_mouse),
226 BROADWAY_REPLY_QUERY_MOUSE);
228 case BROADWAY_REQUEST_DESTROY_WINDOW:
230 g_list_remove (client->windows,
231 GUINT_TO_POINTER (request->destroy_window.id));
232 _gdk_broadway_server_destroy_window (server, request->destroy_window.id);
234 case BROADWAY_REQUEST_SHOW_WINDOW:
235 _gdk_broadway_server_window_show (server, request->show_window.id);
237 case BROADWAY_REQUEST_HIDE_WINDOW:
238 _gdk_broadway_server_window_hide (server, request->hide_window.id);
240 case BROADWAY_REQUEST_SET_TRANSIENT_FOR:
241 _gdk_broadway_server_window_set_transient_for (server,
242 request->set_transient_for.id,
243 request->set_transient_for.parent);
245 case BROADWAY_REQUEST_TRANSLATE:
246 area = region_from_rects (request->translate.rects,
247 request->translate.n_rects);
248 _gdk_broadway_server_window_translate (server,
249 request->translate.id,
251 request->translate.dx,
252 request->translate.dy);
253 cairo_region_destroy (area);
255 case BROADWAY_REQUEST_UPDATE:
256 surface = open_surface (request->update.name,
257 request->update.width,
258 request->update.height);
261 _gdk_broadway_server_window_update (server,
264 cairo_surface_destroy (surface);
267 case BROADWAY_REQUEST_MOVE_RESIZE:
268 if (!_gdk_broadway_server_window_move_resize (server,
269 request->move_resize.id,
270 request->move_resize.x,
271 request->move_resize.y,
272 request->move_resize.width,
273 request->move_resize.height))
275 /* TODO: Send configure request */
278 case BROADWAY_REQUEST_GRAB_POINTER:
279 reply_grab_pointer.status =
280 _gdk_broadway_server_grab_pointer (server,
281 request->grab_pointer.id,
282 request->grab_pointer.owner_events,
283 request->grab_pointer.event_mask,
284 request->grab_pointer.time_);
285 send_reply (client, request, (BroadwayReply *)&reply_grab_pointer, sizeof (reply_grab_pointer),
286 BROADWAY_REPLY_GRAB_POINTER);
288 case BROADWAY_REQUEST_UNGRAB_POINTER:
289 reply_ungrab_pointer.status =
290 _gdk_broadway_server_ungrab_pointer (server,
291 request->ungrab_pointer.time_);
292 send_reply (client, request, (BroadwayReply *)&reply_ungrab_pointer, sizeof (reply_ungrab_pointer),
293 BROADWAY_REPLY_UNGRAB_POINTER);
296 g_warning ("Unknown request of type %d\n", request->base.type);
301 client_fill_cb (GObject *source_object,
302 GAsyncResult *result,
305 BroadwayClient *client = user_data;
308 res = g_buffered_input_stream_fill_finish (client->in, result, NULL);
313 gsize count, remaining;
315 BroadwayRequest request;
317 buffer = (guint8 *)g_buffered_input_stream_peek_buffer (client->in, &count);
320 while (remaining >= sizeof (guint32))
322 memcpy (&size, buffer, sizeof (guint32));
324 if (size <= remaining)
326 g_assert (size >= sizeof (BroadwayRequestBase));
327 g_assert (size <= sizeof (BroadwayRequest));
329 memcpy (&request, buffer, size);
330 client_handle_request (client, &request);
337 /* This is guaranteed not to block */
338 g_input_stream_skip (G_INPUT_STREAM (client->in), count - remaining, NULL, NULL);
340 g_buffered_input_stream_fill_async (client->in,
344 client_fill_cb, client);
348 client_disconnected (client);
354 incoming_client (GSocketService *service,
355 GSocketConnection *connection,
356 GObject *source_object)
358 BroadwayClient *client;
361 client = g_new0 (BroadwayClient, 1);
362 client->id = client_id_count++;
363 client->connection = g_object_ref (connection);
365 input = g_io_stream_get_input_stream (G_IO_STREAM (client->connection));
366 client->in = (GBufferedInputStream *)g_buffered_input_stream_new (input);
368 clients = g_list_prepend (clients, client);
370 g_buffered_input_stream_fill_async (client->in,
374 client_fill_cb, client);
380 main (int argc, char *argv[])
384 GSocketAddress *address;
385 GSocketService *listener;
389 server = _gdk_broadway_server_new (8080, &error);
392 g_printerr ("%s\n", error->message);
396 path = g_build_filename (g_get_user_runtime_dir (), "broadway1.socket", NULL);
397 g_print ("Listening on %s\n", path);
398 address = g_unix_socket_address_new_with_type (path, -1,
399 G_UNIX_SOCKET_ADDRESS_ABSTRACT);
402 listener = g_socket_service_new ();
403 if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener),
405 G_SOCKET_TYPE_STREAM,
406 G_SOCKET_PROTOCOL_DEFAULT,
411 g_printerr ("Can't listen: %s\n", error->message);
414 g_object_unref (address);
416 g_signal_connect (listener, "incoming", G_CALLBACK (incoming_client), NULL);
418 g_socket_service_start (G_SOCKET_SERVICE (listener));
420 loop = g_main_loop_new (NULL, FALSE);
421 g_main_loop_run (loop);
427 get_event_size (int type)
431 case BROADWAY_EVENT_ENTER:
432 case BROADWAY_EVENT_LEAVE:
433 return sizeof (BroadwayInputCrossingMsg);
434 case BROADWAY_EVENT_POINTER_MOVE:
435 return sizeof (BroadwayInputPointerMsg);
436 case BROADWAY_EVENT_BUTTON_PRESS:
437 case BROADWAY_EVENT_BUTTON_RELEASE:
438 return sizeof (BroadwayInputButtonMsg);
439 case BROADWAY_EVENT_SCROLL:
440 return sizeof (BroadwayInputScrollMsg);
441 case BROADWAY_EVENT_KEY_PRESS:
442 case BROADWAY_EVENT_KEY_RELEASE:
443 return sizeof (BroadwayInputKeyMsg);
444 case BROADWAY_EVENT_GRAB_NOTIFY:
445 case BROADWAY_EVENT_UNGRAB_NOTIFY:
446 return sizeof (BroadwayInputGrabReply);
447 case BROADWAY_EVENT_CONFIGURE_NOTIFY:
448 return sizeof (BroadwayInputConfigureNotify);
449 case BROADWAY_EVENT_DELETE_NOTIFY:
450 return sizeof (BroadwayInputDeleteNotify);
451 case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
452 return sizeof (BroadwayInputScreenResizeNotify);
454 g_assert_not_reached ();
460 _gdk_broadway_events_got_input (BroadwayInputMsg *message)
463 BroadwayReplyEvent reply_event;
466 size = get_event_size (message->base.type);
468 reply_event.msg = *message;
470 for (l = clients; l != NULL; l = l->next)
472 BroadwayClient *client = l->data;
474 send_reply (client, NULL, (BroadwayReply *)&reply_event,
475 sizeof (BroadwayReplyBase) + size,
476 BROADWAY_REPLY_EVENT);