diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h index 0e82e94297..58e64347b0 100644 --- a/gdk/broadway/broadway-protocol.h +++ b/gdk/broadway/broadway-protocol.h @@ -157,7 +157,9 @@ typedef enum { BROADWAY_REQUEST_GRAB_POINTER, BROADWAY_REQUEST_UNGRAB_POINTER, BROADWAY_REQUEST_FOCUS_WINDOW, - BROADWAY_REQUEST_SET_SHOW_KEYBOARD + BROADWAY_REQUEST_SET_SHOW_KEYBOARD, + BROADWAY_REQUEST_UPLOAD_TEXTURE, + BROADWAY_REQUEST_RELEASE_TEXTURE, } BroadwayRequestType; typedef struct { @@ -194,6 +196,18 @@ typedef struct { guint32 height; } BroadwayRequestUpdate; +typedef struct { + BroadwayRequestBase base; + guint32 id; + guint32 offset; + guint32 size; +} BroadwayRequestUploadTexture; + +typedef struct { + BroadwayRequestBase base; + guint32 id; +} BroadwayRequestReleaseTexture; + typedef struct { BroadwayRequestBase base; guint32 id; @@ -248,6 +262,8 @@ typedef union { BroadwayRequestTranslate translate; BroadwayRequestFocusWindow focus_window; BroadwayRequestSetShowKeyboard set_show_keyboard; + BroadwayRequestUploadTexture upload_texture; + BroadwayRequestReleaseTexture release_texture; } BroadwayRequest; typedef enum { diff --git a/gdk/broadway/broadwayd.c b/gdk/broadway/broadwayd.c index f29eb774d8..d26c5dc6dc 100644 --- a/gdk/broadway/broadwayd.c +++ b/gdk/broadway/broadwayd.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ typedef struct { GList *windows; guint disconnect_idle; GList *fds; + GHashTable *textures; } BroadwayClient; static void @@ -75,6 +77,7 @@ client_free (BroadwayClient *client) g_string_free (client->buffer, TRUE); g_slist_free_full (client->serial_mappings, g_free); g_list_free_full (client->fds, close_fd); + g_hash_table_destroy (client->textures); g_free (client); } @@ -218,6 +221,7 @@ client_handle_request (BroadwayClient *client, BroadwayReplyUngrabPointer reply_ungrab_pointer; cairo_surface_t *surface; guint32 before_serial, now_serial; + int fd; before_serial = broadway_server_get_next_serial (server); @@ -286,6 +290,54 @@ client_handle_request (BroadwayClient *client, cairo_surface_destroy (surface); } break; + case BROADWAY_REQUEST_UPLOAD_TEXTURE: + if (client->fds == NULL) + g_warning ("FD passing mismatch"); + else + { + char *data, *p; + gsize to_read; + gssize num_read; + GBytes *texture; + + fd = GPOINTER_TO_INT (client->fds->data); + client->fds = g_list_delete_link (client->fds, client->fds); + + data = g_malloc (request->upload_texture.size); + to_read = request->upload_texture.size; + lseek (fd, request->upload_texture.offset, SEEK_SET); + + p = data; + do + { + num_read = read (fd, p, to_read); + if (num_read == -1 && errno == EAGAIN) + continue; + + if (num_read > 0) + { + p += num_read; + to_read -= num_read; + } + else + { + g_warning ("Unexpected short read of texture"); + break; + } + } + while (to_read > 0); + close (fd); + + texture = g_bytes_new_take (data, request->upload_texture.size); + g_hash_table_replace (client->textures, + GINT_TO_POINTER (request->release_texture.id), + texture); + } + break; + case BROADWAY_REQUEST_RELEASE_TEXTURE: + g_hash_table_remove (client->textures, GINT_TO_POINTER (request->release_texture.id)); + + break; case BROADWAY_REQUEST_MOVE_RESIZE: broadway_server_window_move_resize (server, request->move_resize.id, @@ -429,6 +481,7 @@ incoming_client (GSocketService *service, client = g_new0 (BroadwayClient, 1); client->id = client_id_count++; client->connection = g_object_ref (connection); + client->textures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_bytes_unref); input = g_io_stream_get_input_stream (G_IO_STREAM (client->connection)); client->in = input; diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c index 57cb48d5b8..2ee75ab128 100644 --- a/gdk/broadway/gdkbroadway-server.c +++ b/gdk/broadway/gdkbroadway-server.c @@ -1,5 +1,9 @@ #include "config.h" +#ifdef HAVE_LINUX_MEMFD_H +#include +#endif + #ifdef HAVE_SYS_MMAN_H #include #endif @@ -9,6 +13,7 @@ #include "gdkbroadway-server.h" #include "gdkprivate-broadway.h" +#include #include #include @@ -35,6 +40,7 @@ struct _GdkBroadwayServer { GObject parent_instance; guint32 next_serial; + guint32 next_texture_id; GSocketConnection *connection; guint32 recv_buffer_size; @@ -197,7 +203,6 @@ gdk_broadway_server_send_message_with_size (GdkBroadwayServer *server, BroadwayR exit (1); } - g_print ("socket send message wrote %d of %d\n", (int)bytes_written, (int)size); buf += bytes_written; size -= bytes_written; @@ -775,6 +780,120 @@ _gdk_broadway_server_window_update (GdkBroadwayServer *server, BROADWAY_REQUEST_UPDATE); } +static int +open_shared_memory (void) +{ + static gboolean force_shm_open = FALSE; + int ret = -1; + +#if !defined (__NR_memfd_create) + force_shm_open = TRUE; +#endif + + do + { +#if defined (__NR_memfd_create) + if (!force_shm_open) + { + ret = syscall (__NR_memfd_create, "gdk-broadway", MFD_CLOEXEC); + + /* fall back to shm_open until debian stops shipping 3.16 kernel + * See bug 766341 + */ + if (ret < 0 && errno == ENOSYS) + force_shm_open = TRUE; + } +#endif + + if (force_shm_open) + { + char name[NAME_MAX - 1] = ""; + + sprintf (name, "/gdk-broadway-%x", g_random_int ()); + + ret = shm_open (name, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); + + if (ret >= 0) + shm_unlink (name); + else if (errno == EEXIST) + continue; + } + } + while (ret < 0 && errno == EINTR); + + if (ret < 0) + g_critical (G_STRLOC ": creating shared memory file (using %s) failed: %m", + force_shm_open? "shm_open" : "memfd_create"); + + return ret; +} + +typedef struct { + int fd; + gsize size; +} PngData; + +static cairo_status_t +write_png_cb (void *closure, + const guchar *data, + unsigned int length) +{ + PngData *png_data = closure; + int fd = png_data->fd; + + while (length) + { + gssize ret = write (fd, data, length); + + if (ret <= 0) + return CAIRO_STATUS_WRITE_ERROR; + + png_data->size += ret; + length -= ret; + data += ret; + } + + return CAIRO_STATUS_SUCCESS; +} + +guint32 +gdk_broadway_server_upload_texture (GdkBroadwayServer *server, + GdkTexture *texture) +{ + guint32 id; + cairo_surface_t *surface = gdk_texture_download_surface (texture); + BroadwayRequestUploadTexture msg; + PngData data; + + id = ++server->next_texture_id; + + data.fd = open_shared_memory (); + data.size = 0; + cairo_surface_write_to_png_stream (surface, write_png_cb, &data); + + msg.id = id; + msg.offset = 0; + msg.size = data.size; + + /* This passes ownership of fd */ + gdk_broadway_server_send_fd_message (server, msg, + BROADWAY_REQUEST_UPLOAD_TEXTURE, data.fd); + + return id; +} + +void +gdk_broadway_server_release_texture (GdkBroadwayServer *server, + guint32 id) +{ + BroadwayRequestReleaseTexture msg; + + msg.id = id; + + gdk_broadway_server_send_message (server, msg, + BROADWAY_REQUEST_RELEASE_TEXTURE); +} + gboolean _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server, gint id, diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h index 89a3076992..ea16e5598f 100644 --- a/gdk/broadway/gdkbroadway-server.h +++ b/gdk/broadway/gdkbroadway-server.h @@ -59,6 +59,10 @@ gboolean _gdk_broadway_server_window_translate (GdkBroadwaySer cairo_region_t *area, gint dx, gint dy); +guint32 gdk_broadway_server_upload_texture (GdkBroadwayServer *server, + GdkTexture *texture); +void gdk_broadway_server_release_texture (GdkBroadwayServer *server, + guint32 id); cairo_surface_t *_gdk_broadway_server_create_surface (int width, int height); void _gdk_broadway_server_window_update (GdkBroadwayServer *server,