diff options
author | Alexander Larsson <alexl@redhat.com> | 2012-12-22 00:02:55 +0100 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2012-12-27 22:56:03 +0100 |
commit | fc96ef51d75f571de49cafb4bbbdc9fda5b80c6e (patch) | |
tree | e1175dfdda6d9d860dfe0534716b061aa87e5e6b /gdk | |
parent | 4addb2108efb788592f4b4732dd234276aab4628 (diff) | |
download | gtk+-fc96ef51d75f571de49cafb4bbbdc9fda5b80c6e.tar.gz |
broadway: Initial version of separate broadway server
This kinda works but is very rudimentary
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/broadway/Makefile.am | 8 | ||||
-rw-r--r-- | gdk/broadway/broadway-protocol.h | 151 | ||||
-rw-r--r-- | gdk/broadway/broadway-server.c | 413 | ||||
-rw-r--r-- | gdk/broadway/broadway.h | 6 | ||||
-rw-r--r-- | gdk/broadway/gdkbroadway-server-client.c | 693 | ||||
-rw-r--r-- | gdk/broadway/gdkbroadway-server.c | 5 | ||||
-rw-r--r-- | gdk/broadway/gdkbroadway-server.h | 9 | ||||
-rw-r--r-- | gdk/broadway/gdkdisplay-broadway.c | 2 | ||||
-rw-r--r-- | gdk/broadway/gdkwindow-broadway.c | 7 |
9 files changed, 1271 insertions, 23 deletions
diff --git a/gdk/broadway/Makefile.am b/gdk/broadway/Makefile.am index 2352c666fa..39b0a4b555 100644 --- a/gdk/broadway/Makefile.am +++ b/gdk/broadway/Makefile.am @@ -78,14 +78,14 @@ libgdk_broadway_la_SOURCES = \ gdkvisual-broadway.c \ gdkwindow-broadway.c \ gdkwindow-broadway.h \ - gdkprivate-broadway.h - -libgdk_broadway_la_LIBADD = libbroadway.la + gdkprivate-broadway.h \ + gdkbroadway-server.h \ + gdkbroadway-server-client.c broadway_server_SOURCES = \ broadway-server.c -broadway_server_LDADD = libbroadway.la $(GDK_DEP_LIBS) +broadway_server_LDADD = libbroadway.la $(GDK_DEP_LIBS) -lrt MAINTAINERCLEANFILES = $(broadway_built_sources) EXTRA_DIST += $(broadway_built_sources) diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h index 734212a2cd..6472910be3 100644 --- a/gdk/broadway/broadway-protocol.h +++ b/gdk/broadway/broadway-protocol.h @@ -3,6 +3,11 @@ #include <glib.h> +typedef struct { + gint32 x, y; + gint32 width, height; +} BroadwayRect; + typedef enum { BROADWAY_EVENT_ENTER = 'e', BROADWAY_EVENT_LEAVE = 'l', @@ -110,4 +115,150 @@ typedef union { BroadwayInputScreenResizeNotify screen_resize_notify; } BroadwayInputMsg; +typedef enum { + BROADWAY_REQUEST_NEW_WINDOW, + BROADWAY_REQUEST_FLUSH, + BROADWAY_REQUEST_SYNC, + BROADWAY_REQUEST_QUERY_MOUSE, + BROADWAY_REQUEST_DESTROY_WINDOW, + BROADWAY_REQUEST_SHOW_WINDOW, + BROADWAY_REQUEST_HIDE_WINDOW, + BROADWAY_REQUEST_SET_TRANSIENT_FOR, + BROADWAY_REQUEST_TRANSLATE, + BROADWAY_REQUEST_UPDATE, + BROADWAY_REQUEST_MOVE_RESIZE, + BROADWAY_REQUEST_GRAB_POINTER, + BROADWAY_REQUEST_UNGRAB_POINTER +} BroadwayRequestType; + +typedef struct { + guint32 size; + guint32 serial; + guint32 type; +} BroadwayRequestBase, BroadwayRequestFlush, BroadwayRequestSync, BroadwayRequestQueryMouse; + +typedef struct { + BroadwayRequestBase base; + guint32 id; +} BroadwayRequestDestroyWindow, BroadwayRequestShowWindow, BroadwayRequestHideWindow; + +typedef struct { + BroadwayRequestBase base; + guint32 id; + guint32 parent; +} BroadwayRequestSetTransientFor; + +typedef struct { + BroadwayRequestBase base; + guint32 id; + gint32 dx; + gint32 dy; + guint32 n_rects; + BroadwayRect rects[1]; +} BroadwayRequestTranslate; + +typedef struct { + BroadwayRequestBase base; + guint32 id; + char name[34]; + guint32 width; + guint32 height; +} BroadwayRequestUpdate; + +typedef struct { + BroadwayRequestBase base; + guint32 id; + guint32 owner_events; + guint32 event_mask; + guint32 time_; +} BroadwayRequestGrabPointer; + +typedef struct { + BroadwayRequestBase base; + guint32 time_; +} BroadwayRequestUngrabPointer; + +typedef struct { + BroadwayRequestBase base; + gint32 x; + gint32 y; + guint32 width; + guint32 height; + guint32 is_temp; +} BroadwayRequestNewWindow; + +typedef struct { + BroadwayRequestBase base; + guint32 id; + gint32 x; + gint32 y; + guint32 width; + guint32 height; +} BroadwayRequestMoveResize; + +typedef union { + BroadwayRequestBase base; + BroadwayRequestNewWindow new_window; + BroadwayRequestFlush flush; + BroadwayRequestSync sync; + BroadwayRequestQueryMouse query_mouse; + BroadwayRequestDestroyWindow destroy_window; + BroadwayRequestShowWindow show_window; + BroadwayRequestHideWindow hide_window; + BroadwayRequestSetTransientFor set_transient_for; + BroadwayRequestUpdate update; + BroadwayRequestMoveResize move_resize; + BroadwayRequestGrabPointer grab_pointer; + BroadwayRequestUngrabPointer ungrab_pointer; + BroadwayRequestTranslate translate; +} BroadwayRequest; + +typedef enum { + BROADWAY_REPLY_EVENT, + BROADWAY_REPLY_SYNC, + BROADWAY_REPLY_QUERY_MOUSE, + BROADWAY_REPLY_NEW_WINDOW, + BROADWAY_REPLY_GRAB_POINTER, + BROADWAY_REPLY_UNGRAB_POINTER +} BroadwayReplyType; + +typedef struct { + guint32 size; + guint32 last_serial; + guint32 in_reply_to; + guint32 type; +} BroadwayReplyBase, BroadwayReplySync; + +typedef struct { + BroadwayReplyBase base; + guint32 id; +} BroadwayReplyNewWindow; + +typedef struct { + BroadwayReplyBase base; + guint32 status; +} BroadwayReplyGrabPointer, BroadwayReplyUngrabPointer; + +typedef struct { + BroadwayReplyBase base; + guint32 toplevel; + gint32 root_x; + gint32 root_y; + guint32 mask; +} BroadwayReplyQueryMouse; + +typedef struct { + BroadwayReplyBase base; + BroadwayInputMsg msg; +} BroadwayReplyEvent; + +typedef union { + BroadwayReplyBase base; + BroadwayReplyEvent event; + BroadwayReplyQueryMouse query_mouse; + BroadwayReplyNewWindow new_window; + BroadwayReplyGrabPointer grab_pointer; + BroadwayReplyUngrabPointer ungrab_pointer; +} BroadwayReply; + #endif /* __BROADWAY_PROTOCOL_H__ */ diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c index 7395abd1ea..abb750f857 100644 --- a/gdk/broadway/broadway-server.c +++ b/gdk/broadway/broadway-server.c @@ -1,13 +1,346 @@ +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + #include <glib.h> +#include <gio/gio.h> +#include <gio/gunixsocketaddress.h> #include "gdkbroadway-server.h" +GdkBroadwayServer *server; +GList *clients; + +static guint32 client_id_count = 1; + +typedef struct { + guint32 id; + GSocketConnection *connection; + GBufferedInputStream *in; + guint32 last_seen_serial; + GList *windows; +} BroadwayClient; + +static void +client_free (BroadwayClient *client) +{ + clients = g_list_remove (clients, client); + g_object_unref (client->connection); + g_object_unref (client->in); + g_free (client); +} + +static void +client_disconnected (BroadwayClient *client) +{ + g_print ("client %d disconnected\n", client->id); + /* TODO: destroy client windows, also maybe do this in an idle, at least in some cases like on an i/o error */ + client_free (client); +} + +static void +send_reply (BroadwayClient *client, + BroadwayRequest *request, + BroadwayReply *reply, + gsize size, + guint32 type) +{ + GOutputStream *output; + + reply->base.size = size; + reply->base.last_serial = client->last_seen_serial; + reply->base.in_reply_to = request ? request->base.serial : 0; + reply->base.type = type; + + output = g_io_stream_get_output_stream (G_IO_STREAM (client->connection)); + if (!g_output_stream_write_all (output, reply, size, NULL, NULL, NULL)) + { + g_printerr ("can't write to client"); + client_disconnected (client); + /* TODO: Make sure we don't access client after this */ + } +} + +static cairo_region_t * +region_from_rects (BroadwayRect *rects, int n_rects) +{ + cairo_region_t *region; + cairo_rectangle_int_t *cairo_rects; + int i; + + cairo_rects = g_new (cairo_rectangle_int_t, n_rects); + for (i = 0; i < n_rects; i++) + { + cairo_rects[i].x = rects[i].x; + cairo_rects[i].y = rects[i].y; + cairo_rects[i].width = rects[i].width; + cairo_rects[i].height = rects[i].height; + } + region = cairo_region_create_rectangles (cairo_rects, n_rects); + g_free (cairo_rects); + return region; +} + +static const cairo_user_data_key_t shm_cairo_key; + +typedef struct { + void *data; + gsize data_size; +} ShmSurfaceData; + +static void +shm_data_unmap (void *_data) +{ + ShmSurfaceData *data = _data; + munmap (data->data, data->data_size); + g_free (data); +} + +cairo_surface_t * +open_surface (char *name, int width, int height) +{ + ShmSurfaceData *data; + cairo_surface_t *surface; + gsize size; + void *ptr; + int fd; + + /* TODO: Cache this */ + + size = width * height * sizeof (guint32); + + fd = shm_open(name, O_RDONLY, 0600); + if (fd == -1) + { + perror ("Failed to shm_open"); + return NULL; + } + + ptr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); + (void) close(fd); + + if (ptr == NULL) + return NULL; + + data = g_new0 (ShmSurfaceData, 1); + + data->data = ptr; + data->data_size = size; + + surface = cairo_image_surface_create_for_data ((guchar *)data->data, + CAIRO_FORMAT_RGB24, + width, height, + width * sizeof (guint32)); + g_assert (surface != NULL); + + cairo_surface_set_user_data (surface, &shm_cairo_key, + data, shm_data_unmap); + + return surface; +} + +static void +client_handle_request (BroadwayClient *client, + BroadwayRequest *request) +{ + BroadwayReplyNewWindow reply_new_window; + BroadwayReplySync reply_sync; + BroadwayReplyQueryMouse reply_query_mouse; + BroadwayReplyGrabPointer reply_grab_pointer; + BroadwayReplyUngrabPointer reply_ungrab_pointer; + cairo_region_t *area; + cairo_surface_t *surface; + + client->last_seen_serial = request->base.serial; + + switch (request->base.type) + { + case BROADWAY_REQUEST_NEW_WINDOW: + reply_new_window.id = + _gdk_broadway_server_new_window (server, + request->new_window.x, + request->new_window.y, + request->new_window.width, + request->new_window.height, + request->new_window.is_temp); + + send_reply (client, request, (BroadwayReply *)&reply_new_window, sizeof (reply_new_window), + BROADWAY_REPLY_NEW_WINDOW); + break; + case BROADWAY_REQUEST_FLUSH: + _gdk_broadway_server_flush (server); + break; + case BROADWAY_REQUEST_SYNC: + _gdk_broadway_server_flush (server); + send_reply (client, request, (BroadwayReply *)&reply_sync, sizeof (reply_sync), + BROADWAY_REPLY_SYNC); + break; + case BROADWAY_REQUEST_QUERY_MOUSE: + _gdk_broadway_server_query_mouse (server, + &reply_query_mouse.toplevel, + &reply_query_mouse.root_x, + &reply_query_mouse.root_y, + &reply_query_mouse.mask); + send_reply (client, request, (BroadwayReply *)&reply_query_mouse, sizeof (reply_query_mouse), + BROADWAY_REPLY_QUERY_MOUSE); + break; + case BROADWAY_REQUEST_DESTROY_WINDOW: + _gdk_broadway_server_destroy_window (server, request->destroy_window.id); + break; + case BROADWAY_REQUEST_SHOW_WINDOW: + _gdk_broadway_server_window_show (server, request->show_window.id); + break; + case BROADWAY_REQUEST_HIDE_WINDOW: + _gdk_broadway_server_window_hide (server, request->hide_window.id); + break; + case BROADWAY_REQUEST_SET_TRANSIENT_FOR: + _gdk_broadway_server_window_set_transient_for (server, + request->set_transient_for.id, + request->set_transient_for.parent); + break; + case BROADWAY_REQUEST_TRANSLATE: + area = region_from_rects (request->translate.rects, + request->translate.n_rects); + _gdk_broadway_server_window_translate (server, + request->translate.id, + area, + request->translate.dx, + request->translate.dy); + cairo_region_destroy (area); + break; + case BROADWAY_REQUEST_UPDATE: + surface = open_surface (request->update.name, + request->update.width, + request->update.height); + if (surface != NULL) + { + _gdk_broadway_server_window_update (server, + request->update.id, + surface); + cairo_surface_destroy (surface); + } + break; + case BROADWAY_REQUEST_MOVE_RESIZE: + if (!_gdk_broadway_server_window_move_resize (server, + request->move_resize.id, + request->move_resize.x, + request->move_resize.y, + request->move_resize.width, + request->move_resize.height)) + { + /* TODO: Send configure request */ + } + break; + case BROADWAY_REQUEST_GRAB_POINTER: + reply_grab_pointer.status = + _gdk_broadway_server_grab_pointer (server, + request->grab_pointer.id, + request->grab_pointer.owner_events, + request->grab_pointer.event_mask, + request->grab_pointer.time_); + send_reply (client, request, (BroadwayReply *)&reply_grab_pointer, sizeof (reply_grab_pointer), + BROADWAY_REPLY_GRAB_POINTER); + break; + case BROADWAY_REQUEST_UNGRAB_POINTER: + reply_ungrab_pointer.status = + _gdk_broadway_server_ungrab_pointer (server, + request->ungrab_pointer.time_); + send_reply (client, request, (BroadwayReply *)&reply_ungrab_pointer, sizeof (reply_ungrab_pointer), + BROADWAY_REPLY_UNGRAB_POINTER); + break; + default: + g_warning ("Unknown request of type %d\n", request->base.type); + } +} + +static void +client_fill_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + BroadwayClient *client = user_data; + gssize res; + + res = g_buffered_input_stream_fill_finish (client->in, result, NULL); + + if (res > 0) + { + guint32 size; + gsize count, remaining; + guint8 *buffer; + BroadwayRequest request; + + buffer = (guint8 *)g_buffered_input_stream_peek_buffer (client->in, &count); + + remaining = count; + while (remaining >= sizeof (guint32)) + { + memcpy (&size, buffer, sizeof (guint32)); + + if (size <= remaining) + { + g_assert (size >= sizeof (BroadwayRequestBase)); + g_assert (size <= sizeof (BroadwayRequest)); + + memcpy (&request, buffer, size); + client_handle_request (client, &request); + + remaining -= size; + buffer += size; + } + } + + /* This is guaranteed not to block */ + g_input_stream_skip (G_INPUT_STREAM (client->in), count - remaining, NULL, NULL); + + g_buffered_input_stream_fill_async (client->in, + 4*1024, + 0, + NULL, + client_fill_cb, client); + } + else + { + client_disconnected (client); + } +} + + +static gboolean +incoming_client (GSocketService *service, + GSocketConnection *connection, + GObject *source_object) +{ + BroadwayClient *client; + GInputStream *input; + + client = g_new0 (BroadwayClient, 1); + client->id = client_id_count++; + client->connection = g_object_ref (connection); + + input = g_io_stream_get_input_stream (G_IO_STREAM (client->connection)); + client->in = (GBufferedInputStream *)g_buffered_input_stream_new (input); + + clients = g_list_prepend (clients, client); + + g_buffered_input_stream_fill_async (client->in, + 4*1024, + 0, + NULL, + client_fill_cb, client); + + return TRUE; +} + int main (int argc, char *argv[]) { - GdkBroadwayServer *server; GError *error; GMainLoop *loop; + GSocketAddress *address; + GSocketService *listener; + char *path; error = NULL; server = _gdk_broadway_server_new (8080, &error); @@ -17,16 +350,90 @@ main (int argc, char *argv[]) return 1; } + path = g_build_filename (g_get_user_runtime_dir (), "broadway1.socket", NULL); + g_print ("Listening on %s\n", path); + address = g_unix_socket_address_new_with_type (path, -1, + G_UNIX_SOCKET_ADDRESS_ABSTRACT); + g_free (path); + + listener = g_socket_service_new (); + if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener), + address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + G_OBJECT (server), + NULL, + &error)) + { + g_printerr ("Can't listen: %s\n", error->message); + return 1; + } + g_object_unref (address); + + g_signal_connect (listener, "incoming", G_CALLBACK (incoming_client), NULL); + + g_socket_service_start (G_SOCKET_SERVICE (listener)); + loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); return 0; } - -/* TODO: */ +static gsize +get_event_size (int type) +{ + switch (type) + { + case BROADWAY_EVENT_ENTER: + case BROADWAY_EVENT_LEAVE: + return sizeof (BroadwayInputCrossingMsg); + case BROADWAY_EVENT_POINTER_MOVE: + return sizeof (BroadwayInputPointerMsg); + case BROADWAY_EVENT_BUTTON_PRESS: + case BROADWAY_EVENT_BUTTON_RELEASE: + return sizeof (BroadwayInputButtonMsg); + case BROADWAY_EVENT_SCROLL: + return sizeof (BroadwayInputScrollMsg); + case BROADWAY_EVENT_KEY_PRESS: + case BROADWAY_EVENT_KEY_RELEASE: + return sizeof (BroadwayInputKeyMsg); + case BROADWAY_EVENT_GRAB_NOTIFY: + case BROADWAY_EVENT_UNGRAB_NOTIFY: + return sizeof (BroadwayInputGrabReply); + case BROADWAY_EVENT_CONFIGURE_NOTIFY: + return sizeof (BroadwayInputConfigureNotify); + case BROADWAY_EVENT_DELETE_NOTIFY: + return sizeof (BroadwayInputDeleteNotify); + case BROADWAY_EVENT_SCREEN_SIZE_CHANGED: + return sizeof (BroadwayInputScreenResizeNotify); + default: + g_assert_not_reached (); + } + return 0; +} void _gdk_broadway_events_got_input (BroadwayInputMsg *message) { + GList *l; + BroadwayReplyEvent reply_event; + gsize size; + + size = get_event_size (message->base.type); + + reply_event.msg = *message; + + /* TODO: + Don't send to all clients + Rewrite serials, etc + */ + for (l = clients; l != NULL; l = l->next) + { + BroadwayClient *client = l->data; + + send_reply (client, NULL, (BroadwayReply *)&reply_event, + sizeof (BroadwayReplyBase) + size, + BROADWAY_REPLY_EVENT); + } } diff --git a/gdk/broadway/broadway.h b/gdk/broadway/broadway.h index 269a59d77e..02436f4189 100644 --- a/gdk/broadway/broadway.h +++ b/gdk/broadway/broadway.h @@ -3,14 +3,10 @@ #include <glib.h> #include <gio/gio.h> +#include "broadway-protocol.h" typedef struct BroadwayOutput BroadwayOutput; -typedef struct { - int x, y; - int width, height; -} BroadwayRect; - typedef enum { BROADWAY_WS_CONTINUATION = 0, BROADWAY_WS_TEXT = 1, diff --git a/gdk/broadway/gdkbroadway-server-client.c b/gdk/broadway/gdkbroadway-server-client.c new file mode 100644 index 0000000000..ef3ef31802 --- /dev/null +++ b/gdk/broadway/gdkbroadway-server-client.c @@ -0,0 +1,693 @@ +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "gdkbroadway-server.h" + +#include "broadway.h" +#include "gdkprivate-broadway.h" + +#include <glib.h> +#include <glib/gprintf.h> +#include <gio/gunixsocketaddress.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +typedef struct BroadwayInput BroadwayInput; + +struct _GdkBroadwayServer { + GObject parent_instance; + + guint32 next_serial; + GSocketConnection *connection; + + guint32 recv_buffer_size; + guint8 recv_buffer[1024]; + + guint process_input_idle; + GList *incomming; + +}; + +struct _GdkBroadwayServerClass +{ + GObjectClass parent_class; +}; + +static gboolean input_available_cb (gpointer stream, gpointer user_data); + +G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT) + +static void +gdk_broadway_server_init (GdkBroadwayServer *server) +{ + server->next_serial = 1; +} + +static void +gdk_broadway_server_finalize (GObject *object) +{ + G_OBJECT_CLASS (gdk_broadway_server_parent_class)->finalize (object); +} + +static void +gdk_broadway_server_class_init (GdkBroadwayServerClass * class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = gdk_broadway_server_finalize; +} + +gboolean +_gdk_broadway_server_lookahead_event (GdkBroadwayServer *server, + const char *types) +{ + + return FALSE; +} + +gulong +_gdk_broadway_server_get_next_serial (GdkBroadwayServer *server) +{ + return (gulong)server->next_serial; +} + +GdkBroadwayServer * +_gdk_broadway_server_new (int port, GError **error) +{ + GdkBroadwayServer *server; + char *basename; + GSocketClient *client; + GSocketConnection *connection; + GSocketAddress *address; + GPollableInputStream *pollable; + GInputStream *in; + GSource *source; + char *path; + + basename = g_strdup_printf ("broadway%d.socket", port); + path = g_build_filename (g_get_user_runtime_dir (), basename, NULL); + g_free (basename); + + address = g_unix_socket_address_new_with_type (path, -1, + G_UNIX_SOCKET_ADDRESS_ABSTRACT); + g_free (path); + + client = g_socket_client_new (); + + error = NULL; + connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (address), NULL, error); + + g_object_unref (address); + g_object_unref (client); + + if (connection == NULL) + return NULL; + + server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL); + server->connection = connection; + + in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection)); + pollable = G_POLLABLE_INPUT_STREAM (in); + + source = g_pollable_input_stream_create_source (pollable, NULL); + g_source_attach (source, NULL); + g_source_set_callback (source, (GSourceFunc)input_available_cb, server, NULL); + + return server; +} + +guint32 +_gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server) +{ + return 0; +} + +static guint32 +gdk_broadway_server_send_message_with_size (GdkBroadwayServer *server, BroadwayRequestBase *base, + gsize size, guint32 type) +{ + GOutputStream *out; + gsize written; + + base->size = size; + base->type = type; + base->serial = server->next_serial++; + + out = g_io_stream_get_output_stream (G_IO_STREAM (server->connection)); + + if (!g_output_stream_write_all (out, base, size, &written, NULL, NULL)) + { + g_printerr ("Unable to write to server\n"); + exit (1); + } + + g_assert (written == size); + + return base->serial; +} + +#define gdk_broadway_server_send_message(_server, _msg, _type) \ + gdk_broadway_server_send_message_with_size(_server, (BroadwayRequestBase *)&_msg, sizeof (_msg), _type) + +static void +parse_all_input (GdkBroadwayServer *server) +{ + guint8 *p, *end; + guint32 size; + BroadwayReply *reply; + + p = server->recv_buffer; + end = p + server->recv_buffer_size; + + while (p + sizeof (guint32) <= end) + { + memcpy (&size, p, sizeof (guint32)); + if (p + size > end) + break; + + reply = g_memdup (p, size); + p += size; + + server->incomming = g_list_append (server->incomming, reply); + } + + if (p < end) + memmove (server->recv_buffer, p, end - p); + server->recv_buffer_size = end - p; +} + +static void +read_some_input_blocking (GdkBroadwayServer *server) +{ + GInputStream *in; + gssize res; + + in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection)); + + g_assert (server->recv_buffer_size < sizeof (server->recv_buffer)); + res = g_input_stream_read (in, &server->recv_buffer[server->recv_buffer_size], + sizeof (server->recv_buffer) - server->recv_buffer_size, + NULL, NULL); + + if (res <= 0) + { + g_printerr ("Unable to read from broadway server\n"); + exit (1); + } + + server->recv_buffer_size += res; +} + +static void +read_some_input_nonblocking (GdkBroadwayServer *server) +{ + GInputStream *in; + GPollableInputStream *pollable; + gssize res; + GError *error; + + in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection)); + pollable = G_POLLABLE_INPUT_STREAM (in); + + g_assert (server->recv_buffer_size < sizeof (server->recv_buffer)); + error = NULL; + res = g_pollable_input_stream_read_nonblocking (pollable, &server->recv_buffer[server->recv_buffer_size], + sizeof (server->recv_buffer) - server->recv_buffer_size, + NULL, &error); + + if (res < 0 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + { + g_error_free (error); + res = 0; + } + else if (res <= 0) + { + g_printerr ("Unable to read from broadway server: %s\n", error ? error->message : "eof"); + exit (1); + } + + server->recv_buffer_size += res; +} + +static BroadwayReply * +find_response_by_serial (GdkBroadwayServer *server, guint32 serial) +{ + GList *l; + + for (l = server->incomming; l != NULL; l = l->next) + { + BroadwayReply *reply = l->data; + + if (reply->base.in_reply_to == serial) + return reply; + } + + return NULL; +} + +static void +process_input_messages (GdkBroadwayServer *server) +{ + BroadwayReply *reply; + + if (server->process_input_idle != 0) + { + g_source_remove (server->process_input_idle); + server->process_input_idle = 0; + } + + while (server->incomming) + { + reply = server->incomming->data; + server->incomming = + g_list_delete_link (server->incomming, + server->incomming); + + if (reply->base.type == BROADWAY_REPLY_EVENT) + _gdk_broadway_events_got_input (&reply->event.msg); + else + g_warning ("Unhandled reply type %d\n", reply->base.type); + g_free (reply); + } +} + +static gboolean +process_input_idle_cb (GdkBroadwayServer *server) +{ + server->process_input_idle = 0; + process_input_messages (server); + return G_SOURCE_REMOVE; +} + +static void +queue_process_input_at_idle (GdkBroadwayServer *server) +{ + if (server->process_input_idle == 0) + server->process_input_idle = + g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL); +} + +static gboolean +input_available_cb (gpointer stream, gpointer user_data) +{ + GdkBroadwayServer *server = user_data; + + read_some_input_nonblocking (server); + parse_all_input (server); + + process_input_messages (server); + + return G_SOURCE_CONTINUE; +} + +static BroadwayReply * +gdk_broadway_server_wait_for_reply (GdkBroadwayServer *server, + guint32 serial) +{ + BroadwayReply *reply; + + while (TRUE) + { + reply = find_response_by_serial (server, serial); + if (reply) + { + server->incomming = g_list_remove (server->incomming, reply); + return reply; + } + + read_some_input_blocking (server); + parse_all_input (server); + } + + queue_process_input_at_idle (server); +} + +void +_gdk_broadway_server_flush (GdkBroadwayServer *server) +{ + BroadwayRequestFlush msg; + + gdk_broadway_server_send_message(server, msg, BROADWAY_REQUEST_FLUSH); +} + +void +_gdk_broadway_server_sync (GdkBroadwayServer *server) +{ + BroadwayRequestSync msg; + guint32 serial; + BroadwayReply *reply; + + serial = gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_SYNC); + reply = gdk_broadway_server_wait_for_reply (server, serial); + + g_assert (reply->base.type == BROADWAY_REPLY_SYNC); + + g_free (reply); + + return; +} + +void +_gdk_broadway_server_query_mouse (GdkBroadwayServer *server, + guint32 *toplevel, + gint32 *root_x, + gint32 *root_y, + guint32 *mask) +{ + BroadwayRequestQueryMouse msg; + guint32 serial; + BroadwayReply *reply; + + serial = gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_QUERY_MOUSE); + reply = gdk_broadway_server_wait_for_reply (server, serial); + + g_assert (reply->base.type == BROADWAY_REPLY_QUERY_MOUSE); + + if (toplevel) + *toplevel = reply->query_mouse.toplevel; + if (root_x) + *root_x = reply->query_mouse.root_x; + if (root_y) + *root_y = reply->query_mouse.root_y; + if (mask) + *mask = reply->query_mouse.mask; + + g_free (reply); +} + +guint32 +_gdk_broadway_server_new_window (GdkBroadwayServer *server, + int x, + int y, + int width, + int height, + gboolean is_temp) +{ + BroadwayRequestNewWindow msg; + guint32 serial, id; + BroadwayReply *reply; + + msg.x = x; + msg.y = y; + msg.width = width; + msg.height = height; + msg.is_temp = is_temp; + + serial = gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_NEW_WINDOW); + reply = gdk_broadway_server_wait_for_reply (server, serial); + + g_assert (reply->base.type == BROADWAY_REPLY_NEW_WINDOW); + + id = reply->new_window.id; + + g_free (reply); + + return id; +} + +void +_gdk_broadway_server_destroy_window (GdkBroadwayServer *server, + gint id) +{ + BroadwayRequestDestroyWindow msg; + + msg.id = id; + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_DESTROY_WINDOW); +} + +gboolean +_gdk_broadway_server_window_show (GdkBroadwayServer *server, + gint id) +{ + BroadwayRequestShowWindow msg; + + msg.id = id; + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_SHOW_WINDOW); + + return TRUE; +} + +gboolean +_gdk_broadway_server_window_hide (GdkBroadwayServer *server, + gint id) +{ + BroadwayRequestHideWindow msg; + + msg.id = id; + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_HIDE_WINDOW); + + return TRUE; +} + +void +_gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server, + gint id, gint parent) +{ + BroadwayRequestSetTransientFor msg; + + msg.id = id; + msg.parent = parent; + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_SET_TRANSIENT_FOR); +} + +gboolean +_gdk_broadway_server_has_client (GdkBroadwayServer *server) +{ + /* TODO */ + return FALSE; +} + +gboolean +_gdk_broadway_server_window_translate (GdkBroadwayServer *server, + gint id, + cairo_region_t *area, + gint dx, + gint dy) +{ + BroadwayRequestTranslate *msg; + cairo_rectangle_int_t rect; + int i, n_rects; + gsize msg_size; + + n_rects = cairo_region_num_rectangles (area); + + msg_size = sizeof (BroadwayRequestTranslate) + (n_rects-1) * sizeof (BroadwayRect); + msg = g_malloc (msg_size); + + msg->id = id; + msg->dx = dx; + msg->dy = dy; + msg->n_rects = n_rects; + + for (i = 0; i < n_rects; i++) + { + cairo_region_get_rectangle (area, i, &rect); + msg->rects[i].x = rect.x; + msg->rects[i].y = rect.y; + msg->rects[i].width = rect.width; + msg->rects[i].height = rect.height; + } + + gdk_broadway_server_send_message_with_size (server, (BroadwayRequestBase *)msg, msg_size, + BROADWAY_REQUEST_TRANSLATE); + g_free (msg); + return TRUE; +} + +static char +make_valid_fs_char (char c) +{ + if (c == 0 || c == '/') + return c + 1; + return c; +} + +/* name must have at least space for 34 bytes */ +static int +create_random_shm (char *name) +{ + guint32 r; + int i, o, fd; + + while (TRUE) + { + o = 0; + name[o++] = '/'; + for (i = 0; i < 32/4; i++) + { + r = g_random_int (); + name[o++] = make_valid_fs_char ((r >> 0) & 0xff); + name[o++] = make_valid_fs_char ((r >> 8) & 0xff); + name[o++] = make_valid_fs_char ((r >> 16) & 0xff); + name[o++] = make_valid_fs_char ((r >> 24) & 0xff); + } + name[o++] = 0; + + fd = shm_open(name, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + return fd; + + if (errno != EEXIST) + { + g_printerr ("Unable to allocate shared mem for window"); + exit (1); + } + } + +} + +static const cairo_user_data_key_t gdk_broadway_shm_cairo_key; + +typedef struct { + char name[34]; + void *data; + gsize data_size; +} BroadwayShmSurfaceData; + +static void +shm_data_destroy (void *_data) +{ + BroadwayShmSurfaceData *data = _data; + + munmap (data->data, data->data_size); + shm_unlink (data->name); + g_free (data); +} + +cairo_surface_t * +_gdk_broadway_server_create_surface (int width, + int height) +{ + BroadwayShmSurfaceData *data; + cairo_surface_t *surface; + int res; + int fd; + + data = g_new (BroadwayShmSurfaceData, 1); + data->data_size = width * height * sizeof (guint32); + + fd = create_random_shm (data->name); + + res = ftruncate (fd, data->data_size); + g_assert (res != -1); + + data->data = mmap(0, data->data_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + (void) close(fd); + + surface = cairo_image_surface_create_for_data ((guchar *)data->data, + CAIRO_FORMAT_RGB24, width, height, width * sizeof (guint32)); + g_assert (surface != NULL); + + cairo_surface_set_user_data (surface, &gdk_broadway_shm_cairo_key, + data, shm_data_destroy); + + return surface; +} + +void +_gdk_broadway_server_window_update (GdkBroadwayServer *server, + gint id, + cairo_surface_t *surface) +{ + BroadwayRequestUpdate msg; + BroadwayShmSurfaceData *data; + + data = cairo_surface_get_user_data (surface, &gdk_broadway_shm_cairo_key); + g_assert (data != NULL); + + msg.id = id; + memcpy (msg.name, data->name, 34); + msg.width = cairo_image_surface_get_width (surface); + msg.height = cairo_image_surface_get_height (surface); + + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_UPDATE); +} + +gboolean +_gdk_broadway_server_window_move_resize (GdkBroadwayServer *server, + gint id, + int x, + int y, + int width, + int height) +{ + BroadwayRequestMoveResize msg; + + msg.id = id; + msg.x = x; + msg.y = y; + msg.width = width; + msg.height = height; + + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_MOVE_RESIZE); + + return TRUE; +} + +GdkGrabStatus +_gdk_broadway_server_grab_pointer (GdkBroadwayServer *server, + gint id, + gboolean owner_events, + guint32 event_mask, + guint32 time_) +{ + BroadwayRequestGrabPointer msg; + guint32 serial, status; + BroadwayReply *reply; + + msg.id = id; + msg.owner_events = owner_events; + msg.event_mask = event_mask; + msg.time_ = time_; + + serial = gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_GRAB_POINTER); + reply = gdk_broadway_server_wait_for_reply (server, serial); + + g_assert (reply->base.type == BROADWAY_REPLY_GRAB_POINTER); + + status = reply->grab_pointer.status; + + g_free (reply); + + return status; +} + +guint32 +_gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server, + guint32 time_) +{ + BroadwayRequestUngrabPointer msg; + guint32 serial, status; + BroadwayReply *reply; + + msg.time_ = time_; + + serial = gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_UNGRAB_POINTER); + reply = gdk_broadway_server_wait_for_reply (server, serial); + + g_assert (reply->base.type == BROADWAY_REPLY_UNGRAB_POINTER); + + status = reply->ungrab_pointer.status; + + g_free (reply); + + return status; +} diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c index 3a14c4cc55..4f99bea33a 100644 --- a/gdk/broadway/gdkbroadway-server.c +++ b/gdk/broadway/gdkbroadway-server.c @@ -594,7 +594,7 @@ _gdk_broadway_server_read_all_input_nonblocking (GdkBroadwayServer *server) broadway_input_free (input); if (res < 0) { - g_print ("input error %s\n", error->message); + g_printerr ("input error %s\n", error->message); g_error_free (error); } return; @@ -1172,7 +1172,7 @@ _gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server) void _gdk_broadway_server_query_mouse (GdkBroadwayServer *server, - gint32 *toplevel, + guint32 *toplevel, gint32 *root_x, gint32 *root_y, guint32 *mask) @@ -1690,6 +1690,7 @@ _gdk_broadway_server_resync_windows (GdkBroadwayServer *server) cairo_image_surface_get_stride (window->last_surface), cairo_image_surface_get_data (window->last_surface)); } + broadway_output_surface_flush (server->output, window->id); } } diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h index fdd769ad18..c93b8d7671 100644 --- a/gdk/broadway/gdkbroadway-server.h +++ b/gdk/broadway/gdkbroadway-server.h @@ -14,7 +14,6 @@ typedef struct _GdkBroadwayServerClass GdkBroadwayServerClass; #define GDK_IS_BROADWAY_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_SERVER)) #define GDK_BROADWAY_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServerClass)) - GdkBroadwayServer *_gdk_broadway_server_new (int port, GError **error); gboolean _gdk_broadway_server_has_client (GdkBroadwayServer *server); @@ -25,9 +24,9 @@ guint32 _gdk_broadway_server_get_last_seen_time (GdkBroadwaySer gboolean _gdk_broadway_server_lookahead_event (GdkBroadwayServer *server, const char *types); void _gdk_broadway_server_query_mouse (GdkBroadwayServer *server, - gint *toplevel, - gint *root_x, - gint *root_y, + guint32 *toplevel, + gint32 *root_x, + gint32 *root_y, guint32 *mask); GdkGrabStatus _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server, gint id, @@ -57,6 +56,8 @@ gboolean _gdk_broadway_server_window_translate (GdkBroadwaySer cairo_region_t *area, gint dx, gint dy); +cairo_surface_t *_gdk_broadway_server_create_surface (int width, + int height); void _gdk_broadway_server_window_update (GdkBroadwayServer *server, gint id, cairo_surface_t *surface); diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c index ad5d24b6ff..ef3084c8fc 100644 --- a/gdk/broadway/gdkdisplay-broadway.c +++ b/gdk/broadway/gdkdisplay-broadway.c @@ -159,7 +159,7 @@ _gdk_broadway_display_open (const gchar *display_name) if (display_name != NULL) port = strtol(display_name, NULL, 10); if (port == 0) - port = 8080; + port = 1; broadway_display->server = _gdk_broadway_server_new (port, NULL); if (broadway_display->server == NULL) diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c index 9709ff6864..e676cc6d46 100644 --- a/gdk/broadway/gdkwindow-broadway.c +++ b/gdk/broadway/gdkwindow-broadway.c @@ -241,9 +241,8 @@ _gdk_broadway_window_resize_surface (GdkWindow *window) { old = impl->surface; - impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - gdk_window_get_width (impl->wrapper), - gdk_window_get_height (impl->wrapper)); + impl->surface = _gdk_broadway_server_create_surface (gdk_window_get_width (impl->wrapper), + gdk_window_get_height (impl->wrapper)); cairo_surface_destroy (old); } @@ -281,7 +280,7 @@ gdk_window_broadway_ref_cairo_surface (GdkWindow *window) /* Create actual backing store if missing */ if (!impl->surface) - impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + impl->surface = _gdk_broadway_server_create_surface (w, h); /* Create a destroyable surface referencing the real one */ if (!impl->ref_surface) |