diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 513b8fd8..78587f11 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -28,10 +28,14 @@ SOFTWARE. - + The core global object. This is a special singleton object. It is used for internal Wayland protocol features. + + This interface guarantees that the semantics of requests of this interface + are unaffected by the minor version of this interface. The semantics of + events may change and new events may be added. @@ -110,7 +114,7 @@ - + The singleton global registry object. The server has a number of global objects that are available to all clients. These objects @@ -132,6 +136,10 @@ request. This creates a client-side handle that lets the object emit events to the client and lets the client invoke requests on the object. + + This interface guarantees that the semantics of the messages of this + interface are unaffected by the minor version of this interface; and that + no new events will be added to any minor version of this interface. @@ -171,6 +179,14 @@ + + + + + + Objects created from this registry are not affected by this request. + + @@ -3362,4 +3378,56 @@ + + + This global allows the IPC library to upgrade the version of the + wl_display object. + + This global must not be bound more than once over the lifetime of the + connection, otherwise the already_bound error is emitted. + + This global should only be bound by the IPC library because the IPC + library must be aware of the version of the wl_display object. + + A compositor that advertises this global must also advertise the wl_fixes + global. + + + + + These errors can be emitted in response to wl_display_upgrade requests. + + + + + + + + + This event informs the client of the maximum wl_display version + supported by the compositor. It is sent immediately after this global + has been bound. The version is guaranteed to be at least 1. + + + + + + + Negotiates an upgrade of the version of the wl_display object. + + At the time of the request, no objects other than the wl_display and + this object must exist, otherwise the has_objects error is emitted. In + particular, the client must use wl_fixes to destroy the registry from + which this object was created. + + The version must be at least 1 and must not be larger than the version + sent in the version event, otherwise the out_of_bounds error is emitted. + + + + + diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index e0523e49..24911d6c 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -245,6 +245,9 @@ wl_display_connect_to_fd(int fd); void wl_display_disconnect(struct wl_display *display); +void +wl_display_upgrade(struct wl_display *display); + int wl_display_get_fd(struct wl_display *display); diff --git a/src/wayland-client.c b/src/wayland-client.c index 50b3b4b2..19b4bb7f 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1405,6 +1405,84 @@ wl_display_disconnect(struct wl_display *display) free(display); } +struct upgrade_listener { + struct wl_fixes *wl_fixes; + struct wl_display_upgrade *wl_display_upgrade; + uint32_t version; +}; + +static void upgrade_max_version(void *data, + struct wl_display_upgrade *wl_display_upgrade, + uint32_t version) +{ + struct upgrade_listener *listener = data; + listener->version = version; +} + +static struct wl_display_upgrade_listener upgrade_upgrade_listener = { + .max_version = upgrade_max_version, +}; + +static void upgrade_global(void *data, struct wl_registry *wl_registry, + uint32_t name, const char *interface, + uint32_t version) +{ + struct upgrade_listener *listener = data; + if (!listener->wl_fixes && strcmp(interface, wl_fixes_interface.name) == 0) + listener->wl_fixes = wl_registry_bind(wl_registry, name, &wl_fixes_interface, 1); + else if (!listener->wl_display_upgrade && strcmp(interface, wl_display_upgrade_interface.name) == 0) { + listener->wl_display_upgrade = wl_registry_bind(wl_registry, name, &wl_display_upgrade_interface, 1); + wl_display_upgrade_add_listener(listener->wl_display_upgrade, &upgrade_upgrade_listener, data); + } +} + +static void upgrade_global_remove(void *data, struct wl_registry *wl_registry, + uint32_t name) +{ + // nothing +} + +static struct wl_registry_listener upgrade_registry_listener = { + .global = upgrade_global, + .global_remove = upgrade_global_remove, +}; + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +/** Upgrade the wl_display version. + * + * \param display The display context object + * + * Tries to upgrade the version of the wl_display. After this function returns, + * wl_display_get_version can be used to retrieve the new version of the + * wl_display. + * + * This function should be called immediately after creating the display. It + * must not be called any later than that or multiple times, otherwise a + * protocol error might occur. + * + * \memberof wl_display + */ +WL_EXPORT void +wl_display_upgrade(struct wl_display *display) +{ + struct wl_registry *registry = wl_display_get_registry(display); + struct upgrade_listener listener = { }; + wl_registry_add_listener(registry, &upgrade_registry_listener, &listener); + wl_display_roundtrip(display); + if (listener.wl_display_upgrade) + wl_display_roundtrip(display); + if (listener.wl_fixes) { + wl_fixes_destroy_registry(listener.wl_fixes, registry); + wl_fixes_destroy(listener.wl_fixes); + } + if (listener.wl_display_upgrade) { + uint32_t version = min(listener.version, (uint32_t)wl_display_interface.version); + wl_display_upgrade_upgrade(listener.wl_display_upgrade, version); + display->proxy.version = version; + } +} + /** Get a display context's file descriptor * * \param display The display context object diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index c8a64772..59f36a7f 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -697,6 +697,9 @@ wl_shm_buffer_create(struct wl_client *client, uint32_t id, int32_t width, int32_t height, int32_t stride, uint32_t format); +int +wl_display_init_upgrade(struct wl_display *display, uint32_t max_version); + void wl_log_set_handler_server(wl_log_func_t handler); diff --git a/src/wayland-server.c b/src/wayland-server.c index 31cc9824..bbd7bc60 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -86,6 +86,7 @@ struct wl_client { struct wl_priv_signal resource_created_signal; void *data; wl_user_data_destroy_func_t data_dtor; + bool has_upgrade; }; struct wl_display { @@ -113,6 +114,7 @@ struct wl_display { struct wl_event_source *term_source; size_t max_buffer_size; + uint32_t max_upgrade_version; }; struct wl_global { @@ -1077,8 +1079,15 @@ registry_bind(struct wl_client *client, global->bind(client, global->data, version, id); } +static void +registry_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + static const struct wl_registry_interface registry_interface = { - registry_bind + .bind = registry_bind, + .release = registry_release, }; static void @@ -1114,7 +1123,7 @@ display_get_registry(struct wl_client *client, struct wl_global *global; registry_resource = - wl_resource_create(client, &wl_registry_interface, 1, id); + wl_resource_create(client, &wl_registry_interface, resource->version, id); if (registry_resource == NULL) { wl_client_post_no_memory(client); return; @@ -2428,6 +2437,59 @@ wl_client_set_max_buffer_size(struct wl_client *client, size_t max_buffer_size) wl_connection_set_max_buffer_size(client->connection, max_buffer_size); } +static void +upgrade_display(struct wl_client *client, struct wl_resource *resource, + uint32_t version) +{ + wl_resource_destroy(resource); + // TODO: Check that there are no other objects. + if (version < 1 || version > client->display->max_upgrade_version) { + wl_resource_post_error(resource, + WL_DISPLAY_UPGRADE_ERROR_OUT_OF_BOUNDS, + "version is out of bounds"); + return; + } + client->display_resource->version = (int)version; +} + +static struct wl_display_upgrade_interface upgrade_interface = { + .upgrade = upgrade_display, +}; + +static void +bind_upgrade(struct wl_client *client, void *data, uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + resource = wl_resource_create(client, &wl_display_upgrade_interface, + (int)version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + if (client->has_upgrade) { + wl_resource_post_error(resource, + WL_DISPLAY_UPGRADE_ERROR_ALREADY_BOUND, + "wl_display_upgrade has already been bound"); + return; + } + client->has_upgrade = true; + wl_display_upgrade_send_max_version(resource, + client->display->max_upgrade_version); + wl_resource_set_implementation(resource, &upgrade_interface, NULL, NULL); +} + +WL_EXPORT int +wl_display_init_upgrade(struct wl_display *display, uint32_t max_version) +{ + if (max_version > (uint32_t)wl_display_upgrade_interface.version) + max_version = wl_display_upgrade_interface.version; + display->max_upgrade_version = max_version; + if (!wl_global_create(display, &wl_display_upgrade_interface, 1, NULL, bind_upgrade)) + return -1; + return 0; +} + /** \cond INTERNAL */ /** Initialize a wl_priv_signal object