#define EXECUTIVE_MODE_ENABLED #define E_COMP_WL #include "e.h" #include E_API int E_EVENT_TEXT_INPUT_PANEL_VISIBILITY_CHANGE = -1; static void _e_comp_wl_input_update_seat_caps(void) { Eina_List *l; struct wl_resource *res; enum wl_seat_capability caps = 0; if (e_comp_wl->ptr.enabled) caps |= WL_SEAT_CAPABILITY_POINTER; if (e_comp_wl->kbd.enabled) caps |= WL_SEAT_CAPABILITY_KEYBOARD; if (e_comp_wl->touch.enabled) caps |= WL_SEAT_CAPABILITY_TOUCH; EINA_LIST_FOREACH(e_comp_wl->seat.resources, l, res) wl_seat_send_capabilities(res, caps); } static void _e_comp_wl_input_cb_resource_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) { wl_resource_destroy(resource); } static void _e_comp_wl_input_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource EINA_UNUSED, uint32_t serial EINA_UNUSED, struct wl_resource *surface_resource, int32_t x, int32_t y) { E_Client *ec; Eina_Bool got_mouse = EINA_FALSE; E_CLIENT_FOREACH(ec) { if (e_pixmap_type_get(ec->pixmap) != E_PIXMAP_TYPE_WL) continue; if (!ec->comp_data->surface) continue; if (client != wl_resource_get_client(ec->comp_data->surface)) continue; if (ec->mouse.in) { got_mouse = EINA_TRUE; break; } } if (!got_mouse) return; if (!surface_resource) { e_pointer_object_set(e_comp->pointer, NULL, x, y); return; } ec = wl_resource_get_user_data(surface_resource); if (!ec->re_manage) { ec->re_manage = 1; ec->ignored = 0; ec->lock_focus_out = ec->layer_block = ec->visible = ec->override = 1; ec->icccm.title = eina_stringshare_add("noshadow"); evas_object_pass_events_set(ec->frame, 1); e_client_focus_stack_set(eina_list_remove(e_client_focus_stack_get(), ec)); EC_CHANGED(ec); } /* ignore cursor changes during resize/move I guess */ if (e_client_action_get()) return; e_pointer_object_set(e_comp->pointer, ec->frame, x, y); } static const struct wl_pointer_interface _e_pointer_interface = { _e_comp_wl_input_pointer_cb_cursor_set, _e_comp_wl_input_cb_resource_destroy }; static const struct wl_keyboard_interface _e_keyboard_interface = { _e_comp_wl_input_cb_resource_destroy }; static const struct wl_touch_interface _e_touch_interface = { _e_comp_wl_input_cb_resource_destroy }; static void _e_comp_wl_input_cb_pointer_unbind(struct wl_resource *resource) { e_comp_wl->ptr.resources = eina_list_remove(e_comp_wl->ptr.resources, resource); } static void _e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct wl_resource *res; /* try to create pointer resource */ res = wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(resource), id); if (!res) { ERR("Could not create pointer on seat %s: %m", e_comp_wl->seat.name); wl_client_post_no_memory(client); return; } e_comp_wl->ptr.resources = eina_list_append(e_comp_wl->ptr.resources, res); wl_resource_set_implementation(res, &_e_pointer_interface, e_comp->wl_comp_data, _e_comp_wl_input_cb_pointer_unbind); } static void _e_comp_wl_input_cb_keyboard_unbind(struct wl_resource *resource) { Eina_List *l, *ll; struct wl_resource *res; e_comp_wl->kbd.resources = eina_list_remove(e_comp_wl->kbd.resources, resource); EINA_LIST_FOREACH_SAFE(e_comp_wl->kbd.focused, l, ll, res) { if (res == resource) { e_comp_wl->kbd.focused = eina_list_remove_list(e_comp_wl->kbd.focused, l); } } } void e_comp_wl_input_keyboard_enter_send(E_Client *ec) { struct wl_resource *res; Eina_List *l; uint32_t serial; if (!ec->comp_data->surface) return; if (!e_comp_wl->kbd.focused) return; e_comp_wl_input_keyboard_modifiers_serialize(); serial = wl_display_next_serial(e_comp_wl->wl.disp); EINA_LIST_FOREACH(e_comp_wl->kbd.focused, l, res) { wl_keyboard_send_enter(res, serial, ec->comp_data->surface, &e_comp_wl->kbd.keys); wl_keyboard_send_modifiers(res, serial, e_comp_wl->kbd.mod_depressed, e_comp_wl->kbd.mod_latched, e_comp_wl->kbd.mod_locked, e_comp_wl->kbd.mod_group); } } static void _e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, uint32_t id) { E_Client *focused; struct wl_resource *res; /* try to create keyboard resource */ res = wl_resource_create(client, &wl_keyboard_interface, wl_resource_get_version(resource), id); if (!res) { ERR("Could not create keyboard on seat %s: %m", e_comp_wl->seat.name); wl_client_post_no_memory(client); return; } e_comp_wl->kbd.resources = eina_list_append(e_comp_wl->kbd.resources, res); wl_resource_set_implementation(res, &_e_keyboard_interface, e_comp->wl_comp_data, _e_comp_wl_input_cb_keyboard_unbind); /* FIXME: These values should be configurable */ if (wl_resource_get_version(res) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) wl_keyboard_send_repeat_info(res, 25, 400); /* send current keymap */ wl_keyboard_send_keymap(res, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, e_comp_wl->xkb.fd, e_comp_wl->xkb.size); /* if the client owns the focused surface, we need to send an enter */ focused = e_client_focused_get(); if (!focused) return; if (client != wl_resource_get_client(focused->comp_data->surface)) return; e_comp_wl->kbd.focused = eina_list_append(e_comp_wl->kbd.focused, res); e_comp_wl_input_keyboard_enter_send(focused); } static void _e_comp_wl_input_cb_touch_unbind(struct wl_resource *resource) { e_comp_wl->touch.resources = eina_list_remove(e_comp_wl->touch.resources, resource); } static void _e_comp_wl_input_cb_touch_get(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t id EINA_UNUSED) { struct wl_resource *res; /* try to create pointer resource */ res = wl_resource_create(client, &wl_touch_interface, wl_resource_get_version(resource), id); if (!res) { ERR("Could not create touch on seat %s: %m", e_comp_wl->seat.name); wl_client_post_no_memory(client); return; } e_comp_wl->touch.resources = eina_list_append(e_comp_wl->touch.resources, res); wl_resource_set_implementation(res, &_e_touch_interface, e_comp->wl_comp_data, _e_comp_wl_input_cb_touch_unbind); } static const struct wl_seat_interface _e_seat_interface = { _e_comp_wl_input_cb_pointer_get, _e_comp_wl_input_cb_keyboard_get, _e_comp_wl_input_cb_touch_get, }; static void _e_comp_wl_input_cb_unbind_seat(struct wl_resource *resource) { e_comp_wl->seat.resources = eina_list_remove(e_comp_wl->seat.resources, resource); } static void _e_comp_wl_input_cb_bind_seat(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id) { struct wl_resource *res; res = wl_resource_create(client, &wl_seat_interface, MIN(version, 4), id); if (!res) { ERR("Could not create seat resource: %m"); return; } /* store version of seat interface for reuse in updating capabilities */ e_comp_wl->seat.version = version; e_comp_wl->seat.resources = eina_list_append(e_comp_wl->seat.resources, res); wl_resource_set_implementation(res, &_e_seat_interface, e_comp->wl_comp_data, _e_comp_wl_input_cb_unbind_seat); _e_comp_wl_input_update_seat_caps(); if (e_comp_wl->seat.version >= WL_SEAT_NAME_SINCE_VERSION) wl_seat_send_name(res, e_comp_wl->seat.name); } static int _e_comp_wl_input_keymap_fd_get(off_t size) { int fd = 0, blen = 0, len = 0; const char *path; char tmp[PATH_MAX]; long flags; blen = sizeof(tmp) - 1; if (!(path = getenv("XDG_RUNTIME_DIR"))) return -1; len = strlen(path); if (len < blen) { strcpy(tmp, path); strcat(tmp, "/e-wl-keymap-XXXXXX"); } else return -1; if ((fd = mkstemp(tmp)) < 0) return -1; flags = fcntl(fd, F_GETFD); if (flags < 0) { close(fd); return -1; } if (fcntl(fd, F_SETFD, (flags | FD_CLOEXEC)) == -1) { close(fd); return -1; } if (ftruncate(fd, size) < 0) { close(fd); return -1; } unlink(tmp); return fd; } static void _e_comp_wl_input_keymap_update(struct xkb_keymap *keymap) { char *tmp; xkb_mod_mask_t latched = 0, locked = 0, group = 0; struct wl_resource *res; Eina_List *l; /* unreference any existing keymap */ if (e_comp_wl->xkb.keymap) xkb_map_unref(e_comp_wl->xkb.keymap); /* unmap any existing keyboard area */ if (e_comp_wl->xkb.area) munmap(e_comp_wl->xkb.area, e_comp_wl->xkb.size); if (e_comp_wl->xkb.fd >= 0) close(e_comp_wl->xkb.fd); /* unreference any existing keyboard state */ if (e_comp_wl->xkb.state) { latched = xkb_state_serialize_mods(e_comp_wl->xkb.state, XKB_STATE_MODS_LATCHED); locked = xkb_state_serialize_mods(e_comp_wl->xkb.state, XKB_STATE_MODS_LOCKED); group = xkb_state_serialize_layout(e_comp_wl->xkb.state, XKB_STATE_LAYOUT_EFFECTIVE); xkb_state_unref(e_comp_wl->xkb.state); } /* create a new xkb state */ e_comp_wl->xkb.state = xkb_state_new(keymap); if ((latched) || (locked) || (group)) xkb_state_update_mask(e_comp_wl->xkb.state, 0, latched, locked, 0, 0, group); /* increment keymap reference */ e_comp_wl->xkb.keymap = keymap; /* fetch updated modifiers */ e_comp_wl->kbd.mod_shift = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); e_comp_wl->kbd.mod_caps = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS); e_comp_wl->kbd.mod_ctrl = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CTRL); e_comp_wl->kbd.mod_alt = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_ALT); e_comp_wl->kbd.mod_super = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_LOGO); if (!(tmp = xkb_map_get_as_string(keymap))) { ERR("Could not get keymap string"); return; } e_comp_wl->xkb.size = strlen(tmp) + 1; e_comp_wl->xkb.fd = _e_comp_wl_input_keymap_fd_get(e_comp_wl->xkb.size); if (e_comp_wl->xkb.fd < 0) { ERR("Could not create keymap file"); free(tmp); return; } e_comp_wl->xkb.area = mmap(NULL, e_comp_wl->xkb.size, (PROT_READ | PROT_WRITE), MAP_SHARED, e_comp_wl->xkb.fd, 0); if (e_comp_wl->xkb.area == MAP_FAILED) { ERR("Failed to mmap keymap area: %m"); free(tmp); return; } strcpy(e_comp_wl->xkb.area, tmp); free(tmp); /* send updated keymap */ EINA_LIST_FOREACH(e_comp_wl->kbd.resources, l, res) wl_keyboard_send_keymap(res, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, e_comp_wl->xkb.fd, e_comp_wl->xkb.size); /* update modifiers */ e_comp_wl_input_keyboard_modifiers_update(); } EINTERN Eina_Bool e_comp_wl_input_init(void) { /* set default seat name */ if (!e_comp_wl->seat.name) e_comp_wl->seat.name = "default"; e_comp_wl->xkb.fd = -1; /* create the global resource for input seat */ e_comp_wl->seat.global = wl_global_create(e_comp_wl->wl.disp, &wl_seat_interface, 4, e_comp->wl_comp_data, _e_comp_wl_input_cb_bind_seat); if (!e_comp_wl->seat.global) { ERR("Could not create global for seat: %m"); return EINA_FALSE; } wl_array_init(&e_comp_wl->kbd.keys); E_EVENT_TEXT_INPUT_PANEL_VISIBILITY_CHANGE = ecore_event_type_new(); return EINA_TRUE; } EINTERN void e_comp_wl_input_shutdown(void) { struct wl_resource *res; /* destroy pointer resources */ EINA_LIST_FREE(e_comp_wl->ptr.resources, res) wl_resource_destroy(res); /* destroy keyboard resources */ EINA_LIST_FREE(e_comp_wl->kbd.resources, res) wl_resource_destroy(res); e_comp_wl->kbd.resources = eina_list_free(e_comp_wl->kbd.resources); /* destroy touch resources */ EINA_LIST_FREE(e_comp_wl->touch.resources, res) wl_resource_destroy(res); /* destroy e_comp_wl->kbd.keys array */ wl_array_release(&e_comp_wl->kbd.keys); /* unmap any existing keyboard area */ if (e_comp_wl->xkb.area) munmap(e_comp_wl->xkb.area, e_comp_wl->xkb.size); if (e_comp_wl->xkb.fd >= 0) close(e_comp_wl->xkb.fd); /* unreference any existing keyboard state */ if (e_comp_wl->xkb.state) xkb_state_unref(e_comp_wl->xkb.state); /* unreference any existing keymap */ if (e_comp_wl->xkb.keymap) xkb_map_unref(e_comp_wl->xkb.keymap); /* unreference any existing context */ if (e_comp_wl->xkb.context) xkb_context_unref(e_comp_wl->xkb.context); /* destroy the global seat resource */ if (e_comp_wl->seat.global) wl_global_destroy(e_comp_wl->seat.global); e_comp_wl->seat.global = NULL; } EINTERN Eina_Bool e_comp_wl_input_pointer_check(struct wl_resource *res) { return wl_resource_instance_of(res, &wl_pointer_interface, &_e_pointer_interface); } EINTERN Eina_Bool e_comp_wl_input_keyboard_check(struct wl_resource *res) { return wl_resource_instance_of(res, &wl_keyboard_interface, &_e_keyboard_interface); } EINTERN Eina_Bool e_comp_wl_input_keyboard_modifiers_serialize(void) { Eina_Bool changed = EINA_FALSE; xkb_mod_mask_t mod; xkb_layout_index_t grp; mod = xkb_state_serialize_mods(e_comp_wl->xkb.state, XKB_STATE_DEPRESSED); changed |= mod != e_comp_wl->kbd.mod_depressed; e_comp_wl->kbd.mod_depressed = mod; mod = xkb_state_serialize_mods(e_comp_wl->xkb.state, XKB_STATE_MODS_LATCHED); changed |= mod != e_comp_wl->kbd.mod_latched; e_comp_wl->kbd.mod_latched = mod; mod = xkb_state_serialize_mods(e_comp_wl->xkb.state, XKB_STATE_MODS_LOCKED); changed |= mod != e_comp_wl->kbd.mod_locked; e_comp_wl->kbd.mod_locked = mod; grp = xkb_state_serialize_layout(e_comp_wl->xkb.state, XKB_STATE_LAYOUT_EFFECTIVE); changed |= grp != e_comp_wl->kbd.mod_group; e_comp_wl->kbd.mod_group = grp; return changed; } EINTERN void e_comp_wl_input_keyboard_modifiers_update(void) { uint32_t serial; struct wl_resource *res; Eina_List *l; if (!e_comp_wl_input_keyboard_modifiers_serialize()) return; if (!e_comp_wl->kbd.focused) return; serial = wl_display_next_serial(e_comp_wl->wl.disp); EINA_LIST_FOREACH(e_comp_wl->kbd.focused, l, res) wl_keyboard_send_modifiers(res, serial, e_comp_wl->kbd.mod_depressed, e_comp_wl->kbd.mod_latched, e_comp_wl->kbd.mod_locked, e_comp_wl->kbd.mod_group); } EINTERN void e_comp_wl_input_keyboard_state_update(uint32_t keycode, Eina_Bool pressed) { enum xkb_key_direction dir; if (!e_comp_wl->xkb.state) return; if (pressed) dir = XKB_KEY_DOWN; else dir = XKB_KEY_UP; e_comp_wl->kbd.mod_changed = xkb_state_update_key(e_comp_wl->xkb.state, keycode + 8, dir); e_comp_wl_input_keyboard_modifiers_update(); } E_API void e_comp_wl_input_pointer_enabled_set(Eina_Bool enabled) { /* check for valid compositor data */ if (!e_comp->wl_comp_data) { ERR("No compositor data"); return; } e_comp_wl->ptr.enabled = !!enabled; _e_comp_wl_input_update_seat_caps(); } E_API void e_comp_wl_input_keyboard_enabled_set(Eina_Bool enabled) { /* check for valid compositor data */ if (!e_comp->wl_comp_data) { ERR("No compositor data"); return; } e_comp_wl->kbd.enabled = !!enabled; _e_comp_wl_input_update_seat_caps(); } E_API void e_comp_wl_input_keymap_set(const char *rules, const char *model, const char *layout) { struct xkb_keymap *keymap; struct xkb_rule_names names; /* DBG("COMP_WL: Keymap Set: %s %s %s", rules, model, layout); */ /* assemble xkb_rule_names so we can fetch keymap */ memset(&names, 0, sizeof(names)); if (rules) names.rules = strdup(rules); else names.rules = strdup("evdev"); if (model) names.model = strdup(model); else names.model = strdup("pc105"); if (layout) names.layout = strdup(layout); else names.layout = strdup("us"); /* unreference any existing context */ if (e_comp_wl->xkb.context) xkb_context_unref(e_comp_wl->xkb.context); /* create a new xkb context */ e_comp_wl->xkb.context = xkb_context_new(0); /* fetch new keymap based on names */ keymap = xkb_map_new_from_names(e_comp_wl->xkb.context, &names, 0); if (keymap) { /* update compositor keymap */ _e_comp_wl_input_keymap_update(keymap); } /* cleanup */ free((char *)names.rules); free((char *)names.model); free((char *)names.layout); } E_API void e_comp_wl_input_touch_enabled_set(Eina_Bool enabled) { /* check for valid compositor data */ if (!e_comp->wl_comp_data) { ERR("No compositor data"); return; } e_comp_wl->touch.enabled = !!enabled; _e_comp_wl_input_update_seat_caps(); } EINTERN Eina_Bool e_comp_wl_input_touch_check(struct wl_resource *res) { return wl_resource_instance_of(res, &wl_touch_interface, &_e_touch_interface); }