Add protocol for setting the pointer image

dev
Kristian Høgsberg 14 years ago
parent b036ad4a9a
commit 77fb167956
  1. 11
      TODO
  2. 155
      compositor.c
  3. 2
      compositor.h
  4. 6
      protocol.xml
  5. 18
      spec/main.tex

11
TODO

@ -13,17 +13,6 @@ Core wayland protocol
or do X style (content mime-type negotiation, but data goes away or do X style (content mime-type negotiation, but data goes away
when client quits). 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 - Discard buffer, as in "wayland discarded your buffer, it's no
longer visible, you can stop updating it now.", reattach, as in "oh 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 hey, I'm about to show your buffer that I threw away, what was it

@ -125,12 +125,24 @@ wlsc_matrix_transform(struct wlsc_matrix *matrix, struct wlsc_vector *v)
*v = t; *v = t;
} }
static void static struct wlsc_surface *
wlsc_surface_init(struct wlsc_surface *surface, wlsc_surface_create(struct wlsc_compositor *compositor,
struct wlsc_compositor *compositor, struct wl_visual *visual, struct wl_visual *visual,
int32_t x, int32_t y, int32_t width, int32_t height) 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); 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->compositor = compositor;
surface->visual = visual; surface->visual = visual;
wlsc_matrix_init(&surface->matrix); wlsc_matrix_init(&surface->matrix);
@ -140,35 +152,8 @@ wlsc_surface_init(struct wlsc_surface *surface,
wlsc_matrix_init(&surface->matrix_inv); wlsc_matrix_init(&surface->matrix_inv);
wlsc_matrix_translate(&surface->matrix_inv, -x, -y, 0); wlsc_matrix_translate(&surface->matrix_inv, -x, -y, 0);
wlsc_matrix_scale(&surface->matrix_inv, 1.0 / width, 1.0 / height, 1); 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 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; const int hotspot_x = 16, hotspot_y = 16;
cairo_surface_t *surface; cairo_surface_t *surface;
cairo_t *cr; 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, surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height); width, height);
@ -231,17 +226,37 @@ pointer_create(struct wlsc_compositor *ec, int x, int y, int width, int height)
cairo_fill(cr); cairo_fill(cr);
cairo_destroy(cr); cairo_destroy(cr);
es = wlsc_surface_create_from_cairo_surface(ec, es = wlsc_surface_create(ec, &ec->premultiplied_argb_visual,
surface, x, y, width, height);
x - hotspot_x,
y - hotspot_y, stride = cairo_image_surface_get_stride(surface);
width, height); 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); cairo_surface_destroy(surface);
return es; 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 * static struct wlsc_surface *
background_create(struct wlsc_output *output, const char *filename) background_create(struct wlsc_output *output, const char *filename)
{ {
@ -251,11 +266,12 @@ background_create(struct wlsc_output *output, const char *filename)
void *data; void *data;
GLenum format; 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) if (background == NULL)
return NULL; return NULL;
g_type_init();
pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
output->width, output->width,
@ -268,17 +284,7 @@ background_create(struct wlsc_output *output, const char *filename)
data = gdk_pixbuf_get_pixels(pixbuf); data = gdk_pixbuf_get_pixels(pixbuf);
wlsc_surface_init(background, output->compositor, if (gdk_pixbuf_get_has_alpha(pixbuf))
&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))
format = GL_RGBA; format = GL_RGBA;
else else
format = GL_RGB; format = GL_RGB;
@ -432,10 +438,6 @@ surface_attach(struct wl_client *client,
struct wlsc_buffer *buffer = (struct wlsc_buffer *) buffer_base; struct wlsc_buffer *buffer = (struct wlsc_buffer *) buffer_base;
glBindTexture(GL_TEXTURE_2D, es->texture); 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); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
es->visual = buffer->visual; 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_compositor *ec = (struct wlsc_compositor *) compositor;
struct wlsc_surface *surface; struct wlsc_surface *surface;
surface = malloc(sizeof *surface); surface = wlsc_surface_create(ec, NULL, 0, 0, 0, 0);
if (surface == NULL) { if (surface == NULL) {
wl_client_post_event(client, wl_client_post_event(client,
(struct wl_object *) ec->wl_display, (struct wl_object *) ec->wl_display,
@ -528,8 +530,6 @@ compositor_create_surface(struct wl_client *client,
return; return;
} }
wlsc_surface_init(surface, ec, NULL, 0, 0, 0, 0);
wl_list_insert(ec->surface_list.prev, &surface->link); wl_list_insert(ec->surface_list.prev, &surface->link);
surface->base.base.destroy = destroy_surface; surface->base.base.destroy = destroy_surface;
wl_client_add_surface(client, &surface->base, 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, time, &surface->base,
x, y, sx, sy); x, y, sx, sy);
if (!surface)
wlsc_input_device_set_default_pointer_image(device);
device->pointer_focus = surface; 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_surface *es;
struct wlsc_compositor *ec = device->ec; struct wlsc_compositor *ec = device->ec;
struct wlsc_output *output; struct wlsc_output *output;
const int hotspot_x = 16, hotspot_y = 16;
int32_t sx, sy, width, height; int32_t sx, sy, width, height;
/* FIXME: We need some multi head love here. */ /* 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_init(&device->sprite->matrix);
wlsc_matrix_scale(&device->sprite->matrix, 64, 64, 1); wlsc_matrix_scale(&device->sprite->matrix, 64, 64, 1);
wlsc_matrix_translate(&device->sprite->matrix, 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); wlsc_compositor_schedule_repaint(device->ec);
} }
@ -829,6 +831,30 @@ notify_key(struct wlsc_input_device *device,
WL_INPUT_DEVICE_KEY, time, key, state); 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 static uint32_t
get_time(void) get_time(void)
{ {
@ -866,7 +892,8 @@ wlsc_input_device_init(struct wlsc_input_device *device,
struct wlsc_compositor *ec) struct wlsc_compositor *ec)
{ {
device->base.interface = &wl_input_device_interface; 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_object(ec->wl_display, &device->base);
wl_display_add_global(ec->wl_display, &device->base, NULL); 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->y = 100;
device->ec = ec; device->ec = ec;
device->sprite = pointer_create(ec, device->x, device->y, 64, 64); 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; device->listener.func = handle_surface_destroy;
wl_list_insert(ec->surface_destroy_listener_list.prev, wl_list_insert(ec->surface_destroy_listener_list.prev,
@ -1092,6 +1121,8 @@ int main(int argc, char *argv[])
GError *error = NULL; GError *error = NULL;
GOptionContext *context; GOptionContext *context;
g_type_init(); /* GdkPixbuf needs this, it seems. */
context = g_option_context_new(NULL); context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, option_entries, "Wayland"); g_option_context_add_main_entries(context, option_entries, "Wayland");
if (!g_option_context_parse(context, &argc, &argv, &error)) { if (!g_option_context_parse(context, &argc, &argv, &error)) {

@ -81,6 +81,7 @@ struct wlsc_input_device {
int32_t x, y; int32_t x, y;
struct wlsc_compositor *ec; struct wlsc_compositor *ec;
struct wlsc_surface *sprite; struct wlsc_surface *sprite;
int32_t hotspot_x, hotspot_y;
struct wl_list link; struct wl_list link;
struct wlsc_surface *pointer_focus; struct wlsc_surface *pointer_focus;
@ -118,6 +119,7 @@ struct wlsc_compositor {
EGLContext context; EGLContext context;
GLuint fbo, vbo; GLuint fbo, vbo;
GLuint proj_uniform, tex_uniform; GLuint proj_uniform, tex_uniform;
EGLImageKHR default_pointer_image;
struct wl_display *wl_display; struct wl_display *wl_display;
/* We implement the shell interface. */ /* We implement the shell interface. */

@ -119,6 +119,12 @@
</interface> </interface>
<interface name="input_device" version="1"> <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"> <event name="motion">
<arg name="time" type="uint"/> <arg name="time" type="uint"/>
<arg name="x" type="int"/> <arg name="x" type="int"/>

@ -159,7 +159,7 @@ delivered in both screen coordinates and surface local coordinates.
\hline \hline
Interface \texttt{cache} \\ \hline Interface \texttt{cache} \\ \hline
Requests \\ \hline Requests \\ \hline
no requests \\ \hline \texttt{attach(buffer, x, y)} \\
Events \\ \hline Events \\ \hline
\texttt{motion(x, y, sx, sy)} \\ \texttt{motion(x, y, sx, sy)} \\
\texttt{button(button, state, 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 A surface can change the pointer image when the surface is the pointer
focus of the input device. Wayland doesn't automatically change the focus of the input device. Wayland doesn't automatically change the
pointer image when a pointer enters a surface, but expects the pointer image when a pointer enters a surface, but expects the
application to set the cursor it wants in response the the motion application to set the cursor it wants in response the the pointer
event. The rationale is that a client has to manage changing pointer focus and motion events. The rationale is that a client has to manage
images for UI elements within the surface in response to motion events changing pointer images for UI elements within the surface in response
anyway, so we'll make that the only mechanism for setting changing the to motion events anyway, so we'll make that the only mechanism for
pointer image. If the server receives a request to set the pointer setting changing the pointer image. If the server receives a request
image after the surface loses pointer focus, the request is ignored. to set the pointer image after the surface loses pointer focus, the
To the client this will look like it successfully set the pointer request is ignored. To the client this will look like it successfully
image. set the pointer image.
The compositor will revert the pointer image back to a default 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 when no surface has the pointer focus for that device. Clients can

Loading…
Cancel
Save