Track protocol object versions inside wl_proxy.
authorJason Ekstrand <jason@jlekstrand.net>
Thu, 12 Nov 2015 19:53:15 +0000 (13:53 -0600)
committerDerek Foreman <derekf@osg.samsung.com>
Tue, 19 Jan 2016 19:58:50 +0000 (13:58 -0600)
This provides a standardized mechanism for tracking protocol object
versions in client code.  The wl_display object is created with version 1.
Every time an object is created from within wl_registry_bind, it gets the
bound version.  Every other time an object is created, it simply inherits
it's version from the parent object that created it.

(comments and minor reformatting added
by Derek Foreman <derekf@osg.samsung.com>)

Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Second trivial commit squashed into this one:
Authored by Derek Foreman <derekf@osg.samsung.com>
Signed-off-by: Derek Foreman <derekf@osg.samsung.com>
(it's literally one of code and a lot of comments)

This sets wl_display's version (for proxy version query purposes)
to 0.  Any proxy created with unversioned API (this happens when
a client compiled with old headers links against new wayland)
will inherit this 0.

This gives us a way for new libraries linked by old clients to
realize they can't know a proxy's version.

wl_display's version being unqueryable (always returning 0) is
an acceptable side effect, since it's a special object you can't
bind specific versions of anyway.

Second half:
Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
src/scanner.c
src/wayland-client-core.h
src/wayland-client.c

index b00f036..1d626f4 100644 (file)
@@ -956,6 +956,14 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
               interface->name, interface->name, interface->name,
               interface->name);
 
