-----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``. |
@ -1,70 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2016 Benoit Gschwind |
|
||||||
* |
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef WESTON_COMPOSITOR_FBDEV_H |
|
||||||
#define WESTON_COMPOSITOR_FBDEV_H |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
#include <libweston/libweston.h> |
|
||||||
|
|
||||||
#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 2 |
|
||||||
|
|
||||||
struct libinput_device; |
|
||||||
|
|
||||||
struct weston_fbdev_backend_config { |
|
||||||
struct weston_backend_config base; |
|
||||||
|
|
||||||
int tty; |
|
||||||
char *device; |
|
||||||
|
|
||||||
/** Callback used to configure input devices.
|
|
||||||
* |
|
||||||
* This function will be called by the backend when a new input device |
|
||||||
* needs to be configured. |
|
||||||
* If NULL the device will use the default configuration. |
|
||||||
*/ |
|
||||||
void (*configure_device)(struct weston_compositor *compositor, |
|
||||||
struct libinput_device *device); |
|
||||||
|
|
||||||
/** The seat to be used for input and output.
|
|
||||||
* |
|
||||||
* If seat_id is NULL, the seat is taken from XDG_SEAT environment |
|
||||||
* variable. If neither is set, "seat0" is used. The backend will |
|
||||||
* take ownership of the seat_id pointer and will free it on |
|
||||||
* backend destruction. |
|
||||||
*/ |
|
||||||
char *seat_id; |
|
||||||
}; |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif /* WESTON_COMPOSITOR_FBDEV_H */ |
|
@ -1,78 +0,0 @@ |
|||||||
In-vehicle infotainment (information and entertainment) |
|
||||||
graphical environment support modules for Weston |
|
||||||
|
|
||||||
|
|
||||||
IVI-shell is an alternative shell for Weston, a Wayland display server. |
|
||||||
Window management and application interaction with the display server |
|
||||||
are very different to that of a normal desktop, which is why this is |
|
||||||
a separate shell and not an extension to the desktop-shell suite with |
|
||||||
xdg_shell. As such, applications need to be specifically written to use |
|
||||||
IVI-shell. |
|
||||||
|
|
||||||
IVI-shell contains two main features: |
|
||||||
- Common layout library for surface, which allow ivi-shell developer |
|
||||||
to develop own shell, linking Common layout library. |
|
||||||
For the time being, the library refers Genivi ilm interface. |
|
||||||
|
|
||||||
https://at.projects.genivi.org/wiki/display/WIE/Wayland+IVI+Extension+Home |
|
||||||
|
|
||||||
- Extension protocol; ivi-application to tie wl_surface and a given ID. |
|
||||||
With this ID, shell can identify which wl_surface is drawn by which |
|
||||||
application. In in-vehicle infortainment system, a shell has to update |
|
||||||
a property of a wl_surface. E.g. there may be a use case when vehicle |
|
||||||
starts to move, the wl_surface drawn by Car navigation is expected to |
|
||||||
move top of surfaces. |
|
||||||
|
|
||||||
The actual software components delivered with Weston are: |
|
||||||
|
|
||||||
- ivi-application.xml: |
|
||||||
Wayland protocol extension for IVI-applications; the public |
|
||||||
shell protocol (the same concept as xdg_shell). |
|
||||||
Implemented by ivi-shell.so. |
|
||||||
|
|
||||||
- ivi-shell.so: |
|
||||||
A Weston shell module that implements ivi-application.xml interfaces. |
|
||||||
Loads ivi-layout.so. |
|
||||||
|
|
||||||
- ivi-layout.so: |
|
||||||
Implements the IVI window management concepts: Screen, Layer, |
|
||||||
Surface, groups of Layers, groups of Surfaces, see: |
|
||||||
https://at.projects.genivi.org/wiki/display/WIE/Summary+of+Layer+manager+APIs |
|
||||||
Offers a stable API for writing IVI-controller modules like |
|
||||||
hmi-controller.so against the IVI concepts. In other words, |
|
||||||
it offers an API to write IVI window manager modules. |
|
||||||
|
|
||||||
- hmi-controller.so: |
|
||||||
A sample implementation of an IVI-controller module, usually |
|
||||||
replaced by IVI system vendors. |
|
||||||
Uses ivi-layout.so to perform essentially window manager tasks. |
|
||||||
This implementation keeps all window management inside the module, |
|
||||||
while IVI-systems may use another module that exposes all window |
|
||||||
management via Wayland or other protocol for an external process |
|
||||||
to control. |
|
||||||
|
|
||||||
- ivi-hmi-controller.xml: |
|
||||||
Wayland protocol extension for IVI display control; the private |
|
||||||
shell protocol for weston-ivi-shell-user-interface client |
|
||||||
(the same concept as desktop-shell.xml). |
|
||||||
Implemented by hmi-controller.so, and usually replaced by IVI |
|
||||||
system vendors. |
|
||||||
|
|
||||||
- weston-ivi-shell-user-interface: |
|
||||||
A sample implementation of an IVI shell helper client, usually |
|
||||||
replaced by IVI system vendors. |
|
||||||
A helper client for basic display content, similar to |
|
||||||
weston-desktop-shell. |
|
||||||
|
|
||||||
|
|
||||||
How to compile: |
|
||||||
same as weston. To disable, use option: --disable-ivi-shell for configure. |
|
||||||
|
|
||||||
How to configure weston.ini: |
|
||||||
reference ini file will be generated in <build_dir>/ivi-shell. |
|
||||||
|
|
||||||
How to run: |
|
||||||
same as weston. exec weston. |
|
||||||
|
|
||||||
How to use UI: |
|
||||||
http://lists.freedesktop.org/archives/wayland-devel/attachments/20140625/abbfc064/attachment-0001.png |
|
@ -1,36 +0,0 @@ |
|||||||
srcs_libdesktop = [ |
|
||||||
'libweston-desktop.c', |
|
||||||
'client.c', |
|
||||||
'seat.c', |
|
||||||
'surface.c', |
|
||||||
'xwayland.c', |
|
||||||
'wl-shell.c', |
|
||||||
'xdg-shell.c', |
|
||||||
'xdg-shell-v6.c', |
|
||||||
xdg_shell_unstable_v6_server_protocol_h, |
|
||||||
xdg_shell_unstable_v6_protocol_c, |
|
||||||
xdg_shell_server_protocol_h, |
|
||||||
xdg_shell_protocol_c, |
|
||||||
] |
|
||||||
lib_desktop = shared_library( |
|
||||||
'weston-desktop-@0@'.format(libweston_major), |
|
||||||
srcs_libdesktop, |
|
||||||
include_directories: common_inc, |
|
||||||
install: true, |
|
||||||
version: '0.0.@0@'.format(libweston_revision), |
|
||||||
dependencies: dep_libweston_public |
|
||||||
) |
|
||||||
dep_lib_desktop = declare_dependency( |
|
||||||
link_with: lib_desktop, |
|
||||||
dependencies: dep_libweston_public |
|
||||||
) |
|
||||||
|
|
||||||
pkgconfig.generate( |
|
||||||
lib_desktop, |
|
||||||
filebase: 'libweston-desktop-@0@'.format(libweston_major), |
|
||||||
name: 'libweston-desktop', |
|
||||||
version: version_weston, |
|
||||||
description: 'Desktop shells abstraction library for libweston compositors', |
|
||||||
requires_private: [ lib_weston, dep_wayland_server ], |
|
||||||
subdirs: dir_include_libweston |
|
||||||
) |
|
@ -1,497 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2010-2012 Intel Corporation |
|
||||||
* Copyright © 2011-2012 Collabora, Ltd. |
|
||||||
* Copyright © 2013 Raspberry Pi Foundation |
|
||||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic |
|
||||||
* |
|
||||||
* 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 <assert.h> |
|
||||||
|
|
||||||
#include <wayland-server.h> |
|
||||||
|
|
||||||
#include <libweston/libweston.h> |
|
||||||
#include <libweston/zalloc.h> |
|
||||||
|
|
||||||
#include <libweston-desktop/libweston-desktop.h> |
|
||||||
#include "internal.h" |
|
||||||
|
|
||||||
#define WD_WL_SHELL_PROTOCOL_VERSION 1 |
|
||||||
|
|
||||||
enum weston_desktop_wl_shell_surface_state { |
|
||||||
NONE, |
|
||||||
TOPLEVEL, |
|
||||||
MAXIMIZED, |
|
||||||
FULLSCREEN, |
|
||||||
TRANSIENT, |
|
||||||
POPUP, |
|
||||||
}; |
|
||||||
|
|
||||||
struct weston_desktop_wl_shell_surface { |
|
||||||
struct wl_resource *resource; |
|
||||||
struct weston_desktop *desktop; |
|
||||||
struct wl_display *display; |
|
||||||
struct weston_desktop_surface *surface; |
|
||||||
struct weston_desktop_surface *parent; |
|
||||||
bool added; |
|
||||||
struct weston_desktop_seat *popup_seat; |
|
||||||
enum weston_desktop_wl_shell_surface_state state; |
|
||||||
struct wl_listener wl_surface_resource_destroy_listener; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_set_size(struct weston_desktop_surface *dsurface, |
|
||||||
void *user_data, |
|
||||||
int32_t width, int32_t height) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
struct weston_surface *wsurface = |
|
||||||
weston_desktop_surface_get_surface(surface->surface); |
|
||||||
|
|
||||||
if ((wsurface->width == width && wsurface->height == height) || |
|
||||||
(width == 0 && height == 0)) |
|
||||||
return; |
|
||||||
|
|
||||||
wl_shell_surface_send_configure(surface->resource, |
|
||||||
WL_SHELL_SURFACE_RESIZE_NONE, |
|
||||||
width, height); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_maybe_ungrab(struct weston_desktop_wl_shell_surface *surface) |
|
||||||
{ |
|
||||||
if (surface->state != POPUP || |
|
||||||
!weston_desktop_surface_get_grab(surface->surface)) |
|
||||||
return; |
|
||||||
|
|
||||||
weston_desktop_surface_popup_ungrab(surface->surface, |
|
||||||
surface->popup_seat); |
|
||||||
surface->popup_seat = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_committed(struct weston_desktop_surface *dsurface, |
|
||||||
void *user_data, |
|
||||||
int32_t sx, int32_t sy) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
struct weston_surface *wsurface = |
|
||||||
weston_desktop_surface_get_surface(dsurface); |
|
||||||
|
|
||||||
if (wsurface->buffer_ref.buffer == NULL) |
|
||||||
weston_desktop_wl_shell_surface_maybe_ungrab(surface); |
|
||||||
|
|
||||||
if (surface->added) |
|
||||||
weston_desktop_api_committed(surface->desktop, surface->surface, |
|
||||||
sx, sy); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_ping(struct weston_desktop_surface *dsurface, |
|
||||||
uint32_t serial, void *user_data) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
|
|
||||||
wl_shell_surface_send_ping(surface->resource, serial); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_close(struct weston_desktop_surface *dsurface, |
|
||||||
void *user_data) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
|
|
||||||
if (surface->state == POPUP) |
|
||||||
wl_shell_surface_send_popup_done(surface->resource); |
|
||||||
} |
|
||||||
|
|
||||||
static bool |
|
||||||
weston_desktop_wl_shell_surface_get_maximized(struct weston_desktop_surface *dsurface, |
|
||||||
void *user_data) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
|
|
||||||
return surface->state == MAXIMIZED; |
|
||||||
} |
|
||||||
|
|
||||||
static bool |
|
||||||
weston_desktop_wl_shell_surface_get_fullscreen(struct weston_desktop_surface *dsurface, |
|
||||||
void *user_data) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
|
|
||||||
return surface->state == FULLSCREEN; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_change_state(struct weston_desktop_wl_shell_surface *surface, |
|
||||||
enum weston_desktop_wl_shell_surface_state state, |
|
||||||
struct weston_desktop_surface *parent, |
|
||||||
int32_t x, int32_t y) |
|
||||||
{ |
|
||||||
bool to_add = (parent == NULL); |
|
||||||
|
|
||||||
assert(state != NONE); |
|
||||||
|
|
||||||
if (to_add && surface->added) { |
|
||||||
surface->state = state; |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if (surface->state != state) { |
|
||||||
if (surface->state == POPUP) |
|
||||||
weston_desktop_wl_shell_surface_maybe_ungrab(surface); |
|
||||||
|
|
||||||
if (to_add) { |
|
||||||
weston_desktop_surface_unset_relative_to(surface->surface); |
|
||||||
weston_desktop_api_surface_added(surface->desktop, |
|
||||||
surface->surface); |
|
||||||
} else if (surface->added) { |
|
||||||
weston_desktop_api_surface_removed(surface->desktop, |
|
||||||
surface->surface); |
|
||||||
} |
|
||||||
|
|
||||||
surface->state = state; |
|
||||||
surface->added = to_add; |
|
||||||
} |
|
||||||
|
|
||||||
if (parent != NULL) |
|
||||||
weston_desktop_surface_set_relative_to(surface->surface, parent, |
|
||||||
x, y, false); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_destroy(struct weston_desktop_surface *dsurface, |
|
||||||
void *user_data) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = user_data; |
|
||||||
|
|
||||||
wl_list_remove(&surface->wl_surface_resource_destroy_listener.link); |
|
||||||
|
|
||||||
weston_desktop_wl_shell_surface_maybe_ungrab(surface); |
|
||||||
weston_desktop_surface_unset_relative_to(surface->surface); |
|
||||||
if (surface->added) |
|
||||||
weston_desktop_api_surface_removed(surface->desktop, |
|
||||||
surface->surface); |
|
||||||
|
|
||||||
free(surface); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_pong(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
uint32_t serial) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *surface = wl_resource_get_user_data(resource); |
|
||||||
|
|
||||||
weston_desktop_client_pong(weston_desktop_surface_get_client(surface), serial); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_move(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
struct wl_resource *seat_resource, |
|
||||||
uint32_t serial) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_seat *seat = |
|
||||||
wl_resource_get_user_data(seat_resource); |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
|
|
||||||
if (seat == NULL) |
|
||||||
return; |
|
||||||
|
|
||||||
weston_desktop_api_move(surface->desktop, dsurface, seat, serial); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_resize(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
struct wl_resource *seat_resource, |
|
||||||
uint32_t serial, |
|
||||||
enum wl_shell_surface_resize edges) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_seat *seat = wl_resource_get_user_data(seat_resource); |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
enum weston_desktop_surface_edge surf_edges = |
|
||||||
(enum weston_desktop_surface_edge) edges; |
|
||||||
|
|
||||||
if (seat == NULL) |
|
||||||
return; |
|
||||||
|
|
||||||
weston_desktop_api_resize(surface->desktop, dsurface, seat, serial, surf_edges); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_toplevel(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
|
|
||||||
weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL, 0, 0); |
|
||||||
if (surface->parent == NULL) |
|
||||||
return; |
|
||||||
surface->parent = NULL; |
|
||||||
weston_desktop_api_set_parent(surface->desktop, surface->surface, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_transient(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
struct wl_resource *parent_resource, |
|
||||||
int32_t x, int32_t y, |
|
||||||
enum wl_shell_surface_transient flags) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_surface *wparent = |
|
||||||
wl_resource_get_user_data(parent_resource); |
|
||||||
struct weston_desktop_surface *parent; |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
|
|
||||||
if (!weston_surface_is_desktop_surface(wparent)) |
|
||||||
return; |
|
||||||
|
|
||||||
parent = weston_surface_get_desktop_surface(wparent); |
|
||||||
if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) { |
|
||||||
weston_desktop_wl_shell_change_state(surface, TRANSIENT, parent, |
|
||||||
x, y); |
|
||||||
} else { |
|
||||||
weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL, |
|
||||||
0, 0); |
|
||||||
surface->parent = parent; |
|
||||||
weston_desktop_api_set_parent(surface->desktop, |
|
||||||
surface->surface, parent); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_fullscreen(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
enum wl_shell_surface_fullscreen_method method, |
|
||||||
uint32_t framerate, |
|
||||||
struct wl_resource *output_resource) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
struct weston_output *output = NULL; |
|
||||||
|
|
||||||
if (output_resource != NULL) |
|
||||||
output = weston_head_from_resource(output_resource)->output; |
|
||||||
|
|
||||||
weston_desktop_wl_shell_change_state(surface, FULLSCREEN, NULL, 0, 0); |
|
||||||
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface, |
|
||||||
true, output); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_popup(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
struct wl_resource *seat_resource, |
|
||||||
uint32_t serial, |
|
||||||
struct wl_resource *parent_resource, |
|
||||||
int32_t x, int32_t y, |
|
||||||
enum wl_shell_surface_transient flags) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_seat *wseat = wl_resource_get_user_data(seat_resource); |
|
||||||
struct weston_desktop_seat *seat = weston_desktop_seat_from_seat(wseat); |
|
||||||
struct weston_surface *parent = |
|
||||||
wl_resource_get_user_data(parent_resource); |
|
||||||
struct weston_desktop_surface *parent_surface; |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
|
|
||||||
/* Check that if we have a valid wseat we also got a valid desktop seat */ |
|
||||||
if (wseat != NULL && seat == NULL) { |
|
||||||
wl_client_post_no_memory(wl_client); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if (!weston_surface_is_desktop_surface(parent)) |
|
||||||
return; |
|
||||||
|
|
||||||
parent_surface = weston_surface_get_desktop_surface(parent); |
|
||||||
|
|
||||||
weston_desktop_wl_shell_change_state(surface, POPUP, |
|
||||||
parent_surface, x, y); |
|
||||||
weston_desktop_surface_popup_grab(surface->surface, seat, serial); |
|
||||||
surface->popup_seat = seat; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_maximized(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
struct wl_resource *output_resource) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *dsurface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
weston_desktop_surface_get_implementation_data(dsurface); |
|
||||||
|
|
||||||
weston_desktop_wl_shell_change_state(surface, MAXIMIZED, NULL, 0, 0); |
|
||||||
weston_desktop_api_maximized_requested(surface->desktop, dsurface, true); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_title(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
const char *title) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *surface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
|
|
||||||
weston_desktop_surface_set_title(surface, title); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_surface_protocol_set_class(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
const char *class_) |
|
||||||
{ |
|
||||||
struct weston_desktop_surface *surface = |
|
||||||
wl_resource_get_user_data(resource); |
|
||||||
|
|
||||||
weston_desktop_surface_set_app_id(surface, class_); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
static const struct wl_shell_surface_interface weston_desktop_wl_shell_surface_implementation = { |
|
||||||
.pong = weston_desktop_wl_shell_surface_protocol_pong, |
|
||||||
.move = weston_desktop_wl_shell_surface_protocol_move, |
|
||||||
.resize = weston_desktop_wl_shell_surface_protocol_resize, |
|
||||||
.set_toplevel = weston_desktop_wl_shell_surface_protocol_set_toplevel, |
|
||||||
.set_transient = weston_desktop_wl_shell_surface_protocol_set_transient, |
|
||||||
.set_fullscreen = weston_desktop_wl_shell_surface_protocol_set_fullscreen, |
|
||||||
.set_popup = weston_desktop_wl_shell_surface_protocol_set_popup, |
|
||||||
.set_maximized = weston_desktop_wl_shell_surface_protocol_set_maximized, |
|
||||||
.set_title = weston_desktop_wl_shell_surface_protocol_set_title, |
|
||||||
.set_class = weston_desktop_wl_shell_surface_protocol_set_class, |
|
||||||
}; |
|
||||||
|
|
||||||
static const struct weston_desktop_surface_implementation weston_desktop_wl_shell_surface_internal_implementation = { |
|
||||||
.set_size = weston_desktop_wl_shell_surface_set_size, |
|
||||||
.committed = weston_desktop_wl_shell_surface_committed, |
|
||||||
.ping = weston_desktop_wl_shell_surface_ping, |
|
||||||
.close = weston_desktop_wl_shell_surface_close, |
|
||||||
|
|
||||||
.get_maximized = weston_desktop_wl_shell_surface_get_maximized, |
|
||||||
.get_fullscreen = weston_desktop_wl_shell_surface_get_fullscreen, |
|
||||||
|
|
||||||
.destroy = weston_desktop_wl_shell_surface_destroy, |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
wl_surface_resource_destroyed(struct wl_listener *listener, |
|
||||||
void *data) |
|
||||||
{ |
|
||||||
struct weston_desktop_wl_shell_surface *surface = |
|
||||||
wl_container_of(listener, surface, |
|
||||||
wl_surface_resource_destroy_listener); |
|
||||||
|
|
||||||
/* the wl_shell_surface spec says that wl_shell_surfaces are to be
|
|
||||||
* destroyed automatically when the wl_surface is destroyed. */ |
|
||||||
weston_desktop_surface_destroy(surface->surface); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_protocol_get_shell_surface(struct wl_client *wl_client, |
|
||||||
struct wl_resource *resource, |
|
||||||
uint32_t id, |
|
||||||
struct wl_resource *surface_resource) |
|
||||||
{ |
|
||||||
struct weston_desktop_client *client = wl_resource_get_user_data(resource); |
|
||||||
struct weston_surface *wsurface = wl_resource_get_user_data(surface_resource); |
|
||||||
struct weston_desktop_wl_shell_surface *surface; |
|
||||||
|
|
||||||
|
|
||||||
if (weston_surface_set_role(wsurface, "wl_shell_surface", resource, WL_SHELL_ERROR_ROLE) < 0) |
|
||||||
return; |
|
||||||
|
|
||||||
surface = zalloc(sizeof(struct weston_desktop_wl_shell_surface)); |
|
||||||
if (surface == NULL) { |
|
||||||
wl_client_post_no_memory(wl_client); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
surface->desktop = weston_desktop_client_get_desktop(client); |
|
||||||
surface->display = weston_desktop_get_display(surface->desktop); |
|
||||||
|
|
||||||
surface->surface = |
|
||||||
weston_desktop_surface_create(surface->desktop, client, wsurface, |
|
||||||
&weston_desktop_wl_shell_surface_internal_implementation, |
|
||||||
surface); |
|
||||||
if (surface->surface == NULL) { |
|
||||||
free(surface); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
surface->wl_surface_resource_destroy_listener.notify = |
|
||||||
wl_surface_resource_destroyed; |
|
||||||
wl_resource_add_destroy_listener(wsurface->resource, |
|
||||||
&surface->wl_surface_resource_destroy_listener); |
|
||||||
|
|
||||||
surface->resource = |
|
||||||
weston_desktop_surface_add_resource(surface->surface, |
|
||||||
&wl_shell_surface_interface, |
|
||||||
&weston_desktop_wl_shell_surface_implementation, |
|
||||||
id, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
static const struct wl_shell_interface weston_desktop_wl_shell_implementation = { |
|
||||||
.get_shell_surface = weston_desktop_wl_shell_protocol_get_shell_surface, |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
weston_desktop_wl_shell_bind(struct wl_client *client, void *data, |
|
||||||
uint32_t version, uint32_t id) |
|
||||||
{ |
|
||||||
struct weston_desktop *desktop = data; |
|
||||||
|
|
||||||
weston_desktop_client_create(desktop, client, NULL, &wl_shell_interface, |
|
||||||
&weston_desktop_wl_shell_implementation, |
|
||||||
version, id); |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_global * |
|
||||||
weston_desktop_wl_shell_create(struct weston_desktop *desktop, |
|
||||||
struct wl_display *display) |
|
||||||
{ |
|
||||||
return wl_global_create(display, |
|
||||||
&wl_shell_interface, |
|
||||||
WD_WL_SHELL_PROTOCOL_VERSION, desktop, |
|
||||||
weston_desktop_wl_shell_bind); |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,178 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2021-2022 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 <libweston/libweston.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <string.h> |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
#include "drm-internal.h" |
||||||
|
|
||||||
|
static inline uint16_t |
||||||
|
color_xy_to_u16(float v) |
||||||
|
{ |
||||||
|
assert(v >= 0.0f); |
||||||
|
assert(v <= 1.0f); |
||||||
|
/*
|
||||||
|
* CTA-861-G |
||||||
|
* 6.9.1 Static Metadata Type 1 |
||||||
|
* chromaticity coordinate encoding |
||||||
|
*/ |
||||||
|
return (uint16_t)round(v * 50000.0); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint16_t |
||||||
|
nits_to_u16(float nits) |
||||||
|
{ |
||||||
|
assert(nits >= 1.0f); |
||||||
|
assert(nits <= 65535.0f); |
||||||
|
/*
|
||||||
|
* CTA-861-G |
||||||
|
* 6.9.1 Static Metadata Type 1 |
||||||
|
* max display mastering luminance, max content light level, |
||||||
|
* max frame-average light level |
||||||
|
*/ |
||||||
|
return (uint16_t)round(nits); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint16_t |
||||||
|
nits_to_u16_dark(float nits) |
||||||
|
{ |
||||||
|
assert(nits >= 0.0001f); |
||||||
|
assert(nits <= 6.5535f); |
||||||
|
/*
|
||||||
|
* CTA-861-G |
||||||
|
* 6.9.1 Static Metadata Type 1 |
||||||
|
* min display mastering luminance |
||||||
|
*/ |
||||||
|
return (uint16_t)round(nits * 10000.0); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst, |
||||||
|
const struct weston_hdr_metadata_type1 *src) |
||||||
|
{ |
||||||
|
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES) { |
||||||
|
unsigned i; |
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) { |
||||||
|
dst->display_primaries[i].x = color_xy_to_u16(src->primary[i].x); |
||||||
|
dst->display_primaries[i].y = color_xy_to_u16(src->primary[i].y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_WHITE) { |
||||||
|
dst->white_point.x = color_xy_to_u16(src->white.x); |
||||||
|
dst->white_point.y = color_xy_to_u16(src->white.y); |
||||||
|
} |
||||||
|
|
||||||
|
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML) |
||||||
|
dst->max_display_mastering_luminance = nits_to_u16(src->maxDML); |
||||||
|
|
||||||
|
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MINDML) |
||||||
|
dst->min_display_mastering_luminance = nits_to_u16_dark(src->minDML); |
||||||
|
|
||||||
|
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL) |
||||||
|
dst->max_cll = nits_to_u16(src->maxCLL); |
||||||
|
|
||||||
|
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL) |
||||||
|
dst->max_fall = nits_to_u16(src->maxFALL); |
||||||
|
} |
||||||
|
|
||||||
|
int |
||||||
|
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) |
||||||
|
{ |
||||||
|
struct drm_device *device = output->device; |
||||||
|
const struct weston_hdr_metadata_type1 *src; |
||||||
|
struct hdr_output_metadata meta; |
||||||
|
uint32_t blob_id = 0; |
||||||
|
int ret; |
||||||
|
|
||||||
|
if (output->hdr_output_metadata_blob_id && |
||||||
|
output->ackd_color_outcome_serial == output->base.color_outcome_serial) |
||||||
|
return 0; |
||||||
|
|
||||||
|
src = weston_output_get_hdr_metadata_type1(&output->base); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the data for Dynamic Range and Mastering InfoFrame, |
||||||
|
* CTA-861-G, a.k.a the static HDR metadata. |
||||||
|
*/ |
||||||
|
|
||||||
|
memset(&meta, 0, sizeof meta); |
||||||
|
|
||||||
|
meta.metadata_type = 0; /* Static Metadata Type 1 */ |
||||||
|
|
||||||
|
/* Duplicated field in UABI struct */ |
||||||
|
meta.hdmi_metadata_type1.metadata_type = meta.metadata_type; |
||||||
|
|
||||||
|
switch (output->base.eotf_mode) { |
||||||
|
case WESTON_EOTF_MODE_NONE: |
||||||
|
assert(0 && "bad eotf_mode: none"); |
||||||
|
return -1; |
||||||
|
case WESTON_EOTF_MODE_SDR: |
||||||
|
/*
|
||||||
|
* Do not send any static HDR metadata. Video sinks should |
||||||
|
* respond by switching to traditional SDR mode. If they |
||||||
|
* do not, the kernel should fix that up. |
||||||
|
*/ |
||||||
|
assert(output->hdr_output_metadata_blob_id == 0); |
||||||
|
return 0; |
||||||
|
case WESTON_EOTF_MODE_TRADITIONAL_HDR: |
||||||
|
meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */ |
||||||
|
break; |
||||||
|
case WESTON_EOTF_MODE_ST2084: |
||||||
|
meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */ |
||||||
|
weston_hdr_metadata_type1_to_kms(&meta.hdmi_metadata_type1, src); |
||||||
|
break; |
||||||
|
case WESTON_EOTF_MODE_HLG: |
||||||
|
meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */ |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (meta.hdmi_metadata_type1.eotf == 0) { |
||||||
|
assert(0 && "bad eotf_mode"); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
ret = drmModeCreatePropertyBlob(device->drm.fd, |
||||||
|
&meta, sizeof meta, &blob_id); |
||||||
|
if (ret != 0) { |
||||||
|
weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n", |
||||||
|
output->base.name, strerror(-ret)); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
drmModeDestroyPropertyBlob(device->drm.fd, |
||||||
|
output->hdr_output_metadata_blob_id); |
||||||
|
|
||||||
|
output->hdr_output_metadata_blob_id = blob_id; |
||||||
|
output->ackd_color_outcome_serial = output->base.color_outcome_serial; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -1,998 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2008-2011 Kristian Høgsberg |
|
||||||
* Copyright © 2011 Intel Corporation |
|
||||||
* Copyright © 2012 Raspberry Pi Foundation |
|
||||||
* Copyright © 2013 Philip Withnall |
|
||||||
* |
|
||||||
* 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 <errno.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <math.h> |
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <linux/fb.h> |
|
||||||
#include <linux/input.h> |
|
||||||
#include <assert.h> |
|
||||||
|
|
||||||
#include <libudev.h> |
|
||||||
|
|
||||||
#include "shared/helpers.h" |
|
||||||
#include <libweston/libweston.h> |
|
||||||
#include <libweston/backend-fbdev.h> |
|
||||||
#include "launcher-util.h" |
|
||||||
#include "pixman-renderer.h" |
|
||||||
#include "libinput-seat.h" |
|
||||||
#include "presentation-time-server-protocol.h" |
|
||||||
|
|
||||||
struct fbdev_backend { |
|
||||||
struct weston_backend base; |
|
||||||
struct weston_compositor *compositor; |
|
||||||
uint32_t prev_state; |
|
||||||
|
|
||||||
struct udev *udev; |
|
||||||
struct udev_input input; |
|
||||||
uint32_t output_transform; |
|
||||||
struct wl_listener session_listener; |
|
||||||
}; |
|
||||||
|
|
||||||
struct fbdev_screeninfo { |
|
||||||
unsigned int x_resolution; /* pixels, visible area */ |
|
||||||
unsigned int y_resolution; /* pixels, visible area */ |
|
||||||
unsigned int width_mm; /* visible screen width in mm */ |
|
||||||
unsigned int height_mm; /* visible screen height in mm */ |
|
||||||
unsigned int bits_per_pixel; |
|
||||||
|
|
||||||
size_t buffer_length; /* length of frame buffer memory in bytes */ |
|
||||||
size_t line_length; /* length of a line in bytes */ |
|
||||||
char id[16]; /* screen identifier */ |
|
||||||
|
|
||||||
pixman_format_code_t pixel_format; /* frame buffer pixel format */ |
|
||||||
unsigned int refresh_rate; /* Hertz */ |
|
||||||
}; |
|
||||||
|
|
||||||
struct fbdev_head { |
|
||||||
struct weston_head base; |
|
||||||
|
|
||||||
/* Frame buffer details. */ |
|
||||||
char *device; |
|
||||||
struct fbdev_screeninfo fb_info; |
|
||||||
}; |
|
||||||
|
|
||||||
struct fbdev_output { |
|
||||||
struct fbdev_backend *backend; |
|
||||||
struct weston_output base; |
|
||||||
|
|
||||||
struct weston_mode mode; |
|
||||||
struct wl_event_source *finish_frame_timer; |
|
||||||
|
|
||||||
/* framebuffer mmap details */ |
|
||||||
size_t buffer_length; |
|
||||||
void *fb; |
|
||||||
|
|
||||||
/* pixman details. */ |
|
||||||
pixman_image_t *hw_surface; |
|
||||||
}; |
|
||||||
|
|
||||||
static const char default_seat[] = "seat0"; |
|
||||||
|
|
||||||
static inline struct fbdev_head * |
|
||||||
to_fbdev_head(struct weston_head *base) |
|
||||||
{ |
|
||||||
return container_of(base, struct fbdev_head, base); |
|
||||||
} |
|
||||||
|
|
||||||
static inline struct fbdev_output * |
|
||||||
to_fbdev_output(struct weston_output *base) |
|
||||||
{ |
|
||||||
return container_of(base, struct fbdev_output, base); |
|
||||||
} |
|
||||||
|
|
||||||
static inline struct fbdev_backend * |
|
||||||
to_fbdev_backend(struct weston_compositor *base) |
|
||||||
{ |
|
||||||
return container_of(base->backend, struct fbdev_backend, base); |
|
||||||
} |
|
||||||
|
|
||||||
static struct fbdev_head * |
|
||||||
fbdev_output_get_head(struct fbdev_output *output) |
|
||||||
{ |
|
||||||
if (wl_list_length(&output->base.head_list) != 1) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
return container_of(output->base.head_list.next, |
|
||||||
struct fbdev_head, base.output_link); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_output_start_repaint_loop(struct weston_output *output) |
|
||||||
{ |
|
||||||
struct timespec ts; |
|
||||||
|
|
||||||
weston_compositor_read_presentation_clock(output->compositor, &ts); |
|
||||||
weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage, |
|
||||||
void *repaint_data) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = to_fbdev_output(base); |
|
||||||
struct weston_compositor *ec = output->base.compositor; |
|
||||||
|
|
||||||
/* Repaint the damaged region onto the back buffer. */ |
|
||||||
pixman_renderer_output_set_buffer(base, output->hw_surface); |
|
||||||
ec->renderer->repaint_output(base, damage); |
|
||||||
|
|
||||||
/* Update the damage region. */ |
|
||||||
pixman_region32_subtract(&ec->primary_plane.damage, |
|
||||||
&ec->primary_plane.damage, damage); |
|
||||||
|
|
||||||
/* Schedule the end of the frame. We do not sync this to the frame
|
|
||||||
* buffer clock because users who want that should be using the DRM |
|
||||||
* compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires |
|
||||||
* panning, which is broken in most kernel drivers. |
|
||||||
* |
|
||||||
* Finish the frame synchronised to the specified refresh rate. The |
|
||||||
* refresh rate is given in mHz and the interval in ms. */ |
|
||||||
wl_event_source_timer_update(output->finish_frame_timer, |
|
||||||
1000000 / output->mode.refresh); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
finish_frame_handler(void *data) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = data; |
|
||||||
struct timespec ts; |
|
||||||
|
|
||||||
weston_compositor_read_presentation_clock(output->base.compositor, &ts); |
|
||||||
weston_output_finish_frame(&output->base, &ts, 0); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
static pixman_format_code_t |
|
||||||
calculate_pixman_format(struct fb_var_screeninfo *vinfo, |
|
||||||
struct fb_fix_screeninfo *finfo) |
|
||||||
{ |
|
||||||
/* Calculate the pixman format supported by the frame buffer from the
|
|
||||||
* buffer's metadata. Return 0 if no known pixman format is supported |
|
||||||
* (since this has depth 0 it's guaranteed to not conflict with any |
|
||||||
* actual pixman format). |
|
||||||
* |
|
||||||
* Documentation on the vinfo and finfo structures: |
|
||||||
* http://www.mjmwired.net/kernel/Documentation/fb/api.txt
|
|
||||||
* |
|
||||||
* TODO: Try a bit harder to support other formats, including setting |
|
||||||
* the preferred format in the hardware. */ |
|
||||||
int type; |
|
||||||
|
|
||||||
weston_log("Calculating pixman format from:\n" |
|
||||||
STAMP_SPACE " - type: %i (aux: %i)\n" |
|
||||||
STAMP_SPACE " - visual: %i\n" |
|
||||||
STAMP_SPACE " - bpp: %i (grayscale: %i)\n" |
|
||||||
STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n" |
|
||||||
STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n" |
|
||||||
STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n" |
|
||||||
STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n", |
|
||||||
finfo->type, finfo->type_aux, finfo->visual, |
|
||||||
vinfo->bits_per_pixel, vinfo->grayscale, |
|
||||||
vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right, |
|
||||||
vinfo->green.offset, vinfo->green.length, |
|
||||||
vinfo->green.msb_right, |
|
||||||
vinfo->blue.offset, vinfo->blue.length, |
|
||||||
vinfo->blue.msb_right, |
|
||||||
vinfo->transp.offset, vinfo->transp.length, |
|
||||||
vinfo->transp.msb_right); |
|
||||||
|
|
||||||
/* We only handle packed formats at the moment. */ |
|
||||||
if (finfo->type != FB_TYPE_PACKED_PIXELS) |
|
||||||
return 0; |
|
||||||
|
|
||||||
/* We only handle true-colour frame buffers at the moment. */ |
|
||||||
switch(finfo->visual) { |
|
||||||
case FB_VISUAL_TRUECOLOR: |
|
||||||
case FB_VISUAL_DIRECTCOLOR: |
|
||||||
if (vinfo->grayscale != 0) |
|
||||||
return 0; |
|
||||||
break; |
|
||||||
default: |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
/* We only support formats with MSBs on the left. */ |
|
||||||
if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 || |
|
||||||
vinfo->blue.msb_right != 0) |
|
||||||
return 0; |
|
||||||
|
|
||||||
/* Work out the format type from the offsets. We only support RGBA, ARGB
|
|
||||||
* and ABGR at the moment. */ |
|
||||||
type = PIXMAN_TYPE_OTHER; |
|
||||||
|
|
||||||
if ((vinfo->transp.offset >= vinfo->red.offset || |
|
||||||
vinfo->transp.length == 0) && |
|
||||||
vinfo->red.offset >= vinfo->green.offset && |
|
||||||
vinfo->green.offset >= vinfo->blue.offset) |
|
||||||
type = PIXMAN_TYPE_ARGB; |
|
||||||
else if (vinfo->red.offset >= vinfo->green.offset && |
|
||||||
vinfo->green.offset >= vinfo->blue.offset && |
|
||||||
vinfo->blue.offset >= vinfo->transp.offset) |
|
||||||
type = PIXMAN_TYPE_RGBA; |
|
||||||
else if (vinfo->transp.offset >= vinfo->blue.offset && |
|
||||||
vinfo->blue.offset >= vinfo->green.offset && |
|
||||||
vinfo->green.offset >= vinfo->red.offset) |
|
||||||
type = PIXMAN_TYPE_ABGR; |
|
||||||
|
|
||||||
if (type == PIXMAN_TYPE_OTHER) |
|
||||||
return 0; |
|
||||||
|
|
||||||
/* Build the format. */ |
|
||||||
return PIXMAN_FORMAT(vinfo->bits_per_pixel, type, |
|
||||||
vinfo->transp.length, |
|
||||||
vinfo->red.length, |
|
||||||
vinfo->green.length, |
|
||||||
vinfo->blue.length); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
calculate_refresh_rate(struct fb_var_screeninfo *vinfo) |
|
||||||
{ |
|
||||||
uint64_t quot; |
|
||||||
|
|
||||||
/* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */ |
|
||||||
quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres); |
|
||||||
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres); |
|
||||||
quot *= vinfo->pixclock; |
|
||||||
|
|
||||||
if (quot > 0) { |
|
||||||
uint64_t refresh_rate; |
|
||||||
|
|
||||||
refresh_rate = 1000000000000000LLU / quot; |
|
||||||
if (refresh_rate > 200000) |
|
||||||
refresh_rate = 200000; /* cap at 200 Hz */ |
|
||||||
|
|
||||||
if (refresh_rate >= 1000) /* at least 1 Hz */ |
|
||||||
return refresh_rate; |
|
||||||
} |
|
||||||
|
|
||||||
return 60 * 1000; /* default to 60 Hz */ |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info) |
|
||||||
{ |
|
||||||
struct fb_var_screeninfo varinfo; |
|
||||||
struct fb_fix_screeninfo fixinfo; |
|
||||||
|
|
||||||
/* Probe the device for screen information. */ |
|
||||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 || |
|
||||||
ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Store the pertinent data. */ |
|
||||||
info->x_resolution = varinfo.xres; |
|
||||||
info->y_resolution = varinfo.yres; |
|
||||||
info->width_mm = varinfo.width; |
|
||||||
info->height_mm = varinfo.height; |
|
||||||
info->bits_per_pixel = varinfo.bits_per_pixel; |
|
||||||
|
|
||||||
info->buffer_length = fixinfo.smem_len; |
|
||||||
info->line_length = fixinfo.line_length; |
|
||||||
strncpy(info->id, fixinfo.id, sizeof(info->id)); |
|
||||||
info->id[sizeof(info->id)-1] = '\0'; |
|
||||||
|
|
||||||
info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo); |
|
||||||
info->refresh_rate = calculate_refresh_rate(&varinfo); |
|
||||||
|
|
||||||
if (info->pixel_format == 0) { |
|
||||||
weston_log("Frame buffer uses an unsupported format.\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info) |
|
||||||
{ |
|
||||||
struct fb_var_screeninfo varinfo; |
|
||||||
|
|
||||||
/* Grab the current screen information. */ |
|
||||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Update the information. */ |
|
||||||
varinfo.xres = info->x_resolution; |
|
||||||
varinfo.yres = info->y_resolution; |
|
||||||
varinfo.width = info->width_mm; |
|
||||||
varinfo.height = info->height_mm; |
|
||||||
varinfo.bits_per_pixel = info->bits_per_pixel; |
|
||||||
|
|
||||||
/* Try to set up an ARGB (x8r8g8b8) pixel format. */ |
|
||||||
varinfo.grayscale = 0; |
|
||||||
varinfo.transp.offset = 24; |
|
||||||
varinfo.transp.length = 0; |
|
||||||
varinfo.transp.msb_right = 0; |
|
||||||
varinfo.red.offset = 16; |
|
||||||
varinfo.red.length = 8; |
|
||||||
varinfo.red.msb_right = 0; |
|
||||||
varinfo.green.offset = 8; |
|
||||||
varinfo.green.length = 8; |
|
||||||
varinfo.green.msb_right = 0; |
|
||||||
varinfo.blue.offset = 0; |
|
||||||
varinfo.blue.length = 8; |
|
||||||
varinfo.blue.msb_right = 0; |
|
||||||
|
|
||||||
/* Set the device's screen information. */ |
|
||||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_wakeup_screen(int fd, struct fbdev_screeninfo *info) |
|
||||||
{ |
|
||||||
struct fb_var_screeninfo varinfo; |
|
||||||
|
|
||||||
/* Grab the current screen information. */ |
|
||||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* force the framebuffer to wake up */ |
|
||||||
varinfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; |
|
||||||
|
|
||||||
/* Set the device's screen information. */ |
|
||||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Returns an FD for the frame buffer device. */ |
|
||||||
static int |
|
||||||
fbdev_frame_buffer_open(const char *fb_dev, |
|
||||||
struct fbdev_screeninfo *screen_info) |
|
||||||
{ |
|
||||||
int fd = -1; |
|
||||||
|
|
||||||
weston_log("Opening fbdev frame buffer.\n"); |
|
||||||
|
|
||||||
/* Open the frame buffer device. */ |
|
||||||
fd = open(fb_dev, O_RDWR | O_CLOEXEC); |
|
||||||
if (fd < 0) { |
|
||||||
weston_log("Failed to open frame buffer device ‘%s’: %s\n", |
|
||||||
fb_dev, strerror(errno)); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Grab the screen info. */ |
|
||||||
if (fbdev_query_screen_info(fd, screen_info) < 0) { |
|
||||||
weston_log("Failed to get frame buffer info: %s\n", |
|
||||||
strerror(errno)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Attempt to wake up the framebuffer device, needed for secondary
|
|
||||||
* framebuffer devices */ |
|
||||||
if (fbdev_wakeup_screen(fd, screen_info) < 0) { |
|
||||||
weston_log("Failed to activate framebuffer display. " |
|
||||||
"Attempting to open output anyway.\n"); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return fd; |
|
||||||
} |
|
||||||
|
|
||||||
/* Closes the FD on success or failure. */ |
|
||||||
static int |
|
||||||
fbdev_frame_buffer_map(struct fbdev_output *output, int fd) |
|
||||||
{ |
|
||||||
struct fbdev_head *head; |
|
||||||
int retval = -1; |
|
||||||
|
|
||||||
head = fbdev_output_get_head(output); |
|
||||||
|
|
||||||
weston_log("Mapping fbdev frame buffer.\n"); |
|
||||||
|
|
||||||
/* Map the frame buffer. Write-only mode, since we don't want to read
|
|
||||||
* anything back (because it's slow). */ |
|
||||||
output->buffer_length = head->fb_info.buffer_length; |
|
||||||
output->fb = mmap(NULL, output->buffer_length, |
|
||||||
PROT_WRITE, MAP_SHARED, fd, 0); |
|
||||||
if (output->fb == MAP_FAILED) { |
|
||||||
weston_log("Failed to mmap frame buffer: %s\n", |
|
||||||
strerror(errno)); |
|
||||||
output->fb = NULL; |
|
||||||
goto out_close; |
|
||||||
} |
|
||||||
|
|
||||||
/* Create a pixman image to wrap the memory mapped frame buffer. */ |
|
||||||
output->hw_surface = |
|
||||||
pixman_image_create_bits(head->fb_info.pixel_format, |
|
||||||
head->fb_info.x_resolution, |
|
||||||
head->fb_info.y_resolution, |
|
||||||
output->fb, |
|
||||||
head->fb_info.line_length); |
|
||||||
if (output->hw_surface == NULL) { |
|
||||||
weston_log("Failed to create surface for frame buffer.\n"); |
|
||||||
goto out_unmap; |
|
||||||
} |
|
||||||
|
|
||||||
/* Success! */ |
|
||||||
retval = 0; |
|
||||||
|
|
||||||
out_unmap: |
|
||||||
if (retval != 0 && output->fb != NULL) { |
|
||||||
munmap(output->fb, output->buffer_length); |
|
||||||
output->fb = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
out_close: |
|
||||||
if (fd >= 0) |
|
||||||
close(fd); |
|
||||||
|
|
||||||
return retval; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
fbdev_frame_buffer_unmap(struct fbdev_output *output) |
|
||||||
{ |
|
||||||
if (!output->fb) { |
|
||||||
assert(!output->hw_surface); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
weston_log("Unmapping fbdev frame buffer.\n"); |
|
||||||
|
|
||||||
if (output->hw_surface) |
|
||||||
pixman_image_unref(output->hw_surface); |
|
||||||
output->hw_surface = NULL; |
|
||||||
|
|
||||||
if (munmap(output->fb, output->buffer_length) < 0) |
|
||||||
weston_log("Failed to munmap frame buffer: %s\n", |
|
||||||
strerror(errno)); |
|
||||||
|
|
||||||
output->fb = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_output_attach_head(struct weston_output *output_base, |
|
||||||
struct weston_head *head_base) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = to_fbdev_output(output_base); |
|
||||||
struct fbdev_head *head = to_fbdev_head(head_base); |
|
||||||
|
|
||||||
/* Clones not supported. */ |
|
||||||
if (!wl_list_empty(&output->base.head_list)) |
|
||||||
return -1; |
|
||||||
|
|
||||||
/* only one static mode in list */ |
|
||||||
output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; |
|
||||||
output->mode.width = head->fb_info.x_resolution; |
|
||||||
output->mode.height = head->fb_info.y_resolution; |
|
||||||
output->mode.refresh = head->fb_info.refresh_rate; |
|
||||||
wl_list_init(&output->base.mode_list); |
|
||||||
wl_list_insert(&output->base.mode_list, &output->mode.link); |
|
||||||
output->base.current_mode = &output->mode; |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void fbdev_output_destroy(struct weston_output *base); |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_output_enable(struct weston_output *base) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = to_fbdev_output(base); |
|
||||||
struct fbdev_backend *backend = to_fbdev_backend(base->compositor); |
|
||||||
struct fbdev_head *head; |
|
||||||
int fb_fd; |
|
||||||
struct wl_event_loop *loop; |
|
||||||
const struct pixman_renderer_output_options options = { |
|
||||||
.use_shadow = true, |
|
||||||
}; |
|
||||||
|
|
||||||
head = fbdev_output_get_head(output); |
|
||||||
|
|
||||||
/* Create the frame buffer. */ |
|
||||||
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info); |
|
||||||
if (fb_fd < 0) { |
|
||||||
weston_log("Creating frame buffer failed.\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) { |
|
||||||
weston_log("Mapping frame buffer failed.\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
output->base.start_repaint_loop = fbdev_output_start_repaint_loop; |
|
||||||
output->base.repaint = fbdev_output_repaint; |
|
||||||
|
|
||||||
if (pixman_renderer_output_create(&output->base, &options) < 0) |
|
||||||
goto out_hw_surface; |
|
||||||
|
|
||||||
loop = wl_display_get_event_loop(backend->compositor->wl_display); |
|
||||||
output->finish_frame_timer = |
|
||||||
wl_event_loop_add_timer(loop, finish_frame_handler, output); |
|
||||||
|
|
||||||
weston_log("fbdev output %d×%d px\n", |
|
||||||
output->mode.width, output->mode.height); |
|
||||||
weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n", |
|
||||||
output->mode.refresh / 1000); |
|
||||||
|
|
||||||
return 0; |
|
||||||
|
|
||||||
out_hw_surface: |
|
||||||
fbdev_frame_buffer_unmap(output); |
|
||||||
|
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_output_disable(struct weston_output *base) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = to_fbdev_output(base); |
|
||||||
|
|
||||||
if (!base->enabled) |
|
||||||
return 0; |
|
||||||
|
|
||||||
wl_event_source_remove(output->finish_frame_timer); |
|
||||||
output->finish_frame_timer = NULL; |
|
||||||
|
|
||||||
pixman_renderer_output_destroy(&output->base); |
|
||||||
fbdev_frame_buffer_unmap(output); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static struct fbdev_head * |
|
||||||
fbdev_head_create(struct fbdev_backend *backend, const char *device) |
|
||||||
{ |
|
||||||
struct fbdev_head *head; |
|
||||||
int fb_fd; |
|
||||||
|
|
||||||
head = zalloc(sizeof *head); |
|
||||||
if (!head) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
head->device = strdup(device); |
|
||||||
|
|
||||||
/* Create the frame buffer. */ |
|
||||||
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info); |
|
||||||
if (fb_fd < 0) { |
|
||||||
weston_log("Creating frame buffer head failed.\n"); |
|
||||||
goto out_free; |
|
||||||
} |
|
||||||
close(fb_fd); |
|
||||||
|
|
||||||
weston_head_init(&head->base, "fbdev"); |
|
||||||
weston_head_set_connection_status(&head->base, true); |
|
||||||
weston_head_set_monitor_strings(&head->base, "unknown", |
|
||||||
head->fb_info.id, NULL); |
|
||||||
weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN); |
|
||||||
weston_head_set_physical_size(&head->base, head->fb_info.width_mm, |
|
||||||
head->fb_info.height_mm); |
|
||||||
|
|
||||||
weston_compositor_add_head(backend->compositor, &head->base); |
|
||||||
|
|
||||||
weston_log("Created head '%s' for device %s (%s)\n", |
|
||||||
head->base.name, head->device, head->base.model); |
|
||||||
|
|
||||||
return head; |
|
||||||
|
|
||||||
out_free: |
|
||||||
free(head->device); |
|
||||||
free(head); |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
fbdev_head_destroy(struct fbdev_head *head) |
|
||||||
{ |
|
||||||
weston_head_release(&head->base); |
|
||||||
free(head->device); |
|
||||||
free(head); |
|
||||||
} |
|
||||||
|
|
||||||
static struct weston_output * |
|
||||||
fbdev_output_create(struct weston_compositor *compositor, |
|
||||||
const char *name) |
|
||||||
{ |
|
||||||
struct fbdev_output *output; |
|
||||||
|
|
||||||
weston_log("Creating fbdev output.\n"); |
|
||||||
|
|
||||||
output = zalloc(sizeof *output); |
|
||||||
if (output == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
output->backend = to_fbdev_backend(compositor); |
|
||||||
|
|
||||||
weston_output_init(&output->base, compositor, name); |
|
||||||
|
|
||||||
output->base.destroy = fbdev_output_destroy; |
|
||||||
output->base.disable = fbdev_output_disable; |
|
||||||
output->base.enable = fbdev_output_enable; |
|
||||||
output->base.attach_head = fbdev_output_attach_head; |
|
||||||
|
|
||||||
weston_compositor_add_pending_output(&output->base, compositor); |
|
||||||
|
|
||||||
return &output->base; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
fbdev_output_destroy(struct weston_output *base) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = to_fbdev_output(base); |
|
||||||
|
|
||||||
weston_log("Destroying fbdev output.\n"); |
|
||||||
|
|
||||||
fbdev_output_disable(base); |
|
||||||
|
|
||||||
/* Remove the output. */ |
|
||||||
weston_output_release(&output->base); |
|
||||||
|
|
||||||
free(output); |
|
||||||
} |
|
||||||
|
|
||||||
/* strcmp()-style return values. */ |
|
||||||
static int |
|
||||||
compare_screen_info (const struct fbdev_screeninfo *a, |
|
||||||
const struct fbdev_screeninfo *b) |
|
||||||
{ |
|
||||||
if (a->x_resolution == b->x_resolution && |
|
||||||
a->y_resolution == b->y_resolution && |
|
||||||
a->width_mm == b->width_mm && |
|
||||||
a->height_mm == b->height_mm && |
|
||||||
a->bits_per_pixel == b->bits_per_pixel && |
|
||||||
a->pixel_format == b->pixel_format && |
|
||||||
a->refresh_rate == b->refresh_rate) |
|
||||||
return 0; |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
fbdev_output_reenable(struct fbdev_backend *backend, |
|
||||||
struct weston_output *base) |
|
||||||
{ |
|
||||||
struct fbdev_output *output = to_fbdev_output(base); |
|
||||||
struct fbdev_head *head; |
|
||||||
struct fbdev_screeninfo new_screen_info; |
|
||||||
int fb_fd; |
|
||||||
|
|
||||||
head = fbdev_output_get_head(output); |
|
||||||
|
|
||||||
weston_log("Re-enabling fbdev output.\n"); |
|
||||||
assert(output->base.enabled); |
|
||||||
|
|
||||||
/* Create the frame buffer. */ |
|
||||||
fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info); |
|
||||||
if (fb_fd < 0) { |
|
||||||
weston_log("Creating frame buffer failed.\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Check whether the frame buffer details have changed since we were
|
|
||||||
* disabled. */ |
|
||||||
if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) { |
|
||||||
/* Perform a mode-set to restore the old mode. */ |
|
||||||
if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) { |
|
||||||
weston_log("Failed to restore mode settings. " |
|
||||||
"Attempting to re-open output anyway.\n"); |
|
||||||
} |
|
||||||
|
|
||||||
close(fb_fd); |
|
||||||
|
|
||||||
/* Disable and enable the output so that resources depending on
|
|
||||||
* the frame buffer X/Y resolution (such as the shadow buffer) |
|
||||||
* are re-initialised. */ |
|
||||||
fbdev_output_disable(&output->base); |
|
||||||
return fbdev_output_enable(&output->base); |
|
||||||
} |
|
||||||
|
|
||||||
/* Map the device if it has the same details as before. */ |
|
||||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) { |
|
||||||
weston_log("Mapping frame buffer failed.\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
fbdev_backend_destroy(struct weston_compositor *base) |
|
||||||
{ |
|
||||||
struct fbdev_backend *backend = to_fbdev_backend(base); |
|
||||||
struct weston_head *head, *next; |
|
||||||
|
|
||||||
udev_input_destroy(&backend->input); |
|
||||||
|
|
||||||
/* Destroy the output. */ |
|
||||||
weston_compositor_shutdown(base); |
|
||||||
|
|
||||||
wl_list_for_each_safe(head, next, &base->head_list, compositor_link) |
|
||||||
fbdev_head_destroy(to_fbdev_head(head)); |
|
||||||
|
|
||||||
/* Chain up. */ |
|
||||||
weston_launcher_destroy(base->launcher); |
|
||||||
|
|
||||||
udev_unref(backend->udev); |
|
||||||
|
|
||||||
free(backend); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
session_notify(struct wl_listener *listener, void *data) |
|
||||||
{ |
|
||||||
struct weston_compositor *compositor = data; |
|
||||||
struct fbdev_backend *backend = to_fbdev_backend(compositor); |
|
||||||
struct weston_output *output; |
|
||||||
|
|
||||||
if (compositor->session_active) { |
|
||||||
weston_log("entering VT\n"); |
|
||||||
compositor->state = backend->prev_state; |
|
||||||
|
|
||||||
wl_list_for_each(output, &compositor->output_list, link) { |
|
||||||
fbdev_output_reenable(backend, output); |
|
||||||
} |
|
||||||
|
|
||||||
weston_compositor_damage_all(compositor); |
|
||||||
|
|
||||||
udev_input_enable(&backend->input); |
|
||||||
} else { |
|
||||||
weston_log("leaving VT\n"); |
|
||||||
udev_input_disable(&backend->input); |
|
||||||
|
|
||||||
wl_list_for_each(output, &compositor->output_list, link) { |
|
||||||
fbdev_frame_buffer_unmap(to_fbdev_output(output)); |
|
||||||
} |
|
||||||
|
|
||||||
backend->prev_state = compositor->state; |
|
||||||
weston_compositor_offscreen(compositor); |
|
||||||
|
|
||||||
/* If we have a repaint scheduled (from the idle handler), make
|
|
||||||
* sure we cancel that so we don't try to pageflip when we're |
|
||||||
* vt switched away. The OFFSCREEN state will prevent |
|
||||||
* further attempts at repainting. When we switch |
|
||||||
* back, we schedule a repaint, which will process |
|
||||||
* pending frame callbacks. */ |
|
||||||
|
|
||||||
wl_list_for_each(output, |
|
||||||
&compositor->output_list, link) { |
|
||||||
output->repaint_needed = false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static char * |
|
||||||
find_framebuffer_device(struct fbdev_backend *b, const char *seat) |
|
||||||
{ |
|
||||||
struct udev_enumerate *e; |
|
||||||
struct udev_list_entry *entry; |
|
||||||
const char *path, *device_seat, *id; |
|
||||||
char *fb_device_path = NULL; |
|
||||||
struct udev_device *device, *fb_device, *pci; |
|
||||||
|
|
||||||
e = udev_enumerate_new(b->udev); |
|
||||||
udev_enumerate_add_match_subsystem(e, "graphics"); |
|
||||||
udev_enumerate_add_match_sysname(e, "fb[0-9]*"); |
|
||||||
|
|
||||||
udev_enumerate_scan_devices(e); |
|
||||||
fb_device = NULL; |
|
||||||
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { |
|
||||||
bool is_boot_vga = false; |
|
||||||
|
|
||||||
path = udev_list_entry_get_name(entry); |
|
||||||
device = udev_device_new_from_syspath(b->udev, path); |
|
||||||
if (!device) |
|
||||||
continue; |
|
||||||
device_seat = udev_device_get_property_value(device, "ID_SEAT"); |
|
||||||
if (!device_seat) |
|
||||||
device_seat = default_seat; |
|
||||||
if (strcmp(device_seat, seat)) { |
|
||||||
udev_device_unref(device); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
pci = udev_device_get_parent_with_subsystem_devtype(device, |
|
||||||
"pci", NULL); |
|
||||||
if (pci) { |
|
||||||
id = udev_device_get_sysattr_value(pci, "boot_vga"); |
|
||||||
if (id && !strcmp(id, "1")) |
|
||||||
is_boot_vga = true; |
|
||||||
} |
|
||||||
|
|
||||||
/* If a framebuffer device was found, and this device isn't
|
|
||||||
* the boot-VGA device, don't use it. */ |
|
||||||
if (!is_boot_vga && fb_device) { |
|
||||||
udev_device_unref(device); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
/* There can only be one boot_vga device. Try to use it
|
|
||||||
* at all costs. */ |
|
||||||
if (is_boot_vga) { |
|
||||||
if (fb_device) |
|
||||||
udev_device_unref(fb_device); |
|
||||||
fb_device = device; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
/* Per the (!is_boot_vga && fb_device) test above, only
|
|
||||||
* trump existing saved devices with boot-VGA devices, so if |
|
||||||
* the test ends up here, this must be the first device seen. */ |
|
||||||
assert(!fb_device); |
|
||||||
fb_device = device; |
|
||||||
} |
|
||||||
|
|
||||||
udev_enumerate_unref(e); |
|
||||||
|
|
||||||
if (fb_device) { |
|
||||||
fb_device_path = strdup(udev_device_get_devnode(fb_device)); |
|
||||||
udev_device_unref(fb_device); |
|
||||||
} |
|
||||||
|
|
||||||
return fb_device_path; |
|
||||||
} |
|
||||||
|
|
||||||
static struct fbdev_backend * |
|
||||||
fbdev_backend_create(struct weston_compositor *compositor, |
|
||||||
struct weston_fbdev_backend_config *param) |
|
||||||
{ |
|
||||||
struct fbdev_backend *backend; |
|
||||||
const char *seat_id = default_seat; |
|
||||||
const char *session_seat; |
|
||||||
|
|
||||||
session_seat = getenv("XDG_SEAT"); |
|
||||||
if (session_seat) |
|
||||||
seat_id = session_seat; |
|
||||||
if (param->seat_id) |
|
||||||
seat_id = param->seat_id; |
|
||||||
|
|
||||||
weston_log("initializing fbdev backend\n"); |
|
||||||
weston_log("warning: the fbdev backend is deprecated, please migrate " |
|
||||||
"to the DRM backend\n"); |
|
||||||
|
|
||||||
backend = zalloc(sizeof *backend); |
|
||||||
if (backend == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
backend->compositor = compositor; |
|
||||||
compositor->backend = &backend->base; |
|
||||||
if (weston_compositor_set_presentation_clock_software( |
|
||||||
compositor) < 0) |
|
||||||
goto out_compositor; |
|
||||||
|
|
||||||
backend->udev = udev_new(); |
|
||||||
if (backend->udev == NULL) { |
|
||||||
weston_log("Failed to initialize udev context.\n"); |
|
||||||
goto out_compositor; |
|
||||||
} |
|
||||||
|
|
||||||
if (!param->device) |
|
||||||
param->device = find_framebuffer_device(backend, seat_id); |
|
||||||
if (!param->device) { |
|
||||||
weston_log("fatal: no framebuffer devices detected.\n"); |
|
||||||
goto out_udev; |
|
||||||
} |
|
||||||
|
|
||||||
/* Set up the TTY. */ |
|
||||||
backend->session_listener.notify = session_notify; |
|
||||||
wl_signal_add(&compositor->session_signal, |
|
||||||
&backend->session_listener); |
|
||||||
compositor->launcher = |
|
||||||
weston_launcher_connect(compositor, param->tty, seat_id, false); |
|
||||||
if (!compositor->launcher) { |
|
||||||
weston_log("fatal: fbdev backend should be run using " |
|
||||||
"weston-launch binary, or your system should " |
|
||||||
"provide the logind D-Bus API.\n"); |
|
||||||
goto out_udev; |
|
||||||
} |
|
||||||
|
|
||||||
backend->base.destroy = fbdev_backend_destroy; |
|
||||||
backend->base.create_output = fbdev_output_create; |
|
||||||
|
|
||||||
backend->prev_state = WESTON_COMPOSITOR_ACTIVE; |
|
||||||
|
|
||||||
weston_setup_vt_switch_bindings(compositor); |
|
||||||
|
|
||||||
if (pixman_renderer_init(compositor) < 0) |
|
||||||
goto out_launcher; |
|
||||||
|
|
||||||
if (!fbdev_head_create(backend, param->device)) |
|
||||||
goto out_launcher; |
|
||||||
|
|
||||||
free(param->device); |
|
||||||
|
|
||||||
udev_input_init(&backend->input, compositor, backend->udev, |
|
||||||
seat_id, param->configure_device); |
|
||||||
|
|
||||||
return backend; |
|
||||||
|
|
||||||
out_launcher: |
|
||||||
free(param->device); |
|
||||||
weston_launcher_destroy(compositor->launcher); |
|
||||||
|
|
||||||
out_udev: |
|
||||||
udev_unref(backend->udev); |
|
||||||
|
|
||||||
out_compositor: |
|
||||||
weston_compositor_shutdown(compositor); |
|
||||||
free(backend); |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
config_init_to_defaults(struct weston_fbdev_backend_config *config) |
|
||||||
{ |
|
||||||
config->tty = 0; /* default to current tty */ |
|
||||||
config->device = NULL; |
|
||||||
config->seat_id = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
weston_backend_init(struct weston_compositor *compositor, |
|
||||||
struct weston_backend_config *config_base) |
|
||||||
{ |
|
||||||
struct fbdev_backend *b; |
|
||||||
struct weston_fbdev_backend_config config = {{ 0, }}; |
|
||||||
|
|
||||||
if (config_base == NULL || |
|
||||||
config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION || |
|
||||||
config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) { |
|
||||||
weston_log("fbdev backend config structure is invalid\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
config_init_to_defaults(&config); |
|
||||||
memcpy(&config, config_base, config_base->struct_size); |
|
||||||
|
|
||||||
b = fbdev_backend_create(compositor, &config); |
|
||||||
if (b == NULL) |
|
||||||
return -1; |
|
||||||
return 0; |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
if not get_option('deprecated-backend-fbdev') |
|
||||||
subdir_done() |
|
||||||
endif |
|
||||||
|
|
||||||
warning('Support for the deprecated fbdev backend is enabled.') |
|
||||||
warning('This feature will be removed in a future version.') |
|
||||||
|
|
||||||
config_h.set('BUILD_FBDEV_COMPOSITOR', '1') |
|
||||||
|
|
||||||
srcs_fbdev = [ |
|
||||||
'fbdev.c', |
|
||||||
presentation_time_server_protocol_h, |
|
||||||
] |
|
||||||
|
|
||||||
deps_fbdev = [ |
|
||||||
dep_libweston_private, |
|
||||||
dep_session_helper, |
|
||||||
dep_libinput_backend, |
|
||||||
dependency('libudev', version: '>= 136'), |
|
||||||
] |
|
||||||
|
|
||||||
plugin_fbdev = shared_library( |
|
||||||
'fbdev-backend', |
|
||||||
srcs_fbdev, |
|
||||||
include_directories: common_inc, |
|
||||||
dependencies: deps_fbdev, |
|
||||||
name_prefix: '', |
|
||||||
install: true, |
|
||||||
install_dir: dir_module_libweston |
|
||||||
) |
|
||||||
env_modmap += 'fbdev-backend.so=@0@;'.format(plugin_fbdev.full_path()) |
|
||||||
|
|
||||||
install_headers(backend_fbdev_h, subdir: dir_include_libweston_install) |
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,263 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Hardening <rdp.effort@gmail.com> |
||||||
|
* Copyright © 2020 Microsoft |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef RDP_H |
||||||
|
#define RDP_H |
||||||
|
|
||||||
|
#include <freerdp/version.h> |
||||||
|
|
||||||
|
#include <freerdp/freerdp.h> |
||||||
|
#include <freerdp/listener.h> |
||||||
|
#include <freerdp/update.h> |
||||||
|
#include <freerdp/input.h> |
||||||
|
#include <freerdp/codec/color.h> |
||||||
|
#include <freerdp/codec/rfx.h> |
||||||
|
#include <freerdp/codec/nsc.h> |
||||||
|
#include <freerdp/locale/keyboard.h> |
||||||
|
#include <freerdp/channels/wtsvc.h> |
||||||
|
#include <freerdp/server/cliprdr.h> |
||||||
|
|
||||||
|
#include <libweston/libweston.h> |
||||||
|
#include <libweston/backend-rdp.h> |
||||||
|
#include <libweston/weston-log.h> |
||||||
|
|
||||||
|
#include "backend.h" |
||||||
|
|
||||||
|
#include "shared/helpers.h" |
||||||
|
#include "shared/string-helpers.h" |
||||||
|
|
||||||
|
#define MAX_FREERDP_FDS 32 |
||||||
|
#define DEFAULT_AXIS_STEP_DISTANCE 10 |
||||||
|
#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32 |
||||||
|
|
||||||
|
/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardtype
|
||||||
|
* defines a keyboard type that isn't currently defined in FreeRDP, but is |
||||||
|
* available for RDP connections */ |
||||||
|
#ifndef KBD_TYPE_KOREAN |
||||||
|
#define KBD_TYPE_KOREAN 8 |
||||||
|
#endif |
||||||
|
|
||||||
|
/* WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys */ |
||||||
|
/* 0x1f1 and 0x1f2 keys are only exists on Korean 103 keyboard (Type 8:SubType 6) */ |
||||||
|
|
||||||
|
/* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */ |
||||||
|
#define ATKBD_RET_HANJA 0xf1 |
||||||
|
#define ATKBD_RET_HANGEUL 0xf2 |
||||||
|
|
||||||
|
struct rdp_output; |
||||||
|
|
||||||
|
struct rdp_backend { |
||||||
|
struct weston_backend base; |
||||||
|
struct weston_compositor *compositor; |
||||||
|
|
||||||
|
freerdp_listener *listener; |
||||||
|
struct wl_event_source *listener_events[MAX_FREERDP_FDS]; |
||||||
|
struct rdp_output *output; |
||||||
|
struct weston_log_scope *debug; |
||||||
|
struct weston_log_scope *verbose; |
||||||
|
|
||||||
|
struct weston_log_scope *clipboard_debug; |
||||||
|
struct weston_log_scope *clipboard_verbose; |
||||||
|
|
||||||
|
struct wl_list peers; |
||||||
|
|
||||||
|
char *server_cert; |
||||||
|
char *server_key; |
||||||
|
char *rdp_key; |
||||||
|
int tls_enabled; |
||||||
|
int no_clients_resize; |
||||||
|
int force_no_compression; |
||||||
|
bool remotefx_codec; |
||||||
|
int external_listener_fd; |
||||||
|
int rdp_monitor_refresh_rate; |
||||||
|
pid_t compositor_tid; |
||||||
|
|
||||||
|
rdp_audio_in_setup audio_in_setup; |
||||||
|
rdp_audio_in_teardown audio_in_teardown; |
||||||
|
rdp_audio_out_setup audio_out_setup; |
||||||
|
rdp_audio_out_teardown audio_out_teardown; |
||||||
|
}; |
||||||
|
|
||||||
|
enum peer_item_flags { |
||||||
|
RDP_PEER_ACTIVATED = (1 << 0), |
||||||
|
RDP_PEER_OUTPUT_ENABLED = (1 << 1), |
||||||
|
}; |
||||||
|
|
||||||
|
struct rdp_peers_item { |
||||||
|
int flags; |
||||||
|
freerdp_peer *peer; |
||||||
|
struct weston_seat *seat; |
||||||
|
|
||||||
|
struct wl_list link; |
||||||
|
}; |
||||||
|
|
||||||
|
struct rdp_head { |
||||||
|
struct weston_head base; |
||||||
|
}; |
||||||
|
|
||||||
|
struct rdp_output { |
||||||
|
struct weston_output base; |
||||||
|
struct wl_event_source *finish_frame_timer; |
||||||
|
pixman_image_t *shadow_surface; |
||||||
|
}; |
||||||
|
|
||||||
|
struct rdp_peer_context { |
||||||
|
rdpContext _p; |
||||||
|
|
||||||
|
struct rdp_backend *rdpBackend; |
||||||
|
struct wl_event_source *events[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor */ |
||||||
|
RFX_CONTEXT *rfx_context; |
||||||
|
wStream *encode_stream; |
||||||
|
RFX_RECT *rfx_rects; |
||||||
|
NSC_CONTEXT *nsc_context; |
||||||
|
|
||||||
|
struct rdp_peers_item item; |
||||||
|
|
||||||
|
bool button_state[5]; |
||||||
|
|
||||||
|
int verticalAccumWheelRotationPrecise; |
||||||
|
int verticalAccumWheelRotationDiscrete; |
||||||
|
int horizontalAccumWheelRotationPrecise; |
||||||
|
int horizontalAccumWheelRotationDiscrete; |
||||||
|
|
||||||
|
HANDLE vcm; |
||||||
|
|
||||||
|
/* list of outstanding event_source sent from FreeRDP thread to display loop.*/ |
||||||
|
int loop_task_event_source_fd; |
||||||
|
struct wl_event_source *loop_task_event_source; |
||||||
|
pthread_mutex_t loop_task_list_mutex; |
||||||
|
struct wl_list loop_task_list; /* struct rdp_loop_task::link */ |
||||||
|
|
||||||
|
/* Clipboard support */ |
||||||
|
CliprdrServerContext *clipboard_server_context; |
||||||
|
|
||||||
|
void *audio_in_private; |
||||||
|
void *audio_out_private; |
||||||
|
|
||||||
|
struct rdp_clipboard_data_source *clipboard_client_data_source; |
||||||
|
struct rdp_clipboard_data_source *clipboard_inflight_client_data_source; |
||||||
|
|
||||||
|
struct wl_listener clipboard_selection_listener; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct rdp_peer_context RdpPeerContext; |
||||||
|
|
||||||
|
typedef void (*rdp_loop_task_func_t)(bool freeOnly, void *data); |
||||||
|
|
||||||
|
struct rdp_loop_task { |
||||||
|
struct wl_list link; |
||||||
|
RdpPeerContext *peerCtx; |
||||||
|
rdp_loop_task_func_t func; |
||||||
|
}; |
||||||
|
|
||||||
|
#define rdp_debug_verbose(b, ...) \ |
||||||
|
rdp_debug_print(b->verbose, false, __VA_ARGS__) |
||||||
|
#define rdp_debug_verbose_continue(b, ...) \ |
||||||
|
rdp_debug_print(b->verbose, true, __VA_ARGS__) |
||||||
|
#define rdp_debug(b, ...) \ |
||||||
|
rdp_debug_print(b->debug, false, __VA_ARGS__) |
||||||
|
#define rdp_debug_continue(b, ...) \ |
||||||
|
rdp_debug_print(b->debug, true, __VA_ARGS__) |
||||||
|
|
||||||
|
#define rdp_debug_clipboard_verbose(b, ...) \ |
||||||
|
rdp_debug_print(b->clipboard_verbose, false, __VA_ARGS__) |
||||||
|
#define rdp_debug_clipboard_verbose_continue(b, ...) \ |
||||||
|
rdp_debug_print(b->clipboard_verbose, true, __VA_ARGS__) |
||||||
|
#define rdp_debug_clipboard(b, ...) \ |
||||||
|
rdp_debug_print(b->clipboard_debug, false, __VA_ARGS__) |
||||||
|
#define rdp_debug_clipboard_continue(b, ...) \ |
||||||
|
rdp_debug_print(b->clipboard_debug, true, __VA_ARGS__) |
||||||
|
|
||||||
|
/* rdputil.c */ |
||||||
|
void |
||||||
|
rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...); |
||||||
|
|
||||||
|
int |
||||||
|
rdp_wl_array_read_fd(struct wl_array *array, int fd); |
||||||
|
|
||||||
|
void |
||||||
|
convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames); |
||||||
|
|
||||||
|
void |
||||||
|
assert_compositor_thread(struct rdp_backend *b); |
||||||
|
|
||||||
|
void |
||||||
|
assert_not_compositor_thread(struct rdp_backend *b); |
||||||
|
|
||||||
|
bool |
||||||
|
rdp_event_loop_add_fd(struct wl_event_loop *loop, |
||||||
|
int fd, uint32_t mask, |
||||||
|
wl_event_loop_fd_func_t func, |
||||||
|
void *data, |
||||||
|
struct wl_event_source **event_source); |
||||||
|
|
||||||
|
void |
||||||
|
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx, |
||||||
|
rdp_loop_task_func_t func, |
||||||
|
struct rdp_loop_task *task); |
||||||
|
|
||||||
|
bool |
||||||
|
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx); |
||||||
|
|
||||||
|
void |
||||||
|
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx); |
||||||
|
|
||||||
|
/* rdpclip.c */ |
||||||
|
int |
||||||
|
rdp_clipboard_init(freerdp_peer *client); |
||||||
|
|
||||||
|
void |
||||||
|
rdp_clipboard_destroy(RdpPeerContext *peerCtx); |
||||||
|
|
||||||
|
void |
||||||
|
rdp_head_destroy(struct weston_head *base); |
||||||
|
|
||||||
|
static inline struct rdp_head * |
||||||
|
to_rdp_head(struct weston_head *base) |
||||||
|
{ |
||||||
|
if (base->backend_id != rdp_head_destroy) |
||||||
|
return NULL; |
||||||
|
return container_of(base, struct rdp_head, base); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
rdp_output_destroy(struct weston_output *base); |
||||||
|
|
||||||
|
static inline struct rdp_output * |
||||||
|
to_rdp_output(struct weston_output *base) |
||||||
|
{ |
||||||
|
if (base->destroy != rdp_output_destroy) |
||||||
|
return NULL; |
||||||
|
return container_of(base, struct rdp_output, base); |
||||||
|
} |
||||||
|
|
||||||
|
static inline struct rdp_backend * |
||||||
|
to_rdp_backend(struct weston_compositor *base) |
||||||
|
{ |
||||||
|
return container_of(base->backend, struct rdp_backend, base); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,262 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Microsoft |
||||||
|
* |
||||||
|
* 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 <assert.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <pthread.h> |
||||||
|
#include <sys/mman.h> |
||||||
|
#include <sys/syscall.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/eventfd.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <fcntl.h> |
||||||
|
|
||||||
|
#include "rdp.h" |
||||||
|
|
||||||
|
static int cached_tm_mday = -1; |
||||||
|
|
||||||
|
void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...) |
||||||
|
{ |
||||||
|
char timestr[128]; |
||||||
|
int len_va; |
||||||
|
char *str; |
||||||
|
|
||||||
|
if (!log_scope || !weston_log_scope_is_enabled(log_scope)) |
||||||
|
return; |
||||||
|
|
||||||
|
va_list ap; |
||||||
|
va_start(ap, fmt); |
||||||
|
|
||||||
|
if (cont) { |
||||||
|
weston_log_scope_vprintf(log_scope, fmt, ap); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
|
||||||
|
weston_log_timestamp(timestr, sizeof(timestr), &cached_tm_mday); |
||||||
|
len_va = vasprintf(&str, fmt, ap); |
||||||
|
if (len_va >= 0) { |
||||||
|
weston_log_scope_printf(log_scope, "%s %s", |
||||||
|
timestr, str); |
||||||
|
free(str); |
||||||
|
} else { |
||||||
|
const char *oom = "Out of memory"; |
||||||
|
|
||||||
|
weston_log_scope_printf(log_scope, "%s %s", |
||||||
|
timestr, oom); |
||||||
|
} |
||||||
|
end: |
||||||
|
va_end(ap); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
assert_compositor_thread(struct rdp_backend *b) |
||||||
|
{ |
||||||
|
assert(b->compositor_tid == gettid()); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
assert_not_compositor_thread(struct rdp_backend *b) |
||||||
|
{ |
||||||
|
assert(b->compositor_tid != gettid()); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
rdp_event_loop_add_fd(struct wl_event_loop *loop, |
||||||
|
int fd, uint32_t mask, |
||||||
|
wl_event_loop_fd_func_t func, |
||||||
|
void *data, struct wl_event_source **event_source) |
||||||
|
{ |
||||||
|
*event_source = wl_event_loop_add_fd(loop, fd, 0, func, data); |
||||||
|
if (!*event_source) { |
||||||
|
weston_log("%s: wl_event_loop_add_fd failed.\n", __func__); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
wl_event_source_fd_update(*event_source, mask); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx, |
||||||
|
rdp_loop_task_func_t func, |
||||||
|
struct rdp_loop_task *task) |
||||||
|
{ |
||||||
|
/* this function is ONLY used to queue the task from FreeRDP thread,
|
||||||
|
* and the task to be processed at wayland display loop thread. */ |
||||||
|
assert_not_compositor_thread(peerCtx->rdpBackend); |
||||||
|
|
||||||
|
task->peerCtx = peerCtx; |
||||||
|
task->func = func; |
||||||
|
|
||||||
|
pthread_mutex_lock(&peerCtx->loop_task_list_mutex); |
||||||
|
/* this inserts at head */ |
||||||
|
wl_list_insert(&peerCtx->loop_task_list, &task->link); |
||||||
|
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex); |
||||||
|
|
||||||
|
eventfd_write(peerCtx->loop_task_event_source_fd, 1); |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
rdp_dispatch_task(int fd, uint32_t mask, void *arg) |
||||||
|
{ |
||||||
|
RdpPeerContext *peerCtx = (RdpPeerContext *)arg; |
||||||
|
struct rdp_loop_task *task, *tmp; |
||||||
|
eventfd_t dummy; |
||||||
|
|
||||||
|
/* this must be called back at wayland display loop thread */ |
||||||
|
assert_compositor_thread(peerCtx->rdpBackend); |
||||||
|
|
||||||
|
eventfd_read(peerCtx->loop_task_event_source_fd, &dummy); |
||||||
|
|
||||||
|
pthread_mutex_lock(&peerCtx->loop_task_list_mutex); |
||||||
|
/* dequeue the first task which is at last, so use reverse. */ |
||||||
|
assert(!wl_list_empty(&peerCtx->loop_task_list)); |
||||||
|
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) { |
||||||
|
wl_list_remove(&task->link); |
||||||
|
break; |
||||||
|
} |
||||||
|
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex); |
||||||
|
|
||||||
|
/* Dispatch and task will be freed by caller. */ |
||||||
|
task->func(false, task); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx) |
||||||
|
{ |
||||||
|
struct rdp_backend *b = peerCtx->rdpBackend; |
||||||
|
struct wl_event_loop *loop; |
||||||
|
bool ret; |
||||||
|
|
||||||
|
if (pthread_mutex_init(&peerCtx->loop_task_list_mutex, NULL) == -1) { |
||||||
|
weston_log("%s: pthread_mutex_init failed. %s\n", __func__, strerror(errno)); |
||||||
|
goto error_mutex; |
||||||
|
} |
||||||
|
|
||||||
|
assert(peerCtx->loop_task_event_source_fd == -1); |
||||||
|
peerCtx->loop_task_event_source_fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC); |
||||||
|
if (peerCtx->loop_task_event_source_fd == -1) { |
||||||
|
weston_log("%s: eventfd(EFD_SEMAPHORE) failed. %s\n", __func__, strerror(errno)); |
||||||
|
goto error_event_source_fd; |
||||||
|
} |
||||||
|
|
||||||
|
assert(wl_list_empty(&peerCtx->loop_task_list)); |
||||||
|
|
||||||
|
loop = wl_display_get_event_loop(b->compositor->wl_display); |
||||||
|
assert(peerCtx->loop_task_event_source == NULL); |
||||||
|
|
||||||
|
ret = rdp_event_loop_add_fd(loop, |
||||||
|
peerCtx->loop_task_event_source_fd, |
||||||
|
WL_EVENT_READABLE, rdp_dispatch_task, |
||||||
|
peerCtx, |
||||||
|
&peerCtx->loop_task_event_source); |
||||||
|
if (!ret) |
||||||
|
goto error_event_loop_add_fd; |
||||||
|
|
||||||
|
return true; |
||||||
|
|
||||||
|
error_event_loop_add_fd: |
||||||
|
close(peerCtx->loop_task_event_source_fd); |
||||||
|
peerCtx->loop_task_event_source_fd = -1; |
||||||
|
|
||||||
|
error_event_source_fd: |
||||||
|
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); |
||||||
|
|
||||||
|
error_mutex: |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx) |
||||||
|
{ |
||||||
|
struct rdp_loop_task *task, *tmp; |
||||||
|
|
||||||
|
/* This function must be called all virtual channel thread at FreeRDP is terminated,
|
||||||
|
* that ensures no more incoming tasks. */ |
||||||
|
|
||||||
|
if (peerCtx->loop_task_event_source) { |
||||||
|
wl_event_source_remove(peerCtx->loop_task_event_source); |
||||||
|
peerCtx->loop_task_event_source = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) { |
||||||
|
wl_list_remove(&task->link); |
||||||
|
/* inform caller task is not really scheduled prior to context destruction,
|
||||||
|
* inform them to clean them up. */ |
||||||
|
task->func(true /* freeOnly */, task); |
||||||
|
} |
||||||
|
assert(wl_list_empty(&peerCtx->loop_task_list)); |
||||||
|
|
||||||
|
if (peerCtx->loop_task_event_source_fd != -1) { |
||||||
|
close(peerCtx->loop_task_event_source_fd); |
||||||
|
peerCtx->loop_task_event_source_fd = -1; |
||||||
|
} |
||||||
|
|
||||||
|
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); |
||||||
|
} |
||||||
|
|
||||||
|
/* This is a little tricky - it makes sure there's always at least
|
||||||
|
* one spare byte in the array in case the caller needs to add a |
||||||
|
* null terminator to it. We can't just null terminate the array |
||||||
|
* here, because some callers won't want that - and some won't |
||||||
|
* like having an odd number of bytes. |
||||||
|
*/ |
||||||
|
int |
||||||
|
rdp_wl_array_read_fd(struct wl_array *array, int fd) |
||||||
|
{ |
||||||
|
int len, size; |
||||||
|
char *data; |
||||||
|
|
||||||
|
/* Make sure we have at least 1024 bytes of space left */ |
||||||
|
if (array->alloc - array->size < 1024) { |
||||||
|
if (!wl_array_add(array, 1024)) { |
||||||
|
errno = ENOMEM; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
array->size -= 1024; |
||||||
|
} |
||||||
|
data = (char *)array->data + array->size; |
||||||
|
/* Leave one char in case the caller needs space for a
|
||||||
|
* null terminator */ |
||||||
|
size = array->alloc - array->size - 1; |
||||||
|
do { |
||||||
|
len = read(fd, data, size); |
||||||
|
} while (len == -1 && errno == EINTR); |
||||||
|
|
||||||
|
if (len == -1) |
||||||
|
return -1; |
||||||
|
|
||||||
|
array->size += len; |
||||||
|
|
||||||
|
return len; |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue