diff --git a/Makefile.am b/Makefile.am index e00e475f..c7d6431c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = shared src clients data protocol +SUBDIRS = shared src clients data protocol tests diff --git a/clients/.gitignore b/clients/.gitignore index 0ce4b08a..5954a540 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -1,3 +1,4 @@ +clickdot desktop-shell-client-protocol.h desktop-shell-protocol.c dnd @@ -12,11 +13,12 @@ screenshooter-protocol.c screenshot simple-egl simple-shm +simple-touch smoke tablet-shell-client-protocol.h tablet-shell-protocol.c terminal view -wayland-desktop-shell -wayland-tablet-shell +weston-desktop-shell +weston-tablet-shell wscreensaver diff --git a/clients/Makefile.am b/clients/Makefile.am index bf73e406..b64c38a6 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -37,6 +37,7 @@ clients_programs = \ smoke \ resizor \ eventdemo \ + clickdot \ $(full_gl_client_programs) desktop_shell = weston-desktop-shell @@ -83,6 +84,9 @@ resizor_LDADD = $(toolkit_libs) eventdemo_SOURCES = eventdemo.c eventdemo_LDADD = $(toolkit_libs) +clickdot_SOURCES = clickdot.c +clickdot_LDADD = $(toolkit_libs) + weston_desktop_shell_SOURCES = \ desktop-shell.c \ desktop-shell-client-protocol.h \ diff --git a/clients/clickdot.c b/clients/clickdot.c new file mode 100644 index 00000000..71bc091c --- /dev/null +++ b/clients/clickdot.c @@ -0,0 +1,181 @@ +/* + * Copyright © 2010 Intel Corporation + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "window.h" + +#include + +struct clickdot { + struct display *display; + struct window *window; + struct widget *widget; + + int32_t x, y; +}; + +static void +redraw_handler(struct widget *widget, void *data) +{ + static const double r = 10.0; + struct clickdot *clickdot = data; + cairo_surface_t *surface; + cairo_t *cr; + struct rectangle allocation; + + widget_get_allocation(clickdot->widget, &allocation); + + surface = window_get_surface(clickdot->window); + + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(cr, + allocation.x, + allocation.y, + allocation.width, + allocation.height); + cairo_set_source_rgba(cr, 0, 0, 0, 0.8); + cairo_fill(cr); + + cairo_translate(cr, clickdot->x + 0.5, clickdot->y + 0.5); + cairo_set_line_width(cr, 1.0); + cairo_set_source_rgb(cr, 0.1, 0.9, 0.9); + cairo_move_to(cr, 0.0, -r); + cairo_line_to(cr, 0.0, r); + cairo_move_to(cr, -r, 0.0); + cairo_line_to(cr, r, 0.0); + cairo_arc(cr, 0.0, 0.0, r, 0.0, 2.0 * M_PI); + cairo_stroke(cr); + + cairo_destroy(cr); + + cairo_surface_destroy(surface); +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct clickdot *clickdot = data; + + window_schedule_redraw(clickdot->window); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, uint32_t state, void *data) +{ + struct clickdot *clickdot = data; + + if (state == 0) + return; + + switch (sym) { + case XK_Escape: + display_exit(clickdot->display); + break; + } +} + +static void +button_handler(struct widget *widget, + struct input *input, uint32_t time, + int button, int state, void *data) +{ + struct clickdot *clickdot = data; + + if (state && button == BTN_LEFT) + input_get_position(input, &clickdot->x, &clickdot->y); + + widget_schedule_redraw(widget); +} + +static struct clickdot * +clickdot_create(struct display *display) +{ + struct clickdot *clickdot; + + clickdot = malloc(sizeof *clickdot); + if (clickdot == NULL) + return clickdot; + memset(clickdot, 0, sizeof *clickdot); + + clickdot->window = window_create(display, 500, 400); + clickdot->widget = frame_create(clickdot->window, clickdot); + window_set_title(clickdot->window, "Wayland ClickDot"); + clickdot->display = display; + + window_set_key_handler(clickdot->window, key_handler); + window_set_user_data(clickdot->window, clickdot); + window_set_keyboard_focus_handler(clickdot->window, + keyboard_focus_handler); + + widget_set_redraw_handler(clickdot->widget, redraw_handler); + widget_set_button_handler(clickdot->widget, button_handler); + + widget_schedule_resize(clickdot->widget, 500, 400); + clickdot->x = 250; + clickdot->y = 200; + + return clickdot; +} + +static void +clickdot_destroy(struct clickdot *clickdot) +{ + widget_destroy(clickdot->widget); + window_destroy(clickdot->window); + free(clickdot); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct clickdot *clickdot; + + display = display_create(&argc, &argv, NULL); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + clickdot = clickdot_create(display); + + display_run(display); + + clickdot_destroy(clickdot); + display_destroy(display); + + return 0; +} diff --git a/configure.ac b/configure.ac index 3522bee5..62d36ebd 100644 --- a/configure.ac +++ b/configure.ac @@ -148,6 +148,9 @@ AC_ARG_ENABLE(tablet-shell, [ --enable-tablet-shell],, AM_CONDITIONAL(ENABLE_TABLET_SHELL, test x$enable_tablet_shell == xyes) +AC_ARG_ENABLE(tests, [ --enable-tests],,enable_tests=yes) +AM_CONDITIONAL(BUILD_TESTS, test x$enable_tests == xyes) + if test "x$GCC" = "xyes"; then GCC_CFLAGS="-Wall -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden" fi @@ -160,7 +163,8 @@ AC_CONFIG_FILES([Makefile src/Makefile clients/Makefile data/Makefile - protocol/Makefile]) + protocol/Makefile + tests/Makefile]) AC_OUTPUT if test "x$enable_setuid_install" == xyes; then diff --git a/src/.gitignore b/src/.gitignore index 0a6ccbb4..c53b4022 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,4 +1,4 @@ -wayland-compositor +weston screenshooter-protocol.c screenshooter-server-protocol.h tablet-shell-protocol.c diff --git a/src/Makefile.am b/src/Makefile.am index cea7cd32..efb2acd0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,8 @@ weston_SOURCES = \ screenshooter-protocol.c \ screenshooter-server-protocol.h \ util.c \ + matrix.c \ + matrix.h \ $(xserver_launcher_sources) if ENABLE_SETUID_INSTALL diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 661ee324..de6feb4f 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -97,16 +97,18 @@ drm_output_prepare_scanout_surface(struct drm_output *output) struct weston_surface, link); if (es->visual != WESTON_RGB_VISUAL || - es->x != output->base.x || - es->y != output->base.y || - es->width != output->base.current->width || - es->height != output->base.current->height || + es->geometry.x != output->base.x || + es->geometry.y != output->base.y || + es->geometry.width != output->base.current->width || + es->geometry.height != output->base.current->height || + es->transform.enabled || es->image == EGL_NO_IMAGE_KHR) return -1; bo = gbm_bo_create_from_egl_image(c->gbm, c->base.display, es->image, - es->width, es->height, + es->geometry.width, + es->geometry.height, GBM_BO_USE_SCANOUT); handle = gbm_bo_get_handle(bo).s32; @@ -231,8 +233,10 @@ drm_output_set_cursor(struct weston_output *output_base, } pixman_region32_init_rect(&cursor_region, - eid->sprite->x, eid->sprite->y, - eid->sprite->width, eid->sprite->height); + eid->sprite->geometry.x, + eid->sprite->geometry.y, + eid->sprite->geometry.width, + eid->sprite->geometry.height); pixman_region32_intersect_rect(&cursor_region, &cursor_region, output->base.x, output->base.y, @@ -245,7 +249,8 @@ drm_output_set_cursor(struct weston_output *output_base, if (eid->sprite->image == EGL_NO_IMAGE_KHR) goto out; - if (eid->sprite->width > 64 || eid->sprite->height > 64) + if (eid->sprite->geometry.width > 64 || + eid->sprite->geometry.height > 64) goto out; bo = gbm_bo_create_from_egl_image(c->gbm, @@ -272,8 +277,8 @@ drm_output_set_cursor(struct weston_output *output_base, } ret = drmModeMoveCursor(c->drm.fd, output->crtc_id, - eid->sprite->x - output->base.x, - eid->sprite->y - output->base.y); + eid->sprite->geometry.x - output->base.x, + eid->sprite->geometry.y - output->base.y); if (ret) { fprintf(stderr, "failed to move cursor: %s\n", strerror(-ret)); goto out; diff --git a/src/compositor.c b/src/compositor.c index 9eb643c4..9593ec8e 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -1,6 +1,7 @@ /* * Copyright © 2010-2011 Intel Corporation * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -189,10 +190,10 @@ weston_surface_create(struct weston_compositor *compositor, surface->compositor = compositor; surface->visual = WESTON_NONE_VISUAL; surface->image = EGL_NO_IMAGE_KHR; - surface->x = x; - surface->y = y; - surface->width = width; - surface->height = height; + surface->geometry.x = x; + surface->geometry.y = y; + surface->geometry.width = width; + surface->geometry.height = height; surface->alpha = 255; surface->fullscreen_output = NULL; @@ -205,7 +206,12 @@ weston_surface_create(struct weston_compositor *compositor, surface->buffer_destroy_listener.func = surface_handle_buffer_destroy; - surface->transform = NULL; + wl_list_init(&surface->geometry.transformation_list); + wl_list_insert(&surface->geometry.transformation_list, + &surface->transform.position.link); + weston_matrix_init(&surface->transform.position.matrix); + pixman_region32_init(&surface->transform.boundingbox); + surface->geometry.dirty = 1; return surface; } @@ -226,25 +232,189 @@ weston_surface_set_color(struct weston_surface *surface, surface->shader = &surface->compositor->solid_shader; } +static void +surface_compute_bbox(struct weston_surface *surface, int32_t sx, int32_t sy, + int32_t width, int32_t height, + pixman_region32_t *bbox) +{ + int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN; + int32_t s[4][2] = { + { sx, sy }, + { sx, sy + height }, + { sx + width, sy }, + { sx + width, sy + height } + }; + int i; + + for (i = 0; i < 4; ++i) { + int32_t x, y; + weston_surface_to_global(surface, s[i][0], s[i][1], &x, &y); + if (x < min_x) + min_x = x; + if (x > max_x) + max_x = x; + if (y < min_y) + min_y = y; + if (y > max_y) + max_y = y; + } + + /* weston_surface_to_global rounds with floor(), add the + * minimum required safety margin. + */ + pixman_region32_init_rect(bbox, min_x, min_y, + max_x - min_x + 1, max_y - min_y + 1); +} + +WL_EXPORT void +weston_surface_update_transform(struct weston_surface *surface) +{ + struct weston_matrix *matrix = &surface->transform.matrix; + struct weston_matrix *inverse = &surface->transform.inverse; + struct weston_transform *tform; + + if (!surface->geometry.dirty) + return; + + surface->geometry.dirty = 0; + + pixman_region32_fini(&surface->transform.boundingbox); + + /* transform.position is always in transformation_list */ + if (surface->geometry.transformation_list.next == + &surface->transform.position.link && + surface->geometry.transformation_list.prev == + &surface->transform.position.link) { + surface->transform.enabled = 0; + + pixman_region32_init_rect(&surface->transform.boundingbox, + surface->geometry.x, + surface->geometry.y, + surface->geometry.width, + surface->geometry.height); + return; + } + + surface->transform.enabled = 1; + + surface->transform.position.matrix.d[12] = surface->geometry.x; + surface->transform.position.matrix.d[13] = surface->geometry.y; + + weston_matrix_init(matrix); + wl_list_for_each(tform, &surface->geometry.transformation_list, link) + weston_matrix_multiply(matrix, &tform->matrix); + + if (weston_matrix_invert(inverse, matrix) < 0) { + /* Oops, bad total transformation, not invertible */ + surface->transform.enabled = 0; + fprintf(stderr, "error: weston_surface %p" + " transformation not invertible.\n", surface); + } + + surface_compute_bbox(surface, 0, 0, surface->geometry.width, + surface->geometry.height, + &surface->transform.boundingbox); +} + +WL_EXPORT void +weston_surface_to_global(struct weston_surface *surface, + int32_t sx, int32_t sy, int32_t *x, int32_t *y) +{ + weston_surface_update_transform(surface); + + if (surface->transform.enabled) { + struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; + + weston_matrix_transform(&surface->transform.matrix, &v); + + if (fabsf(v.f[3]) < 1e-6) { + fprintf(stderr, "warning: numerical instability in " + "weston_surface_to_global(), divisor = %g\n", + v.f[3]); + *x = 0; + *y = 0; + return; + } + + *x = floorf(v.f[0] / v.f[3]); + *y = floorf(v.f[1] / v.f[3]); + } else { + *x = sx + surface->geometry.x; + *y = sy + surface->geometry.y; + } +} + +static void +surface_from_global_float(struct weston_surface *surface, + int32_t x, int32_t y, GLfloat *sx, GLfloat *sy) +{ + if (surface->transform.enabled) { + struct weston_vector v = { { x, y, 0.0f, 1.0f } }; + + weston_matrix_transform(&surface->transform.inverse, &v); + + if (fabsf(v.f[3]) < 1e-6) { + fprintf(stderr, "warning: numerical instability in " + "weston_surface_from_global(), divisor = %g\n", + v.f[3]); + *sx = 0; + *sy = 0; + return; + } + + *sx = v.f[0] / v.f[3]; + *sy = v.f[1] / v.f[3]; + } else { + *sx = x - surface->geometry.x; + *sy = y - surface->geometry.y; + } +} + +WL_EXPORT void +weston_surface_from_global(struct weston_surface *surface, + int32_t x, int32_t y, int32_t *sx, int32_t *sy) +{ + GLfloat sxf, syf; + + weston_surface_update_transform(surface); + + surface_from_global_float(surface, x, y, &sxf, &syf); + *sx = floorf(sxf); + *sy = floorf(syf); +} + WL_EXPORT void weston_surface_damage_rectangle(struct weston_surface *surface, - int32_t x, int32_t y, - int32_t width, int32_t height) + int32_t sx, int32_t sy, + int32_t width, int32_t height) { - struct weston_compositor *compositor = surface->compositor; + weston_surface_update_transform(surface); - pixman_region32_union_rect(&surface->damage, - &surface->damage, - surface->x + x, surface->y + y, - width, height); - weston_compositor_schedule_repaint(compositor); + if (surface->transform.enabled) { + pixman_region32_t box; + surface_compute_bbox(surface, sx, sy, width, height, &box); + pixman_region32_union(&surface->damage, &surface->damage, + &box); + pixman_region32_fini(&box); + } else { + int32_t x, y; + weston_surface_to_global(surface, sx, sy, &x, &y); + pixman_region32_union_rect(&surface->damage, &surface->damage, + x, y, width, height); + } + + weston_compositor_schedule_repaint(surface->compositor); } WL_EXPORT void weston_surface_damage(struct weston_surface *surface) { - weston_surface_damage_rectangle(surface, 0, 0, - surface->width, surface->height); + weston_surface_update_transform(surface); + + pixman_region32_union(&surface->damage, &surface->damage, + &surface->transform.boundingbox); + + weston_compositor_schedule_repaint(surface->compositor); } WL_EXPORT void @@ -260,10 +430,10 @@ weston_surface_damage_below(struct weston_surface *surface) below = container_of(surface->link.next, struct weston_surface, link); - pixman_region32_union_rect(&below->damage, - &below->damage, - surface->x, surface->y, - surface->width, surface->height); + weston_surface_update_transform(surface); + pixman_region32_union(&below->damage, &below->damage, + &surface->transform.boundingbox); + weston_compositor_schedule_repaint(surface->compositor); } @@ -288,10 +458,11 @@ weston_surface_configure(struct weston_surface *surface, { weston_surface_damage_below(surface); - surface->x = x; - surface->y = y; - surface->width = width; - surface->height = height; + surface->geometry.x = x; + surface->geometry.y = y; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; weston_surface_assign_output(surface); weston_surface_damage(surface); @@ -299,20 +470,14 @@ weston_surface_configure(struct weston_surface *surface, pixman_region32_fini(&surface->opaque); if (surface->visual == WESTON_RGB_VISUAL) pixman_region32_init_rect(&surface->opaque, - surface->x, surface->y, - surface->width, surface->height); + surface->geometry.x, + surface->geometry.y, + surface->geometry.width, + surface->geometry.height); else pixman_region32_init(&surface->opaque); } -static void -weston_surface_transform(struct weston_surface *surface, - int32_t x, int32_t y, int32_t *sx, int32_t *sy) -{ - *sx = x - surface->x; - *sy = y - surface->y; -} - WL_EXPORT uint32_t weston_compositor_get_time(void) { @@ -344,8 +509,8 @@ weston_device_repick(struct wl_input_device *device, uint32_t time) focus = (struct weston_surface *) device->grab->focus; if (focus) - weston_surface_transform(focus, device->x, device->y, - &device->grab->x, &device->grab->y); + weston_surface_from_global(focus, device->x, device->y, + &device->grab->x, &device->grab->y); } WL_EXPORT void @@ -387,6 +552,7 @@ destroy_surface(struct wl_resource *resource) wl_list_remove(&surface->buffer_link); + pixman_region32_fini(&surface->transform.boundingbox); pixman_region32_fini(&surface->damage); pixman_region32_fini(&surface->opaque); @@ -442,7 +608,7 @@ weston_buffer_attach(struct wl_buffer *buffer, struct wl_surface *surface) ec->image_target_texture_2d(GL_TEXTURE_2D, es->image); es->visual = WESTON_ARGB_VISUAL; - es->pitch = es->width; + es->pitch = es->geometry.width; } } @@ -451,6 +617,7 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) { struct weston_compositor *ec = es->compositor; GLfloat *v, inv_width, inv_height; + GLfloat sx, sy; pixman_box32_t *rectangles; unsigned int *p; int i, n; @@ -459,28 +626,36 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) v = wl_array_add(&ec->vertices, n * 16 * sizeof *v); p = wl_array_add(&ec->indices, n * 6 * sizeof *p); inv_width = 1.0 / es->pitch; - inv_height = 1.0 / es->height; + inv_height = 1.0 / es->geometry.height; for (i = 0; i < n; i++, v += 16, p += 6) { + surface_from_global_float(es, rectangles[i].x1, + rectangles[i].y1, &sx, &sy); v[ 0] = rectangles[i].x1; v[ 1] = rectangles[i].y1; - v[ 2] = (GLfloat) (rectangles[i].x1 - es->x) * inv_width; - v[ 3] = (GLfloat) (rectangles[i].y1 - es->y) * inv_height; + v[ 2] = sx * inv_width; + v[ 3] = sy * inv_height; + surface_from_global_float(es, rectangles[i].x1, + rectangles[i].y2, &sx, &sy); v[ 4] = rectangles[i].x1; v[ 5] = rectangles[i].y2; - v[ 6] = v[ 2]; - v[ 7] = (GLfloat) (rectangles[i].y2 - es->y) * inv_height; + v[ 6] = sx * inv_width; + v[ 7] = sy * inv_height; + surface_from_global_float(es, rectangles[i].x2, + rectangles[i].y1, &sx, &sy); v[ 8] = rectangles[i].x2; v[ 9] = rectangles[i].y1; - v[10] = (GLfloat) (rectangles[i].x2 - es->x) * inv_width; - v[11] = v[ 3]; + v[10] = sx * inv_width; + v[11] = sy * inv_height; + surface_from_global_float(es, rectangles[i].x2, + rectangles[i].y2, &sx, &sy); v[12] = rectangles[i].x2; v[13] = rectangles[i].y2; - v[14] = v[10]; - v[15] = v[ 7]; + v[14] = sx * inv_width; + v[15] = sy * inv_height; p[0] = i * 4 + 0; p[1] = i * 4 + 1; @@ -493,51 +668,6 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) return n; } -static void -transform_vertex(struct weston_surface *surface, - GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r) -{ - struct weston_vector t; - - t.f[0] = x; - t.f[1] = y; - t.f[2] = 0.0; - t.f[3] = 1.0; - - weston_matrix_transform(&surface->transform->matrix, &t); - - r[ 0] = t.f[0]; - r[ 1] = t.f[1]; - r[ 2] = u; - r[ 3] = v; -} - -static int -texture_transformed_surface(struct weston_surface *es) -{ - struct weston_compositor *ec = es->compositor; - GLfloat *v; - unsigned int *p; - - v = wl_array_add(&ec->vertices, 16 * sizeof *v); - p = wl_array_add(&ec->indices, 6 * sizeof *p); - - transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0]); - transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4]); - transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8]); - transform_vertex(es, es->x + es->width, es->y + es->height, - 1.0, 1.0, &v[12]); - - p[0] = 0; - p[1] = 1; - p[2] = 2; - p[3] = 2; - p[4] = 1; - p[5] = 3; - - return 1; -} - WL_EXPORT void weston_surface_draw(struct weston_surface *es, struct weston_output *output) { @@ -547,13 +677,15 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) GLint filter; int n; - pixman_region32_init_rect(&repaint, - es->x, es->y, es->width, es->height); - pixman_region32_intersect(&repaint, &repaint, &output->region); + weston_surface_update_transform(es); + + pixman_region32_init(&repaint); + pixman_region32_intersect(&repaint, &es->transform.boundingbox, + &output->region); pixman_region32_intersect(&repaint, &repaint, &es->damage); if (!pixman_region32_not_empty(&repaint)) - return; + goto out; switch (es->visual) { case WESTON_ARGB_VISUAL: @@ -584,13 +716,16 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) ec->current_alpha = es->alpha; } - if (es->transform == NULL) { - filter = GL_NEAREST; - n = texture_region(es, &repaint); - } else { + if (es->shader->texwidth_uniform != GL_NONE) + glUniform1f(es->shader->texwidth_uniform, + (GLfloat)es->geometry.width / es->pitch); + + if (es->transform.enabled) filter = GL_LINEAR; - n = texture_transformed_surface(es); - } + else + filter = GL_NEAREST; + + n = texture_region(es, &repaint); glBindTexture(GL_TEXTURE_2D, es->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); @@ -601,10 +736,16 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); + glDrawElements(GL_TRIANGLES, n * 6, GL_UNSIGNED_INT, ec->indices.data); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); + ec->vertices.size = 0; ec->indices.size = 0; + +out: pixman_region32_fini(&repaint); } @@ -716,12 +857,12 @@ weston_output_set_cursor(struct weston_output *output, if (device->sprite == NULL) return; - pixman_region32_init_rect(&cursor_region, - device->sprite->x, device->sprite->y, - device->sprite->width, - device->sprite->height); + weston_surface_update_transform(device->sprite); - pixman_region32_intersect(&cursor_region, &cursor_region, &output->region); + pixman_region32_init(&cursor_region); + pixman_region32_intersect(&cursor_region, + &device->sprite->transform.boundingbox, + &output->region); if (!pixman_region32_not_empty(&cursor_region)) { output->set_hardware_cursor(output, NULL); @@ -770,14 +911,15 @@ weston_output_repaint(struct weston_output *output, int msecs) pixman_region32_init(&overlap); wl_list_for_each(es, &ec->surface_list, link) { + weston_surface_update_transform(es); + pixman_region32_init(&surface_overlap); - pixman_region32_intersect_rect(&surface_overlap, - &overlap, es->x, es->y, - es->width, es->height); + pixman_region32_intersect(&surface_overlap, &overlap, + &es->transform.boundingbox); es->overlapped = pixman_region32_not_empty(&surface_overlap); pixman_region32_fini(&surface_overlap); - pixman_region32_union_rect(&overlap, &overlap, es->x, es->y, - es->width, es->height); + pixman_region32_union(&overlap, &overlap, + &es->transform.boundingbox); } weston_output_set_cursor(output, ec->input_device); @@ -903,12 +1045,14 @@ weston_surface_assign_output(struct weston_surface *es) uint32_t max, area; pixman_box32_t *e; + weston_surface_update_transform(es); + new_output = NULL; max = 0; + pixman_region32_init(®ion); wl_list_for_each(output, &ec->output_list, link) { - pixman_region32_init_rect(®ion, - es->x, es->y, es->width, es->height); - pixman_region32_intersect(®ion, ®ion, &output->region); + pixman_region32_intersect(®ion, &es->transform.boundingbox, + &output->region); e = pixman_region32_extents(®ion); area = (e->x2 - e->x1) * (e->y2 - e->y1); @@ -918,6 +1062,7 @@ weston_surface_assign_output(struct weston_surface *es) max = area; } } + pixman_region32_fini(®ion); es->output = new_output; if (!wl_list_empty(&es->frame_callback_list)) { @@ -951,9 +1096,11 @@ surface_attach(struct wl_client *client, if (es->visual == WESTON_NONE_VISUAL) { shell->map(shell, es, buffer->width, buffer->height); } else if (x != 0 || y != 0 || - es->width != buffer->width || - es->height != buffer->height) { - shell->configure(shell, es, es->x + x, es->y + y, + es->geometry.width != buffer->width || + es->geometry.height != buffer->height) { + /* FIXME: the x,y delta should be in surface-local coords */ + shell->configure(shell, es, es->geometry.x + x, + es->geometry.y + y, buffer->width, buffer->height); } @@ -1052,9 +1199,9 @@ weston_compositor_pick_surface(struct weston_compositor *compositor, wl_list_for_each(surface, &compositor->surface_list, link) { if (surface->surface.resource.client == NULL) continue; - weston_surface_transform(surface, x, y, sx, sy); - if (0 <= *sx && *sx < surface->width && - 0 <= *sy && *sy < surface->height) + weston_surface_from_global(surface, x, y, sx, sy); + if (0 <= *sx && *sx < surface->geometry.width && + 0 <= *sy && *sy < surface->geometry.height) return surface; } @@ -1163,8 +1310,9 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y) if (wd->sprite) { weston_surface_damage_below(wd->sprite); - wd->sprite->x = device->x - wd->hotspot_x; - wd->sprite->y = device->y - wd->hotspot_y; + wd->sprite->geometry.x = device->x - wd->hotspot_x; + wd->sprite->geometry.y = device->y - wd->hotspot_y; + wd->sprite->geometry.dirty = 1; weston_surface_damage(wd->sprite); } @@ -1440,7 +1588,7 @@ notify_touch(struct wl_input_device *device, uint32_t time, int touch_id, touch_set_focus(wd, &es->surface, time); } else if (wd->touch_focus) { es = (struct weston_surface *) wd->touch_focus; - weston_surface_transform(es, x, y, &sx, &sy); + weston_surface_from_global(es, x, y, &sx, &sy); } if (wd->touch_focus_resource && wd->touch_focus) @@ -1454,7 +1602,7 @@ notify_touch(struct wl_input_device *device, uint32_t time, int touch_id, if (!es) break; - weston_surface_transform(es, x, y, &sx, &sy); + weston_surface_from_global(es, x, y, &sx, &sy); if (wd->touch_focus_resource) wl_resource_post_event(wd->touch_focus_resource, touch_type, time, @@ -1515,10 +1663,11 @@ input_device_attach(struct wl_client *client, device->hotspot_x = x; device->hotspot_y = y; - device->sprite->width = buffer->width; - device->sprite->height = buffer->height; - device->sprite->x = device->input_device.x - device->hotspot_x; - device->sprite->y = device->input_device.y - device->hotspot_y; + device->sprite->geometry.width = buffer->width; + device->sprite->geometry.height = buffer->height; + device->sprite->geometry.x = device->input_device.x - device->hotspot_x; + device->sprite->geometry.y = device->input_device.y - device->hotspot_y; + device->sprite->geometry.dirty = 1; weston_surface_damage(device->sprite); } @@ -1624,8 +1773,12 @@ static const char texture_fragment_shader[] = "varying vec2 v_texcoord;\n" "uniform sampler2D tex;\n" "uniform float alpha;\n" + "uniform float texwidth;\n" "void main()\n" "{\n" + " if (v_texcoord.x < 0.0 || v_texcoord.x > texwidth ||\n" + " v_texcoord.y < 0.0 || v_texcoord.y > 1.0)\n" + " discard;\n" " gl_FragColor = texture2D(tex, v_texcoord)\n;" " gl_FragColor = alpha * gl_FragColor;\n" "}\n"; @@ -1687,6 +1840,8 @@ weston_shader_init(struct weston_shader *shader, shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); shader->tex_uniform = glGetUniformLocation(shader->program, "tex"); shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); + shader->texwidth_uniform = glGetUniformLocation(shader->program, + "texwidth"); return 0; } diff --git a/src/compositor.h b/src/compositor.h index fe7176a1..a7b29648 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -1,5 +1,6 @@ /* * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -32,27 +33,11 @@ #include #include -struct weston_matrix { - GLfloat d[16]; -}; - -struct weston_vector { - GLfloat f[4]; -}; - -void -weston_matrix_init(struct weston_matrix *matrix); -void -weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z); -void -weston_matrix_translate(struct weston_matrix *matrix, - GLfloat x, GLfloat y, GLfloat z); -void -weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); +#include "matrix.h" struct weston_transform { struct weston_matrix matrix; - struct weston_matrix inverse; + struct wl_list link; }; struct weston_surface; @@ -118,6 +103,7 @@ struct weston_shader { GLuint tex_uniform; GLuint alpha_uniform; GLuint color_uniform; + GLuint texwidth_uniform; }; struct weston_animation { @@ -162,7 +148,6 @@ struct weston_compositor { EGLContext context; EGLConfig config; GLuint fbo; - GLuint proj_uniform, tex_uniform, alpha_uniform; uint32_t current_alpha; struct weston_shader texture_shader; struct weston_shader solid_shader; @@ -230,17 +215,45 @@ struct weston_surface { GLuint texture; pixman_region32_t damage; pixman_region32_t opaque; - int32_t x, y, width, height; int32_t pitch; struct wl_list link; struct wl_list buffer_link; - struct weston_transform *transform; struct weston_shader *shader; GLfloat color[4]; uint32_t alpha; uint32_t visual; int overlapped; + /* Surface geometry state, mutable. + * If you change anything, set dirty = 1. + * That includes the transformations referenced from the list. + */ + struct { + int32_t x, y; /* surface translation on display */ + int32_t width, height; + + /* struct weston_transform */ + struct wl_list transformation_list; + + int dirty; + } geometry; + + /* State derived from geometry state, read-only. + * This is updated by weston_surface_update_transform(). + */ + struct { + pixman_region32_t boundingbox; + + /* matrix and inverse are used only if enabled = 1. + * If enabled = 0, use x, y, width, height directly. + */ + int enabled; + struct weston_matrix matrix; + struct weston_matrix inverse; + + struct weston_transform position; /* matrix from x, y */ + } transform; + /* * Which output to vsync this surface to. * Used to determine, whether to send or queue frame events. @@ -257,6 +270,17 @@ struct weston_surface { struct wl_listener buffer_destroy_listener; }; +void +weston_surface_update_transform(struct weston_surface *surface); + +void +weston_surface_to_global(struct weston_surface *surface, + int32_t sx, int32_t sy, int32_t *x, int32_t *y); + +void +weston_surface_from_global(struct weston_surface *surface, + int32_t x, int32_t y, int32_t *sx, int32_t *sy); + void weston_device_repick(struct wl_input_device *device, uint32_t time); diff --git a/src/matrix.c b/src/matrix.c new file mode 100644 index 00000000..98ccf4ca --- /dev/null +++ b/src/matrix.c @@ -0,0 +1,250 @@ +/* + * Copyright © 2011 Intel Corporation + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "matrix.h" + + +/* + * Matrices are stored in column-major order, that is the array indices are: + * 0 4 8 12 + * 1 5 9 13 + * 2 6 10 14 + * 3 7 11 15 + */ + +WL_EXPORT void +weston_matrix_init(struct weston_matrix *matrix) +{ + static const struct weston_matrix identity = { + { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 } + }; + + memcpy(matrix, &identity, sizeof identity); +} + +/* m <- n * m, that is, m is multiplied on the LEFT. */ +WL_EXPORT void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) +{ + struct weston_matrix tmp; + const GLfloat *row, *column; + div_t d; + int i, j; + + for (i = 0; i < 16; i++) { + tmp.d[i] = 0; + d = div(i, 4); + row = m->d + d.quot * 4; + column = n->d + d.rem; + for (j = 0; j < 4; j++) + tmp.d[i] += row[j] * column[j * 4]; + } + memcpy(m, &tmp, sizeof tmp); +} + +WL_EXPORT void +weston_matrix_translate(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) +{ + struct weston_matrix translate = { + { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 } + }; + + weston_matrix_multiply(matrix, &translate); +} + +WL_EXPORT void +weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) +{ + struct weston_matrix scale = { + { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 } + }; + + weston_matrix_multiply(matrix, &scale); +} + +/* v <- m * v */ +WL_EXPORT void +weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) +{ + int i, j; + struct weston_vector t; + + for (i = 0; i < 4; i++) { + t.f[i] = 0; + for (j = 0; j < 4; j++) + t.f[i] += v->f[j] * matrix->d[i + j * 4]; + } + + *v = t; +} + +static inline void +swap_rows(double *a, double *b) +{ + unsigned k; + double tmp; + + for (k = 0; k < 13; k += 4) { + tmp = a[k]; + a[k] = b[k]; + b[k] = tmp; + } +} + +static inline void +swap_unsigned(unsigned *a, unsigned *b) +{ + unsigned tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +static inline unsigned +find_pivot(double *column, unsigned k) +{ + unsigned p = k; + for (++k; k < 4; ++k) + if (fabs(column[p]) < fabs(column[k])) + p = k; + + return p; +} + +/* + * reference: Gene H. Golub and Charles F. van Loan. Matrix computations. + * 3rd ed. The Johns Hopkins University Press. 1996. + * LU decomposition, forward and back substitution: Chapter 3. + */ + +MATRIX_TEST_EXPORT inline int +matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix) +{ + unsigned i, j, k; + unsigned pivot; + double pv; + + for (i = 0; i < 4; ++i) + p[i] = i; + for (i = 16; i--; ) + A[i] = matrix->d[i]; + + /* LU decomposition with partial pivoting */ + for (k = 0; k < 4; ++k) { + pivot = find_pivot(&A[k * 4], k); + if (pivot != k) { + swap_unsigned(&p[k], &p[pivot]); + swap_rows(&A[k], &A[pivot]); + } + + pv = A[k * 4 + k]; + if (fabs(pv) < 1e-9) + return -1; /* zero pivot, not invertible */ + + for (i = k + 1; i < 4; ++i) { + A[i + k * 4] /= pv; + + for (j = k + 1; j < 4; ++j) + A[i + j * 4] -= A[i + k * 4] * A[k + j * 4]; + } + } + + return 0; +} + +MATRIX_TEST_EXPORT inline void +inverse_transform(const double *LU, const unsigned *p, GLfloat *v) +{ + /* Solve A * x = v, when we have P * A = L * U. + * P * A * x = P * v => L * U * x = P * v + * Let U * x = b, then L * b = P * v. + */ + double b[4]; + unsigned j; + + /* Forward substitution, column version, solves L * b = P * v */ + /* The diagonal of L is all ones, and not explicitly stored. */ + b[0] = v[p[0]]; + b[1] = (double)v[p[1]] - b[0] * LU[1 + 0 * 4]; + b[2] = (double)v[p[2]] - b[0] * LU[2 + 0 * 4]; + b[3] = (double)v[p[3]] - b[0] * LU[3 + 0 * 4]; + b[2] -= b[1] * LU[2 + 1 * 4]; + b[3] -= b[1] * LU[3 + 1 * 4]; + b[3] -= b[2] * LU[3 + 2 * 4]; + + /* backward substitution, column version, solves U * y = b */ +#if 1 + /* hand-unrolled, 25% faster for whole function */ + b[3] /= LU[3 + 3 * 4]; + b[0] -= b[3] * LU[0 + 3 * 4]; + b[1] -= b[3] * LU[1 + 3 * 4]; + b[2] -= b[3] * LU[2 + 3 * 4]; + + b[2] /= LU[2 + 2 * 4]; + b[0] -= b[2] * LU[0 + 2 * 4]; + b[1] -= b[2] * LU[1 + 2 * 4]; + + b[1] /= LU[1 + 1 * 4]; + b[0] -= b[1] * LU[0 + 1 * 4]; + + b[0] /= LU[0 + 0 * 4]; +#else + for (j = 3; j > 0; --j) { + unsigned k; + b[j] /= LU[j + j * 4]; + for (k = 0; k < j; ++k) + b[k] -= b[j] * LU[k + j * 4]; + } + + b[0] /= LU[0 + 0 * 4]; +#endif + + /* the result */ + for (j = 0; j < 4; ++j) + v[j] = b[j]; +} + +WL_EXPORT int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix) +{ + double LU[16]; /* column-major */ + unsigned perm[4]; /* permutation */ + unsigned c; + + if (matrix_invert(LU, perm, matrix) < 0) + return -1; + + weston_matrix_init(inverse); + for (c = 0; c < 4; ++c) + inverse_transform(LU, perm, &inverse->d[c * 4]); + + return 0; +} diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 00000000..04b179ff --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WESTON_MATRIX_H +#define WESTON_MATRIX_H + +struct weston_matrix { + GLfloat d[16]; +}; + +struct weston_vector { + GLfloat f[4]; +}; + +void +weston_matrix_init(struct weston_matrix *matrix); +void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n); +void +weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z); +void +weston_matrix_translate(struct weston_matrix *matrix, + GLfloat x, GLfloat y, GLfloat z); +void +weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); + +int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix); + +#ifdef UNIT_TEST +# define MATRIX_TEST_EXPORT WL_EXPORT + +int +matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix); + +void +inverse_transform(const double *LU, const unsigned *p, GLfloat *v); + +#else +# define MATRIX_TEST_EXPORT static +#endif + +#endif /* WESTON_MATRIX_H */ diff --git a/src/shell.c b/src/shell.c index 92196994..c7806106 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,6 +1,6 @@ /* * Copyright © 2010 Intel Corporation - * Copyright © 2011 Collabora, Ltd. + * Copyright © 2011-2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "compositor.h" @@ -93,6 +94,10 @@ struct shell_surface { enum shell_surface_type type; int32_t saved_x, saved_y; + struct { + struct weston_transform transform; + } rotation; + struct { struct wl_grab grab; uint32_t time; @@ -110,6 +115,15 @@ struct weston_move_grab { int32_t dx, dy; }; +struct rotate_grab { + struct wl_grab grab; + struct shell_surface *surface; + struct { + int32_t x; + int32_t y; + } center; +}; + static void shell_configuration(struct wl_shell *shell) { @@ -152,7 +166,7 @@ move_grab_motion(struct wl_grab *grab, weston_surface_configure(es, device->x + move->dx, device->y + move->dy, - es->width, es->height); + es->geometry.width, es->geometry.height); } static void @@ -184,8 +198,8 @@ weston_surface_move(struct weston_surface *es, return -1; move->grab.interface = &move_grab_interface; - move->dx = es->x - wd->input_device.grab_x; - move->dy = es->y - wd->input_device.grab_y; + move->dx = es->geometry.x - wd->input_device.grab_x; + move->dy = es->geometry.y - wd->input_device.grab_y; move->surface = es; wl_input_device_start_grab(&wd->input_device, &move->grab, time); @@ -282,10 +296,10 @@ weston_surface_resize(struct shell_surface *shsurf, resize->grab.interface = &resize_grab_interface; resize->edges = edges; - resize->dx = es->x - wd->input_device.grab_x; - resize->dy = es->y - wd->input_device.grab_y; - resize->width = es->width; - resize->height = es->height; + resize->dx = es->geometry.x - wd->input_device.grab_x; + resize->dy = es->geometry.y - wd->input_device.grab_y; + resize->width = es->geometry.width; + resize->height = es->geometry.height; resize->shsurf = shsurf; if (edges == 0 || edges > 15 || @@ -328,8 +342,9 @@ reset_shell_surface_type(struct shell_surface *surface) { switch (surface->type) { case SHELL_SURFACE_FULLSCREEN: - surface->surface->x = surface->saved_x; - surface->surface->y = surface->saved_y; + surface->surface->geometry.x = surface->saved_x; + surface->surface->geometry.y = surface->saved_y; + surface->surface->geometry.dirty = 1; surface->surface->fullscreen_output = NULL; break; case SHELL_SURFACE_PANEL: @@ -385,8 +400,9 @@ shell_surface_set_transient(struct wl_client *client, /* assign to parents output */ es->output = pes->output; - es->x = pes->x + x; - es->y = pes->y + y; + es->geometry.x = pes->geometry.x + x; + es->geometry.y = pes->geometry.y + y; + es->geometry.dirty = 1; weston_surface_damage(es); shsurf->type = SHELL_SURFACE_TRANSIENT; @@ -416,10 +432,11 @@ shell_surface_set_fullscreen(struct wl_client *client, output = get_default_output(es->compositor); es->output = output; - shsurf->saved_x = es->x; - shsurf->saved_y = es->y; - es->x = (output->current->width - es->width) / 2; - es->y = (output->current->height - es->height) / 2; + shsurf->saved_x = es->geometry.x; + shsurf->saved_y = es->geometry.y; + es->geometry.x = (output->current->width - es->geometry.width) / 2; + es->geometry.y = (output->current->height - es->geometry.height) / 2; + es->geometry.dirty = 1; es->fullscreen_output = output; weston_surface_damage(es); shsurf->type = SHELL_SURFACE_FULLSCREEN; @@ -501,8 +518,9 @@ shell_map_popup(struct shell_surface *shsurf, uint32_t time) shsurf->popup.grab.interface = &popup_grab_interface; device = es->compositor->input_device; - es->x = shsurf->parent->surface->x + shsurf->popup.x; - es->y = shsurf->parent->surface->y + shsurf->popup.y; + es->geometry.x = shsurf->parent->surface->geometry.x + shsurf->popup.x; + es->geometry.y = shsurf->parent->surface->geometry.y + shsurf->popup.y; + es->geometry.dirty = 1; shsurf->popup.grab.input_device = device; shsurf->popup.time = device->grab_time; @@ -621,6 +639,9 @@ shell_get_shell_surface(struct wl_client *client, /* init link so its safe to always remove it in destroy_shell_surface */ wl_list_init(&shsurf->link); + /* empty when not in use */ + wl_list_init(&shsurf->rotation.transform.link); + shsurf->type = SHELL_SURFACE_NONE; wl_client_add_resource(client, &shsurf->resource); @@ -673,10 +694,10 @@ show_screensaver(struct wl_shell *shell, struct shell_surface *surface) wl_list_remove(&surface->surface->link); wl_list_insert(list, &surface->surface->link); weston_surface_configure(surface->surface, - surface->surface->x, - surface->surface->y, - surface->surface->width, - surface->surface->height); + surface->surface->geometry.x, + surface->surface->geometry.y, + surface->surface->geometry.width, + surface->surface->geometry.height); surface->surface->output = surface->output; } @@ -716,8 +737,9 @@ desktop_shell_set_background(struct wl_client *client, wl_list_insert(&shell->backgrounds, &shsurf->link); - surface->x = shsurf->output->x; - surface->y = shsurf->output->y; + surface->geometry.x = shsurf->output->x; + surface->geometry.y = shsurf->output->y; + surface->geometry.dirty = 1; wl_resource_post_event(resource, DESKTOP_SHELL_CONFIGURE, @@ -754,8 +776,9 @@ desktop_shell_set_panel(struct wl_client *client, wl_list_insert(&shell->panels, &shsurf->link); - surface->x = shsurf->output->x; - surface->y = shsurf->output->y; + surface->geometry.x = shsurf->output->x; + surface->geometry.y = shsurf->output->y; + surface->geometry.dirty = 1; wl_resource_post_event(resource, DESKTOP_SHELL_CONFIGURE, @@ -813,8 +836,10 @@ resume_desktop(struct wl_shell *shell) terminate_screensaver(shell); wl_list_for_each(surface, &shell->hidden_surface_list, link) - weston_surface_configure(surface, surface->x, surface->y, - surface->width, surface->height); + weston_surface_configure(surface, surface->geometry.x, + surface->geometry.y, + surface->geometry.width, + surface->geometry.height); if (wl_list_empty(&shell->backgrounds)) { list = &shell->compositor->surface_list; @@ -915,19 +940,20 @@ resize_binding(struct wl_input_device *device, uint32_t time, break; } - x = device->grab_x - surface->x; - y = device->grab_y - surface->y; + /* FIXME: convert properly to surface coordinates */ + x = device->grab_x - surface->geometry.x; + y = device->grab_y - surface->geometry.y; - if (x < surface->width / 3) + if (x < surface->geometry.width / 3) edges |= WL_SHELL_SURFACE_RESIZE_LEFT; - else if (x < 2 * surface->width / 3) + else if (x < 2 * surface->geometry.width / 3) edges |= 0; else edges |= WL_SHELL_SURFACE_RESIZE_RIGHT; - if (y < surface->height / 3) + if (y < surface->geometry.height / 3) edges |= WL_SHELL_SURFACE_RESIZE_TOP; - else if (y < 2 * surface->height / 3) + else if (y < 2 * surface->geometry.height / 3) edges |= 0; else edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM; @@ -946,6 +972,114 @@ terminate_binding(struct wl_input_device *device, uint32_t time, wl_display_terminate(compositor->wl_display); } +static void +rotate_grab_motion(struct wl_grab *grab, + uint32_t time, int32_t x, int32_t y) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, grab); + struct wl_input_device *device = grab->input_device; + struct shell_surface *surface = rotate->surface; + GLfloat dx, dy; + GLfloat r; + + dx = device->x - rotate->center.x; + dy = device->y - rotate->center.y; + r = sqrtf(dx * dx + dy * dy); + + wl_list_remove(&surface->rotation.transform.link); + surface->surface->geometry.dirty = 1; + + if (r > 20.0f) { + struct weston_matrix roto; + struct weston_matrix *matrix = + &surface->rotation.transform.matrix; + + weston_matrix_init(&roto); + roto.d[0] = dx / r; + roto.d[4] = -dy / r; + roto.d[1] = -roto.d[4]; + roto.d[5] = roto.d[0]; + + weston_matrix_init(matrix); + weston_matrix_translate(matrix, -rotate->center.x, + -rotate->center.y, 0.0f); + weston_matrix_multiply(matrix, &roto); + weston_matrix_translate(matrix, rotate->center.x, + rotate->center.y, 0.0f); + + wl_list_insert( + surface->surface->geometry.transformation_list.prev, + &surface->rotation.transform.link); + } else { + wl_list_init(&surface->rotation.transform.link); + } + + weston_compositor_damage_all(surface->surface->compositor); +} + +static void +rotate_grab_button(struct wl_grab *grab, + uint32_t time, int32_t button, int32_t state) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, grab); + struct wl_input_device *device = grab->input_device; + + if (device->button_count == 0 && state == 0) { + wl_input_device_end_grab(device, time); + free(rotate); + } +} + +static const struct wl_grab_interface rotate_grab_interface = { + noop_grab_focus, + rotate_grab_motion, + rotate_grab_button, +}; + +static void +rotate_binding(struct wl_input_device *device, uint32_t time, + uint32_t key, uint32_t button, uint32_t state, void *data) +{ + struct weston_surface *base_surface = + (struct weston_surface *) device->pointer_focus; + struct shell_surface *surface; + struct rotate_grab *rotate; + + if (base_surface == NULL) + return; + + surface = get_shell_surface(base_surface); + if (!surface) + return; + + switch (surface->type) { + case SHELL_SURFACE_PANEL: + case SHELL_SURFACE_BACKGROUND: + case SHELL_SURFACE_FULLSCREEN: + case SHELL_SURFACE_SCREENSAVER: + return; + default: + break; + } + + rotate = malloc(sizeof *rotate); + if (!rotate) + return; + + rotate->grab.interface = &rotate_grab_interface; + rotate->surface = surface; + + weston_surface_to_global(surface->surface, + surface->surface->geometry.width / 2, + surface->surface->geometry.height / 2, + &rotate->center.x, &rotate->center.y); + + wl_input_device_start_grab(device, &rotate->grab, time); + wl_input_device_set_pointer_focus(device, NULL, time, 0, 0, 0, 0); +} + static void activate(struct weston_shell *base, struct weston_surface *es, struct weston_input_device *device, uint32_t time) @@ -1098,8 +1232,11 @@ center_on_output(struct weston_surface *surface, struct weston_output *output) { struct weston_mode *mode = output->current; - surface->x = output->x + (mode->width - surface->width) / 2; - surface->y = output->y + (mode->height - surface->height) / 2; + surface->geometry.x = + output->x + (mode->width - surface->geometry.width) / 2; + surface->geometry.y = + output->y + (mode->height - surface->geometry.height) / 2; + surface->geometry.dirty = 1; } static void @@ -1125,14 +1262,16 @@ map(struct weston_shell *base, do_configure = 1; } - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; /* initial positioning, see also configure() */ switch (surface_type) { case SHELL_SURFACE_TOPLEVEL: - surface->x = 10 + random() % 400; - surface->y = 10 + random() % 400; + surface->geometry.x = 10 + random() % 400; + surface->geometry.y = 10 + random() % 400; + surface->geometry.dirty = 1; break; case SHELL_SURFACE_SCREENSAVER: case SHELL_SURFACE_FULLSCREEN: @@ -1189,8 +1328,9 @@ map(struct weston_shell *base, switch (surface_type) { case SHELL_SURFACE_TOPLEVEL: - surface->x = 10 + random() % 400; - surface->y = 10 + random() % 400; + surface->geometry.x = 10 + random() % 400; + surface->geometry.y = 10 + random() % 400; + surface->geometry.dirty = 1; break; case SHELL_SURFACE_POPUP: shell_map_popup(shsurf, shsurf->popup.time); @@ -1199,10 +1339,12 @@ map(struct weston_shell *base, break; } - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; if (do_configure) { - weston_surface_configure(surface, surface->x, surface->y, + weston_surface_configure(surface, surface->geometry.x, + surface->geometry.y, width, height); weston_compositor_repick(compositor); } @@ -1238,8 +1380,9 @@ configure(struct weston_shell *base, struct weston_surface *surface, if (shsurf) surface_type = shsurf->type; - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; switch (surface_type) { case SHELL_SURFACE_SCREENSAVER: @@ -1474,6 +1617,9 @@ shell_init(struct weston_compositor *ec) terminate_binding, ec); weston_compositor_add_binding(ec, 0, BTN_LEFT, 0, click_to_activate_binding, ec); + weston_compositor_add_binding(ec, 0, BTN_LEFT, + MODIFIER_SUPER | MODIFIER_ALT, + rotate_binding, NULL); ec->shell = &shell->shell; diff --git a/src/tablet-shell.c b/src/tablet-shell.c index 51b259cc..14ac8895 100644 --- a/src/tablet-shell.c +++ b/src/tablet-shell.c @@ -110,8 +110,9 @@ tablet_shell_map(struct weston_shell *base, struct weston_surface *surface, struct tablet_shell *shell = container_of(base, struct tablet_shell, shell); - surface->x = 0; - surface->y = 0; + surface->geometry.x = 0; + surface->geometry.y = 0; + surface->geometry.dirty = 1; if (surface == shell->lockscreen_surface) { /* */ @@ -133,7 +134,8 @@ tablet_shell_map(struct weston_shell *base, struct weston_surface *surface, } wl_list_insert(&shell->compositor->surface_list, &surface->link); - weston_surface_configure(surface, surface->x, surface->y, width, height); + weston_surface_configure(surface, surface->geometry.x, + surface->geometry.y, width, height); } static void @@ -165,8 +167,9 @@ tablet_shell_set_lockscreen(struct wl_client *client, struct tablet_shell *shell = resource->data; struct weston_surface *es = surface_resource->data; - es->x = 0; - es->y = 0; + es->geometry.x = 0; + es->geometry.y = 0; + es->geometry.dirty = 1; shell->lockscreen_surface = es; shell->lockscreen_listener.func = handle_lockscreen_surface_destroy; wl_list_insert(es->surface.resource.destroy_listener_list.prev, @@ -199,8 +202,9 @@ tablet_shell_set_switcher(struct wl_client *client, * layer idea, we should be able to hit the framerate on the * fade/zoom in. */ shell->switcher_surface = es; - shell->switcher_surface->x = 0; - shell->switcher_surface->y = 0; + shell->switcher_surface->geometry.x = 0; + shell->switcher_surface->geometry.y = 0; + shell->switcher_surface->geometry.dirty = 1; shell->switcher_listener.func = handle_switcher_surface_destroy; wl_list_insert(es->surface.resource.destroy_listener_list.prev, @@ -215,8 +219,9 @@ tablet_shell_set_homescreen(struct wl_client *client, struct tablet_shell *shell = resource->data; shell->home_surface = surface_resource->data; - shell->home_surface->x = 0; - shell->home_surface->y = 0; + shell->home_surface->geometry.x = 0; + shell->home_surface->geometry.y = 0; + shell->home_surface->geometry.dirty = 1; } static void diff --git a/src/util.c b/src/util.c index 99dc09e2..ee8e7cf3 100644 --- a/src/util.c +++ b/src/util.c @@ -27,70 +27,6 @@ #include "compositor.h" -WL_EXPORT void -weston_matrix_init(struct weston_matrix *matrix) -{ - static const struct weston_matrix identity = { - { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 } - }; - - memcpy(matrix, &identity, sizeof identity); -} - -static void -weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) -{ - struct weston_matrix tmp; - const GLfloat *row, *column; - div_t d; - int i, j; - - for (i = 0; i < 16; i++) { - tmp.d[i] = 0; - d = div(i, 4); - row = m->d + d.quot * 4; - column = n->d + d.rem; - for (j = 0; j < 4; j++) - tmp.d[i] += row[j] * column[j * 4]; - } - memcpy(m, &tmp, sizeof tmp); -} - -WL_EXPORT void -weston_matrix_translate(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) -{ - struct weston_matrix translate = { - { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 } - }; - - weston_matrix_multiply(matrix, &translate); -} - -WL_EXPORT void -weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) -{ - struct weston_matrix scale = { - { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 } - }; - - weston_matrix_multiply(matrix, &scale); -} - -WL_EXPORT void -weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) -{ - int i, j; - struct weston_vector t; - - for (i = 0; i < 4; i++) { - t.f[i] = 0; - for (j = 0; j < 4; j++) - t.f[i] += v->f[j] * matrix->d[i + j * 4]; - } - - *v = t; -} - WL_EXPORT void weston_spring_init(struct weston_spring *spring, double k, double current, double target) @@ -162,7 +98,8 @@ weston_zoom_destroy(struct weston_zoom *zoom) { wl_list_remove(&zoom->animation.link); wl_list_remove(&zoom->listener.link); - zoom->surface->transform = NULL; + wl_list_remove(&zoom->transform.link); + zoom->surface->geometry.dirty = 1; if (zoom->done) zoom->done(zoom, zoom->data); free(zoom); @@ -198,19 +135,18 @@ weston_zoom_frame(struct weston_animation *animation, (zoom->stop - zoom->start) * zoom->spring.current; weston_matrix_init(&zoom->transform.matrix); weston_matrix_translate(&zoom->transform.matrix, - -(es->x + es->width / 2.0), - -(es->y + es->height / 2.0), 0); + -0.5f * es->geometry.width, + -0.5f * es->geometry.height, 0); weston_matrix_scale(&zoom->transform.matrix, scale, scale, scale); weston_matrix_translate(&zoom->transform.matrix, - es->x + es->width / 2.0, - es->y + es->height / 2.0, 0); + 0.5f * es->geometry.width, + 0.5f * es->geometry.height, 0); es->alpha = zoom->spring.current * 255; if (es->alpha > 255) es->alpha = 255; - scale = 1.0 / zoom->spring.current; - weston_matrix_init(&zoom->transform.inverse); - weston_matrix_scale(&zoom->transform.inverse, scale, scale, scale); + + zoom->surface->geometry.dirty = 1; weston_compositor_damage_all(es->compositor); } @@ -230,7 +166,8 @@ weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop, zoom->data = data; zoom->start = start; zoom->stop = stop; - surface->transform = &zoom->transform; + wl_list_insert(&surface->geometry.transformation_list, + &zoom->transform.link); weston_spring_init(&zoom->spring, 200.0, 0.0, 1.0); zoom->spring.friction = 700; zoom->spring.timestamp = weston_compositor_get_time(); @@ -241,7 +178,7 @@ weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop, wl_list_insert(surface->surface.resource.destroy_listener_list.prev, &zoom->listener.link); - wl_list_insert(surface->compositor->animation_list.prev, + wl_list_insert(&surface->compositor->animation_list, &zoom->animation.link); return zoom; diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..e8df81b8 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +matrix-test + diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..a1f361e1 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,15 @@ +if BUILD_TESTS + +noinst_PROGRAMS = matrix-test + +endif + + +AM_CFLAGS = $(GCC_CFLAGS) +AM_CPPFLAGS = -I../src -DUNIT_TEST + +matrix_test_SOURCES = \ + matrix-test.c \ + ../src/matrix.c \ + ../src/matrix.h +matrix_test_LDADD = -lm -lrt diff --git a/tests/matrix-test.c b/tests/matrix-test.c new file mode 100644 index 00000000..6fdab839 --- /dev/null +++ b/tests/matrix-test.c @@ -0,0 +1,422 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "matrix.h" + +struct inverse_matrix { + double LU[16]; /* column-major */ + unsigned perm[4]; /* permutation */ +}; + +static struct timespec begin_time; + +static void +reset_timer(void) +{ + clock_gettime(CLOCK_MONOTONIC, &begin_time); +} + +static double +read_timer(void) +{ + struct timespec t; + + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)(t.tv_sec - begin_time.tv_sec) + + 1e-9 * (t.tv_nsec - begin_time.tv_nsec); +} + +static double +det3x3(const GLfloat *c0, const GLfloat *c1, const GLfloat *c2) +{ + return (double) + c0[0] * c1[1] * c2[2] + + c1[0] * c2[1] * c0[2] + + c2[0] * c0[1] * c1[2] - + c0[2] * c1[1] * c2[0] - + c1[2] * c2[1] * c0[0] - + c2[2] * c0[1] * c1[0]; +} + +static double +determinant(const struct weston_matrix *m) +{ + double det = 0; +#if 1 + /* develop on last row */ + det -= m->d[3 + 0 * 4] * det3x3(&m->d[4], &m->d[8], &m->d[12]); + det += m->d[3 + 1 * 4] * det3x3(&m->d[0], &m->d[8], &m->d[12]); + det -= m->d[3 + 2 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[12]); + det += m->d[3 + 3 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[8]); +#else + /* develop on first row */ + det += m->d[0 + 0 * 4] * det3x3(&m->d[5], &m->d[9], &m->d[13]); + det -= m->d[0 + 1 * 4] * det3x3(&m->d[1], &m->d[9], &m->d[13]); + det += m->d[0 + 2 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[13]); + det -= m->d[0 + 3 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[9]); +#endif + return det; +} + +static void +print_permutation_matrix(const struct inverse_matrix *m) +{ + const unsigned *p = m->perm; + const char *row[4] = { + "1 0 0 0\n", + "0 1 0 0\n", + "0 0 1 0\n", + "0 0 0 1\n" + }; + + printf(" P =\n%s%s%s%s", row[p[0]], row[p[1]], row[p[2]], row[p[3]]); +} + +static void +print_LU_decomposition(const struct inverse_matrix *m) +{ + unsigned r, c; + + printf(" L " + " U\n"); + for (r = 0; r < 4; ++r) { + double v; + + for (c = 0; c < 4; ++c) { + if (c < r) + v = m->LU[r + c * 4]; + else if (c == r) + v = 1.0; + else + v = 0.0; + printf(" %12.6f", v); + } + + printf(" | "); + + for (c = 0; c < 4; ++c) { + if (c >= r) + v = m->LU[r + c * 4]; + else + v = 0.0; + printf(" %12.6f", v); + } + printf("\n"); + } +} + +static void +print_inverse_data_matrix(const struct inverse_matrix *m) +{ + unsigned r, c; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) + printf(" %12.6f", m->LU[r + c * 4]); + printf("\n"); + } + + printf("permutation: "); + for (r = 0; r < 4; ++r) + printf(" %u", m->perm[r]); + printf("\n"); +} + +static void +print_matrix(const struct weston_matrix *m) +{ + unsigned r, c; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) + printf(" %14.6e", m->d[r + c * 4]); + printf("\n"); + } +} + +static double +frand(void) +{ + double r = random(); + return r / (double)(RAND_MAX / 2) - 1.0f; +} + +static void +randomize_matrix(struct weston_matrix *m) +{ + unsigned i; + for (i = 0; i < 16; ++i) +#if 1 + m->d[i] = frand() * exp(10.0 * frand()); +#else + m->d[i] = frand(); +#endif +} + +/* Take a matrix, compute inverse, multiply together + * and subtract the identity matrix to get the error matrix. + * Return the largest absolute value from the error matrix. + */ +static double +test_inverse(struct weston_matrix *m) +{ + unsigned i; + struct inverse_matrix q; + double errsup = 0.0; + + if (matrix_invert(q.LU, q.perm, m) != 0) + return INFINITY; + + for (i = 0; i < 4; ++i) + inverse_transform(q.LU, q.perm, &m->d[i * 4]); + + m->d[0] -= 1.0f; + m->d[5] -= 1.0f; + m->d[10] -= 1.0f; + m->d[15] -= 1.0f; + + for (i = 0; i < 16; ++i) { + double err = fabs(m->d[i]); + if (err > errsup) + errsup = err; + } + + return errsup; +} + +enum { + TEST_OK, + TEST_NOT_INVERTIBLE_OK, + TEST_FAIL, + TEST_COUNT +}; + +static int +test(void) +{ + struct weston_matrix m; + struct weston_matrix n; + double det, errsup; + + randomize_matrix(&m); + n = m; + det = determinant(&m); + + errsup = test_inverse(&m); + if (errsup < 1e-6) + return TEST_OK; + + if (fabs(det) < 1e-5 && isinf(errsup)) + return TEST_NOT_INVERTIBLE_OK; + + printf("test fail, det: %g, error sup: %g\n", det, errsup); +/* print_matrix(&n);*/ + return TEST_FAIL; +} + +static int running; +static void +stopme(int n) +{ + running = 0; +} + +static void +test_loop_precision(void) +{ + int counts[TEST_COUNT] = { 0 }; + + printf("\nRunning a test loop for 10 seconds...\n"); + running = 1; + alarm(10); + while (running) { + counts[test()]++; + } + + printf("tests: %d ok, %d not invertible but ok, %d failed.\n" + "Total: %d iterations.\n", + counts[TEST_OK], counts[TEST_NOT_INVERTIBLE_OK], + counts[TEST_FAIL], + counts[TEST_OK] + counts[TEST_NOT_INVERTIBLE_OK] + + counts[TEST_FAIL]); +} + +static void __attribute__((noinline)) +test_loop_speed_matrixvector(void) +{ + struct weston_matrix m; + struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on weston_matrix_transform()...\n"); + + weston_matrix_init(&m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + weston_matrix_transform(&m, &v); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n", + count, t, 1e9 * t / count); +} + +static void __attribute__((noinline)) +test_loop_speed_inversetransform(void) +{ + struct weston_matrix m; + struct inverse_matrix inv; + struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on inverse_transform()...\n"); + + weston_matrix_init(&m); + matrix_invert(inv.LU, inv.perm, &m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + inverse_transform(inv.LU, inv.perm, v.f); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n", + count, t, 1e9 * t / count); +} + +static void __attribute__((noinline)) +test_loop_speed_invert(void) +{ + struct weston_matrix m; + struct inverse_matrix inv; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on matrix_invert()...\n"); + + weston_matrix_init(&m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + matrix_invert(inv.LU, inv.perm, &m); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", + count, t, 1e9 * t / count); +} + +static void __attribute__((noinline)) +test_loop_speed_invert_explicit(void) +{ + struct weston_matrix m; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on weston_matrix_invert()...\n"); + + weston_matrix_init(&m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + weston_matrix_invert(&m, &m); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", + count, t, 1e9 * t / count); +} + +int main(void) +{ + struct sigaction ding; + struct weston_matrix M; + struct inverse_matrix Q; + int ret; + double errsup; + double det; + + ding.sa_handler = stopme; + sigemptyset(&ding.sa_mask); + ding.sa_flags = 0; + sigaction(SIGALRM, &ding, NULL); + + srandom(13); + + M.d[0] = 3.0; M.d[4] = 17.0; M.d[8] = 10.0; M.d[12] = 0.0; + M.d[1] = 2.0; M.d[5] = 4.0; M.d[9] = -2.0; M.d[13] = 0.0; + M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0; + M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0; + + ret = matrix_invert(Q.LU, Q.perm, &M); + printf("ret = %d\n", ret); + printf("det = %g\n\n", determinant(&M)); + + if (ret != 0) + return 1; + + print_inverse_data_matrix(&Q); + printf("P * A = L * U\n"); + print_permutation_matrix(&Q); + print_LU_decomposition(&Q); + + + printf("a random matrix:\n"); + randomize_matrix(&M); + det = determinant(&M); + print_matrix(&M); + errsup = test_inverse(&M); + printf("\nThe matrix multiplied by its inverse, error:\n"); + print_matrix(&M); + printf("max abs error: %g, original determinant %g\n", errsup, det); + + test_loop_precision(); + test_loop_speed_matrixvector(); + test_loop_speed_inversetransform(); + test_loop_speed_invert(); + test_loop_speed_invert_explicit(); + + return 0; +}