Rename src/ to libweston/
This clarifies what is supposed to be the libweston code. v2: screen-share.c is already in compositor/ instead. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Reviewed-by: Yong Bakos <ybakos@humanoriented.com> Acked-by: Daniel Stone <daniels@collabora.com> Reviewed-by: Quentin Glidic <sardemff7+git@sardemff7.net> Tested-by: Quentin Glidic <sardemff7+git@sardemff7.net> Tested-by: Benoit Gschwind <gschwind@gnu-log.net> Acked-by: Benoit Gschwind <gschwind@gnu-log.net> [Pekka: rebased]
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Copyright © 2011 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
WL_EXPORT void
|
||||
weston_spring_init(struct weston_spring *spring,
|
||||
double k, double current, double target)
|
||||
{
|
||||
spring->k = k;
|
||||
spring->friction = 400.0;
|
||||
spring->current = current;
|
||||
spring->previous = current;
|
||||
spring->target = target;
|
||||
spring->clip = WESTON_SPRING_OVERSHOOT;
|
||||
spring->min = 0.0;
|
||||
spring->max = 1.0;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_spring_update(struct weston_spring *spring, uint32_t msec)
|
||||
{
|
||||
double force, v, current, step;
|
||||
|
||||
/* Limit the number of executions of the loop below by ensuring that
|
||||
* the timestamp for last update of the spring is no more than 1s ago.
|
||||
* This handles the case where time moves backwards or forwards in
|
||||
* large jumps.
|
||||
*/
|
||||
if (msec - spring->timestamp > 1000) {
|
||||
weston_log("unexpectedly large timestamp jump (from %u to %u)\n",
|
||||
spring->timestamp, msec);
|
||||
spring->timestamp = msec - 1000;
|
||||
}
|
||||
|
||||
step = 0.01;
|
||||
while (4 < msec - spring->timestamp) {
|
||||
current = spring->current;
|
||||
v = current - spring->previous;
|
||||
force = spring->k * (spring->target - current) / 10.0 +
|
||||
(spring->previous - current) - v * spring->friction;
|
||||
|
||||
spring->current =
|
||||
current + (current - spring->previous) +
|
||||
force * step * step;
|
||||
spring->previous = current;
|
||||
|
||||
switch (spring->clip) {
|
||||
case WESTON_SPRING_OVERSHOOT:
|
||||
break;
|
||||
|
||||
case WESTON_SPRING_CLAMP:
|
||||
if (spring->current > spring->max) {
|
||||
spring->current = spring->max;
|
||||
spring->previous = spring->max;
|
||||
} else if (spring->current < 0.0) {
|
||||
spring->current = spring->min;
|
||||
spring->previous = spring->min;
|
||||
}
|
||||
break;
|
||||
|
||||
case WESTON_SPRING_BOUNCE:
|
||||
if (spring->current > spring->max) {
|
||||
spring->current =
|
||||
2 * spring->max - spring->current;
|
||||
spring->previous =
|
||||
2 * spring->max - spring->previous;
|
||||
} else if (spring->current < spring->min) {
|
||||
spring->current =
|
||||
2 * spring->min - spring->current;
|
||||
spring->previous =
|
||||
2 * spring->min - spring->previous;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
spring->timestamp += 4;
|
||||
}
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_spring_done(struct weston_spring *spring)
|
||||
{
|
||||
return fabs(spring->previous - spring->target) < 0.002 &&
|
||||
fabs(spring->current - spring->target) < 0.002;
|
||||
}
|
||||
|
||||
typedef void (*weston_view_animation_frame_func_t)(struct weston_view_animation *animation);
|
||||
|
||||
struct weston_view_animation {
|
||||
struct weston_view *view;
|
||||
struct weston_animation animation;
|
||||
struct weston_spring spring;
|
||||
struct weston_transform transform;
|
||||
struct wl_listener listener;
|
||||
float start, stop;
|
||||
weston_view_animation_frame_func_t frame;
|
||||
weston_view_animation_frame_func_t reset;
|
||||
weston_view_animation_done_func_t done;
|
||||
void *data;
|
||||
void *private;
|
||||
};
|
||||
|
||||
WL_EXPORT void
|
||||
weston_view_animation_destroy(struct weston_view_animation *animation)
|
||||
{
|
||||
wl_list_remove(&animation->animation.link);
|
||||
wl_list_remove(&animation->listener.link);
|
||||
wl_list_remove(&animation->transform.link);
|
||||
if (animation->reset)
|
||||
animation->reset(animation);
|
||||
weston_view_geometry_dirty(animation->view);
|
||||
if (animation->done)
|
||||
animation->done(animation, animation->data);
|
||||
free(animation);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_animation_view_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_view_animation *animation =
|
||||
container_of(listener,
|
||||
struct weston_view_animation, listener);
|
||||
|
||||
weston_view_animation_destroy(animation);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_view_animation_frame(struct weston_animation *base,
|
||||
struct weston_output *output, uint32_t msecs)
|
||||
{
|
||||
struct weston_view_animation *animation =
|
||||
container_of(base,
|
||||
struct weston_view_animation, animation);
|
||||
struct weston_compositor *compositor =
|
||||
animation->view->surface->compositor;
|
||||
|
||||
if (base->frame_counter <= 1)
|
||||
animation->spring.timestamp = msecs;
|
||||
|
||||
weston_spring_update(&animation->spring, msecs);
|
||||
|
||||
if (weston_spring_done(&animation->spring)) {
|
||||
weston_view_schedule_repaint(animation->view);
|
||||
weston_view_animation_destroy(animation);
|
||||
return;
|
||||
}
|
||||
|
||||
if (animation->frame)
|
||||
animation->frame(animation);
|
||||
|
||||
weston_view_geometry_dirty(animation->view);
|
||||
weston_view_schedule_repaint(animation->view);
|
||||
|
||||
/* The view's output_mask will be zero if its position is
|
||||
* offscreen. Animations should always run but as they are also
|
||||
* run off the repaint cycle, if there's nothing to repaint
|
||||
* the animation stops running. Therefore if we catch this situation
|
||||
* and schedule a repaint on all outputs it will be avoided.
|
||||
*/
|
||||
if (animation->view->output_mask == 0)
|
||||
weston_compositor_schedule_repaint(compositor);
|
||||
}
|
||||
|
||||
static struct weston_view_animation *
|
||||
weston_view_animation_create(struct weston_view *view,
|
||||
float start, float stop,
|
||||
weston_view_animation_frame_func_t frame,
|
||||
weston_view_animation_frame_func_t reset,
|
||||
weston_view_animation_done_func_t done,
|
||||
void *data,
|
||||
void *private)
|
||||
{
|
||||
struct weston_view_animation *animation;
|
||||
|
||||
animation = malloc(sizeof *animation);
|
||||
if (!animation)
|
||||
return NULL;
|
||||
|
||||
animation->view = view;
|
||||
animation->frame = frame;
|
||||
animation->reset = reset;
|
||||
animation->done = done;
|
||||
animation->data = data;
|
||||
animation->start = start;
|
||||
animation->stop = stop;
|
||||
animation->private = private;
|
||||
|
||||
weston_matrix_init(&animation->transform.matrix);
|
||||
wl_list_insert(&view->geometry.transformation_list,
|
||||
&animation->transform.link);
|
||||
|
||||
animation->animation.frame = weston_view_animation_frame;
|
||||
|
||||
animation->listener.notify = handle_animation_view_destroy;
|
||||
wl_signal_add(&view->destroy_signal, &animation->listener);
|
||||
|
||||
wl_list_insert(&view->output->animation_list,
|
||||
&animation->animation.link);
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_view_animation_run(struct weston_view_animation *animation)
|
||||
{
|
||||
animation->animation.frame_counter = 0;
|
||||
weston_view_animation_frame(&animation->animation, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_alpha(struct weston_view_animation *animation)
|
||||
{
|
||||
struct weston_view *view = animation->view;
|
||||
|
||||
view->alpha = animation->stop;
|
||||
}
|
||||
|
||||
static void
|
||||
zoom_frame(struct weston_view_animation *animation)
|
||||
{
|
||||
struct weston_view *es = animation->view;
|
||||
float scale;
|
||||
|
||||
scale = animation->start +
|
||||
(animation->stop - animation->start) *
|
||||
animation->spring.current;
|
||||
weston_matrix_init(&animation->transform.matrix);
|
||||
weston_matrix_translate(&animation->transform.matrix,
|
||||
-0.5f * es->surface->width,
|
||||
-0.5f * es->surface->height, 0);
|
||||
weston_matrix_scale(&animation->transform.matrix, scale, scale, scale);
|
||||
weston_matrix_translate(&animation->transform.matrix,
|
||||
0.5f * es->surface->width,
|
||||
0.5f * es->surface->height, 0);
|
||||
|
||||
es->alpha = animation->spring.current;
|
||||
if (es->alpha > 1.0)
|
||||
es->alpha = 1.0;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_view_animation *
|
||||
weston_zoom_run(struct weston_view *view, float start, float stop,
|
||||
weston_view_animation_done_func_t done, void *data)
|
||||
{
|
||||
struct weston_view_animation *zoom;
|
||||
|
||||
zoom = weston_view_animation_create(view, start, stop,
|
||||
zoom_frame, reset_alpha,
|
||||
done, data, NULL);
|
||||
|
||||
if (zoom == NULL)
|
||||
return NULL;
|
||||
|
||||
weston_spring_init(&zoom->spring, 300.0, start, stop);
|
||||
zoom->spring.friction = 1400;
|
||||
zoom->spring.previous = start - (stop - start) * 0.03;
|
||||
|
||||
weston_view_animation_run(zoom);
|
||||
|
||||
return zoom;
|
||||
}
|
||||
|
||||
static void
|
||||
fade_frame(struct weston_view_animation *animation)
|
||||
{
|
||||
if (animation->spring.current > 0.999)
|
||||
animation->view->alpha = 1;
|
||||
else if (animation->spring.current < 0.001 )
|
||||
animation->view->alpha = 0;
|
||||
else
|
||||
animation->view->alpha = animation->spring.current;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_view_animation *
|
||||
weston_fade_run(struct weston_view *view,
|
||||
float start, float end, float k,
|
||||
weston_view_animation_done_func_t done, void *data)
|
||||
{
|
||||
struct weston_view_animation *fade;
|
||||
|
||||
fade = weston_view_animation_create(view, start, end,
|
||||
fade_frame, reset_alpha,
|
||||
done, data, NULL);
|
||||
|
||||
if (fade == NULL)
|
||||
return NULL;
|
||||
|
||||
weston_spring_init(&fade->spring, 1000.0, start, end);
|
||||
fade->spring.friction = 4000;
|
||||
fade->spring.previous = start - (end - start) * 0.1;
|
||||
|
||||
view->alpha = start;
|
||||
|
||||
weston_view_animation_run(fade);
|
||||
|
||||
return fade;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_fade_update(struct weston_view_animation *fade, float target)
|
||||
{
|
||||
fade->spring.target = target;
|
||||
fade->stop = target;
|
||||
}
|
||||
|
||||
static void
|
||||
stable_fade_frame(struct weston_view_animation *animation)
|
||||
{
|
||||
struct weston_view *back_view;
|
||||
|
||||
if (animation->spring.current > 0.999)
|
||||
animation->view->alpha = 1;
|
||||
else if (animation->spring.current < 0.001 )
|
||||
animation->view->alpha = 0;
|
||||
else
|
||||
animation->view->alpha = animation->spring.current;
|
||||
|
||||
back_view = (struct weston_view *) animation->private;
|
||||
back_view->alpha =
|
||||
(animation->spring.target - animation->view->alpha) /
|
||||
(1.0 - animation->view->alpha);
|
||||
weston_view_geometry_dirty(back_view);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_view_animation *
|
||||
weston_stable_fade_run(struct weston_view *front_view, float start,
|
||||
struct weston_view *back_view, float end,
|
||||
weston_view_animation_done_func_t done, void *data)
|
||||
{
|
||||
struct weston_view_animation *fade;
|
||||
|
||||
fade = weston_view_animation_create(front_view, 0, 0,
|
||||
stable_fade_frame, NULL,
|
||||
done, data, back_view);
|
||||
|
||||
if (fade == NULL)
|
||||
return NULL;
|
||||
|
||||
weston_spring_init(&fade->spring, 400, start, end);
|
||||
fade->spring.friction = 1150;
|
||||
|
||||
front_view->alpha = start;
|
||||
back_view->alpha = end;
|
||||
|
||||
weston_view_animation_run(fade);
|
||||
|
||||
return fade;
|
||||
}
|
||||
|
||||
static void
|
||||
slide_frame(struct weston_view_animation *animation)
|
||||
{
|
||||
float scale;
|
||||
|
||||
scale = animation->start +
|
||||
(animation->stop - animation->start) *
|
||||
animation->spring.current;
|
||||
weston_matrix_init(&animation->transform.matrix);
|
||||
weston_matrix_translate(&animation->transform.matrix, 0, scale, 0);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_view_animation *
|
||||
weston_slide_run(struct weston_view *view, float start, float stop,
|
||||
weston_view_animation_done_func_t done, void *data)
|
||||
{
|
||||
struct weston_view_animation *animation;
|
||||
|
||||
animation = weston_view_animation_create(view, start, stop,
|
||||
slide_frame, NULL, done,
|
||||
data, NULL);
|
||||
if (!animation)
|
||||
return NULL;
|
||||
|
||||
weston_spring_init(&animation->spring, 400.0, 0.0, 1.0);
|
||||
animation->spring.friction = 600;
|
||||
animation->spring.clip = WESTON_SPRING_BOUNCE;
|
||||
|
||||
weston_view_animation_run(animation);
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
struct weston_move_animation {
|
||||
int dx;
|
||||
int dy;
|
||||
int reverse;
|
||||
weston_view_animation_done_func_t done;
|
||||
};
|
||||
|
||||
static void
|
||||
move_frame(struct weston_view_animation *animation)
|
||||
{
|
||||
struct weston_move_animation *move = animation->private;
|
||||
float scale;
|
||||
float progress = animation->spring.current;
|
||||
|
||||
if (move->reverse)
|
||||
progress = 1.0 - progress;
|
||||
|
||||
scale = animation->start +
|
||||
(animation->stop - animation->start) *
|
||||
progress;
|
||||
weston_matrix_init(&animation->transform.matrix);
|
||||
weston_matrix_scale(&animation->transform.matrix, scale, scale, 1.0f);
|
||||
weston_matrix_translate(&animation->transform.matrix,
|
||||
move->dx * progress, move->dy * progress,
|
||||
0);
|
||||
}
|
||||
|
||||
static void
|
||||
move_done(struct weston_view_animation *animation, void *data)
|
||||
{
|
||||
struct weston_move_animation *move = animation->private;
|
||||
|
||||
if (move->done)
|
||||
move->done(animation, data);
|
||||
|
||||
free(move);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_view_animation *
|
||||
weston_move_scale_run(struct weston_view *view, int dx, int dy,
|
||||
float start, float end, int reverse,
|
||||
weston_view_animation_done_func_t done, void *data)
|
||||
{
|
||||
struct weston_move_animation *move;
|
||||
struct weston_view_animation *animation;
|
||||
|
||||
move = malloc(sizeof(*move));
|
||||
if (!move)
|
||||
return NULL;
|
||||
move->dx = dx;
|
||||
move->dy = dy;
|
||||
move->reverse = reverse;
|
||||
move->done = done;
|
||||
|
||||
animation = weston_view_animation_create(view, start, end, move_frame,
|
||||
NULL, move_done, data, move);
|
||||
|
||||
if (animation == NULL){
|
||||
free(move);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
weston_spring_init(&animation->spring, 400.0, 0.0, 1.0);
|
||||
animation->spring.friction = 1150;
|
||||
|
||||
weston_view_animation_run(animation);
|
||||
|
||||
return animation;
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* Copyright © 2011-2012 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 <stdlib.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct weston_binding {
|
||||
uint32_t key;
|
||||
uint32_t button;
|
||||
uint32_t axis;
|
||||
uint32_t modifier;
|
||||
void *handler;
|
||||
void *data;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
static struct weston_binding *
|
||||
weston_compositor_add_binding(struct weston_compositor *compositor,
|
||||
uint32_t key, uint32_t button, uint32_t axis,
|
||||
uint32_t modifier, void *handler, void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = malloc(sizeof *binding);
|
||||
if (binding == NULL)
|
||||
return NULL;
|
||||
|
||||
binding->key = key;
|
||||
binding->button = button;
|
||||
binding->axis = axis;
|
||||
binding->modifier = modifier;
|
||||
binding->handler = handler;
|
||||
binding->data = data;
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_binding *
|
||||
weston_compositor_add_key_binding(struct weston_compositor *compositor,
|
||||
uint32_t key, uint32_t modifier,
|
||||
weston_key_binding_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = weston_compositor_add_binding(compositor, key, 0, 0,
|
||||
modifier, handler, data);
|
||||
if (binding == NULL)
|
||||
return NULL;
|
||||
|
||||
wl_list_insert(compositor->key_binding_list.prev, &binding->link);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_binding *
|
||||
weston_compositor_add_modifier_binding(struct weston_compositor *compositor,
|
||||
uint32_t modifier,
|
||||
weston_modifier_binding_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = weston_compositor_add_binding(compositor, 0, 0, 0,
|
||||
modifier, handler, data);
|
||||
if (binding == NULL)
|
||||
return NULL;
|
||||
|
||||
wl_list_insert(compositor->modifier_binding_list.prev, &binding->link);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_binding *
|
||||
weston_compositor_add_button_binding(struct weston_compositor *compositor,
|
||||
uint32_t button, uint32_t modifier,
|
||||
weston_button_binding_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = weston_compositor_add_binding(compositor, 0, button, 0,
|
||||
modifier, handler, data);
|
||||
if (binding == NULL)
|
||||
return NULL;
|
||||
|
||||
wl_list_insert(compositor->button_binding_list.prev, &binding->link);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_binding *
|
||||
weston_compositor_add_touch_binding(struct weston_compositor *compositor,
|
||||
uint32_t modifier,
|
||||
weston_touch_binding_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = weston_compositor_add_binding(compositor, 0, 0, 0,
|
||||
modifier, handler, data);
|
||||
if (binding == NULL)
|
||||
return NULL;
|
||||
|
||||
wl_list_insert(compositor->touch_binding_list.prev, &binding->link);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_binding *
|
||||
weston_compositor_add_axis_binding(struct weston_compositor *compositor,
|
||||
uint32_t axis, uint32_t modifier,
|
||||
weston_axis_binding_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = weston_compositor_add_binding(compositor, 0, 0, axis,
|
||||
modifier, handler, data);
|
||||
if (binding == NULL)
|
||||
return NULL;
|
||||
|
||||
wl_list_insert(compositor->axis_binding_list.prev, &binding->link);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_binding *
|
||||
weston_compositor_add_debug_binding(struct weston_compositor *compositor,
|
||||
uint32_t key,
|
||||
weston_key_binding_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct weston_binding *binding;
|
||||
|
||||
binding = weston_compositor_add_binding(compositor, key, 0, 0, 0,
|
||||
handler, data);
|
||||
|
||||
wl_list_insert(compositor->debug_binding_list.prev, &binding->link);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_binding_destroy(struct weston_binding *binding)
|
||||
{
|
||||
wl_list_remove(&binding->link);
|
||||
free(binding);
|
||||
}
|
||||
|
||||
void
|
||||
weston_binding_list_destroy_all(struct wl_list *list)
|
||||
{
|
||||
struct weston_binding *binding, *tmp;
|
||||
|
||||
wl_list_for_each_safe(binding, tmp, list, link)
|
||||
weston_binding_destroy(binding);
|
||||
}
|
||||
|
||||
struct binding_keyboard_grab {
|
||||
uint32_t key;
|
||||
struct weston_keyboard_grab grab;
|
||||
};
|
||||
|
||||
static void
|
||||
binding_key(struct weston_keyboard_grab *grab,
|
||||
uint32_t time, uint32_t key, uint32_t state_w)
|
||||
{
|
||||
struct binding_keyboard_grab *b =
|
||||
container_of(grab, struct binding_keyboard_grab, grab);
|
||||
struct wl_resource *resource;
|
||||
enum wl_keyboard_key_state state = state_w;
|
||||
uint32_t serial;
|
||||
struct weston_keyboard *keyboard = grab->keyboard;
|
||||
struct wl_display *display = keyboard->seat->compositor->wl_display;
|
||||
|
||||
if (key == b->key) {
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
|
||||
weston_keyboard_end_grab(grab->keyboard);
|
||||
if (keyboard->input_method_resource)
|
||||
keyboard->grab = &keyboard->input_method_grab;
|
||||
free(b);
|
||||
} else {
|
||||
/* Don't send the key press event for the binding key */
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!wl_list_empty(&keyboard->focus_resource_list)) {
|
||||
serial = wl_display_next_serial(display);
|
||||
wl_resource_for_each(resource, &keyboard->focus_resource_list) {
|
||||
wl_keyboard_send_key(resource,
|
||||
serial,
|
||||
time,
|
||||
key,
|
||||
state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
|
||||
wl_resource_for_each(resource, &grab->keyboard->focus_resource_list) {
|
||||
wl_keyboard_send_modifiers(resource, serial, mods_depressed,
|
||||
mods_latched, mods_locked, group);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
binding_cancel(struct weston_keyboard_grab *grab)
|
||||
{
|
||||
struct binding_keyboard_grab *binding_grab =
|
||||
container_of(grab, struct binding_keyboard_grab, grab);
|
||||
|
||||
weston_keyboard_end_grab(grab->keyboard);
|
||||
free(binding_grab);
|
||||
}
|
||||
|
||||
static const struct weston_keyboard_grab_interface binding_grab = {
|
||||
binding_key,
|
||||
binding_modifiers,
|
||||
binding_cancel,
|
||||
};
|
||||
|
||||
static void
|
||||
install_binding_grab(struct weston_keyboard *keyboard, uint32_t time,
|
||||
uint32_t key, struct weston_surface *focus)
|
||||
{
|
||||
struct binding_keyboard_grab *grab;
|
||||
|
||||
grab = malloc(sizeof *grab);
|
||||
grab->key = key;
|
||||
grab->grab.interface = &binding_grab;
|
||||
weston_keyboard_start_grab(keyboard, &grab->grab);
|
||||
|
||||
/* Notify the surface which had the focus before this binding
|
||||
* triggered that we stole a keypress from under it, by forcing
|
||||
* a wl_keyboard leave/enter pair. The enter event will contain
|
||||
* the pressed key in the keys array, so the client will know
|
||||
* the exact state of the keyboard.
|
||||
* If the old focus surface is different than the new one it
|
||||
* means it was changed in the binding handler, so it received
|
||||
* the enter event already. */
|
||||
if (focus && keyboard->focus == focus) {
|
||||
weston_keyboard_set_focus(keyboard, NULL);
|
||||
weston_keyboard_set_focus(keyboard, focus);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
weston_compositor_run_key_binding(struct weston_compositor *compositor,
|
||||
struct weston_keyboard *keyboard,
|
||||
uint32_t time, uint32_t key,
|
||||
enum wl_keyboard_key_state state)
|
||||
{
|
||||
struct weston_binding *b, *tmp;
|
||||
struct weston_surface *focus;
|
||||
struct weston_seat *seat = keyboard->seat;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
/* Invalidate all active modifier bindings. */
|
||||
wl_list_for_each(b, &compositor->modifier_binding_list, link)
|
||||
b->key = key;
|
||||
|
||||
wl_list_for_each_safe(b, tmp, &compositor->key_binding_list, link) {
|
||||
if (b->key == key && b->modifier == seat->modifier_state) {
|
||||
weston_key_binding_handler_t handler = b->handler;
|
||||
focus = keyboard->focus;
|
||||
handler(keyboard, time, key, b->data);
|
||||
|
||||
/* If this was a key binding and it didn't
|
||||
* install a keyboard grab, install one now to
|
||||
* swallow the key press. */
|
||||
if (keyboard->grab ==
|
||||
&keyboard->default_grab)
|
||||
install_binding_grab(keyboard,
|
||||
time,
|
||||
key,
|
||||
focus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
weston_compositor_run_modifier_binding(struct weston_compositor *compositor,
|
||||
struct weston_keyboard *keyboard,
|
||||
enum weston_keyboard_modifier modifier,
|
||||
enum wl_keyboard_key_state state)
|
||||
{
|
||||
struct weston_binding *b, *tmp;
|
||||
|
||||
if (keyboard->grab != &keyboard->default_grab)
|
||||
return;
|
||||
|
||||
wl_list_for_each_safe(b, tmp, &compositor->modifier_binding_list, link) {
|
||||
weston_modifier_binding_handler_t handler = b->handler;
|
||||
|
||||
if (b->modifier != modifier)
|
||||
continue;
|
||||
|
||||
/* Prime the modifier binding. */
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
b->key = 0;
|
||||
continue;
|
||||
}
|
||||
/* Ignore the binding if a key was pressed in between. */
|
||||
else if (b->key != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
handler(keyboard, modifier, b->data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
weston_compositor_run_button_binding(struct weston_compositor *compositor,
|
||||
struct weston_pointer *pointer,
|
||||
uint32_t time, uint32_t button,
|
||||
enum wl_pointer_button_state state)
|
||||
{
|
||||
struct weston_binding *b, *tmp;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
/* Invalidate all active modifier bindings. */
|
||||
wl_list_for_each(b, &compositor->modifier_binding_list, link)
|
||||
b->key = button;
|
||||
|
||||
wl_list_for_each_safe(b, tmp, &compositor->button_binding_list, link) {
|
||||
if (b->button == button &&
|
||||
b->modifier == pointer->seat->modifier_state) {
|
||||
weston_button_binding_handler_t handler = b->handler;
|
||||
handler(pointer, time, button, b->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
weston_compositor_run_touch_binding(struct weston_compositor *compositor,
|
||||
struct weston_touch *touch, uint32_t time,
|
||||
int touch_type)
|
||||
{
|
||||
struct weston_binding *b, *tmp;
|
||||
|
||||
if (touch->num_tp != 1 || touch_type != WL_TOUCH_DOWN)
|
||||
return;
|
||||
|
||||
wl_list_for_each_safe(b, tmp, &compositor->touch_binding_list, link) {
|
||||
if (b->modifier == touch->seat->modifier_state) {
|
||||
weston_touch_binding_handler_t handler = b->handler;
|
||||
handler(touch, time, b->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
weston_compositor_run_axis_binding(struct weston_compositor *compositor,
|
||||
struct weston_pointer *pointer,
|
||||
uint32_t time,
|
||||
struct weston_pointer_axis_event *event)
|
||||
{
|
||||
struct weston_binding *b, *tmp;
|
||||
|
||||
/* Invalidate all active modifier bindings. */
|
||||
wl_list_for_each(b, &compositor->modifier_binding_list, link)
|
||||
b->key = event->axis;
|
||||
|
||||
wl_list_for_each_safe(b, tmp, &compositor->axis_binding_list, link) {
|
||||
if (b->axis == event->axis &&
|
||||
b->modifier == pointer->seat->modifier_state) {
|
||||
weston_axis_binding_handler_t handler = b->handler;
|
||||
handler(pointer, time, event, b->data);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
weston_compositor_run_debug_binding(struct weston_compositor *compositor,
|
||||
struct weston_keyboard *keyboard,
|
||||
uint32_t time, uint32_t key,
|
||||
enum wl_keyboard_key_state state)
|
||||
{
|
||||
weston_key_binding_handler_t handler;
|
||||
struct weston_binding *binding, *tmp;
|
||||
int count = 0;
|
||||
|
||||
wl_list_for_each_safe(binding, tmp, &compositor->debug_binding_list, link) {
|
||||
if (key != binding->key)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
handler = binding->handler;
|
||||
handler(keyboard, time, key, binding->data);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct debug_binding_grab {
|
||||
struct weston_keyboard_grab grab;
|
||||
struct weston_seat *seat;
|
||||
uint32_t key[2];
|
||||
int key_released[2];
|
||||
};
|
||||
|
||||
static void
|
||||
debug_binding_key(struct weston_keyboard_grab *grab, uint32_t time,
|
||||
uint32_t key, uint32_t state)
|
||||
{
|
||||
struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
|
||||
struct weston_compositor *ec = db->seat->compositor;
|
||||
struct wl_display *display = ec->wl_display;
|
||||
struct wl_resource *resource;
|
||||
uint32_t serial;
|
||||
int send = 0, terminate = 0;
|
||||
int check_binding = 1;
|
||||
int i;
|
||||
struct wl_list *resource_list;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
|
||||
/* Do not run bindings on key releases */
|
||||
check_binding = 0;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
if (key == db->key[i])
|
||||
db->key_released[i] = 1;
|
||||
|
||||
if (db->key_released[0] && db->key_released[1]) {
|
||||
/* All key releases been swalled so end the grab */
|
||||
terminate = 1;
|
||||
} else if (key != db->key[0] && key != db->key[1]) {
|
||||
/* Should not swallow release of other keys */
|
||||
send = 1;
|
||||
}
|
||||
} else if (key == db->key[0] && !db->key_released[0]) {
|
||||
/* Do not check bindings for the first press of the binding
|
||||
* key. This allows it to be used as a debug shortcut.
|
||||
* We still need to swallow this event. */
|
||||
check_binding = 0;
|
||||
} else if (db->key[1]) {
|
||||
/* If we already ran a binding don't process another one since
|
||||
* we can't keep track of all the binding keys that were
|
||||
* pressed in order to swallow the release events. */
|
||||
send = 1;
|
||||
check_binding = 0;
|
||||
}
|
||||
|
||||
if (check_binding) {
|
||||
if (weston_compositor_run_debug_binding(ec, grab->keyboard,
|
||||
time, key, state)) {
|
||||
/* We ran a binding so swallow the press and keep the
|
||||
* grab to swallow the released too. */
|
||||
send = 0;
|
||||
terminate = 0;
|
||||
db->key[1] = key;
|
||||
} else {
|
||||
/* Terminate the grab since the key pressed is not a
|
||||
* debug binding key. */
|
||||
send = 1;
|
||||
terminate = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (send) {
|
||||
serial = wl_display_next_serial(display);
|
||||
resource_list = &grab->keyboard->focus_resource_list;
|
||||
wl_resource_for_each(resource, resource_list) {
|
||||
wl_keyboard_send_key(resource, serial, time, key, state);
|
||||
}
|
||||
}
|
||||
|
||||
if (terminate) {
|
||||
weston_keyboard_end_grab(grab->keyboard);
|
||||
if (grab->keyboard->input_method_resource)
|
||||
grab->keyboard->grab = &grab->keyboard->input_method_grab;
|
||||
free(db);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
struct wl_list *resource_list;
|
||||
|
||||
resource_list = &grab->keyboard->focus_resource_list;
|
||||
|
||||
wl_resource_for_each(resource, resource_list) {
|
||||
wl_keyboard_send_modifiers(resource, serial, mods_depressed,
|
||||
mods_latched, mods_locked, group);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
debug_binding_cancel(struct weston_keyboard_grab *grab)
|
||||
{
|
||||
struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
|
||||
|
||||
weston_keyboard_end_grab(grab->keyboard);
|
||||
free(db);
|
||||
}
|
||||
|
||||
struct weston_keyboard_grab_interface debug_binding_keyboard_grab = {
|
||||
debug_binding_key,
|
||||
debug_binding_modifiers,
|
||||
debug_binding_cancel,
|
||||
};
|
||||
|
||||
static void
|
||||
debug_binding(struct weston_keyboard *keyboard, uint32_t time,
|
||||
uint32_t key, void *data)
|
||||
{
|
||||
struct debug_binding_grab *grab;
|
||||
|
||||
grab = calloc(1, sizeof *grab);
|
||||
if (!grab)
|
||||
return;
|
||||
|
||||
grab->seat = keyboard->seat;
|
||||
grab->key[0] = key;
|
||||
grab->grab.interface = &debug_binding_keyboard_grab;
|
||||
weston_keyboard_start_grab(keyboard, &grab->grab);
|
||||
}
|
||||
|
||||
/** Install the trigger binding for debug bindings.
|
||||
*
|
||||
* \param compositor The compositor.
|
||||
* \param mod The modifier.
|
||||
*
|
||||
* This will add a key binding for modifier+SHIFT+SPACE that will trigger
|
||||
* debug key bindings.
|
||||
*/
|
||||
WL_EXPORT void
|
||||
weston_install_debug_key_binding(struct weston_compositor *compositor,
|
||||
uint32_t mod)
|
||||
{
|
||||
weston_compositor_add_key_binding(compositor, KEY_SPACE,
|
||||
mod | MODIFIER_SHIFT,
|
||||
debug_binding, NULL);
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright © 2012 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <linux/input.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct clipboard_source {
|
||||
struct weston_data_source base;
|
||||
struct wl_array contents;
|
||||
struct clipboard *clipboard;
|
||||
struct wl_event_source *event_source;
|
||||
uint32_t serial;
|
||||
int refcount;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct clipboard {
|
||||
struct weston_seat *seat;
|
||||
struct wl_listener selection_listener;
|
||||
struct wl_listener destroy_listener;
|
||||
struct clipboard_source *source;
|
||||
};
|
||||
|
||||
static void clipboard_client_create(struct clipboard_source *source, int fd);
|
||||
|
||||
static void
|
||||
clipboard_source_unref(struct clipboard_source *source)
|
||||
{
|
||||
char **s;
|
||||
|
||||
source->refcount--;
|
||||
if (source->refcount > 0)
|
||||
return;
|
||||
|
||||
if (source->event_source) {
|
||||
wl_event_source_remove(source->event_source);
|
||||
close(source->fd);
|
||||
}
|
||||
wl_signal_emit(&source->base.destroy_signal,
|
||||
&source->base);
|
||||
s = source->base.mime_types.data;
|
||||
free(*s);
|
||||
wl_array_release(&source->base.mime_types);
|
||||
wl_array_release(&source->contents);
|
||||
free(source);
|
||||
}
|
||||
|
||||
static int
|
||||
clipboard_source_data(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct clipboard_source *source = data;
|
||||
struct clipboard *clipboard = source->clipboard;
|
||||
char *p;
|
||||
int len, size;
|
||||
|
||||
if (source->contents.alloc - source->contents.size < 1024) {
|
||||
wl_array_add(&source->contents, 1024);
|
||||
source->contents.size -= 1024;
|
||||
}
|
||||
|
||||
p = source->contents.data + source->contents.size;
|
||||
size = source->contents.alloc - source->contents.size;
|
||||
len = read(fd, p, size);
|
||||
if (len == 0) {
|
||||
wl_event_source_remove(source->event_source);
|
||||
close(fd);
|
||||
source->event_source = NULL;
|
||||
} else if (len < 0) {
|
||||
clipboard_source_unref(source);
|
||||
clipboard->source = NULL;
|
||||
} else {
|
||||
source->contents.size += len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_source_accept(struct weston_data_source *source,
|
||||
uint32_t time, const char *mime_type)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_source_send(struct weston_data_source *base,
|
||||
const char *mime_type, int32_t fd)
|
||||
{
|
||||
struct clipboard_source *source =
|
||||
container_of(base, struct clipboard_source, base);
|
||||
char **s;
|
||||
|
||||
s = source->base.mime_types.data;
|
||||
if (strcmp(mime_type, s[0]) == 0)
|
||||
clipboard_client_create(source, fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_source_cancel(struct weston_data_source *source)
|
||||
{
|
||||
}
|
||||
|
||||
static struct clipboard_source *
|
||||
clipboard_source_create(struct clipboard *clipboard,
|
||||
const char *mime_type, uint32_t serial, int fd)
|
||||
{
|
||||
struct wl_display *display = clipboard->seat->compositor->wl_display;
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop(display);
|
||||
struct clipboard_source *source;
|
||||
char **s;
|
||||
|
||||
source = zalloc(sizeof *source);
|
||||
if (source == NULL)
|
||||
return NULL;
|
||||
|
||||
wl_array_init(&source->contents);
|
||||
wl_array_init(&source->base.mime_types);
|
||||
source->base.resource = NULL;
|
||||
source->base.accept = clipboard_source_accept;
|
||||
source->base.send = clipboard_source_send;
|
||||
source->base.cancel = clipboard_source_cancel;
|
||||
wl_signal_init(&source->base.destroy_signal);
|
||||
source->refcount = 1;
|
||||
source->clipboard = clipboard;
|
||||
source->serial = serial;
|
||||
source->fd = fd;
|
||||
|
||||
s = wl_array_add(&source->base.mime_types, sizeof *s);
|
||||
if (s == NULL)
|
||||
goto err_add;
|
||||
*s = strdup(mime_type);
|
||||
if (*s == NULL)
|
||||
goto err_strdup;
|
||||
source->event_source =
|
||||
wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
||||
clipboard_source_data, source);
|
||||
if (source->event_source == NULL)
|
||||
goto err_source;
|
||||
|
||||
return source;
|
||||
|
||||
err_source:
|
||||
free(*s);
|
||||
err_strdup:
|
||||
wl_array_release(&source->base.mime_types);
|
||||
err_add:
|
||||
free(source);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct clipboard_client {
|
||||
struct wl_event_source *event_source;
|
||||
size_t offset;
|
||||
struct clipboard_source *source;
|
||||
};
|
||||
|
||||
static int
|
||||
clipboard_client_data(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct clipboard_client *client = data;
|
||||
char *p;
|
||||
size_t size;
|
||||
int len;
|
||||
|
||||
size = client->source->contents.size;
|
||||
p = client->source->contents.data;
|
||||
len = write(fd, p + client->offset, size - client->offset);
|
||||
if (len > 0)
|
||||
client->offset += len;
|
||||
|
||||
if (client->offset == size || len <= 0) {
|
||||
close(fd);
|
||||
wl_event_source_remove(client->event_source);
|
||||
clipboard_source_unref(client->source);
|
||||
free(client);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_client_create(struct clipboard_source *source, int fd)
|
||||
{
|
||||
struct weston_seat *seat = source->clipboard->seat;
|
||||
struct clipboard_client *client;
|
||||
struct wl_event_loop *loop =
|
||||
wl_display_get_event_loop(seat->compositor->wl_display);
|
||||
|
||||
client = zalloc(sizeof *client);
|
||||
if (client == NULL)
|
||||
return;
|
||||
|
||||
client->source = source;
|
||||
source->refcount++;
|
||||
client->event_source =
|
||||
wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE,
|
||||
clipboard_client_data, client);
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_set_selection(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct clipboard *clipboard =
|
||||
container_of(listener, struct clipboard, selection_listener);
|
||||
struct weston_seat *seat = data;
|
||||
struct weston_data_source *source = seat->selection_data_source;
|
||||
const char **mime_types;
|
||||
int p[2];
|
||||
|
||||
if (source == NULL) {
|
||||
if (clipboard->source)
|
||||
weston_seat_set_selection(seat,
|
||||
&clipboard->source->base,
|
||||
clipboard->source->serial);
|
||||
return;
|
||||
} else if (source->accept == clipboard_source_accept) {
|
||||
/* Callback for our data source. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (clipboard->source)
|
||||
clipboard_source_unref(clipboard->source);
|
||||
|
||||
clipboard->source = NULL;
|
||||
|
||||
mime_types = source->mime_types.data;
|
||||
|
||||
if (!mime_types || pipe2(p, O_CLOEXEC) == -1)
|
||||
return;
|
||||
|
||||
source->send(source, mime_types[0], p[1]);
|
||||
|
||||
clipboard->source =
|
||||
clipboard_source_create(clipboard, mime_types[0],
|
||||
seat->selection_serial, p[0]);
|
||||
if (clipboard->source == NULL) {
|
||||
close(p[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clipboard_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct clipboard *clipboard =
|
||||
container_of(listener, struct clipboard, destroy_listener);
|
||||
|
||||
wl_list_remove(&clipboard->selection_listener.link);
|
||||
wl_list_remove(&clipboard->destroy_listener.link);
|
||||
|
||||
free(clipboard);
|
||||
}
|
||||
|
||||
struct clipboard *
|
||||
clipboard_create(struct weston_seat *seat)
|
||||
{
|
||||
struct clipboard *clipboard;
|
||||
|
||||
clipboard = zalloc(sizeof *clipboard);
|
||||
if (clipboard == NULL)
|
||||
return NULL;
|
||||
|
||||
clipboard->seat = seat;
|
||||
clipboard->selection_listener.notify = clipboard_set_selection;
|
||||
clipboard->destroy_listener.notify = clipboard_destroy;
|
||||
|
||||
wl_signal_add(&seat->selection_signal,
|
||||
&clipboard->selection_listener);
|
||||
wl_signal_add(&seat->destroy_signal,
|
||||
&clipboard->destroy_listener);
|
||||
|
||||
return clipboard;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
* Copyright © 2011 Intel Corporation
|
||||
* Copyright © 2015 Giulio Camuffo
|
||||
*
|
||||
* 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_DRM_H
|
||||
#define WESTON_COMPOSITOR_DRM_H
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WESTON_DRM_BACKEND_CONFIG_VERSION 1
|
||||
|
||||
struct libinput_device;
|
||||
|
||||
enum weston_drm_backend_output_mode {
|
||||
/** The output is disabled */
|
||||
WESTON_DRM_BACKEND_OUTPUT_OFF,
|
||||
/** The output will use the current active mode */
|
||||
WESTON_DRM_BACKEND_OUTPUT_CURRENT,
|
||||
/** The output will use the preferred mode. A modeline can be provided
|
||||
* by setting weston_backend_output_config::modeline in the form of
|
||||
* "WIDTHxHEIGHT" or in the form of an explicit modeline calculated
|
||||
* using e.g. the cvt tool. If a valid modeline is supplied it will be
|
||||
* used, if invalid or NULL the preferred available mode will be used. */
|
||||
WESTON_DRM_BACKEND_OUTPUT_PREFERRED,
|
||||
};
|
||||
|
||||
struct weston_drm_backend_output_config {
|
||||
struct weston_backend_output_config base;
|
||||
|
||||
/** The pixel format to be used by the output. Valid values are:
|
||||
* - NULL - The format set at backend creation time will be used;
|
||||
* - "xrgb8888";
|
||||
* - "rgb565"
|
||||
* - "xrgb2101010"
|
||||
*/
|
||||
char *gbm_format;
|
||||
/** The seat to be used by the output. Set to NULL to use the
|
||||
* default seat. */
|
||||
char *seat;
|
||||
/** The modeline to be used by the output. Refer to the documentation
|
||||
* of WESTON_DRM_BACKEND_OUTPUT_PREFERRED for details. */
|
||||
char *modeline;
|
||||
};
|
||||
|
||||
/** The backend configuration struct.
|
||||
*
|
||||
* weston_drm_backend_config contains the configuration used by a DRM
|
||||
* backend.
|
||||
*/
|
||||
struct weston_drm_backend_config {
|
||||
struct weston_backend_config base;
|
||||
|
||||
/** The connector id of the output to be initialized.
|
||||
*
|
||||
* A value of 0 will enable all available outputs.
|
||||
*/
|
||||
int connector;
|
||||
|
||||
/** The tty to be used. Set to 0 to use the current tty. */
|
||||
int tty;
|
||||
|
||||
/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
|
||||
bool use_pixman;
|
||||
|
||||
/** The seat to be used for input and output.
|
||||
*
|
||||
* If NULL the default "seat0" will be used. The backend will
|
||||
* take ownership of the seat_id pointer and will free it on
|
||||
* backend destruction.
|
||||
*/
|
||||
char *seat_id;
|
||||
|
||||
/** The pixel format of the framebuffer to be used.
|
||||
*
|
||||
* Valid values are:
|
||||
* - NULL - The default format ("xrgb8888") will be used;
|
||||
* - "xrgb8888";
|
||||
* - "rgb565"
|
||||
* - "xrgb2101010"
|
||||
* The backend will take ownership of the format pointer and will free
|
||||
* it on backend destruction.
|
||||
*/
|
||||
char *gbm_format;
|
||||
|
||||
/** Callback used to configure the outputs.
|
||||
*
|
||||
* This function will be called by the backend when a new DRM
|
||||
* output needs to be configured.
|
||||
*/
|
||||
enum weston_drm_backend_output_mode
|
||||
(*configure_output)(struct weston_compositor *compositor,
|
||||
bool use_current_mode,
|
||||
const char *name,
|
||||
struct weston_drm_backend_output_config *output_config);
|
||||
|
||||
/** 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);
|
||||
bool use_current_mode;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_DRM_H */
|
||||
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
* 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 <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 <libudev.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "compositor.h"
|
||||
#include "compositor-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_output {
|
||||
struct fbdev_backend *backend;
|
||||
struct weston_output base;
|
||||
|
||||
struct weston_mode mode;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
|
||||
/* Frame buffer details. */
|
||||
char *device;
|
||||
struct fbdev_screeninfo fb_info;
|
||||
void *fb; /* length is fb_info.buffer_length */
|
||||
|
||||
/* pixman details. */
|
||||
pixman_image_t *hw_surface;
|
||||
uint8_t depth;
|
||||
};
|
||||
|
||||
static const char default_seat[] = "seat0";
|
||||
|
||||
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 void
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage)
|
||||
{
|
||||
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 and
|
||||
* ARGB 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;
|
||||
|
||||
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 */
|
||||
|
||||
return refresh_rate;
|
||||
}
|
||||
|
||||
return 60 * 1000; /* default to 60 Hz */
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_query_screen_info(struct fbdev_output *output, 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(struct fbdev_output *output, 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 void fbdev_frame_buffer_destroy(struct fbdev_output *output);
|
||||
|
||||
/* Returns an FD for the frame buffer device. */
|
||||
static int
|
||||
fbdev_frame_buffer_open(struct fbdev_output *output, 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(output, fd, screen_info) < 0) {
|
||||
weston_log("Failed to get frame buffer info: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Closes the FD on success or failure. */
|
||||
static int
|
||||
fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
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->fb = mmap(NULL, output->fb_info.buffer_length,
|
||||
PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (output->fb == MAP_FAILED) {
|
||||
weston_log("Failed to mmap frame buffer: %s\n",
|
||||
strerror(errno));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Create a pixman image to wrap the memory mapped frame buffer. */
|
||||
output->hw_surface =
|
||||
pixman_image_create_bits(output->fb_info.pixel_format,
|
||||
output->fb_info.x_resolution,
|
||||
output->fb_info.y_resolution,
|
||||
output->fb,
|
||||
output->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)
|
||||
fbdev_frame_buffer_destroy(output);
|
||||
|
||||
out_close:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_frame_buffer_destroy(struct fbdev_output *output)
|
||||
{
|
||||
weston_log("Destroying fbdev frame buffer.\n");
|
||||
|
||||
if (munmap(output->fb, output->fb_info.buffer_length) < 0)
|
||||
weston_log("Failed to munmap frame buffer: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
output->fb = NULL;
|
||||
}
|
||||
|
||||
static void fbdev_output_destroy(struct weston_output *base);
|
||||
static void fbdev_output_disable(struct weston_output *base);
|
||||
|
||||
static int
|
||||
fbdev_output_create(struct fbdev_backend *backend,
|
||||
const char *device)
|
||||
{
|
||||
struct fbdev_output *output;
|
||||
int fb_fd;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
weston_log("Creating fbdev output.\n");
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (output == NULL)
|
||||
return -1;
|
||||
|
||||
output->backend = backend;
|
||||
output->device = strdup(device);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(output, device, &output->fb_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer failed.\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
|
||||
weston_log("Mapping frame buffer failed.\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
|
||||
output->base.repaint = fbdev_output_repaint;
|
||||
output->base.destroy = fbdev_output_destroy;
|
||||
|
||||
/* only one static mode in list */
|
||||
output->mode.flags =
|
||||
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||
output->mode.width = output->fb_info.x_resolution;
|
||||
output->mode.height = output->fb_info.y_resolution;
|
||||
output->mode.refresh = output->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;
|
||||
output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
|
||||
output->base.make = "unknown";
|
||||
output->base.model = output->fb_info.id;
|
||||
output->base.name = strdup("fbdev");
|
||||
|
||||
weston_output_init(&output->base, backend->compositor,
|
||||
0, 0, output->fb_info.width_mm,
|
||||
output->fb_info.height_mm,
|
||||
backend->output_transform,
|
||||
1);
|
||||
|
||||
if (pixman_renderer_output_create(&output->base) < 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_compositor_add_output(backend->compositor, &output->base);
|
||||
|
||||
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:
|
||||
pixman_image_unref(output->hw_surface);
|
||||
output->hw_surface = NULL;
|
||||
weston_output_destroy(&output->base);
|
||||
fbdev_frame_buffer_destroy(output);
|
||||
out_free:
|
||||
free(output->device);
|
||||
free(output);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
|
||||
weston_log("Destroying fbdev output.\n");
|
||||
|
||||
/* Close the frame buffer. */
|
||||
fbdev_output_disable(base);
|
||||
|
||||
if (base->renderer_state != NULL)
|
||||
pixman_renderer_output_destroy(base);
|
||||
|
||||
/* Remove the output. */
|
||||
weston_output_destroy(&output->base);
|
||||
|
||||
free(output->device);
|
||||
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_screeninfo new_screen_info;
|
||||
int fb_fd;
|
||||
char *device;
|
||||
|
||||
weston_log("Re-enabling fbdev output.\n");
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(output, output->device,
|
||||
&new_screen_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer failed.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check whether the frame buffer details have changed since we were
|
||||
* disabled. */
|
||||
if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
|
||||
/* Perform a mode-set to restore the old mode. */
|
||||
if (fbdev_set_screen_info(output, fb_fd,
|
||||
&output->fb_info) < 0) {
|
||||
weston_log("Failed to restore mode settings. "
|
||||
"Attempting to re-open output anyway.\n");
|
||||
}
|
||||
|
||||
close(fb_fd);
|
||||
|
||||
/* Remove and re-add the output so that resources depending on
|
||||
* the frame buffer X/Y resolution (such as the shadow buffer)
|
||||
* are re-initialised. */
|
||||
device = strdup(output->device);
|
||||
fbdev_output_destroy(&output->base);
|
||||
fbdev_output_create(backend, device);
|
||||
free(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* NOTE: This leaves output->fb_info populated, caching data so that if
|
||||
* fbdev_output_reenable() is called again, it can determine whether a mode-set
|
||||
* is needed. */
|
||||
static void
|
||||
fbdev_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
|
||||
weston_log("Disabling fbdev output.\n");
|
||||
|
||||
if (output->hw_surface != NULL) {
|
||||
pixman_image_unref(output->hw_surface);
|
||||
output->hw_surface = NULL;
|
||||
}
|
||||
|
||||
fbdev_frame_buffer_destroy(output);
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_backend_destroy(struct weston_compositor *base)
|
||||
{
|
||||
struct fbdev_backend *backend = to_fbdev_backend(base);
|
||||
|
||||
udev_input_destroy(&backend->input);
|
||||
|
||||
/* Destroy the output. */
|
||||
weston_compositor_shutdown(base);
|
||||
|
||||
/* Chain up. */
|
||||
weston_launcher_destroy(base->launcher);
|
||||
|
||||
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_output_disable(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 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_restore(struct weston_compositor *compositor)
|
||||
{
|
||||
weston_launcher_restore(compositor->launcher);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
weston_log("initializing fbdev backend\n");
|
||||
|
||||
backend = zalloc(sizeof *backend);
|
||||
if (backend == NULL)
|
||||
return NULL;
|
||||
|
||||
backend->compositor = compositor;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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, "seat0", false);
|
||||
if (!compositor->launcher) {
|
||||
weston_log("fatal: fbdev backend should be run "
|
||||
"using weston-launch binary or as root\n");
|
||||
goto out_udev;
|
||||
}
|
||||
|
||||
backend->base.destroy = fbdev_backend_destroy;
|
||||
backend->base.restore = fbdev_restore;
|
||||
|
||||
backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
|
||||
backend->output_transform = param->output_transform;
|
||||
|
||||
weston_setup_vt_switch_bindings(compositor);
|
||||
|
||||
if (pixman_renderer_init(compositor) < 0)
|
||||
goto out_launcher;
|
||||
|
||||
if (fbdev_output_create(backend, param->device) < 0)
|
||||
goto out_launcher;
|
||||
|
||||
udev_input_init(&backend->input, compositor, backend->udev,
|
||||
seat_id, param->configure_device);
|
||||
|
||||
compositor->backend = &backend->base;
|
||||
return backend;
|
||||
|
||||
out_launcher:
|
||||
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)
|
||||
{
|
||||
/* TODO: Ideally, available frame buffers should be enumerated using
|
||||
* udev, rather than passing a device node in as a parameter. */
|
||||
config->tty = 0; /* default to current tty */
|
||||
config->device = "/dev/fb0"; /* default frame buffer */
|
||||
config->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
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,61 @@
|
||||
/*
|
||||
* 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 "compositor.h"
|
||||
|
||||
#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 1
|
||||
|
||||
struct libinput_device;
|
||||
|
||||
struct weston_fbdev_backend_config {
|
||||
struct weston_backend_config base;
|
||||
|
||||
int tty;
|
||||
char *device;
|
||||
|
||||
uint32_t output_transform;
|
||||
|
||||
/** 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);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_FBDEV_H */
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright © 2010-2011 Benjamin Franzke
|
||||
* Copyright © 2012 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "compositor-headless.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "pixman-renderer.h"
|
||||
#include "presentation-time-server-protocol.h"
|
||||
|
||||
struct headless_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
|
||||
struct weston_seat fake_seat;
|
||||
bool use_pixman;
|
||||
};
|
||||
|
||||
struct headless_output {
|
||||
struct weston_output base;
|
||||
|
||||
struct weston_mode mode;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
uint32_t *image_buf;
|
||||
pixman_image_t *image;
|
||||
};
|
||||
|
||||
static void
|
||||
headless_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);
|
||||
}
|
||||
|
||||
static int
|
||||
finish_frame_handler(void *data)
|
||||
{
|
||||
struct headless_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 int
|
||||
headless_output_repaint(struct weston_output *output_base,
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct headless_output *output = (struct headless_output *) output_base;
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
|
||||
ec->renderer->repaint_output(&output->base, damage);
|
||||
|
||||
pixman_region32_subtract(&ec->primary_plane.damage,
|
||||
&ec->primary_plane.damage, damage);
|
||||
|
||||
wl_event_source_timer_update(output->finish_frame_timer, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
headless_output_destroy(struct weston_output *output_base)
|
||||
{
|
||||
struct headless_output *output = (struct headless_output *) output_base;
|
||||
struct headless_backend *b =
|
||||
(struct headless_backend *) output->base.compositor->backend;
|
||||
|
||||
wl_event_source_remove(output->finish_frame_timer);
|
||||
|
||||
if (b->use_pixman) {
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
pixman_image_unref(output->image);
|
||||
free(output->image_buf);
|
||||
}
|
||||
|
||||
weston_output_destroy(&output->base);
|
||||
|
||||
free(output);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
headless_backend_create_output(struct headless_backend *b,
|
||||
struct weston_headless_backend_config *config)
|
||||
{
|
||||
struct weston_compositor *c = b->compositor;
|
||||
struct headless_output *output;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (output == NULL)
|
||||
return -1;
|
||||
|
||||
output->mode.flags =
|
||||
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||
output->mode.width = config->width;
|
||||
output->mode.height = config->height;
|
||||
output->mode.refresh = 60000;
|
||||
wl_list_init(&output->base.mode_list);
|
||||
wl_list_insert(&output->base.mode_list, &output->mode.link);
|
||||
|
||||
output->base.current_mode = &output->mode;
|
||||
weston_output_init(&output->base, c, 0, 0, config->width,
|
||||
config->height, config->transform, 1);
|
||||
|
||||
output->base.make = "weston";
|
||||
output->base.model = "headless";
|
||||
|
||||
loop = wl_display_get_event_loop(c->wl_display);
|
||||
output->finish_frame_timer =
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
|
||||
output->base.start_repaint_loop = headless_output_start_repaint_loop;
|
||||
output->base.repaint = headless_output_repaint;
|
||||
output->base.destroy = headless_output_destroy;
|
||||
output->base.assign_planes = NULL;
|
||||
output->base.set_backlight = NULL;
|
||||
output->base.set_dpms = NULL;
|
||||
output->base.switch_mode = NULL;
|
||||
|
||||
if (b->use_pixman) {
|
||||
output->image_buf = malloc(config->width * config->height * 4);
|
||||
if (!output->image_buf)
|
||||
return -1;
|
||||
|
||||
output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8,
|
||||
config->width,
|
||||
config->height,
|
||||
output->image_buf,
|
||||
config->width * 4);
|
||||
|
||||
if (pixman_renderer_output_create(&output->base) < 0)
|
||||
return -1;
|
||||
|
||||
pixman_renderer_output_set_buffer(&output->base,
|
||||
output->image);
|
||||
}
|
||||
|
||||
weston_compositor_add_output(c, &output->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
headless_restore(struct weston_compositor *ec)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
headless_destroy(struct weston_compositor *ec)
|
||||
{
|
||||
struct headless_backend *b = (struct headless_backend *) ec->backend;
|
||||
|
||||
weston_compositor_shutdown(ec);
|
||||
|
||||
free(b);
|
||||
}
|
||||
|
||||
static struct headless_backend *
|
||||
headless_backend_create(struct weston_compositor *compositor,
|
||||
struct weston_headless_backend_config *config)
|
||||
{
|
||||
struct headless_backend *b;
|
||||
|
||||
b = zalloc(sizeof *b);
|
||||
if (b == NULL)
|
||||
return NULL;
|
||||
|
||||
b->compositor = compositor;
|
||||
if (weston_compositor_set_presentation_clock_software(compositor) < 0)
|
||||
goto err_free;
|
||||
|
||||
b->base.destroy = headless_destroy;
|
||||
b->base.restore = headless_restore;
|
||||
|
||||
b->use_pixman = config->use_pixman;
|
||||
if (b->use_pixman) {
|
||||
pixman_renderer_init(compositor);
|
||||
}
|
||||
if (headless_backend_create_output(b, config) < 0)
|
||||
goto err_input;
|
||||
|
||||
if (!b->use_pixman && noop_renderer_init(compositor) < 0)
|
||||
goto err_input;
|
||||
|
||||
compositor->backend = &b->base;
|
||||
return b;
|
||||
|
||||
err_input:
|
||||
weston_compositor_shutdown(compositor);
|
||||
err_free:
|
||||
free(b);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
config_init_to_defaults(struct weston_headless_backend_config *config)
|
||||
{
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
backend_init(struct weston_compositor *compositor,
|
||||
struct weston_backend_config *config_base)
|
||||
{
|
||||
struct headless_backend *b;
|
||||
struct weston_headless_backend_config config = {{ 0, }};
|
||||
|
||||
if (config_base == NULL ||
|
||||
config_base->struct_version != WESTON_HEADLESS_BACKEND_CONFIG_VERSION ||
|
||||
config_base->struct_size > sizeof(struct weston_headless_backend_config)) {
|
||||
weston_log("headless backend config structure is invalid\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
config_init_to_defaults(&config);
|
||||
memcpy(&config, config_base, config_base->struct_size);
|
||||
|
||||
b = headless_backend_create(compositor, &config);
|
||||
if (b == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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_HEADLESS_H
|
||||
#define WESTON_COMPOSITOR_HEADLESS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#define WESTON_HEADLESS_BACKEND_CONFIG_VERSION 1
|
||||
|
||||
struct weston_headless_backend_config {
|
||||
struct weston_backend_config base;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
|
||||
int use_pixman;
|
||||
|
||||
uint32_t transform;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_HEADLESS_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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_RDP_H
|
||||
#define WESTON_COMPOSITOR_RDP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#define WESTON_RDP_BACKEND_CONFIG_VERSION 1
|
||||
|
||||
struct weston_rdp_backend_config {
|
||||
struct weston_backend_config base;
|
||||
int width;
|
||||
int height;
|
||||
char *bind_address;
|
||||
int port;
|
||||
char *rdp_key;
|
||||
char *server_cert;
|
||||
char *server_key;
|
||||
int env_socket;
|
||||
int no_clients_resize;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_RDP_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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_WAYLAND_H
|
||||
#define WESTON_COMPOSITOR_WAYLAND_H
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WESTON_WAYLAND_BACKEND_CONFIG_VERSION 1
|
||||
|
||||
struct weston_wayland_backend_output_config {
|
||||
int width;
|
||||
int height;
|
||||
char *name;
|
||||
uint32_t transform;
|
||||
int32_t scale;
|
||||
};
|
||||
|
||||
struct weston_wayland_backend_config {
|
||||
struct weston_backend_config base;
|
||||
int use_pixman;
|
||||
int sprawl;
|
||||
char *display_name;
|
||||
int fullscreen;
|
||||
char *cursor_theme;
|
||||
int cursor_size;
|
||||
int num_outputs;
|
||||
struct weston_wayland_backend_output_config *outputs;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_WAYLAND_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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_X11_H
|
||||
#define WESTON_COMPOSITOR_X11_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#define WESTON_X11_BACKEND_CONFIG_VERSION 1
|
||||
|
||||
struct weston_x11_backend_output_config {
|
||||
int width;
|
||||
int height;
|
||||
char *name;
|
||||
uint32_t transform;
|
||||
int32_t scale;
|
||||
};
|
||||
|
||||
struct weston_x11_backend_config {
|
||||
struct weston_backend_config base;
|
||||
|
||||
bool fullscreen;
|
||||
bool no_input;
|
||||
|
||||
/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
|
||||
bool use_pixman;
|
||||
|
||||
uint32_t num_outputs;
|
||||
struct weston_x11_backend_output_config *outputs;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_X11_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DBus Helpers
|
||||
* This file contains the dbus mainloop integration and several helpers to
|
||||
* make lowlevel libdbus access easier.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "dbus.h"
|
||||
|
||||
/*
|
||||
* DBus Mainloop Integration
|
||||
* weston_dbus_bind() and weston_dbus_unbind() allow to bind an existing
|
||||
* DBusConnection to an existing wl_event_loop object. All dbus dispatching
|
||||
* is then nicely integrated into the wayland event loop.
|
||||
* Note that this only provides basic watch and timeout dispatching. No
|
||||
* remote thread wakeup, signal handling or other dbus insanity is supported.
|
||||
* This is fine as long as you don't use any of the deprecated libdbus
|
||||
* interfaces (like waking up remote threads..). There is really no rational
|
||||
* reason to support these.
|
||||
*/
|
||||
|
||||
static int weston_dbus_dispatch_watch(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
DBusWatch *watch = data;
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (dbus_watch_get_enabled(watch)) {
|
||||
if (mask & WL_EVENT_READABLE)
|
||||
flags |= DBUS_WATCH_READABLE;
|
||||
if (mask & WL_EVENT_WRITABLE)
|
||||
flags |= DBUS_WATCH_WRITABLE;
|
||||
if (mask & WL_EVENT_HANGUP)
|
||||
flags |= DBUS_WATCH_HANGUP;
|
||||
if (mask & WL_EVENT_ERROR)
|
||||
flags |= DBUS_WATCH_ERROR;
|
||||
|
||||
dbus_watch_handle(watch, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static dbus_bool_t weston_dbus_add_watch(DBusWatch *watch, void *data)
|
||||
{
|
||||
struct wl_event_loop *loop = data;
|
||||
struct wl_event_source *s;
|
||||
int fd;
|
||||
uint32_t mask = 0, flags;
|
||||
|
||||
if (dbus_watch_get_enabled(watch)) {
|
||||
flags = dbus_watch_get_flags(watch);
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
mask |= WL_EVENT_READABLE;
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
mask |= WL_EVENT_WRITABLE;
|
||||
}
|
||||
|
||||
fd = dbus_watch_get_unix_fd(watch);
|
||||
s = wl_event_loop_add_fd(loop, fd, mask, weston_dbus_dispatch_watch,
|
||||
watch);
|
||||
if (!s)
|
||||
return FALSE;
|
||||
|
||||
dbus_watch_set_data(watch, s, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void weston_dbus_remove_watch(DBusWatch *watch, void *data)
|
||||
{
|
||||
struct wl_event_source *s;
|
||||
|
||||
s = dbus_watch_get_data(watch);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
wl_event_source_remove(s);
|
||||
}
|
||||
|
||||
static void weston_dbus_toggle_watch(DBusWatch *watch, void *data)
|
||||
{
|
||||
struct wl_event_source *s;
|
||||
uint32_t mask = 0, flags;
|
||||
|
||||
s = dbus_watch_get_data(watch);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
if (dbus_watch_get_enabled(watch)) {
|
||||
flags = dbus_watch_get_flags(watch);
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
mask |= WL_EVENT_READABLE;
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
mask |= WL_EVENT_WRITABLE;
|
||||
}
|
||||
|
||||
wl_event_source_fd_update(s, mask);
|
||||
}
|
||||
|
||||
static int weston_dbus_dispatch_timeout(void *data)
|
||||
{
|
||||
DBusTimeout *timeout = data;
|
||||
|
||||
if (dbus_timeout_get_enabled(timeout))
|
||||
dbus_timeout_handle(timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int weston_dbus_adjust_timeout(DBusTimeout *timeout,
|
||||
struct wl_event_source *s)
|
||||
{
|
||||
int64_t t = 0;
|
||||
|
||||
if (dbus_timeout_get_enabled(timeout))
|
||||
t = dbus_timeout_get_interval(timeout);
|
||||
|
||||
return wl_event_source_timer_update(s, t);
|
||||
}
|
||||
|
||||
static dbus_bool_t weston_dbus_add_timeout(DBusTimeout *timeout, void *data)
|
||||
{
|
||||
struct wl_event_loop *loop = data;
|
||||
struct wl_event_source *s;
|
||||
int r;
|
||||
|
||||
s = wl_event_loop_add_timer(loop, weston_dbus_dispatch_timeout,
|
||||
timeout);
|
||||
if (!s)
|
||||
return FALSE;
|
||||
|
||||
r = weston_dbus_adjust_timeout(timeout, s);
|
||||
if (r < 0) {
|
||||
wl_event_source_remove(s);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dbus_timeout_set_data(timeout, s, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void weston_dbus_remove_timeout(DBusTimeout *timeout, void *data)
|
||||
{
|
||||
struct wl_event_source *s;
|
||||
|
||||
s = dbus_timeout_get_data(timeout);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
wl_event_source_remove(s);
|
||||
}
|
||||
|
||||
static void weston_dbus_toggle_timeout(DBusTimeout *timeout, void *data)
|
||||
{
|
||||
struct wl_event_source *s;
|
||||
|
||||
s = dbus_timeout_get_data(timeout);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
weston_dbus_adjust_timeout(timeout, s);
|
||||
}
|
||||
|
||||
static int weston_dbus_dispatch(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
DBusConnection *c = data;
|
||||
int r;
|
||||
|
||||
do {
|
||||
r = dbus_connection_dispatch(c);
|
||||
if (r == DBUS_DISPATCH_COMPLETE)
|
||||
r = 0;
|
||||
else if (r == DBUS_DISPATCH_DATA_REMAINS)
|
||||
r = -EAGAIN;
|
||||
else if (r == DBUS_DISPATCH_NEED_MEMORY)
|
||||
r = -ENOMEM;
|
||||
else
|
||||
r = -EIO;
|
||||
} while (r == -EAGAIN);
|
||||
|
||||
if (r)
|
||||
weston_log("cannot dispatch dbus events: %d\n", r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int weston_dbus_bind(struct wl_event_loop *loop, DBusConnection *c,
|
||||
struct wl_event_source **ctx_out)
|
||||
{
|
||||
bool b;
|
||||
int r, fd;
|
||||
|
||||
/* Idle events cannot reschedule themselves, therefore we use a dummy
|
||||
* event-fd and mark it for post-dispatch. Hence, the dbus
|
||||
* dispatcher is called after every dispatch-round.
|
||||
* This is required as dbus doesn't allow dispatching events from
|
||||
* within its own event sources. */
|
||||
fd = eventfd(0, EFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
*ctx_out = wl_event_loop_add_fd(loop, fd, 0, weston_dbus_dispatch, c);
|
||||
close(fd);
|
||||
|
||||
if (!*ctx_out)
|
||||
return -ENOMEM;
|
||||
|
||||
wl_event_source_check(*ctx_out);
|
||||
|
||||
b = dbus_connection_set_watch_functions(c,
|
||||
weston_dbus_add_watch,
|
||||
weston_dbus_remove_watch,
|
||||
weston_dbus_toggle_watch,
|
||||
loop,
|
||||
NULL);
|
||||
if (!b) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
b = dbus_connection_set_timeout_functions(c,
|
||||
weston_dbus_add_timeout,
|
||||
weston_dbus_remove_timeout,
|
||||
weston_dbus_toggle_timeout,
|
||||
loop,
|
||||
NULL);
|
||||
if (!b) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dbus_connection_ref(c);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dbus_connection_set_timeout_functions(c, NULL, NULL, NULL,
|
||||
NULL, NULL);
|
||||
dbus_connection_set_watch_functions(c, NULL, NULL, NULL,
|
||||
NULL, NULL);
|
||||
wl_event_source_remove(*ctx_out);
|
||||
*ctx_out = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void weston_dbus_unbind(DBusConnection *c, struct wl_event_source *ctx)
|
||||
{
|
||||
dbus_connection_set_timeout_functions(c, NULL, NULL, NULL,
|
||||
NULL, NULL);
|
||||
dbus_connection_set_watch_functions(c, NULL, NULL, NULL,
|
||||
NULL, NULL);
|
||||
dbus_connection_unref(c);
|
||||
wl_event_source_remove(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience Helpers
|
||||
* Several convenience helpers are provided to make using dbus in weston
|
||||
* easier. We don't use any of the gdbus or qdbus helpers as they pull in
|
||||
* huge dependencies and actually are quite awful to use. Instead, we only
|
||||
* use the basic low-level libdbus library.
|
||||
*/
|
||||
|
||||
int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus,
|
||||
DBusConnection **out, struct wl_event_source **ctx_out)
|
||||
{
|
||||
DBusConnection *c;
|
||||
int r;
|
||||
|
||||
/* Ihhh, global state.. stupid dbus. */
|
||||
dbus_connection_set_change_sigpipe(FALSE);
|
||||
|
||||
/* This is actually synchronous. It blocks for some authentication and
|
||||
* setup. We just trust the dbus-server here and accept this blocking
|
||||
* call. There is no real reason to complicate things further and make
|
||||
* this asynchronous/non-blocking. A context should be created during
|
||||
* thead/process/app setup, so blocking calls should be fine. */
|
||||
c = dbus_bus_get_private(bus, NULL);
|
||||
if (!c)
|
||||
return -EIO;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(c, FALSE);
|
||||
|
||||
r = weston_dbus_bind(loop, c, ctx_out);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
*out = c;
|
||||
return r;
|
||||
|
||||
error:
|
||||
dbus_connection_close(c);
|
||||
dbus_connection_unref(c);
|
||||
return r;
|
||||
}
|
||||
|
||||
void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx)
|
||||
{
|
||||
weston_dbus_unbind(c, ctx);
|
||||
dbus_connection_close(c);
|
||||
dbus_connection_unref(c);
|
||||
}
|
||||
|
||||
int weston_dbus_add_match(DBusConnection *c, const char *format, ...)
|
||||
{
|
||||
DBusError err;
|
||||
int r;
|
||||
va_list list;
|
||||
char *str;
|
||||
|
||||
va_start(list, format);
|
||||
r = vasprintf(&str, format, list);
|
||||
va_end(list);
|
||||
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
dbus_error_init(&err);
|
||||
dbus_bus_add_match(c, str, &err);
|
||||
free(str);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
dbus_error_free(&err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int weston_dbus_add_match_signal(DBusConnection *c, const char *sender,
|
||||
const char *iface, const char *member,
|
||||
const char *path)
|
||||
{
|
||||
return weston_dbus_add_match(c,
|
||||
"type='signal',"
|
||||
"sender='%s',"
|
||||
"interface='%s',"
|
||||
"member='%s',"
|
||||
"path='%s'",
|
||||
sender, iface, member, path);
|
||||
}
|
||||
|
||||
void weston_dbus_remove_match(DBusConnection *c, const char *format, ...)
|
||||
{
|
||||
int r;
|
||||
va_list list;
|
||||
char *str;
|
||||
|
||||
va_start(list, format);
|
||||
r = vasprintf(&str, format, list);
|
||||
va_end(list);
|
||||
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
dbus_bus_remove_match(c, str, NULL);
|
||||
free(str);
|
||||
}
|
||||
|
||||
void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender,
|
||||
const char *iface, const char *member,
|
||||
const char *path)
|
||||
{
|
||||
return weston_dbus_remove_match(c,
|
||||
"type='signal',"
|
||||
"sender='%s',"
|
||||
"interface='%s',"
|
||||
"member='%s',"
|
||||
"path='%s'",
|
||||
sender, iface, member, path);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* 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_DBUS_H_
|
||||
#define _WESTON_DBUS_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
/*
|
||||
* weston_dbus_open() - Open new dbus connection
|
||||
*
|
||||
* Opens a new dbus connection to the bus given as @bus. It automatically
|
||||
* integrates the new connection into the main-loop @loop. The connection
|
||||
* itself is returned in @out.
|
||||
* This also returns a context source used for dbus dispatching. It is
|
||||
* returned on success in @ctx_out and must be passed to weston_dbus_close()
|
||||
* unchanged. You must not access it from outside of a dbus helper!
|
||||
*
|
||||
* Returns 0 on success, negative error code on failure.
|
||||
*/
|
||||
int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus,
|
||||
DBusConnection **out, struct wl_event_source **ctx_out);
|
||||
|
||||
/*
|
||||
* weston_dbus_close() - Close dbus connection
|
||||
*
|
||||
* Closes a dbus connection that was previously opened via weston_dbus_open().
|
||||
* It unbinds the connection from the main-loop it was previously bound to,
|
||||
* closes the dbus connection and frees all resources. If you want to access
|
||||
* @c after this call returns, you must hold a dbus-reference to it. But
|
||||
* notice that the connection is closed after this returns so it cannot be
|
||||
* used to spawn new dbus requests.
|
||||
* You must pass the context source returns by weston_dbus_open() as @ctx.
|
||||
*/
|
||||
void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx);
|
||||
|
||||
/*
|
||||
* weston_dbus_add_match() - Add dbus match
|
||||
*
|
||||
* Configure a dbus-match on the given dbus-connection. This match is saved
|
||||
* on the dbus-server as long as the connection is open. See dbus-manual
|
||||
* for information. Compared to the dbus_bus_add_match() this allows a
|
||||
* var-arg formatted match-string.
|
||||
*/
|
||||
int weston_dbus_add_match(DBusConnection *c, const char *format, ...);
|
||||
|
||||
/*
|
||||
* weston_dbus_add_match_signal() - Add dbus signal match
|
||||
*
|
||||
* Same as weston_dbus_add_match() but does the dbus-match formatting for
|
||||
* signals internally.
|
||||
*/
|
||||
int weston_dbus_add_match_signal(DBusConnection *c, const char *sender,
|
||||
const char *iface, const char *member,
|
||||
const char *path);
|
||||
|
||||
/*
|
||||
* weston_dbus_remove_match() - Remove dbus match
|
||||
*
|
||||
* Remove a previously configured dbus-match from the dbus server. There is
|
||||
* no need to remove dbus-matches if you close the connection, anyway.
|
||||
* Compared to dbus_bus_remove_match() this allows a var-arg formatted
|
||||
* match string.
|
||||
*/
|
||||
void weston_dbus_remove_match(DBusConnection *c, const char *format, ...);
|
||||
|
||||
/*
|
||||
* weston_dbus_remove_match_signal() - Remove dbus signal match
|
||||
*
|
||||
* Same as weston_dbus_remove_match() but does the dbus-match formatting for
|
||||
* signals internally.
|
||||
*/
|
||||
void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender,
|
||||
const char *iface, const char *member,
|
||||
const char *path);
|
||||
|
||||
#endif /* HAVE_DBUS */
|
||||
|
||||
#endif // _WESTON_DBUS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright © 2012 John Kåre Alsaker
|
||||
*
|
||||
* 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 "compositor.h"
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#else
|
||||
|
||||
typedef int EGLint;
|
||||
typedef int EGLenum;
|
||||
typedef void *EGLDisplay;
|
||||
typedef void *EGLSurface;
|
||||
typedef void *EGLConfig;
|
||||
typedef intptr_t EGLNativeDisplayType;
|
||||
typedef intptr_t EGLNativeWindowType;
|
||||
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
|
||||
|
||||
#endif /* ENABLE_EGL */
|
||||
|
||||
#ifndef EGL_EXT_platform_base
|
||||
typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
|
||||
typedef EGLSurface (*PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
|
||||
#endif
|
||||
|
||||
#ifndef EGL_PLATFORM_GBM_KHR
|
||||
#define EGL_PLATFORM_GBM_KHR 0x31D7
|
||||
#endif
|
||||
|
||||
#ifndef EGL_PLATFORM_WAYLAND_KHR
|
||||
#define EGL_PLATFORM_WAYLAND_KHR 0x31D8
|
||||
#endif
|
||||
|
||||
#ifndef EGL_PLATFORM_X11_KHR
|
||||
#define EGL_PLATFORM_X11_KHR 0x31D5
|
||||
#endif
|
||||
|
||||
#define NO_EGL_PLATFORM 0
|
||||
|
||||
enum gl_renderer_border_side {
|
||||
GL_RENDERER_BORDER_TOP = 0,
|
||||
GL_RENDERER_BORDER_LEFT = 1,
|
||||
GL_RENDERER_BORDER_RIGHT = 2,
|
||||
GL_RENDERER_BORDER_BOTTOM = 3,
|
||||
};
|
||||
|
||||
struct gl_renderer_interface {
|
||||
const EGLint *opaque_attribs;
|
||||
const EGLint *alpha_attribs;
|
||||
|
||||
int (*create)(struct weston_compositor *ec,
|
||||
EGLenum platform,
|
||||
void *native_window,
|
||||
const EGLint *attribs,
|
||||
const EGLint *visual_id,
|
||||
const int n_ids);
|
||||
|
||||
EGLDisplay (*display)(struct weston_compositor *ec);
|
||||
|
||||
int (*output_create)(struct weston_output *output,
|
||||
EGLNativeWindowType window_for_legacy,
|
||||
void *window_for_platform,
|
||||
const EGLint *attribs,
|
||||
const EGLint *visual_id,
|
||||
const int n_ids);
|
||||
|
||||
void (*output_destroy)(struct weston_output *output);
|
||||
|
||||
EGLSurface (*output_surface)(struct weston_output *output);
|
||||
|
||||
/* Sets the output border.
|
||||
*
|
||||
* The side specifies the side for which we are setting the border.
|
||||
* The width and height are the width and height of the border.
|
||||
* The tex_width patemeter specifies the width of the actual
|
||||
* texture; this may be larger than width if the data is not
|
||||
* tightly packed.
|
||||
*
|
||||
* The top and bottom textures will extend over the sides to the
|
||||
* full width of the bordered window. The right and left edges,
|
||||
* however, will extend only to the top and bottom of the
|
||||
* compositor surface. This is demonstrated by the picture below:
|
||||
*
|
||||
* +-----------------------+
|
||||
* | TOP |
|
||||
* +-+-------------------+-+
|
||||
* | | | |
|
||||
* |L| |R|
|
||||
* |E| |I|
|
||||
* |F| |G|
|
||||
* |T| |H|
|
||||
* | | |T|
|
||||
* | | | |
|
||||
* +-+-------------------+-+
|
||||
* | BOTTOM |
|
||||
* +-----------------------+
|
||||
*/
|
||||
void (*output_set_border)(struct weston_output *output,
|
||||
enum gl_renderer_border_side side,
|
||||
int32_t width, int32_t height,
|
||||
int32_t tex_width, unsigned char *data);
|
||||
|
||||
void (*print_egl_error_state)(void);
|
||||
};
|
||||
|
||||
+2765
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
* Copyright © 2013 Intel Corporation
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/vt.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/major.h>
|
||||
|
||||
#include "launcher-impl.h"
|
||||
|
||||
#define DRM_MAJOR 226
|
||||
|
||||
#ifndef KDSKBMUTE
|
||||
#define KDSKBMUTE 0x4B51
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBDRM
|
||||
|
||||
#include <xf86drm.h>
|
||||
|
||||
static inline int
|
||||
is_drm_master(int drm_fd)
|
||||
{
|
||||
drm_magic_t magic;
|
||||
|
||||
return drmGetMagic(drm_fd, &magic) == 0 &&
|
||||
drmAuthMagic(drm_fd, magic) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
drmDropMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drmSetMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
is_drm_master(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct launcher_direct {
|
||||
struct weston_launcher base;
|
||||
struct weston_compositor *compositor;
|
||||
int kb_mode, tty, drm_fd;
|
||||
struct wl_event_source *vt_source;
|
||||
};
|
||||
|
||||
static int
|
||||
vt_handler(int signal_number, void *data)
|
||||
{
|
||||
struct launcher_direct *launcher = data;
|
||||
struct weston_compositor *compositor = launcher->compositor;
|
||||
|
||||
if (compositor->session_active) {
|
||||
compositor->session_active = 0;
|
||||
wl_signal_emit(&compositor->session_signal, compositor);
|
||||
drmDropMaster(launcher->drm_fd);
|
||||
ioctl(launcher->tty, VT_RELDISP, 1);
|
||||
} else {
|
||||
ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ);
|
||||
drmSetMaster(launcher->drm_fd);
|
||||
compositor->session_active = 1;
|
||||
wl_signal_emit(&compositor->session_signal, compositor);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_tty(struct launcher_direct *launcher, int tty)
|
||||
{
|
||||
struct wl_event_loop *loop;
|
||||
struct vt_mode mode = { 0 };
|
||||
struct stat buf;
|
||||
char tty_device[32] ="<stdin>";
|
||||
int ret, kd_mode;
|
||||
|
||||
if (tty == 0) {
|
||||
launcher->tty = dup(tty);
|
||||
if (launcher->tty == -1) {
|
||||
weston_log("couldn't dup stdin: %m\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
snprintf(tty_device, sizeof tty_device, "/dev/tty%d", tty);
|
||||
launcher->tty = open(tty_device, O_RDWR | O_CLOEXEC);
|
||||
if (launcher->tty == -1) {
|
||||
weston_log("couldn't open tty %s: %m\n", tty_device);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fstat(launcher->tty, &buf) == -1 ||
|
||||
major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) {
|
||||
weston_log("%s not a vt\n", tty_device);
|
||||
weston_log("if running weston from ssh, "
|
||||
"use --tty to specify a tty\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
ret = ioctl(launcher->tty, KDGETMODE, &kd_mode);
|
||||
if (ret) {
|
||||
weston_log("failed to get VT mode: %m\n");
|
||||
return -1;
|
||||
}
|
||||
if (kd_mode != KD_TEXT) {
|
||||
weston_log("%s is already in graphics mode, "
|
||||
"is another display server running?\n", tty_device);
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
ioctl(launcher->tty, VT_ACTIVATE, minor(buf.st_rdev));
|
||||
ioctl(launcher->tty, VT_WAITACTIVE, minor(buf.st_rdev));
|
||||
|
||||
if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) {
|
||||
weston_log("failed to read keyboard mode: %m\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
if (ioctl(launcher->tty, KDSKBMUTE, 1) &&
|
||||
ioctl(launcher->tty, KDSKBMODE, K_OFF)) {
|
||||
weston_log("failed to set K_OFF keyboard mode: %m\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS);
|
||||
if (ret) {
|
||||
weston_log("failed to set KD_GRAPHICS mode on tty: %m\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGRTMIN is used as global VT-acquire+release signal. Note that
|
||||
* SIGRT* must be tested on runtime, as their exact values are not
|
||||
* known at compile-time. POSIX requires 32 of them to be available.
|
||||
*/
|
||||
if (SIGRTMIN > SIGRTMAX) {
|
||||
weston_log("not enough RT signals available: %u-%u\n",
|
||||
SIGRTMIN, SIGRTMAX);
|
||||
ret = -EINVAL;
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
mode.mode = VT_PROCESS;
|
||||
mode.relsig = SIGRTMIN;
|
||||
mode.acqsig = SIGRTMIN;
|
||||
if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) {
|
||||
weston_log("failed to take control of vt handling\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
loop = wl_display_get_event_loop(launcher->compositor->wl_display);
|
||||
launcher->vt_source =
|
||||
wl_event_loop_add_signal(loop, SIGRTMIN, vt_handler, launcher);
|
||||
if (!launcher->vt_source)
|
||||
goto err_close;
|
||||
|
||||
return 0;
|
||||
|
||||
err_close:
|
||||
close(launcher->tty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_direct_open(struct weston_launcher *launcher_base, const char *path, int flags)
|
||||
{
|
||||
struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
struct stat s;
|
||||
int fd;
|
||||
|
||||
fd = open(path, flags | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
if (fstat(fd, &s) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (major(s.st_rdev) == DRM_MAJOR) {
|
||||
launcher->drm_fd = fd;
|
||||
if (!is_drm_master(fd)) {
|
||||
weston_log("drm fd not master\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_direct_close(struct weston_launcher *launcher_base, int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_direct_restore(struct weston_launcher *launcher_base)
|
||||
{
|
||||
struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
struct vt_mode mode = { 0 };
|
||||
|
||||
if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
|
||||
ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
|
||||
weston_log("failed to restore kb mode: %m\n");
|
||||
|
||||
if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
|
||||
weston_log("failed to set KD_TEXT mode on tty: %m\n");
|
||||
|
||||
/* We have to drop master before we switch the VT back in
|
||||
* VT_AUTO, so we don't risk switching to a VT with another
|
||||
* display server, that will then fail to set drm master. */
|
||||
drmDropMaster(launcher->drm_fd);
|
||||
|
||||
mode.mode = VT_AUTO;
|
||||
if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
|
||||
weston_log("could not reset vt handling\n");
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_direct_activate_vt(struct weston_launcher *launcher_base, int vt)
|
||||
{
|
||||
struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
return ioctl(launcher->tty, VT_ACTIVATE, vt);
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_direct_connect(struct weston_launcher **out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm)
|
||||
{
|
||||
struct launcher_direct *launcher;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return -EINVAL;
|
||||
|
||||
launcher = zalloc(sizeof(*launcher));
|
||||
if (launcher == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
launcher->base.iface = &launcher_direct_iface;
|
||||
launcher->compositor = compositor;
|
||||
|
||||
if (setup_tty(launcher, tty) == -1) {
|
||||
free(launcher);
|
||||
return -1;
|
||||
}
|
||||
|
||||
* (struct launcher_direct **) out = launcher;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_direct_destroy(struct weston_launcher *launcher_base)
|
||||
{
|
||||
struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
|
||||
launcher_direct_restore(&launcher->base);
|
||||
wl_event_source_remove(launcher->vt_source);
|
||||
|
||||
if (launcher->tty >= 0)
|
||||
close(launcher->tty);
|
||||
|
||||
free(launcher);
|
||||
}
|
||||
|
||||
struct launcher_interface launcher_direct_iface = {
|
||||
launcher_direct_connect,
|
||||
launcher_direct_destroy,
|
||||
launcher_direct_open,
|
||||
launcher_direct_close,
|
||||
launcher_direct_activate_vt,
|
||||
launcher_direct_restore,
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright © 2015 Jasper St. Pierre
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
struct weston_launcher;
|
||||
|
||||
struct launcher_interface {
|
||||
int (* connect) (struct weston_launcher **launcher_out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm);
|
||||
void (* destroy) (struct weston_launcher *launcher);
|
||||
int (* open) (struct weston_launcher *launcher, const char *path, int flags);
|
||||
void (* close) (struct weston_launcher *launcher, int fd);
|
||||
int (* activate_vt) (struct weston_launcher *launcher, int vt);
|
||||
void (* restore) (struct weston_launcher *launcher);
|
||||
};
|
||||
|
||||
struct weston_launcher {
|
||||
struct launcher_interface *iface;
|
||||
};
|
||||
|
||||
extern struct launcher_interface launcher_logind_iface;
|
||||
extern struct launcher_interface launcher_weston_launch_iface;
|
||||
extern struct launcher_interface launcher_direct_iface;
|
||||
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
* Copyright © 2013 David Herrmann
|
||||
*
|
||||
* 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 <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "dbus.h"
|
||||
#include "launcher-impl.h"
|
||||
|
||||
#define DRM_MAJOR 226
|
||||
|
||||
struct launcher_logind {
|
||||
struct weston_launcher base;
|
||||
struct weston_compositor *compositor;
|
||||
bool sync_drm;
|
||||
char *seat;
|
||||
char *sid;
|
||||
unsigned int vtnr;
|
||||
int vt;
|
||||
int kb_mode;
|
||||
|
||||
DBusConnection *dbus;
|
||||
struct wl_event_source *dbus_ctx;
|
||||
char *spath;
|
||||
DBusPendingCall *pending_active;
|
||||
};
|
||||
|
||||
static int
|
||||
launcher_logind_take_device(struct launcher_logind *wl, uint32_t major,
|
||||
uint32_t minor, bool *paused_out)
|
||||
{
|
||||
DBusMessage *m, *reply;
|
||||
bool b;
|
||||
int r, fd;
|
||||
dbus_bool_t paused;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.login1.Session",
|
||||
"TakeDevice");
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
b = dbus_message_append_args(m,
|
||||
DBUS_TYPE_UINT32, &major,
|
||||
DBUS_TYPE_UINT32, &minor,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!b) {
|
||||
r = -ENOMEM;
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
reply = dbus_connection_send_with_reply_and_block(wl->dbus, m,
|
||||
-1, NULL);
|
||||
if (!reply) {
|
||||
r = -ENODEV;
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
b = dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_UNIX_FD, &fd,
|
||||
DBUS_TYPE_BOOLEAN, &paused,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!b) {
|
||||
r = -ENODEV;
|
||||
goto err_reply;
|
||||
}
|
||||
|
||||
r = fd;
|
||||
if (paused_out)
|
||||
*paused_out = paused;
|
||||
|
||||
err_reply:
|
||||
dbus_message_unref(reply);
|
||||
err_unref:
|
||||
dbus_message_unref(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_release_device(struct launcher_logind *wl, uint32_t major,
|
||||
uint32_t minor)
|
||||
{
|
||||
DBusMessage *m;
|
||||
bool b;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.login1.Session",
|
||||
"ReleaseDevice");
|
||||
if (m) {
|
||||
b = dbus_message_append_args(m,
|
||||
DBUS_TYPE_UINT32, &major,
|
||||
DBUS_TYPE_UINT32, &minor,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (b)
|
||||
dbus_connection_send(wl->dbus, m, NULL);
|
||||
dbus_message_unref(m);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_pause_device_complete(struct launcher_logind *wl, uint32_t major,
|
||||
uint32_t minor)
|
||||
{
|
||||
DBusMessage *m;
|
||||
bool b;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.login1.Session",
|
||||
"PauseDeviceComplete");
|
||||
if (m) {
|
||||
b = dbus_message_append_args(m,
|
||||
DBUS_TYPE_UINT32, &major,
|
||||
DBUS_TYPE_UINT32, &minor,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (b)
|
||||
dbus_connection_send(wl->dbus, m, NULL);
|
||||
dbus_message_unref(m);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_logind_open(struct weston_launcher *launcher, const char *path, int flags)
|
||||
{
|
||||
struct launcher_logind *wl = wl_container_of(launcher, wl, base);
|
||||
struct stat st;
|
||||
int fl, r, fd;
|
||||
|
||||
r = stat(path, &st);
|
||||
if (r < 0)
|
||||
return -1;
|
||||
if (!S_ISCHR(st.st_mode)) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = launcher_logind_take_device(wl, major(st.st_rdev),
|
||||
minor(st.st_rdev), NULL);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
/* Compared to weston_launcher_open() we cannot specify the open-mode
|
||||
* directly. Instead, logind passes us an fd with sane default modes.
|
||||
* For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want
|
||||
* something else, we need to change it afterwards. We currently
|
||||
* only support setting O_NONBLOCK. Changing access-modes is not
|
||||
* possible so accept whatever logind passes us. */
|
||||
|
||||
fl = fcntl(fd, F_GETFL);
|
||||
if (fl < 0) {
|
||||
r = -errno;
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
if (flags & O_NONBLOCK)
|
||||
fl |= O_NONBLOCK;
|
||||
|
||||
r = fcntl(fd, F_SETFL, fl);
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
goto err_close;
|
||||
}
|
||||
return fd;
|
||||
|
||||
err_close:
|
||||
close(fd);
|
||||
launcher_logind_release_device(wl, major(st.st_rdev),
|
||||
minor(st.st_rdev));
|
||||
errno = -r;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_close(struct weston_launcher *launcher, int fd)
|
||||
{
|
||||
struct launcher_logind *wl = wl_container_of(launcher, wl, base);
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
r = fstat(fd, &st);
|
||||
if (r < 0) {
|
||||
weston_log("logind: cannot fstat fd: %m\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!S_ISCHR(st.st_mode)) {
|
||||
weston_log("logind: invalid device passed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
launcher_logind_release_device(wl, major(st.st_rdev),
|
||||
minor(st.st_rdev));
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_restore(struct weston_launcher *launcher)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_logind_activate_vt(struct weston_launcher *launcher, int vt)
|
||||
{
|
||||
struct launcher_logind *wl = wl_container_of(launcher, wl, base);
|
||||
DBusMessage *m;
|
||||
bool b;
|
||||
int r;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
"/org/freedesktop/login1/seat/self",
|
||||
"org.freedesktop.login1.Seat",
|
||||
"SwitchTo");
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
b = dbus_message_append_args(m,
|
||||
DBUS_TYPE_UINT32, &vt,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!b) {
|
||||
r = -ENOMEM;
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
dbus_connection_send(wl->dbus, m, NULL);
|
||||
r = 0;
|
||||
|
||||
err_unref:
|
||||
dbus_message_unref(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_set_active(struct launcher_logind *wl, bool active)
|
||||
{
|
||||
if (!wl->compositor->session_active == !active)
|
||||
return;
|
||||
|
||||
wl->compositor->session_active = active;
|
||||
|
||||
wl_signal_emit(&wl->compositor->session_signal,
|
||||
wl->compositor);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_active(struct launcher_logind *wl, DBusMessage *m, DBusMessageIter *iter)
|
||||
{
|
||||
DBusMessageIter sub;
|
||||
dbus_bool_t b;
|
||||
|
||||
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
|
||||
return;
|
||||
|
||||
dbus_message_iter_recurse(iter, &sub);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
|
||||
return;
|
||||
|
||||
dbus_message_iter_get_basic(&sub, &b);
|
||||
|
||||
/* If the backend requested DRM master-device synchronization, we only
|
||||
* wake-up the compositor once the master-device is up and running. For
|
||||
* other backends, we immediately forward the Active-change event. */
|
||||
if (!wl->sync_drm || !b)
|
||||
launcher_logind_set_active(wl, b);
|
||||
}
|
||||
|
||||
static void
|
||||
get_active_cb(DBusPendingCall *pending, void *data)
|
||||
{
|
||||
struct launcher_logind *wl = data;
|
||||
DBusMessageIter iter;
|
||||
DBusMessage *m;
|
||||
int type;
|
||||
|
||||
dbus_pending_call_unref(wl->pending_active);
|
||||
wl->pending_active = NULL;
|
||||
|
||||
m = dbus_pending_call_steal_reply(pending);
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
type = dbus_message_get_type(m);
|
||||
if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN &&
|
||||
dbus_message_iter_init(m, &iter))
|
||||
parse_active(wl, m, &iter);
|
||||
|
||||
dbus_message_unref(m);
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_get_active(struct launcher_logind *wl)
|
||||
{
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *m;
|
||||
bool b;
|
||||
const char *iface, *name;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
iface = "org.freedesktop.login1.Session";
|
||||
name = "Active";
|
||||
b = dbus_message_append_args(m,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!b)
|
||||
goto err_unref;
|
||||
|
||||
b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1);
|
||||
if (!b)
|
||||
goto err_unref;
|
||||
|
||||
b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL);
|
||||
if (!b) {
|
||||
dbus_pending_call_cancel(pending);
|
||||
dbus_pending_call_unref(pending);
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
if (wl->pending_active) {
|
||||
dbus_pending_call_cancel(wl->pending_active);
|
||||
dbus_pending_call_unref(wl->pending_active);
|
||||
}
|
||||
wl->pending_active = pending;
|
||||
return;
|
||||
|
||||
err_unref:
|
||||
dbus_message_unref(m);
|
||||
}
|
||||
|
||||
static void
|
||||
disconnected_dbus(struct launcher_logind *wl)
|
||||
{
|
||||
weston_log("logind: dbus connection lost, exiting..\n");
|
||||
launcher_logind_restore(&wl->base);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void
|
||||
session_removed(struct launcher_logind *wl, DBusMessage *m)
|
||||
{
|
||||
const char *name, *obj;
|
||||
bool r;
|
||||
|
||||
r = dbus_message_get_args(m, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_OBJECT_PATH, &obj,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!r) {
|
||||
weston_log("logind: cannot parse SessionRemoved dbus signal\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(name, wl->sid)) {
|
||||
weston_log("logind: our session got closed, exiting..\n");
|
||||
launcher_logind_restore(&wl->base);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
property_changed(struct launcher_logind *wl, DBusMessage *m)
|
||||
{
|
||||
DBusMessageIter iter, sub, entry;
|
||||
const char *interface, *name;
|
||||
|
||||
if (!dbus_message_iter_init(m, &iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||||
goto error;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &interface);
|
||||
|
||||
if (!dbus_message_iter_next(&iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
|
||||
goto error;
|
||||
|
||||
dbus_message_iter_recurse(&iter, &sub);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) {
|
||||
dbus_message_iter_recurse(&sub, &entry);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
|
||||
goto error;
|
||||
|
||||
dbus_message_iter_get_basic(&entry, &name);
|
||||
if (!dbus_message_iter_next(&entry))
|
||||
goto error;
|
||||
|
||||
if (!strcmp(name, "Active")) {
|
||||
parse_active(wl, m, &entry);
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&sub);
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_next(&iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
|
||||
goto error;
|
||||
|
||||
dbus_message_iter_recurse(&iter, &sub);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
|
||||
dbus_message_iter_get_basic(&sub, &name);
|
||||
|
||||
if (!strcmp(name, "Active")) {
|
||||
launcher_logind_get_active(wl);
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&sub);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
weston_log("logind: cannot parse PropertiesChanged dbus signal\n");
|
||||
}
|
||||
|
||||
static void
|
||||
device_paused(struct launcher_logind *wl, DBusMessage *m)
|
||||
{
|
||||
bool r;
|
||||
const char *type;
|
||||
uint32_t major, minor;
|
||||
|
||||
r = dbus_message_get_args(m, NULL,
|
||||
DBUS_TYPE_UINT32, &major,
|
||||
DBUS_TYPE_UINT32, &minor,
|
||||
DBUS_TYPE_STRING, &type,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!r) {
|
||||
weston_log("logind: cannot parse PauseDevice dbus signal\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* "pause" means synchronous pausing. Acknowledge it unconditionally
|
||||
* as we support asynchronous device shutdowns, anyway.
|
||||
* "force" means asynchronous pausing.
|
||||
* "gone" means the device is gone. We handle it the same as "force" as
|
||||
* a following udev event will be caught, too.
|
||||
*
|
||||
* If it's our main DRM device, tell the compositor to go asleep. */
|
||||
|
||||
if (!strcmp(type, "pause"))
|
||||
launcher_logind_pause_device_complete(wl, major, minor);
|
||||
|
||||
if (wl->sync_drm && major == DRM_MAJOR)
|
||||
launcher_logind_set_active(wl, false);
|
||||
}
|
||||
|
||||
static void
|
||||
device_resumed(struct launcher_logind *wl, DBusMessage *m)
|
||||
{
|
||||
bool r;
|
||||
uint32_t major;
|
||||
|
||||
r = dbus_message_get_args(m, NULL,
|
||||
DBUS_TYPE_UINT32, &major,
|
||||
/*DBUS_TYPE_UINT32, &minor,
|
||||
DBUS_TYPE_UNIX_FD, &fd,*/
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!r) {
|
||||
weston_log("logind: cannot parse ResumeDevice dbus signal\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* DeviceResumed messages provide us a new file-descriptor for
|
||||
* resumed devices. For DRM devices it's the same as before, for evdev
|
||||
* devices it's a new open-file. As we reopen evdev devices, anyway,
|
||||
* there is no need for us to handle this event for evdev. For DRM, we
|
||||
* notify the compositor to wake up. */
|
||||
|
||||
if (wl->sync_drm && major == DRM_MAJOR)
|
||||
launcher_logind_set_active(wl, true);
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
filter_dbus(DBusConnection *c, DBusMessage *m, void *data)
|
||||
{
|
||||
struct launcher_logind *wl = data;
|
||||
|
||||
if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
|
||||
disconnected_dbus(wl);
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager",
|
||||
"SessionRemoved")) {
|
||||
session_removed(wl, m);
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged")) {
|
||||
property_changed(wl, m);
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
|
||||
"PauseDevice")) {
|
||||
device_paused(wl, m);
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
|
||||
"ResumeDevice")) {
|
||||
device_resumed(wl, m);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_logind_setup_dbus(struct launcher_logind *wl)
|
||||
{
|
||||
bool b;
|
||||
int r;
|
||||
|
||||
r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s",
|
||||
wl->sid);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL);
|
||||
if (!b) {
|
||||
weston_log("logind: cannot add dbus filter\n");
|
||||
r = -ENOMEM;
|
||||
goto err_spath;
|
||||
}
|
||||
|
||||
r = weston_dbus_add_match_signal(wl->dbus,
|
||||
"org.freedesktop.login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
"SessionRemoved",
|
||||
"/org/freedesktop/login1");
|
||||
if (r < 0) {
|
||||
weston_log("logind: cannot add dbus match\n");
|
||||
goto err_spath;
|
||||
}
|
||||
|
||||
r = weston_dbus_add_match_signal(wl->dbus,
|
||||
"org.freedesktop.login1",
|
||||
"org.freedesktop.login1.Session",
|
||||
"PauseDevice",
|
||||
wl->spath);
|
||||
if (r < 0) {
|
||||
weston_log("logind: cannot add dbus match\n");
|
||||
goto err_spath;
|
||||
}
|
||||
|
||||
r = weston_dbus_add_match_signal(wl->dbus,
|
||||
"org.freedesktop.login1",
|
||||
"org.freedesktop.login1.Session",
|
||||
"ResumeDevice",
|
||||
wl->spath);
|
||||
if (r < 0) {
|
||||
weston_log("logind: cannot add dbus match\n");
|
||||
goto err_spath;
|
||||
}
|
||||
|
||||
r = weston_dbus_add_match_signal(wl->dbus,
|
||||
"org.freedesktop.login1",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
wl->spath);
|
||||
if (r < 0) {
|
||||
weston_log("logind: cannot add dbus match\n");
|
||||
goto err_spath;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_spath:
|
||||
/* don't remove any dbus-match as the connection is closed, anyway */
|
||||
free(wl->spath);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_destroy_dbus(struct launcher_logind *wl)
|
||||
{
|
||||
/* don't remove any dbus-match as the connection is closed, anyway */
|
||||
free(wl->spath);
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_logind_take_control(struct launcher_logind *wl)
|
||||
{
|
||||
DBusError err;
|
||||
DBusMessage *m, *reply;
|
||||
dbus_bool_t force;
|
||||
bool b;
|
||||
int r;
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.login1.Session",
|
||||
"TakeControl");
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
force = false;
|
||||
b = dbus_message_append_args(m,
|
||||
DBUS_TYPE_BOOLEAN, &force,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (!b) {
|
||||
r = -ENOMEM;
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
reply = dbus_connection_send_with_reply_and_block(wl->dbus,
|
||||
m, -1, &err);
|
||||
if (!reply) {
|
||||
if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD))
|
||||
weston_log("logind: old systemd version detected\n");
|
||||
else
|
||||
weston_log("logind: cannot take control over session %s\n", wl->sid);
|
||||
|
||||
dbus_error_free(&err);
|
||||
r = -EIO;
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
dbus_message_unref(reply);
|
||||
dbus_message_unref(m);
|
||||
return 0;
|
||||
|
||||
err_unref:
|
||||
dbus_message_unref(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_release_control(struct launcher_logind *wl)
|
||||
{
|
||||
DBusMessage *m;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.login1.Session",
|
||||
"ReleaseControl");
|
||||
if (m) {
|
||||
dbus_connection_send(wl->dbus, m, NULL);
|
||||
dbus_message_unref(m);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
weston_sd_session_get_vt(const char *sid, unsigned int *out)
|
||||
{
|
||||
#ifdef HAVE_SYSTEMD_LOGIN_209
|
||||
return sd_session_get_vt(sid, out);
|
||||
#else
|
||||
int r;
|
||||
char *tty;
|
||||
|
||||
r = sd_session_get_tty(sid, &tty);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sscanf(tty, "tty%u", out);
|
||||
free(tty);
|
||||
|
||||
if (r != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_logind_activate(struct launcher_logind *wl)
|
||||
{
|
||||
DBusMessage *m;
|
||||
|
||||
m = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
wl->spath,
|
||||
"org.freedesktop.login1.Session",
|
||||
"Activate");
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
dbus_connection_send(wl->dbus, m, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm)
|
||||
{
|
||||
struct launcher_logind *wl;
|
||||
struct wl_event_loop *loop;
|
||||
char *t;
|
||||
int r;
|
||||
|
||||
wl = zalloc(sizeof(*wl));
|
||||
if (wl == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
wl->base.iface = &launcher_logind_iface;
|
||||
wl->compositor = compositor;
|
||||
wl->sync_drm = sync_drm;
|
||||
|
||||
wl->seat = strdup(seat_id);
|
||||
if (!wl->seat) {
|
||||
r = -ENOMEM;
|
||||
goto err_wl;
|
||||
}
|
||||
|
||||
r = sd_pid_get_session(getpid(), &wl->sid);
|
||||
if (r < 0) {
|
||||
weston_log("logind: not running in a systemd session\n");
|
||||
goto err_seat;
|
||||
}
|
||||
|
||||
t = NULL;
|
||||
r = sd_session_get_seat(wl->sid, &t);
|
||||
if (r < 0) {
|
||||
weston_log("logind: failed to get session seat\n");
|
||||
free(t);
|
||||
goto err_session;
|
||||
} else if (strcmp(seat_id, t)) {
|
||||
weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n",
|
||||
seat_id, t);
|
||||
r = -EINVAL;
|
||||
free(t);
|
||||
goto err_session;
|
||||
}
|
||||
free(t);
|
||||
|
||||
r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
|
||||
if (r < 0) {
|
||||
weston_log("logind: session not running on a VT\n");
|
||||
goto err_session;
|
||||
} else if (tty > 0 && wl->vtnr != (unsigned int )tty) {
|
||||
weston_log("logind: requested VT --tty=%d differs from real session VT %u\n",
|
||||
tty, wl->vtnr);
|
||||
r = -EINVAL;
|
||||
goto err_session;
|
||||
}
|
||||
|
||||
loop = wl_display_get_event_loop(compositor->wl_display);
|
||||
r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx);
|
||||
if (r < 0) {
|
||||
weston_log("logind: cannot connect to system dbus\n");
|
||||
goto err_session;
|
||||
}
|
||||
|
||||
r = launcher_logind_setup_dbus(wl);
|
||||
if (r < 0)
|
||||
goto err_dbus;
|
||||
|
||||
r = launcher_logind_take_control(wl);
|
||||
if (r < 0)
|
||||
goto err_dbus_cleanup;
|
||||
|
||||
r = launcher_logind_activate(wl);
|
||||
if (r < 0)
|
||||
goto err_dbus_cleanup;
|
||||
|
||||
weston_log("logind: session control granted\n");
|
||||
* (struct launcher_logind **) out = wl;
|
||||
return 0;
|
||||
|
||||
err_dbus_cleanup:
|
||||
launcher_logind_destroy_dbus(wl);
|
||||
err_dbus:
|
||||
weston_dbus_close(wl->dbus, wl->dbus_ctx);
|
||||
err_session:
|
||||
free(wl->sid);
|
||||
err_seat:
|
||||
free(wl->seat);
|
||||
err_wl:
|
||||
free(wl);
|
||||
err_out:
|
||||
weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r);
|
||||
errno = -r;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_logind_destroy(struct weston_launcher *launcher)
|
||||
{
|
||||
struct launcher_logind *wl = wl_container_of(launcher, wl, base);
|
||||
|
||||
if (wl->pending_active) {
|
||||
dbus_pending_call_cancel(wl->pending_active);
|
||||
dbus_pending_call_unref(wl->pending_active);
|
||||
}
|
||||
|
||||
launcher_logind_release_control(wl);
|
||||
launcher_logind_destroy_dbus(wl);
|
||||
weston_dbus_close(wl->dbus, wl->dbus_ctx);
|
||||
free(wl->sid);
|
||||
free(wl->seat);
|
||||
free(wl);
|
||||
}
|
||||
|
||||
struct launcher_interface launcher_logind_iface = {
|
||||
launcher_logind_connect,
|
||||
launcher_logind_destroy,
|
||||
launcher_logind_open,
|
||||
launcher_logind_close,
|
||||
launcher_logind_activate_vt,
|
||||
launcher_logind_restore,
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
* 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 "compositor.h"
|
||||
|
||||
#include "launcher-util.h"
|
||||
#include "launcher-impl.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
static struct launcher_interface *ifaces[] = {
|
||||
#ifdef HAVE_SYSTEMD_LOGIN
|
||||
&launcher_logind_iface,
|
||||
#endif
|
||||
&launcher_weston_launch_iface,
|
||||
&launcher_direct_iface,
|
||||
NULL,
|
||||
};
|
||||
|
||||
WL_EXPORT struct weston_launcher *
|
||||
weston_launcher_connect(struct weston_compositor *compositor, int tty,
|
||||
const char *seat_id, bool sync_drm)
|
||||
{
|
||||
struct launcher_interface **it;
|
||||
|
||||
for (it = ifaces; *it != NULL; it++) {
|
||||
struct launcher_interface *iface = *it;
|
||||
struct weston_launcher *launcher;
|
||||
|
||||
if (iface->connect(&launcher, compositor, tty, seat_id, sync_drm) == 0)
|
||||
return launcher;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_launcher_destroy(struct weston_launcher *launcher)
|
||||
{
|
||||
launcher->iface->destroy(launcher);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_launcher_open(struct weston_launcher *launcher,
|
||||
const char *path, int flags)
|
||||
{
|
||||
return launcher->iface->open(launcher, path, flags);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_launcher_close(struct weston_launcher *launcher, int fd)
|
||||
{
|
||||
launcher->iface->close(launcher, fd);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_launcher_activate_vt(struct weston_launcher *launcher, int vt)
|
||||
{
|
||||
return launcher->iface->activate_vt(launcher, vt);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_launcher_restore(struct weston_launcher *launcher)
|
||||
{
|
||||
launcher->iface->restore(launcher);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
switch_vt_binding(struct weston_keyboard *keyboard,
|
||||
uint32_t time, uint32_t key, void *data)
|
||||
{
|
||||
struct weston_compositor *compositor = data;
|
||||
|
||||
weston_launcher_activate_vt(compositor->launcher, key - KEY_F1 + 1);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_setup_vt_switch_bindings(struct weston_compositor *compositor)
|
||||
{
|
||||
uint32_t key;
|
||||
|
||||
if (compositor->vt_switching == false)
|
||||
return;
|
||||
|
||||
for (key = KEY_F1; key < KEY_F9; key++)
|
||||
weston_compositor_add_key_binding(compositor, key,
|
||||
MODIFIER_CTRL | MODIFIER_ALT,
|
||||
switch_vt_binding,
|
||||
compositor);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
*
|
||||
* 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_LAUNCHER_UTIL_H_
|
||||
#define _WESTON_LAUNCHER_UTIL_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
struct weston_launcher;
|
||||
|
||||
struct weston_launcher *
|
||||
weston_launcher_connect(struct weston_compositor *compositor, int tty,
|
||||
const char *seat_id, bool sync_drm);
|
||||
|
||||
void
|
||||
weston_launcher_destroy(struct weston_launcher *launcher);
|
||||
|
||||
int
|
||||
weston_launcher_open(struct weston_launcher *launcher,
|
||||
const char *path, int flags);
|
||||
|
||||
void
|
||||
weston_launcher_close(struct weston_launcher *launcher, int fd);
|
||||
|
||||
int
|
||||
weston_launcher_activate_vt(struct weston_launcher *launcher, int vt);
|
||||
|
||||
void
|
||||
weston_launcher_restore(struct weston_launcher *launcher);
|
||||
|
||||
void
|
||||
weston_setup_vt_switch_bindings(struct weston_compositor *compositor);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
* Copyright © 2013 Intel Corporation
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/vt.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/major.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "weston-launch.h"
|
||||
#include "launcher-impl.h"
|
||||
|
||||
#define DRM_MAJOR 226
|
||||
|
||||
#ifndef KDSKBMUTE
|
||||
#define KDSKBMUTE 0x4B51
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBDRM
|
||||
|
||||
#include <xf86drm.h>
|
||||
|
||||
static inline int
|
||||
is_drm_master(int drm_fd)
|
||||
{
|
||||
drm_magic_t magic;
|
||||
|
||||
return drmGetMagic(drm_fd, &magic) == 0 &&
|
||||
drmAuthMagic(drm_fd, magic) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
drmDropMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drmSetMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
is_drm_master(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
union cmsg_data { unsigned char b[4]; int fd; };
|
||||
|
||||
struct launcher_weston_launch {
|
||||
struct weston_launcher base;
|
||||
struct weston_compositor *compositor;
|
||||
struct wl_event_loop *loop;
|
||||
int fd;
|
||||
struct wl_event_source *source;
|
||||
|
||||
int kb_mode, tty, drm_fd;
|
||||
};
|
||||
|
||||
static int
|
||||
launcher_weston_launch_open(struct weston_launcher *launcher_base,
|
||||
const char *path, int flags)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
int n, ret;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
union cmsg_data *data;
|
||||
char control[CMSG_SPACE(sizeof data->fd)];
|
||||
ssize_t len;
|
||||
struct weston_launcher_open *message;
|
||||
|
||||
n = sizeof(*message) + strlen(path) + 1;
|
||||
message = malloc(n);
|
||||
if (!message)
|
||||
return -1;
|
||||
|
||||
message->header.opcode = WESTON_LAUNCHER_OPEN;
|
||||
message->flags = flags;
|
||||
strcpy(message->path, path);
|
||||
|
||||
do {
|
||||
len = send(launcher->fd, message, n, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
free(message);
|
||||
|
||||
memset(&msg, 0, sizeof msg);
|
||||
iov.iov_base = &ret;
|
||||
iov.iov_len = sizeof ret;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = control;
|
||||
msg.msg_controllen = sizeof control;
|
||||
|
||||
do {
|
||||
len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
if (len != sizeof ret ||
|
||||
ret < 0)
|
||||
return -1;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf(stderr, "invalid control message\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (union cmsg_data *) CMSG_DATA(cmsg);
|
||||
if (data->fd == -1) {
|
||||
fprintf(stderr, "missing drm fd in socket request\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return data->fd;
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_weston_launch_close(struct weston_launcher *launcher_base, int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_weston_launch_restore(struct weston_launcher *launcher_base)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
struct vt_mode mode = { 0 };
|
||||
|
||||
if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
|
||||
ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
|
||||
weston_log("failed to restore kb mode: %m\n");
|
||||
|
||||
if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
|
||||
weston_log("failed to set KD_TEXT mode on tty: %m\n");
|
||||
|
||||
/* We have to drop master before we switch the VT back in
|
||||
* VT_AUTO, so we don't risk switching to a VT with another
|
||||
* display server, that will then fail to set drm master. */
|
||||
drmDropMaster(launcher->drm_fd);
|
||||
|
||||
mode.mode = VT_AUTO;
|
||||
if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
|
||||
weston_log("could not reset vt handling\n");
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_weston_launch_data(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = data;
|
||||
int len, ret;
|
||||
|
||||
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
|
||||
weston_log("launcher socket closed, exiting\n");
|
||||
/* Normally the weston-launch will reset the tty, but
|
||||
* in this case it died or something, so do it here so
|
||||
* we don't end up with a stuck vt. */
|
||||
launcher_weston_launch_restore(&launcher->base);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
do {
|
||||
len = recv(launcher->fd, &ret, sizeof ret, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
switch (ret) {
|
||||
case WESTON_LAUNCHER_ACTIVATE:
|
||||
launcher->compositor->session_active = 1;
|
||||
wl_signal_emit(&launcher->compositor->session_signal,
|
||||
launcher->compositor);
|
||||
break;
|
||||
case WESTON_LAUNCHER_DEACTIVATE:
|
||||
launcher->compositor->session_active = 0;
|
||||
wl_signal_emit(&launcher->compositor->session_signal,
|
||||
launcher->compositor);
|
||||
break;
|
||||
default:
|
||||
weston_log("unexpected event from weston-launch\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_weston_launch_activate_vt(struct weston_launcher *launcher_base, int vt)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
return ioctl(launcher->tty, VT_ACTIVATE, vt);
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_weston_launch_connect(struct weston_launcher **out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm)
|
||||
{
|
||||
struct launcher_weston_launch *launcher;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
launcher = malloc(sizeof *launcher);
|
||||
if (launcher == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
launcher->base.iface = &launcher_weston_launch_iface;
|
||||
* (struct launcher_weston_launch **) out = launcher;
|
||||
launcher->compositor = compositor;
|
||||
launcher->drm_fd = -1;
|
||||
launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
|
||||
if (launcher->fd != -1) {
|
||||
launcher->tty = weston_environment_get_fd("WESTON_TTY_FD");
|
||||
/* We don't get a chance to read out the original kb
|
||||
* mode for the tty, so just hard code K_UNICODE here
|
||||
* in case we have to clean if weston-launch dies. */
|
||||
launcher->kb_mode = K_UNICODE;
|
||||
|
||||
loop = wl_display_get_event_loop(compositor->wl_display);
|
||||
launcher->source = wl_event_loop_add_fd(loop, launcher->fd,
|
||||
WL_EVENT_READABLE,
|
||||
launcher_weston_launch_data,
|
||||
launcher);
|
||||
if (launcher->source == NULL) {
|
||||
free(launcher);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
launcher_weston_launch_destroy(struct weston_launcher *launcher_base)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
|
||||
|
||||
if (launcher->fd != -1) {
|
||||
close(launcher->fd);
|
||||
wl_event_source_remove(launcher->source);
|
||||
} else {
|
||||
launcher_weston_launch_restore(&launcher->base);
|
||||
}
|
||||
|
||||
if (launcher->tty >= 0)
|
||||
close(launcher->tty);
|
||||
|
||||
free(launcher);
|
||||
}
|
||||
|
||||
struct launcher_interface launcher_weston_launch_iface = {
|
||||
launcher_weston_launch_connect,
|
||||
launcher_weston_launch_destroy,
|
||||
launcher_weston_launch_open,
|
||||
launcher_weston_launch_close,
|
||||
launcher_weston_launch_activate_vt,
|
||||
launcher_weston_launch_restore,
|
||||
};
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* libbacklight - userspace interface to Linux backlight control
|
||||
*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
* Copyright 2010 Red Hat <mjg@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Matthew Garrett <mjg@redhat.com>
|
||||
* Tiago Vignatti <vignatti@freedesktop.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libbacklight.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/types.h>
|
||||
#include <dirent.h>
|
||||
#include <drm.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static long backlight_get(struct backlight *backlight, char *node)
|
||||
{
|
||||
char buffer[100];
|
||||
char *path;
|
||||
int fd;
|
||||
long value, ret;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
|
||||
return -ENOMEM;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = read(fd, &buffer, sizeof(buffer));
|
||||
if (ret < 1) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
value = strtol(buffer, NULL, 10);
|
||||
ret = value;
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long backlight_get_brightness(struct backlight *backlight)
|
||||
{
|
||||
return backlight_get(backlight, "brightness");
|
||||
}
|
||||
|
||||
long backlight_get_max_brightness(struct backlight *backlight)
|
||||
{
|
||||
return backlight_get(backlight, "max_brightness");
|
||||
}
|
||||
|
||||
long backlight_get_actual_brightness(struct backlight *backlight)
|
||||
{
|
||||
return backlight_get(backlight, "actual_brightness");
|
||||
}
|
||||
|
||||
long backlight_set_brightness(struct backlight *backlight, long brightness)
|
||||
{
|
||||
char *path;
|
||||
char *buffer = NULL;
|
||||
int fd;
|
||||
long ret;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = read(fd, &buffer, sizeof(buffer));
|
||||
if (ret < 1) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (asprintf(&buffer, "%ld", brightness) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = write(fd, buffer, strlen(buffer));
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = backlight_get_brightness(backlight);
|
||||
backlight->brightness = ret;
|
||||
out:
|
||||
free(buffer);
|
||||
free(path);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void backlight_destroy(struct backlight *backlight)
|
||||
{
|
||||
if (!backlight)
|
||||
return;
|
||||
|
||||
if (backlight->path)
|
||||
free(backlight->path);
|
||||
|
||||
free(backlight);
|
||||
}
|
||||
|
||||
struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
uint32_t connector_type)
|
||||
{
|
||||
const char *syspath = NULL;
|
||||
char *pci_name = NULL;
|
||||
char *chosen_path = NULL;
|
||||
char *path = NULL;
|
||||
DIR *backlights = NULL;
|
||||
struct dirent *entry;
|
||||
enum backlight_type type = 0;
|
||||
char buffer[100];
|
||||
struct backlight *backlight = NULL;
|
||||
int ret;
|
||||
|
||||
if (!drm_device)
|
||||
return NULL;
|
||||
|
||||
syspath = udev_device_get_syspath(drm_device);
|
||||
if (!syspath)
|
||||
return NULL;
|
||||
|
||||
if (asprintf(&path, "%s/%s", syspath, "device") < 0)
|
||||
return NULL;
|
||||
|
||||
ret = readlink(path, buffer, sizeof(buffer) - 1);
|
||||
free(path);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
buffer[ret] = '\0';
|
||||
pci_name = basename(buffer);
|
||||
|
||||
if (connector_type <= 0)
|
||||
return NULL;
|
||||
|
||||
backlights = opendir("/sys/class/backlight");
|
||||
if (!backlights)
|
||||
return NULL;
|
||||
|
||||
/* Find the "best" backlight for the device. Firmware
|
||||
interfaces are preferred over platform interfaces are
|
||||
preferred over raw interfaces. For raw interfaces we'll
|
||||
check if the device ID in the form of pci match, while
|
||||
for firmware interfaces we require the pci ID to
|
||||
match. It's assumed that platform interfaces always match,
|
||||
since we can't actually associate them with IDs.
|
||||
|
||||
A further awkwardness is that, while it's theoretically
|
||||
possible for an ACPI interface to include support for
|
||||
changing the backlight of external devices, it's unlikely
|
||||
to ever be done. It's effectively impossible for a platform
|
||||
interface to do so. So if we get asked about anything that
|
||||
isn't LVDS or eDP, we pretty much have to require that the
|
||||
control be supplied via a raw interface */
|
||||
|
||||
while ((entry = readdir(backlights))) {
|
||||
char *backlight_path;
|
||||
char *parent;
|
||||
enum backlight_type entry_type;
|
||||
int fd;
|
||||
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
|
||||
entry->d_name) < 0)
|
||||
goto err;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
|
||||
free(backlight_path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
|
||||
if (fd < 0)
|
||||
goto out;
|
||||
|
||||
ret = read (fd, &buffer, sizeof(buffer));
|
||||
close (fd);
|
||||
|
||||
if (ret < 1)
|
||||
goto out;
|
||||
|
||||
buffer[ret] = '\0';
|
||||
|
||||
if (!strncmp(buffer, "raw\n", sizeof(buffer)))
|
||||
entry_type = BACKLIGHT_RAW;
|
||||
else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
|
||||
entry_type = BACKLIGHT_PLATFORM;
|
||||
else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
|
||||
entry_type = BACKLIGHT_FIRMWARE;
|
||||
else
|
||||
goto out;
|
||||
|
||||
if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
|
||||
connector_type != DRM_MODE_CONNECTOR_eDP) {
|
||||
/* External displays are assumed to require
|
||||
gpu control at the moment */
|
||||
if (entry_type != BACKLIGHT_RAW)
|
||||
goto out;
|
||||
}
|
||||
|
||||
free (path);
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
|
||||
goto err;
|
||||
|
||||
ret = readlink(path, buffer, sizeof(buffer) - 1);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
buffer[ret] = '\0';
|
||||
|
||||
parent = basename(buffer);
|
||||
|
||||
/* Perform matching for raw and firmware backlights -
|
||||
platform backlights have to be assumed to match */
|
||||
if (entry_type == BACKLIGHT_RAW ||
|
||||
entry_type == BACKLIGHT_FIRMWARE) {
|
||||
if (!(pci_name && !strcmp(pci_name, parent)))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (entry_type < type)
|
||||
goto out;
|
||||
|
||||
type = entry_type;
|
||||
|
||||
if (chosen_path)
|
||||
free(chosen_path);
|
||||
chosen_path = strdup(backlight_path);
|
||||
|
||||
out:
|
||||
free(backlight_path);
|
||||
free(path);
|
||||
}
|
||||
|
||||
if (!chosen_path)
|
||||
goto err;
|
||||
|
||||
backlight = malloc(sizeof(struct backlight));
|
||||
|
||||
if (!backlight)
|
||||
goto err;
|
||||
|
||||
backlight->path = chosen_path;
|
||||
backlight->type = type;
|
||||
|
||||
backlight->max_brightness = backlight_get_max_brightness(backlight);
|
||||
if (backlight->max_brightness < 0)
|
||||
goto err;
|
||||
|
||||
backlight->brightness = backlight_get_actual_brightness(backlight);
|
||||
if (backlight->brightness < 0)
|
||||
goto err;
|
||||
|
||||
closedir(backlights);
|
||||
return backlight;
|
||||
err:
|
||||
closedir(backlights);
|
||||
free (chosen_path);
|
||||
free (backlight);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* libbacklight - userspace interface to Linux backlight control
|
||||
*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
* Copyright 2010 Red Hat <mjg@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Matthew Garrett <mjg@redhat.com>
|
||||
* Tiago Vignatti <vignatti@freedesktop.org>
|
||||
*/
|
||||
#ifndef LIBBACKLIGHT_H
|
||||
#define LIBBACKLIGHT_H
|
||||
#include <libudev.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum backlight_type {
|
||||
BACKLIGHT_RAW,
|
||||
BACKLIGHT_PLATFORM,
|
||||
BACKLIGHT_FIRMWARE
|
||||
};
|
||||
|
||||
struct backlight {
|
||||
char *path;
|
||||
int max_brightness;
|
||||
int brightness;
|
||||
enum backlight_type type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Find and set up a backlight for a valid udev connector device, i.e. one
|
||||
* matching drm subsytem and with status of connected.
|
||||
*/
|
||||
struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
uint32_t connector_type);
|
||||
|
||||
/* Free backlight resources */
|
||||
void backlight_destroy(struct backlight *backlight);
|
||||
|
||||
/* Provide the maximum backlight value */
|
||||
long backlight_get_max_brightness(struct backlight *backlight);
|
||||
|
||||
/* Provide the cached backlight value */
|
||||
long backlight_get_brightness(struct backlight *backlight);
|
||||
|
||||
/* Provide the hardware backlight value */
|
||||
long backlight_get_actual_brightness(struct backlight *backlight);
|
||||
|
||||
/* Set the backlight to a value between 0 and max */
|
||||
long backlight_set_brightness(struct backlight *backlight, long brightness);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBBACKLIGHT_H */
|
||||
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2013 Jonas Ådahl
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <mtdev.h>
|
||||
#include <assert.h>
|
||||
#include <libinput.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "libinput-device.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
void
|
||||
evdev_led_update(struct evdev_device *device, enum weston_led weston_leds)
|
||||
{
|
||||
enum libinput_led leds = 0;
|
||||
|
||||
if (weston_leds & LED_NUM_LOCK)
|
||||
leds |= LIBINPUT_LED_NUM_LOCK;
|
||||
if (weston_leds & LED_CAPS_LOCK)
|
||||
leds |= LIBINPUT_LED_CAPS_LOCK;
|
||||
if (weston_leds & LED_SCROLL_LOCK)
|
||||
leds |= LIBINPUT_LED_SCROLL_LOCK;
|
||||
|
||||
libinput_device_led_update(device->device, leds);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_keyboard_key(struct libinput_device *libinput_device,
|
||||
struct libinput_event_keyboard *keyboard_event)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
int key_state =
|
||||
libinput_event_keyboard_get_key_state(keyboard_event);
|
||||
int seat_key_count =
|
||||
libinput_event_keyboard_get_seat_key_count(keyboard_event);
|
||||
|
||||
/* Ignore key events that are not seat wide state changes. */
|
||||
if ((key_state == LIBINPUT_KEY_STATE_PRESSED &&
|
||||
seat_key_count != 1) ||
|
||||
(key_state == LIBINPUT_KEY_STATE_RELEASED &&
|
||||
seat_key_count != 0))
|
||||
return;
|
||||
|
||||
notify_key(device->seat,
|
||||
libinput_event_keyboard_get_time(keyboard_event),
|
||||
libinput_event_keyboard_get_key(keyboard_event),
|
||||
key_state, STATE_UPDATE_AUTOMATIC);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_pointer_motion(struct libinput_device *libinput_device,
|
||||
struct libinput_event_pointer *pointer_event)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
struct weston_pointer_motion_event event = { 0 };
|
||||
|
||||
event = (struct weston_pointer_motion_event) {
|
||||
.mask = WESTON_POINTER_MOTION_REL,
|
||||
.dx = libinput_event_pointer_get_dx(pointer_event),
|
||||
.dy = libinput_event_pointer_get_dy(pointer_event),
|
||||
};
|
||||
|
||||
notify_motion(device->seat,
|
||||
libinput_event_pointer_get_time(pointer_event),
|
||||
&event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_pointer_motion_absolute(
|
||||
struct libinput_device *libinput_device,
|
||||
struct libinput_event_pointer *pointer_event)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
struct weston_output *output = device->output;
|
||||
uint32_t time;
|
||||
double x, y;
|
||||
uint32_t width, height;
|
||||
|
||||
if (!output)
|
||||
return false;
|
||||
|
||||
time = libinput_event_pointer_get_time(pointer_event);
|
||||
width = device->output->current_mode->width;
|
||||
height = device->output->current_mode->height;
|
||||
|
||||
x = libinput_event_pointer_get_absolute_x_transformed(pointer_event,
|
||||
width);
|
||||
y = libinput_event_pointer_get_absolute_y_transformed(pointer_event,
|
||||
height);
|
||||
|
||||
weston_output_transform_coordinate(device->output, x, y, &x, &y);
|
||||
notify_motion_absolute(device->seat, time, x, y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_pointer_button(struct libinput_device *libinput_device,
|
||||
struct libinput_event_pointer *pointer_event)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
int button_state =
|
||||
libinput_event_pointer_get_button_state(pointer_event);
|
||||
int seat_button_count =
|
||||
libinput_event_pointer_get_seat_button_count(pointer_event);
|
||||
|
||||
/* Ignore button events that are not seat wide state changes. */
|
||||
if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED &&
|
||||
seat_button_count != 1) ||
|
||||
(button_state == LIBINPUT_BUTTON_STATE_RELEASED &&
|
||||
seat_button_count != 0))
|
||||
return false;
|
||||
|
||||
notify_button(device->seat,
|
||||
libinput_event_pointer_get_time(pointer_event),
|
||||
libinput_event_pointer_get_button(pointer_event),
|
||||
button_state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static double
|
||||
normalize_scroll(struct libinput_event_pointer *pointer_event,
|
||||
enum libinput_pointer_axis axis)
|
||||
{
|
||||
enum libinput_pointer_axis_source source;
|
||||
double value = 0.0;
|
||||
|
||||
source = libinput_event_pointer_get_axis_source(pointer_event);
|
||||
/* libinput < 0.8 sent wheel click events with value 10. Since 0.8
|
||||
the value is the angle of the click in degrees. To keep
|
||||
backwards-compat with existing clients, we just send multiples of
|
||||
the click count.
|
||||
*/
|
||||
switch (source) {
|
||||
case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
|
||||
value = 10 * libinput_event_pointer_get_axis_value_discrete(
|
||||
pointer_event,
|
||||
axis);
|
||||
break;
|
||||
case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
|
||||
case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
|
||||
value = libinput_event_pointer_get_axis_value(pointer_event,
|
||||
axis);
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
get_axis_discrete(struct libinput_event_pointer *pointer_event,
|
||||
enum libinput_pointer_axis axis)
|
||||
{
|
||||
enum libinput_pointer_axis_source source;
|
||||
|
||||
source = libinput_event_pointer_get_axis_source(pointer_event);
|
||||
|
||||
if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
|
||||
return 0;
|
||||
|
||||
return libinput_event_pointer_get_axis_value_discrete(pointer_event,
|
||||
axis);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_pointer_axis(struct libinput_device *libinput_device,
|
||||
struct libinput_event_pointer *pointer_event)
|
||||
{
|
||||
static int warned;
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
double vert, horiz;
|
||||
int32_t vert_discrete, horiz_discrete;
|
||||
enum libinput_pointer_axis axis;
|
||||
struct weston_pointer_axis_event weston_event;
|
||||
enum libinput_pointer_axis_source source;
|
||||
uint32_t wl_axis_source;
|
||||
bool has_vert, has_horiz;
|
||||
|
||||
has_vert = libinput_event_pointer_has_axis(pointer_event,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
||||
has_horiz = libinput_event_pointer_has_axis(pointer_event,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
|
||||
|
||||
if (!has_vert && !has_horiz)
|
||||
return false;
|
||||
|
||||
source = libinput_event_pointer_get_axis_source(pointer_event);
|
||||
switch (source) {
|
||||
case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
|
||||
wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
|
||||
break;
|
||||
case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
|
||||
wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
|
||||
break;
|
||||
case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
|
||||
wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
|
||||
break;
|
||||
default:
|
||||
if (warned < 5) {
|
||||
weston_log("Unknown scroll source %d.\n", source);
|
||||
warned++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
notify_axis_source(device->seat, wl_axis_source);
|
||||
|
||||
if (has_vert) {
|
||||
axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
|
||||
vert_discrete = get_axis_discrete(pointer_event, axis);
|
||||
vert = normalize_scroll(pointer_event, axis);
|
||||
|
||||
weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
|
||||
weston_event.value = vert;
|
||||
weston_event.discrete = vert_discrete;
|
||||
weston_event.has_discrete = (vert_discrete != 0);
|
||||
|
||||
notify_axis(device->seat,
|
||||
libinput_event_pointer_get_time(pointer_event),
|
||||
&weston_event);
|
||||
}
|
||||
|
||||
if (has_horiz) {
|
||||
axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
|
||||
horiz_discrete = get_axis_discrete(pointer_event, axis);
|
||||
horiz = normalize_scroll(pointer_event, axis);
|
||||
|
||||
weston_event.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
|
||||
weston_event.value = horiz;
|
||||
weston_event.discrete = horiz_discrete;
|
||||
weston_event.has_discrete = (horiz_discrete != 0);
|
||||
|
||||
notify_axis(device->seat,
|
||||
libinput_event_pointer_get_time(pointer_event),
|
||||
&weston_event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_touch_with_coords(struct libinput_device *libinput_device,
|
||||
struct libinput_event_touch *touch_event,
|
||||
int touch_type)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
double x;
|
||||
double y;
|
||||
uint32_t width, height;
|
||||
uint32_t time;
|
||||
int32_t slot;
|
||||
|
||||
if (!device->output)
|
||||
return;
|
||||
|
||||
time = libinput_event_touch_get_time(touch_event);
|
||||
slot = libinput_event_touch_get_seat_slot(touch_event);
|
||||
|
||||
width = device->output->current_mode->width;
|
||||
height = device->output->current_mode->height;
|
||||
x = libinput_event_touch_get_x_transformed(touch_event, width);
|
||||
y = libinput_event_touch_get_y_transformed(touch_event, height);
|
||||
|
||||
weston_output_transform_coordinate(device->output,
|
||||
x, y, &x, &y);
|
||||
|
||||
notify_touch(device->seat, time, slot, x, y, touch_type);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_touch_down(struct libinput_device *device,
|
||||
struct libinput_event_touch *touch_event)
|
||||
{
|
||||
handle_touch_with_coords(device, touch_event, WL_TOUCH_DOWN);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_touch_motion(struct libinput_device *device,
|
||||
struct libinput_event_touch *touch_event)
|
||||
{
|
||||
handle_touch_with_coords(device, touch_event, WL_TOUCH_MOTION);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_touch_up(struct libinput_device *libinput_device,
|
||||
struct libinput_event_touch *touch_event)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
uint32_t time = libinput_event_touch_get_time(touch_event);
|
||||
int32_t slot = libinput_event_touch_get_seat_slot(touch_event);
|
||||
|
||||
notify_touch(device->seat, time, slot, 0, 0, WL_TOUCH_UP);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_touch_frame(struct libinput_device *libinput_device,
|
||||
struct libinput_event_touch *touch_event)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
struct weston_seat *seat = device->seat;
|
||||
|
||||
notify_touch_frame(seat);
|
||||
}
|
||||
|
||||
int
|
||||
evdev_device_process_event(struct libinput_event *event)
|
||||
{
|
||||
struct libinput_device *libinput_device =
|
||||
libinput_event_get_device(event);
|
||||
struct evdev_device *device =
|
||||
libinput_device_get_user_data(libinput_device);
|
||||
int handled = 1;
|
||||
bool need_frame = false;
|
||||
|
||||
switch (libinput_event_get_type(event)) {
|
||||
case LIBINPUT_EVENT_KEYBOARD_KEY:
|
||||
handle_keyboard_key(libinput_device,
|
||||
libinput_event_get_keyboard_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_POINTER_MOTION:
|
||||
need_frame = handle_pointer_motion(libinput_device,
|
||||
libinput_event_get_pointer_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
|
||||
need_frame = handle_pointer_motion_absolute(
|
||||
libinput_device,
|
||||
libinput_event_get_pointer_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_POINTER_BUTTON:
|
||||
need_frame = handle_pointer_button(libinput_device,
|
||||
libinput_event_get_pointer_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_POINTER_AXIS:
|
||||
need_frame = handle_pointer_axis(
|
||||
libinput_device,
|
||||
libinput_event_get_pointer_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_TOUCH_DOWN:
|
||||
handle_touch_down(libinput_device,
|
||||
libinput_event_get_touch_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_TOUCH_MOTION:
|
||||
handle_touch_motion(libinput_device,
|
||||
libinput_event_get_touch_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_TOUCH_UP:
|
||||
handle_touch_up(libinput_device,
|
||||
libinput_event_get_touch_event(event));
|
||||
break;
|
||||
case LIBINPUT_EVENT_TOUCH_FRAME:
|
||||
handle_touch_frame(libinput_device,
|
||||
libinput_event_get_touch_event(event));
|
||||
break;
|
||||
default:
|
||||
handled = 0;
|
||||
weston_log("unknown libinput event %d\n",
|
||||
libinput_event_get_type(event));
|
||||
}
|
||||
|
||||
if (need_frame)
|
||||
notify_pointer_frame(device->seat);
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static void
|
||||
notify_output_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct evdev_device *device =
|
||||
container_of(listener,
|
||||
struct evdev_device, output_destroy_listener);
|
||||
struct weston_compositor *c = device->seat->compositor;
|
||||
struct weston_output *output;
|
||||
|
||||
if (!device->output_name && !wl_list_empty(&c->output_list)) {
|
||||
output = container_of(c->output_list.next,
|
||||
struct weston_output, link);
|
||||
evdev_device_set_output(device, output);
|
||||
} else {
|
||||
device->output = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The WL_CALIBRATION property requires a pixel-specific matrix to be
|
||||
* applied after scaling device coordinates to screen coordinates. libinput
|
||||
* can't do that, so we need to convert the calibration to the normalized
|
||||
* format libinput expects.
|
||||
*/
|
||||
void
|
||||
evdev_device_set_calibration(struct evdev_device *device)
|
||||
{
|
||||
struct udev *udev;
|
||||
struct udev_device *udev_device = NULL;
|
||||
const char *sysname = libinput_device_get_sysname(device->device);
|
||||
const char *calibration_values;
|
||||
uint32_t width, height;
|
||||
float calibration[6];
|
||||
enum libinput_config_status status;
|
||||
|
||||
if (!device->output)
|
||||
return;
|
||||
|
||||
width = device->output->width;
|
||||
height = device->output->height;
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
/* If libinput has a pre-set calibration matrix, don't override it */
|
||||
if (!libinput_device_config_calibration_has_matrix(device->device) ||
|
||||
libinput_device_config_calibration_get_default_matrix(
|
||||
device->device,
|
||||
calibration) != 0)
|
||||
return;
|
||||
|
||||
udev = udev_new();
|
||||
if (!udev)
|
||||
return;
|
||||
|
||||
udev_device = udev_device_new_from_subsystem_sysname(udev,
|
||||
"input",
|
||||
sysname);
|
||||
if (!udev_device)
|
||||
goto out;
|
||||
|
||||
calibration_values =
|
||||
udev_device_get_property_value(udev_device,
|
||||
"WL_CALIBRATION");
|
||||
|
||||
if (!calibration_values || sscanf(calibration_values,
|
||||
"%f %f %f %f %f %f",
|
||||
&calibration[0],
|
||||
&calibration[1],
|
||||
&calibration[2],
|
||||
&calibration[3],
|
||||
&calibration[4],
|
||||
&calibration[5]) != 6)
|
||||
goto out;
|
||||
|
||||
weston_log("Applying calibration: %f %f %f %f %f %f "
|
||||
"(normalized %f %f)\n",
|
||||
calibration[0],
|
||||
calibration[1],
|
||||
calibration[2],
|
||||
calibration[3],
|
||||
calibration[4],
|
||||
calibration[5],
|
||||
calibration[2] / width,
|
||||
calibration[5] / height);
|
||||
|
||||
/* normalize to a format libinput can use. There is a chance of
|
||||
this being wrong if the width/height don't match the device
|
||||
width/height but I'm not sure how to fix that */
|
||||
calibration[2] /= width;
|
||||
calibration[5] /= height;
|
||||
|
||||
status = libinput_device_config_calibration_set_matrix(device->device,
|
||||
calibration);
|
||||
if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
|
||||
weston_log("Failed to apply calibration.\n");
|
||||
|
||||
out:
|
||||
if (udev_device)
|
||||
udev_device_unref(udev_device);
|
||||
udev_unref(udev);
|
||||
}
|
||||
|
||||
void
|
||||
evdev_device_set_output(struct evdev_device *device,
|
||||
struct weston_output *output)
|
||||
{
|
||||
if (device->output_destroy_listener.notify) {
|
||||
wl_list_remove(&device->output_destroy_listener.link);
|
||||
device->output_destroy_listener.notify = NULL;
|
||||
}
|
||||
|
||||
device->output = output;
|
||||
device->output_destroy_listener.notify = notify_output_destroy;
|
||||
wl_signal_add(&output->destroy_signal,
|
||||
&device->output_destroy_listener);
|
||||
evdev_device_set_calibration(device);
|
||||
}
|
||||
|
||||
struct evdev_device *
|
||||
evdev_device_create(struct libinput_device *libinput_device,
|
||||
struct weston_seat *seat)
|
||||
{
|
||||
struct evdev_device *device;
|
||||
|
||||
device = zalloc(sizeof *device);
|
||||
if (device == NULL)
|
||||
return NULL;
|
||||
|
||||
device->seat = seat;
|
||||
wl_list_init(&device->link);
|
||||
device->device = libinput_device;
|
||||
|
||||
if (libinput_device_has_capability(libinput_device,
|
||||
LIBINPUT_DEVICE_CAP_KEYBOARD)) {
|
||||
weston_seat_init_keyboard(seat, NULL);
|
||||
device->seat_caps |= EVDEV_SEAT_KEYBOARD;
|
||||
}
|
||||
if (libinput_device_has_capability(libinput_device,
|
||||
LIBINPUT_DEVICE_CAP_POINTER)) {
|
||||
weston_seat_init_pointer(seat);
|
||||
device->seat_caps |= EVDEV_SEAT_POINTER;
|
||||
}
|
||||
if (libinput_device_has_capability(libinput_device,
|
||||
LIBINPUT_DEVICE_CAP_TOUCH)) {
|
||||
weston_seat_init_touch(seat);
|
||||
device->seat_caps |= EVDEV_SEAT_TOUCH;
|
||||
}
|
||||
|
||||
libinput_device_set_user_data(libinput_device, device);
|
||||
libinput_device_ref(libinput_device);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void
|
||||
evdev_device_destroy(struct evdev_device *device)
|
||||
{
|
||||
if (device->seat_caps & EVDEV_SEAT_POINTER)
|
||||
weston_seat_release_pointer(device->seat);
|
||||
if (device->seat_caps & EVDEV_SEAT_KEYBOARD)
|
||||
weston_seat_release_keyboard(device->seat);
|
||||
if (device->seat_caps & EVDEV_SEAT_TOUCH)
|
||||
weston_seat_release_touch(device->seat);
|
||||
|
||||
if (device->output)
|
||||
wl_list_remove(&device->output_destroy_listener.link);
|
||||
wl_list_remove(&device->link);
|
||||
libinput_device_unref(device->device);
|
||||
free(device->devnode);
|
||||
free(device->output_name);
|
||||
free(device);
|
||||
}
|
||||
|
||||
void
|
||||
evdev_notify_keyboard_focus(struct weston_seat *seat,
|
||||
struct wl_list *evdev_devices)
|
||||
{
|
||||
struct wl_array keys;
|
||||
|
||||
if (seat->keyboard_device_count == 0)
|
||||
return;
|
||||
|
||||
wl_array_init(&keys);
|
||||
notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
|
||||
wl_array_release(&keys);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2011, 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBINPUT_DEVICE_H_
|
||||
#define _LIBINPUT_DEVICE_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-util.h>
|
||||
#include <libinput.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
enum evdev_device_seat_capability {
|
||||
EVDEV_SEAT_POINTER = (1 << 0),
|
||||
EVDEV_SEAT_KEYBOARD = (1 << 1),
|
||||
EVDEV_SEAT_TOUCH = (1 << 2)
|
||||
};
|
||||
|
||||
struct evdev_device {
|
||||
struct weston_seat *seat;
|
||||
enum evdev_device_seat_capability seat_caps;
|
||||
struct libinput_device *device;
|
||||
struct wl_list link;
|
||||
struct weston_output *output;
|
||||
struct wl_listener output_destroy_listener;
|
||||
char *devnode;
|
||||
char *output_name;
|
||||
int fd;
|
||||
};
|
||||
|
||||
void
|
||||
evdev_led_update(struct evdev_device *device, enum weston_led leds);
|
||||
|
||||
struct evdev_device *
|
||||
evdev_device_create(struct libinput_device *libinput_device,
|
||||
struct weston_seat *seat);
|
||||
|
||||
int
|
||||
evdev_device_process_event(struct libinput_event *event);
|
||||
|
||||
void
|
||||
evdev_device_set_output(struct evdev_device *device,
|
||||
struct weston_output *output);
|
||||
void
|
||||
evdev_device_destroy(struct evdev_device *device);
|
||||
|
||||
void
|
||||
evdev_notify_keyboard_focus(struct weston_seat *seat,
|
||||
struct wl_list *evdev_devices);
|
||||
void
|
||||
evdev_device_set_calibration(struct evdev_device *device);
|
||||
|
||||
int
|
||||
dispatch_libinput(struct libinput *libinput);
|
||||
|
||||
#endif /* _LIBINPUT_DEVICE_H_ */
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright © 2013 Intel Corporation
|
||||
* Copyright © 2013 Jonas Ådahl
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libinput.h>
|
||||
#include <libudev.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "launcher-util.h"
|
||||
#include "libinput-seat.h"
|
||||
#include "libinput-device.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
static void
|
||||
process_events(struct udev_input *input);
|
||||
static struct udev_seat *
|
||||
udev_seat_create(struct udev_input *input, const char *seat_name);
|
||||
static void
|
||||
udev_seat_destroy(struct udev_seat *seat);
|
||||
|
||||
static struct udev_seat *
|
||||
get_udev_seat(struct udev_input *input, struct libinput_device *device)
|
||||
{
|
||||
struct libinput_seat *libinput_seat;
|
||||
const char *seat_name;
|
||||
|
||||
libinput_seat = libinput_device_get_seat(device);
|
||||
seat_name = libinput_seat_get_logical_name(libinput_seat);
|
||||
return udev_seat_get_named(input, seat_name);
|
||||
}
|
||||
|
||||
static void
|
||||
device_added(struct udev_input *input, struct libinput_device *libinput_device)
|
||||
{
|
||||
struct weston_compositor *c;
|
||||
struct evdev_device *device;
|
||||
struct weston_output *output;
|
||||
const char *output_name;
|
||||
struct weston_seat *seat;
|
||||
struct udev_seat *udev_seat;
|
||||
struct weston_pointer *pointer;
|
||||
|
||||
c = input->compositor;
|
||||
|
||||
udev_seat = get_udev_seat(input, libinput_device);
|
||||
if (!udev_seat)
|
||||
return;
|
||||
|
||||
seat = &udev_seat->base;
|
||||
device = evdev_device_create(libinput_device, seat);
|
||||
if (device == NULL)
|
||||
return;
|
||||
|
||||
if (input->configure_device != NULL)
|
||||
input->configure_device(c, device->device);
|
||||
evdev_device_set_calibration(device);
|
||||
udev_seat = (struct udev_seat *) seat;
|
||||
wl_list_insert(udev_seat->devices_list.prev, &device->link);
|
||||
|
||||
pointer = weston_seat_get_pointer(seat);
|
||||
if (seat->output && pointer)
|
||||
weston_pointer_clamp(pointer,
|
||||
&pointer->x,
|
||||
&pointer->y);
|
||||
|
||||
output_name = libinput_device_get_output_name(libinput_device);
|
||||
if (output_name) {
|
||||
device->output_name = strdup(output_name);
|
||||
wl_list_for_each(output, &c->output_list, link)
|
||||
if (output->name &&
|
||||
strcmp(output->name, device->output_name) == 0)
|
||||
evdev_device_set_output(device, output);
|
||||
} else if (device->output == NULL && !wl_list_empty(&c->output_list)) {
|
||||
output = container_of(c->output_list.next,
|
||||
struct weston_output, link);
|
||||
evdev_device_set_output(device, output);
|
||||
}
|
||||
|
||||
if (!input->suspended)
|
||||
weston_seat_repick(seat);
|
||||
}
|
||||
|
||||
static void
|
||||
device_removed(struct udev_input *input, struct libinput_device *libinput_device)
|
||||
{
|
||||
struct evdev_device *device;
|
||||
|
||||
device = libinput_device_get_user_data(libinput_device);
|
||||
evdev_device_destroy(device);
|
||||
}
|
||||
|
||||
static void
|
||||
udev_seat_remove_devices(struct udev_seat *seat)
|
||||
{
|
||||
struct evdev_device *device, *next;
|
||||
|
||||
wl_list_for_each_safe(device, next, &seat->devices_list, link) {
|
||||
evdev_device_destroy(device);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
udev_input_disable(struct udev_input *input)
|
||||
{
|
||||
if (input->suspended)
|
||||
return;
|
||||
|
||||
libinput_suspend(input->libinput);
|
||||
process_events(input);
|
||||
input->suspended = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
udev_input_process_event(struct libinput_event *event)
|
||||
{
|
||||
struct libinput *libinput = libinput_event_get_context(event);
|
||||
struct libinput_device *libinput_device =
|
||||
libinput_event_get_device(event);
|
||||
struct udev_input *input = libinput_get_user_data(libinput);
|
||||
int handled = 1;
|
||||
|
||||
switch (libinput_event_get_type(event)) {
|
||||
case LIBINPUT_EVENT_DEVICE_ADDED:
|
||||
device_added(input, libinput_device);
|
||||
break;
|
||||
case LIBINPUT_EVENT_DEVICE_REMOVED:
|
||||
device_removed(input, libinput_device);
|
||||
break;
|
||||
default:
|
||||
handled = 0;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static void
|
||||
process_event(struct libinput_event *event)
|
||||
{
|
||||
if (udev_input_process_event(event))
|
||||
return;
|
||||
if (evdev_device_process_event(event))
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
process_events(struct udev_input *input)
|
||||
{
|
||||
struct libinput_event *event;
|
||||
|
||||
while ((event = libinput_get_event(input->libinput))) {
|
||||
process_event(event);
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
udev_input_dispatch(struct udev_input *input)
|
||||
{
|
||||
if (libinput_dispatch(input->libinput) != 0)
|
||||
weston_log("libinput: Failed to dispatch libinput\n");
|
||||
|
||||
process_events(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
libinput_source_dispatch(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct udev_input *input = data;
|
||||
|
||||
return udev_input_dispatch(input) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
open_restricted(const char *path, int flags, void *user_data)
|
||||
{
|
||||
struct udev_input *input = user_data;
|
||||
struct weston_launcher *launcher = input->compositor->launcher;
|
||||
|
||||
return weston_launcher_open(launcher, path, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
close_restricted(int fd, void *user_data)
|
||||
{
|
||||
struct udev_input *input = user_data;
|
||||
struct weston_launcher *launcher = input->compositor->launcher;
|
||||
|
||||
weston_launcher_close(launcher, fd);
|
||||
}
|
||||
|
||||
const struct libinput_interface libinput_interface = {
|
||||
open_restricted,
|
||||
close_restricted,
|
||||
};
|
||||
|
||||
int
|
||||
udev_input_enable(struct udev_input *input)
|
||||
{
|
||||
struct wl_event_loop *loop;
|
||||
struct weston_compositor *c = input->compositor;
|
||||
int fd;
|
||||
struct udev_seat *seat;
|
||||
int devices_found = 0;
|
||||
|
||||
loop = wl_display_get_event_loop(c->wl_display);
|
||||
fd = libinput_get_fd(input->libinput);
|
||||
input->libinput_source =
|
||||
wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
||||
libinput_source_dispatch, input);
|
||||
if (!input->libinput_source) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (input->suspended) {
|
||||
if (libinput_resume(input->libinput) != 0) {
|
||||
wl_event_source_remove(input->libinput_source);
|
||||
input->libinput_source = NULL;
|
||||
return -1;
|
||||
}
|
||||
input->suspended = 0;
|
||||
process_events(input);
|
||||
}
|
||||
|
||||
wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
|
||||
evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
|
||||
|
||||
if (!wl_list_empty(&seat->devices_list))
|
||||
devices_found = 1;
|
||||
}
|
||||
|
||||
if (devices_found == 0) {
|
||||
weston_log(
|
||||
"warning: no input devices on entering Weston. "
|
||||
"Possible causes:\n"
|
||||
"\t- no permissions to read /dev/input/event*\n"
|
||||
"\t- seats misconfigured "
|
||||
"(Weston backend option 'seat', "
|
||||
"udev device property ID_SEAT)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
libinput_log_func(struct libinput *libinput,
|
||||
enum libinput_log_priority priority,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
weston_vlog(format, args);
|
||||
}
|
||||
|
||||
int
|
||||
udev_input_init(struct udev_input *input, struct weston_compositor *c,
|
||||
struct udev *udev, const char *seat_id,
|
||||
udev_configure_device_t configure_device)
|
||||
{
|
||||
enum libinput_log_priority priority = LIBINPUT_LOG_PRIORITY_INFO;
|
||||
const char *log_priority = NULL;
|
||||
|
||||
memset(input, 0, sizeof *input);
|
||||
|
||||
input->compositor = c;
|
||||
input->configure_device = configure_device;
|
||||
|
||||
log_priority = getenv("WESTON_LIBINPUT_LOG_PRIORITY");
|
||||
|
||||
input->libinput = libinput_udev_create_context(&libinput_interface,
|
||||
input, udev);
|
||||
if (!input->libinput) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
libinput_log_set_handler(input->libinput, &libinput_log_func);
|
||||
|
||||
if (log_priority) {
|
||||
if (strcmp(log_priority, "debug") == 0) {
|
||||
priority = LIBINPUT_LOG_PRIORITY_DEBUG;
|
||||
} else if (strcmp(log_priority, "info") == 0) {
|
||||
priority = LIBINPUT_LOG_PRIORITY_INFO;
|
||||
} else if (strcmp(log_priority, "error") == 0) {
|
||||
priority = LIBINPUT_LOG_PRIORITY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
libinput_log_set_priority(input->libinput, priority);
|
||||
|
||||
if (libinput_udev_assign_seat(input->libinput, seat_id) != 0) {
|
||||
libinput_unref(input->libinput);
|
||||
return -1;
|
||||
}
|
||||
|
||||
process_events(input);
|
||||
|
||||
return udev_input_enable(input);
|
||||
}
|
||||
|
||||
void
|
||||
udev_input_destroy(struct udev_input *input)
|
||||
{
|
||||
struct udev_seat *seat, *next;
|
||||
|
||||
wl_event_source_remove(input->libinput_source);
|
||||
wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link)
|
||||
udev_seat_destroy(seat);
|
||||
libinput_unref(input->libinput);
|
||||
}
|
||||
|
||||
static void
|
||||
udev_seat_led_update(struct weston_seat *seat_base, enum weston_led leds)
|
||||
{
|
||||
struct udev_seat *seat = (struct udev_seat *) seat_base;
|
||||
struct evdev_device *device;
|
||||
|
||||
wl_list_for_each(device, &seat->devices_list, link)
|
||||
evdev_led_update(device, leds);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_output_create(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct udev_seat *seat = container_of(listener, struct udev_seat,
|
||||
output_create_listener);
|
||||
struct evdev_device *device;
|
||||
struct weston_output *output = data;
|
||||
|
||||
wl_list_for_each(device, &seat->devices_list, link) {
|
||||
if (device->output_name &&
|
||||
strcmp(output->name, device->output_name) == 0) {
|
||||
evdev_device_set_output(device, output);
|
||||
}
|
||||
|
||||
if (device->output_name == NULL && device->output == NULL)
|
||||
evdev_device_set_output(device, output);
|
||||
}
|
||||
}
|
||||
|
||||
static struct udev_seat *
|
||||
udev_seat_create(struct udev_input *input, const char *seat_name)
|
||||
{
|
||||
struct weston_compositor *c = input->compositor;
|
||||
struct udev_seat *seat;
|
||||
|
||||
seat = zalloc(sizeof *seat);
|
||||
if (!seat)
|
||||
return NULL;
|
||||
|
||||
weston_seat_init(&seat->base, c, seat_name);
|
||||
seat->base.led_update = udev_seat_led_update;
|
||||
|
||||
seat->output_create_listener.notify = notify_output_create;
|
||||
wl_signal_add(&c->output_created_signal,
|
||||
&seat->output_create_listener);
|
||||
|
||||
wl_list_init(&seat->devices_list);
|
||||
|
||||
return seat;
|
||||
}
|
||||
|
||||
static void
|
||||
udev_seat_destroy(struct udev_seat *seat)
|
||||
{
|
||||
struct weston_keyboard *keyboard =
|
||||
weston_seat_get_keyboard(&seat->base);
|
||||
|
||||
if (keyboard)
|
||||
notify_keyboard_focus_out(&seat->base);
|
||||
|
||||
udev_seat_remove_devices(seat);
|
||||
weston_seat_release(&seat->base);
|
||||
wl_list_remove(&seat->output_create_listener.link);
|
||||
free(seat);
|
||||
}
|
||||
|
||||
struct udev_seat *
|
||||
udev_seat_get_named(struct udev_input *input, const char *seat_name)
|
||||
{
|
||||
struct udev_seat *seat;
|
||||
|
||||
wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
|
||||
if (strcmp(seat->base.seat_name, seat_name) == 0)
|
||||
return seat;
|
||||
}
|
||||
|
||||
return udev_seat_create(input, seat_name);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright © 2013 Intel Corporation
|
||||
* Copyright © 2013 Jonas Ådahl
|
||||
*
|
||||
* 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 _LIBINPUT_SEAT_H_
|
||||
#define _LIBINPUT_SEAT_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libudev.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
struct libinput_device;
|
||||
|
||||
struct udev_seat {
|
||||
struct weston_seat base;
|
||||
struct wl_list devices_list;
|
||||
struct wl_listener output_create_listener;
|
||||
};
|
||||
|
||||
typedef void (*udev_configure_device_t)(struct weston_compositor *compositor,
|
||||
struct libinput_device *device);
|
||||
|
||||
struct udev_input {
|
||||
struct libinput *libinput;
|
||||
struct wl_event_source *libinput_source;
|
||||
struct weston_compositor *compositor;
|
||||
int suspended;
|
||||
udev_configure_device_t configure_device;
|
||||
};
|
||||
|
||||
int
|
||||
udev_input_enable(struct udev_input *input);
|
||||
void
|
||||
udev_input_disable(struct udev_input *input);
|
||||
int
|
||||
udev_input_init(struct udev_input *input,
|
||||
struct weston_compositor *c,
|
||||
struct udev *udev,
|
||||
const char *seat_id,
|
||||
udev_configure_device_t configure_device);
|
||||
void
|
||||
udev_input_destroy(struct udev_input *input);
|
||||
|
||||
struct udev_seat *
|
||||
udev_seat_get_named(struct udev_input *u,
|
||||
const char *seat_name);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
pkgincludedir=${includedir}/libweston-@LIBWESTON_ABI_VERSION@
|
||||
|
||||
Name: libweston API
|
||||
Description: Header files for libweston compositors development
|
||||
Version: @WESTON_VERSION@
|
||||
Requires.private: wayland-server pixman-1 xkbcommon
|
||||
Cflags: -I${pkgincludedir}
|
||||
Libs: -L${libdir} -lweston-@LIBWESTON_ABI_VERSION@
|
||||
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
* Copyright © 2014, 2015 Collabora, Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "linux-dmabuf.h"
|
||||
#include "linux-dmabuf-unstable-v1-server-protocol.h"
|
||||
|
||||
static void
|
||||
linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < buffer->attributes.n_planes; i++) {
|
||||
close(buffer->attributes.fd[i]);
|
||||
buffer->attributes.fd[i] = -1;
|
||||
}
|
||||
|
||||
buffer->attributes.n_planes = 0;
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_params(struct wl_resource *params_resource)
|
||||
{
|
||||
struct linux_dmabuf_buffer *buffer;
|
||||
|
||||
buffer = wl_resource_get_user_data(params_resource);
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
linux_dmabuf_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
params_destroy(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void
|
||||
params_add(struct wl_client *client,
|
||||
struct wl_resource *params_resource,
|
||||
int32_t name_fd,
|
||||
uint32_t plane_idx,
|
||||
uint32_t offset,
|
||||
uint32_t stride,
|
||||
uint32_t modifier_hi,
|
||||
uint32_t modifier_lo)
|
||||
{
|
||||
struct linux_dmabuf_buffer *buffer;
|
||||
|
||||
buffer = wl_resource_get_user_data(params_resource);
|
||||
if (!buffer) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
|
||||
"params was already used to create a wl_buffer");
|
||||
close(name_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(buffer->params_resource == params_resource);
|
||||
assert(!buffer->buffer_resource);
|
||||
|
||||
if (plane_idx >= MAX_DMABUF_PLANES) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
|
||||
"plane index %u is too high", plane_idx);
|
||||
close(name_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer->attributes.fd[plane_idx] != -1) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
|
||||
"a dmabuf has already been added for plane %u",
|
||||
plane_idx);
|
||||
close(name_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->attributes.fd[plane_idx] = name_fd;
|
||||
buffer->attributes.offset[plane_idx] = offset;
|
||||
buffer->attributes.stride[plane_idx] = stride;
|
||||
buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
|
||||
modifier_lo;
|
||||
buffer->attributes.n_planes++;
|
||||
}
|
||||
|
||||
static void
|
||||
linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
|
||||
linux_dmabuf_wl_buffer_destroy
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
|
||||
{
|
||||
struct linux_dmabuf_buffer *buffer;
|
||||
|
||||
buffer = wl_resource_get_user_data(resource);
|
||||
assert(buffer->buffer_resource == resource);
|
||||
assert(!buffer->params_resource);
|
||||
|
||||
if (buffer->user_data_destroy_func)
|
||||
buffer->user_data_destroy_func(buffer);
|
||||
|
||||
linux_dmabuf_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
params_create(struct wl_client *client,
|
||||
struct wl_resource *params_resource,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t format,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct linux_dmabuf_buffer *buffer;
|
||||
int i;
|
||||
|
||||
buffer = wl_resource_get_user_data(params_resource);
|
||||
|
||||
if (!buffer) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
|
||||
"params was already used to create a wl_buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(buffer->params_resource == params_resource);
|
||||
assert(!buffer->buffer_resource);
|
||||
|
||||
/* Switch the linux_dmabuf_buffer object from params resource to
|
||||
* eventually wl_buffer resource.
|
||||
*/
|
||||
wl_resource_set_user_data(buffer->params_resource, NULL);
|
||||
buffer->params_resource = NULL;
|
||||
|
||||
if (!buffer->attributes.n_planes) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
|
||||
"no dmabuf has been added to the params");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
|
||||
for (i = 0; i < buffer->attributes.n_planes; i++) {
|
||||
if (buffer->attributes.fd[i] == -1) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
|
||||
"no dmabuf has been added for plane %i", i);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
buffer->attributes.width = width;
|
||||
buffer->attributes.height = height;
|
||||
buffer->attributes.format = format;
|
||||
buffer->attributes.flags = flags;
|
||||
|
||||
if (width < 1 || height < 1) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
|
||||
"invalid width %d or height %d", width, height);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (i = 0; i < buffer->attributes.n_planes; i++) {
|
||||
off_t size;
|
||||
|
||||
if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
|
||||
"size overflow for plane %i", i);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (i == 0 &&
|
||||
(uint64_t) buffer->attributes.offset[i] +
|
||||
(uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
|
||||
"size overflow for plane %i", i);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Don't report an error as it might be caused
|
||||
* by the kernel not supporting seeking on dmabuf */
|
||||
size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
|
||||
if (size == -1)
|
||||
continue;
|
||||
|
||||
if (buffer->attributes.offset[i] >= size) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
|
||||
"invalid offset %i for plane %i",
|
||||
buffer->attributes.offset[i], i);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
|
||||
"invalid stride %i for plane %i",
|
||||
buffer->attributes.stride[i], i);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Only valid for first plane as other planes might be
|
||||
* sub-sampled according to fourcc format */
|
||||
if (i == 0 &&
|
||||
buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
|
||||
wl_resource_post_error(params_resource,
|
||||
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
|
||||
"invalid buffer stride or height for plane %i", i);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: Some additional sanity checks could be done with respect
|
||||
* to the fourcc format. A centralized collection (kernel or
|
||||
* libdrm) would be useful to avoid code duplication for these
|
||||
* checks (e.g. drm_format_num_planes).
|
||||
*/
|
||||
|
||||
if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
|
||||
goto err_failed;
|
||||
|
||||
buffer->buffer_resource = wl_resource_create(client,
|
||||
&wl_buffer_interface,
|
||||
1, 0);
|
||||
if (!buffer->buffer_resource) {
|
||||
wl_resource_post_no_memory(params_resource);
|
||||
goto err_buffer;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(buffer->buffer_resource,
|
||||
&linux_dmabuf_buffer_implementation,
|
||||
buffer, destroy_linux_dmabuf_wl_buffer);
|
||||
|
||||
zwp_linux_buffer_params_v1_send_created(params_resource,
|
||||
buffer->buffer_resource);
|
||||
|
||||
return;
|
||||
|
||||
err_buffer:
|
||||
if (buffer->user_data_destroy_func)
|
||||
buffer->user_data_destroy_func(buffer);
|
||||
|
||||
err_failed:
|
||||
zwp_linux_buffer_params_v1_send_failed(params_resource);
|
||||
|
||||
err_out:
|
||||
linux_dmabuf_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
static const struct zwp_linux_buffer_params_v1_interface
|
||||
zwp_linux_buffer_params_implementation = {
|
||||
params_destroy,
|
||||
params_add,
|
||||
params_create
|
||||
};
|
||||
|
||||
static void
|
||||
linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void
|
||||
linux_dmabuf_create_params(struct wl_client *client,
|
||||
struct wl_resource *linux_dmabuf_resource,
|
||||
uint32_t params_id)
|
||||
{
|
||||
struct weston_compositor *compositor;
|
||||
struct linux_dmabuf_buffer *buffer;
|
||||
uint32_t version;
|
||||
int i;
|
||||
|
||||
version = wl_resource_get_version(linux_dmabuf_resource);
|
||||
compositor = wl_resource_get_user_data(linux_dmabuf_resource);
|
||||
|
||||
buffer = zalloc(sizeof *buffer);
|
||||
if (!buffer)
|
||||
goto err_out;
|
||||
|
||||
for (i = 0; i < MAX_DMABUF_PLANES; i++)
|
||||
buffer->attributes.fd[i] = -1;
|
||||
|
||||
buffer->compositor = compositor;
|
||||
buffer->params_resource =
|
||||
wl_resource_create(client,
|
||||
&zwp_linux_buffer_params_v1_interface,
|
||||
version, params_id);
|
||||
if (!buffer->params_resource)
|
||||
goto err_dealloc;
|
||||
|
||||
wl_resource_set_implementation(buffer->params_resource,
|
||||
&zwp_linux_buffer_params_implementation,
|
||||
buffer, destroy_params);
|
||||
|
||||
return;
|
||||
|
||||
err_dealloc:
|
||||
free(buffer);
|
||||
|
||||
err_out:
|
||||
wl_resource_post_no_memory(linux_dmabuf_resource);
|
||||
}
|
||||
|
||||
/** Get the linux_dmabuf_buffer from a wl_buffer resource
|
||||
*
|
||||
* If the given wl_buffer resource was created through the linux_dmabuf
|
||||
* protocol interface, returns the linux_dmabuf_buffer object. This can
|
||||
* be used as a type check for a wl_buffer.
|
||||
*
|
||||
* \param resource A wl_buffer resource.
|
||||
* \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
|
||||
*/
|
||||
WL_EXPORT struct linux_dmabuf_buffer *
|
||||
linux_dmabuf_buffer_get(struct wl_resource *resource)
|
||||
{
|
||||
struct linux_dmabuf_buffer *buffer;
|
||||
|
||||
if (!resource)
|
||||
return NULL;
|
||||
|
||||
if (!wl_resource_instance_of(resource, &wl_buffer_interface,
|
||||
&linux_dmabuf_buffer_implementation))
|
||||
return NULL;
|
||||
|
||||
buffer = wl_resource_get_user_data(resource);
|
||||
assert(buffer);
|
||||
assert(!buffer->params_resource);
|
||||
assert(buffer->buffer_resource == resource);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** Set renderer-private data
|
||||
*
|
||||
* Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
|
||||
* a non-NULL user data with a new non-NULL pointer. This is meant to
|
||||
* protect against renderers fighting over linux_dmabuf_buffer user data
|
||||
* ownership.
|
||||
*
|
||||
* The renderer-private data is usually set from the
|
||||
* weston_renderer::import_dmabuf hook.
|
||||
*
|
||||
* \param buffer The linux_dmabuf_buffer object to set for.
|
||||
* \param data The new renderer-private data pointer.
|
||||
* \param func Destructor function to be called for the renderer-private
|
||||
* data when the linux_dmabuf_buffer gets destroyed.
|
||||
*
|
||||
* \sa weston_compositor_import_dmabuf
|
||||
*/
|
||||
WL_EXPORT void
|
||||
linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
|
||||
void *data,
|
||||
dmabuf_user_data_destroy_func func)
|
||||
{
|
||||
assert(data == NULL || buffer->user_data == NULL);
|
||||
|
||||
buffer->user_data = data;
|
||||
buffer->user_data_destroy_func = func;
|
||||
}
|
||||
|
||||
/** Get renderer-private data
|
||||
*
|
||||
* Get the user data from the linux_dmabuf_buffer.
|
||||
*
|
||||
* \param buffer The linux_dmabuf_buffer to query.
|
||||
* \return Renderer-private data pointer.
|
||||
*
|
||||
* \sa linux_dmabuf_buffer_set_user_data
|
||||
*/
|
||||
WL_EXPORT void *
|
||||
linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
|
||||
{
|
||||
return buffer->user_data;
|
||||
}
|
||||
|
||||
static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
|
||||
linux_dmabuf_destroy,
|
||||
linux_dmabuf_create_params
|
||||
};
|
||||
|
||||
static void
|
||||
bind_linux_dmabuf(struct wl_client *client,
|
||||
void *data, uint32_t version, uint32_t id)
|
||||
{
|
||||
struct weston_compositor *compositor = data;
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
|
||||
version, id);
|
||||
if (resource == NULL) {
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
|
||||
compositor, NULL);
|
||||
|
||||
/* EGL_EXT_image_dma_buf_import does not provide a way to query the
|
||||
* supported pixel formats. */
|
||||
/* XXX: send formats */
|
||||
}
|
||||
|
||||
/** Advertise linux_dmabuf support
|
||||
*
|
||||
* Calling this initializes the zwp_linux_dmabuf protocol support, so that
|
||||
* the interface will be advertised to clients. Essentially it creates a
|
||||
* global. Do not call this function multiple times in the compositor's
|
||||
* lifetime. There is no way to deinit explicitly, globals will be reaped
|
||||
* when the wl_display gets destroyed.
|
||||
*
|
||||
* \param compositor The compositor to init for.
|
||||
* \return Zero on success, -1 on failure.
|
||||
*/
|
||||
WL_EXPORT int
|
||||
linux_dmabuf_setup(struct weston_compositor *compositor)
|
||||
{
|
||||
if (!wl_global_create(compositor->wl_display,
|
||||
&zwp_linux_dmabuf_v1_interface, 1,
|
||||
compositor, bind_linux_dmabuf))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Resolve an internal compositor error by disconnecting the client.
|
||||
*
|
||||
* This function is used in cases when the dmabuf-based wl_buffer
|
||||
* turns out unusable and there is no fallback path. This is used by
|
||||
* renderers which are the fallback path in the first place.
|
||||
*
|
||||
* It is possible the fault is caused by a compositor bug, the underlying
|
||||
* graphics stack bug or normal behaviour, or perhaps a client mistake.
|
||||
* In any case, the options are to either composite garbage or nothing,
|
||||
* or disconnect the client. This is a helper function for the latter.
|
||||
*
|
||||
* The error is sent as a INVALID_OBJECT error on the client's wl_display.
|
||||
*
|
||||
* \param buffer The linux_dmabuf_buffer that is unusable.
|
||||
* \param msg A custom error message attached to the protocol error.
|
||||
*/
|
||||
WL_EXPORT void
|
||||
linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
|
||||
const char *msg)
|
||||
{
|
||||
struct wl_client *client;
|
||||
struct wl_resource *display_resource;
|
||||
uint32_t id;
|
||||
|
||||
assert(buffer->buffer_resource);
|
||||
id = wl_resource_get_id(buffer->buffer_resource);
|
||||
client = wl_resource_get_client(buffer->buffer_resource);
|
||||
display_resource = wl_client_get_object(client, 1);
|
||||
|
||||
assert(display_resource);
|
||||
wl_resource_post_error(display_resource,
|
||||
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"linux_dmabuf server error with "
|
||||
"wl_buffer@%u: %s", id, msg);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright © 2014, 2015 Collabora, Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_LINUX_DMABUF_H
|
||||
#define WESTON_LINUX_DMABUF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_DMABUF_PLANES 4
|
||||
|
||||
struct linux_dmabuf_buffer;
|
||||
typedef void (*dmabuf_user_data_destroy_func)(
|
||||
struct linux_dmabuf_buffer *buffer);
|
||||
|
||||
struct dmabuf_attributes {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint32_t format;
|
||||
uint32_t flags; /* enum zlinux_buffer_params_flags */
|
||||
int n_planes;
|
||||
int fd[MAX_DMABUF_PLANES];
|
||||
uint32_t offset[MAX_DMABUF_PLANES];
|
||||
uint32_t stride[MAX_DMABUF_PLANES];
|
||||
uint64_t modifier[MAX_DMABUF_PLANES];
|
||||
};
|
||||
|
||||
struct linux_dmabuf_buffer {
|
||||
struct wl_resource *buffer_resource;
|
||||
struct wl_resource *params_resource;
|
||||
struct weston_compositor *compositor;
|
||||
struct dmabuf_attributes attributes;
|
||||
|
||||
void *user_data;
|
||||
dmabuf_user_data_destroy_func user_data_destroy_func;
|
||||
|
||||
/* XXX:
|
||||
*
|
||||
* Add backend private data. This would be for the backend
|
||||
* to do all additional imports it might ever use in advance.
|
||||
* The basic principle, even if not implemented in drivers today,
|
||||
* is that dmabufs are first attached, but the actual allocation
|
||||
* is deferred to first use. This would allow the exporter and all
|
||||
* attachers to agree on how to allocate.
|
||||
*
|
||||
* The DRM backend would use this to create drmFBs for each
|
||||
* dmabuf_buffer, just in case at some point it would become
|
||||
* feasible to scan it out directly. This would improve the
|
||||
* possibilities to successfully scan out, avoiding compositing.
|
||||
*/
|
||||
};
|
||||
|
||||
int
|
||||
linux_dmabuf_setup(struct weston_compositor *compositor);
|
||||
|
||||
struct linux_dmabuf_buffer *
|
||||
linux_dmabuf_buffer_get(struct wl_resource *resource);
|
||||
|
||||
void
|
||||
linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
|
||||
void *data,
|
||||
dmabuf_user_data_destroy_func func);
|
||||
void *
|
||||
linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer);
|
||||
|
||||
void
|
||||
linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
|
||||
const char *msg);
|
||||
|
||||
#endif /* WESTON_LINUX_DMABUF_H */
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright © 2012 Martin Minarik
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
static log_func_t log_handler = 0;
|
||||
static log_func_t log_continue_handler = 0;
|
||||
|
||||
/** Install the log handler
|
||||
*
|
||||
* The given functions will be called to output text as passed to the
|
||||
* \a weston_log and \a weston_log_continue functions.
|
||||
*
|
||||
* \param log The log function. This function will be called when
|
||||
* \a weston_log is called, and should begin a new line,
|
||||
* with user defined line headers, if any.
|
||||
* \param cont The continue log function. This function will be called
|
||||
* when \a weston_log_continue is called, and should append
|
||||
* its output to the current line, without any header or
|
||||
* other content in between.
|
||||
*/
|
||||
WL_EXPORT void
|
||||
weston_log_set_handler(log_func_t log, log_func_t cont)
|
||||
{
|
||||
log_handler = log;
|
||||
log_continue_handler = cont;
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_vlog(const char *fmt, va_list ap)
|
||||
{
|
||||
return log_handler(fmt, ap);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_log(const char *fmt, ...)
|
||||
{
|
||||
int l;
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, fmt);
|
||||
l = weston_vlog(fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_vlog_continue(const char *fmt, va_list argp)
|
||||
{
|
||||
return log_continue_handler(fmt, argp);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_log_continue(const char *fmt, ...)
|
||||
{
|
||||
int l;
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, fmt);
|
||||
l = weston_vlog_continue(fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
return l;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright © 2012 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 <stdlib.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
static int
|
||||
noop_renderer_read_pixels(struct weston_output *output,
|
||||
pixman_format_code_t format, void *pixels,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_repaint_output(struct weston_output *output,
|
||||
pixman_region32_t *output_damage)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_flush_damage(struct weston_surface *surface)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
{
|
||||
struct wl_shm_buffer *shm_buffer;
|
||||
uint8_t *data;
|
||||
uint32_t size, i, width, height, stride;
|
||||
volatile unsigned char unused = 0; /* volatile so it's not optimized out */
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
shm_buffer = wl_shm_buffer_get(buffer->resource);
|
||||
|
||||
if (!shm_buffer) {
|
||||
weston_log("No-op renderer supports only SHM buffers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
data = wl_shm_buffer_get_data(shm_buffer);
|
||||
stride = wl_shm_buffer_get_stride(shm_buffer);
|
||||
width = wl_shm_buffer_get_width(shm_buffer);
|
||||
height = wl_shm_buffer_get_height(shm_buffer);
|
||||
size = stride * height;
|
||||
|
||||
/* Access the buffer data to make sure the buffer's client gets killed
|
||||
* if the buffer size is invalid. This makes the bad_buffer test pass.
|
||||
* This can be removed if we start reading the buffer contents
|
||||
* somewhere else, e.g. in repaint_output(). */
|
||||
wl_shm_buffer_begin_access(shm_buffer);
|
||||
for (i = 0; i < size; i++)
|
||||
unused ^= data[i];
|
||||
wl_shm_buffer_end_access(shm_buffer);
|
||||
|
||||
buffer->shm_buffer = shm_buffer;
|
||||
buffer->width = width;
|
||||
buffer->height = height;
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_surface_set_color(struct weston_surface *surface,
|
||||
float red, float green, float blue, float alpha)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_destroy(struct weston_compositor *ec)
|
||||
{
|
||||
free(ec->renderer);
|
||||
ec->renderer = NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
noop_renderer_init(struct weston_compositor *ec)
|
||||
{
|
||||
struct weston_renderer *renderer;
|
||||
|
||||
renderer = malloc(sizeof *renderer);
|
||||
if (renderer == NULL)
|
||||
return -1;
|
||||
|
||||
renderer->read_pixels = noop_renderer_read_pixels;
|
||||
renderer->repaint_output = noop_renderer_repaint_output;
|
||||
renderer->flush_damage = noop_renderer_flush_damage;
|
||||
renderer->attach = noop_renderer_attach;
|
||||
renderer->surface_set_color = noop_renderer_surface_set_color;
|
||||
renderer->destroy = noop_renderer_destroy;
|
||||
ec->renderer = renderer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,931 @@
|
||||
/*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
* Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
* Copyright © 2015 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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "pixman-renderer.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
struct pixman_output_state {
|
||||
void *shadow_buffer;
|
||||
pixman_image_t *shadow_image;
|
||||
pixman_image_t *hw_buffer;
|
||||
};
|
||||
|
||||
struct pixman_surface_state {
|
||||
struct weston_surface *surface;
|
||||
|
||||
pixman_image_t *image;
|
||||
struct weston_buffer_reference buffer_ref;
|
||||
|
||||
struct wl_listener buffer_destroy_listener;
|
||||
struct wl_listener surface_destroy_listener;
|
||||
struct wl_listener renderer_destroy_listener;
|
||||
};
|
||||
|
||||
struct pixman_renderer {
|
||||
struct weston_renderer base;
|
||||
|
||||
int repaint_debug;
|
||||
pixman_image_t *debug_color;
|
||||
struct weston_binding *debug_binding;
|
||||
|
||||
struct wl_signal destroy_signal;
|
||||
};
|
||||
|
||||
static inline struct pixman_output_state *
|
||||
get_output_state(struct weston_output *output)
|
||||
{
|
||||
return (struct pixman_output_state *)output->renderer_state;
|
||||
}
|
||||
|
||||
static int
|
||||
pixman_renderer_create_surface(struct weston_surface *surface);
|
||||
|
||||
static inline struct pixman_surface_state *
|
||||
get_surface_state(struct weston_surface *surface)
|
||||
{
|
||||
if (!surface->renderer_state)
|
||||
pixman_renderer_create_surface(surface);
|
||||
|
||||
return (struct pixman_surface_state *)surface->renderer_state;
|
||||
}
|
||||
|
||||
static inline struct pixman_renderer *
|
||||
get_renderer(struct weston_compositor *ec)
|
||||
{
|
||||
return (struct pixman_renderer *)ec->renderer;
|
||||
}
|
||||
|
||||
static int
|
||||
pixman_renderer_read_pixels(struct weston_output *output,
|
||||
pixman_format_code_t format, void *pixels,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
pixman_transform_t transform;
|
||||
pixman_image_t *out_buf;
|
||||
|
||||
if (!po->hw_buffer) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
out_buf = pixman_image_create_bits(format,
|
||||
width,
|
||||
height,
|
||||
pixels,
|
||||
(PIXMAN_FORMAT_BPP(format) / 8) * width);
|
||||
|
||||
/* Caller expects vflipped source image */
|
||||
pixman_transform_init_translate(&transform,
|
||||
pixman_int_to_fixed (x),
|
||||
pixman_int_to_fixed (y - pixman_image_get_height (po->hw_buffer)));
|
||||
pixman_transform_scale(&transform, NULL,
|
||||
pixman_fixed_1,
|
||||
pixman_fixed_minus_1);
|
||||
pixman_image_set_transform(po->hw_buffer, &transform);
|
||||
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
po->hw_buffer, /* src */
|
||||
NULL /* mask */,
|
||||
out_buf, /* dest */
|
||||
0, 0, /* src_x, src_y */
|
||||
0, 0, /* mask_x, mask_y */
|
||||
0, 0, /* dest_x, dest_y */
|
||||
pixman_image_get_width (po->hw_buffer), /* width */
|
||||
pixman_image_get_height (po->hw_buffer) /* height */);
|
||||
pixman_image_set_transform(po->hw_buffer, NULL);
|
||||
|
||||
pixman_image_unref(out_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
region_global_to_output(struct weston_output *output, pixman_region32_t *region)
|
||||
{
|
||||
if (output->zoom.active) {
|
||||
weston_matrix_transform_region(region, &output->matrix, region);
|
||||
} else {
|
||||
pixman_region32_translate(region, -output->x, -output->y);
|
||||
weston_transformed_region(output->width, output->height,
|
||||
output->transform,
|
||||
output->current_scale,
|
||||
region, region);
|
||||
}
|
||||
}
|
||||
|
||||
#define D2F(v) pixman_double_to_fixed((double)v)
|
||||
|
||||
static void
|
||||
weston_matrix_to_pixman_transform(pixman_transform_t *pt,
|
||||
const struct weston_matrix *wm)
|
||||
{
|
||||
/* Pixman supports only 2D transform matrix, but Weston uses 3D, *
|
||||
* so we're omitting Z coordinate here. */
|
||||
pt->matrix[0][0] = pixman_double_to_fixed(wm->d[0]);
|
||||
pt->matrix[0][1] = pixman_double_to_fixed(wm->d[4]);
|
||||
pt->matrix[0][2] = pixman_double_to_fixed(wm->d[12]);
|
||||
pt->matrix[1][0] = pixman_double_to_fixed(wm->d[1]);
|
||||
pt->matrix[1][1] = pixman_double_to_fixed(wm->d[5]);
|
||||
pt->matrix[1][2] = pixman_double_to_fixed(wm->d[13]);
|
||||
pt->matrix[2][0] = pixman_double_to_fixed(wm->d[3]);
|
||||
pt->matrix[2][1] = pixman_double_to_fixed(wm->d[7]);
|
||||
pt->matrix[2][2] = pixman_double_to_fixed(wm->d[15]);
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_compute_transform(pixman_transform_t *transform_out,
|
||||
struct weston_view *ev,
|
||||
struct weston_output *output)
|
||||
{
|
||||
struct weston_matrix matrix;
|
||||
|
||||
/* Set up the source transformation based on the surface
|
||||
position, the output position/transform/scale and the client
|
||||
specified buffer transform/scale */
|
||||
matrix = output->inverse_matrix;
|
||||
|
||||
if (ev->transform.enabled) {
|
||||
weston_matrix_multiply(&matrix, &ev->transform.inverse);
|
||||
} else {
|
||||
weston_matrix_translate(&matrix,
|
||||
-ev->geometry.x, -ev->geometry.y, 0);
|
||||
}
|
||||
|
||||
weston_matrix_multiply(&matrix, &ev->surface->surface_to_buffer_matrix);
|
||||
|
||||
weston_matrix_to_pixman_transform(transform_out, &matrix);
|
||||
}
|
||||
|
||||
static bool
|
||||
view_transformation_is_translation(struct weston_view *view)
|
||||
{
|
||||
if (!view->transform.enabled)
|
||||
return true;
|
||||
|
||||
if (view->transform.matrix.type <= WESTON_MATRIX_TRANSFORM_TRANSLATE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
region_intersect_only_translation(pixman_region32_t *result_global,
|
||||
pixman_region32_t *global,
|
||||
pixman_region32_t *surf,
|
||||
struct weston_view *view)
|
||||
{
|
||||
float view_x, view_y;
|
||||
|
||||
assert(view_transformation_is_translation(view));
|
||||
|
||||
/* Convert from surface to global coordinates */
|
||||
pixman_region32_copy(result_global, surf);
|
||||
weston_view_to_global_float(view, 0, 0, &view_x, &view_y);
|
||||
pixman_region32_translate(result_global, (int)view_x, (int)view_y);
|
||||
|
||||
pixman_region32_intersect(result_global, result_global, global);
|
||||
}
|
||||
|
||||
static void
|
||||
composite_whole(pixman_op_t op,
|
||||
pixman_image_t *src,
|
||||
pixman_image_t *mask,
|
||||
pixman_image_t *dest,
|
||||
const pixman_transform_t *transform,
|
||||
pixman_filter_t filter)
|
||||
{
|
||||
int32_t dest_width;
|
||||
int32_t dest_height;
|
||||
|
||||
dest_width = pixman_image_get_width(dest);
|
||||
dest_height = pixman_image_get_height(dest);
|
||||
|
||||
pixman_image_set_transform(src, transform);
|
||||
pixman_image_set_filter(src, filter, NULL, 0);
|
||||
|
||||
pixman_image_composite32(op, src, mask, dest,
|
||||
0, 0, /* src_x, src_y */
|
||||
0, 0, /* mask_x, mask_y */
|
||||
0, 0, /* dest_x, dest_y */
|
||||
dest_width, dest_height);
|
||||
}
|
||||
|
||||
static void
|
||||
composite_clipped(pixman_image_t *src,
|
||||
pixman_image_t *mask,
|
||||
pixman_image_t *dest,
|
||||
const pixman_transform_t *transform,
|
||||
pixman_filter_t filter,
|
||||
pixman_region32_t *src_clip)
|
||||
{
|
||||
int n_box;
|
||||
pixman_box32_t *boxes;
|
||||
int32_t dest_width;
|
||||
int32_t dest_height;
|
||||
int src_stride;
|
||||
int bitspp;
|
||||
pixman_format_code_t src_format;
|
||||
void *src_data;
|
||||
int i;
|
||||
|
||||
/* Hardcoded to use PIXMAN_OP_OVER, because sampling outside of
|
||||
* a Pixman image produces (0,0,0,0) instead of discarding the
|
||||
* fragment.
|
||||
*/
|
||||
|
||||
dest_width = pixman_image_get_width(dest);
|
||||
dest_height = pixman_image_get_height(dest);
|
||||
src_format = pixman_image_get_format(src);
|
||||
src_stride = pixman_image_get_stride(src);
|
||||
bitspp = PIXMAN_FORMAT_BPP(src_format);
|
||||
src_data = pixman_image_get_data(src);
|
||||
|
||||
assert(src_format);
|
||||
|
||||
/* This would be massive overdraw, except when n_box is 1. */
|
||||
boxes = pixman_region32_rectangles(src_clip, &n_box);
|
||||
for (i = 0; i < n_box; i++) {
|
||||
uint8_t *ptr = src_data;
|
||||
pixman_image_t *boximg;
|
||||
pixman_transform_t adj = *transform;
|
||||
|
||||
ptr += boxes[i].y1 * src_stride;
|
||||
ptr += boxes[i].x1 * bitspp / 8;
|
||||
boximg = pixman_image_create_bits_no_clear(src_format,
|
||||
boxes[i].x2 - boxes[i].x1,
|
||||
boxes[i].y2 - boxes[i].y1,
|
||||
(uint32_t *)ptr, src_stride);
|
||||
|
||||
pixman_transform_translate(&adj, NULL,
|
||||
pixman_int_to_fixed(-boxes[i].x1),
|
||||
pixman_int_to_fixed(-boxes[i].y1));
|
||||
pixman_image_set_transform(boximg, &adj);
|
||||
|
||||
pixman_image_set_filter(boximg, filter, NULL, 0);
|
||||
pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest,
|
||||
0, 0, /* src_x, src_y */
|
||||
0, 0, /* mask_x, mask_y */
|
||||
0, 0, /* dest_x, dest_y */
|
||||
dest_width, dest_height);
|
||||
|
||||
pixman_image_unref(boximg);
|
||||
}
|
||||
|
||||
if (n_box > 1) {
|
||||
static bool warned = false;
|
||||
|
||||
if (!warned)
|
||||
weston_log("Pixman-renderer warning: %dx overdraw\n",
|
||||
n_box);
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Paint an intersected region
|
||||
*
|
||||
* \param ev The view to be painted.
|
||||
* \param output The output being painted.
|
||||
* \param repaint_output The region to be painted in output coordinates.
|
||||
* \param source_clip The region of the source image to use, in source image
|
||||
* coordinates. If NULL, use the whole source image.
|
||||
* \param pixman_op Compositing operator, either SRC or OVER.
|
||||
*/
|
||||
static void
|
||||
repaint_region(struct weston_view *ev, struct weston_output *output,
|
||||
pixman_region32_t *repaint_output,
|
||||
pixman_region32_t *source_clip,
|
||||
pixman_op_t pixman_op)
|
||||
{
|
||||
struct pixman_renderer *pr =
|
||||
(struct pixman_renderer *) output->compositor->renderer;
|
||||
struct pixman_surface_state *ps = get_surface_state(ev->surface);
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
struct weston_buffer_viewport *vp = &ev->surface->buffer_viewport;
|
||||
pixman_transform_t transform;
|
||||
pixman_filter_t filter;
|
||||
pixman_image_t *mask_image;
|
||||
pixman_color_t mask = { 0, };
|
||||
|
||||
/* Clip rendering to the damaged output region */
|
||||
pixman_image_set_clip_region32(po->shadow_image, repaint_output);
|
||||
|
||||
pixman_renderer_compute_transform(&transform, ev, output);
|
||||
|
||||
if (ev->transform.enabled || output->current_scale != vp->buffer.scale)
|
||||
filter = PIXMAN_FILTER_BILINEAR;
|
||||
else
|
||||
filter = PIXMAN_FILTER_NEAREST;
|
||||
|
||||
if (ps->buffer_ref.buffer)
|
||||
wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer);
|
||||
|
||||
if (ev->alpha < 1.0) {
|
||||
mask.alpha = 0xffff * ev->alpha;
|
||||
mask_image = pixman_image_create_solid_fill(&mask);
|
||||
} else {
|
||||
mask_image = NULL;
|
||||
}
|
||||
|
||||
if (source_clip)
|
||||
composite_clipped(ps->image, mask_image, po->shadow_image,
|
||||
&transform, filter, source_clip);
|
||||
else
|
||||
composite_whole(pixman_op, ps->image, mask_image,
|
||||
po->shadow_image, &transform, filter);
|
||||
|
||||
if (mask_image)
|
||||
pixman_image_unref(mask_image);
|
||||
|
||||
if (ps->buffer_ref.buffer)
|
||||
wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer);
|
||||
|
||||
if (pr->repaint_debug)
|
||||
pixman_image_composite32(PIXMAN_OP_OVER,
|
||||
pr->debug_color, /* src */
|
||||
NULL /* mask */,
|
||||
po->shadow_image, /* dest */
|
||||
0, 0, /* src_x, src_y */
|
||||
0, 0, /* mask_x, mask_y */
|
||||
0, 0, /* dest_x, dest_y */
|
||||
pixman_image_get_width (po->shadow_image), /* width */
|
||||
pixman_image_get_height (po->shadow_image) /* height */);
|
||||
|
||||
pixman_image_set_clip_region32 (po->shadow_image, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_view_translated(struct weston_view *view, struct weston_output *output,
|
||||
pixman_region32_t *repaint_global)
|
||||
{
|
||||
struct weston_surface *surface = view->surface;
|
||||
/* non-opaque region in surface coordinates: */
|
||||
pixman_region32_t surface_blend;
|
||||
/* region to be painted in output coordinates: */
|
||||
pixman_region32_t repaint_output;
|
||||
|
||||
pixman_region32_init(&repaint_output);
|
||||
|
||||
/* Blended region is whole surface minus opaque region,
|
||||
* unless surface alpha forces us to blend all.
|
||||
*/
|
||||
pixman_region32_init_rect(&surface_blend, 0, 0,
|
||||
surface->width, surface->height);
|
||||
|
||||
if (!(view->alpha < 1.0)) {
|
||||
pixman_region32_subtract(&surface_blend, &surface_blend,
|
||||
&surface->opaque);
|
||||
|
||||
if (pixman_region32_not_empty(&surface->opaque)) {
|
||||
region_intersect_only_translation(&repaint_output,
|
||||
repaint_global,
|
||||
&surface->opaque,
|
||||
view);
|
||||
region_global_to_output(output, &repaint_output);
|
||||
|
||||
repaint_region(view, output, &repaint_output, NULL,
|
||||
PIXMAN_OP_SRC);
|
||||
}
|
||||
}
|
||||
|
||||
if (pixman_region32_not_empty(&surface_blend)) {
|
||||
region_intersect_only_translation(&repaint_output,
|
||||
repaint_global,
|
||||
&surface_blend, view);
|
||||
region_global_to_output(output, &repaint_output);
|
||||
|
||||
repaint_region(view, output, &repaint_output, NULL,
|
||||
PIXMAN_OP_OVER);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&surface_blend);
|
||||
pixman_region32_fini(&repaint_output);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_view_source_clipped(struct weston_view *view,
|
||||
struct weston_output *output,
|
||||
pixman_region32_t *repaint_global)
|
||||
{
|
||||
struct weston_surface *surface = view->surface;
|
||||
pixman_region32_t surf_region;
|
||||
pixman_region32_t buffer_region;
|
||||
pixman_region32_t repaint_output;
|
||||
|
||||
/* Do not bother separating the opaque region from non-opaque.
|
||||
* Source clipping requires PIXMAN_OP_OVER in all cases, so painting
|
||||
* opaque separately has no benefit.
|
||||
*/
|
||||
|
||||
pixman_region32_init_rect(&surf_region, 0, 0,
|
||||
surface->width, surface->height);
|
||||
if (view->geometry.scissor_enabled)
|
||||
pixman_region32_intersect(&surf_region, &surf_region,
|
||||
&view->geometry.scissor);
|
||||
|
||||
pixman_region32_init(&buffer_region);
|
||||
weston_surface_to_buffer_region(surface, &surf_region, &buffer_region);
|
||||
|
||||
pixman_region32_init(&repaint_output);
|
||||
pixman_region32_copy(&repaint_output, repaint_global);
|
||||
region_global_to_output(output, &repaint_output);
|
||||
|
||||
repaint_region(view, output, &repaint_output, &buffer_region,
|
||||
PIXMAN_OP_OVER);
|
||||
|
||||
pixman_region32_fini(&repaint_output);
|
||||
pixman_region32_fini(&buffer_region);
|
||||
pixman_region32_fini(&surf_region);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_view(struct weston_view *ev, struct weston_output *output,
|
||||
pixman_region32_t *damage) /* in global coordinates */
|
||||
{
|
||||
struct pixman_surface_state *ps = get_surface_state(ev->surface);
|
||||
/* repaint bounding region in global coordinates: */
|
||||
pixman_region32_t repaint;
|
||||
|
||||
/* No buffer attached */
|
||||
if (!ps->image)
|
||||
return;
|
||||
|
||||
pixman_region32_init(&repaint);
|
||||
pixman_region32_intersect(&repaint,
|
||||
&ev->transform.boundingbox, damage);
|
||||
pixman_region32_subtract(&repaint, &repaint, &ev->clip);
|
||||
|
||||
if (!pixman_region32_not_empty(&repaint))
|
||||
goto out;
|
||||
|
||||
if (view_transformation_is_translation(ev)) {
|
||||
/* The simple case: The surface regions opaque, non-opaque,
|
||||
* etc. are convertible to global coordinate space.
|
||||
* There is no need to use a source clip region.
|
||||
* It is possible to paint opaque region as PIXMAN_OP_SRC.
|
||||
* Also the boundingbox is accurate rather than an
|
||||
* approximation.
|
||||
*/
|
||||
draw_view_translated(ev, output, &repaint);
|
||||
} else {
|
||||
/* The complex case: the view transformation does not allow
|
||||
* converting opaque etc. regions into global coordinate space.
|
||||
* Therefore we need source clipping to avoid sampling from
|
||||
* unwanted source image areas, unless the source image is
|
||||
* to be used whole. Source clipping does not work with
|
||||
* PIXMAN_OP_SRC.
|
||||
*/
|
||||
draw_view_source_clipped(ev, output, &repaint);
|
||||
}
|
||||
|
||||
out:
|
||||
pixman_region32_fini(&repaint);
|
||||
}
|
||||
static void
|
||||
repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
|
||||
{
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
struct weston_view *view;
|
||||
|
||||
wl_list_for_each_reverse(view, &compositor->view_list, link)
|
||||
if (view->plane == &compositor->primary_plane)
|
||||
draw_view(view, output, damage);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region)
|
||||
{
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
pixman_region32_t output_region;
|
||||
|
||||
pixman_region32_init(&output_region);
|
||||
pixman_region32_copy(&output_region, region);
|
||||
|
||||
region_global_to_output(output, &output_region);
|
||||
|
||||
pixman_image_set_clip_region32 (po->hw_buffer, &output_region);
|
||||
pixman_region32_fini(&output_region);
|
||||
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
po->shadow_image, /* src */
|
||||
NULL /* mask */,
|
||||
po->hw_buffer, /* dest */
|
||||
0, 0, /* src_x, src_y */
|
||||
0, 0, /* mask_x, mask_y */
|
||||
0, 0, /* dest_x, dest_y */
|
||||
pixman_image_get_width (po->hw_buffer), /* width */
|
||||
pixman_image_get_height (po->hw_buffer) /* height */);
|
||||
|
||||
pixman_image_set_clip_region32 (po->hw_buffer, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_repaint_output(struct weston_output *output,
|
||||
pixman_region32_t *output_damage)
|
||||
{
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
|
||||
if (!po->hw_buffer)
|
||||
return;
|
||||
|
||||
repaint_surfaces(output, output_damage);
|
||||
copy_to_hw_buffer(output, output_damage);
|
||||
|
||||
pixman_region32_copy(&output->previous_damage, output_damage);
|
||||
wl_signal_emit(&output->frame_signal, output);
|
||||
|
||||
/* Actual flip should be done by caller */
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_flush_damage(struct weston_surface *surface)
|
||||
{
|
||||
/* No-op for pixman renderer */
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct pixman_surface_state *ps;
|
||||
|
||||
ps = container_of(listener, struct pixman_surface_state,
|
||||
buffer_destroy_listener);
|
||||
|
||||
if (ps->image) {
|
||||
pixman_image_unref(ps->image);
|
||||
ps->image = NULL;
|
||||
}
|
||||
|
||||
ps->buffer_destroy_listener.notify = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
{
|
||||
struct pixman_surface_state *ps = get_surface_state(es);
|
||||
struct wl_shm_buffer *shm_buffer;
|
||||
pixman_format_code_t pixman_format;
|
||||
|
||||
weston_buffer_reference(&ps->buffer_ref, buffer);
|
||||
|
||||
if (ps->buffer_destroy_listener.notify) {
|
||||
wl_list_remove(&ps->buffer_destroy_listener.link);
|
||||
ps->buffer_destroy_listener.notify = NULL;
|
||||
}
|
||||
|
||||
if (ps->image) {
|
||||
pixman_image_unref(ps->image);
|
||||
ps->image = NULL;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
shm_buffer = wl_shm_buffer_get(buffer->resource);
|
||||
|
||||
if (! shm_buffer) {
|
||||
weston_log("Pixman renderer supports only SHM buffers\n");
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (wl_shm_buffer_get_format(shm_buffer)) {
|
||||
case WL_SHM_FORMAT_XRGB8888:
|
||||
pixman_format = PIXMAN_x8r8g8b8;
|
||||
break;
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
pixman_format = PIXMAN_a8r8g8b8;
|
||||
break;
|
||||
case WL_SHM_FORMAT_RGB565:
|
||||
pixman_format = PIXMAN_r5g6b5;
|
||||
break;
|
||||
default:
|
||||
weston_log("Unsupported SHM buffer format\n");
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer->shm_buffer = shm_buffer;
|
||||
buffer->width = wl_shm_buffer_get_width(shm_buffer);
|
||||
buffer->height = wl_shm_buffer_get_height(shm_buffer);
|
||||
|
||||
ps->image = pixman_image_create_bits(pixman_format,
|
||||
buffer->width, buffer->height,
|
||||
wl_shm_buffer_get_data(shm_buffer),
|
||||
wl_shm_buffer_get_stride(shm_buffer));
|
||||
|
||||
ps->buffer_destroy_listener.notify =
|
||||
buffer_state_handle_buffer_destroy;
|
||||
wl_signal_add(&buffer->destroy_signal,
|
||||
&ps->buffer_destroy_listener);
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
|
||||
{
|
||||
wl_list_remove(&ps->surface_destroy_listener.link);
|
||||
wl_list_remove(&ps->renderer_destroy_listener.link);
|
||||
if (ps->buffer_destroy_listener.notify) {
|
||||
wl_list_remove(&ps->buffer_destroy_listener.link);
|
||||
ps->buffer_destroy_listener.notify = NULL;
|
||||
}
|
||||
|
||||
ps->surface->renderer_state = NULL;
|
||||
|
||||
if (ps->image) {
|
||||
pixman_image_unref(ps->image);
|
||||
ps->image = NULL;
|
||||
}
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL);
|
||||
free(ps);
|
||||
}
|
||||
|
||||
static void
|
||||
surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct pixman_surface_state *ps;
|
||||
|
||||
ps = container_of(listener, struct pixman_surface_state,
|
||||
surface_destroy_listener);
|
||||
|
||||
pixman_renderer_surface_state_destroy(ps);
|
||||
}
|
||||
|
||||
static void
|
||||
surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct pixman_surface_state *ps;
|
||||
|
||||
ps = container_of(listener, struct pixman_surface_state,
|
||||
renderer_destroy_listener);
|
||||
|
||||
pixman_renderer_surface_state_destroy(ps);
|
||||
}
|
||||
|
||||
static int
|
||||
pixman_renderer_create_surface(struct weston_surface *surface)
|
||||
{
|
||||
struct pixman_surface_state *ps;
|
||||
struct pixman_renderer *pr = get_renderer(surface->compositor);
|
||||
|
||||
ps = zalloc(sizeof *ps);
|
||||
if (ps == NULL)
|
||||
return -1;
|
||||
|
||||
surface->renderer_state = ps;
|
||||
|
||||
ps->surface = surface;
|
||||
|
||||
ps->surface_destroy_listener.notify =
|
||||
surface_state_handle_surface_destroy;
|
||||
wl_signal_add(&surface->destroy_signal,
|
||||
&ps->surface_destroy_listener);
|
||||
|
||||
ps->renderer_destroy_listener.notify =
|
||||
surface_state_handle_renderer_destroy;
|
||||
wl_signal_add(&pr->destroy_signal,
|
||||
&ps->renderer_destroy_listener);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_surface_set_color(struct weston_surface *es,
|
||||
float red, float green, float blue, float alpha)
|
||||
{
|
||||
struct pixman_surface_state *ps = get_surface_state(es);
|
||||
pixman_color_t color;
|
||||
|
||||
color.red = red * 0xffff;
|
||||
color.green = green * 0xffff;
|
||||
color.blue = blue * 0xffff;
|
||||
color.alpha = alpha * 0xffff;
|
||||
|
||||
if (ps->image) {
|
||||
pixman_image_unref(ps->image);
|
||||
ps->image = NULL;
|
||||
}
|
||||
|
||||
ps->image = pixman_image_create_solid_fill(&color);
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_destroy(struct weston_compositor *ec)
|
||||
{
|
||||
struct pixman_renderer *pr = get_renderer(ec);
|
||||
|
||||
wl_signal_emit(&pr->destroy_signal, pr);
|
||||
weston_binding_destroy(pr->debug_binding);
|
||||
free(pr);
|
||||
|
||||
ec->renderer = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_surface_get_content_size(struct weston_surface *surface,
|
||||
int *width, int *height)
|
||||
{
|
||||
struct pixman_surface_state *ps = get_surface_state(surface);
|
||||
|
||||
if (ps->image) {
|
||||
*width = pixman_image_get_width(ps->image);
|
||||
*height = pixman_image_get_height(ps->image);
|
||||
} else {
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
pixman_renderer_surface_copy_content(struct weston_surface *surface,
|
||||
void *target, size_t size,
|
||||
int src_x, int src_y,
|
||||
int width, int height)
|
||||
{
|
||||
const pixman_format_code_t format = PIXMAN_a8b8g8r8;
|
||||
const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
|
||||
struct pixman_surface_state *ps = get_surface_state(surface);
|
||||
pixman_image_t *out_buf;
|
||||
|
||||
if (!ps->image)
|
||||
return -1;
|
||||
|
||||
out_buf = pixman_image_create_bits(format, width, height,
|
||||
target, width * bytespp);
|
||||
|
||||
pixman_image_set_transform(ps->image, NULL);
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
ps->image, /* src */
|
||||
NULL, /* mask */
|
||||
out_buf, /* dest */
|
||||
src_x, src_y, /* src_x, src_y */
|
||||
0, 0, /* mask_x, mask_y */
|
||||
0, 0, /* dest_x, dest_y */
|
||||
width, height);
|
||||
|
||||
pixman_image_unref(out_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
debug_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
|
||||
void *data)
|
||||
{
|
||||
struct weston_compositor *ec = data;
|
||||
struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer;
|
||||
|
||||
pr->repaint_debug ^= 1;
|
||||
|
||||
if (pr->repaint_debug) {
|
||||
pixman_color_t red = {
|
||||
0x3fff, 0x0000, 0x0000, 0x3fff
|
||||
};
|
||||
|
||||
pr->debug_color = pixman_image_create_solid_fill(&red);
|
||||
} else {
|
||||
pixman_image_unref(pr->debug_color);
|
||||
weston_compositor_damage_all(ec);
|
||||
}
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
pixman_renderer_init(struct weston_compositor *ec)
|
||||
{
|
||||
struct pixman_renderer *renderer;
|
||||
|
||||
renderer = zalloc(sizeof *renderer);
|
||||
if (renderer == NULL)
|
||||
return -1;
|
||||
|
||||
renderer->repaint_debug = 0;
|
||||
renderer->debug_color = NULL;
|
||||
renderer->base.read_pixels = pixman_renderer_read_pixels;
|
||||
renderer->base.repaint_output = pixman_renderer_repaint_output;
|
||||
renderer->base.flush_damage = pixman_renderer_flush_damage;
|
||||
renderer->base.attach = pixman_renderer_attach;
|
||||
renderer->base.surface_set_color = pixman_renderer_surface_set_color;
|
||||
renderer->base.destroy = pixman_renderer_destroy;
|
||||
renderer->base.surface_get_content_size =
|
||||
pixman_renderer_surface_get_content_size;
|
||||
renderer->base.surface_copy_content =
|
||||
pixman_renderer_surface_copy_content;
|
||||
ec->renderer = &renderer->base;
|
||||
ec->capabilities |= WESTON_CAP_ROTATION_ANY;
|
||||
ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
|
||||
ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
|
||||
|
||||
renderer->debug_binding =
|
||||
weston_compositor_add_debug_binding(ec, KEY_R,
|
||||
debug_binding, ec);
|
||||
|
||||
wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
|
||||
|
||||
wl_signal_init(&renderer->destroy_signal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer)
|
||||
{
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
|
||||
if (po->hw_buffer)
|
||||
pixman_image_unref(po->hw_buffer);
|
||||
po->hw_buffer = buffer;
|
||||
|
||||
if (po->hw_buffer) {
|
||||
output->compositor->read_format = pixman_image_get_format(po->hw_buffer);
|
||||
pixman_image_ref(po->hw_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
pixman_renderer_output_create(struct weston_output *output)
|
||||
{
|
||||
struct pixman_output_state *po;
|
||||
int w, h;
|
||||
|
||||
po = zalloc(sizeof *po);
|
||||
if (po == NULL)
|
||||
return -1;
|
||||
|
||||
/* set shadow image transformation */
|
||||
w = output->current_mode->width;
|
||||
h = output->current_mode->height;
|
||||
|
||||
po->shadow_buffer = malloc(w * h * 4);
|
||||
|
||||
if (!po->shadow_buffer) {
|
||||
free(po);
|
||||
return -1;
|
||||
}
|
||||
|
||||
po->shadow_image =
|
||||
pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h,
|
||||
po->shadow_buffer, w * 4);
|
||||
|
||||
if (!po->shadow_image) {
|
||||
free(po->shadow_buffer);
|
||||
free(po);
|
||||
return -1;
|
||||
}
|
||||
|
||||
output->renderer_state = po;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
pixman_renderer_output_destroy(struct weston_output *output)
|
||||
{
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
|
||||
pixman_image_unref(po->shadow_image);
|
||||
|
||||
if (po->hw_buffer)
|
||||
pixman_image_unref(po->hw_buffer);
|
||||
|
||||
free(po->shadow_buffer);
|
||||
|
||||
po->shadow_buffer = NULL;
|
||||
po->shadow_image = NULL;
|
||||
po->hw_buffer = NULL;
|
||||
|
||||
free(po);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* 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 "compositor.h"
|
||||
|
||||
int
|
||||
pixman_renderer_init(struct weston_compositor *ec);
|
||||
|
||||
int
|
||||
pixman_renderer_output_create(struct weston_output *output);
|
||||
|
||||
void
|
||||
pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer);
|
||||
|
||||
void
|
||||
pixman_renderer_output_destroy(struct weston_output *output);
|
||||
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright © 2008-2011 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <linux/input.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
#include "wcap/wcap-decode.h"
|
||||
|
||||
struct screenshooter_frame_listener {
|
||||
struct wl_listener listener;
|
||||
struct weston_buffer *buffer;
|
||||
weston_screenshooter_done_func_t done;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static void
|
||||
copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
|
||||
{
|
||||
uint8_t *end;
|
||||
|
||||
end = dst + height * stride;
|
||||
while (dst < end) {
|
||||
memcpy(dst, src, stride);
|
||||
dst += stride;
|
||||
src -= stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride)
|
||||
{
|
||||
/* TODO: optimize this out */
|
||||
memcpy(dst, src, height * stride);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_row_swap_RB(void *vdst, void *vsrc, int bytes)
|
||||
{
|
||||
uint32_t *dst = vdst;
|
||||
uint32_t *src = vsrc;
|
||||
uint32_t *end = dst + bytes / 4;
|
||||
|
||||
while (dst < end) {
|
||||
uint32_t v = *src++;
|
||||
/* A R G B */
|
||||
uint32_t tmp = v & 0xff00ff00;
|
||||
tmp |= (v >> 16) & 0x000000ff;
|
||||
tmp |= (v << 16) & 0x00ff0000;
|
||||
*dst++ = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
|
||||
{
|
||||
uint8_t *end;
|
||||
|
||||
end = dst + height * stride;
|
||||
while (dst < end) {
|
||||
copy_row_swap_RB(dst, src, stride);
|
||||
dst += stride;
|
||||
src -= stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride)
|
||||
{
|
||||
uint8_t *end;
|
||||
|
||||
end = dst + height * stride;
|
||||
while (dst < end) {
|
||||
copy_row_swap_RB(dst, src, stride);
|
||||
dst += stride;
|
||||
src += stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
screenshooter_frame_notify(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct screenshooter_frame_listener *l =
|
||||
container_of(listener,
|
||||
struct screenshooter_frame_listener, listener);
|
||||
struct weston_output *output = data;
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
int32_t stride;
|
||||
uint8_t *pixels, *d, *s;
|
||||
|
||||
output->disable_planes--;
|
||||
wl_list_remove(&listener->link);
|
||||
stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
|
||||
pixels = malloc(stride * l->buffer->height);
|
||||
|
||||
if (pixels == NULL) {
|
||||
l->done(l->data, WESTON_SCREENSHOOTER_NO_MEMORY);
|
||||
free(l);
|
||||
return;
|
||||
}
|
||||
|
||||
compositor->renderer->read_pixels(output,
|
||||
compositor->read_format, pixels,
|
||||
0, 0, output->current_mode->width,
|
||||
output->current_mode->height);
|
||||
|
||||
stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer);
|
||||
|
||||
d = wl_shm_buffer_get_data(l->buffer->shm_buffer);
|
||||
s = pixels + stride * (l->buffer->height - 1);
|
||||
|
||||
wl_shm_buffer_begin_access(l->buffer->shm_buffer);
|
||||
|
||||
switch (compositor->read_format) {
|
||||
case PIXMAN_a8r8g8b8:
|
||||
case PIXMAN_x8r8g8b8:
|
||||
if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
|
||||
copy_bgra_yflip(d, s, output->current_mode->height, stride);
|
||||
else
|
||||
copy_bgra(d, pixels, output->current_mode->height, stride);
|
||||
break;
|
||||
case PIXMAN_x8b8g8r8:
|
||||
case PIXMAN_a8b8g8r8:
|
||||
if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
|
||||
copy_rgba_yflip(d, s, output->current_mode->height, stride);
|
||||
else
|
||||
copy_rgba(d, pixels, output->current_mode->height, stride);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
wl_shm_buffer_end_access(l->buffer->shm_buffer);
|
||||
|
||||
l->done(l->data, WESTON_SCREENSHOOTER_SUCCESS);
|
||||
free(pixels);
|
||||
free(l);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_screenshooter_shoot(struct weston_output *output,
|
||||
struct weston_buffer *buffer,
|
||||
weston_screenshooter_done_func_t done, void *data)
|
||||
{
|
||||
struct screenshooter_frame_listener *l;
|
||||
|
||||
if (!wl_shm_buffer_get(buffer->resource)) {
|
||||
done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
|
||||
buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
|
||||
buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
|
||||
|
||||
if (buffer->width < output->current_mode->width ||
|
||||
buffer->height < output->current_mode->height) {
|
||||
done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l = malloc(sizeof *l);
|
||||
if (l == NULL) {
|
||||
done(data, WESTON_SCREENSHOOTER_NO_MEMORY);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l->buffer = buffer;
|
||||
l->done = done;
|
||||
l->data = data;
|
||||
l->listener.notify = screenshooter_frame_notify;
|
||||
wl_signal_add(&output->frame_signal, &l->listener);
|
||||
output->disable_planes++;
|
||||
weston_output_schedule_repaint(output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct weston_recorder {
|
||||
struct weston_output *output;
|
||||
uint32_t *frame, *rect;
|
||||
uint32_t *tmpbuf;
|
||||
uint32_t total;
|
||||
int fd;
|
||||
struct wl_listener frame_listener;
|
||||
int count, destroying;
|
||||
};
|
||||
|
||||
static uint32_t *
|
||||
output_run(uint32_t *p, uint32_t delta, int run)
|
||||
{
|
||||
int i;
|
||||
|
||||
while (run > 0) {
|
||||
if (run <= 0xe0) {
|
||||
*p++ = delta | ((run - 1) << 24);
|
||||
break;
|
||||
}
|
||||
|
||||
i = 24 - __builtin_clz(run);
|
||||
*p++ = delta | ((i + 0xe0) << 24);
|
||||
run -= 1 << (7 + i);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
component_delta(uint32_t next, uint32_t prev)
|
||||
{
|
||||
unsigned char dr, dg, db;
|
||||
|
||||
dr = (next >> 16) - (prev >> 16);
|
||||
dg = (next >> 8) - (prev >> 8);
|
||||
db = (next >> 0) - (prev >> 0);
|
||||
|
||||
return (dr << 16) | (dg << 8) | (db << 0);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_recorder_destroy(struct weston_recorder *recorder);
|
||||
|
||||
static void
|
||||
weston_recorder_frame_notify(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_recorder *recorder =
|
||||
container_of(listener, struct weston_recorder, frame_listener);
|
||||
struct weston_output *output = data;
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
uint32_t msecs = output->frame_time;
|
||||
pixman_box32_t *r;
|
||||
pixman_region32_t damage, transformed_damage;
|
||||
int i, j, k, n, width, height, run, stride;
|
||||
uint32_t delta, prev, *d, *s, *p, next;
|
||||
struct {
|
||||
uint32_t msecs;
|
||||
uint32_t nrects;
|
||||
} header;
|
||||
struct iovec v[2];
|
||||
int do_yflip;
|
||||
int y_orig;
|
||||
uint32_t *outbuf;
|
||||
|
||||
do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
|
||||
if (do_yflip)
|
||||
outbuf = recorder->rect;
|
||||
else
|
||||
outbuf = recorder->tmpbuf;
|
||||
|
||||
pixman_region32_init(&damage);
|
||||
pixman_region32_init(&transformed_damage);
|
||||
pixman_region32_intersect(&damage, &output->region,
|
||||
&output->previous_damage);
|
||||
pixman_region32_translate(&damage, -output->x, -output->y);
|
||||
weston_transformed_region(output->width, output->height,
|
||||
output->transform, output->current_scale,
|
||||
&damage, &transformed_damage);
|
||||
pixman_region32_fini(&damage);
|
||||
|
||||
r = pixman_region32_rectangles(&transformed_damage, &n);
|
||||
if (n == 0) {
|
||||
pixman_region32_fini(&transformed_damage);
|
||||
return;
|
||||
}
|
||||
|
||||
header.msecs = msecs;
|
||||
header.nrects = n;
|
||||
v[0].iov_base = &header;
|
||||
v[0].iov_len = sizeof header;
|
||||
v[1].iov_base = r;
|
||||
v[1].iov_len = n * sizeof *r;
|
||||
recorder->total += writev(recorder->fd, v, 2);
|
||||
stride = output->current_mode->width;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
width = r[i].x2 - r[i].x1;
|
||||
height = r[i].y2 - r[i].y1;
|
||||
|
||||
if (do_yflip)
|
||||
y_orig = output->current_mode->height - r[i].y2;
|
||||
else
|
||||
y_orig = r[i].y1;
|
||||
|
||||
compositor->renderer->read_pixels(output,
|
||||
compositor->read_format, recorder->rect,
|
||||
r[i].x1, y_orig, width, height);
|
||||
|
||||
p = outbuf;
|
||||
run = prev = 0; /* quiet gcc */
|
||||
for (j = 0; j < height; j++) {
|
||||
if (do_yflip)
|
||||
s = recorder->rect + width * j;
|
||||
else
|
||||
s = recorder->rect + width * (height - j - 1);
|
||||
y_orig = r[i].y2 - j - 1;
|
||||
d = recorder->frame + stride * y_orig + r[i].x1;
|
||||
|
||||
for (k = 0; k < width; k++) {
|
||||
next = *s++;
|
||||
delta = component_delta(next, *d);
|
||||
*d++ = next;
|
||||
if (run == 0 || delta == prev) {
|
||||
run++;
|
||||
} else {
|
||||
p = output_run(p, prev, run);
|
||||
run = 1;
|
||||
}
|
||||
prev = delta;
|
||||
}
|
||||
}
|
||||
|
||||
p = output_run(p, prev, run);
|
||||
|
||||
recorder->total += write(recorder->fd,
|
||||
outbuf, (p - outbuf) * 4);
|
||||
|
||||
#if 0
|
||||
fprintf(stderr,
|
||||
"%dx%d at %d,%d rle from %d to %d bytes (%f) total %dM\n",
|
||||
width, height, r[i].x1, r[i].y1,
|
||||
width * height * 4, (int) (p - outbuf) * 4,
|
||||
(float) (p - outbuf) / (width * height),
|
||||
recorder->total / 1024 / 1024);
|
||||
#endif
|
||||
}
|
||||
|
||||
pixman_region32_fini(&transformed_damage);
|
||||
recorder->count++;
|
||||
|
||||
if (recorder->destroying)
|
||||
weston_recorder_destroy(recorder);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_recorder_free(struct weston_recorder *recorder)
|
||||
{
|
||||
if (recorder == NULL)
|
||||
return;
|
||||
|
||||
free(recorder->tmpbuf);
|
||||
free(recorder->rect);
|
||||
free(recorder->frame);
|
||||
free(recorder);
|
||||
}
|
||||
|
||||
static struct weston_recorder *
|
||||
weston_recorder_create(struct weston_output *output, const char *filename)
|
||||
{
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
struct weston_recorder *recorder;
|
||||
int stride, size;
|
||||
struct { uint32_t magic, format, width, height; } header;
|
||||
int do_yflip;
|
||||
|
||||
do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
|
||||
|
||||
recorder = zalloc(sizeof *recorder);
|
||||
if (recorder == NULL) {
|
||||
weston_log("%s: out of memory\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stride = output->current_mode->width;
|
||||
size = stride * 4 * output->current_mode->height;
|
||||
recorder->frame = zalloc(size);
|
||||
recorder->rect = malloc(size);
|
||||
recorder->output = output;
|
||||
|
||||
if ((recorder->frame == NULL) || (recorder->rect == NULL)) {
|
||||
weston_log("%s: out of memory\n", __func__);
|
||||
goto err_recorder;
|
||||
}
|
||||
|
||||
if (!do_yflip) {
|
||||
recorder->tmpbuf = malloc(size);
|
||||
if (recorder->tmpbuf == NULL) {
|
||||
weston_log("%s: out of memory\n", __func__);
|
||||
goto err_recorder;
|
||||
}
|
||||
}
|
||||
|
||||
header.magic = WCAP_HEADER_MAGIC;
|
||||
|
||||
switch (compositor->read_format) {
|
||||
case PIXMAN_x8r8g8b8:
|
||||
case PIXMAN_a8r8g8b8:
|
||||
header.format = WCAP_FORMAT_XRGB8888;
|
||||
break;
|
||||
case PIXMAN_a8b8g8r8:
|
||||
header.format = WCAP_FORMAT_XBGR8888;
|
||||
break;
|
||||
default:
|
||||
weston_log("unknown recorder format\n");
|
||||
goto err_recorder;
|
||||
}
|
||||
|
||||
recorder->fd = open(filename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
|
||||
|
||||
if (recorder->fd < 0) {
|
||||
weston_log("problem opening output file %s: %m\n", filename);
|
||||
goto err_recorder;
|
||||
}
|
||||
|
||||
header.width = output->current_mode->width;
|
||||
header.height = output->current_mode->height;
|
||||
recorder->total += write(recorder->fd, &header, sizeof header);
|
||||
|
||||
recorder->frame_listener.notify = weston_recorder_frame_notify;
|
||||
wl_signal_add(&output->frame_signal, &recorder->frame_listener);
|
||||
output->disable_planes++;
|
||||
weston_output_damage(output);
|
||||
|
||||
return recorder;
|
||||
|
||||
err_recorder:
|
||||
weston_recorder_free(recorder);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_recorder_destroy(struct weston_recorder *recorder)
|
||||
{
|
||||
wl_list_remove(&recorder->frame_listener.link);
|
||||
close(recorder->fd);
|
||||
recorder->output->disable_planes--;
|
||||
weston_recorder_free(recorder);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_recorder *
|
||||
weston_recorder_start(struct weston_output *output, const char *filename)
|
||||
{
|
||||
struct wl_listener *listener;
|
||||
|
||||
listener = wl_signal_get(&output->frame_signal,
|
||||
weston_recorder_frame_notify);
|
||||
if (listener) {
|
||||
weston_log("a recorder on output %s is already running\n",
|
||||
output->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
weston_log("starting recorder for output %s, file %s\n",
|
||||
output->name, filename);
|
||||
return weston_recorder_create(output, filename);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_recorder_stop(struct weston_recorder *recorder)
|
||||
{
|
||||
weston_log("stopping recorder, total file size %dM, %d frames\n",
|
||||
recorder->total / (1024 * 1024), recorder->count);
|
||||
|
||||
recorder->destroying = 1;
|
||||
weston_output_schedule_repaint(recorder->output);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright © 2011 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 "compositor.h"
|
||||
|
||||
WL_EXPORT void
|
||||
weston_view_geometry_dirty(struct weston_view *view)
|
||||
{
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_log(const char *fmt, ...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_view_schedule_repaint(struct weston_view *view)
|
||||
{
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_compositor_schedule_repaint(struct weston_compositor *compositor)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const double k = 300.0;
|
||||
const double current = 0.5;
|
||||
const double target = 1.0;
|
||||
const double friction = 1400;
|
||||
|
||||
struct weston_spring spring;
|
||||
uint32_t time = 0;
|
||||
|
||||
weston_spring_init(&spring, k, current, target);
|
||||
spring.friction = friction;
|
||||
spring.previous = 0.48;
|
||||
spring.timestamp = 0;
|
||||
|
||||
while (!weston_spring_done(&spring)) {
|
||||
printf("\t%d\t%f\n", time, spring.current);
|
||||
weston_spring_update(&spring, time);
|
||||
time += 16;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright © 2014 Pekka Paalanen <pq@iki.fi>
|
||||
* Copyright © 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_TIMELINE_OBJECT_H
|
||||
#define WESTON_TIMELINE_OBJECT_H
|
||||
|
||||
/*
|
||||
* This struct can be embedded in objects related to timeline output.
|
||||
* It must be initialized to all-zero. Afterwards, the timeline code
|
||||
* will handle it alone. No clean-up is necessary.
|
||||
*/
|
||||
struct weston_timeline_object {
|
||||
/*
|
||||
* Timeline series gets bumped every time a new log is opened.
|
||||
* This triggers id allocation and object info emission.
|
||||
* 0 is an invalid series value.
|
||||
*/
|
||||
unsigned series;
|
||||
|
||||
/* Object id in the timeline JSON output. 0 is invalid. */
|
||||
unsigned id;
|
||||
|
||||
/*
|
||||
* If non-zero, forces a re-emission of object description.
|
||||
* Should be set to non-zero, when changing long-lived
|
||||
* object state that is not emitted on normal timeline
|
||||
* events.
|
||||
*/
|
||||
unsigned force_refresh;
|
||||
};
|
||||
|
||||
#endif /* WESTON_TIMELINE_OBJECT_H */
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright © 2014 Pekka Paalanen <pq@iki.fi>
|
||||
* Copyright © 2014 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "timeline.h"
|
||||
#include "compositor.h"
|
||||
#include "file-util.h"
|
||||
|
||||
struct timeline_log {
|
||||
clock_t clk_id;
|
||||
FILE *file;
|
||||
unsigned series;
|
||||
struct wl_listener compositor_destroy_listener;
|
||||
};
|
||||
|
||||
WL_EXPORT int weston_timeline_enabled_;
|
||||
static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 };
|
||||
|
||||
static int
|
||||
weston_timeline_do_open(void)
|
||||
{
|
||||
const char *prefix = "weston-timeline-";
|
||||
const char *suffix = ".log";
|
||||
char fname[1000];
|
||||
|
||||
timeline_.file = file_create_dated(prefix, suffix,
|
||||
fname, sizeof(fname));
|
||||
if (!timeline_.file) {
|
||||
const char *msg;
|
||||
|
||||
switch (errno) {
|
||||
case ETIME:
|
||||
msg = "failure in datetime formatting";
|
||||
break;
|
||||
default:
|
||||
msg = strerror(errno);
|
||||
}
|
||||
|
||||
weston_log("Cannot open '%s*%s' for writing: %s\n",
|
||||
prefix, suffix, msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
weston_log("Opened timeline file '%s'\n", fname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
timeline_notify_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
weston_timeline_close();
|
||||
}
|
||||
|
||||
void
|
||||
weston_timeline_open(struct weston_compositor *compositor)
|
||||
{
|
||||
if (weston_timeline_enabled_)
|
||||
return;
|
||||
|
||||
if (weston_timeline_do_open() < 0)
|
||||
return;
|
||||
|
||||
timeline_.compositor_destroy_listener.notify = timeline_notify_destroy;
|
||||
wl_signal_add(&compositor->destroy_signal,
|
||||
&timeline_.compositor_destroy_listener);
|
||||
|
||||
if (++timeline_.series == 0)
|
||||
++timeline_.series;
|
||||
|
||||
weston_timeline_enabled_ = 1;
|
||||
}
|
||||
|
||||
void
|
||||
weston_timeline_close(void)
|
||||
{
|
||||
if (!weston_timeline_enabled_)
|
||||
return;
|
||||
|
||||
weston_timeline_enabled_ = 0;
|
||||
|
||||
wl_list_remove(&timeline_.compositor_destroy_listener.link);
|
||||
|
||||
fclose(timeline_.file);
|
||||
timeline_.file = NULL;
|
||||
weston_log("Timeline log file closed.\n");
|
||||
}
|
||||
|
||||
struct timeline_emit_context {
|
||||
FILE *cur;
|
||||
FILE *out;
|
||||
unsigned series;
|
||||
};
|
||||
|
||||
static unsigned
|
||||
timeline_new_id(void)
|
||||
{
|
||||
static unsigned idc;
|
||||
|
||||
if (++idc == 0)
|
||||
++idc;
|
||||
|
||||
return idc;
|
||||
}
|
||||
|
||||
static int
|
||||
check_series(struct timeline_emit_context *ctx,
|
||||
struct weston_timeline_object *to)
|
||||
{
|
||||
if (to->series == 0 || to->series != ctx->series) {
|
||||
to->series = ctx->series;
|
||||
to->id = timeline_new_id();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (to->force_refresh) {
|
||||
to->force_refresh = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fprint_quoted_string(FILE *fp, const char *str)
|
||||
{
|
||||
if (!str) {
|
||||
fprintf(fp, "null");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "\"%s\"", str);
|
||||
}
|
||||
|
||||
static int
|
||||
emit_weston_output(struct timeline_emit_context *ctx, void *obj)
|
||||
{
|
||||
struct weston_output *o = obj;
|
||||
|
||||
if (check_series(ctx, &o->timeline)) {
|
||||
fprintf(ctx->out, "{ \"id\":%u, "
|
||||
"\"type\":\"weston_output\", \"name\":",
|
||||
o->timeline.id);
|
||||
fprint_quoted_string(ctx->out, o->name);
|
||||
fprintf(ctx->out, " }\n");
|
||||
}
|
||||
|
||||
fprintf(ctx->cur, "\"wo\":%u", o->timeline.id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
check_weston_surface_description(struct timeline_emit_context *ctx,
|
||||
struct weston_surface *s)
|
||||
{
|
||||
struct weston_surface *mains;
|
||||
char d[512];
|
||||
char mainstr[32];
|
||||
|
||||
if (!check_series(ctx, &s->timeline))
|
||||
return;
|
||||
|
||||
mains = weston_surface_get_main_surface(s);
|
||||
if (mains != s) {
|
||||
check_weston_surface_description(ctx, mains);
|
||||
if (snprintf(mainstr, sizeof(mainstr),
|
||||
", \"main_surface\":%u", mains->timeline.id) < 0)
|
||||
mainstr[0] = '\0';
|
||||
} else {
|
||||
mainstr[0] = '\0';
|
||||
}
|
||||
|
||||
if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
|
||||
d[0] = '\0';
|
||||
|
||||
fprintf(ctx->out, "{ \"id\":%u, "
|
||||
"\"type\":\"weston_surface\", \"desc\":", s->timeline.id);
|
||||
fprint_quoted_string(ctx->out, d[0] ? d : NULL);
|
||||
fprintf(ctx->out, "%s }\n", mainstr);
|
||||
}
|
||||
|
||||
static int
|
||||
emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
|
||||
{
|
||||
struct weston_surface *s = obj;
|
||||
|
||||
check_weston_surface_description(ctx, s);
|
||||
fprintf(ctx->cur, "\"ws\":%u", s->timeline.id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
|
||||
{
|
||||
struct timespec *ts = obj;
|
||||
|
||||
fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
|
||||
(int64_t)ts->tv_sec, ts->tv_nsec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
|
||||
|
||||
static const type_func type_dispatch[] = {
|
||||
[TLT_OUTPUT] = emit_weston_output,
|
||||
[TLT_SURFACE] = emit_weston_surface,
|
||||
[TLT_VBLANK] = emit_vblank_timestamp,
|
||||
};
|
||||
|
||||
WL_EXPORT void
|
||||
weston_timeline_point(const char *name, ...)
|
||||
{
|
||||
va_list argp;
|
||||
struct timespec ts;
|
||||
enum timeline_type otype;
|
||||
void *obj;
|
||||
char buf[512];
|
||||
struct timeline_emit_context ctx;
|
||||
|
||||
clock_gettime(timeline_.clk_id, &ts);
|
||||
|
||||
ctx.out = timeline_.file;
|
||||
ctx.cur = fmemopen(buf, sizeof(buf), "w");
|
||||
ctx.series = timeline_.series;
|
||||
|
||||
if (!ctx.cur) {
|
||||
weston_log("Timeline error in fmemopen, closing.\n");
|
||||
weston_timeline_close();
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
|
||||
(int64_t)ts.tv_sec, ts.tv_nsec, name);
|
||||
|
||||
va_start(argp, name);
|
||||
while (1) {
|
||||
otype = va_arg(argp, enum timeline_type);
|
||||
if (otype == TLT_END)
|
||||
break;
|
||||
|
||||
obj = va_arg(argp, void *);
|
||||
if (type_dispatch[otype]) {
|
||||
fprintf(ctx.cur, ", ");
|
||||
type_dispatch[otype](&ctx, obj);
|
||||
}
|
||||
}
|
||||
va_end(argp);
|
||||
|
||||
fprintf(ctx.cur, " }\n");
|
||||
fflush(ctx.cur);
|
||||
if (ferror(ctx.cur)) {
|
||||
weston_log("Timeline error in constructing entry, closing.\n");
|
||||
weston_timeline_close();
|
||||
} else {
|
||||
fprintf(ctx.out, "%s", buf);
|
||||
}
|
||||
|
||||
fclose(ctx.cur);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright © 2014 Pekka Paalanen <pq@iki.fi>
|
||||
* Copyright © 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_TIMELINE_H
|
||||
#define WESTON_TIMELINE_H
|
||||
|
||||
extern int weston_timeline_enabled_;
|
||||
|
||||
struct weston_compositor;
|
||||
|
||||
void
|
||||
weston_timeline_open(struct weston_compositor *compositor);
|
||||
|
||||
void
|
||||
weston_timeline_close(void);
|
||||
|
||||
enum timeline_type {
|
||||
TLT_END = 0,
|
||||
TLT_OUTPUT,
|
||||
TLT_SURFACE,
|
||||
TLT_VBLANK,
|
||||
};
|
||||
|
||||
#define TYPEVERIFY(type, arg) ({ \
|
||||
typeof(arg) tmp___ = (arg); \
|
||||
(void)((type)0 == tmp___); \
|
||||
tmp___; })
|
||||
|
||||
#define TLP_END TLT_END, NULL
|
||||
#define TLP_OUTPUT(o) TLT_OUTPUT, TYPEVERIFY(struct weston_output *, (o))
|
||||
#define TLP_SURFACE(s) TLT_SURFACE, TYPEVERIFY(struct weston_surface *, (s))
|
||||
#define TLP_VBLANK(t) TLT_VBLANK, TYPEVERIFY(const struct timespec *, (t))
|
||||
|
||||
#define TL_POINT(...) do { \
|
||||
if (weston_timeline_enabled_) \
|
||||
weston_timeline_point(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
void
|
||||
weston_timeline_point(const char *name, ...);
|
||||
|
||||
#endif /* WESTON_TIMELINE_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _VAAPI_RECORDER_H_
|
||||
#define _VAAPI_RECORDER_H_
|
||||
|
||||
struct vaapi_recorder;
|
||||
|
||||
struct vaapi_recorder *
|
||||
vaapi_recorder_create(int drm_fd, int width, int height, const char *filename);
|
||||
void
|
||||
vaapi_recorder_destroy(struct vaapi_recorder *r);
|
||||
int
|
||||
vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride);
|
||||
|
||||
#endif /* _VAAPI_RECORDER_H_ */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_VERSION_H
|
||||
#define WESTON_VERSION_H
|
||||
|
||||
#define WESTON_VERSION_MAJOR @WESTON_VERSION_MAJOR@
|
||||
#define WESTON_VERSION_MINOR @WESTON_VERSION_MINOR@
|
||||
#define WESTON_VERSION_MICRO @WESTON_VERSION_MICRO@
|
||||
#define WESTON_VERSION "@WESTON_VERSION@"
|
||||
|
||||
/* This macro may not do what you expect. Weston doesn't guarantee
|
||||
* a stable API between 1.X and 1.Y, and thus this macro will return
|
||||
* FALSE on any WESTON_VERSION_AT_LEAST(1,X,0) if the actual version
|
||||
* is 1.Y.0 and X != Y). In particular, it fails if X < Y, that is,
|
||||
* 1.3.0 is considered to not be "at least" 1.4.0.
|
||||
*
|
||||
* If you want to test for the version number being 1.3.0 or above or
|
||||
* maybe in a range (eg 1.2.0 to 1.4.0), just use the WESTON_VERSION_*
|
||||
* defines above directly.
|
||||
*/
|
||||
|
||||
#define WESTON_VERSION_AT_LEAST(major, minor, micro) \
|
||||
(WESTON_VERSION_MAJOR == (major) && \
|
||||
WESTON_VERSION_MINOR == (minor) && \
|
||||
WESTON_VERSION_MICRO >= (micro))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright © 2012 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 <assert.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "vertex-clipping.h"
|
||||
|
||||
float
|
||||
float_difference(float a, float b)
|
||||
{
|
||||
/* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
|
||||
static const float max_diff = 4.0f * FLT_MIN;
|
||||
static const float max_rel_diff = 4.0e-5;
|
||||
float diff = a - b;
|
||||
float adiff = fabsf(diff);
|
||||
|
||||
if (adiff <= max_diff)
|
||||
return 0.0f;
|
||||
|
||||
a = fabsf(a);
|
||||
b = fabsf(b);
|
||||
if (adiff <= (a > b ? a : b) * max_rel_diff)
|
||||
return 0.0f;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
|
||||
* Compute the y coordinate of the intersection.
|
||||
*/
|
||||
static float
|
||||
clip_intersect_y(float p1x, float p1y, float p2x, float p2y,
|
||||
float x_arg)
|
||||
{
|
||||
float a;
|
||||
float diff = float_difference(p1x, p2x);
|
||||
|
||||
/* Practically vertical line segment, yet the end points have already
|
||||
* been determined to be on different sides of the line. Therefore
|
||||
* the line segment is part of the line and intersects everywhere.
|
||||
* Return the end point, so we use the whole line segment.
|
||||
*/
|
||||
if (diff == 0.0f)
|
||||
return p2y;
|
||||
|
||||
a = (x_arg - p2x) / diff;
|
||||
return p2y + (p1y - p2y) * a;
|
||||
}
|
||||
|
||||
/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
|
||||
* Compute the x coordinate of the intersection.
|
||||
*/
|
||||
static float
|
||||
clip_intersect_x(float p1x, float p1y, float p2x, float p2y,
|
||||
float y_arg)
|
||||
{
|
||||
float a;
|
||||
float diff = float_difference(p1y, p2y);
|
||||
|
||||
/* Practically horizontal line segment, yet the end points have already
|
||||
* been determined to be on different sides of the line. Therefore
|
||||
* the line segment is part of the line and intersects everywhere.
|
||||
* Return the end point, so we use the whole line segment.
|
||||
*/
|
||||
if (diff == 0.0f)
|
||||
return p2x;
|
||||
|
||||
a = (y_arg - p2y) / diff;
|
||||
return p2x + (p1x - p2x) * a;
|
||||
}
|
||||
|
||||
enum path_transition {
|
||||
PATH_TRANSITION_OUT_TO_OUT = 0,
|
||||
PATH_TRANSITION_OUT_TO_IN = 1,
|
||||
PATH_TRANSITION_IN_TO_OUT = 2,
|
||||
PATH_TRANSITION_IN_TO_IN = 3,
|
||||
};
|
||||
|
||||
static void
|
||||
clip_append_vertex(struct clip_context *ctx, float x, float y)
|
||||
{
|
||||
*ctx->vertices.x++ = x;
|
||||
*ctx->vertices.y++ = y;
|
||||
}
|
||||
|
||||
static enum path_transition
|
||||
path_transition_left_edge(struct clip_context *ctx, float x, float y)
|
||||
{
|
||||
return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
|
||||
}
|
||||
|
||||
static enum path_transition
|
||||
path_transition_right_edge(struct clip_context *ctx, float x, float y)
|
||||
{
|
||||
return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
|
||||
}
|
||||
|
||||
static enum path_transition
|
||||
path_transition_top_edge(struct clip_context *ctx, float x, float y)
|
||||
{
|
||||
return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
|
||||
}
|
||||
|
||||
static enum path_transition
|
||||
path_transition_bottom_edge(struct clip_context *ctx, float x, float y)
|
||||
{
|
||||
return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
|
||||
}
|
||||
|
||||
static void
|
||||
clip_polygon_leftright(struct clip_context *ctx,
|
||||
enum path_transition transition,
|
||||
float x, float y, float clip_x)
|
||||
{
|
||||
float yi;
|
||||
|
||||
switch (transition) {
|
||||
case PATH_TRANSITION_IN_TO_IN:
|
||||
clip_append_vertex(ctx, x, y);
|
||||
break;
|
||||
case PATH_TRANSITION_IN_TO_OUT:
|
||||
yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
|
||||
clip_append_vertex(ctx, clip_x, yi);
|
||||
break;
|
||||
case PATH_TRANSITION_OUT_TO_IN:
|
||||
yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
|
||||
clip_append_vertex(ctx, clip_x, yi);
|
||||
clip_append_vertex(ctx, x, y);
|
||||
break;
|
||||
case PATH_TRANSITION_OUT_TO_OUT:
|
||||
/* nothing */
|
||||
break;
|
||||
default:
|
||||
assert(0 && "bad enum path_transition");
|
||||
}
|
||||
|
||||
ctx->prev.x = x;
|
||||
ctx->prev.y = y;
|
||||
}
|
||||
|
||||
static void
|
||||
clip_polygon_topbottom(struct clip_context *ctx,
|
||||
enum path_transition transition,
|
||||
float x, float y, float clip_y)
|
||||
{
|
||||
float xi;
|
||||
|
||||
switch (transition) {
|
||||
case PATH_TRANSITION_IN_TO_IN:
|
||||
clip_append_vertex(ctx, x, y);
|
||||
break;
|
||||
case PATH_TRANSITION_IN_TO_OUT:
|
||||
xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
|
||||
clip_append_vertex(ctx, xi, clip_y);
|
||||
break;
|
||||
case PATH_TRANSITION_OUT_TO_IN:
|
||||
xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
|
||||
clip_append_vertex(ctx, xi, clip_y);
|
||||
clip_append_vertex(ctx, x, y);
|
||||
break;
|
||||
case PATH_TRANSITION_OUT_TO_OUT:
|
||||
/* nothing */
|
||||
break;
|
||||
default:
|
||||
assert(0 && "bad enum path_transition");
|
||||
}
|
||||
|
||||
ctx->prev.x = x;
|
||||
ctx->prev.y = y;
|
||||
}
|
||||
|
||||
static void
|
||||
clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
|
||||
float *dst_x, float *dst_y)
|
||||
{
|
||||
ctx->prev.x = src->x[src->n - 1];
|
||||
ctx->prev.y = src->y[src->n - 1];
|
||||
ctx->vertices.x = dst_x;
|
||||
ctx->vertices.y = dst_y;
|
||||
}
|
||||
|
||||
static int
|
||||
clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
|
||||
float *dst_x, float *dst_y)
|
||||
{
|
||||
enum path_transition trans;
|
||||
int i;
|
||||
|
||||
if (src->n < 2)
|
||||
return 0;
|
||||
|
||||
clip_context_prepare(ctx, src, dst_x, dst_y);
|
||||
for (i = 0; i < src->n; i++) {
|
||||
trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
|
||||
clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
|
||||
ctx->clip.x1);
|
||||
}
|
||||
return ctx->vertices.x - dst_x;
|
||||
}
|
||||
|
||||
static int
|
||||
clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
|
||||
float *dst_x, float *dst_y)
|
||||
{
|
||||
enum path_transition trans;
|
||||
int i;
|
||||
|
||||
if (src->n < 2)
|
||||
return 0;
|
||||
|
||||
clip_context_prepare(ctx, src, dst_x, dst_y);
|
||||
for (i = 0; i < src->n; i++) {
|
||||
trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
|
||||
clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
|
||||
ctx->clip.x2);
|
||||
}
|
||||
return ctx->vertices.x - dst_x;
|
||||
}
|
||||
|
||||
static int
|
||||
clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
|
||||
float *dst_x, float *dst_y)
|
||||
{
|
||||
enum path_transition trans;
|
||||
int i;
|
||||
|
||||
if (src->n < 2)
|
||||
return 0;
|
||||
|
||||
clip_context_prepare(ctx, src, dst_x, dst_y);
|
||||
for (i = 0; i < src->n; i++) {
|
||||
trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
|
||||
clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
|
||||
ctx->clip.y1);
|
||||
}
|
||||
return ctx->vertices.x - dst_x;
|
||||
}
|
||||
|
||||
static int
|
||||
clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
|
||||
float *dst_x, float *dst_y)
|
||||
{
|
||||
enum path_transition trans;
|
||||
int i;
|
||||
|
||||
if (src->n < 2)
|
||||
return 0;
|
||||
|
||||
clip_context_prepare(ctx, src, dst_x, dst_y);
|
||||
for (i = 0; i < src->n; i++) {
|
||||
trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
|
||||
clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
|
||||
ctx->clip.y2);
|
||||
}
|
||||
return ctx->vertices.x - dst_x;
|
||||
}
|
||||
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define min(a, b) (((a) > (b)) ? (b) : (a))
|
||||
#define clip(x, a, b) min(max(x, a), b)
|
||||
|
||||
int
|
||||
clip_simple(struct clip_context *ctx,
|
||||
struct polygon8 *surf,
|
||||
float *ex,
|
||||
float *ey)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < surf->n; i++) {
|
||||
ex[i] = clip(surf->x[i], ctx->clip.x1, ctx->clip.x2);
|
||||
ey[i] = clip(surf->y[i], ctx->clip.y1, ctx->clip.y2);
|
||||
}
|
||||
return surf->n;
|
||||
}
|
||||
|
||||
int
|
||||
clip_transformed(struct clip_context *ctx,
|
||||
struct polygon8 *surf,
|
||||
float *ex,
|
||||
float *ey)
|
||||
{
|
||||
struct polygon8 polygon;
|
||||
int i, n;
|
||||
|
||||
polygon.n = clip_polygon_left(ctx, surf, polygon.x, polygon.y);
|
||||
surf->n = clip_polygon_right(ctx, &polygon, surf->x, surf->y);
|
||||
polygon.n = clip_polygon_top(ctx, surf, polygon.x, polygon.y);
|
||||
surf->n = clip_polygon_bottom(ctx, &polygon, surf->x, surf->y);
|
||||
|
||||
/* Get rid of duplicate vertices */
|
||||
ex[0] = surf->x[0];
|
||||
ey[0] = surf->y[0];
|
||||
n = 1;
|
||||
for (i = 1; i < surf->n; i++) {
|
||||
if (float_difference(ex[n - 1], surf->x[i]) == 0.0f &&
|
||||
float_difference(ey[n - 1], surf->y[i]) == 0.0f)
|
||||
continue;
|
||||
ex[n] = surf->x[i];
|
||||
ey[n] = surf->y[i];
|
||||
n++;
|
||||
}
|
||||
if (float_difference(ex[n - 1], surf->x[0]) == 0.0f &&
|
||||
float_difference(ey[n - 1], surf->y[0]) == 0.0f)
|
||||
n--;
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright © 2012 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.
|
||||
*/
|
||||
#ifndef _WESTON_VERTEX_CLIPPING_H
|
||||
#define _WESTON_VERTEX_CLIPPING_H
|
||||
|
||||
struct polygon8 {
|
||||
float x[8];
|
||||
float y[8];
|
||||
int n;
|
||||
};
|
||||
|
||||
struct clip_context {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
} prev;
|
||||
|
||||
struct {
|
||||
float x1, y1;
|
||||
float x2, y2;
|
||||
} clip;
|
||||
|
||||
struct {
|
||||
float *x;
|
||||
float *y;
|
||||
} vertices;
|
||||
};
|
||||
|
||||
float
|
||||
float_difference(float a, float b);
|
||||
|
||||
int
|
||||
clip_simple(struct clip_context *ctx,
|
||||
struct polygon8 *surf,
|
||||
float *ex,
|
||||
float *ey);
|
||||
|
||||
int
|
||||
clip_transformed(struct clip_context *ctx,
|
||||
struct polygon8 *surf,
|
||||
float *ex,
|
||||
float *ey);\
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/* Extensions used by Weston, copied from Mesa's eglmesaext.h, */
|
||||
|
||||
#ifndef WESTON_EGL_EXT_H
|
||||
#define WESTON_EGL_EXT_H
|
||||
|
||||
#ifndef EGL_WL_bind_wayland_display
|
||||
#define EGL_WL_bind_wayland_display 1
|
||||
|
||||
struct wl_display;
|
||||
|
||||
#ifdef EGL_EGLEXT_PROTOTYPES
|
||||
EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
|
||||
EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
|
||||
#endif
|
||||
typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
|
||||
typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is a little different to the tests shipped with EGL implementations,
|
||||
* which wrap the entire thing in #ifndef EGL_WL_bind_wayland_display, then go
|
||||
* on to define both BindWaylandDisplay and QueryWaylandBuffer.
|
||||
*
|
||||
* Unfortunately, some implementations (particularly the version of Mesa shipped
|
||||
* in Ubuntu 12.04) define EGL_WL_bind_wayland_display, but then only provide
|
||||
* prototypes for (Un)BindWaylandDisplay, completely omitting
|
||||
* QueryWaylandBuffer.
|
||||
*
|
||||
* Detect this, and provide our own definitions if necessary.
|
||||
*/
|
||||
#ifndef EGL_WAYLAND_BUFFER_WL
|
||||
#define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */
|
||||
#define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */
|
||||
|
||||
#define EGL_TEXTURE_Y_U_V_WL 0x31D7
|
||||
#define EGL_TEXTURE_Y_UV_WL 0x31D8
|
||||
#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
|
||||
#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
|
||||
|
||||
struct wl_resource;
|
||||
#ifdef EGL_EGLEXT_PROTOTYPES
|
||||
EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
|
||||
#endif
|
||||
typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
|
||||
#endif
|
||||
|
||||
#ifndef EGL_WL_create_wayland_buffer_from_image
|
||||
#define EGL_WL_create_wayland_buffer_from_image 1
|
||||
|
||||
#ifdef EGL_EGLEXT_PROTOTYPES
|
||||
EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
|
||||
#endif
|
||||
typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
|
||||
#endif
|
||||
|
||||
#ifndef EGL_TEXTURE_EXTERNAL_WL
|
||||
#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
|
||||
#endif
|
||||
|
||||
#ifndef EGL_BUFFER_AGE_EXT
|
||||
#define EGL_BUFFER_AGE_EXT 0x313D
|
||||
#endif
|
||||
|
||||
#ifndef EGL_WAYLAND_Y_INVERTED_WL
|
||||
#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB /* eglQueryWaylandBufferWL attribute */
|
||||
#endif
|
||||
|
||||
/* Mesas gl2ext.h and probably Khronos upstream defined
|
||||
* GL_EXT_unpack_subimage with non _EXT suffixed GL_UNPACK_* tokens.
|
||||
* In case we're using that mess, manually define the _EXT versions
|
||||
* of the tokens here.*/
|
||||
#if defined(GL_EXT_unpack_subimage) && !defined(GL_UNPACK_ROW_LENGTH_EXT)
|
||||
#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2
|
||||
#define GL_UNPACK_SKIP_ROWS_EXT 0x0CF3
|
||||
#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
|
||||
#endif
|
||||
|
||||
/* Define needed tokens from EGL_EXT_image_dma_buf_import extension
|
||||
* here to avoid having to add ifdefs everywhere.*/
|
||||
#ifndef EGL_EXT_image_dma_buf_import
|
||||
#define EGL_LINUX_DMA_BUF_EXT 0x3270
|
||||
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
|
||||
#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
|
||||
#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
|
||||
#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
|
||||
#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
|
||||
#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
|
||||
#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
|
||||
#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
|
||||
#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
|
||||
#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,772 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <error.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <linux/vt.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/kd.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
#ifdef HAVE_SYSTEMD_LOGIN
|
||||
#include <systemd/sd-login.h>
|
||||
#endif
|
||||
|
||||
#include "weston-launch.h"
|
||||
|
||||
#define DRM_MAJOR 226
|
||||
|
||||
#ifndef KDSKBMUTE
|
||||
#define KDSKBMUTE 0x4B51
|
||||
#endif
|
||||
|
||||
#ifndef EVIOCREVOKE
|
||||
#define EVIOCREVOKE _IOW('E', 0x91, int)
|
||||
#endif
|
||||
|
||||
#define MAX_ARGV_SIZE 256
|
||||
|
||||
#ifdef HAVE_LIBDRM
|
||||
|
||||
#include <xf86drm.h>
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
drmDropMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drmSetMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct weston_launch {
|
||||
struct pam_conv pc;
|
||||
pam_handle_t *ph;
|
||||
int tty;
|
||||
int ttynr;
|
||||
int sock[2];
|
||||
int drm_fd;
|
||||
int last_input_fd;
|
||||
int kb_mode;
|
||||
struct passwd *pw;
|
||||
|
||||
int signalfd;
|
||||
|
||||
pid_t child;
|
||||
int verbose;
|
||||
char *new_user;
|
||||
};
|
||||
|
||||
union cmsg_data { unsigned char b[4]; int fd; };
|
||||
|
||||
static gid_t *
|
||||
read_groups(void)
|
||||
{
|
||||
int n;
|
||||
gid_t *groups;
|
||||
|
||||
n = getgroups(0, NULL);
|
||||
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Unable to retrieve groups: %m\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
groups = malloc(n * sizeof(gid_t));
|
||||
if (!groups)
|
||||
return NULL;
|
||||
|
||||
if (getgroups(n, groups) < 0) {
|
||||
fprintf(stderr, "Unable to retrieve groups: %m\n");
|
||||
free(groups);
|
||||
return NULL;
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
static bool
|
||||
weston_launch_allowed(struct weston_launch *wl)
|
||||
{
|
||||
struct group *gr;
|
||||
gid_t *groups;
|
||||
int i;
|
||||
#ifdef HAVE_SYSTEMD_LOGIN
|
||||
char *session, *seat;
|
||||
int err;
|
||||
#endif
|
||||
|
||||
if (getuid() == 0)
|
||||
return true;
|
||||
|
||||
gr = getgrnam("weston-launch");
|
||||
if (gr) {
|
||||
groups = read_groups();
|
||||
if (groups) {
|
||||
for (i = 0; groups[i]; ++i) {
|
||||
if (groups[i] == gr->gr_gid) {
|
||||
free(groups);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
free(groups);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD_LOGIN
|
||||
err = sd_pid_get_session(getpid(), &session);
|
||||
if (err == 0 && session) {
|
||||
if (sd_session_is_active(session) &&
|
||||
sd_session_get_seat(session, &seat) == 0) {
|
||||
free(seat);
|
||||
free(session);
|
||||
return true;
|
||||
}
|
||||
free(session);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
pam_conversation_fn(int msg_count,
|
||||
const struct pam_message **messages,
|
||||
struct pam_response **responses,
|
||||
void *user_data)
|
||||
{
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_pam(struct weston_launch *wl)
|
||||
{
|
||||
int err;
|
||||
|
||||
wl->pc.conv = pam_conversation_fn;
|
||||
wl->pc.appdata_ptr = wl;
|
||||
|
||||
err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
|
||||
if (err != PAM_SUCCESS) {
|
||||
fprintf(stderr, "failed to start pam transaction: %d: %s\n",
|
||||
err, pam_strerror(wl->ph, err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
|
||||
if (err != PAM_SUCCESS) {
|
||||
fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
|
||||
err, pam_strerror(wl->ph, err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = pam_open_session(wl->ph, 0);
|
||||
if (err != PAM_SUCCESS) {
|
||||
fprintf(stderr, "failed to open pam session: %d: %s\n",
|
||||
err, pam_strerror(wl->ph, err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_launcher_socket(struct weston_launch *wl)
|
||||
{
|
||||
if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0)
|
||||
error(1, errno, "socketpair failed");
|
||||
|
||||
if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0)
|
||||
error(1, errno, "fcntl failed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_signals(struct weston_launch *wl)
|
||||
{
|
||||
int ret;
|
||||
sigset_t mask;
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
|
||||
ret = sigaction(SIGCHLD, &sa, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
|
||||
ret = sigemptyset(&mask);
|
||||
assert(ret == 0);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
sigaddset(&mask, SIGUSR2);
|
||||
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (wl->signalfd < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
setenv_fd(const char *env, int fd)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
snprintf(buf, sizeof buf, "%d", fd);
|
||||
setenv(env, buf, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
send_reply(struct weston_launch *wl, int reply)
|
||||
{
|
||||
int len;
|
||||
|
||||
do {
|
||||
len = send(wl->sock[0], &reply, sizeof reply, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
{
|
||||
int fd = -1, ret = -1;
|
||||
char control[CMSG_SPACE(sizeof(fd))];
|
||||
struct cmsghdr *cmsg;
|
||||
struct stat s;
|
||||
struct msghdr nmsg;
|
||||
struct iovec iov;
|
||||
struct weston_launcher_open *message;
|
||||
union cmsg_data *data;
|
||||
|
||||
message = msg->msg_iov->iov_base;
|
||||
if ((size_t)len < sizeof(*message))
|
||||
goto err0;
|
||||
|
||||
/* Ensure path is null-terminated */
|
||||
((char *) message)[len-1] = '\0';
|
||||
|
||||
fd = open(message->path, message->flags);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening device %s: %m\n",
|
||||
message->path);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (fstat(fd, &s) < 0) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
fprintf(stderr, "Failed to stat %s\n", message->path);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (major(s.st_rdev) != INPUT_MAJOR &&
|
||||
major(s.st_rdev) != DRM_MAJOR) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
fprintf(stderr, "Device %s is not an input or drm device\n",
|
||||
message->path);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
err0:
|
||||
memset(&nmsg, 0, sizeof nmsg);
|
||||
nmsg.msg_iov = &iov;
|
||||
nmsg.msg_iovlen = 1;
|
||||
if (fd != -1) {
|
||||
nmsg.msg_control = control;
|
||||
nmsg.msg_controllen = sizeof control;
|
||||
cmsg = CMSG_FIRSTHDR(&nmsg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
|
||||
data = (union cmsg_data *) CMSG_DATA(cmsg);
|
||||
data->fd = fd;
|
||||
nmsg.msg_controllen = cmsg->cmsg_len;
|
||||
ret = 0;
|
||||
}
|
||||
iov.iov_base = &ret;
|
||||
iov.iov_len = sizeof ret;
|
||||
|
||||
if (wl->verbose)
|
||||
fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
|
||||
message->path, ret, fd);
|
||||
do {
|
||||
len = sendmsg(wl->sock[0], &nmsg, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
if (fd != -1 && major(s.st_rdev) == DRM_MAJOR)
|
||||
wl->drm_fd = fd;
|
||||
if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR &&
|
||||
wl->last_input_fd < fd)
|
||||
wl->last_input_fd = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_socket_msg(struct weston_launch *wl)
|
||||
{
|
||||
char control[CMSG_SPACE(sizeof(int))];
|
||||
char buf[BUFSIZ];
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
int ret = -1;
|
||||
ssize_t len;
|
||||
struct weston_launcher_message *message;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof buf;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = control;
|
||||
msg.msg_controllen = sizeof control;
|
||||
|
||||
do {
|
||||
len = recvmsg(wl->sock[0], &msg, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
if (len < 1)
|
||||
return -1;
|
||||
|
||||
message = (void *) buf;
|
||||
switch (message->opcode) {
|
||||
case WESTON_LAUNCHER_OPEN:
|
||||
ret = handle_open(wl, &msg, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
quit(struct weston_launch *wl, int status)
|
||||
{
|
||||
struct vt_mode mode = { 0 };
|
||||
int err;
|
||||
|
||||
close(wl->signalfd);
|
||||
close(wl->sock[0]);
|
||||
|
||||
if (wl->new_user) {
|
||||
err = pam_close_session(wl->ph, 0);
|
||||
if (err)
|
||||
fprintf(stderr, "pam_close_session failed: %d: %s\n",
|
||||
err, pam_strerror(wl->ph, err));
|
||||
pam_end(wl->ph, err);
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, KDSKBMUTE, 0) &&
|
||||
ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
|
||||
fprintf(stderr, "failed to restore keyboard mode: %m\n");
|
||||
|
||||
if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
|
||||
fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
|
||||
|
||||
/* We have to drop master before we switch the VT back in
|
||||
* VT_AUTO, so we don't risk switching to a VT with another
|
||||
* display server, that will then fail to set drm master. */
|
||||
drmDropMaster(wl->drm_fd);
|
||||
|
||||
mode.mode = VT_AUTO;
|
||||
if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
|
||||
fprintf(stderr, "could not reset vt handling\n");
|
||||
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void
|
||||
close_input_fds(struct weston_launch *wl)
|
||||
{
|
||||
struct stat s;
|
||||
int fd;
|
||||
|
||||
for (fd = 3; fd <= wl->last_input_fd; fd++) {
|
||||
if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) {
|
||||
/* EVIOCREVOKE may fail if the kernel doesn't
|
||||
* support it, but all we can do is ignore it. */
|
||||
ioctl(fd, EVIOCREVOKE, 0);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
handle_signal(struct weston_launch *wl)
|
||||
{
|
||||
struct signalfd_siginfo sig;
|
||||
int pid, status, ret;
|
||||
|
||||
if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
|
||||
error(0, errno, "reading signalfd failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (sig.ssi_signo) {
|
||||
case SIGCHLD:
|
||||
pid = waitpid(-1, &status, 0);
|
||||
if (pid == wl->child) {
|
||||
wl->child = 0;
|
||||
if (WIFEXITED(status))
|
||||
ret = WEXITSTATUS(status);
|
||||
else if (WIFSIGNALED(status))
|
||||
/*
|
||||
* If weston dies because of signal N, we
|
||||
* return 10+N. This is distinct from
|
||||
* weston-launch dying because of a signal
|
||||
* (128+N).
|
||||
*/
|
||||
ret = 10 + WTERMSIG(status);
|
||||
else
|
||||
ret = 0;
|
||||
quit(wl, ret);
|
||||
}
|
||||
break;
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
if (wl->child)
|
||||
kill(wl->child, sig.ssi_signo);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
send_reply(wl, WESTON_LAUNCHER_DEACTIVATE);
|
||||
close_input_fds(wl);
|
||||
drmDropMaster(wl->drm_fd);
|
||||
ioctl(wl->tty, VT_RELDISP, 1);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
|
||||
drmSetMaster(wl->drm_fd);
|
||||
send_reply(wl, WESTON_LAUNCHER_ACTIVATE);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_tty(struct weston_launch *wl, const char *tty)
|
||||
{
|
||||
struct stat buf;
|
||||
struct vt_mode mode = { 0 };
|
||||
char *t;
|
||||
|
||||
if (!wl->new_user) {
|
||||
wl->tty = STDIN_FILENO;
|
||||
} else if (tty) {
|
||||
t = ttyname(STDIN_FILENO);
|
||||
if (t && strcmp(t, tty) == 0)
|
||||
wl->tty = STDIN_FILENO;
|
||||
else
|
||||
wl->tty = open(tty, O_RDWR | O_NOCTTY);
|
||||
} else {
|
||||
int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
|
||||
char filename[16];
|
||||
|
||||
if (tty0 < 0)
|
||||
error(1, errno, "could not open tty0");
|
||||
|
||||
if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
|
||||
error(1, errno, "failed to find non-opened console");
|
||||
|
||||
snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
|
||||
wl->tty = open(filename, O_RDWR | O_NOCTTY);
|
||||
close(tty0);
|
||||
}
|
||||
|
||||
if (wl->tty < 0)
|
||||
error(1, errno, "failed to open tty");
|
||||
|
||||
if (fstat(wl->tty, &buf) == -1 ||
|
||||
major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0)
|
||||
error(1, 0, "weston-launch must be run from a virtual terminal");
|
||||
|
||||
if (tty) {
|
||||
if (fstat(wl->tty, &buf) < 0)
|
||||
error(1, errno, "stat %s failed", tty);
|
||||
|
||||
if (major(buf.st_rdev) != TTY_MAJOR)
|
||||
error(1, 0, "invalid tty device: %s", tty);
|
||||
|
||||
wl->ttynr = minor(buf.st_rdev);
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode))
|
||||
error(1, errno, "failed to get current keyboard mode: %m\n");
|
||||
|
||||
if (ioctl(wl->tty, KDSKBMUTE, 1) &&
|
||||
ioctl(wl->tty, KDSKBMODE, K_OFF))
|
||||
error(1, errno, "failed to set K_OFF keyboard mode: %m\n");
|
||||
|
||||
if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS))
|
||||
error(1, errno, "failed to set KD_GRAPHICS mode on tty: %m\n");
|
||||
|
||||
mode.mode = VT_PROCESS;
|
||||
mode.relsig = SIGUSR1;
|
||||
mode.acqsig = SIGUSR2;
|
||||
if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
|
||||
error(1, errno, "failed to take control of vt handling\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_session(struct weston_launch *wl)
|
||||
{
|
||||
char **env;
|
||||
char *term;
|
||||
int i;
|
||||
|
||||
if (wl->tty != STDIN_FILENO) {
|
||||
if (setsid() < 0)
|
||||
error(1, errno, "setsid failed");
|
||||
if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
|
||||
error(1, errno, "TIOCSCTTY failed - tty is in use");
|
||||
}
|
||||
|
||||
term = getenv("TERM");
|
||||
clearenv();
|
||||
if (term)
|
||||
setenv("TERM", term, 1);
|
||||
setenv("USER", wl->pw->pw_name, 1);
|
||||
setenv("LOGNAME", wl->pw->pw_name, 1);
|
||||
setenv("HOME", wl->pw->pw_dir, 1);
|
||||
setenv("SHELL", wl->pw->pw_shell, 1);
|
||||
|
||||
env = pam_getenvlist(wl->ph);
|
||||
if (env) {
|
||||
for (i = 0; env[i]; ++i) {
|
||||
if (putenv(env[i]) != 0)
|
||||
error(0, 0, "putenv %s failed", env[i]);
|
||||
}
|
||||
free(env);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
drop_privileges(struct weston_launch *wl)
|
||||
{
|
||||
if (setgid(wl->pw->pw_gid) < 0 ||
|
||||
#ifdef HAVE_INITGROUPS
|
||||
initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
|
||||
#endif
|
||||
setuid(wl->pw->pw_uid) < 0)
|
||||
error(1, errno, "dropping privileges failed");
|
||||
}
|
||||
|
||||
static void
|
||||
launch_compositor(struct weston_launch *wl, int argc, char *argv[])
|
||||
{
|
||||
char *child_argv[MAX_ARGV_SIZE];
|
||||
sigset_t mask;
|
||||
int i;
|
||||
|
||||
if (wl->verbose)
|
||||
printf("weston-launch: spawned weston with pid: %d\n", getpid());
|
||||
if (wl->new_user)
|
||||
setup_session(wl);
|
||||
|
||||
if (geteuid() == 0)
|
||||
drop_privileges(wl);
|
||||
|
||||
setenv_fd("WESTON_TTY_FD", wl->tty);
|
||||
setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
|
||||
|
||||
unsetenv("DISPLAY");
|
||||
|
||||
/* Do not give our signal mask to the new process. */
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
|
||||
child_argv[0] = "/bin/sh";
|
||||
child_argv[1] = "-l";
|
||||
child_argv[2] = "-c";
|
||||
child_argv[3] = BINDIR "/weston \"$@\"";
|
||||
child_argv[4] = "weston";
|
||||
for (i = 0; i < argc; ++i)
|
||||
child_argv[5 + i] = argv[i];
|
||||
child_argv[5 + i] = NULL;
|
||||
|
||||
execv(child_argv[0], child_argv);
|
||||
error(1, errno, "exec failed");
|
||||
}
|
||||
|
||||
static void
|
||||
help(const char *name)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
|
||||
fprintf(stderr, " -u, --user Start session as specified username\n");
|
||||
fprintf(stderr, " -t, --tty Start session on alternative tty\n");
|
||||
fprintf(stderr, " -v, --verbose Be verbose\n");
|
||||
fprintf(stderr, " -h, --help Display this help message\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct weston_launch wl;
|
||||
int i, c;
|
||||
char *tty = NULL;
|
||||
struct option opts[] = {
|
||||
{ "user", required_argument, NULL, 'u' },
|
||||
{ "tty", required_argument, NULL, 't' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
memset(&wl, 0, sizeof wl);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
|
||||
switch (c) {
|
||||
case 'u':
|
||||
wl.new_user = optarg;
|
||||
if (getuid() != 0)
|
||||
error(1, 0, "Permission denied. -u allowed for root only");
|
||||
break;
|
||||
case 't':
|
||||
tty = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
wl.verbose = 1;
|
||||
break;
|
||||
case 'h':
|
||||
help("weston-launch");
|
||||
exit(EXIT_FAILURE);
|
||||
default:
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) > (MAX_ARGV_SIZE - 6))
|
||||
error(1, E2BIG, "Too many arguments to pass to weston");
|
||||
|
||||
if (wl.new_user)
|
||||
wl.pw = getpwnam(wl.new_user);
|
||||
else
|
||||
wl.pw = getpwuid(getuid());
|
||||
if (wl.pw == NULL)
|
||||
error(1, errno, "failed to get username");
|
||||
|
||||
if (!weston_launch_allowed(&wl))
|
||||
error(1, 0, "Permission denied. You should either:\n"
|
||||
#ifdef HAVE_SYSTEMD_LOGIN
|
||||
" - run from an active and local (systemd) session.\n"
|
||||
#else
|
||||
" - enable systemd session support for weston-launch.\n"
|
||||
#endif
|
||||
" - or add yourself to the 'weston-launch' group.");
|
||||
|
||||
if (setup_tty(&wl, tty) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (wl.new_user && setup_pam(&wl) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (setup_launcher_socket(&wl) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (setup_signals(&wl) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
wl.child = fork();
|
||||
if (wl.child == -1)
|
||||
error(EXIT_FAILURE, errno, "fork failed");
|
||||
|
||||
if (wl.child == 0)
|
||||
launch_compositor(&wl, argc - optind, argv + optind);
|
||||
|
||||
close(wl.sock[1]);
|
||||
if (wl.tty != STDIN_FILENO)
|
||||
close(wl.tty);
|
||||
|
||||
while (1) {
|
||||
struct pollfd fds[2];
|
||||
int n;
|
||||
|
||||
fds[0].fd = wl.sock[0];
|
||||
fds[0].events = POLLIN;
|
||||
fds[1].fd = wl.signalfd;
|
||||
fds[1].events = POLLIN;
|
||||
|
||||
n = poll(fds, 2, -1);
|
||||
if (n < 0)
|
||||
error(0, errno, "poll failed");
|
||||
if (fds[0].revents & POLLIN)
|
||||
handle_socket_msg(&wl);
|
||||
if (fds[1].revents)
|
||||
handle_signal(&wl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
*
|
||||
* 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_LAUNCH_H_
|
||||
#define _WESTON_LAUNCH_H_
|
||||
|
||||
enum weston_launcher_opcode {
|
||||
WESTON_LAUNCHER_OPEN,
|
||||
};
|
||||
|
||||
enum weston_launcher_event {
|
||||
WESTON_LAUNCHER_SUCCESS,
|
||||
WESTON_LAUNCHER_ACTIVATE,
|
||||
WESTON_LAUNCHER_DEACTIVATE
|
||||
};
|
||||
|
||||
struct weston_launcher_message {
|
||||
int opcode;
|
||||
};
|
||||
|
||||
struct weston_launcher_open {
|
||||
struct weston_launcher_message header;
|
||||
int flags;
|
||||
char path[0];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright © 2012 Scott Moreau
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "compositor.h"
|
||||
#include "text-cursor-position-server-protocol.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
static void
|
||||
weston_zoom_frame_z(struct weston_animation *animation,
|
||||
struct weston_output *output, uint32_t msecs)
|
||||
{
|
||||
if (animation->frame_counter <= 1)
|
||||
output->zoom.spring_z.timestamp = msecs;
|
||||
|
||||
weston_spring_update(&output->zoom.spring_z, msecs);
|
||||
|
||||
if (output->zoom.spring_z.current > output->zoom.max_level)
|
||||
output->zoom.spring_z.current = output->zoom.max_level;
|
||||
else if (output->zoom.spring_z.current < 0.0)
|
||||
output->zoom.spring_z.current = 0.0;
|
||||
|
||||
if (weston_spring_done(&output->zoom.spring_z)) {
|
||||
if (output->zoom.active && output->zoom.level <= 0.0) {
|
||||
output->zoom.active = false;
|
||||
output->zoom.seat = NULL;
|
||||
output->disable_planes--;
|
||||
wl_list_remove(&output->zoom.motion_listener.link);
|
||||
}
|
||||
output->zoom.spring_z.current = output->zoom.level;
|
||||
wl_list_remove(&animation->link);
|
||||
wl_list_init(&animation->link);
|
||||
}
|
||||
|
||||
output->dirty = 1;
|
||||
weston_output_damage(output);
|
||||
}
|
||||
|
||||
static void
|
||||
zoom_area_center_from_point(struct weston_output *output,
|
||||
double *x, double *y)
|
||||
{
|
||||
float level = output->zoom.spring_z.current;
|
||||
|
||||
*x = (*x - output->x) * level + output->width / 2.;
|
||||
*y = (*y - output->y) * level + output->height / 2.;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_output_update_zoom_transform(struct weston_output *output)
|
||||
{
|
||||
double x = output->zoom.current.x; /* global pointer coords */
|
||||
double y = output->zoom.current.y;
|
||||
float level;
|
||||
|
||||
level = output->zoom.spring_z.current;
|
||||
|
||||
if (!output->zoom.active || level > output->zoom.max_level ||
|
||||
level == 0.0f)
|
||||
return;
|
||||
|
||||
zoom_area_center_from_point(output, &x, &y);
|
||||
|
||||
output->zoom.trans_x = x - output->width / 2;
|
||||
output->zoom.trans_y = y - output->height / 2;
|
||||
|
||||
if (output->zoom.trans_x < 0)
|
||||
output->zoom.trans_x = 0;
|
||||
if (output->zoom.trans_y < 0)
|
||||
output->zoom.trans_y = 0;
|
||||
if (output->zoom.trans_x > level * output->width)
|
||||
output->zoom.trans_x = level * output->width;
|
||||
if (output->zoom.trans_y > level * output->height)
|
||||
output->zoom.trans_y = level * output->height;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_zoom_transition(struct weston_output *output)
|
||||
{
|
||||
if (output->zoom.level != output->zoom.spring_z.current) {
|
||||
output->zoom.spring_z.target = output->zoom.level;
|
||||
if (wl_list_empty(&output->zoom.animation_z.link)) {
|
||||
output->zoom.animation_z.frame_counter = 0;
|
||||
wl_list_insert(output->animation_list.prev,
|
||||
&output->zoom.animation_z.link);
|
||||
}
|
||||
}
|
||||
|
||||
output->dirty = 1;
|
||||
weston_output_damage(output);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_output_update_zoom(struct weston_output *output)
|
||||
{
|
||||
struct weston_seat *seat = output->zoom.seat;
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
||||
|
||||
assert(output->zoom.active);
|
||||
|
||||
output->zoom.current.x = wl_fixed_to_double(pointer->x);
|
||||
output->zoom.current.y = wl_fixed_to_double(pointer->y);
|
||||
|
||||
weston_zoom_transition(output);
|
||||
weston_output_update_zoom_transform(output);
|
||||
}
|
||||
|
||||
static void
|
||||
motion(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_output_zoom *zoom =
|
||||
container_of(listener, struct weston_output_zoom, motion_listener);
|
||||
struct weston_output *output =
|
||||
container_of(zoom, struct weston_output, zoom);
|
||||
|
||||
weston_output_update_zoom(output);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_output_activate_zoom(struct weston_output *output,
|
||||
struct weston_seat *seat)
|
||||
{
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
||||
|
||||
if (output->zoom.active)
|
||||
return;
|
||||
|
||||
output->zoom.active = true;
|
||||
output->zoom.seat = seat;
|
||||
output->disable_planes++;
|
||||
wl_signal_add(&pointer->motion_signal,
|
||||
&output->zoom.motion_listener);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_output_init_zoom(struct weston_output *output)
|
||||
{
|
||||
output->zoom.active = false;
|
||||
output->zoom.seat = NULL;
|
||||
output->zoom.increment = 0.07;
|
||||
output->zoom.max_level = 0.95;
|
||||
output->zoom.level = 0.0;
|
||||
output->zoom.trans_x = 0.0;
|
||||
output->zoom.trans_y = 0.0;
|
||||
weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0);
|
||||
output->zoom.spring_z.friction = 1000;
|
||||
output->zoom.animation_z.frame = weston_zoom_frame_z;
|
||||
wl_list_init(&output->zoom.animation_z.link);
|
||||
output->zoom.motion_listener.notify = motion;
|
||||
}
|
||||
Reference in New Issue
Block a user