]> Pileus Git - ~andy/gtk/commitdiff
broadway: Initial version of separate broadway server
authorAlexander Larsson <alexl@redhat.com>
Fri, 21 Dec 2012 23:02:55 +0000 (00:02 +0100)
committerAlexander Larsson <alexl@redhat.com>
Thu, 27 Dec 2012 21:56:03 +0000 (22:56 +0100)
This kinda works but is very rudimentary

gdk/broadway/Makefile.am
gdk/broadway/broadway-protocol.h
gdk/broadway/broadway-server.c
gdk/broadway/broadway.h
gdk/broadway/gdkbroadway-server-client.c [new file with mode: 0644]
gdk/broadway/gdkbroadway-server.c
gdk/broadway/gdkbroadway-server.h
gdk/broadway/gdkdisplay-broadway.c
gdk/broadway/gdkwindow-broadway.c

index 2352c666fa21bd3ee7cd4e09075926a2316b7c91..39b0a4b5558ee7a6711df5782835e99f3bad656e 100644 (file)
@@ -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)
index 734212a2cdc9928a856f14663e68fff5387e975f..6472910be3a3c8e04083577e116bb3978478e2db 100644 (file)
@@ -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__ */
index 7395abd1ea6316147723889ab837a40d81a0bb68..abb750f8575be0e291e36a7293623cabc943ff43 100644 (file)
+#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);
+    }
 }
index 269a59d77e7ebd582c19f18ba9d360ea0c8ca85a..02436f4189ac37eccf0c5ea0b9e21e0824e1f650 100644 (file)
@@ -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 (file)
index 0000000..ef3ef31
--- /dev/null
@@ -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;
+}
index 3a14c4cc551141c2b00d161c3511f38b59de54d5..4f99bea33a08b2104f12078acf45c945ca6bce56 100644 (file)
@@ -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);
        }
     }
 
index fdd769ad18a71c1d7f6a2ce3d0c4139197ad0f9a..c93b8d767163fd8c8ea012e4b55c696ad29b24a3 100644 (file)
@@ -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);
index ad5d24b6ff8ee29b4e55f40ee8bc9887d960b748..ef3084c8fcfb643f9e3b74c076239b4708f9f715 100644 (file)
@@ -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)
index 9709ff686403f86a14ac49b075573f0fc8bcc3e7..e676cc6d464f58f5dd4dbaef41327e68f033b4b6 100644 (file)
@@ -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)