#include <wayland-server.h>
#include <common.h>
#include "x11-internal.h"
+#include "pepper-internal.h"
#include <stdlib.h>
* xcb is faster than xlib
*/
+/* TODO: debugging */
+#undef PEPPER_TRACE
+#define PEPPER_TRACE(x)
+
static inline pepper_bool_t
x11_get_next_event(xcb_connection_t *xcb_conn, xcb_generic_event_t **event, uint32_t mask)
{
return *event != NULL;
}
+static x11_output_t *
+x11_find_output_by_window(pepper_x11_connection_t *conn, xcb_window_t window)
+{
+ x11_output_t *output = NULL;
+
+ if (!wl_list_empty(&conn->outputs))
+ {
+ wl_list_for_each(output, &conn->outputs, link)
+ if ( window == output->window )
+ return output;
+ }
+ return NULL;
+}
+
static int
x11_handle_event(int fd, uint32_t mask, void *data)
{
uint32_t count = 0;
- if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR))
- return 0;
-
if (!connection->use_xinput)
return 0;
while(x11_get_next_event(connection->xcb_connection, &event, mask))
{
uint32_t type = event->response_type & ~0x80;
+
switch (type)
{
case XCB_ENTER_NOTIFY:
x11_handle_input_event(seat, type, event);
break;
case XCB_EXPOSE:
- /*PEPPER_ERROR("x11:event:not input event\n");*/
+ PEPPER_TRACE("XCB_EXPOSE\n");
+ {
+ xcb_expose_event_t *expose = (xcb_expose_event_t *)event;
+ x11_output_t *output = x11_find_output_by_window(connection, expose->window);
+ if (output)
+ pepper_output_schedule_repaint(output->base);
+ }
+ break;
+ case XCB_FOCUS_IN:
+ /* TODO: Cursor handling */
+ PEPPER_TRACE("XCB_FOCUS_IN\n");
+ break;
+ case XCB_FOCUS_OUT:
+ PEPPER_TRACE("XCB_FOCUS_OUT\n");
+ break;
+ case XCB_KEYMAP_NOTIFY:
+ PEPPER_TRACE("XCB_KEYMAP_NOTIFY\n");
+ break;
+ case XCB_CONFIGURE_NOTIFY:
+ /* Window moved */
+ PEPPER_TRACE("XCB_CONFIGURE_NOTIFY\n");
+ break;
+ case XCB_QUERY_EXTENSION:
+ PEPPER_TRACE("XCB_QUERY_EXTENSION\n");
+ break;
+ case XCB_DESTROY_NOTIFY:
+ PEPPER_TRACE("XCB_DESTROY_NOTIFY\n");
+ break;
+ case XCB_UNMAP_NOTIFY:
+ PEPPER_TRACE("XCB_UNMAP_NOTIFY\n");
+ break;
+ case XCB_CLIENT_MESSAGE:
+ PEPPER_TRACE("XCB_CLIENT_MESSAGE\n");
+ {
+ xcb_client_message_event_t *msg;
+ xcb_atom_t atom;
+ xcb_window_t window;
+ x11_output_t *output = NULL;
+
+ msg = (xcb_client_message_event_t *)event;
+ atom = msg->data.data32[0];
+ window = msg->window;
+
+ if (atom == connection->atom.wm_delete_window)
+ {
+ output = x11_find_output_by_window(connection, window);
+ if (output)
+ x11_output_destroy(output);
+ }
+ }
break;
default :
- PEPPER_ERROR("x11:common:Unknown event\n");
+ PEPPER_ERROR("unknown event: type [0x%x] \n", type);
break;
}
if (!compositor)
{
- PEPPER_ERROR("x11:common:%s: compositor is null\n", __FUNCTION__);
+ PEPPER_ERROR("Compositor is null\n");
return NULL;
}
connection = (pepper_x11_connection_t *)pepper_calloc(1, sizeof(pepper_x11_connection_t));
if (!connection)
{
- PEPPER_ERROR("x11:common:%s: memory allocation failed\n", __FUNCTION__);
+ PEPPER_ERROR("Memory allocation failed\n");
return NULL;
}
connection->display = XOpenDisplay(display_name);
if (!connection->display)
{
- PEPPER_ERROR("x11:common:%s: XOpenDisplay failed\n", __FUNCTION__);
+ PEPPER_ERROR("XOpenDisplay failed\n");
pepper_free(connection);
return NULL;
}
if (xcb_connection_has_error(connection->xcb_connection))
{
- PEPPER_ERROR("x11:common:%s: xcb connection has error\n", __FUNCTION__);
+ PEPPER_ERROR("xcb connection has error\n");
pepper_free(connection);
return NULL;
}
#include "x11-internal.h"
+#include "pepper-gl-renderer.h"
+#include "pepper-pixman-renderer.h"
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
static uint8_t
-x11_get_depth_of_visual(xcb_screen_t *screen, xcb_visualid_t id)
+xcb_depth_get(xcb_screen_t *screen, xcb_visualid_t id)
{
xcb_depth_iterator_t i;
xcb_visualtype_iterator_t j;
return 0;
}
-static void
-x11_output_visual_iterate(void *o)
+static xcb_visualtype_t *
+xcb_visualtype_get(xcb_screen_t *screen, xcb_visualid_t id)
{
- x11_output_t *output = o;
-
- xcb_screen_t *screen;
- xcb_visualtype_t *visual_type = NULL; /* the returned visual type */
-
- screen = output->connection->screen;
if (screen)
{
xcb_depth_iterator_t depth_iter;
visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
for (; visual_iter.rem; xcb_visualtype_next(&visual_iter))
- {
- visual_type = visual_iter.data;
- printf("visual %d, class: %d, bits per rgb value: %d, colormap_entries %d, r:%#010x, g:%#010x, b:%#010x, depth %d\n",
- visual_type->visual_id,
- visual_type->_class,
- visual_type->colormap_entries,
- visual_type->bits_per_rgb_value,
- visual_type->red_mask,
- visual_type->green_mask,
- visual_type->blue_mask,
- x11_get_depth_of_visual(screen, visual_type->visual_id));
- }
+ if (visual_iter.data->visual_id == id)
+ return visual_iter.data;
}
}
+ return NULL;
}
static void
+x11_output_wait_for_map(x11_output_t *output)
+{
+ xcb_generic_event_t *event;
+ xcb_map_notify_event_t *map_notify;
+
+ uint8_t response_type;
+ int mapped = 0, configured = 0;
+
+ while (!mapped || !configured)
+ {
+ event = xcb_wait_for_event(output->connection->xcb_connection);
+ 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:
+ configured = 1;
+ break;
+ }
+ }
+}
+
+static int
+frame_done_handler(void* data)
+{
+ x11_output_t *output = data;
+ wl_signal_emit(&output->frame_signal, NULL);
+
+ return 1;
+}
+
+static pepper_bool_t
+gl_renderer_init(x11_output_t *output)
+{
+ xcb_visualid_t visual = output->connection->screen->root_visual;
+
+ output->renderer = pepper_gl_renderer_create(output->connection->display,
+ (void *)(uintptr_t)output->window,
+ PEPPER_FORMAT_XRGB8888, /* FIXME: */
+ &visual);
+ if (!output->renderer)
+ {
+ PEPPER_ERROR("gl_renderer_create failed\n");
+ return PEPPER_FALSE;
+ }
+
+ return PEPPER_TRUE;
+}
+
+static void
+x11_shm_image_deinit(xcb_connection_t *conn, x11_shm_image_t *shm)
+{
+ if (shm->image)
+ {
+ pixman_image_unref(shm->image);
+ shm->image = NULL;
+ }
+
+ if (shm->segment)
+ {
+ xcb_shm_detach(conn, shm->segment);
+ shm->segment = 0;
+ }
+
+ if (shm->buf)
+ {
+ shmdt(shm->buf);
+ shm->buf = NULL;
+ }
+
+ if (shm->shm_id)
+ {
+ shmctl(shm->shm_id, IPC_RMID, NULL);
+ shm->shm_id = -1;
+ }
+}
+
+static pepper_bool_t
+x11_shm_image_init(x11_shm_image_t *shm, xcb_connection_t *conn, int w, int h, int bpp)
+{
+ xcb_generic_error_t *err = NULL;
+ xcb_void_cookie_t cookie;
+ pixman_format_code_t pixman_format;
+
+ /* FIXME: Hard coded */
+ if (bpp == 32)
+ {
+ pixman_format = PIXMAN_x8r8g8b8;
+ }
+ else if (bpp == 16)
+ {
+ pixman_format = PIXMAN_r5g6b5;
+ }
+ else
+ {
+ PEPPER_ERROR("cannot find pixman format\n");
+ goto err;
+ }
+
+ /* Create MIT-SHM id and attach */
+ shm->shm_id = shmget(IPC_PRIVATE, w * h * (bpp/ 8), IPC_CREAT | S_IRWXU);
+ if (shm->shm_id == -1)
+ {
+ PEPPER_ERROR("shmget() failed\n");
+ goto err;
+ }
+
+ shm->buf = shmat(shm->shm_id, NULL, 0 /* read/write */);
+ if (-1 == (long)shm->buf)
+ {
+ PEPPER_ERROR("shmat() failed\n");
+ goto err;
+ }
+
+ /* Create XCB-SHM segment and attach */
+ shm->segment = xcb_generate_id(conn);
+ cookie = xcb_shm_attach_checked(conn, shm->segment, shm->shm_id, 1);
+ err = xcb_request_check(conn, cookie);
+ if (err)
+ {
+ PEPPER_ERROR("xcb_shm_attach error %d\n", err->error_code);
+ goto err;
+ }
+
+ shmctl(shm->shm_id, IPC_RMID, NULL);
+
+ /* Now create pixman image */
+ shm->image = pixman_image_create_bits(pixman_format, w, h, shm->buf,
+ w * (bpp/ 8));
+ if (!shm->image)
+ {
+ PEPPER_ERROR("pixman_image_create failed\n");
+ goto err;
+ }
+
+ return PEPPER_TRUE;
+
+err:
+ if (err)
+ free(err);
+
+ if (shm->buf)
+ shmdt(shm->buf);
+
+ if (shm->shm_id)
+ shmctl(shm->shm_id, IPC_RMID, NULL);
+
+ return PEPPER_FALSE;
+}
+
+static pepper_bool_t
+x11_shm_init(x11_output_t *output)
+{
+ xcb_screen_iterator_t scr_iter;
+ xcb_format_iterator_t fmt_iter;
+ xcb_visualtype_t *visual_type;
+ xcb_connection_t *xcb_conn = output->connection->xcb_connection;
+ int bpp = 0;
+
+ /* Check if XCB-SHM is available */
+ {
+ const xcb_query_extension_reply_t *ext;
+ ext = xcb_get_extension_data(xcb_conn, &xcb_shm_id);
+ if (ext == NULL || !ext->present)
+ {
+ PEPPER_ERROR("xcb-shm extension is not available\n");
+ return PEPPER_FALSE;
+ }
+ }
+
+ /* Find root visual */
+ scr_iter = xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
+ visual_type = xcb_visualtype_get(scr_iter.data,
+ scr_iter.data->root_visual);
+ if (!visual_type)
+ {
+ PEPPER_ERROR("Failed to lookup visual for root window\n");
+ return PEPPER_FALSE;;
+ }
+
+ output->depth = xcb_depth_get(scr_iter.data,
+ scr_iter.data->root_visual);
+ PEPPER_TRACE("Visual depth is %d\n", output->depth);
+
+ fmt_iter = xcb_setup_pixmap_formats_iterator(xcb_get_setup(xcb_conn));
+ for (; fmt_iter.rem; xcb_format_next(&fmt_iter))
+ {
+ if (fmt_iter.data->depth == output->depth)
+ {
+ bpp = fmt_iter.data->bits_per_pixel;
+ break;
+ }
+ }
+ output->bpp = bpp;
+ PEPPER_TRACE("Found format for depth %d, bpp: %d\n", output->depth, bpp);
+
+ /* Init x11_shm_image */
+ if (!x11_shm_image_init(&output->shm, xcb_conn, output->w, output->h, bpp))
+ {
+ PEPPER_ERROR("x11_shm_image_init failed\n");
+ return PEPPER_FALSE;
+ }
+
+ output->gc = xcb_generate_id(xcb_conn);
+ xcb_create_gc(xcb_conn, output->gc, output->window, 0, NULL);
+
+ return PEPPER_TRUE;
+}
+
+static pepper_bool_t
+pixman_renderer_init(x11_output_t *output)
+{
+ /* Initialize xcb-shm infra and init shm-buffer */
+ if (!x11_shm_init(output))
+ {
+ PEPPER_ERROR("shm_init failed\n");
+ return PEPPER_FALSE;
+ }
+
+ /* Create pixman renderer */
+ output->renderer = pepper_pixman_renderer_create();
+ if (!output->renderer)
+ {
+ PEPPER_ERROR("pixman_renderer_create failed\n");
+ return PEPPER_FALSE;
+ }
+
+ return PEPPER_TRUE;
+}
+
+static pepper_bool_t
+renderer_init(x11_output_t *output, const char *renderer)
+{
+ if (!strcmp(renderer, "gl"))
+ {
+ if (gl_renderer_init(output))
+ return PEPPER_TRUE;
+ }
+
+ /* Pixman is default renderer */
+ return pixman_renderer_init(output);
+}
+
+void
x11_output_destroy(void *o)
{
x11_output_t *output;
if (!o)
{
- PEPPER_ERROR("x11:output:%s: output is null\n", __FUNCTION__);
+ PEPPER_ERROR("output is null\n");
return ;
}
output = o;
conn = output->connection;
- wl_signal_emit(&output->destroy_signal, output);
+ /* XXX */
+ x11_shm_image_deinit(conn->xcb_connection, &output->shm);
+
+ wl_event_source_remove(output->frame_done_timer);
xcb_destroy_window(conn->xcb_connection, output->window);
+ wl_signal_emit(&output->destroy_signal, output);
+
wl_list_remove(&output->link);
+ xcb_flush(conn->xcb_connection);
+
pepper_free(output);
}
x11_output_get_maker_name(void *o)
{
PEPPER_IGNORE(o);
- return "pepper_x11";
+ return "PePPer_x11";
}
static const char *
x11_output_get_model_name(void *o)
{
PEPPER_IGNORE(o);
- return "pepper_x11";
+ return "PePPer_x11";
}
static int
output->w = mode->w;
output->h = mode->h;
- /* TODO: Handle resize here. */
+ /* Resize output window. */
+ {
+ xcb_connection_t *conn = output->connection->xcb_connection;
+ xcb_size_hints_t hints;
+ uint32_t values[2];
+
+ values[0] = output->w;
+ values[1] = output->h;
+
+ /* set hints for window */
+ memset(&hints, 0, sizeof(hints));
+ hints.flags = WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
+ hints.min_width = hints.max_width = output->w*output->scale;
+ hints.min_height = hints.max_height = output->h*output->scale;
+ xcb_change_property(conn,
+ XCB_PROP_MODE_REPLACE,
+ output->window,
+ output->connection->atom.wm_normal_hints,
+ output->connection->atom.wm_size_hints,
+ 32,
+ sizeof(hints) / 4,
+ (uint8_t *)&hints);
+
+ /* resize window */
+ xcb_configure_window (conn,
+ output->window,
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
+ values);
+
+ /* resize pixman image */
+ if (output->shm.image)
+ {
+ /* Release existing shm-buffer and pixman-image */
+ x11_shm_image_deinit(conn, &output->shm);
+
+ /* Init x11_shm_image */
+ if (!x11_shm_image_init(&output->shm, conn, output->w, output->h, output->bpp))
+ {
+ PEPPER_ERROR("x11_shm_image_init failed\n");
+ return PEPPER_FALSE;
+ }
+ }
+
+ /* flush connection */
+ xcb_flush(output->connection->xcb_connection);
+ }
wl_signal_emit(&output->mode_change_signal, output);
}
wl_signal_add(&output->mode_change_signal, listener);
}
+static void
+x11_output_repaint_shm(x11_output_t* output)
+{
+ xcb_connection_t *conn = output->connection->xcb_connection;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+
+ /* TODO: Set clipping area from damages
+ * pixman_region32_rectangles(...);
+ * xcb_set_clip_rectangles(...);
+ */
+ cookie = xcb_shm_put_image_checked(conn,
+ output->window,
+ output->gc,
+ output->w * output->scale, /* total_width */
+ output->h * output->scale, /* total_height */
+ 0, /* src_x */
+ 0, /* src_y */
+ pixman_image_get_width(output->shm.image), /* src_w */
+ pixman_image_get_height(output->shm.image), /* src_h */
+ 0, /* dst_x */
+ 0, /* dst_y */
+ output->depth, /* depth */
+ XCB_IMAGE_FORMAT_Z_PIXMAP, /* format */
+ 0, /* send_event */
+ output->shm.segment, /* xcb shm segment */
+ 0); /* offset */
+ err = xcb_request_check(conn, cookie);
+ if (err)
+ {
+ PEPPER_ERROR("Failed to put shm image, err: %d\n", err->error_code);
+ free(err);
+ }
+
+ /* XXX: frame_done callback called after 10ms, referenced from weston */
+ wl_event_source_timer_update(output->frame_done_timer, 10);
+}
+
+static void
+x11_output_repaint(void *o)
+{
+ x11_output_t *output = o;
+
+ output->renderer->draw(output->renderer, output->shm.image, NULL);
+
+ /* FIXME: hack, only pixman-render use shm.image */
+ if (output->shm.image)
+ {
+ x11_output_repaint_shm(output);
+ }
+ else
+ {
+ /* TODO: gl case */
+ PEPPER_ERROR("TODO : GL\n");
+ }
+}
+
+static void
+x11_output_add_frame_listener(void *o, struct wl_listener *listener)
+{
+ x11_output_t *output = o;
+ wl_signal_add(&output->frame_signal, listener);
+}
+
+/* X11 output interface to export for PePPer core */
static const pepper_output_interface_t x11_output_interface =
{
x11_output_destroy,
x11_output_get_mode_count,
x11_output_get_mode,
x11_output_set_mode,
+
+ x11_output_repaint,
+ x11_output_add_frame_listener,
};
static void
}
PEPPER_API pepper_output_t *
-pepper_x11_output_create(pepper_x11_connection_t *connection, int32_t w, int32_t h)
+pepper_x11_output_create(pepper_x11_connection_t *connection,
+ int32_t w,
+ int32_t h,
+ const char *renderer)
{
- static const char *window_name = "PePPer Compositor";
- static const char *class_name = "pepper-1\0PePPer Compositor";
+ static const char *window_name = "PePPer Compositor";
+ static const char *class_name = "pepper-1\0PePPer Compositor";
+
+ pepper_output_t *base;
+ x11_output_t *output;
- pepper_output_t *base;
- x11_output_t *output;
+ struct wl_display *wldisplay;
+ struct wl_event_loop *loop;
output = pepper_calloc(1, sizeof(x11_output_t));
if (!output)
{
- PEPPER_ERROR("x11:output:%s: memory allocation failed", __FUNCTION__);
+ PEPPER_ERROR("memory allocation failed");
return NULL;
}
/* Hard-Coded: scale value to 1. */
output->scale = 1;
- /* create X11 window */
+ /* Create X11 window */
{
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t values[2] = {
connection->screen->white_pixel,
- 0
+ XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
xcb_size_hints_t hints;
+ xcb_atom_t list[1];
output->window = xcb_generate_id(connection->xcb_connection);
xcb_create_window(connection->xcb_connection,
mask,
values);
-
/* cannot resize */
memset(&hints, 0, sizeof(hints));
hints.flags = WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
connection->atom.string, 8,
strlen(class_name), class_name);
+ /* set property to receive wm_delete_window message */
+ list[0] = connection->atom.wm_delete_window;
+ xcb_change_property(connection->xcb_connection, XCB_PROP_MODE_REPLACE,
+ output->window,
+ connection->atom.wm_protocols,
+ XCB_ATOM_ATOM, 32,
+ 1, list);
+
+
xcb_map_window(connection->xcb_connection, output->window);
if (connection->use_xinput)
wl_list_insert(&connection->outputs, &output->link);
xcb_flush(connection->xcb_connection);
+
+ x11_output_wait_for_map(output);
}
+ /* Init signals and listeners */
wl_signal_init(&output->destroy_signal);
wl_signal_init(&output->mode_change_signal);
+ wl_signal_init(&output->frame_signal);
+
+ wldisplay = pepper_compositor_get_display(connection->compositor);
+ loop = wl_display_get_event_loop(wldisplay);
+ output->frame_done_timer = wl_event_loop_add_timer(loop, frame_done_handler, output);
- x11_output_visual_iterate(output);
+ output->conn_destroy_listener.notify = handle_connection_destroy;
+ wl_signal_add(&connection->destroy_signal, &output->conn_destroy_listener);
+
+ /* Init renderer */
+ renderer_init(output, renderer);
+ /* Register output object */
base = pepper_compositor_add_output(connection->compositor,
&x11_output_interface,
output);
if (!base)
{
- PEPPER_ERROR("x11:output:%s: pepper_compositor_add_output failed\n", __FUNCTION__);
+ PEPPER_ERROR("pepper_compositor_add_output failed\n");
x11_output_destroy(output);
return NULL;
}
- output->conn_destroy_listener.notify = handle_connection_destroy;
- wl_signal_add(&connection->destroy_signal, &output->conn_destroy_listener);
+ output->base = base;
return base;
}