+       printf("static inline uint32_t\n"
+              "%s_get_version(struct %s *%s)\n"
+              "{\n"
+              "\treturn wl_proxy_get_version((struct wl_proxy *) %s);\n"
+              "}\n\n",
+              interface->name, interface->name, interface->name,
+              interface->name);
+
        has_destructor = 0;
        has_destroy = 0;
        wl_list_for_each(m, message_list, link) {
@@ -1027,21 +1035,31 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
 
                printf(")\n"
                       "{\n");
-               if (ret) {
+               if (ret && ret->interface_name == NULL) {
+                       /* an arg has type ="new_id" but interface is not
+                        * provided, such as in wl_registry.bind */
                        printf("\tstruct wl_proxy *%s;\n\n"
-                              "\t%s = wl_proxy_marshal_constructor("
+                              "\t%s = wl_proxy_marshal_constructor_versioned("
                               "(struct wl_proxy *) %s,\n"
-                              "\t\t\t %s_%s, ",
+                              "\t\t\t %s_%s, interface, version",
                               ret->name, ret->name,
                               interface->name,
                               interface->uppercase_name,
                               m->uppercase_name);
-
-                       if (ret->interface_name == NULL)
-                               printf("interface");
-                       else
-                               printf("&%s_interface", ret->interface_name);
+               } else if (ret) {
+                       /* Normal factory case, an arg has type="new_id" and
+                        * an interface is provided */
+                       printf("\tstruct wl_proxy *%s;\n\n"
+                              "\t%s = wl_proxy_marshal_constructor("
+                              "(struct wl_proxy *) %s,\n"
+                              "\t\t\t %s_%s, &%s_interface",
+                              ret->name, ret->name,
+                              interface->name,
+                              interface->uppercase_name,
+                              m->uppercase_name,
+                              ret->interface_name);
                } else {
+                       /* No args have type="new_id" */
                        printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n"
                               "\t\t\t %s_%s",
                               interface->name,
index a729395..91f7e7b 100644 (file)
@@ -139,9 +139,21 @@ wl_proxy_marshal_constructor(struct wl_proxy *proxy,
                             ...);
 
 struct wl_proxy *
+wl_proxy_marshal_constructor_versioned(struct wl_proxy *proxy,
+                                      uint32_t opcode,
+                                      const struct wl_interface *interface,
+                                      uint32_t version,
+                                      ...);
+struct wl_proxy *
 wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
                                   uint32_t opcode, union wl_argument *args,
                                   const struct wl_interface *interface);
+struct wl_proxy *
+wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
+                                            uint32_t opcode,
+                                            union wl_argument *args,
+                                            const struct wl_interface *interface,
+                                            uint32_t version);
 
 void
 wl_proxy_destroy(struct wl_proxy *proxy);
@@ -165,6 +177,9 @@ void *
 wl_proxy_get_user_data(struct wl_proxy *proxy);
 
 uint32_t
+wl_proxy_get_version(struct wl_proxy *proxy);
+
+uint32_t
 wl_proxy_get_id(struct wl_proxy *proxy);
 
 const char *
index 55483c6..ef12bfc 100644 (file)
@@ -62,6 +62,7 @@ struct wl_proxy {
        int refcount;
        void *user_data;
        wl_dispatcher_func_t dispatcher;
+       uint32_t version;
 };
 
 struct wl_global {
@@ -326,7 +327,8 @@ wl_display_create_queue(struct wl_display *display)
 }
 
 static struct wl_proxy *
-proxy_create(struct wl_proxy *factory, const struct wl_interface *interface)
+proxy_create(struct wl_proxy *factory, const struct wl_interface *interface,
+            uint32_t version)
 {
        struct wl_proxy *proxy;
        struct wl_display *display = factory->display;
@@ -339,6 +341,7 @@ proxy_create(struct wl_proxy *factory, const struct wl_interface *interface)
        proxy->display = display;
        proxy->queue = factory->queue;
        proxy->refcount = 1;
+       proxy->version = version;
 
        proxy->object.id = wl_map_insert_new(&display->objects, 0, proxy);
 
@@ -371,7 +374,7 @@ wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface)
        struct wl_proxy *proxy;
 
        pthread_mutex_lock(&display->mutex);
-       proxy = proxy_create(factory, interface);
+       proxy = proxy_create(factory, interface, factory->version);
        pthread_mutex_unlock(&display->mutex);
 
        return proxy;
@@ -394,6 +397,7 @@ wl_proxy_create_for_id(struct wl_proxy *factory,
        proxy->display = display;
        proxy->queue = factory->queue;
        proxy->refcount = 1;
+       proxy->version = factory->version;
 
        wl_map_insert_at(&display->objects, 0, id, proxy);
 
@@ -525,7 +529,7 @@ wl_proxy_add_dispatcher(struct wl_proxy *proxy,
 static struct wl_proxy *
 create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
                      union wl_argument *args,
-                     const struct wl_interface *interface)
+                     const struct wl_interface *interface, uint32_t version)
 {
        int i, count;
        const char *signature;
@@ -539,7 +543,7 @@ create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
 
                switch (arg.type) {
                case 'n':
-                       new_proxy = proxy_create(proxy, interface);
+                       new_proxy = proxy_create(proxy, interface, version);
                        if (new_proxy == NULL)
                                return NULL;
 
@@ -564,7 +568,8 @@ create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
  *
  * For new-id arguments, this function will allocate a new wl_proxy
  * and send the ID to the server.  The new wl_proxy will be returned
- * on success or NULL on errror with errno set accordingly.
+ * on success or NULL on errror with errno set accordingly.  The newly
+ * created proxy will inherit their version from their parent.
  *
  * \note This is intended to be used by language bindings and not in
  * non-generated code.
@@ -578,6 +583,43 @@ wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
                                   uint32_t opcode, union wl_argument *args,
                                   const struct wl_interface *interface)
 {
+       return wl_proxy_marshal_array_constructor_versioned(proxy, opcode,
+                                                           args, interface,
+                                                           proxy->version);
+}
+
+
+/** Prepare a request to be sent to the compositor
+ *
+ * \param proxy The proxy object
+ * \param opcode Opcode of the request to be sent
+ * \param args Extra arguments for the given request
+ * \param interface The interface to use for the new proxy
+ * \param version The protocol object version for the new proxy
+ *
+ * Translates the request given by opcode and the extra arguments into the
+ * wire format and write it to the connection buffer.  This version takes an
+ * array of the union type wl_argument.
+ *
+ * For new-id arguments, this function will allocate a new wl_proxy
+ * and send the ID to the server.  The new wl_proxy will be returned
+ * on success or NULL on errror with errno set accordingly.  The newly
+ * created proxy will have the version specified.
+ *
+ * \note This is intended to be used by language bindings and not in
+ * non-generated code.
+ *
+ * \sa wl_proxy_marshal()
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT struct wl_proxy *
+wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
+                                            uint32_t opcode,
+                                            union wl_argument *args,
+                                            const struct wl_interface *interface,
+                                            uint32_t version)
+{
        struct wl_closure *closure;
        struct wl_proxy *new_proxy = NULL;
        const struct wl_message *message;
@@ -587,7 +629,8 @@ wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
        message = &proxy->object.interface->methods[opcode];
        if (interface) {
                new_proxy = create_outgoing_proxy(proxy, message,
-                                                 args, interface);
+                                                 args, interface,
+                                                 version);
                if (new_proxy == NULL)
                        goto err_unlock;
        }
@@ -655,7 +698,8 @@ wl_proxy_marshal(struct wl_proxy *proxy, uint32_t opcode, ...)
  *
  * For new-id arguments, this function will allocate a new wl_proxy
  * and send the ID to the server.  The new wl_proxy will be returned
- * on success or NULL on errror with errno set accordingly.
+ * on success or NULL on errror with errno set accordingly.  The newly
+ * created proxy will inherit their version from their parent.
  *
  * \note This should not normally be used by non-generated code.
  *
@@ -677,6 +721,46 @@ wl_proxy_marshal_constructor(struct wl_proxy *proxy, uint32_t opcode,
                                                  args, interface);
 }
 
+
+/** Prepare a request to be sent to the compositor
+ *
+ * \param proxy The proxy object
+ * \param opcode Opcode of the request to be sent
+ * \param interface The interface to use for the new proxy
+ * \param version The protocol object version of the new proxy
+ * \param ... Extra arguments for the given request
+ * \return A new wl_proxy for the new_id argument or NULL on error
+ *
+ * Translates the request given by opcode and the extra arguments into the
+ * wire format and write it to the connection buffer.
+ *
+ * For new-id arguments, this function will allocate a new wl_proxy
+ * and send the ID to the server.  The new wl_proxy will be returned
+ * on success or NULL on errror with errno set accordingly.  The newly
+ * created proxy will have the version specified.
+ *
+ * \note This should not normally be used by non-generated code.
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT struct wl_proxy *
+wl_proxy_marshal_constructor_versioned(struct wl_proxy *proxy, uint32_t opcode,
+                                      const struct wl_interface *interface,
+                                      uint32_t version, ...)
+{
+       union wl_argument args[WL_CLOSURE_MAX_ARGS];
+       va_list ap;
+
+       va_start(ap, version);
+       wl_argument_from_va_list(proxy->object.interface->methods[opcode].signature,
+                                args, WL_CLOSURE_MAX_ARGS, ap);
+       va_end(ap);
+
+       return wl_proxy_marshal_array_constructor_versioned(proxy, opcode,
+                                                           args, interface,
+                                                           version);
+}
+
 /** Prepare a request to be sent to the compositor
  *
  * \param proxy The proxy object
@@ -839,6 +923,25 @@ wl_display_connect_to_fd(int fd)
        display->proxy.flags = 0;
        display->proxy.refcount = 1;
 
+       /* We set this version to 0 for backwards compatibility.
+        *
+        * If a client is using old versions of protocol headers,
+        * it will use unversioned API to create proxies.  Those
+        * proxies will inherit this 0.
+        *
+        * A client could be passing these proxies into library
+        * code newer than the headers that checks proxy
+        * versions.  When the proxy version is reported as 0
+        * the library will know that it can't reliably determine
+        * the proxy version, and should do whatever fallback is
+        * required.
+        *
+        * This trick forces wl_display to always report 0, but
+        * since it's a special object that we can't bind
+        * specific versions of anyway, this should be fine.
+        */
+       display->proxy.version = 0;
+
        display->connection = wl_connection_create(display->fd);
        if (display->connection == NULL)
                goto err_connection;
@@ -1764,6 +1867,28 @@ wl_proxy_get_user_data(struct wl_proxy *proxy)
        return proxy->user_data;
 }
 
+/** Get the protocol object version of a proxy object
+ *
+ * \param proxy The proxy object
+ * \return The protocol object version of the proxy or 0
+ *
+ * Gets the protocol object version of a proxy object, or 0
+ * if the proxy was created with unversioned API.
+ *
+ * A returned value of 0 means that no version information is
+ * available, so the caller must make safe assumptions about
+ * the object's real version.
+ *
+ * wl_display's version will always return 0.
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT uint32_t
+wl_proxy_get_version(struct wl_proxy *proxy)
+{
+       return proxy->version;
+}
+
 /** Get the id of a proxy object
  *
  * \param proxy The proxy object