Add protocol for setting the pointer image
authorKristian Høgsberg <krh@bitplanet.net>
Mon, 16 Aug 2010 14:38:29 +0000 (10:38 -0400)
committerKristian Høgsberg <krh@bitplanet.net>
Mon, 16 Aug 2010 14:38:29 +0000 (10:38 -0400)
TODO
compositor.c
compositor.h
protocol.xml
spec/main.tex

diff --git a/TODO b/TODO
index 77a7bdd..820288a 100644 (file)
--- a/TODO
+++ b/TODO
@@ -13,17 +13,6 @@ Core wayland protocol
    or do X style (content mime-type negotiation, but data goes away
    when client quits).
 
- - protocol for setting the cursor image
-
-    - should we have a mechanism to attach surface to cursor for
-      guaranteed non-laggy drag?
-
-    - drawing cursors, moving them, cursor themes, attaching surfaces
-      to cursors.  How do you change cursors when you mouse over a
-      text field if you don't have subwindows?  This is what we do: a
-      client can set a cursor for a surface and wayland will set that
-      on enter and revert to default on leave
-
  - Discard buffer, as in "wayland discarded your buffer, it's no
    longer visible, you can stop updating it now.", reattach, as in "oh
    hey, I'm about to show your buffer that I threw away, what was it
index 454d81e..0ecbb7d 100644 (file)
@@ -125,12 +125,24 @@ wlsc_matrix_transform(struct wlsc_matrix *matrix, struct wlsc_vector *v)
        *v = t;
 }
 
-static void
-wlsc_surface_init(struct wlsc_surface *surface,
-                 struct wlsc_compositor *compositor, struct wl_visual *visual,
-                 int32_t x, int32_t y, int32_t width, int32_t height)
+static struct wlsc_surface *
+wlsc_surface_create(struct wlsc_compositor *compositor,
+                   struct wl_visual *visual,
+                   int32_t x, int32_t y, int32_t width, int32_t height)
 {
+       struct wlsc_surface *surface;
+
+       surface = malloc(sizeof *surface);
+       if (surface == NULL)
+               return NULL;
+
        glGenTextures(1, &surface->texture);
+       glBindTexture(GL_TEXTURE_2D, surface->texture);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
        surface->compositor = compositor;
        surface->visual = visual;
        wlsc_matrix_init(&surface->matrix);
@@ -140,35 +152,8 @@ wlsc_surface_init(struct wlsc_surface *surface,
        wlsc_matrix_init(&surface->matrix_inv);
        wlsc_matrix_translate(&surface->matrix_inv, -x, -y, 0);
        wlsc_matrix_scale(&surface->matrix_inv, 1.0 / width, 1.0 / height, 1);
-}
-
-static struct wlsc_surface *
-wlsc_surface_create_from_cairo_surface(struct wlsc_compositor *ec,
-                                     cairo_surface_t *surface,
-                                     int x, int y, int width, int height)
-{
-       struct wlsc_surface *es;
-       int stride;
-       void *data;
-
-       stride = cairo_image_surface_get_stride(surface);
-       data = cairo_image_surface_get_data(surface);
-
-       es = malloc(sizeof *es);
-       if (es == NULL)
-               return NULL;
-
-       wlsc_surface_init(es, ec, &ec->premultiplied_argb_visual,
-                         x, y, width, height);
-       glBindTexture(GL_TEXTURE_2D, es->texture);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
-                    GL_RGBA, GL_UNSIGNED_BYTE, data);
 
-       return es;
+       return surface;
 }
 
 static void
@@ -213,6 +198,16 @@ pointer_create(struct wlsc_compositor *ec, int x, int y, int width, int height)
        const int hotspot_x = 16, hotspot_y = 16;
        cairo_surface_t *surface;
        cairo_t *cr;
+       int stride;
+       void *data;
+
+       EGLint image_attribs[] = {
+               EGL_WIDTH,              0,
+               EGL_HEIGHT,             0,
+               EGL_IMAGE_FORMAT_MESA,  EGL_IMAGE_FORMAT_ARGB8888_MESA,
+               EGL_IMAGE_USE_MESA,     EGL_IMAGE_USE_SCANOUT_MESA,
+               EGL_NONE
+       };
 
        surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
                                             width, height);
@@ -231,17 +226,37 @@ pointer_create(struct wlsc_compositor *ec, int x, int y, int width, int height)
        cairo_fill(cr);
        cairo_destroy(cr);
 
