Merge tag '11.0.3' into dev
11.0.3
This commit is contained in:
@@ -118,8 +118,9 @@ drm_backend_create_gl_renderer(struct drm_backend *b)
|
||||
int
|
||||
init_egl(struct drm_backend *b)
|
||||
{
|
||||
b->gbm = create_gbm_device(b->drm.fd);
|
||||
struct drm_device *device = b->drm;
|
||||
|
||||
b->gbm = create_gbm_device(device->drm.fd);
|
||||
if (!b->gbm)
|
||||
return -1;
|
||||
|
||||
@@ -145,6 +146,7 @@ static void drm_output_fini_cursor_egl(struct drm_output *output)
|
||||
static int
|
||||
drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
|
||||
{
|
||||
struct drm_device *device = output->device;
|
||||
unsigned int i;
|
||||
|
||||
/* No point creating cursors if we don't have a plane for them. */
|
||||
@@ -154,14 +156,14 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
|
||||
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
|
||||
struct gbm_bo *bo;
|
||||
|
||||
bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
|
||||
bo = gbm_bo_create(b->gbm, device->cursor_width, device->cursor_height,
|
||||
GBM_FORMAT_ARGB8888,
|
||||
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
|
||||
if (!bo)
|
||||
goto err;
|
||||
|
||||
output->gbm_cursor_fb[i] =
|
||||
drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR);
|
||||
drm_fb_get_from_bo(bo, device, false, BUFFER_CURSOR);
|
||||
if (!output->gbm_cursor_fb[i]) {
|
||||
gbm_bo_destroy(bo);
|
||||
goto err;
|
||||
@@ -173,7 +175,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
|
||||
|
||||
err:
|
||||
weston_log("cursor buffers unavailable, using gl cursors\n");
|
||||
b->cursors_are_broken = true;
|
||||
device->cursors_are_broken = true;
|
||||
drm_output_fini_cursor_egl(output);
|
||||
return -1;
|
||||
}
|
||||
@@ -183,6 +185,7 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
|
||||
{
|
||||
struct weston_mode *mode = output->base.current_mode;
|
||||
struct drm_plane *plane = output->scanout_plane;
|
||||
const struct pixel_format_info *pixel_format;
|
||||
struct weston_drm_format *fmt;
|
||||
const uint64_t *modifiers;
|
||||
unsigned int num_modifiers;
|
||||
@@ -190,8 +193,15 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
|
||||
fmt = weston_drm_format_array_find_format(&plane->formats,
|
||||
output->gbm_format);
|
||||
if (!fmt) {
|
||||
weston_log("format 0x%x not supported by output %s\n",
|
||||
output->gbm_format, output->base.name);
|
||||
pixel_format = pixel_format_get_info(output->gbm_format);
|
||||
if (pixel_format)
|
||||
weston_log("format %s not supported by output %s\n",
|
||||
pixel_format->drm_format_name,
|
||||
output->base.name);
|
||||
else
|
||||
weston_log("format 0x%x not supported by output %s\n",
|
||||
output->gbm_format,
|
||||
output->base.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -280,7 +290,7 @@ struct drm_fb *
|
||||
drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct gbm_bo *bo;
|
||||
struct drm_fb *ret;
|
||||
|
||||
@@ -295,7 +305,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
}
|
||||
|
||||
/* The renderer always produces an opaque image. */
|
||||
ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE);
|
||||
ret = drm_fb_get_from_bo(bo, device, true, BUFFER_GBM_SURFACE);
|
||||
if (!ret) {
|
||||
weston_log("failed to get drm_fb for bo\n");
|
||||
gbm_surface_release_buffer(output->gbm_surface, bo);
|
||||
@@ -305,68 +315,3 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
switch_to_gl_renderer(struct drm_backend *b)
|
||||
{
|
||||
struct drm_output *output;
|
||||
bool dmabuf_support_inited;
|
||||
bool linux_explicit_sync_inited;
|
||||
|
||||
if (!b->use_pixman)
|
||||
return;
|
||||
|
||||
dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
|
||||
linux_explicit_sync_inited =
|
||||
b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC;
|
||||
|
||||
weston_log("Switching to GL renderer\n");
|
||||
|
||||
b->gbm = create_gbm_device(b->drm.fd);
|
||||
if (!b->gbm) {
|
||||
weston_log("Failed to create gbm device. "
|
||||
"Aborting renderer switch\n");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_for_each(output, &b->compositor->output_list, base.link)
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
|
||||
b->compositor->renderer->destroy(b->compositor);
|
||||
|
||||
if (drm_backend_create_gl_renderer(b) < 0) {
|
||||
gbm_device_destroy(b->gbm);
|
||||
weston_log("Failed to create GL renderer. Quitting.\n");
|
||||
/* FIXME: we need a function to shutdown cleanly */
|
||||
assert(0);
|
||||
}
|
||||
|
||||
wl_list_for_each(output, &b->compositor->output_list, base.link)
|
||||
drm_output_init_egl(output, b);
|
||||
|
||||
b->use_pixman = 0;
|
||||
|
||||
if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) {
|
||||
if (linux_dmabuf_setup(b->compositor) < 0)
|
||||
weston_log("Error: initializing dmabuf "
|
||||
"support failed.\n");
|
||||
}
|
||||
|
||||
if (!linux_explicit_sync_inited &&
|
||||
(b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC)) {
|
||||
if (linux_explicit_synchronization_setup(b->compositor) < 0)
|
||||
weston_log("Error: initializing explicit "
|
||||
" synchronization support failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
renderer_switch_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data)
|
||||
{
|
||||
struct drm_backend *b =
|
||||
to_drm_backend(keyboard->seat->compositor);
|
||||
|
||||
switch_to_gl_renderer(b);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +186,8 @@ enum wdrm_connector_property {
|
||||
WDRM_CONNECTOR_CONTENT_PROTECTION,
|
||||
WDRM_CONNECTOR_HDCP_CONTENT_TYPE,
|
||||
WDRM_CONNECTOR_PANEL_ORIENTATION,
|
||||
WDRM_CONNECTOR_HDR_OUTPUT_METADATA,
|
||||
WDRM_CONNECTOR_MAX_BPC,
|
||||
WDRM_CONNECTOR__COUNT
|
||||
};
|
||||
|
||||
@@ -232,11 +234,20 @@ enum wdrm_crtc_property {
|
||||
*/
|
||||
enum try_view_on_plane_failure_reasons {
|
||||
FAILURE_REASONS_NONE = 0,
|
||||
FAILURE_REASONS_FORCE_RENDERER = (1 << 0),
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = (1 << 1),
|
||||
FAILURE_REASONS_DMABUF_MODIFIER_INVALID = (1 << 2),
|
||||
FAILURE_REASONS_ADD_FB_FAILED = (1 << 3),
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED = (1 << 4)
|
||||
FAILURE_REASONS_FORCE_RENDERER = 1 << 0,
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = 1 << 1,
|
||||
FAILURE_REASONS_DMABUF_MODIFIER_INVALID = 1 << 2,
|
||||
FAILURE_REASONS_ADD_FB_FAILED = 1 << 3,
|
||||
FAILURE_REASONS_NO_PLANES_AVAILABLE = 1 << 4,
|
||||
FAILURE_REASONS_PLANES_REJECTED = 1 << 5,
|
||||
FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION = 1 << 6,
|
||||
FAILURE_REASONS_INCOMPATIBLE_TRANSFORM = 1 << 7,
|
||||
FAILURE_REASONS_NO_BUFFER = 1 << 8,
|
||||
FAILURE_REASONS_BUFFER_TYPE = 1 << 9,
|
||||
FAILURE_REASONS_GLOBAL_ALPHA = 1 << 10,
|
||||
FAILURE_REASONS_NO_GBM = 1 << 11,
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED = 1 << 12,
|
||||
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED = 1 << 13,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -249,6 +260,48 @@ enum actions_needed_dmabuf_feedback {
|
||||
ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1),
|
||||
};
|
||||
|
||||
struct drm_device {
|
||||
struct drm_backend *backend;
|
||||
|
||||
struct {
|
||||
int id;
|
||||
int fd;
|
||||
char *filename;
|
||||
dev_t devnum;
|
||||
} drm;
|
||||
|
||||
/* drm_crtc::link */
|
||||
struct wl_list crtc_list;
|
||||
|
||||
struct wl_list plane_list;
|
||||
|
||||
/* drm_writeback::link */
|
||||
struct wl_list writeback_connector_list;
|
||||
|
||||
bool state_invalid;
|
||||
|
||||
bool atomic_modeset;
|
||||
|
||||
bool aspect_ratio_supported;
|
||||
|
||||
int32_t cursor_width;
|
||||
int32_t cursor_height;
|
||||
|
||||
bool cursors_are_broken;
|
||||
bool sprites_are_broken;
|
||||
|
||||
void *repaint_data;
|
||||
|
||||
bool fb_modifiers;
|
||||
|
||||
/* we need these parameters in order to not fail drmModeAddFB2()
|
||||
* due to out of bounds dimensions, and then mistakenly set
|
||||
* sprites_are_broken:
|
||||
*/
|
||||
int min_width, max_width;
|
||||
int min_height, max_height;
|
||||
};
|
||||
|
||||
struct drm_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
@@ -259,57 +312,20 @@ struct drm_backend {
|
||||
struct udev_monitor *udev_monitor;
|
||||
struct wl_event_source *udev_drm_source;
|
||||
|
||||
struct {
|
||||
int id;
|
||||
int fd;
|
||||
char *filename;
|
||||
dev_t devnum;
|
||||
} drm;
|
||||
struct drm_device *drm;
|
||||
struct gbm_device *gbm;
|
||||
struct wl_listener session_listener;
|
||||
uint32_t gbm_format;
|
||||
|
||||
/* we need these parameters in order to not fail drmModeAddFB2()
|
||||
* due to out of bounds dimensions, and then mistakenly set
|
||||
* sprites_are_broken:
|
||||
*/
|
||||
int min_width, max_width;
|
||||
int min_height, max_height;
|
||||
|
||||
struct wl_list plane_list;
|
||||
uint32_t next_plane_idx;
|
||||
|
||||
void *repaint_data;
|
||||
|
||||
bool state_invalid;
|
||||
|
||||
/* drm_crtc::link */
|
||||
struct wl_list crtc_list;
|
||||
|
||||
/* drm_writeback::link */
|
||||
struct wl_list writeback_connector_list;
|
||||
|
||||
bool sprites_are_broken;
|
||||
bool cursors_are_broken;
|
||||
|
||||
bool atomic_modeset;
|
||||
|
||||
bool use_pixman;
|
||||
bool use_pixman_shadow;
|
||||
|
||||
struct udev_input input;
|
||||
|
||||
int32_t cursor_width;
|
||||
int32_t cursor_height;
|
||||
|
||||
uint32_t pageflip_timeout;
|
||||
|
||||
bool shutting_down;
|
||||
|
||||
bool aspect_ratio_supported;
|
||||
|
||||
bool fb_modifiers;
|
||||
|
||||
struct weston_log_scope *debug;
|
||||
};
|
||||
|
||||
@@ -373,7 +389,7 @@ struct drm_edid {
|
||||
* output state will complete and be retired separately.
|
||||
*/
|
||||
struct drm_pending_state {
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
struct wl_list output_list;
|
||||
};
|
||||
|
||||
@@ -396,16 +412,6 @@ struct drm_output_state {
|
||||
struct wl_list plane_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* An instance of this class is created each time we believe we have a plane
|
||||
* suitable to be used by a view as a direct scan-out. The list is initialized
|
||||
* and populated locally.
|
||||
*/
|
||||
struct drm_plane_zpos {
|
||||
struct drm_plane *plane;
|
||||
struct wl_list link; /**< :candidate_plane_zpos_list */
|
||||
};
|
||||
|
||||
/**
|
||||
* Plane state holds the dynamic state for a plane: where it is positioned,
|
||||
* and which buffer it is currently displaying.
|
||||
@@ -461,7 +467,7 @@ struct drm_plane_state {
|
||||
struct drm_plane {
|
||||
struct weston_plane base;
|
||||
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
|
||||
enum wdrm_plane_type type;
|
||||
|
||||
@@ -483,7 +489,7 @@ struct drm_plane {
|
||||
};
|
||||
|
||||
struct drm_connector {
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
|
||||
drmModeConnector *conn;
|
||||
uint32_t connector_id;
|
||||
@@ -495,16 +501,15 @@ struct drm_connector {
|
||||
};
|
||||
|
||||
struct drm_writeback {
|
||||
/* drm_backend::writeback_connector_list */
|
||||
/* drm_device::writeback_connector_list */
|
||||
struct wl_list link;
|
||||
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
struct drm_connector connector;
|
||||
};
|
||||
|
||||
struct drm_head {
|
||||
struct weston_head base;
|
||||
struct drm_backend *backend;
|
||||
struct drm_connector connector;
|
||||
|
||||
struct drm_edid edid;
|
||||
@@ -512,13 +517,17 @@ struct drm_head {
|
||||
struct backlight *backlight;
|
||||
|
||||
drmModeModeInfo inherited_mode; /**< Original mode on the connector */
|
||||
uint32_t inherited_max_bpc; /**< Original max_bpc on the connector */
|
||||
uint32_t inherited_crtc_id; /**< Original CRTC assignment */
|
||||
|
||||
/* drm_output::disable_head */
|
||||
struct wl_list disable_head_link;
|
||||
};
|
||||
|
||||
struct drm_crtc {
|
||||
/* drm_backend::crtc_list */
|
||||
/* drm_device::crtc_list */
|
||||
struct wl_list link;
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
|
||||
/* The output driven by the CRTC */
|
||||
struct drm_output *output;
|
||||
@@ -532,14 +541,18 @@ struct drm_crtc {
|
||||
|
||||
struct drm_output {
|
||||
struct weston_output base;
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
/* drm_head::disable_head_link */
|
||||
struct wl_list disable_head;
|
||||
|
||||
bool page_flip_pending;
|
||||
bool atomic_complete_pending;
|
||||
bool destroy_pending;
|
||||
bool disable_pending;
|
||||
bool dpms_off_pending;
|
||||
bool mode_switch_pending;
|
||||
|
||||
uint32_t gbm_cursor_handle[2];
|
||||
struct drm_fb *gbm_cursor_fb[2];
|
||||
@@ -552,6 +565,11 @@ struct drm_output {
|
||||
uint32_t gbm_format;
|
||||
uint32_t gbm_bo_flags;
|
||||
|
||||
uint32_t hdr_output_metadata_blob_id;
|
||||
uint64_t ackd_color_outcome_serial;
|
||||
|
||||
unsigned max_bpc;
|
||||
|
||||
/* Plane being displayed directly on the CRTC */
|
||||
struct drm_plane *scanout_plane;
|
||||
|
||||
@@ -572,19 +590,36 @@ struct drm_output {
|
||||
struct wl_event_source *pageflip_timer;
|
||||
|
||||
bool virtual;
|
||||
void (*virtual_destroy)(struct weston_output *base);
|
||||
|
||||
submit_frame_cb virtual_submit_frame;
|
||||
};
|
||||
|
||||
void
|
||||
drm_head_destroy(struct weston_head *head_base);
|
||||
|
||||
static inline struct drm_head *
|
||||
to_drm_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != drm_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct drm_head, base);
|
||||
}
|
||||
|
||||
void
|
||||
drm_output_destroy(struct weston_output *output_base);
|
||||
void
|
||||
drm_virtual_output_destroy(struct weston_output *output_base);
|
||||
|
||||
static inline struct drm_output *
|
||||
to_drm_output(struct weston_output *base)
|
||||
{
|
||||
if (
|
||||
#ifdef BUILD_DRM_VIRTUAL
|
||||
base->destroy != drm_virtual_output_destroy &&
|
||||
#endif
|
||||
base->destroy != drm_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct drm_output, base);
|
||||
}
|
||||
|
||||
@@ -617,7 +652,7 @@ drm_output_get_plane_type_name(struct drm_plane *p)
|
||||
}
|
||||
|
||||
struct drm_crtc *
|
||||
drm_crtc_find(struct drm_backend *b, uint32_t crtc_id);
|
||||
drm_crtc_find(struct drm_device *device, uint32_t crtc_id);
|
||||
|
||||
struct drm_head *
|
||||
drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id);
|
||||
@@ -642,7 +677,7 @@ drm_view_transform_supported(struct weston_view *ev, struct weston_output *outpu
|
||||
}
|
||||
|
||||
int
|
||||
drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode);
|
||||
drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode);
|
||||
|
||||
struct drm_mode *
|
||||
drm_output_choose_mode(struct drm_output *output,
|
||||
@@ -651,7 +686,7 @@ void
|
||||
update_head_from_connector(struct drm_head *head);
|
||||
|
||||
void
|
||||
drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list);
|
||||
drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list);
|
||||
|
||||
void
|
||||
drm_output_print_modes(struct drm_output *output);
|
||||
@@ -662,7 +697,7 @@ drm_output_set_mode(struct weston_output *base,
|
||||
const char *modeline);
|
||||
|
||||
void
|
||||
drm_property_info_populate(struct drm_backend *b,
|
||||
drm_property_info_populate(struct drm_device *device,
|
||||
const struct drm_property_info *src,
|
||||
struct drm_property_info *info,
|
||||
unsigned int num_infos,
|
||||
@@ -690,7 +725,7 @@ extern const struct drm_property_info connector_props[];
|
||||
extern const struct drm_property_info crtc_props[];
|
||||
|
||||
int
|
||||
init_kms_caps(struct drm_backend *b);
|
||||
init_kms_caps(struct drm_device *device);
|
||||
|
||||
int
|
||||
drm_pending_state_test(struct drm_pending_state *pending_state);
|
||||
@@ -717,15 +752,18 @@ void
|
||||
drm_fb_unref(struct drm_fb *fb);
|
||||
|
||||
struct drm_fb *
|
||||
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
drm_fb_create_dumb(struct drm_device *device, int width, int height,
|
||||
uint32_t format);
|
||||
struct drm_fb *
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device,
|
||||
bool is_opaque, enum drm_fb_type type);
|
||||
|
||||
void
|
||||
drm_output_set_cursor_view(struct drm_output *output, struct weston_view *ev);
|
||||
|
||||
int
|
||||
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output);
|
||||
|
||||
#ifdef BUILD_DRM_GBM
|
||||
extern struct drm_fb *
|
||||
drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
@@ -749,7 +787,7 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
|
||||
#endif
|
||||
|
||||
struct drm_pending_state *
|
||||
drm_pending_state_alloc(struct drm_backend *backend);
|
||||
drm_pending_state_alloc(struct drm_device *device);
|
||||
void
|
||||
drm_pending_state_free(struct drm_pending_state *pending_state);
|
||||
struct drm_output_state *
|
||||
@@ -800,7 +838,7 @@ void
|
||||
drm_plane_reset_state(struct drm_plane *plane);
|
||||
|
||||
void
|
||||
drm_assign_planes(struct weston_output *output_base, void *repaint_data);
|
||||
drm_assign_planes(struct weston_output *output_base);
|
||||
|
||||
bool
|
||||
drm_plane_is_available(struct drm_plane *plane, struct drm_output *output);
|
||||
@@ -837,9 +875,6 @@ drm_output_fini_egl(struct drm_output *output);
|
||||
struct drm_fb *
|
||||
drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage);
|
||||
|
||||
void
|
||||
renderer_switch_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data);
|
||||
#else
|
||||
inline static int
|
||||
init_egl(struct drm_backend *b)
|
||||
@@ -864,11 +899,4 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline static void
|
||||
renderer_switch_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data)
|
||||
{
|
||||
weston_log("Compiled without GBM/EGL support\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
* CRTC's. Also, as this is a fake CRTC, it will not try to populate props.
|
||||
*/
|
||||
static struct drm_crtc *
|
||||
drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
|
||||
drm_virtual_crtc_create(struct drm_device *device, struct drm_output *output)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
@@ -55,7 +55,7 @@ drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
|
||||
if (!crtc)
|
||||
return NULL;
|
||||
|
||||
crtc->backend = b;
|
||||
crtc->device = device;
|
||||
crtc->output = output;
|
||||
|
||||
crtc->crtc_id = 0;
|
||||
@@ -81,17 +81,32 @@ drm_virtual_crtc_destroy(struct drm_crtc *crtc)
|
||||
free(crtc);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_drm_plane_index_maximum(struct drm_device *device)
|
||||
{
|
||||
uint32_t max = 0;
|
||||
struct drm_plane *p;
|
||||
|
||||
wl_list_for_each(p, &device->plane_list, link) {
|
||||
if (p->plane_idx > max)
|
||||
max = p->plane_idx;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a drm_plane for virtual output
|
||||
*
|
||||
* Call drm_virtual_plane_destroy to clean up the plane.
|
||||
*
|
||||
* @param b DRM compositor backend
|
||||
* @param device DRM device
|
||||
* @param output Output to create internal plane for
|
||||
*/
|
||||
static struct drm_plane *
|
||||
drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
drm_virtual_plane_create(struct drm_device *device, struct drm_output *output)
|
||||
{
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane *plane;
|
||||
struct weston_drm_format *fmt;
|
||||
uint64_t mod;
|
||||
@@ -103,7 +118,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
}
|
||||
|
||||
plane->type = WDRM_PLANE_TYPE_PRIMARY;
|
||||
plane->backend = b;
|
||||
plane->device = device;
|
||||
plane->state_cur = drm_plane_state_alloc(NULL, plane);
|
||||
plane->state_cur->complete = true;
|
||||
|
||||
@@ -115,7 +130,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
/* If output supports linear modifier, we add it to the plane.
|
||||
* Otherwise we add DRM_FORMAT_MOD_INVALID, as explicit modifiers
|
||||
* are not supported. */
|
||||
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers)
|
||||
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && device->fb_modifiers)
|
||||
mod = DRM_FORMAT_MOD_LINEAR;
|
||||
else
|
||||
mod = DRM_FORMAT_MOD_INVALID;
|
||||
@@ -124,7 +139,8 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
goto err;
|
||||
|
||||
weston_plane_init(&plane->base, b->compositor, 0, 0);
|
||||
wl_list_insert(&b->plane_list, &plane->link);
|
||||
plane->plane_idx = get_drm_plane_index_maximum(device) + 1;
|
||||
wl_list_insert(&device->plane_list, &plane->link);
|
||||
|
||||
return plane;
|
||||
|
||||
@@ -163,11 +179,10 @@ static int
|
||||
drm_virtual_output_submit_frame(struct drm_output *output,
|
||||
struct drm_fb *fb)
|
||||
{
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
int fd, ret;
|
||||
|
||||
assert(fb->num_planes == 1);
|
||||
ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd);
|
||||
ret = drmPrimeHandleToFD(fb->fd, fb->handles[0], DRM_CLOEXEC, &fd);
|
||||
if (ret) {
|
||||
weston_log("drmPrimeHandleFD failed, errno=%d\n", errno);
|
||||
return -1;
|
||||
@@ -185,17 +200,20 @@ drm_virtual_output_submit_frame(struct drm_output *output,
|
||||
|
||||
static int
|
||||
drm_virtual_output_repaint(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct drm_pending_state *pending_state = repaint_data;
|
||||
struct drm_output_state *state = NULL;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_plane_state *scanout_state;
|
||||
struct drm_pending_state *pending_state;
|
||||
struct drm_device *device;
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
device = output->device;
|
||||
pending_state = device->repaint_data;
|
||||
|
||||
if (output->disable_pending || output->destroy_pending)
|
||||
goto err;
|
||||
|
||||
@@ -242,7 +260,7 @@ drm_virtual_output_deinit(struct weston_output *base)
|
||||
drm_virtual_crtc_destroy(output->crtc);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
drm_virtual_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
@@ -256,6 +274,9 @@ drm_virtual_output_destroy(struct weston_output *base)
|
||||
|
||||
drm_output_state_free(output->state_cur);
|
||||
|
||||
if (output->virtual_destroy)
|
||||
output->virtual_destroy(base);
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
@@ -263,7 +284,8 @@ static int
|
||||
drm_virtual_output_enable(struct weston_output *output_base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *b = to_drm_backend(output_base->compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
@@ -277,7 +299,7 @@ drm_virtual_output_enable(struct weston_output *output_base)
|
||||
goto err;
|
||||
}
|
||||
|
||||
output->scanout_plane = drm_virtual_plane_create(b, output);
|
||||
output->scanout_plane = drm_virtual_plane_create(device, output);
|
||||
if (!output->scanout_plane) {
|
||||
weston_log("Failed to find primary plane for output %s\n",
|
||||
output->base.name);
|
||||
@@ -320,22 +342,27 @@ drm_virtual_output_disable(struct weston_output *base)
|
||||
}
|
||||
|
||||
static struct weston_output *
|
||||
drm_virtual_output_create(struct weston_compositor *c, char *name)
|
||||
drm_virtual_output_create(struct weston_compositor *c, char *name,
|
||||
void (*destroy_func)(struct weston_output *))
|
||||
{
|
||||
struct drm_output *output;
|
||||
struct drm_backend *b = to_drm_backend(c);
|
||||
/* Always use the main device for virtual outputs */
|
||||
struct drm_device *device = b->drm;
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
output->crtc = drm_virtual_crtc_create(b, output);
|
||||
output->device = device;
|
||||
output->crtc = drm_virtual_crtc_create(device, output);
|
||||
if (!output->crtc) {
|
||||
free(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output->virtual = true;
|
||||
output->virtual_destroy = destroy_func;
|
||||
output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING;
|
||||
|
||||
weston_output_init(&output->base, c, name);
|
||||
@@ -357,7 +384,8 @@ drm_virtual_output_set_gbm_format(struct weston_output *base,
|
||||
const char *gbm_format)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
struct drm_backend *b = to_drm_backend(base->compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
|
||||
if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
|
||||
output->gbm_format = b->gbm_format;
|
||||
|
||||
+413
-290
File diff suppressed because it is too large
Load Diff
+72
-46
@@ -69,7 +69,7 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
|
||||
}
|
||||
|
||||
static int
|
||||
drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
drm_fb_addfb(struct drm_device *device, struct drm_fb *fb)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
uint64_t mods[4] = { };
|
||||
@@ -77,7 +77,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
|
||||
/* If we have a modifier set, we must only use the WithModifiers
|
||||
* entrypoint; we cannot import it through legacy ioctls. */
|
||||
if (b->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
if (device->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
/* KMS demands that if a modifier is set, it must be the same
|
||||
* for all planes. */
|
||||
for (i = 0; i < ARRAY_LENGTH(mods) && fb->handles[i]; i++)
|
||||
@@ -98,7 +98,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
|
||||
/* Legacy AddFB can't always infer the format from depth/bpp alone, so
|
||||
* check if our format is one of the lucky ones. */
|
||||
if (!fb->format->depth || !fb->format->bpp)
|
||||
if (!fb->format->addfb_legacy_depth || !fb->format->bpp)
|
||||
return ret;
|
||||
|
||||
/* Cannot fall back to AddFB for multi-planar formats either. */
|
||||
@@ -106,13 +106,13 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
return ret;
|
||||
|
||||
ret = drmModeAddFB(fb->fd, fb->width, fb->height,
|
||||
fb->format->depth, fb->format->bpp,
|
||||
fb->format->addfb_legacy_depth, fb->format->bpp,
|
||||
fb->strides[0], fb->handles[0], &fb->fb_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct drm_fb *
|
||||
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
drm_fb_create_dumb(struct drm_device *device, int width, int height,
|
||||
uint32_t format)
|
||||
{
|
||||
struct drm_fb *fb;
|
||||
@@ -134,7 +134,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
goto err_fb;
|
||||
}
|
||||
|
||||
if (!fb->format->depth || !fb->format->bpp) {
|
||||
if (!fb->format->addfb_legacy_depth || !fb->format->bpp) {
|
||||
weston_log("format 0x%lx is not compatible with dumb buffers\n",
|
||||
(unsigned long) format);
|
||||
goto err_fb;
|
||||
@@ -145,7 +145,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
create_arg.width = width;
|
||||
create_arg.height = height;
|
||||
|
||||
ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
|
||||
ret = drmIoctl(device->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
|
||||
if (ret)
|
||||
goto err_fb;
|
||||
|
||||
@@ -157,9 +157,9 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
fb->size = create_arg.size;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
fb->fd = b->drm.fd;
|
||||
fb->fd = device->drm.fd;
|
||||
|
||||
if (drm_fb_addfb(b, fb) != 0) {
|
||||
if (drm_fb_addfb(device, fb) != 0) {
|
||||
weston_log("failed to create kms fb: %s\n", strerror(errno));
|
||||
goto err_bo;
|
||||
}
|
||||
@@ -171,18 +171,18 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
goto err_add_fb;
|
||||
|
||||
fb->map = mmap(NULL, fb->size, PROT_WRITE,
|
||||
MAP_SHARED, b->drm.fd, map_arg.offset);
|
||||
MAP_SHARED, device->drm.fd, map_arg.offset);
|
||||
if (fb->map == MAP_FAILED)
|
||||
goto err_add_fb;
|
||||
|
||||
return fb;
|
||||
|
||||
err_add_fb:
|
||||
drmModeRmFB(b->drm.fd, fb->fb_id);
|
||||
drmModeRmFB(device->drm.fd, fb->fb_id);
|
||||
err_bo:
|
||||
memset(&destroy_arg, 0, sizeof(destroy_arg));
|
||||
destroy_arg.handle = create_arg.handle;
|
||||
drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
|
||||
drmIoctl(device->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
|
||||
err_fb:
|
||||
free(fb);
|
||||
return NULL;
|
||||
@@ -218,7 +218,7 @@ drm_fb_destroy_dmabuf(struct drm_fb *fb)
|
||||
|
||||
static struct drm_fb *
|
||||
drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
struct drm_backend *backend, bool is_opaque,
|
||||
struct drm_device *device, bool is_opaque,
|
||||
uint32_t *try_view_on_plane_failure_reasons)
|
||||
{
|
||||
#ifndef HAVE_GBM_FD_IMPORT
|
||||
@@ -227,6 +227,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
* of GBM_BO_IMPORT_FD_MODIFIER. */
|
||||
return NULL;
|
||||
#else
|
||||
struct drm_backend *backend = device->backend;
|
||||
struct drm_fb *fb;
|
||||
int i;
|
||||
struct gbm_import_fd_modifier_data import_mod = {
|
||||
@@ -287,7 +288,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
fb->height = dmabuf->attributes.height;
|
||||
fb->modifier = dmabuf->attributes.modifier[0];
|
||||
fb->size = 0;
|
||||
fb->fd = backend->drm.fd;
|
||||
fb->fd = device->drm.fd;
|
||||
|
||||
ARRAY_COPY(fb->strides, dmabuf->attributes.stride);
|
||||
ARRAY_COPY(fb->offsets, dmabuf->attributes.offset);
|
||||
@@ -302,10 +303,10 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
if (is_opaque)
|
||||
fb->format = pixel_format_get_opaque_substitute(fb->format);
|
||||
|
||||
if (backend->min_width > fb->width ||
|
||||
fb->width > backend->max_width ||
|
||||
backend->min_height > fb->height ||
|
||||
fb->height > backend->max_height) {
|
||||
if (device->min_width > fb->width ||
|
||||
fb->width > device->max_width ||
|
||||
device->min_height > fb->height ||
|
||||
fb->height > device->max_height) {
|
||||
weston_log("bo geometry out of bounds\n");
|
||||
goto err_free;
|
||||
}
|
||||
@@ -315,12 +316,15 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
union gbm_bo_handle handle;
|
||||
|
||||
handle = gbm_bo_get_handle_for_plane(fb->bo, i);
|
||||
if (handle.s32 == -1)
|
||||
if (handle.s32 == -1) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED;
|
||||
goto err_free;
|
||||
}
|
||||
fb->handles[i] = handle.u32;
|
||||
}
|
||||
|
||||
if (drm_fb_addfb(backend, fb) != 0) {
|
||||
if (drm_fb_addfb(device, fb) != 0) {
|
||||
if (try_view_on_plane_failure_reasons)
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_ADD_FB_FAILED;
|
||||
@@ -336,7 +340,7 @@ err_free:
|
||||
}
|
||||
|
||||
struct drm_fb *
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device,
|
||||
bool is_opaque, enum drm_fb_type type)
|
||||
{
|
||||
struct drm_fb *fb = gbm_bo_get_user_data(bo);
|
||||
@@ -356,7 +360,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
fb->type = type;
|
||||
fb->refcnt = 1;
|
||||
fb->bo = bo;
|
||||
fb->fd = backend->drm.fd;
|
||||
fb->fd = device->drm.fd;
|
||||
|
||||
fb->width = gbm_bo_get_width(bo);
|
||||
fb->height = gbm_bo_get_height(bo);
|
||||
@@ -389,15 +393,15 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
if (is_opaque)
|
||||
fb->format = pixel_format_get_opaque_substitute(fb->format);
|
||||
|
||||
if (backend->min_width > fb->width ||
|
||||
fb->width > backend->max_width ||
|
||||
backend->min_height > fb->height ||
|
||||
fb->height > backend->max_height) {
|
||||
if (device->min_width > fb->width ||
|
||||
fb->width > device->max_width ||
|
||||
device->min_height > fb->height ||
|
||||
fb->height > device->max_height) {
|
||||
weston_log("bo geometry out of bounds\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (drm_fb_addfb(backend, fb) != 0) {
|
||||
if (drm_fb_addfb(device, fb) != 0) {
|
||||
if (type == BUFFER_GBM_SURFACE)
|
||||
weston_log("failed to create kms fb: %s\n",
|
||||
strerror(errno));
|
||||
@@ -453,22 +457,25 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
|
||||
{
|
||||
struct drm_fb *fb;
|
||||
struct drm_backend *b = to_drm_backend(ec);
|
||||
struct drm_device *device = b->drm;
|
||||
bool ret = false;
|
||||
uint32_t try_reason = 0x0;
|
||||
|
||||
fb = drm_fb_get_from_dmabuf(dmabuf, b, true, NULL);
|
||||
fb = drm_fb_get_from_dmabuf(dmabuf, device, true, &try_reason);
|
||||
if (fb)
|
||||
ret = true;
|
||||
|
||||
drm_fb_unref(fb);
|
||||
drm_debug(b, "[dmabuf] dmabuf %p, import test %s\n", dmabuf,
|
||||
ret ? "succeeded" : "failed");
|
||||
drm_debug(b, "[dmabuf] dmabuf %p, import test %s, with reason 0x%x\n", dmabuf,
|
||||
ret ? "succeeded" : "failed", try_reason);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane)
|
||||
{
|
||||
struct drm_backend *b = plane->backend;
|
||||
struct drm_device *device = plane->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct weston_drm_format *fmt;
|
||||
|
||||
/* Check whether the format is supported */
|
||||
@@ -508,6 +515,8 @@ drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
|
||||
struct drm_buffer_fb *buf_fb =
|
||||
container_of(listener, struct drm_buffer_fb, buffer_destroy_listener);
|
||||
|
||||
wl_list_remove(&buf_fb->buffer_destroy_listener.link);
|
||||
|
||||
if (buf_fb->fb) {
|
||||
assert(buf_fb->fb->type == BUFFER_CLIENT ||
|
||||
buf_fb->fb->type == BUFFER_DMABUF);
|
||||
@@ -523,25 +532,36 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||
struct drm_buffer_fb *buf_fb;
|
||||
bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox);
|
||||
struct linux_dmabuf_buffer *dmabuf;
|
||||
struct drm_fb *fb;
|
||||
struct drm_plane *plane;
|
||||
|
||||
if (ev->alpha != 1.0f)
|
||||
if (ev->alpha != 1.0f) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_GLOBAL_ALPHA;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!drm_view_transform_supported(ev, &output->base))
|
||||
if (!drm_view_transform_supported(ev, &output->base)) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_INCOMPATIBLE_TRANSFORM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
|
||||
ev->surface->desired_protection > output->base.current_protection)
|
||||
ev->surface->desired_protection > output->base.current_protection) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
if (!buffer) {
|
||||
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_BUFFER;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buffer->backend_private) {
|
||||
buf_fb = buffer->backend_private;
|
||||
@@ -554,20 +574,18 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
|
||||
wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener);
|
||||
|
||||
if (wl_shm_buffer_get(buffer->resource))
|
||||
goto unsuitable;
|
||||
|
||||
/* GBM is used for dmabuf import as well as from client wl_buffer. */
|
||||
if (!b->gbm)
|
||||
if (!b->gbm) {
|
||||
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_GBM;
|
||||
goto unsuitable;
|
||||
}
|
||||
|
||||
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
|
||||
if (dmabuf) {
|
||||
fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque,
|
||||
if (buffer->type == WESTON_BUFFER_DMABUF) {
|
||||
fb = drm_fb_get_from_dmabuf(buffer->dmabuf, device, is_opaque,
|
||||
&buf_fb->failure_reasons);
|
||||
if (!fb)
|
||||
goto unsuitable;
|
||||
} else {
|
||||
} else if (buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) {
|
||||
struct gbm_bo *bo;
|
||||
|
||||
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
|
||||
@@ -575,16 +593,24 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
if (!bo)
|
||||
goto unsuitable;
|
||||
|
||||
fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
|
||||
fb = drm_fb_get_from_bo(bo, device, is_opaque, BUFFER_CLIENT);
|
||||
if (!fb) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
(1 << FAILURE_REASONS_ADD_FB_FAILED);
|
||||
gbm_bo_destroy(bo);
|
||||
goto unsuitable;
|
||||
}
|
||||
} else {
|
||||
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_BUFFER_TYPE;
|
||||
goto unsuitable;
|
||||
}
|
||||
|
||||
/* Check if this buffer can ever go on any planes. If it can't, we have
|
||||
* no reason to ever have a drm_fb, so we fail it here. */
|
||||
wl_list_for_each(plane, &b->plane_list, link) {
|
||||
wl_list_for_each(plane, &device->plane_list, link) {
|
||||
/* only SHM buffers can go into cursor planes */
|
||||
if (plane->type == WDRM_PLANE_TYPE_CURSOR)
|
||||
continue;
|
||||
if (drm_fb_compatible_with_plane(fb, plane))
|
||||
fb->plane_mask |= (1 << plane->plane_idx);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2021-2022 Collabora, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libweston/libweston.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "drm-internal.h"
|
||||
|
||||
static inline uint16_t
|
||||
color_xy_to_u16(float v)
|
||||
{
|
||||
assert(v >= 0.0f);
|
||||
assert(v <= 1.0f);
|
||||
/*
|
||||
* CTA-861-G
|
||||
* 6.9.1 Static Metadata Type 1
|
||||
* chromaticity coordinate encoding
|
||||
*/
|
||||
return (uint16_t)round(v * 50000.0);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
nits_to_u16(float nits)
|
||||
{
|
||||
assert(nits >= 1.0f);
|
||||
assert(nits <= 65535.0f);
|
||||
/*
|
||||
* CTA-861-G
|
||||
* 6.9.1 Static Metadata Type 1
|
||||
* max display mastering luminance, max content light level,
|
||||
* max frame-average light level
|
||||
*/
|
||||
return (uint16_t)round(nits);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
nits_to_u16_dark(float nits)
|
||||
{
|
||||
assert(nits >= 0.0001f);
|
||||
assert(nits <= 6.5535f);
|
||||
/*
|
||||
* CTA-861-G
|
||||
* 6.9.1 Static Metadata Type 1
|
||||
* min display mastering luminance
|
||||
*/
|
||||
return (uint16_t)round(nits * 10000.0);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst,
|
||||
const struct weston_hdr_metadata_type1 *src)
|
||||
{
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
dst->display_primaries[i].x = color_xy_to_u16(src->primary[i].x);
|
||||
dst->display_primaries[i].y = color_xy_to_u16(src->primary[i].y);
|
||||
}
|
||||
}
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_WHITE) {
|
||||
dst->white_point.x = color_xy_to_u16(src->white.x);
|
||||
dst->white_point.y = color_xy_to_u16(src->white.y);
|
||||
}
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML)
|
||||
dst->max_display_mastering_luminance = nits_to_u16(src->maxDML);
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MINDML)
|
||||
dst->min_display_mastering_luminance = nits_to_u16_dark(src->minDML);
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL)
|
||||
dst->max_cll = nits_to_u16(src->maxCLL);
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL)
|
||||
dst->max_fall = nits_to_u16(src->maxFALL);
|
||||
}
|
||||
|
||||
int
|
||||
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output)
|
||||
{
|
||||
struct drm_device *device = output->device;
|
||||
const struct weston_hdr_metadata_type1 *src;
|
||||
struct hdr_output_metadata meta;
|
||||
uint32_t blob_id = 0;
|
||||
int ret;
|
||||
|
||||
if (output->hdr_output_metadata_blob_id &&
|
||||
output->ackd_color_outcome_serial == output->base.color_outcome_serial)
|
||||
return 0;
|
||||
|
||||
src = weston_output_get_hdr_metadata_type1(&output->base);
|
||||
|
||||
/*
|
||||
* Set up the data for Dynamic Range and Mastering InfoFrame,
|
||||
* CTA-861-G, a.k.a the static HDR metadata.
|
||||
*/
|
||||
|
||||
memset(&meta, 0, sizeof meta);
|
||||
|
||||
meta.metadata_type = 0; /* Static Metadata Type 1 */
|
||||
|
||||
/* Duplicated field in UABI struct */
|
||||
meta.hdmi_metadata_type1.metadata_type = meta.metadata_type;
|
||||
|
||||
switch (output->base.eotf_mode) {
|
||||
case WESTON_EOTF_MODE_NONE:
|
||||
assert(0 && "bad eotf_mode: none");
|
||||
return -1;
|
||||
case WESTON_EOTF_MODE_SDR:
|
||||
/*
|
||||
* Do not send any static HDR metadata. Video sinks should
|
||||
* respond by switching to traditional SDR mode. If they
|
||||
* do not, the kernel should fix that up.
|
||||
*/
|
||||
assert(output->hdr_output_metadata_blob_id == 0);
|
||||
return 0;
|
||||
case WESTON_EOTF_MODE_TRADITIONAL_HDR:
|
||||
meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */
|
||||
break;
|
||||
case WESTON_EOTF_MODE_ST2084:
|
||||
meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */
|
||||
weston_hdr_metadata_type1_to_kms(&meta.hdmi_metadata_type1, src);
|
||||
break;
|
||||
case WESTON_EOTF_MODE_HLG:
|
||||
meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */
|
||||
break;
|
||||
}
|
||||
|
||||
if (meta.hdmi_metadata_type1.eotf == 0) {
|
||||
assert(0 && "bad eotf_mode");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = drmModeCreatePropertyBlob(device->drm.fd,
|
||||
&meta, sizeof meta, &blob_id);
|
||||
if (ret != 0) {
|
||||
weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n",
|
||||
output->base.name, strerror(-ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
drmModeDestroyPropertyBlob(device->drm.fd,
|
||||
output->hdr_output_metadata_blob_id);
|
||||
|
||||
output->hdr_output_metadata_blob_id = blob_id;
|
||||
output->ackd_color_outcome_serial = output->base.color_outcome_serial;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+180
-133
@@ -143,6 +143,10 @@ const struct drm_property_info connector_props[] = {
|
||||
.enum_values = panel_orientation_enums,
|
||||
.num_enum_values = WDRM_PANEL_ORIENTATION__COUNT,
|
||||
},
|
||||
[WDRM_CONNECTOR_HDR_OUTPUT_METADATA] = {
|
||||
.name = "HDR_OUTPUT_METADATA",
|
||||
},
|
||||
[WDRM_CONNECTOR_MAX_BPC] = { .name = "max bpc", },
|
||||
};
|
||||
|
||||
const struct drm_property_info crtc_props[] = {
|
||||
@@ -272,14 +276,14 @@ drm_property_get_range_values(struct drm_property_info *info,
|
||||
* The values given in enum_names are searched for, and stored in the
|
||||
* same-indexed field of the map array.
|
||||
*
|
||||
* @param b DRM backend object
|
||||
* @param device DRM device object
|
||||
* @param src DRM property info array to source from
|
||||
* @param info DRM property info array to copy into
|
||||
* @param num_infos Number of entries in the source array
|
||||
* @param props DRM object properties for the object
|
||||
*/
|
||||
void
|
||||
drm_property_info_populate(struct drm_backend *b,
|
||||
drm_property_info_populate(struct drm_device *device,
|
||||
const struct drm_property_info *src,
|
||||
struct drm_property_info *info,
|
||||
unsigned int num_infos,
|
||||
@@ -311,7 +315,7 @@ drm_property_info_populate(struct drm_backend *b,
|
||||
for (i = 0; i < props->count_props; i++) {
|
||||
unsigned int k;
|
||||
|
||||
prop = drmModeGetProperty(b->drm.fd, props->props[i]);
|
||||
prop = drmModeGetProperty(device->drm.fd, props->props[i]);
|
||||
if (!prop)
|
||||
continue;
|
||||
|
||||
@@ -411,19 +415,6 @@ drm_property_info_free(struct drm_property_info *info, int num_props)
|
||||
memset(info, 0, sizeof(*info) * num_props);
|
||||
}
|
||||
|
||||
static inline uint32_t *
|
||||
formats_ptr(struct drm_format_modifier_blob *blob)
|
||||
{
|
||||
return (uint32_t *)(((char *)blob) + blob->formats_offset);
|
||||
}
|
||||
|
||||
static inline struct drm_format_modifier *
|
||||
modifiers_ptr(struct drm_format_modifier_blob *blob)
|
||||
{
|
||||
return (struct drm_format_modifier *)
|
||||
(((char *)blob) + blob->modifiers_offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the plane's formats array, using either the IN_FORMATS blob
|
||||
* property (if available), or the plane's format list if not.
|
||||
@@ -433,13 +424,11 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
|
||||
const drmModeObjectProperties *props,
|
||||
const bool use_modifiers)
|
||||
{
|
||||
unsigned i, j;
|
||||
struct drm_device *device = plane->device;
|
||||
uint32_t i, blob_id, fmt_prev = DRM_FORMAT_INVALID;
|
||||
drmModeFormatModifierIterator drm_iter = {0};
|
||||
struct weston_drm_format *fmt = NULL;
|
||||
drmModePropertyBlobRes *blob = NULL;
|
||||
struct drm_format_modifier_blob *fmt_mod_blob;
|
||||
struct drm_format_modifier *blob_modifiers;
|
||||
uint32_t *blob_formats;
|
||||
uint32_t blob_id;
|
||||
struct weston_drm_format *fmt;
|
||||
int ret = 0;
|
||||
|
||||
if (!use_modifiers)
|
||||
@@ -451,39 +440,26 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
|
||||
if (blob_id == 0)
|
||||
goto fallback;
|
||||
|
||||
blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
|
||||
blob = drmModeGetPropertyBlob(device->drm.fd, blob_id);
|
||||
if (!blob)
|
||||
goto fallback;
|
||||
|
||||
fmt_mod_blob = blob->data;
|
||||
blob_formats = formats_ptr(fmt_mod_blob);
|
||||
blob_modifiers = modifiers_ptr(fmt_mod_blob);
|
||||
|
||||
assert(kplane->count_formats == fmt_mod_blob->count_formats);
|
||||
|
||||
for (i = 0; i < fmt_mod_blob->count_formats; i++) {
|
||||
fmt = weston_drm_format_array_add_format(&plane->formats,
|
||||
blob_formats[i]);
|
||||
if (!fmt) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
|
||||
struct drm_format_modifier *mod = &blob_modifiers[j];
|
||||
|
||||
if ((i < mod->offset) || (i > mod->offset + 63))
|
||||
continue;
|
||||
if (!(mod->formats & (1 << (i - mod->offset))))
|
||||
continue;
|
||||
|
||||
ret = weston_drm_format_add_modifier(fmt, mod->modifier);
|
||||
if (ret < 0)
|
||||
while (drmModeFormatModifierBlobIterNext(blob, &drm_iter)) {
|
||||
if (fmt_prev != drm_iter.fmt) {
|
||||
fmt = weston_drm_format_array_add_format(&plane->formats,
|
||||
drm_iter.fmt);
|
||||
if (!fmt) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fmt_prev = drm_iter.fmt;
|
||||
}
|
||||
|
||||
if (fmt->modifiers.size == 0)
|
||||
weston_drm_format_array_remove_latest_format(&plane->formats);
|
||||
ret = weston_drm_format_add_modifier(fmt, drm_iter.mod);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -510,14 +486,15 @@ drm_output_set_gamma(struct weston_output *output_base,
|
||||
{
|
||||
int rc;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *backend =
|
||||
to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
|
||||
assert(output);
|
||||
|
||||
/* check */
|
||||
if (output_base->gamma_size != size)
|
||||
return;
|
||||
|
||||
rc = drmModeCrtcSetGamma(backend->drm.fd,
|
||||
rc = drmModeCrtcSetGamma(device->drm.fd,
|
||||
output->crtc->crtc_id,
|
||||
size, r, g, b);
|
||||
if (rc)
|
||||
@@ -535,7 +512,8 @@ drm_output_assign_state(struct drm_output_state *state,
|
||||
enum drm_state_apply_mode mode)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_head *head;
|
||||
|
||||
@@ -552,13 +530,13 @@ drm_output_assign_state(struct drm_output_state *state,
|
||||
|
||||
output->state_cur = state;
|
||||
|
||||
if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
|
||||
if (device->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
|
||||
drm_debug(b, "\t[CRTC:%u] setting pending flip\n",
|
||||
output->crtc->crtc_id);
|
||||
output->atomic_complete_pending = true;
|
||||
}
|
||||
|
||||
if (b->atomic_modeset &&
|
||||
if (device->atomic_modeset &&
|
||||
state->protection == WESTON_HDCP_DISABLE)
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link)
|
||||
weston_head_set_content_protection_status(&head->base,
|
||||
@@ -580,7 +558,7 @@ drm_output_assign_state(struct drm_output_state *state,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
continue;
|
||||
|
||||
assert(plane->type != WDRM_PLANE_TYPE_OVERLAY);
|
||||
@@ -593,7 +571,7 @@ static void
|
||||
drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_crtc *crtc = output->crtc;
|
||||
struct drm_plane *plane = output->cursor_plane;
|
||||
struct drm_plane_state *state;
|
||||
@@ -609,7 +587,7 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
if (!state->fb) {
|
||||
pixman_region32_fini(&plane->base.damage);
|
||||
pixman_region32_init(&plane->base.damage);
|
||||
drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -618,8 +596,8 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
|
||||
handle = output->gbm_cursor_handle[output->current_cursor];
|
||||
if (plane->state_cur->fb != state->fb) {
|
||||
if (drmModeSetCursor(b->drm.fd, crtc->crtc_id, handle,
|
||||
b->cursor_width, b->cursor_height)) {
|
||||
if (drmModeSetCursor(device->drm.fd, crtc->crtc_id, handle,
|
||||
device->cursor_width, device->cursor_height)) {
|
||||
weston_log("failed to set cursor: %s\n",
|
||||
strerror(errno));
|
||||
goto err;
|
||||
@@ -629,7 +607,7 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
pixman_region32_fini(&plane->base.damage);
|
||||
pixman_region32_init(&plane->base.damage);
|
||||
|
||||
if (drmModeMoveCursor(b->drm.fd, crtc->crtc_id,
|
||||
if (drmModeMoveCursor(device->drm.fd, crtc->crtc_id,
|
||||
state->dest_x, state->dest_y)) {
|
||||
weston_log("failed to move cursor: %s\n", strerror(errno));
|
||||
goto err;
|
||||
@@ -638,15 +616,16 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
return;
|
||||
|
||||
err:
|
||||
b->cursors_are_broken = true;
|
||||
drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
device->cursors_are_broken = true;
|
||||
drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *backend = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *backend = device->backend;
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_crtc *crtc = output->crtc;
|
||||
struct drm_property_info *dpms_prop;
|
||||
@@ -678,14 +657,14 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
|
||||
if (state->dpms != WESTON_DPMS_ON) {
|
||||
if (output->cursor_plane) {
|
||||
ret = drmModeSetCursor(backend->drm.fd, crtc->crtc_id,
|
||||
ret = drmModeSetCursor(device->drm.fd, crtc->crtc_id,
|
||||
0, 0, 0);
|
||||
if (ret)
|
||||
weston_log("drmModeSetCursor failed disable: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
weston_log("drmModeSetCrtc failed disabling: %s\n",
|
||||
@@ -719,12 +698,12 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
assert(scanout_state->in_fence_fd == -1);
|
||||
|
||||
mode = to_drm_mode(output->base.current_mode);
|
||||
if (backend->state_invalid ||
|
||||
if (device->state_invalid ||
|
||||
!scanout_plane->state_cur->fb ||
|
||||
scanout_plane->state_cur->fb->strides[0] !=
|
||||
scanout_state->fb->strides[0]) {
|
||||
|
||||
ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id,
|
||||
ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id,
|
||||
scanout_state->fb->fb_id,
|
||||
0, 0,
|
||||
connectors, n_conn,
|
||||
@@ -740,7 +719,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
crtc->crtc_id, scanout_state->plane->plane_id,
|
||||
pinfo ? pinfo->drm_format_name : "UNKNOWN");
|
||||
|
||||
if (drmModePageFlip(backend->drm.fd, crtc->crtc_id,
|
||||
if (drmModePageFlip(device->drm.fd, crtc->crtc_id,
|
||||
scanout_state->fb->fb_id,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
|
||||
weston_log("queueing pageflip failed: %s\n", strerror(errno));
|
||||
@@ -761,7 +740,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
if (dpms_prop->prop_id == 0)
|
||||
continue;
|
||||
|
||||
ret = drmModeConnectorSetProperty(backend->drm.fd,
|
||||
ret = drmModeConnectorSetProperty(device->drm.fd,
|
||||
head->connector.connector_id,
|
||||
dpms_prop->prop_id,
|
||||
state->dpms);
|
||||
@@ -786,6 +765,8 @@ static int
|
||||
crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
|
||||
enum wdrm_crtc_property prop, uint64_t val)
|
||||
{
|
||||
struct drm_device *device = crtc->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_property_info *info = &crtc->props_crtc[prop];
|
||||
int ret;
|
||||
|
||||
@@ -794,7 +775,7 @@ crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
|
||||
|
||||
ret = drmModeAtomicAddProperty(req, crtc->crtc_id, info->prop_id,
|
||||
val);
|
||||
drm_debug(crtc->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
(unsigned long) crtc->crtc_id,
|
||||
(unsigned long) info->prop_id, info->name,
|
||||
(unsigned long long) val, (unsigned long long) val);
|
||||
@@ -805,6 +786,8 @@ static int
|
||||
connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
|
||||
enum wdrm_connector_property prop, uint64_t val)
|
||||
{
|
||||
struct drm_device *device = connector->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_property_info *info = &connector->props[prop];
|
||||
uint32_t connector_id = connector->connector_id;
|
||||
int ret;
|
||||
@@ -813,7 +796,7 @@ connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
|
||||
return -1;
|
||||
|
||||
ret = drmModeAtomicAddProperty(req, connector_id, info->prop_id, val);
|
||||
drm_debug(connector->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
(unsigned long) connector_id,
|
||||
(unsigned long) info->prop_id, info->name,
|
||||
(unsigned long long) val, (unsigned long long) val);
|
||||
@@ -824,6 +807,8 @@ static int
|
||||
plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
|
||||
enum wdrm_plane_property prop, uint64_t val)
|
||||
{
|
||||
struct drm_device *device = plane->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_property_info *info = &plane->props[prop];
|
||||
int ret;
|
||||
|
||||
@@ -832,7 +817,7 @@ plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
|
||||
|
||||
ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
|
||||
val);
|
||||
drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
drm_debug(b, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
(unsigned long) plane->plane_id,
|
||||
(unsigned long) info->prop_id, info->name,
|
||||
(unsigned long long) val, (unsigned long long) val);
|
||||
@@ -921,17 +906,52 @@ drm_connector_set_hdcp_property(struct drm_connector *connector,
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_connector_set_max_bpc(struct drm_connector *connector,
|
||||
struct drm_output *output,
|
||||
drmModeAtomicReq *req)
|
||||
{
|
||||
const struct drm_property_info *info;
|
||||
struct drm_head *head;
|
||||
struct drm_backend *backend = output->device->backend;
|
||||
uint64_t max_bpc;
|
||||
uint64_t a, b;
|
||||
|
||||
if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_MAX_BPC))
|
||||
return 0;
|
||||
|
||||
if (output->max_bpc == 0) {
|
||||
/* A value of 0 means that the current max_bpc must be programmed. */
|
||||
head = drm_head_find_by_connector(backend, connector->connector_id);
|
||||
max_bpc = head->inherited_max_bpc;
|
||||
} else {
|
||||
info = &connector->props[WDRM_CONNECTOR_MAX_BPC];
|
||||
assert(info->flags & DRM_MODE_PROP_RANGE);
|
||||
assert(info->num_range_values == 2);
|
||||
a = info->range_values[0];
|
||||
b = info->range_values[1];
|
||||
assert(a <= b);
|
||||
|
||||
max_bpc = MAX(a, MIN(output->max_bpc, b));
|
||||
}
|
||||
|
||||
return connector_add_prop(req, connector,
|
||||
WDRM_CONNECTOR_MAX_BPC, max_bpc);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
drmModeAtomicReq *req,
|
||||
uint32_t *flags)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_crtc *crtc = output->crtc;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
|
||||
struct drm_head *head;
|
||||
struct drm_head *tmp;
|
||||
int ret = 0;
|
||||
|
||||
drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
|
||||
@@ -944,7 +964,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
}
|
||||
|
||||
if (state->dpms == WESTON_DPMS_ON) {
|
||||
ret = drm_mode_ensure_blob(b, current_mode);
|
||||
ret = drm_mode_ensure_blob(device, current_mode);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
@@ -968,12 +988,30 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link)
|
||||
ret |= connector_add_prop(req, &head->connector,
|
||||
WDRM_CONNECTOR_CRTC_ID, 0);
|
||||
|
||||
wl_list_for_each_safe(head, tmp, &output->disable_head,
|
||||
disable_head_link) {
|
||||
ret |= connector_add_prop(req, &head->connector,
|
||||
WDRM_CONNECTOR_CRTC_ID, 0);
|
||||
wl_list_remove(&head->disable_head_link);
|
||||
wl_list_init(&head->disable_head_link);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link)
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link) {
|
||||
drm_connector_set_hdcp_property(&head->connector,
|
||||
state->protection, req);
|
||||
|
||||
if (drm_connector_has_prop(&head->connector,
|
||||
WDRM_CONNECTOR_HDR_OUTPUT_METADATA)) {
|
||||
ret |= connector_add_prop(req, &head->connector,
|
||||
WDRM_CONNECTOR_HDR_OUTPUT_METADATA,
|
||||
output->hdr_output_metadata_blob_id);
|
||||
}
|
||||
|
||||
ret |= drm_connector_set_max_bpc(&head->connector, output, req);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
weston_log("couldn't set atomic CRTC/connector state\n");
|
||||
return ret;
|
||||
@@ -1010,9 +1048,9 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
if (plane_state->fb && plane_state->fb->format)
|
||||
pinfo = plane_state->fb->format;
|
||||
|
||||
drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
|
||||
(unsigned long) plane->plane_id,
|
||||
pinfo ? pinfo->drm_format_name : "UNKNOWN");
|
||||
drm_debug(b, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
|
||||
(unsigned long) plane->plane_id,
|
||||
pinfo ? pinfo->drm_format_name : "UNKNOWN");
|
||||
|
||||
if (plane_state->in_fence_fd >= 0) {
|
||||
ret |= plane_add_prop(req, plane,
|
||||
@@ -1044,7 +1082,8 @@ static int
|
||||
drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
enum drm_state_apply_mode mode)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_output_state *output_state, *tmp;
|
||||
struct drm_plane *plane;
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
@@ -1066,7 +1105,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
break;
|
||||
}
|
||||
|
||||
if (b->state_invalid) {
|
||||
if (device->state_invalid) {
|
||||
struct weston_head *head_base;
|
||||
struct drm_head *head;
|
||||
struct drm_crtc *crtc;
|
||||
@@ -1082,12 +1121,16 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
wl_list_for_each(head_base,
|
||||
&b->compositor->head_list, compositor_link) {
|
||||
struct drm_property_info *info;
|
||||
head = to_drm_head(head_base);
|
||||
if (!head)
|
||||
continue;
|
||||
|
||||
if (weston_head_is_enabled(head_base))
|
||||
continue;
|
||||
|
||||
head = to_drm_head(head_base);
|
||||
connector_id = head->connector.connector_id;
|
||||
if (head->connector.device != device)
|
||||
continue;
|
||||
|
||||
drm_debug(b, "\t\t[atomic] disabling inactive head %s\n",
|
||||
head_base->name);
|
||||
@@ -1103,7 +1146,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
wl_list_for_each(crtc, &b->crtc_list, link) {
|
||||
wl_list_for_each(crtc, &device->crtc_list, link) {
|
||||
struct drm_property_info *info;
|
||||
drmModeObjectProperties *props;
|
||||
uint64_t active;
|
||||
@@ -1116,7 +1159,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
* off, as the kernel will refuse to generate an event
|
||||
* for an off->off state and fail the commit.
|
||||
*/
|
||||
props = drmModeObjectGetProperties(b->drm.fd,
|
||||
props = drmModeObjectGetProperties(device->drm.fd,
|
||||
crtc->crtc_id,
|
||||
DRM_MODE_OBJECT_CRTC);
|
||||
if (!props) {
|
||||
@@ -1139,7 +1182,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
|
||||
/* Disable all the planes; planes which are being used will
|
||||
* override this state in the output-state application. */
|
||||
wl_list_for_each(plane, &b->plane_list, link) {
|
||||
wl_list_for_each(plane, &device->plane_list, link) {
|
||||
drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n",
|
||||
(unsigned long) plane->plane_id);
|
||||
plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0);
|
||||
@@ -1162,7 +1205,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
|
||||
ret = drmModeAtomicCommit(device->drm.fd, req, flags, device);
|
||||
drm_debug(b, "[atomic] drmModeAtomicCommit\n");
|
||||
|
||||
/* Test commits do not take ownership of the state; return
|
||||
@@ -1182,7 +1225,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
link)
|
||||
drm_output_assign_state(output_state, mode);
|
||||
|
||||
b->state_invalid = false;
|
||||
device->state_invalid = false;
|
||||
|
||||
assert(wl_list_empty(&pending_state->output_list));
|
||||
|
||||
@@ -1213,9 +1256,9 @@ out:
|
||||
int
|
||||
drm_pending_state_test(struct drm_pending_state *pending_state)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
return drm_pending_state_apply_atomic(pending_state,
|
||||
DRM_STATE_TEST_ONLY);
|
||||
|
||||
@@ -1234,24 +1277,25 @@ drm_pending_state_test(struct drm_pending_state *pending_state)
|
||||
int
|
||||
drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_output_state *output_state, *tmp;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
return drm_pending_state_apply_atomic(pending_state,
|
||||
DRM_STATE_APPLY_ASYNC);
|
||||
|
||||
if (b->state_invalid) {
|
||||
if (device->state_invalid) {
|
||||
/* If we need to reset all our state (e.g. because we've
|
||||
* just started, or just been VT-switched in), explicitly
|
||||
* disable all the CRTCs we aren't using. This also disables
|
||||
* all connectors on these CRTCs, so we don't need to do that
|
||||
* separately with the pre-atomic API. */
|
||||
wl_list_for_each(crtc, &b->crtc_list, link) {
|
||||
wl_list_for_each(crtc, &device->crtc_list, link) {
|
||||
if (crtc->output)
|
||||
continue;
|
||||
drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1274,7 +1318,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
weston_output_repaint_failed(&output->base);
|
||||
drm_output_state_free(output->state_cur);
|
||||
output->state_cur = drm_output_state_alloc(output, NULL);
|
||||
b->state_invalid = true;
|
||||
device->state_invalid = true;
|
||||
if (!b->use_pixman) {
|
||||
drm_output_fini_egl(output);
|
||||
drm_output_init_egl(output, b);
|
||||
@@ -1282,7 +1326,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
}
|
||||
}
|
||||
|
||||
b->state_invalid = false;
|
||||
device->state_invalid = false;
|
||||
|
||||
assert(wl_list_empty(&pending_state->output_list));
|
||||
|
||||
@@ -1301,24 +1345,24 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
int
|
||||
drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
struct drm_output_state *output_state, *tmp;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
return drm_pending_state_apply_atomic(pending_state,
|
||||
DRM_STATE_APPLY_SYNC);
|
||||
|
||||
if (b->state_invalid) {
|
||||
if (device->state_invalid) {
|
||||
/* If we need to reset all our state (e.g. because we've
|
||||
* just started, or just been VT-switched in), explicitly
|
||||
* disable all the CRTCs we aren't using. This also disables
|
||||
* all connectors on these CRTCs, so we don't need to do that
|
||||
* separately with the pre-atomic API. */
|
||||
wl_list_for_each(crtc, &b->crtc_list, link) {
|
||||
wl_list_for_each(crtc, &device->crtc_list, link) {
|
||||
if (crtc->output)
|
||||
continue;
|
||||
drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1335,7 +1379,7 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
|
||||
}
|
||||
}
|
||||
|
||||
b->state_invalid = false;
|
||||
device->state_invalid = false;
|
||||
|
||||
assert(wl_list_empty(&pending_state->output_list));
|
||||
|
||||
@@ -1360,14 +1404,14 @@ page_flip_handler(int fd, unsigned int frame,
|
||||
unsigned int sec, unsigned int usec, void *data)
|
||||
{
|
||||
struct drm_output *output = data;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
|
||||
|
||||
drm_output_update_msc(output, frame);
|
||||
|
||||
assert(!b->atomic_modeset);
|
||||
assert(!device->atomic_modeset);
|
||||
assert(output->page_flip_pending);
|
||||
output->page_flip_pending = false;
|
||||
|
||||
@@ -1378,14 +1422,15 @@ static void
|
||||
atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
||||
unsigned int usec, unsigned int crtc_id, void *data)
|
||||
{
|
||||
struct drm_backend *b = data;
|
||||
struct drm_device *device = data;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_output *output;
|
||||
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
|
||||
|
||||
crtc = drm_crtc_find(b, crtc_id);
|
||||
crtc = drm_crtc_find(device, crtc_id);
|
||||
assert(crtc);
|
||||
|
||||
output = crtc->output;
|
||||
@@ -1399,7 +1444,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
||||
drm_output_update_msc(output, frame);
|
||||
|
||||
drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
|
||||
assert(b->atomic_modeset);
|
||||
assert(device->atomic_modeset);
|
||||
assert(output->atomic_complete_pending);
|
||||
output->atomic_complete_pending = false;
|
||||
|
||||
@@ -1410,12 +1455,12 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
||||
int
|
||||
on_drm_input(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct drm_backend *b = data;
|
||||
struct drm_device *device = data;
|
||||
drmEventContext evctx;
|
||||
|
||||
memset(&evctx, 0, sizeof evctx);
|
||||
evctx.version = 3;
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
evctx.page_flip_handler2 = atomic_flip_handler;
|
||||
else
|
||||
evctx.page_flip_handler = page_flip_handler;
|
||||
@@ -1425,61 +1470,63 @@ on_drm_input(int fd, uint32_t mask, void *data)
|
||||
}
|
||||
|
||||
int
|
||||
init_kms_caps(struct drm_backend *b)
|
||||
init_kms_caps(struct drm_device *device)
|
||||
{
|
||||
struct drm_backend *b = device->backend;
|
||||
struct weston_compositor *compositor = b->compositor;
|
||||
uint64_t cap;
|
||||
int ret;
|
||||
|
||||
weston_log("using %s\n", b->drm.filename);
|
||||
weston_log("using %s\n", device->drm.filename);
|
||||
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
|
||||
if (ret != 0 || cap != 1) {
|
||||
weston_log("Error: kernel DRM KMS does not support DRM_CAP_TIMESTAMP_MONOTONIC.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (weston_compositor_set_presentation_clock(b->compositor, CLOCK_MONOTONIC) < 0) {
|
||||
if (weston_compositor_set_presentation_clock(compositor, CLOCK_MONOTONIC) < 0) {
|
||||
weston_log("Error: failed to set presentation clock to CLOCK_MONOTONIC.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
|
||||
if (ret == 0)
|
||||
b->cursor_width = cap;
|
||||
device->cursor_width = cap;
|
||||
else
|
||||
b->cursor_width = 64;
|
||||
device->cursor_width = 64;
|
||||
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
|
||||
if (ret == 0)
|
||||
b->cursor_height = cap;
|
||||
device->cursor_height = cap;
|
||||
else
|
||||
b->cursor_height = 64;
|
||||
device->cursor_height = 64;
|
||||
|
||||
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||
if (ret) {
|
||||
weston_log("Error: drm card doesn't support universal planes!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!getenv("WESTON_DISABLE_ATOMIC")) {
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
|
||||
if (ret != 0)
|
||||
cap = 0;
|
||||
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
||||
b->atomic_modeset = ((ret == 0) && (cap == 1));
|
||||
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
||||
device->atomic_modeset = ((ret == 0) && (cap == 1));
|
||||
}
|
||||
weston_log("DRM: %s atomic modesetting\n",
|
||||
b->atomic_modeset ? "supports" : "does not support");
|
||||
device->atomic_modeset ? "supports" : "does not support");
|
||||
|
||||
if (!getenv("WESTON_DISABLE_GBM_MODIFIERS")) {
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
|
||||
if (ret == 0)
|
||||
b->fb_modifiers = cap;
|
||||
device->fb_modifiers = cap;
|
||||
}
|
||||
weston_log("DRM: %s GBM modifiers\n",
|
||||
b->fb_modifiers ? "supports" : "does not support");
|
||||
device->fb_modifiers ? "supports" : "does not support");
|
||||
|
||||
drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
|
||||
drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
|
||||
|
||||
/*
|
||||
* KMS support for hardware planes cannot properly synchronize
|
||||
@@ -1489,13 +1536,13 @@ init_kms_caps(struct drm_backend *b)
|
||||
* to a fraction. For cursors, it's not so bad, so they are
|
||||
* enabled.
|
||||
*/
|
||||
if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
|
||||
b->sprites_are_broken = true;
|
||||
if (!device->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
|
||||
device->sprites_are_broken = true;
|
||||
|
||||
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
|
||||
b->aspect_ratio_supported = (ret == 0);
|
||||
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
|
||||
device->aspect_ratio_supported = (ret == 0);
|
||||
weston_log("DRM: %s picture aspect ratio\n",
|
||||
b->aspect_ratio_supported ? "supports" : "does not support");
|
||||
device->aspect_ratio_supported ? "supports" : "does not support");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,10 @@ static long backlight_get(struct backlight *backlight, char *node)
|
||||
int fd, value;
|
||||
long ret;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
|
||||
str_printf(&path, "%s/%s", backlight->path, node);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -1;
|
||||
@@ -67,6 +69,9 @@ static long backlight_get(struct backlight *backlight, char *node)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buffer[ret - 1] == '\n')
|
||||
buffer[ret - 1] = '\0';
|
||||
|
||||
if (!safe_strtoint(buffer, &value)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@@ -103,7 +108,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness)
|
||||
int fd;
|
||||
long ret;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
|
||||
str_printf(&path, "%s/%s", backlight->path, "brightness");
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
@@ -118,7 +124,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (asprintf(&buffer, "%ld", brightness) < 0) {
|
||||
str_printf(&buffer, "%ld", brightness);
|
||||
if (!buffer) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
@@ -171,7 +178,8 @@ struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
if (!syspath)
|
||||
return NULL;
|
||||
|
||||
if (asprintf(&path, "%s/%s", syspath, "device") < 0)
|
||||
str_printf(&path, "%s/%s", syspath, "device");
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
ret = readlink(path, buffer, sizeof(buffer) - 1);
|
||||
@@ -214,11 +222,13 @@ struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
|
||||
entry->d_name) < 0)
|
||||
str_printf(&backlight_path, "%s/%s", "/sys/class/backlight",
|
||||
entry->d_name);
|
||||
if (!backlight_path)
|
||||
goto err;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
|
||||
str_printf(&path, "%s/%s", backlight_path, "type");
|
||||
if (!path) {
|
||||
free(backlight_path);
|
||||
goto err;
|
||||
}
|
||||
@@ -255,7 +265,8 @@ struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
|
||||
free (path);
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
|
||||
str_printf(&path, "%s/%s", backlight_path, "device");
|
||||
if (!path)
|
||||
goto err;
|
||||
|
||||
ret = readlink(path, buffer, sizeof(buffer) - 1);
|
||||
|
||||
@@ -24,6 +24,7 @@ srcs_drm = [
|
||||
'fb.c',
|
||||
'modes.c',
|
||||
'kms.c',
|
||||
'kms-color.c',
|
||||
'state-helpers.c',
|
||||
'state-propose.c',
|
||||
linux_dmabuf_unstable_v1_protocol_c,
|
||||
@@ -32,6 +33,7 @@ srcs_drm = [
|
||||
]
|
||||
|
||||
deps_drm = [
|
||||
dep_libm,
|
||||
dep_libdl,
|
||||
dep_libweston_private,
|
||||
dep_session_helper,
|
||||
|
||||
@@ -98,14 +98,15 @@ drm_subpixel_to_wayland(int drm_value)
|
||||
}
|
||||
|
||||
int
|
||||
drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode)
|
||||
drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode)
|
||||
{
|
||||
struct drm_backend *backend = device->backend;
|
||||
int ret;
|
||||
|
||||
if (mode->blob_id)
|
||||
return 0;
|
||||
|
||||
ret = drmModeCreatePropertyBlob(backend->drm.fd,
|
||||
ret = drmModeCreatePropertyBlob(device->drm.fd,
|
||||
&mode->mode_info,
|
||||
sizeof(mode->mode_info),
|
||||
&mode->blob_id);
|
||||
@@ -304,6 +305,8 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
|
||||
* \param[out] make The monitor make (PNP ID).
|
||||
* \param[out] model The monitor model (name).
|
||||
* \param[out] serial_number The monitor serial number.
|
||||
* \param[out] eotf_mask The monitor supported EOTF modes, combination of
|
||||
* enum weston_eotf_mode bits.
|
||||
*
|
||||
* Each of \c *make, \c *model and \c *serial_number are set only if the
|
||||
* information is found in the EDID. The pointers they are set to must not
|
||||
@@ -315,8 +318,10 @@ find_and_parse_output_edid(struct drm_head *head,
|
||||
drmModeObjectPropertiesPtr props,
|
||||
const char **make,
|
||||
const char **model,
|
||||
const char **serial_number)
|
||||
const char **serial_number,
|
||||
uint32_t *eotf_mask)
|
||||
{
|
||||
struct drm_device *device = head->connector.device;
|
||||
drmModePropertyBlobPtr edid_blob = NULL;
|
||||
uint32_t blob_id;
|
||||
int rc;
|
||||
@@ -328,7 +333,7 @@ find_and_parse_output_edid(struct drm_head *head,
|
||||
if (!blob_id)
|
||||
return;
|
||||
|
||||
edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id);
|
||||
edid_blob = drmModeGetPropertyBlob(device->drm.fd, blob_id);
|
||||
if (!edid_blob)
|
||||
return;
|
||||
|
||||
@@ -344,6 +349,21 @@ find_and_parse_output_edid(struct drm_head *head,
|
||||
*serial_number = head->edid.serial_number;
|
||||
}
|
||||
drmModeFreePropertyBlob(edid_blob);
|
||||
|
||||
/* TODO: parse this from EDID */
|
||||
*eotf_mask = WESTON_EOTF_MODE_ALL_MASK;
|
||||
}
|
||||
|
||||
static void
|
||||
prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask)
|
||||
{
|
||||
const struct drm_property_info *info;
|
||||
|
||||
/* Without the KMS property, cannot do anything but SDR. */
|
||||
|
||||
info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA];
|
||||
if (!head->connector.device->atomic_modeset || info->prop_id == 0)
|
||||
*eotf_mask = WESTON_EOTF_MODE_SDR;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
@@ -406,26 +426,26 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
* Destroys a mode, and removes it from the list.
|
||||
*/
|
||||
static void
|
||||
drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode)
|
||||
drm_output_destroy_mode(struct drm_device *device, struct drm_mode *mode)
|
||||
{
|
||||
if (mode->blob_id)
|
||||
drmModeDestroyPropertyBlob(backend->drm.fd, mode->blob_id);
|
||||
drmModeDestroyPropertyBlob(device->drm.fd, mode->blob_id);
|
||||
wl_list_remove(&mode->base.link);
|
||||
free(mode);
|
||||
}
|
||||
|
||||
/** Destroy a list of drm_modes
|
||||
*
|
||||
* @param backend The backend for releasing mode property blobs.
|
||||
* @param device The device for releasing mode property blobs.
|
||||
* @param mode_list The list linked by drm_mode::base.link.
|
||||
*/
|
||||
void
|
||||
drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list)
|
||||
drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list)
|
||||
{
|
||||
struct drm_mode *mode, *next;
|
||||
|
||||
wl_list_for_each_safe(mode, next, mode_list, base.link)
|
||||
drm_output_destroy_mode(backend, mode);
|
||||
drm_output_destroy_mode(device, mode);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -469,16 +489,16 @@ drm_output_choose_mode(struct drm_output *output,
|
||||
struct drm_mode *tmp_mode = NULL, *mode_fall_back = NULL, *mode;
|
||||
enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE;
|
||||
enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE;
|
||||
struct drm_backend *b;
|
||||
struct drm_device *device;
|
||||
|
||||
b = to_drm_backend(output->base.compositor);
|
||||
device = output->device;
|
||||
target_aspect = target_mode->aspect_ratio;
|
||||
src_aspect = output->base.current_mode->aspect_ratio;
|
||||
if (output->base.current_mode->width == target_mode->width &&
|
||||
output->base.current_mode->height == target_mode->height &&
|
||||
(output->base.current_mode->refresh == target_mode->refresh ||
|
||||
target_mode->refresh == 0)) {
|
||||
if (!b->aspect_ratio_supported || src_aspect == target_aspect)
|
||||
if (!device->aspect_ratio_supported || src_aspect == target_aspect)
|
||||
return to_drm_mode(output->base.current_mode);
|
||||
}
|
||||
|
||||
@@ -489,7 +509,7 @@ drm_output_choose_mode(struct drm_output *output,
|
||||
mode->mode_info.vdisplay == target_mode->height) {
|
||||
if (mode->base.refresh == target_mode->refresh ||
|
||||
target_mode->refresh == 0) {
|
||||
if (!b->aspect_ratio_supported ||
|
||||
if (!device->aspect_ratio_supported ||
|
||||
src_aspect == target_aspect)
|
||||
return mode;
|
||||
else if (!mode_fall_back)
|
||||
@@ -515,9 +535,12 @@ update_head_from_connector(struct drm_head *head)
|
||||
const char *make = "unknown";
|
||||
const char *model = "unknown";
|
||||
const char *serial_number = "unknown";
|
||||
uint32_t eotf_mask = WESTON_EOTF_MODE_SDR;
|
||||
|
||||
find_and_parse_output_edid(head, props, &make, &model, &serial_number);
|
||||
find_and_parse_output_edid(head, props, &make, &model, &serial_number, &eotf_mask);
|
||||
weston_head_set_monitor_strings(&head->base, make, model, serial_number);
|
||||
prune_eotf_modes_by_kms_support(head, &eotf_mask);
|
||||
weston_head_set_supported_eotf_mask(&head->base, eotf_mask);
|
||||
weston_head_set_non_desktop(&head->base,
|
||||
check_non_desktop(connector, props));
|
||||
weston_head_set_subpixel(&head->base,
|
||||
@@ -539,7 +562,7 @@ update_head_from_connector(struct drm_head *head)
|
||||
* Find the most suitable mode to use for initial setup (or reconfiguration on
|
||||
* hotplug etc) for a DRM output.
|
||||
*
|
||||
* @param backend the DRM backend
|
||||
* @param device the DRM device
|
||||
* @param output DRM output to choose mode for
|
||||
* @param mode Strategy and preference to use when choosing mode
|
||||
* @param modeline Manually-entered mode (may be NULL)
|
||||
@@ -547,7 +570,7 @@ update_head_from_connector(struct drm_head *head)
|
||||
* @returns A mode from the output's mode list, or NULL if none available
|
||||
*/
|
||||
static struct drm_mode *
|
||||
drm_output_choose_initial_mode(struct drm_backend *backend,
|
||||
drm_output_choose_initial_mode(struct drm_device *device,
|
||||
struct drm_output *output,
|
||||
enum weston_drm_backend_output_mode mode,
|
||||
const char *modeline,
|
||||
@@ -571,7 +594,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
|
||||
if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && modeline) {
|
||||
n = sscanf(modeline, "%dx%d@%d %u:%u", &width, &height,
|
||||
&refresh, &aspect_width, &aspect_height);
|
||||
if (backend->aspect_ratio_supported && n == 5) {
|
||||
if (device->aspect_ratio_supported && n == 5) {
|
||||
if (aspect_width == 4 && aspect_height == 3)
|
||||
aspect_ratio = WESTON_MODE_PIC_AR_4_3;
|
||||
else if (aspect_width == 16 && aspect_height == 9)
|
||||
@@ -602,7 +625,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
|
||||
if (width == drm_mode->base.width &&
|
||||
height == drm_mode->base.height &&
|
||||
(refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) {
|
||||
if (!backend->aspect_ratio_supported ||
|
||||
if (!device->aspect_ratio_supported ||
|
||||
aspect_ratio == drm_mode->base.aspect_ratio)
|
||||
configured = drm_mode;
|
||||
else
|
||||
@@ -714,7 +737,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
{
|
||||
struct weston_mode *base;
|
||||
struct drm_mode *mode = NULL;
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device = output->device;
|
||||
const drmModeModeInfo *chosen = NULL;
|
||||
|
||||
assert(info);
|
||||
@@ -728,8 +751,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
|
||||
if (chosen == info) {
|
||||
assert(mode);
|
||||
backend = to_drm_backend(output->base.compositor);
|
||||
drm_output_destroy_mode(backend, mode);
|
||||
drm_output_destroy_mode(device, mode);
|
||||
chosen = NULL;
|
||||
}
|
||||
|
||||
@@ -756,7 +778,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
static int
|
||||
drm_output_update_modelist_from_heads(struct drm_output *output)
|
||||
{
|
||||
struct drm_backend *backend = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct weston_head *head_base;
|
||||
struct drm_head *head;
|
||||
drmModeConnector *conn;
|
||||
@@ -765,7 +787,7 @@ drm_output_update_modelist_from_heads(struct drm_output *output)
|
||||
|
||||
assert(!output->base.enabled);
|
||||
|
||||
drm_mode_list_destroy(backend, &output->base.mode_list);
|
||||
drm_mode_list_destroy(device, &output->base.mode_list);
|
||||
|
||||
wl_list_for_each(head_base, &output->base.head_list, output_link) {
|
||||
head = to_drm_head(head_base);
|
||||
@@ -786,7 +808,7 @@ drm_output_set_mode(struct weston_output *base,
|
||||
const char *modeline)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
struct drm_backend *b = to_drm_backend(base->compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
|
||||
|
||||
struct drm_mode *current;
|
||||
@@ -797,7 +819,7 @@ drm_output_set_mode(struct weston_output *base,
|
||||
if (drm_output_update_modelist_from_heads(output) < 0)
|
||||
return -1;
|
||||
|
||||
current = drm_output_choose_initial_mode(b, output, mode, modeline,
|
||||
current = drm_output_choose_initial_mode(device, output, mode, modeline,
|
||||
&head->inherited_mode);
|
||||
if (!current)
|
||||
return -1;
|
||||
|
||||
@@ -73,6 +73,8 @@ drm_plane_state_alloc(struct drm_output_state *state_output,
|
||||
void
|
||||
drm_plane_state_free(struct drm_plane_state *state, bool force)
|
||||
{
|
||||
struct drm_device *device;
|
||||
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
@@ -86,14 +88,17 @@ drm_plane_state_free(struct drm_plane_state *state, bool force)
|
||||
* by the kernel, which means we can safely discard it.
|
||||
*/
|
||||
if (state->damage_blob_id != 0) {
|
||||
drmModeDestroyPropertyBlob(state->plane->backend->drm.fd,
|
||||
device = state->plane->device;
|
||||
|
||||
drmModeDestroyPropertyBlob(device->drm.fd,
|
||||
state->damage_blob_id);
|
||||
state->damage_blob_id = 0;
|
||||
}
|
||||
|
||||
if (force || state != state->plane->state_cur) {
|
||||
drm_fb_unref(state->fb);
|
||||
weston_buffer_reference(&state->fb_ref.buffer, NULL);
|
||||
weston_buffer_reference(&state->fb_ref.buffer, NULL,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&state->fb_ref.release, NULL);
|
||||
free(state);
|
||||
}
|
||||
@@ -135,10 +140,20 @@ drm_plane_state_duplicate(struct drm_output_state *state_output,
|
||||
* buffer, then we must also transfer the reference on the client
|
||||
* buffer. */
|
||||
if (src->fb) {
|
||||
struct weston_buffer *buffer;
|
||||
|
||||
dst->fb = drm_fb_ref(src->fb);
|
||||
memset(&dst->fb_ref, 0, sizeof(dst->fb_ref));
|
||||
weston_buffer_reference(&dst->fb_ref.buffer,
|
||||
src->fb_ref.buffer.buffer);
|
||||
|
||||
if (src->fb->type == BUFFER_CLIENT ||
|
||||
src->fb->type == BUFFER_DMABUF) {
|
||||
buffer = src->fb_ref.buffer.buffer;
|
||||
} else {
|
||||
buffer = NULL;
|
||||
}
|
||||
weston_buffer_reference(&dst->fb_ref.buffer, buffer,
|
||||
buffer ? BUFFER_MAY_BE_ACCESSED :
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&dst->fb_ref.release,
|
||||
src->fb_ref.release.buffer_release);
|
||||
} else {
|
||||
@@ -421,11 +436,11 @@ drm_output_state_free(struct drm_output_state *state)
|
||||
* Allocate a new, empty, 'pending state' structure to be used across a
|
||||
* repaint cycle or similar.
|
||||
*
|
||||
* @param backend DRM backend
|
||||
* @param device DRM device
|
||||
* @returns Newly-allocated pending state structure
|
||||
*/
|
||||
struct drm_pending_state *
|
||||
drm_pending_state_alloc(struct drm_backend *backend)
|
||||
drm_pending_state_alloc(struct drm_device *device)
|
||||
{
|
||||
struct drm_pending_state *ret;
|
||||
|
||||
@@ -433,7 +448,7 @@ drm_pending_state_alloc(struct drm_backend *backend)
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->backend = backend;
|
||||
ret->device = device;
|
||||
wl_list_init(&ret->output_list);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "color.h"
|
||||
#include "linux-dmabuf.h"
|
||||
#include "presentation-time-server-protocol.h"
|
||||
#include "linux-dmabuf-unstable-v1-server-protocol.h"
|
||||
|
||||
enum drm_output_propose_state_mode {
|
||||
DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
|
||||
@@ -63,55 +64,6 @@ drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
|
||||
return drm_output_propose_state_mode_as_string[mode];
|
||||
}
|
||||
|
||||
static void
|
||||
drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
|
||||
{
|
||||
struct drm_backend *b = plane->backend;
|
||||
struct drm_plane_zpos *h_plane;
|
||||
struct drm_plane_zpos *plane_zpos;
|
||||
|
||||
plane_zpos = zalloc(sizeof(*plane_zpos));
|
||||
if (!plane_zpos)
|
||||
return;
|
||||
|
||||
plane_zpos->plane = plane;
|
||||
|
||||
drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
|
||||
plane->plane_id);
|
||||
|
||||
if (wl_list_empty(planes)) {
|
||||
wl_list_insert(planes, &plane_zpos->link);
|
||||
return;
|
||||
}
|
||||
|
||||
h_plane = wl_container_of(planes->next, h_plane, link);
|
||||
if (h_plane->plane->zpos_max >= plane->zpos_max) {
|
||||
wl_list_insert(planes->prev, &plane_zpos->link);
|
||||
} else {
|
||||
struct drm_plane_zpos *p_zpos = NULL;
|
||||
|
||||
if (wl_list_length(planes) == 1) {
|
||||
wl_list_insert(planes->prev, &plane_zpos->link);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_for_each(p_zpos, planes, link) {
|
||||
if (p_zpos->plane->zpos_max >
|
||||
plane_zpos->plane->zpos_max)
|
||||
break;
|
||||
}
|
||||
|
||||
wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
|
||||
{
|
||||
wl_list_remove(&plane_zpos->link);
|
||||
free(plane_zpos);
|
||||
}
|
||||
|
||||
static bool
|
||||
drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
|
||||
struct drm_output_state *output_state)
|
||||
@@ -125,87 +77,79 @@ drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_prepare_overlay_view(struct drm_plane *plane,
|
||||
struct drm_output_state *output_state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
drm_output_try_view_on_plane(struct drm_plane *plane,
|
||||
struct drm_output_state *output_state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct drm_backend *b = to_drm_backend(ec);
|
||||
struct weston_surface *surface = ev->surface;
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane_state *state = NULL;
|
||||
int ret;
|
||||
|
||||
assert(!b->sprites_are_broken);
|
||||
assert(b->atomic_modeset);
|
||||
|
||||
if (!fb) {
|
||||
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
|
||||
" couldn't get fb\n", ev);
|
||||
return NULL;
|
||||
}
|
||||
assert(!device->sprites_are_broken);
|
||||
assert(device->atomic_modeset);
|
||||
assert(fb);
|
||||
assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY ||
|
||||
(mode == DRM_OUTPUT_PROPOSE_STATE_MIXED &&
|
||||
plane->type == WDRM_PLANE_TYPE_OVERLAY));
|
||||
|
||||
state = drm_output_state_get_plane(output_state, plane);
|
||||
/* we can't have a 'pending' framebuffer as never set one before reaching here */
|
||||
assert(!state->fb);
|
||||
|
||||
state->ev = ev;
|
||||
state->output = output;
|
||||
|
||||
if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
|
||||
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
|
||||
drm_debug(b, "\t\t\t\t[view] not placing view %p on plane: "
|
||||
"unsuitable transform\n", ev);
|
||||
drm_plane_state_put_back(state);
|
||||
state = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the surface buffer has an in-fence fd, but the plane
|
||||
* doesn't support fences, we can't place the buffer on this
|
||||
* plane. */
|
||||
if (ev->surface->acquire_fence_fd >= 0 &&
|
||||
plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
|
||||
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
|
||||
"no in-fence support\n", ev);
|
||||
drm_plane_state_put_back(state);
|
||||
state = NULL;
|
||||
goto out;
|
||||
/* Should've been ensured by weston_view_matches_entire_output. */
|
||||
if (plane->type == WDRM_PLANE_TYPE_PRIMARY) {
|
||||
assert(state->dest_x == 0 && state->dest_y == 0 &&
|
||||
state->dest_w == (unsigned) output->base.current_mode->width &&
|
||||
state->dest_h == (unsigned) output->base.current_mode->height);
|
||||
}
|
||||
|
||||
/* We hold one reference for the lifetime of this function; from
|
||||
* calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
|
||||
* so, we take another reference here to live within the state. */
|
||||
state->ev = ev;
|
||||
state->fb = drm_fb_ref(fb);
|
||||
|
||||
state->in_fence_fd = ev->surface->acquire_fence_fd;
|
||||
|
||||
/* In planes-only mode, we don't have an incremental state to
|
||||
* test against, so we just hope it'll work. */
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
|
||||
drm_debug(b, "\t\t\t[overlay] provisionally placing "
|
||||
"view %p on overlay %lu in planes-only mode\n",
|
||||
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY &&
|
||||
drm_pending_state_test(output_state->pending_state) != 0) {
|
||||
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
|
||||
"atomic test failed\n",
|
||||
ev, (unsigned long) plane->plane_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drm_pending_state_test(output_state->pending_state);
|
||||
if (ret == 0) {
|
||||
drm_debug(b, "\t\t\t[overlay] provisionally placing "
|
||||
"view %p on overlay %d in mixed mode\n",
|
||||
ev, plane->plane_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
|
||||
"in mixed mode: kernel test failed\n",
|
||||
drm_debug(b, "\t\t\t[view] provisionally placing view %p on plane %lu\n",
|
||||
ev, (unsigned long) plane->plane_id);
|
||||
|
||||
drm_plane_state_put_back(state);
|
||||
state = NULL;
|
||||
/* Take a reference on the buffer so that we don't release it
|
||||
* back to the client until we're done with it; cursor buffers
|
||||
* don't require a reference since we copy them. */
|
||||
assert(state->fb_ref.buffer.buffer == NULL);
|
||||
assert(state->fb_ref.release.buffer_release == NULL);
|
||||
weston_buffer_reference(&state->fb_ref.buffer,
|
||||
surface->buffer_ref.buffer,
|
||||
BUFFER_MAY_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&state->fb_ref.release,
|
||||
surface->buffer_release_ref.buffer_release);
|
||||
|
||||
return state;
|
||||
|
||||
out:
|
||||
return state;
|
||||
drm_plane_state_put_back(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef BUILD_DRM_GBM
|
||||
@@ -218,18 +162,18 @@ out:
|
||||
static void
|
||||
cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
|
||||
{
|
||||
struct drm_backend *b = plane_state->plane->backend;
|
||||
struct drm_output *output = plane_state->output;
|
||||
struct drm_device *device = output->device;
|
||||
struct gbm_bo *bo = plane_state->fb->bo;
|
||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||
uint32_t buf[b->cursor_width * b->cursor_height];
|
||||
uint32_t buf[device->cursor_width * device->cursor_height];
|
||||
int32_t stride;
|
||||
uint8_t *s;
|
||||
int i;
|
||||
|
||||
assert(buffer && buffer->shm_buffer);
|
||||
assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
|
||||
assert(buffer->width <= b->cursor_width);
|
||||
assert(buffer->height <= b->cursor_height);
|
||||
assert(buffer->width <= device->cursor_width);
|
||||
assert(buffer->height <= device->cursor_height);
|
||||
|
||||
memset(buf, 0, sizeof buf);
|
||||
stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
|
||||
@@ -237,7 +181,7 @@ cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
|
||||
|
||||
wl_shm_buffer_begin_access(buffer->shm_buffer);
|
||||
for (i = 0; i < buffer->height; i++)
|
||||
memcpy(buf + i * b->cursor_width,
|
||||
memcpy(buf + i * device->cursor_width,
|
||||
s + i * stride,
|
||||
buffer->width * 4);
|
||||
wl_shm_buffer_end_access(buffer->shm_buffer);
|
||||
@@ -251,32 +195,23 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
struct weston_view *ev, uint64_t zpos)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane *plane = output->cursor_plane;
|
||||
struct drm_plane_state *plane_state;
|
||||
bool needs_update = false;
|
||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
|
||||
assert(!b->cursors_are_broken);
|
||||
|
||||
if (!plane)
|
||||
return NULL;
|
||||
|
||||
if (!plane->state_cur->complete)
|
||||
return NULL;
|
||||
|
||||
if (plane->state_cur->output && plane->state_cur->output != output)
|
||||
return NULL;
|
||||
assert(!device->cursors_are_broken);
|
||||
assert(plane);
|
||||
assert(plane->state_cur->complete);
|
||||
assert(!plane->state_cur->output || plane->state_cur->output == output);
|
||||
|
||||
/* We use GBM to import SHM buffers. */
|
||||
if (b->gbm == NULL)
|
||||
return NULL;
|
||||
assert(b->gbm);
|
||||
|
||||
plane_state = drm_output_state_get_plane(output_state, plane);
|
||||
|
||||
if (plane_state && plane_state->fb)
|
||||
return NULL;
|
||||
assert(!plane_state->fb);
|
||||
|
||||
/* We can't scale with the legacy API, and we don't try to account for
|
||||
* simple cropping/translation in cursor_bo_update. */
|
||||
@@ -287,19 +222,9 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (buffer->width > b->cursor_width ||
|
||||
buffer->height > b->cursor_height) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
|
||||
"(surface buffer (%dx%d) larger than permitted"
|
||||
" (%dx%d))\n", p_name, ev, p_name,
|
||||
buffer->width, buffer->height,
|
||||
b->cursor_width, b->cursor_height);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
|
||||
plane_state->src_w > (unsigned) b->cursor_width << 16 ||
|
||||
plane_state->src_h > (unsigned) b->cursor_height << 16 ||
|
||||
plane_state->src_w > (unsigned) device->cursor_width << 16 ||
|
||||
plane_state->src_h > (unsigned) device->cursor_height << 16 ||
|
||||
plane_state->src_w != plane_state->dest_w << 16 ||
|
||||
plane_state->src_h != plane_state->dest_h << 16) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
|
||||
@@ -338,10 +263,10 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
* a buffer which is always cursor_width x cursor_height, even if the
|
||||
* surface we want to promote is actually smaller than this. Manually
|
||||
* mangle the plane state to deal with this. */
|
||||
plane_state->src_w = b->cursor_width << 16;
|
||||
plane_state->src_h = b->cursor_height << 16;
|
||||
plane_state->dest_w = b->cursor_width;
|
||||
plane_state->dest_h = b->cursor_height;
|
||||
plane_state->src_w = device->cursor_width << 16;
|
||||
plane_state->src_h = device->cursor_height << 16;
|
||||
plane_state->dest_w = device->cursor_width;
|
||||
plane_state->dest_h = device->cursor_height;
|
||||
|
||||
drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
|
||||
p_name, ev);
|
||||
@@ -361,190 +286,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_plane_state *state;
|
||||
const char *p_name = drm_output_get_plane_type_name(scanout_plane);
|
||||
|
||||
assert(!b->sprites_are_broken);
|
||||
assert(b->atomic_modeset);
|
||||
assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
|
||||
|
||||
/* Check the view spans exactly the output size, calculated in the
|
||||
* logical co-ordinate space. */
|
||||
if (!weston_view_matches_output_entirely(ev, &output->base)) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
" view does not match output entirely\n",
|
||||
p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If the surface buffer has an in-fence fd, but the plane doesn't
|
||||
* support fences, we can't place the buffer on this plane. */
|
||||
if (ev->surface->acquire_fence_fd >= 0 &&
|
||||
scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
"no in-fence support\n", p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!fb) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
" couldn't get fb\n", p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = drm_output_state_get_plane(output_state, scanout_plane);
|
||||
|
||||
/* The only way we can already have a buffer in the scanout plane is
|
||||
* if we are in mixed mode, or if a client buffer has already been
|
||||
* placed into scanout. The former case will never call into here,
|
||||
* and in the latter case, the view must have been marked as occluded,
|
||||
* meaning we should never have ended up here. */
|
||||
assert(!state->fb);
|
||||
|
||||
/* take another reference here to live within the state */
|
||||
state->fb = drm_fb_ref(fb);
|
||||
state->ev = ev;
|
||||
state->output = output;
|
||||
if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
"unsuitable transform\n", p_name, ev, p_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (state->dest_x != 0 || state->dest_y != 0 ||
|
||||
state->dest_w != (unsigned) output->base.current_mode->width ||
|
||||
state->dest_h != (unsigned) output->base.current_mode->height) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
" invalid plane state\n", p_name, ev, p_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
state->in_fence_fd = ev->surface->acquire_fence_fd;
|
||||
|
||||
/* In plane-only mode, we don't need to test the state now, as we
|
||||
* will only test it once at the end. */
|
||||
return state;
|
||||
|
||||
err:
|
||||
drm_plane_state_put_back(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_try_view_on_plane(struct drm_plane *plane,
|
||||
struct drm_output_state *state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
{
|
||||
struct drm_backend *b = state->pending_state->backend;
|
||||
struct weston_output *wet_output = &state->output->base;
|
||||
bool view_matches_entire_output, scanout_has_view_assigned;
|
||||
struct drm_plane *scanout_plane = state->output->scanout_plane;
|
||||
struct drm_plane_state *ps = NULL;
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
struct weston_surface *surface = ev->surface;
|
||||
enum {
|
||||
NO_PLANES, /* generic err-handle */
|
||||
NO_PLANES_ACCEPTED,
|
||||
PLACED_ON_PLANE,
|
||||
} availability = NO_PLANES;
|
||||
|
||||
/* sanity checks in case we over/underflow zpos or pass incorrect
|
||||
* values */
|
||||
assert(zpos <= plane->zpos_max ||
|
||||
zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
|
||||
|
||||
switch (plane->type) {
|
||||
case WDRM_PLANE_TYPE_CURSOR:
|
||||
if (b->cursors_are_broken) {
|
||||
availability = NO_PLANES_ACCEPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps = drm_output_prepare_cursor_view(state, ev, zpos);
|
||||
if (ps)
|
||||
availability = PLACED_ON_PLANE;
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_OVERLAY:
|
||||
/* do not attempt to place it in the overlay if we don't have
|
||||
* anything in the scanout/primary and the view doesn't cover
|
||||
* the entire output */
|
||||
view_matches_entire_output =
|
||||
weston_view_matches_output_entirely(ev, wet_output);
|
||||
scanout_has_view_assigned =
|
||||
drm_output_check_plane_has_view_assigned(scanout_plane,
|
||||
state);
|
||||
|
||||
if (view_matches_entire_output && !scanout_has_view_assigned) {
|
||||
availability = NO_PLANES_ACCEPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
|
||||
fb, zpos);
|
||||
if (ps)
|
||||
availability = PLACED_ON_PLANE;
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_PRIMARY:
|
||||
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
|
||||
availability = NO_PLANES_ACCEPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps = drm_output_prepare_scanout_view(state, ev, mode,
|
||||
fb, zpos);
|
||||
if (ps)
|
||||
availability = PLACED_ON_PLANE;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
switch (availability) {
|
||||
case NO_PLANES:
|
||||
/* set initial to this catch-all case, such that
|
||||
* prepare_cursor/overlay/scanout() should have/contain the
|
||||
* reason for failling */
|
||||
break;
|
||||
case NO_PLANES_ACCEPTED:
|
||||
drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
|
||||
"place view %p in %s\n",
|
||||
plane->plane_id, ev, p_name);
|
||||
break;
|
||||
case PLACED_ON_PLANE:
|
||||
/* Take a reference on the buffer so that we don't release it
|
||||
* back to the client until we're done with it; cursor buffers
|
||||
* don't require a reference since we copy them. */
|
||||
assert(ps->fb_ref.buffer.buffer == NULL);
|
||||
assert(ps->fb_ref.release.buffer_release == NULL);
|
||||
if (ps->plane->type == WDRM_PLANE_TYPE_CURSOR) {
|
||||
assert(ps->fb->type == BUFFER_CURSOR);
|
||||
} else if (fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF) {
|
||||
assert(ps->fb == fb);
|
||||
weston_buffer_reference(&ps->fb_ref.buffer,
|
||||
surface->buffer_ref.buffer);
|
||||
weston_buffer_release_reference(&ps->fb_ref.release,
|
||||
surface->buffer_release_ref.buffer_release);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
static void
|
||||
drm_output_check_zpos_plane_states(struct drm_output_state *state)
|
||||
{
|
||||
@@ -585,12 +326,13 @@ drm_output_check_zpos_plane_states(struct drm_output_state *state)
|
||||
}
|
||||
|
||||
static bool
|
||||
dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
|
||||
dmabuf_feedback_maybe_update(struct drm_device *device, struct weston_view *ev,
|
||||
uint32_t try_view_on_plane_failure_reasons)
|
||||
{
|
||||
struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback;
|
||||
struct weston_dmabuf_feedback_tranche *scanout_tranche;
|
||||
dev_t scanout_dev = b->drm.devnum;
|
||||
struct drm_backend *b = device->backend;
|
||||
dev_t scanout_dev = device->drm.devnum;
|
||||
uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT;
|
||||
uint32_t action_needed = ACTION_NEEDED_NONE;
|
||||
struct timespec current_time, delta_time;
|
||||
@@ -603,7 +345,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
|
||||
(FAILURE_REASONS_ADD_FB_FAILED |
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE |
|
||||
FAILURE_REASONS_DMABUF_MODIFIER_INVALID |
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED))
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED |
|
||||
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED))
|
||||
action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE;
|
||||
|
||||
assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE |
|
||||
@@ -681,58 +424,161 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_prepare_plane_view(struct drm_output_state *state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_plane_state *scanout_state,
|
||||
uint64_t current_lowest_zpos,
|
||||
uint32_t *try_view_on_plane_failure_reasons)
|
||||
drm_output_find_plane_for_view(struct drm_output_state *state,
|
||||
struct weston_paint_node *pnode,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_plane_state *scanout_state,
|
||||
uint64_t current_lowest_zpos)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
|
||||
struct drm_plane_state *ps = NULL;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_zpos *p_zpos, *p_zpos_next;
|
||||
struct wl_list zpos_candidate_list;
|
||||
|
||||
struct weston_view *ev = pnode->view;
|
||||
struct weston_buffer *buffer;
|
||||
struct wl_shm_buffer *shmbuf;
|
||||
struct drm_fb *fb;
|
||||
struct drm_fb *fb = NULL;
|
||||
|
||||
wl_list_init(&zpos_candidate_list);
|
||||
bool view_matches_entire_output, scanout_has_view_assigned;
|
||||
uint32_t possible_plane_mask = 0;
|
||||
|
||||
pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
|
||||
|
||||
/* check view for valid buffer, doesn't make sense to even try */
|
||||
if (!weston_view_has_valid_buffer(ev))
|
||||
return ps;
|
||||
if (!weston_view_has_valid_buffer(ev)) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = ev->surface->buffer_ref.buffer;
|
||||
shmbuf = wl_shm_buffer_get(buffer->resource);
|
||||
fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons);
|
||||
if (!shmbuf && !fb)
|
||||
if (buffer->type == WESTON_BUFFER_SOLID) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
} else if (buffer->type == WESTON_BUFFER_SHM) {
|
||||
if (!output->cursor_plane || device->cursors_are_broken) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Even though this is a SHM buffer, pixel_format stores the
|
||||
* format code as DRM FourCC */
|
||||
if (buffer->pixel_format->format != DRM_FORMAT_ARGB8888) {
|
||||
drm_debug(b, "\t\t\t\t[view] not placing view %p on "
|
||||
"plane; SHM buffers must be ARGB8888 for "
|
||||
"cursor view\n", ev);
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buffer->width > device->cursor_width ||
|
||||
buffer->height > device->cursor_height) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(buffer (%dx%d) too large for cursor plane)\n",
|
||||
ev, buffer->width, buffer->height);
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
possible_plane_mask = (1 << output->cursor_plane->plane_idx);
|
||||
} else {
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p "
|
||||
"to plane: renderer-only mode\n", ev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fb = drm_fb_get_from_view(state, ev,
|
||||
&pnode->try_view_on_plane_failure_reasons);
|
||||
if (!fb) {
|
||||
drm_debug(b, "\t\t\t[view] couldn't get FB for view: 0x%lx\n",
|
||||
(unsigned long) pnode->try_view_on_plane_failure_reasons);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
possible_plane_mask = fb->plane_mask;
|
||||
}
|
||||
|
||||
view_matches_entire_output =
|
||||
weston_view_matches_output_entirely(ev, &output->base);
|
||||
scanout_has_view_assigned =
|
||||
drm_output_check_plane_has_view_assigned(output->scanout_plane,
|
||||
state);
|
||||
|
||||
/* assemble a list with possible candidates */
|
||||
wl_list_for_each(plane, &b->plane_list, link) {
|
||||
wl_list_for_each(plane, &device->plane_list, link) {
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
uint64_t zpos;
|
||||
|
||||
if (possible_plane_mask == 0)
|
||||
break;
|
||||
|
||||
if (!(possible_plane_mask & (1 << plane->plane_idx)))
|
||||
continue;
|
||||
|
||||
possible_plane_mask &= ~(1 << plane->plane_idx);
|
||||
|
||||
switch (plane->type) {
|
||||
case WDRM_PLANE_TYPE_CURSOR:
|
||||
assert(buffer->shm_buffer);
|
||||
assert(plane == output->cursor_plane);
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_PRIMARY:
|
||||
assert(fb);
|
||||
if (plane != output->scanout_plane)
|
||||
continue;
|
||||
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
|
||||
continue;
|
||||
if (!view_matches_entire_output)
|
||||
continue;
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_OVERLAY:
|
||||
assert(fb);
|
||||
assert(mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
|
||||
/* if the view covers the whole output, put it in the
|
||||
* scanout plane, not overlay */
|
||||
if (view_matches_entire_output &&
|
||||
!scanout_has_view_assigned)
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
assert(false && "unknown plane type");
|
||||
}
|
||||
|
||||
if (!drm_plane_is_available(plane, output))
|
||||
continue;
|
||||
|
||||
if (drm_output_check_plane_has_view_assigned(plane, state)) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
|
||||
" candidate list: view already assigned "
|
||||
"to a plane\n", plane->plane_id);
|
||||
drm_debug(b, "\t\t\t\t[plane] not trying plane %d: "
|
||||
"another view already assigned\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plane->zpos_min >= current_lowest_zpos) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
|
||||
"candidate list: minimum zpos (%"PRIu64") "
|
||||
"plane's above current lowest zpos "
|
||||
"(%"PRIu64")\n", plane->plane_id,
|
||||
plane->zpos_min, current_lowest_zpos);
|
||||
drm_debug(b, "\t\t\t\t[plane] not trying plane %d: "
|
||||
"plane's minimum zpos (%"PRIu64") above "
|
||||
"current lowest zpos (%"PRIu64")\n",
|
||||
plane->plane_id, plane->zpos_min,
|
||||
current_lowest_zpos);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the surface buffer has an in-fence fd, but the plane doesn't
|
||||
* support fences, we can't place the buffer on this plane. */
|
||||
if (ev->surface->acquire_fence_fd >= 0 &&
|
||||
plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
"no in-fence support\n", p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
|
||||
assert(scanout_state != NULL);
|
||||
if (scanout_state->zpos >= plane->zpos_max) {
|
||||
@@ -746,49 +592,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
|
||||
(plane->type == WDRM_PLANE_TYPE_OVERLAY ||
|
||||
plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
|
||||
"candidate list: renderer-only mode\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plane->type == WDRM_PLANE_TYPE_CURSOR &&
|
||||
(!shmbuf || wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
|
||||
"candidate list: cursor planes only support ARGB8888"
|
||||
"wl_shm buffers and the view buffer is of another type\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plane->type != WDRM_PLANE_TYPE_CURSOR &&
|
||||
(!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
|
||||
"candidate list: invalid pixel format\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
drm_output_add_zpos_plane(plane, &zpos_candidate_list);
|
||||
}
|
||||
|
||||
/* go over the potential candidate list and try to find a possible
|
||||
* plane suitable for \c ev; start with the highest zpos value of a
|
||||
* plane to maximize our chances, but do note we pass the zpos value
|
||||
* based on current tracked value by \c current_lowest_zpos_in_use */
|
||||
while (!wl_list_empty(&zpos_candidate_list)) {
|
||||
struct drm_plane_zpos *head_p_zpos =
|
||||
wl_container_of(zpos_candidate_list.next,
|
||||
head_p_zpos, link);
|
||||
struct drm_plane *plane = head_p_zpos->plane;
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
uint64_t zpos;
|
||||
|
||||
if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
|
||||
zpos = plane->zpos_max;
|
||||
else
|
||||
@@ -798,20 +601,32 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
|
||||
"from candidate list, type: %s\n",
|
||||
plane->plane_id, p_name);
|
||||
|
||||
ps = drm_output_try_view_on_plane(plane, state, ev,
|
||||
mode, fb, zpos);
|
||||
drm_output_destroy_zpos_plane(head_p_zpos);
|
||||
if (plane->type == WDRM_PLANE_TYPE_CURSOR) {
|
||||
ps = drm_output_prepare_cursor_view(state, ev, zpos);
|
||||
} else {
|
||||
ps = drm_output_try_view_on_plane(plane, state, ev,
|
||||
mode, fb, zpos);
|
||||
}
|
||||
|
||||
if (ps) {
|
||||
drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
|
||||
"%s plane with computed zpos %"PRIu64"\n",
|
||||
ev, p_name, zpos);
|
||||
break;
|
||||
}
|
||||
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_PLANES_REJECTED;
|
||||
}
|
||||
|
||||
wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
|
||||
drm_output_destroy_zpos_plane(p_zpos);
|
||||
if (!ps &&
|
||||
pnode->try_view_on_plane_failure_reasons == FAILURE_REASONS_NONE) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_NO_PLANES_AVAILABLE;
|
||||
}
|
||||
|
||||
/* if we have a plane state, it has its own ref to the fb; if not then
|
||||
* we drop ours here */
|
||||
drm_fb_unref(fb);
|
||||
return ps;
|
||||
}
|
||||
@@ -822,7 +637,8 @@ drm_output_propose_state(struct weston_output *output_base,
|
||||
enum drm_output_propose_state_mode mode)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct weston_paint_node *pnode;
|
||||
struct drm_output_state *state;
|
||||
struct drm_plane_state *scanout_state = NULL;
|
||||
@@ -957,12 +773,29 @@ drm_output_propose_state(struct weston_output *output_base,
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
if (!b->gbm) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(GBM not available)\n", ev);
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
if (!weston_view_has_valid_buffer(ev)) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(no buffer available)\n", ev);
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
/* We can support this with the 'CRTC background colour' property,
|
||||
* if it is fullscreen (i.e. we disable the primary plane), and
|
||||
* opaque (as it is only shown in the absence of any covering
|
||||
* plane, not as a replacement for the primary plane per se). */
|
||||
if (ev->surface->buffer_ref.buffer &&
|
||||
ev->surface->buffer_ref.buffer->type == WESTON_BUFFER_SOLID) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(solid-colour surface)\n", ev);
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
if (pnode->surf_xform.transform != NULL ||
|
||||
!pnode->surf_xform.identity_pipeline) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
@@ -996,15 +829,9 @@ drm_output_propose_state(struct weston_output *output_base,
|
||||
if (!force_renderer) {
|
||||
drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
|
||||
current_lowest_zpos);
|
||||
ps = drm_output_prepare_plane_view(state, ev, mode,
|
||||
scanout_state,
|
||||
current_lowest_zpos,
|
||||
&pnode->try_view_on_plane_failure_reasons);
|
||||
/* If we were able to place the view in a plane, set
|
||||
* failure reasons to none. */
|
||||
if (ps)
|
||||
pnode->try_view_on_plane_failure_reasons =
|
||||
FAILURE_REASONS_NONE;
|
||||
ps = drm_output_find_plane_for_view(state, pnode, mode,
|
||||
scanout_state,
|
||||
current_lowest_zpos);
|
||||
} else {
|
||||
/* We are forced to place the view in the renderer, set
|
||||
* the failure reason accordingly. */
|
||||
@@ -1088,21 +915,24 @@ err:
|
||||
}
|
||||
|
||||
void
|
||||
drm_assign_planes(struct weston_output *output_base, void *repaint_data)
|
||||
drm_assign_planes(struct weston_output *output_base)
|
||||
{
|
||||
struct drm_backend *b = to_drm_backend(output_base->compositor);
|
||||
struct drm_pending_state *pending_state = repaint_data;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_pending_state *pending_state = device->repaint_data;
|
||||
struct drm_output_state *state = NULL;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct weston_paint_node *pnode;
|
||||
struct weston_plane *primary = &output_base->compositor->primary_plane;
|
||||
enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
|
||||
|
||||
assert(output);
|
||||
|
||||
drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
|
||||
output_base->name, (unsigned long) output_base->id);
|
||||
|
||||
if (!b->sprites_are_broken && !output->virtual) {
|
||||
if (!device->sprites_are_broken && !output->virtual && b->gbm) {
|
||||
drm_debug(b, "\t[repaint] trying planes-only build state\n");
|
||||
state = drm_output_propose_state(output_base, pending_state, mode);
|
||||
if (!state) {
|
||||
@@ -1144,26 +974,24 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
|
||||
|
||||
/* Update dmabuf-feedback if needed */
|
||||
if (ev->surface->dmabuf_feedback)
|
||||
dmabuf_feedback_maybe_update(b, ev,
|
||||
dmabuf_feedback_maybe_update(device, ev,
|
||||
pnode->try_view_on_plane_failure_reasons);
|
||||
pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
|
||||
|
||||
/* Test whether this buffer can ever go into a plane:
|
||||
* non-shm, or small enough to be a cursor.
|
||||
*
|
||||
* Also, keep a reference when using the pixman renderer.
|
||||
* That makes it possible to do a seamless switch to the GL
|
||||
* renderer and since the pixman renderer keeps a reference
|
||||
* to the buffer anyway, there is no side effects.
|
||||
*/
|
||||
if (b->use_pixman ||
|
||||
(weston_view_has_valid_buffer(ev) &&
|
||||
(!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
|
||||
(ev->surface->width <= b->cursor_width &&
|
||||
ev->surface->height <= b->cursor_height))))
|
||||
ev->surface->keep_buffer = true;
|
||||
else
|
||||
ev->surface->keep_buffer = false;
|
||||
* non-shm, or small enough to be a cursor. */
|
||||
ev->surface->keep_buffer = false;
|
||||
if (weston_view_has_valid_buffer(ev)) {
|
||||
struct weston_buffer *buffer =
|
||||
ev->surface->buffer_ref.buffer;
|
||||
if (buffer->type == WESTON_BUFFER_DMABUF ||
|
||||
buffer->type == WESTON_BUFFER_RENDERER_OPAQUE)
|
||||
ev->surface->keep_buffer = true;
|
||||
else if (buffer->type == WESTON_BUFFER_SHM &&
|
||||
(ev->surface->width <= device->cursor_width &&
|
||||
ev->surface->height <= device->cursor_height))
|
||||
ev->surface->keep_buffer = true;
|
||||
}
|
||||
|
||||
/* This is a bit unpleasant, but lacking a temporary place to
|
||||
* hang a plane off the view, we have to do a nested walk.
|
||||
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
* Copyright © 2011 Intel Corporation
|
||||
* Copyright © 2012 Raspberry Pi Foundation
|
||||
* Copyright © 2013 Philip Withnall
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/input.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libudev.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/backend-fbdev.h>
|
||||
#include "launcher-util.h"
|
||||
#include "pixman-renderer.h"
|
||||
#include "libinput-seat.h"
|
||||
#include "presentation-time-server-protocol.h"
|
||||
|
||||
struct fbdev_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
uint32_t prev_state;
|
||||
|
||||
struct udev *udev;
|
||||
struct udev_input input;
|
||||
uint32_t output_transform;
|
||||
struct wl_listener session_listener;
|
||||
};
|
||||
|
||||
struct fbdev_screeninfo {
|
||||
unsigned int x_resolution; /* pixels, visible area */
|
||||
unsigned int y_resolution; /* pixels, visible area */
|
||||
unsigned int width_mm; /* visible screen width in mm */
|
||||
unsigned int height_mm; /* visible screen height in mm */
|
||||
unsigned int bits_per_pixel;
|
||||
|
||||
size_t buffer_length; /* length of frame buffer memory in bytes */
|
||||
size_t line_length; /* length of a line in bytes */
|
||||
char id[16]; /* screen identifier */
|
||||
|
||||
pixman_format_code_t pixel_format; /* frame buffer pixel format */
|
||||
unsigned int refresh_rate; /* Hertz */
|
||||
};
|
||||
|
||||
struct fbdev_head {
|
||||
struct weston_head base;
|
||||
|
||||
/* Frame buffer details. */
|
||||
char *device;
|
||||
struct fbdev_screeninfo fb_info;
|
||||
};
|
||||
|
||||
struct fbdev_output {
|
||||
struct fbdev_backend *backend;
|
||||
struct weston_output base;
|
||||
|
||||
struct weston_mode mode;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
|
||||
/* framebuffer mmap details */
|
||||
size_t buffer_length;
|
||||
void *fb;
|
||||
|
||||
/* pixman details. */
|
||||
pixman_image_t *hw_surface;
|
||||
};
|
||||
|
||||
static const char default_seat[] = "seat0";
|
||||
|
||||
static inline struct fbdev_head *
|
||||
to_fbdev_head(struct weston_head *base)
|
||||
{
|
||||
return container_of(base, struct fbdev_head, base);
|
||||
}
|
||||
|
||||
static inline struct fbdev_output *
|
||||
to_fbdev_output(struct weston_output *base)
|
||||
{
|
||||
return container_of(base, struct fbdev_output, base);
|
||||
}
|
||||
|
||||
static inline struct fbdev_backend *
|
||||
to_fbdev_backend(struct weston_compositor *base)
|
||||
{
|
||||
return container_of(base->backend, struct fbdev_backend, base);
|
||||
}
|
||||
|
||||
static struct fbdev_head *
|
||||
fbdev_output_get_head(struct fbdev_output *output)
|
||||
{
|
||||
if (wl_list_length(&output->base.head_list) != 1)
|
||||
return NULL;
|
||||
|
||||
return container_of(output->base.head_list.next,
|
||||
struct fbdev_head, base.output_link);
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_start_repaint_loop(struct weston_output *output)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
weston_compositor_read_presentation_clock(output->compositor, &ts);
|
||||
weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
|
||||
/* Repaint the damaged region onto the back buffer. */
|
||||
pixman_renderer_output_set_buffer(base, output->hw_surface);
|
||||
ec->renderer->repaint_output(base, damage);
|
||||
|
||||
/* Update the damage region. */
|
||||
pixman_region32_subtract(&ec->primary_plane.damage,
|
||||
&ec->primary_plane.damage, damage);
|
||||
|
||||
/* Schedule the end of the frame. We do not sync this to the frame
|
||||
* buffer clock because users who want that should be using the DRM
|
||||
* compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
|
||||
* panning, which is broken in most kernel drivers.
|
||||
*
|
||||
* Finish the frame synchronised to the specified refresh rate. The
|
||||
* refresh rate is given in mHz and the interval in ms. */
|
||||
wl_event_source_timer_update(output->finish_frame_timer,
|
||||
1000000 / output->mode.refresh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
finish_frame_handler(void *data)
|
||||
{
|
||||
struct fbdev_output *output = data;
|
||||
struct timespec ts;
|
||||
|
||||
weston_compositor_read_presentation_clock(output->base.compositor, &ts);
|
||||
weston_output_finish_frame(&output->base, &ts, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static pixman_format_code_t
|
||||
calculate_pixman_format(struct fb_var_screeninfo *vinfo,
|
||||
struct fb_fix_screeninfo *finfo)
|
||||
{
|
||||
/* Calculate the pixman format supported by the frame buffer from the
|
||||
* buffer's metadata. Return 0 if no known pixman format is supported
|
||||
* (since this has depth 0 it's guaranteed to not conflict with any
|
||||
* actual pixman format).
|
||||
*
|
||||
* Documentation on the vinfo and finfo structures:
|
||||
* http://www.mjmwired.net/kernel/Documentation/fb/api.txt
|
||||
*
|
||||
* TODO: Try a bit harder to support other formats, including setting
|
||||
* the preferred format in the hardware. */
|
||||
int type;
|
||||
|
||||
weston_log("Calculating pixman format from:\n"
|
||||
STAMP_SPACE " - type: %i (aux: %i)\n"
|
||||
STAMP_SPACE " - visual: %i\n"
|
||||
STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
|
||||
STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
|
||||
STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
|
||||
STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
|
||||
STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
|
||||
finfo->type, finfo->type_aux, finfo->visual,
|
||||
vinfo->bits_per_pixel, vinfo->grayscale,
|
||||
vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
|
||||
vinfo->green.offset, vinfo->green.length,
|
||||
vinfo->green.msb_right,
|
||||
vinfo->blue.offset, vinfo->blue.length,
|
||||
vinfo->blue.msb_right,
|
||||
vinfo->transp.offset, vinfo->transp.length,
|
||||
vinfo->transp.msb_right);
|
||||
|
||||
/* We only handle packed formats at the moment. */
|
||||
if (finfo->type != FB_TYPE_PACKED_PIXELS)
|
||||
return 0;
|
||||
|
||||
/* We only handle true-colour frame buffers at the moment. */
|
||||
switch(finfo->visual) {
|
||||
case FB_VISUAL_TRUECOLOR:
|
||||
case FB_VISUAL_DIRECTCOLOR:
|
||||
if (vinfo->grayscale != 0)
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only support formats with MSBs on the left. */
|
||||
if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
|
||||
vinfo->blue.msb_right != 0)
|
||||
return 0;
|
||||
|
||||
/* Work out the format type from the offsets. We only support RGBA, ARGB
|
||||
* and ABGR at the moment. */
|
||||
type = PIXMAN_TYPE_OTHER;
|
||||
|
||||
if ((vinfo->transp.offset >= vinfo->red.offset ||
|
||||
vinfo->transp.length == 0) &&
|
||||
vinfo->red.offset >= vinfo->green.offset &&
|
||||
vinfo->green.offset >= vinfo->blue.offset)
|
||||
type = PIXMAN_TYPE_ARGB;
|
||||
else if (vinfo->red.offset >= vinfo->green.offset &&
|
||||
vinfo->green.offset >= vinfo->blue.offset &&
|
||||
vinfo->blue.offset >= vinfo->transp.offset)
|
||||
type = PIXMAN_TYPE_RGBA;
|
||||
else if (vinfo->transp.offset >= vinfo->blue.offset &&
|
||||
vinfo->blue.offset >= vinfo->green.offset &&
|
||||
vinfo->green.offset >= vinfo->red.offset)
|
||||
type = PIXMAN_TYPE_ABGR;
|
||||
|
||||
if (type == PIXMAN_TYPE_OTHER)
|
||||
return 0;
|
||||
|
||||
/* Build the format. */
|
||||
return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
|
||||
vinfo->transp.length,
|
||||
vinfo->red.length,
|
||||
vinfo->green.length,
|
||||
vinfo->blue.length);
|
||||
}
|
||||
|
||||
static int
|
||||
calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
|
||||
{
|
||||
uint64_t quot;
|
||||
|
||||
/* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
|
||||
quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
|
||||
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
|
||||
quot *= vinfo->pixclock;
|
||||
|
||||
if (quot > 0) {
|
||||
uint64_t refresh_rate;
|
||||
|
||||
refresh_rate = 1000000000000000LLU / quot;
|
||||
if (refresh_rate > 200000)
|
||||
refresh_rate = 200000; /* cap at 200 Hz */
|
||||
|
||||
if (refresh_rate >= 1000) /* at least 1 Hz */
|
||||
return refresh_rate;
|
||||
}
|
||||
|
||||
return 60 * 1000; /* default to 60 Hz */
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info)
|
||||
{
|
||||
struct fb_var_screeninfo varinfo;
|
||||
struct fb_fix_screeninfo fixinfo;
|
||||
|
||||
/* Probe the device for screen information. */
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
|
||||
ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Store the pertinent data. */
|
||||
info->x_resolution = varinfo.xres;
|
||||
info->y_resolution = varinfo.yres;
|
||||
info->width_mm = varinfo.width;
|
||||
info->height_mm = varinfo.height;
|
||||
info->bits_per_pixel = varinfo.bits_per_pixel;
|
||||
|
||||
info->buffer_length = fixinfo.smem_len;
|
||||
info->line_length = fixinfo.line_length;
|
||||
strncpy(info->id, fixinfo.id, sizeof(info->id));
|
||||
info->id[sizeof(info->id)-1] = '\0';
|
||||
|
||||
info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
|
||||
info->refresh_rate = calculate_refresh_rate(&varinfo);
|
||||
|
||||
if (info->pixel_format == 0) {
|
||||
weston_log("Frame buffer uses an unsupported format.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info)
|
||||
{
|
||||
struct fb_var_screeninfo varinfo;
|
||||
|
||||
/* Grab the current screen information. */
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update the information. */
|
||||
varinfo.xres = info->x_resolution;
|
||||
varinfo.yres = info->y_resolution;
|
||||
varinfo.width = info->width_mm;
|
||||
varinfo.height = info->height_mm;
|
||||
varinfo.bits_per_pixel = info->bits_per_pixel;
|
||||
|
||||
/* Try to set up an ARGB (x8r8g8b8) pixel format. */
|
||||
varinfo.grayscale = 0;
|
||||
varinfo.transp.offset = 24;
|
||||
varinfo.transp.length = 0;
|
||||
varinfo.transp.msb_right = 0;
|
||||
varinfo.red.offset = 16;
|
||||
varinfo.red.length = 8;
|
||||
varinfo.red.msb_right = 0;
|
||||
varinfo.green.offset = 8;
|
||||
varinfo.green.length = 8;
|
||||
varinfo.green.msb_right = 0;
|
||||
varinfo.blue.offset = 0;
|
||||
varinfo.blue.length = 8;
|
||||
varinfo.blue.msb_right = 0;
|
||||
|
||||
/* Set the device's screen information. */
|
||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_wakeup_screen(int fd, struct fbdev_screeninfo *info)
|
||||
{
|
||||
struct fb_var_screeninfo varinfo;
|
||||
|
||||
/* Grab the current screen information. */
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* force the framebuffer to wake up */
|
||||
varinfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
|
||||
|
||||
/* Set the device's screen information. */
|
||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns an FD for the frame buffer device. */
|
||||
static int
|
||||
fbdev_frame_buffer_open(const char *fb_dev,
|
||||
struct fbdev_screeninfo *screen_info)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
weston_log("Opening fbdev frame buffer.\n");
|
||||
|
||||
/* Open the frame buffer device. */
|
||||
fd = open(fb_dev, O_RDWR | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
weston_log("Failed to open frame buffer device ‘%s’: %s\n",
|
||||
fb_dev, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Grab the screen info. */
|
||||
if (fbdev_query_screen_info(fd, screen_info) < 0) {
|
||||
weston_log("Failed to get frame buffer info: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Attempt to wake up the framebuffer device, needed for secondary
|
||||
* framebuffer devices */
|
||||
if (fbdev_wakeup_screen(fd, screen_info) < 0) {
|
||||
weston_log("Failed to activate framebuffer display. "
|
||||
"Attempting to open output anyway.\n");
|
||||
}
|
||||
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Closes the FD on success or failure. */
|
||||
static int
|
||||
fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
|
||||
{
|
||||
struct fbdev_head *head;
|
||||
int retval = -1;
|
||||
|
||||
head = fbdev_output_get_head(output);
|
||||
|
||||
weston_log("Mapping fbdev frame buffer.\n");
|
||||
|
||||
/* Map the frame buffer. Write-only mode, since we don't want to read
|
||||
* anything back (because it's slow). */
|
||||
output->buffer_length = head->fb_info.buffer_length;
|
||||
output->fb = mmap(NULL, output->buffer_length,
|
||||
PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (output->fb == MAP_FAILED) {
|
||||
weston_log("Failed to mmap frame buffer: %s\n",
|
||||
strerror(errno));
|
||||
output->fb = NULL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Create a pixman image to wrap the memory mapped frame buffer. */
|
||||
output->hw_surface =
|
||||
pixman_image_create_bits(head->fb_info.pixel_format,
|
||||
head->fb_info.x_resolution,
|
||||
head->fb_info.y_resolution,
|
||||
output->fb,
|
||||
head->fb_info.line_length);
|
||||
if (output->hw_surface == NULL) {
|
||||
weston_log("Failed to create surface for frame buffer.\n");
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
/* Success! */
|
||||
retval = 0;
|
||||
|
||||
out_unmap:
|
||||
if (retval != 0 && output->fb != NULL) {
|
||||
munmap(output->fb, output->buffer_length);
|
||||
output->fb = NULL;
|
||||
}
|
||||
|
||||
out_close:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_frame_buffer_unmap(struct fbdev_output *output)
|
||||
{
|
||||
if (!output->fb) {
|
||||
assert(!output->hw_surface);
|
||||
return;
|
||||
}
|
||||
|
||||
weston_log("Unmapping fbdev frame buffer.\n");
|
||||
|
||||
if (output->hw_surface)
|
||||
pixman_image_unref(output->hw_surface);
|
||||
output->hw_surface = NULL;
|
||||
|
||||
if (munmap(output->fb, output->buffer_length) < 0)
|
||||
weston_log("Failed to munmap frame buffer: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
output->fb = NULL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
fbdev_output_attach_head(struct weston_output *output_base,
|
||||
struct weston_head *head_base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(output_base);
|
||||
struct fbdev_head *head = to_fbdev_head(head_base);
|
||||
|
||||
/* Clones not supported. */
|
||||
if (!wl_list_empty(&output->base.head_list))
|
||||
return -1;
|
||||
|
||||
/* only one static mode in list */
|
||||
output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||
output->mode.width = head->fb_info.x_resolution;
|
||||
output->mode.height = head->fb_info.y_resolution;
|
||||
output->mode.refresh = head->fb_info.refresh_rate;
|
||||
wl_list_init(&output->base.mode_list);
|
||||
wl_list_insert(&output->base.mode_list, &output->mode.link);
|
||||
output->base.current_mode = &output->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fbdev_output_destroy(struct weston_output *base);
|
||||
|
||||
static int
|
||||
fbdev_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
|
||||
struct fbdev_head *head;
|
||||
int fb_fd;
|
||||
struct wl_event_loop *loop;
|
||||
const struct pixman_renderer_output_options options = {
|
||||
.use_shadow = true,
|
||||
};
|
||||
|
||||
head = fbdev_output_get_head(output);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
|
||||
weston_log("Mapping frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
|
||||
output->base.repaint = fbdev_output_repaint;
|
||||
|
||||
if (pixman_renderer_output_create(&output->base, &options) < 0)
|
||||
goto out_hw_surface;
|
||||
|
||||
loop = wl_display_get_event_loop(backend->compositor->wl_display);
|
||||
output->finish_frame_timer =
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
|
||||
weston_log("fbdev output %d×%d px\n",
|
||||
output->mode.width, output->mode.height);
|
||||
weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
|
||||
output->mode.refresh / 1000);
|
||||
|
||||
return 0;
|
||||
|
||||
out_hw_surface:
|
||||
fbdev_frame_buffer_unmap(output);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
|
||||
if (!base->enabled)
|
||||
return 0;
|
||||
|
||||
wl_event_source_remove(output->finish_frame_timer);
|
||||
output->finish_frame_timer = NULL;
|
||||
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
fbdev_frame_buffer_unmap(output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fbdev_head *
|
||||
fbdev_head_create(struct fbdev_backend *backend, const char *device)
|
||||
{
|
||||
struct fbdev_head *head;
|
||||
int fb_fd;
|
||||
|
||||
head = zalloc(sizeof *head);
|
||||
if (!head)
|
||||
return NULL;
|
||||
|
||||
head->device = strdup(device);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer head failed.\n");
|
||||
goto out_free;
|
||||
}
|
||||
close(fb_fd);
|
||||
|
||||
weston_head_init(&head->base, "fbdev");
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_head_set_monitor_strings(&head->base, "unknown",
|
||||
head->fb_info.id, NULL);
|
||||
weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN);
|
||||
weston_head_set_physical_size(&head->base, head->fb_info.width_mm,
|
||||
head->fb_info.height_mm);
|
||||
|
||||
weston_compositor_add_head(backend->compositor, &head->base);
|
||||
|
||||
weston_log("Created head '%s' for device %s (%s)\n",
|
||||
head->base.name, head->device, head->base.model);
|
||||
|
||||
return head;
|
||||
|
||||
out_free:
|
||||
free(head->device);
|
||||
free(head);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_head_destroy(struct fbdev_head *head)
|
||||
{
|
||||
weston_head_release(&head->base);
|
||||
free(head->device);
|
||||
free(head);
|
||||
}
|
||||
|
||||
static struct weston_output *
|
||||
fbdev_output_create(struct weston_compositor *compositor,
|
||||
const char *name)
|
||||
{
|
||||
struct fbdev_output *output;
|
||||
|
||||
weston_log("Creating fbdev output.\n");
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (output == NULL)
|
||||
return NULL;
|
||||
|
||||
output->backend = to_fbdev_backend(compositor);
|
||||
|
||||
weston_output_init(&output->base, compositor, name);
|
||||
|
||||
output->base.destroy = fbdev_output_destroy;
|
||||
output->base.disable = fbdev_output_disable;
|
||||
output->base.enable = fbdev_output_enable;
|
||||
output->base.attach_head = fbdev_output_attach_head;
|
||||
|
||||
weston_compositor_add_pending_output(&output->base, compositor);
|
||||
|
||||
return &output->base;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
|
||||
weston_log("Destroying fbdev output.\n");
|
||||
|
||||
fbdev_output_disable(base);
|
||||
|
||||
/* Remove the output. */
|
||||
weston_output_release(&output->base);
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
/* strcmp()-style return values. */
|
||||
static int
|
||||
compare_screen_info (const struct fbdev_screeninfo *a,
|
||||
const struct fbdev_screeninfo *b)
|
||||
{
|
||||
if (a->x_resolution == b->x_resolution &&
|
||||
a->y_resolution == b->y_resolution &&
|
||||
a->width_mm == b->width_mm &&
|
||||
a->height_mm == b->height_mm &&
|
||||
a->bits_per_pixel == b->bits_per_pixel &&
|
||||
a->pixel_format == b->pixel_format &&
|
||||
a->refresh_rate == b->refresh_rate)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_reenable(struct fbdev_backend *backend,
|
||||
struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
struct fbdev_head *head;
|
||||
struct fbdev_screeninfo new_screen_info;
|
||||
int fb_fd;
|
||||
|
||||
head = fbdev_output_get_head(output);
|
||||
|
||||
weston_log("Re-enabling fbdev output.\n");
|
||||
assert(output->base.enabled);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check whether the frame buffer details have changed since we were
|
||||
* disabled. */
|
||||
if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) {
|
||||
/* Perform a mode-set to restore the old mode. */
|
||||
if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) {
|
||||
weston_log("Failed to restore mode settings. "
|
||||
"Attempting to re-open output anyway.\n");
|
||||
}
|
||||
|
||||
close(fb_fd);
|
||||
|
||||
/* Disable and enable the output so that resources depending on
|
||||
* the frame buffer X/Y resolution (such as the shadow buffer)
|
||||
* are re-initialised. */
|
||||
fbdev_output_disable(&output->base);
|
||||
return fbdev_output_enable(&output->base);
|
||||
}
|
||||
|
||||
/* Map the device if it has the same details as before. */
|
||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
|
||||
weston_log("Mapping frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_backend_destroy(struct weston_compositor *base)
|
||||
{
|
||||
struct fbdev_backend *backend = to_fbdev_backend(base);
|
||||
struct weston_head *head, *next;
|
||||
|
||||
udev_input_destroy(&backend->input);
|
||||
|
||||
/* Destroy the output. */
|
||||
weston_compositor_shutdown(base);
|
||||
|
||||
wl_list_for_each_safe(head, next, &base->head_list, compositor_link)
|
||||
fbdev_head_destroy(to_fbdev_head(head));
|
||||
|
||||
/* Chain up. */
|
||||
weston_launcher_destroy(base->launcher);
|
||||
|
||||
udev_unref(backend->udev);
|
||||
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static void
|
||||
session_notify(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_compositor *compositor = data;
|
||||
struct fbdev_backend *backend = to_fbdev_backend(compositor);
|
||||
struct weston_output *output;
|
||||
|
||||
if (compositor->session_active) {
|
||||
weston_log("entering VT\n");
|
||||
compositor->state = backend->prev_state;
|
||||
|
||||
wl_list_for_each(output, &compositor->output_list, link) {
|
||||
fbdev_output_reenable(backend, output);
|
||||
}
|
||||
|
||||
weston_compositor_damage_all(compositor);
|
||||
|
||||
udev_input_enable(&backend->input);
|
||||
} else {
|
||||
weston_log("leaving VT\n");
|
||||
udev_input_disable(&backend->input);
|
||||
|
||||
wl_list_for_each(output, &compositor->output_list, link) {
|
||||
fbdev_frame_buffer_unmap(to_fbdev_output(output));
|
||||
}
|
||||
|
||||
backend->prev_state = compositor->state;
|
||||
weston_compositor_offscreen(compositor);
|
||||
|
||||
/* If we have a repaint scheduled (from the idle handler), make
|
||||
* sure we cancel that so we don't try to pageflip when we're
|
||||
* vt switched away. The OFFSCREEN state will prevent
|
||||
* further attempts at repainting. When we switch
|
||||
* back, we schedule a repaint, which will process
|
||||
* pending frame callbacks. */
|
||||
|
||||
wl_list_for_each(output,
|
||||
&compositor->output_list, link) {
|
||||
output->repaint_needed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
find_framebuffer_device(struct fbdev_backend *b, const char *seat)
|
||||
{
|
||||
struct udev_enumerate *e;
|
||||
struct udev_list_entry *entry;
|
||||
const char *path, *device_seat, *id;
|
||||
char *fb_device_path = NULL;
|
||||
struct udev_device *device, *fb_device, *pci;
|
||||
|
||||
e = udev_enumerate_new(b->udev);
|
||||
udev_enumerate_add_match_subsystem(e, "graphics");
|
||||
udev_enumerate_add_match_sysname(e, "fb[0-9]*");
|
||||
|
||||
udev_enumerate_scan_devices(e);
|
||||
fb_device = NULL;
|
||||
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
|
||||
bool is_boot_vga = false;
|
||||
|
||||
path = udev_list_entry_get_name(entry);
|
||||
device = udev_device_new_from_syspath(b->udev, path);
|
||||
if (!device)
|
||||
continue;
|
||||
device_seat = udev_device_get_property_value(device, "ID_SEAT");
|
||||
if (!device_seat)
|
||||
device_seat = default_seat;
|
||||
if (strcmp(device_seat, seat)) {
|
||||
udev_device_unref(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
pci = udev_device_get_parent_with_subsystem_devtype(device,
|
||||
"pci", NULL);
|
||||
if (pci) {
|
||||
id = udev_device_get_sysattr_value(pci, "boot_vga");
|
||||
if (id && !strcmp(id, "1"))
|
||||
is_boot_vga = true;
|
||||
}
|
||||
|
||||
/* If a framebuffer device was found, and this device isn't
|
||||
* the boot-VGA device, don't use it. */
|
||||
if (!is_boot_vga && fb_device) {
|
||||
udev_device_unref(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* There can only be one boot_vga device. Try to use it
|
||||
* at all costs. */
|
||||
if (is_boot_vga) {
|
||||
if (fb_device)
|
||||
udev_device_unref(fb_device);
|
||||
fb_device = device;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Per the (!is_boot_vga && fb_device) test above, only
|
||||
* trump existing saved devices with boot-VGA devices, so if
|
||||
* the test ends up here, this must be the first device seen. */
|
||||
assert(!fb_device);
|
||||
fb_device = device;
|
||||
}
|
||||
|
||||
udev_enumerate_unref(e);
|
||||
|
||||
if (fb_device) {
|
||||
fb_device_path = strdup(udev_device_get_devnode(fb_device));
|
||||
udev_device_unref(fb_device);
|
||||
}
|
||||
|
||||
return fb_device_path;
|
||||
}
|
||||
|
||||
static struct fbdev_backend *
|
||||
fbdev_backend_create(struct weston_compositor *compositor,
|
||||
struct weston_fbdev_backend_config *param)
|
||||
{
|
||||
struct fbdev_backend *backend;
|
||||
const char *seat_id = default_seat;
|
||||
const char *session_seat;
|
||||
|
||||
session_seat = getenv("XDG_SEAT");
|
||||
if (session_seat)
|
||||
seat_id = session_seat;
|
||||
if (param->seat_id)
|
||||
seat_id = param->seat_id;
|
||||
|
||||
weston_log("initializing fbdev backend\n");
|
||||
weston_log("warning: the fbdev backend is deprecated, please migrate "
|
||||
"to the DRM backend\n");
|
||||
|
||||
backend = zalloc(sizeof *backend);
|
||||
if (backend == NULL)
|
||||
return NULL;
|
||||
|
||||
backend->compositor = compositor;
|
||||
compositor->backend = &backend->base;
|
||||
if (weston_compositor_set_presentation_clock_software(
|
||||
compositor) < 0)
|
||||
goto out_compositor;
|
||||
|
||||
backend->udev = udev_new();
|
||||
if (backend->udev == NULL) {
|
||||
weston_log("Failed to initialize udev context.\n");
|
||||
goto out_compositor;
|
||||
}
|
||||
|
||||
if (!param->device)
|
||||
param->device = find_framebuffer_device(backend, seat_id);
|
||||
if (!param->device) {
|
||||
weston_log("fatal: no framebuffer devices detected.\n");
|
||||
goto out_udev;
|
||||
}
|
||||
|
||||
/* Set up the TTY. */
|
||||
backend->session_listener.notify = session_notify;
|
||||
wl_signal_add(&compositor->session_signal,
|
||||
&backend->session_listener);
|
||||
compositor->launcher =
|
||||
weston_launcher_connect(compositor, param->tty, seat_id, false);
|
||||
if (!compositor->launcher) {
|
||||
weston_log("fatal: fbdev backend should be run using "
|
||||
"weston-launch binary, or your system should "
|
||||
"provide the logind D-Bus API.\n");
|
||||
goto out_udev;
|
||||
}
|
||||
|
||||
backend->base.destroy = fbdev_backend_destroy;
|
||||
backend->base.create_output = fbdev_output_create;
|
||||
|
||||
backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
|
||||
|
||||
weston_setup_vt_switch_bindings(compositor);
|
||||
|
||||
if (pixman_renderer_init(compositor) < 0)
|
||||
goto out_launcher;
|
||||
|
||||
if (!fbdev_head_create(backend, param->device))
|
||||
goto out_launcher;
|
||||
|
||||
free(param->device);
|
||||
|
||||
udev_input_init(&backend->input, compositor, backend->udev,
|
||||
seat_id, param->configure_device);
|
||||
|
||||
return backend;
|
||||
|
||||
out_launcher:
|
||||
free(param->device);
|
||||
weston_launcher_destroy(compositor->launcher);
|
||||
|
||||
out_udev:
|
||||
udev_unref(backend->udev);
|
||||
|
||||
out_compositor:
|
||||
weston_compositor_shutdown(compositor);
|
||||
free(backend);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
config_init_to_defaults(struct weston_fbdev_backend_config *config)
|
||||
{
|
||||
config->tty = 0; /* default to current tty */
|
||||
config->device = NULL;
|
||||
config->seat_id = NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_backend_init(struct weston_compositor *compositor,
|
||||
struct weston_backend_config *config_base)
|
||||
{
|
||||
struct fbdev_backend *b;
|
||||
struct weston_fbdev_backend_config config = {{ 0, }};
|
||||
|
||||
if (config_base == NULL ||
|
||||
config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
|
||||
config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
|
||||
weston_log("fbdev backend config structure is invalid\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
config_init_to_defaults(&config);
|
||||
memcpy(&config, config_base, config_base->struct_size);
|
||||
|
||||
b = fbdev_backend_create(compositor, &config);
|
||||
if (b == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
if not get_option('deprecated-backend-fbdev')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
warning('Support for the deprecated fbdev backend is enabled.')
|
||||
warning('This feature will be removed in a future version.')
|
||||
|
||||
config_h.set('BUILD_FBDEV_COMPOSITOR', '1')
|
||||
|
||||
srcs_fbdev = [
|
||||
'fbdev.c',
|
||||
presentation_time_server_protocol_h,
|
||||
]
|
||||
|
||||
deps_fbdev = [
|
||||
dep_libweston_private,
|
||||
dep_session_helper,
|
||||
dep_libinput_backend,
|
||||
dependency('libudev', version: '>= 136'),
|
||||
]
|
||||
|
||||
plugin_fbdev = shared_library(
|
||||
'fbdev-backend',
|
||||
srcs_fbdev,
|
||||
include_directories: common_inc,
|
||||
dependencies: deps_fbdev,
|
||||
name_prefix: '',
|
||||
install: true,
|
||||
install_dir: dir_module_libweston
|
||||
)
|
||||
env_modmap += 'fbdev-backend.so=@0@;'.format(plugin_fbdev.full_path())
|
||||
|
||||
install_headers(backend_fbdev_h, subdir: dir_include_libweston_install)
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <libweston/backend-headless.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "linux-explicit-synchronization.h"
|
||||
#include "pixel-formats.h"
|
||||
#include "pixman-renderer.h"
|
||||
#include "renderer-gl/gl-renderer.h"
|
||||
#include "shared/weston-drm-fourcc.h"
|
||||
@@ -70,24 +71,33 @@ struct headless_output {
|
||||
|
||||
struct weston_mode mode;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
uint32_t *image_buf;
|
||||
pixman_image_t *image;
|
||||
};
|
||||
|
||||
static const uint32_t headless_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XRGB8888, /* default for pixman-renderer */
|
||||
DRM_FORMAT_ARGB8888,
|
||||
};
|
||||
|
||||
static void
|
||||
headless_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct headless_head *
|
||||
to_headless_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != headless_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct headless_head, base);
|
||||
}
|
||||
|
||||
static void
|
||||
headless_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct headless_output *
|
||||
to_headless_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != headless_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct headless_output, base);
|
||||
}
|
||||
|
||||
@@ -122,11 +132,14 @@ finish_frame_handler(void *data)
|
||||
|
||||
static int
|
||||
headless_output_repaint(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct weston_compositor *ec;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
|
||||
ec->renderer->repaint_output(&output->base, damage);
|
||||
|
||||
@@ -152,18 +165,21 @@ headless_output_disable_pixman(struct headless_output *output)
|
||||
{
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
pixman_image_unref(output->image);
|
||||
free(output->image_buf);
|
||||
}
|
||||
|
||||
static int
|
||||
headless_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(base);
|
||||
struct headless_backend *b = to_headless_backend(base->compositor);
|
||||
struct headless_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
if (!output->base.enabled)
|
||||
return 0;
|
||||
|
||||
b = to_headless_backend(base->compositor);
|
||||
|
||||
wl_event_source_remove(output->finish_frame_timer);
|
||||
|
||||
switch (b->renderer_type) {
|
||||
@@ -185,6 +201,8 @@ headless_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(base);
|
||||
|
||||
assert(output);
|
||||
|
||||
headless_output_disable(&output->base);
|
||||
weston_output_release(&output->base);
|
||||
|
||||
@@ -214,20 +232,20 @@ headless_output_enable_gl(struct headless_output *output)
|
||||
static int
|
||||
headless_output_enable_pixman(struct headless_output *output)
|
||||
{
|
||||
const struct pixel_format_info *pfmt;
|
||||
const struct pixman_renderer_output_options options = {
|
||||
.use_shadow = true,
|
||||
};
|
||||
|
||||
output->image_buf = malloc(output->base.current_mode->width *
|
||||
output->base.current_mode->height * 4);
|
||||
if (!output->image_buf)
|
||||
return -1;
|
||||
pfmt = pixel_format_get_info(headless_formats[0]);
|
||||
|
||||
output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
output->image_buf,
|
||||
output->base.current_mode->width * 4);
|
||||
output->image =
|
||||
pixman_image_create_bits_no_clear(pfmt->pixman_format,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
NULL, 0);
|
||||
if (!output->image)
|
||||
return -1;
|
||||
|
||||
if (pixman_renderer_output_create(&output->base, &options) < 0)
|
||||
goto err_renderer;
|
||||
@@ -238,7 +256,6 @@ headless_output_enable_pixman(struct headless_output *output)
|
||||
|
||||
err_renderer:
|
||||
pixman_image_unref(output->image);
|
||||
free(output->image_buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -247,10 +264,14 @@ static int
|
||||
headless_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(base);
|
||||
struct headless_backend *b = to_headless_backend(base->compositor);
|
||||
struct headless_backend *b;
|
||||
struct wl_event_loop *loop;
|
||||
int ret = 0;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_headless_backend(base->compositor);
|
||||
|
||||
loop = wl_display_get_event_loop(b->compositor->wl_display);
|
||||
output->finish_frame_timer =
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
@@ -287,6 +308,9 @@ headless_output_set_size(struct weston_output *base,
|
||||
struct weston_head *head;
|
||||
int output_width, output_height;
|
||||
|
||||
if (!output)
|
||||
return -1;
|
||||
|
||||
/* We can only be called once. */
|
||||
assert(!output->base.current_mode);
|
||||
|
||||
@@ -361,7 +385,12 @@ headless_head_create(struct weston_compositor *compositor,
|
||||
return -1;
|
||||
|
||||
weston_head_init(&head->base, name);
|
||||
|
||||
head->base.backend_id = headless_head_destroy;
|
||||
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_head_set_supported_eotf_mask(&head->base,
|
||||
WESTON_EOTF_MODE_ALL_MASK);
|
||||
|
||||
/* Ideally all attributes of the head would be set here, so that the
|
||||
* user has all the information when deciding to create outputs.
|
||||
@@ -374,8 +403,12 @@ headless_head_create(struct weston_compositor *compositor,
|
||||
}
|
||||
|
||||
static void
|
||||
headless_head_destroy(struct headless_head *head)
|
||||
headless_head_destroy(struct weston_head *base)
|
||||
{
|
||||
struct headless_head *head = to_headless_head(base);
|
||||
|
||||
assert(head);
|
||||
|
||||
weston_head_release(&head->base);
|
||||
free(head);
|
||||
}
|
||||
@@ -388,8 +421,10 @@ headless_destroy(struct weston_compositor *ec)
|
||||
|
||||
weston_compositor_shutdown(ec);
|
||||
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
|
||||
headless_head_destroy(to_headless_head(base));
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
|
||||
if (to_headless_head(base))
|
||||
headless_head_destroy(base);
|
||||
}
|
||||
|
||||
free(b);
|
||||
}
|
||||
|
||||
@@ -4,24 +4,36 @@ endif
|
||||
|
||||
config_h.set('BUILD_RDP_COMPOSITOR', '1')
|
||||
|
||||
dep_frdp = dependency('freerdp2', version: '>= 2.2.0', required: false)
|
||||
dep_frdp = dependency('freerdp2', version: '>= 2.3.0', required: false)
|
||||
if not dep_frdp.found()
|
||||
error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
error('RDP-backend requires freerdp >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
endif
|
||||
|
||||
dep_wpr = dependency('winpr2', version: '>= 2.2.0', required: false)
|
||||
dep_frdp_server = dependency('freerdp-server2', version: '>= 2.3.0', required: false)
|
||||
if not dep_frdp_server.found()
|
||||
error('RDP-backend requires freerdp-server2 >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
endif
|
||||
|
||||
dep_wpr = dependency('winpr2', version: '>= 2.3.0', required: false)
|
||||
if not dep_wpr.found()
|
||||
error('RDP-backend requires winpr >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
error('RDP-backend requires winpr >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
endif
|
||||
|
||||
deps_rdp = [
|
||||
dep_libweston_private,
|
||||
dep_frdp,
|
||||
dep_frdp_server,
|
||||
dep_wpr,
|
||||
]
|
||||
srcs_rdp = [
|
||||
'rdp.c',
|
||||
'rdpclip.c',
|
||||
'rdputil.c',
|
||||
]
|
||||
|
||||
plugin_rdp = shared_library(
|
||||
'rdp-backend',
|
||||
'rdp.c',
|
||||
srcs_rdp,
|
||||
include_directories: common_inc,
|
||||
dependencies: deps_rdp,
|
||||
name_prefix: '',
|
||||
|
||||
+613
-216
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright © 2013 Hardening <rdp.effort@gmail.com>
|
||||
* Copyright © 2020 Microsoft
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef RDP_H
|
||||
#define RDP_H
|
||||
|
||||
#include <freerdp/version.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/update.h>
|
||||
#include <freerdp/input.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
#include <freerdp/codec/nsc.h>
|
||||
#include <freerdp/locale/keyboard.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#include <freerdp/server/cliprdr.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/backend-rdp.h>
|
||||
#include <libweston/weston-log.h>
|
||||
|
||||
#include "backend.h"
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
#define MAX_FREERDP_FDS 32
|
||||
#define DEFAULT_AXIS_STEP_DISTANCE 10
|
||||
#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32
|
||||
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardtype
|
||||
* defines a keyboard type that isn't currently defined in FreeRDP, but is
|
||||
* available for RDP connections */
|
||||
#ifndef KBD_TYPE_KOREAN
|
||||
#define KBD_TYPE_KOREAN 8
|
||||
#endif
|
||||
|
||||
/* WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys */
|
||||
/* 0x1f1 and 0x1f2 keys are only exists on Korean 103 keyboard (Type 8:SubType 6) */
|
||||
|
||||
/* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */
|
||||
#define ATKBD_RET_HANJA 0xf1
|
||||
#define ATKBD_RET_HANGEUL 0xf2
|
||||
|
||||
struct rdp_output;
|
||||
|
||||
struct rdp_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
|
||||
freerdp_listener *listener;
|
||||
struct wl_event_source *listener_events[MAX_FREERDP_FDS];
|
||||
struct rdp_output *output;
|
||||
struct weston_log_scope *debug;
|
||||
struct weston_log_scope *verbose;
|
||||
|
||||
struct weston_log_scope *clipboard_debug;
|
||||
struct weston_log_scope *clipboard_verbose;
|
||||
|
||||
struct wl_list peers;
|
||||
|
||||
char *server_cert;
|
||||
char *server_key;
|
||||
char *rdp_key;
|
||||
int tls_enabled;
|
||||
int no_clients_resize;
|
||||
int force_no_compression;
|
||||
bool remotefx_codec;
|
||||
int external_listener_fd;
|
||||
int rdp_monitor_refresh_rate;
|
||||
pid_t compositor_tid;
|
||||
|
||||
rdp_audio_in_setup audio_in_setup;
|
||||
rdp_audio_in_teardown audio_in_teardown;
|
||||
rdp_audio_out_setup audio_out_setup;
|
||||
rdp_audio_out_teardown audio_out_teardown;
|
||||
};
|
||||
|
||||
enum peer_item_flags {
|
||||
RDP_PEER_ACTIVATED = (1 << 0),
|
||||
RDP_PEER_OUTPUT_ENABLED = (1 << 1),
|
||||
};
|
||||
|
||||
struct rdp_peers_item {
|
||||
int flags;
|
||||
freerdp_peer *peer;
|
||||
struct weston_seat *seat;
|
||||
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct rdp_head {
|
||||
struct weston_head base;
|
||||
};
|
||||
|
||||
struct rdp_output {
|
||||
struct weston_output base;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
pixman_image_t *shadow_surface;
|
||||
};
|
||||
|
||||
struct rdp_peer_context {
|
||||
rdpContext _p;
|
||||
|
||||
struct rdp_backend *rdpBackend;
|
||||
struct wl_event_source *events[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor */
|
||||
RFX_CONTEXT *rfx_context;
|
||||
wStream *encode_stream;
|
||||
RFX_RECT *rfx_rects;
|
||||
NSC_CONTEXT *nsc_context;
|
||||
|
||||
struct rdp_peers_item item;
|
||||
|
||||
bool button_state[5];
|
||||
|
||||
int verticalAccumWheelRotationPrecise;
|
||||
int verticalAccumWheelRotationDiscrete;
|
||||
int horizontalAccumWheelRotationPrecise;
|
||||
int horizontalAccumWheelRotationDiscrete;
|
||||
|
||||
HANDLE vcm;
|
||||
|
||||
/* list of outstanding event_source sent from FreeRDP thread to display loop.*/
|
||||
int loop_task_event_source_fd;
|
||||
struct wl_event_source *loop_task_event_source;
|
||||
pthread_mutex_t loop_task_list_mutex;
|
||||
struct wl_list loop_task_list; /* struct rdp_loop_task::link */
|
||||
|
||||
/* Clipboard support */
|
||||
CliprdrServerContext *clipboard_server_context;
|
||||
|
||||
void *audio_in_private;
|
||||
void *audio_out_private;
|
||||
|
||||
struct rdp_clipboard_data_source *clipboard_client_data_source;
|
||||
struct rdp_clipboard_data_source *clipboard_inflight_client_data_source;
|
||||
|
||||
struct wl_listener clipboard_selection_listener;
|
||||
};
|
||||
|
||||
typedef struct rdp_peer_context RdpPeerContext;
|
||||
|
||||
typedef void (*rdp_loop_task_func_t)(bool freeOnly, void *data);
|
||||
|
||||
struct rdp_loop_task {
|
||||
struct wl_list link;
|
||||
RdpPeerContext *peerCtx;
|
||||
rdp_loop_task_func_t func;
|
||||
};
|
||||
|
||||
#define rdp_debug_verbose(b, ...) \
|
||||
rdp_debug_print(b->verbose, false, __VA_ARGS__)
|
||||
#define rdp_debug_verbose_continue(b, ...) \
|
||||
rdp_debug_print(b->verbose, true, __VA_ARGS__)
|
||||
#define rdp_debug(b, ...) \
|
||||
rdp_debug_print(b->debug, false, __VA_ARGS__)
|
||||
#define rdp_debug_continue(b, ...) \
|
||||
rdp_debug_print(b->debug, true, __VA_ARGS__)
|
||||
|
||||
#define rdp_debug_clipboard_verbose(b, ...) \
|
||||
rdp_debug_print(b->clipboard_verbose, false, __VA_ARGS__)
|
||||
#define rdp_debug_clipboard_verbose_continue(b, ...) \
|
||||
rdp_debug_print(b->clipboard_verbose, true, __VA_ARGS__)
|
||||
#define rdp_debug_clipboard(b, ...) \
|
||||
rdp_debug_print(b->clipboard_debug, false, __VA_ARGS__)
|
||||
#define rdp_debug_clipboard_continue(b, ...) \
|
||||
rdp_debug_print(b->clipboard_debug, true, __VA_ARGS__)
|
||||
|
||||
/* rdputil.c */
|
||||
void
|
||||
rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...);
|
||||
|
||||
int
|
||||
rdp_wl_array_read_fd(struct wl_array *array, int fd);
|
||||
|
||||
void
|
||||
convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames);
|
||||
|
||||
void
|
||||
assert_compositor_thread(struct rdp_backend *b);
|
||||
|
||||
void
|
||||
assert_not_compositor_thread(struct rdp_backend *b);
|
||||
|
||||
bool
|
||||
rdp_event_loop_add_fd(struct wl_event_loop *loop,
|
||||
int fd, uint32_t mask,
|
||||
wl_event_loop_fd_func_t func,
|
||||
void *data,
|
||||
struct wl_event_source **event_source);
|
||||
|
||||
void
|
||||
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx,
|
||||
rdp_loop_task_func_t func,
|
||||
struct rdp_loop_task *task);
|
||||
|
||||
bool
|
||||
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx);
|
||||
|
||||
void
|
||||
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx);
|
||||
|
||||
/* rdpclip.c */
|
||||
int
|
||||
rdp_clipboard_init(freerdp_peer *client);
|
||||
|
||||
void
|
||||
rdp_clipboard_destroy(RdpPeerContext *peerCtx);
|
||||
|
||||
void
|
||||
rdp_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct rdp_head *
|
||||
to_rdp_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != rdp_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct rdp_head, base);
|
||||
}
|
||||
|
||||
void
|
||||
rdp_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct rdp_output *
|
||||
to_rdp_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != rdp_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct rdp_output, base);
|
||||
}
|
||||
|
||||
static inline struct rdp_backend *
|
||||
to_rdp_backend(struct weston_compositor *base)
|
||||
{
|
||||
return container_of(base->backend, struct rdp_backend, base);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright © 2020 Microsoft
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "rdp.h"
|
||||
|
||||
static int cached_tm_mday = -1;
|
||||
|
||||
void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...)
|
||||
{
|
||||
char timestr[128];
|
||||
int len_va;
|
||||
char *str;
|
||||
|
||||
if (!log_scope || !weston_log_scope_is_enabled(log_scope))
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (cont) {
|
||||
weston_log_scope_vprintf(log_scope, fmt, ap);
|
||||
goto end;
|
||||
}
|
||||
|
||||
weston_log_timestamp(timestr, sizeof(timestr), &cached_tm_mday);
|
||||
len_va = vasprintf(&str, fmt, ap);
|
||||
if (len_va >= 0) {
|
||||
weston_log_scope_printf(log_scope, "%s %s",
|
||||
timestr, str);
|
||||
free(str);
|
||||
} else {
|
||||
const char *oom = "Out of memory";
|
||||
|
||||
weston_log_scope_printf(log_scope, "%s %s",
|
||||
timestr, oom);
|
||||
}
|
||||
end:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
assert_compositor_thread(struct rdp_backend *b)
|
||||
{
|
||||
assert(b->compositor_tid == gettid());
|
||||
}
|
||||
|
||||
void
|
||||
assert_not_compositor_thread(struct rdp_backend *b)
|
||||
{
|
||||
assert(b->compositor_tid != gettid());
|
||||
}
|
||||
|
||||
bool
|
||||
rdp_event_loop_add_fd(struct wl_event_loop *loop,
|
||||
int fd, uint32_t mask,
|
||||
wl_event_loop_fd_func_t func,
|
||||
void *data, struct wl_event_source **event_source)
|
||||
{
|
||||
*event_source = wl_event_loop_add_fd(loop, fd, 0, func, data);
|
||||
if (!*event_source) {
|
||||
weston_log("%s: wl_event_loop_add_fd failed.\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_event_source_fd_update(*event_source, mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx,
|
||||
rdp_loop_task_func_t func,
|
||||
struct rdp_loop_task *task)
|
||||
{
|
||||
/* this function is ONLY used to queue the task from FreeRDP thread,
|
||||
* and the task to be processed at wayland display loop thread. */
|
||||
assert_not_compositor_thread(peerCtx->rdpBackend);
|
||||
|
||||
task->peerCtx = peerCtx;
|
||||
task->func = func;
|
||||
|
||||
pthread_mutex_lock(&peerCtx->loop_task_list_mutex);
|
||||
/* this inserts at head */
|
||||
wl_list_insert(&peerCtx->loop_task_list, &task->link);
|
||||
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex);
|
||||
|
||||
eventfd_write(peerCtx->loop_task_event_source_fd, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
rdp_dispatch_task(int fd, uint32_t mask, void *arg)
|
||||
{
|
||||
RdpPeerContext *peerCtx = (RdpPeerContext *)arg;
|
||||
struct rdp_loop_task *task, *tmp;
|
||||
eventfd_t dummy;
|
||||
|
||||
/* this must be called back at wayland display loop thread */
|
||||
assert_compositor_thread(peerCtx->rdpBackend);
|
||||
|
||||
eventfd_read(peerCtx->loop_task_event_source_fd, &dummy);
|
||||
|
||||
pthread_mutex_lock(&peerCtx->loop_task_list_mutex);
|
||||
/* dequeue the first task which is at last, so use reverse. */
|
||||
assert(!wl_list_empty(&peerCtx->loop_task_list));
|
||||
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) {
|
||||
wl_list_remove(&task->link);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex);
|
||||
|
||||
/* Dispatch and task will be freed by caller. */
|
||||
task->func(false, task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx)
|
||||
{
|
||||
struct rdp_backend *b = peerCtx->rdpBackend;
|
||||
struct wl_event_loop *loop;
|
||||
bool ret;
|
||||
|
||||
if (pthread_mutex_init(&peerCtx->loop_task_list_mutex, NULL) == -1) {
|
||||
weston_log("%s: pthread_mutex_init failed. %s\n", __func__, strerror(errno));
|
||||
goto error_mutex;
|
||||
}
|
||||
|
||||
assert(peerCtx->loop_task_event_source_fd == -1);
|
||||
peerCtx->loop_task_event_source_fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
|
||||
if (peerCtx->loop_task_event_source_fd == -1) {
|
||||
weston_log("%s: eventfd(EFD_SEMAPHORE) failed. %s\n", __func__, strerror(errno));
|
||||
goto error_event_source_fd;
|
||||
}
|
||||
|
||||
assert(wl_list_empty(&peerCtx->loop_task_list));
|
||||
|
||||
loop = wl_display_get_event_loop(b->compositor->wl_display);
|
||||
assert(peerCtx->loop_task_event_source == NULL);
|
||||
|
||||
ret = rdp_event_loop_add_fd(loop,
|
||||
peerCtx->loop_task_event_source_fd,
|
||||
WL_EVENT_READABLE, rdp_dispatch_task,
|
||||
peerCtx,
|
||||
&peerCtx->loop_task_event_source);
|
||||
if (!ret)
|
||||
goto error_event_loop_add_fd;
|
||||
|
||||
return true;
|
||||
|
||||
error_event_loop_add_fd:
|
||||
close(peerCtx->loop_task_event_source_fd);
|
||||
peerCtx->loop_task_event_source_fd = -1;
|
||||
|
||||
error_event_source_fd:
|
||||
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex);
|
||||
|
||||
error_mutex:
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx)
|
||||
{
|
||||
struct rdp_loop_task *task, *tmp;
|
||||
|
||||
/* This function must be called all virtual channel thread at FreeRDP is terminated,
|
||||
* that ensures no more incoming tasks. */
|
||||
|
||||
if (peerCtx->loop_task_event_source) {
|
||||
wl_event_source_remove(peerCtx->loop_task_event_source);
|
||||
peerCtx->loop_task_event_source = NULL;
|
||||
}
|
||||
|
||||
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) {
|
||||
wl_list_remove(&task->link);
|
||||
/* inform caller task is not really scheduled prior to context destruction,
|
||||
* inform them to clean them up. */
|
||||
task->func(true /* freeOnly */, task);
|
||||
}
|
||||
assert(wl_list_empty(&peerCtx->loop_task_list));
|
||||
|
||||
if (peerCtx->loop_task_event_source_fd != -1) {
|
||||
close(peerCtx->loop_task_event_source_fd);
|
||||
peerCtx->loop_task_event_source_fd = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex);
|
||||
}
|
||||
|
||||
/* This is a little tricky - it makes sure there's always at least
|
||||
* one spare byte in the array in case the caller needs to add a
|
||||
* null terminator to it. We can't just null terminate the array
|
||||
* here, because some callers won't want that - and some won't
|
||||
* like having an odd number of bytes.
|
||||
*/
|
||||
int
|
||||
rdp_wl_array_read_fd(struct wl_array *array, int fd)
|
||||
{
|
||||
int len, size;
|
||||
char *data;
|
||||
|
||||
/* Make sure we have at least 1024 bytes of space left */
|
||||
if (array->alloc - array->size < 1024) {
|
||||
if (!wl_array_add(array, 1024)) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
array->size -= 1024;
|
||||
}
|
||||
data = (char *)array->data + array->size;
|
||||
/* Leave one char in case the caller needs space for a
|
||||
* null terminator */
|
||||
size = array->alloc - array->size - 1;
|
||||
do {
|
||||
len = read(fd, data, size);
|
||||
} while (len == -1 && errno == EINTR);
|
||||
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
array->size += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
+135
-160
@@ -76,7 +76,6 @@ struct wayland_backend {
|
||||
struct wl_display *wl_display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shell *shell;
|
||||
struct xdg_wm_base *xdg_wm_base;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
struct wl_shm *shm;
|
||||
@@ -112,7 +111,6 @@ struct wayland_output {
|
||||
struct wl_output *output;
|
||||
uint32_t global_id;
|
||||
|
||||
struct wl_shell_surface *shell_surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
int configure_width, configure_height;
|
||||
@@ -233,15 +231,25 @@ struct wayland_input {
|
||||
|
||||
struct gl_renderer_interface *gl_renderer;
|
||||
|
||||
static void
|
||||
wayland_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct wayland_head *
|
||||
to_wayland_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != wayland_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct wayland_head, base);
|
||||
}
|
||||
|
||||
static void
|
||||
wayland_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct wayland_output *
|
||||
to_wayland_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != wayland_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct wayland_output, base);
|
||||
}
|
||||
|
||||
@@ -293,7 +301,7 @@ wayland_output_get_shm_buffer(struct wayland_output *output)
|
||||
|
||||
struct wl_shm_pool *pool;
|
||||
int width, height, stride;
|
||||
int32_t fx, fy;
|
||||
struct weston_geometry area;
|
||||
int fd;
|
||||
unsigned char *data;
|
||||
|
||||
@@ -369,13 +377,20 @@ wayland_output_get_shm_buffer(struct wayland_output *output)
|
||||
cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
|
||||
width, height, stride);
|
||||
|
||||
fx = 0;
|
||||
fy = 0;
|
||||
if (output->frame)
|
||||
frame_interior(output->frame, &fx, &fy, 0, 0);
|
||||
if (output->frame) {
|
||||
frame_interior(output->frame, &area.x, &area.y,
|
||||
&area.width, &area.height);
|
||||
} else {
|
||||
area.x = 0;
|
||||
area.y = 0;
|
||||
area.width = output->base.current_mode->width;
|
||||
area.height = output->base.current_mode->height;
|
||||
}
|
||||
|
||||
/* Address only the interior, excluding output decorations */
|
||||
sb->pm_image =
|
||||
pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height,
|
||||
(uint32_t *)(data + fy * stride) + fx,
|
||||
pixman_image_create_bits(PIXMAN_a8r8g8b8, area.width, area.height,
|
||||
(uint32_t *)(data + area.y * stride) + area.x,
|
||||
stride);
|
||||
|
||||
return sb;
|
||||
@@ -501,8 +516,11 @@ static int
|
||||
wayland_output_start_repaint_loop(struct weston_output *output_base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_backend *wb =
|
||||
to_wayland_backend(output->base.compositor);
|
||||
struct wayland_backend *wb;
|
||||
|
||||
assert(output);
|
||||
|
||||
wb = to_wayland_backend(output->base.compositor);
|
||||
|
||||
/* If this is the initial frame, we need to attach a buffer so that
|
||||
* the compositor can map the surface and include it in its render
|
||||
@@ -526,11 +544,14 @@ wayland_output_start_repaint_loop(struct weston_output *output_base)
|
||||
#ifdef ENABLE_EGL
|
||||
static int
|
||||
wayland_output_repaint_gl(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct weston_compositor *ec;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
|
||||
output->frame_cb = wl_surface_frame(output->parent.surface);
|
||||
wl_callback_add_listener(output->frame_cb, &frame_listener, output);
|
||||
@@ -638,14 +659,16 @@ wayland_shm_buffer_attach(struct wayland_shm_buffer *sb)
|
||||
|
||||
static int
|
||||
wayland_output_repaint_pixman(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_backend *b =
|
||||
to_wayland_backend(output->base.compositor);
|
||||
struct wayland_backend *b;
|
||||
struct wayland_shm_buffer *sb;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_wayland_backend(output->base.compositor);
|
||||
|
||||
if (output->frame) {
|
||||
if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
|
||||
wl_list_for_each(sb, &output->shm.buffers, link)
|
||||
@@ -692,11 +715,6 @@ wayland_backend_destroy_output_surface(struct wayland_output *output)
|
||||
output->parent.xdg_surface = NULL;
|
||||
}
|
||||
|
||||
if (output->parent.shell_surface) {
|
||||
wl_shell_surface_destroy(output->parent.shell_surface);
|
||||
output->parent.shell_surface = NULL;
|
||||
}
|
||||
|
||||
wl_surface_destroy(output->parent.surface);
|
||||
output->parent.surface = NULL;
|
||||
}
|
||||
@@ -718,7 +736,11 @@ static int
|
||||
wayland_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(base);
|
||||
struct wayland_backend *b = to_wayland_backend(base->compositor);
|
||||
struct wayland_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_wayland_backend(base->compositor);
|
||||
|
||||
if (!output->base.enabled)
|
||||
return 0;
|
||||
@@ -752,6 +774,8 @@ wayland_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(base);
|
||||
|
||||
assert(output);
|
||||
|
||||
wayland_output_disable(&output->base);
|
||||
|
||||
weston_output_release(&output->base);
|
||||
@@ -763,8 +787,6 @@ wayland_output_destroy(struct weston_output *base)
|
||||
free(output);
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener;
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
static int
|
||||
wayland_output_init_gl_renderer(struct wayland_output *output)
|
||||
@@ -816,64 +838,54 @@ wayland_output_init_pixman_renderer(struct wayland_output *output)
|
||||
static void
|
||||
wayland_output_resize_surface(struct wayland_output *output)
|
||||
{
|
||||
struct wayland_backend *b =
|
||||
to_wayland_backend(output->base.compositor);
|
||||
int32_t ix, iy, iwidth, iheight;
|
||||
int32_t width, height;
|
||||
struct wayland_backend *b = to_wayland_backend(output->base.compositor);
|
||||
/* Defaults for without frame: */
|
||||
struct weston_size fb_size = {
|
||||
.width = output->base.current_mode->width,
|
||||
.height = output->base.current_mode->height
|
||||
};
|
||||
struct weston_geometry area = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = fb_size.width,
|
||||
.height = fb_size.height
|
||||
};
|
||||
struct weston_geometry inp = area;
|
||||
struct weston_geometry opa = area;
|
||||
struct wl_region *region;
|
||||
|
||||
width = output->base.current_mode->width;
|
||||
height = output->base.current_mode->height;
|
||||
|
||||
if (output->frame) {
|
||||
frame_resize_inside(output->frame, width, height);
|
||||
frame_resize_inside(output->frame, area.width, area.height);
|
||||
frame_interior(output->frame, &area.x, &area.y, NULL, NULL);
|
||||
fb_size.width = frame_width(output->frame);
|
||||
fb_size.height = frame_height(output->frame);
|
||||
|
||||
frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight);
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, ix, iy, iwidth, iheight);
|
||||
wl_surface_set_input_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
if (output->parent.xdg_surface) {
|
||||
xdg_surface_set_window_geometry(output->parent.xdg_surface,
|
||||
ix,
|
||||
iy,
|
||||
iwidth,
|
||||
iheight);
|
||||
}
|
||||
|
||||
frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight);
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, ix, iy, iwidth, iheight);
|
||||
wl_surface_set_opaque_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
width = frame_width(output->frame);
|
||||
height = frame_height(output->frame);
|
||||
} else {
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, 0, 0, width, height);
|
||||
wl_surface_set_input_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, 0, 0, width, height);
|
||||
wl_surface_set_opaque_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
if (output->parent.xdg_surface) {
|
||||
xdg_surface_set_window_geometry(output->parent.xdg_surface,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
frame_input_rect(output->frame, &inp.x, &inp.y,
|
||||
&inp.width, &inp.height);
|
||||
frame_opaque_rect(output->frame, &opa.x, &opa.y,
|
||||
&opa.width, &opa.height);
|
||||
}
|
||||
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, inp.x, inp.y, inp.width, inp.height);
|
||||
wl_surface_set_input_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
if (output->parent.xdg_surface) {
|
||||
xdg_surface_set_window_geometry(output->parent.xdg_surface,
|
||||
inp.x, inp.y,
|
||||
inp.width, inp.height);
|
||||
}
|
||||
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, opa.x, opa.y, opa.width, opa.height);
|
||||
wl_surface_set_opaque_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
if (output->gl.egl_window) {
|
||||
wl_egl_window_resize(output->gl.egl_window,
|
||||
width, height, 0, 0);
|
||||
fb_size.width, fb_size.height, 0, 0);
|
||||
|
||||
/* These will need to be re-created due to the resize */
|
||||
gl_renderer->output_set_border(&output->base,
|
||||
@@ -928,8 +940,6 @@ wayland_output_set_windowed(struct wayland_output *output)
|
||||
|
||||
if (output->parent.xdg_toplevel) {
|
||||
xdg_toplevel_unset_fullscreen(output->parent.xdg_toplevel);
|
||||
} else if (output->parent.shell_surface) {
|
||||
wl_shell_surface_set_toplevel(output->parent.shell_surface);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
@@ -939,7 +949,6 @@ wayland_output_set_windowed(struct wayland_output *output)
|
||||
|
||||
static void
|
||||
wayland_output_set_fullscreen(struct wayland_output *output,
|
||||
enum wl_shell_surface_fullscreen_method method,
|
||||
uint32_t framerate, struct wl_output *target)
|
||||
{
|
||||
if (output->frame) {
|
||||
@@ -951,9 +960,6 @@ wayland_output_set_fullscreen(struct wayland_output *output,
|
||||
|
||||
if (output->parent.xdg_toplevel) {
|
||||
xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel, target);
|
||||
} else if (output->parent.shell_surface) {
|
||||
wl_shell_surface_set_fullscreen(output->parent.shell_surface,
|
||||
method, framerate, target);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
@@ -1062,7 +1068,7 @@ static int
|
||||
wayland_output_switch_mode(struct weston_output *output_base,
|
||||
struct weston_mode *mode)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_output *output;
|
||||
struct wayland_backend *b;
|
||||
struct wl_surface *old_surface;
|
||||
struct weston_mode *old_mode;
|
||||
@@ -1073,6 +1079,9 @@ wayland_output_switch_mode(struct weston_output *output_base,
|
||||
return -1;
|
||||
}
|
||||
|
||||
output = to_wayland_output(output_base);
|
||||
assert(output);
|
||||
|
||||
if (mode == NULL) {
|
||||
weston_log("mode is NULL.\n");
|
||||
return -1;
|
||||
@@ -1080,7 +1089,7 @@ wayland_output_switch_mode(struct weston_output *output_base,
|
||||
|
||||
b = to_wayland_backend(output_base->compositor);
|
||||
|
||||
if (output->parent.xdg_surface || output->parent.shell_surface || !b->parent.fshell)
|
||||
if (output->parent.xdg_surface || !b->parent.fshell)
|
||||
return -1;
|
||||
|
||||
mode = wayland_output_choose_mode(output, mode);
|
||||
@@ -1221,20 +1230,6 @@ wayland_backend_create_output_surface(struct wayland_output *output)
|
||||
|
||||
weston_log("wayland-backend: Using xdg_wm_base\n");
|
||||
}
|
||||
else if (b->parent.shell) {
|
||||
output->parent.shell_surface =
|
||||
wl_shell_get_shell_surface(b->parent.shell,
|
||||
output->parent.surface);
|
||||
if (!output->parent.shell_surface) {
|
||||
wl_surface_destroy(output->parent.surface);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl_shell_surface_add_listener(output->parent.shell_surface,
|
||||
&shell_surface_listener, output);
|
||||
|
||||
weston_log("wayland-backend: Using wl_shell\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1243,10 +1238,14 @@ static int
|
||||
wayland_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(base);
|
||||
struct wayland_backend *b = to_wayland_backend(base->compositor);
|
||||
struct wayland_backend *b;
|
||||
enum mode_status mode_status;
|
||||
int ret = 0;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_wayland_backend(base->compositor);
|
||||
|
||||
wl_list_init(&output->shm.buffers);
|
||||
wl_list_init(&output->shm.free_buffers);
|
||||
|
||||
@@ -1295,13 +1294,9 @@ wayland_output_enable(struct weston_output *base)
|
||||
|
||||
output->parent.draw_initial_frame = true;
|
||||
}
|
||||
} else {
|
||||
wayland_output_set_fullscreen(output,
|
||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER,
|
||||
output->mode.refresh, output->parent.output);
|
||||
}
|
||||
} else if (b->fullscreen) {
|
||||
wayland_output_set_fullscreen(output, 0, 0, NULL);
|
||||
wayland_output_set_fullscreen(output, 0, NULL);
|
||||
} else {
|
||||
wayland_output_set_windowed(output);
|
||||
}
|
||||
@@ -1326,9 +1321,16 @@ static int
|
||||
wayland_output_attach_head(struct weston_output *output_base,
|
||||
struct weston_head *head_base)
|
||||
{
|
||||
struct wayland_backend *b = to_wayland_backend(output_base->compositor);
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_head *head = to_wayland_head(head_base);
|
||||
struct wayland_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
if (!head)
|
||||
return -1;
|
||||
|
||||
b = to_wayland_backend(output_base->compositor);
|
||||
|
||||
if (!wl_list_empty(&output->base.head_list))
|
||||
return -1;
|
||||
@@ -1353,6 +1355,8 @@ wayland_output_detach_head(struct weston_output *output_base,
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
|
||||
assert(output);
|
||||
|
||||
/* Rely on the disable hook if the output was enabled. We do not
|
||||
* support cloned heads, so detaching is guaranteed to disable the
|
||||
* output.
|
||||
@@ -1411,6 +1415,9 @@ wayland_head_create(struct weston_compositor *compositor, const char *name)
|
||||
return NULL;
|
||||
|
||||
weston_head_init(&head->base, name);
|
||||
|
||||
head->base.backend_id = wayland_head_destroy;
|
||||
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_compositor_add_head(compositor, &head->base);
|
||||
|
||||
@@ -1458,8 +1465,12 @@ wayland_head_create_for_parent_output(struct weston_compositor *compositor,
|
||||
}
|
||||
|
||||
static void
|
||||
wayland_head_destroy(struct wayland_head *head)
|
||||
wayland_head_destroy(struct weston_head *base)
|
||||
{
|
||||
struct wayland_head *head = to_wayland_head(base);
|
||||
|
||||
assert(head);
|
||||
|
||||
if (head->parent_output)
|
||||
head->parent_output->head = NULL;
|
||||
|
||||
@@ -1474,6 +1485,9 @@ wayland_output_set_size(struct weston_output *base, int width, int height)
|
||||
struct weston_head *head;
|
||||
int output_width, output_height;
|
||||
|
||||
if (!output)
|
||||
return -1;
|
||||
|
||||
/* We can only be called once. */
|
||||
assert(!output->base.current_mode);
|
||||
|
||||
@@ -1564,13 +1578,10 @@ wayland_output_setup_fullscreen(struct wayland_output *output,
|
||||
return -1;
|
||||
|
||||
/* What should size be set if conditional is false? */
|
||||
if (b->parent.xdg_wm_base || b->parent.shell) {
|
||||
if (b->parent.xdg_wm_base) {
|
||||
if (output->parent.xdg_toplevel)
|
||||
xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel,
|
||||
output->parent.output);
|
||||
else if (output->parent.shell_surface)
|
||||
wl_shell_surface_set_fullscreen(output->parent.shell_surface,
|
||||
0, 0, NULL);
|
||||
|
||||
wl_display_roundtrip(b->parent.wl_display);
|
||||
|
||||
@@ -1594,36 +1605,6 @@ err_set_size:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_ping(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
wl_shell_surface_pong(shell_surface, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_configure(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t edges, int32_t width, int32_t height)
|
||||
{
|
||||
struct wayland_output *output = data;
|
||||
|
||||
output->parent.configure_width = width;
|
||||
output->parent.configure_height = height;
|
||||
|
||||
/* FIXME: implement resizing */
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
shell_surface_ping,
|
||||
shell_surface_configure,
|
||||
shell_surface_popup_done
|
||||
};
|
||||
|
||||
/* Events received from the wayland-server this compositor is client of: */
|
||||
|
||||
/* parent input interface */
|
||||
@@ -1801,9 +1782,6 @@ input_handle_button(void *data, struct wl_pointer *pointer,
|
||||
if (input->output->parent.xdg_toplevel)
|
||||
xdg_toplevel_move(input->output->parent.xdg_toplevel,
|
||||
input->parent.seat, serial);
|
||||
else if (input->output->parent.shell_surface)
|
||||
wl_shell_surface_move(input->output->parent.shell_surface,
|
||||
input->parent.seat, serial);
|
||||
frame_status_clear(input->output->frame,
|
||||
FRAME_STATUS_MOVE);
|
||||
return;
|
||||
@@ -2157,9 +2135,6 @@ input_handle_touch_down(void *data, struct wl_touch *wl_touch,
|
||||
if (output->parent.xdg_toplevel)
|
||||
xdg_toplevel_move(output->parent.xdg_toplevel,
|
||||
input->parent.seat, serial);
|
||||
else if (output->parent.shell_surface)
|
||||
wl_shell_surface_move(output->parent.shell_surface,
|
||||
input->parent.seat, serial);
|
||||
frame_status_clear(output->frame,
|
||||
FRAME_STATUS_MOVE);
|
||||
return;
|
||||
@@ -2187,10 +2162,6 @@ input_handle_touch_up(void *data, struct wl_touch *wl_touch,
|
||||
timespec_from_msec(&ts, time);
|
||||
|
||||
input->touch_points--;
|
||||
if (input->touch_points == 0) {
|
||||
input->touch_focus = NULL;
|
||||
input->touch_active = false;
|
||||
}
|
||||
|
||||
if (!output)
|
||||
return;
|
||||
@@ -2253,6 +2224,11 @@ input_handle_touch_frame(void *data, struct wl_touch *wl_touch)
|
||||
return;
|
||||
|
||||
notify_touch_frame(input->touch_device);
|
||||
|
||||
if (input->touch_points == 0) {
|
||||
input->touch_focus = NULL;
|
||||
input->touch_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2629,7 +2605,7 @@ wayland_parent_output_destroy(struct wayland_parent_output *output)
|
||||
wl_callback_destroy(output->sync_cb);
|
||||
|
||||
if (output->head)
|
||||
wayland_head_destroy(output->head);
|
||||
wayland_head_destroy(&output->head->base);
|
||||
|
||||
wl_output_destroy(output->global);
|
||||
free(output->physical.make);
|
||||
@@ -2671,10 +2647,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
||||
&xdg_wm_base_interface, 1);
|
||||
xdg_wm_base_add_listener(b->parent.xdg_wm_base,
|
||||
&wm_base_listener, b);
|
||||
} else if (strcmp(interface, "wl_shell") == 0) {
|
||||
b->parent.shell =
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_shell_interface, 1);
|
||||
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
|
||||
b->parent.fshell =
|
||||
wl_registry_bind(registry, name,
|
||||
@@ -2742,14 +2714,20 @@ wayland_destroy(struct weston_compositor *ec)
|
||||
{
|
||||
struct wayland_backend *b = to_wayland_backend(ec);
|
||||
struct weston_head *base, *next;
|
||||
struct wayland_parent_output *output, *next_output;
|
||||
struct wayland_input *input, *next_input;
|
||||
|
||||
wl_event_source_remove(b->parent.wl_source);
|
||||
|
||||
weston_compositor_shutdown(ec);
|
||||
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
|
||||
wayland_head_destroy(to_wayland_head(base));
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
|
||||
if (to_wayland_head(base))
|
||||
wayland_head_destroy(base);
|
||||
}
|
||||
|
||||
wl_list_for_each_safe(output, next_output, &b->parent.output_list, link)
|
||||
wayland_parent_output_destroy(output);
|
||||
|
||||
wl_list_for_each_safe(input, next_input, &b->input_list, link)
|
||||
wayland_input_destroy(input);
|
||||
@@ -2763,9 +2741,6 @@ wayland_destroy(struct weston_compositor *ec)
|
||||
if (b->parent.xdg_wm_base)
|
||||
xdg_wm_base_destroy(b->parent.xdg_wm_base);
|
||||
|
||||
if (b->parent.shell)
|
||||
wl_shell_destroy(b->parent.shell);
|
||||
|
||||
if (b->parent.fshell)
|
||||
zwp_fullscreen_shell_v1_release(b->parent.fshell);
|
||||
|
||||
@@ -2833,7 +2808,7 @@ fullscreen_binding(struct weston_keyboard *keyboard,
|
||||
return;
|
||||
|
||||
if (input->output->frame)
|
||||
wayland_output_set_fullscreen(input->output, 0, 0, NULL);
|
||||
wayland_output_set_fullscreen(input->output, 0, NULL);
|
||||
else
|
||||
wayland_output_set_windowed(input->output);
|
||||
|
||||
|
||||
+93
-47
@@ -119,6 +119,7 @@ struct x11_backend {
|
||||
xcb_atom_t cardinal;
|
||||
xcb_atom_t xkb_names;
|
||||
} atom;
|
||||
xcb_generic_event_t *prev_event;
|
||||
};
|
||||
|
||||
struct x11_head {
|
||||
@@ -151,15 +152,25 @@ struct window_delete_data {
|
||||
|
||||
struct gl_renderer_interface *gl_renderer;
|
||||
|
||||
static void
|
||||
x11_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct x11_head *
|
||||
to_x11_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != x11_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct x11_head, base);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct x11_output *
|
||||
to_x11_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != x11_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct x11_output, base);
|
||||
}
|
||||
|
||||
@@ -417,11 +428,14 @@ x11_output_start_repaint_loop(struct weston_output *output)
|
||||
|
||||
static int
|
||||
x11_output_repaint_gl(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct weston_compositor *ec;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
|
||||
ec->renderer->repaint_output(output_base, damage);
|
||||
|
||||
@@ -436,8 +450,8 @@ static void
|
||||
set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct x11_backend *b = to_x11_backend(ec);
|
||||
struct weston_compositor *ec;
|
||||
struct x11_backend *b;
|
||||
pixman_region32_t transformed_region;
|
||||
pixman_box32_t *rects;
|
||||
xcb_rectangle_t *output_rects;
|
||||
@@ -445,6 +459,12 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
|
||||
int nrects, i;
|
||||
xcb_generic_error_t *err;
|
||||
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
ec = output->base.compositor;
|
||||
b = to_x11_backend(ec);
|
||||
|
||||
pixman_region32_init(&transformed_region);
|
||||
pixman_region32_copy(&transformed_region, region);
|
||||
pixman_region32_translate(&transformed_region,
|
||||
@@ -486,15 +506,19 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
|
||||
|
||||
static int
|
||||
x11_output_repaint_shm(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct x11_backend *b = to_x11_backend(ec);
|
||||
struct weston_compositor *ec;
|
||||
struct x11_backend *b;
|
||||
xcb_void_cookie_t cookie;
|
||||
xcb_generic_error_t *err;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
b = to_x11_backend(ec);
|
||||
|
||||
pixman_renderer_output_set_buffer(output_base, output->hw_surface);
|
||||
ec->renderer->repaint_output(output_base, damage);
|
||||
|
||||
@@ -567,13 +591,13 @@ x11_output_set_wm_protocols(struct x11_backend *b,
|
||||
}
|
||||
|
||||
struct wm_normal_hints {
|
||||
uint32_t flags;
|
||||
uint32_t flags;
|
||||
uint32_t pad[4];
|
||||
int32_t min_width, min_height;
|
||||
int32_t max_width, max_height;
|
||||
int32_t width_inc, height_inc;
|
||||
int32_t min_aspect_x, min_aspect_y;
|
||||
int32_t max_aspect_x, max_aspect_y;
|
||||
int32_t width_inc, height_inc;
|
||||
int32_t min_aspect_x, min_aspect_y;
|
||||
int32_t max_aspect_x, max_aspect_y;
|
||||
int32_t base_width, base_height;
|
||||
int32_t win_gravity;
|
||||
};
|
||||
@@ -803,12 +827,13 @@ static int
|
||||
x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode)
|
||||
{
|
||||
struct x11_backend *b;
|
||||
struct x11_output *output;
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
static uint32_t values[2];
|
||||
int ret;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_x11_backend(base->compositor);
|
||||
output = to_x11_output(base);
|
||||
|
||||
if (mode->width == output->mode.width &&
|
||||
mode->height == output->mode.height)
|
||||
@@ -880,7 +905,11 @@ static int
|
||||
x11_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
struct x11_backend *backend = to_x11_backend(base->compositor);
|
||||
struct x11_backend *backend;
|
||||
|
||||
assert(output);
|
||||
|
||||
backend = to_x11_backend(base->compositor);
|
||||
|
||||
if (!output->base.enabled)
|
||||
return 0;
|
||||
@@ -905,6 +934,8 @@ x11_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
|
||||
assert(output);
|
||||
|
||||
x11_output_disable(&output->base);
|
||||
weston_output_release(&output->base);
|
||||
|
||||
@@ -915,7 +946,12 @@ static int
|
||||
x11_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
struct x11_backend *b = to_x11_backend(base->compositor);
|
||||
const struct weston_mode *mode = output->base.current_mode;
|
||||
struct x11_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_x11_backend(base->compositor);
|
||||
|
||||
static const char name[] = "Weston Compositor";
|
||||
static const char class[] = "weston-1\0Weston Compositor";
|
||||
@@ -954,8 +990,7 @@ x11_output_enable(struct weston_output *base)
|
||||
output->window,
|
||||
screen->root,
|
||||
0, 0,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
mode->width, mode->height,
|
||||
0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
screen->root_visual,
|
||||
@@ -1020,8 +1055,7 @@ x11_output_enable(struct weston_output *base)
|
||||
.use_shadow = true,
|
||||
};
|
||||
if (x11_output_init_shm(b, output,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height) < 0) {
|
||||
mode->width, mode->height) < 0) {
|
||||
weston_log("Failed to initialize SHM for the X11 output\n");
|
||||
goto err;
|
||||
}
|
||||
@@ -1062,9 +1096,7 @@ x11_output_enable(struct weston_output *base)
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
|
||||
weston_log("x11 output %dx%d, window id %d\n",
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
output->window);
|
||||
mode->width, mode->height, output->window);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1079,11 +1111,17 @@ static int
|
||||
x11_output_set_size(struct weston_output *base, int width, int height)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
struct x11_backend *b = to_x11_backend(base->compositor);
|
||||
struct x11_backend *b;
|
||||
struct weston_head *head;
|
||||
xcb_screen_t *scrn = b->screen;
|
||||
xcb_screen_t *scrn;
|
||||
int output_width, output_height;
|
||||
|
||||
if (!output)
|
||||
return -1;
|
||||
|
||||
b = to_x11_backend(base->compositor);
|
||||
scrn = b->screen;
|
||||
|
||||
/* We can only be called once. */
|
||||
assert(!output->base.current_mode);
|
||||
|
||||
@@ -1165,6 +1203,9 @@ x11_head_create(struct weston_compositor *compositor, const char *name)
|
||||
return -1;
|
||||
|
||||
weston_head_init(&head->base, name);
|
||||
|
||||
head->base.backend_id = x11_head_destroy;
|
||||
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_compositor_add_head(compositor, &head->base);
|
||||
|
||||
@@ -1172,8 +1213,12 @@ x11_head_create(struct weston_compositor *compositor, const char *name)
|
||||
}
|
||||
|
||||
static void
|
||||
x11_head_destroy(struct x11_head *head)
|
||||
x11_head_destroy(struct weston_head *base)
|
||||
{
|
||||
struct x11_head *head = to_x11_head(base);
|
||||
|
||||
assert(head);
|
||||
|
||||
weston_head_release(&head->base);
|
||||
free(head);
|
||||
}
|
||||
@@ -1450,7 +1495,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct x11_backend *b = data;
|
||||
struct x11_output *output;
|
||||
xcb_generic_event_t *event, *prev;
|
||||
xcb_generic_event_t *event;
|
||||
xcb_client_message_event_t *client_message;
|
||||
xcb_enter_notify_event_t *enter_notify;
|
||||
xcb_key_press_event_t *key_press, *key_release;
|
||||
@@ -1466,16 +1511,15 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
int count;
|
||||
struct timespec time;
|
||||
|
||||
prev = NULL;
|
||||
count = 0;
|
||||
while (x11_backend_next_event(b, &event, mask)) {
|
||||
response_type = event->response_type & ~0x80;
|
||||
|
||||
switch (prev ? prev->response_type & ~0x80 : 0x80) {
|
||||
switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) {
|
||||
case XCB_KEY_RELEASE:
|
||||
/* Suppress key repeat events; this is only used if we
|
||||
* don't have XCB XKB support. */
|
||||
key_release = (xcb_key_press_event_t *) prev;
|
||||
key_release = (xcb_key_press_event_t *) b->prev_event;
|
||||
key_press = (xcb_key_press_event_t *) event;
|
||||
if (response_type == XCB_KEY_PRESS &&
|
||||
key_release->time == key_press->time &&
|
||||
@@ -1483,8 +1527,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
/* Don't deliver the held key release
|
||||
* event or the new key press event. */
|
||||
free(event);
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
continue;
|
||||
} else {
|
||||
/* Deliver the held key release now
|
||||
@@ -1497,8 +1541,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
key_release->detail - 8,
|
||||
WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
STATE_UPDATE_AUTOMATIC);
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1522,8 +1566,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
notify_keyboard_focus_in(&b->core_seat, &b->keys,
|
||||
STATE_UPDATE_AUTOMATIC);
|
||||
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1548,7 +1592,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
/* If we don't have XKB, we need to use the lame
|
||||
* autorepeat detection above. */
|
||||
if (!b->has_xkb) {
|
||||
prev = event;
|
||||
b->prev_event = event;
|
||||
break;
|
||||
}
|
||||
key_release = (xcb_key_press_event_t *) event;
|
||||
@@ -1643,7 +1687,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
|
||||
break;
|
||||
|
||||
prev = event;
|
||||
b->prev_event = event;
|
||||
break;
|
||||
|
||||
case XCB_FOCUS_OUT:
|
||||
@@ -1677,13 +1721,13 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
#endif
|
||||
|
||||
count++;
|
||||
if (prev != event)
|
||||
free (event);
|
||||
if (b->prev_event != event)
|
||||
free(event);
|
||||
}
|
||||
|
||||
switch (prev ? prev->response_type & ~0x80 : 0x80) {
|
||||
switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) {
|
||||
case XCB_KEY_RELEASE:
|
||||
key_release = (xcb_key_press_event_t *) prev;
|
||||
key_release = (xcb_key_press_event_t *) b->prev_event;
|
||||
update_xkb_state_from_core(b, key_release->state);
|
||||
weston_compositor_get_time(&time);
|
||||
notify_key(&b->core_seat,
|
||||
@@ -1691,8 +1735,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
key_release->detail - 8,
|
||||
WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
STATE_UPDATE_AUTOMATIC);
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -1792,8 +1836,10 @@ x11_destroy(struct weston_compositor *ec)
|
||||
|
||||
weston_compositor_shutdown(ec); /* destroys outputs, too */
|
||||
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
|
||||
x11_head_destroy(to_x11_head(base));
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
|
||||
if (to_x11_head(base))
|
||||
x11_head_destroy(base);
|
||||
}
|
||||
|
||||
XCloseDisplay(backend->dpy);
|
||||
free(backend);
|
||||
|
||||
+12
-12
@@ -32,6 +32,8 @@
|
||||
#ifndef LIBWESTON_BACKEND_INTERNAL_H
|
||||
#define LIBWESTON_BACKEND_INTERNAL_H
|
||||
|
||||
struct weston_hdr_metadata_type1;
|
||||
|
||||
struct weston_backend {
|
||||
void (*destroy)(struct weston_compositor *compositor);
|
||||
|
||||
@@ -46,27 +48,21 @@ struct weston_backend {
|
||||
* Returns an opaque pointer, which the backend may use as private
|
||||
* data referring to the repaint cycle.
|
||||
*/
|
||||
void * (*repaint_begin)(struct weston_compositor *compositor);
|
||||
void (*repaint_begin)(struct weston_compositor *compositor);
|
||||
|
||||
/** Cancel a repaint sequence
|
||||
*
|
||||
* Cancels a repaint sequence, when an error has occurred during
|
||||
* one output's repaint; see repaint_begin.
|
||||
*
|
||||
* @param repaint_data Data returned by repaint_begin
|
||||
*/
|
||||
void (*repaint_cancel)(struct weston_compositor *compositor,
|
||||
void *repaint_data);
|
||||
void (*repaint_cancel)(struct weston_compositor *compositor);
|
||||
|
||||
/** Conclude a repaint sequence
|
||||
*
|
||||
* Called on successful completion of a repaint sequence; see
|
||||
* repaint_begin.
|
||||
*
|
||||
* @param repaint_data Data returned by repaint_begin
|
||||
*/
|
||||
int (*repaint_flush)(struct weston_compositor *compositor,
|
||||
void *repaint_data);
|
||||
int (*repaint_flush)(struct weston_compositor *compositor);
|
||||
|
||||
/** Allocate a new output
|
||||
*
|
||||
@@ -142,6 +138,10 @@ weston_head_set_subpixel(struct weston_head *head,
|
||||
void
|
||||
weston_head_set_transform(struct weston_head *head, uint32_t transform);
|
||||
|
||||
void
|
||||
weston_head_set_supported_eotf_mask(struct weston_head *head,
|
||||
uint32_t eotf_mask);
|
||||
|
||||
/* weston_output */
|
||||
|
||||
void
|
||||
@@ -154,9 +154,6 @@ weston_output_damage(struct weston_output *output);
|
||||
void
|
||||
weston_output_release(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_init_zoom(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_finish_frame(struct weston_output *output,
|
||||
const struct timespec *stamp,
|
||||
@@ -178,6 +175,9 @@ void
|
||||
weston_output_region_from_global(struct weston_output *output,
|
||||
pixman_region32_t *region);
|
||||
|
||||
const struct weston_hdr_metadata_type1 *
|
||||
weston_output_get_hdr_metadata_type1(const struct weston_output *output);
|
||||
|
||||
/* weston_seat */
|
||||
|
||||
void
|
||||
|
||||
@@ -310,8 +310,8 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor,
|
||||
/* 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)
|
||||
if (keyboard->grab == &keyboard->default_grab ||
|
||||
keyboard->grab == &keyboard->input_method_grab)
|
||||
install_binding_grab(keyboard,
|
||||
time,
|
||||
key,
|
||||
|
||||
@@ -110,7 +110,7 @@ clipboard_source_data(int fd, uint32_t mask, void *data)
|
||||
|
||||
static void
|
||||
clipboard_source_accept(struct weston_data_source *source,
|
||||
uint32_t time, const char *mime_type)
|
||||
uint32_t serial, const char *mime_type)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,37 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <libweston/libweston.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "color-lcms.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
static cmsUInt32Number
|
||||
cmlcms_get_render_intent(enum cmlcms_category cat,
|
||||
struct weston_surface *surface,
|
||||
struct weston_output *output)
|
||||
{
|
||||
/*
|
||||
* TODO: Take into account client provided content profile,
|
||||
* output profile, and the category of the wanted color
|
||||
* transformation.
|
||||
*/
|
||||
cmsUInt32Number intent = INTENT_RELATIVE_COLORIMETRIC;
|
||||
return intent;
|
||||
}
|
||||
|
||||
static struct cmlcms_color_profile *
|
||||
get_cprof_or_stock_sRGB(struct weston_color_manager_lcms *cm,
|
||||
struct weston_color_profile *cprof_base)
|
||||
{
|
||||
if (cprof_base)
|
||||
return get_cprof(cprof_base);
|
||||
else
|
||||
return cm->sRGB_profile;
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_destroy_color_transform(struct weston_color_transform *xform_base)
|
||||
{
|
||||
@@ -48,47 +73,52 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_surface_color_transform *surf_xform)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
/*
|
||||
* Assumes both content and output color spaces are sRGB SDR.
|
||||
* This defines the blending space as optical sRGB SDR.
|
||||
*/
|
||||
.type = CMLCMS_TYPE_EOTF_sRGB,
|
||||
};
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
|
||||
.input_profile = get_cprof_or_stock_sRGB(cm, NULL /* TODO: surface->color_profile */),
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
surface, output);
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
return false;
|
||||
|
||||
surf_xform->transform = &xform->base;
|
||||
surf_xform->identity_pipeline = true;
|
||||
/*
|
||||
* When we introduce LCMS plug-in we can precisely answer this question
|
||||
* by examining the color pipeline using precision parameters. For now
|
||||
* we just compare if it is same pointer or not.
|
||||
*/
|
||||
if (xform->search_key.input_profile == xform->search_key.output_profile)
|
||||
surf_xform->identity_pipeline = true;
|
||||
else
|
||||
surf_xform->identity_pipeline = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
/*
|
||||
* Assumes blending space is optical sRGB SDR and
|
||||
* output color space is sRGB SDR.
|
||||
*/
|
||||
.type = CMLCMS_TYPE_EOTF_sRGB_INV,
|
||||
};
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
|
||||
.input_profile = NULL,
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
NULL, output);
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
@@ -99,37 +129,54 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
|
||||
cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
/* Assumes output color space is sRGB SDR */
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
|
||||
.input_profile = cm->sRGB_profile,
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
NULL, output);
|
||||
|
||||
/*
|
||||
* Create a color transformation when output profile is not stock
|
||||
* sRGB profile.
|
||||
*/
|
||||
if (param.output_profile != cm->sRGB_profile) {
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
return false;
|
||||
*xform_out = &xform->base;
|
||||
} else {
|
||||
*xform_out = NULL; /* Identity transform */
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
|
||||
cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
/* Assumes blending space is optical sRGB SDR */
|
||||
.type = CMLCMS_TYPE_EOTF_sRGB,
|
||||
};
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
|
||||
.input_profile = cm->sRGB_profile,
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
NULL, output);
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
@@ -139,6 +186,145 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
|
||||
return true;
|
||||
}
|
||||
|
||||
static float
|
||||
meta_clamp(float value, const char *valname, float min, float max,
|
||||
struct weston_output *output)
|
||||
{
|
||||
float ret = value;
|
||||
|
||||
/* Paranoia against NaN */
|
||||
if (!(ret >= min))
|
||||
ret = min;
|
||||
|
||||
if (!(ret <= max))
|
||||
ret = max;
|
||||
|
||||
if (ret != value) {
|
||||
weston_log("output '%s' clamping %s value from %f to %f.\n",
|
||||
output->name, valname, value, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_hdr_meta(struct weston_output *output,
|
||||
struct weston_hdr_metadata_type1 *hdr_meta)
|
||||
{
|
||||
const struct weston_color_characteristics *cc;
|
||||
|
||||
hdr_meta->group_mask = 0;
|
||||
|
||||
/* Only SMPTE ST 2084 mode uses HDR Static Metadata Type 1 */
|
||||
if (weston_output_get_eotf_mode(output) != WESTON_EOTF_MODE_ST2084)
|
||||
return true;
|
||||
|
||||
/* ICC profile overrides color characteristics */
|
||||
if (output->color_profile) {
|
||||
/*
|
||||
* TODO: extract characteristics from profile?
|
||||
* Get dynamic range from weston_color_characteristics?
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cc = weston_output_get_color_characteristics(output);
|
||||
|
||||
/* Target content chromaticity */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
hdr_meta->primary[i].x = meta_clamp(cc->primary[i].x,
|
||||
"primary", 0.0, 1.0,
|
||||
output);
|
||||
hdr_meta->primary[i].y = meta_clamp(cc->primary[i].y,
|
||||
"primary", 0.0, 1.0,
|
||||
output);
|
||||
}
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES;
|
||||
}
|
||||
|
||||
/* Target content white point */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE) {
|
||||
hdr_meta->white.x = meta_clamp(cc->white.x, "white",
|
||||
0.0, 1.0, output);
|
||||
hdr_meta->white.y = meta_clamp(cc->white.y, "white",
|
||||
0.0, 1.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_WHITE;
|
||||
}
|
||||
|
||||
/* Target content peak and max mastering luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL) {
|
||||
hdr_meta->maxDML = meta_clamp(cc->max_luminance, "maxDML",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->maxCLL = meta_clamp(cc->max_luminance, "maxCLL",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML;
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL;
|
||||
}
|
||||
|
||||
/* Target content min mastering luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MINL) {
|
||||
hdr_meta->minDML = meta_clamp(cc->min_luminance, "minDML",
|
||||
0.0001, 6.5535, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MINDML;
|
||||
}
|
||||
|
||||
/* Target content max frame-average luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL) {
|
||||
hdr_meta->maxFALL = meta_clamp(cc->maxFALL, "maxFALL",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct weston_output_color_outcome *
|
||||
cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct weston_output_color_outcome *co;
|
||||
|
||||
co = zalloc(sizeof *co);
|
||||
if (!co)
|
||||
return NULL;
|
||||
|
||||
if (!cmlcms_get_hdr_meta(output, &co->hdr_meta))
|
||||
goto out_fail;
|
||||
|
||||
/*
|
||||
* TODO: if output->color_profile is NULL, maybe manufacture a
|
||||
* profile from weston_color_characteristics if it has enough
|
||||
* information?
|
||||
* Or let the frontend decide to call a "create a profile from
|
||||
* characteristics" API?
|
||||
*/
|
||||
|
||||
/* TODO: take container color space into account */
|
||||
|
||||
if (!cmlcms_get_blend_to_output_color_transform(cm, output,
|
||||
&co->from_blend_to_output))
|
||||
goto out_fail;
|
||||
|
||||
if (!cmlcms_get_sRGB_to_blend_color_transform(cm, output,
|
||||
&co->from_sRGB_to_blend))
|
||||
goto out_fail;
|
||||
|
||||
if (!cmlcms_get_sRGB_to_output_color_transform(cm, output,
|
||||
&co->from_sRGB_to_output))
|
||||
goto out_fail;
|
||||
|
||||
return co;
|
||||
|
||||
out_fail:
|
||||
weston_output_color_outcome_destroy(&co);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
lcms_error_logger(cmsContext context_id,
|
||||
cmsUInt32Number error_code,
|
||||
@@ -165,6 +351,10 @@ cmlcms_init(struct weston_color_manager *cm_base)
|
||||
|
||||
cmsSetLogErrorHandlerTHR(cm->lcms_ctx, lcms_error_logger);
|
||||
|
||||
if (!cmlcms_create_stock_profile(cm)) {
|
||||
weston_log("color-lcms: error: cmlcms_create_stock_profile failed\n");
|
||||
return false;
|
||||
}
|
||||
weston_log("LittleCMS %d initialized.\n", cmsGetEncodedCMMversion());
|
||||
|
||||
return true;
|
||||
@@ -175,6 +365,8 @@ cmlcms_destroy(struct weston_color_manager *cm_base)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
|
||||
if (cm->sRGB_profile)
|
||||
cmlcms_color_profile_destroy(cm->sRGB_profile);
|
||||
assert(wl_list_empty(&cm->color_transform_list));
|
||||
assert(wl_list_empty(&cm->color_profile_list));
|
||||
|
||||
@@ -199,13 +391,8 @@ weston_color_manager_create(struct weston_compositor *compositor)
|
||||
cm->base.destroy_color_profile = cmlcms_destroy_color_profile;
|
||||
cm->base.get_color_profile_from_icc = cmlcms_get_color_profile_from_icc;
|
||||
cm->base.destroy_color_transform = cmlcms_destroy_color_transform;
|
||||
cm->base.get_surface_color_transform =
|
||||
cmlcms_get_surface_color_transform;
|
||||
cm->base.get_output_color_transform = cmlcms_get_output_color_transform;
|
||||
cm->base.get_sRGB_to_output_color_transform =
|
||||
cmlcms_get_sRGB_to_output_color_transform;
|
||||
cm->base.get_sRGB_to_blend_color_transform =
|
||||
cmlcms_get_sRGB_to_blend_color_transform;
|
||||
cm->base.get_surface_color_transform = cmlcms_get_surface_color_transform;
|
||||
cm->base.create_output_color_outcome = cmlcms_create_output_color_outcome;
|
||||
|
||||
wl_list_init(&cm->color_transform_list);
|
||||
wl_list_init(&cm->color_profile_list);
|
||||
|
||||
@@ -39,6 +39,7 @@ struct weston_color_manager_lcms {
|
||||
|
||||
struct wl_list color_transform_list; /* cmlcms_color_transform::link */
|
||||
struct wl_list color_profile_list; /* cmlcms_color_profile::link */
|
||||
struct cmlcms_color_profile *sRGB_profile; /* stock profile */
|
||||
};
|
||||
|
||||
static inline struct weston_color_manager_lcms *
|
||||
@@ -59,6 +60,58 @@ struct cmlcms_color_profile {
|
||||
|
||||
cmsHPROFILE profile;
|
||||
struct cmlcms_md5_sum md5sum;
|
||||
|
||||
/**
|
||||
* If the profile does support being an output profile and it is used as an
|
||||
* output then this field represents a light linearizing transfer function
|
||||
* and it can not be null. The field is null only if the profile is not
|
||||
* usable as an output profile. The field is set when cmlcms_color_profile
|
||||
* is created.
|
||||
*/
|
||||
cmsToneCurve *output_eotf[3];
|
||||
|
||||
/**
|
||||
* If the profile does support being an output profile and it is used as an
|
||||
* output then this field represents a concatenation of inverse EOTF + VCGT,
|
||||
* if the tag exists and it can not be null.
|
||||
* VCGT is part of monitor calibration which means: even though we must
|
||||
* apply VCGT in the compositor, we pretend that it happens inside the
|
||||
* monitor. This is how the classic color management and ICC profiles work.
|
||||
* The ICC profile (ignoring the VCGT tag) characterizes the output which
|
||||
* is VCGT + monitor behavior. The field is null only if the profile is not
|
||||
* usable as an output profile. The field is set when cmlcms_color_profile
|
||||
* is created.
|
||||
*/
|
||||
cmsToneCurve *output_inv_eotf_vcgt[3];
|
||||
|
||||
/**
|
||||
* VCGT tag cached from output profile, it could be null if not exist
|
||||
*/
|
||||
cmsToneCurve *vcgt[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of LCMS transforms
|
||||
*/
|
||||
enum cmlcms_category {
|
||||
/**
|
||||
* Uses combination of input profile with output profile, but
|
||||
* without INV EOTF or with additional EOTF in the transform pipeline
|
||||
* input→blend = input profile + output profile + output EOTF
|
||||
*/
|
||||
CMLCMS_CATEGORY_INPUT_TO_BLEND = 0,
|
||||
|
||||
/**
|
||||
* Uses INV EOTF only concatenated with VCGT tag if present
|
||||
* blend→output = output inverse EOTF + VCGT
|
||||
*/
|
||||
CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
|
||||
|
||||
/**
|
||||
* Transform uses input profile and output profile as is
|
||||
* input→output = input profile + output profile + VCGT
|
||||
*/
|
||||
CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
|
||||
};
|
||||
|
||||
static inline struct cmlcms_color_profile *
|
||||
@@ -78,18 +131,12 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm,
|
||||
void
|
||||
cmlcms_destroy_color_profile(struct weston_color_profile *cprof_base);
|
||||
|
||||
/*
|
||||
* Perhaps a placeholder, until we get actual color spaces involved and
|
||||
* see how this would work better.
|
||||
*/
|
||||
enum cmlcms_color_transform_type {
|
||||
CMLCMS_TYPE_EOTF_sRGB = 0,
|
||||
CMLCMS_TYPE_EOTF_sRGB_INV,
|
||||
CMLCMS_TYPE__END,
|
||||
};
|
||||
|
||||
struct cmlcms_color_transform_search_param {
|
||||
enum cmlcms_color_transform_type type;
|
||||
enum cmlcms_category category;
|
||||
struct cmlcms_color_profile *input_profile;
|
||||
struct cmlcms_color_profile *output_profile;
|
||||
cmsUInt32Number intent_output; /* selected intent from output profile */
|
||||
};
|
||||
|
||||
struct cmlcms_color_transform {
|
||||
@@ -100,8 +147,15 @@ struct cmlcms_color_transform {
|
||||
|
||||
struct cmlcms_color_transform_search_param search_key;
|
||||
|
||||
/* for EOTF types */
|
||||
cmsToneCurve *curve;
|
||||
/**
|
||||
* 3D LUT color mapping part of the transformation, if needed.
|
||||
* For category CMLCMS_CATEGORY_INPUT_TO_OUTPUT it includes pre-curve and
|
||||
* post-curve.
|
||||
* For category CMLCMS_CATEGORY_INPUT_TO_BLEND it includes pre-curve.
|
||||
* For category CMLCMS_CATEGORY_BLEND_TO_OUTPUT and when identity it is
|
||||
* not used
|
||||
*/
|
||||
cmsHTRANSFORM cmap_3dlut;
|
||||
};
|
||||
|
||||
static inline struct cmlcms_color_transform *
|
||||
@@ -117,4 +171,27 @@ cmlcms_color_transform_get(struct weston_color_manager_lcms *cm,
|
||||
void
|
||||
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform);
|
||||
|
||||
struct cmlcms_color_profile *
|
||||
ref_cprof(struct cmlcms_color_profile *cprof);
|
||||
|
||||
void
|
||||
unref_cprof(struct cmlcms_color_profile *cprof);
|
||||
|
||||
bool
|
||||
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm);
|
||||
|
||||
void
|
||||
cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof);
|
||||
|
||||
bool
|
||||
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
|
||||
cmsHPROFILE hProfile,
|
||||
cmsToneCurve *output_eotf[3],
|
||||
cmsToneCurve *output_inv_eotf_vcgt[3],
|
||||
cmsToneCurve *vcgt[3],
|
||||
unsigned int num_points);
|
||||
|
||||
unsigned int
|
||||
cmlcms_reasonable_1D_points(void);
|
||||
|
||||
#endif /* WESTON_COLOR_LCMS_H */
|
||||
|
||||
@@ -36,6 +36,223 @@
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
struct xyz_arr_flt {
|
||||
float v[3];
|
||||
};
|
||||
|
||||
static double
|
||||
xyz_dot_prod(const struct xyz_arr_flt a, const struct xyz_arr_flt b)
|
||||
{
|
||||
return (double)a.v[0] * b.v[0] +
|
||||
(double)a.v[1] * b.v[1] +
|
||||
(double)a.v[2] * b.v[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Graeme sketched a linearization method there:
|
||||
* https://lists.freedesktop.org/archives/wayland-devel/2019-March/040171.html
|
||||
*/
|
||||
static bool
|
||||
build_eotf_from_clut_profile(cmsContext lcms_ctx,
|
||||
cmsHPROFILE profile,
|
||||
cmsToneCurve *output_eotf[3],
|
||||
int num_points)
|
||||
{
|
||||
int ch, point;
|
||||
float *curve_array[3];
|
||||
float *red = NULL;
|
||||
cmsHPROFILE xyz_profile = NULL;
|
||||
cmsHTRANSFORM transform_rgb_to_xyz = NULL;
|
||||
bool ret = false;
|
||||
const float div = num_points - 1;
|
||||
|
||||
red = malloc(sizeof(float) * num_points * 3);
|
||||
if (!red)
|
||||
goto release;
|
||||
|
||||
curve_array[0] = red;
|
||||
curve_array[1] = red + num_points;
|
||||
curve_array[2] = red + 2 * num_points;
|
||||
|
||||
xyz_profile = cmsCreateXYZProfileTHR(lcms_ctx);
|
||||
if (!xyz_profile)
|
||||
goto release;
|
||||
|
||||
transform_rgb_to_xyz = cmsCreateTransformTHR(lcms_ctx, profile,
|
||||
TYPE_RGB_FLT, xyz_profile,
|
||||
TYPE_XYZ_FLT,
|
||||
INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
0);
|
||||
if (!transform_rgb_to_xyz)
|
||||
goto release;
|
||||
|
||||
for (ch = 0; ch < 3; ch++) {
|
||||
struct xyz_arr_flt prim_xyz_max;
|
||||
struct xyz_arr_flt prim_xyz;
|
||||
double xyz_square_magnitude;
|
||||
float rgb[3] = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
rgb[ch] = 1.0f;
|
||||
cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz_max.v, 1);
|
||||
|
||||
/**
|
||||
* Calculate xyz square of magnitude uses single channel 100% and
|
||||
* others are zero.
|
||||
*/
|
||||
xyz_square_magnitude = xyz_dot_prod(prim_xyz_max, prim_xyz_max);
|
||||
/**
|
||||
* Build rgb tone curves
|
||||
*/
|
||||
for (point = 0; point < num_points; point++) {
|
||||
rgb[ch] = (float)point / div;
|
||||
cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz.v, 1);
|
||||
curve_array[ch][point] = xyz_dot_prod(prim_xyz,
|
||||
prim_xyz_max) /
|
||||
xyz_square_magnitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create LCMS object of rgb tone curves and validate whether
|
||||
* monotonic
|
||||
*/
|
||||
output_eotf[ch] = cmsBuildTabulatedToneCurveFloat(lcms_ctx,
|
||||
num_points,
|
||||
curve_array[ch]);
|
||||
if (!output_eotf[ch])
|
||||
goto release;
|
||||
if (!cmsIsToneCurveMonotonic(output_eotf[ch])) {
|
||||
/**
|
||||
* It is interesting to see how this profile was created.
|
||||
* We assume that such a curve could not be used for linearization
|
||||
* of arbitrary profile.
|
||||
*/
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
ret = true;
|
||||
|
||||
release:
|
||||
if (transform_rgb_to_xyz)
|
||||
cmsDeleteTransform(transform_rgb_to_xyz);
|
||||
if (xyz_profile)
|
||||
cmsCloseProfile(xyz_profile);
|
||||
free(red);
|
||||
if (ret == false)
|
||||
cmsFreeToneCurveTriple(output_eotf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenation of two monotonic tone curves.
|
||||
* LCMS API cmsJoinToneCurve does y = Y^-1(X(t)),
|
||||
* but want to have y = Y^(X(t))
|
||||
*/
|
||||
static cmsToneCurve *
|
||||
lcmsJoinToneCurve(cmsContext context_id, const cmsToneCurve *X,
|
||||
const cmsToneCurve *Y, unsigned int resulting_points)
|
||||
{
|
||||
cmsToneCurve *out = NULL;
|
||||
float t, x;
|
||||
float *res = NULL;
|
||||
unsigned int i;
|
||||
|
||||
res = zalloc(resulting_points * sizeof(float));
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < resulting_points; i++) {
|
||||
t = (float)i / (resulting_points - 1);
|
||||
x = cmsEvalToneCurveFloat(X, t);
|
||||
res[i] = cmsEvalToneCurveFloat(Y, x);
|
||||
}
|
||||
|
||||
out = cmsBuildTabulatedToneCurveFloat(context_id, resulting_points, res);
|
||||
|
||||
error:
|
||||
if (res != NULL)
|
||||
free(res);
|
||||
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Extract EOTF from matrix-shaper and cLUT profiles,
|
||||
* then invert and concatenate with 'vcgt' curve if it
|
||||
* is available.
|
||||
*/
|
||||
bool
|
||||
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
|
||||
cmsHPROFILE hProfile,
|
||||
cmsToneCurve *output_eotf[3],
|
||||
cmsToneCurve *output_inv_eotf_vcgt[3],
|
||||
cmsToneCurve *vcgt[3],
|
||||
unsigned int num_points)
|
||||
{
|
||||
cmsToneCurve *curve = NULL;
|
||||
const cmsToneCurve * const *vcgt_curves;
|
||||
unsigned i;
|
||||
cmsTagSignature tags[] = {
|
||||
cmsSigRedTRCTag, cmsSigGreenTRCTag, cmsSigBlueTRCTag
|
||||
};
|
||||
|
||||
if (cmsIsMatrixShaper(hProfile)) {
|
||||
/**
|
||||
* Optimization for matrix-shaper profile
|
||||
* May have 1DLUT->3x3->3x3->1DLUT, 1DLUT->3x3->1DLUT
|
||||
*/
|
||||
for (i = 0 ; i < 3; i++) {
|
||||
curve = cmsReadTag(hProfile, tags[i]);
|
||||
if (!curve)
|
||||
goto fail;
|
||||
output_eotf[i] = cmsDupToneCurve(curve);
|
||||
if (!output_eotf[i])
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Linearization of cLUT profile may have 1DLUT->3DLUT->1DLUT,
|
||||
* 1DLUT->3DLUT, 3DLUT
|
||||
*/
|
||||
if (!build_eotf_from_clut_profile(lcms_ctx, hProfile,
|
||||
output_eotf, num_points))
|
||||
goto fail;
|
||||
}
|
||||
/**
|
||||
* If the caller looking for eotf only then return early.
|
||||
* It could be used for input profile when identity case: EOTF + INV_EOTF
|
||||
* in pipeline only.
|
||||
*/
|
||||
if (output_inv_eotf_vcgt == NULL)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
curve = cmsReverseToneCurve(output_eotf[i]);
|
||||
if (!curve)
|
||||
goto fail;
|
||||
output_inv_eotf_vcgt[i] = curve;
|
||||
}
|
||||
vcgt_curves = cmsReadTag(hProfile, cmsSigVcgtTag);
|
||||
if (vcgt_curves && vcgt_curves[0] && vcgt_curves[1] && vcgt_curves[2]) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
curve = lcmsJoinToneCurve(lcms_ctx,
|
||||
output_inv_eotf_vcgt[i],
|
||||
vcgt_curves[i], num_points);
|
||||
if (!curve)
|
||||
goto fail;
|
||||
cmsFreeToneCurve(output_inv_eotf_vcgt[i]);
|
||||
output_inv_eotf_vcgt[i] = curve;
|
||||
if (vcgt)
|
||||
vcgt[i] = cmsDupToneCurve(vcgt_curves[i]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
fail:
|
||||
cmsFreeToneCurveTriple(output_eotf);
|
||||
cmsFreeToneCurveTriple(output_inv_eotf_vcgt);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* FIXME: sync with spec! */
|
||||
static bool
|
||||
validate_icc_profile(cmsHPROFILE profile, char **errmsg)
|
||||
@@ -102,15 +319,37 @@ cmlcms_color_profile_create(struct weston_color_manager_lcms *cm,
|
||||
return cprof;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
wl_list_remove(&cprof->link);
|
||||
cmsFreeToneCurveTriple(cprof->vcgt);
|
||||
cmsFreeToneCurveTriple(cprof->output_eotf);
|
||||
cmsFreeToneCurveTriple(cprof->output_inv_eotf_vcgt);
|
||||
cmsCloseProfile(cprof->profile);
|
||||
free(cprof->base.description);
|
||||
free(cprof);
|
||||
}
|
||||
|
||||
struct cmlcms_color_profile *
|
||||
ref_cprof(struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
if (!cprof)
|
||||
return NULL;
|
||||
|
||||
weston_color_profile_ref(&cprof->base);
|
||||
return cprof;
|
||||
}
|
||||
|
||||
void
|
||||
unref_cprof(struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
if (!cprof)
|
||||
return;
|
||||
|
||||
weston_color_profile_unref(&cprof->base);
|
||||
}
|
||||
|
||||
static char *
|
||||
make_icc_file_description(cmsHPROFILE profile,
|
||||
const struct cmlcms_md5_sum *md5sum,
|
||||
@@ -131,6 +370,52 @@ make_icc_file_description(cmsHPROFILE profile,
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Build stock profile which available for clients unaware of color management
|
||||
*/
|
||||
bool
|
||||
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm)
|
||||
{
|
||||
cmsHPROFILE profile;
|
||||
struct cmlcms_md5_sum md5sum;
|
||||
char *desc = NULL;
|
||||
|
||||
profile = cmsCreate_sRGBProfileTHR(cm->lcms_ctx);
|
||||
if (!profile) {
|
||||
weston_log("color-lcms: error: cmsCreate_sRGBProfileTHR failed\n");
|
||||
return false;
|
||||
}
|
||||
if (!cmsMD5computeID(profile)) {
|
||||
weston_log("Failed to compute MD5 for ICC profile\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
cmsGetHeaderProfileID(profile, md5sum.bytes);
|
||||
desc = make_icc_file_description(profile, &md5sum, "sRGB stock");
|
||||
if (!desc)
|
||||
goto err_close;
|
||||
|
||||
cm->sRGB_profile = cmlcms_color_profile_create(cm, profile, desc, NULL);
|
||||
if (!cm->sRGB_profile)
|
||||
goto err_close;
|
||||
|
||||
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
|
||||
cm->sRGB_profile->profile,
|
||||
cm->sRGB_profile->output_eotf,
|
||||
cm->sRGB_profile->output_inv_eotf_vcgt,
|
||||
cm->sRGB_profile->vcgt,
|
||||
cmlcms_reasonable_1D_points()))
|
||||
goto err_close;
|
||||
|
||||
return true;
|
||||
|
||||
err_close:
|
||||
free(desc);
|
||||
cmsCloseProfile(profile);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
|
||||
const void *icc_data,
|
||||
|
||||
@@ -33,48 +33,98 @@
|
||||
#include "color-lcms.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
/* Arguments to cmsBuildParametricToneCurve() */
|
||||
struct tone_curve_def {
|
||||
cmsInt32Number cmstype;
|
||||
cmsFloat64Number params[5];
|
||||
};
|
||||
|
||||
/*
|
||||
* LCMS uses the required number of 'params' based on 'cmstype', the parametric
|
||||
* tone curve number. LCMS honors negative 'cmstype' as inverse function.
|
||||
* These are LCMS built-in parametric tone curves.
|
||||
/**
|
||||
* The method is used in linearization of an arbitrary color profile
|
||||
* when EOTF is retrieved we want to know a generic way to decide the number
|
||||
* of points
|
||||
*/
|
||||
static const struct tone_curve_def predefined_eotf_curves[] = {
|
||||
[CMLCMS_TYPE_EOTF_sRGB] = {
|
||||
.cmstype = 4,
|
||||
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
|
||||
},
|
||||
[CMLCMS_TYPE_EOTF_sRGB_INV] = {
|
||||
.cmstype = -4,
|
||||
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
|
||||
},
|
||||
};
|
||||
unsigned int
|
||||
cmlcms_reasonable_1D_points(void)
|
||||
{
|
||||
return 1024;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cmlcms_reasonable_3D_points(void)
|
||||
{
|
||||
return 33;
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_fill_in_tone_curve(struct weston_color_transform *xform_base,
|
||||
float *values, unsigned len)
|
||||
fill_in_curves(cmsToneCurve *curves[3], float *values, unsigned len)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
||||
float *R_lut = values;
|
||||
float *G_lut = R_lut + len;
|
||||
float *B_lut = G_lut + len;
|
||||
unsigned i;
|
||||
cmsFloat32Number x, y;
|
||||
|
||||
assert(xform->curve != NULL);
|
||||
assert(len > 1);
|
||||
cmsFloat32Number x;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
x = (double)i / (len - 1);
|
||||
y = cmsEvalToneCurveFloat(xform->curve, x);
|
||||
R_lut[i] = y;
|
||||
G_lut[i] = y;
|
||||
B_lut[i] = y;
|
||||
R_lut[i] = cmsEvalToneCurveFloat(curves[0], x);
|
||||
G_lut[i] = cmsEvalToneCurveFloat(curves[1], x);
|
||||
B_lut[i] = cmsEvalToneCurveFloat(curves[2], x);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base,
|
||||
float *values, unsigned len)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
||||
|
||||
assert(xform->search_key.category == CMLCMS_CATEGORY_BLEND_TO_OUTPUT);
|
||||
|
||||
assert(len > 1);
|
||||
|
||||
fill_in_curves(xform->search_key.output_profile->output_inv_eotf_vcgt,
|
||||
values, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp value to [0.0, 1.0], except pass NaN through.
|
||||
*
|
||||
* This function is not intended for hiding NaN.
|
||||
*/
|
||||
static float
|
||||
ensure_unorm(float v)
|
||||
{
|
||||
if (v <= 0.0f)
|
||||
return 0.0f;
|
||||
if (v > 1.0f)
|
||||
return 1.0f;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_fill_in_3dlut(struct weston_color_transform *xform_base,
|
||||
float *lut, unsigned int len)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
||||
float rgb_in[3];
|
||||
float rgb_out[3];
|
||||
unsigned int index;
|
||||
unsigned int value_b, value_r, value_g;
|
||||
float divider = len - 1;
|
||||
|
||||
assert(xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_BLEND ||
|
||||
xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT);
|
||||
|
||||
for (value_b = 0; value_b < len; value_b++) {
|
||||
for (value_g = 0; value_g < len; value_g++) {
|
||||
for (value_r = 0; value_r < len; value_r++) {
|
||||
rgb_in[0] = (float)value_r / divider;
|
||||
rgb_in[1] = (float)value_g / divider;
|
||||
rgb_in[2] = (float)value_b / divider;
|
||||
|
||||
cmsDoTransform(xform->cmap_3dlut, rgb_in, rgb_out, 1);
|
||||
|
||||
index = 3 * (value_r + len * (value_g + len * value_b));
|
||||
lut[index ] = ensure_unorm(rgb_out[0]);
|
||||
lut[index + 1] = ensure_unorm(rgb_out[1]);
|
||||
lut[index + 2] = ensure_unorm(rgb_out[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,55 +132,135 @@ void
|
||||
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform)
|
||||
{
|
||||
wl_list_remove(&xform->link);
|
||||
if (xform->curve)
|
||||
cmsFreeToneCurve(xform->curve);
|
||||
|
||||
if (xform->cmap_3dlut)
|
||||
cmsDeleteTransform(xform->cmap_3dlut);
|
||||
|
||||
unref_cprof(xform->search_key.input_profile);
|
||||
unref_cprof(xform->search_key.output_profile);
|
||||
free(xform);
|
||||
}
|
||||
|
||||
static bool
|
||||
xform_set_cmap_3dlut(struct cmlcms_color_transform *xform,
|
||||
cmsHPROFILE input_profile,
|
||||
cmsHPROFILE output_profile,
|
||||
cmsToneCurve *curves[3],
|
||||
cmsUInt32Number intent)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(xform->base.cm);
|
||||
cmsHPROFILE arr_prof[3] = { input_profile, output_profile, NULL };
|
||||
int num_profiles = 2;
|
||||
|
||||
if (curves[0]) {
|
||||
arr_prof[2] = cmsCreateLinearizationDeviceLinkTHR(cm->lcms_ctx,
|
||||
cmsSigRgbData,
|
||||
curves);
|
||||
if (!arr_prof[2])
|
||||
return false;
|
||||
|
||||
num_profiles = 3;
|
||||
}
|
||||
|
||||
xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(cm->lcms_ctx,
|
||||
arr_prof,
|
||||
num_profiles,
|
||||
TYPE_RGB_FLT,
|
||||
TYPE_RGB_FLT,
|
||||
intent,
|
||||
0);
|
||||
if (!xform->cmap_3dlut) {
|
||||
cmsCloseProfile(arr_prof[2]);
|
||||
weston_log("color-lcms error: fail cmsCreateMultiprofileTransformTHR.\n");
|
||||
return false;
|
||||
}
|
||||
xform->base.mapping.type = WESTON_COLOR_MAPPING_TYPE_3D_LUT;
|
||||
xform->base.mapping.u.lut3d.fill_in = cmlcms_fill_in_3dlut;
|
||||
xform->base.mapping.u.lut3d.optimal_len =
|
||||
cmlcms_reasonable_3D_points();
|
||||
cmsCloseProfile(arr_prof[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct cmlcms_color_transform *
|
||||
cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
|
||||
const struct cmlcms_color_transform_search_param *param)
|
||||
const struct cmlcms_color_transform_search_param *search_param)
|
||||
{
|
||||
struct cmlcms_color_profile *input_profile = search_param->input_profile;
|
||||
struct cmlcms_color_profile *output_profile = search_param->output_profile;
|
||||
struct cmlcms_color_transform *xform;
|
||||
const struct tone_curve_def *tonedef;
|
||||
|
||||
if (param->type < 0 || param->type >= CMLCMS_TYPE__END) {
|
||||
weston_log("color-lcms error: bad color transform type in %s.\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
tonedef = &predefined_eotf_curves[param->type];
|
||||
bool ok = false;
|
||||
|
||||
xform = zalloc(sizeof *xform);
|
||||
if (!xform)
|
||||
return NULL;
|
||||
|
||||
xform->curve = cmsBuildParametricToneCurve(cm->lcms_ctx,
|
||||
tonedef->cmstype,
|
||||
tonedef->params);
|
||||
if (xform->curve == NULL) {
|
||||
weston_log("color-lcms error: failed to build parametric tone curve.\n");
|
||||
free(xform);
|
||||
return NULL;
|
||||
weston_color_transform_init(&xform->base, &cm->base);
|
||||
wl_list_init(&xform->link);
|
||||
xform->search_key = *search_param;
|
||||
xform->search_key.input_profile = ref_cprof(input_profile);
|
||||
xform->search_key.output_profile = ref_cprof(output_profile);
|
||||
|
||||
/* Ensure the linearization etc. have been extracted. */
|
||||
if (!output_profile->output_eotf[0]) {
|
||||
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
|
||||
output_profile->profile,
|
||||
output_profile->output_eotf,
|
||||
output_profile->output_inv_eotf_vcgt,
|
||||
output_profile->vcgt,
|
||||
cmlcms_reasonable_1D_points()))
|
||||
goto error;
|
||||
}
|
||||
|
||||
weston_color_transform_init(&xform->base, &cm->base);
|
||||
xform->search_key = *param;
|
||||
switch (search_param->category) {
|
||||
case CMLCMS_CATEGORY_INPUT_TO_BLEND:
|
||||
/* Use EOTF to linearize the result. */
|
||||
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
|
||||
output_profile->profile,
|
||||
output_profile->output_eotf,
|
||||
search_param->intent_output);
|
||||
break;
|
||||
|
||||
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
|
||||
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_tone_curve;
|
||||
xform->base.pre_curve.u.lut_3x1d.optimal_len = 256;
|
||||
case CMLCMS_CATEGORY_INPUT_TO_OUTPUT:
|
||||
/* Apply also VCGT if it exists. */
|
||||
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
|
||||
output_profile->profile,
|
||||
output_profile->vcgt,
|
||||
search_param->intent_output);
|
||||
break;
|
||||
|
||||
case CMLCMS_CATEGORY_BLEND_TO_OUTPUT:
|
||||
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
|
||||
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_pre_curve;
|
||||
xform->base.pre_curve.u.lut_3x1d.optimal_len =
|
||||
cmlcms_reasonable_1D_points();
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
goto error;
|
||||
|
||||
wl_list_insert(&cm->color_transform_list, &xform->link);
|
||||
|
||||
return xform;
|
||||
|
||||
error:
|
||||
cmlcms_color_transform_destroy(xform);
|
||||
weston_log("CM cmlcms_color_transform_create failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
transform_matches_params(const struct cmlcms_color_transform *xform,
|
||||
const struct cmlcms_color_transform_search_param *param)
|
||||
{
|
||||
if (xform->search_key.type != param->type)
|
||||
if (xform->search_key.category != param->category)
|
||||
return false;
|
||||
|
||||
if (xform->search_key.intent_output != param->intent_output ||
|
||||
xform->search_key.output_profile != param->output_profile ||
|
||||
xform->search_key.input_profile != param->input_profile)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -2,7 +2,6 @@ if not get_option('color-management-lcms')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false)
|
||||
if not dep_lcms2.found()
|
||||
error('color-lcms plugin requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.')
|
||||
endif
|
||||
|
||||
+33
-37
@@ -35,6 +35,18 @@ struct weston_color_manager_noop {
|
||||
struct weston_color_manager base;
|
||||
};
|
||||
|
||||
static bool
|
||||
check_output_eotf_mode(struct weston_output *output)
|
||||
{
|
||||
if (output->eotf_mode == WESTON_EOTF_MODE_SDR)
|
||||
return true;
|
||||
|
||||
weston_log("Error: color manager no-op does not support EOTF mode %s of output %s.\n",
|
||||
weston_eotf_mode_to_str(output->eotf_mode),
|
||||
output->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct weston_color_manager_noop *
|
||||
get_cmnoop(struct weston_color_manager *cm_base)
|
||||
{
|
||||
@@ -74,6 +86,9 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
|
||||
/* TODO: Assert surface has no colorspace set */
|
||||
assert(output->color_profile == NULL);
|
||||
|
||||
if (!check_output_eotf_mode(output))
|
||||
return false;
|
||||
|
||||
/* Identity transform */
|
||||
surf_xform->transform = NULL;
|
||||
surf_xform->identity_pipeline = true;
|
||||
@@ -81,43 +96,29 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmnoop_get_output_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
static struct weston_output_color_outcome *
|
||||
cmnoop_create_output_color_outcome(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output)
|
||||
{
|
||||
struct weston_output_color_outcome *co;
|
||||
|
||||
assert(output->color_profile == NULL);
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
if (!check_output_eotf_mode(output))
|
||||
return NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
co = zalloc(sizeof *co);
|
||||
if (!co)
|
||||
return NULL;
|
||||
|
||||
static bool
|
||||
cmnoop_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
assert(output->color_profile == NULL);
|
||||
/* Identity transform on everything */
|
||||
co->from_blend_to_output = NULL;
|
||||
co->from_sRGB_to_blend = NULL;
|
||||
co->from_sRGB_to_output = NULL;
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
co->hdr_meta.group_mask = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmnoop_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
assert(output->color_profile == NULL);
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
|
||||
return true;
|
||||
return co;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -153,13 +154,8 @@ weston_color_manager_noop_create(struct weston_compositor *compositor)
|
||||
cm->base.destroy_color_profile = cmnoop_destroy_color_profile;
|
||||
cm->base.get_color_profile_from_icc = cmnoop_get_color_profile_from_icc;
|
||||
cm->base.destroy_color_transform = cmnoop_destroy_color_transform;
|
||||
cm->base.get_surface_color_transform =
|
||||
cmnoop_get_surface_color_transform;
|
||||
cm->base.get_output_color_transform = cmnoop_get_output_color_transform;
|
||||
cm->base.get_sRGB_to_output_color_transform =
|
||||
cmnoop_get_sRGB_to_output_color_transform;
|
||||
cm->base.get_sRGB_to_blend_color_transform =
|
||||
cmnoop_get_sRGB_to_blend_color_transform;
|
||||
cm->base.get_surface_color_transform = cmnoop_get_surface_color_transform;
|
||||
cm->base.create_output_color_outcome = cmnoop_create_output_color_outcome;
|
||||
|
||||
return &cm->base;
|
||||
}
|
||||
|
||||
@@ -297,3 +297,55 @@ out_close:
|
||||
close(fd);
|
||||
return cprof;
|
||||
}
|
||||
|
||||
/** Get a string naming the EOTF mode
|
||||
*
|
||||
* \internal
|
||||
*/
|
||||
WL_EXPORT const char *
|
||||
weston_eotf_mode_to_str(enum weston_eotf_mode e)
|
||||
{
|
||||
switch (e) {
|
||||
case WESTON_EOTF_MODE_NONE: return "(none)";
|
||||
case WESTON_EOTF_MODE_SDR: return "SDR";
|
||||
case WESTON_EOTF_MODE_TRADITIONAL_HDR: return "traditional gamma HDR";
|
||||
case WESTON_EOTF_MODE_ST2084: return "ST2084";
|
||||
case WESTON_EOTF_MODE_HLG: return "HLG";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
/** A list of EOTF modes as a string
|
||||
*
|
||||
* \param eotf_mask Bitwise-or'd enum weston_eotf_mode values.
|
||||
* \return Comma separated names of the listed EOTF modes. Must be free()'d by
|
||||
* the caller.
|
||||
*/
|
||||
WL_EXPORT char *
|
||||
weston_eotf_mask_to_str(uint32_t eotf_mask)
|
||||
{
|
||||
FILE *fp;
|
||||
char *str = NULL;
|
||||
size_t size = 0;
|
||||
unsigned i;
|
||||
const char *sep = "";
|
||||
|
||||
fp = open_memstream(&str, &size);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; eotf_mask; i++) {
|
||||
uint32_t bitmask = 1u << i;
|
||||
|
||||
if (eotf_mask & bitmask) {
|
||||
fprintf(fp, "%s%s", sep,
|
||||
weston_eotf_mode_to_str(bitmask));
|
||||
sep = ", ";
|
||||
}
|
||||
|
||||
eotf_mask &= ~bitmask;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
+93
-44
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2021 Collabora, Ltd.
|
||||
* Copyright 2021 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -98,6 +99,79 @@ struct weston_color_curve {
|
||||
} u;
|
||||
};
|
||||
|
||||
/** Type or formula for a color mapping */
|
||||
enum weston_color_mapping_type {
|
||||
/** Identity function, no-op */
|
||||
WESTON_COLOR_MAPPING_TYPE_IDENTITY = 0,
|
||||
|
||||
/** 3D-dimensional look-up table */
|
||||
WESTON_COLOR_MAPPING_TYPE_3D_LUT,
|
||||
};
|
||||
|
||||
/**
|
||||
* A three-dimensional look-up table
|
||||
*
|
||||
* A 3D LUT is a three-dimensional array where each element is an RGB triplet.
|
||||
* A 3D LUT is usually an approximation of some arbitrary color mapping
|
||||
* function that cannot be represented in any simpler form. The array contains
|
||||
* samples from the approximated function, and values between samples are
|
||||
* estimated by interpolation. The array is accessed with three indices, one
|
||||
* for each input dimension (color channel).
|
||||
*
|
||||
* Color channel values in the range [0.0, 1.0] are mapped linearly to
|
||||
* 3D LUT indices such that 0.0 maps exactly to the first element and 1.0 maps
|
||||
* exactly to the last element in each dimension.
|
||||
*
|
||||
* This object represents a 3D LUT and offers an interface for realizing it
|
||||
* as a data array with a custom size.
|
||||
*/
|
||||
struct weston_color_mapping_3dlut {
|
||||
/**
|
||||
* Create a 3D LUT data array
|
||||
*
|
||||
* \param xform This color transformation object.
|
||||
* \param values Memory to hold the resulting data array.
|
||||
* \param len The number of elements in each dimension.
|
||||
*
|
||||
* The array \c values must be at least 3 * len * len * len elements
|
||||
* in size.
|
||||
*
|
||||
* Given the red index ri, green index gi and blue index bi, the
|
||||
* corresponding array element index
|
||||
*
|
||||
* i = 3 * (len * len * bi + len * gi + ri) + c
|
||||
*
|
||||
* where
|
||||
*
|
||||
* c = 0 for red output value,
|
||||
* c = 1 for green output value, and
|
||||
* c = 2 for blue output value
|
||||
*/
|
||||
void
|
||||
(*fill_in)(struct weston_color_transform *xform,
|
||||
float *values, unsigned len);
|
||||
|
||||
/** Optimal 3D LUT size along each dimension */
|
||||
unsigned optimal_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Color mapping function
|
||||
*
|
||||
* This object can represent a 3D LUT to do a color space conversion
|
||||
*
|
||||
*/
|
||||
struct weston_color_mapping {
|
||||
/** Which member of 'u' defines the color mapping type */
|
||||
enum weston_color_mapping_type type;
|
||||
|
||||
/** Parameters for the color mapping function */
|
||||
union {
|
||||
/* identity: no parameters */
|
||||
struct weston_color_mapping_3dlut lut3d;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes a color transformation formula
|
||||
*
|
||||
@@ -124,7 +198,7 @@ struct weston_color_transform {
|
||||
struct weston_color_curve pre_curve;
|
||||
|
||||
/** Step 3: color mapping */
|
||||
/* TBD: e.g. a 3D LUT or a matrix */
|
||||
struct weston_color_mapping mapping;
|
||||
|
||||
/** Step 4: color curve after color mapping */
|
||||
/* struct weston_color_curve post_curve; */
|
||||
@@ -223,53 +297,19 @@ struct weston_color_manager {
|
||||
struct weston_output *output,
|
||||
struct weston_surface_color_transform *surf_xform);
|
||||
|
||||
/** Get output's blending space to output transformation
|
||||
/** Compute derived color properties for an output
|
||||
*
|
||||
* \param cm The color manager.
|
||||
* \param output The output for the destination color space.
|
||||
* \param xform_out Pointer for storing the weston_color_transform.
|
||||
* \return True on success, false on failure.
|
||||
* \param output The output.
|
||||
* \return A new color_outcome object on success, NULL on failure.
|
||||
*
|
||||
* The callee is responsible for increasing the reference count on the
|
||||
* weston_color_transform it stores via xform_out. On failure, xform_out
|
||||
* is untouched.
|
||||
* The callee (color manager) must inspect the weston_output (color
|
||||
* profile, EOTF mode, etc.) and create a fully populated
|
||||
* weston_output_color_outcome object.
|
||||
*/
|
||||
bool
|
||||
(*get_output_color_transform)(struct weston_color_manager *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out);
|
||||
|
||||
/** Get sRGB to output transformation
|
||||
*
|
||||
* \param cm The color manager.
|
||||
* \param output The output for the destination color space.
|
||||
* \param xform_out Pointer for storing the weston_color_transform.
|
||||
* \return True on success, false on failure.
|
||||
*
|
||||
* The callee is responsible for increasing the reference count on the
|
||||
* weston_color_transform it stores via xform_out. On failure, xform_out
|
||||
* is untouched.
|
||||
*/
|
||||
bool
|
||||
(*get_sRGB_to_output_color_transform)(struct weston_color_manager *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out);
|
||||
|
||||
/** Get sRGB to output's blending space transformation
|
||||
*
|
||||
* \param cm The color manager.
|
||||
* \param output The output for the destination blending color space.
|
||||
* \param xform_out Pointer for storing the weston_color_transform.
|
||||
* \return True on success, false on failure.
|
||||
*
|
||||
* The callee is responsible for increasing the reference count on the
|
||||
* weston_color_transform it stores via xform_out. On failure, xform_out
|
||||
* is untouched.
|
||||
*/
|
||||
bool
|
||||
(*get_sRGB_to_blend_color_transform)(struct weston_color_manager *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out);
|
||||
struct weston_output_color_outcome *
|
||||
(*create_output_color_outcome)(struct weston_color_manager *cm,
|
||||
struct weston_output *output);
|
||||
};
|
||||
|
||||
void
|
||||
@@ -305,4 +345,13 @@ weston_color_manager_noop_create(struct weston_compositor *compositor);
|
||||
struct weston_color_manager *
|
||||
weston_color_manager_create(struct weston_compositor *compositor);
|
||||
|
||||
const char *
|
||||
weston_eotf_mode_to_str(enum weston_eotf_mode e);
|
||||
|
||||
char *
|
||||
weston_eotf_mask_to_str(uint32_t eotf_mask);
|
||||
|
||||
void
|
||||
weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco);
|
||||
|
||||
#endif /* WESTON_COLOR_H */
|
||||
|
||||
+967
-283
File diff suppressed because it is too large
Load Diff
+10
-6
@@ -420,7 +420,7 @@ drag_surface_configure(struct weston_drag *drag,
|
||||
assert((pointer != NULL && touch == NULL) ||
|
||||
(pointer == NULL && touch != NULL));
|
||||
|
||||
if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) {
|
||||
if (!weston_surface_is_mapped(es) && weston_surface_has_content(es)) {
|
||||
if (pointer && pointer->sprite &&
|
||||
weston_view_is_mapped(pointer->sprite))
|
||||
list = &pointer->sprite->layer_link;
|
||||
@@ -431,7 +431,7 @@ drag_surface_configure(struct weston_drag *drag,
|
||||
weston_layer_entry_insert(list, &drag->icon->layer_link);
|
||||
weston_view_update_transform(drag->icon);
|
||||
pixman_region32_clear(&es->pending.input);
|
||||
es->is_mapped = true;
|
||||
weston_surface_map(es);
|
||||
drag->icon->is_mapped = true;
|
||||
}
|
||||
|
||||
@@ -1057,13 +1057,17 @@ data_device_start_drag(struct wl_client *client, struct wl_resource *resource,
|
||||
touch->focus &&
|
||||
touch->focus->surface == origin;
|
||||
|
||||
if (!is_pointer_grab && !is_touch_grab)
|
||||
return;
|
||||
|
||||
/* FIXME: Check that the data source type array isn't empty. */
|
||||
|
||||
if (source_resource)
|
||||
source = wl_resource_get_user_data(source_resource);
|
||||
|
||||
if (!is_pointer_grab && !is_touch_grab) {
|
||||
if (source)
|
||||
wl_data_source_send_cancelled(source->resource);
|
||||
return;
|
||||
}
|
||||
|
||||
if (icon_resource)
|
||||
icon = wl_resource_get_user_data(icon_resource);
|
||||
|
||||
@@ -1235,7 +1239,7 @@ destroy_data_source(struct wl_resource *resource)
|
||||
|
||||
static void
|
||||
client_source_accept(struct weston_data_source *source,
|
||||
uint32_t time, const char *mime_type)
|
||||
uint32_t serial, const char *mime_type)
|
||||
{
|
||||
wl_data_source_send_target(source->resource, mime_type);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wayland-server.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
#include <libweston-desktop/libweston-desktop.h>
|
||||
#include "internal.h"
|
||||
|
||||
struct weston_desktop_client {
|
||||
struct weston_desktop *desktop;
|
||||
struct wl_client *client;
|
||||
struct wl_resource *resource;
|
||||
struct wl_list surface_list;
|
||||
uint32_t ping_serial;
|
||||
struct wl_event_source *ping_timer;
|
||||
struct wl_signal destroy_signal;
|
||||
};
|
||||
|
||||
void
|
||||
weston_desktop_client_add_destroy_listener(struct weston_desktop_client *client,
|
||||
struct wl_listener *listener)
|
||||
{
|
||||
wl_signal_add(&client->destroy_signal, listener);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_client_destroy(struct weston_desktop_client *client)
|
||||
{
|
||||
struct wl_list *list = &client->surface_list;
|
||||
struct wl_list *link, *tmp;
|
||||
|
||||
assert(client->resource == NULL);
|
||||
|
||||
wl_signal_emit(&client->destroy_signal, client);
|
||||
|
||||
for (link = list->next, tmp = link->next;
|
||||
link != list;
|
||||
link = tmp, tmp = link->next) {
|
||||
wl_list_remove(link);
|
||||
wl_list_init(link);
|
||||
}
|
||||
|
||||
if (client->ping_timer != NULL)
|
||||
wl_event_source_remove(client->ping_timer);
|
||||
|
||||
free(client);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_client_handle_destroy(struct wl_resource *resource)
|
||||
{
|
||||
struct weston_desktop_client *client =
|
||||
wl_resource_get_user_data(resource);
|
||||
|
||||
assert(client->resource == resource);
|
||||
client->resource = NULL;
|
||||
|
||||
weston_desktop_client_destroy(client);
|
||||
}
|
||||
|
||||
static int
|
||||
weston_desktop_client_ping_timeout(void *user_data)
|
||||
{
|
||||
struct weston_desktop_client *client = user_data;
|
||||
|
||||
weston_desktop_api_ping_timeout(client->desktop, client);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct weston_desktop_client *
|
||||
weston_desktop_client_create(struct weston_desktop *desktop,
|
||||
struct wl_client *wl_client,
|
||||
wl_dispatcher_func_t dispatcher,
|
||||
const struct wl_interface *interface,
|
||||
const void *implementation, uint32_t version,
|
||||
uint32_t id)
|
||||
{
|
||||
struct weston_desktop_client *client;
|
||||
struct wl_display *display;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
client = zalloc(sizeof(struct weston_desktop_client));
|
||||
if (client == NULL) {
|
||||
if (wl_client != NULL)
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
client->desktop = desktop;
|
||||
client->client = wl_client;
|
||||
|
||||
wl_list_init(&client->surface_list);
|
||||
wl_signal_init(&client->destroy_signal);
|
||||
|
||||
if (wl_client == NULL)
|
||||
return client;
|
||||
|
||||
client->resource = wl_resource_create(wl_client, interface, version, id);
|
||||
if (client->resource == NULL) {
|
||||
wl_client_post_no_memory(wl_client);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dispatcher != NULL)
|
||||
wl_resource_set_dispatcher(client->resource, dispatcher,
|
||||
weston_desktop_client_handle_destroy, client,
|
||||
weston_desktop_client_handle_destroy);
|
||||
else
|
||||
wl_resource_set_implementation(client->resource, implementation,
|
||||
client,
|
||||
weston_desktop_client_handle_destroy);
|
||||
|
||||
display = wl_client_get_display(client->client);
|
||||
loop = wl_display_get_event_loop(display);
|
||||
client->ping_timer =
|
||||
wl_event_loop_add_timer(loop,
|
||||
weston_desktop_client_ping_timeout,
|
||||
client);
|
||||
if (client->ping_timer == NULL)
|
||||
wl_client_post_no_memory(wl_client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
struct weston_desktop *
|
||||
weston_desktop_client_get_desktop(struct weston_desktop_client *client)
|
||||
{
|
||||
return client->desktop;
|
||||
}
|
||||
|
||||
struct wl_resource *
|
||||
weston_desktop_client_get_resource(struct weston_desktop_client *client)
|
||||
{
|
||||
return client->resource;
|
||||
}
|
||||
|
||||
struct wl_list *
|
||||
weston_desktop_client_get_surface_list(struct weston_desktop_client *client)
|
||||
{
|
||||
return &client->surface_list;
|
||||
}
|
||||
|
||||
WL_EXPORT struct wl_client *
|
||||
weston_desktop_client_get_client(struct weston_desktop_client *client)
|
||||
{
|
||||
return client->client;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_client_for_each_surface(struct weston_desktop_client *client,
|
||||
void (*callback)(struct weston_desktop_surface *surface, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
struct wl_list *list = &client->surface_list;
|
||||
struct wl_list *link;
|
||||
|
||||
for (link = list->next; link != list; link = link->next)
|
||||
callback(weston_desktop_surface_from_client_link(link),
|
||||
user_data);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_desktop_client_ping(struct weston_desktop_client *client)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
weston_desktop_surface_from_client_link(client->surface_list.next);
|
||||
const struct weston_desktop_surface_implementation *implementation =
|
||||
weston_desktop_surface_get_implementation(surface);
|
||||
void *implementation_data =
|
||||
weston_desktop_surface_get_implementation_data(surface);
|
||||
|
||||
if (implementation->ping == NULL)
|
||||
return -1;
|
||||
|
||||
if (client->ping_serial != 0)
|
||||
return 1;
|
||||
|
||||
client->ping_serial =
|
||||
wl_display_next_serial(wl_client_get_display(client->client));
|
||||
wl_event_source_timer_update(client->ping_timer, 10000);
|
||||
|
||||
implementation->ping(surface, client->ping_serial, implementation_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_client_pong(struct weston_desktop_client *client, uint32_t serial)
|
||||
{
|
||||
if (client->ping_serial != serial)
|
||||
return;
|
||||
|
||||
weston_desktop_api_pong(client->desktop, client);
|
||||
|
||||
wl_event_source_timer_update(client->ping_timer, 0);
|
||||
client->ping_serial = 0;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_DESKTOP_INTERNAL_H
|
||||
#define WESTON_DESKTOP_INTERNAL_H
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
|
||||
struct weston_desktop_seat;
|
||||
struct weston_desktop_client;
|
||||
|
||||
struct weston_compositor *
|
||||
weston_desktop_get_compositor(struct weston_desktop *desktop);
|
||||
struct wl_display *
|
||||
weston_desktop_get_display(struct weston_desktop *desktop);
|
||||
|
||||
void
|
||||
weston_desktop_api_ping_timeout(struct weston_desktop *desktop,
|
||||
struct weston_desktop_client *client);
|
||||
void
|
||||
weston_desktop_api_pong(struct weston_desktop *desktop,
|
||||
struct weston_desktop_client *client);
|
||||
void
|
||||
weston_desktop_api_surface_added(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface);
|
||||
void
|
||||
weston_desktop_api_surface_removed(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface);
|
||||
void
|
||||
weston_desktop_api_committed(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
int32_t sx, int32_t sy);
|
||||
void
|
||||
weston_desktop_api_show_window_menu(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_seat *seat,
|
||||
int32_t x, int32_t y);
|
||||
void
|
||||
weston_desktop_api_set_parent(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_desktop_surface *parent);
|
||||
void
|
||||
weston_desktop_api_move(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_seat *seat, uint32_t serial);
|
||||
void
|
||||
weston_desktop_api_resize(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_seat *seat, uint32_t serial,
|
||||
enum weston_desktop_surface_edge edges);
|
||||
void
|
||||
weston_desktop_api_fullscreen_requested(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
bool fullscreen,
|
||||
struct weston_output *output);
|
||||
void
|
||||
weston_desktop_api_maximized_requested(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
bool maximized);
|
||||
void
|
||||
weston_desktop_api_minimized_requested(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface);
|
||||
|
||||
void
|
||||
weston_desktop_api_set_xwayland_position(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
int32_t x, int32_t y);
|
||||
|
||||
void
|
||||
weston_desktop_api_get_position(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
int32_t *x, int32_t *y);
|
||||
|
||||
struct weston_desktop_seat *
|
||||
weston_desktop_seat_from_seat(struct weston_seat *wseat);
|
||||
|
||||
struct weston_desktop_surface_implementation {
|
||||
void (*set_activated)(struct weston_desktop_surface *surface,
|
||||
void *user_data, bool activated);
|
||||
void (*set_fullscreen)(struct weston_desktop_surface *surface,
|
||||
void *user_data, bool fullscreen);
|
||||
void (*set_maximized)(struct weston_desktop_surface *surface,
|
||||
void *user_data, bool maximized);
|
||||
void (*set_resizing)(struct weston_desktop_surface *surface,
|
||||
void *user_data, bool resizing);
|
||||
void (*set_size)(struct weston_desktop_surface *surface,
|
||||
void *user_data, int32_t width, int32_t height);
|
||||
void (*set_orientation)(struct weston_desktop_surface *surface,
|
||||
void *user_data, enum weston_top_level_tiled_orientation tiled_orientation);
|
||||
void (*committed)(struct weston_desktop_surface *surface, void *user_data,
|
||||
int32_t sx, int32_t sy);
|
||||
void (*update_position)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
void (*ping)(struct weston_desktop_surface *surface, uint32_t serial,
|
||||
void *user_data);
|
||||
void (*close)(struct weston_desktop_surface *surface, void *user_data);
|
||||
|
||||
bool (*get_activated)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
bool (*get_fullscreen)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
bool (*get_maximized)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
bool (*get_resizing)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
struct weston_size
|
||||
(*get_max_size)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
struct weston_size
|
||||
(*get_min_size)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
|
||||
void (*destroy)(struct weston_desktop_surface *surface,
|
||||
void *user_data);
|
||||
};
|
||||
|
||||
struct weston_desktop_client *
|
||||
weston_desktop_client_create(struct weston_desktop *desktop,
|
||||
struct wl_client *client,
|
||||
wl_dispatcher_func_t dispatcher,
|
||||
const struct wl_interface *interface,
|
||||
const void *implementation, uint32_t version,
|
||||
uint32_t id);
|
||||
void
|
||||
weston_desktop_client_destroy(struct weston_desktop_client *client);
|
||||
|
||||
void
|
||||
weston_desktop_client_add_destroy_listener(struct weston_desktop_client *client,
|
||||
struct wl_listener *listener);
|
||||
struct weston_desktop *
|
||||
weston_desktop_client_get_desktop(struct weston_desktop_client *client);
|
||||
struct wl_resource *
|
||||
weston_desktop_client_get_resource(struct weston_desktop_client *client);
|
||||
struct wl_list *
|
||||
weston_desktop_client_get_surface_list(struct weston_desktop_client *client);
|
||||
|
||||
void
|
||||
weston_desktop_client_pong(struct weston_desktop_client *client,
|
||||
uint32_t serial);
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_create(struct weston_desktop *desktop,
|
||||
struct weston_desktop_client *client,
|
||||
struct weston_surface *surface,
|
||||
const struct weston_desktop_surface_implementation *implementation,
|
||||
void *implementation_data);
|
||||
void
|
||||
weston_desktop_surface_destroy(struct weston_desktop_surface *surface);
|
||||
void
|
||||
weston_desktop_surface_resource_destroy(struct wl_resource *resource);
|
||||
struct wl_resource *
|
||||
weston_desktop_surface_add_resource(struct weston_desktop_surface *surface,
|
||||
const struct wl_interface *interface,
|
||||
const void *implementation, uint32_t id,
|
||||
wl_resource_destroy_func_t destroy);
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_from_grab_link(struct wl_list *grab_link);
|
||||
|
||||
struct wl_list *
|
||||
weston_desktop_surface_get_client_link(struct weston_desktop_surface *surface);
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_from_client_link(struct wl_list *link);
|
||||
bool
|
||||
weston_desktop_surface_has_implementation(struct weston_desktop_surface *surface,
|
||||
const struct weston_desktop_surface_implementation *implementation);
|
||||
const struct weston_desktop_surface_implementation *
|
||||
weston_desktop_surface_get_implementation(struct weston_desktop_surface *surface);
|
||||
void *
|
||||
weston_desktop_surface_get_implementation_data(struct weston_desktop_surface *surface);
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_get_parent(struct weston_desktop_surface *surface);
|
||||
bool
|
||||
weston_desktop_surface_get_grab(struct weston_desktop_surface *surface);
|
||||
|
||||
void
|
||||
weston_desktop_surface_set_title(struct weston_desktop_surface *surface,
|
||||
const char *title);
|
||||
void
|
||||
weston_desktop_surface_set_app_id(struct weston_desktop_surface *surface,
|
||||
const char *app_id);
|
||||
void
|
||||
weston_desktop_surface_set_pid(struct weston_desktop_surface *surface,
|
||||
pid_t pid);
|
||||
void
|
||||
weston_desktop_surface_set_geometry(struct weston_desktop_surface *surface,
|
||||
struct weston_geometry geometry);
|
||||
void
|
||||
weston_desktop_surface_set_relative_to(struct weston_desktop_surface *surface,
|
||||
struct weston_desktop_surface *parent,
|
||||
int32_t x, int32_t y, bool use_geometry);
|
||||
void
|
||||
weston_desktop_surface_unset_relative_to(struct weston_desktop_surface *surface);
|
||||
void
|
||||
weston_desktop_surface_popup_grab(struct weston_desktop_surface *popup,
|
||||
struct weston_desktop_seat *seat,
|
||||
uint32_t serial);
|
||||
void
|
||||
weston_desktop_surface_popup_ungrab(struct weston_desktop_surface *popup,
|
||||
struct weston_desktop_seat *seat);
|
||||
void
|
||||
weston_desktop_surface_popup_dismiss(struct weston_desktop_surface *surface);
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_seat_popup_grab_get_topmost_surface(struct weston_desktop_seat *seat);
|
||||
bool
|
||||
weston_desktop_seat_popup_grab_start(struct weston_desktop_seat *seat,
|
||||
struct wl_client *client, uint32_t serial);
|
||||
void
|
||||
weston_desktop_seat_popup_grab_add_surface(struct weston_desktop_seat *seat,
|
||||
struct wl_list *link);
|
||||
void
|
||||
weston_desktop_seat_popup_grab_remove_surface(struct weston_desktop_seat *seat,
|
||||
struct wl_list *link);
|
||||
|
||||
void
|
||||
weston_desktop_destroy_request(struct wl_client *client,
|
||||
struct wl_resource *resource);
|
||||
struct wl_global *
|
||||
weston_desktop_xdg_wm_base_create(struct weston_desktop *desktop,
|
||||
struct wl_display *display);
|
||||
struct wl_global *
|
||||
weston_desktop_xdg_shell_v6_create(struct weston_desktop *desktop,
|
||||
struct wl_display *display);
|
||||
void
|
||||
weston_desktop_xwayland_init(struct weston_desktop *desktop);
|
||||
void
|
||||
weston_desktop_xwayland_fini(struct weston_desktop *desktop);
|
||||
|
||||
#endif /* WESTON_DESKTOP_INTERNAL_H */
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/zalloc.h>
|
||||
#include "shared/helpers.h"
|
||||
|
||||
#include <libweston-desktop/libweston-desktop.h>
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
struct weston_desktop {
|
||||
struct weston_compositor *compositor;
|
||||
struct weston_desktop_api api;
|
||||
void *user_data;
|
||||
struct wl_global *xdg_wm_base; /* Stable protocol xdg_shell replaces xdg_shell_unstable_v6 */
|
||||
struct wl_global *xdg_shell_v6; /* Unstable xdg_shell_unstable_v6 protocol. */
|
||||
};
|
||||
|
||||
void
|
||||
weston_desktop_destroy_request(struct wl_client *client,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_desktop *
|
||||
weston_desktop_create(struct weston_compositor *compositor,
|
||||
const struct weston_desktop_api *api, void *user_data)
|
||||
{
|
||||
struct weston_desktop *desktop;
|
||||
struct wl_display *display = compositor->wl_display;
|
||||
|
||||
assert(api->surface_added);
|
||||
assert(api->surface_removed);
|
||||
|
||||
desktop = zalloc(sizeof(struct weston_desktop));
|
||||
desktop->compositor = compositor;
|
||||
desktop->user_data = user_data;
|
||||
|
||||
desktop->api.struct_size =
|
||||
MIN(sizeof(struct weston_desktop_api), api->struct_size);
|
||||
memcpy(&desktop->api, api, desktop->api.struct_size);
|
||||
|
||||
desktop->xdg_wm_base =
|
||||
weston_desktop_xdg_wm_base_create(desktop, display);
|
||||
if (desktop->xdg_wm_base == NULL) {
|
||||
weston_desktop_destroy(desktop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
weston_desktop_xwayland_init(desktop);
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_destroy(struct weston_desktop *desktop)
|
||||
{
|
||||
if (desktop == NULL)
|
||||
return;
|
||||
|
||||
weston_desktop_xwayland_fini(desktop);
|
||||
|
||||
if (desktop->xdg_shell_v6 != NULL)
|
||||
wl_global_destroy(desktop->xdg_shell_v6);
|
||||
if (desktop->xdg_wm_base != NULL)
|
||||
wl_global_destroy(desktop->xdg_wm_base);
|
||||
|
||||
free(desktop);
|
||||
}
|
||||
|
||||
|
||||
struct weston_compositor *
|
||||
weston_desktop_get_compositor(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->compositor;
|
||||
}
|
||||
|
||||
struct wl_display *
|
||||
weston_desktop_get_display(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->compositor->wl_display;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_ping_timeout(struct weston_desktop *desktop,
|
||||
struct weston_desktop_client *client)
|
||||
{
|
||||
if (desktop->api.ping_timeout != NULL)
|
||||
desktop->api.ping_timeout(client, desktop->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_pong(struct weston_desktop *desktop,
|
||||
struct weston_desktop_client *client)
|
||||
{
|
||||
if (desktop->api.pong != NULL)
|
||||
desktop->api.pong(client, desktop->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_surface_added(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_client *client =
|
||||
weston_desktop_surface_get_client(surface);
|
||||
struct wl_list *list = weston_desktop_client_get_surface_list(client);
|
||||
struct wl_list *link = weston_desktop_surface_get_client_link(surface);
|
||||
|
||||
desktop->api.surface_added(surface, desktop->user_data);
|
||||
wl_list_insert(list, link);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_surface_removed(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct wl_list *link = weston_desktop_surface_get_client_link(surface);
|
||||
|
||||
wl_list_remove(link);
|
||||
wl_list_init(link);
|
||||
desktop->api.surface_removed(surface, desktop->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_committed(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
int32_t sx, int32_t sy)
|
||||
{
|
||||
if (desktop->api.committed != NULL)
|
||||
desktop->api.committed(surface, sx, sy, desktop->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_show_window_menu(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_seat *seat,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
if (desktop->api.show_window_menu != NULL)
|
||||
desktop->api.show_window_menu(surface, seat, x, y,
|
||||
desktop->user_data);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_window_menu_supported(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->api.show_window_menu != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_set_parent(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_desktop_surface *parent)
|
||||
{
|
||||
if (desktop->api.set_parent != NULL)
|
||||
desktop->api.set_parent(surface, parent, desktop->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_move(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_seat *seat, uint32_t serial)
|
||||
{
|
||||
if (desktop->api.move != NULL)
|
||||
desktop->api.move(surface, seat, serial, desktop->user_data);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_move_supported(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->api.move != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_resize(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
struct weston_seat *seat, uint32_t serial,
|
||||
enum weston_desktop_surface_edge edges)
|
||||
{
|
||||
if (desktop->api.resize != NULL)
|
||||
desktop->api.resize(surface, seat, serial, edges,
|
||||
desktop->user_data);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_resize_supported(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->api.resize != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_fullscreen_requested(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
bool fullscreen,
|
||||
struct weston_output *output)
|
||||
{
|
||||
if (desktop->api.fullscreen_requested != NULL)
|
||||
desktop->api.fullscreen_requested(surface, fullscreen, output,
|
||||
desktop->user_data);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_fullscreen_supported(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->api.fullscreen_requested != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_maximized_requested(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
bool maximized)
|
||||
{
|
||||
if (desktop->api.maximized_requested != NULL)
|
||||
desktop->api.maximized_requested(surface, maximized,
|
||||
desktop->user_data);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_maximize_supported(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->api.maximized_requested != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_minimized_requested(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (desktop->api.minimized_requested != NULL)
|
||||
desktop->api.minimized_requested(surface, desktop->user_data);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_minimize_supported(struct weston_desktop *desktop)
|
||||
{
|
||||
return desktop->api.minimized_requested != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_set_xwayland_position(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
if (desktop->api.set_xwayland_position != NULL)
|
||||
desktop->api.set_xwayland_position(surface, x, y,
|
||||
desktop->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_api_get_position(struct weston_desktop *desktop,
|
||||
struct weston_desktop_surface *surface,
|
||||
int32_t *x, int32_t *y)
|
||||
{
|
||||
if (!desktop->api.get_position)
|
||||
return;
|
||||
|
||||
desktop->api.get_position(surface, x, y, desktop->user_data);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
srcs_libweston += files([
|
||||
'libweston-desktop.c',
|
||||
'client.c',
|
||||
'seat.c',
|
||||
'surface.c',
|
||||
'xwayland.c',
|
||||
'xdg-shell.c',
|
||||
'xdg-shell-v6.c',
|
||||
])
|
||||
|
||||
srcs_libweston += [
|
||||
xdg_shell_unstable_v6_server_protocol_h,
|
||||
xdg_shell_unstable_v6_protocol_c,
|
||||
xdg_shell_server_protocol_h,
|
||||
xdg_shell_protocol_c,
|
||||
]
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
#include <libweston-desktop/libweston-desktop.h>
|
||||
#include "internal.h"
|
||||
#include "shared/timespec-util.h"
|
||||
|
||||
struct weston_desktop_seat {
|
||||
struct wl_listener seat_destroy_listener;
|
||||
struct weston_seat *seat;
|
||||
struct {
|
||||
struct weston_keyboard_grab keyboard;
|
||||
struct weston_pointer_grab pointer;
|
||||
struct weston_touch_grab touch;
|
||||
bool initial_up;
|
||||
struct wl_client *client;
|
||||
struct wl_list surfaces;
|
||||
} popup_grab;
|
||||
};
|
||||
|
||||
static void weston_desktop_seat_popup_grab_end(struct weston_desktop_seat *seat);
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_keyboard_key(struct weston_keyboard_grab *grab,
|
||||
const struct timespec *time,
|
||||
uint32_t key,
|
||||
enum wl_keyboard_key_state state)
|
||||
{
|
||||
weston_keyboard_send_key(grab->keyboard, time, key, state);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_keyboard_modifiers(struct weston_keyboard_grab *grab,
|
||||
uint32_t serial,
|
||||
uint32_t mods_depressed,
|
||||
uint32_t mods_latched,
|
||||
uint32_t mods_locked,
|
||||
uint32_t group)
|
||||
{
|
||||
weston_keyboard_send_modifiers(grab->keyboard, serial, mods_depressed,
|
||||
mods_latched, mods_locked, group);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_keyboard_cancel(struct weston_keyboard_grab *grab)
|
||||
{
|
||||
struct weston_desktop_seat *seat =
|
||||
wl_container_of(grab, seat, popup_grab.keyboard);
|
||||
|
||||
weston_desktop_seat_popup_grab_end(seat);
|
||||
}
|
||||
|
||||
static const struct weston_keyboard_grab_interface weston_desktop_seat_keyboard_popup_grab_interface = {
|
||||
.key = weston_desktop_seat_popup_grab_keyboard_key,
|
||||
.modifiers = weston_desktop_seat_popup_grab_keyboard_modifiers,
|
||||
.cancel = weston_desktop_seat_popup_grab_keyboard_cancel,
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_focus(struct weston_pointer_grab *grab)
|
||||
{
|
||||
struct weston_desktop_seat *seat =
|
||||
wl_container_of(grab, seat, popup_grab.pointer);
|
||||
struct weston_pointer *pointer = grab->pointer;
|
||||
struct weston_view *view;
|
||||
wl_fixed_t sx, sy;
|
||||
|
||||
view = weston_compositor_pick_view(pointer->seat->compositor,
|
||||
pointer->x, pointer->y, &sx, &sy);
|
||||
|
||||
if (view != NULL &&
|
||||
view->surface->resource != NULL &&
|
||||
wl_resource_get_client(view->surface->resource) == seat->popup_grab.client)
|
||||
weston_pointer_set_focus(pointer, view, sx, sy);
|
||||
else
|
||||
weston_pointer_clear_focus(pointer);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_motion(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
struct weston_pointer_motion_event *event)
|
||||
{
|
||||
weston_pointer_send_motion(grab->pointer, time, event);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_button(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state)
|
||||
{
|
||||
struct weston_desktop_seat *seat =
|
||||
wl_container_of(grab, seat, popup_grab.pointer);
|
||||
struct weston_pointer *pointer = grab->pointer;
|
||||
bool initial_up = seat->popup_grab.initial_up;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
seat->popup_grab.initial_up = true;
|
||||
|
||||
if (weston_pointer_has_focus_resource(pointer))
|
||||
weston_pointer_send_button(pointer, time, button, state);
|
||||
else if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
|
||||
(initial_up ||
|
||||
(timespec_sub_to_msec(time, &grab->pointer->grab_time) > 500)))
|
||||
weston_desktop_seat_popup_grab_end(seat);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_axis(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
struct weston_pointer_axis_event *event)
|
||||
{
|
||||
weston_pointer_send_axis(grab->pointer, time, event);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_axis_source(struct weston_pointer_grab *grab,
|
||||
uint32_t source)
|
||||
{
|
||||
weston_pointer_send_axis_source(grab->pointer, source);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_frame(struct weston_pointer_grab *grab)
|
||||
{
|
||||
weston_pointer_send_frame(grab->pointer);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_pointer_cancel(struct weston_pointer_grab *grab)
|
||||
{
|
||||
struct weston_desktop_seat *seat =
|
||||
wl_container_of(grab, seat, popup_grab.pointer);
|
||||
|
||||
weston_desktop_seat_popup_grab_end(seat);
|
||||
}
|
||||
|
||||
static const struct weston_pointer_grab_interface weston_desktop_seat_pointer_popup_grab_interface = {
|
||||
.focus = weston_desktop_seat_popup_grab_pointer_focus,
|
||||
.motion = weston_desktop_seat_popup_grab_pointer_motion,
|
||||
.button = weston_desktop_seat_popup_grab_pointer_button,
|
||||
.axis = weston_desktop_seat_popup_grab_pointer_axis,
|
||||
.axis_source = weston_desktop_seat_popup_grab_pointer_axis_source,
|
||||
.frame = weston_desktop_seat_popup_grab_pointer_frame,
|
||||
.cancel = weston_desktop_seat_popup_grab_pointer_cancel,
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_touch_down(struct weston_touch_grab *grab,
|
||||
const struct timespec *time,
|
||||
int touch_id,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
weston_touch_send_down(grab->touch, time, touch_id, sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_touch_up(struct weston_touch_grab *grab,
|
||||
const struct timespec *time,
|
||||
int touch_id)
|
||||
{
|
||||
weston_touch_send_up(grab->touch, time, touch_id);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_touch_motion(struct weston_touch_grab *grab,
|
||||
const struct timespec *time,
|
||||
int touch_id,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
weston_touch_send_motion(grab->touch, time, touch_id, sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_touch_frame(struct weston_touch_grab *grab)
|
||||
{
|
||||
weston_touch_send_frame(grab->touch);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_touch_cancel(struct weston_touch_grab *grab)
|
||||
{
|
||||
struct weston_desktop_seat *seat =
|
||||
wl_container_of(grab, seat, popup_grab.touch);
|
||||
|
||||
weston_desktop_seat_popup_grab_end(seat);
|
||||
}
|
||||
|
||||
static const struct weston_touch_grab_interface weston_desktop_seat_touch_popup_grab_interface = {
|
||||
.down = weston_desktop_seat_popup_grab_touch_down,
|
||||
.up = weston_desktop_seat_popup_grab_touch_up,
|
||||
.motion = weston_desktop_seat_popup_grab_touch_motion,
|
||||
.frame = weston_desktop_seat_popup_grab_touch_frame,
|
||||
.cancel = weston_desktop_seat_popup_grab_touch_cancel,
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_seat_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_desktop_seat *seat =
|
||||
wl_container_of(listener, seat, seat_destroy_listener);
|
||||
|
||||
free(seat);
|
||||
}
|
||||
|
||||
struct weston_desktop_seat *
|
||||
weston_desktop_seat_from_seat(struct weston_seat *wseat)
|
||||
{
|
||||
struct wl_listener *listener;
|
||||
struct weston_desktop_seat *seat;
|
||||
|
||||
if (wseat == NULL)
|
||||
return NULL;
|
||||
|
||||
listener = wl_signal_get(&wseat->destroy_signal,
|
||||
weston_desktop_seat_destroy);
|
||||
if (listener != NULL)
|
||||
return wl_container_of(listener, seat, seat_destroy_listener);
|
||||
|
||||
seat = zalloc(sizeof(struct weston_desktop_seat));
|
||||
if (seat == NULL)
|
||||
return NULL;
|
||||
|
||||
seat->seat = wseat;
|
||||
|
||||
seat->seat_destroy_listener.notify = weston_desktop_seat_destroy;
|
||||
wl_signal_add(&wseat->destroy_signal, &seat->seat_destroy_listener);
|
||||
|
||||
seat->popup_grab.keyboard.interface =
|
||||
&weston_desktop_seat_keyboard_popup_grab_interface;
|
||||
seat->popup_grab.pointer.interface =
|
||||
&weston_desktop_seat_pointer_popup_grab_interface;
|
||||
seat->popup_grab.touch.interface =
|
||||
&weston_desktop_seat_touch_popup_grab_interface;
|
||||
wl_list_init(&seat->popup_grab.surfaces);
|
||||
|
||||
return seat;
|
||||
}
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_seat_popup_grab_get_topmost_surface(struct weston_desktop_seat *seat)
|
||||
{
|
||||
if (seat == NULL || wl_list_empty(&seat->popup_grab.surfaces))
|
||||
return NULL;
|
||||
|
||||
struct wl_list *grab_link = seat->popup_grab.surfaces.next;
|
||||
|
||||
return weston_desktop_surface_from_grab_link(grab_link);
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_seat_popup_grab_start(struct weston_desktop_seat *seat,
|
||||
struct wl_client *client, uint32_t serial)
|
||||
{
|
||||
assert(seat == NULL || seat->popup_grab.client == NULL ||
|
||||
seat->popup_grab.client == client);
|
||||
|
||||
struct weston_seat *wseat = seat != NULL ? seat->seat : NULL;
|
||||
/* weston_seat_get_* functions can properly handle a NULL wseat */
|
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(wseat);
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(wseat);
|
||||
struct weston_touch *touch = weston_seat_get_touch(wseat);
|
||||
|
||||
if ((keyboard == NULL || keyboard->grab_serial != serial) &&
|
||||
(pointer == NULL || pointer->grab_serial != serial) &&
|
||||
(touch == NULL || touch->grab_serial != serial)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyboard != NULL &&
|
||||
keyboard->grab->interface != &weston_desktop_seat_keyboard_popup_grab_interface)
|
||||
weston_keyboard_start_grab(keyboard, &seat->popup_grab.keyboard);
|
||||
|
||||
if (pointer != NULL &&
|
||||
pointer->grab->interface != &weston_desktop_seat_pointer_popup_grab_interface)
|
||||
weston_pointer_start_grab(pointer, &seat->popup_grab.pointer);
|
||||
|
||||
if (touch != NULL &&
|
||||
touch->grab->interface != &weston_desktop_seat_touch_popup_grab_interface)
|
||||
weston_touch_start_grab(touch, &seat->popup_grab.touch);
|
||||
|
||||
seat->popup_grab.initial_up =
|
||||
(pointer == NULL || pointer->button_count == 0);
|
||||
seat->popup_grab.client = client;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_seat_popup_grab_end(struct weston_desktop_seat *seat)
|
||||
{
|
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat->seat);
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat->seat);
|
||||
struct weston_touch *touch = weston_seat_get_touch(seat->seat);
|
||||
|
||||
while (!wl_list_empty(&seat->popup_grab.surfaces)) {
|
||||
struct wl_list *link = seat->popup_grab.surfaces.prev;
|
||||
struct weston_desktop_surface *surface =
|
||||
weston_desktop_surface_from_grab_link(link);
|
||||
|
||||
wl_list_remove(link);
|
||||
wl_list_init(link);
|
||||
weston_desktop_surface_popup_dismiss(surface);
|
||||
}
|
||||
|
||||
if (keyboard != NULL &&
|
||||
keyboard->grab->interface == &weston_desktop_seat_keyboard_popup_grab_interface)
|
||||
weston_keyboard_end_grab(keyboard);
|
||||
|
||||
if (pointer != NULL &&
|
||||
pointer->grab->interface == &weston_desktop_seat_pointer_popup_grab_interface)
|
||||
weston_pointer_end_grab(pointer);
|
||||
|
||||
if (touch != NULL &&
|
||||
touch->grab->interface == &weston_desktop_seat_touch_popup_grab_interface)
|
||||
weston_touch_end_grab(touch);
|
||||
|
||||
seat->popup_grab.client = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_seat_popup_grab_add_surface(struct weston_desktop_seat *seat,
|
||||
struct wl_list *link)
|
||||
{
|
||||
assert(seat->popup_grab.client != NULL);
|
||||
|
||||
wl_list_insert(&seat->popup_grab.surfaces, link);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_seat_popup_grab_remove_surface(struct weston_desktop_seat *seat,
|
||||
struct wl_list *link)
|
||||
{
|
||||
assert(seat->popup_grab.client != NULL);
|
||||
|
||||
wl_list_remove(link);
|
||||
wl_list_init(link);
|
||||
if (wl_list_empty(&seat->popup_grab.surfaces))
|
||||
weston_desktop_seat_popup_grab_end(seat);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_seat_break_desktop_grabs(struct weston_seat *wseat)
|
||||
{
|
||||
struct weston_desktop_seat *seat = weston_desktop_seat_from_seat(wseat);
|
||||
|
||||
weston_desktop_seat_popup_grab_end(seat);
|
||||
}
|
||||
@@ -0,0 +1,840 @@
|
||||
/*
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
#include <libweston-desktop/libweston-desktop.h>
|
||||
#include "internal.h"
|
||||
|
||||
struct weston_desktop_view {
|
||||
struct wl_list link;
|
||||
struct weston_view *view;
|
||||
struct weston_desktop_view *parent;
|
||||
struct wl_list children_list;
|
||||
struct wl_list children_link;
|
||||
};
|
||||
|
||||
struct weston_desktop_surface {
|
||||
struct weston_desktop *desktop;
|
||||
struct weston_desktop_client *client;
|
||||
struct wl_list client_link;
|
||||
const struct weston_desktop_surface_implementation *implementation;
|
||||
void *implementation_data;
|
||||
void *user_data;
|
||||
struct weston_surface *surface;
|
||||
struct wl_list view_list;
|
||||
struct weston_position buffer_move;
|
||||
struct wl_listener surface_commit_listener;
|
||||
struct wl_listener surface_destroy_listener;
|
||||
struct wl_listener client_destroy_listener;
|
||||
struct wl_list children_list;
|
||||
|
||||
struct wl_list resource_list;
|
||||
bool has_geometry;
|
||||
struct weston_geometry geometry;
|
||||
struct {
|
||||
char *title;
|
||||
char *app_id;
|
||||
pid_t pid;
|
||||
struct wl_signal metadata_signal;
|
||||
};
|
||||
struct {
|
||||
struct weston_desktop_surface *parent;
|
||||
struct wl_list children_link;
|
||||
struct weston_position position;
|
||||
bool use_geometry;
|
||||
};
|
||||
struct {
|
||||
struct wl_list grab_link;
|
||||
};
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_surface_update_view_position(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_view *view;
|
||||
int32_t x, y;
|
||||
|
||||
x = surface->position.x;
|
||||
y = surface->position.y;
|
||||
|
||||
if (surface->use_geometry) {
|
||||
struct weston_desktop_surface *parent =
|
||||
weston_desktop_surface_get_parent(surface);
|
||||
struct weston_geometry geometry, parent_geometry;
|
||||
|
||||
geometry = weston_desktop_surface_get_geometry(surface);
|
||||
parent_geometry = weston_desktop_surface_get_geometry(parent);
|
||||
|
||||
x += parent_geometry.x - geometry.x;
|
||||
y += parent_geometry.y - geometry.y;
|
||||
}
|
||||
wl_list_for_each(view, &surface->view_list, link)
|
||||
weston_view_set_position(view->view, x, y);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
weston_desktop_view_propagate_layer(struct weston_desktop_view *view);
|
||||
|
||||
static void
|
||||
weston_desktop_view_destroy(struct weston_desktop_view *view)
|
||||
{
|
||||
struct weston_desktop_view *child_view, *tmp;
|
||||
|
||||
wl_list_for_each_safe(child_view, tmp, &view->children_list, children_link)
|
||||
weston_desktop_view_destroy(child_view);
|
||||
|
||||
wl_list_remove(&view->children_link);
|
||||
wl_list_remove(&view->link);
|
||||
|
||||
weston_view_damage_below(view->view);
|
||||
if (view->parent != NULL)
|
||||
weston_view_destroy(view->view);
|
||||
|
||||
free(view);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_destroy(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_view *view, *next_view;
|
||||
struct weston_desktop_surface *child, *next_child;
|
||||
|
||||
wl_list_remove(&surface->surface_commit_listener.link);
|
||||
wl_list_remove(&surface->surface_destroy_listener.link);
|
||||
wl_list_remove(&surface->client_destroy_listener.link);
|
||||
|
||||
if (!wl_list_empty(&surface->resource_list)) {
|
||||
struct wl_resource *resource, *tmp;
|
||||
wl_resource_for_each_safe(resource, tmp, &surface->resource_list) {
|
||||
wl_resource_set_user_data(resource, NULL);
|
||||
wl_list_remove(wl_resource_get_link(resource));
|
||||
}
|
||||
}
|
||||
|
||||
surface->implementation->destroy(surface, surface->implementation_data);
|
||||
|
||||
surface->surface->committed = NULL;
|
||||
surface->surface->committed_private = NULL;
|
||||
|
||||
weston_desktop_surface_unset_relative_to(surface);
|
||||
wl_list_remove(&surface->client_link);
|
||||
|
||||
wl_list_for_each_safe(child, next_child,
|
||||
&surface->children_list,
|
||||
children_link)
|
||||
weston_desktop_surface_unset_relative_to(child);
|
||||
|
||||
wl_list_for_each_safe(view, next_view, &surface->view_list, link)
|
||||
weston_desktop_view_destroy(view);
|
||||
|
||||
free(surface->title);
|
||||
free(surface->app_id);
|
||||
|
||||
free(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_surface_surface_committed(struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_container_of(listener, surface, surface_commit_listener);
|
||||
|
||||
if (surface->implementation->committed != NULL)
|
||||
surface->implementation->committed(surface,
|
||||
surface->implementation_data,
|
||||
surface->buffer_move.x,
|
||||
surface->buffer_move.y);
|
||||
|
||||
if (surface->parent != NULL) {
|
||||
struct weston_desktop_view *view;
|
||||
|
||||
wl_list_for_each(view, &surface->view_list, link) {
|
||||
weston_view_set_transform_parent(view->view,
|
||||
view->parent->view);
|
||||
weston_desktop_view_propagate_layer(view->parent);
|
||||
}
|
||||
weston_desktop_surface_update_view_position(surface);
|
||||
}
|
||||
|
||||
if (!wl_list_empty(&surface->children_list)) {
|
||||
struct weston_desktop_surface *child;
|
||||
|
||||
wl_list_for_each(child, &surface->children_list, children_link)
|
||||
weston_desktop_surface_update_view_position(child);
|
||||
}
|
||||
|
||||
surface->buffer_move.x = 0;
|
||||
surface->buffer_move.y = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_surface_surface_destroyed(struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_container_of(listener, surface, surface_destroy_listener);
|
||||
|
||||
weston_desktop_surface_destroy(surface);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_resource_destroy(struct wl_resource *resource)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_resource_get_user_data(resource);
|
||||
|
||||
if (surface != NULL)
|
||||
weston_desktop_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_surface_committed(struct weston_surface *wsurface,
|
||||
int32_t sx, int32_t sy)
|
||||
{
|
||||
struct weston_desktop_surface *surface = wsurface->committed_private;
|
||||
|
||||
surface->buffer_move.x = sx;
|
||||
surface->buffer_move.y = sy;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_surface_client_destroyed(struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_container_of(listener, surface, client_destroy_listener);
|
||||
|
||||
weston_desktop_surface_destroy(surface);
|
||||
}
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_create(struct weston_desktop *desktop,
|
||||
struct weston_desktop_client *client,
|
||||
struct weston_surface *wsurface,
|
||||
const struct weston_desktop_surface_implementation *implementation,
|
||||
void *implementation_data)
|
||||
{
|
||||
assert(implementation->destroy != NULL);
|
||||
|
||||
struct weston_desktop_surface *surface;
|
||||
|
||||
surface = zalloc(sizeof(struct weston_desktop_surface));
|
||||
if (surface == NULL) {
|
||||
if (client != NULL)
|
||||
wl_client_post_no_memory(weston_desktop_client_get_client(client));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
surface->desktop = desktop;
|
||||
surface->implementation = implementation;
|
||||
surface->implementation_data = implementation_data;
|
||||
surface->surface = wsurface;
|
||||
|
||||
surface->client = client;
|
||||
surface->client_destroy_listener.notify =
|
||||
weston_desktop_surface_client_destroyed;
|
||||
weston_desktop_client_add_destroy_listener(
|
||||
client, &surface->client_destroy_listener);
|
||||
|
||||
wsurface->committed = weston_desktop_surface_committed;
|
||||
wsurface->committed_private = surface;
|
||||
|
||||
surface->pid = -1;
|
||||
|
||||
surface->surface_commit_listener.notify =
|
||||
weston_desktop_surface_surface_committed;
|
||||
wl_signal_add(&surface->surface->commit_signal,
|
||||
&surface->surface_commit_listener);
|
||||
surface->surface_destroy_listener.notify =
|
||||
weston_desktop_surface_surface_destroyed;
|
||||
wl_signal_add(&surface->surface->destroy_signal,
|
||||
&surface->surface_destroy_listener);
|
||||
|
||||
wl_list_init(&surface->client_link);
|
||||
wl_list_init(&surface->resource_list);
|
||||
wl_list_init(&surface->children_list);
|
||||
wl_list_init(&surface->children_link);
|
||||
wl_list_init(&surface->view_list);
|
||||
wl_list_init(&surface->grab_link);
|
||||
|
||||
wl_signal_init(&surface->metadata_signal);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
struct wl_resource *
|
||||
weston_desktop_surface_add_resource(struct weston_desktop_surface *surface,
|
||||
const struct wl_interface *interface,
|
||||
const void *implementation, uint32_t id,
|
||||
wl_resource_destroy_func_t destroy)
|
||||
{
|
||||
struct wl_resource *client_resource =
|
||||
weston_desktop_client_get_resource(surface->client);
|
||||
struct wl_client *wl_client =
|
||||
weston_desktop_client_get_client(surface->client);
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = wl_resource_create(wl_client,
|
||||
interface,
|
||||
wl_resource_get_version(client_resource),
|
||||
id);
|
||||
if (resource == NULL) {
|
||||
wl_client_post_no_memory(wl_client);
|
||||
weston_desktop_surface_destroy(surface);
|
||||
return NULL;
|
||||
}
|
||||
if (destroy == NULL)
|
||||
destroy = weston_desktop_surface_resource_destroy;
|
||||
wl_resource_set_implementation(resource, implementation, surface, destroy);
|
||||
wl_list_insert(&surface->resource_list, wl_resource_get_link(resource));
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_from_grab_link(struct wl_list *grab_link)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_container_of(grab_link, surface, grab_link);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
WL_EXPORT bool
|
||||
weston_surface_is_desktop_surface(struct weston_surface *wsurface)
|
||||
{
|
||||
return wsurface->committed == weston_desktop_surface_committed;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_desktop_surface *
|
||||
weston_surface_get_desktop_surface(struct weston_surface *wsurface)
|
||||
{
|
||||
if (!weston_surface_is_desktop_surface(wsurface))
|
||||
return NULL;
|
||||
return wsurface->committed_private;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_user_data(struct weston_desktop_surface *surface,
|
||||
void *user_data)
|
||||
{
|
||||
surface->user_data = user_data;
|
||||
}
|
||||
|
||||
static struct weston_desktop_view *
|
||||
weston_desktop_surface_create_desktop_view(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct wl_client *wl_client=
|
||||
weston_desktop_client_get_client(surface->client);
|
||||
struct weston_desktop_view *view, *child_view;
|
||||
struct weston_view *wview;
|
||||
struct weston_desktop_surface *child;
|
||||
|
||||
wview = weston_view_create(surface->surface);
|
||||
if (wview == NULL) {
|
||||
if (wl_client != NULL)
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
view = zalloc(sizeof(struct weston_desktop_view));
|
||||
if (view == NULL) {
|
||||
if (wl_client != NULL)
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
view->view = wview;
|
||||
wl_list_init(&view->children_list);
|
||||
wl_list_init(&view->children_link);
|
||||
wl_list_insert(surface->view_list.prev, &view->link);
|
||||
|
||||
wl_list_for_each(child, &surface->children_list, children_link) {
|
||||
child_view =
|
||||
weston_desktop_surface_create_desktop_view(child);
|
||||
if (child_view == NULL) {
|
||||
weston_desktop_view_destroy(view);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
child_view->parent = view;
|
||||
wl_list_insert(view->children_list.prev,
|
||||
&child_view->children_link);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_view *
|
||||
weston_desktop_surface_create_view(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_view *view;
|
||||
|
||||
view = weston_desktop_surface_create_desktop_view(surface);
|
||||
if (view == NULL)
|
||||
return NULL;
|
||||
|
||||
return view->view;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_unlink_view(struct weston_view *wview)
|
||||
{
|
||||
struct weston_desktop_surface *surface;
|
||||
struct weston_desktop_view *view;
|
||||
|
||||
if (!weston_surface_is_desktop_surface(wview->surface))
|
||||
return;
|
||||
|
||||
surface = weston_surface_get_desktop_surface(wview->surface);
|
||||
wl_list_for_each(view, &surface->view_list, link) {
|
||||
if (view->view == wview) {
|
||||
weston_desktop_view_destroy(view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_view_propagate_layer(struct weston_desktop_view *view)
|
||||
{
|
||||
struct weston_desktop_view *child;
|
||||
struct wl_list *link = &view->view->layer_link.link;
|
||||
|
||||
wl_list_for_each_reverse(child, &view->children_list, children_link) {
|
||||
struct weston_layer_entry *prev =
|
||||
wl_container_of(link->prev, prev, link);
|
||||
|
||||
if (prev == &child->view->layer_link)
|
||||
continue;
|
||||
|
||||
child->view->is_mapped = true;
|
||||
weston_view_damage_below(child->view);
|
||||
weston_view_geometry_dirty(child->view);
|
||||
weston_layer_entry_remove(&child->view->layer_link);
|
||||
weston_layer_entry_insert(prev, &child->view->layer_link);
|
||||
weston_view_geometry_dirty(child->view);
|
||||
weston_surface_damage(child->view->surface);
|
||||
weston_view_update_transform(child->view);
|
||||
|
||||
weston_desktop_view_propagate_layer(child);
|
||||
}
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_propagate_layer(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_view *view;
|
||||
|
||||
wl_list_for_each(view, &surface->view_list, link)
|
||||
weston_desktop_view_propagate_layer(view);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_activated(struct weston_desktop_surface *surface, bool activated)
|
||||
{
|
||||
if (surface->implementation->set_activated != NULL)
|
||||
surface->implementation->set_activated(surface,
|
||||
surface->implementation_data,
|
||||
activated);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_fullscreen(struct weston_desktop_surface *surface, bool fullscreen)
|
||||
{
|
||||
if (surface->implementation->set_fullscreen != NULL)
|
||||
surface->implementation->set_fullscreen(surface,
|
||||
surface->implementation_data,
|
||||
fullscreen);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_maximized(struct weston_desktop_surface *surface, bool maximized)
|
||||
{
|
||||
if (surface->implementation->set_maximized != NULL)
|
||||
surface->implementation->set_maximized(surface,
|
||||
surface->implementation_data,
|
||||
maximized);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_resizing(struct weston_desktop_surface *surface, bool resizing)
|
||||
{
|
||||
if (surface->implementation->set_resizing != NULL)
|
||||
surface->implementation->set_resizing(surface,
|
||||
surface->implementation_data,
|
||||
resizing);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_size(struct weston_desktop_surface *surface, int32_t width, int32_t height)
|
||||
{
|
||||
if (surface->implementation->set_size != NULL)
|
||||
surface->implementation->set_size(surface,
|
||||
surface->implementation_data,
|
||||
width, height);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_set_orientation(struct weston_desktop_surface *surface,
|
||||
enum weston_top_level_tiled_orientation tile_orientation)
|
||||
{
|
||||
if (surface->implementation->set_orientation != NULL)
|
||||
surface->implementation->set_orientation(surface,
|
||||
surface->implementation_data,
|
||||
tile_orientation);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_close(struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (surface->implementation->close != NULL)
|
||||
surface->implementation->close(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_desktop_surface_add_metadata_listener(struct weston_desktop_surface *surface,
|
||||
struct wl_listener *listener)
|
||||
{
|
||||
wl_signal_add(&surface->metadata_signal, listener);
|
||||
}
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_from_client_link(struct wl_list *link)
|
||||
{
|
||||
struct weston_desktop_surface *surface;
|
||||
|
||||
surface = wl_container_of(link, surface, client_link);
|
||||
return surface;
|
||||
}
|
||||
|
||||
struct wl_list *
|
||||
weston_desktop_surface_get_client_link(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return &surface->client_link;
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_surface_has_implementation(struct weston_desktop_surface *surface,
|
||||
const struct weston_desktop_surface_implementation *implementation)
|
||||
{
|
||||
return surface->implementation == implementation;
|
||||
}
|
||||
|
||||
const struct weston_desktop_surface_implementation *
|
||||
weston_desktop_surface_get_implementation(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->implementation;
|
||||
}
|
||||
|
||||
void *
|
||||
weston_desktop_surface_get_implementation_data(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->implementation_data;
|
||||
}
|
||||
|
||||
struct weston_desktop_surface *
|
||||
weston_desktop_surface_get_parent(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->parent;
|
||||
}
|
||||
|
||||
bool
|
||||
weston_desktop_surface_get_grab(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return !wl_list_empty(&surface->grab_link);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_desktop_client *
|
||||
weston_desktop_surface_get_client(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->client;
|
||||
}
|
||||
|
||||
WL_EXPORT void *
|
||||
weston_desktop_surface_get_user_data(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->user_data;
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_surface *
|
||||
weston_desktop_surface_get_surface(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->surface;
|
||||
}
|
||||
|
||||
WL_EXPORT const char *
|
||||
weston_desktop_surface_get_title(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->title;
|
||||
}
|
||||
|
||||
WL_EXPORT const char *
|
||||
weston_desktop_surface_get_app_id(struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface->app_id;
|
||||
}
|
||||
|
||||
WL_EXPORT pid_t
|
||||
weston_desktop_surface_get_pid(struct weston_desktop_surface *surface)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (surface->pid != -1) {
|
||||
pid = surface->pid;
|
||||
} else {
|
||||
struct weston_desktop_client *client =
|
||||
weston_desktop_surface_get_client(surface);
|
||||
struct wl_client *wl_client =
|
||||
weston_desktop_client_get_client(client);
|
||||
|
||||
/* wl_client should always be valid, because only in the
|
||||
* xwayland case it wouldn't be, but in that case we won't
|
||||
* reach here, as the pid is initialized to 0. */
|
||||
assert(wl_client);
|
||||
wl_client_get_credentials(wl_client, &pid, NULL, NULL);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
WL_EXPORT bool
|
||||
weston_desktop_surface_get_activated(struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (surface->implementation->get_activated == NULL)
|
||||
return false;
|
||||
return surface->implementation->get_activated(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
WL_EXPORT bool
|
||||
weston_desktop_surface_get_resizing(struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (surface->implementation->get_resizing == NULL)
|
||||
return false;
|
||||
return surface->implementation->get_resizing(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
WL_EXPORT bool
|
||||
weston_desktop_surface_get_maximized(struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (surface->implementation->get_maximized == NULL)
|
||||
return false;
|
||||
return surface->implementation->get_maximized(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
WL_EXPORT bool
|
||||
weston_desktop_surface_get_fullscreen(struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (surface->implementation->get_fullscreen == NULL)
|
||||
return false;
|
||||
return surface->implementation->get_fullscreen(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_geometry
|
||||
weston_desktop_surface_get_geometry(struct weston_desktop_surface *surface)
|
||||
{
|
||||
if (surface->has_geometry)
|
||||
return surface->geometry;
|
||||
return weston_surface_get_bounding_box(surface->surface);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_size
|
||||
weston_desktop_surface_get_max_size(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_size size = { 0, 0 };
|
||||
|
||||
if (surface->implementation->get_max_size == NULL)
|
||||
return size;
|
||||
return surface->implementation->get_max_size(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
WL_EXPORT struct weston_size
|
||||
weston_desktop_surface_get_min_size(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_size size = { 0, 0 };
|
||||
|
||||
if (surface->implementation->get_min_size == NULL)
|
||||
return size;
|
||||
return surface->implementation->get_min_size(surface,
|
||||
surface->implementation_data);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_set_title(struct weston_desktop_surface *surface,
|
||||
const char *title)
|
||||
{
|
||||
char *tmp, *old;
|
||||
|
||||
tmp = strdup(title);
|
||||
if (tmp == NULL)
|
||||
return;
|
||||
|
||||
old = surface->title;
|
||||
surface->title = tmp;
|
||||
wl_signal_emit(&surface->metadata_signal, surface);
|
||||
free(old);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_set_app_id(struct weston_desktop_surface *surface,
|
||||
const char *app_id)
|
||||
{
|
||||
char *tmp, *old;
|
||||
|
||||
tmp = strdup(app_id);
|
||||
if (tmp == NULL)
|
||||
return;
|
||||
|
||||
old = surface->app_id;
|
||||
surface->app_id = tmp;
|
||||
wl_signal_emit(&surface->metadata_signal, surface);
|
||||
free(old);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_set_pid(struct weston_desktop_surface *surface,
|
||||
pid_t pid)
|
||||
{
|
||||
surface->pid = pid;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_set_geometry(struct weston_desktop_surface *surface,
|
||||
struct weston_geometry geometry)
|
||||
{
|
||||
surface->has_geometry = true;
|
||||
surface->geometry = geometry;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_set_relative_to(struct weston_desktop_surface *surface,
|
||||
struct weston_desktop_surface *parent,
|
||||
int32_t x, int32_t y, bool use_geometry)
|
||||
{
|
||||
struct weston_desktop_view *view, *parent_view;
|
||||
struct wl_list *link, *tmp;
|
||||
|
||||
assert(parent);
|
||||
|
||||
surface->position.x = x;
|
||||
surface->position.y = y;
|
||||
surface->use_geometry = use_geometry;
|
||||
|
||||
if (surface->parent == parent)
|
||||
return;
|
||||
|
||||
surface->parent = parent;
|
||||
wl_list_remove(&surface->children_link);
|
||||
wl_list_insert(surface->parent->children_list.prev,
|
||||
&surface->children_link);
|
||||
|
||||
link = surface->view_list.next;
|
||||
tmp = link->next;
|
||||
wl_list_for_each(parent_view, &parent->view_list, link) {
|
||||
if (link == &surface->view_list) {
|
||||
view = weston_desktop_surface_create_desktop_view(surface);
|
||||
if (view == NULL)
|
||||
return;
|
||||
tmp = &surface->view_list;
|
||||
} else {
|
||||
view = wl_container_of(link, view, link);
|
||||
wl_list_remove(&view->children_link);
|
||||
}
|
||||
|
||||
view->parent = parent_view;
|
||||
wl_list_insert(parent_view->children_list.prev,
|
||||
&view->children_link);
|
||||
weston_desktop_view_propagate_layer(view);
|
||||
|
||||
link = tmp;
|
||||
tmp = link->next;
|
||||
}
|
||||
for (; link != &surface->view_list; link = tmp, tmp = link->next) {
|
||||
view = wl_container_of(link, view, link);
|
||||
weston_desktop_view_destroy(view);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_unset_relative_to(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_view *view, *tmp;
|
||||
|
||||
if (surface->parent == NULL)
|
||||
return;
|
||||
|
||||
surface->parent = NULL;
|
||||
wl_list_remove(&surface->children_link);
|
||||
wl_list_init(&surface->children_link);
|
||||
|
||||
wl_list_for_each_safe(view, tmp, &surface->view_list, link)
|
||||
weston_desktop_view_destroy(view);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_popup_grab(struct weston_desktop_surface *surface,
|
||||
struct weston_desktop_seat *seat,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct wl_client *wl_client =
|
||||
weston_desktop_client_get_client(surface->client);
|
||||
if (weston_desktop_seat_popup_grab_start(seat, wl_client, serial))
|
||||
weston_desktop_seat_popup_grab_add_surface(seat, &surface->grab_link);
|
||||
else
|
||||
weston_desktop_surface_popup_dismiss(surface);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_popup_ungrab(struct weston_desktop_surface *surface,
|
||||
struct weston_desktop_seat *seat)
|
||||
{
|
||||
weston_desktop_seat_popup_grab_remove_surface(seat, &surface->grab_link);
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_surface_popup_dismiss(struct weston_desktop_surface *surface)
|
||||
{
|
||||
struct weston_desktop_view *view, *tmp;
|
||||
|
||||
wl_list_for_each_safe(view, tmp, &surface->view_list, link)
|
||||
weston_desktop_view_destroy(view);
|
||||
wl_list_remove(&surface->grab_link);
|
||||
wl_list_init(&surface->grab_link);
|
||||
weston_desktop_surface_close(surface);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
#include <libweston-desktop/libweston-desktop.h>
|
||||
#include "internal.h"
|
||||
#include "xwayland/xwayland-internal-interface.h"
|
||||
|
||||
enum weston_desktop_xwayland_surface_state {
|
||||
NONE,
|
||||
TOPLEVEL,
|
||||
MAXIMIZED,
|
||||
FULLSCREEN,
|
||||
TRANSIENT,
|
||||
XWAYLAND,
|
||||
};
|
||||
|
||||
struct weston_desktop_xwayland {
|
||||
struct weston_desktop *desktop;
|
||||
struct weston_desktop_client *client;
|
||||
struct weston_layer layer;
|
||||
};
|
||||
|
||||
struct weston_desktop_xwayland_surface {
|
||||
struct weston_desktop_xwayland *xwayland;
|
||||
struct weston_desktop *desktop;
|
||||
struct weston_desktop_surface *surface;
|
||||
struct wl_listener resource_destroy_listener;
|
||||
struct weston_view *view;
|
||||
const struct weston_xwayland_client_interface *client_interface;
|
||||
struct weston_geometry next_geometry;
|
||||
bool has_next_geometry;
|
||||
bool committed;
|
||||
bool added;
|
||||
enum weston_desktop_xwayland_surface_state state;
|
||||
enum weston_desktop_xwayland_surface_state prev_state;
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_xwayland_surface_change_state(struct weston_desktop_xwayland_surface *surface,
|
||||
enum weston_desktop_xwayland_surface_state state,
|
||||
struct weston_desktop_surface *parent,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
struct weston_surface *wsurface;
|
||||
bool to_add = (parent == NULL && state != XWAYLAND);
|
||||
|
||||
assert(state != NONE);
|
||||
assert(!parent || state == TRANSIENT);
|
||||
|
||||
if (to_add && surface->added) {
|
||||
surface->state = state;
|
||||
return;
|
||||
}
|
||||
|
||||
wsurface = weston_desktop_surface_get_surface(surface->surface);
|
||||
|
||||
if (surface->state != state) {
|
||||
if (surface->state == XWAYLAND) {
|
||||
assert(!surface->added);
|
||||
|
||||
weston_desktop_surface_unlink_view(surface->view);
|
||||
weston_view_destroy(surface->view);
|
||||
surface->view = NULL;
|
||||
weston_surface_unmap(wsurface);
|
||||
}
|
||||
|
||||
if (to_add) {
|
||||
weston_desktop_surface_unset_relative_to(surface->surface);
|
||||
weston_desktop_api_surface_added(surface->desktop,
|
||||
surface->surface);
|
||||
surface->added = true;
|
||||
if (surface->state == NONE && surface->committed)
|
||||
/* We had a race, and wl_surface.commit() was
|
||||
* faster, just fake a commit to map the
|
||||
* surface */
|
||||
weston_desktop_api_committed(surface->desktop,
|
||||
surface->surface,
|
||||
0, 0);
|
||||
|
||||
} else if (surface->added) {
|
||||
weston_desktop_api_surface_removed(surface->desktop,
|
||||
surface->surface);
|
||||
surface->added = false;
|
||||
}
|
||||
|
||||
if (state == XWAYLAND) {
|
||||
assert(!surface->added);
|
||||
|
||||
surface->view =
|
||||
weston_desktop_surface_create_view(surface->surface);
|
||||
weston_layer_entry_insert(&surface->xwayland->layer.view_list,
|
||||
&surface->view->layer_link);
|
||||
surface->view->is_mapped = true;
|
||||
weston_surface_map(wsurface);
|
||||
}
|
||||
|
||||
surface->state = state;
|
||||
}
|
||||
|
||||
if (parent != NULL)
|
||||
weston_desktop_surface_set_relative_to(surface->surface, parent,
|
||||
x, y, false);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_xwayland_surface_committed(struct weston_desktop_surface *dsurface,
|
||||
void *user_data,
|
||||
int32_t sx, int32_t sy)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface = user_data;
|
||||
struct weston_geometry oldgeom;
|
||||
|
||||
assert(dsurface == surface->surface);
|
||||
surface->committed = true;
|
||||
|
||||
#ifdef WM_DEBUG
|
||||
weston_log("%s: xwayland surface %p\n", __func__, surface);
|
||||
#endif
|
||||
|
||||
if (surface->has_next_geometry) {
|
||||
oldgeom = weston_desktop_surface_get_geometry(surface->surface);
|
||||
/* If we're transitioning away from fullscreen or maximized
|
||||
* we've moved to old saved co-ordinates that were saved
|
||||
* with window geometry in place, so avoid adajusting by
|
||||
* the geometry in those cases.
|
||||
*/
|
||||
if (surface->state == surface->prev_state) {
|
||||
sx -= surface->next_geometry.x - oldgeom.x;
|
||||
sy -= surface->next_geometry.y - oldgeom.y;
|
||||
}
|
||||
surface->prev_state = surface->state;
|
||||
|
||||
surface->has_next_geometry = false;
|
||||
weston_desktop_surface_set_geometry(surface->surface,
|
||||
surface->next_geometry);
|
||||
}
|
||||
|
||||
if (surface->added)
|
||||
weston_desktop_api_committed(surface->desktop, surface->surface,
|
||||
sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_xwayland_surface_set_size(struct weston_desktop_surface *dsurface,
|
||||
void *user_data,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface = user_data;
|
||||
struct weston_surface *wsurface =
|
||||
weston_desktop_surface_get_surface(surface->surface);
|
||||
|
||||
surface->client_interface->send_configure(wsurface, width, height);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_xwayland_surface_destroy(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface = user_data;
|
||||
|
||||
wl_list_remove(&surface->resource_destroy_listener.link);
|
||||
|
||||
weston_desktop_surface_unset_relative_to(surface->surface);
|
||||
if (surface->added)
|
||||
weston_desktop_api_surface_removed(surface->desktop,
|
||||
surface->surface);
|
||||
else if (surface->state == XWAYLAND)
|
||||
weston_desktop_surface_unlink_view(surface->view);
|
||||
|
||||
free(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_xwayland_surface_close(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface = user_data;
|
||||
struct weston_surface *wsurface =
|
||||
weston_desktop_surface_get_surface(surface->surface);
|
||||
|
||||
surface->client_interface->send_close(wsurface);
|
||||
}
|
||||
|
||||
static bool
|
||||
weston_desktop_xwayland_surface_get_maximized(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface = user_data;
|
||||
|
||||
return surface->state == MAXIMIZED;
|
||||
}
|
||||
|
||||
static bool
|
||||
weston_desktop_xwayland_surface_get_fullscreen(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface = user_data;
|
||||
|
||||
return surface->state == FULLSCREEN;
|
||||
}
|
||||
|
||||
static const struct weston_desktop_surface_implementation weston_desktop_xwayland_surface_internal_implementation = {
|
||||
.committed = weston_desktop_xwayland_surface_committed,
|
||||
.set_size = weston_desktop_xwayland_surface_set_size,
|
||||
|
||||
.get_maximized = weston_desktop_xwayland_surface_get_maximized,
|
||||
.get_fullscreen = weston_desktop_xwayland_surface_get_fullscreen,
|
||||
|
||||
.destroy = weston_desktop_xwayland_surface_destroy,
|
||||
.close = weston_desktop_xwayland_surface_close,
|
||||
};
|
||||
|
||||
static void
|
||||
weston_destop_xwayland_resource_destroyed(struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface =
|
||||
wl_container_of(listener, surface, resource_destroy_listener);
|
||||
|
||||
weston_desktop_surface_destroy(surface->surface);
|
||||
}
|
||||
|
||||
static struct weston_desktop_xwayland_surface *
|
||||
create_surface(struct weston_desktop_xwayland *xwayland,
|
||||
struct weston_surface *wsurface,
|
||||
const struct weston_xwayland_client_interface *client_interface)
|
||||
{
|
||||
struct weston_desktop_xwayland_surface *surface;
|
||||
|
||||
surface = zalloc(sizeof(struct weston_desktop_xwayland_surface));
|
||||
if (surface == NULL)
|
||||
return NULL;
|
||||
|
||||
surface->xwayland = xwayland;
|
||||
surface->desktop = xwayland->desktop;
|
||||
surface->client_interface = client_interface;
|
||||
|
||||
surface->surface =
|
||||
weston_desktop_surface_create(surface->desktop,
|
||||
xwayland->client, wsurface,
|
||||
&weston_desktop_xwayland_surface_internal_implementation,
|
||||
surface);
|
||||
if (surface->surface == NULL) {
|
||||
free(surface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
surface->resource_destroy_listener.notify =
|
||||
weston_destop_xwayland_resource_destroyed;
|
||||
wl_resource_add_destroy_listener(wsurface->resource,
|
||||
&surface->resource_destroy_listener);
|
||||
|
||||
weston_desktop_surface_set_pid(surface->surface, 0);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
static void
|
||||
set_toplevel(struct weston_desktop_xwayland_surface *surface)
|
||||
{
|
||||
weston_desktop_xwayland_surface_change_state(surface, TOPLEVEL, NULL,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
set_toplevel_with_position(struct weston_desktop_xwayland_surface *surface,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
weston_desktop_xwayland_surface_change_state(surface, TOPLEVEL, NULL,
|
||||
0, 0);
|
||||
weston_desktop_api_set_xwayland_position(surface->desktop,
|
||||
surface->surface, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
set_parent(struct weston_desktop_xwayland_surface *surface,
|
||||
struct weston_surface *wparent)
|
||||
{
|
||||
struct weston_desktop_surface *parent;
|
||||
|
||||
if (!weston_surface_is_desktop_surface(wparent))
|
||||
return;
|
||||
|
||||
parent = weston_surface_get_desktop_surface(wparent);
|
||||
weston_desktop_api_set_parent(surface->desktop, surface->surface, parent);
|
||||
}
|
||||
|
||||
static void
|
||||
set_transient(struct weston_desktop_xwayland_surface *surface,
|
||||
struct weston_surface *wparent, int x, int y)
|
||||
{
|
||||
struct weston_desktop_surface *parent;
|
||||
|
||||
if (!weston_surface_is_desktop_surface(wparent))
|
||||
return;
|
||||
|
||||
parent = weston_surface_get_desktop_surface(wparent);
|
||||
weston_desktop_xwayland_surface_change_state(surface, TRANSIENT, parent,
|
||||
x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
set_fullscreen(struct weston_desktop_xwayland_surface *surface,
|
||||
struct weston_output *output)
|
||||
{
|
||||
weston_desktop_xwayland_surface_change_state(surface, FULLSCREEN, NULL,
|
||||
0, 0);
|
||||
weston_desktop_api_fullscreen_requested(surface->desktop,
|
||||
surface->surface, true, output);
|
||||
}
|
||||
|
||||
static void
|
||||
set_xwayland(struct weston_desktop_xwayland_surface *surface, int x, int y)
|
||||
{
|
||||
weston_desktop_xwayland_surface_change_state(surface, XWAYLAND, NULL,
|
||||
x, y);
|
||||
weston_view_set_position(surface->view, x, y);
|
||||
}
|
||||
|
||||
static int
|
||||
move(struct weston_desktop_xwayland_surface *surface,
|
||||
struct weston_pointer *pointer)
|
||||
{
|
||||
if (surface->state == TOPLEVEL ||
|
||||
surface->state == MAXIMIZED ||
|
||||
surface->state == FULLSCREEN)
|
||||
weston_desktop_api_move(surface->desktop, surface->surface,
|
||||
pointer->seat, pointer->grab_serial);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
resize(struct weston_desktop_xwayland_surface *surface,
|
||||
struct weston_pointer *pointer, uint32_t edges)
|
||||
{
|
||||
if (surface->state == TOPLEVEL ||
|
||||
surface->state == MAXIMIZED ||
|
||||
surface->state == FULLSCREEN)
|
||||
weston_desktop_api_resize(surface->desktop, surface->surface,
|
||||
pointer->seat, pointer->grab_serial,
|
||||
edges);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
set_title(struct weston_desktop_xwayland_surface *surface, const char *title)
|
||||
{
|
||||
weston_desktop_surface_set_title(surface->surface, title);
|
||||
}
|
||||
|
||||
static void
|
||||
set_window_geometry(struct weston_desktop_xwayland_surface *surface,
|
||||
int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
{
|
||||
surface->has_next_geometry = true;
|
||||
surface->next_geometry.x = x;
|
||||
surface->next_geometry.y = y;
|
||||
surface->next_geometry.width = width;
|
||||
surface->next_geometry.height = height;
|
||||
}
|
||||
|
||||
static void
|
||||
set_maximized(struct weston_desktop_xwayland_surface *surface)
|
||||
{
|
||||
weston_desktop_xwayland_surface_change_state(surface, MAXIMIZED, NULL,
|
||||
0, 0);
|
||||
weston_desktop_api_maximized_requested(surface->desktop,
|
||||
surface->surface, true);
|
||||
}
|
||||
|
||||
static void
|
||||
set_minimized(struct weston_desktop_xwayland_surface *surface)
|
||||
{
|
||||
weston_desktop_api_minimized_requested(surface->desktop,
|
||||
surface->surface);
|
||||
}
|
||||
|
||||
static void
|
||||
set_pid(struct weston_desktop_xwayland_surface *surface, pid_t pid)
|
||||
{
|
||||
weston_desktop_surface_set_pid(surface->surface, pid);
|
||||
}
|
||||
|
||||
static void
|
||||
get_position(struct weston_desktop_xwayland_surface *surface,
|
||||
int32_t *x, int32_t *y)
|
||||
{
|
||||
if (!surface->surface) {
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
return;
|
||||
}
|
||||
weston_desktop_api_get_position(surface->desktop, surface->surface, x, y);
|
||||
}
|
||||
|
||||
static const struct weston_desktop_xwayland_interface weston_desktop_xwayland_interface = {
|
||||
.create_surface = create_surface,
|
||||
.set_toplevel = set_toplevel,
|
||||
.set_toplevel_with_position = set_toplevel_with_position,
|
||||
.set_parent = set_parent,
|
||||
.set_transient = set_transient,
|
||||
.set_fullscreen = set_fullscreen,
|
||||
.set_xwayland = set_xwayland,
|
||||
.move = move,
|
||||
.resize = resize,
|
||||
.set_title = set_title,
|
||||
.set_window_geometry = set_window_geometry,
|
||||
.set_maximized = set_maximized,
|
||||
.set_minimized = set_minimized,
|
||||
.set_pid = set_pid,
|
||||
.get_position = get_position,
|
||||
};
|
||||
|
||||
void
|
||||
weston_desktop_xwayland_init(struct weston_desktop *desktop)
|
||||
{
|
||||
struct weston_compositor *compositor = weston_desktop_get_compositor(desktop);
|
||||
struct weston_desktop_xwayland *xwayland;
|
||||
|
||||
xwayland = zalloc(sizeof(struct weston_desktop_xwayland));
|
||||
if (xwayland == NULL)
|
||||
return;
|
||||
|
||||
xwayland->desktop = desktop;
|
||||
xwayland->client = weston_desktop_client_create(desktop, NULL, NULL, NULL, NULL, 0, 0);
|
||||
|
||||
weston_layer_init(&xwayland->layer, compositor);
|
||||
/* This is the layer we use for override redirect "windows", which
|
||||
* ends up used for tooltips and drop down menus, among other things.
|
||||
* Previously this was WESTON_LAYER_POSITION_NORMAL + 1, but this is
|
||||
* below the fullscreen layer, so fullscreen apps would be above their
|
||||
* menus and tooltips.
|
||||
*
|
||||
* Moving this to just below the TOP_UI layer ensures visibility at all
|
||||
* times, with the minor drawback that they could be rendered above
|
||||
* DESKTOP_UI.
|
||||
*
|
||||
* For tooltips with no transient window hints, this is probably the best
|
||||
* we can do.
|
||||
*/
|
||||
weston_layer_set_position(&xwayland->layer,
|
||||
WESTON_LAYER_POSITION_TOP_UI - 1);
|
||||
|
||||
compositor->xwayland = xwayland;
|
||||
compositor->xwayland_interface = &weston_desktop_xwayland_interface;
|
||||
}
|
||||
|
||||
void
|
||||
weston_desktop_xwayland_fini(struct weston_desktop *desktop)
|
||||
{
|
||||
struct weston_compositor *compositor = weston_desktop_get_compositor(desktop);
|
||||
struct weston_desktop_xwayland *xwayland;
|
||||
|
||||
xwayland = compositor->xwayland;
|
||||
|
||||
weston_desktop_client_destroy(xwayland->client);
|
||||
weston_layer_fini(&xwayland->layer);
|
||||
free(xwayland);
|
||||
|
||||
compositor->xwayland = NULL;
|
||||
compositor->xwayland_interface = NULL;
|
||||
}
|
||||
+69
-9
@@ -75,6 +75,15 @@ struct border {
|
||||
enum motion_direction blocking_dir;
|
||||
};
|
||||
|
||||
struct pending_touch {
|
||||
struct weston_touch_device *touch_device;
|
||||
bool pending_focus_touch_reset;
|
||||
|
||||
struct wl_list touch_link;
|
||||
};
|
||||
|
||||
static struct wl_list pending_touch_list;
|
||||
|
||||
static void
|
||||
maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint);
|
||||
|
||||
@@ -143,6 +152,7 @@ weston_touch_create_touch_device(struct weston_touch *touch,
|
||||
const struct weston_touch_device_ops *ops)
|
||||
{
|
||||
struct weston_touch_device *device;
|
||||
struct pending_touch *pt;
|
||||
|
||||
assert(syspath);
|
||||
if (ops) {
|
||||
@@ -164,20 +174,55 @@ weston_touch_create_touch_device(struct weston_touch *touch,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pt = zalloc(sizeof(*pt));
|
||||
if (!pt) {
|
||||
free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
device->backend_data = backend_data;
|
||||
device->ops = ops;
|
||||
|
||||
pt->touch_device = device;
|
||||
pt->pending_focus_touch_reset = false;
|
||||
|
||||
device->aggregate = touch;
|
||||
wl_list_insert(touch->device_list.prev, &device->link);
|
||||
wl_list_insert(&pending_touch_list, &pt->touch_link);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
static struct pending_touch *
|
||||
weston_touch_get_pending(struct weston_touch_device *dev)
|
||||
{
|
||||
struct pending_touch *pt_iter = NULL;
|
||||
struct pending_touch *pt = NULL;
|
||||
|
||||
wl_list_for_each(pt_iter, &pending_touch_list, touch_link) {
|
||||
if (pt_iter->touch_device == dev) {
|
||||
pt = pt_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
/** Destroy the touch device. */
|
||||
WL_EXPORT void
|
||||
weston_touch_device_destroy(struct weston_touch_device *device)
|
||||
{
|
||||
struct pending_touch *pt = NULL;
|
||||
|
||||
wl_list_remove(&device->link);
|
||||
pt = weston_touch_get_pending(device);
|
||||
|
||||
assert(pt);
|
||||
wl_list_remove(&pt->touch_link);
|
||||
free(pt);
|
||||
|
||||
wl_signal_emit(&device->destroy_signal, device);
|
||||
free(device->syspath);
|
||||
free(device);
|
||||
@@ -2197,12 +2242,6 @@ notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key,
|
||||
struct weston_keyboard_grab *grab = keyboard->grab;
|
||||
uint32_t *k, *end;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
weston_compositor_idle_inhibit(compositor);
|
||||
} else {
|
||||
weston_compositor_idle_release(compositor);
|
||||
}
|
||||
|
||||
end = keyboard->keys.data + keyboard->keys.size;
|
||||
for (k = keyboard->keys.data; k < end; k++) {
|
||||
if (*k == key) {
|
||||
@@ -2218,6 +2257,12 @@ notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key,
|
||||
*k = key;
|
||||
}
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
weston_compositor_idle_inhibit(compositor);
|
||||
} else {
|
||||
weston_compositor_idle_release(compositor);
|
||||
}
|
||||
|
||||
if (grab == &keyboard->default_grab ||
|
||||
grab == &keyboard->input_method_grab) {
|
||||
weston_compositor_run_key_binding(compositor, keyboard, time,
|
||||
@@ -2329,6 +2374,7 @@ notify_keyboard_focus_out(struct weston_seat *seat)
|
||||
if (focus) {
|
||||
seat->use_saved_kbd_focus = true;
|
||||
seat->saved_kbd_focus = focus;
|
||||
assert(seat->saved_kbd_focus_listener.notify == NULL);
|
||||
seat->saved_kbd_focus_listener.notify =
|
||||
destroy_device_saved_kbd_focus;
|
||||
wl_signal_add(&focus->destroy_signal,
|
||||
@@ -2387,6 +2433,7 @@ process_touch_normal(struct weston_touch_device *device,
|
||||
struct weston_touch_grab *grab = device->aggregate->grab;
|
||||
struct weston_compositor *ec = device->aggregate->seat->compositor;
|
||||
struct weston_view *ev;
|
||||
struct pending_touch *pt = NULL;
|
||||
wl_fixed_t sx, sy;
|
||||
wl_fixed_t x = wl_fixed_from_double(double_x);
|
||||
wl_fixed_t y = wl_fixed_from_double(double_y);
|
||||
@@ -2437,8 +2484,9 @@ process_touch_normal(struct weston_touch_device *device,
|
||||
break;
|
||||
case WL_TOUCH_UP:
|
||||
grab->interface->up(grab, time, touch_id);
|
||||
if (touch->num_tp == 0)
|
||||
weston_touch_set_focus(touch, NULL);
|
||||
pt = weston_touch_get_pending(device);
|
||||
assert(pt);
|
||||
pt->pending_focus_touch_reset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2627,12 +2675,22 @@ WL_EXPORT void
|
||||
notify_touch_frame(struct weston_touch_device *device)
|
||||
{
|
||||
struct weston_touch_grab *grab;
|
||||
struct pending_touch *pt;
|
||||
|
||||
switch (weston_touch_device_get_mode(device)) {
|
||||
case WESTON_TOUCH_MODE_NORMAL:
|
||||
case WESTON_TOUCH_MODE_PREP_CALIB:
|
||||
grab = device->aggregate->grab;
|
||||
grab->interface->frame(grab);
|
||||
|
||||
pt = weston_touch_get_pending(device);
|
||||
assert(pt);
|
||||
|
||||
if (pt->pending_focus_touch_reset) {
|
||||
if (grab->touch->num_tp == 0)
|
||||
weston_touch_set_focus(grab->touch, NULL);
|
||||
pt->pending_focus_touch_reset = false;
|
||||
}
|
||||
break;
|
||||
case WESTON_TOUCH_MODE_CALIB:
|
||||
case WESTON_TOUCH_MODE_PREP_NORMAL:
|
||||
@@ -2697,7 +2755,7 @@ pointer_cursor_surface_committed(struct weston_surface *es,
|
||||
weston_layer_entry_insert(&es->compositor->cursor_layer.view_list,
|
||||
&pointer->sprite->layer_link);
|
||||
weston_view_update_transform(pointer->sprite);
|
||||
es->is_mapped = true;
|
||||
weston_surface_map(es);
|
||||
pointer->sprite->is_mapped = true;
|
||||
}
|
||||
}
|
||||
@@ -5058,6 +5116,8 @@ bind_input_timestamps_manager(struct wl_client *client, void *data,
|
||||
int
|
||||
weston_input_init(struct weston_compositor *compositor)
|
||||
{
|
||||
wl_list_init(&pending_touch_list);
|
||||
|
||||
if (!wl_global_create(compositor->wl_display,
|
||||
&zwp_relative_pointer_manager_v1_interface, 1,
|
||||
compositor, bind_relative_pointer_manager))
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
/*
|
||||
* 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 <libweston/libweston.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <string.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
|
||||
|
||||
/* major()/minor() */
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
#include <sys/mkdev.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_DRM_COMPOSITOR
|
||||
|
||||
#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 = false;
|
||||
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 = true;
|
||||
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 (geteuid() != 0)
|
||||
return -1;
|
||||
|
||||
if (tty == 0) {
|
||||
launcher->tty = dup(tty);
|
||||
if (launcher->tty == -1) {
|
||||
weston_log("couldn't dup stdin: %s\n",
|
||||
strerror(errno));
|
||||
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: %s\n", tty_device,
|
||||
strerror(errno));
|
||||
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: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (kd_mode != KD_TEXT) {
|
||||
weston_log("%s is already in graphics mode, "
|
||||
"is another display server running?\n", tty_device);
|
||||
}
|
||||
|
||||
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: %s\n",
|
||||
strerror(errno));
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
if (ioctl(launcher->tty, KDSKBMUTE, 1) &&
|
||||
ioctl(launcher->tty, KDSKBMODE, K_OFF)) {
|
||||
weston_log("failed to set K_OFF keyboard mode: %s\n",
|
||||
strerror(errno));
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS);
|
||||
if (ret) {
|
||||
weston_log("failed to set KD_GRAPHICS mode on tty: %s\n",
|
||||
strerror(errno));
|
||||
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) {
|
||||
weston_log("failed to add SIGRTMIN signal\n");
|
||||
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) {
|
||||
weston_log("couldn't open: %s! error=%s\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (geteuid() != 0) {
|
||||
weston_log("WARNING! Succeeded opening %s as non-root user."
|
||||
" This implies your device can be spied on.\n",
|
||||
path);
|
||||
}
|
||||
|
||||
if (fstat(fd, &s) == -1) {
|
||||
weston_log("couldn't fstat: %s! error=%s\n", path, strerror(errno));
|
||||
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: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
|
||||
weston_log("failed to set KD_TEXT mode on tty: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
/* 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! error=%s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
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;
|
||||
struct stat buf;
|
||||
|
||||
launcher = zalloc(sizeof(*launcher));
|
||||
if (launcher == NULL) {
|
||||
weston_log("failed to alloc for launcher\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
launcher->base.iface = &launcher_direct_iface;
|
||||
launcher->compositor = compositor;
|
||||
|
||||
/* Checking the existance of /dev/tty0 and verifying it's a TTY
|
||||
* device, as kernels compiled with CONFIG_VT=0 do not create these
|
||||
* devices. */
|
||||
if (stat("/dev/tty0", &buf) == 0 &&
|
||||
strcmp("seat0", seat_id) == 0 && major(buf.st_rdev) == TTY_MAJOR) {
|
||||
if (setup_tty(launcher, tty) == -1) {
|
||||
free(launcher);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
launcher->tty = -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);
|
||||
|
||||
if (launcher->tty >= 0) {
|
||||
launcher_direct_restore(&launcher->base);
|
||||
wl_event_source_remove(launcher->vt_source);
|
||||
close(launcher->tty);
|
||||
}
|
||||
|
||||
free(launcher);
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_direct_get_vt(struct weston_launcher *base)
|
||||
{
|
||||
struct launcher_direct *launcher = wl_container_of(base, launcher, base);
|
||||
struct stat s;
|
||||
if (fstat(launcher->tty, &s) < 0) {
|
||||
weston_log("couldn't fstat launcher tty: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return minor(s.st_rdev);
|
||||
}
|
||||
|
||||
const struct launcher_interface launcher_direct_iface = {
|
||||
.name = "direct",
|
||||
.connect = launcher_direct_connect,
|
||||
.destroy = launcher_direct_destroy,
|
||||
.open = launcher_direct_open,
|
||||
.close = launcher_direct_close,
|
||||
.activate_vt = launcher_direct_activate_vt,
|
||||
.get_vt = launcher_direct_get_vt,
|
||||
};
|
||||
@@ -32,7 +32,7 @@ struct weston_launcher;
|
||||
struct launcher_interface {
|
||||
char *name;
|
||||
int (* connect) (struct weston_launcher **launcher_out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm);
|
||||
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);
|
||||
@@ -47,5 +47,3 @@ struct weston_launcher {
|
||||
|
||||
extern const struct launcher_interface launcher_libseat_iface;
|
||||
extern const struct launcher_interface launcher_logind_iface;
|
||||
extern const struct launcher_interface launcher_weston_launch_iface;
|
||||
extern const struct launcher_interface launcher_direct_iface;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -40,6 +41,8 @@
|
||||
#include <libseat.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/weston-log.h>
|
||||
#include "weston-log-internal.h"
|
||||
#include "backend.h"
|
||||
#include "dbus.h"
|
||||
#include "launcher-impl.h"
|
||||
@@ -60,6 +63,11 @@ struct launcher_libseat {
|
||||
struct wl_list devices;
|
||||
};
|
||||
|
||||
/* debug messages go into a dedicated libseat-debug scope, while info and err
|
||||
* log level messages go into the log_scope, which the compositor has a
|
||||
* subscription by default*/
|
||||
static struct weston_log_scope *libseat_debug_scope = NULL;
|
||||
|
||||
static struct launcher_libseat_device *
|
||||
find_device_by_fd(struct launcher_libseat *wl, int fd)
|
||||
{
|
||||
@@ -179,9 +187,47 @@ libseat_event(int fd, uint32_t mask, void *data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
log_libseat_info_err(const char *fmt, va_list ap)
|
||||
{
|
||||
/* these all have been set-up by the compositor and use the 'log' scope */
|
||||
weston_vlog(fmt, ap);
|
||||
weston_log_continue("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
log_libseat_debug(const char *fmt, va_list ap)
|
||||
{
|
||||
int len_va;
|
||||
char *str;
|
||||
const char *oom = "Out of memory";
|
||||
|
||||
if (!weston_log_scope_is_enabled(libseat_debug_scope))
|
||||
return;
|
||||
|
||||
len_va = vasprintf(&str, fmt, ap);
|
||||
if (len_va >= 0) {
|
||||
weston_log_scope_printf(libseat_debug_scope, "%s\n", str);
|
||||
free(str);
|
||||
} else {
|
||||
weston_log_scope_printf(libseat_debug_scope, "%s\n", oom);
|
||||
}
|
||||
}
|
||||
|
||||
static void log_libseat(enum libseat_log_level level,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
if (level == LIBSEAT_LOG_LEVEL_DEBUG) {
|
||||
log_libseat_debug(fmt, ap);
|
||||
return;
|
||||
}
|
||||
|
||||
log_libseat_info_err(fmt, ap);
|
||||
}
|
||||
|
||||
static int
|
||||
seat_open(struct weston_launcher **out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm)
|
||||
const char *seat_id, bool sync_drm)
|
||||
{
|
||||
struct launcher_libseat *wl;
|
||||
struct wl_event_loop *event_loop;
|
||||
@@ -195,6 +241,13 @@ seat_open(struct weston_launcher **out, struct weston_compositor *compositor,
|
||||
wl->compositor = compositor;
|
||||
wl_list_init(&wl->devices);
|
||||
|
||||
libseat_debug_scope = compositor->libseat_debug;
|
||||
assert(libseat_debug_scope);
|
||||
libseat_set_log_handler(log_libseat);
|
||||
|
||||
/* includes (all) other log levels available <= LOG_LEVEL_DEBUG */
|
||||
libseat_set_log_level(LIBSEAT_LOG_LEVEL_DEBUG);
|
||||
|
||||
wl->seat = libseat_open_seat(&seat_listener, wl);
|
||||
if (wl->seat == NULL) {
|
||||
weston_log("libseat: could not open seat\n");
|
||||
@@ -231,6 +284,9 @@ seat_close(struct weston_launcher *launcher)
|
||||
{
|
||||
struct launcher_libseat *wl = wl_container_of(launcher, wl, base);
|
||||
|
||||
libseat_debug_scope = NULL;
|
||||
libseat_set_log_handler(NULL);
|
||||
|
||||
if (wl->seat != NULL) {
|
||||
libseat_close_seat(wl->seat);
|
||||
}
|
||||
|
||||
@@ -695,29 +695,6 @@ launcher_logind_release_control(struct launcher_logind *wl)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -759,7 +736,7 @@ launcher_logind_get_session(char **session)
|
||||
|
||||
static int
|
||||
launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor,
|
||||
int tty, const char *seat_id, bool sync_drm)
|
||||
const char *seat_id, bool sync_drm)
|
||||
{
|
||||
struct launcher_logind *wl;
|
||||
struct wl_event_loop *loop;
|
||||
@@ -803,15 +780,10 @@ launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *
|
||||
r = sd_seat_can_tty(t);
|
||||
free(t);
|
||||
if (r > 0) {
|
||||
r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
|
||||
r = 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;
|
||||
}
|
||||
} else if (r < 0) {
|
||||
weston_log("logind: could not determine if seat %s has ttys or not", t);
|
||||
@@ -881,9 +853,6 @@ static int
|
||||
launcher_logind_get_vt(struct weston_launcher *launcher)
|
||||
{
|
||||
struct launcher_logind *wl = wl_container_of(launcher, wl, base);
|
||||
if (wl->vtnr <= 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return wl->vtnr;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,11 @@ static const 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,
|
||||
weston_launcher_connect(struct weston_compositor *compositor,
|
||||
const char *seat_id, bool sync_drm)
|
||||
{
|
||||
const struct launcher_interface **it;
|
||||
@@ -59,7 +57,7 @@ weston_launcher_connect(struct weston_compositor *compositor, int tty,
|
||||
struct weston_launcher *launcher;
|
||||
|
||||
weston_log("Trying %s launcher...\n", iface->name);
|
||||
if (iface->connect(&launcher, compositor, tty, seat_id, sync_drm) == 0)
|
||||
if (iface->connect(&launcher, compositor, seat_id, sync_drm) == 0)
|
||||
return launcher;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
struct weston_launcher;
|
||||
|
||||
struct weston_launcher *
|
||||
weston_launcher_connect(struct weston_compositor *compositor, int tty,
|
||||
weston_launcher_connect(struct weston_compositor *compositor,
|
||||
const char *seat_id, bool sync_drm);
|
||||
|
||||
void
|
||||
|
||||
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* 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 <stdint.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 <libweston/libweston.h>
|
||||
#include "weston-launch.h"
|
||||
#include "launcher-impl.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
#define DRM_MAJOR 226
|
||||
|
||||
#ifndef KDSKBMUTE
|
||||
#define KDSKBMUTE 0x4B51
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_DRM_COMPOSITOR
|
||||
|
||||
#include <xf86drm.h>
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
drmDropMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drmSetMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* major()/minor() */
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
#include <sys/mkdev.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
union cmsg_data { unsigned char b[4]; int fd; };
|
||||
|
||||
struct launcher_weston_launch {
|
||||
struct weston_launcher base;
|
||||
struct weston_compositor *compositor;
|
||||
int fd;
|
||||
struct wl_event_source *source;
|
||||
|
||||
int kb_mode, tty, drm_fd;
|
||||
int deferred_deactivate;
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
launcher_weston_launch_send(int sockfd, void *buf, size_t buflen)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = send(sockfd, buf, buflen, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_deactivate(struct launcher_weston_launch *launcher)
|
||||
{
|
||||
int reply;
|
||||
|
||||
launcher->compositor->session_active = false;
|
||||
wl_signal_emit(&launcher->compositor->session_signal,
|
||||
launcher->compositor);
|
||||
|
||||
reply = WESTON_LAUNCHER_DEACTIVATE_DONE;
|
||||
launcher_weston_launch_send(launcher->fd, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
static void
|
||||
idle_deactivate(void *data)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = data;
|
||||
|
||||
if (launcher->deferred_deactivate) {
|
||||
launcher->deferred_deactivate = 0;
|
||||
handle_deactivate((struct launcher_weston_launch*)data);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
struct { int id; int ret; } event;
|
||||
|
||||
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);
|
||||
|
||||
launcher_weston_launch_send(launcher->fd, message, n);
|
||||
free(message);
|
||||
|
||||
memset(&msg, 0, sizeof msg);
|
||||
iov.iov_base = &event;
|
||||
iov.iov_len = sizeof event;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = control;
|
||||
|
||||
while (1) {
|
||||
msg.msg_controllen = sizeof control;
|
||||
|
||||
do {
|
||||
len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
// Only OPEN_REPLY and up to one DEACTIVATE message should be possible here
|
||||
if ((len == sizeof event) && (event.id == WESTON_LAUNCHER_OPEN_REPLY))
|
||||
break;
|
||||
|
||||
if ((len == sizeof event.id) && (event.id == WESTON_LAUNCHER_DEACTIVATE) && (launcher->deferred_deactivate == 0)) {
|
||||
wl_event_loop_add_idle(wl_display_get_event_loop(launcher->compositor->wl_display), idle_deactivate, launcher);
|
||||
launcher->deferred_deactivate = 1;
|
||||
} else {
|
||||
weston_log("unexpected event %d (len=%zd) from weston-launch\n", event.id, len);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.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: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
|
||||
weston_log("failed to set KD_TEXT mode on tty: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
if (launcher->deferred_deactivate) {
|
||||
launcher->deferred_deactivate = 0;
|
||||
handle_deactivate(launcher);
|
||||
return 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 = true;
|
||||
wl_signal_emit(&launcher->compositor->session_signal,
|
||||
launcher->compositor);
|
||||
break;
|
||||
case WESTON_LAUNCHER_DEACTIVATE:
|
||||
handle_deactivate(launcher);
|
||||
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_environment_get_fd(const char *env)
|
||||
{
|
||||
char *e;
|
||||
int fd, flags;
|
||||
|
||||
e = getenv(env);
|
||||
if (!e || !safe_strtoint(e, &fd)) {
|
||||
weston_log("could not get launcher fd from env\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1) {
|
||||
weston_log("could not get fd flags!, env: %s, error: %s\n",
|
||||
env, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
||||
unsetenv(env);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
launcher->compositor = compositor;
|
||||
launcher->drm_fd = -1;
|
||||
launcher->deferred_deactivate = 0;
|
||||
launcher->fd = launcher_weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
|
||||
if (launcher->fd == -1) {
|
||||
free(launcher);
|
||||
return -1;
|
||||
}
|
||||
|
||||
launcher->tty = launcher_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);
|
||||
weston_log("failed to get weston-launcher socket fd event source\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
* (struct launcher_weston_launch **) out = launcher;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
launcher_weston_launch_get_vt(struct weston_launcher *base)
|
||||
{
|
||||
struct launcher_weston_launch *launcher = wl_container_of(base, launcher, base);
|
||||
struct stat s;
|
||||
if (fstat(launcher->tty, &s) < 0) {
|
||||
weston_log("could not fstat launcher tty: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return minor(s.st_rdev);
|
||||
}
|
||||
|
||||
const struct launcher_interface launcher_weston_launch_iface = {
|
||||
.name = "weston_launch",
|
||||
.connect = launcher_weston_launch_connect,
|
||||
.destroy = launcher_weston_launch_destroy,
|
||||
.open = launcher_weston_launch_open,
|
||||
.close = launcher_weston_launch_close,
|
||||
.activate_vt = launcher_weston_launch_activate_vt,
|
||||
.get_vt = launcher_weston_launch_get_vt,
|
||||
};
|
||||
@@ -43,6 +43,37 @@
|
||||
#include <libweston/libweston.h>
|
||||
#include "color.h"
|
||||
|
||||
/* compositor <-> renderer interface */
|
||||
|
||||
struct weston_renderer {
|
||||
int (*read_pixels)(struct weston_output *output,
|
||||
const struct pixel_format_info *format, void *pixels,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height);
|
||||
void (*repaint_output)(struct weston_output *output,
|
||||
pixman_region32_t *output_damage);
|
||||
void (*flush_damage)(struct weston_surface *surface,
|
||||
struct weston_buffer *buffer);
|
||||
void (*attach)(struct weston_surface *es, struct weston_buffer *buffer);
|
||||
void (*destroy)(struct weston_compositor *ec);
|
||||
|
||||
/** See weston_surface_copy_content() */
|
||||
int (*surface_copy_content)(struct weston_surface *surface,
|
||||
void *target, size_t size,
|
||||
int src_x, int src_y,
|
||||
int width, int height);
|
||||
|
||||
/** See weston_compositor_import_dmabuf() */
|
||||
bool (*import_dmabuf)(struct weston_compositor *ec,
|
||||
struct linux_dmabuf_buffer *buffer);
|
||||
|
||||
const struct weston_drm_format_array *
|
||||
(*get_supported_formats)(struct weston_compositor *ec);
|
||||
|
||||
bool (*fill_buffer_info)(struct weston_compositor *ec,
|
||||
struct weston_buffer *buffer);
|
||||
};
|
||||
|
||||
/* weston_buffer */
|
||||
|
||||
void
|
||||
@@ -50,7 +81,8 @@ weston_buffer_send_server_error(struct weston_buffer *buffer,
|
||||
const char *msg);
|
||||
void
|
||||
weston_buffer_reference(struct weston_buffer_reference *ref,
|
||||
struct weston_buffer *buffer);
|
||||
struct weston_buffer *buffer,
|
||||
enum weston_buffer_reference_type type);
|
||||
|
||||
void
|
||||
weston_buffer_release_move(struct weston_buffer_release_reference *dest,
|
||||
@@ -389,6 +421,8 @@ const uint64_t *
|
||||
weston_drm_format_get_modifiers(const struct weston_drm_format *format,
|
||||
unsigned int *count_out);
|
||||
|
||||
void
|
||||
weston_compositor_destroy_touch_calibrator(struct weston_compositor *compositor);
|
||||
/**
|
||||
* paint node
|
||||
*
|
||||
@@ -428,4 +462,9 @@ weston_view_find_paint_node(struct weston_view *view,
|
||||
int
|
||||
wl_data_device_manager_init(struct wl_display *display);
|
||||
|
||||
/* Exclusively for unit tests */
|
||||
|
||||
bool
|
||||
weston_output_set_color_outcome(struct weston_output *output);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "linux-dmabuf.h"
|
||||
#include "linux-dmabuf-unstable-v1-server-protocol.h"
|
||||
#include "shared/os-compatibility.h"
|
||||
#include "libweston-internal.h"
|
||||
#include "shared/weston-drm-fourcc.h"
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#define WESTON_LINUX_DMABUF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "linux-dmabuf-unstable-v1-server-protocol.h"
|
||||
|
||||
#define MAX_DMABUF_PLANES 4
|
||||
|
||||
|
||||
+20
-30
@@ -35,7 +35,6 @@ srcs_libweston = [
|
||||
'weston-log-flight-rec.c',
|
||||
'weston-log.c',
|
||||
'weston-direct-display.c',
|
||||
'zoom.c',
|
||||
linux_dmabuf_unstable_v1_protocol_c,
|
||||
linux_dmabuf_unstable_v1_server_protocol_h,
|
||||
linux_explicit_synchronization_unstable_v1_protocol_c,
|
||||
@@ -52,6 +51,8 @@ srcs_libweston = [
|
||||
relative_pointer_unstable_v1_server_protocol_h,
|
||||
weston_screenshooter_protocol_c,
|
||||
weston_screenshooter_server_protocol_h,
|
||||
single_pixel_buffer_v1_protocol_c,
|
||||
single_pixel_buffer_v1_server_protocol_h,
|
||||
text_cursor_position_protocol_c,
|
||||
text_cursor_position_server_protocol_h,
|
||||
text_input_unstable_v1_protocol_c,
|
||||
@@ -70,6 +71,8 @@ srcs_libweston = [
|
||||
weston_direct_display_server_protocol_h,
|
||||
]
|
||||
|
||||
subdir('desktop')
|
||||
|
||||
if get_option('renderer-gl')
|
||||
dep_egl = dependency('egl', required: false)
|
||||
if not dep_egl.found()
|
||||
@@ -131,6 +134,20 @@ pkgconfig.generate(
|
||||
subdirs: dir_include_libweston
|
||||
)
|
||||
|
||||
if version_weston.version_compare('>= 11.0.90')
|
||||
error('Remove libweston-desktop.pc for Weston 12.x')
|
||||
endif
|
||||
|
||||
pkgconfig.generate(
|
||||
lib_weston,
|
||||
filebase: 'libweston-desktop-@0@'.format(libweston_major),
|
||||
name: 'libweston-desktop',
|
||||
version: version_weston,
|
||||
description: 'Desktop shell abstraction library for libweston compositors',
|
||||
requires_private: deps_for_libweston_users,
|
||||
subdirs: dir_include_libweston
|
||||
)
|
||||
|
||||
pkgconfig.generate(
|
||||
filebase: 'libweston-@0@-protocols'.format(libweston_major),
|
||||
name: 'libWeston Protocols',
|
||||
@@ -144,9 +161,7 @@ pkgconfig.generate(
|
||||
)
|
||||
|
||||
srcs_session_helper = [
|
||||
'launcher-direct.c',
|
||||
'launcher-util.c',
|
||||
'launcher-weston-launch.c',
|
||||
]
|
||||
deps_session_helper = [ dep_libweston_private_h ]
|
||||
|
||||
@@ -157,13 +172,8 @@ endif
|
||||
systemd_dep = dependency('', required: false)
|
||||
if get_option('launcher-logind')
|
||||
systemd_dep = dependency('libsystemd', version: '>= 209', required: false)
|
||||
if systemd_dep.found()
|
||||
config_h.set('HAVE_SYSTEMD_LOGIN_209', '1')
|
||||
else
|
||||
systemd_dep = dependency('libsystemd-login', version: '>= 198', required: false)
|
||||
if not systemd_dep.found()
|
||||
error('logind support requires libsystemd or libsystemd-login but neither was found. Or, you can use \'-Dlauncher-logind=false\'')
|
||||
endif
|
||||
if not systemd_dep.found()
|
||||
error('logind support requires libsystemd >= 209. Or, you can use \'-Dlauncher-logind=false\'')
|
||||
endif
|
||||
|
||||
dbus_dep = dependency('dbus-1', version: '>= 1.6', required: false)
|
||||
@@ -228,29 +238,9 @@ dep_vertex_clipping = declare_dependency(
|
||||
include_directories: include_directories('.')
|
||||
)
|
||||
|
||||
if get_option('deprecated-weston-launch')
|
||||
warning('weston-launch is deprecated and will be removed in a future release. Please migrate to libseat and seatd-launch.')
|
||||
dep_pam = cc.find_library('pam')
|
||||
|
||||
if not cc.has_function('pam_open_session', dependencies: dep_pam)
|
||||
error('pam_open_session not found for weston-launch')
|
||||
endif
|
||||
|
||||
executable(
|
||||
'weston-launch',
|
||||
'weston-launch.c',
|
||||
dependencies: [dep_pam, systemd_dep, dep_libdrm],
|
||||
include_directories: common_inc,
|
||||
install: true
|
||||
)
|
||||
|
||||
meson.add_install_script('echo', 'REMINDER: You are installing weston-launch, please make it setuid-root.')
|
||||
endif
|
||||
|
||||
subdir('color-lcms')
|
||||
subdir('renderer-gl')
|
||||
subdir('backend-drm')
|
||||
subdir('backend-fbdev')
|
||||
subdir('backend-headless')
|
||||
subdir('backend-rdp')
|
||||
subdir('backend-wayland')
|
||||
|
||||
+38
-28
@@ -31,11 +31,16 @@
|
||||
#include <libweston/libweston.h>
|
||||
#include "libweston-internal.h"
|
||||
|
||||
struct noop_renderer {
|
||||
struct weston_renderer base;
|
||||
unsigned char seed; /* see comment in attach() */
|
||||
};
|
||||
|
||||
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)
|
||||
const struct pixel_format_info *format, void *pixels,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -47,32 +52,40 @@ noop_renderer_repaint_output(struct weston_output *output,
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_flush_damage(struct weston_surface *surface)
|
||||
noop_renderer_flush_damage(struct weston_surface *surface,
|
||||
struct weston_buffer *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
{
|
||||
struct noop_renderer *renderer =
|
||||
wl_container_of(es->compositor->renderer, renderer, base);
|
||||
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 */
|
||||
uint32_t size, i, height, stride;
|
||||
unsigned char unused = 0;
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
shm_buffer = wl_shm_buffer_get(buffer->resource);
|
||||
|
||||
if (!shm_buffer) {
|
||||
switch (buffer->type) {
|
||||
case WESTON_BUFFER_SOLID:
|
||||
/* no-op, early exit */
|
||||
return;
|
||||
case WESTON_BUFFER_SHM:
|
||||
/* fine */
|
||||
break;
|
||||
default:
|
||||
weston_log("No-op renderer supports only SHM buffers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
shm_buffer = buffer->shm_buffer;
|
||||
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);
|
||||
height = buffer->height;
|
||||
size = stride * height;
|
||||
|
||||
/* Access the buffer data to make sure the buffer's client gets killed
|
||||
@@ -84,20 +97,18 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
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)
|
||||
{
|
||||
/* Make sure that our unused is actually used, otherwise the compiler
|
||||
* is free to notice that our reads have no effect and elide them. */
|
||||
renderer->seed = unused;
|
||||
}
|
||||
|
||||
static void
|
||||
noop_renderer_destroy(struct weston_compositor *ec)
|
||||
{
|
||||
struct noop_renderer *renderer =
|
||||
wl_container_of(ec->renderer, renderer, base);
|
||||
|
||||
weston_log("no-op renderer SHM seed: %d\n", renderer->seed);
|
||||
free(ec->renderer);
|
||||
ec->renderer = NULL;
|
||||
}
|
||||
@@ -105,19 +116,18 @@ noop_renderer_destroy(struct weston_compositor *ec)
|
||||
WL_EXPORT int
|
||||
noop_renderer_init(struct weston_compositor *ec)
|
||||
{
|
||||
struct weston_renderer *renderer;
|
||||
struct noop_renderer *renderer;
|
||||
|
||||
renderer = zalloc(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;
|
||||
renderer->base.read_pixels = noop_renderer_read_pixels;
|
||||
renderer->base.repaint_output = noop_renderer_repaint_output;
|
||||
renderer->base.flush_damage = noop_renderer_flush_damage;
|
||||
renderer->base.attach = noop_renderer_attach;
|
||||
renderer->base.destroy = noop_renderer_destroy;
|
||||
ec->renderer = &renderer->base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+149
-25
@@ -48,6 +48,7 @@
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#define GL_FORMAT(fmt) .gl_format = (fmt)
|
||||
#define GL_TYPE(type) .gl_type = (type)
|
||||
#define SAMPLER_TYPE(type) .sampler_type = (type)
|
||||
@@ -64,6 +65,12 @@
|
||||
.bits.b = b_, \
|
||||
.bits.a = a_, \
|
||||
.component_type = PIXEL_COMPONENT_TYPE_FIXED
|
||||
#define BITS_RGBA_FLOAT(r_, g_, b_, a_) \
|
||||
.bits.r = r_, \
|
||||
.bits.g = g_, \
|
||||
.bits.b = b_, \
|
||||
.bits.a = a_, \
|
||||
.component_type = PIXEL_COMPONENT_TYPE_FLOAT
|
||||
|
||||
#define PIXMAN_FMT(fmt) .pixman_format = (PIXMAN_ ## fmt)
|
||||
|
||||
@@ -75,27 +82,48 @@
|
||||
* colour channels, are not supported.
|
||||
*/
|
||||
static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(R8),
|
||||
BITS_RGBA_FIXED(8, 0, 0, 0),
|
||||
.bpp = 8,
|
||||
.hide_from_clients = true,
|
||||
GL_FORMAT(GL_R8_EXT),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(GR88),
|
||||
BITS_RGBA_FIXED(8, 8, 0, 0),
|
||||
.bpp = 16,
|
||||
.hide_from_clients = true,
|
||||
GL_FORMAT(GL_RG8_EXT),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XRGB4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 0),
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ARGB4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 4),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_XRGB4444,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XBGR4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 0),
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ABGR4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 4),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_XBGR4444,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(RGBX4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 0),
|
||||
.bpp = 16,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA),
|
||||
GL_TYPE(GL_UNSIGNED_SHORT_4_4_4_4),
|
||||
@@ -104,6 +132,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(RGBA4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 4),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_RGBX4444,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA),
|
||||
@@ -113,35 +142,41 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(BGRX4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 0),
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(BGRA4444),
|
||||
BITS_RGBA_FIXED(4, 4, 4, 4),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_BGRX4444,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XRGB1555),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 0),
|
||||
.depth = 15,
|
||||
.addfb_legacy_depth = 15,
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ARGB1555),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 1),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_XRGB1555,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XBGR1555),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 0),
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ABGR1555),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 1),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_XBGR1555,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(RGBX5551),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 0),
|
||||
.bpp = 16,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA),
|
||||
GL_TYPE(GL_UNSIGNED_SHORT_5_5_5_1),
|
||||
@@ -150,6 +185,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(RGBA5551),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 1),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_RGBX5551,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA),
|
||||
@@ -159,16 +195,18 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(BGRX5551),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 0),
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(BGRA5551),
|
||||
BITS_RGBA_FIXED(5, 5, 5, 1),
|
||||
.bpp = 16,
|
||||
.opaque_substitute = DRM_FORMAT_BGRX5551,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(RGB565),
|
||||
BITS_RGBA_FIXED(5, 6, 5, 0),
|
||||
.depth = 16,
|
||||
.addfb_legacy_depth = 16,
|
||||
.bpp = 16,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGB),
|
||||
@@ -179,21 +217,24 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(BGR565),
|
||||
BITS_RGBA_FIXED(5, 6, 5, 0),
|
||||
.bpp = 16,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(RGB888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 0),
|
||||
.bpp = 24,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(BGR888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 0),
|
||||
.bpp = 24,
|
||||
GL_FORMAT(GL_RGB),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XRGB8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 0),
|
||||
.depth = 24,
|
||||
.addfb_legacy_depth = 24,
|
||||
.bpp = 32,
|
||||
GL_FORMAT(GL_BGRA_EXT),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
@@ -207,7 +248,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
DRM_FORMAT(ARGB8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 8),
|
||||
.opaque_substitute = DRM_FORMAT_XRGB8888,
|
||||
.depth = 32,
|
||||
.addfb_legacy_depth = 32,
|
||||
.bpp = 32,
|
||||
GL_FORMAT(GL_BGRA_EXT),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
@@ -220,6 +261,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(XBGR8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 0),
|
||||
.bpp = 32,
|
||||
GL_FORMAT(GL_RGBA),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
@@ -231,6 +273,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(ABGR8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 8),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_XBGR8888,
|
||||
GL_FORMAT(GL_RGBA),
|
||||
GL_TYPE(GL_UNSIGNED_BYTE),
|
||||
@@ -243,6 +286,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(RGBX8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 0),
|
||||
.bpp = 32,
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
PIXMAN_FMT(r8g8b8x8),
|
||||
#else
|
||||
@@ -252,6 +296,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(RGBA8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 8),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_RGBX8888,
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
PIXMAN_FMT(r8g8b8a8),
|
||||
@@ -262,6 +307,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(BGRX8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 0),
|
||||
.bpp = 32,
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
PIXMAN_FMT(b8g8r8x8),
|
||||
#else
|
||||
@@ -271,6 +317,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(BGRA8888),
|
||||
BITS_RGBA_FIXED(8, 8, 8, 8),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_BGRX8888,
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
PIXMAN_FMT(b8g8r8a8),
|
||||
@@ -281,7 +328,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(XRGB2101010),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 0),
|
||||
.depth = 30,
|
||||
.addfb_legacy_depth = 30,
|
||||
.bpp = 32,
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
PIXMAN_FMT(x2r10g10b10),
|
||||
@@ -290,6 +337,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(ARGB2101010),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 2),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_XRGB2101010,
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
PIXMAN_FMT(a2r10g10b10),
|
||||
@@ -298,6 +346,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(XBGR2101010),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 0),
|
||||
.bpp = 32,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA),
|
||||
GL_TYPE(GL_UNSIGNED_INT_2_10_10_10_REV_EXT),
|
||||
@@ -307,6 +356,7 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(ABGR2101010),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 2),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_XBGR2101010,
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA),
|
||||
@@ -317,21 +367,74 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
{
|
||||
DRM_FORMAT(RGBX1010102),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 0),
|
||||
.bpp = 32,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(RGBA1010102),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 2),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_RGBX1010102,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(BGRX1010102),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 0),
|
||||
.bpp = 32,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(BGRA1010102),
|
||||
BITS_RGBA_FIXED(10, 10, 10, 2),
|
||||
.bpp = 32,
|
||||
.opaque_substitute = DRM_FORMAT_BGRX1010102,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XBGR16161616),
|
||||
BITS_RGBA_FIXED(16, 16, 16, 0),
|
||||
.bpp = 64,
|
||||
#if __BYTE_ORDER__ == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA16_EXT),
|
||||
GL_TYPE(GL_UNSIGNED_SHORT),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ABGR16161616),
|
||||
BITS_RGBA_FIXED(16, 16, 16, 16),
|
||||
.bpp = 64,
|
||||
.opaque_substitute = DRM_FORMAT_XBGR16161616,
|
||||
#if __BYTE_ORDER__ == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA16_EXT),
|
||||
GL_TYPE(GL_UNSIGNED_SHORT),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XBGR16161616F),
|
||||
BITS_RGBA_FLOAT(16, 16, 16, 0),
|
||||
.bpp = 64,
|
||||
#if __BYTE_ORDER__ == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA16F),
|
||||
GL_TYPE(GL_HALF_FLOAT),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ABGR16161616F),
|
||||
BITS_RGBA_FLOAT(16, 16, 16, 16),
|
||||
.bpp = 64,
|
||||
.opaque_substitute = DRM_FORMAT_XBGR16161616F,
|
||||
#if __BYTE_ORDER__ == __LITTLE_ENDIAN
|
||||
GL_FORMAT(GL_RGBA16F),
|
||||
GL_TYPE(GL_HALF_FLOAT),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XRGB16161616F),
|
||||
BITS_RGBA_FLOAT(16, 16, 16, 0),
|
||||
.bpp = 64,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(ARGB16161616F),
|
||||
BITS_RGBA_FLOAT(16, 16, 16, 16),
|
||||
.bpp = 64,
|
||||
.opaque_substitute = DRM_FORMAT_XRGB16161616F,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(YUYV),
|
||||
SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL),
|
||||
@@ -472,6 +575,10 @@ static const struct pixel_format_info pixel_format_table[] = {
|
||||
.num_planes = 3,
|
||||
.chroma_order = ORDER_VU,
|
||||
},
|
||||
{
|
||||
DRM_FORMAT(XYUV8888),
|
||||
.bpp = 32,
|
||||
},
|
||||
};
|
||||
|
||||
WL_EXPORT const struct pixel_format_info *
|
||||
@@ -529,6 +636,19 @@ pixel_format_get_info_by_drm_name(const char *drm_format_name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT const struct pixel_format_info *
|
||||
pixel_format_get_info_by_pixman(pixman_format_code_t pixman_format)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(pixel_format_table); i++) {
|
||||
if (pixel_format_table[i].pixman_format == pixman_format)
|
||||
return &pixel_format_table[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT unsigned int
|
||||
pixel_format_get_plane_count(const struct pixel_format_info *info)
|
||||
{
|
||||
@@ -563,16 +683,34 @@ pixel_format_get_info_by_opaque_substitute(uint32_t format)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT unsigned int
|
||||
pixel_format_hsub(const struct pixel_format_info *info,
|
||||
unsigned int plane)
|
||||
{
|
||||
/* We don't support any formats where the first plane is subsampled. */
|
||||
if (plane == 0 || info->hsub == 0)
|
||||
return 1;
|
||||
|
||||
return info->hsub;
|
||||
}
|
||||
|
||||
WL_EXPORT unsigned int
|
||||
pixel_format_vsub(const struct pixel_format_info *info,
|
||||
unsigned int plane)
|
||||
{
|
||||
/* We don't support any formats where the first plane is subsampled. */
|
||||
if (plane == 0 || info->vsub == 0)
|
||||
return 1;
|
||||
|
||||
return info->vsub;
|
||||
}
|
||||
|
||||
WL_EXPORT unsigned int
|
||||
pixel_format_width_for_plane(const struct pixel_format_info *info,
|
||||
unsigned int plane,
|
||||
unsigned int width)
|
||||
{
|
||||
/* We don't support any formats where the first plane is subsampled. */
|
||||
if (plane == 0 || !info->hsub)
|
||||
return width;
|
||||
|
||||
return width / info->hsub;
|
||||
return width / pixel_format_hsub(info, plane);
|
||||
}
|
||||
|
||||
WL_EXPORT unsigned int
|
||||
@@ -580,14 +718,9 @@ pixel_format_height_for_plane(const struct pixel_format_info *info,
|
||||
unsigned int plane,
|
||||
unsigned int height)
|
||||
{
|
||||
/* We don't support any formats where the first plane is subsampled. */
|
||||
if (plane == 0 || !info->vsub)
|
||||
return height;
|
||||
|
||||
return height / info->vsub;
|
||||
return height / pixel_format_vsub(info, plane);
|
||||
}
|
||||
|
||||
#ifdef HAVE_HUMAN_FORMAT_MODIFIER
|
||||
WL_EXPORT char *
|
||||
pixel_format_get_modifier(uint64_t modifier)
|
||||
{
|
||||
@@ -627,12 +760,3 @@ pixel_format_get_modifier(uint64_t modifier)
|
||||
|
||||
return mod_str;
|
||||
}
|
||||
#else
|
||||
WL_EXPORT char *
|
||||
pixel_format_get_modifier(uint64_t modifier)
|
||||
{
|
||||
char *mod_str;
|
||||
str_printf(&mod_str, "0x%llx", (unsigned long long) modifier);
|
||||
return mod_str;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,10 @@ struct pixel_format_info {
|
||||
/** The DRM format name without the DRM_FORMAT_ prefix. */
|
||||
const char *drm_format_name;
|
||||
|
||||
/** If true, is only for internal use and should not be advertised to
|
||||
* clients to allow them to create buffers of this format. */
|
||||
bool hide_from_clients;
|
||||
|
||||
/** If non-zero, number of planes in base (non-modified) format. */
|
||||
int num_planes;
|
||||
|
||||
@@ -76,9 +80,10 @@ struct pixel_format_info {
|
||||
|
||||
/** If set, this format can be used with the legacy drmModeAddFB()
|
||||
* function (not AddFB2), using this and the bpp member. */
|
||||
int depth;
|
||||
int addfb_legacy_depth;
|
||||
|
||||
/** See 'depth' member above. */
|
||||
/** Number of bits required to store a single pixel, for
|
||||
* single-planar formats. */
|
||||
int bpp;
|
||||
|
||||
/** Horizontal subsampling; if non-zero, divide the width by this
|
||||
@@ -185,6 +190,19 @@ pixel_format_get_info_count(void);
|
||||
const struct pixel_format_info *
|
||||
pixel_format_get_info_by_drm_name(const char *drm_format_name);
|
||||
|
||||
/**
|
||||
* Get pixel format information for a Pixman format code
|
||||
*
|
||||
* Given a Pixman format code, return a pixel format info structure describing
|
||||
* the properties of that format.
|
||||
*
|
||||
* @param pixman_format Pixman format code to get info for
|
||||
* @returns A pixel format structure (must not be freed), or NULL if the
|
||||
* format could not be found
|
||||
*/
|
||||
const struct pixel_format_info *
|
||||
pixel_format_get_info_by_pixman(pixman_format_code_t pixman_format);
|
||||
|
||||
/**
|
||||
* Get number of planes used by a pixel format
|
||||
*
|
||||
@@ -247,6 +265,36 @@ pixel_format_get_opaque_substitute(const struct pixel_format_info *format);
|
||||
const struct pixel_format_info *
|
||||
pixel_format_get_info_by_opaque_substitute(uint32_t format);
|
||||
|
||||
/**
|
||||
* Return the horizontal subsampling factor for a given plane
|
||||
*
|
||||
* When horizontal subsampling is effective, a sampler bound to a secondary
|
||||
* plane must bind the sampler with a smaller effective width. This function
|
||||
* returns the subsampling factor to use for the given plane.
|
||||
*
|
||||
* @param format Pixel format info structure
|
||||
* @param plane Zero-indexed plane number
|
||||
* @returns Horizontal subsampling factor for the given plane
|
||||
*/
|
||||
unsigned int
|
||||
pixel_format_hsub(const struct pixel_format_info *format,
|
||||
unsigned int plane);
|
||||
|
||||
/**
|
||||
* Return the vertical subsampling factor for a given plane
|
||||
*
|
||||
* When vertical subsampling is effective, a sampler bound to a secondary
|
||||
* plane must bind the sampler with a smaller effective height. This function
|
||||
* returns the subsampling factor to use for the given plane.
|
||||
*
|
||||
* @param format Pixel format info structure
|
||||
* @param plane Zero-indexed plane number
|
||||
* @returns Vertical subsampling factor for the given plane
|
||||
*/
|
||||
unsigned int
|
||||
pixel_format_vsub(const struct pixel_format_info *format,
|
||||
unsigned int plane);
|
||||
|
||||
/**
|
||||
* Return the effective sampling width for a given plane
|
||||
*
|
||||
|
||||
+73
-74
@@ -36,11 +36,11 @@
|
||||
#include "color.h"
|
||||
#include "pixel-formats.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/signal.h"
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
struct pixman_output_state {
|
||||
void *shadow_buffer;
|
||||
pixman_image_t *shadow_image;
|
||||
pixman_image_t *hw_buffer;
|
||||
pixman_region32_t *hw_extra_damage;
|
||||
@@ -94,9 +94,9 @@ get_renderer(struct weston_compositor *ec)
|
||||
|
||||
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)
|
||||
const struct pixel_format_info *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_image_t *out_buf;
|
||||
@@ -106,11 +106,11 @@ pixman_renderer_read_pixels(struct weston_output *output,
|
||||
return -1;
|
||||
}
|
||||
|
||||
out_buf = pixman_image_create_bits(format,
|
||||
out_buf = pixman_image_create_bits(format->pixman_format,
|
||||
width,
|
||||
height,
|
||||
pixels,
|
||||
(PIXMAN_FORMAT_BPP(format) / 8) * width);
|
||||
(PIXMAN_FORMAT_BPP(format->pixman_format) / 8) * width);
|
||||
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
po->hw_buffer, /* src */
|
||||
@@ -489,6 +489,19 @@ draw_paint_node(struct weston_paint_node *pnode,
|
||||
if (!ps->image)
|
||||
return;
|
||||
|
||||
/* if we still have a reference, but the underlying buffer is no longer
|
||||
* available signal that we should unref image_t as well. This happens
|
||||
* when using close animations, with the reference surviving the
|
||||
* animation while the underlying buffer went away as the client was
|
||||
* terminated. This is a particular use-case and should probably be
|
||||
* refactored to provide some analogue with the GL-renderer (as in, to
|
||||
* still maintain the buffer and let the compositor dispose of it). */
|
||||
if (ps->buffer_ref.buffer && !ps->buffer_ref.buffer->shm_buffer) {
|
||||
pixman_image_unref(ps->image);
|
||||
ps->image = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
pixman_region32_init(&repaint);
|
||||
pixman_region32_intersect(&repaint,
|
||||
&pnode->view->transform.boundingbox, damage);
|
||||
@@ -568,7 +581,7 @@ pixman_renderer_repaint_output(struct weston_output *output,
|
||||
pixman_region32_t hw_damage;
|
||||
|
||||
assert(output->from_blend_to_output_by_backend ||
|
||||
output->from_blend_to_output == NULL);
|
||||
output->color_outcome->from_blend_to_output == NULL);
|
||||
|
||||
if (!po->hw_buffer) {
|
||||
po->hw_extra_damage = NULL;
|
||||
@@ -598,7 +611,8 @@ pixman_renderer_repaint_output(struct weston_output *output,
|
||||
}
|
||||
|
||||
static void
|
||||
pixman_renderer_flush_damage(struct weston_surface *surface)
|
||||
pixman_renderer_flush_damage(struct weston_surface *surface,
|
||||
struct weston_buffer *buffer)
|
||||
{
|
||||
/* No-op for pixman renderer */
|
||||
}
|
||||
@@ -619,6 +633,26 @@ buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
|
||||
ps->buffer_destroy_listener.notify = NULL;
|
||||
}
|
||||
|
||||
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_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
{
|
||||
@@ -626,7 +660,9 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
struct wl_shm_buffer *shm_buffer;
|
||||
const struct pixel_format_info *pixel_info;
|
||||
|
||||
weston_buffer_reference(&ps->buffer_ref, buffer);
|
||||
weston_buffer_reference(&ps->buffer_ref, buffer,
|
||||
buffer ? BUFFER_MAY_BE_ACCESSED :
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&ps->buffer_release_ref,
|
||||
es->buffer_release_ref.buffer_release);
|
||||
|
||||
@@ -643,32 +679,40 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
||||
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);
|
||||
if (buffer->type == WESTON_BUFFER_SOLID) {
|
||||
pixman_renderer_surface_set_color(es,
|
||||
buffer->solid.r,
|
||||
buffer->solid.g,
|
||||
buffer->solid.b,
|
||||
buffer->solid.a);
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer->type != WESTON_BUFFER_SHM) {
|
||||
weston_log("Pixman renderer supports only SHM buffers\n");
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
shm_buffer = buffer->shm_buffer;
|
||||
|
||||
pixel_info = pixel_format_get_info_shm(wl_shm_buffer_get_format(shm_buffer));
|
||||
if (!pixel_info || !pixman_format_supported_source(pixel_info->pixman_format)) {
|
||||
weston_log("Unsupported SHM buffer format 0x%x\n",
|
||||
wl_shm_buffer_get_format(shm_buffer));
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL);
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
||||
weston_buffer_send_server_error(buffer,
|
||||
"disconnecting due to unhandled buffer type");
|
||||
return;
|
||||
}
|
||||
|
||||
es->is_opaque = pixel_format_is_opaque(pixel_info);
|
||||
|
||||
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(pixel_info->pixman_format,
|
||||
buffer->width, buffer->height,
|
||||
wl_shm_buffer_get_data(shm_buffer),
|
||||
@@ -696,7 +740,8 @@ pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
|
||||
pixman_image_unref(ps->image);
|
||||
ps->image = NULL;
|
||||
}
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL);
|
||||
weston_buffer_reference(&ps->buffer_ref, NULL,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
||||
free(ps);
|
||||
}
|
||||
@@ -750,26 +795,6 @@ pixman_renderer_create_surface(struct weston_surface *surface)
|
||||
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)
|
||||
{
|
||||
@@ -782,21 +807,6 @@ pixman_renderer_destroy(struct weston_compositor *ec)
|
||||
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,
|
||||
@@ -867,10 +877,7 @@ pixman_renderer_init(struct weston_compositor *ec)
|
||||
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;
|
||||
@@ -907,13 +914,16 @@ pixman_renderer_output_set_buffer(struct weston_output *output,
|
||||
pixman_image_t *buffer)
|
||||
{
|
||||
struct pixman_output_state *po = get_output_state(output);
|
||||
pixman_format_code_t pixman_format;
|
||||
|
||||
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_format = pixman_image_get_format(po->hw_buffer);
|
||||
output->compositor->read_format =
|
||||
pixel_format_get_info_by_pixman(pixman_format);
|
||||
pixman_image_ref(po->hw_buffer);
|
||||
}
|
||||
}
|
||||
@@ -943,19 +953,11 @@ pixman_renderer_output_create(struct weston_output *output,
|
||||
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);
|
||||
pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8,
|
||||
w, h, NULL, 0);
|
||||
|
||||
if (!po->shadow_image) {
|
||||
free(po->shadow_buffer);
|
||||
free(po);
|
||||
return -1;
|
||||
}
|
||||
@@ -977,9 +979,6 @@ pixman_renderer_output_destroy(struct weston_output *output)
|
||||
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;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/platform.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
#include "gl-renderer.h"
|
||||
#include "gl-renderer-internal.h"
|
||||
@@ -477,7 +478,7 @@ gl_renderer_set_egl_device(struct gl_renderer *gr)
|
||||
return;
|
||||
}
|
||||
|
||||
gl_renderer_log_extensions("EGL device extensions", extensions);
|
||||
gl_renderer_log_extensions(gr, "EGL device extensions", extensions);
|
||||
|
||||
/* Try to query the render node using EGL_DRM_RENDER_NODE_FILE_EXT */
|
||||
if (weston_check_egl_extension(extensions, "EGL_EXT_device_drm_render_node"))
|
||||
@@ -490,8 +491,10 @@ gl_renderer_set_egl_device(struct gl_renderer *gr)
|
||||
gr->drm_device = gr->query_device_string(gr->egl_device,
|
||||
EGL_DRM_DEVICE_FILE_EXT);
|
||||
|
||||
if (!gr->drm_device)
|
||||
weston_log("failed to query DRM device from EGL\n");
|
||||
if (gr->drm_device)
|
||||
weston_log("Using rendering device: %s\n", gr->drm_device);
|
||||
else
|
||||
weston_log("warning: failed to query rendering device from EGL\n");
|
||||
}
|
||||
|
||||
int
|
||||
@@ -573,8 +576,7 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
gl_renderer_log_extensions("EGL client extensions",
|
||||
extensions);
|
||||
gl_renderer_log_extensions(gr, "EGL client extensions", extensions);
|
||||
|
||||
if (weston_check_egl_extension(extensions, "EGL_EXT_device_query")) {
|
||||
gr->query_display_attrib =
|
||||
@@ -594,8 +596,10 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr)
|
||||
weston_log("warning: EGL_EXT_platform_base not supported.\n");
|
||||
|
||||
/* Surfaceless is unusable without platform_base extension */
|
||||
if (gr->platform == EGL_PLATFORM_SURFACELESS_MESA)
|
||||
if (gr->platform == EGL_PLATFORM_SURFACELESS_MESA) {
|
||||
weston_log("Error: EGL surfaceless platform cannot be used.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -615,6 +619,7 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr)
|
||||
/* at this point we definitely have some platform extensions but
|
||||
* haven't found the supplied platform, so chances are it's
|
||||
* not supported. */
|
||||
weston_log("Error: EGL does not support %s platform.\n", extension_suffix);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -739,5 +744,25 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
|
||||
"to missing EGL_KHR_wait_sync extension\n");
|
||||
}
|
||||
|
||||
weston_log("EGL features:\n");
|
||||
weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n",
|
||||
yesno(gr->has_bind_display));
|
||||
weston_log_continue(STAMP_SPACE "context priority: %s\n",
|
||||
yesno(gr->has_context_priority));
|
||||
weston_log_continue(STAMP_SPACE "buffer age: %s\n",
|
||||
yesno(gr->has_egl_buffer_age));
|
||||
weston_log_continue(STAMP_SPACE "partial update: %s\n",
|
||||
yesno(gr->has_egl_partial_update));
|
||||
weston_log_continue(STAMP_SPACE "swap buffers with damage: %s\n",
|
||||
yesno(gr->swap_buffers_with_damage));
|
||||
weston_log_continue(STAMP_SPACE "configless context: %s\n",
|
||||
yesno(gr->has_configless_context));
|
||||
weston_log_continue(STAMP_SPACE "surfaceless context: %s\n",
|
||||
yesno(gr->has_surfaceless_context));
|
||||
weston_log_continue(STAMP_SPACE "dmabuf support: %s\n",
|
||||
gr->has_dmabuf_import ?
|
||||
(gr->has_dmabuf_import_modifiers ? "modifiers" : "legacy") :
|
||||
"no");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright 2012 Intel Corporation
|
||||
* Copyright 2015,2019,2021 Collabora, Ltd.
|
||||
* Copyright 2016 NVIDIA Corporation
|
||||
* Copyright 2021 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -46,10 +47,18 @@
|
||||
#define SHADER_COLOR_CURVE_IDENTITY 0
|
||||
#define SHADER_COLOR_CURVE_LUT_3x1D 1
|
||||
|
||||
/* enum gl_shader_color_mapping */
|
||||
#define SHADER_COLOR_MAPPING_IDENTITY 0
|
||||
#define SHADER_COLOR_MAPPING_3DLUT 1
|
||||
|
||||
#if DEF_VARIANT == SHADER_VARIANT_EXTERNAL
|
||||
#extension GL_OES_EGL_image_external : require
|
||||
#endif
|
||||
|
||||
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
|
||||
#extension GL_OES_texture_3D : require
|
||||
#endif
|
||||
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
#define HIGHPRECISION highp
|
||||
#else
|
||||
@@ -66,6 +75,11 @@ compile_const int c_variant = DEF_VARIANT;
|
||||
compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT;
|
||||
compile_const bool c_green_tint = DEF_GREEN_TINT;
|
||||
compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE;
|
||||
compile_const int c_color_mapping = DEF_COLOR_MAPPING;
|
||||
|
||||
compile_const bool c_need_color_pipeline =
|
||||
c_color_pre_curve != SHADER_COLOR_CURVE_IDENTITY ||
|
||||
c_color_mapping != SHADER_COLOR_MAPPING_IDENTITY;
|
||||
|
||||
compile_const bool c_need_color_pipeline =
|
||||
c_color_pre_curve != SHADER_COLOR_CURVE_IDENTITY;
|
||||
@@ -108,11 +122,16 @@ uniform sampler2D tex;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex1;
|
||||
uniform sampler2D tex2;
|
||||
uniform float alpha;
|
||||
uniform float view_alpha;
|
||||
uniform vec4 unicolor;
|
||||
uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d;
|
||||
uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset;
|
||||
|
||||
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
|
||||
uniform HIGHPRECISION sampler3D color_mapping_lut_3d;
|
||||
uniform HIGHPRECISION vec2 color_mapping_lut_scale_offset;
|
||||
#endif
|
||||
|
||||
vec4
|
||||
sample_input_texture()
|
||||
{
|
||||
@@ -171,6 +190,12 @@ lut_texcoord(float x, vec2 scale_offset)
|
||||
return x * scale_offset.s + scale_offset.t;
|
||||
}
|
||||
|
||||
vec3
|
||||
lut_texcoord(vec3 pos, vec2 scale_offset)
|
||||
{
|
||||
return pos * scale_offset.s + scale_offset.t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample a 1D LUT which is a single row of a 2D texture. The 2D texture has
|
||||
* four rows so that the centers of texels have precise y-coordinates.
|
||||
@@ -202,10 +227,41 @@ color_pre_curve(vec3 color)
|
||||
}
|
||||
}
|
||||
|
||||
vec3
|
||||
sample_color_mapping_lut_3d(vec3 color)
|
||||
{
|
||||
vec3 pos, ret = vec3(0.0, 0.0, 0.0);
|
||||
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
|
||||
pos = lut_texcoord(color, color_mapping_lut_scale_offset);
|
||||
ret = texture3D(color_mapping_lut_3d, pos).rgb;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
vec3
|
||||
color_mapping(vec3 color)
|
||||
{
|
||||
if (c_color_mapping == SHADER_COLOR_MAPPING_IDENTITY)
|
||||
return color;
|
||||
else if (c_color_mapping == SHADER_COLOR_MAPPING_3DLUT)
|
||||
return sample_color_mapping_lut_3d(color);
|
||||
else /* Never reached, bad c_color_mapping. */
|
||||
return vec3(1.0, 0.3, 1.0);
|
||||
}
|
||||
|
||||
vec4
|
||||
color_pipeline(vec4 color)
|
||||
{
|
||||
/* Ensure straight alpha */
|
||||
if (c_input_is_premult) {
|
||||
if (color.a == 0.0)
|
||||
color.rgb = vec3(0, 0, 0);
|
||||
else
|
||||
color.rgb *= 1.0 / color.a;
|
||||
}
|
||||
|
||||
color.rgb = color_pre_curve(color.rgb);
|
||||
color.rgb = color_mapping(color.rgb);
|
||||
|
||||
return color;
|
||||
}
|
||||
@@ -218,35 +274,14 @@ main()
|
||||
/* Electrical (non-linear) RGBA values, may be premult or not */
|
||||
color = sample_input_texture();
|
||||
|
||||
if (c_need_color_pipeline) {
|
||||
/* Ensure straight alpha */
|
||||
if (c_input_is_premult) {
|
||||
if (color.a == 0.0)
|
||||
color.rgb = vec3(0, 0, 0);
|
||||
else
|
||||
color.rgb *= 1.0 / color.a;
|
||||
}
|
||||
if (c_need_color_pipeline)
|
||||
color = color_pipeline(color); /* Produces straight alpha */
|
||||
|
||||
color = color_pipeline(color);
|
||||
|
||||
/* View alpha (opacity) */
|
||||
color.a *= alpha;
|
||||
|
||||
/* pre-multiply for blending */
|
||||
/* Ensure pre-multiplied for blending */
|
||||
if (!c_input_is_premult || c_need_color_pipeline)
|
||||
color.rgb *= color.a;
|
||||
} else {
|
||||
/* Fast path for disabled color management */
|
||||
|
||||
if (c_input_is_premult) {
|
||||
/* View alpha (opacity) */
|
||||
color *= alpha;
|
||||
} else {
|
||||
/* View alpha (opacity) */
|
||||
color.a *= alpha;
|
||||
/* pre-multiply for blending */
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
}
|
||||
color *= view_alpha;
|
||||
|
||||
if (c_green_tint)
|
||||
color = vec4(0.0, 0.3, 0.0, 0.2) + color * 0.8;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright © 2019 Collabora, Ltd.
|
||||
* Copyright © 2019 Harish Krupo
|
||||
* Copyright © 2019 Intel Corporation
|
||||
* Copyright 2021 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -56,6 +57,12 @@ enum gl_shader_color_curve {
|
||||
SHADER_COLOR_CURVE_LUT_3x1D,
|
||||
};
|
||||
|
||||
/* Keep the following in sync with fragment.glsl. */
|
||||
enum gl_shader_color_mapping {
|
||||
SHADER_COLOR_MAPPING_IDENTITY = 0,
|
||||
SHADER_COLOR_MAPPING_3DLUT,
|
||||
};
|
||||
|
||||
/** GL shader requirements key
|
||||
*
|
||||
* This structure is used as a binary blob key for building and searching
|
||||
@@ -70,13 +77,14 @@ struct gl_shader_requirements
|
||||
unsigned variant:4; /* enum gl_shader_texture_variant */
|
||||
bool input_is_premult:1;
|
||||
bool green_tint:1;
|
||||
unsigned color_pre_curve:1; /* enum gl_shader_color_curve */
|
||||
|
||||
unsigned color_pre_curve:1; /* enum gl_shader_color_curve */
|
||||
unsigned color_mapping:1; /* enum gl_shader_color_mapping */
|
||||
/*
|
||||
* The total size of all bitfields plus pad_bits_ must fill up exactly
|
||||
* how many bytes the compiler allocates for them together.
|
||||
*/
|
||||
unsigned pad_bits_:25;
|
||||
unsigned pad_bits_:24;
|
||||
};
|
||||
static_assert(sizeof(struct gl_shader_requirements) ==
|
||||
4 /* total bitfield size in bytes */,
|
||||
@@ -96,11 +104,18 @@ struct gl_shader_config {
|
||||
GLuint input_tex[GL_SHADER_INPUT_TEX_MAX];
|
||||
GLuint color_pre_curve_lut_tex;
|
||||
GLfloat color_pre_curve_lut_scale_offset[2];
|
||||
union {
|
||||
struct {
|
||||
GLuint tex;
|
||||
GLfloat scale_offset[2];
|
||||
} lut3d;
|
||||
} color_mapping;
|
||||
};
|
||||
|
||||
struct gl_renderer {
|
||||
struct weston_renderer base;
|
||||
struct weston_compositor *compositor;
|
||||
struct weston_log_scope *renderer_scope;
|
||||
|
||||
bool fragment_shader_debug;
|
||||
bool fan_debug;
|
||||
@@ -156,6 +171,7 @@ struct gl_renderer {
|
||||
|
||||
bool has_texture_type_2_10_10_10_rev;
|
||||
bool has_gl_texture_rg;
|
||||
bool has_texture_norm16;
|
||||
|
||||
struct gl_shader *current_shader;
|
||||
struct gl_shader *fallback_shader;
|
||||
@@ -198,7 +214,8 @@ void
|
||||
gl_renderer_print_egl_error_state(void);
|
||||
|
||||
void
|
||||
gl_renderer_log_extensions(const char *name, const char *extensions);
|
||||
gl_renderer_log_extensions(struct gl_renderer *gr,
|
||||
const char *name, const char *extensions);
|
||||
|
||||
void
|
||||
log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig);
|
||||
|
||||
+766
-808
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2021 Collabora, Ltd.
|
||||
* Copyright 2021 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -44,11 +45,22 @@ struct gl_renderer_color_curve {
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct gl_renderer_color_mapping {
|
||||
enum gl_shader_color_mapping type;
|
||||
union {
|
||||
struct {
|
||||
GLuint tex3d;
|
||||
float scale;
|
||||
float offset;
|
||||
} lut3d;
|
||||
};
|
||||
} ;
|
||||
|
||||
struct gl_renderer_color_transform {
|
||||
struct weston_color_transform *owner;
|
||||
struct wl_listener destroy_listener;
|
||||
|
||||
struct gl_renderer_color_curve pre_curve;
|
||||
struct gl_renderer_color_mapping mapping;
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -58,10 +70,19 @@ gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve)
|
||||
glDeleteTextures(1, &gl_curve->tex);
|
||||
}
|
||||
|
||||
static void
|
||||
gl_renderer_color_mapping_fini(struct gl_renderer_color_mapping *gl_mapping)
|
||||
{
|
||||
if (gl_mapping->type == SHADER_COLOR_MAPPING_3DLUT &&
|
||||
gl_mapping->lut3d.tex3d)
|
||||
glDeleteTextures(1, &gl_mapping->lut3d.tex3d);
|
||||
}
|
||||
|
||||
static void
|
||||
gl_renderer_color_transform_destroy(struct gl_renderer_color_transform *gl_xform)
|
||||
{
|
||||
gl_renderer_color_curve_fini(&gl_xform->pre_curve);
|
||||
gl_renderer_color_mapping_fini(&gl_xform->mapping);
|
||||
wl_list_remove(&gl_xform->destroy_listener.link);
|
||||
free(gl_xform);
|
||||
}
|
||||
@@ -152,6 +173,47 @@ gl_color_curve_lut_3x1d(struct gl_renderer_color_curve *gl_curve,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
gl_3d_lut(struct gl_renderer_color_transform *gl_xform,
|
||||
struct weston_color_transform *xform)
|
||||
{
|
||||
|
||||
GLuint tex3d;
|
||||
float *lut;
|
||||
const unsigned dim_size = xform->mapping.u.lut3d.optimal_len;
|
||||
|
||||
lut = calloc(3 * dim_size * dim_size * dim_size, sizeof *lut);
|
||||
if (!lut)
|
||||
return false;
|
||||
|
||||
xform->mapping.u.lut3d.fill_in(xform, lut, dim_size);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGenTextures(1, &tex3d);
|
||||
glBindTexture(GL_TEXTURE_3D, tex3d);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB32F, dim_size, dim_size, dim_size, 0,
|
||||
GL_RGB, GL_FLOAT, lut);
|
||||
|
||||
glBindTexture(GL_TEXTURE_3D, 0);
|
||||
gl_xform->mapping.type = SHADER_COLOR_MAPPING_3DLUT;
|
||||
gl_xform->mapping.lut3d.tex3d = tex3d;
|
||||
gl_xform->mapping.lut3d.scale = (float)(dim_size - 1) / dim_size;
|
||||
gl_xform->mapping.lut3d.offset = 0.5f / dim_size;
|
||||
|
||||
free(lut);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const struct gl_renderer_color_transform *
|
||||
gl_renderer_color_transform_from(struct weston_color_transform *xform)
|
||||
{
|
||||
@@ -160,6 +222,7 @@ gl_renderer_color_transform_from(struct weston_color_transform *xform)
|
||||
.pre_curve.tex = 0,
|
||||
.pre_curve.scale = 0.0f,
|
||||
.pre_curve.offset = 0.0f,
|
||||
.mapping.type = SHADER_COLOR_MAPPING_IDENTITY,
|
||||
};
|
||||
struct gl_renderer_color_transform *gl_xform;
|
||||
bool ok = false;
|
||||
@@ -190,6 +253,19 @@ gl_renderer_color_transform_from(struct weston_color_transform *xform)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
gl_renderer_color_transform_destroy(gl_xform);
|
||||
return NULL;
|
||||
}
|
||||
switch (xform->mapping.type) {
|
||||
case WESTON_COLOR_MAPPING_TYPE_IDENTITY:
|
||||
gl_xform->mapping = no_op_gl_xform.mapping;
|
||||
ok = true;
|
||||
break;
|
||||
case WESTON_COLOR_MAPPING_TYPE_3D_LUT:
|
||||
ok = gl_3d_lut(gl_xform, xform);
|
||||
break;
|
||||
}
|
||||
if (!ok) {
|
||||
gl_renderer_color_transform_destroy(gl_xform);
|
||||
return NULL;
|
||||
@@ -203,6 +279,7 @@ gl_shader_config_set_color_transform(struct gl_shader_config *sconf,
|
||||
struct weston_color_transform *xform)
|
||||
{
|
||||
const struct gl_renderer_color_transform *gl_xform;
|
||||
bool ret = false;
|
||||
|
||||
gl_xform = gl_renderer_color_transform_from(xform);
|
||||
if (!gl_xform)
|
||||
@@ -213,5 +290,22 @@ gl_shader_config_set_color_transform(struct gl_shader_config *sconf,
|
||||
sconf->color_pre_curve_lut_scale_offset[0] = gl_xform->pre_curve.scale;
|
||||
sconf->color_pre_curve_lut_scale_offset[1] = gl_xform->pre_curve.offset;
|
||||
|
||||
return true;
|
||||
sconf->req.color_mapping = gl_xform->mapping.type;
|
||||
switch (gl_xform->mapping.type) {
|
||||
case SHADER_COLOR_MAPPING_3DLUT:
|
||||
sconf->color_mapping.lut3d.tex = gl_xform->mapping.lut3d.tex3d;
|
||||
sconf->color_mapping.lut3d.scale_offset[0] =
|
||||
gl_xform->mapping.lut3d.scale;
|
||||
sconf->color_mapping.lut3d.scale_offset[1] =
|
||||
gl_xform->mapping.lut3d.offset;
|
||||
assert(sconf->color_mapping.lut3d.scale_offset[0] > 0.0);
|
||||
assert(sconf->color_mapping.lut3d.scale_offset[1] > 0.0);
|
||||
ret = true;
|
||||
break;
|
||||
case SHADER_COLOR_MAPPING_IDENTITY:
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright 2016 NVIDIA Corporation
|
||||
* Copyright 2019 Harish Krupo
|
||||
* Copyright 2019 Intel Corporation
|
||||
* Copyright 2021 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -56,10 +57,16 @@ struct gl_shader {
|
||||
GLuint vertex_shader, fragment_shader;
|
||||
GLint proj_uniform;
|
||||
GLint tex_uniforms[3];
|
||||
GLint alpha_uniform;
|
||||
GLint view_alpha_uniform;
|
||||
GLint color_uniform;
|
||||
GLint color_pre_curve_lut_2d_uniform;
|
||||
GLint color_pre_curve_lut_scale_offset_uniform;
|
||||
union {
|
||||
struct {
|
||||
GLint tex_uniform;
|
||||
GLint scale_offset_uniform;
|
||||
} lut3d;
|
||||
} color_mapping;
|
||||
struct wl_list link; /* gl_renderer::shader_list */
|
||||
struct timespec last_used;
|
||||
};
|
||||
@@ -97,6 +104,19 @@ gl_shader_color_curve_to_string(enum gl_shader_color_curve kind)
|
||||
return "!?!?"; /* never reached */
|
||||
}
|
||||
|
||||
static const char *
|
||||
gl_shader_color_mapping_to_string(enum gl_shader_color_mapping kind)
|
||||
{
|
||||
switch (kind) {
|
||||
#define CASERET(x) case x: return #x;
|
||||
CASERET(SHADER_COLOR_MAPPING_IDENTITY)
|
||||
CASERET(SHADER_COLOR_MAPPING_3DLUT)
|
||||
#undef CASERET
|
||||
}
|
||||
|
||||
return "!?!?"; /* never reached */
|
||||
}
|
||||
|
||||
static void
|
||||
dump_program_with_line_numbers(int count, const char **sources)
|
||||
{
|
||||
@@ -162,9 +182,10 @@ create_shader_description_string(const struct gl_shader_requirements *req)
|
||||
int size;
|
||||
char *str;
|
||||
|
||||
size = asprintf(&str, "%s %s %cinput_is_premult %cgreen",
|
||||
size = asprintf(&str, "%s %s %s %cinput_is_premult %cgreen",
|
||||
gl_shader_texture_variant_to_string(req->variant),
|
||||
gl_shader_color_curve_to_string(req->color_pre_curve),
|
||||
gl_shader_color_mapping_to_string(req->color_mapping),
|
||||
req->input_is_premult ? '+' : '-',
|
||||
req->green_tint ? '+' : '-');
|
||||
if (size < 0)
|
||||
@@ -182,10 +203,12 @@ create_shader_config_string(const struct gl_shader_requirements *req)
|
||||
"#define DEF_GREEN_TINT %s\n"
|
||||
"#define DEF_INPUT_IS_PREMULT %s\n"
|
||||
"#define DEF_COLOR_PRE_CURVE %s\n"
|
||||
"#define DEF_COLOR_MAPPING %s\n"
|
||||
"#define DEF_VARIANT %s\n",
|
||||
req->green_tint ? "true" : "false",
|
||||
req->input_is_premult ? "true" : "false",
|
||||
gl_shader_color_curve_to_string(req->color_pre_curve),
|
||||
gl_shader_color_mapping_to_string(req->color_mapping),
|
||||
gl_shader_texture_variant_to_string(req->variant));
|
||||
if (size < 0)
|
||||
return NULL;
|
||||
@@ -260,7 +283,7 @@ gl_shader_create(struct gl_renderer *gr,
|
||||
shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex");
|
||||
shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1");
|
||||
shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2");
|
||||
shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
|
||||
shader->view_alpha_uniform = glGetUniformLocation(shader->program, "view_alpha");
|
||||
shader->color_uniform = glGetUniformLocation(shader->program,
|
||||
"unicolor");
|
||||
shader->color_pre_curve_lut_2d_uniform =
|
||||
@@ -268,6 +291,16 @@ gl_shader_create(struct gl_renderer *gr,
|
||||
shader->color_pre_curve_lut_scale_offset_uniform =
|
||||
glGetUniformLocation(shader->program, "color_pre_curve_lut_scale_offset");
|
||||
|
||||
switch(requirements->color_mapping) {
|
||||
case SHADER_COLOR_MAPPING_3DLUT:
|
||||
shader->color_mapping.lut3d.tex_uniform =
|
||||
glGetUniformLocation(shader->program, "color_mapping_lut_3d");
|
||||
shader->color_mapping.lut3d.scale_offset_uniform =
|
||||
glGetUniformLocation(shader->program,"color_mapping_lut_scale_offset");
|
||||
break;
|
||||
case SHADER_COLOR_MAPPING_IDENTITY:
|
||||
break;
|
||||
}
|
||||
free(conf);
|
||||
|
||||
wl_list_insert(&gr->shader_list, &shader->link);
|
||||
@@ -376,6 +409,7 @@ gl_renderer_create_fallback_shader(struct gl_renderer *gr)
|
||||
.variant = SHADER_VARIANT_SOLID,
|
||||
.input_is_premult = true,
|
||||
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
|
||||
.color_mapping = SHADER_COLOR_MAPPING_IDENTITY,
|
||||
};
|
||||
struct gl_shader *shader;
|
||||
|
||||
@@ -481,7 +515,7 @@ gl_shader_load_config(struct gl_shader *shader,
|
||||
glUniformMatrix4fv(shader->proj_uniform,
|
||||
1, GL_FALSE, sconf->projection.d);
|
||||
glUniform4fv(shader->color_uniform, 1, sconf->unicolor);
|
||||
glUniform1f(shader->alpha_uniform, sconf->view_alpha);
|
||||
glUniform1f(shader->view_alpha_uniform, sconf->view_alpha);
|
||||
|
||||
in_tgt = gl_shader_texture_variant_get_target(sconf->req.variant);
|
||||
for (i = 0; i < GL_SHADER_INPUT_TEX_MAX; i++) {
|
||||
@@ -497,9 +531,8 @@ gl_shader_load_config(struct gl_shader *shader,
|
||||
glTexParameteri(in_tgt, GL_TEXTURE_MAG_FILTER, in_filter);
|
||||
}
|
||||
|
||||
/* Fixed texture unit for color_pre_curve LUT */
|
||||
/* Fixed texture unit for color_pre_curve LUT if it is available */
|
||||
i = GL_SHADER_INPUT_TEX_MAX;
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
switch (sconf->req.color_pre_curve) {
|
||||
case SHADER_COLOR_CURVE_IDENTITY:
|
||||
assert(sconf->color_pre_curve_lut_tex == 0);
|
||||
@@ -508,13 +541,32 @@ gl_shader_load_config(struct gl_shader *shader,
|
||||
assert(sconf->color_pre_curve_lut_tex != 0);
|
||||
assert(shader->color_pre_curve_lut_2d_uniform != -1);
|
||||
assert(shader->color_pre_curve_lut_scale_offset_uniform != -1);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_2D, sconf->color_pre_curve_lut_tex);
|
||||
glUniform1i(shader->color_pre_curve_lut_2d_uniform, i);
|
||||
i++;
|
||||
glUniform2fv(shader->color_pre_curve_lut_scale_offset_uniform,
|
||||
1, sconf->color_pre_curve_lut_scale_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (sconf->req.color_mapping) {
|
||||
case SHADER_COLOR_MAPPING_IDENTITY:
|
||||
break;
|
||||
case SHADER_COLOR_MAPPING_3DLUT:
|
||||
assert(shader->color_mapping.lut3d.tex_uniform != -1);
|
||||
assert(sconf->color_mapping.lut3d.tex != 0);
|
||||
assert(shader->color_mapping.lut3d.scale_offset_uniform != -1);
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_3D, sconf->color_mapping.lut3d.tex);
|
||||
glUniform1i(shader->color_mapping.lut3d.tex_uniform, i);
|
||||
glUniform2fv(shader->color_mapping.lut3d.scale_offset_uniform,
|
||||
1, sconf->color_mapping.lut3d.scale_offset);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -540,7 +592,7 @@ gl_renderer_use_program(struct gl_renderer *gr,
|
||||
shader = gr->fallback_shader;
|
||||
glUseProgram(shader->program);
|
||||
glUniform4fv(shader->color_uniform, 1, fallback_shader_color);
|
||||
glUniform1f(shader->alpha_uniform, 1.0f);
|
||||
glUniform1f(shader->view_alpha_uniform, 1.0f);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+36
-12
@@ -40,11 +40,13 @@
|
||||
#include "shared/timespec-util.h"
|
||||
#include "backend.h"
|
||||
#include "libweston-internal.h"
|
||||
#include "pixel-formats.h"
|
||||
|
||||
#include "wcap/wcap-decode.h"
|
||||
|
||||
struct screenshooter_frame_listener {
|
||||
struct wl_listener listener;
|
||||
struct wl_listener frame_listener;
|
||||
struct wl_listener buffer_destroy_listener;
|
||||
struct weston_buffer *buffer;
|
||||
struct weston_output *output;
|
||||
weston_screenshooter_done_func_t done;
|
||||
@@ -119,15 +121,20 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct screenshooter_frame_listener *l =
|
||||
container_of(listener,
|
||||
struct screenshooter_frame_listener, listener);
|
||||
struct screenshooter_frame_listener,
|
||||
frame_listener);
|
||||
struct weston_output *output = l->output;
|
||||
struct weston_compositor *compositor = output->compositor;
|
||||
const pixman_format_code_t pixman_format =
|
||||
compositor->read_format->pixman_format;
|
||||
int32_t stride;
|
||||
uint8_t *pixels, *d, *s;
|
||||
|
||||
weston_output_disable_planes_decr(output);
|
||||
wl_list_remove(&listener->link);
|
||||
stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
|
||||
wl_list_remove(&l->buffer_destroy_listener.link);
|
||||
|
||||
stride = l->buffer->width * (PIXMAN_FORMAT_BPP(pixman_format) / 8);
|
||||
pixels = malloc(stride * l->buffer->height);
|
||||
|
||||
if (pixels == NULL) {
|
||||
@@ -148,7 +155,7 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data)
|
||||
|
||||
wl_shm_buffer_begin_access(l->buffer->shm_buffer);
|
||||
|
||||
switch (compositor->read_format) {
|
||||
switch (pixman_format) {
|
||||
case PIXMAN_a8r8g8b8:
|
||||
case PIXMAN_x8r8g8b8:
|
||||
if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
|
||||
@@ -174,6 +181,22 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data)
|
||||
free(l);
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_destroy_handle(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct screenshooter_frame_listener *l =
|
||||
container_of(listener,
|
||||
struct screenshooter_frame_listener,
|
||||
buffer_destroy_listener);
|
||||
|
||||
weston_output_disable_planes_decr(l->output);
|
||||
wl_list_remove(&listener->link);
|
||||
wl_list_remove(&l->frame_listener.link);
|
||||
l->done(l->data, WESTON_SCREENSHOOTER_BAD_BUFFER);
|
||||
|
||||
free(l);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_screenshooter_shoot(struct weston_output *output,
|
||||
struct weston_buffer *buffer,
|
||||
@@ -181,15 +204,11 @@ weston_screenshooter_shoot(struct weston_output *output,
|
||||
{
|
||||
struct screenshooter_frame_listener *l;
|
||||
|
||||
if (!wl_shm_buffer_get(buffer->resource)) {
|
||||
if (buffer->type != WESTON_BUFFER_SHM) {
|
||||
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);
|
||||
@@ -206,8 +225,13 @@ weston_screenshooter_shoot(struct weston_output *output,
|
||||
l->output = output;
|
||||
l->done = done;
|
||||
l->data = data;
|
||||
l->listener.notify = screenshooter_frame_notify;
|
||||
wl_signal_add(&output->frame_signal, &l->listener);
|
||||
|
||||
l->frame_listener.notify = screenshooter_frame_notify;
|
||||
wl_signal_add(&output->frame_signal, &l->frame_listener);
|
||||
|
||||
l->buffer_destroy_listener.notify = buffer_destroy_handle;
|
||||
wl_signal_add(&buffer->destroy_signal, &l->buffer_destroy_listener);
|
||||
|
||||
weston_output_disable_planes_incr(output);
|
||||
weston_output_schedule_repaint(output);
|
||||
|
||||
@@ -418,7 +442,7 @@ weston_recorder_create(struct weston_output *output, const char *filename)
|
||||
|
||||
header.magic = WCAP_HEADER_MAGIC;
|
||||
|
||||
switch (compositor->read_format) {
|
||||
switch (compositor->read_format->pixman_format) {
|
||||
case PIXMAN_x8r8g8b8:
|
||||
case PIXMAN_a8r8g8b8:
|
||||
header.format = WCAP_FORMAT_XRGB8888;
|
||||
|
||||
@@ -206,7 +206,7 @@ map_calibrator(struct weston_touch_calibrator *calibrator)
|
||||
calibrator->view->is_mapped = true;
|
||||
|
||||
calibrator->surface->output = calibrator->output;
|
||||
calibrator->surface->is_mapped = true;
|
||||
weston_surface_map(calibrator->surface);
|
||||
|
||||
weston_output_schedule_repaint(calibrator->output);
|
||||
|
||||
@@ -674,6 +674,16 @@ bind_touch_calibration(struct wl_client *client,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
weston_compositor_destroy_touch_calibrator(struct weston_compositor *ec)
|
||||
{
|
||||
/* TODO: handle weston_compositor::touch_calibrator destruction
|
||||
* see
|
||||
* https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/819#note_1345191
|
||||
*/
|
||||
weston_layer_fini(&ec->calibrator_layer);
|
||||
}
|
||||
|
||||
/** Advertise touch_calibration support
|
||||
*
|
||||
* \param compositor The compositor to init for.
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "vertex-clipping.h"
|
||||
|
||||
float
|
||||
WESTON_EXPORT_FOR_TESTS float
|
||||
float_difference(float a, float b)
|
||||
{
|
||||
/* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
|
||||
@@ -282,7 +283,7 @@ clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
|
||||
#define min(a, b) (((a) > (b)) ? (b) : (a))
|
||||
#define clip(x, a, b) min(max(x, a), b)
|
||||
|
||||
int
|
||||
WESTON_EXPORT_FOR_TESTS int
|
||||
clip_simple(struct clip_context *ctx,
|
||||
struct polygon8 *surf,
|
||||
float *ex,
|
||||
@@ -296,7 +297,7 @@ clip_simple(struct clip_context *ctx,
|
||||
return surf->n;
|
||||
}
|
||||
|
||||
int
|
||||
WESTON_EXPORT_FOR_TESTS int
|
||||
clip_transformed(struct clip_context *ctx,
|
||||
struct polygon8 *surf,
|
||||
float *ex,
|
||||
|
||||
@@ -1,917 +0,0 @@
|
||||
/*
|
||||
* 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 <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 BUILD_DRM_COMPOSITOR
|
||||
|
||||
#include <xf86drm.h>
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
drmDropMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drmSetMaster(int drm_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* major()/minor() */
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
# include <sys/mkdev.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
# include <sys/sysmacros.h>
|
||||
#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(int *ngroups)
|
||||
{
|
||||
int n;
|
||||
gid_t *groups;
|
||||
|
||||
n = getgroups(0, NULL);
|
||||
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Unable to retrieve groups: %s\n",
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
groups = malloc(n * sizeof(gid_t));
|
||||
if (!groups)
|
||||
return NULL;
|
||||
|
||||
if (getgroups(n, groups) < 0) {
|
||||
fprintf(stderr, "Unable to retrieve groups: %s\n",
|
||||
strerror(errno));
|
||||
free(groups);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ngroups = n;
|
||||
return groups;
|
||||
}
|
||||
|
||||
static bool
|
||||
weston_launch_allowed(struct weston_launch *wl)
|
||||
{
|
||||
struct group *gr;
|
||||
gid_t *groups;
|
||||
int ngroups;
|
||||
#ifdef HAVE_SYSTEMD_LOGIN
|
||||
char *session, *seat;
|
||||
int err;
|
||||
#endif
|
||||
|
||||
if (getuid() == 0)
|
||||
return true;
|
||||
|
||||
gr = getgrnam("weston-launch");
|
||||
if (gr) {
|
||||
groups = read_groups(&ngroups);
|
||||
if (groups && ngroups > 0) {
|
||||
while (ngroups--) {
|
||||
if (groups[ngroups] == 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) {
|
||||
fprintf(stderr, "weston: socketpair failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0) {
|
||||
fprintf(stderr, "weston: fcntl failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
open_tty_by_number(int ttynr)
|
||||
{
|
||||
int ret;
|
||||
char filename[16];
|
||||
|
||||
ret = snprintf(filename, sizeof filename, "/dev/tty%d", ttynr);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return open(filename, O_RDWR | O_NOCTTY);
|
||||
}
|
||||
|
||||
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: %s\n",
|
||||
message->path, strerror(errno));
|
||||
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;
|
||||
}
|
||||
struct { int reply_id; int ret; } reply_iov_data = { WESTON_LAUNCHER_OPEN_REPLY, ret };
|
||||
iov.iov_base = &reply_iov_data;
|
||||
iov.iov_len = sizeof reply_iov_data;
|
||||
|
||||
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 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_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;
|
||||
case WESTON_LAUNCHER_DEACTIVATE_DONE:
|
||||
close_input_fds(wl);
|
||||
drmDropMaster(wl->drm_fd);
|
||||
ioctl(wl->tty, VT_RELDISP, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
quit(struct weston_launch *wl, int status)
|
||||
{
|
||||
struct vt_mode mode = { 0 };
|
||||
int err;
|
||||
int oldtty;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a fresh handle to the tty as the previous one is in
|
||||
* hang-up state since weston (the controlling process for
|
||||
* the tty) exit at this point. Reopen before closing the
|
||||
* file descriptor to avoid a potential race condition.
|
||||
*
|
||||
* A similar fix exists in logind, see:
|
||||
* https://github.com/systemd/systemd/pull/990
|
||||
*/
|
||||
oldtty = wl->tty;
|
||||
wl->tty = open_tty_by_number(wl->ttynr);
|
||||
close(oldtty);
|
||||
|
||||
if (ioctl(wl->tty, KDSKBMUTE, 0) &&
|
||||
ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
|
||||
fprintf(stderr, "failed to restore keyboard mode: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
|
||||
fprintf(stderr, "failed to set KD_TEXT mode on tty: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
/* 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");
|
||||
|
||||
if (wl->tty != STDIN_FILENO)
|
||||
close(wl->tty);
|
||||
|
||||
exit(status);
|
||||
}
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "weston: reading signalfd failed: %s\n",
|
||||
strerror(errno));
|
||||
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)
|
||||
break;
|
||||
|
||||
if (wl->verbose)
|
||||
fprintf(stderr, "weston-launch: sending %s to pid %d\n",
|
||||
strsignal(sig.ssi_signo), wl->child);
|
||||
|
||||
kill(wl->child, sig.ssi_signo);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
send_reply(wl, WESTON_LAUNCHER_DEACTIVATE);
|
||||
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);
|
||||
|
||||
if (tty0 < 0) {
|
||||
fprintf(stderr, "weston: could not open tty0: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
|
||||
{
|
||||
fprintf(stderr, "weston: failed to find non-opened console: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl->tty = open_tty_by_number(wl->ttynr);
|
||||
close(tty0);
|
||||
}
|
||||
|
||||
if (wl->tty < 0) {
|
||||
fprintf(stderr, "weston: failed to open tty: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat(wl->tty, &buf) == -1 ||
|
||||
major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) {
|
||||
fprintf(stderr, "weston: weston-launch must be run from a virtual terminal\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!wl->new_user || tty) {
|
||||
if (fstat(wl->tty, &buf) < 0) {
|
||||
fprintf(stderr, "weston: stat %s failed: %s\n", tty,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (major(buf.st_rdev) != TTY_MAJOR) {
|
||||
fprintf(stderr,
|
||||
"weston: invalid tty device: %s\n", tty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl->ttynr = minor(buf.st_rdev);
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, VT_ACTIVATE, wl->ttynr) < 0) {
|
||||
fprintf(stderr,
|
||||
"weston: failed to activate VT: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, VT_WAITACTIVE, wl->ttynr) < 0) {
|
||||
fprintf(stderr,
|
||||
"weston: failed to wait for VT to be active: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode)) {
|
||||
fprintf(stderr,
|
||||
"weston: failed to get current keyboard mode: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, KDSKBMUTE, 1) &&
|
||||
ioctl(wl->tty, KDSKBMODE, K_OFF)) {
|
||||
fprintf(stderr,
|
||||
"weston: failed to set K_OFF keyboard mode: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS)) {
|
||||
fprintf(stderr,
|
||||
"weston: failed to set KD_GRAPHICS mode on tty: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mode.mode = VT_PROCESS;
|
||||
mode.relsig = SIGUSR1;
|
||||
mode.acqsig = SIGUSR2;
|
||||
if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) {
|
||||
fprintf(stderr,
|
||||
"weston: failed to take control of vt handling %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_session(struct weston_launch *wl, char **child_argv)
|
||||
{
|
||||
char **env;
|
||||
char *term;
|
||||
int i;
|
||||
|
||||
if (wl->tty != STDIN_FILENO) {
|
||||
if (setsid() < 0) {
|
||||
fprintf(stderr, "weston: setsid failed %s\n",
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ioctl(wl->tty, TIOCSCTTY, 0) < 0) {
|
||||
fprintf(stderr, "TIOCSCTTY failed - tty is in use %s\n",
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
fprintf(stderr, "putenv %s failed\n", env[i]);
|
||||
}
|
||||
free(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* We open a new session, so it makes sense
|
||||
* to run a new login shell
|
||||
*/
|
||||
child_argv[0] = "/bin/sh";
|
||||
child_argv[1] = "-l";
|
||||
child_argv[2] = "-c";
|
||||
child_argv[3] = "exec " BINDIR "/weston \"$@\"";
|
||||
child_argv[4] = "weston";
|
||||
return 5;
|
||||
}
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "weston: dropping privileges failed %s\n",
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
launch_compositor(struct weston_launch *wl, int argc, char *argv[])
|
||||
{
|
||||
char *child_argv[MAX_ARGV_SIZE];
|
||||
sigset_t mask;
|
||||
int o, i;
|
||||
|
||||
if (wl->verbose)
|
||||
printf("weston-launch: spawned weston with pid: %d\n", getpid());
|
||||
if (wl->new_user) {
|
||||
o = setup_session(wl, child_argv);
|
||||
} else {
|
||||
child_argv[0] = BINDIR "/weston";
|
||||
o = 1;
|
||||
}
|
||||
for (i = 0; i < argc; ++i)
|
||||
child_argv[o + i] = argv[i];
|
||||
child_argv[o + i] = NULL;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
execv(child_argv[0], child_argv);
|
||||
fprintf(stderr, "weston: exec failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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"
|
||||
" e.g. -u joe, requires root.\n");
|
||||
fprintf(stderr, " -t, --tty Start session on alternative tty,\n"
|
||||
" e.g. -t /dev/tty4, requires -u option.\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) {
|
||||
fprintf(stderr, "weston: Permission denied. -u allowed for root only\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
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)) {
|
||||
fprintf(stderr,
|
||||
"weston: Too many arguments to pass to weston: %s\n",
|
||||
strerror(E2BIG));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (tty && !wl.new_user) {
|
||||
fprintf(stderr, "weston: -t/--tty option requires -u/--user option as well\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (wl.new_user)
|
||||
wl.pw = getpwnam(wl.new_user);
|
||||
else
|
||||
wl.pw = getpwuid(getuid());
|
||||
if (wl.pw == NULL) {
|
||||
fprintf(stderr, "weston: failed to get username: %s\n",
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!weston_launch_allowed(&wl)) {
|
||||
fprintf(stderr, "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.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "weston: fork failed %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (wl.child == 0)
|
||||
launch_compositor(&wl, argc - optind, argv + optind);
|
||||
|
||||
close(wl.sock[1]);
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "poll failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (fds[0].revents & POLLIN)
|
||||
handle_socket_msg(&wl);
|
||||
if (fds[1].revents)
|
||||
handle_signal(&wl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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_ACTIVATE,
|
||||
WESTON_LAUNCHER_DEACTIVATE,
|
||||
WESTON_LAUNCHER_DEACTIVATE_DONE,
|
||||
// This event is followed by an fd handle
|
||||
WESTON_LAUNCHER_OPEN_REPLY,
|
||||
};
|
||||
|
||||
struct weston_launcher_message {
|
||||
int opcode;
|
||||
};
|
||||
|
||||
struct weston_launcher_open {
|
||||
struct weston_launcher_message header;
|
||||
int flags;
|
||||
char path[0];
|
||||
};
|
||||
|
||||
#endif
|
||||
+50
-2
@@ -238,8 +238,6 @@ weston_log_subscription_get_data(struct weston_log_subscription *sub)
|
||||
* subscription
|
||||
* @param scope the scope in order to add the subscription to the scope's
|
||||
* subscription list
|
||||
* @returns a weston_log_subscription object in case of success, or NULL
|
||||
* otherwise
|
||||
*
|
||||
* @sa weston_log_subscription_destroy, weston_log_subscription_remove,
|
||||
* weston_log_subscription_add
|
||||
@@ -913,6 +911,56 @@ weston_log_scope_timestamp(struct weston_log_scope *scope,
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Returns a timestamp useful for adding it to a log scope.
|
||||
*
|
||||
* @example
|
||||
* char timestr[128];
|
||||
* static int cached_dm = -1;
|
||||
* char *time_buff = weston_log_timestamp(timestr, sizeof(timestr), &cached_dm);
|
||||
* weston_log_scope_printf(log_scope, "%s %s", time_buff, other_data);
|
||||
*
|
||||
* @param buf a user-supplied buffer
|
||||
* @param len user-supplied length of the buffer
|
||||
* @param cached_tm_mday a cached day of the month, as an integer. Setting this
|
||||
* pointer different from NULL, to an integer value other than was retrieved as
|
||||
* current day of the month, would add an additional line under the form of
|
||||
* 'Date: Y-m-d Z\n'. Setting the pointer to NULL would not print any date, nor
|
||||
* if the value matches the current day of month. Helps identify logs that
|
||||
* spawn multiple days, while still having a shorter time stamp format.
|
||||
* @ingroup log
|
||||
*/
|
||||
WL_EXPORT char *
|
||||
weston_log_timestamp(char *buf, size_t len, int *cached_tm_mday)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm *brokendown_time;
|
||||
char datestr[128];
|
||||
char timestr[128];
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
brokendown_time = localtime(&tv.tv_sec);
|
||||
if (brokendown_time == NULL) {
|
||||
snprintf(buf, len, "%s", "[(NULL)localtime] ");
|
||||
return buf;
|
||||
}
|
||||
|
||||
memset(datestr, 0, sizeof(datestr));
|
||||
if (cached_tm_mday && brokendown_time->tm_mday != *cached_tm_mday) {
|
||||
strftime(datestr, sizeof(datestr), "Date: %Y-%m-%d %Z\n",
|
||||
brokendown_time);
|
||||
*cached_tm_mday = brokendown_time->tm_mday;
|
||||
}
|
||||
|
||||
strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time);
|
||||
/* if datestr is empty it prints only timestr*/
|
||||
snprintf(buf, len, "%s[%s.%03li]", datestr,
|
||||
timestr, (tv.tv_usec / 1000));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
weston_log_subscriber_release(struct weston_log_subscriber *subscriber)
|
||||
{
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "backend.h"
|
||||
#include "libweston-internal.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,
|
||||
const struct timespec *time)
|
||||
{
|
||||
if (animation->frame_counter <= 1)
|
||||
output->zoom.spring_z.timestamp = *time;
|
||||
|
||||
weston_spring_update(&output->zoom.spring_z, time);
|
||||
|
||||
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;
|
||||
weston_output_disable_planes_decr(output);
|
||||
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);
|
||||
|
||||
if (!pointer)
|
||||
return;
|
||||
|
||||
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 (!pointer || output->zoom.active)
|
||||
return;
|
||||
|
||||
output->zoom.active = true;
|
||||
output->zoom.seat = seat;
|
||||
weston_output_disable_planes_incr(output);
|
||||
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