Add code to support initialize, setup and destruction of wayland

inputs (pointer, keyboard, touch).

Signed-off-by: Chris Michael <cp.michael@samsung.com>
This commit is contained in:
Chris Michael 2013-04-09 11:03:14 +01:00
parent a1447c102e
commit deec13f2ce
1 changed files with 636 additions and 17 deletions

View File

@ -7,14 +7,33 @@ static void _e_comp_wl_cb_bind(struct wl_client *client, void *data EINA_UNUSED,
static Eina_Bool _e_comp_wl_cb_read(void *data EINA_UNUSED, Ecore_Fd_Handler *hdl EINA_UNUSED);
static Eina_Bool _e_comp_wl_cb_idle(void *data EINA_UNUSED);
/* input function prototypes */
static Eina_Bool _e_comp_wl_input_init(void);
static void _e_comp_wl_input_shutdown(void);
/* compositor interface prototypes */
static void _e_comp_wl_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id);
/* input function prototypes */
static Eina_Bool _e_comp_wl_input_init(void);
static void _e_comp_wl_input_shutdown(void);
static void _e_comp_wl_input_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id);
static void _e_comp_wl_input_cb_unbind(struct wl_resource *resource);
static struct xkb_keymap *_e_comp_wl_input_keymap_get(void);
static int _e_comp_wl_input_keymap_fd_get(off_t size);
static E_Wayland_Keyboard_Info *_e_comp_wl_input_keyboard_info_get(struct xkb_keymap *keymap);
static void _e_comp_wl_input_modifiers_update(unsigned int serial);
/* input interface prototypes */
static void _e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_input_cb_touch_get(struct wl_client *client, struct wl_resource *resource, unsigned int id);
/* pointer function prototypes */
static void _e_comp_wl_pointer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED);
static void _e_comp_wl_pointer_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h);
static void _e_comp_wl_pointer_unmap(E_Wayland_Surface *ews);
/* pointer interface prototypes */
static void _e_comp_wl_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource, unsigned int serial, struct wl_resource *surface_resource, int x, int y);
/* local wayland interfaces */
static const struct wl_compositor_interface _e_compositor_interface =
{
@ -22,6 +41,18 @@ static const struct wl_compositor_interface _e_compositor_interface =
_e_comp_wl_cb_region_create
};
static const struct wl_seat_interface _e_input_interface =
{
_e_comp_wl_input_cb_pointer_get,
_e_comp_wl_input_cb_keyboard_get,
_e_comp_wl_input_cb_touch_get,
};
static const struct wl_pointer_interface _e_pointer_interface =
{
_e_comp_wl_pointer_cb_cursor_set
};
/* local variables */
/* external variables */
@ -218,19 +249,6 @@ _e_comp_wl_cb_idle(void *data EINA_UNUSED)
return ECORE_CALLBACK_RENEW;
}
/* input functions */
static Eina_Bool
_e_comp_wl_input_init(void)
{
return EINA_TRUE;
}
static void
_e_comp_wl_input_shutdown(void)
{
}
/* compositor interface functions */
static void
_e_comp_wl_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id)
@ -244,3 +262,604 @@ _e_comp_wl_cb_region_create(struct wl_client *client, struct wl_resource *resour
}
/* input functions */
static Eina_Bool
_e_comp_wl_input_init(void)
{
struct xkb_keymap *keymap;
/* try to allocate space for a new compositor */
if (!(_e_wl_comp->input = E_NEW(E_Wayland_Input, 1)))
return EINA_FALSE;
/* initialize the seat */
wl_seat_init(&_e_wl_comp->input->wl.seat);
/* try to add this input to the diplay's list of globals */
if (!wl_display_add_global(_e_wl_comp->wl.display, &wl_seat_interface,
_e_wl_comp->input, _e_comp_wl_input_cb_bind))
{
ERR("Could not add Input to Wayland Display Globals: %m");
goto err;
}
_e_wl_comp->input->pointer.surface = NULL;
_e_wl_comp->input->pointer.surface_destroy.notify =
_e_comp_wl_pointer_cb_destroy;
_e_wl_comp->input->pointer.hot.x = 16;
_e_wl_comp->input->pointer.hot.y = 16;
/* initialize wayland pointer */
wl_pointer_init(&_e_wl_comp->input->wl.pointer);
/* tell the seat about this pointer */
wl_seat_set_pointer(&_e_wl_comp->input->wl.seat,
&_e_wl_comp->input->wl.pointer);
/* set flag to indicate we have a pointer */
_e_wl_comp->input->has_pointer = EINA_TRUE;
/* create the xkb context */
_e_wl_comp->xkb.context = xkb_context_new(0);
/* try to fetch the keymap */
if ((keymap = _e_comp_wl_input_keymap_get()))
{
/* try to create new keyboard info */
_e_wl_comp->input->xkb.info =
_e_comp_wl_input_keyboard_info_get(keymap);
/* create new xkb state */
_e_wl_comp->input->xkb.state = xkb_state_new(keymap);
/* unreference the keymap */
xkb_map_unref(keymap);
}
/* initialize the keyboard */
wl_keyboard_init(&_e_wl_comp->input->wl.keyboard);
/* tell the seat about this keyboard */
wl_seat_set_keyboard(&_e_wl_comp->input->wl.seat,
&_e_wl_comp->input->wl.keyboard);
/* set flag to indicate we have a keyboard */
_e_wl_comp->input->has_keyboard = EINA_TRUE;
wl_list_init(&_e_wl_comp->input->wl.link);
/* append this input to the list */
_e_wl_comp->seats = eina_list_append(_e_wl_comp->seats, _e_wl_comp->input);
/* emit a signal saying that input has been initialized */
wl_signal_emit(&_e_wl_comp->signals.seat, _e_wl_comp->input);
/* return success */
return EINA_TRUE;
err:
/* release the wl_seat */
wl_seat_release(&_e_wl_comp->input->wl.seat);
/* free the allocated input structure */
E_FREE(_e_wl_comp->input);
/* return failure */
return EINA_FALSE;
}
static void
_e_comp_wl_input_shutdown(void)
{
/* safety check */
if (!_e_wl_comp->input) return;
/* destroy keyboard */
if (_e_wl_comp->input->xkb.info)
{
/* if we have a keymap, unreference it */
if (_e_wl_comp->input->xkb.info->keymap)
xkb_map_unref(_e_wl_comp->input->xkb.info->keymap);
/* if we have a keymap mmap'd area, unmap it */
if (_e_wl_comp->input->xkb.info->area)
munmap(_e_wl_comp->input->xkb.info->area,
_e_wl_comp->input->xkb.info->size);
/* if we created an fd for keyboard input, close it */
if (_e_wl_comp->input->xkb.info->fd)
close(_e_wl_comp->input->xkb.info->fd);
/* free the allocated keyboard info structure */
E_FREE(_e_wl_comp->input->xkb.info);
}
/* unreference the xkb state we created */
if (_e_wl_comp->input->xkb.state)
xkb_state_unref(_e_wl_comp->input->xkb.state);
/* unreference the xkb context we created */
if (_e_wl_comp->xkb.context)
xkb_context_unref(_e_wl_comp->xkb.context);
/* TODO: destroy pointer surface
*
* NB: Currently, we do not create one */
wl_list_remove(&_e_wl_comp->input->wl.link);
/* release the seat */
wl_seat_release(&_e_wl_comp->input->wl.seat);
/* free the allocated input structure */
E_FREE(_e_wl_comp->input);
}
static void
_e_comp_wl_input_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id)
{
struct wl_seat *seat = NULL;
struct wl_resource *resource = NULL;
enum wl_seat_capability caps = 0;
/* try to cast data to our seat */
if (!(seat = data)) return;
/* add the seat object to the client */
resource =
wl_client_add_object(client, &wl_seat_interface,
&_e_input_interface, id, data);
wl_list_insert(&seat->base_resource_list, &resource->link);
/* set resource destroy callback */
resource->destroy = _e_comp_wl_input_cb_unbind;
/* set capabilities based on seat */
if (seat->pointer) caps |= WL_SEAT_CAPABILITY_POINTER;
if (seat->keyboard) caps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (seat->touch) caps |= WL_SEAT_CAPABILITY_TOUCH;
/* inform the resource about the seat capabilities */
wl_seat_send_capabilities(resource, caps);
}
static void
_e_comp_wl_input_cb_unbind(struct wl_resource *resource)
{
/* remove the link */
wl_list_remove(&resource->link);
/* free the resource */
free(resource);
}
static struct xkb_keymap *
_e_comp_wl_input_keymap_get(void)
{
struct xkb_rule_names names;
memset(&names, 0, sizeof(names));
/* if we are running under X11, try to get the xkb rule names atom */
if (getenv("DISPLAY"))
{
Ecore_X_Atom rules = 0;
Ecore_X_Window root = 0;
int len = 0;
unsigned char *data;
/* TODO: FIXME: NB:
*
* The below commented out code is for using the "already" configured
* E xkb settings in the wayland clients. The only Major problem with
* that is: That the E_Config_XKB_Layout does not define a
* 'RULES' which we need ....
*
*/
/* E_Config_XKB_Layout *kbd_layout; */
/* kbd_layout = e_xkb_layout_get(); */
/* names.model = strdup(kbd_layout->model); */
/* names.layout = strdup(kbd_layout->name); */
root = ecore_x_window_root_first_get();
rules = ecore_x_atom_get("_XKB_RULES_NAMES");
ecore_x_window_prop_property_get(root, rules, ECORE_X_ATOM_STRING,
1024, &data, &len);
if ((data) && (len > 0))
{
names.rules = strdup((const char *)data);
data += strlen((const char *)data) + 1;
names.model = strdup((const char *)data);
data += strlen((const char *)data) + 1;
names.layout = strdup((const char *)data);
// free(data);
}
}
/* printf("Names\n"); */
/* printf("\tRules: %s\n", names.rules); */
/* printf("\tModel: %s\n", names.model); */
/* printf("\tLayout: %s\n", names.layout); */
return xkb_map_new_from_names(_e_wl_comp->xkb.context, &names, 0);
}
static int
_e_comp_wl_input_keymap_fd_get(off_t size)
{
char tmp[PATH_MAX];
const char *path;
int fd = 0;
long flags;
if (!(path = getenv("XDG_RUNTIME_DIR")))
return -1;
strcpy(tmp, path);
strcat(tmp, "/e-wl-keymap-XXXXXX");
if ((fd = mkstemp(tmp)) < 0)
return -1;
/* TODO: really should error check the returns here */
flags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
if (ftruncate(fd, size) < 0)
{
close(fd);
return -1;
}
unlink(tmp);
return fd;
}
static E_Wayland_Keyboard_Info *
_e_comp_wl_input_keyboard_info_get(struct xkb_keymap *keymap)
{
E_Wayland_Keyboard_Info *info = NULL;
char *tmp;
/* try to allocate space for the keyboard info structure */
if (!(info = E_NEW(E_Wayland_Keyboard_Info, 1)))
return NULL;
info->keymap = xkb_map_ref(keymap);
/* init modifiers */
info->mod_shift = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
info->mod_caps = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
info->mod_ctrl = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
info->mod_alt = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_ALT);
info->mod_super = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_LOGO);
/* try to get a string of this keymap */
if (!(tmp = xkb_map_get_as_string(keymap)))
{
printf("Could not map get as string\n");
}
info->size = strlen(tmp) + 1;
/* try to create an fd we can listen on for input */
info->fd = _e_comp_wl_input_keymap_fd_get(info->size);
if (info->fd < 0)
{
printf("Could not create keymap fd\n");
}
info->area =
mmap(NULL, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, info->fd, 0);
/* TODO: error check mmap */
strcpy(info->area, tmp);
free(tmp);
return info;
}
static void
_e_comp_wl_input_modifiers_update(unsigned int serial)
{
struct wl_keyboard *kbd;
struct wl_keyboard_grab *grab;
unsigned int pressed = 0, latched = 0, locked = 0, group = 0;
Eina_Bool changed = EINA_FALSE;
/* check for valid keyboard */
if (!(kbd = _e_wl_comp->input->wl.seat.keyboard))
return;
/* try to get the current keyboard's grab interface.
* Fallback to the default grab */
if (!(grab = kbd->grab)) grab = &kbd->default_grab;
pressed = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state,
XKB_STATE_DEPRESSED);
latched = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state,
XKB_STATE_LATCHED);
locked = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state,
XKB_STATE_LOCKED);
group = xkb_state_serialize_group(_e_wl_comp->input->xkb.state,
XKB_STATE_EFFECTIVE);
if ((pressed != kbd->modifiers.mods_depressed) ||
(latched != kbd->modifiers.mods_latched) ||
(locked != kbd->modifiers.mods_locked) ||
(group != kbd->modifiers.group))
changed = EINA_TRUE;
kbd->modifiers.mods_depressed = pressed;
kbd->modifiers.mods_latched = latched;
kbd->modifiers.mods_locked = locked;
kbd->modifiers.group = group;
/* TODO: update leds ? */
if (changed)
grab->interface->modifiers(grab, serial,
kbd->modifiers.mods_depressed,
kbd->modifiers.mods_latched,
kbd->modifiers.mods_locked,
kbd->modifiers.group);
}
/* input interface functions */
static void
_e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
E_Wayland_Input *input = NULL;
struct wl_resource *ptr = NULL;
/* try to cast the resource data to our input structure */
if (!(input = resource->data)) return;
/* check that input has a pointer */
if (!input->has_pointer) return;
/* add a pointer object to the client */
ptr = wl_client_add_object(client, &wl_pointer_interface,
&_e_pointer_interface, id, input);
wl_list_insert(&input->wl.seat.pointer->resource_list, &ptr->link);
/* set pointer destroy callback */
ptr->destroy = _e_comp_wl_input_cb_unbind;
/* if the pointer has a focused surface, set it */
if ((input->wl.seat.pointer->focus) &&
(input->wl.seat.pointer->focus->resource.client == client))
{
/* FIXME: what to pass to wl_pointer_set_focus (x/y) */
wl_pointer_set_focus(input->wl.seat.pointer,
input->wl.seat.pointer->focus,
input->wl.seat.pointer->x,
input->wl.seat.pointer->y);
}
}
static void
_e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
E_Wayland_Input *input = NULL;
struct wl_resource *kbd = NULL;
/* try to cast the resource data to our input structure */
if (!(input = resource->data)) return;
/* check that input has a keyboard */
if (!input->has_keyboard) return;
/* add a keyboard object to the client */
kbd = wl_client_add_object(client, &wl_keyboard_interface, NULL, id, input);
wl_list_insert(&input->wl.seat.keyboard->resource_list, &kbd->link);
/* set keyboard destroy callback */
kbd->destroy = _e_comp_wl_input_cb_unbind;
/* send the current keymap to the keyboard object */
wl_keyboard_send_keymap(kbd, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
input->xkb.info->fd, input->xkb.info->size);
/* test if keyboard has a focused client */
if ((input->wl.seat.keyboard->focus) &&
(input->wl.seat.keyboard->focus->resource.client == client))
{
/* set keyboard focus */
wl_keyboard_set_focus(input->wl.seat.keyboard,
input->wl.seat.keyboard->focus);
/* update the keyboard focus in the data device */
wl_data_device_set_keyboard_focus(&input->wl.seat);
}
}
static void
_e_comp_wl_input_cb_touch_get(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
E_Wayland_Input *input = NULL;
struct wl_resource *tch = NULL;
/* try to cast the resource data to our input structure */
if (!(input = resource->data)) return;
/* check that input has a touch */
if (!input->has_touch) return;
/* add a touch object to the client */
tch = wl_client_add_object(client, &wl_touch_interface, NULL, id, input);
wl_list_insert(&input->wl.seat.touch->resource_list, &tch->link);
/* set touch destroy callback */
tch->destroy = _e_comp_wl_input_cb_unbind;
}
/* pointer functions */
static void
_e_comp_wl_pointer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
E_Wayland_Input *input = NULL;
/* try to get the input from this listener */
if (!(input = container_of(listener, E_Wayland_Input, pointer.surface_destroy)))
return;
input->pointer.surface = NULL;
}
static void
_e_comp_wl_pointer_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
E_Wayland_Input *input = NULL;
E_Wayland_Surface *focus = NULL;
/* safety check */
if (!ews) return;
/* see if this surface has 'input' */
if (!(input = ews->input)) return;
input->pointer.hot.x -= x;
input->pointer.hot.y -= y;
/* configure the surface geometry */
ews->geometry.x = x;
ews->geometry.h = h;
ews->geometry.w = w;
ews->geometry.h = h;
ews->geometry.changed = EINA_TRUE;
/* tell pixman we are finished with this region */
pixman_region32_fini(&ews->pending.input);
/* reinitalize the pending input region */
pixman_region32_init(&ews->pending.input);
/* do we have a focused surface ? */
if ((focus = (E_Wayland_Surface *)input->wl.seat.pointer->focus))
{
/* NB: Ideally, I wanted to use the e_pointer methods here so that
* the cursor would match the E theme, however Wayland currently
* provides NO Method to get the cursor name :( so we are stuck
* using the pixels from their cursor surface */
/* is it mapped ? */
if ((focus->mapped) && (focus->ee))
{
Ecore_Window win;
/* try to get the ecore_window */
if ((win = ecore_evas_window_get(focus->ee)))
{
void *pixels;
Ecore_X_Cursor cur;
/* grab the pixels from the cursor surface */
pixels = wl_shm_buffer_get_data(ews->reference.buffer);
/* create the new X cursor with this image */
cur = ecore_x_cursor_new(win, pixels, w, h,
input->pointer.hot.x,
input->pointer.hot.y);
/* set the cursor on this window */
ecore_x_window_cursor_set(win, cur);
/* free the cursor */
ecore_x_cursor_free(cur);
}
}
}
}
static void
_e_comp_wl_pointer_unmap(E_Wayland_Surface *ews)
{
E_Wayland_Input *input = NULL;
/* safety check */
if (!ews) return;
if (!(input = ews->input)) return;
wl_list_remove(&input->pointer.surface_destroy.link);
if (input->pointer.surface)
input->pointer.surface->configure = NULL;
input->pointer.surface = NULL;
}
/* pointer interface functions */
static void
_e_comp_wl_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource, unsigned int serial, struct wl_resource *surface_resource, int x, int y)
{
E_Wayland_Input *input = NULL;
E_Wayland_Surface *ews = NULL;
/* try to cast the resource data to our input structure */
if (!(input = resource->data)) return;
/* if we were passed in a surface, try to cast it to our structure */
if (surface_resource) ews = (E_Wayland_Surface *)surface_resource->data;
/* if the input has no current focus, get out */
if (!input->wl.seat.pointer->focus) return;
if (input->wl.seat.pointer->focus->resource.client != client) return;
if ((input->wl.seat.pointer->focus_serial - serial) > (UINT32_MAX / 2))
return;
/* is the passed in surface the same as our pointer surface ? */
if ((ews) && (ews != input->pointer.surface))
{
if (ews->configure)
{
wl_resource_post_error(&ews->wl.surface.resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"Surface already configured");
return;
}
}
/* if we have an existing pointer surface, unmap it */
if (input->pointer.surface)
{
/* call the unmap function */
if (input->pointer.surface->unmap)
input->pointer.surface->unmap(input->pointer.surface);
}
if (!ews) return;
/* set the destroy listener */
wl_signal_add(&ews->wl.surface.resource.destroy_signal,
&input->pointer.surface_destroy);
/* set some properties on this surface */
ews->unmap = _e_comp_wl_pointer_unmap;
ews->configure = _e_comp_wl_pointer_configure;
ews->input = input;
/* update input structure with new values */
input->pointer.surface = ews;
input->pointer.hot.x = x;
input->pointer.hot.y = y;
if (ews->reference.buffer)
{
Evas_Coord bw = 0, bh = 0;
/* grab the size of the buffer */
bw = ews->reference.buffer->width;
bh = ews->reference.buffer->height;
/* configure the pointer surface */
_e_comp_wl_pointer_configure(ews, 0, 0, bw, bh);
}
}