-       es = wlsc_surface_create_from_cairo_surface(ec,
-                                                  surface,
-                                                  x - hotspot_x,
-                                                  y - hotspot_y,
-                                                  width, height);
-       
+       es = wlsc_surface_create(ec, &ec->premultiplied_argb_visual,
+                                x, y, width, height);
+
+       stride = cairo_image_surface_get_stride(surface);
+       data = cairo_image_surface_get_data(surface);
+
+       image_attribs[1] = width;
+       image_attribs[3] = height;
+       ec->default_pointer_image =
+               eglCreateDRMImageMESA(ec->display, image_attribs);
+       glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ec->default_pointer_image);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
+                    GL_RGBA, GL_UNSIGNED_BYTE, data);
+
        cairo_surface_destroy(surface);
 
        return es;
 }
 
+static void
+wlsc_input_device_set_default_pointer_image(struct wlsc_input_device *device)
+{
+       struct wlsc_compositor *ec = device->ec;
+
+       glBindTexture(GL_TEXTURE_2D, device->sprite->texture);
+       glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ec->default_pointer_image);
+       device->sprite->visual = &ec->premultiplied_argb_visual;
+       device->hotspot_x = 16;
+       device->hotspot_y = 16;
+}
+
 static struct wlsc_surface *
 background_create(struct wlsc_output *output, const char *filename)
 {
@@ -251,11 +266,12 @@ background_create(struct wlsc_output *output, const char *filename)
        void *data;
        GLenum format;
 
-       background = malloc(sizeof *background);
+       background = wlsc_surface_create(output->compositor,
+                                        &output->compositor->rgb_visual,
+                                        output->x, output->y,
+                                        output->width, output->height);
        if (background == NULL)
                return NULL;
-       
-       g_type_init();
 
        pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
                                                   output->width,
@@ -268,17 +284,7 @@ background_create(struct wlsc_output *output, const char *filename)
 
        data = gdk_pixbuf_get_pixels(pixbuf);
 
-       wlsc_surface_init(background, output->compositor,
-                         &output->compositor->rgb_visual,
-                         output->x, output->y, output->width, output->height);
-
-       glBindTexture(GL_TEXTURE_2D, background->texture);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-        if (gdk_pixbuf_get_has_alpha(pixbuf))
+       if (gdk_pixbuf_get_has_alpha(pixbuf))
                format = GL_RGBA;
        else
                format = GL_RGB;
@@ -432,10 +438,6 @@ surface_attach(struct wl_client *client,
        struct wlsc_buffer *buffer = (struct wlsc_buffer *) buffer_base;
 
        glBindTexture(GL_TEXTURE_2D, es->texture);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
        es->visual = buffer->visual;
 }
@@ -520,7 +522,7 @@ compositor_create_surface(struct wl_client *client,
        struct wlsc_compositor *ec = (struct wlsc_compositor *) compositor;
        struct wlsc_surface *surface;
 
-       surface = malloc(sizeof *surface);
+       surface = wlsc_surface_create(ec, NULL, 0, 0, 0, 0);
        if (surface == NULL) {
                wl_client_post_event(client,
                                     (struct wl_object *) ec->wl_display,
@@ -528,8 +530,6 @@ compositor_create_surface(struct wl_client *client,
                return;
        }
 
-       wlsc_surface_init(surface, ec, NULL, 0, 0, 0, 0);
-
        wl_list_insert(ec->surface_list.prev, &surface->link);
        surface->base.base.destroy = destroy_surface;
        wl_client_add_surface(client, &surface->base,
@@ -609,6 +609,9 @@ wlsc_input_device_set_pointer_focus(struct wlsc_input_device *device,
                                      time, &surface->base,
                                      x, y, sx, sy);
 
+       if (!surface)
+               wlsc_input_device_set_default_pointer_image(device);
+
        device->pointer_focus = surface;
 }
 
@@ -640,7 +643,6 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
        struct wlsc_surface *es;
        struct wlsc_compositor *ec = device->ec;
        struct wlsc_output *output;
-       const int hotspot_x = 16, hotspot_y = 16;
        int32_t sx, sy, width, height;
 
        /* FIXME: We need some multi head love here. */
@@ -727,7 +729,7 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
        wlsc_matrix_init(&device->sprite->matrix);
        wlsc_matrix_scale(&device->sprite->matrix, 64, 64, 1);
        wlsc_matrix_translate(&device->sprite->matrix,
-                             x - hotspot_x, y - hotspot_y, 0);
+                             x - device->hotspot_x, y - device->hotspot_y, 0);
 
        wlsc_compositor_schedule_repaint(device->ec);
 }
@@ -829,6 +831,30 @@ notify_key(struct wlsc_input_device *device,
                                      WL_INPUT_DEVICE_KEY, time, key, state);
 }
 
+static void
+input_device_attach(struct wl_client *client,
+                   struct wl_input_device *device_base,
+                   struct wl_buffer *buffer_base, int32_t x, int32_t y)
+{
+       struct wlsc_input_device *device =
+               (struct wlsc_input_device *) device_base;
+       struct wlsc_buffer *buffer = (struct wlsc_buffer *) buffer_base;
+
+       if (device->pointer_focus == NULL ||
+           device->pointer_focus->base.client != client)
+               return;
+
+       glBindTexture(GL_TEXTURE_2D, device->sprite->texture);
+       glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
+       device->sprite->visual = buffer->visual;
+       device->hotspot_x = x;
+       device->hotspot_y = y;
+}
+
+const static struct wl_input_device_interface input_device_interface = {
+       input_device_attach,
+};
+
 static uint32_t
 get_time(void)
 {
@@ -866,7 +892,8 @@ wlsc_input_device_init(struct wlsc_input_device *device,
                       struct wlsc_compositor *ec)
 {
        device->base.interface = &wl_input_device_interface;
-       device->base.implementation = NULL;
+       device->base.implementation =
+               (void (**)(void)) &input_device_interface;
        wl_display_add_object(ec->wl_display, &device->base);
        wl_display_add_global(ec->wl_display, &device->base, NULL);
 
@@ -874,6 +901,8 @@ wlsc_input_device_init(struct wlsc_input_device *device,
        device->y = 100;
        device->ec = ec;
        device->sprite = pointer_create(ec, device->x, device->y, 64, 64);
+       device->hotspot_x = 16;
+       device->hotspot_y = 16;
 
        device->listener.func = handle_surface_destroy;
        wl_list_insert(ec->surface_destroy_listener_list.prev,
@@ -1092,6 +1121,8 @@ int main(int argc, char *argv[])
        GError *error = NULL;
        GOptionContext *context;
 
+       g_type_init(); /* GdkPixbuf needs this, it seems. */
+
        context = g_option_context_new(NULL);
        g_option_context_add_main_entries(context, option_entries, "Wayland");
        if (!g_option_context_parse(context, &argc, &argv, &error)) {
index 1237fbc..ac812dc 100644 (file)
@@ -81,6 +81,7 @@ struct wlsc_input_device {
        int32_t x, y;
        struct wlsc_compositor *ec;
        struct wlsc_surface *sprite;
+       int32_t hotspot_x, hotspot_y;
        struct wl_list link;
 
        struct wlsc_surface *pointer_focus;
@@ -118,6 +119,7 @@ struct wlsc_compositor {
        EGLContext context;
        GLuint fbo, vbo;
        GLuint proj_uniform, tex_uniform;
+       EGLImageKHR default_pointer_image;
        struct wl_display *wl_display;
 
        /* We implement the shell interface. */
index be2e79b..d8c644c 100644 (file)
   </interface>
 
   <interface name="input_device" version="1">
+    <request name="attach">
+      <arg name="buffer" type="object" interface="buffer"/>
+      <arg name="hotspot_x" type="int"/>
+      <arg name="hotspot_y" type="int"/>
+    </request>
+
     <event name="motion">
       <arg name="time" type="uint"/>
       <arg name="x" type="int"/>
index 330d672..41e0367 100644 (file)
@@ -159,7 +159,7 @@ delivered in both screen coordinates and surface local coordinates.
   \hline 
   Interface \texttt{cache} \\ \hline 
   Requests \\ \hline 
-  no requests \\ \hline
+  \texttt{attach(buffer, x, y)} \\
   Events \\ \hline
   \texttt{motion(x, y, sx, sy)} \\
   \texttt{button(button, state, x, y, sx, sy)} \\
@@ -179,14 +179,14 @@ Talk about:
 A surface can change the pointer image when the surface is the pointer
 focus of the input device.  Wayland doesn't automatically change the
 pointer image when a pointer enters a surface, but expects the
-application to set the cursor it wants in response the the motion
-event.  The rationale is that a client has to manage changing pointer
-images for UI elements within the surface in response to motion events
-anyway, so we'll make that the only mechanism for setting changing the
-pointer image.  If the server receives a request to set the pointer
-image after the surface loses pointer focus, the request is ignored.
-To the client this will look like it successfully set the pointer
-image.
+application to set the cursor it wants in response the the pointer
+focus and motion events.  The rationale is that a client has to manage
+changing pointer images for UI elements within the surface in response
+to motion events anyway, so we'll make that the only mechanism for
+setting changing the pointer image.  If the server receives a request
+to set the pointer image after the surface loses pointer focus, the
+request is ignored.  To the client this will look like it successfully
+set the pointer image.
 
 The compositor will revert the pointer image back to a default image
 when no surface has the pointer focus for that device.  Clients can