From 77fb1679566566d881f16d2547d8b00857dc55bc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 16 Aug 2010 10:38:29 -0400 Subject: [PATCH] Add protocol for setting the pointer image --- TODO | 11 ----- compositor.c | 155 +++++++++++++++++++++++++++++++++++----------------------- compositor.h | 2 + protocol.xml | 6 +++ spec/main.tex | 18 +++---- 5 files changed, 110 insertions(+), 82 deletions(-) diff --git a/TODO b/TODO index 77a7bdd..820288a 100644 --- 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 diff --git a/compositor.c b/compositor.c index 454d81e..0ecbb7d 100644 --- a/compositor.c +++ b/compositor.c @@ -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)) { diff --git a/compositor.h b/compositor.h index 1237fbc..ac812dc 100644 --- a/compositor.h +++ b/compositor.h @@ -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. */ diff --git a/protocol.xml b/protocol.xml index be2e79b..d8c644c 100644 --- a/protocol.xml +++ b/protocol.xml @@ -119,6 +119,12 @@ + + + + + + diff --git a/spec/main.tex b/spec/main.tex index 330d672..41e0367 100644 --- a/spec/main.tex +++ b/spec/main.tex @@ -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 -- 2.7.4