-----BEGIN PGP SIGNATURE----- iQJOBAABCAA4FiEEcDKHej6x6uPk3J379jQS5glH1u8FAmTKfCgaHG1hcml1cy52 bGFkQGNvbGxhYm9yYS5jb20ACgkQ9jQS5glH1u+ihA//e09DRmit8rw2Gglk5N5G P/O4m0BukEcXZy4dEo9WEkhrJ/hGTyZ3iqUMtfdbWouEpEMR25ck8n3jTEtUj2QN T4Gm/Ljcr8zzmtljbjEBDfBOaE07tISa7aX+IPTg+imf/t54kg80WwuKrW4FbNz2 azr2L+UAIiFZ4fJ5jA0uiGRiBo2gEwxylujZlt609SQiQCYND+XrFY3fWkHC8vyW JfR3IfrYER7KXlxkdJiFE36eIEpgYGIguVp6Uj6B35gxr0+npIrcjY38v/KJILGl 5mbxnwblGQpLgnBRMb2JmffetK+puFrTSOzEQDSIaOTjuQ0hHX1Ocjg3NfiEoxfi eG25SNaqMMUE2U2+cdojP97vCscoU71rwTpuklY1MG37O50jWsjgcUpVKYwcoljA TBOhwIJ2jLIzs5Ax37V81Bh7zj95nGYHupHnZOcQ/+SPnjnQ5Je6RE4Me4d0JJdN aR/hj/mF+DU80/ySfQwN9/bFp9hbCsozjFClIKiJxnWi71wQZl2o8ZfA8oJZyG6K VwYkVTQMoQtTuyDdyD6lbNbbC8/uq8ZnX9qKVHuEZlHWigvGSvuDQLGExuK7Ksjy sQPYBdGGWoIJGbqwuJX+FQ/hJgSnY1/VBp82N4//OUYs65LW01ACxF7J8SIIp+nE EJhNfCTDnO1hrN5xhmhk+qs= =GzNE -----END PGP SIGNATURE----- Merge tag '11.0.3' into dev 11.0.3dev
commit
0cc7369023
@ -0,0 +1,20 @@ |
||||
#!/bin/bash |
||||
|
||||
# When running Debian's Xwayland and fontconfig, we hit memory leaks which |
||||
# aren't visible on other setups. We do have suppressions for these tests, but |
||||
# regrettably ASan can't see through omitted frame pointers in Expat, so for |
||||
# Xwayland specifically, we disable fast-unwind. |
||||
# |
||||
# Doing it globally makes the other tests far, far, too slow to run. |
||||
case "$1" in |
||||
*xwayland*) |
||||
export ASAN_OPTIONS="detect_leaks=0,fast_unwind_on_malloc=0" |
||||
;; |
||||
*) |
||||
export ASAN_OPTIONS="detect_leaks=0" |
||||
;; |
||||
esac |
||||
|
||||
export ASAN_OPTIONS |
||||
|
||||
exec "$@" |
@ -1,504 +0,0 @@ |
||||
/*
|
||||
* Copyright ยฉ 2008 Kristian Hรธgsberg |
||||
* |
||||
* 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 <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <math.h> |
||||
#include <time.h> |
||||
#include <errno.h> |
||||
|
||||
#include <GL/gl.h> |
||||
#include <EGL/egl.h> |
||||
#include <EGL/eglext.h> |
||||
|
||||
#include <linux/input.h> |
||||
#include <wayland-client.h> |
||||
|
||||
#include "window.h" |
||||
|
||||
struct gears { |
||||
struct window *window; |
||||
struct widget *widget; |
||||
|
||||
struct display *d; |
||||
|
||||
EGLDisplay display; |
||||
EGLDisplay config; |
||||
EGLContext context; |
||||
GLfloat angle; |
||||
|
||||
struct { |
||||
GLfloat rotx; |
||||
GLfloat roty; |
||||
} view; |
||||
|
||||
int button_down; |
||||
int last_x, last_y; |
||||
|
||||
GLint gear_list[3]; |
||||
int fullscreen; |
||||
int frames; |
||||
uint32_t last_fps; |
||||
}; |
||||
|
||||
struct gear_template { |
||||
GLfloat material[4]; |
||||
GLfloat inner_radius; |
||||
GLfloat outer_radius; |
||||
GLfloat width; |
||||
GLint teeth; |
||||
GLfloat tooth_depth; |
||||
}; |
||||
|
||||
static const struct gear_template gear_templates[] = { |
||||
{ { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 }, |
||||
{ { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 }, |
||||
{ { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 }, |
||||
}; |
||||
|
||||
static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0}; |
||||
|
||||
static void die(const char *msg) |
||||
{ |
||||
fprintf(stderr, "%s", msg); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
static void |
||||
make_gear(const struct gear_template *t) |
||||
{ |
||||
GLint i; |
||||
GLfloat r0, r1, r2; |
||||
GLfloat angle, da; |
||||
GLfloat u, v, len; |
||||
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material); |
||||
|
||||
r0 = t->inner_radius; |
||||
r1 = t->outer_radius - t->tooth_depth / 2.0; |
||||
r2 = t->outer_radius + t->tooth_depth / 2.0; |
||||
|
||||
da = 2.0 * M_PI / t->teeth / 4.0; |
||||
|
||||
glShadeModel(GL_FLAT); |
||||
|
||||
glNormal3f(0.0, 0.0, 1.0); |
||||
|
||||
/* draw front face */ |
||||
glBegin(GL_QUAD_STRIP); |
||||
for (i = 0; i <= t->teeth; i++) { |
||||
angle = i * 2.0 * M_PI / t->teeth; |
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); |
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5); |
||||
if (i < t->teeth) { |
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); |
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5); |
||||
} |
||||
} |
||||
glEnd(); |
||||
|
||||
/* draw front sides of teeth */ |
||||
glBegin(GL_QUADS); |
||||
da = 2.0 * M_PI / t->teeth / 4.0; |
||||
for (i = 0; i < t->teeth; i++) { |
||||
angle = i * 2.0 * M_PI / t->teeth; |
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5); |
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5); |
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5); |
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5); |
||||
} |
||||
glEnd(); |
||||
|
||||
glNormal3f(0.0, 0.0, -1.0); |
||||
|
||||
/* draw back face */ |
||||
glBegin(GL_QUAD_STRIP); |
||||
for (i = 0; i <= t->teeth; i++) { |
||||
angle = i * 2.0 * M_PI / t->teeth; |
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); |
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); |
||||
if (i < t->teeth) { |
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5); |
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); |
||||
} |
||||
} |
||||
glEnd(); |
||||
|
||||
/* draw back sides of teeth */ |
||||
glBegin(GL_QUADS); |
||||
da = 2.0 * M_PI / t->teeth / 4.0; |
||||
for (i = 0; i < t->teeth; i++) { |
||||
angle = i * 2.0 * M_PI / t->teeth; |
||||
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5); |
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5); |
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5); |
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); |
||||
} |
||||
glEnd(); |
||||
|
||||
/* draw outward faces of teeth */ |
||||
glBegin(GL_QUAD_STRIP); |
||||
for (i = 0; i < t->teeth; i++) { |
||||
angle = i * 2.0 * M_PI / t->teeth; |
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5); |
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); |
||||
u = r2 * cos(angle + da) - r1 * cos(angle); |
||||
v = r2 * sin(angle + da) - r1 * sin(angle); |
||||
len = sqrt(u * u + v * v); |
||||
u /= len; |
||||
v /= len; |
||||
glNormal3f(v, -u, 0.0); |
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5); |
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5); |
||||
glNormal3f(cos(angle), sin(angle), 0.0); |
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5); |
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5); |
||||
u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); |
||||
v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); |
||||
glNormal3f(v, -u, 0.0); |
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5); |
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5); |
||||
glNormal3f(cos(angle), sin(angle), 0.0); |
||||
} |
||||
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5); |
||||
glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5); |
||||
|
||||
glEnd(); |
||||
|
||||
glShadeModel(GL_SMOOTH); |
||||
|
||||
/* draw inside radius cylinder */ |
||||
glBegin(GL_QUAD_STRIP); |
||||
for (i = 0; i <= t->teeth; i++) { |
||||
angle = i * 2.0 * M_PI / t->teeth; |
||||
glNormal3f(-cos(angle), -sin(angle), 0.0); |
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); |
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); |
||||
} |
||||
glEnd(); |
||||
} |
||||
|
||||
static void |
||||
update_fps(struct gears *gears, uint32_t time) |
||||
{ |
||||
long diff_ms; |
||||
static bool first_call = true; |
||||
|
||||
if (first_call) { |
||||
gears->last_fps = time; |
||||
first_call = false; |
||||
} else |
||||
gears->frames++; |
||||
|
||||
diff_ms = time - gears->last_fps; |
||||
|
||||
if (diff_ms > 5000) { |
||||
float seconds = diff_ms / 1000.0; |
||||
float fps = gears->frames / seconds; |
||||
|
||||
printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps); |
||||
fflush(stdout); |
||||
|
||||
gears->frames = 0; |
||||
gears->last_fps = time; |
||||
} |
||||
} |
||||
|
||||
static void |
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time) |
||||
{ |
||||
struct gears *gears = data; |
||||
|
||||
update_fps(gears, time); |
||||
|
||||
gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0; |
||||
|
||||
window_schedule_redraw(gears->window); |
||||
|
||||
if (callback) |
||||
wl_callback_destroy(callback); |
||||
} |
||||
|
||||
static const struct wl_callback_listener listener = { |
||||
frame_callback |
||||
}; |
||||
|
||||
static int |
||||
motion_handler(struct widget *widget, struct input *input, |
||||
uint32_t time, float x, float y, void *data) |
||||
{ |
||||
struct gears *gears = data; |
||||
int offset_x, offset_y; |
||||
float step = 0.5; |
||||
|
||||
if (gears->button_down) { |
||||
offset_x = x - gears->last_x; |
||||
offset_y = y - gears->last_y; |
||||
gears->last_x = x; |
||||
gears->last_y = y; |
||||
gears->view.roty += offset_x * step; |
||||
gears->view.rotx += offset_y * step; |
||||
if (gears->view.roty >= 360) |
||||
gears->view.roty = gears->view.roty - 360; |
||||
if (gears->view.roty <= 0) |
||||
gears->view.roty = gears->view.roty + 360; |
||||
if (gears->view.rotx >= 360) |
||||
gears->view.rotx = gears->view.rotx - 360; |
||||
if (gears->view.rotx <= 0) |
||||
gears->view.rotx = gears->view.rotx + 360; |
||||
} |
||||
|
||||
return CURSOR_LEFT_PTR; |
||||
} |
||||
|
||||
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 gears *gears = data; |
||||
|
||||
if (button == BTN_LEFT) { |
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
||||
gears->button_down = 1; |
||||
input_get_position(input, |
||||
&gears->last_x, &gears->last_y); |
||||
} else { |
||||
gears->button_down = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void |
||||
redraw_handler(struct widget *widget, void *data) |
||||
{ |
||||
struct rectangle window_allocation; |
||||
struct rectangle allocation; |
||||
struct wl_callback *callback; |
||||
struct gears *gears = data; |
||||
|
||||
widget_get_allocation(gears->widget, &allocation); |
||||
window_get_allocation(gears->window, &window_allocation); |
||||
|
||||
if (display_acquire_window_surface(gears->d, |
||||
gears->window, |
||||
gears->context) < 0) { |
||||
die("Unable to acquire window surface, " |
||||
"compiled without cairo-egl?\n"); |
||||
} |
||||
|
||||
glViewport(allocation.x, |
||||
window_allocation.height - allocation.height - allocation.y, |
||||
allocation.width, allocation.height); |
||||
glScissor(allocation.x, |
||||
window_allocation.height - allocation.height - allocation.y, |
||||
allocation.width, allocation.height); |
||||
|
||||
glEnable(GL_SCISSOR_TEST); |
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||||
|
||||
glPushMatrix(); |
||||
|
||||
glTranslatef(0.0, 0.0, -50); |
||||
|
||||
glRotatef(gears->view.rotx, 1.0, 0.0, 0.0); |
||||
glRotatef(gears->view.roty, 0.0, 1.0, 0.0); |
||||
|
||||
glPushMatrix(); |
||||
glTranslatef(-3.0, -2.0, 0.0); |
||||
glRotatef(gears->angle, 0.0, 0.0, 1.0); |
||||
glCallList(gears->gear_list[0]); |
||||
glPopMatrix(); |
||||
|
||||
glPushMatrix(); |
||||
glTranslatef(3.1, -2.0, 0.0); |
||||
glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0); |
||||
glCallList(gears->gear_list[1]); |
||||
glPopMatrix(); |
||||
|
||||
glPushMatrix(); |
||||
glTranslatef(-3.1, 4.2, 0.0); |
||||
glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0); |
||||
glCallList(gears->gear_list[2]); |
||||
glPopMatrix(); |
||||
|
||||
glPopMatrix(); |
||||
|
||||
glFlush(); |
||||
|
||||
display_release_window_surface(gears->d, gears->window); |
||||
|
||||
callback = wl_surface_frame(window_get_wl_surface(gears->window)); |
||||
wl_callback_add_listener(callback, &listener, gears); |
||||
} |
||||
|
||||
static void |
||||
resize_handler(struct widget *widget, |
||||
int32_t width, int32_t height, void *data) |
||||
{ |
||||
struct gears *gears = data; |
||||
int32_t size, big, small; |
||||
|
||||
/* Constrain child size to be square and at least 300x300 */ |
||||
if (width < height) { |
||||
small = width; |
||||
big = height; |
||||
} else { |
||||
small = height; |
||||
big = width; |
||||
} |
||||
|
||||
if (gears->fullscreen) |
||||
size = small; |
||||
else |
||||
size = big; |
||||
|
||||
widget_set_size(gears->widget, size, size); |
||||
} |
||||
|
||||
static void |
||||
keyboard_focus_handler(struct window *window, |
||||
struct input *device, void *data) |
||||
{ |
||||
window_schedule_redraw(window); |
||||
} |
||||
|
||||
static void |
||||
fullscreen_handler(struct window *window, void *data) |
||||
{ |
||||
struct gears *gears = data; |
||||
|
||||
gears->fullscreen ^= 1; |
||||
window_set_fullscreen(window, gears->fullscreen); |
||||
} |
||||
|
||||
static struct gears * |
||||
gears_create(struct display *display) |
||||
{ |
||||
const int width = 450, height = 500; |
||||
struct gears *gears; |
||||
int i; |
||||
|
||||
gears = zalloc(sizeof *gears); |
||||
gears->d = display; |
||||
gears->window = window_create(display); |
||||
gears->widget = window_frame_create(gears->window, gears); |
||||
window_set_title(gears->window, "Wayland Gears"); |
||||
window_set_appid(gears->window, "org.freedesktop.weston.wayland-gears"); |
||||
|
||||
gears->display = display_get_egl_display(gears->d); |
||||
if (gears->display == NULL) |
||||
die("failed to create egl display\n"); |
||||
|
||||
eglBindAPI(EGL_OPENGL_API); |
||||
|
||||
gears->config = display_get_argb_egl_config(gears->d); |
||||
|
||||
gears->context = eglCreateContext(gears->display, gears->config, |
||||
EGL_NO_CONTEXT, NULL); |
||||
if (gears->context == NULL) |
||||
die("failed to create context\n"); |
||||
|
||||
if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context)) |
||||
die("failed to make context current\n"); |
||||
|
||||
for (i = 0; i < 3; i++) { |
||||
gears->gear_list[i] = glGenLists(1); |
||||
glNewList(gears->gear_list[i], GL_COMPILE); |
||||
make_gear(&gear_templates[i]); |
||||
glEndList(); |
||||
} |
||||
|
||||
gears->button_down = 0; |
||||
gears->last_x = 0; |
||||
gears->last_y = 0; |
||||
|
||||
gears->view.rotx = 20.0; |
||||
gears->view.roty = 30.0; |
||||
|
||||
printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n"); |
||||
|
||||
glEnable(GL_NORMALIZE); |
||||
|
||||
glMatrixMode(GL_PROJECTION); |
||||
glLoadIdentity(); |
||||
glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0); |
||||
glMatrixMode(GL_MODELVIEW); |
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, light_pos); |
||||
glEnable(GL_CULL_FACE); |
||||
glEnable(GL_LIGHTING); |
||||
glEnable(GL_LIGHT0); |
||||
glEnable(GL_DEPTH_TEST); |
||||
glClearColor(0, 0, 0, 0.92); |
||||
|
||||
window_set_user_data(gears->window, gears); |
||||
widget_set_resize_handler(gears->widget, resize_handler); |
||||
widget_set_redraw_handler(gears->widget, redraw_handler); |
||||
widget_set_button_handler(gears->widget, button_handler); |
||||
widget_set_motion_handler(gears->widget, motion_handler); |
||||
window_set_keyboard_focus_handler(gears->window, |
||||
keyboard_focus_handler); |
||||
window_set_fullscreen_handler(gears->window, fullscreen_handler); |
||||
|
||||
window_schedule_resize(gears->window, width, height); |
||||
|
||||
return gears; |
||||
} |
||||
|
||||
static void |
||||
gears_destroy(struct gears *gears) |
||||
{ |
||||
widget_destroy(gears->widget); |
||||
window_destroy(gears->window); |
||||
free(gears); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
struct display *d; |
||||
struct gears *gears; |
||||
|
||||
d = display_create(&argc, argv); |
||||
if (d == NULL) { |
||||
fprintf(stderr, "failed to create display: %s\n", |
||||
strerror(errno)); |
||||
return -1; |
||||
} |
||||
gears = gears_create(d); |
||||
display_run(d); |
||||
|
||||
gears_destroy(gears); |
||||
display_destroy(d); |
||||
|
||||
return 0; |
||||
} |
@ -1,374 +0,0 @@ |
||||
/*
|
||||
* Copyright ยฉ 2013 Intel Corporation |
||||
* |
||||
* 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 <wayland-egl.h> |
||||
#include <wayland-cursor.h> |
||||
|
||||
#include <GLES2/gl2.h> |
||||
#include <EGL/egl.h> |
||||
|
||||
#include "shared/platform.h" |
||||
|
||||
struct window; |
||||
struct seat; |
||||
|
||||
struct nested_client { |
||||
struct wl_display *display; |
||||
struct wl_registry *registry; |
||||
struct wl_compositor *compositor; |
||||
|
||||
EGLDisplay egl_display; |
||||
EGLContext egl_context; |
||||
EGLConfig egl_config; |
||||
EGLSurface egl_surface; |
||||
struct program *color_program; |
||||
|
||||
GLuint vert, frag, program; |
||||
GLuint rotation; |
||||
GLuint pos; |
||||
GLuint col; |
||||
|
||||
struct wl_surface *surface; |
||||
struct wl_egl_window *native; |
||||
int width, height; |
||||
}; |
||||
|
||||
#define POS 0 |
||||
#define COL 1 |
||||
|
||||
static GLuint |
||||
create_shader(const char *source, GLenum shader_type) |
||||
{ |
||||
GLuint shader; |
||||
GLint status; |
||||
|
||||
shader = glCreateShader(shader_type); |
||||
if (shader == 0) |
||||
return 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); |
||||
return 0; |
||||
} |
||||
|
||||
return shader; |
||||
} |
||||
|
||||
static void |
||||
create_program(struct nested_client *client, |
||||
const char *vert, const char *frag) |
||||
{ |
||||
GLint status; |
||||
|
||||
client->vert = create_shader(vert, GL_VERTEX_SHADER); |
||||
client->frag = create_shader(frag, GL_FRAGMENT_SHADER); |
||||
|
||||
client->program = glCreateProgram(); |
||||
glAttachShader(client->program, client->frag); |
||||
glAttachShader(client->program, client->vert); |
||||
glBindAttribLocation(client->program, POS, "pos"); |
||||
glBindAttribLocation(client->program, COL, "color"); |
||||
glLinkProgram(client->program); |
||||
|
||||
glGetProgramiv(client->program, GL_LINK_STATUS, &status); |
||||
if (!status) { |
||||
char log[1000]; |
||||
GLsizei len; |
||||
glGetProgramInfoLog(client->program, 1000, &len, log); |
||||
fprintf(stderr, "Error: linking:\n%.*s\n", len, log); |
||||
exit(1); |
||||
} |
||||
|
||||
client->rotation = |
||||
glGetUniformLocation(client->program, "rotation"); |
||||
} |
||||
|
||||
static const char vertex_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 color_fragment_shader_text[] = |
||||
"precision mediump float;\n" |
||||
"varying vec4 v_color;\n" |
||||
"void main() {\n" |
||||
" gl_FragColor = v_color;\n" |
||||
"}\n"; |
||||
|
||||
static void |
||||
render_triangle(struct nested_client *client, 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; |
||||
static uint32_t start_time = 0; |
||||
|
||||
if (client->program == 0) |
||||
create_program(client, vertex_shader_text, |
||||
color_fragment_shader_text); |
||||
|
||||
if (start_time == 0) |
||||
start_time = time; |
||||
|
||||
angle = ((time - start_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); |
||||
|
||||
glClearColor(0.4, 0.4, 0.4, 1.0); |
||||
glClear(GL_COLOR_BUFFER_BIT); |
||||
|
||||
glUseProgram(client->program); |
||||
|
||||
glViewport(0, 0, client->width, client->height); |
||||
|
||||
glUniformMatrix4fv(client->rotation, 1, GL_FALSE, |
||||
(GLfloat *) rotation); |
||||
|
||||
glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts); |
||||
glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors); |
||||
glEnableVertexAttribArray(POS); |
||||
glEnableVertexAttribArray(COL); |
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3); |
||||
|
||||
glDisableVertexAttribArray(POS); |
||||
glDisableVertexAttribArray(COL); |
||||
|
||||
glFlush(); |
||||
} |
||||
|
||||
static void |
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time); |
||||
|
||||
static const struct wl_callback_listener frame_listener = { |
||||
frame_callback |
||||
}; |
||||
|
||||
static void |
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time) |
||||
{ |
||||
struct nested_client *client = data; |
||||
|
||||
if (callback) |
||||
wl_callback_destroy(callback); |
||||
|
||||
callback = wl_surface_frame(client->surface); |
||||
wl_callback_add_listener(callback, &frame_listener, client); |
||||
|
||||
render_triangle(client, time); |
||||
|
||||
eglSwapBuffers(client->egl_display, client->egl_surface); |
||||
} |
||||
|
||||
static void |
||||
registry_handle_global(void *data, struct wl_registry *registry, |
||||
uint32_t name, const char *interface, uint32_t version) |
||||
{ |
||||
struct nested_client *client = data; |
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) { |
||||
client->compositor = |
||||
wl_registry_bind(registry, name, |
||||
&wl_compositor_interface, 1); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
registry_handle_global_remove(void *data, struct wl_registry *registry, |
||||
uint32_t name) |
||||
{ |
||||
} |
||||
|
||||
static const struct wl_registry_listener registry_listener = { |
||||
registry_handle_global, |
||||
registry_handle_global_remove |
||||
}; |
||||
|
||||
static struct nested_client * |
||||
nested_client_create(void) |
||||
{ |
||||
static const EGLint context_attribs[] = { |
||||
EGL_CONTEXT_CLIENT_VERSION, 2, |
||||
EGL_NONE |
||||
}; |
||||
|
||||
static const 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; |
||||
|
||||
struct nested_client *client; |
||||
|
||||
client = malloc(sizeof *client); |
||||
if (client == NULL) |
||||
return NULL; |
||||
|
||||
client->width = 250; |
||||
client->height = 250; |
||||
|
||||
client->display = wl_display_connect(NULL); |
||||
|
||||
client->registry = wl_display_get_registry(client->display); |
||||
wl_registry_add_listener(client->registry, |
||||
®istry_listener, client); |
||||
|
||||
/* get globals */ |
||||
wl_display_roundtrip(client->display); |
||||
|
||||
client->egl_display = |
||||
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, |
||||
client->display, NULL); |
||||
if (client->egl_display == NULL) |
||||
return NULL; |
||||
|
||||
ret = eglInitialize(client->egl_display, &major, &minor); |
||||
if (!ret) |
||||
return NULL; |
||||
ret = eglBindAPI(EGL_OPENGL_ES_API); |
||||
if (!ret) |
||||
return NULL; |
||||
|
||||
ret = eglChooseConfig(client->egl_display, config_attribs, |
||||
&client->egl_config, 1, &n); |
||||
if (!ret || n != 1) |
||||
return NULL; |
||||
|
||||
client->egl_context = eglCreateContext(client->egl_display, |
||||
client->egl_config, |
||||
EGL_NO_CONTEXT, |
||||
context_attribs); |
||||
if (!client->egl_context) |
||||
return NULL; |
||||
|
||||
client->surface = wl_compositor_create_surface(client->compositor); |
||||
|
||||
client->native = wl_egl_window_create(client->surface, |
||||
client->width, client->height); |
||||
|
||||
client->egl_surface = weston_platform_create_egl_surface(client->egl_display, |
||||
client->egl_config, |
||||
client->native, NULL); |
||||
|
||||
eglMakeCurrent(client->egl_display, client->egl_surface, |
||||
client->egl_surface, client->egl_context); |
||||
|
||||
wl_egl_window_resize(client->native, |
||||
client->width, client->height, 0, 0); |
||||
|
||||
frame_callback(client, NULL, 0); |
||||
|
||||
return client; |
||||
} |
||||
|
||||
static void |
||||
nested_client_destroy(struct nested_client *client) |
||||
{ |
||||
eglMakeCurrent(client->egl_display, |
||||
EGL_NO_SURFACE, EGL_NO_SURFACE, |
||||
EGL_NO_CONTEXT); |
||||
|
||||
weston_platform_destroy_egl_surface(client->egl_display, |
||||
client->egl_surface); |
||||
wl_egl_window_destroy(client->native); |
||||
|
||||
wl_surface_destroy(client->surface); |
||||
|
||||
if (client->compositor) |
||||
wl_compositor_destroy(client->compositor); |
||||
|
||||
wl_registry_destroy(client->registry); |
||||
eglTerminate(client->egl_display); |
||||
eglReleaseThread(); |
||||
wl_display_flush(client->display); |
||||
wl_display_disconnect(client->display); |
||||
} |
||||
|
||||
int |
||||
main(int argc, char **argv) |
||||
{ |
||||
struct nested_client *client; |
||||
int ret = 0; |
||||
|
||||
if (getenv("WAYLAND_SOCKET") == NULL) { |
||||
fprintf(stderr, |
||||
"must be run by nested, don't run standalone\n"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
client = nested_client_create(); |
||||
if (client == NULL) |
||||
return EXIT_FAILURE; |
||||
|
||||
while (ret != -1) |
||||
ret = wl_display_dispatch(client->display); |
||||
|
||||
nested_client_destroy(client); |
||||
|
||||
return 0; |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,737 +0,0 @@ |
||||
/*
|
||||
* Copyright ยฉ 2010-2012 Intel Corporation |
||||
* Copyright ยฉ 2011-2012 Collabora, Ltd. |
||||
* Copyright ยฉ 2013 Raspberry Pi Foundation |
||||
* |
||||
* 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 <linux/input.h> |
||||
|
||||
#include "shell.h" |
||||
#include "shared/helpers.h" |
||||
|
||||
struct exposay_surface { |
||||
struct desktop_shell *shell; |
||||
struct exposay_output *eoutput; |
||||
struct weston_surface *surface; |
||||
struct weston_view *view; |
||||
struct wl_listener view_destroy_listener; |
||||
struct wl_list link; |
||||
|
||||
int x; |
||||
int y; |
||||
int width; |
||||
int height; |
||||
double scale; |
||||
|
||||
int row; |
||||
int column; |
||||
|
||||
/* The animations only apply a transformation for their own lifetime,
|
||||
* and don't have an option to indefinitely maintain the |
||||
* transformation in a steady state - so, we apply our own once the |
||||
* animation has finished. */ |
||||
struct weston_transform transform; |
||||
}; |
||||
|
||||
static void exposay_set_state(struct desktop_shell *shell, |
||||
enum exposay_target_state state, |
||||
struct weston_seat *seat); |
||||
static void exposay_check_state(struct desktop_shell *shell); |
||||
|
||||
static void |
||||
exposay_surface_destroy(struct exposay_surface *esurface) |
||||
{ |
||||
wl_list_remove(&esurface->link); |
||||
wl_list_remove(&esurface->view_destroy_listener.link); |
||||
|
||||
if (esurface->shell->exposay.focus_current == esurface->view) |
||||
esurface->shell->exposay.focus_current = NULL; |
||||
if (esurface->shell->exposay.focus_prev == esurface->view) |
||||
esurface->shell->exposay.focus_prev = NULL; |
||||
|
||||
free(esurface); |
||||
} |
||||
|
||||
static void |
||||
exposay_in_flight_inc(struct desktop_shell *shell) |
||||
{ |
||||
shell->exposay.in_flight++; |
||||
} |
||||
|
||||
static void |
||||
exposay_in_flight_dec(struct desktop_shell *shell) |
||||
{ |
||||
if (--shell->exposay.in_flight > 0) |
||||
return; |
||||
|
||||
exposay_check_state(shell); |
||||
} |
||||
|
||||
static void |
||||
exposay_animate_in_done(struct weston_view_animation *animation, void *data) |
||||
{ |
||||
struct exposay_surface *esurface = data; |
||||
|
||||
wl_list_insert(&esurface->view->geometry.transformation_list, |
||||
&esurface->transform.link); |
||||
weston_matrix_init(&esurface->transform.matrix); |
||||
weston_matrix_scale(&esurface->transform.matrix, |
||||
esurface->scale, esurface->scale, 1.0f); |
||||
weston_matrix_translate(&esurface->transform.matrix, |
||||
esurface->x - esurface->view->geometry.x, |
||||
esurface->y - esurface->view->geometry.y, |
||||
0); |
||||
|
||||
weston_view_geometry_dirty(esurface->view); |
||||
weston_compositor_schedule_repaint(esurface->view->surface->compositor); |
||||
|
||||
exposay_in_flight_dec(esurface->shell); |
||||
} |
||||
|
||||
static void |
||||
exposay_animate_in(struct exposay_surface *esurface) |
||||
{ |
||||
exposay_in_flight_inc(esurface->shell); |
||||
|
||||
weston_move_scale_run(esurface->view, |
||||
esurface->x - esurface->view->geometry.x, |
||||
esurface->y - esurface->view->geometry.y, |
||||
1.0, esurface->scale, 0, |
||||
exposay_animate_in_done, esurface); |
||||
} |
||||
|
||||
static void |
||||
exposay_animate_out_done(struct weston_view_animation *animation, void *data) |
||||
{ |
||||
struct exposay_surface *esurface = data; |
||||
struct desktop_shell *shell = esurface->shell; |
||||
|
||||
exposay_surface_destroy(esurface); |
||||
|
||||
exposay_in_flight_dec(shell); |
||||
} |
||||
|
||||
static void |
||||
exposay_animate_out(struct exposay_surface *esurface) |
||||
{ |
||||
exposay_in_flight_inc(esurface->shell); |
||||
|
||||
/* Remove the static transformation set up by
|
||||
* exposay_transform_in_done(). */ |
||||
wl_list_remove(&esurface->transform.link); |
||||
weston_view_geometry_dirty(esurface->view); |
||||
|
||||
weston_move_scale_run(esurface->view, |
||||
esurface->x - esurface->view->geometry.x, |
||||
esurface->y - esurface->view->geometry.y, |
||||
1.0, esurface->scale, 1, |
||||
exposay_animate_out_done, esurface); |
||||
} |
||||
|
||||
static void |
||||
exposay_highlight_surface(struct desktop_shell *shell, |
||||
struct exposay_surface *esurface) |
||||
{ |
||||
struct weston_view *view = esurface->view; |
||||
|
||||
if (shell->exposay.focus_current == view) |
||||
return; |
||||
|
||||
shell->exposay.row_current = esurface->row; |
||||
shell->exposay.column_current = esurface->column; |
||||
shell->exposay.cur_output = esurface->eoutput; |
||||
|
||||
activate(shell, view, shell->exposay.seat, |
||||
WESTON_ACTIVATE_FLAG_NONE); |
||||
shell->exposay.focus_current = view; |
||||
} |
||||
|
||||
static int |
||||
exposay_is_animating(struct desktop_shell *shell) |
||||
{ |
||||
if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE || |
||||
shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW) |
||||
return 0; |
||||
|
||||
return (shell->exposay.in_flight > 0); |
||||
} |
||||
|
||||
static void |
||||
exposay_pick(struct desktop_shell *shell, int x, int y) |
||||
{ |
||||
struct exposay_surface *esurface; |
||||
|
||||
if (exposay_is_animating(shell)) |
||||
return; |
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) { |
||||
if (x < esurface->x || x > esurface->x + esurface->width) |
||||
continue; |
||||
if (y < esurface->y || y > esurface->y + esurface->height) |
||||
continue; |
||||
|
||||
exposay_highlight_surface(shell, esurface); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
static void |
||||
handle_view_destroy(struct wl_listener *listener, void *data) |
||||
{ |
||||
struct exposay_surface *esurface = container_of(listener, |
||||
struct exposay_surface, |
||||
view_destroy_listener); |
||||
|
||||
exposay_surface_destroy(esurface); |
||||
} |
||||
|
||||
/* Compute each surface size and then inner pad (10% of surface size).
|
||||
* After that, it's necessary to recompute surface size (90% of its |
||||
* original size). Also, each surface can't be bigger than half the |
||||
* exposay area width and height. |
||||
*/ |
||||
static void |
||||
exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area, struct exposay_output *eoutput) |
||||
{ |
||||
if (exposay_area.height < exposay_area.width) |
||||
eoutput->surface_size = exposay_area.height / eoutput->grid_size; |
||||
else |
||||
eoutput->surface_size = exposay_area.width / eoutput->grid_size; |
||||
|
||||
eoutput->padding_inner = eoutput->surface_size / 10; |
||||
eoutput->surface_size -= eoutput->padding_inner; |
||||
|
||||
if ((uint32_t)eoutput->surface_size > (exposay_area.width / 2)) |
||||
eoutput->surface_size = exposay_area.width / 2; |
||||
if ((uint32_t)eoutput->surface_size > (exposay_area.height / 2)) |
||||
eoutput->surface_size = exposay_area.height / 2; |
||||
} |
||||
|
||||
/* Compute the exposay top/left margin in order to centralize it */ |
||||
static void |
||||
exposay_margin_size(struct desktop_shell *shell, pixman_rectangle32_t exposay_area, |
||||
int row_size, int column_size, int *left_margin, int *top_margin) |
||||
{ |
||||
(*left_margin) = exposay_area.x + (exposay_area.width - row_size) / 2; |
||||
(*top_margin) = exposay_area.y + (exposay_area.height - column_size) / 2; |
||||
} |
||||
|
||||
/* Pretty lame layout for now; just tries to make a square. Should take
|
||||
* aspect ratio into account really. Also needs to be notified of surface |
||||
* addition and removal and adjust layout/animate accordingly. |
||||
* |
||||
* Lay the grid out as square as possible, losing surfaces from the |
||||
* bottom row if required. Start with fixed padding of a 10% margin |
||||
* around the outside, and maximise the area made available to surfaces |
||||
* after this. Also, add an inner padding between surfaces that varies |
||||
* with the surface size (10% of its size). |
||||
* |
||||
* If we can't make a square grid, add one extra row at the bottom which |
||||
* will have a smaller number of columns. |
||||
*/ |
||||
static enum exposay_layout_state |
||||
exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) |
||||
{ |
||||
struct workspace *workspace = shell->exposay.workspace; |
||||
struct weston_output *output = shell_output->output; |
||||
struct exposay_output *eoutput = &shell_output->eoutput; |
||||
struct weston_view *view; |
||||
struct exposay_surface *esurface, *highlight = NULL; |
||||
pixman_rectangle32_t exposay_area; |
||||
int pad, row_size, column_size, left_margin, top_margin; |
||||
int last_row_size, last_row_margin_increase; |
||||
int populated_rows; |
||||
int i; |
||||
|
||||
eoutput->num_surfaces = 0; |
||||
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) { |
||||
if (!get_shell_surface(view->surface)) |
||||
continue; |
||||
if (view->output != output) |
||||
continue; |
||||
eoutput->num_surfaces++; |
||||
} |
||||
|
||||
if (eoutput->num_surfaces == 0) { |
||||
eoutput->grid_size = 0; |
||||
eoutput->padding_inner = 0; |
||||
eoutput->surface_size = 0; |
||||
return EXPOSAY_LAYOUT_OVERVIEW; |
||||
} |
||||
|
||||
/* Get exposay area and position, taking into account
|
||||
* the shell panel position and size */ |
||||
get_output_work_area(shell, output, &exposay_area); |
||||
|
||||
/* Compute grid size */ |
||||
eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces)); |
||||
if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces) |
||||
eoutput->grid_size++; |
||||
|
||||
/* Compute each surface size and the inner padding between them */ |
||||
exposay_surface_and_inner_pad_size(exposay_area, eoutput); |
||||
|
||||
/* Compute each row/column size */ |
||||
pad = eoutput->surface_size + eoutput->padding_inner; |
||||
row_size = (pad * eoutput->grid_size) - eoutput->padding_inner; |
||||
/* We may have empty rows that should be desconsidered to compute
|
||||
* column size */ |
||||
populated_rows = ceil(eoutput->num_surfaces / (float) eoutput->grid_size); |
||||
column_size = (pad * populated_rows) - eoutput->padding_inner; |
||||
|
||||
/* The last row size can be different, since it may have less surfaces
|
||||
* than the grid size. Also, its margin may be increased to centralize |
||||
* its surfaces, in the case where we don't have a perfect grid. */ |
||||
last_row_size = ((eoutput->num_surfaces % eoutput->grid_size) * pad) - eoutput->padding_inner; |
||||
if (eoutput->num_surfaces % eoutput->grid_size) |
||||
last_row_margin_increase = (row_size - last_row_size) / 2; |
||||
else |
||||
last_row_margin_increase = 0; |
||||
|
||||
/* Compute a top/left margin to centralize the exposay */ |
||||
exposay_margin_size(shell, exposay_area, row_size, column_size, &left_margin, &top_margin); |
||||
|
||||
i = 0; |
||||
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) { |
||||
|
||||
if (!get_shell_surface(view->surface)) |
||||
continue; |
||||
if (view->output != output) |
||||
continue; |
||||
|
||||
esurface = malloc(sizeof(*esurface)); |
||||
if (!esurface) { |
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, |
||||
shell->exposay.seat); |
||||
break; |
||||
} |
||||
|
||||
wl_list_insert(&shell->exposay.surface_list, &esurface->link); |
||||
esurface->shell = shell; |
||||
esurface->eoutput = eoutput; |
||||
esurface->view = view; |
||||
|
||||
esurface->row = i / eoutput->grid_size; |
||||
esurface->column = i % eoutput->grid_size; |
||||
|
||||
esurface->x = left_margin + (pad * esurface->column); |
||||
esurface->y = top_margin + (pad * esurface->row); |
||||
|
||||
/* If this is the last row, increase left margin (it sums 0 if
|
||||
* we have a perfect square) to centralize the surfaces */ |
||||
if (eoutput->num_surfaces / eoutput->grid_size == esurface->row) |
||||
esurface->x += last_row_margin_increase; |
||||
|
||||
if (view->surface->width > view->surface->height) |
||||
esurface->scale = eoutput->surface_size / (float) view->surface->width; |
||||
else |
||||
esurface->scale = eoutput->surface_size / (float) view->surface->height; |
||||
esurface->width = view->surface->width * esurface->scale; |
||||
esurface->height = view->surface->height * esurface->scale; |
||||
|
||||
/* Surfaces are usually rectangular, but their exposay surfaces
|
||||
* are square. centralize them in their own square */ |
||||
if (esurface->width > esurface->height) |
||||
esurface->y += (esurface->width - esurface->height) / 2; |
||||
else |
||||
esurface->x += (esurface->height - esurface->width) / 2; |
||||
|
||||
if (shell->exposay.focus_current == esurface->view) |
||||
highlight = esurface; |
||||
|
||||
exposay_animate_in(esurface); |
||||
|
||||
/* We want our destroy handler to be after the animation
|
||||
* destroy handler in the list, this way when the view is |
||||
* destroyed, the animation can safely call the animation |
||||
* completion callback before we free the esurface in our |
||||
* destroy handler. |
||||
*/ |
||||
esurface->view_destroy_listener.notify = handle_view_destroy; |
||||
wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener); |
||||
|
||||
i++; |
||||
} |
||||
|
||||
if (highlight) { |
||||
shell->exposay.focus_current = NULL; |
||||
exposay_highlight_surface(shell, highlight); |
||||
} |
||||
|
||||
weston_compositor_schedule_repaint(shell->compositor); |
||||
|
||||
return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW; |
||||
} |
||||
|
||||
static void |
||||
exposay_focus(struct weston_pointer_grab *grab) |
||||
{ |
||||
} |
||||
|
||||
static void |
||||
exposay_motion(struct weston_pointer_grab *grab, |
||||
const struct timespec *time, |
||||
struct weston_pointer_motion_event *event) |
||||
{ |
||||
struct desktop_shell *shell = |
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr); |
||||
|
||||
weston_pointer_move(grab->pointer, event); |
||||
|
||||
exposay_pick(shell, |
||||
wl_fixed_to_int(grab->pointer->x), |
||||
wl_fixed_to_int(grab->pointer->y)); |
||||
} |
||||
|
||||
static void |
||||
exposay_button(struct weston_pointer_grab *grab, const struct timespec *time, |
||||
uint32_t button, uint32_t state_w) |
||||
{ |
||||
struct desktop_shell *shell = |
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr); |
||||
struct weston_seat *seat = grab->pointer->seat; |
||||
enum wl_pointer_button_state state = state_w; |
||||
|
||||
if (button != BTN_LEFT) |
||||
return; |
||||
|
||||
/* Store the surface we clicked on, and don't do anything if we end up
|
||||
* releasing on a different surface. */ |
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
||||
shell->exposay.clicked = shell->exposay.focus_current; |
||||
return; |
||||
} |
||||
|
||||
if (shell->exposay.focus_current == shell->exposay.clicked) |
||||
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); |
||||
else |
||||
shell->exposay.clicked = NULL; |
||||
} |
||||
|
||||
static void |
||||
exposay_axis(struct weston_pointer_grab *grab, |
||||
const struct timespec *time, |
||||
struct weston_pointer_axis_event *event) |
||||
{ |
||||
} |
||||
|
||||
static void |
||||
exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source) |
||||
{ |
||||
} |
||||
|
||||
static void |
||||
exposay_frame(struct weston_pointer_grab *grab) |
||||
{ |
||||
} |
||||
|
||||
static void |
||||
exposay_pointer_grab_cancel(struct weston_pointer_grab *grab) |
||||
{ |
||||
struct desktop_shell *shell = |
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr); |
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat); |
||||
} |
||||
|
||||
static const struct weston_pointer_grab_interface exposay_ptr_grab = { |
||||
exposay_focus, |
||||
exposay_motion, |
||||
exposay_button, |
||||
exposay_axis, |
||||
exposay_axis_source, |
||||
exposay_frame, |
||||
exposay_pointer_grab_cancel, |
||||
}; |
||||
|
||||
static int |
||||
exposay_maybe_move(struct desktop_shell *shell, int row, int column) |
||||
{ |
||||
struct exposay_surface *esurface; |
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) { |
||||
if (esurface->eoutput != shell->exposay.cur_output || |
||||
esurface->row != row || esurface->column != column) |
||||
continue; |
||||
|
||||
exposay_highlight_surface(shell, esurface); |
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void |
||||
exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time, |
||||
uint32_t key, uint32_t state_w) |
||||
{ |
||||
struct weston_seat *seat = grab->keyboard->seat; |
||||
struct desktop_shell *shell = |
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd); |
||||
enum wl_keyboard_key_state state = state_w; |
||||
|
||||
if (state != WL_KEYBOARD_KEY_STATE_RELEASED) |
||||
return; |
||||
|
||||
switch (key) { |
||||
case KEY_ESC: |
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); |
||||
break; |
||||
case KEY_ENTER: |
||||
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); |
||||
break; |
||||
case KEY_UP: |
||||
exposay_maybe_move(shell, shell->exposay.row_current - 1, |
||||
shell->exposay.column_current); |
||||
break; |
||||
case KEY_DOWN: |
||||
/* Special case for trying to move to the bottom row when it
|
||||
* has fewer items than all the others. */ |
||||
if (!exposay_maybe_move(shell, shell->exposay.row_current + 1, |
||||
shell->exposay.column_current) && |
||||
shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) { |
||||
exposay_maybe_move(shell, shell->exposay.row_current + 1, |
||||
(shell->exposay.cur_output->num_surfaces % |
||||
shell->exposay.cur_output->grid_size) - 1); |
||||
} |
||||
break; |
||||
case KEY_LEFT: |
||||
exposay_maybe_move(shell, shell->exposay.row_current, |
||||
shell->exposay.column_current - 1); |
||||
break; |
||||
case KEY_RIGHT: |
||||
exposay_maybe_move(shell, shell->exposay.row_current, |
||||
shell->exposay.column_current + 1); |
||||
break; |
||||
case KEY_TAB: |
||||
/* Try to move right, then down (and to the leftmost column),
|
||||
* then if all else fails, to the top left. */ |
||||
if (!exposay_maybe_move(shell, shell->exposay.row_current, |
||||
shell->exposay.column_current + 1) && |
||||
!exposay_maybe_move(shell, shell->exposay.row_current + 1, 0)) |
||||
exposay_maybe_move(shell, 0, 0); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static void |
||||
exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial, |
||||
uint32_t mods_depressed, uint32_t mods_latched, |
||||
uint32_t mods_locked, uint32_t group) |
||||
{ |
||||
struct desktop_shell *shell = |
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd); |
||||
struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat; |
||||
|
||||
/* We want to know when mod has been pressed and released.
|
||||
* FIXME: There is a problem here: if mod is pressed, then a key |
||||
* is pressed and released, then mod is released, we will treat that |
||||
* as if only mod had been pressed and released. */ |
||||
if (seat->modifier_state) { |
||||
if (seat->modifier_state == shell->binding_modifier) { |
||||
shell->exposay.mod_pressed = true; |
||||
} else { |
||||
shell->exposay.mod_invalid = true; |
||||
} |
||||
} else { |
||||
if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid) |
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); |
||||
|
||||
shell->exposay.mod_invalid = false; |
||||
shell->exposay.mod_pressed = false; |
||||
} |
||||
|
||||
return; |
||||
} |
||||
|
||||
static void |
||||
exposay_cancel(struct weston_keyboard_grab *grab) |
||||
{ |
||||
struct desktop_shell *shell = |
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd); |
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat); |
||||
} |
||||
|
||||
static const struct weston_keyboard_grab_interface exposay_kbd_grab = { |
||||
exposay_key, |
||||
exposay_modifier, |
||||
exposay_cancel, |
||||
}; |
||||
|
||||
/**
|
||||
* Called when the transition from overview -> inactive has completed. |
||||
*/ |
||||
static enum exposay_layout_state |
||||
exposay_set_inactive(struct desktop_shell *shell) |
||||
{ |
||||
struct weston_seat *seat = shell->exposay.seat; |
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); |
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat); |
||||
|
||||
if (pointer) |
||||
weston_pointer_end_grab(pointer); |
||||
|
||||
if (keyboard) { |
||||
weston_keyboard_end_grab(keyboard); |
||||
if (keyboard->input_method_resource) |
||||
keyboard->grab = &keyboard->input_method_grab; |
||||
} |
||||
|
||||
return EXPOSAY_LAYOUT_INACTIVE; |
||||
} |
||||
|
||||
/**
|
||||
* Begins the transition from overview to inactive. */ |
||||
static enum exposay_layout_state |
||||
exposay_transition_inactive(struct desktop_shell *shell, int switch_focus) |
||||
{ |
||||
struct exposay_surface *esurface; |
||||
|
||||
/* Call activate() before we start the animations to avoid
|
||||
* animating back the old state and then immediately transitioning |
||||
* to the new. */ |
||||
if (switch_focus && shell->exposay.focus_current) |
||||
activate(shell, shell->exposay.focus_current, |
||||
shell->exposay.seat, |
||||
WESTON_ACTIVATE_FLAG_CONFIGURE); |
||||
else if (shell->exposay.focus_prev) |
||||
activate(shell, shell->exposay.focus_prev, |
||||
shell->exposay.seat, |
||||
WESTON_ACTIVATE_FLAG_CONFIGURE); |
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) |
||||
exposay_animate_out(esurface); |
||||
weston_compositor_schedule_repaint(shell->compositor); |
||||
|
||||
return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE; |
||||
} |
||||
|
||||
static enum exposay_layout_state |
||||
exposay_transition_active(struct desktop_shell *shell) |
||||
{ |
||||
struct weston_seat *seat = shell->exposay.seat; |
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat); |
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); |
||||
struct shell_output *shell_output; |
||||
bool animate = false; |
||||
|
||||
shell->exposay.workspace = get_current_workspace(shell); |
||||
shell->exposay.focus_prev = get_default_view(keyboard->focus); |
||||
shell->exposay.focus_current = get_default_view(keyboard->focus); |
||||
shell->exposay.clicked = NULL; |
||||
wl_list_init(&shell->exposay.surface_list); |
||||
|
||||
lower_fullscreen_layer(shell, NULL); |
||||
shell->exposay.grab_kbd.interface = &exposay_kbd_grab; |
||||
weston_keyboard_start_grab(keyboard, |
||||
&shell->exposay.grab_kbd); |
||||
weston_keyboard_set_focus(keyboard, NULL); |
||||
|
||||
shell->exposay.grab_ptr.interface = &exposay_ptr_grab; |
||||
if (pointer) { |
||||
weston_pointer_start_grab(pointer, |
||||
&shell->exposay.grab_ptr); |
||||
weston_pointer_clear_focus(pointer); |
||||
} |
||||
wl_list_for_each(shell_output, &shell->output_list, link) { |
||||
enum exposay_layout_state state; |
||||
|
||||
state = exposay_layout(shell, shell_output); |
||||
|
||||
if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW) |
||||
animate = true; |
||||
} |
||||
|
||||
return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW |
||||
: EXPOSAY_LAYOUT_OVERVIEW; |
||||
} |
||||
|
||||
static void |
||||
exposay_check_state(struct desktop_shell *shell) |
||||
{ |
||||
enum exposay_layout_state state_new = shell->exposay.state_cur; |
||||
int do_switch = 0; |
||||
|
||||
/* Don't do anything whilst animations are running, just store up
|
||||
* target state changes and only act on them when the animations have |
||||
* completed. */ |
||||
if (exposay_is_animating(shell)) |
||||
return; |
||||
|
||||
switch (shell->exposay.state_target) { |
||||
case EXPOSAY_TARGET_OVERVIEW: |
||||
switch (shell->exposay.state_cur) { |
||||
case EXPOSAY_LAYOUT_OVERVIEW: |
||||
goto out; |
||||
case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW: |
||||
state_new = EXPOSAY_LAYOUT_OVERVIEW; |
||||
break; |
||||
default: |
||||
state_new = exposay_transition_active(shell); |
||||
break; |
||||
} |
||||
break; |
||||
|
||||
case EXPOSAY_TARGET_SWITCH: |
||||
do_switch = 1; /* fallthrough */ |
||||
case EXPOSAY_TARGET_CANCEL: |
||||
switch (shell->exposay.state_cur) { |
||||
case EXPOSAY_LAYOUT_INACTIVE: |
||||
goto out; |
||||
case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE: |
||||
state_new = exposay_set_inactive(shell); |
||||
break; |
||||
default: |
||||
state_new = exposay_transition_inactive(shell, do_switch); |
||||
break; |
||||
} |
||||
|
||||
break; |
||||
} |
||||
|
||||
out: |
||||
shell->exposay.state_cur = state_new; |
||||
} |
||||
|
||||
static void |
||||
exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state, |
||||
struct weston_seat *seat) |
||||
{ |
||||
shell->exposay.state_target = state; |
||||
shell->exposay.seat = seat; |
||||
exposay_check_state(shell); |
||||
} |
||||
|
||||
void |
||||
exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier, |
||||
void *data) |
||||
{ |
||||
struct desktop_shell *shell = data; |
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat); |
||||
} |
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 108 KiB |
@ -0,0 +1,9 @@ |
||||
# Sphinx does not know look for these files in the source directory, so |
||||
# they must be copied to the build directory. |
||||
files = [ |
||||
'ivi-shell.png', |
||||
] |
||||
|
||||
foreach file : files |
||||
configure_file(input: file, output: file, copy: true) |
||||
endforeach |
@ -0,0 +1,111 @@ |
||||
Weston IVI-shell |
||||
================ |
||||
|
||||
Weston's IVI-shell is a highly customizable shell targeted at use cases which |
||||
need custom control over the shell's window layout with one or more applications |
||||
without interactive configuration of the layout by the user. |
||||
|
||||
Example use cases for the IVI-shell are IVI applications or industrial human |
||||
machine interfaces. In general, whenever the user interface requires the exact |
||||
positioning of multiple application surfaces on one or more screens. |
||||
|
||||
The IVI-shell also provides a means for applications to identify themselves to |
||||
the shell by application IDs via the ivi_application Wayland protocol. |
||||
|
||||
IVI-shell client protocol |
||||
------------------------- |
||||
|
||||
Wayland clients can implement the ``ivi_application`` Wayland protocol, which |
||||
allows them to specify an ``ivi_id`` to allow the IVI controller to identify the |
||||
application. This allows the controller to implement special behavior for |
||||
well-known applications. |
||||
|
||||
The IVI-shell is able to also handle clients that use the ``xdg-shell`` |
||||
protocol, but in these cases the IVI-shell needs other means to identify client |
||||
applications. |
||||
|
||||
See ``ivi-application.xml`` for the protocol specification. |
||||
|
||||
IVI-shell Weston modules |
||||
------------------------ |
||||
|
||||
The IVI-shell consists of two main components: The ``ivi-shell.so`` and custom |
||||
IVI controller (with the ``hmi-controller.so`` example implementation). |
||||
|
||||
The ``ivi-shell.so`` is responsible for handling the application IDs and for |
||||
providing abstractions to configure the window layout via the |
||||
``ivi_layout_interface``. This interface is discussed in `IVI-shell compositor |
||||
implementation`. |
||||
|
||||
The IVI controller uses the ``ivi_layout_interface`` to implement a window |
||||
manager and is responsible for configuring the window layout, i.e. the position |
||||
of the applications on the screens. |
||||
|
||||
Due to this separation, both modules must be loaded in your ``weston.ini`` to |
||||
use the IVI-shell. |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[core] |
||||
shell=ivi-shell.so |
||||
modules=hmi-controller.so |
||||
|
||||
If you are using your custom controller, replace ``hmi-controller.so`` with the |
||||
name of your own controller module. |
||||
|
||||
.. figure:: images/ivi-shell.png |
||||
:alt: IVI-shell architecture overview |
||||
|
||||
Controlling the IVI-shell |
||||
------------------------- |
||||
|
||||
The IVI-shell provides the ``ivi_layout_interface`` API that a controller must |
||||
use to control the window layout of the IVI-shell. See |
||||
``ivi-shell/ivi-layout-export.h`` for the definition of this API. |
||||
|
||||
For the initial configuration, the controller has to create at least one |
||||
``ivi_layout_layer`` and add the ``ivi_layout_layer`` to a ``weston_output``. |
||||
The layers allow to group multiple applications surfaces and control them |
||||
together and are the main mechanism to group and organize surfaces. These are |
||||
always necessary to show something using the IVI-shell. The IVI-shell will |
||||
internally create an ``ivi_layout_screen``, but a controller always uses the |
||||
``weston_output`` directly. |
||||
|
||||
To get control over the client surfaces, the controller must use notifiers that |
||||
trigger whenever there are changes to the client surfaces. The client surfaces |
||||
then show up as ``ivi_layout_surface``. These have an ID, which allows the |
||||
controller to identify the surface and reconfigure the window layout |
||||
accordingly. |
||||
|
||||
The controller must add the ``ivi_layout_surface`` to an ``ivi_layout_layer`` |
||||
and configure it's position and z-order wrt. the other surfaces in the layer. |
||||
Otherwise, the newly added surface will not show up on the screen. |
||||
|
||||
The IVI-shell will internally create an ``ivi_layout_view`` for each layer that |
||||
the surface was added to. However, the views are not provided to the IVI |
||||
controller. |
||||
|
||||
After configuring all expected changes, the controller must call the |
||||
``commit_changes`` to atomically update the display layout. |
||||
|
||||
IVI-shell example implementation |
||||
-------------------------------- |
||||
|
||||
The IVI-shell comes with an example implementation of an IVI controller -- the |
||||
`hmi-controller`. The hmi-controller will usually replaced by a custom |
||||
implementation that implements the use-case-specific behavior. |
||||
|
||||
The hmi-controller is split into two parts: |
||||
|
||||
The ``hmi-controller.so`` is a Weston Plugin that uses the |
||||
``ivi_layout_interface`` to perform the window manager tasks. It allows some |
||||
reconfiguration of the window layout via the ``ivi_hmi_controller`` protocol. |
||||
Other implementations may keep all window management inside the module or may |
||||
expose even more window management via a custom protocol to an external process. |
||||
|
||||
The ``weston-ivi-shell-user-interface`` is an example hmi-controller helper |
||||
client that serves as a user interface for controlling the hmi-controller. |
||||
|
||||
The hmi-controller can be customized using the ``[ivi-shell]`` section in the |
||||
``weston.ini``. An example configuration will be generated in |
||||
``<build_dir>/ivi-shell/weston.ini``. |