diff --git a/Makefile.am b/Makefile.am index 979ba2573..18afe05c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS=src protocol po doc +SUBDIRS=src protocol data po doc EXTRA_DIST = HACKING MAINTAINERS rationales.txt diff --git a/configure.ac b/configure.ac index 7c20ac5a3..4a4d2dad2 100644 --- a/configure.ac +++ b/configure.ac @@ -495,6 +495,7 @@ src/mutter-plugins.pc src/tools/Makefile src/compositor/plugins/Makefile protocol/Makefile +data/Makefile po/Makefile.in ]) diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 000000000..0fbff4569 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,7 @@ +defaultcursordir = $(datadir)/mutter/cursors + +dist_defaultcursor_DATA = + +if HAVE_WAYLAND +dist_defaultcursor_DATA += left_ptr.png +endif diff --git a/data/left_ptr.png b/data/left_ptr.png new file mode 100644 index 000000000..d3818ccfb Binary files /dev/null and b/data/left_ptr.png differ diff --git a/src/Makefile.am b/src/Makefile.am index a323a207e..6fed47bc1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -187,7 +187,9 @@ libmutter_la_SOURCES += \ wayland/meta-wayland-pointer.c \ wayland/meta-wayland-pointer.h \ wayland/meta-wayland-seat.c \ - wayland/meta-wayland-seat.h + wayland/meta-wayland-seat.h \ + wayland/meta-wayland-stage.h \ + wayland/meta-wayland-stage.c endif libmutter_la_LDFLAGS = -no-undefined diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c index fb78d0920..960b6138f 100644 --- a/src/wayland/meta-wayland-seat.c +++ b/src/wayland/meta-wayland-seat.c @@ -21,6 +21,7 @@ #include "config.h" +#include #include #include #include @@ -34,6 +35,7 @@ #include "meta-wayland-data-device.h" #include "meta-window-actor-private.h" #include "meta/meta-shaped-texture.h" +#include "meta-wayland-stage.h" #define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10) @@ -71,23 +73,40 @@ transform_stage_point_fixed (MetaWaylandSurface *surface, static void pointer_unmap_sprite (MetaWaylandSeat *seat) { + if (seat->current_stage) + { + MetaWaylandStage *stage = META_WAYLAND_STAGE (seat->current_stage); + meta_wayland_stage_set_invisible_cursor (stage); + } + if (seat->sprite) { - if (seat->sprite->window) - { - GObject *window_actor_object = - meta_window_get_compositor_private (seat->sprite->window); - ClutterActor *window_actor = CLUTTER_ACTOR (window_actor_object); - - if (window_actor) - clutter_actor_hide (window_actor); - } - wl_list_remove (&seat->sprite_destroy_listener.link); seat->sprite = NULL; } } +void +meta_wayland_seat_update_sprite (MetaWaylandSeat *seat) +{ + if (seat->current_stage) + { + MetaWaylandStage *stage = META_WAYLAND_STAGE (seat->current_stage); + ClutterBackend *backend = clutter_get_default_backend (); + CoglContext *context = clutter_backend_get_cogl_context (backend); + struct wl_resource *buffer = seat->sprite->buffer_ref.buffer->resource; + CoglTexture2D *texture = + cogl_wayland_texture_2d_new_from_buffer (context, buffer, NULL); + + meta_wayland_stage_set_cursor_from_texture (stage, + COGL_TEXTURE (texture), + seat->hotspot_x, + seat->hotspot_y); + + cogl_object_unref (texture); + } +} + static void pointer_set_cursor (struct wl_client *client, struct wl_resource *resource, @@ -109,17 +128,24 @@ pointer_set_cursor (struct wl_client *client, if (seat->pointer.focus_serial - serial > G_MAXUINT32 / 2) return; - pointer_unmap_sprite (seat); - - if (!surface) - return; - - wl_resource_add_destroy_listener (surface->resource, - &seat->sprite_destroy_listener); - - seat->sprite = surface; seat->hotspot_x = x; seat->hotspot_y = y; + + if (seat->sprite != surface) + { + pointer_unmap_sprite (seat); + + if (!surface) + return; + + wl_resource_add_destroy_listener (surface->resource, + &seat->sprite_destroy_listener); + + seat->sprite = surface; + + if (seat->sprite->buffer_ref.buffer) + meta_wayland_seat_update_sprite (seat); + } } static const struct wl_pointer_interface @@ -228,7 +254,7 @@ pointer_handle_sprite_destroy (struct wl_listener *listener, void *data) MetaWaylandSeat *seat = wl_container_of (listener, seat, sprite_destroy_listener); - seat->sprite = NULL; + pointer_unmap_sprite (seat); } MetaWaylandSeat * diff --git a/src/wayland/meta-wayland-seat.h b/src/wayland/meta-wayland-seat.h index ee567785e..89bcd76cc 100644 --- a/src/wayland/meta-wayland-seat.h +++ b/src/wayland/meta-wayland-seat.h @@ -41,6 +41,9 @@ meta_wayland_seat_repick (MetaWaylandSeat *seat, uint32_t time, ClutterActor *actor); +void +meta_wayland_seat_update_sprite (MetaWaylandSeat *seat); + void meta_wayland_seat_free (MetaWaylandSeat *seat); diff --git a/src/wayland/meta-wayland-stage.c b/src/wayland/meta-wayland-stage.c new file mode 100644 index 000000000..c3036084d --- /dev/null +++ b/src/wayland/meta-wayland-stage.c @@ -0,0 +1,243 @@ +/* + * Wayland Support + * + * Copyright (C) 2012 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include +#include +#include + +#include "meta-wayland-stage.h" +#include "meta/meta-window-actor.h" +#include "meta/meta-shaped-texture.h" + +#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X 7 +#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y 4 + +G_DEFINE_TYPE (MetaWaylandStage, meta_wayland_stage, CLUTTER_TYPE_STAGE); + +static void +meta_wayland_stage_finalize (GObject *object) +{ + MetaWaylandStage *self = (MetaWaylandStage *) object; + + cogl_object_unref (self->default_cursor_pipeline); + cogl_object_unref (self->texture_cursor_pipeline); + + G_OBJECT_CLASS (meta_wayland_stage_parent_class)->finalize (object); +} + +static void +get_cursor_draw_position (MetaWaylandStage *self, + cairo_rectangle_int_t *rect) +{ + rect->x = self->cursor_x - self->cursor_hotspot_x; + rect->y = self->cursor_y - self->cursor_hotspot_y; + rect->width = self->cursor_width; + rect->height = self->cursor_height; +} + +static void +draw_cursor_pipeline (MetaWaylandStage *self, + CoglPipeline *pipeline) +{ + cairo_rectangle_int_t rect; + + get_cursor_draw_position (self, &rect); + + cogl_framebuffer_draw_rectangle (cogl_get_draw_framebuffer (), + pipeline, + rect.x, rect.y, + rect.x + rect.width, + rect.y + rect.height); + + self->has_last_cursor_position = TRUE; + self->last_cursor_position = rect; +} + +static void +meta_wayland_stage_paint (ClutterActor *actor) +{ + MetaWaylandStage *self = META_WAYLAND_STAGE (actor); + + CLUTTER_ACTOR_CLASS (meta_wayland_stage_parent_class)->paint (actor); + + /* Make sure the cursor is always painted on top of all of the other + actors */ + + switch (self->cursor_type) + { + case META_WAYLAND_STAGE_CURSOR_INVISIBLE: + break; + + case META_WAYLAND_STAGE_CURSOR_DEFAULT: + draw_cursor_pipeline (self, self->default_cursor_pipeline); + break; + + case META_WAYLAND_STAGE_CURSOR_TEXTURE: + draw_cursor_pipeline (self, self->texture_cursor_pipeline); + break; + } +} + +static void +update_cursor_position (MetaWaylandStage *self) +{ + cairo_rectangle_int_t rect; + + if (self->has_last_cursor_position) + { + clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (self), + &self->last_cursor_position); + self->has_last_cursor_position = FALSE; + } + + get_cursor_draw_position (self, &rect); + if (rect.width != 0 && rect.height != 0) + clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (self), &rect); +} + +static void +meta_wayland_stage_class_init (MetaWaylandStageClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + ClutterActorClass *actor_class = (ClutterActorClass *) klass; + + gobject_class->finalize = meta_wayland_stage_finalize; + + actor_class->paint = meta_wayland_stage_paint; +} + +static void +load_default_cursor_pipeline (MetaWaylandStage *self) +{ + CoglContext *context = + clutter_backend_get_cogl_context (clutter_get_default_backend ()); + CoglTexture *texture; + CoglError *error = NULL; + char *filename; + + filename = g_build_filename (MUTTER_DATADIR, + "mutter/cursors/left_ptr.png", + NULL); + + texture = cogl_texture_new_from_file (filename, + COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_ANY, + &error); + + g_free (filename); + + self->default_cursor_pipeline = cogl_pipeline_new (context); + cogl_pipeline_set_layer_filters (self->default_cursor_pipeline, + 0, /* layer */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + if (texture == NULL) + { + g_warning ("Failed to load default cursor: %s", + error->message); + cogl_error_free (error); + } + else + { + self->default_cursor_width = cogl_texture_get_width (texture); + self->default_cursor_height = cogl_texture_get_height (texture); + + cogl_pipeline_set_layer_texture (self->default_cursor_pipeline, + 0, /* layer */ + texture); + cogl_object_unref (texture); + } +} + +static void +meta_wayland_stage_init (MetaWaylandStage *self) +{ + load_default_cursor_pipeline (self); + + self->texture_cursor_pipeline = + cogl_pipeline_copy (self->default_cursor_pipeline); + + meta_wayland_stage_set_default_cursor (self); +} + +ClutterActor * +meta_wayland_stage_new (void) +{ + return g_object_new (META_WAYLAND_TYPE_STAGE, + "cursor-visible", FALSE, + NULL); +} + +void +meta_wayland_stage_set_cursor_position (MetaWaylandStage *self, + int x, + int y) +{ + self->cursor_x = x; + self->cursor_y = y; + update_cursor_position (self); +} + +void +meta_wayland_stage_set_cursor_from_texture (MetaWaylandStage *self, + CoglTexture *texture, + int hotspot_x, + int hotspot_y) +{ + CoglPipeline *pipeline; + + self->cursor_hotspot_x = hotspot_x; + self->cursor_hotspot_y = hotspot_y; + self->cursor_type = META_WAYLAND_STAGE_CURSOR_TEXTURE; + + pipeline = cogl_pipeline_copy (self->texture_cursor_pipeline); + cogl_pipeline_set_layer_texture (pipeline, 0, texture); + cogl_object_unref (self->texture_cursor_pipeline); + self->texture_cursor_pipeline = pipeline; + + self->cursor_width = cogl_texture_get_width (texture); + self->cursor_height = cogl_texture_get_height (texture); + + update_cursor_position (self); +} + +void +meta_wayland_stage_set_invisible_cursor (MetaWaylandStage *self) +{ + self->cursor_type = META_WAYLAND_STAGE_CURSOR_INVISIBLE; + self->cursor_width = 0; + self->cursor_height = 0; + update_cursor_position (self); +} + +void +meta_wayland_stage_set_default_cursor (MetaWaylandStage *self) +{ + self->cursor_type = META_WAYLAND_STAGE_CURSOR_DEFAULT; + self->cursor_hotspot_x = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X; + self->cursor_hotspot_y = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y; + self->cursor_width = self->default_cursor_width; + self->cursor_height = self->default_cursor_height; + update_cursor_position (self); +} diff --git a/src/wayland/meta-wayland-stage.h b/src/wayland/meta-wayland-stage.h new file mode 100644 index 000000000..d25f2707f --- /dev/null +++ b/src/wayland/meta-wayland-stage.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_WAYLAND_STAGE_H +#define META_WAYLAND_STAGE_H + +#include +#include + +#include "window-private.h" + +G_BEGIN_DECLS + +#define META_WAYLAND_TYPE_STAGE \ + (meta_wayland_stage_get_type()) +#define META_WAYLAND_STAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + META_WAYLAND_TYPE_STAGE, \ + MetaWaylandStage)) +#define META_WAYLAND_STAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + META_WAYLAND_TYPE_STAGE, \ + MetaWaylandStageClass)) +#define META_WAYLAND_IS_STAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + META_WAYLAND_TYPE_STAGE)) +#define META_WAYLAND_IS_STAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + META_WAYLAND_TYPE_STAGE)) +#define META_WAYLAND_STAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + META_WAYLAND_STAGE, \ + MetaWaylandStageClass)) + +typedef struct _MetaWaylandStage MetaWaylandStage; +typedef struct _MetaWaylandStageClass MetaWaylandStageClass; + +struct _MetaWaylandStageClass +{ + ClutterStageClass parent_class; +}; + +struct _MetaWaylandStage +{ + ClutterStage parent; + + /* A pipeline containing the cursor texture that will be used when + the cursor is not over a surface */ + CoglPipeline *default_cursor_pipeline; + int default_cursor_width; + int default_cursor_height; + + CoglPipeline *texture_cursor_pipeline; + + int cursor_x; + int cursor_y; + int cursor_width; + int cursor_height; + int cursor_hotspot_x; + int cursor_hotspot_y; + + enum + { + /* No cursor will be drawn */ + META_WAYLAND_STAGE_CURSOR_INVISIBLE, + /* The cursor will be drawn from our default cursor image */ + META_WAYLAND_STAGE_CURSOR_DEFAULT, + /* The cursor will be drawn using a custom texture */ + META_WAYLAND_STAGE_CURSOR_TEXTURE + } cursor_type; + + gboolean has_last_cursor_position; + cairo_rectangle_int_t last_cursor_position; +}; + +GType meta_wayland_stage_get_type (void) G_GNUC_CONST; + +ClutterActor *meta_wayland_stage_new (void); + +void meta_wayland_stage_set_cursor_position (MetaWaylandStage *stage, + int x, + int y); + +void meta_wayland_stage_set_default_cursor (MetaWaylandStage *self); + +void meta_wayland_stage_set_cursor_from_texture (MetaWaylandStage *self, + CoglTexture *texture, + int hotspot_x, + int hotspot_y); + +void meta_wayland_stage_set_invisible_cursor (MetaWaylandStage *self); + +G_END_DECLS + +#endif /* META_WAYLAND_STAGE_H */ diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index c364de0e4..dfd5fa8fd 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -39,6 +39,7 @@ #include "meta-wayland-private.h" #include "meta-xwayland-private.h" +#include "meta-wayland-stage.h" #include "meta-window-actor-private.h" #include "meta-wayland-seat.h" #include "meta-wayland-keyboard.h" @@ -368,6 +369,8 @@ meta_wayland_surface_commit (struct wl_client *client, (buffer->width != rect.width || buffer->height != rect.height)) meta_window_resize (surface->window, FALSE, buffer->width, buffer->height); } + else if (surface == compositor->seat->sprite) + meta_wayland_seat_update_sprite (compositor->seat); } } @@ -1200,6 +1203,13 @@ event_cb (ClutterActor *stage, } } + meta_wayland_stage_set_cursor_position (META_WAYLAND_STAGE (stage), + wl_fixed_to_int (pointer->x), + wl_fixed_to_int (pointer->y)); + + if (pointer->current == NULL) + meta_wayland_stage_set_default_cursor (META_WAYLAND_STAGE (stage)); + display = meta_get_display (); if (!display) return FALSE; @@ -1326,7 +1336,7 @@ meta_wayland_init (void) if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) g_error ("Failed to initialize Clutter"); - compositor->stage = clutter_stage_new (); + compositor->stage = meta_wayland_stage_new (); clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE); g_signal_connect_after (compositor->stage, "paint", G_CALLBACK (paint_finished_cb), compositor);