From ce5325d3edf85d17254241ae7d3299ea4979900f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 14 Jun 2010 11:54:00 -0400 Subject: [PATCH] Add x11 backend for compositor This still needs all the bells and whistles from the egl-kms mesa branch, but it makes it a lot easier to work on wayland. --- Makefile | 1 + compositor-drm.c | 258 ++++++++++++------ compositor-x11.c | 666 +++++++++++++++++++++++++++++++++++++++++++++++ compositor.c | 108 +++----- compositor.h | 45 ++-- configure.ac | 2 +- event-loop.c | 1 + 7 files changed, 899 insertions(+), 182 deletions(-) create mode 100644 compositor-x11.c diff --git a/Makefile b/Makefile index ea41aa3c..2ea2d3a6 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ $(libs) : compositor : \ compositor.o \ compositor-drm.o \ + compositor-x11.o \ screenshooter.o \ cairo-util.o diff --git a/compositor-drm.c b/compositor-drm.c index 178941c1..608ada91 100644 --- a/compositor-drm.c +++ b/compositor-drm.c @@ -37,8 +37,43 @@ #include "wayland-protocol.h" #include "compositor.h" +struct drm_compositor { + struct wlsc_compositor base; + + struct udev *udev; + struct wl_event_source *drm_source; + int drm_fd; + + struct wl_event_source *term_signal_source; + + /* tty handling state */ + int tty_fd; + uint32_t vt_active : 1; + + struct termios terminal_attributes; + struct wl_event_source *tty_input_source; + struct wl_event_source *enter_vt_source; + struct wl_event_source *leave_vt_source; +}; + +struct drm_output { + struct wlsc_output base; + + drmModeModeInfo mode; + uint32_t crtc_id; + uint32_t connector_id; + GLuint rbo[2]; + uint32_t fb_id[2]; + EGLImageKHR image[2]; + uint32_t current; +}; + +struct drm_input { + struct wlsc_input_device base; +}; + struct evdev_input_device { - struct wlsc_input_device *device; + struct drm_input *master; struct wl_event_source *source; int tool, new_x, new_y; int base_x, base_y; @@ -47,16 +82,21 @@ struct evdev_input_device { static void evdev_input_device_data(int fd, uint32_t mask, void *data) { + struct drm_compositor *c; struct evdev_input_device *device = data; struct input_event ev[8], *e, *end; int len, value, dx, dy, absolute_event; int x, y; + c = (struct drm_compositor *) device->master->base.ec; + if (!c->vt_active) + return; + dx = 0; dy = 0; absolute_event = 0; - x = device->device->x; - y = device->device->y; + x = device->master->base.x; + y = device->master->base.y; len = read(fd, &ev, sizeof ev); if (len < 0 || len % sizeof e[0] != 0) { @@ -132,24 +172,26 @@ static void evdev_input_device_data(int fd, uint32_t mask, void *data) case BTN_FORWARD: case BTN_BACK: case BTN_TASK: - notify_button(device->device, e->code, value); + notify_button(&device->master->base, + e->code, value); break; default: - notify_key(device->device, e->code, value); + notify_key(&device->master->base, + e->code, value); break; } } } if (dx != 0 || dy != 0) - notify_motion(device->device, x + dx, y + dy); + notify_motion(&device->master->base, x + dx, y + dy); if (absolute_event && device->tool) - notify_motion(device->device, x, y); + notify_motion(&device->master->base, x, y); } static struct evdev_input_device * -evdev_input_device_create(struct wlsc_input_device *master, +evdev_input_device_create(struct drm_input *master, struct wl_display *display, const char *path) { struct evdev_input_device *device; @@ -162,7 +204,7 @@ evdev_input_device_create(struct wlsc_input_device *master, device->tool = 1; device->new_x = 1; device->new_y = 1; - device->device = master; + device->master = master; device->fd = open(path, O_RDONLY); if (device->fd < 0) { @@ -184,13 +226,52 @@ evdev_input_device_create(struct wlsc_input_device *master, return device; } -void -wlsc_compositor_present_drm(struct wlsc_compositor *ec) +static void +drm_input_create(struct drm_compositor *c) +{ + struct drm_input *input; + struct udev_enumerate *e; + struct udev_list_entry *entry; + struct udev_device *device; + const char *path; + + input = malloc(sizeof *input); + if (input == NULL) + return; + + memset(input, 0, sizeof *input); + wlsc_input_device_init(&input->base, &c->base); + + e = udev_enumerate_new(c->udev); + udev_enumerate_add_match_subsystem(e, "input"); + udev_enumerate_add_match_property(e, "WAYLAND_SEAT", "1"); + udev_enumerate_scan_devices(e); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(c->udev, path); + evdev_input_device_create(input, c->base.wl_display, + udev_device_get_devnode(device)); + } + udev_enumerate_unref(e); + + c->base.input_device = &input->base; +} + +static void +drm_compositor_present(struct wlsc_compositor *ec) { - struct wlsc_output *output; + struct drm_compositor *c = (struct drm_compositor *) ec; + struct drm_output *output; + + wl_list_for_each(output, &ec->output_list, base.link) { + output->current ^= 1; - wl_list_for_each(output, &ec->output_list, link) { - drmModePageFlip(ec->drm_fd, output->crtc_id, + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, + output->rbo[output->current]); + + drmModePageFlip(c->drm_fd, output->crtc_id, output->fb_id[output->current ^ 1], DRM_MODE_PAGE_FLIP_EVENT, output); } @@ -220,9 +301,8 @@ on_drm_input(int fd, uint32_t mask, void *data) } static int -init_egl(struct wlsc_compositor *ec, struct udev_device *device) +init_egl(struct drm_compositor *ec, struct udev_device *device) { - struct wl_event_loop *loop; EGLint major, minor, count; EGLConfig config; PFNEGLGETTYPEDDISPLAYMESA get_typed_display_mesa; @@ -241,8 +321,8 @@ init_egl(struct wlsc_compositor *ec, struct udev_device *device) return -1; } - ec->base.device = strdup(udev_device_get_devnode(device)); - ec->drm_fd = open(ec->base.device, O_RDWR); + ec->base.base.device = strdup(udev_device_get_devnode(device)); + ec->drm_fd = open(ec->base.base.device, O_RDWR); if (ec->drm_fd < 0) { /* Probably permissions error */ fprintf(stderr, "couldn't open %s, skipping\n", @@ -250,41 +330,39 @@ init_egl(struct wlsc_compositor *ec, struct udev_device *device) return -1; } - ec->display = get_typed_display_mesa(EGL_DRM_DISPLAY_TYPE_MESA, - (void *) ec->drm_fd); - if (ec->display == NULL) { + ec->base.display = get_typed_display_mesa(EGL_DRM_DISPLAY_TYPE_MESA, + (void *) ec->drm_fd); + if (ec->base.display == NULL) { fprintf(stderr, "failed to create display\n"); return -1; } - if (!eglInitialize(ec->display, &major, &minor)) { + if (!eglInitialize(ec->base.display, &major, &minor)) { fprintf(stderr, "failed to initialize display\n"); return -1; } - if (!eglChooseConfig(ec->display, config_attribs, &config, 1, &count) || + if (!eglChooseConfig(ec->base.display, + config_attribs, &config, 1, &count) || count == 0) { fprintf(stderr, "eglChooseConfig() failed\n"); return -1; } eglBindAPI(EGL_OPENGL_API); - ec->context = eglCreateContext(ec->display, config, EGL_NO_CONTEXT, NULL); - if (ec->context == NULL) { + ec->base.context = eglCreateContext(ec->base.display, + config, EGL_NO_CONTEXT, NULL); + if (ec->base.context == NULL) { fprintf(stderr, "failed to create context\n"); return -1; } - if (!eglMakeCurrent(ec->display, EGL_NO_SURFACE, EGL_NO_SURFACE, ec->context)) { + if (!eglMakeCurrent(ec->base.display, EGL_NO_SURFACE, + EGL_NO_SURFACE, ec->base.context)) { fprintf(stderr, "failed to make context current\n"); return -1; } - loop = wl_display_get_event_loop(ec->wl_display); - ec->drm_source = - wl_event_loop_add_fd(loop, ec->drm_fd, - WL_EVENT_READABLE, on_drm_input, ec); - return 0; } @@ -299,11 +377,11 @@ static drmModeModeInfo builtin_1024x768 = { }; static int -create_output_for_connector(struct wlsc_compositor *ec, +create_output_for_connector(struct drm_compositor *ec, drmModeRes *resources, drmModeConnector *connector) { - struct wlsc_output *output; + struct drm_output *output; drmModeEncoder *encoder; drmModeModeInfo *mode; int i, ret; @@ -339,14 +417,13 @@ create_output_for_connector(struct wlsc_compositor *ec, return -1; } - output->compositor = ec; + memset(output, 0, sizeof *output); + wlsc_output_init(&output->base, &ec->base, 0, 0, + mode->hdisplay, mode->vdisplay); + output->crtc_id = resources->crtcs[i]; output->connector_id = connector->connector_id; output->mode = *mode; - output->x = 0; - output->y = 0; - output->width = mode->hdisplay; - output->height = mode->vdisplay; drmModeFreeEncoder(encoder); @@ -354,13 +431,17 @@ create_output_for_connector(struct wlsc_compositor *ec, for (i = 0; i < 2; i++) { glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]); - attribs[1] = output->width; - attribs[3] = output->height; - output->image[i] = eglCreateDRMImageMESA(ec->display, attribs); - glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, output->image[i]); - eglExportDRMImageMESA(ec->display, output->image[i], NULL, &handle, &stride); - - ret = drmModeAddFB(ec->drm_fd, output->width, output->height, + attribs[1] = output->base.width; + attribs[3] = output->base.height; + output->image[i] = + eglCreateDRMImageMESA(ec->base.display, attribs); + glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, + output->image[i]); + eglExportDRMImageMESA(ec->base.display, output->image[i], + NULL, &handle, &stride); + + ret = drmModeAddFB(ec->drm_fd, + output->base.width, output->base.height, 32, 32, stride, handle, &output->fb_id[i]); if (ret) { fprintf(stderr, "failed to add fb %d: %m\n", i); @@ -381,13 +462,13 @@ create_output_for_connector(struct wlsc_compositor *ec, return -1; } - wl_list_insert(ec->output_list.prev, &output->link); + wl_list_insert(ec->base.output_list.prev, &output->base.link); return 0; } static int -create_outputs(struct wlsc_compositor *ec) +create_outputs(struct drm_compositor *ec) { drmModeConnector *connector; drmModeRes *resources; @@ -413,7 +494,7 @@ create_outputs(struct wlsc_compositor *ec) drmModeFreeConnector(connector); } - if (wl_list_empty(&ec->output_list)) { + if (wl_list_empty(&ec->base.output_list)) { fprintf(stderr, "No currently active connector found.\n"); return -1; } @@ -425,8 +506,8 @@ create_outputs(struct wlsc_compositor *ec) static void on_enter_vt(int signal_number, void *data) { - struct wlsc_compositor *ec = data; - struct wlsc_output *output; + struct drm_compositor *ec = data; + struct drm_output *output; int ret; ret = drmSetMaster(ec->drm_fd); @@ -441,19 +522,20 @@ static void on_enter_vt(int signal_number, void *data) ioctl(ec->tty_fd, VT_RELDISP, VT_ACKACQ); ec->vt_active = 1; - wl_list_for_each(output, &ec->output_list, link) { + wl_list_for_each(output, &ec->base.output_list, base.link) { ret = drmModeSetCrtc(ec->drm_fd, output->crtc_id, output->fb_id[output->current ^ 1], 0, 0, &output->connector_id, 1, &output->mode); if (ret) - fprintf(stderr, "failed to set mode for connector %d: %m\n", + fprintf(stderr, + "failed to set mode for connector %d: %m\n", output->connector_id); } } static void on_leave_vt(int signal_number, void *data) { - struct wlsc_compositor *ec = data; + struct drm_compositor *ec = data; int ret; ret = drmDropMaster(ec->drm_fd); @@ -470,7 +552,7 @@ static void on_leave_vt(int signal_number, void *data) static void on_tty_input(int fd, uint32_t mask, void *data) { - struct wlsc_compositor *ec = data; + struct drm_compositor *ec = data; /* Ignore input to tty. We get keyboard events from evdev */ @@ -479,7 +561,7 @@ on_tty_input(int fd, uint32_t mask, void *data) static void on_term_signal(int signal_number, void *data) { - struct wlsc_compositor *ec = data; + struct drm_compositor *ec = data; if (tcsetattr(ec->tty_fd, TCSANOW, &ec->terminal_attributes) < 0) fprintf(stderr, "could not restore terminal to canonical mode\n"); @@ -487,7 +569,7 @@ static void on_term_signal(int signal_number, void *data) exit(0); } -static int setup_tty(struct wlsc_compositor *ec, struct wl_event_loop *loop) +static int setup_tty(struct drm_compositor *ec, struct wl_event_loop *loop) { struct termios raw_attributes; struct vt_mode mode = { 0 }; @@ -536,59 +618,65 @@ static int setup_tty(struct wlsc_compositor *ec, struct wl_event_loop *loop) return 0; } -int -wlsc_compositor_init_drm(struct wlsc_compositor *ec) +struct wlsc_compositor * +drm_compositor_create(struct wl_display *display) { + struct drm_compositor *ec; struct udev_enumerate *e; struct udev_list_entry *entry; struct udev_device *device; const char *path; - struct wlsc_input_device *input_device; struct wl_event_loop *loop; + ec = malloc(sizeof *ec); + if (ec == NULL) + return NULL; + + memset(ec, 0, sizeof *ec); ec->udev = udev_new(); if (ec->udev == NULL) { fprintf(stderr, "failed to initialize udev context\n"); - return -1; + return NULL; } - input_device = wlsc_input_device_create(ec); - ec->input_device = input_device; - e = udev_enumerate_new(ec->udev); - udev_enumerate_add_match_subsystem(e, "input"); + udev_enumerate_add_match_subsystem(e, "drm"); udev_enumerate_add_match_property(e, "WAYLAND_SEAT", "1"); udev_enumerate_scan_devices(e); + device = NULL; udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { path = udev_list_entry_get_name(entry); device = udev_device_new_from_syspath(ec->udev, path); - evdev_input_device_create(input_device, ec->wl_display, - udev_device_get_devnode(device)); + break; } udev_enumerate_unref(e); - e = udev_enumerate_new(ec->udev); - udev_enumerate_add_match_subsystem(e, "drm"); - udev_enumerate_add_match_property(e, "WAYLAND_SEAT", "1"); - udev_enumerate_scan_devices(e); - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(ec->udev, path); - fprintf(stderr, "creating output for %s\n", path); + if (device == NULL) { + fprintf(stderr, "no drm device found\n"); + return NULL; + } - if (init_egl(ec, device) < 0) { - fprintf(stderr, "failed to initialize egl\n"); - return -1; - } - if (create_outputs(ec) < 0) { - fprintf(stderr, "failed to create output for %s\n", path); - return -1; - } + if (init_egl(ec, device) < 0) { + fprintf(stderr, "failed to initialize egl\n"); + return NULL; } - udev_enumerate_unref(e); + + /* Can't init base class until we have a current egl context */ + wlsc_compositor_init(&ec->base, display); + + if (create_outputs(ec) < 0) { + fprintf(stderr, "failed to create output for %s\n", path); + return NULL; + } + + drm_input_create(ec); - loop = wl_display_get_event_loop(ec->wl_display); + loop = wl_display_get_event_loop(ec->base.wl_display); + ec->drm_source = + wl_event_loop_add_fd(loop, ec->drm_fd, + WL_EVENT_READABLE, on_drm_input, ec); setup_tty(ec, loop); + ec->base.present = drm_compositor_present; - return 0; + return &ec->base; } diff --git a/compositor-x11.c b/compositor-x11.c new file mode 100644 index 00000000..82fecc8d --- /dev/null +++ b/compositor-x11.c @@ -0,0 +1,666 @@ +/* + * Copyright © 2008-2010 Kristian Høgsberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#include +#include +#include +#include + +#include "wayland.h" +#include "wayland-protocol.h" +#include "compositor.h" + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +struct x11_compositor { + struct wlsc_compositor base; + + xcb_connection_t *conn; + xcb_screen_t *screen; + xcb_cursor_t null_cursor; + int dri2_major; + int dri2_minor; + int drm_fd; + struct wl_event_source *xcb_source; + 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 net_wm_name; + xcb_atom_t utf8_string; + } atom; +}; + +struct x11_output { + struct wlsc_output base; + + xcb_xfixes_region_t region; + xcb_window_t window; + GLuint rbo; + EGLImageKHR image; + xcb_rectangle_t damage[16]; + int damage_count; +}; + +struct x11_input { + struct wlsc_input_device base; +}; + + +static void +x11_input_create(struct x11_compositor *c) +{ + struct x11_input *input; + + input = malloc(sizeof *input); + if (input == NULL) + return; + + memset(input, 0, sizeof *input); + wlsc_input_device_init(&input->base, &c->base); + + c->base.input_device = &input->base; +} + + +static int +dri2_connect(struct x11_compositor *c) +{ + xcb_xfixes_query_version_reply_t *xfixes_query; + xcb_xfixes_query_version_cookie_t xfixes_query_cookie; + xcb_dri2_query_version_reply_t *dri2_query; + xcb_dri2_query_version_cookie_t dri2_query_cookie; + xcb_dri2_connect_reply_t *connect; + xcb_dri2_connect_cookie_t connect_cookie; + xcb_generic_error_t *error; + + xcb_prefetch_extension_data (c->conn, &xcb_xfixes_id); + xcb_prefetch_extension_data (c->conn, &xcb_dri2_id); + + xfixes_query_cookie = + xcb_xfixes_query_version(c->conn, + XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION); + + dri2_query_cookie = + xcb_dri2_query_version (c->conn, + XCB_DRI2_MAJOR_VERSION, + XCB_DRI2_MINOR_VERSION); + + connect_cookie = xcb_dri2_connect_unchecked (c->conn, + c->screen->root, + XCB_DRI2_DRIVER_TYPE_DRI); + + xfixes_query = + xcb_xfixes_query_version_reply (c->conn, + xfixes_query_cookie, &error); + if (xfixes_query == NULL || + error != NULL || xfixes_query->major_version < 2) { + free(error); + return -1; + } + free(xfixes_query); + + dri2_query = + xcb_dri2_query_version_reply (c->conn, + dri2_query_cookie, &error); + if (dri2_query == NULL || error != NULL) { + fprintf(stderr, "DRI2: failed to query version"); + free(error); + return EGL_FALSE; + } + c->dri2_major = dri2_query->major_version; + c->dri2_minor = dri2_query->minor_version; + free(dri2_query); + + connect = xcb_dri2_connect_reply (c->conn, + connect_cookie, NULL); + if (connect == NULL || + connect->driver_name_length + connect->device_name_length == 0) { + fprintf(stderr, "DRI2: failed to authenticate"); + return -1; + } + + c->base.base.device = + strndup(xcb_dri2_connect_device_name (connect), + xcb_dri2_connect_device_name_length (connect)); + + if (c->base.base.device == NULL) { + free(connect); + return -1; + } + free(connect); + + return 0; +} + +static int +dri2_authenticate(struct x11_compositor *c) +{ + xcb_dri2_authenticate_reply_t *authenticate; + xcb_dri2_authenticate_cookie_t authenticate_cookie; + drm_magic_t magic; + + if (drmGetMagic(c->drm_fd, &magic)) { + fprintf(stderr, "DRI2: failed to get drm magic"); + return -1; + } + + authenticate_cookie = + xcb_dri2_authenticate_unchecked(c->conn, + c->screen->root, magic); + authenticate = + xcb_dri2_authenticate_reply(c->conn, + authenticate_cookie, NULL); + if (authenticate == NULL || !authenticate->authenticated) { + fprintf(stderr, "DRI2: failed to authenticate"); + free(authenticate); + return -1; + } + + free(authenticate); + + return 0; +} + +static int +x11_compositor_init_egl(struct x11_compositor *c) +{ + PFNEGLGETTYPEDDISPLAYMESA get_typed_display_mesa; + EGLint major, minor, count; + EGLConfig config; + + static const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, 0, + EGL_NO_SURFACE_CAPABLE_MESA, EGL_OPENGL_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + + if (dri2_connect(c) < 0) + return -1; + + c->drm_fd = open(c->base.base.device, O_RDWR); + if (c->drm_fd == -1) { + fprintf(stderr, + "DRI2: could not open %s (%s)", c->base.base.device, + strerror(errno)); + return -1; + } + + if (dri2_authenticate(c) < 0) + return -1; + + get_typed_display_mesa = + (PFNEGLGETTYPEDDISPLAYMESA) + eglGetProcAddress("eglGetTypedDisplayMESA"); + if (get_typed_display_mesa == NULL) { + fprintf(stderr, "eglGetTypedDisplayMESA() not found\n"); + return -1; + } + + c->base.display = get_typed_display_mesa(EGL_DRM_DISPLAY_TYPE_MESA, + (void *) c->drm_fd); + if (c->base.display == NULL) { + fprintf(stderr, "failed to create display\n"); + return -1; + } + + if (!eglInitialize(c->base.display, &major, &minor)) { + fprintf(stderr, "failed to initialize display\n"); + return -1; + } + + if (!eglChooseConfig(c->base.display, + config_attribs, &config, 1, &count) || + count == 0) { + fprintf(stderr, "eglChooseConfig() failed\n"); + return -1; + } + + eglBindAPI(EGL_OPENGL_API); + c->base.context = eglCreateContext(c->base.display, + config, EGL_NO_CONTEXT, NULL); + if (c->base.context == NULL) { + fprintf(stderr, "failed to create context\n"); + return -1; + } + + if (!eglMakeCurrent(c->base.display, EGL_NO_SURFACE, + EGL_NO_SURFACE, c->base.context)) { + fprintf(stderr, "failed to make context current\n"); + return -1; + } + + return 0; +} + +static void +x11_compositor_present(struct wlsc_compositor *base) +{ + struct x11_compositor *c = (struct x11_compositor *) base; + struct x11_output *output; + xcb_dri2_copy_region_cookie_t cookie; + struct timeval tv; + uint32_t msec; + + glFlush(); + + wl_list_for_each(output, &c->base.output_list, base.link) { + cookie = xcb_dri2_copy_region_unchecked(c->conn, + output->window, + output->region, + XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT, + XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT); + free(xcb_dri2_copy_region_reply(c->conn, cookie, NULL)); + } + + gettimeofday(&tv, NULL); + msec = tv.tv_sec * 1000 + tv.tv_usec / 1000; + wlsc_compositor_finish_frame(&c->base, msec); +} + +static void +x11_output_set_wm_protocols(struct x11_output *output) +{ + xcb_atom_t list[1]; + struct x11_compositor *c = + (struct x11_compositor *) output->base.compositor; + + list[0] = c->atom.wm_delete_window; + xcb_change_property (c->conn, + XCB_PROP_MODE_REPLACE, + output->window, + c->atom.wm_protocols, + XCB_ATOM_ATOM, + 32, + ARRAY_SIZE(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 int +x11_compositor_create_output(struct x11_compositor *c, int width, int height) +{ + static const char name[] = "Wayland Compositor"; + struct x11_output *output; + xcb_dri2_dri2_buffer_t *buffers; + xcb_dri2_get_buffers_reply_t *reply; + xcb_dri2_get_buffers_cookie_t cookie; + xcb_screen_iterator_t iter; + xcb_rectangle_t rectangle; + struct wm_normal_hints normal_hints; + unsigned int attachments[] = + { XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT}; + uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR; + uint32_t values[2] = { + 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_EXPOSURE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY, + 0 + }; + + EGLint attribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_IMAGE_STRIDE_MESA, 0, + EGL_IMAGE_FORMAT_MESA, EGL_IMAGE_FORMAT_ARGB8888_MESA, + EGL_NONE + }; + + output = malloc(sizeof *output); + if (output == NULL) + return -1; + + memset(output, 0, sizeof *output); + wlsc_output_init(&output->base, &c->base, 0, 0, width, height); + + values[1] = c->null_cursor; + output->window = xcb_generate_id(c->conn); + iter = xcb_setup_roots_iterator(xcb_get_setup(c->conn)); + xcb_create_window(c->conn, + XCB_COPY_FROM_PARENT, + output->window, + iter.data->root, + 0, 0, + width, height, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + iter.data->root_visual, + mask, values); + + /* Don't resize me. */ + memset(&normal_hints, 0, sizeof normal_hints); + normal_hints.flags = + WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE; + normal_hints.min_width = width; + normal_hints.min_height = height; + normal_hints.max_width = width; + normal_hints.max_height = height; + xcb_change_property (c->conn, XCB_PROP_MODE_REPLACE, output->window, + c->atom.wm_normal_hints, + c->atom.wm_size_hints, 32, + sizeof normal_hints / 4, + (uint8_t *) &normal_hints); + + xcb_map_window(c->conn, output->window); + + /* Set window name. Don't bother with non-EWMH WMs. */ + xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window, + c->atom.net_wm_name, c->atom.utf8_string, 8, + strlen(name), name); + + rectangle.x = 0; + rectangle.y = 0; + rectangle.width = width; + rectangle.height = height; + output->region = xcb_generate_id(c->conn); + xcb_xfixes_create_region(c->conn, output->region, 1, &rectangle); + + xcb_dri2_create_drawable (c->conn, output->window); + + x11_output_set_wm_protocols(output); + + cookie = xcb_dri2_get_buffers_unchecked (c->conn, + output->window, + 1, 1, attachments); + reply = xcb_dri2_get_buffers_reply (c->conn, cookie, NULL); + if (reply == NULL) + return -1; + buffers = xcb_dri2_get_buffers_buffers (reply); + if (buffers == NULL) + return -1; + + if (reply->count != 1) { + fprintf(stderr, + "got wrong number of buffers (%d)\n", reply->count); + return -1; + } + + attribs[1] = reply->width; + attribs[3] = reply->height; + attribs[5] = buffers[0].pitch / 4; + free(reply); + + output->image = + eglCreateImageKHR(c->base.display, c->base.context, + EGL_DRM_IMAGE_MESA, + (EGLClientBuffer) buffers[0].name, + attribs); + + glGenRenderbuffers(1, &output->rbo); + glBindRenderbuffer(GL_RENDERBUFFER, output->rbo); + + glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, + output->image); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, + output->rbo); + + wl_list_insert(c->base.output_list.prev, &output->base.link); + + return 0; +} + +static void +idle_repaint(void *data) +{ + struct x11_output *output = data; + struct x11_compositor *c = + (struct x11_compositor *) output->base.compositor; + xcb_xfixes_region_t region; + xcb_dri2_copy_region_cookie_t cookie; + + if (output->damage_count <= ARRAY_SIZE(output->damage)) { + region = xcb_generate_id(c->conn); + xcb_xfixes_create_region(c->conn, region, + output->damage_count, output->damage); + } else { + region = output->region; + } + + cookie = xcb_dri2_copy_region_unchecked(c->conn, + output->window, + region, + XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT, + XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT); + + if (region != output->region) + xcb_xfixes_destroy_region(c->conn, region); + + free(xcb_dri2_copy_region_reply(c->conn, cookie, NULL)); + output->damage_count = 0; +} + +static struct x11_output * +x11_compositor_find_output(struct x11_compositor *c, xcb_window_t window) +{ + struct x11_output *output; + + wl_list_for_each(output, &c->base.output_list, base.link) { + if (output->window == window) + return output; + } + + return NULL; +} + +static void +x11_compositor_handle_event(int fd, uint32_t mask, void *data) +{ + struct x11_compositor *c = data; + struct x11_output *output; + xcb_generic_event_t *event; + struct wl_event_loop *loop; + xcb_client_message_event_t *client_message; + xcb_motion_notify_event_t *motion_notify; + xcb_key_press_event_t *key_press; + xcb_button_press_event_t *button_press; + xcb_expose_event_t *expose; + xcb_rectangle_t *r; + xcb_atom_t atom; + + loop = wl_display_get_event_loop(c->base.wl_display); + while (event = xcb_poll_for_event (c->conn), event != NULL) { + switch (event->response_type & ~0x80) { + + case XCB_KEY_PRESS: + key_press = (xcb_key_press_event_t *) event; + notify_key(c->base.input_device, + key_press->detail, 1); + + fprintf(stderr, "code %d, sequence %d\n", + key_press->detail, key_press->sequence); + + break; + case XCB_KEY_RELEASE: + key_press = (xcb_key_press_event_t *) event; + notify_key(c->base.input_device, + key_press->detail, 0); + break; + case XCB_BUTTON_PRESS: + button_press = (xcb_button_press_event_t *) event; + notify_button(c->base.input_device, + button_press->detail, 1); + break; + case XCB_BUTTON_RELEASE: + button_press = (xcb_button_press_event_t *) event; + notify_button(c->base.input_device, + button_press->detail, 0); + break; + + case XCB_MOTION_NOTIFY: + motion_notify = (xcb_motion_notify_event_t *) event; + notify_motion(c->base.input_device, + motion_notify->event_x, + motion_notify->event_y); + break; + + case XCB_EXPOSE: + expose = (xcb_expose_event_t *) event; + output = x11_compositor_find_output(c, expose->window); + if (output->damage_count == 0) + wl_event_loop_add_idle(loop, + idle_repaint, output); + + r = &output->damage[output->damage_count++]; + if (output->damage_count > 16) + break; + r->x = expose->x; + r->y = expose->y; + r->width = expose->width; + r->height = expose->height; + break; + case XCB_CLIENT_MESSAGE: + client_message = (xcb_client_message_event_t *) event; + atom = client_message->data.data32[0]; + if (atom == c->atom.wm_delete_window) + exit(1); + break; + default: + + break; + } + + free (event); + } +} + +#define F(field) offsetof(struct x11_compositor, field) + +static void +x11_compositor_get_resources(struct x11_compositor *c) +{ + 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) }, + { "_NET_WM_NAME", F(atom.net_wm_name) }, + { "UTF8_STRING", F(atom.utf8_string) }, + }; + + xcb_intern_atom_cookie_t cookies[ARRAY_SIZE(atoms)]; + xcb_intern_atom_reply_t *reply; + xcb_pixmap_t pixmap; + xcb_gc_t gc; + int i; + uint8_t data[] = { 0, 0, 0, 0 }; + + for (i = 0; i < ARRAY_SIZE(atoms); i++) + cookies[i] = xcb_intern_atom (c->conn, 0, + strlen(atoms[i].name), + atoms[i].name); + + for (i = 0; i < ARRAY_SIZE(atoms); i++) { + reply = xcb_intern_atom_reply (c->conn, cookies[i], NULL); + *(xcb_atom_t *) ((char *) c + atoms[i].offset) = reply->atom; + free(reply); + } + + pixmap = xcb_generate_id(c->conn); + gc = xcb_generate_id(c->conn); + xcb_create_pixmap(c->conn, 1, pixmap, c->screen->root, 1, 1); + xcb_create_gc(c->conn, gc, pixmap, 0, NULL); + xcb_put_image(c->conn, XCB_IMAGE_FORMAT_XY_PIXMAP, + pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data); + c->null_cursor = xcb_generate_id(c->conn); + xcb_create_cursor (c->conn, c->null_cursor, + pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1); + xcb_free_gc(c->conn, gc); + xcb_free_pixmap(c->conn, pixmap); +} + +struct wlsc_compositor * +x11_compositor_create(struct wl_display *display) +{ + struct x11_compositor *c; + struct wl_event_loop *loop; + xcb_screen_iterator_t s; + + c = malloc(sizeof *c); + if (c == NULL) + return NULL; + + memset(c, 0, sizeof *c); + c->conn = xcb_connect(0, 0); + + if (xcb_connection_has_error(c->conn)) + return NULL; + + s = xcb_setup_roots_iterator(xcb_get_setup(c->conn)); + c->screen = s.data; + + x11_compositor_get_resources(c); + + x11_compositor_init_egl(c); + + /* Can't init base class until we have a current egl context */ + wlsc_compositor_init(&c->base, display); + + x11_compositor_create_output(c, 1024, 640); + + x11_input_create(c); + + loop = wl_display_get_event_loop(c->base.wl_display); + + c->xcb_source = + wl_event_loop_add_fd(loop, xcb_get_file_descriptor(c->conn), + WL_EVENT_READABLE, + x11_compositor_handle_event, c); + + c->base.present = x11_compositor_present; + + return &c->base; +} diff --git a/compositor.c b/compositor.c index 204c8dbe..fad9b7b4 100644 --- a/compositor.c +++ b/compositor.c @@ -355,8 +355,6 @@ wlsc_compositor_finish_frame(struct wlsc_compositor *compositor, int msecs) compositor->current_frame, msecs); wl_event_source_timer_update(compositor->timer_source, 5); - compositor->repaint_on_timeout = 1; - compositor->current_frame++; } @@ -379,13 +377,6 @@ wlsc_output_repaint(struct wlsc_output *output) wl_list_for_each(eid, &ec->input_device_list, link) wlsc_surface_draw(eid->sprite, output); - - output->current ^= 1; - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, - output->rbo[output->current]); } static void @@ -402,7 +393,7 @@ repaint(void *data) wl_list_for_each(output, &ec->output_list, link) wlsc_output_repaint(output); - wlsc_compositor_present_drm(ec); + ec->present(ec); ec->repaint_needed = 0; } @@ -628,9 +619,6 @@ notify_motion(struct wlsc_input_device *device, int x, int y) const int hotspot_x = 16, hotspot_y = 16; int32_t sx, sy; - if (!ec->vt_active) - return; - /* FIXME: We need some multi head love here. */ output = container_of(ec->output_list.next, struct wlsc_output, link); if (x < output->x) @@ -668,8 +656,7 @@ notify_button(struct wlsc_input_device *device, struct wlsc_compositor *compositor = device->ec; int32_t sx, sy; - if (!compositor->vt_active) - return; + fprintf(stderr, "notify button: button %d, state %d\n", button, state); surface = pick_surface(device, &sx, &sy); if (surface) { @@ -700,9 +687,6 @@ notify_key(struct wlsc_input_device *device, uint32_t *k, *end; uint32_t modifier; - if (!compositor->vt_active) - return; - switch (key | compositor->modifier_state) { case KEY_BACKSPACE | MODIFIER_CTRL | MODIFIER_ALT: kill(0, SIGTERM); @@ -769,16 +753,10 @@ handle_surface_destroy(struct wlsc_listener *listener, } } -struct wlsc_input_device * -wlsc_input_device_create(struct wlsc_compositor *ec) +void +wlsc_input_device_init(struct wlsc_input_device *device, + struct wlsc_compositor *ec) { - struct wlsc_input_device *device; - - device = malloc(sizeof *device); - if (device == NULL) - return NULL; - - memset(device, 0, sizeof *device); device->base.interface = &wl_input_device_interface; device->base.implementation = NULL; wl_display_add_object(ec->wl_display, &device->base); @@ -786,13 +764,12 @@ wlsc_input_device_create(struct wlsc_compositor *ec) device->x = 100; device->y = 100; device->ec = ec; + device->sprite = pointer_create(ec, device->x, device->y, 64, 64); device->listener.func = handle_surface_destroy; wl_list_insert(ec->surface_destroy_listener_list.prev, &device->listener.link); wl_list_insert(ec->input_device_list.prev, &device->link); - - return device; } static void @@ -916,18 +893,37 @@ add_visuals(struct wlsc_compositor *ec) wl_display_add_global(ec->wl_display, &ec->rgb_visual.base, NULL); } -static struct wlsc_compositor * -wlsc_compositor_create(struct wl_display *display) +void +wlsc_output_init(struct wlsc_output *output, struct wlsc_compositor *c, + int x, int y, int width, int height) { - struct wlsc_compositor *ec; - struct wl_event_loop *loop; - struct wlsc_output *output; + output->compositor = c; + output->x = x; + output->y = y; + output->width = width; + output->height = height; - ec = malloc(sizeof *ec); - if (ec == NULL) - return NULL; + output->background = + background_create(output, option_background); + + wlsc_matrix_init(&output->matrix); + wlsc_matrix_translate(&output->matrix, + -output->x - output->width / 2.0, + -output->y - output->height / 2.0, 0); + wlsc_matrix_scale(&output->matrix, + 2.0 / output->width, 2.0 / output->height, 1); + + output->base.interface = &wl_output_interface; + wl_display_add_object(c->wl_display, &output->base); + wl_display_add_global(c->wl_display, &output->base, + wlsc_output_post_geometry); +} + +int +wlsc_compositor_init(struct wlsc_compositor *ec, struct wl_display *display) +{ + struct wl_event_loop *loop; - memset(ec, 0, sizeof *ec); ec->wl_display = display; wl_display_set_compositor(display, &ec->base, &compositor_interface); @@ -941,34 +937,6 @@ wlsc_compositor_create(struct wl_display *display) screenshooter_create(ec); - if (wlsc_compositor_init_drm(ec) < 0) { - fprintf(stderr, "failed to initialize devices\n"); - return NULL; - } - - /* Create the pointer and background surfaces now that we have - * a current EGL context. */ - ec->input_device->sprite = - pointer_create(ec, - ec->input_device->x, - ec->input_device->y, 64, 64); - wl_list_for_each(output, &ec->output_list, link) { - output->background = background_create(output, - option_background); - - wlsc_matrix_init(&output->matrix); - - wlsc_matrix_translate(&output->matrix, - -output->x - output->width / 2.0, - -output->y - output->height / 2.0, 0); - wlsc_matrix_scale(&output->matrix, - 2.0 / output->width, 2.0 / output->height, 1); - output->base.interface = &wl_output_interface; - wl_display_add_object(ec->wl_display, &output->base); - wl_display_add_global(ec->wl_display, &output->base, - wlsc_output_post_geometry); - } - glGenFramebuffers(1, &ec->fbo); glBindFramebuffer(GL_FRAMEBUFFER, ec->fbo); glActiveTexture(GL_TEXTURE0); @@ -978,7 +946,7 @@ wlsc_compositor_create(struct wl_display *display) ec->timer_source = wl_event_loop_add_timer(loop, repaint, ec); wlsc_compositor_schedule_repaint(ec); - return ec; + return 0; } /* The plan here is to generate a random anonymous socket name and @@ -1002,7 +970,11 @@ int main(int argc, char *argv[]) display = wl_display_create(); - ec = wlsc_compositor_create(display); + if (getenv("DISPLAY")) + ec = x11_compositor_create(display); + else + ec = drm_compositor_create(display); + if (ec == NULL) { fprintf(stderr, "failed to create compositor\n"); exit(EXIT_FAILURE); diff --git a/compositor.h b/compositor.h index a4e42f05..99cce303 100644 --- a/compositor.h +++ b/compositor.h @@ -54,15 +54,6 @@ struct wlsc_output { struct wlsc_surface *background; struct wlsc_matrix matrix; int32_t x, y, width, height; - - drmModeModeInfo mode; - uint32_t crtc_id; - uint32_t connector_id; - - GLuint rbo[2]; - uint32_t fb_id[2]; - EGLImageKHR image[2]; - uint32_t current; }; struct wlsc_input_device { @@ -87,7 +78,6 @@ struct wlsc_compositor { EGLDisplay display; EGLContext context; - int drm_fd; GLuint fbo, vbo; GLuint proj_uniform, tex_uniform; struct wl_display *wl_display; @@ -101,28 +91,16 @@ struct wlsc_compositor { struct wl_list surface_destroy_listener_list; - struct wl_event_source *term_signal_source; - - /* tty handling state */ - int tty_fd; - uint32_t vt_active : 1; - - struct termios terminal_attributes; - struct wl_event_source *tty_input_source; - struct wl_event_source *enter_vt_source; - struct wl_event_source *leave_vt_source; - - struct udev *udev; - /* Repaint state. */ struct wl_event_source *timer_source; int repaint_needed; int repaint_on_timeout; struct timespec previous_swap; uint32_t current_frame; - struct wl_event_source *drm_source; uint32_t modifier_state; + + void (*present)(struct wlsc_compositor *c); }; #define MODIFIER_CTRL (1 << 8) @@ -151,15 +129,26 @@ notify_button(struct wlsc_input_device *device, int32_t button, int32_t state); void notify_key(struct wlsc_input_device *device, uint32_t key, uint32_t state); -void -wlsc_compositor_present_drm(struct wlsc_compositor *wlsc); -int -wlsc_compositor_init_drm(struct wlsc_compositor *ec); void wlsc_compositor_finish_frame(struct wlsc_compositor *compositor, int msecs); struct wlsc_input_device * wlsc_input_device_create(struct wlsc_compositor *ec); +int +wlsc_compositor_init(struct wlsc_compositor *ec, struct wl_display *display); +void +wlsc_output_init(struct wlsc_output *output, struct wlsc_compositor *c, + int x, int y, int width, int height); +void +wlsc_input_device_init(struct wlsc_input_device *device, + struct wlsc_compositor *ec); + +struct wlsc_compositor * +x11_compositor_create(struct wl_display *display); + +struct wlsc_compositor * +drm_compositor_create(struct wl_display *display); + void screenshooter_create(struct wlsc_compositor *ec); diff --git a/configure.ac b/configure.ac index 40401216..8097035b 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ PKG_PROG_PKG_CONFIG() PKG_CHECK_MODULES(FFI, [libffi]) PKG_CHECK_MODULES(COMPOSITOR, - [egl gl libpng cairo gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.17]) + [egl gl libpng cairo gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.17] xcb-dri2 xcb-xfixes) PKG_CHECK_MODULES(CLIENT, [egl gl cairo-gl gdk-pixbuf-2.0 glib-2.0 gobject-2.0]) PKG_CHECK_MODULES(POPPLER, [poppler-glib gdk-2.0]) diff --git a/event-loop.c b/event-loop.c index f6252a80..8568f407 100644 --- a/event-loop.c +++ b/event-loop.c @@ -342,6 +342,7 @@ wl_event_source_idle_remove(struct wl_event_source *source) (struct wl_event_source_idle *) source; wl_list_remove(&idle_source->link); + free(source); return 0; }