mirror of
https://gitlab.freedesktop.org/wayland/weston
synced 2024-09-06 18:05:39 +00:00
![Emil Velikov](/assets/img/avatar_default.png)
... over a direct eglDestroySurface call. Provides symmetry in the create/destroy paths. Signed-off-by: Emil Velikov <emil.velikov@collabora.com> Reviewed-by: Daniel Stone <daniels@collabora.com>
810 lines
20 KiB
C
810 lines
20 KiB
C
/*
|
|
* 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;
|
|
}
|