| author | Pekka Paalanen <ppaalanen@gmail.com> | 2012-09-18 10:29:13 (GMT) |
|---|---|---|
| committer | Pekka Paalanen <ppaalanen@gmail.com> | 2012-09-18 10:29:13 (GMT) |
| commit | 57bb3c287ee36481d098b9aa1f5aab366eeaa4ed (patch) (side-by-side diff) | |
| tree | 4433f6daecf3b277f5aa112339723baafd8deddb | |
| parent | 744b9f8a4d5ccc98808cc9ccfcd6e9921d14e0a4 (diff) | |
| parent | d7f282b84e1729f4692488a8af7e696e4d6b69d7 (diff) | |
| download | weston-57bb3c287ee36481d098b9aa1f5aab366eeaa4ed.tar.gz weston-57bb3c287ee36481d098b9aa1f5aab366eeaa4ed.tar.bz2 | |
Merge branch 'master' of git://anongit.freedesktop.org/wayland/weston into ccumaster
| -rw-r--r-- | clients/.gitignore | 24 | ||||
| -rw-r--r-- | clients/Makefile.am | 11 | ||||
| -rw-r--r-- | clients/cliptest.c | 907 | ||||
| -rw-r--r-- | clients/editor.c | 642 | ||||
| -rw-r--r-- | clients/keyboard.c | 306 | ||||
| -rw-r--r-- | clients/simple-egl.c | 13 | ||||
| -rw-r--r-- | man/weston.man | 40 | ||||
| -rw-r--r-- | protocol/Makefile.am | 1 | ||||
| -rw-r--r-- | protocol/input-method.xml | 98 | ||||
| -rw-r--r-- | protocol/text.xml | 135 | ||||
| -rw-r--r-- | src/.gitignore | 4 | ||||
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/compositor-android.c | 4 | ||||
| -rw-r--r-- | src/compositor.c | 131 | ||||
| -rw-r--r-- | src/compositor.h | 21 | ||||
| -rw-r--r-- | src/gles2-renderer.c | 596 | ||||
| -rw-r--r-- | src/screenshooter.c | 15 | ||||
| -rw-r--r-- | src/shell.c | 16 | ||||
| -rw-r--r-- | src/tablet-shell.c | 13 | ||||
| -rw-r--r-- | src/text-backend.c | 245 | ||||
| -rw-r--r-- | src/xwayland/launcher.c | 2 | ||||
| -rw-r--r-- | tests/event-test.c | 2 | ||||
| -rw-r--r-- | tests/test-text-client.c | 19 | ||||
| -rw-r--r-- | tests/text-test.c | 4 | ||||
| -rw-r--r-- | tests/weston-test | 2 |
25 files changed, 2660 insertions, 595 deletions
diff --git a/clients/.gitignore b/clients/.gitignore index 014c0fb..6ed849d 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -1,32 +1,36 @@ clickdot +cliptest desktop-shell-client-protocol.h desktop-shell-protocol.c dnd +editor eventdemo flower gears image +input-method-protocol.c +input-method-client-protocol.h +keyboard libtoytoolkit.a resizor screenshooter-client-protocol.h screenshooter-protocol.c -text-cursor-position-client-protocol.h -text-cursor-position-protocol.c simple-egl simple-shm simple-touch smoke tablet-shell-client-protocol.h tablet-shell-protocol.c +text-client-protocol.h +text-cursor-position-client-protocol.h +text-cursor-position-protocol.c +text-protocol.c view weston-desktop-shell -weston-tablet-shell +weston-info +weston-screensaver weston-screenshooter +weston-tablet-shell weston-terminal -weston-screensaver -wscreensaver -editor -text-protocol.c -text-client-protocol.h -keyboard -weston-info +workspaces-client-protocol.h +workspaces-protocol.c diff --git a/clients/Makefile.am b/clients/Makefile.am index be8b2bc..49469f6 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -48,6 +48,7 @@ terminal = weston-terminal clients_programs = \ flower \ image \ + cliptest \ dnd \ smoke \ resizor \ @@ -88,6 +89,10 @@ weston_terminal_LDADD = $(toolkit_libs) -lutil image_SOURCES = image.c image_LDADD = $(toolkit_libs) +cliptest_SOURCES = cliptest.c +cliptest_CPPFLAGS = $(AM_CPPFLAGS) $(PIXMAN_CFLAGS) +cliptest_LDADD = $(toolkit_libs) $(PIXMAN_LIBS) + dnd_SOURCES = dnd.c dnd_LDADD = $(toolkit_libs) @@ -113,8 +118,8 @@ keyboard_SOURCES = \ keyboard.c \ desktop-shell-client-protocol.h \ desktop-shell-protocol.c \ - text-client-protocol.h \ - text-protocol.c + input-method-protocol.c \ + input-method-client-protocol.h keyboard_LDADD = $(toolkit_libs) weston_info_SOURCES = \ @@ -142,6 +147,8 @@ BUILT_SOURCES = \ text-cursor-position-protocol.c \ text-protocol.c \ text-client-protocol.h \ + input-method-protocol.c \ + input-method-client-protocol.h \ desktop-shell-client-protocol.h \ desktop-shell-protocol.c \ tablet-shell-client-protocol.h \ diff --git a/clients/cliptest.c b/clients/cliptest.c new file mode 100644 index 0000000..4dd4380 --- a/dev/null +++ b/clients/cliptest.c @@ -0,0 +1,907 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * Copyright © 2012 Rob Clark + * + * 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. + */ + +/* cliptest: for debugging calculate_edges() function, which is copied + * from compositor.c. + * controls: + * clip box position: mouse left drag, keys: w a s d + * clip box size: mouse right drag, keys: i j k l + * surface orientation: mouse wheel, keys: n m + * surface transform disable key: r + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <libgen.h> +#include <unistd.h> +#include <math.h> +#include <time.h> +#include <pixman.h> +#include <cairo.h> +#include <float.h> +#include <assert.h> + +#include <linux/input.h> +#include <wayland-client.h> + +#include "window.h" + +typedef float GLfloat; + +struct geometry { + pixman_box32_t clip; + + pixman_box32_t surf; + float s; /* sin phi */ + float c; /* cos phi */ + float phi; +}; + +struct weston_surface { + struct { + int enabled; + } transform; + + struct geometry *geometry; +}; + +static void +weston_surface_to_global_float(struct weston_surface *surface, + GLfloat sx, GLfloat sy, GLfloat *x, GLfloat *y) +{ + struct geometry *g = surface->geometry; + + /* pure rotation around origin by sine and cosine */ + *x = g->c * sx + g->s * sy; + *y = -g->s * sx + g->c * sy; +} + +/* ---------------------- copied begins -----------------------*/ + +struct polygon8 { + GLfloat x[8]; + GLfloat y[8]; + int n; +}; + +struct clip_context { + struct { + GLfloat x; + GLfloat y; + } prev; + + struct { + GLfloat x1, y1; + GLfloat x2, y2; + } clip; + + struct { + GLfloat *x; + GLfloat *y; + } vertices; +}; + +static GLfloat +float_difference(GLfloat a, GLfloat b) +{ + /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */ + static const GLfloat max_diff = 4.0f * FLT_MIN; + static const GLfloat max_rel_diff = 4.0e-5; + GLfloat diff = a - b; + GLfloat adiff = fabsf(diff); + + if (adiff <= max_diff) + return 0.0f; + + a = fabsf(a); + b = fabsf(b); + if (adiff <= (a > b ? a : b) * max_rel_diff) + return 0.0f; + + return diff; +} + +/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg. + * Compute the y coordinate of the intersection. + */ +static GLfloat +clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, + GLfloat x_arg) +{ + GLfloat a; + GLfloat diff = float_difference(p1x, p2x); + + /* Practically vertical line segment, yet the end points have already + * been determined to be on different sides of the line. Therefore + * the line segment is part of the line and intersects everywhere. + * Return the end point, so we use the whole line segment. + */ + if (diff == 0.0f) + return p2y; + + a = (x_arg - p2x) / diff; + return p2y + (p1y - p2y) * a; +} + +/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg. + * Compute the x coordinate of the intersection. + */ +static GLfloat +clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, + GLfloat y_arg) +{ + GLfloat a; + GLfloat diff = float_difference(p1y, p2y); + + /* Practically horizontal line segment, yet the end points have already + * been determined to be on different sides of the line. Therefore + * the line segment is part of the line and intersects everywhere. + * Return the end point, so we use the whole line segment. + */ + if (diff == 0.0f) + return p2x; + + a = (y_arg - p2y) / diff; + return p2x + (p1x - p2x) * a; +} + +enum path_transition { + PATH_TRANSITION_OUT_TO_OUT = 0, + PATH_TRANSITION_OUT_TO_IN = 1, + PATH_TRANSITION_IN_TO_OUT = 2, + PATH_TRANSITION_IN_TO_IN = 3, +}; + +static void +clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + *ctx->vertices.x++ = x; + *ctx->vertices.y++ = y; +} + +static enum path_transition +path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1); +} + +static enum path_transition +path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2); +} + +static enum path_transition +path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1); +} + +static enum path_transition +path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2); +} + +static void +clip_polygon_leftright(struct clip_context *ctx, + enum path_transition transition, + GLfloat x, GLfloat y, GLfloat clip_x) +{ + GLfloat yi; + + switch (transition) { + case PATH_TRANSITION_IN_TO_IN: + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_IN_TO_OUT: + yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x); + clip_append_vertex(ctx, clip_x, yi); + break; + case PATH_TRANSITION_OUT_TO_IN: + yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x); + clip_append_vertex(ctx, clip_x, yi); + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_OUT_TO_OUT: + /* nothing */ + break; + default: + assert(0 && "bad enum path_transition"); + } + + ctx->prev.x = x; + ctx->prev.y = y; +} + +static void +clip_polygon_topbottom(struct clip_context *ctx, + enum path_transition transition, + GLfloat x, GLfloat y, GLfloat clip_y) +{ + GLfloat xi; + + switch (transition) { + case PATH_TRANSITION_IN_TO_IN: + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_IN_TO_OUT: + xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y); + clip_append_vertex(ctx, xi, clip_y); + break; + case PATH_TRANSITION_OUT_TO_IN: + xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y); + clip_append_vertex(ctx, xi, clip_y); + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_OUT_TO_OUT: + /* nothing */ + break; + default: + assert(0 && "bad enum path_transition"); + } + + ctx->prev.x = x; + ctx->prev.y = y; +} + +static void +clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + ctx->prev.x = src->x[src->n - 1]; + ctx->prev.y = src->y[src->n - 1]; + ctx->vertices.x = dst_x; + ctx->vertices.y = dst_y; +} + +static int +clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_left_edge(ctx, src->x[i], src->y[i]); + clip_polygon_leftright(ctx, trans, src->x[i], src->y[i], + ctx->clip.x1); + } + return ctx->vertices.x - dst_x; +} + +static int +clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_right_edge(ctx, src->x[i], src->y[i]); + clip_polygon_leftright(ctx, trans, src->x[i], src->y[i], + ctx->clip.x2); + } + return ctx->vertices.x - dst_x; +} + +static int +clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_top_edge(ctx, src->x[i], src->y[i]); + clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i], + ctx->clip.y1); + } + return ctx->vertices.x - dst_x; +} + +static int +clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]); + clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i], + ctx->clip.y2); + } + return ctx->vertices.x - dst_x; +} + +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) (((a) > (b)) ? (b) : (a)) +#define clip(x, a, b) min(max(x, a), b) + +/* + * Compute the boundary vertices of the intersection of the global coordinate + * aligned rectangle 'rect', and an arbitrary quadrilateral produced from + * 'surf_rect' when transformed from surface coordinates into global coordinates. + * The vertices are written to 'ex' and 'ey', and the return value is the + * number of vertices. Vertices are produced in clockwise winding order. + * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero + * polygon area. + */ +static int +calculate_edges(struct weston_surface *es, pixman_box32_t *rect, + pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey) +{ + struct polygon8 polygon; + struct clip_context ctx; + int i, n; + GLfloat min_x, max_x, min_y, max_y; + struct polygon8 surf = { + { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 }, + { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 }, + 4 + }; + + ctx.clip.x1 = rect->x1; + ctx.clip.y1 = rect->y1; + ctx.clip.x2 = rect->x2; + ctx.clip.y2 = rect->y2; + + /* transform surface to screen space: */ + for (i = 0; i < surf.n; i++) + weston_surface_to_global_float(es, surf.x[i], surf.y[i], + &surf.x[i], &surf.y[i]); + + /* find bounding box: */ + min_x = max_x = surf.x[0]; + min_y = max_y = surf.y[0]; + + for (i = 1; i < surf.n; i++) { + min_x = min(min_x, surf.x[i]); + max_x = max(max_x, surf.x[i]); + min_y = min(min_y, surf.y[i]); + max_y = max(max_y, surf.y[i]); + } + + /* First, simple bounding box check to discard early transformed + * surface rects that do not intersect with the clip region: + */ + if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) || + (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1)) + return 0; + + /* Simple case, bounding box edges are parallel to surface edges, + * there will be only four edges. We just need to clip the surface + * vertices to the clip rect bounds: + */ + if (!es->transform.enabled) { + for (i = 0; i < surf.n; i++) { + ex[i] = clip(surf.x[i], ctx.clip.x1, ctx.clip.x2); + ey[i] = clip(surf.y[i], ctx.clip.y1, ctx.clip.y2); + } + return surf.n; + } + + /* Transformed case: use a general polygon clipping algorithm to + * clip the surface rectangle with each side of 'rect'. + * The algorithm is Sutherland-Hodgman, as explained in + * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm + * but without looking at any of that code. + */ + polygon.n = clip_polygon_left(&ctx, &surf, polygon.x, polygon.y); + surf.n = clip_polygon_right(&ctx, &polygon, surf.x, surf.y); + polygon.n = clip_polygon_top(&ctx, &surf, polygon.x, polygon.y); + surf.n = clip_polygon_bottom(&ctx, &polygon, surf.x, surf.y); + + /* Get rid of duplicate vertices */ + ex[0] = surf.x[0]; + ey[0] = surf.y[0]; + n = 1; + for (i = 1; i < surf.n; i++) { + if (float_difference(ex[n - 1], surf.x[i]) == 0.0f && + float_difference(ey[n - 1], surf.y[i]) == 0.0f) + continue; + ex[n] = surf.x[i]; + ey[n] = surf.y[i]; + n++; + } + if (float_difference(ex[n - 1], surf.x[0]) == 0.0f && + float_difference(ey[n - 1], surf.y[0]) == 0.0f) + n--; + + if (n < 3) + return 0; + + return n; +} + + +/* ---------------------- copied ends -----------------------*/ + +static void +geometry_set_phi(struct geometry *g, float phi) +{ + g->phi = phi; + g->s = sin(phi); + g->c = cos(phi); +} + +static void +geometry_init(struct geometry *g) +{ + g->clip.x1 = -50; + g->clip.y1 = -50; + g->clip.x2 = -10; + g->clip.y2 = -10; + + g->surf.x1 = -20; + g->surf.y1 = -20; + g->surf.x2 = 20; + g->surf.y2 = 20; + + geometry_set_phi(g, 0.0); +} + +struct ui_state { + uint32_t button; + int down; + + int down_pos[2]; + struct geometry geometry; +}; + +struct cliptest { + struct window *window; + struct widget *widget; + struct display *display; + int fullscreen; + + struct ui_state ui; + + struct geometry geometry; + struct weston_surface surface; +}; + +static void +draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n) +{ + int i; + + cairo_move_to(cr, x[0], y[0]); + for (i = 1; i < n; i++) + cairo_line_to(cr, x[i], y[i]); + cairo_line_to(cr, x[0], y[0]); +} + +static void +draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n) +{ + char str[16]; + int i; + + for (i = 0; i < n; i++) { + snprintf(str, 16, "%d", i); + cairo_move_to(cr, x[i], y[i]); + cairo_show_text(cr, str); + } +} + +static void +draw_coordinates(cairo_t *cr, double ox, double oy, GLfloat *x, GLfloat *y, int n) +{ + char str[64]; + int i; + cairo_font_extents_t ext; + + cairo_font_extents(cr, &ext); + for (i = 0; i < n; i++) { + snprintf(str, 64, "%d: %14.9f, %14.9f", i, x[i], y[i]); + cairo_move_to(cr, ox, oy + ext.height * (i + 1)); + cairo_show_text(cr, str); + } +} + +static void +draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_surface *surface) +{ + GLfloat x[4], y[4]; + + if (surface) { + weston_surface_to_global_float(surface, box->x1, box->y1, &x[0], &y[0]); + weston_surface_to_global_float(surface, box->x2, box->y1, &x[1], &y[1]); + weston_surface_to_global_float(surface, box->x2, box->y2, &x[2], &y[2]); + weston_surface_to_global_float(surface, box->x1, box->y2, &x[3], &y[3]); + } else { + x[0] = box->x1; y[0] = box->y1; + x[1] = box->x2; y[1] = box->y1; + x[2] = box->x2; y[2] = box->y2; + x[3] = box->x1; y[3] = box->y2; + } + + draw_polygon_closed(cr, x, y, 4); +} + +static void +draw_geometry(cairo_t *cr, struct weston_surface *surface, + GLfloat *ex, GLfloat *ey, int n) +{ + struct geometry *g = surface->geometry; + GLfloat cx, cy; + + draw_box(cr, &g->surf, surface); + cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4); + cairo_fill(cr); + weston_surface_to_global_float(surface, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy); + cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI); + if (surface->transform.enabled == 0) + cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8); + cairo_fill(cr); + + draw_box(cr, &g->clip, NULL); + cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4); + cairo_fill(cr); + + draw_polygon_closed(cr, ex, ey, n); + cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); + cairo_stroke(cr); + + cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5); + draw_polygon_labels(cr, ex, ey, n); +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct cliptest *cliptest = data; + struct geometry *g = cliptest->surface.geometry; + struct rectangle allocation; + cairo_t *cr; + cairo_surface_t *surface; + GLfloat ex[8]; + GLfloat ey[8]; + int n; + + n = calculate_edges(&cliptest->surface, &g->clip, &g->surf, ex, ey); + + widget_get_allocation(cliptest->widget, &allocation); + + surface = window_get_surface(cliptest->window); + cr = cairo_create(surface); + widget_get_allocation(cliptest->widget, &allocation); + cairo_rectangle(cr, allocation.x, allocation.y, + allocation.width, allocation.height); + cairo_clip(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 0, 0, 0, 1); + cairo_paint(cr); + + cairo_translate(cr, allocation.x, allocation.y); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, allocation.width / 2.0, 0.0); + cairo_line_to(cr, allocation.width / 2.0, allocation.height); + cairo_move_to(cr, 0.0, allocation.height / 2.0); + cairo_line_to(cr, allocation.width, allocation.height / 2.0); + cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0); + cairo_stroke(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_push_group(cr); + cairo_translate(cr, allocation.width / 2.0, + allocation.height / 2.0); + cairo_scale(cr, 4.0, 4.0); + cairo_set_line_width(cr, 0.5); + cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL); + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(cr, 5.0); + draw_geometry(cr, &cliptest->surface, ex, ey, n); + cairo_pop_group_to_source(cr); + cairo_paint(cr); + + cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); + cairo_select_font_face(cr, "monospace", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 12.0); + draw_coordinates(cr, 10.0, 10.0, ex, ey, n); + + cairo_destroy(cr); + + cairo_surface_destroy(surface); +} + +static int +motion_handler(struct widget *widget, struct input *input, + uint32_t time, float x, float y, void *data) +{ + struct cliptest *cliptest = data; + struct ui_state *ui = &cliptest->ui; + struct geometry *ref = &ui->geometry; + struct geometry *geom = &cliptest->geometry; + float dx, dy; + + if (!ui->down) + return CURSOR_LEFT_PTR; + + dx = (x - ui->down_pos[0]) * 0.25; + dy = (y - ui->down_pos[1]) * 0.25; + + switch (ui->button) { + case BTN_LEFT: + geom->clip.x1 = ref->clip.x1 + dx; + geom->clip.y1 = ref->clip.y1 + dy; + /* fall through */ + case BTN_RIGHT: + geom->clip.x2 = ref->clip.x2 + dx; + geom->clip.y2 = ref->clip.y2 + dy; + break; + default: + return CURSOR_LEFT_PTR; + } + + widget_schedule_redraw(cliptest->widget); + return CURSOR_BLANK; +} + +static void +button_handler(struct widget *widget, struct input *input, + uint32_t time, uint32_t button, + enum wl_pointer_button_state state, void *data) +{ + struct cliptest *cliptest = data; + struct ui_state *ui = &cliptest->ui; + + ui->button = button; + + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + ui->down = 1; + input_get_position(input, &ui->down_pos[0], &ui->down_pos[1]); + } else { + ui->down = 0; + ui->geometry = cliptest->geometry; + } +} + +static void +axis_handler(struct widget *widget, struct input *input, uint32_t time, + uint32_t axis, wl_fixed_t value, void *data) +{ + struct cliptest *cliptest = data; + struct geometry *geom = &cliptest->geometry; + + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) + return; + + geometry_set_phi(geom, geom->phi + + (M_PI / 12.0) * wl_fixed_to_double(value)); + cliptest->surface.transform.enabled = 1; + + widget_schedule_redraw(cliptest->widget); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, + enum wl_keyboard_key_state state, void *data) +{ + struct cliptest *cliptest = data; + struct geometry *g = &cliptest->geometry; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (sym) { + case XKB_KEY_Escape: + display_exit(cliptest->display); + return; + case XKB_KEY_w: + g->clip.y1 -= 1; + g->clip.y2 -= 1; + break; + case XKB_KEY_a: + g->clip.x1 -= 1; + g->clip.x2 -= 1; + break; + case XKB_KEY_s: + g->clip.y1 += 1; + g->clip.y2 += 1; + break; + case XKB_KEY_d: + g->clip.x1 += 1; + g->clip.x2 += 1; + break; + case XKB_KEY_i: + g->clip.y2 -= 1; + break; + case XKB_KEY_j: + g->clip.x2 -= 1; + break; + case XKB_KEY_k: + g->clip.y2 += 1; + break; + case XKB_KEY_l: + g->clip.x2 += 1; + break; + case XKB_KEY_n: + geometry_set_phi(g, g->phi + (M_PI / 24.0)); + cliptest->surface.transform.enabled = 1; + break; + case XKB_KEY_m: + geometry_set_phi(g, g->phi - (M_PI / 24.0)); + cliptest->surface.transform.enabled = 1; + break; + case XKB_KEY_r: + geometry_set_phi(g, 0.0); + cliptest->surface.transform.enabled = 0; + break; + default: + return; + } + + widget_schedule_redraw(cliptest->widget); +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct cliptest *cliptest = data; + + window_schedule_redraw(cliptest->window); +} + +static void +fullscreen_handler(struct window *window, void *data) +{ + struct cliptest *cliptest = data; + + cliptest->fullscreen ^= 1; + window_set_fullscreen(window, cliptest->fullscreen); +} + +static struct cliptest * +cliptest_create(struct display *display) +{ + struct cliptest *cliptest; + + cliptest = malloc(sizeof *cliptest); + if (cliptest == NULL) + return cliptest; + memset(cliptest, 0, sizeof *cliptest); + + cliptest->surface.geometry = &cliptest->geometry; + cliptest->surface.transform.enabled = 0; + geometry_init(&cliptest->geometry); + geometry_init(&cliptest->ui.geometry); + + cliptest->window = window_create(display); + cliptest->widget = frame_create(cliptest->window, cliptest); + window_set_title(cliptest->window, "cliptest"); + cliptest->display = display; + + window_set_user_data(cliptest->window, cliptest); + widget_set_redraw_handler(cliptest->widget, redraw_handler); + widget_set_button_handler(cliptest->widget, button_handler); + widget_set_motion_handler(cliptest->widget, motion_handler); + widget_set_axis_handler(cliptest->widget, axis_handler); + + window_set_keyboard_focus_handler(cliptest->window, + keyboard_focus_handler); + window_set_key_handler(cliptest->window, key_handler); + window_set_fullscreen_handler(cliptest->window, fullscreen_handler); + + /* set minimum size */ + widget_schedule_resize(cliptest->widget, 200, 100); + + /* set current size */ + widget_schedule_resize(cliptest->widget, 500, 400); + + return cliptest; +} + +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 int +benchmark(void) +{ + struct weston_surface surface; + struct geometry geom; + GLfloat ex[8], ey[8]; + int i; + double t; + const int N = 1000000; + + geom.clip.x1 = -19; + geom.clip.y1 = -19; + geom.clip.x2 = 19; + geom.clip.y2 = 19; + + geom.surf.x1 = -20; + geom.surf.y1 = -20; + geom.surf.x2 = 20; + geom.surf.y2 = 20; + + geometry_set_phi(&geom, 0.0); + + surface.transform.enabled = 1; + surface.geometry = &geom; + + reset_timer(); + for (i = 0; i < N; i++) { + geometry_set_phi(&geom, (float)i / 360.0f); + calculate_edges(&surface, &geom.clip, &geom.surf, ex, ey); + } + t = read_timer(); + + printf("%d calls took %g s, average %g us/call\n", N, t, t / N * 1e6); + + return 0; +} + +int +main(int argc, char *argv[]) +{ + struct display *d; + struct cliptest *cliptest; + + if (argc > 1) + return benchmark(); + + d = display_create(argc, argv); + if (d == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + cliptest = cliptest_create(d); + display_run(d); + + widget_destroy(cliptest->widget); + window_destroy(cliptest->window); + free(cliptest); + + return 0; +} diff --git a/clients/editor.c b/clients/editor.c index a5347fe..301cbe2 100644 --- a/clients/editor.c +++ b/clients/editor.c @@ -1,5 +1,6 @@ /* * Copyright © 2012 Openismus GmbH + * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -31,13 +32,29 @@ #include "window.h" #include "text-client-protocol.h" +static const char *font_name = "sans-serif"; +static int font_size = 14; + +struct text_layout { + cairo_glyph_t *glyphs; + int num_glyphs; + cairo_text_cluster_t *clusters; + int num_clusters; + cairo_text_cluster_flags_t cluster_flags; + cairo_scaled_font_t *font; +}; + struct text_entry { struct widget *widget; struct window *window; char *text; int active; - struct rectangle allocation; + uint32_t cursor; + uint32_t anchor; + char *preedit_text; + uint32_t preedit_cursor; struct text_model *model; + struct text_layout *layout; }; struct editor { @@ -49,13 +66,154 @@ struct editor { struct text_entry *editor; }; +static struct text_layout * +text_layout_create(void) +{ + struct text_layout *layout; + cairo_surface_t *surface; + cairo_t *cr; + + layout = malloc(sizeof *layout); + if (!layout) + return NULL; + + layout->glyphs = NULL; + layout->num_glyphs = 0; + + layout->clusters = NULL; + layout->num_clusters = 0; + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); + cr = cairo_create(surface); + cairo_set_font_size(cr, font_size); + cairo_select_font_face(cr, font_name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + layout->font = cairo_get_scaled_font(cr); + cairo_scaled_font_reference(layout->font); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + + return layout; +} + static void -text_entry_append(struct text_entry *entry, const char *text) +text_layout_destroy(struct text_layout *layout) { - entry->text = realloc(entry->text, strlen(entry->text) + strlen(text) + 1); - strcat(entry->text, text); + if (layout->glyphs) + cairo_glyph_free(layout->glyphs); + + if (layout->clusters) + cairo_text_cluster_free(layout->clusters); + + cairo_scaled_font_destroy(layout->font); + + free(layout); } +static void +text_layout_set_text(struct text_layout *layout, + const char *text) +{ + if (layout->glyphs) + cairo_glyph_free(layout->glyphs); + + if (layout->clusters) + cairo_text_cluster_free(layout->clusters); + + layout->glyphs = NULL; + layout->num_glyphs = 0; + layout->clusters = NULL; + layout->num_clusters = 0; + + cairo_scaled_font_text_to_glyphs(layout->font, 0, 0, text, -1, + &layout->glyphs, &layout->num_glyphs, + &layout->clusters, &layout->num_clusters, + &layout->cluster_flags); +} + +static void +text_layout_draw(struct text_layout *layout, cairo_t *cr) +{ + cairo_save(cr); + cairo_set_scaled_font(cr, layout->font); + cairo_show_glyphs(cr, layout->glyphs, layout->num_glyphs); + cairo_restore(cr); +} + +static void +text_layout_extents(struct text_layout *layout, cairo_text_extents_t *extents) +{ + cairo_scaled_font_glyph_extents(layout->font, + layout->glyphs, layout->num_glyphs, + extents); +} + +static int +text_layout_xy_to_index(struct text_layout *layout, double x, double y) +{ + cairo_text_extents_t extents; + int i; + + cairo_scaled_font_glyph_extents(layout->font, + layout->glyphs, layout->num_glyphs, + &extents); + + for (i = 1; i < layout->num_glyphs; i++) { + if (layout->glyphs[i].x >= x) { + return i - 1; + } + } + + if (x >= layout->glyphs[layout->num_glyphs - 1].x && x < extents.width) + return layout->num_glyphs - 1; + + return layout->num_glyphs; +} + +static void +text_layout_index_to_pos(struct text_layout *layout, uint32_t index, cairo_rectangle_t *pos) +{ + cairo_text_extents_t extents; + + if (!pos) + return; + + cairo_scaled_font_glyph_extents(layout->font, + layout->glyphs, layout->num_glyphs, + &extents); + + if ((int)index >= layout->num_glyphs) { + pos->x = extents.x_advance; + pos->y = layout->num_glyphs ? layout->glyphs[layout->num_glyphs - 1].y : 0; + pos->width = 1; + pos->height = extents.height; + return; + } + + pos->x = layout->glyphs[index].x; + pos->y = layout->glyphs[index].y; + pos->width = (int)index < layout->num_glyphs - 1 ? layout->glyphs[index + 1].x : extents.x_advance - pos->x; + pos->height = extents.height; +} + +static void +text_layout_get_cursor_pos(struct text_layout *layout, int index, cairo_rectangle_t *pos) +{ + text_layout_index_to_pos(layout, index, pos); + pos->width = 1; +} + +static void text_entry_redraw_handler(struct widget *widget, void *data); +static void text_entry_button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, + enum wl_pointer_button_state state, void *data); +static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text); +static void text_entry_set_preedit(struct text_entry *entry, + const char *preedit_text, + int preedit_cursor); +static void text_entry_delete_text(struct text_entry *entry, + uint32_t index, uint32_t length); static void text_model_commit_string(void *data, @@ -65,7 +223,12 @@ text_model_commit_string(void *data, { struct text_entry *entry = data; - text_entry_append(entry, text); + if (index > strlen(text)) { + fprintf(stderr, "Invalid cursor index %d\n", index); + index = strlen(text); + } + + text_entry_insert_at_cursor(entry, text); widget_schedule_redraw(entry->widget); } @@ -76,6 +239,41 @@ text_model_preedit_string(void *data, const char *text, uint32_t index) { + struct text_entry *entry = data; + + if (index > strlen(text)) { + fprintf(stderr, "Invalid cursor index %d\n", index); + index = strlen(text); + } + + text_entry_set_preedit(entry, text, index); + + widget_schedule_redraw(entry->widget); +} + +static void +text_model_delete_surrounding_text(void *data, + struct text_model *text_model, + int32_t index, + uint32_t length) +{ + struct text_entry *entry = data; + uint32_t cursor_index = index + entry->cursor; + + if (cursor_index > strlen(entry->text)) { + fprintf(stderr, "Invalid cursor index %d\n", index); + return; + } + + if (cursor_index + length > strlen(entry->text)) { + fprintf(stderr, "Invalid length %d\n", length); + return; + } + + if (length == 0) + return; + + text_entry_delete_text(entry, cursor_index, length); } static void @@ -86,8 +284,31 @@ text_model_preedit_styling(void *data, static void text_model_key(void *data, - struct text_model *text_model) + struct text_model *text_model, + uint32_t key, + uint32_t state) { + const char *state_label; + const char *key_label; + + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + state_label = "pressed"; + } else { + state_label = "released"; + } + + switch (key) { + case XKB_KEY_Tab: + key_label = "Tab"; + break; + case XKB_KEY_KP_Enter: + key_label = "Enter"; + break; + default: + key_label = "Unknown"; + } + + fprintf(stderr, "%s key was %s.\n", key_label, state_label); } static void @@ -133,6 +354,7 @@ text_model_deactivated(void *data, static const struct text_model_listener text_model_listener = { text_model_commit_string, text_model_preedit_string, + text_model_delete_surrounding_text, text_model_preedit_styling, text_model_key, text_model_selection_replacement, @@ -146,62 +368,40 @@ static struct text_entry* text_entry_create(struct editor *editor, const char *text) { struct text_entry *entry; - struct wl_surface *surface; entry = malloc(sizeof *entry); - surface = window_get_wl_surface(editor->window); - - entry->widget = editor->widget; + entry->widget = widget_add_widget(editor->widget, entry); entry->window = editor->window; entry->text = strdup(text); entry->active = 0; - entry->model = text_model_factory_create_text_model(editor->text_model_factory, surface); + entry->cursor = strlen(text); + entry->anchor = entry->cursor; + entry->preedit_text = NULL; + entry->preedit_cursor = 0; + entry->model = text_model_factory_create_text_model(editor->text_model_factory); text_model_add_listener(entry->model, &text_model_listener, entry); + entry->layout = text_layout_create(); + text_layout_set_text(entry->layout, entry->text); + + widget_set_redraw_handler(entry->widget, text_entry_redraw_handler); + widget_set_button_handler(entry->widget, text_entry_button_handler); + return entry; } static void text_entry_destroy(struct text_entry *entry) { + widget_destroy(entry->widget); text_model_destroy(entry->model); + text_layout_destroy(entry->layout); free(entry->text); free(entry); } static void -text_entry_draw(struct text_entry *entry, cairo_t *cr) -{ - cairo_save(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - - cairo_rectangle(cr, entry->allocation.x, entry->allocation.y, entry->allocation.width, entry->allocation.height); - cairo_clip(cr); - - cairo_translate(cr, entry->allocation.x, entry->allocation.y); - cairo_rectangle(cr, 0, 0, entry->allocation.width, entry->allocation.height); - cairo_set_source_rgba(cr, 1, 1, 1, 0.5); - cairo_fill(cr); - if (entry->active) { - cairo_rectangle(cr, 0, 0, entry->allocation.width, entry->allocation.height); - cairo_set_source_rgba(cr, 0, 0, 1, 0.5); - cairo_stroke(cr); - } - - cairo_set_source_rgb(cr, 0, 0, 0); - cairo_select_font_face(cr, "sans", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD); - cairo_set_font_size(cr, 14); - - cairo_translate(cr, 10, entry->allocation.height / 2); - cairo_show_text(cr, entry->text); - - cairo_restore(cr); -} - -static void redraw_handler(struct widget *widget, void *data) { struct editor *editor = data; @@ -220,17 +420,11 @@ redraw_handler(struct widget *widget, void *data) /* Draw background */ cairo_push_group(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 1, 1, 1, 1); cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); cairo_fill(cr); - /* Entry */ - text_entry_draw(editor->entry, cr); - - /* Editor */ - text_entry_draw(editor->editor, cr); - cairo_pop_group_to_source(cr); cairo_paint(cr); @@ -242,10 +436,7 @@ static void text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y, int32_t width, int32_t height) { - entry->allocation.x = x; - entry->allocation.y = y; - entry->allocation.width = width; - entry->allocation.height = height; + widget_set_allocation(entry->widget, x, y, width, height); } static void @@ -253,28 +444,21 @@ resize_handler(struct widget *widget, int32_t width, int32_t height, void *data) { struct editor *editor = data; + struct rectangle allocation; - text_entry_allocate(editor->entry, 20, 20, width - 40, height / 2 - 40); - text_entry_allocate(editor->editor, 20, height / 2 + 20, width - 40, height / 2 - 40); -} - -static int32_t -rectangle_contains(struct rectangle *rectangle, int32_t x, int32_t y) -{ - if (x < rectangle->x || x > rectangle->x + rectangle->width) { - return 0; - } - - if (y < rectangle->y || y > rectangle->y + rectangle->height) { - return 0; - } + widget_get_allocation(editor->widget, &allocation); - return 1; + text_entry_allocate(editor->entry, + allocation.x + 20, allocation.y + 20, + width - 40, height / 2 - 40); + text_entry_allocate(editor->editor, + allocation.x + 20, allocation.y + height / 2 + 20, + width - 40, height / 2 - 40); } static void text_entry_activate(struct text_entry *entry, - struct wl_seat *seat) + struct wl_seat *seat) { struct wl_surface *surface = window_get_wl_surface(entry->window); @@ -292,42 +476,315 @@ text_entry_deactivate(struct text_entry *entry, } static void -button_handler(struct widget *widget, - struct input *input, uint32_t time, - uint32_t button, - enum wl_pointer_button_state state, void *data) +text_entry_update_layout(struct text_entry *entry) { - struct editor *editor = data; + char *text; + + assert(((unsigned int)entry->cursor) <= strlen(entry->text)); + + if (!entry->preedit_text) { + text_layout_set_text(entry->layout, entry->text); + return; + } + + text = malloc(strlen(entry->text) + strlen(entry->preedit_text) + 1); + strncpy(text, entry->text, entry->cursor); + strcpy(text + entry->cursor, entry->preedit_text); + strcpy(text + entry->cursor + strlen(entry->preedit_text), + entry->text + entry->cursor); + + text_layout_set_text(entry->layout, text); + free(text); + + widget_schedule_redraw(entry->widget); + + text_model_set_surrounding_text(entry->model, + entry->text, + entry->cursor, + entry->anchor); +} + +static void +text_entry_insert_at_cursor(struct text_entry *entry, const char *text) +{ + char *new_text = malloc(strlen(entry->text) + strlen(text) + 1); + + strncpy(new_text, entry->text, entry->cursor); + strcpy(new_text + entry->cursor, text); + strcpy(new_text + entry->cursor + strlen(text), + entry->text + entry->cursor); + + free(entry->text); + entry->text = new_text; + entry->cursor += strlen(text); + entry->anchor += strlen(text); + + text_entry_update_layout(entry); +} + +static void +text_entry_set_preedit(struct text_entry *entry, + const char *preedit_text, + int preedit_cursor) +{ + if (entry->preedit_text) { + free(entry->preedit_text); + entry->preedit_text = NULL; + entry->preedit_cursor = 0; + } + + if (!preedit_text) + return; + + entry->preedit_text = strdup(preedit_text); + entry->preedit_cursor = preedit_cursor; + + text_entry_update_layout(entry); +} + +static void +text_entry_set_cursor_position(struct text_entry *entry, + int32_t x, int32_t y) +{ + entry->cursor = text_layout_xy_to_index(entry->layout, x, y); + + text_model_reset(entry->model); + + if (entry->cursor >= entry->preedit_cursor) { + entry->cursor -= entry->preedit_cursor; + } + + text_entry_update_layout(entry); + + widget_schedule_redraw(entry->widget); +} + +static void +text_entry_set_anchor_position(struct text_entry *entry, + int32_t x, int32_t y) +{ + entry->anchor = text_layout_xy_to_index(entry->layout, x, y); + + widget_schedule_redraw(entry->widget); +} + +static void +text_entry_delete_text(struct text_entry *entry, + uint32_t index, uint32_t length) +{ + if (entry->cursor > index) + entry->cursor -= length; + + entry->text[index] = '\0'; + strcat(entry->text, entry->text + index + length); + + text_entry_update_layout(entry); + + widget_schedule_redraw(entry->widget); +} + +static void +text_entry_draw_selection(struct text_entry *entry, cairo_t *cr) +{ + cairo_text_extents_t extents; + uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor; + uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor; + cairo_rectangle_t start; + cairo_rectangle_t end; + + if (entry->anchor == entry->cursor) + return; + + text_layout_extents(entry->layout, &extents); + + text_layout_index_to_pos(entry->layout, start_index, &start); + text_layout_index_to_pos(entry->layout, end_index, &end); + + cairo_save (cr); + + cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0); + cairo_rectangle(cr, + start.x, extents.y_bearing + extents.height + 2, + end.x - start.x, -extents.height - 4); + cairo_fill(cr); + + cairo_rectangle(cr, + start.x, extents.y_bearing + extents.height, + end.x - start.x, -extents.height); + cairo_clip(cr); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + text_layout_draw(entry->layout, cr); + + cairo_restore (cr); +} + +static void +text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr) +{ + cairo_text_extents_t extents; + cairo_rectangle_t cursor_pos; + + text_layout_extents(entry->layout, &extents); + text_layout_get_cursor_pos(entry->layout, + entry->cursor + entry->preedit_cursor, + &cursor_pos); + + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2); + cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2); + cairo_stroke(cr); +} + +static void +text_entry_draw_preedit(struct text_entry *entry, cairo_t *cr) +{ + cairo_text_extents_t extents; + cairo_rectangle_t start; + cairo_rectangle_t end; + + if (!entry->preedit_text) + return; + + text_layout_extents(entry->layout, &extents); + + text_layout_index_to_pos(entry->layout, entry->cursor, &start); + text_layout_index_to_pos(entry->layout, + entry->cursor + strlen(entry->preedit_text), + &end); + + cairo_save (cr); + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + cairo_rectangle(cr, + start.x, 0, + end.x - start.x, 1); + cairo_fill(cr); + + cairo_restore (cr); +} + +static void +text_entry_redraw_handler(struct widget *widget, void *data) +{ + struct text_entry *entry = data; + cairo_surface_t *surface; + struct rectangle allocation; + cairo_t *cr; + + surface = window_get_surface(entry->window); + widget_get_allocation(entry->widget, &allocation); + + cr = cairo_create(surface); + cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height); + cairo_clip(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + cairo_push_group(cr); + cairo_translate(cr, allocation.x, allocation.y); + + cairo_set_source_rgba(cr, 1, 1, 1, 1); + cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); + cairo_fill(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + if (entry->active) { + cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); + cairo_set_line_width (cr, 3); + cairo_set_source_rgba(cr, 0, 0, 1, 1.0); + cairo_stroke(cr); + } + + cairo_set_source_rgba(cr, 0, 0, 0, 1); + + cairo_translate(cr, 10, allocation.height / 2); + text_layout_draw(entry->layout, cr); + + text_entry_draw_selection(entry, cr); + + text_entry_draw_cursor(entry, cr); + + text_entry_draw_preedit(entry, cr); + + cairo_pop_group_to_source(cr); + cairo_paint(cr); + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static int +text_entry_motion_handler(struct widget *widget, + struct input *input, uint32_t time, + float x, float y, void *data) +{ + struct text_entry *entry = data; + struct rectangle allocation; + + widget_get_allocation(entry->widget, &allocation); + + text_entry_set_cursor_position(entry, + x - allocation.x, + y - allocation.y); + + return CURSOR_IBEAM; +} + +static void +text_entry_button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, + enum wl_pointer_button_state state, void *data) +{ + struct text_entry *entry = data; struct rectangle allocation; int32_t x, y; - struct wl_seat *seat; - if (state != WL_POINTER_BUTTON_STATE_PRESSED || button != BTN_LEFT) { + widget_get_allocation(entry->widget, &allocation); + input_get_position(input, &x, &y); + + if (button != BTN_LEFT) { return; } - input_get_position(input, &x, &y); + text_entry_set_cursor_position(entry, + x - allocation.x, + y - allocation.y); - widget_get_allocation(editor->widget, &allocation); - x -= allocation.x; - y -= allocation.y; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + struct wl_seat *seat = input_get_seat(input); - int32_t activate_entry = rectangle_contains(&editor->entry->allocation, x, y); - int32_t activate_editor = rectangle_contains(&editor->editor->allocation, x, y); - assert(!(activate_entry && activate_editor)); + text_entry_activate(entry, seat); - seat = input_get_seat(input); + text_entry_set_anchor_position(entry, + x - allocation.x, + y - allocation.y); - if (activate_entry) { - text_entry_activate(editor->entry, seat); - } else if (activate_editor) { - text_entry_activate(editor->editor, seat); + widget_set_motion_handler(entry->widget, text_entry_motion_handler); } else { + widget_set_motion_handler(entry->widget, NULL); + } +} + +static void +editor_button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, + enum wl_pointer_button_state state, void *data) +{ + struct editor *editor = data; + + if (button != BTN_LEFT) { + return; + } + + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + struct wl_seat *seat = input_get_seat(input); + text_entry_deactivate(editor->entry, seat); text_entry_deactivate(editor->editor, seat); } - - widget_schedule_redraw(widget); } static void @@ -339,8 +796,6 @@ global_handler(struct wl_display *display, uint32_t id, if (!strcmp(interface, "text_model_factory")) { editor->text_model_factory = wl_display_bind(display, id, &text_model_factory_interface); - } else if (!strcmp(interface, "wl_seat")) { - fprintf(stderr, "wl_seat added\n"); } } @@ -363,12 +818,13 @@ main(int argc, char *argv[]) editor.entry = text_entry_create(&editor, "Entry"); editor.editor = text_entry_create(&editor, "Editor"); + text_entry_set_preedit(editor.editor, "preedit", strlen("preedit")); window_set_title(editor.window, "Text Editor"); widget_set_redraw_handler(editor.widget, redraw_handler); widget_set_resize_handler(editor.widget, resize_handler); - widget_set_button_handler(editor.widget, button_handler); + widget_set_button_handler(editor.widget, editor_button_handler); window_schedule_resize(editor.window, 500, 400); diff --git a/clients/keyboard.c b/clients/keyboard.c index 9fdd8cc..c90c5b2 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -1,5 +1,6 @@ /* * Copyright © 2012 Openismus GmbH + * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -28,32 +29,144 @@ #include <cairo.h> #include "window.h" -#include "text-client-protocol.h" +#include "input-method-client-protocol.h" #include "desktop-shell-client-protocol.h" struct virtual_keyboard { struct input_panel *input_panel; struct input_method *input_method; + struct input_method_context *context; struct display *display; + char *preedit_string; +}; + +enum key_type { + keytype_default, + keytype_backspace, + keytype_enter, + keytype_space, + keytype_switch, + keytype_symbols, + keytype_tab +}; + +struct key { + enum key_type key_type; + + char *label; + char *alt; + + unsigned int width; +}; + +static const struct key keys[] = { + { keytype_default, "q", "Q", 1}, + { keytype_default, "w", "W", 1}, + { keytype_default, "e", "E", 1}, + { keytype_default, "r", "R", 1}, + { keytype_default, "t", "T", 1}, + { keytype_default, "y", "Y", 1}, + { keytype_default, "u", "U", 1}, + { keytype_default, "i", "I", 1}, + { keytype_default, "o", "O", 1}, + { keytype_default, "p", "P", 1}, + { keytype_backspace, "<--", "<--", 2}, + + { keytype_tab, "->|", "->|", 1}, + { keytype_default, "a", "A", 1}, + { keytype_default, "s", "S", 1}, + { keytype_default, "d", "D", 1}, + { keytype_default, "f", "F", 1}, + { keytype_default, "g", "G", 1}, + { keytype_default, "h", "H", 1}, + { keytype_default, "j", "J", 1}, + { keytype_default, "k", "K", 1}, + { keytype_default, "l", "L", 1}, + { keytype_enter, "Enter", "Enter", 2}, + + { keytype_switch, "ABC", "abc", 2}, + { keytype_default, "z", "Z", 1}, + { keytype_default, "x", "X", 1}, + { keytype_default, "c", "C", 1}, + { keytype_default, "v", "V", 1}, + { keytype_default, "b", "B", 1}, + { keytype_default, "n", "N", 1}, + { keytype_default, "m", "M", 1}, + { keytype_default, ",", ",", 1}, + { keytype_default, ".", ".", 1}, + { keytype_switch, "ABC", "abc", 1}, + + { keytype_symbols, "?123", "?123", 2}, + { keytype_space, "", "", 8}, + { keytype_symbols, "?123", "?123", 2} +}; + +static const unsigned int columns = 12; +static const unsigned int rows = 4; + +static const double key_width = 60; +static const double key_height = 50; + +enum keyboard_state { + keyboardstate_default, + keyboardstate_uppercase }; struct keyboard { struct virtual_keyboard *keyboard; struct window *window; struct widget *widget; - int cx; - int cy; + + enum keyboard_state state; }; static void +draw_key(const struct key *key, + cairo_t *cr, + enum keyboard_state state, + unsigned int row, + unsigned int col) +{ + const char *label; + cairo_text_extents_t extents; + + cairo_save(cr); + cairo_rectangle(cr, + col * key_width, row * key_height, + key->width * key_width, key_height); + cairo_clip(cr); + + /* Paint frame */ + cairo_rectangle(cr, + col * key_width, row * key_height, + key->width * key_width, key_height); + cairo_set_line_width(cr, 3); + cairo_stroke(cr); + + /* Paint text */ + label = state == keyboardstate_default ? key->label : key->alt; + cairo_text_extents(cr, label, &extents); + + cairo_translate(cr, + col * key_width, + row * key_height); + cairo_translate(cr, + (key->width * key_width - extents.width) / 2, + (key_height - extents.y_bearing) / 2); + cairo_show_text(cr, label); + + cairo_restore(cr); +} + +static void redraw_handler(struct widget *widget, void *data) { struct keyboard *keyboard = data; cairo_surface_t *surface; struct rectangle allocation; cairo_t *cr; - int cx, cy; - int i; + unsigned int i; + unsigned int row = 0, col = 0; surface = window_get_surface(keyboard->window); widget_get_allocation(keyboard->widget, &allocation); @@ -62,37 +175,26 @@ redraw_handler(struct widget *widget, void *data) cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height); cairo_clip(cr); - cairo_translate(cr, allocation.x, allocation.y); + cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(cr, 16); - cx = keyboard->cx; - cy = keyboard->cy; + cairo_translate(cr, allocation.x, allocation.y); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 1, 1, 1, 0.5); - cairo_rectangle(cr, 0, 0, 10 * cx, 5 * cy); + cairo_set_source_rgba(cr, 1, 1, 1, 0.75); + cairo_rectangle(cr, 0, 0, columns * key_width, rows * key_height); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - for (i = 0; i <= 'Z' - '0'; ++i) { - const int x = i % 10; - const int y = i / 10; - char text[] = { i + '0', '\0' }; - cairo_text_extents_t extents; - int dx, dy; - - cairo_text_extents(cr, text, &extents); - dx = extents.width; - dy = extents.height; - + for (i = 0; i < sizeof(keys) / sizeof(*keys); ++i) { cairo_set_source_rgb(cr, 0, 0, 0); - cairo_rectangle(cr, x * cx, y * cy, cx, cy); - cairo_stroke(cr); - - cairo_move_to(cr, x * cx + 0.5 * (cx - dx), y * cy + 0.5 * (cy - dy)); - - cairo_set_source_rgb(cr, 0, 0, 0); - cairo_show_text(cr, text); + draw_key(&keys[i], cr, keyboard->state, row, col); + col += keys[i].width; + if (col >= columns) { + row += 1; + col = 0; + } } cairo_destroy(cr); @@ -107,6 +209,56 @@ resize_handler(struct widget *widget, } static void +keyboard_handle_key(struct keyboard *keyboard, const struct key *key) +{ + const char *label = keyboard->state == keyboardstate_default ? key->label : key->alt; + + switch (key->key_type) { + case keytype_default: + keyboard->keyboard->preedit_string = strcat(keyboard->keyboard->preedit_string, + label); + input_method_context_preedit_string(keyboard->keyboard->context, + keyboard->keyboard->preedit_string, + strlen(keyboard->keyboard->preedit_string)); + break; + case keytype_backspace: + if (strlen(keyboard->keyboard->preedit_string) == 0) { + input_method_context_delete_surrounding_text(keyboard->keyboard->context, + -1, 1); + } + break; + case keytype_enter: + input_method_context_key(keyboard->keyboard->context, + XKB_KEY_KP_Enter, WL_KEYBOARD_KEY_STATE_PRESSED); + break; + case keytype_space: + keyboard->keyboard->preedit_string = strcat(keyboard->keyboard->preedit_string, + " "); + input_method_context_preedit_string(keyboard->keyboard->context, + "", + 0); + input_method_context_commit_string(keyboard->keyboard->context, + keyboard->keyboard->preedit_string, + strlen(keyboard->keyboard->preedit_string)); + free(keyboard->keyboard->preedit_string); + keyboard->keyboard->preedit_string = strdup(""); + break; + case keytype_switch: + if (keyboard->state == keyboardstate_default) + keyboard->state = keyboardstate_uppercase; + else + keyboard->state = keyboardstate_default; + break; + case keytype_symbols: + break; + case keytype_tab: + input_method_context_key(keyboard->keyboard->context, + XKB_KEY_Tab, WL_KEYBOARD_KEY_STATE_PRESSED); + break; + } +} + +static void button_handler(struct widget *widget, struct input *input, uint32_t time, uint32_t button, @@ -114,8 +266,9 @@ button_handler(struct widget *widget, { struct keyboard *keyboard = data; struct rectangle allocation; - int32_t x, y; - char text[] = { '0', '\0' }; + int32_t x, y; + int row, col; + unsigned int i; if (state != WL_POINTER_BUTTON_STATE_PRESSED || button != BTN_LEFT) { return; @@ -127,14 +280,92 @@ button_handler(struct widget *widget, x -= allocation.x; y -= allocation.y; - text[0] = y / keyboard->cy * 10 + x / keyboard->cx + '0'; + row = y / key_height; + col = x / key_width + row * columns; + for (i = 0; i < sizeof(keys) / sizeof(*keys); ++i) { + col -= keys[i].width; + if (col < 0) + break; + } - input_method_commit_string(keyboard->keyboard->input_method, text, -1); + keyboard_handle_key(keyboard, &keys[i]); widget_schedule_redraw(widget); } static void +input_method_context_surrounding_text(void *data, + struct input_method_context *context, + const char *text, + uint32_t cursor, + uint32_t anchor) +{ + fprintf(stderr, "Surrounding text updated: %s\n", text); +} + +static void +input_method_context_reset(void *data, + struct input_method_context *context) +{ + struct virtual_keyboard *keyboard = data; + + fprintf(stderr, "Reset pre-edit buffer\n"); + + if (strlen(keyboard->preedit_string)) { + input_method_context_preedit_string(context, + "", + 0); + free(keyboard->preedit_string); + keyboard->preedit_string = strdup(""); + } +} + +static const struct input_method_context_listener input_method_context_listener = { + input_method_context_surrounding_text, + input_method_context_reset +}; + +static void +input_method_activate(void *data, + struct input_method *input_method, + struct input_method_context *context) +{ + struct virtual_keyboard *keyboard = data; + + if (keyboard->context) + input_method_context_destroy(keyboard->context); + + if (keyboard->preedit_string) + free(keyboard->preedit_string); + + keyboard->preedit_string = strdup(""); + + keyboard->context = context; + input_method_context_add_listener(context, + &input_method_context_listener, + keyboard); +} + +static void +input_method_deactivate(void *data, + struct input_method *input_method, + struct input_method_context *context) +{ + struct virtual_keyboard *keyboard = data; + + if (!keyboard->context) + return; + + input_method_context_destroy(keyboard->context); + keyboard->context = NULL; +} + +static const struct input_method_listener input_method_listener = { + input_method_activate, + input_method_deactivate +}; + +static void global_handler(struct wl_display *display, uint32_t id, const char *interface, uint32_t version, void *data) { @@ -144,6 +375,7 @@ global_handler(struct wl_display *display, uint32_t id, keyboard->input_panel = wl_display_bind(display, id, &input_panel_interface); } else if (!strcmp(interface, "input_method")) { keyboard->input_method = wl_display_bind(display, id, &input_method_interface); + input_method_add_listener(keyboard->input_method, &input_method_listener, keyboard); } } @@ -161,15 +393,14 @@ keyboard_create(struct output *output, struct virtual_keyboard *virtual_keyboard window_set_title(keyboard->window, "Virtual keyboard"); window_set_user_data(keyboard->window, keyboard); - - keyboard->cx = 40; - keyboard->cy = 40; widget_set_redraw_handler(keyboard->widget, redraw_handler); widget_set_resize_handler(keyboard->widget, resize_handler); widget_set_button_handler(keyboard->widget, button_handler); - window_schedule_resize(keyboard->window, keyboard->cx * 10, keyboard->cy * 5); + window_schedule_resize(keyboard->window, + columns * key_width, + rows * key_height); input_panel_set_surface(virtual_keyboard->input_panel, window_get_wl_surface(keyboard->window), @@ -201,6 +432,9 @@ main(int argc, char *argv[]) return -1; } + virtual_keyboard.context = NULL; + virtual_keyboard.preedit_string = NULL; + wl_display_add_global_listener(display_get_display(virtual_keyboard.display), global_handler, &virtual_keyboard); diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 808c2ee..a67ce2f 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -280,12 +280,10 @@ toggle_fullscreen(struct window *window, int fullscreen) window->configured = 0; if (fullscreen) { - window->opaque = 1; wl_shell_surface_set_fullscreen(window->shell_surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); } else { - window->opaque = 0; wl_shell_surface_set_toplevel(window->shell_surface); handle_configure(window, window->shell_surface, 0, window->window_size.width, @@ -403,7 +401,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) eglSwapBuffers(window->display->egl.dpy, window->egl_surface); - if (window->opaque) { + if (window->opaque || window->fullscreen) { region = wl_compositor_create_region(window->display->compositor); wl_region_add(region, 0, 0, window->window_size.width, @@ -594,7 +592,7 @@ main(int argc, char **argv) struct sigaction sigint; struct display display = { 0 }; struct window window = { 0 }; - int i, opaque = 0; + int i; window.display = &display; display.window = &window; @@ -605,7 +603,7 @@ main(int argc, char **argv) if (strcmp("-f", argv[i]) == 0) window.fullscreen = 1; else if (strcmp("-o", argv[i]) == 0) - opaque = 1; + window.opaque = 1; else if (strcmp("-h", argv[i]) == 0) usage(EXIT_SUCCESS); else @@ -621,10 +619,7 @@ main(int argc, char **argv) wl_display_get_fd(display.display, event_mask_update, &display); wl_display_iterate(display.display, WL_DISPLAY_READABLE); - if (window.fullscreen) - window.opaque = 1; - - init_egl(&display, opaque); + init_egl(&display, window.opaque); create_surface(&window); init_gl(&window); diff --git a/man/weston.man b/man/weston.man index eff09c0..7667076 100644 --- a/man/weston.man +++ b/man/weston.man @@ -1,4 +1,4 @@ -.TH WESTON 1 "2012-08-29" "Weston __version__" +.TH WESTON 1 "2012-09-13" "Weston __version__" .SH NAME weston \- the reference Wayland server .SH SYNOPSIS @@ -22,11 +22,8 @@ under another Wayland server), it should be done with the command .B weston-launch to set up proper privileged access to devices. -Weston also supports X clients by XWayland. XWayland requires a special -X.org server to be installed. This X server will connect to a Wayland -server as a Wayland client, and X clients will connect to the X server. -XWayland provides backwards compatibility to X applications in a Wayland -stack. +Weston also supports X clients via +.BR XWayland ", see below." . .\" *************************************************************** .SH BACKENDS @@ -70,6 +67,21 @@ and the special client which provides the basic user interface. . .\" *************************************************************** +.SH XWAYLAND +XWayland requires a special X.org server to be installed. This X server will +connect to a Wayland server as a Wayland client, and X clients will connect to +the X server. XWayland provides backwards compatibility to X applications in a +Wayland stack. + +XWayland is activated by instructing +.BR weston " to load " xwayland.so " module, see " EXAMPLES . +Weston starts listening on a new X display socket, and exports it in the +environment variable +.BR DISPLAY . +When the first X client connects, Weston launches a special X server as a +Wayland client to handle the X client and all future X clients. +. +.\" *************************************************************** .SH OPTIONS . .SS Weston core options: @@ -100,8 +112,8 @@ Append log messages to the file .I file.log instead of writing them to stderr. .TP -\fB\-\-module\fR=\fImodule.so\fR -Load a generic plugin module \fImodule.so\fR. Only used by the test +\fB\-\-modules\fR=\fImodule1.so,module2.so\fR +Load the comma-separated list of modules. Only used by the test suite. The file is searched for in .IR "__weston_modules_dir__" , or you can pass an absolute path. @@ -113,14 +125,6 @@ Weston will export .B WAYLAND_DISPLAY with this value in the environment for all child processes to allow them to connect to the right server automatically. -.TP -.B \-\-xserver -Activate XWayland. Weston starts listening on a new X display socket, and -exports it in the environment variable -.BR DISPLAY . -When the first X client connects, Weston launches a special X server as a -Wayland client to handle the X client and all future X clients. -. .SS DRM backend options: .TP \fB\-\-connector\fR=\fIconnectorid\fR @@ -220,8 +224,10 @@ http://wayland.freedesktop.org/ . .\" *************************************************************** .SH EXAMPLES -.IP "Launch Weston with the DRM backend, directly on a VT" +.IP "Launch Weston with the DRM backend on a VT" weston-launch +.IP "Launch Weston with the DRM backend and XWayland support" +weston-launch -- --modules=xwayland.so .IP "Launch Weston (wayland-1) nested in another Weston instance (wayland-0)" WAYLAND_DISPLAY=wayland-0 weston -Swayland-1 .IP "From an X terminal, launch Weston with the x11 backend" diff --git a/protocol/Makefile.am b/protocol/Makefile.am index bf5ef99..c68e4c1 100644 --- a/protocol/Makefile.am +++ b/protocol/Makefile.am @@ -4,4 +4,5 @@ EXTRA_DIST = \ tablet-shell.xml \ xserver.xml \ text.xml \ + input-method.xml \ workspaces.xml diff --git a/protocol/input-method.xml b/protocol/input-method.xml new file mode 100644 index 0000000..09ab10c --- a/dev/null +++ b/protocol/input-method.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="input_method"> + <copyright> + Copyright © 2012 Intel Corporation + + 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. + </copyright> + + + <interface name="input_method_context" version="1"> + <description summary="input method context"> + Corresponds to a text model on input method side. An input method context + is created on text mode activation on the input method side. It allows to + receive information about the text model from the application via events. + Input method contexts do not keep state after deactivation and should be + destroyed after deactivation is handled. + </description> + <request name="destroy" type="destructor"/> + <request name="commit_string"> + <description summary="commit string"> + Send the commit string text to the applications text model. + </description> + <arg name="text" type="string"/> + <arg name="index" type="uint"/> + </request> + <request name="preedit_string"> + <description summary="pre-edit string"> + Send the pre-edit string text to the applications text model. + </description> + <arg name="text" type="string"/> + <arg name="index" type="uint"/> + </request> + <request name="delete_surrounding_text"> + <arg name="index" type="int"/> + <arg name="length" type="uint"/> + </request> + <request name="key"> + <arg name="key" type="uint"/> + <arg name="state" type="uint"/> + </request> + <event name="surrounding_text"> + <description summary="surrounding text event"> + The plain surrounding text around the input position. Cursor is the + position within the surrounding text. Anchor is the position of the + selection anchor within the surrounding text. If there is no selected + text anchor is the same as cursor. + </description> + <arg name="text" type="string"/> + <arg name="cursor" type="uint"/> + <arg name="anchor" type="uint"/> + </event> + <event name="reset"> + </event> + </interface> + + <interface name="input_method" version="1"> + <description summary="input method"> + An input method object is responsible to compose text in response to + input from hardware or virtual keyboards. There is one input method + object per seat. On activate there is a new input method context object + created which allows the input method to communicate with the text model. + </description> + <event name="activate"> + <description summary="activate event"> + A text model was activated. Creates an input method context object + which allows communication with the text model. + </description> + <arg name="id" type="new_id" interface="input_method_context"/> + </event> + <event name="deactivate"> + <description summary="activate event"> + The text model corresponding to the context argument was deactivated. + The input method context should be destroyed after deactivation is + handled. + </description> + <arg name="context" type="object" interface="input_method_context"/> + </event> + </interface> +</protocol> diff --git a/protocol/text.xml b/protocol/text.xml index e73cacb..857fd83 100644 --- a/protocol/text.xml +++ b/protocol/text.xml @@ -1,22 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> <protocol name="text"> - <interface name="text_model" version="1"> + + <copyright> + Copyright © 2012 Intel Corporation + + 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. + </copyright> + + <interface name="text_model" version="1"> + <description summary="text model"> + A model for text input. Adds support for text input and input methods to + applications. A text_model object is created from a text_model_factory and + corresponds typically to a text entry in an application. Requests are used + to activate/deactivate the model and set information like surrounding and + selected text or the content type. The information about entered text is + sent to the model via the pre-edit and commit events. Using this interface + removes the need for applications to directly process hardware key events + and compose text out of them. + </description> <request name="set_surrounding_text"> + <description summary="sets the surrounding text"> + Sets the plain surrounding text around the input position. Cursor is the + byte index within the surrounding text. Anchor is the byte index of the + selection anchor within the surrounding text. If there is no selected + text anchor is the same as cursor. + </description> <arg name="text" type="string"/> - </request> - <request name="set_cursor_index"> - <arg name="index" type="uint"/> + <arg name="cursor" type="uint"/> + <arg name="anchor" type="uint"/> </request> <request name="activate"> + <description summary="request activation"> + Requests the model to be activated (typically when the text entry gets + focus). The seat argument is a wl_seat which maintains the focus for + this activation. The surface argument is a wl_surface assigned to the + model and tracked for focus lost. The activated event is emitted on + successful activation. + </description> <arg name="seat" type="object" interface="wl_seat"/> <arg name="surface" type="object" interface="wl_surface"/> </request> <request name="deactivate"> + <description summary="request deactivation"> + Requests the model to be deactivated (typically when the text entry + lost focus). The seat argument is a wl_seat which was used for + activation. + </description> <arg name="seat" type="object" interface="wl_seat"/> </request> - <request name="set_selected_text"> - <arg name="text" type="string"/> - <arg name="index" type="int"/> - </request> + <request name="reset"> + <description summary="reset"> + Should be called by an editor widget when the input state should + be reseted, for example after the text was changed outside of the + normal input method flow. + </description> + </request> <request name="set_micro_focus"> <arg name="x" type="int"/> <arg name="y" type="int"/> @@ -27,33 +84,73 @@ <request name="set_content_type"/> <event name="commit_string"> + <description summary="commit"> + Notify when text should be inserted into the editor widget. The text + to commit could be either just a single character after a key press + or the result of some composing (pre-edit). It also sets the new + cursor position (as byte index) relative to the inserted text. + </description> <arg name="text" type="string"/> <arg name="index" type="uint"/> </event> <event name="preedit_string"> + <description summary="pre-edit"> + Notify when a new composing text (pre-edit) should be set around the + current cursor position. Any previously set composing text should + be removed. It also sets the cursor positon (as byte index) relative + to the start of the composing text. + </description> <arg name="text" type="string"/> <arg name="index" type="uint"/> </event> + <event name="delete_surrounding_text"> + <description summary="delete surrounding text"> + Notify when the text around the current cursor position should be + deleted. Index is relative to the current cursor (as byte index). + Length is the length of deleted text (as bytes). + </description> + <arg name="index" type="int"/> + <arg name="length" type="uint"/> + </event> <event name="preedit_styling"/> - <event name="key"/> + <event name="key"> + <description summary="key"> + Notify when a key event was sent. Key events should not be used + for normal text input operations, which should be done with + commit_string, delete_surrounfing_text, etc. The key event follows + the wl_keyboard key event convention. Key is a XKB keycode, state a + wl_keyboard key_state. + </description> + <arg name="key" type="uint"/> + <arg name="state" type="uint"/> + </event> <event name="selection_replacement"/> <event name="direction"/> <event name="locale"/> - <event name="activated"/> - <event name="deactivated"/> + <event name="activated"> + <description summary="activated event"> + Notify the model when it is activated. Typically in response to an + activate request. + </description> + </event> + <event name="deactivated"> + <description summary="deactivated event"> + Notify the model when it is deactivated. Either in response to a + deactivate request or when the assigned surface lost focus or was + destroyed. + </description> + </event> </interface> <interface name="text_model_factory" version="1"> + <description summary="text model factory"> + A factory for text models. This object is a singleton global. + </description> <request name="create_text_model"> + <description summary="create text model"> + Creates a new text model object. + </description> <arg name="id" type="new_id" interface="text_model"/> - <arg name="surface" type="object" interface="wl_surface"/> - </request> - </interface> - - <interface name="input_method" version="1"> - <request name="commit_string"> - <arg name="text" type="string"/> - <arg name="index" type="uint"/> </request> </interface> </protocol> diff --git a/src/.gitignore b/src/.gitignore index cd68a3e..3c27953 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -13,3 +13,7 @@ desktop-shell-protocol.c desktop-shell-server-protocol.h text-protocol.c text-server-protocol.h +workspaces-protocol.c +workspaces-server-protocol.h +input-method-protocol.c +input-method-server-protocol.h diff --git a/src/Makefile.am b/src/Makefile.am index a20d584..3dd326c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,8 @@ weston_SOURCES = \ text-backend.c \ text-protocol.c \ text-server-protocol.h \ + input-method-protocol.c \ + input-method-server-protocol.h \ workspaces-protocol.c \ workspaces-server-protocol.h \ util.c \ @@ -176,6 +178,8 @@ BUILT_SOURCES = \ desktop-shell-server-protocol.h \ text-protocol.c \ text-server-protocol.h \ + input-method-protocol.c \ + input-method-server-protocol.h \ workspaces-server-protocol.h \ workspaces-protocol.c \ git-version.h diff --git a/src/compositor-android.c b/src/compositor-android.c index 8e13da0..c5b1aa5 100644 --- a/src/compositor-android.c +++ b/src/compositor-android.c @@ -476,11 +476,11 @@ android_compositor_create(struct wl_display *display, int argc, char *argv[], if (android_init_egl(compositor, output) < 0) goto err_output; + android_compositor_add_output(compositor, output); + if (gles2_renderer_init(&compositor->base) < 0) goto err_egl; - android_compositor_add_output(compositor, output); - compositor->seat = android_seat_create(compositor); if (!compositor->seat) goto err_egl; diff --git a/src/compositor.c b/src/compositor.c index f23a658..b5d192a 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -456,8 +456,7 @@ weston_surface_update_transform(struct weston_surface *surface) weston_surface_damage_below(surface); - if (weston_surface_is_mapped(surface)) - weston_surface_assign_output(surface); + weston_surface_assign_output(surface); } WL_EXPORT void @@ -847,7 +846,6 @@ surface_accumulate_damage(struct weston_surface *surface, surface->geometry.y - surface->plane->y); } - pixman_region32_subtract(&surface->damage, &surface->damage, opaque); pixman_region32_union(&surface->plane->damage, &surface->plane->damage, &surface->damage); empty_region(&surface->damage); @@ -864,7 +862,7 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs) struct weston_animation *animation, *next; struct weston_frame_callback *cb, *cnext; struct wl_list frame_callback_list; - pixman_region32_t opaque, output_damage, new_damage; + pixman_region32_t opaque, output_damage; weston_compositor_update_drag_surfaces(ec); @@ -897,21 +895,10 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs) pixman_region32_fini(&opaque); pixman_region32_init(&output_damage); - - pixman_region32_init(&new_damage); - pixman_region32_copy(&new_damage, &ec->primary_plane.damage); - - pixman_region32_union(&ec->primary_plane.damage, - &ec->primary_plane.damage, - &output->previous_damage); - - pixman_region32_intersect(&output->previous_damage, - &new_damage, &output->region); - pixman_region32_intersect(&output_damage, &ec->primary_plane.damage, &output->region); - - pixman_region32_fini(&new_damage); + pixman_region32_subtract(&ec->primary_plane.damage, + &ec->primary_plane.damage, &output->region); if (output->dirty) weston_output_update_matrix(output); @@ -1038,7 +1025,7 @@ weston_compositor_fade(struct weston_compositor *compositor, float tint) weston_surface_set_color(surface, 0.0, 0.0, 0.0, 0.0); wl_list_insert(&compositor->fade_layer.surface_list, &surface->layer_link); - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); compositor->fade.surface = surface; pixman_region32_init(&surface->input); } @@ -1941,7 +1928,7 @@ pointer_cursor_surface_configure(struct weston_surface *es, if (!weston_surface_is_mapped(es)) { wl_list_insert(&es->compositor->cursor_layer.surface_list, &es->layer_link); - weston_surface_assign_output(es); + weston_surface_update_transform(es); } } @@ -2428,7 +2415,7 @@ device_map_drag_surface(struct weston_seat *seat) list = &seat->compositor->cursor_layer.surface_list; wl_list_insert(list, &seat->drag_surface->layer_link); - weston_surface_assign_output(seat->drag_surface); + weston_surface_update_transform(seat->drag_surface); empty_region(&seat->drag_surface->input); } @@ -2522,9 +2509,13 @@ WL_EXPORT void weston_output_destroy(struct weston_output *output) { struct weston_compositor *c = output->compositor; + int i; pixman_region32_fini(&output->region); - pixman_region32_fini(&output->previous_damage); + + for (i = 0; i < 2; i++) + pixman_region32_fini(&output->buffer_damage[i]); + output->compositor->output_id_pool &= ~(1 << output->id); wl_display_remove_global(c->wl_display, output->global); @@ -2648,10 +2639,15 @@ weston_output_transform_init(struct weston_output *output, uint32_t transform) WL_EXPORT void weston_output_move(struct weston_output *output, int x, int y) { + int i; + output->x = x; output->y = y; - pixman_region32_init(&output->previous_damage); + output->current_buffer = 0; + for (i = 0; i < 2; i++) + pixman_region32_init(&output->buffer_damage[i]); + pixman_region32_init_rect(&output->region, x, y, output->width, output->height); @@ -2770,6 +2766,7 @@ weston_compositor_init(struct weston_compositor *ec, screenshooter_create(ec); text_cursor_position_notifier_create(ec); + text_model_factory_create(ec); wl_data_device_manager_init(ec->wl_display); @@ -2861,7 +2858,7 @@ on_segv_signal(int s, siginfo_t *siginfo, void *context) static void * -load_module(const char *name, const char *entrypoint, void **handle) +load_module(const char *name, const char *entrypoint) { char path[PATH_MAX]; void *module, *init; @@ -2871,6 +2868,13 @@ load_module(const char *name, const char *entrypoint, void **handle) else snprintf(path, sizeof path, "%s", name); + module = dlopen(path, RTLD_NOW | RTLD_NOLOAD); + if (module) { + weston_log("Module '%s' already loaded\n", path); + dlclose(module); + return NULL; + } + weston_log("Loading module '%s'\n", path); module = dlopen(path, RTLD_NOW); if (!module) { @@ -2887,6 +2891,32 @@ load_module(const char *name, const char *entrypoint, void **handle) return init; } +static int +load_modules(struct weston_compositor *ec, const char *modules) +{ + const char *p, *end; + char buffer[256]; + int (*module_init)(struct weston_compositor *ec); + + if (modules == NULL) + return 0; + + p = modules; + while (*p) { + end = strchrnul(p, ','); + snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p); + module_init = load_module(buffer, "module_init"); + if (module_init) + module_init(ec); + p = end; + while (*p == ',') + p++; + + } + + return 0; +} + static const char xdg_error_message[] = "fatal: environment variable XDG_RUNTIME_DIR is not set.\n"; @@ -2945,8 +2975,7 @@ usage(int error_code) "\t\t\t\tx11-backend.so or wayland-backend.so\n" " -S, --socket=NAME\tName of socket to listen on\n" " -i, --idle-time=SECS\tIdle time in seconds\n" - " --xserver\t\tEnable X server integration\n" - " --module\t\tLoad the specified module\n" + " --modules\t\tLoad the comma-separated list of modules\n" " --log==FILE\t\tLog to the given file\n" " -h, --help\t\tThis help message\n\n"); @@ -2982,37 +3011,32 @@ int main(int argc, char *argv[]) struct wl_event_source *signals[4]; struct wl_event_loop *loop; struct sigaction segv_action; - void *shell_module, *backend_module, *xserver_module; - int (*module_init)(struct weston_compositor *ec); struct weston_compositor *(*backend_init)(struct wl_display *display, int argc, char *argv[], const char *config_file); int i; char *backend = NULL; - char *shell = NULL; - char *module = NULL; + const char *modules = "desktop-shell.so", *option_modules = NULL; char *log = NULL; int32_t idle_time = 300; - int32_t xserver = 0; int32_t help = 0; - char *socket_name = NULL; + char *socket_name = "wayland-0"; char *config_file; - const struct config_key shell_config_keys[] = { - { "type", CONFIG_KEY_STRING, &shell }, + const struct config_key core_config_keys[] = { + { "modules", CONFIG_KEY_STRING, &modules }, }; const struct config_section cs[] = { - { "shell", - shell_config_keys, ARRAY_LENGTH(shell_config_keys) }, + { "core", + core_config_keys, ARRAY_LENGTH(core_config_keys) }, }; const struct weston_option core_options[] = { { WESTON_OPTION_STRING, "backend", 'B', &backend }, { WESTON_OPTION_STRING, "socket", 'S', &socket_name }, { WESTON_OPTION_INTEGER, "idle-time", 'i', &idle_time }, - { WESTON_OPTION_BOOLEAN, "xserver", 0, &xserver }, - { WESTON_OPTION_STRING, "module", 0, &module }, + { WESTON_OPTION_STRING, "modules", 0, &option_modules }, { WESTON_OPTION_STRING, "log", 0, &log }, { WESTON_OPTION_BOOLEAN, "help", 'h', &help }, }; @@ -3062,9 +3086,9 @@ int main(int argc, char *argv[]) } config_file = config_file_path("weston.ini"); - parse_config_file(config_file, cs, ARRAY_LENGTH(cs), shell); + parse_config_file(config_file, cs, ARRAY_LENGTH(cs), NULL); - backend_init = load_module(backend, "backend_init", &backend_module); + backend_init = load_module(backend, "backend_init"); if (!backend_init) exit(EXIT_FAILURE); @@ -3092,35 +3116,12 @@ int main(int argc, char *argv[]) ec->option_idle_time = idle_time; ec->idle_time = idle_time; - module_init = NULL; - if (xserver) - module_init = load_module("xwayland.so", - "weston_xserver_init", - &xserver_module); - if (module_init && module_init(ec) < 0) { - ret = EXIT_FAILURE; - goto out; - } - - if (socket_name) - setenv("WAYLAND_DISPLAY", socket_name, 1); + setenv("WAYLAND_DISPLAY", socket_name, 1); - if (!shell) - shell = "desktop-shell.so"; - module_init = load_module(shell, "shell_init", &shell_module); - if (!module_init || module_init(ec) < 0) { - ret = EXIT_FAILURE; + if (load_modules(ec, modules) < 0) goto out; - } - - - module_init = NULL; - if (module) - module_init = load_module(module, "module_init", NULL); - if (module_init && module_init(ec) < 0) { - ret = EXIT_FAILURE; + if (load_modules(ec, option_modules) < 0) goto out; - } if (wl_display_add_socket(display, socket_name)) { weston_log("fatal: failed to add socket: %m\n"); diff --git a/src/compositor.h b/src/compositor.h index 2954703..4760993 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -160,7 +160,8 @@ struct weston_output { int32_t mm_width, mm_height; struct weston_border border; pixman_region32_t region; - pixman_region32_t previous_damage; + int current_buffer; + pixman_region32_t buffer_damage[2]; int repaint_needed; int repaint_scheduled; struct weston_output_zoom zoom; @@ -276,7 +277,6 @@ struct weston_renderer { }; struct weston_compositor { - struct wl_shm *shm; struct wl_signal destroy_signal; EGLDisplay egl_display; @@ -748,6 +748,9 @@ void text_cursor_position_notifier_create(struct weston_compositor *ec); void +text_model_factory_create(struct weston_compositor *ec); + +void input_method_create(struct weston_compositor *ec, struct weston_seat *seat); @@ -770,9 +773,6 @@ weston_client_launch(struct weston_compositor *compositor, void weston_watch_process(struct weston_process *process); -int -weston_xserver_init(struct weston_compositor *compositor); - struct weston_surface_animation; typedef void (*weston_surface_animation_done_func_t)(struct weston_surface_animation *animation, void *data); @@ -794,10 +794,6 @@ weston_surface_set_color(struct weston_surface *surface, void weston_surface_destroy(struct weston_surface *surface); -struct weston_compositor * -backend_init(struct wl_display *display, int argc, char *argv[], - const char *config_file); - int weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode); @@ -806,4 +802,11 @@ gles2_renderer_init(struct weston_compositor *ec); void gles2_renderer_destroy(struct weston_compositor *ec); +struct weston_compositor * +backend_init(struct wl_display *display, int argc, char *argv[], + const char *config_file); + +int +module_init(struct weston_compositor *compositor); + #endif diff --git a/src/gles2-renderer.c b/src/gles2-renderer.c index a19c8c5..761f4fe 100644 --- a/src/gles2-renderer.c +++ b/src/gles2-renderer.c @@ -25,6 +25,8 @@ #include <stdlib.h> #include <string.h> #include <ctype.h> +#include <float.h> +#include <assert.h> #include "compositor.h" @@ -64,65 +66,320 @@ print_egl_error_state(void) egl_error_string(code), (long)code); } +struct polygon8 { + GLfloat x[8]; + GLfloat y[8]; + int n; +}; + +struct clip_context { + struct { + GLfloat x; + GLfloat y; + } prev; + + struct { + GLfloat x1, y1; + GLfloat x2, y2; + } clip; + + struct { + GLfloat *x; + GLfloat *y; + } vertices; +}; + +static GLfloat +float_difference(GLfloat a, GLfloat b) +{ + /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */ + static const GLfloat max_diff = 4.0f * FLT_MIN; + static const GLfloat max_rel_diff = 4.0e-5; + GLfloat diff = a - b; + GLfloat adiff = fabsf(diff); + + if (adiff <= max_diff) + return 0.0f; + + a = fabsf(a); + b = fabsf(b); + if (adiff <= (a > b ? a : b) * max_rel_diff) + return 0.0f; + + return diff; +} + +/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg. + * Compute the y coordinate of the intersection. + */ +static GLfloat +clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, + GLfloat x_arg) +{ + GLfloat a; + GLfloat diff = float_difference(p1x, p2x); + + /* Practically vertical line segment, yet the end points have already + * been determined to be on different sides of the line. Therefore + * the line segment is part of the line and intersects everywhere. + * Return the end point, so we use the whole line segment. + */ + if (diff == 0.0f) + return p2y; + + a = (x_arg - p2x) / diff; + return p2y + (p1y - p2y) * a; +} + +/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg. + * Compute the x coordinate of the intersection. + */ +static GLfloat +clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, + GLfloat y_arg) +{ + GLfloat a; + GLfloat diff = float_difference(p1y, p2y); + + /* Practically horizontal line segment, yet the end points have already + * been determined to be on different sides of the line. Therefore + * the line segment is part of the line and intersects everywhere. + * Return the end point, so we use the whole line segment. + */ + if (diff == 0.0f) + return p2x; + + a = (y_arg - p2y) / diff; + return p2x + (p1x - p2x) * a; +} + +enum path_transition { + PATH_TRANSITION_OUT_TO_OUT = 0, + PATH_TRANSITION_OUT_TO_IN = 1, + PATH_TRANSITION_IN_TO_OUT = 2, + PATH_TRANSITION_IN_TO_IN = 3, +}; + +static void +clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + *ctx->vertices.x++ = x; + *ctx->vertices.y++ = y; +} + +static enum path_transition +path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1); +} + +static enum path_transition +path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2); +} + +static enum path_transition +path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1); +} + +static enum path_transition +path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +{ + return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2); +} + +static void +clip_polygon_leftright(struct clip_context *ctx, + enum path_transition transition, + GLfloat x, GLfloat y, GLfloat clip_x) +{ + GLfloat yi; + + switch (transition) { + case PATH_TRANSITION_IN_TO_IN: + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_IN_TO_OUT: + yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x); + clip_append_vertex(ctx, clip_x, yi); + break; + case PATH_TRANSITION_OUT_TO_IN: + yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x); + clip_append_vertex(ctx, clip_x, yi); + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_OUT_TO_OUT: + /* nothing */ + break; + default: + assert(0 && "bad enum path_transition"); + } + + ctx->prev.x = x; + ctx->prev.y = y; +} + +static void +clip_polygon_topbottom(struct clip_context *ctx, + enum path_transition transition, + GLfloat x, GLfloat y, GLfloat clip_y) +{ + GLfloat xi; + + switch (transition) { + case PATH_TRANSITION_IN_TO_IN: + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_IN_TO_OUT: + xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y); + clip_append_vertex(ctx, xi, clip_y); + break; + case PATH_TRANSITION_OUT_TO_IN: + xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y); + clip_append_vertex(ctx, xi, clip_y); + clip_append_vertex(ctx, x, y); + break; + case PATH_TRANSITION_OUT_TO_OUT: + /* nothing */ + break; + default: + assert(0 && "bad enum path_transition"); + } + + ctx->prev.x = x; + ctx->prev.y = y; +} + +static void +clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + ctx->prev.x = src->x[src->n - 1]; + ctx->prev.y = src->y[src->n - 1]; + ctx->vertices.x = dst_x; + ctx->vertices.y = dst_y; +} + +static int +clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_left_edge(ctx, src->x[i], src->y[i]); + clip_polygon_leftright(ctx, trans, src->x[i], src->y[i], + ctx->clip.x1); + } + return ctx->vertices.x - dst_x; +} + +static int +clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_right_edge(ctx, src->x[i], src->y[i]); + clip_polygon_leftright(ctx, trans, src->x[i], src->y[i], + ctx->clip.x2); + } + return ctx->vertices.x - dst_x; +} + +static int +clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_top_edge(ctx, src->x[i], src->y[i]); + clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i], + ctx->clip.y1); + } + return ctx->vertices.x - dst_x; +} + +static int +clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src, + GLfloat *dst_x, GLfloat *dst_y) +{ + enum path_transition trans; + int i; + + clip_context_prepare(ctx, src, dst_x, dst_y); + for (i = 0; i < src->n; i++) { + trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]); + clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i], + ctx->clip.y2); + } + return ctx->vertices.x - dst_x; +} + #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) > (b)) ? (b) : (a)) #define clip(x, a, b) min(max(x, a), b) -#define sign(x) ((x) >= 0) +/* + * Compute the boundary vertices of the intersection of the global coordinate + * aligned rectangle 'rect', and an arbitrary quadrilateral produced from + * 'surf_rect' when transformed from surface coordinates into global coordinates. + * The vertices are written to 'ex' and 'ey', and the return value is the + * number of vertices. Vertices are produced in clockwise winding order. + * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero + * polygon area. + */ static int calculate_edges(struct weston_surface *es, pixman_box32_t *rect, pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey) { - int i, n = 0; + struct polygon8 polygon; + struct clip_context ctx; + int i, n; GLfloat min_x, max_x, min_y, max_y; - GLfloat x[4] = { - surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1, + struct polygon8 surf = { + { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 }, + { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 }, + 4 }; - GLfloat y[4] = { - surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2, - }; - GLfloat cx1 = rect->x1; - GLfloat cx2 = rect->x2; - GLfloat cy1 = rect->y1; - GLfloat cy2 = rect->y2; - - GLfloat dist_squared(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) - { - GLfloat dx = (x1 - x2); - GLfloat dy = (y1 - y2); - return dx * dx + dy * dy; - } - void append_vertex(GLfloat x, GLfloat y) - { - /* don't emit duplicate vertices: */ - if ((n > 0) && (ex[n-1] == x) && (ey[n-1] == y)) - return; - ex[n] = x; - ey[n] = y; - n++; - } + ctx.clip.x1 = rect->x1; + ctx.clip.y1 = rect->y1; + ctx.clip.x2 = rect->x2; + ctx.clip.y2 = rect->y2; /* transform surface to screen space: */ - for (i = 0; i < 4; i++) - weston_surface_to_global_float(es, x[i], y[i], &x[i], &y[i]); + for (i = 0; i < surf.n; i++) + weston_surface_to_global_float(es, surf.x[i], surf.y[i], + &surf.x[i], &surf.y[i]); /* find bounding box: */ - min_x = max_x = x[0]; - min_y = max_y = y[0]; - - for (i = 1; i < 4; i++) { - min_x = min(min_x, x[i]); - max_x = max(max_x, x[i]); - min_y = min(min_y, y[i]); - max_y = max(max_y, y[i]); + min_x = max_x = surf.x[0]; + min_y = max_y = surf.y[0]; + + for (i = 1; i < surf.n; i++) { + min_x = min(min_x, surf.x[i]); + max_x = max(max_x, surf.x[i]); + min_y = min(min_y, surf.y[i]); + max_y = max(max_y, surf.y[i]); } /* First, simple bounding box check to discard early transformed * surface rects that do not intersect with the clip region: */ - if ((min_x > cx2) || (max_x < cx1) || - (min_y > cy2) || (max_y < cy1)) + if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) || + (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1)) return 0; /* Simple case, bounding box edges are parallel to surface edges, @@ -130,224 +387,42 @@ calculate_edges(struct weston_surface *es, pixman_box32_t *rect, * vertices to the clip rect bounds: */ if (!es->transform.enabled) { - for (i = 0; i < 4; i++) { - ex[n] = clip(x[i], cx1, cx2); - ey[n] = clip(y[i], cy1, cy2); - n++; + for (i = 0; i < surf.n; i++) { + ex[i] = clip(surf.x[i], ctx.clip.x1, ctx.clip.x2); + ey[i] = clip(surf.y[i], ctx.clip.y1, ctx.clip.y2); } - return 4; + return surf.n; } - /* Hard case, transformation applied. We need to find the vertices - * of the shape that is the intersection of the clip rect and - * transformed surface. This can be anything from 3 to 8 sides. - * - * Observation: all the resulting vertices will be the intersection - * points of the transformed surface and the clip rect, plus the - * vertices of the clip rect which are enclosed by the transformed - * surface and the vertices of the transformed surface which are - * enclosed by the clip rect. - * - * Observation: there will be zero, one, or two resulting vertices - * for each edge of the src rect. - * - * Loop over four edges of the transformed rect: + /* Transformed case: use a general polygon clipping algorithm to + * clip the surface rectangle with each side of 'rect'. + * The algorithm is Sutherland-Hodgman, as explained in + * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm + * but without looking at any of that code. */ - for (i = 0; i < 4; i++) { - GLfloat x1, y1, x2, y2; - int last_n = n; - - x1 = x[i]; - y1 = y[i]; - - /* if this vertex is contained in the clip rect, use it as-is: */ - if ((cx1 <= x1) && (x1 <= cx2) && - (cy1 <= y1) && (y1 <= cy2)) - append_vertex(x1, y1); - - /* for remaining, we consider the point as part of a line: */ - x2 = x[(i+1) % 4]; - y2 = y[(i+1) % 4]; - - if (x1 == x2) { - append_vertex(clip(x1, cx1, cx2), clip(y1, cy1, cy2)); - append_vertex(clip(x2, cx1, cx2), clip(y2, cy1, cy2)); - } else if (y1 == y2) { - append_vertex(clip(x1, cx1, cx2), clip(y1, cy1, cy2)); - append_vertex(clip(x2, cx1, cx2), clip(y2, cy1, cy2)); - } else { - GLfloat m, c, p; - GLfloat tx[2], ty[2]; - int tn = 0; - - int intersect_horiz(GLfloat y, GLfloat *p) - { - GLfloat x; - - /* if y does not lie between y1 and y2, no - * intersection possible - */ - if (sign(y-y1) == sign(y-y2)) - return 0; - - x = (y - c) / m; - - /* if x does not lie between cx1 and cx2, no - * intersection: - */ - if (sign(x-cx1) == sign(x-cx2)) - return 0; - - *p = x; - return 1; - } - - int intersect_vert(GLfloat x, GLfloat *p) - { - GLfloat y; - - if (sign(x-x1) == sign(x-x2)) - return 0; - - y = m * x + c; - - if (sign(y-cy1) == sign(y-cy2)) - return 0; - - *p = y; - return 1; - } - - /* y = mx + c */ - m = (y2 - y1) / (x2 - x1); - c = y1 - m * x1; - - /* check for up to two intersections with the four edges - * of the clip rect. Note that we don't know the orientation - * of the transformed surface wrt. the clip rect. So if when - * there are two intersection points, we need to put the one - * closest to x1,y1 first: - */ - - /* check top clip rect edge: */ - if (intersect_horiz(cy1, &p)) { - ty[tn] = cy1; - tx[tn] = p; - tn++; - } - - /* check right clip rect edge: */ - if (intersect_vert(cx2, &p)) { - ty[tn] = p; - tx[tn] = cx2; - tn++; - if (tn == 2) - goto edge_check_done; - } - - /* check bottom clip rect edge: */ - if (intersect_horiz(cy2, &p)) { - ty[tn] = cy2; - tx[tn] = p; - tn++; - if (tn == 2) - goto edge_check_done; - } - - /* check left clip rect edge: */ - if (intersect_vert(cx1, &p)) { - ty[tn] = p; - tx[tn] = cx1; - tn++; - } - -edge_check_done: - if (tn == 1) { - append_vertex(tx[0], ty[0]); - } else if (tn == 2) { - if (dist_squared(x1, y1, tx[0], ty[0]) < - dist_squared(x1, y1, tx[1], ty[1])) { - append_vertex(tx[0], ty[0]); - append_vertex(tx[1], ty[1]); - } else { - append_vertex(tx[1], ty[1]); - append_vertex(tx[0], ty[0]); - } - } - - if (n == last_n) { - GLfloat best_x=0, best_y=0; - uint32_t d, best_d = (unsigned int)-1; /* distance squared */ - uint32_t max_d = dist_squared(x2, y2, - x[(i+2) % 4], y[(i+2) % 4]); - - /* if there are no vertices on this line, it could be that - * there is a vertex of the clip rect that is enclosed by - * the transformed surface. Find the vertex of the clip - * rect that is reached by the shortest line perpendicular - * to the current edge, if any. - * - * slope of perpendicular is 1/m, so - * - * cy = -cx/m + c2 - * c2 = cy + cx/m - * - */ - - int perp_intersect(GLfloat cx, GLfloat cy, uint32_t *d) - { - GLfloat c2 = cy + cx/m; - GLfloat x = (c2 - c) / (m + 1/m); - - /* if the x position of the intersection of the - * perpendicular with the transformed edge does - * not lie within the bounds of the edge, then - * no intersection: - */ - if (sign(x-x1) == sign(x-x2)) - return 0; - - *d = dist_squared(cx, cy, x, (m * x) + c); - - /* if intersection distance is further away than - * opposite edge of surface region, it is invalid: - */ - if (*d > max_d) - return 0; - - return 1; - } - - if (perp_intersect(cx1, cy1, &d)) { - best_x = cx1; - best_y = cy1; - best_d = d; - } - - if (perp_intersect(cx1, cy2, &d) && (d < best_d)) { - best_x = cx1; - best_y = cy2; - best_d = d; - } - - if (perp_intersect(cx2, cy2, &d) && (d < best_d)) { - best_x = cx2; - best_y = cy2; - best_d = d; - } - - if (perp_intersect(cx2, cy1, &d) && (d < best_d)) { - best_x = cx2; - best_y = cy1; - best_d = d; - } - - if (best_d != (unsigned int)-1) // XXX can this happen? - append_vertex(best_x, best_y); - } - } - + polygon.n = clip_polygon_left(&ctx, &surf, polygon.x, polygon.y); + surf.n = clip_polygon_right(&ctx, &polygon, surf.x, surf.y); + polygon.n = clip_polygon_top(&ctx, &surf, polygon.x, polygon.y); + surf.n = clip_polygon_bottom(&ctx, &polygon, surf.x, surf.y); + + /* Get rid of duplicate vertices */ + ex[0] = surf.x[0]; + ey[0] = surf.y[0]; + n = 1; + for (i = 1; i < surf.n; i++) { + if (float_difference(ex[n - 1], surf.x[i]) == 0.0f && + float_difference(ey[n - 1], surf.y[i]) == 0.0f) + continue; + ex[n] = surf.x[i]; + ey[n] = surf.y[i]; + n++; } + if (float_difference(ex[n - 1], surf.x[0]) == 0.0f && + float_difference(ey[n - 1], surf.y[0]) == 0.0f) + n--; + + if (n < 3) + return 0; return n; } @@ -537,6 +612,7 @@ draw_surface(struct weston_surface *es, struct weston_output *output, pixman_region32_t repaint; /* non-opaque region in surface coordinates: */ pixman_region32_t surface_blend; + pixman_region32_t *buffer_damage; GLint filter; int i; @@ -548,8 +624,8 @@ draw_surface(struct weston_surface *es, struct weston_output *output, if (!pixman_region32_not_empty(&repaint)) goto out; - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, &repaint); + buffer_damage = &output->buffer_damage[output->current_buffer]; + pixman_region32_subtract(buffer_damage, buffer_damage, &repaint); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -627,7 +703,7 @@ gles2_renderer_repaint_output(struct weston_output *output, struct weston_compositor *compositor = output->compositor; EGLBoolean ret; static int errored; - int32_t width, height; + int32_t width, height, i; width = output->current->width + output->border.left + output->border.right; @@ -661,6 +737,14 @@ gles2_renderer_repaint_output(struct weston_output *output, pixman_region32_fini(&undamaged); } + for (i = 0; i < 2; i++) + pixman_region32_union(&output->buffer_damage[i], + &output->buffer_damage[i], + output_damage); + + pixman_region32_union(output_damage, output_damage, + &output->buffer_damage[output->current_buffer]); + repaint_surfaces(output, output_damage); wl_signal_emit(&output->frame_signal, output); @@ -672,6 +756,8 @@ gles2_renderer_repaint_output(struct weston_output *output, print_egl_error_state(); } + output->current_buffer ^= 1; + } static void diff --git a/src/screenshooter.c b/src/screenshooter.c index ba80ce5..ffcc970 100644 --- a/src/screenshooter.c +++ b/src/screenshooter.c @@ -261,7 +261,7 @@ weston_recorder_frame_notify(struct wl_listener *listener, void *data) struct weston_output *output = data; uint32_t msecs = output->frame_time; pixman_box32_t *r; - pixman_region32_t damage; + pixman_region32_t damage, *previous_damage; int i, j, k, n, width, height, run, stride; uint32_t delta, prev, *d, *s, *p, next; struct { @@ -270,9 +270,18 @@ weston_recorder_frame_notify(struct wl_listener *listener, void *data) } header; struct iovec v[2]; + /* When recording, this will be exactly the region that was repainted + * in this frame. Since overlays are disabled, the whole primary plane + * damage is rendered. For the first frame, the whole output will be + * damaged and that damage will be added to both buffers causing the + * non-current buffer damage to be while output. Rendering will clear + * all the damage in the current buffer so in the next frame (when + * that is non-current) the only damage left will be the one added + * from the primary plane. */ + previous_damage = &output->buffer_damage[output->current_buffer ^ 1]; + pixman_region32_init(&damage); - pixman_region32_intersect(&damage, &output->region, - &output->previous_damage); + pixman_region32_intersect(&damage, &output->region, previous_damage); r = pixman_region32_rectangles(&damage, &n); if (n == 0) diff --git a/src/shell.c b/src/shell.c index 86ba202..f6c26eb 100644 --- a/src/shell.c +++ b/src/shell.c @@ -2190,7 +2190,7 @@ lock_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy) if (!weston_surface_is_mapped(surface)) { wl_list_insert(&shell->lock_layer.surface_list, &surface->layer_link); - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); weston_compositor_wake(shell->compositor); } } @@ -2764,7 +2764,7 @@ show_input_panels(struct wl_listener *listener, void *data) ws = surface->surface; wl_list_insert(&shell->input_panel_layer.surface_list, &ws->layer_link); - weston_surface_assign_output(ws); + weston_surface_update_transform(ws); weston_surface_damage(ws); weston_slide_run(ws, ws->geometry.height, 0, NULL, NULL); } @@ -2919,7 +2919,7 @@ map(struct desktop_shell *shell, struct weston_surface *surface, } if (surface_type != SHELL_SURFACE_NONE) { - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); if (surface_type == SHELL_SURFACE_MAXIMIZED) surface->output = shsurf->output; } @@ -2992,7 +2992,7 @@ configure(struct desktop_shell *shell, struct weston_surface *surface, /* XXX: would a fullscreen surface need the same handling? */ if (surface->output) { - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); if (surface_type == SHELL_SURFACE_MAXIMIZED) surface->output = shsurf->output; @@ -3130,7 +3130,7 @@ screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy) if (wl_list_empty(&surface->layer_link)) { wl_list_insert(shell->lock_layer.surface_list.prev, &surface->layer_link); - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); shell->compositor->idle_time = shell->screensaver.duration; weston_compositor_wake(shell->compositor); shell->compositor->state = WESTON_COMPOSITOR_IDLE; @@ -3469,7 +3469,6 @@ debug_repaint_binding(struct wl_seat *seat, uint32_t time, uint32_t key, weston_surface_configure(surface, 0, 0, 8192, 8192); wl_list_insert(&compositor->fade_layer.surface_list, &surface->layer_link); - weston_surface_assign_output(surface); pixman_region32_init(&surface->input); /* Here's the dirty little trick that makes the @@ -3694,11 +3693,8 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) } } -int -shell_init(struct weston_compositor *ec); - WL_EXPORT int -shell_init(struct weston_compositor *ec) +module_init(struct weston_compositor *ec) { struct weston_seat *seat; struct desktop_shell *shell; diff --git a/src/tablet-shell.c b/src/tablet-shell.c index f17d888..3623736 100644 --- a/src/tablet-shell.c +++ b/src/tablet-shell.c @@ -162,7 +162,7 @@ tablet_shell_surface_configure(struct weston_surface *surface, &surface->layer_link); } - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); } static void @@ -530,18 +530,15 @@ tablet_shell_destroy(struct wl_listener *listener, void *data) free(shell); } -void -shell_init(struct weston_compositor *compositor); - -WL_EXPORT void -shell_init(struct weston_compositor *compositor) +WL_EXPORT int +module_init(struct weston_compositor *compositor) { struct tablet_shell *shell; struct wl_event_loop *loop; shell = malloc(sizeof *shell); if (shell == NULL) - return; + return -1; memset(shell, 0, sizeof *shell); shell->compositor = compositor; @@ -583,4 +580,6 @@ shell_init(struct weston_compositor *compositor) launch_ux_daemon(shell); tablet_shell_set_state(shell, STATE_STARTING); + + return 0; } diff --git a/src/text-backend.c b/src/text-backend.c index ddeec20..4190fe7 100644 --- a/src/text-backend.c +++ b/src/text-backend.c @@ -1,5 +1,6 @@ /* * Copyright © 2012 Openismus GmbH + * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -24,8 +25,10 @@ #include "compositor.h" #include "text-server-protocol.h" +#include "input-method-server-protocol.h" struct input_method; +struct input_method_context; struct text_model { struct wl_resource resource; @@ -37,13 +40,19 @@ struct text_model { struct wl_surface *surface; }; +struct text_model_factory { + struct wl_global *text_model_factory_global; + struct wl_listener destroy_listener; + + struct weston_compositor *ec; +}; + struct input_method { struct wl_resource *input_method_binding; struct wl_global *input_method_global; - struct wl_global *text_model_factory_global; struct wl_listener destroy_listener; - struct weston_compositor *ec; + struct weston_seat *seat; struct text_model *model; struct wl_list link; @@ -51,8 +60,21 @@ struct input_method { struct wl_listener keyboard_focus_listener; int focus_listener_initialized; + + struct input_method_context *context; }; +struct input_method_context { + struct wl_resource resource; + + struct text_model *model; + + struct wl_list link; +}; + +static void input_method_context_create(struct text_model *model, + struct input_method *input_method); + static void input_method_init_seat(struct weston_seat *seat); static void @@ -62,8 +84,11 @@ deactivate_text_model(struct text_model *text_model, struct weston_compositor *ec = text_model->ec; if (input_method->model == text_model) { + if (input_method->input_method_binding) + input_method_send_deactivate(input_method->input_method_binding, &input_method->context->resource); wl_list_remove(&input_method->link); input_method->model = NULL; + input_method->context = NULL; wl_signal_emit(&ec->hide_input_panel_signal, ec); text_model_send_deactivated(&text_model->resource); } @@ -85,15 +110,21 @@ destroy_text_model(struct wl_resource *resource) static void text_model_set_surrounding_text(struct wl_client *client, struct wl_resource *resource, - const char *text) + const char *text, + uint32_t cursor, + uint32_t anchor) { -} + struct text_model *text_model = resource->data; + struct input_method *input_method, *next; -static void -text_model_set_cursor_index(struct wl_client *client, - struct wl_resource *resource, - uint32_t index) -{ + wl_list_for_each_safe(input_method, next, &text_model->input_methods, link) { + if (!input_method->context) + continue; + input_method_context_send_surrounding_text(&input_method->context->resource, + text, + cursor, + anchor); + } } static void @@ -104,6 +135,7 @@ text_model_activate(struct wl_client *client, { struct text_model *text_model = resource->data; struct weston_seat *weston_seat = seat->data; + struct input_method *input_method = weston_seat->input_method; struct text_model *old = weston_seat->input_method->model; struct weston_compositor *ec = text_model->ec; @@ -115,12 +147,14 @@ text_model_activate(struct wl_client *client, weston_seat->input_method); } - weston_seat->input_method->model = text_model; - wl_list_insert(&text_model->input_methods, &weston_seat->input_method->link); + input_method->model = text_model; + wl_list_insert(&text_model->input_methods, &input_method->link); input_method_init_seat(weston_seat); text_model->surface = surface->data; + input_method_context_create(text_model, input_method); + wl_signal_emit(&ec->show_input_panel_signal, ec); text_model_send_activated(&text_model->resource); @@ -139,11 +173,17 @@ text_model_deactivate(struct wl_client *client, } static void -text_model_set_selected_text(struct wl_client *client, - struct wl_resource *resource, - const char *text, - int32_t index) +text_model_reset(struct wl_client *client, + struct wl_resource *resource) { + struct text_model *text_model = resource->data; + struct input_method *input_method, *next; + + wl_list_for_each_safe(input_method, next, &text_model->input_methods, link) { + if (!input_method->context) + continue; + input_method_context_send_reset(&input_method->context->resource); + } } static void @@ -170,10 +210,9 @@ text_model_set_content_type(struct wl_client *client, static const struct text_model_interface text_model_implementation = { text_model_set_surrounding_text, - text_model_set_cursor_index, text_model_activate, text_model_deactivate, - text_model_set_selected_text, + text_model_reset, text_model_set_micro_focus, text_model_set_preedit, text_model_set_content_type @@ -181,27 +220,26 @@ static const struct text_model_interface text_model_implementation = { static void text_model_factory_create_text_model(struct wl_client *client, struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface) + uint32_t id) { - struct input_method *input_method = resource->data; + struct text_model_factory *text_model_factory = resource->data; struct text_model *text_model; text_model = calloc(1, sizeof *text_model); - text_model->resource.destroy = destroy_text_model; - text_model->resource.object.id = id; text_model->resource.object.interface = &text_model_interface; text_model->resource.object.implementation = (void (**)(void)) &text_model_implementation; - text_model->resource.data = text_model; - text_model->ec = input_method->ec; + text_model->resource.data = text_model; + text_model->resource.destroy = destroy_text_model; - wl_client_add_resource(client, &text_model->resource); + text_model->ec = text_model_factory->ec; wl_list_init(&text_model->input_methods); + + wl_client_add_resource(client, &text_model->resource); }; static const struct text_model_factory_interface text_model_factory_implementation = { @@ -214,38 +252,149 @@ bind_text_model_factory(struct wl_client *client, uint32_t version, uint32_t id) { - struct input_method *input_method = data; + struct text_model_factory *text_model_factory = data; /* No checking for duplicate binding necessary. * No events have to be sent, so we don't need the return value. */ wl_client_add_object(client, &text_model_factory_interface, &text_model_factory_implementation, - id, input_method); + id, text_model_factory); } static void -input_method_commit_string(struct wl_client *client, - struct wl_resource *resource, - const char *text, - uint32_t index) +text_model_factory_notifier_destroy(struct wl_listener *listener, void *data) { - struct input_method *input_method = resource->data; + struct text_model_factory *text_model_factory = + container_of(listener, struct text_model_factory, destroy_listener); - if (input_method->model) { - text_model_send_commit_string(&input_method->model->resource, text, index); - } + wl_display_remove_global(text_model_factory->ec->wl_display, + text_model_factory->text_model_factory_global); + + free(text_model_factory); } -static const struct input_method_interface input_method_implementation = { - input_method_commit_string +WL_EXPORT void +text_model_factory_create(struct weston_compositor *ec) +{ + struct text_model_factory *text_model_factory; + + text_model_factory = calloc(1, sizeof *text_model_factory); + + text_model_factory->ec = ec; + + text_model_factory->text_model_factory_global = + wl_display_add_global(ec->wl_display, + &text_model_factory_interface, + text_model_factory, bind_text_model_factory); + + text_model_factory->destroy_listener.notify = text_model_factory_notifier_destroy; + wl_signal_add(&ec->destroy_signal, &text_model_factory->destroy_listener); +} + +static void +input_method_context_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +input_method_context_commit_string(struct wl_client *client, + struct wl_resource *resource, + const char *text, + uint32_t index) +{ + struct input_method_context *context = resource->data; + + text_model_send_commit_string(&context->model->resource, text, index); +} + +static void +input_method_context_preedit_string(struct wl_client *client, + struct wl_resource *resource, + const char *text, + uint32_t index) +{ + struct input_method_context *context = resource->data; + + text_model_send_preedit_string(&context->model->resource, text, index); +} + +static void +input_method_context_delete_surrounding_text(struct wl_client *client, + struct wl_resource *resource, + int32_t index, + uint32_t length) +{ + struct input_method_context *context = resource->data; + + text_model_send_delete_surrounding_text(&context->model->resource, index, length); +} + +static void +input_method_context_key(struct wl_client *client, + struct wl_resource *resource, + uint32_t key, + uint32_t state) +{ + struct input_method_context *context = resource->data; + + text_model_send_key(&context->model->resource, key, state); +} + +static const struct input_method_context_interface input_method_context_implementation = { + input_method_context_destroy, + input_method_context_commit_string, + input_method_context_preedit_string, + input_method_context_delete_surrounding_text, + input_method_context_key }; static void +destroy_input_method_context(struct wl_resource *resource) +{ + struct input_method_context *context = resource->data; + + free(context); +} + +static void +input_method_context_create(struct text_model *model, + struct input_method *input_method) +{ + struct input_method_context *context; + + if (!input_method->input_method_binding) + return; + + context = malloc(sizeof *context); + if (context == NULL) + return; + + context->resource.destroy = destroy_input_method_context; + context->resource.object.id = 0; + context->resource.object.interface = &input_method_context_interface; + context->resource.object.implementation = + (void (**)(void)) &input_method_context_implementation; + context->resource.data = context; + wl_signal_init(&context->resource.destroy_signal); + + context->model = model; + input_method->context = context; + + wl_client_add_resource(input_method->input_method_binding->client, &context->resource); + + input_method_send_activate(input_method->input_method_binding, &context->resource); +} + +static void unbind_input_method(struct wl_resource *resource) { struct input_method *input_method = resource->data; input_method->input_method_binding = NULL; + input_method->context = NULL; + free(resource); } @@ -259,7 +408,7 @@ bind_input_method(struct wl_client *client, struct wl_resource *resource; resource = wl_client_add_object(client, &input_method_interface, - &input_method_implementation, + NULL, id, input_method); if (input_method->input_method_binding == NULL) { @@ -279,10 +428,12 @@ input_method_notifier_destroy(struct wl_listener *listener, void *data) struct input_method *input_method = container_of(listener, struct input_method, destroy_listener); - wl_display_remove_global(input_method->ec->wl_display, + if (input_method->model) + deactivate_text_model(input_method->model, input_method); + + wl_display_remove_global(input_method->seat->compositor->wl_display, input_method->input_method_global); - wl_display_remove_global(input_method->ec->wl_display, - input_method->text_model_factory_global); + free(input_method); } @@ -316,7 +467,7 @@ input_method_init_seat(struct weston_seat *seat) seat->input_method->focus_listener_initialized = 1; } -void +WL_EXPORT void input_method_create(struct weston_compositor *ec, struct weston_seat *seat) { @@ -324,22 +475,18 @@ input_method_create(struct weston_compositor *ec, input_method = calloc(1, sizeof *input_method); - input_method->ec = ec; + input_method->seat = seat; input_method->model = NULL; input_method->focus_listener_initialized = 0; + input_method->context = NULL; input_method->input_method_global = wl_display_add_global(ec->wl_display, &input_method_interface, input_method, bind_input_method); - input_method->text_model_factory_global = - wl_display_add_global(ec->wl_display, - &text_model_factory_interface, - input_method, bind_text_model_factory); - input_method->destroy_listener.notify = input_method_notifier_destroy; - wl_signal_add(&ec->destroy_signal, &input_method->destroy_listener); + wl_signal_add(&seat->seat.destroy_signal, &input_method->destroy_listener); seat->input_method = input_method; } diff --git a/src/xwayland/launcher.c b/src/xwayland/launcher.c index aab74f0..00f064e 100644 --- a/src/xwayland/launcher.c +++ b/src/xwayland/launcher.c @@ -312,7 +312,7 @@ weston_xserver_destroy(struct wl_listener *l, void *data) } WL_EXPORT int -weston_xserver_init(struct weston_compositor *compositor) +module_init(struct weston_compositor *compositor) { struct wl_display *display = compositor->wl_display; struct weston_xserver *mxs; diff --git a/tests/event-test.c b/tests/event-test.c index e137213..2cbfc2d 100644 --- a/tests/event-test.c +++ b/tests/event-test.c @@ -49,7 +49,7 @@ handle_surface(struct test_client *client) surface = (struct weston_surface *) resource; weston_surface_configure(surface, 100, 100, 200, 200); - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); wl_list_insert(&layer->surface_list, &surface->layer_link); weston_surface_damage(surface); diff --git a/tests/test-text-client.c b/tests/test-text-client.c index 828f3a0..897909a 100644 --- a/tests/test-text-client.c +++ b/tests/test-text-client.c @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Openismus GmbH + * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -60,6 +60,14 @@ text_model_preedit_string(void *data, } static void +text_model_delete_surrounding_text(void *data, + struct text_model *text_model, + int32_t index, + uint32_t length) +{ +} + +static void text_model_preedit_styling(void *data, struct text_model *text_model) { @@ -67,10 +75,12 @@ text_model_preedit_styling(void *data, static void text_model_key(void *data, - struct text_model *text_model) + struct text_model *text_model, + uint32_t key, + uint32_t state) { } - + static void text_model_selection_replacement(void *data, struct text_model *text_model) @@ -112,6 +122,7 @@ text_model_deactivated(void *data, static const struct text_model_listener text_model_listener = { text_model_commit_string, text_model_preedit_string, + text_model_delete_surrounding_text, text_model_preedit_styling, text_model_key, text_model_selection_replacement, @@ -160,7 +171,7 @@ create_text_model(int fd, struct display *display) char buf[64]; int len; - display->text_model = text_model_factory_create_text_model(display->factory, display->surface); + display->text_model = text_model_factory_create_text_model(display->factory); text_model_add_listener(display->text_model, &text_model_listener, display); wl_display_flush(display->display); diff --git a/tests/text-test.c b/tests/text-test.c index bd2a5c5..942cb3b 100644 --- a/tests/text-test.c +++ b/tests/text-test.c @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Openismus GmbH + * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -178,7 +178,7 @@ handle_surface(struct test_client *client) surface = (struct weston_surface *) resource; weston_surface_configure(surface, 100, 100, 200, 200); - weston_surface_assign_output(surface); + weston_surface_update_transform(surface); weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); data->layer = malloc(sizeof *data->layer); diff --git a/tests/weston-test b/tests/weston-test index 2314514..27b40de 100644 --- a/tests/weston-test +++ b/tests/weston-test @@ -1,4 +1,4 @@ #!/bin/sh -../src/weston --module=$abs_builddir/.libs/${1/.la/.so} +../src/weston --modules=$abs_builddir/.libs/${1/.la/.so} |
