blob: 4a9d068f6293f9741afe263fbe438552d8ead16f [file] [log] [blame]
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2010-2011 Intel Corporation
* Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/shm.h>
#include <linux/input.h>
#include <xcb/xcb.h>
#include <xcb/shm.h>
#ifdef HAVE_XCB_XKB
#include <xcb/xkb.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <xkbcommon/xkbcommon.h>
#include "compositor.h"
#include "compositor-x11.h"
#include "shared/config-parser.h"
#include "shared/helpers.h"
#include "shared/image-loader.h"
#include "shared/timespec-util.h"
#include "shared/file-util.h"
#include "gl-renderer.h"
#include "weston-egl-ext.h"
#include "pixman-renderer.h"
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf.h"
#include "windowed-output-api.h"
#define DEFAULT_AXIS_STEP_DISTANCE 10
#define WINDOW_MIN_WIDTH 128
#define WINDOW_MIN_HEIGHT 128
#define WINDOW_MAX_WIDTH 8192
#define WINDOW_MAX_HEIGHT 8192
struct x11_backend {
struct weston_backend base;
struct weston_compositor *compositor;
Display *dpy;
xcb_connection_t *conn;
xcb_screen_t *screen;
xcb_cursor_t null_cursor;
struct wl_array keys;
struct wl_event_source *xcb_source;
struct xkb_keymap *xkb_keymap;
unsigned int has_xkb;
uint8_t xkb_event_base;
int fullscreen;
int no_input;
int use_pixman;
int has_net_wm_state_fullscreen;
/* We could map multi-pointer X to multiple wayland seats, but
* for now we only support core X input. */
struct weston_seat core_seat;
double prev_x;
double prev_y;
struct {
xcb_atom_t wm_protocols;
xcb_atom_t wm_normal_hints;
xcb_atom_t wm_size_hints;
xcb_atom_t wm_delete_window;
xcb_atom_t wm_class;
xcb_atom_t net_wm_name;
xcb_atom_t net_supporting_wm_check;
xcb_atom_t net_supported;
xcb_atom_t net_wm_icon;
xcb_atom_t net_wm_state;
xcb_atom_t net_wm_state_fullscreen;
xcb_atom_t string;
xcb_atom_t utf8_string;
xcb_atom_t cardinal;
xcb_atom_t xkb_names;
} atom;
};
struct x11_head {
struct weston_head base;
};
struct x11_output {
struct weston_output base;
xcb_window_t window;
struct weston_mode mode;
struct weston_mode native;
struct wl_event_source *finish_frame_timer;
xcb_gc_t gc;
xcb_shm_seg_t segment;
pixman_image_t *hw_surface;
int shm_id;
void *buf;
uint8_t depth;
int32_t scale;
bool resize_pending;
bool window_resized;
};
struct window_delete_data {
struct x11_backend *backend;
xcb_window_t window;
};
struct gl_renderer_interface *gl_renderer;
static inline struct x11_head *
to_x11_head(struct weston_head *base)
{
return container_of(base, struct x11_head, base);
}
static inline struct x11_output *
to_x11_output(struct weston_output *base)
{
return container_of(base, struct x11_output, base);
}
static inline struct x11_backend *
to_x11_backend(struct weston_compositor *base)
{
return container_of(base->backend, struct x11_backend, base);
}
static xcb_screen_t *
x11_compositor_get_default_screen(struct x11_backend *b)
{
xcb_screen_iterator_t iter;
int i, screen_nbr = XDefaultScreen(b->dpy);
iter = xcb_setup_roots_iterator(xcb_get_setup(b->conn));
for (i = 0; iter.rem; xcb_screen_next(&iter), i++)
if (i == screen_nbr)
return iter.data;
return xcb_setup_roots_iterator(xcb_get_setup(b->conn)).data;
}
static struct xkb_keymap *
x11_backend_get_keymap(struct x11_backend *b)
{
xcb_get_property_cookie_t cookie;
xcb_get_property_reply_t *reply;
struct xkb_rule_names names;
struct xkb_keymap *ret;
const char *value_all, *value_part;
int length_all, length_part;
memset(&names, 0, sizeof(names));
cookie = xcb_get_property(b->conn, 0, b->screen->root,
b->atom.xkb_names, b->atom.string, 0, 1024);
reply = xcb_get_property_reply(b->conn, cookie, NULL);
if (reply == NULL)
return NULL;
value_all = xcb_get_property_value(reply);
length_all = xcb_get_property_value_length(reply);
value_part = value_all;
#define copy_prop_value(to) \
length_part = strlen(value_part); \
if (value_part + length_part < (value_all + length_all) && \
length_part > 0) \
names.to = value_part; \
value_part += length_part + 1;
copy_prop_value(rules);
copy_prop_value(model);
copy_prop_value(layout);
copy_prop_value(variant);
copy_prop_value(options);
#undef copy_prop_value
ret = xkb_keymap_new_from_names(b->compositor->xkb_context, &names, 0);
free(reply);
return ret;
}
static uint32_t
get_xkb_mod_mask(struct x11_backend *b, uint32_t in)
{
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(&b->core_seat);
struct weston_xkb_info *info = keyboard->xkb_info;
uint32_t ret = 0;
if ((in & ShiftMask) && info->shift_mod != XKB_MOD_INVALID)
ret |= (1 << info->shift_mod);
if ((in & LockMask) && info->caps_mod != XKB_MOD_INVALID)
ret |= (1 << info->caps_mod);
if ((in & ControlMask) && info->ctrl_mod != XKB_MOD_INVALID)
ret |= (1 << info->ctrl_mod);
if ((in & Mod1Mask) && info->alt_mod != XKB_MOD_INVALID)
ret |= (1 << info->alt_mod);
if ((in & Mod2Mask) && info->mod2_mod != XKB_MOD_INVALID)
ret |= (1 << info->mod2_mod);
if ((in & Mod3Mask) && info->mod3_mod != XKB_MOD_INVALID)
ret |= (1 << info->mod3_mod);
if ((in & Mod4Mask) && info->super_mod != XKB_MOD_INVALID)
ret |= (1 << info->super_mod);
if ((in & Mod5Mask) && info->mod5_mod != XKB_MOD_INVALID)
ret |= (1 << info->mod5_mod);
return ret;
}
static void
x11_backend_setup_xkb(struct x11_backend *b)
{
#ifndef HAVE_XCB_XKB
weston_log("XCB-XKB not available during build\n");
b->has_xkb = 0;
b->xkb_event_base = 0;
return;
#else
struct weston_keyboard *keyboard;
const xcb_query_extension_reply_t *ext;
xcb_generic_error_t *error;
xcb_void_cookie_t select;
xcb_xkb_use_extension_cookie_t use_ext;
xcb_xkb_use_extension_reply_t *use_ext_reply;
xcb_xkb_per_client_flags_cookie_t pcf;
xcb_xkb_per_client_flags_reply_t *pcf_reply;
xcb_xkb_get_state_cookie_t state;
xcb_xkb_get_state_reply_t *state_reply;
uint32_t values[1] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
b->has_xkb = 0;
b->xkb_event_base = 0;
ext = xcb_get_extension_data(b->conn, &xcb_xkb_id);
if (!ext) {
weston_log("XKB extension not available on host X11 server\n");
return;
}
b->xkb_event_base = ext->first_event;
select = xcb_xkb_select_events_checked(b->conn,
XCB_XKB_ID_USE_CORE_KBD,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
0,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
0,
0,
NULL);
error = xcb_request_check(b->conn, select);
if (error) {
weston_log("error: failed to select for XKB state events\n");
free(error);
return;
}
use_ext = xcb_xkb_use_extension(b->conn,
XCB_XKB_MAJOR_VERSION,
XCB_XKB_MINOR_VERSION);
use_ext_reply = xcb_xkb_use_extension_reply(b->conn, use_ext, NULL);
if (!use_ext_reply) {
weston_log("couldn't start using XKB extension\n");
return;
}
if (!use_ext_reply->supported) {
weston_log("XKB extension version on the server is too old "
"(want %d.%d, has %d.%d)\n",
XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
use_ext_reply->serverMajor, use_ext_reply->serverMinor);
free(use_ext_reply);
return;
}
free(use_ext_reply);
pcf = xcb_xkb_per_client_flags(b->conn,
XCB_XKB_ID_USE_CORE_KBD,
XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
0,
0,
0);
pcf_reply = xcb_xkb_per_client_flags_reply(b->conn, pcf, NULL);
if (!pcf_reply ||
!(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT)) {
weston_log("failed to set XKB per-client flags, not using "
"detectable repeat\n");
free(pcf_reply);
return;
}
free(pcf_reply);
state = xcb_xkb_get_state(b->conn, XCB_XKB_ID_USE_CORE_KBD);
state_reply = xcb_xkb_get_state_reply(b->conn, state, NULL);
if (!state_reply) {
weston_log("failed to get initial XKB state\n");
return;
}
keyboard = weston_seat_get_keyboard(&b->core_seat);
xkb_state_update_mask(keyboard->xkb_state.state,
get_xkb_mod_mask(b, state_reply->baseMods),
get_xkb_mod_mask(b, state_reply->latchedMods),
get_xkb_mod_mask(b, state_reply->lockedMods),
0,
0,
state_reply->group);
free(state_reply);
xcb_change_window_attributes(b->conn, b->screen->root,
XCB_CW_EVENT_MASK, values);
b->has_xkb = 1;
#endif
}
#ifdef HAVE_XCB_XKB
static void
update_xkb_keymap(struct x11_backend *b)
{
struct xkb_keymap *keymap;
keymap = x11_backend_get_keymap(b);
if (!keymap) {
weston_log("failed to get XKB keymap\n");
return;
}
weston_seat_update_keymap(&b->core_seat, keymap);
xkb_keymap_unref(keymap);
}
#endif
static int
x11_input_create(struct x11_backend *b, int no_input)
{
struct xkb_keymap *keymap;
weston_seat_init(&b->core_seat, b->compositor, "default");
if (no_input)
return 0;
weston_seat_init_pointer(&b->core_seat);
keymap = x11_backend_get_keymap(b);
if (weston_seat_init_keyboard(&b->core_seat, keymap) < 0)
return -1;
xkb_keymap_unref(keymap);
x11_backend_setup_xkb(b);
return 0;
}
static void
x11_input_destroy(struct x11_backend *b)
{
weston_seat_release(&b->core_seat);
}
static void
x11_output_start_repaint_loop(struct weston_output *output)
{
struct timespec ts;
weston_compositor_read_presentation_clock(output->compositor, &ts);
weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
}
static int
x11_output_repaint_gl(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
{
struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec = output->base.compositor;
ec->renderer->repaint_output(output_base, damage);
pixman_region32_subtract(&ec->primary_plane.damage,
&ec->primary_plane.damage, damage);
wl_event_source_timer_update(output->finish_frame_timer, 10);
return 0;
}
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);
pixman_region32_t transformed_region;
pixman_box32_t *rects;
xcb_rectangle_t *output_rects;
xcb_void_cookie_t cookie;
int nrects, i;
xcb_generic_error_t *err;
pixman_region32_init(&transformed_region);
pixman_region32_copy(&transformed_region, region);
pixman_region32_translate(&transformed_region,
-output_base->x, -output_base->y);
weston_transformed_region(output_base->width, output_base->height,
output_base->transform,
output_base->current_scale,
&transformed_region, &transformed_region);
rects = pixman_region32_rectangles(&transformed_region, &nrects);
output_rects = calloc(nrects, sizeof(xcb_rectangle_t));
if (output_rects == NULL) {
pixman_region32_fini(&transformed_region);
return;
}
for (i = 0; i < nrects; i++) {
output_rects[i].x = rects[i].x1;
output_rects[i].y = rects[i].y1;
output_rects[i].width = rects[i].x2 - rects[i].x1;
output_rects[i].height = rects[i].y2 - rects[i].y1;
}
pixman_region32_fini(&transformed_region);
cookie = xcb_set_clip_rectangles_checked(b->conn, XCB_CLIP_ORDERING_UNSORTED,
output->gc,
0, 0, nrects,
output_rects);
err = xcb_request_check(b->conn, cookie);
if (err != NULL) {
weston_log("Failed to set clip rects, err: %d\n", err->error_code);
free(err);
}
free(output_rects);
}
static int
x11_output_repaint_shm(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
{
struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec = output->base.compositor;
struct x11_backend *b = to_x11_backend(ec);
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
pixman_renderer_output_set_buffer(output_base, output->hw_surface);
ec->renderer->repaint_output(output_base, damage);
pixman_region32_subtract(&ec->primary_plane.damage,
&ec->primary_plane.damage, damage);
set_clip_for_output(output_base, damage);
cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc,
pixman_image_get_width(output->hw_surface),
pixman_image_get_height(output->hw_surface),
0, 0,
pixman_image_get_width(output->hw_surface),
pixman_image_get_height(output->hw_surface),
0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
0, output->segment, 0);
err = xcb_request_check(b->conn, cookie);
if (err != NULL) {
weston_log("Failed to put shm image, err: %d\n", err->error_code);
free(err);
}
wl_event_source_timer_update(output->finish_frame_timer, 10);
return 0;
}
static int
finish_frame_handler(void *data)
{
struct x11_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 void
x11_output_deinit_shm(struct x11_backend *b, struct x11_output *output)
{
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
xcb_free_gc(b->conn, output->gc);
pixman_image_unref(output->hw_surface);
output->hw_surface = NULL;
cookie = xcb_shm_detach_checked(b->conn, output->segment);
err = xcb_request_check(b->conn, cookie);
if (err) {
weston_log("xcb_shm_detach failed, error %d\n", err->error_code);
free(err);
}
shmdt(output->buf);
}
static void
x11_output_set_wm_protocols(struct x11_backend *b,
struct x11_output *output)
{
xcb_atom_t list[1];
list[0] = b->atom.wm_delete_window;
xcb_change_property (b->conn,
XCB_PROP_MODE_REPLACE,
output->window,
b->atom.wm_protocols,
XCB_ATOM_ATOM,
32,
ARRAY_LENGTH(list),
list);
}
struct wm_normal_hints {
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 base_width, base_height;
int32_t win_gravity;
};
#define WM_NORMAL_HINTS_MIN_SIZE 16
#define WM_NORMAL_HINTS_MAX_SIZE 32
static void
x11_output_set_icon(struct x11_backend *b,
struct x11_output *output, const char *filename)
{
uint32_t *icon;
int32_t width, height;
pixman_image_t *image;
image = load_image(filename);
if (!image)
return;
width = pixman_image_get_width(image);
height = pixman_image_get_height(image);
icon = malloc(width * height * 4 + 8);
if (!icon) {
pixman_image_unref(image);
return;
}
icon[0] = width;
icon[1] = height;
memcpy(icon + 2, pixman_image_get_data(image), width * height * 4);
xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
b->atom.net_wm_icon, b->atom.cardinal, 32,
width * height + 2, icon);
free(icon);
pixman_image_unref(image);
}
static void
x11_output_wait_for_map(struct x11_backend *b, struct x11_output *output)
{
xcb_map_notify_event_t *map_notify;
xcb_configure_notify_event_t *configure_notify;
xcb_generic_event_t *event;
int mapped = 0, configured = 0;
uint8_t response_type;
/* This isn't the nicest way to do this. Ideally, we could
* just go back to the main loop and once we get the configure
* notify, we add the output to the compositor. While we do
* support output hotplug, we can't start up with no outputs.
* We could add the output and then resize once we get the
* configure notify, but we don't want to start up and
* immediately resize the output.
*
* Also, some window managers don't give us our final
* fullscreen size before map_notify, so if we don't get a
* configure_notify before map_notify, we just wait for the
* first one and hope that's our size. */
xcb_flush(b->conn);
while (!mapped || !configured) {
event = xcb_wait_for_event(b->conn);
response_type = event->response_type & ~0x80;
switch (response_type) {
case XCB_MAP_NOTIFY:
map_notify = (xcb_map_notify_event_t *) event;
if (map_notify->window == output->window)
mapped = 1;
break;
case XCB_CONFIGURE_NOTIFY:
configure_notify =
(xcb_configure_notify_event_t *) event;
if (configure_notify->width % output->scale != 0 ||
configure_notify->height % output->scale != 0)
weston_log("Resolution is not a multiple of screen size, rounding\n");
output->mode.width = configure_notify->width;
output->mode.height = configure_notify->height;
configured = 1;
break;
}
}
}
static xcb_visualtype_t *
find_visual_by_id(xcb_screen_t *screen,
xcb_visualid_t id)
{
xcb_depth_iterator_t i;
xcb_visualtype_iterator_t j;
for (i = xcb_screen_allowed_depths_iterator(screen);
i.rem;
xcb_depth_next(&i)) {
for (j = xcb_depth_visuals_iterator(i.data);
j.rem;
xcb_visualtype_next(&j)) {
if (j.data->visual_id == id)
return j.data;
}
}
return 0;
}
static uint8_t
get_depth_of_visual(xcb_screen_t *screen,
xcb_visualid_t id)
{
xcb_depth_iterator_t i;
xcb_visualtype_iterator_t j;
for (i = xcb_screen_allowed_depths_iterator(screen);
i.rem;
xcb_depth_next(&i)) {
for (j = xcb_depth_visuals_iterator(i.data);
j.rem;
xcb_visualtype_next(&j)) {
if (j.data->visual_id == id)
return i.data->depth;
}
}
return 0;
}
static int
x11_output_init_shm(struct x11_backend *b, struct x11_output *output,
int width, int height)
{
xcb_visualtype_t *visual_type;
xcb_screen_t *screen;
xcb_format_iterator_t fmt;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
const xcb_query_extension_reply_t *ext;
int bitsperpixel = 0;
pixman_format_code_t pixman_format;
/* Check if SHM is available */
ext = xcb_get_extension_data(b->conn, &xcb_shm_id);
if (ext == NULL || !ext->present) {
/* SHM is missing */
weston_log("SHM extension is not available\n");
errno = ENOENT;
return -1;
}
screen = x11_compositor_get_default_screen(b);
visual_type = find_visual_by_id(screen, screen->root_visual);
if (!visual_type) {
weston_log("Failed to lookup visual for root window\n");
errno = ENOENT;
return -1;
}
weston_log("Found visual, bits per value: %d, red_mask: %.8x, green_mask: %.8x, blue_mask: %.8x\n",
visual_type->bits_per_rgb_value,
visual_type->red_mask,
visual_type->green_mask,
visual_type->blue_mask);
output->depth = get_depth_of_visual(screen, screen->root_visual);
weston_log("Visual depth is %d\n", output->depth);
for (fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(b->conn));
fmt.rem;
xcb_format_next(&fmt)) {
if (fmt.data->depth == output->depth) {
bitsperpixel = fmt.data->bits_per_pixel;
break;
}
}
weston_log("Found format for depth %d, bpp: %d\n",
output->depth, bitsperpixel);
if (bitsperpixel == 32 &&
visual_type->red_mask == 0xff0000 &&
visual_type->green_mask == 0x00ff00 &&
visual_type->blue_mask == 0x0000ff) {
weston_log("Will use x8r8g8b8 format for SHM surfaces\n");
pixman_format = PIXMAN_x8r8g8b8;
} else if (bitsperpixel == 16 &&
visual_type->red_mask == 0x00f800 &&
visual_type->green_mask == 0x0007e0 &&
visual_type->blue_mask == 0x00001f) {
weston_log("Will use r5g6b5 format for SHM surfaces\n");
pixman_format = PIXMAN_r5g6b5;
} else {
weston_log("Can't find appropriate format for SHM pixmap\n");
errno = ENOTSUP;
return -1;
}
/* Create SHM segment and attach it */
output->shm_id = shmget(IPC_PRIVATE, width * height * (bitsperpixel / 8), IPC_CREAT | S_IRWXU);
if (output->shm_id == -1) {
weston_log("x11shm: failed to allocate SHM segment\n");
return -1;
}
output->buf = shmat(output->shm_id, NULL, 0 /* read/write */);
if (-1 == (long)output->buf) {
weston_log("x11shm: failed to attach SHM segment\n");
return -1;
}
output->segment = xcb_generate_id(b->conn);
cookie = xcb_shm_attach_checked(b->conn, output->segment, output->shm_id, 1);
err = xcb_request_check(b->conn, cookie);
if (err) {
weston_log("x11shm: xcb_shm_attach error %d, op code %d, resource id %d\n",
err->error_code, err->major_code, err->minor_code);
free(err);
return -1;
}
shmctl(output->shm_id, IPC_RMID, NULL);
/* Now create pixman image */
output->hw_surface = pixman_image_create_bits(pixman_format, width, height, output->buf,
width * (bitsperpixel / 8));
output->gc = xcb_generate_id(b->conn);
xcb_create_gc(b->conn, output->gc, output->window, 0, NULL);
return 0;
}
static int
x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode)
{
struct x11_backend *b;
struct x11_output *output;
static uint32_t values[2];
int ret;
if (base == NULL) {
weston_log("output is NULL.\n");
return -1;
}
if (mode == NULL) {
weston_log("mode is NULL.\n");
return -1;
}
b = to_x11_backend(base->compositor);
output = to_x11_output(base);
if (mode->width == output->mode.width &&
mode->height == output->mode.height)
return 0;
if (mode->width < WINDOW_MIN_WIDTH || mode->width > WINDOW_MAX_WIDTH)
return -1;
if (mode->height < WINDOW_MIN_HEIGHT || mode->height > WINDOW_MAX_HEIGHT)
return -1;
/* xcb_configure_window will create an event, and we could end up
being called twice */
output->resize_pending = true;
/* window could've been resized by the user, so don't do it twice */
if (!output->window_resized) {
values[0] = mode->width;
values[1] = mode->height;
xcb_configure_window(b->conn, output->window, XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT, values);
}
output->mode.width = mode->width;
output->mode.height = mode->height;
if (b->use_pixman) {
pixman_renderer_output_destroy(&output->base);
x11_output_deinit_shm(b, output);
if (x11_output_init_shm(b, output,
output->base.current_mode->width,
output->base.current_mode->height) < 0) {
weston_log("Failed to initialize SHM for the X11 output\n");
return -1;
}
if (pixman_renderer_output_create(&output->base,
PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) {
weston_log("Failed to create pixman renderer for output\n");
x11_output_deinit_shm(b, output);
return -1;
}
} else {
Window xid = (Window) output->window;
gl_renderer->output_destroy(&output->base);
ret = gl_renderer->output_window_create(&output->base,
(EGLNativeWindowType) output->window,
&xid,
gl_renderer->opaque_attribs,
NULL,
0);
if (ret < 0)
return -1;
}
output->resize_pending = false;
output->window_resized = false;
return 0;
}
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);
if (!output->base.enabled)
return 0;
wl_event_source_remove(output->finish_frame_timer);
if (backend->use_pixman) {
pixman_renderer_output_destroy(&output->base);
x11_output_deinit_shm(backend, output);
} else {
gl_renderer->output_destroy(&output->base);
}
xcb_destroy_window(backend->conn, output->window);
xcb_flush(backend->conn);
return 0;
}
static void
x11_output_destroy(struct weston_output *base)
{
struct x11_output *output = to_x11_output(base);
x11_output_disable(&output->base);
weston_output_release(&output->base);
free(output);
}
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);
static const char name[] = "Weston Compositor";
static const char class[] = "weston-1\0Weston Compositor";
char *title = NULL;
xcb_screen_t *screen;
struct wm_normal_hints normal_hints;
struct wl_event_loop *loop;
char *icon_filename;
int ret;
uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
xcb_atom_t atom_list[1];
uint32_t values[2] = {
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_STRUCTURE_NOTIFY,
0
};
if (!b->no_input)
values[0] |=
XCB_EVENT_MASK_KEY_PRESS |
XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW |
XCB_EVENT_MASK_LEAVE_WINDOW |
XCB_EVENT_MASK_KEYMAP_STATE |
XCB_EVENT_MASK_FOCUS_CHANGE;
values[1] = b->null_cursor;
output->window = xcb_generate_id(b->conn);
screen = x11_compositor_get_default_screen(b);
xcb_create_window(b->conn,
XCB_COPY_FROM_PARENT,
output->window,
screen->root,
0, 0,
output->base.current_mode->width,
output->base.current_mode->height,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, values);
if (b->fullscreen) {
atom_list[0] = b->atom.net_wm_state_fullscreen;
xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE,
output->window,
b->atom.net_wm_state,
XCB_ATOM_ATOM, 32,
ARRAY_LENGTH(atom_list), atom_list);
} else {
memset(&normal_hints, 0, sizeof normal_hints);
normal_hints.flags =
WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
normal_hints.min_width = WINDOW_MIN_WIDTH;
normal_hints.min_height = WINDOW_MIN_HEIGHT;
normal_hints.max_width = WINDOW_MAX_WIDTH;
normal_hints.max_height = WINDOW_MAX_HEIGHT;
xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
b->atom.wm_normal_hints,
b->atom.wm_size_hints, 32,
sizeof normal_hints / 4,
(uint8_t *) &normal_hints);
}
/* Set window name. Don't bother with non-EWMH WMs. */
if (output->base.name) {
if (asprintf(&title, "%s - %s", name, output->base.name) < 0)
title = NULL;
} else {
title = strdup(name);
}
if (title) {
xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
b->atom.net_wm_name, b->atom.utf8_string, 8,
strlen(title), title);
free(title);
} else {
goto err;
}
xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
b->atom.wm_class, b->atom.string, 8,
sizeof class, class);
icon_filename = file_name_with_datadir("wayland.png");
x11_output_set_icon(b, output, icon_filename);
free(icon_filename);
x11_output_set_wm_protocols(b, output);
xcb_map_window(b->conn, output->window);
if (b->fullscreen)
x11_output_wait_for_map(b, output);
if (b->use_pixman) {
if (x11_output_init_shm(b, output,
output->base.current_mode->width,
output->base.current_mode->height) < 0) {
weston_log("Failed to initialize SHM for the X11 output\n");
goto err;
}
if (pixman_renderer_output_create(&output->base,
PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0) {
weston_log("Failed to create pixman renderer for output\n");
x11_output_deinit_shm(b, output);
goto err;
}
output->base.repaint = x11_output_repaint_shm;
} else {
/* eglCreatePlatformWindowSurfaceEXT takes a Window*
* but eglCreateWindowSurface takes a Window. */
Window xid = (Window) output->window;
ret = gl_renderer->output_window_create(
&output->base,
(EGLNativeWindowType) output->window,
&xid,
gl_renderer->opaque_attribs,
NULL,
0);
if (ret < 0)
goto err;
output->base.repaint = x11_output_repaint_gl;
}
output->base.start_repaint_loop = x11_output_start_repaint_loop;
output->base.assign_planes = NULL;
output->base.set_backlight = NULL;
output->base.set_dpms = NULL;
output->base.switch_mode = x11_output_switch_mode;
loop = wl_display_get_event_loop(b->compositor->wl_display);
output->finish_frame_timer =
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);
return 0;
err:
xcb_destroy_window(b->conn, output->window);
xcb_flush(b->conn);
return -1;
}
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 weston_head *head;
xcb_screen_t *scrn = b->screen;
int output_width, output_height;
/* We can only be called once. */
assert(!output->base.current_mode);
/* Make sure we have scale set. */
assert(output->base.scale);
if (width < WINDOW_MIN_WIDTH) {
weston_log("Invalid width \"%d\" for output %s\n",
width, output->base.name);
return -1;
}
if (height < WINDOW_MIN_HEIGHT) {
weston_log("Invalid height \"%d\" for output %s\n",
height, output->base.name);
return -1;
}
wl_list_for_each(head, &output->base.head_list, output_link) {
weston_head_set_monitor_strings(head, "weston-X11", "none", NULL);
weston_head_set_physical_size(head,
width * scrn->width_in_millimeters / scrn->width_in_pixels,
height * scrn->height_in_millimeters / scrn->height_in_pixels);
}
output_width = width * output->base.scale;
output_height = height * output->base.scale;
output->mode.flags =
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
output->mode.width = output_width;
output->mode.height = output_height;
output->mode.refresh = 60000;
output->native = output->mode;
output->scale = output->base.scale;
wl_list_insert(&output->base.mode_list, &output->mode.link);
output->base.current_mode = &output->mode;
output->base.native_mode = &output->native;
output->base.native_scale = output->base.scale;
return 0;
}
static struct weston_output *
x11_output_create(struct weston_compositor *compositor, const char *name)
{
struct x11_output *output;
/* name can't be NULL. */
assert(name);
output = zalloc(sizeof *output);
if (!output)
return NULL;
weston_output_init(&output->base, compositor, name);
output->base.destroy = x11_output_destroy;
output->base.disable = x11_output_disable;
output->base.enable = x11_output_enable;
output->base.attach_head = NULL;
weston_compositor_add_pending_output(&output->base, compositor);
return &output->base;
}
static int
x11_head_create(struct weston_compositor *compositor, const char *name)
{
struct x11_head *head;
assert(name);
head = zalloc(sizeof *head);
if (!head)
return -1;
weston_head_init(&head->base, name);
weston_head_set_connection_status(&head->base, true);
weston_compositor_add_head(compositor, &head->base);
return 0;
}
static void
x11_head_destroy(struct x11_head *head)
{
weston_head_release(&head->base);
free(head);
}
static struct x11_output *
x11_backend_find_output(struct x11_backend *b, xcb_window_t window)
{
struct x11_output *output;
wl_list_for_each(output, &b->compositor->output_list, base.link) {
if (output->window == window)
return output;
}
return NULL;
}
static void
x11_backend_delete_window(struct x11_backend *b, xcb_window_t window)
{
struct x11_output *output;
output = x11_backend_find_output(b, window);
if (output)
x11_output_destroy(&output->base);
if (wl_list_empty(&b->compositor->output_list))
weston_compositor_exit(b->compositor);
}
static void delete_cb(void *data)
{
struct window_delete_data *wd = data;
x11_backend_delete_window(wd->backend, wd->window);
free(wd);
}
#ifdef HAVE_XCB_XKB
static void
update_xkb_state(struct x11_backend *b, xcb_xkb_state_notify_event_t *state)
{
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(&b->core_seat);
xkb_state_update_mask(keyboard->xkb_state.state,
get_xkb_mod_mask(b, state->baseMods),
get_xkb_mod_mask(b, state->latchedMods),
get_xkb_mod_mask(b, state->lockedMods),
0,
0,
state->group);
notify_modifiers(&b->core_seat,
wl_display_next_serial(b->compositor->wl_display));
}
#endif
/**
* This is monumentally unpleasant. If we don't have XCB-XKB bindings,
* the best we can do (given that XCB also lacks XI2 support), is to take
* the state from the core key events. Unfortunately that only gives us
* the effective (i.e. union of depressed/latched/locked) state, and we
* need the granularity.
*
* So we still update the state with every key event we see, but also use
* the state field from X11 events as a mask so we don't get any stuck
* modifiers.
*/
static void
update_xkb_state_from_core(struct x11_backend *b, uint16_t x11_mask)
{
uint32_t mask = get_xkb_mod_mask(b, x11_mask);
struct weston_keyboard *keyboard
= weston_seat_get_keyboard(&b->core_seat);
xkb_state_update_mask(keyboard->xkb_state.state,
keyboard->modifiers.mods_depressed & mask,
keyboard->modifiers.mods_latched & mask,
keyboard->modifiers.mods_locked & mask,
0,
0,
(x11_mask >> 13) & 3);
notify_modifiers(&b->core_seat,
wl_display_next_serial(b->compositor->wl_display));
}
static void
x11_backend_deliver_button_event(struct x11_backend *b,
xcb_generic_event_t *event)
{
xcb_button_press_event_t *button_event =
(xcb_button_press_event_t *) event;
uint32_t button;
struct x11_output *output;
struct weston_pointer_axis_event weston_event;
bool is_button_pressed = event->response_type == XCB_BUTTON_PRESS;
struct timespec time = { 0 };
assert(event->response_type == XCB_BUTTON_PRESS ||
event->response_type == XCB_BUTTON_RELEASE);
output = x11_backend_find_output(b, button_event->event);
if (!output)
return;
if (is_button_pressed)
xcb_grab_pointer(b->conn, 0, output->window,
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW |
XCB_EVENT_MASK_LEAVE_WINDOW,
XCB_GRAB_MODE_ASYNC,
XCB_GRAB_MODE_ASYNC,
output->window, XCB_CURSOR_NONE,
button_event->time);
else
xcb_ungrab_pointer(b->conn, button_event->time);
if (!b->has_xkb)
update_xkb_state_from_core(b, button_event->state);
switch (button_event->detail) {
case 1:
button = BTN_LEFT;
break;
case 2:
button = BTN_MIDDLE;
break;
case 3:
button = BTN_RIGHT;
break;
case 4:
/* Axis are measured in pixels, but the xcb events are discrete
* steps. Therefore move the axis by some pixels every step. */
if (is_button_pressed) {
weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
weston_event.discrete = -1;
weston_event.has_discrete = true;
weston_event.axis =
WL_POINTER_AXIS_VERTICAL_SCROLL;
weston_compositor_get_time(&time);
notify_axis(&b->core_seat, &time, &weston_event);
notify_pointer_frame(&b->core_seat);
}
return;
case 5:
if (is_button_pressed) {
weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
weston_event.discrete = 1;
weston_event.has_discrete = true;
weston_event.axis =
WL_POINTER_AXIS_VERTICAL_SCROLL;
weston_compositor_get_time(&time);
notify_axis(&b->core_seat, &time, &weston_event);
notify_pointer_frame(&b->core_seat);
}
return;
case 6:
if (is_button_pressed) {
weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
weston_event.discrete = -1;
weston_event.has_discrete = true;
weston_event.axis =
WL_POINTER_AXIS_HORIZONTAL_SCROLL;
weston_compositor_get_time(&time);
notify_axis(&b->core_seat, &time, &weston_event);
notify_pointer_frame(&b->core_seat);
}
return;
case 7:
if (is_button_pressed) {
weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
weston_event.discrete = 1;
weston_event.has_discrete = true;
weston_event.axis =
WL_POINTER_AXIS_HORIZONTAL_SCROLL;
weston_compositor_get_time(&time);
notify_axis(&b->core_seat, &time, &weston_event);
notify_pointer_frame(&b->core_seat);
}
return;
default:
button = button_event->detail + BTN_SIDE - 8;
break;
}
weston_compositor_get_time(&time);
notify_button(&b->core_seat, &time, button,
is_button_pressed ? WL_POINTER_BUTTON_STATE_PRESSED :
WL_POINTER_BUTTON_STATE_RELEASED);
notify_pointer_frame(&b->core_seat);
}
static void
x11_backend_deliver_motion_event(struct x11_backend *b,
xcb_generic_event_t *event)
{
struct x11_output *output;
double x, y;
struct weston_pointer_motion_event motion_event = { 0 };
xcb_motion_notify_event_t *motion_notify =
(xcb_motion_notify_event_t *) event;
struct timespec time;
if (!b->has_xkb)
update_xkb_state_from_core(b, motion_notify->state);
output = x11_backend_find_output(b, motion_notify->event);
if (!output)
return;
weston_output_transform_coordinate(&output->base,
motion_notify->event_x,
motion_notify->event_y,
&x, &y);
motion_event = (struct weston_pointer_motion_event) {
.mask = WESTON_POINTER_MOTION_REL,
.dx = x - b->prev_x,
.dy = y - b->prev_y
};
weston_compositor_get_time(&time);
notify_motion(&b->core_seat, &time, &motion_event);
notify_pointer_frame(&b->core_seat);
b->prev_x = x;
b->prev_y = y;
}
static void
x11_backend_deliver_enter_event(struct x11_backend *b,
xcb_generic_event_t *event)
{
struct x11_output *output;
double x, y;
xcb_enter_notify_event_t *enter_notify =
(xcb_enter_notify_event_t *) event;
if (enter_notify->state >= Button1Mask)
return;
if (!b->has_xkb)
update_xkb_state_from_core(b, enter_notify->state);
output = x11_backend_find_output(b, enter_notify->event);
if (!output)
return;
weston_output_transform_coordinate(&output->base,
enter_notify->event_x,
enter_notify->event_y, &x, &y);
notify_pointer_focus(&b->core_seat, &output->base, x, y);
b->prev_x = x;
b->prev_y = y;
}
static int
x11_backend_next_event(struct x11_backend *b,
xcb_generic_event_t **event, uint32_t mask)
{
if (mask & WL_EVENT_READABLE)
*event = xcb_poll_for_event(b->conn);
else
*event = xcb_poll_for_queued_event(b->conn);
return *event != NULL;
}
static int
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_client_message_event_t *client_message;
xcb_enter_notify_event_t *enter_notify;
xcb_key_press_event_t *key_press, *key_release;
xcb_keymap_notify_event_t *keymap_notify;
xcb_focus_in_event_t *focus_in;
xcb_expose_event_t *expose;
xcb_configure_notify_event_t *configure;
xcb_atom_t atom;
xcb_window_t window;
uint32_t *k;
uint32_t i, set;
uint8_t response_type;
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) {
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_press = (xcb_key_press_event_t *) event;
if (response_type == XCB_KEY_PRESS &&
key_release->time == key_press->time &&
key_release->detail == key_press->detail) {
/* Don't deliver the held key release
* event or the new key press event. */
free(event);
free(prev);
prev = NULL;
continue;
} else {
/* Deliver the held key release now
* and fall through and handle the new
* event below. */
update_xkb_state_from_core(b, key_release->state);
weston_compositor_get_time(&time);
notify_key(&b->core_seat,
&time,
key_release->detail - 8,
WL_KEYBOARD_KEY_STATE_RELEASED,
STATE_UPDATE_AUTOMATIC);
free(prev);
prev = NULL;
break;
}
case XCB_FOCUS_IN:
assert(response_type == XCB_KEYMAP_NOTIFY);
keymap_notify = (xcb_keymap_notify_event_t *) event;
b->keys.size = 0;
for (i = 0; i < ARRAY_LENGTH(keymap_notify->keys) * 8; i++) {
set = keymap_notify->keys[i >> 3] &
(1 << (i & 7));
if (set) {
k = wl_array_add(&b->keys, sizeof *k);
*k = i;
}
}
/* Unfortunately the state only comes with the enter
* event, rather than with the focus event. I'm not
* sure of the exact semantics around it and whether
* we can ensure that we get both? */
notify_keyboard_focus_in(&b->core_seat, &b->keys,
STATE_UPDATE_AUTOMATIC);
free(prev);
prev = NULL;
break;
default:
/* No previous event held */
break;
}
switch (response_type) {
case XCB_KEY_PRESS:
key_press = (xcb_key_press_event_t *) event;
if (!b->has_xkb)
update_xkb_state_from_core(b, key_press->state);
weston_compositor_get_time(&time);
notify_key(&b->core_seat,
&time,
key_press->detail - 8,
WL_KEYBOARD_KEY_STATE_PRESSED,
b->has_xkb ? STATE_UPDATE_NONE :
STATE_UPDATE_AUTOMATIC);
break;
case XCB_KEY_RELEASE:
/* If we don't have XKB, we need to use the lame
* autorepeat detection above. */
if (!b->has_xkb) {
prev = event;
break;
}
key_release = (xcb_key_press_event_t *) event;
weston_compositor_get_time(&time);
notify_key(&b->core_seat,
&time,
key_release->detail - 8,
WL_KEYBOARD_KEY_STATE_RELEASED,
STATE_UPDATE_NONE);
break;
case XCB_BUTTON_PRESS:
case XCB_BUTTON_RELEASE:
x11_backend_deliver_button_event(b, event);
break;
case XCB_MOTION_NOTIFY:
x11_backend_deliver_motion_event(b, event);
break;
case XCB_EXPOSE:
expose = (xcb_expose_event_t *) event;
output = x11_backend_find_output(b, expose->window);
if (!output)
break;
weston_output_damage(&output->base);
weston_output_schedule_repaint(&output->base);
break;
case XCB_ENTER_NOTIFY:
x11_backend_deliver_enter_event(b, event);
break;
case XCB_LEAVE_NOTIFY:
enter_notify = (xcb_enter_notify_event_t *) event;
if (enter_notify->state >= Button1Mask)
break;
if (!b->has_xkb)
update_xkb_state_from_core(b, enter_notify->state);
notify_pointer_focus(&b->core_seat, NULL, 0, 0);
break;
case XCB_CLIENT_MESSAGE:
client_message = (xcb_client_message_event_t *) event;
atom = client_message->data.data32[0];
window = client_message->window;
if (atom == b->atom.wm_delete_window) {
struct wl_event_loop *loop;
struct window_delete_data *data = malloc(sizeof *data);
/* if malloc failed we should at least try to
* delete the window, even if it may result in
* a crash.
*/
if (!data) {
x11_backend_delete_window(b, window);
break;
}
data->backend = b;
data->window = window;
loop = wl_display_get_event_loop(b->compositor->wl_display);
wl_event_loop_add_idle(loop, delete_cb, data);
}
break;
case XCB_CONFIGURE_NOTIFY:
configure = (struct xcb_configure_notify_event_t *) event;
struct x11_output *output =
x11_backend_find_output(b, configure->window);
if (!output || output->resize_pending)
break;
struct weston_mode mode = output->mode;
if (mode.width == configure->width &&
mode.height == configure->height)
break;
output->window_resized = true;
mode.width = configure->width;
mode.height = configure->height;
if (weston_output_mode_set_native(&output->base,
&mode, output->scale) < 0)
weston_log("Mode switch failed\n");
break;
case XCB_FOCUS_IN:
focus_in = (xcb_focus_in_event_t *) event;
if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
break;
prev = event;
break;
case XCB_FOCUS_OUT:
focus_in = (xcb_focus_in_event_t *) event;
if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED ||
focus_in->mode == XCB_NOTIFY_MODE_UNGRAB)
break;
notify_keyboard_focus_out(&b->core_seat);
break;
default:
break;
}
#ifdef HAVE_XCB_XKB
if (b->has_xkb) {
if (response_type == b->xkb_event_base) {
xcb_xkb_state_notify_event_t *state =
(xcb_xkb_state_notify_event_t *) event;
if (state->xkbType == XCB_XKB_STATE_NOTIFY)
update_xkb_state(b, state);
} else if (response_type == XCB_PROPERTY_NOTIFY) {
xcb_property_notify_event_t *prop_notify =
(xcb_property_notify_event_t *) event;
if (prop_notify->window == b->screen->root &&
prop_notify->atom == b->atom.xkb_names &&
prop_notify->state == XCB_PROPERTY_NEW_VALUE)
update_xkb_keymap(b);
}
}
#endif
count++;
if (prev != event)
free (event);
}
switch (prev ? prev->response_type & ~0x80 : 0x80) {
case XCB_KEY_RELEASE:
key_release = (xcb_key_press_event_t *) prev;
update_xkb_state_from_core(b, key_release->state);
weston_compositor_get_time(&time);
notify_key(&b->core_seat,
&time,
key_release->detail - 8,
WL_KEYBOARD_KEY_STATE_RELEASED,
STATE_UPDATE_AUTOMATIC);
free(prev);
prev = NULL;
break;
default:
break;
}
return count;
}
#define F(field) offsetof(struct x11_backend, field)
static void
x11_backend_get_resources(struct x11_backend *b)
{
static const struct { const char *name; int offset; } atoms[] = {
{ "WM_PROTOCOLS", F(atom.wm_protocols) },
{ "WM_NORMAL_HINTS", F(atom.wm_normal_hints) },
{ "WM_SIZE_HINTS", F(atom.wm_size_hints) },
{ "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
{ "WM_CLASS", F(atom.wm_class) },
{ "_NET_WM_NAME", F(atom.net_wm_name) },
{ "_NET_WM_ICON", F(atom.net_wm_icon) },
{ "_NET_WM_STATE", F(atom.net_wm_state) },
{ "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
{ "_NET_SUPPORTING_WM_CHECK",
F(atom.net_supporting_wm_check) },
{ "_NET_SUPPORTED", F(atom.net_supported) },
{ "STRING", F(atom.string) },
{ "UTF8_STRING", F(atom.utf8_string) },
{ "CARDINAL", F(atom.cardinal) },
{ "_XKB_RULES_NAMES", F(atom.xkb_names) },
};
xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
xcb_intern_atom_reply_t *reply;
xcb_pixmap_t pixmap;
xcb_gc_t gc;
unsigned int i;
uint8_t data[] = { 0, 0, 0, 0 };
for (i = 0; i < ARRAY_LENGTH(atoms); i++)
cookies[i] = xcb_intern_atom (b->conn, 0,
strlen(atoms[i].name),
atoms[i].name);
for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
reply = xcb_intern_atom_reply (b->conn, cookies[i], NULL);
*(xcb_atom_t *) ((char *) b + atoms[i].offset) = reply->atom;
free(reply);
}
pixmap = xcb_generate_id(b->conn);
gc = xcb_generate_id(b->conn);
xcb_create_pixmap(b->conn, 1, pixmap, b->screen->root, 1, 1);
xcb_create_gc(b->conn, gc, pixmap, 0, NULL);
xcb_put_image(b->conn, XCB_IMAGE_FORMAT_XY_PIXMAP,
pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data);
b->null_cursor = xcb_generate_id(b->conn);
xcb_create_cursor (b->conn, b->null_cursor,
pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1);
xcb_free_gc(b->conn, gc);
xcb_free_pixmap(b->conn, pixmap);
}
static void
x11_backend_get_wm_info(struct x11_backend *c)
{
xcb_get_property_cookie_t cookie;
xcb_get_property_reply_t *reply;
xcb_atom_t *atom;
unsigned int i;
cookie = xcb_get_property(c->conn, 0, c->screen->root,
c->atom.net_supported,
XCB_ATOM_ATOM, 0, 1024);
reply = xcb_get_property_reply(c->conn, cookie, NULL);
if (reply == NULL)
return;
atom = (xcb_atom_t *) xcb_get_property_value(reply);
for (i = 0; i < reply->value_len; i++) {
if (atom[i] == c->atom.net_wm_state_fullscreen)
c->has_net_wm_state_fullscreen = 1;
}
free(reply);
}
static void
x11_destroy(struct weston_compositor *ec)
{
struct x11_backend *backend = to_x11_backend(ec);
struct weston_head *base, *next;
wl_event_source_remove(backend->xcb_source);
x11_input_destroy(backend);
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));
XCloseDisplay(backend->dpy);
free(backend);
}
static int
init_gl_renderer(struct x11_backend *b)
{
int ret;
gl_renderer = weston_load_module("gl-renderer.so",
"gl_renderer_interface");
if (!gl_renderer)
return -1;
ret = gl_renderer->display_create(b->compositor, EGL_PLATFORM_X11_KHR,
(void *) b->dpy, NULL,
gl_renderer->opaque_attribs, NULL, 0);
return ret;
}
static const struct weston_windowed_output_api api = {
x11_output_set_size,
x11_head_create,
};
static struct x11_backend *
x11_backend_create(struct weston_compositor *compositor,
struct weston_x11_backend_config *config)
{
struct x11_backend *b;
struct wl_event_loop *loop;
int ret;
b = zalloc(sizeof *b);
if (b == NULL)
return NULL;
b->compositor = compositor;
b->fullscreen = config->fullscreen;
b->no_input = config->no_input;
compositor->backend = &b->base;
if (weston_compositor_set_presentation_clock_software(compositor) < 0)
goto err_free;
b->dpy = XOpenDisplay(NULL);
if (b->dpy == NULL)
goto err_free;
b->conn = XGetXCBConnection(b->dpy);
XSetEventQueueOwner(b->dpy, XCBOwnsEventQueue);
if (xcb_connection_has_error(b->conn))
goto err_xdisplay;
b->screen = x11_compositor_get_default_screen(b);
wl_array_init(&b->keys);
x11_backend_get_resources(b);
x11_backend_get_wm_info(b);
if (!b->has_net_wm_state_fullscreen && config->fullscreen) {
weston_log("Can not fullscreen without window manager support"
"(need _NET_WM_STATE_FULLSCREEN)\n");
config->fullscreen = 0;
}
b->use_pixman = config->use_pixman;
if (b->use_pixman) {
if (pixman_renderer_init(compositor) < 0) {
weston_log("Failed to initialize pixman renderer for X11 backend\n");
goto err_xdisplay;
}
}
else if (init_gl_renderer(b) < 0) {
goto err_xdisplay;
}
weston_log("Using %s renderer\n", config->use_pixman ? "pixman" : "gl");
b->base.destroy = x11_destroy;
b->base.create_output = x11_output_create;
if (x11_input_create(b, config->no_input) < 0) {
weston_log("Failed to create X11 input\n");
goto err_renderer;
}
loop = wl_display_get_event_loop(compositor->wl_display);
b->xcb_source =
wl_event_loop_add_fd(loop,
xcb_get_file_descriptor(b->conn),
WL_EVENT_READABLE,
x11_backend_handle_event, b);
wl_event_source_check(b->xcb_source);
if (compositor->renderer->import_dmabuf) {
if (linux_dmabuf_setup(compositor) < 0)
weston_log("Error: initializing dmabuf "
"support failed.\n");
}
ret = weston_plugin_api_register(compositor, WESTON_WINDOWED_OUTPUT_API_NAME,
&api, sizeof(api));
if (ret < 0) {
weston_log("Failed to register output API.\n");
goto err_x11_input;
}
return b;
err_x11_input:
x11_input_destroy(b);
err_renderer:
compositor->renderer->destroy(compositor);
err_xdisplay:
XCloseDisplay(b->dpy);
err_free:
free(b);
return NULL;
}
static void
config_init_to_defaults(struct weston_x11_backend_config *config)
{
}
WL_EXPORT int
weston_backend_init(struct weston_compositor *compositor,
struct weston_backend_config *config_base)
{
struct x11_backend *b;
struct weston_x11_backend_config config = {{ 0, }};
if (config_base == NULL ||
config_base->struct_version != WESTON_X11_BACKEND_CONFIG_VERSION ||
config_base->struct_size > sizeof(struct weston_x11_backend_config)) {
weston_log("X11 backend config structure is invalid\n");
return -1;
}
config_init_to_defaults(&config);
memcpy(&config, config_base, config_base->struct_size);
b = x11_backend_create(compositor, &config);
if (b == NULL)
return -1;
return 0;
}