| /* |
| * Copyright © 2010 Intel Corporation |
| * Copyright © 2011 Benjamin Franzke |
| * Copyright © 2012-2013 Collabora, Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include "config.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <cairo.h> |
| #include <math.h> |
| #include <assert.h> |
| |
| #include <linux/input.h> |
| #include <wayland-client.h> |
| |
| #include <wayland-egl.h> |
| #include <GLES2/gl2.h> |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| |
| #include "shared/helpers.h" |
| #include "shared/xalloc.h" |
| #include "shared/zalloc.h" |
| #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 = zalloc(sizeof *egl); |
| assert(egl); |
| |
| egl->dpy = |
| weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, |
| display, NULL); |
| 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); |
| |
| eglMakeCurrent(tri->egl->dpy, tri->egl_surface, |
| tri->egl_surface, tri->egl->ctx); |
| |
| 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 = weston_platform_create_egl_surface(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 = xzalloc(sizeof *tri); |
| |
| tri->egl = egl; |
| tri->widget = window_add_subsurface(window, tri, |
| int_to_mode(option_triangle_mode)); |
| widget_set_use_cairo(tri->widget, 0); |
| 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) |
| weston_platform_destroy_egl_surface(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 = xzalloc(sizeof *app); |
| |
| 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 = window_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; |
| |
| if (parse_options(options, ARRAY_LENGTH(options), &argc, argv) > 1 |
| || 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; |
| } |
| |
| if (!display_has_subcompositor(display)) { |
| fprintf(stderr, "compositor does not support " |
| "the subcompositor extension\n"); |
| return -1; |
| } |
| |
| app = demoapp_create(display); |
| |
| display_run(display); |
| |
| demoapp_destroy(app); |
| display_destroy(display); |
| |
| return 0; |
| } |