diff --git a/clients/.gitignore b/clients/.gitignore index 16088e85..f925ce6e 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -22,6 +22,7 @@ simple-touch smoke subsurface-client-protocol.h subsurface-protocol.c +subsurfaces tablet-shell-client-protocol.h tablet-shell-protocol.c text-client-protocol.h diff --git a/clients/Makefile.am b/clients/Makefile.am index 5f83acd4..d360174f 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -64,6 +64,7 @@ clients_programs = \ clickdot \ transformed \ calibrator \ + $(subsurfaces) \ $(full_gl_client_programs) desktop_shell = weston-desktop-shell @@ -131,6 +132,13 @@ transformed_LDADD = libtoytoolkit.la calibrator_SOURCES = calibrator.c ../shared/matrix.c ../shared/matrix.h calibrator_LDADD = libtoytoolkit.la +if BUILD_SUBSURFACES_CLIENT +subsurfaces = subsurfaces +subsurfaces_SOURCES = subsurfaces.c +subsurfaces_CPPFLAGS = $(AM_CPPFLAGS) $(SIMPLE_EGL_CLIENT_CFLAGS) +subsurfaces_LDADD = libtoytoolkit.la $(SIMPLE_EGL_CLIENT_LIBS) -lm +endif + if HAVE_PANGO pango_programs = editor editor_SOURCES = \ diff --git a/clients/subsurfaces.c b/clients/subsurfaces.c new file mode 100644 index 00000000..7fa8abbc --- /dev/null +++ b/clients/subsurfaces.c @@ -0,0 +1,792 @@ +/* + * Copyright © 2010 Intel Corporation + * Copyright © 2011 Benjamin Franzke + * Copyright © 2012-2013 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "window.h" + +#if 0 +#define DBG(fmt, ...) \ + fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__) +#else +#define DBG(...) do {} while (0) +#endif + +static int32_t option_red_mode; +static int32_t option_triangle_mode; +static int32_t option_no_triangle; +static int32_t option_help; + +static const struct weston_option options[] = { + { WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode }, + { WESTON_OPTION_INTEGER, "triangle-mode", 't', &option_triangle_mode }, + { WESTON_OPTION_BOOLEAN, "no-triangle", 'n', &option_no_triangle }, + { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help }, +}; + +static enum subsurface_mode +int_to_mode(int32_t i) +{ + switch (i) { + case 0: + return SUBSURFACE_DESYNCHRONIZED; + case 1: + return SUBSURFACE_SYNCHRONIZED; + default: + fprintf(stderr, "error: %d is not a valid commit mode.\n", i); + exit(1); + } +} + +static const char help_text[] = +"Usage: %s [options]\n" +"\n" +" -r, --red-mode=MODE\t\tthe commit mode for the red sub-surface (0)\n" +" -t, --triangle-mode=MODE\tthe commit mode for the GL sub-surface (0)\n" +" -n, --no-triangle\t\tDo not create the GL sub-surface.\n" +"\n" +"The MODE is the wl_subsurface commit mode used by default for the\n" +"given sub-surface. Valid values are the integers:\n" +" 0\tfor desynchronized, i.e. free-running\n" +" 1\tfor synchronized\n" +"\n" +"This program demonstrates sub-surfaces with the toytoolkit.\n" +"The main surface contains the decorations, a green canvas, and a\n" +"green spinner. One sub-surface is red with a red spinner. These\n" +"are rendered with Cairo. The other sub-surface contains a spinning\n" +"triangle rendered in EGL/GLESv2, without Cairo, i.e. it is a raw GL\n" +"widget.\n" +"\n" +"The GL widget animates on its own. The spinners follow wall clock\n" +"time and update only when their surface is repainted, so you see\n" +"which surfaces get redrawn. The red sub-surface animates on its own,\n" +"but can be toggled with the spacebar.\n" +"\n" +"Even though the sub-surfaces attempt to animate on their own, they\n" +"are subject to the commit mode. If commit mode is synchronized,\n" +"they will need a commit on the main surface to actually display.\n" +"You can trigger a main surface repaint, without a resize, by\n" +"hovering the pointer over the title bar buttons.\n" +"\n" +"Resizing will temporarily toggle the commit mode of all sub-surfaces\n" +"to guarantee synchronized rendering on size changes. It also forces\n" +"a repaint of all surfaces.\n" +"\n" +"Using -t1 -r1 is especially useful for trying to catch inconsistent\n" +"rendering and deadlocks, since free-running sub-surfaces would\n" +"immediately hide the problem.\n" +"\n" +"Key controls:\n" +" space - toggle red sub-surface animation loop\n" +" up - step window size shorter\n" +" down - step window size taller\n" +"\n"; + +struct egl_state { + EGLDisplay dpy; + EGLContext ctx; + EGLConfig conf; +}; + +struct triangle_gl_state { + GLuint rotation_uniform; + GLuint pos; + GLuint col; +}; + +struct triangle { + struct egl_state *egl; + + struct wl_surface *wl_surface; + struct wl_egl_window *egl_window; + EGLSurface egl_surface; + int width; + int height; + + struct triangle_gl_state gl; + + struct widget *widget; + uint32_t time; + struct wl_callback *frame_cb; +}; + +/******** Pure EGL/GLESv2/libwayland-client component: ***************/ + +static const char *vert_shader_text = + "uniform mat4 rotation;\n" + "attribute vec4 pos;\n" + "attribute vec4 color;\n" + "varying vec4 v_color;\n" + "void main() {\n" + " gl_Position = rotation * pos;\n" + " v_color = color;\n" + "}\n"; + +static const char *frag_shader_text = + "precision mediump float;\n" + "varying vec4 v_color;\n" + "void main() {\n" + " gl_FragColor = v_color;\n" + "}\n"; + +static void +egl_print_config_info(struct egl_state *egl) +{ + EGLint r, g, b, a; + + printf("Chosen EGL config details:\n"); + + printf("\tRGBA bits"); + if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_RED_SIZE, &r) && + eglGetConfigAttrib(egl->dpy, egl->conf, EGL_GREEN_SIZE, &g) && + eglGetConfigAttrib(egl->dpy, egl->conf, EGL_BLUE_SIZE, &b) && + eglGetConfigAttrib(egl->dpy, egl->conf, EGL_ALPHA_SIZE, &a)) + printf(": %d %d %d %d\n", r, g, b, a); + else + printf(" unknown\n"); + + printf("\tswap interval range"); + if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MIN_SWAP_INTERVAL, &a) && + eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MAX_SWAP_INTERVAL, &b)) + printf(": %d - %d\n", a, b); + else + printf(" unknown\n"); +} + +static struct egl_state * +egl_state_create(struct wl_display *display) +{ + struct egl_state *egl; + + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_ALPHA_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint major, minor, n; + EGLBoolean ret; + + egl = calloc(1, sizeof *egl); + assert(egl); + + egl->dpy = eglGetDisplay(display); + assert(egl->dpy); + + ret = eglInitialize(egl->dpy, &major, &minor); + assert(ret == EGL_TRUE); + ret = eglBindAPI(EGL_OPENGL_ES_API); + assert(ret == EGL_TRUE); + + ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n); + assert(ret && n == 1); + + egl->ctx = eglCreateContext(egl->dpy, egl->conf, + EGL_NO_CONTEXT, context_attribs); + assert(egl->ctx); + egl_print_config_info(egl); + + return egl; +} + +static void +egl_state_destroy(struct egl_state *egl) +{ + /* Required, otherwise segfault in egl_dri2.c: dri2_make_current() + * on eglReleaseThread(). */ + eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + + eglTerminate(egl->dpy); + eglReleaseThread(); + free(egl); +} + +static void +egl_make_swapbuffers_nonblock(struct egl_state *egl) +{ + EGLint a = EGL_MIN_SWAP_INTERVAL; + EGLint b = EGL_MAX_SWAP_INTERVAL; + + if (!eglGetConfigAttrib(egl->dpy, egl->conf, a, &a) || + !eglGetConfigAttrib(egl->dpy, egl->conf, b, &b)) { + fprintf(stderr, "warning: swap interval range unknown\n"); + } else + if (a > 0) { + fprintf(stderr, "warning: minimum swap interval is %d, " + "while 0 is required to not deadlock on resize.\n", a); + } + + /* + * We rely on the Wayland compositor to sync to vblank anyway. + * We just need to be able to call eglSwapBuffers() without the + * risk of waiting for a frame callback in it. + */ + if (!eglSwapInterval(egl->dpy, 0)) { + fprintf(stderr, "error: eglSwapInterval() failed.\n"); + } +} + +static GLuint +create_shader(const char *source, GLenum shader_type) +{ + GLuint shader; + GLint status; + + shader = glCreateShader(shader_type); + assert(shader != 0); + + glShaderSource(shader, 1, (const char **) &source, NULL); + glCompileShader(shader); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status) { + char log[1000]; + GLsizei len; + glGetShaderInfoLog(shader, 1000, &len, log); + fprintf(stderr, "Error: compiling %s: %*s\n", + shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment", + len, log); + exit(1); + } + + return shader; +} + +static void +triangle_init_gl(struct triangle_gl_state *trigl) +{ + GLuint frag, vert; + GLuint program; + GLint status; + + frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER); + vert = create_shader(vert_shader_text, GL_VERTEX_SHADER); + + program = glCreateProgram(); + glAttachShader(program, frag); + glAttachShader(program, vert); + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (!status) { + char log[1000]; + GLsizei len; + glGetProgramInfoLog(program, 1000, &len, log); + fprintf(stderr, "Error: linking:\n%*s\n", len, log); + exit(1); + } + + glUseProgram(program); + + trigl->pos = 0; + trigl->col = 1; + + glBindAttribLocation(program, trigl->pos, "pos"); + glBindAttribLocation(program, trigl->col, "color"); + glLinkProgram(program); + + trigl->rotation_uniform = glGetUniformLocation(program, "rotation"); +} + +static void +triangle_draw(const struct triangle_gl_state *trigl, uint32_t time) +{ + static const GLfloat verts[3][2] = { + { -0.5, -0.5 }, + { 0.5, -0.5 }, + { 0, 0.5 } + }; + static const GLfloat colors[3][3] = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; + GLfloat angle; + GLfloat rotation[4][4] = { + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 } + }; + static const int32_t speed_div = 5; + + angle = (time / speed_div) % 360 * M_PI / 180.0; + rotation[0][0] = cos(angle); + rotation[0][2] = sin(angle); + rotation[2][0] = -sin(angle); + rotation[2][2] = cos(angle); + + glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE, + (GLfloat *) rotation); + + glClearColor(0.0, 0.0, 0.0, 0.5); + glClear(GL_COLOR_BUFFER_BIT); + + glVertexAttribPointer(trigl->pos, 2, GL_FLOAT, GL_FALSE, 0, verts); + glVertexAttribPointer(trigl->col, 3, GL_FLOAT, GL_FALSE, 0, colors); + glEnableVertexAttribArray(trigl->pos); + glEnableVertexAttribArray(trigl->col); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glDisableVertexAttribArray(trigl->pos); + glDisableVertexAttribArray(trigl->col); +} + +static void +triangle_frame_callback(void *data, struct wl_callback *callback, + uint32_t time); + +static const struct wl_callback_listener triangle_frame_listener = { + triangle_frame_callback +}; + +static void +triangle_frame_callback(void *data, struct wl_callback *callback, + uint32_t time) +{ + struct triangle *tri = data; + + DBG("%stime %u\n", callback ? "" : "artificial ", time); + assert(callback == tri->frame_cb); + tri->time = time; + + if (callback) + wl_callback_destroy(callback); + + glViewport(0, 0, tri->width, tri->height); + + triangle_draw(&tri->gl, tri->time); + + tri->frame_cb = wl_surface_frame(tri->wl_surface); + wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri); + + eglSwapBuffers(tri->egl->dpy, tri->egl_surface); +} + +static void +triangle_create_egl_surface(struct triangle *tri, int width, int height) +{ + EGLBoolean ret; + + tri->wl_surface = widget_get_wl_surface(tri->widget); + tri->egl_window = wl_egl_window_create(tri->wl_surface, width, height); + tri->egl_surface = eglCreateWindowSurface(tri->egl->dpy, + tri->egl->conf, + tri->egl_window, NULL); + + ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface, + tri->egl_surface, tri->egl->ctx); + assert(ret == EGL_TRUE); + + egl_make_swapbuffers_nonblock(tri->egl); + triangle_init_gl(&tri->gl); +} + +/********* The widget code interfacing the toolkit agnostic code: **********/ + +static void +triangle_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct triangle *tri = data; + + DBG("to %dx%d\n", width, height); + tri->width = width; + tri->height = height; + + if (tri->egl_surface) { + wl_egl_window_resize(tri->egl_window, width, height, 0, 0); + } else { + triangle_create_egl_surface(tri, width, height); + triangle_frame_callback(tri, NULL, 0); + } +} + +static void +triangle_redraw_handler(struct widget *widget, void *data) +{ + struct triangle *tri = data; + int w, h; + + wl_egl_window_get_attached_size(tri->egl_window, &w, &h); + + DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height); + + /* If size is not changing, do not redraw ahead of time. + * That would risk blocking in eglSwapbuffers(). + */ + if (w == tri->width && h == tri->height) + return; + + if (tri->frame_cb) { + wl_callback_destroy(tri->frame_cb); + tri->frame_cb = NULL; + } + triangle_frame_callback(tri, NULL, tri->time); +} + +static void +set_empty_input_region(struct widget *widget, struct display *display) +{ + struct wl_compositor *compositor; + struct wl_surface *surface; + struct wl_region *region; + + compositor = display_get_compositor(display); + surface = widget_get_wl_surface(widget); + region = wl_compositor_create_region(compositor); + wl_surface_set_input_region(surface, region); + wl_region_destroy(region); +} + +static struct triangle * +triangle_create(struct window *window, struct egl_state *egl) +{ + struct triangle *tri; + + tri = calloc(1, sizeof *tri); + + tri->egl = egl; + tri->widget = window_add_subsurface(window, tri, + int_to_mode(option_triangle_mode)); + widget_set_resize_handler(tri->widget, triangle_resize_handler); + widget_set_redraw_handler(tri->widget, triangle_redraw_handler); + + set_empty_input_region(tri->widget, window_get_display(window)); + + return tri; +} + +static void +triangle_destroy(struct triangle *tri) +{ + if (tri->egl_surface) + eglDestroySurface(tri->egl->dpy, tri->egl_surface); + + if (tri->egl_window) + wl_egl_window_destroy(tri->egl_window); + + widget_destroy(tri->widget); + free(tri); +} + +/************** The toytoolkit application code: *********************/ + +struct demoapp { + struct display *display; + struct window *window; + struct widget *widget; + struct widget *subsurface; + + struct egl_state *egl; + struct triangle *triangle; + + int animate; +}; + +static void +draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time) +{ + double cx, cy, r, angle; + unsigned t; + + cx = rect->x + rect->width / 2; + cy = rect->y + rect->height / 2; + r = (rect->width < rect->height ? rect->width : rect->height) * 0.3; + t = time % 2000; + angle = t * (M_PI / 500.0); + + cairo_set_line_width(cr, 4.0); + + if (t < 1000) + cairo_arc(cr, cx, cy, r, 0.0, angle); + else + cairo_arc(cr, cx, cy, r, angle, 0.0); + + cairo_stroke(cr); +} + +static void +sub_redraw_handler(struct widget *widget, void *data) +{ + struct demoapp *app = data; + cairo_t *cr; + struct rectangle allocation; + uint32_t time; + + widget_get_allocation(app->subsurface, &allocation); + + cr = widget_cairo_create(widget); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + /* debug: paint whole surface magenta; no magenta should show */ + cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0); + cairo_paint(cr); + + cairo_rectangle(cr, + allocation.x, + allocation.y, + allocation.width, + allocation.height); + cairo_clip(cr); + + cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8); + cairo_paint(cr); + + time = widget_get_last_time(widget); + cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0); + draw_spinner(cr, &allocation, time); + + cairo_destroy(cr); + + if (app->animate) + widget_schedule_redraw(app->subsurface); + DBG("%dx%d @ %d,%d, last time %u\n", + allocation.width, allocation.height, + allocation.x, allocation.y, time); +} + +static void +sub_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + DBG("%dx%d\n", width, height); + widget_input_region_add(widget, NULL); +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct demoapp *app = data; + cairo_t *cr; + struct rectangle allocation; + uint32_t time; + + widget_get_allocation(app->widget, &allocation); + + cr = widget_cairo_create(widget); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(cr, + allocation.x, + allocation.y, + allocation.width, + allocation.height); + cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8); + cairo_fill(cr); + + time = widget_get_last_time(widget); + cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0); + draw_spinner(cr, &allocation, time); + + cairo_destroy(cr); + + DBG("%dx%d @ %d,%d, last time %u\n", + allocation.width, allocation.height, + allocation.x, allocation.y, time); +} + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct demoapp *app = data; + struct rectangle area; + int side, h; + + widget_get_allocation(widget, &area); + + side = area.width < area.height ? area.width / 2 : area.height / 2; + h = area.height - side; + + widget_set_allocation(app->subsurface, + area.x + area.width - side, + area.y, + side, h); + + if (app->triangle) { + widget_set_allocation(app->triangle->widget, + area.x + area.width - side, + area.y + h, + side, side); + } + + DBG("green %dx%d, red %dx%d, GL %dx%d\n", + area.width, area.height, side, h, side, side); +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct demoapp *app = data; + + window_schedule_redraw(app->window); +} + +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 demoapp *app = data; + struct rectangle winrect; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (sym) { + case XKB_KEY_space: + app->animate = !app->animate; + window_schedule_redraw(window); + break; + case XKB_KEY_Up: + window_get_allocation(window, &winrect); + winrect.height -= 100; + if (winrect.height < 150) + winrect.height = 150; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Down: + window_get_allocation(window, &winrect); + winrect.height += 100; + if (winrect.height > 600) + winrect.height = 600; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Escape: + display_exit(app->display); + break; + } +} + +static struct demoapp * +demoapp_create(struct display *display) +{ + struct demoapp *app; + + app = calloc(1, sizeof *app); + if (!app) + return NULL; + + app->egl = egl_state_create(display_get_display(display)); + + app->display = display; + display_set_user_data(app->display, app); + + app->window = window_create(app->display); + app->widget = frame_create(app->window, app); + window_set_title(app->window, "Wayland Sub-surface Demo"); + + window_set_key_handler(app->window, key_handler); + window_set_user_data(app->window, app); + window_set_keyboard_focus_handler(app->window, keyboard_focus_handler); + + widget_set_redraw_handler(app->widget, redraw_handler); + widget_set_resize_handler(app->widget, resize_handler); + + app->subsurface = window_add_subsurface(app->window, app, + int_to_mode(option_red_mode)); + widget_set_redraw_handler(app->subsurface, sub_redraw_handler); + widget_set_resize_handler(app->subsurface, sub_resize_handler); + + if (app->egl && !option_no_triangle) + app->triangle = triangle_create(app->window, app->egl); + + /* minimum size */ + widget_schedule_resize(app->widget, 100, 100); + + /* initial size */ + widget_schedule_resize(app->widget, 400, 300); + + app->animate = 1; + + return app; +} + +static void +demoapp_destroy(struct demoapp *app) +{ + if (app->triangle) + triangle_destroy(app->triangle); + + if (app->egl) + egl_state_destroy(app->egl); + + widget_destroy(app->subsurface); + widget_destroy(app->widget); + window_destroy(app->window); + free(app); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct demoapp *app; + + parse_options(options, ARRAY_LENGTH(options), &argc, argv); + if (option_help) { + printf(help_text, argv[0]); + return 0; + } + + display = display_create(&argc, argv); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + app = demoapp_create(display); + + display_run(display); + + demoapp_destroy(app); + display_destroy(display); + + return 0; +} diff --git a/clients/window.c b/clients/window.c index 9bcf7ffb..b12c21c9 100644 --- a/clients/window.c +++ b/clients/window.c @@ -197,6 +197,7 @@ struct surface { struct widget *widget; int redraw_needed; struct wl_callback *frame_cb; + uint32_t last_time; struct rectangle allocation; struct rectangle server_allocation; @@ -1547,6 +1548,33 @@ widget_cairo_create(struct widget *widget) return cr; } +struct wl_surface * +widget_get_wl_surface(struct widget *widget) +{ + return widget->surface->surface; +} + +uint32_t +widget_get_last_time(struct widget *widget) +{ + return widget->surface->last_time; +} + +void +widget_input_region_add(struct widget *widget, const struct rectangle *rect) +{ + struct wl_compositor *comp = widget->window->display->compositor; + struct surface *surface = widget->surface; + + if (!surface->input_region) + surface->input_region = wl_compositor_create_region(comp); + + if (rect) { + wl_region_add(surface->input_region, + rect->x, rect->y, rect->width, rect->height); + } +} + void widget_set_resize_handler(struct widget *widget, widget_resize_handler_t handler) @@ -3462,6 +3490,8 @@ frame_callback(void *data, struct wl_callback *callback, uint32_t time) wl_callback_destroy(callback); surface->frame_cb = NULL; + surface->last_time = time; + if (surface->redraw_needed || surface->window->redraw_needed) window_schedule_redraw_task(surface->window); } diff --git a/clients/window.h b/clients/window.h index 2f93bd4b..7db9c63a 100644 --- a/clients/window.h +++ b/clients/window.h @@ -395,6 +395,15 @@ widget_get_user_data(struct widget *widget); cairo_t * widget_cairo_create(struct widget *widget); +struct wl_surface * +widget_get_wl_surface(struct widget *widget); + +uint32_t +widget_get_last_time(struct widget *widget); + +void +widget_input_region_add(struct widget *widget, const struct rectangle *rect); + void widget_set_redraw_handler(struct widget *widget, widget_redraw_handler_t handler); diff --git a/configure.ac b/configure.ac index 7a98ad39..6b01d156 100644 --- a/configure.ac +++ b/configure.ac @@ -268,6 +268,9 @@ AM_CONDITIONAL(HAVE_PANGO, test "x$have_pango" = "xyes") AM_CONDITIONAL(BUILD_FULL_GL_CLIENTS, test x$cairo_modules = "xcairo-gl" -a "x$have_cairo_egl" = "xyes" -a "x$enable_egl" = "xyes") +AM_CONDITIONAL(BUILD_SUBSURFACES_CLIENT, + [test '(' "x$have_cairo_egl" != "xyes" -o "x$cairo_modules" = "xcairo-glesv2" ')' -a "x$enable_simple_egl_clients" = "xyes"]) + AM_CONDITIONAL(ENABLE_DESKTOP_SHELL, true) AC_ARG_ENABLE(tablet-shell,