X11 backend implementation
authorjaehoon01.jeong <jaehoon01.jeong@samsung.com>
Tue, 7 Apr 2015 05:34:57 +0000 (14:34 +0900)
committerTaekyun Kim <tkq.kim@samsung.com>
Fri, 19 Jun 2015 09:06:40 +0000 (18:06 +0900)
    - initial version
    - partially implemented

Change-Id: I68a2f0675bfdc3f6ca110d23daf80cc88ce3b068

configure.ac
src/Makefile.am
src/compositor.c
src/modules/x11/pepper-x11.h [new file with mode: 0644]
src/modules/x11/x11-common.c [new file with mode: 0644]
src/modules/x11/x11-cursor.c [new file with mode: 0644]
src/modules/x11/x11-input.c [new file with mode: 0644]
src/modules/x11/x11-internal.h [new file with mode: 0644]
src/modules/x11/x11-output.c [new file with mode: 0644]

index 7c4de97..b1fddf8 100644 (file)
@@ -50,6 +50,19 @@ if test $enable_drm_backend = yes; then
     PKG_CHECK_MODULES(DRM_BACKEND, [libinput >= 0.6.0])
 fi
 
+# x11 backend
+AC_ARG_ENABLE(x11-backend,
+              AC_HELP_STRING([--enable-x11-backend], [enable x11 backend]),
+              [enable_x11_backend=$enableval], [enable_x11_backend=yes])
+#              [enable_x11_backend=$enableval], [enable_x11_backend=no])
+
+AM_CONDITIONAL(ENABLE_X11_BACKEND, test $enable_x11_backend = yes)
+
+if test $enable_x11_backend = yes; then
+    AC_DEFINE(ENABLE_X11_BACKEND, 1, [Enable x11 backend])
+    PKG_CHECK_MODULES(X11_BACKEND, [x11 xcb-shm x11-xcb])
+fi
+
 # pepper server
 AC_ARG_ENABLE(pepper-server,
               AC_HELP_STRING([--enable-pepper-server], [build pepper reference server]),
index 67e279c..0c979dd 100644 (file)
@@ -7,7 +7,7 @@ noinst_LTLIBRARIES =
 lib_LTLIBRARIES += libpepper.la
 include_HEADERS += pepper.h
 
-libpepper_la_CFLAGS = $(LIB_PEPPER_CFLAGS)
+libpepper_la_CFLAGS = $(LIB_PEPPER_CFLAGS) -Wall
 libpepper_la_LIBADD = $(LIB_PEPPER_LIBS)
 
 libpepper_la_SOURCES = pepper.h                 \
@@ -46,6 +46,20 @@ libpepper_la_LIBADD += $(DRM_BACKEND_LIBS)
 libpepper_la_SOURCES += modules/drm/drm-internal.h
 endif
 
+# x11 backend
+if ENABLE_X11_BACKEND
+include_HEADERS += modules/x11/pepper-x11.h
+
+libpepper_la_CFLAGS += $(X11_BACKEND_CFLAGS)
+libpepper_la_LIBADD += $(X11_BACKEND_LIBS)
+
+libpepper_la_SOURCES += modules/x11/x11-common.c    \
+                        modules/x11/x11-output.c    \
+                        modules/x11/x11-input.c     \
+                        modules/x11/x11-internal.h
+endif
+
+
 # Pepper server executable
 if ENABLE_PEPPER_SERVER
 bin_PROGRAMS += pepper
index 4036429..9d232e6 100644 (file)
@@ -74,6 +74,7 @@ pepper_compositor_create(const char *socket_name)
     wl_global_create(compositor->display, &wl_compositor_interface, 3, compositor,
                      compositor_bind);
     wl_list_init(&compositor->surfaces);
+    wl_list_init(&compositor->seat_list);
 
     compositor->shell = pepper_shell_create(compositor);
     if (!compositor->shell)
diff --git a/src/modules/x11/pepper-x11.h b/src/modules/x11/pepper-x11.h
new file mode 100644 (file)
index 0000000..ff45c23
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef PEPPER_X11_H
+#define PEPPER_X11_h
+
+#include <pepper.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct pepper_x11_connection pepper_x11_connection_t;
+
+PEPPER_API pepper_x11_connection_t *
+pepper_x11_connect(pepper_compositor_t *compositor, const char *display_name);
+
+PEPPER_API void
+pepper_x11_destroy(pepper_x11_connection_t *conn);
+
+PEPPER_API pepper_output_t *
+pepper_x11_output_create(pepper_x11_connection_t *connection, int32_t w, int32_t h);
+
+PEPPER_API void
+pepper_x11_seat_create(pepper_x11_connection_t* connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PEPPER_X11_H */
diff --git a/src/modules/x11/x11-common.c b/src/modules/x11/x11-common.c
new file mode 100644 (file)
index 0000000..b5a4f17
--- /dev/null
@@ -0,0 +1,191 @@
+#include <wayland-server.h>
+#include <common.h>
+#include "x11-internal.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+/*
+ * xcb version
+ * xcb is faster than xlib
+ */
+
+static inline pepper_bool_t
+x11_get_next_event(xcb_connection_t *xcb_conn, xcb_generic_event_t **event, uint32_t mask)
+{
+    if (mask & WL_EVENT_READABLE)
+        *event = xcb_poll_for_event(xcb_conn);
+
+    if (mask & WL_EVENT_WRITABLE)
+        PEPPER_ERROR("WL_EVENT_WRITABLE\n");
+
+    return *event != NULL;
+}
+
+static int
+x11_handle_event(int fd, uint32_t mask, void *data)
+{
+    pepper_x11_connection_t     *connection = data;
+    x11_seat_t                  *seat;
+    xcb_generic_event_t         *event = NULL;
+
+    uint32_t            count = 0;
+
+    if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR))
+        return 0;
+
+    if (!connection->use_xinput)
+        return 0;
+
+    /* TODO: At now, x11-backend has only 1 seat per connection, "seat0"
+     *       but if not, we need to find matched seat at here
+     */
+    seat = connection->seat;
+
+    while(x11_get_next_event(connection->xcb_connection, &event, mask))
+    {
+        uint32_t type = event->response_type & ~0x80;
+        switch (type)
+        {
+        case XCB_ENTER_NOTIFY:
+        case XCB_LEAVE_NOTIFY:
+        case XCB_KEY_PRESS:
+        case XCB_KEY_RELEASE:
+        case XCB_BUTTON_PRESS:
+        case XCB_BUTTON_RELEASE:
+        case XCB_MOTION_NOTIFY:
+            x11_handle_input_event(seat, type, event);
+            break;
+        case XCB_EXPOSE:
+            /*PEPPER_ERROR("x11:event:not input event\n");*/
+            break;
+        default :
+            PEPPER_ERROR("x11:common:Unknown event\n");
+            break;
+        }
+
+        free(event);
+        count++;
+    }
+
+    return count;
+}
+
+#define F(field) offsetof(pepper_x11_connection_t, field)
+static void
+x11_init_atoms(pepper_x11_connection_t *conn)
+{
+    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) },
+    };
+
+    xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
+    xcb_intern_atom_reply_t  *reply;
+
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+    {
+        cookies[i] = xcb_intern_atom(conn->xcb_connection, 0,
+                                     strlen(atoms[i].name),
+                                     atoms[i].name);
+    }
+
+    for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+    {
+        reply = xcb_intern_atom_reply(conn->xcb_connection, cookies[i], NULL);
+        *(xcb_atom_t *) ((char *)conn + atoms[i].offset) = reply->atom;
+        free(reply);
+    }
+}
+
+PEPPER_API pepper_x11_connection_t *
+pepper_x11_connect(pepper_compositor_t *compositor, const char *display_name)
+{
+    pepper_x11_connection_t     *connection = NULL;
+    struct wl_display           *wdisplay;
+    struct wl_event_loop        *loop;
+    xcb_screen_iterator_t       scr_iter;
+
+    if (!compositor)
+    {
+        PEPPER_ERROR("x11:common:%s: compositor is null\n", __FUNCTION__);
+        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__);
+        return NULL;
+    }
+
+    connection->display = XOpenDisplay(display_name);
+    if (!connection->display)
+    {
+        PEPPER_ERROR("x11:common:%s: XOpenDisplay failed\n", __FUNCTION__);
+        pepper_free(connection);
+        return NULL;
+    }
+
+    connection->xcb_connection = XGetXCBConnection(connection->display);
+    XSetEventQueueOwner(connection->display, XCBOwnsEventQueue);
+
+    if (xcb_connection_has_error(connection->xcb_connection))
+    {
+        PEPPER_ERROR("x11:common:%s: xcb connection has error\n", __FUNCTION__);
+        pepper_free(connection);
+        return NULL;
+    }
+
+    scr_iter = xcb_setup_roots_iterator(xcb_get_setup(connection->xcb_connection));
+    connection->screen = scr_iter.data;
+
+    connection->compositor = compositor;
+    connection->fd = xcb_get_file_descriptor(connection->xcb_connection);
+    if (display_name)
+        connection->display_name = pepper_string_copy(display_name);
+    else
+        connection->display_name = NULL;
+
+    x11_init_atoms(connection);
+
+    wdisplay = pepper_compositor_get_display(compositor);
+    loop = wl_display_get_event_loop(wdisplay);
+
+    connection->xcb_event_source = wl_event_loop_add_fd(loop,
+                                                        connection->fd,
+                                                        WL_EVENT_READABLE,
+                                                        x11_handle_event,
+                                                        connection);
+    wl_event_source_check(connection->xcb_event_source);
+
+    wl_list_init(&connection->outputs);
+
+    wl_signal_init(&connection->destroy_signal);
+
+    return connection;
+}
+
+PEPPER_API void
+pepper_x11_destroy(pepper_x11_connection_t *conn)
+{
+    /* TODO */
+}
diff --git a/src/modules/x11/x11-cursor.c b/src/modules/x11/x11-cursor.c
new file mode 100644 (file)
index 0000000..f96057f
--- /dev/null
@@ -0,0 +1,133 @@
+
+
+static void *
+x11_output_cursor_set(void *o, void *c)
+{
+    x11_output_t     *output = o;
+    x11_cursor_t     *cursor = c;
+    x11_cursor_t     *current_cursor;
+    pepper_x11_connection_t *conn;
+
+    if (!output)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s output is null\n", __FUNCTION__);
+        return NULL;
+    }
+
+    if (!cursor)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s cursor is null\n", __FUNCTION__);
+        return NULL;
+    }
+
+    conn = output->connection;
+
+    current_cursor = output->cursor;
+    output->cursor = cursor;
+
+    /* set cursor for window*/
+    {
+        uint32_t mask       = XCB_CW_CURSOR;
+        uint32_t value_list = cursor->xcb_cursor;
+        xcb_change_window_attributes(conn->xcb_connection,
+                                     output->window,
+                                     mask,
+                                     &value_list);
+    }
+
+    return current_cursor;
+}
+
+static void *
+x11_output_cursor_create(void *output, int32_t w, int32_t h, void *image)
+{
+    xcb_pixmap_t     pixmap;
+    xcb_gc_t         gc;
+    pepper_x11_connection_t *conn;
+    x11_cursor_t     *cursor;
+
+    if (!output)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s: output is null\n", __FUNCTION__);
+        return NULL;
+    }
+    if (!image)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s: image is null\n", __FUNCTION__);
+        return NULL;
+    }
+    if (w<0 || h<0)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s: width(%d) or height(%d) is invalid\n",
+                     __FUNCTION__, w, h);
+        return NULL;
+    }
+
+    cursor = pepper_calloc(1, sizeof(x11_cursor_t));
+    if (!cursor)
+    {
+        PEPPER_ERROR("x11:cursor: memory allocation failed");
+        return NULL;
+    }
+    cursor->data = image;
+    cursor->w = w;
+    cursor->h = h;
+
+    conn = ((x11_output_t *)output)->connection;
+
+    pixmap = xcb_generate_id(conn->xcb_connection);
+    gc = xcb_generate_id(conn->xcb_connection);
+
+    xcb_create_pixmap(conn->xcb_connection, 1/*depth?*/,
+                      pixmap, conn->screen->root, w, h);
+    xcb_create_gc(conn->xcb_connection, gc, pixmap, 0, NULL);
+    xcb_put_image(conn->xcb_connection, XCB_IMAGE_FORMAT_XY_PIXMAP, pixmap,
+                  gc, w, h, 0, 0, 0, 32, w*h*sizeof(uint8_t), cursor->data);
+    cursor->xcb_cursor = xcb_generate_id(conn->xcb_connection);
+    /*
+     * cb_void_cookie_t xcb_create_cursor(xcb_connection_t *conn,
+     *                                    xcb_cursor_t cid,
+     *                                    xcb_pixmap_t source,
+     *                                    xcb_pixmap_t mask,
+     *                                    uint16_t fore_red,    TODO: NOT YET DOCUMENTED.
+     *                                    uint16_t fore_green,
+     *                                    uint16_t fore_blue,
+     *                                    uint16_t back_red,
+     *                                    uint16_t back_green,
+     *                                    uint16_t back_blue,
+     *                                    uint16_t x,
+     *                                    uint16_t y);
+     */
+    xcb_create_cursor(conn->xcb_connection, cursor->xcb_cursor,
+                      pixmap, pixmap, 0, 0, 0,  0, 0, 0,  1, 1);
+
+    xcb_free_gc(conn->xcb_connection, gc);
+    xcb_free_pixmap(conn->xcb_connection, pixmap);
+
+    return (void *)cursor;
+}
+
+static void
+x11_output_cursor_destroy(void *o, void *c)
+{
+    xcb_connection_t *conn;
+    x11_cursor_t     *cursor;
+
+    if (!o)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s: output is null\n", __FUNCTION__);
+        return ;
+    }
+    if (!c)
+    {
+        PEPPER_ERROR("x11:output:cursor:%s: cursor is null\n", __FUNCTION__);
+        return ;
+    }
+
+    conn = ((x11_output_t *)o)->connection->xcb_connection;
+    cursor = (x11_cursor_t *)c;
+
+    xcb_free_cursor(conn, cursor->xcb_cursor);
+    /* XXX: pepper_free(cursor->data); ??? */
+    pepper_free(cursor);
+}
diff --git a/src/modules/x11/x11-input.c b/src/modules/x11/x11-input.c
new file mode 100644 (file)
index 0000000..84e6cc2
--- /dev/null
@@ -0,0 +1,197 @@
+#include "x11-internal.h"
+
+void
+x11_handle_input_event(x11_seat_t* seat, uint32_t type, xcb_generic_event_t* xev)
+{
+    pepper_input_event_t        event;
+
+    memset(&event, 0x00, sizeof(pepper_input_event_t));
+
+    switch (type)
+    {
+    case XCB_ENTER_NOTIFY:
+    case XCB_LEAVE_NOTIFY:
+    case XCB_KEY_PRESS:
+    case XCB_KEY_RELEASE:
+        break;
+    case XCB_BUTTON_PRESS:
+        {
+            xcb_button_press_event_t *bp = (xcb_button_press_event_t *)xev;
+            switch (bp->detail)
+            {
+            case XCB_BUTTON_INDEX_1:/* FIXME: LEFT */
+                PEPPER_ERROR("left click\n");
+                event.index = 1;
+                break;
+            case XCB_BUTTON_INDEX_3:/* FIXME: RIGHT */
+                PEPPER_ERROR("right click\n");
+                event.index = 3;
+                break;
+            default:
+                PEPPER_ERROR("wheel or something pressed\n");
+                break;
+            }
+            event.type   = PEPPER_INPUT_EVENT_POINTER_BUTTON;
+            event.time   = bp->time;
+            event.serial = bp->sequence;
+            event.state  = PEPPER_INPUT_EVENT_STATE_PRESSED;
+            event.value  = 0;
+            event.x      = bp->event_x;
+            event.y      = bp->event_y;;
+        }
+        break;
+    case XCB_BUTTON_RELEASE:
+        {
+            xcb_button_release_event_t *br = (xcb_button_release_event_t *)xev;
+            switch (br->detail)
+            {
+            case XCB_BUTTON_INDEX_1:/* FIXME: LEFT */
+                PEPPER_ERROR("left released\n");
+                event.index = 1;
+                break;
+            case XCB_BUTTON_INDEX_3:/* FIXME: RIGHT */
+                PEPPER_ERROR("right released\n");
+                event.index = 3;
+                break;
+            default:
+                PEPPER_ERROR("wheel or something pressed\n");
+                break;
+            }
+            event.type   = PEPPER_INPUT_EVENT_POINTER_BUTTON;
+            event.time   = br->time;
+            event.serial = br->sequence;
+            event.state  = PEPPER_INPUT_EVENT_STATE_RELEASED;
+            event.value  = 0;
+            event.x      = br->event_x;
+            event.y      = br->event_y;;
+        }
+        break;
+    case XCB_MOTION_NOTIFY:
+        {
+            xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)xev;
+            event.type   = PEPPER_INPUT_EVENT_POINTER_MOTION;
+            event.time   = motion->time;
+            event.serial = motion->sequence;
+            event.index  = 0;
+            event.state  = 0;
+            event.value  = 0;
+            event.x      = motion->event_x;
+            event.y      = motion->event_y;
+        }
+        break;
+    default :
+        PEPPER_ERROR("x11:input: unknown input event\n");
+    }
+
+    pepper_seat_handle_event(seat->base, &event);
+}
+
+void
+x11_window_input_property_change(xcb_connection_t *conn, xcb_window_t window)
+{
+     const static uint32_t values[] =
+     {
+         XCB_EVENT_MASK_EXPOSURE |
+         XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+         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
+     };
+     xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
+     xcb_flush(conn);
+}
+
+static void
+x11_seat_destroy(void *data)
+{
+    PEPPER_IGNORE(data);
+    /* TODO : x11_seat_t *seat = (x11_seat_t *)data; */
+    return;
+}
+
+static void
+x11_seat_add_capability_listener(void *data, struct wl_listener *listener)
+{
+    x11_seat_t *seat = (x11_seat_t *)data;
+    wl_signal_add(&seat->capability_signal, listener);
+    return;
+}
+
+static void
+x11_seat_add_name_listener(void *data, struct wl_listener *listener)
+{
+    x11_seat_t *seat = (x11_seat_t *)data;
+    wl_signal_add(&seat->name_signal, listener);
+    return;
+}
+
+static uint32_t
+x11_seat_get_capabilities(void *data)
+{
+    x11_seat_t *seat = (x11_seat_t *)data;
+    return seat->caps;
+}
+
+static const char *
+x11_seat_get_name(void *data)
+{
+    x11_seat_t *seat = (x11_seat_t *)data;
+    return seat->name;
+}
+
+static const pepper_seat_interface_t x11_seat_interface =
+{
+    x11_seat_destroy,
+    x11_seat_add_capability_listener,
+    x11_seat_add_name_listener,
+    x11_seat_get_capabilities,
+    x11_seat_get_name,
+};
+
+PEPPER_API void
+pepper_x11_seat_create(pepper_x11_connection_t* conn)
+{
+    x11_seat_t  *seat;
+
+    if (!conn)
+    {
+        PEPPER_ERROR("x11:input:%s: connection is null...\n", __FUNCTION__);
+        return ;
+    }
+
+    PEPPER_ERROR("x11:input: initialize input module...\n");
+
+    seat = pepper_calloc(1, sizeof(x11_seat_t));
+    if (!seat)
+    {
+        PEPPER_ERROR("x11:input:%s: failed to allocate memory\n", __FUNCTION__);
+        return ;
+    }
+
+    conn->use_xinput = PEPPER_TRUE;
+
+    if (!wl_list_empty(&conn->outputs))
+    {
+        x11_output_t *out;
+        wl_list_for_each(out, &conn->outputs, link)
+            x11_window_input_property_change(conn->xcb_connection, out->window);
+    }
+    /* XXX: if x-input-module used with out x-output-module,
+     * need to create dummy window for input with output-size */
+
+    wl_signal_init(&seat->capability_signal);
+    wl_signal_init(&seat->name_signal);
+
+    seat->base = pepper_compositor_add_seat(conn->compositor, &x11_seat_interface, seat);
+    seat->id = X11_BACKEND_INPUT_ID;
+
+    /* x-connection has only 1 seat */
+    conn->seat = seat;
+}
+
diff --git a/src/modules/x11/x11-internal.h b/src/modules/x11/x11-internal.h
new file mode 100644 (file)
index 0000000..53c0378
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef X11_INTERNAL_H
+#define X11_INTERNAL_H
+
+#include "pepper-x11.h"
+
+#include <common.h>
+#include <xcb/xcb.h>
+#include <xcb/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+#include <string.h>
+
+#define X11_BACKEND_INPUT_ID 0x12345678
+
+typedef struct x11_output   x11_output_t;
+typedef struct x11_cursor   x11_cursor_t;
+typedef struct x11_seat     x11_seat_t;
+
+struct x11_output
+{
+    pepper_x11_connection_t* connection;
+
+    int32_t             x, y;
+    uint32_t            w, h;
+    uint32_t            subpixel;
+    uint32_t            scale;
+    uint8_t             depth;
+
+    xcb_window_t        window;
+    xcb_gc_t            gc;
+    x11_cursor_t        *cursor;
+
+    struct wl_signal    destroy_signal;
+    struct wl_signal    mode_change_signal;
+
+    struct wl_listener  conn_destroy_listener;
+
+    struct wl_list      link;
+};
+
+struct x11_seat
+{
+    pepper_seat_t       *base;
+
+    uint32_t            id;
+    uint32_t            caps;
+    char                *name;
+
+    wl_fixed_t          pointer_x_last;
+    wl_fixed_t          pointer_y_last;
+    wl_fixed_t          touch_x_last;   /* FIXME */
+    wl_fixed_t          touch_y_last;   /* FIXME */
+
+    struct wl_list      link;
+    struct wl_signal    capability_signal;
+    struct wl_signal    name_signal;
+};
+
+struct pepper_x11_connection
+{
+    pepper_compositor_t    *compositor;
+    char                   *display_name;
+
+    Display                *display;
+    xcb_screen_t           *screen;
+    xcb_connection_t       *xcb_connection;
+
+    struct wl_event_source *xcb_event_source;
+    struct wl_list          link;
+    int fd;
+
+    struct wl_list          outputs;
+
+    pepper_bool_t           use_xinput;
+    x11_seat_t              *seat;
+
+    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 wl_signal        destroy_signal;
+};
+
+struct x11_cursor
+{
+    xcb_cursor_t xcb_cursor;
+    int w;
+    int h;
+    uint8_t *data;
+};
+
+/* it declared in xcb-icccm.h */
+typedef struct xcb_size_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;
+}xcb_size_hints_t;
+#define WM_NORMAL_HINTS_MIN_SIZE        16
+#define WM_NORMAL_HINTS_MAX_SIZE        32
+/* -- xcb-icccm.h */
+
+pepper_x11_connection_t *
+pepper_x11_connect(pepper_compositor_t *compositor, const char *display_name);
+
+void
+x11_window_input_property_change(xcb_connection_t *conn, xcb_window_t window);
+
+void
+x11_handle_input_event(x11_seat_t* seat, uint32_t type, xcb_generic_event_t* xev);
+
+#endif  /*X11_INTERNAL_H*/
diff --git a/src/modules/x11/x11-output.c b/src/modules/x11/x11-output.c
new file mode 100644 (file)
index 0000000..01d4627
--- /dev/null
@@ -0,0 +1,293 @@
+#include "x11-internal.h"
+
+static uint8_t
+x11_get_depth_of_visual(xcb_screen_t *screen, xcb_visualid_t id)
+{
+    xcb_depth_iterator_t i;
+    xcb_visualtype_iterator_t j;
+
+    i = xcb_screen_allowed_depths_iterator(screen);
+    for (; i.rem; xcb_depth_next(&i))
+    {
+        j = xcb_depth_visuals_iterator(i.data);
+        for (; j.rem; xcb_visualtype_next(&j))
+        {
+            if (j.data->visual_id == id)
+                return i.data->depth;
+        }
+    }
+    return 0;
+}
+
+static void
+x11_output_visual_iterate(void *o)
+{
+    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;
+
+        depth_iter = xcb_screen_allowed_depths_iterator(screen);
+        for (; depth_iter.rem; xcb_depth_next(&depth_iter))
+        {
+            xcb_visualtype_iterator_t visual_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));
+            }
+        }
+    }
+}
+
+static void
+x11_output_destroy(void *o)
+{
+    x11_output_t            *output;
+    pepper_x11_connection_t *conn;
+
+    if (!o)
+    {
+        PEPPER_ERROR("x11:output:%s: output is null\n", __FUNCTION__);
+        return ;
+    }
+
+    output = o;
+    conn = output->connection;
+
+    wl_signal_emit(&output->destroy_signal, output);
+
+    xcb_destroy_window(conn->xcb_connection, output->window);
+
+    wl_list_remove(&output->link);
+
+    pepper_free(output);
+}
+
+static int32_t
+x11_output_get_subpixel_order(void *o)
+{
+    x11_output_t *output = o;
+    return output->subpixel;
+}
+
+static const char *
+x11_output_get_maker_name(void *o)
+{
+    PEPPER_IGNORE(o);
+    return "pepper_x11";
+}
+
+static const char *
+x11_output_get_model_name(void *o)
+{
+    PEPPER_IGNORE(o);
+    return "pepper_x11";
+}
+
+static int
+x11_output_get_mode_count(void *o)
+{
+    PEPPER_IGNORE(o);
+
+    /* There's only one available mode in x11 backend which is also the current mode. */
+    return 1;
+}
+
+static void
+x11_output_get_mode(void *o, int index, pepper_output_mode_t *mode)
+{
+    x11_output_t *output = o;
+
+    if (index != 0)
+        return;
+
+    mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+    mode->w = output->w;
+    mode->h = output->h;
+    mode->refresh = 60000;
+}
+
+static pepper_bool_t
+x11_output_set_mode(void *o, const pepper_output_mode_t *mode)
+{
+    x11_output_t *output = o;
+
+    if (mode->w <= 0 || mode->h <= 0)
+        return PEPPER_FALSE;
+
+    if (mode->refresh != 60000)
+        return PEPPER_FALSE;
+
+    if (output->w != mode->w || output->h != mode->h)
+    {
+        output->w = mode->w;
+        output->h = mode->h;
+
+        /* TODO: Handle resize here. */
+
+        wl_signal_emit(&output->mode_change_signal, output);
+    }
+
+    return PEPPER_TRUE;
+}
+
+static void
+x11_output_add_destroy_listener(void *o, struct wl_listener *listener)
+{
+    x11_output_t *output = o;
+    wl_signal_add(&output->destroy_signal, listener);
+}
+
+static void
+x11_output_add_mode_change_listener(void *o, struct wl_listener *listener)
+{
+    x11_output_t *output = o;
+    wl_signal_add(&output->mode_change_signal, listener);
+}
+
+static const pepper_output_interface_t x11_output_interface =
+{
+    x11_output_destroy,
+    x11_output_add_destroy_listener,
+    x11_output_add_mode_change_listener,
+
+    x11_output_get_subpixel_order,
+    x11_output_get_maker_name,
+    x11_output_get_model_name,
+
+    x11_output_get_mode_count,
+    x11_output_get_mode,
+    x11_output_set_mode,
+};
+
+static void
+handle_connection_destroy(struct wl_listener *listener, void *data)
+{
+    x11_output_t *output = wl_container_of(listener, output, conn_destroy_listener);
+    x11_output_destroy(output);
+}
+
+PEPPER_API pepper_output_t *
+pepper_x11_output_create(pepper_x11_connection_t *connection, int32_t w, int32_t h)
+{
+    static const char   *window_name = "PePPer Compositor";
+    static const char   *class_name  = "pepper-1\0PePPer Compositor";
+
+    pepper_output_t     *base;
+    x11_output_t        *output;
+
+    output = pepper_calloc(1, sizeof(x11_output_t));
+    if (!output)
+    {
+        PEPPER_ERROR("x11:output:%s: memory allocation failed", __FUNCTION__);
+        return NULL;
+    }
+
+    output->connection = connection;
+    output->w = w;
+    output->h = h;
+
+    /* Hard-Coded: subpixel order to horizontal RGB. */
+    output->subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+
+    /* Hard-Coded: scale value to 1. */
+    output->scale = 1;
+
+    /* create X11 window */
+    {
+        uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+        uint32_t values[2] = {
+                connection->screen->white_pixel,
+                0
+        };
+        xcb_size_hints_t hints;
+
+        output->window = xcb_generate_id(connection->xcb_connection);
+        xcb_create_window(connection->xcb_connection,
+                          XCB_COPY_FROM_PARENT,
+                          output->window,
+                          connection->screen->root,
+                          0,    /* X position of top-left corner of window */
+                          0,    /* Y position of top-left corner of window */
+                          w*output->scale,
+                          h*output->scale,
+                          0,    /* width of windows' border */
+                          XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                          connection->screen->root_visual,
+                          mask,
+                          values);
+
+
+        /* cannot resize */
+        memset(&hints, 0, sizeof(hints));
+        hints.flags = WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
+        hints.min_width  = hints.max_width  = w*output->scale;
+        hints.min_height = hints.max_height = h*output->scale;
+        xcb_change_property(connection->xcb_connection,
+                            XCB_PROP_MODE_REPLACE,
+                            output->window,
+                            connection->atom.wm_normal_hints,
+                            connection->atom.wm_size_hints,
+                            32,
+                            sizeof(hints) / 4,
+                            (uint8_t *)&hints);
+
+        /* set window name */
+        xcb_change_property(connection->xcb_connection, XCB_PROP_MODE_REPLACE,
+                            output->window,
+                            connection->atom.net_wm_name,
+                            connection->atom.utf8_string, 8,
+                            strlen(window_name), window_name);
+        xcb_change_property(connection->xcb_connection, XCB_PROP_MODE_REPLACE,
+                            output->window,
+                            connection->atom.wm_class,
+                            connection->atom.string, 8,
+                            strlen(class_name), class_name);
+
+        xcb_map_window(connection->xcb_connection, output->window);
+
+        if (connection->use_xinput)
+            x11_window_input_property_change(connection->xcb_connection,
+                                             output->window);
+
+        wl_list_insert(&connection->outputs, &output->link);
+
+        xcb_flush(connection->xcb_connection);
+    }
+
+    wl_signal_init(&output->destroy_signal);
+    wl_signal_init(&output->mode_change_signal);
+
+    /*x11_output_visual_iterate(output);*/
+
+    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__);
+        x11_output_destroy(output);
+        return NULL;
+    }
+
+    output->conn_destroy_listener.notify = handle_connection_destroy;
+    wl_signal_add(&connection->destroy_signal, &output->conn_destroy_listener);
+
+    return base;
+}