[RFC] Introduce output zoom.

Scott Moreau oreaus at gmail.com
Mon Feb 20 20:56:16 PST 2012


Implement a camera/modelview matrix for transforms to simulate camera movement.
Ideally, we would want to use <modifier>+Scroll but that will have to wait
for axis events. For now we use keyboard grabs. Zoom in/out with Super+Up/Down.
Zoom area follows mouse pointer.
---
 src/compositor.c |   41 +++++++++++++++++++++++-
 src/compositor.h |   12 +++++++
 src/shell.c      |   91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 143 insertions(+), 1 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index 8339e6c..ccbccd4 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -925,6 +925,7 @@ weston_output_repaint(struct weston_output *output, int msecs)
 	struct weston_surface *es;
 	struct weston_animation *animation, *next;
 	struct weston_frame_callback *cb, *cnext;
+	struct weston_matrix modelview;
 	pixman_region32_t opaque, new_damage, total_damage,
 		overlap, surface_overlap;
 	int32_t width, height;
@@ -937,6 +938,12 @@ weston_output_repaint(struct weston_output *output, int msecs)
 		output->border.top + output->border.bottom;
 	glViewport(0, 0, width, height);
 
+	weston_matrix_init(&output->camera_matrix);
+	weston_matrix_translate(&output->camera_matrix,
+						output->zoom.trans_x,
+						output->zoom.trans_y, 0);
+	weston_matrix_invert(&modelview, &output->camera_matrix);
+
 	pixman_region32_init(&new_damage);
 	pixman_region32_init(&opaque);
 	pixman_region32_init(&overlap);
@@ -953,6 +960,9 @@ weston_output_repaint(struct weston_output *output, int msecs)
 		pixman_region32_fini(&surface_overlap);
 		pixman_region32_union(&overlap, &overlap,
 				      &es->transform.boundingbox);
+		glUniform1f(es->shader->zoom_uniform, output->zoom.level);
+		glUniformMatrix4fv(es->shader->modelview_uniform,
+				   1, GL_FALSE, modelview.d);
 	}
 
 	weston_output_set_cursor(output, ec->input_device);
@@ -1322,6 +1332,9 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y)
 			max_x = output->x + output->current->width;
 		if (output->y + output->current->height > max_y)
 			max_y = output->y + output->current->height;
