From 5d31507819a8b00b636a932f0c32f19e640c9ae7 Mon Sep 17 00:00:00 2001 From: "jaehoon01.jeong" Date: Tue, 7 Apr 2015 14:34:57 +0900 Subject: [PATCH] X11 backend implementation - initial version - partially implemented Change-Id: I68a2f0675bfdc3f6ca110d23daf80cc88ce3b068 --- configure.ac | 13 ++ src/Makefile.am | 16 ++- src/compositor.c | 1 + src/modules/x11/pepper-x11.h | 28 ++++ src/modules/x11/x11-common.c | 191 +++++++++++++++++++++++++++ src/modules/x11/x11-cursor.c | 133 +++++++++++++++++++ src/modules/x11/x11-input.c | 197 +++++++++++++++++++++++++++ src/modules/x11/x11-internal.h | 131 ++++++++++++++++++ src/modules/x11/x11-output.c | 293 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1002 insertions(+), 1 deletion(-) create mode 100644 src/modules/x11/pepper-x11.h create mode 100644 src/modules/x11/x11-common.c create mode 100644 src/modules/x11/x11-cursor.c create mode 100644 src/modules/x11/x11-input.c create mode 100644 src/modules/x11/x11-internal.h create mode 100644 src/modules/x11/x11-output.c diff --git a/configure.ac b/configure.ac index 7c4de97..b1fddf8 100644 --- a/configure.ac +++ b/configure.ac @@ -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]), diff --git a/src/Makefile.am b/src/Makefile.am index 67e279c..0c979dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/compositor.c b/src/compositor.c index 4036429..9d232e6 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -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 index 0000000..ff45c23 --- /dev/null +++ b/src/modules/x11/pepper-x11.h @@ -0,0 +1,28 @@ +#ifndef PEPPER_X11_H +#define PEPPER_X11_h + +#include + +#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 index 0000000..b5a4f17 --- /dev/null +++ b/src/modules/x11/x11-common.c @@ -0,0 +1,191 @@ +#include +#include +#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 index 0000000..f96057f --- /dev/null +++ b/src/modules/x11/x11-cursor.c @@ -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 index 0000000..84e6cc2 --- /dev/null +++ b/src/modules/x11/x11-input.c @@ -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 index 0000000..53c0378 --- /dev/null +++ b/src/modules/x11/x11-internal.h @@ -0,0 +1,131 @@ +#ifndef X11_INTERNAL_H +#define X11_INTERNAL_H + +#include "pepper-x11.h" + +#include +#include +#include +#include +#include +#include + +#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 index 0000000..01d4627 --- /dev/null +++ b/src/modules/x11/x11-output.c @@ -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; +} -- 2.7.4