Compare commits
No commits in common. 'be9f5bd99331c61ff18644035bf5d51ce2e43276' and '2e543aef219b3efc80ce23660fcce64cbcfaed41' have entirely different histories.
be9f5bd993
...
2e543aef21
@ -1,20 +0,0 @@ |
|||||||
#!/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 "$@" |
|
@ -0,0 +1,504 @@ |
|||||||
|
/*
|
||||||
|
* 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; |
||||||
|
} |
@ -0,0 +1,374 @@ |
|||||||
|
/*
|
||||||
|
* 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
@ -0,0 +1,737 @@ |
|||||||
|
/*
|
||||||
|
* 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
Before Width: | Height: | Size: 108 KiB |
@ -1,9 +0,0 @@ |
|||||||
# 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 |
|
@ -1,111 +0,0 @@ |
|||||||
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``. |
|
@ -0,0 +1,70 @@ |
|||||||
|
/*
|
||||||
|
* 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 */ |
@ -0,0 +1,78 @@ |
|||||||
|
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 |
@ -0,0 +1,36 @@ |
|||||||
|
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 |
||||||
|
) |
@ -0,0 +1,497 @@ |
|||||||
|
/*
|
||||||
|
* 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
@ -1,178 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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; |
|
||||||
} |
|
@ -0,0 +1,998 @@ |
|||||||
|
/*
|
||||||
|
* 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; |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
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
@ -1,263 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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
@ -1,262 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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