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