+		if (output->zoom.active &&
+		    pixman_region32_contains_point(&output->region, x, y, NULL))
+			zoom_update(output, x, y);
 	}
 	
 	if (!x_valid) {
@@ -1850,12 +1863,14 @@ bind_output(struct wl_client *client,
 
 static const char vertex_shader[] =
 	"uniform mat4 proj;\n"
+	"uniform mat4 modelview;\n"
+	"uniform float zoom;\n"
 	"attribute vec2 position;\n"
 	"attribute vec2 texcoord;\n"
 	"varying vec2 v_texcoord;\n"
 	"void main()\n"
 	"{\n"
-	"   gl_Position = proj * vec4(position, 0.0, 1.0);\n"
+	"   gl_Position = proj * (modelview * vec4(position, 0.0, zoom));\n"
 	"   v_texcoord = texcoord;\n"
 	"}\n";
 
@@ -1929,6 +1944,8 @@ weston_shader_init(struct weston_shader *shader,
 	}
 
 	shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+	shader->modelview_uniform = glGetUniformLocation(shader->program, "modelview");
+	shader->zoom_uniform = glGetUniformLocation(shader->program, "zoom");
 	shader->tex_uniform = glGetUniformLocation(shader->program, "tex");
 	shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
 	shader->color_uniform = glGetUniformLocation(shader->program, "color");
@@ -1946,6 +1963,21 @@ weston_output_destroy(struct weston_output *output)
 }
 
 WL_EXPORT void
+zoom_update(struct weston_output *output, int x, int y)
+{
+	if (output->zoom.level <= 0)
+		return;
+
+	float ratio = (1 / output->zoom.level) - 1;
+
+	output->zoom.trans_x = (output->mm_width * ratio) *
+					((float)x / output->mm_width);
+	output->zoom.trans_y = (output->mm_height * ratio) *
+					((float)y  / output->mm_height);
+	weston_output_damage(output);
+}
+
+WL_EXPORT void
 weston_output_move(struct weston_output *output, int x, int y)
 {
 	int flip;
@@ -1985,6 +2017,13 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 	output->mm_width = width;
 	output->mm_height = height;
 
+	output->zoom.active = 0;
+	output->zoom.level = 1.0;
+	output->zoom.trans_x = 0.0;
+	output->zoom.trans_y = 0.0;
+
+	weston_matrix_init(&output->camera_matrix);
+
 	output->flags = flags;
 	weston_output_move(output, x, y);
 
diff --git a/src/compositor.h b/src/compositor.h
index 4c82e79..66ec688 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -54,10 +54,17 @@ struct weston_border {
 	int32_t left, right, top, bottom;
 };
 
+struct weston_output_zoom {
+	int active;
+	float level;
+	int trans_x, trans_y;
+};
+
 struct weston_output {
 	struct wl_list link;
 	struct weston_compositor *compositor;
 	struct weston_matrix matrix;
+	struct weston_matrix camera_matrix;
 	struct wl_list frame_callback_list;
 	int32_t x, y, mm_width, mm_height;
 	struct weston_border border;
@@ -66,6 +73,7 @@ struct weston_output {
 	uint32_t flags;
 	int repaint_needed;
 	int repaint_scheduled;
+	struct weston_output_zoom zoom;
 
 	char *make, *model;
 	uint32_t subpixel;
@@ -100,6 +108,8 @@ struct weston_shader {
 	GLuint program;
 	GLuint vertex_shader, fragment_shader;
 	GLint proj_uniform;
+	GLint modelview_uniform;
+	GLint zoom_uniform;
 	GLint tex_uniform;
 	GLint alpha_uniform;
 	GLint color_uniform;
@@ -414,6 +424,8 @@ weston_compositor_init(struct weston_compositor *ec, struct wl_display *display)
 void
 weston_compositor_shutdown(struct weston_compositor *ec);
 void
+zoom_update(struct weston_output *output, int x, int y);
+void
 weston_output_move(struct weston_output *output, int x, int y);
 void
 weston_output_init(struct weston_output *output, struct weston_compositor *c,
diff --git a/src/shell.c b/src/shell.c
index fa165e2..ee406f0 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -113,6 +113,11 @@ struct shell_surface {
 	struct wl_list link;
 };
 
+struct weston_zoom_grab {
+	struct wl_keyboard_grab grab;
+	uint32_t key;
+};
+
 struct weston_move_grab {
 	struct wl_pointer_grab grab;
 	struct weston_surface *surface;
@@ -1035,6 +1040,88 @@ resize_binding(struct wl_input_device *device, uint32_t time,
 }
 
 static void
+zoom_grab_key(struct wl_keyboard_grab *grab,
+		 uint32_t time, uint32_t key, int32_t state)
+{
+	struct weston_zoom_grab *zoom;
+	zoom = container_of(grab, struct weston_zoom_grab, grab);
+
+	if (state == 0) {
+		wl_input_device_end_keyboard_grab(grab->input_device, time);
+		free(zoom);
+	}
+}
+
+static const struct wl_keyboard_grab_interface zoom_grab_interface = {
+	zoom_grab_key,
+};
+
+static void
+zoom_in_binding(struct wl_input_device *device, uint32_t time,
+	       uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+	struct weston_input_device *wd = (struct weston_input_device *) device;
+	struct weston_compositor *compositor = wd->compositor;
+	struct weston_output *output;
+	struct weston_zoom_grab *zoom;
+
+	zoom = malloc(sizeof *zoom);
+	if (!zoom)
+		return;
+
+	zoom->grab.interface = &zoom_grab_interface;
+
+	wl_input_device_start_keyboard_grab(&wd->input_device, &zoom->grab, time);
+
+	wl_list_for_each(output, &compositor->output_list, link) {
+		if (pixman_region32_contains_point(&output->region,
+						device->x, device->y, NULL)) {
+			output->zoom.active = 1;
+			output->zoom.level -= 0.05;
+			if (output->zoom.level > 1.0)
+				output->zoom.level = 1.0;
+			if (output->zoom.level < 0.0)
+				output->zoom.level = 0.05;
+
+			zoom_update(output, device->x, device->y);
+		}
+	}
+}
+
+static void
+zoom_out_binding(struct wl_input_device *device, uint32_t time,
+	       uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+	struct weston_input_device *wd = (struct weston_input_device *) device;
+	struct weston_compositor *compositor = wd->compositor;
+	struct weston_output *output;
+	struct weston_zoom_grab *zoom;
+
+	zoom = malloc(sizeof *zoom);
+	if (!zoom)
+		return;
+
+	zoom->grab.interface = &zoom_grab_interface;
+
+	wl_input_device_start_keyboard_grab(&wd->input_device, &zoom->grab, time);
+
+	wl_list_for_each(output, &compositor->output_list, link) {
+		if (pixman_region32_contains_point(&output->region,
+						device->x, device->y, NULL)) {
+			output->zoom.level += 0.05;
+			if (output->zoom.level > 1.0)
+				output->zoom.level = 1.0;
+			if (output->zoom.level < 0.0) {
+				output->zoom.active = 0;
+				output->zoom.level = 0.0;
+			}
+
+			zoom_update(output, device->x, device->y);
+		}
+	}
+}
+
+static void
 terminate_binding(struct wl_input_device *device, uint32_t time,
 		  uint32_t key, uint32_t button, uint32_t state, void *data)
 {
@@ -1827,6 +1914,10 @@ shell_init(struct weston_compositor *ec)
 				    move_binding, shell);
 	weston_compositor_add_binding(ec, 0, BTN_MIDDLE, MODIFIER_SUPER,
 				    resize_binding, shell);
+	weston_compositor_add_binding(ec, KEY_UP, 0, MODIFIER_SUPER,
+				    zoom_in_binding, shell);
+	weston_compositor_add_binding(ec, KEY_DOWN, 0, MODIFIER_SUPER,
+				    zoom_out_binding, shell);
 	weston_compositor_add_binding(ec, KEY_BACKSPACE, 0,
 				    MODIFIER_CTRL | MODIFIER_ALT,
 				    terminate_binding, ec);
-- 
1.7.4.1



More information about the wayland-devel mailing list