// Copyright 2018 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sommelier.h" // NOLINT(build/include_directory) #include "sommelier-transform.h" // NOLINT(build/include_directory) #include "weak-resource-ptr.h" // NOLINT(build/include_directory) #include #include #include #include #include "text-input-extension-unstable-v1-client-protocol.h" // NOLINT(build/include_directory) #include "text-input-extension-unstable-v1-server-protocol.h" // NOLINT(build/include_directory) #include "text-input-unstable-v1-client-protocol.h" // NOLINT(build/include_directory) #include "text-input-unstable-v1-server-protocol.h" // NOLINT(build/include_directory) #include "text-input-x11-unstable-v1-server-protocol.h" // NOLINT(build/include_directory) namespace { // Versions supported by sommelier. constexpr uint32_t kTextInputManagerVersion = 1; constexpr uint32_t kTextInputExtensionVersion = 4; constexpr uint32_t kTextInputX11Version = 1; } // namespace struct sl_host_text_input_manager { struct sl_context* ctx; struct wl_resource* resource; struct zwp_text_input_manager_v1* proxy; }; struct sl_host_text_input { struct sl_context* ctx; struct wl_resource* resource; struct zwp_text_input_v1* proxy; WeakResourcePtr active_surface; }; MAP_STRUCTS(zwp_text_input_v1, sl_host_text_input); struct sl_host_text_input_extension { struct sl_context* ctx; struct wl_resource* resource; struct zcr_text_input_extension_v1* proxy; }; struct sl_host_extended_text_input { struct sl_context* ctx; struct wl_resource* resource; struct sl_host_text_input* host_text_input; struct zcr_extended_text_input_v1* proxy; }; MAP_STRUCTS(zcr_extended_text_input_v1, sl_host_extended_text_input); static void sl_text_input_activate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) { struct sl_host_text_input* host = static_cast(wl_resource_get_user_data(resource)); struct sl_host_seat* host_seat = static_cast(wl_resource_get_user_data(seat)); struct sl_host_surface* host_surface = static_cast(wl_resource_get_user_data(surface)); host->active_surface = host_surface; zwp_text_input_v1_activate(host->proxy, host_seat->proxy, host_surface->proxy); } static void sl_text_input_deactivate(wl_client* client, wl_resource* resource, wl_resource* seat) { struct sl_host_text_input* host = static_cast(wl_resource_get_user_data(resource)); struct sl_host_seat* host_seat = static_cast(wl_resource_get_user_data(seat)); host->active_surface.Reset(); zwp_text_input_v1_deactivate(host->proxy, host_seat->proxy); } static void sl_text_input_set_cursor_rectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct sl_host_text_input* host = static_cast(wl_resource_get_user_data(resource)); if (!host->active_surface) return; int32_t x1 = x; int32_t y1 = y; int32_t x2 = x + width; int32_t y2 = y + height; sl_transform_guest_to_host(host->ctx, host->active_surface.get(), &x1, &y1); sl_transform_guest_to_host(host->ctx, host->active_surface.get(), &x2, &y2); zwp_text_input_v1_set_cursor_rectangle(host->proxy, x1, y1, x2 - x1, y2 - y1); } static const struct zwp_text_input_v1_interface sl_text_input_implementation = { sl_text_input_activate, sl_text_input_deactivate, ForwardRequest, ForwardRequest, ForwardRequest, ForwardRequest, ForwardRequest, sl_text_input_set_cursor_rectangle, ForwardRequest, ForwardRequest, ForwardRequest, }; static void sl_text_input_enter(void* data, struct zwp_text_input_v1* text_input, struct wl_surface* surface) { // This is not currently used by cros_im. We can't simply forward the event // as for an x11-hosted cros_im instance the text_input and wl_surface // objects will be on different clients. We could add a corresponding event // to text_input_x11 if needed. } static void sl_text_input_leave(void* data, struct zwp_text_input_v1* text_input) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_leave(host->resource); } static void sl_text_input_modifiers_map(void* data, struct zwp_text_input_v1* text_input, struct wl_array* map) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_modifiers_map(host->resource, map); } static void sl_text_input_input_panel_state( void* data, struct zwp_text_input_v1* text_input, uint32_t state) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_input_panel_state(host->resource, state); } static void sl_text_input_preedit_string(void* data, struct zwp_text_input_v1* text_input, uint32_t serial, const char* text, const char* commit) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_preedit_string(host->resource, serial, text, commit); } static void sl_text_input_preedit_styling(void* data, struct zwp_text_input_v1* text_input, uint32_t index, uint32_t length, uint32_t style) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_preedit_styling(host->resource, index, length, style); } static void sl_text_input_preedit_cursor(void* data, struct zwp_text_input_v1* text_input, int32_t index) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_preedit_cursor(host->resource, index); } static void sl_text_input_commit_string(void* data, struct zwp_text_input_v1* text_input, uint32_t serial, const char* text) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_commit_string(host->resource, serial, text); } static void sl_text_input_cursor_position(void* data, struct zwp_text_input_v1* text_input, int32_t index, int32_t anchor) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_cursor_position(host->resource, index, anchor); } static void sl_text_input_delete_surrounding_text( void* data, struct zwp_text_input_v1* text_input, int32_t index, uint32_t length) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_delete_surrounding_text(host->resource, index, length); } static void sl_text_input_keysym(void* data, struct zwp_text_input_v1* text_input, uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_keysym(host->resource, serial, time, sym, state, modifiers); } static void sl_text_input_language(void* data, struct zwp_text_input_v1* text_input, uint32_t serial, const char* language) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_language(host->resource, serial, language); } static void sl_text_input_text_direction(void* data, struct zwp_text_input_v1* text_input, uint32_t serial, uint32_t direction) { struct sl_host_text_input* host = static_cast( zwp_text_input_v1_get_user_data(text_input)); zwp_text_input_v1_send_text_direction(host->resource, serial, direction); } static const struct zwp_text_input_v1_listener sl_text_input_listener = { sl_text_input_enter, sl_text_input_leave, sl_text_input_modifiers_map, sl_text_input_input_panel_state, sl_text_input_preedit_string, sl_text_input_preedit_styling, sl_text_input_preedit_cursor, sl_text_input_commit_string, sl_text_input_cursor_position, sl_text_input_delete_surrounding_text, sl_text_input_keysym, sl_text_input_language, sl_text_input_text_direction, }; static void sl_destroy_host_text_input(struct wl_resource* resource) { struct sl_host_text_input* host = static_cast(wl_resource_get_user_data(resource)); zwp_text_input_v1_destroy(host->proxy); wl_resource_set_user_data(resource, NULL); delete host; } static void sl_text_input_manager_create_text_input( struct wl_client* client, struct wl_resource* resource, uint32_t id) { struct sl_host_text_input_manager* host = static_cast( wl_resource_get_user_data(resource)); struct wl_resource* text_input_resource = wl_resource_create(client, &zwp_text_input_v1_interface, wl_resource_get_version(resource), id); struct sl_host_text_input* text_input_host = new sl_host_text_input(); text_input_host->resource = text_input_resource; text_input_host->ctx = host->ctx; text_input_host->proxy = zwp_text_input_manager_v1_create_text_input(host->proxy); wl_resource_set_implementation(text_input_resource, &sl_text_input_implementation, text_input_host, sl_destroy_host_text_input); zwp_text_input_v1_add_listener(text_input_host->proxy, &sl_text_input_listener, text_input_host); } static void sl_destroy_host_text_input_manager(struct wl_resource* resource) { struct sl_host_text_input_manager* host = static_cast( wl_resource_get_user_data(resource)); zwp_text_input_manager_v1_destroy(host->proxy); wl_resource_set_user_data(resource, NULL); delete host; } static struct zwp_text_input_manager_v1_interface sl_text_input_manager_implementation = { sl_text_input_manager_create_text_input, }; static void sl_bind_host_text_input_manager(struct wl_client* client, void* data, uint32_t version, uint32_t id) { struct sl_context* ctx = (struct sl_context*)data; struct sl_text_input_manager* text_input_manager = ctx->text_input_manager; struct sl_host_text_input_manager* host = new sl_host_text_input_manager(); host->ctx = ctx; host->resource = wl_resource_create(client, &zwp_text_input_manager_v1_interface, std::min(version, kTextInputManagerVersion), id); wl_resource_set_implementation(host->resource, &sl_text_input_manager_implementation, host, sl_destroy_host_text_input_manager); host->proxy = static_cast(wl_registry_bind( wl_display_get_registry(ctx->display), text_input_manager->id, &zwp_text_input_manager_v1_interface, wl_resource_get_version(host->resource))); zwp_text_input_manager_v1_set_user_data(host->proxy, host); } struct sl_global* sl_text_input_manager_global_create(struct sl_context* ctx) { return sl_global_create(ctx, &zwp_text_input_manager_v1_interface, kTextInputManagerVersion, ctx, sl_bind_host_text_input_manager); } static void sl_extended_text_input_destroy(struct wl_client* client, struct wl_resource* resource) { wl_resource_destroy(resource); } static const struct zcr_extended_text_input_v1_interface sl_extended_text_input_implementation = { sl_extended_text_input_destroy, ForwardRequest, ForwardRequest< zcr_extended_text_input_v1_set_grammar_fragment_at_cursor>, ForwardRequest, }; static void sl_extended_text_input_set_preedit_region( void* data, struct zcr_extended_text_input_v1* extended_text_input, int32_t index, uint32_t length) { struct sl_host_extended_text_input* host = static_cast( zcr_extended_text_input_v1_get_user_data(extended_text_input)); zcr_extended_text_input_v1_send_set_preedit_region(host->resource, index, length); } static void sl_extended_text_input_clear_grammar_fragments( void* data, struct zcr_extended_text_input_v1* extended_text_input, uint32_t start, uint32_t end) { struct sl_host_extended_text_input* host = static_cast( zcr_extended_text_input_v1_get_user_data(extended_text_input)); zcr_extended_text_input_v1_send_clear_grammar_fragments(host->resource, start, end); } static void sl_extended_text_input_add_grammar_fragment( void* data, struct zcr_extended_text_input_v1* extended_text_input, uint32_t start, uint32_t end, const char* suggestion) { struct sl_host_extended_text_input* host = static_cast( zcr_extended_text_input_v1_get_user_data(extended_text_input)); zcr_extended_text_input_v1_send_add_grammar_fragment(host->resource, start, end, suggestion); } static void sl_extended_text_input_set_autocorrect_range( void* data, struct zcr_extended_text_input_v1* extended_text_input, uint32_t start, uint32_t end) { struct sl_host_extended_text_input* host = static_cast( zcr_extended_text_input_v1_get_user_data(extended_text_input)); zcr_extended_text_input_v1_send_set_autocorrect_range(host->resource, start, end); } static const struct zcr_extended_text_input_v1_listener sl_extended_text_input_listener = { sl_extended_text_input_set_preedit_region, sl_extended_text_input_clear_grammar_fragments, sl_extended_text_input_add_grammar_fragment, sl_extended_text_input_set_autocorrect_range, }; static void sl_destroy_host_extended_text_input(struct wl_resource* resource) { struct sl_host_extended_text_input* host = static_cast( wl_resource_get_user_data(resource)); zcr_extended_text_input_v1_destroy(host->proxy); wl_resource_set_user_data(resource, NULL); delete host; } static void sl_text_input_extension_get_extended_text_input( struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* text_input) { struct sl_host_text_input_extension* host = static_cast( wl_resource_get_user_data(resource)); struct sl_host_text_input* host_text_input = static_cast(wl_resource_get_user_data(text_input)); struct wl_resource* extended_text_input_resource = wl_resource_create(client, &zcr_extended_text_input_v1_interface, wl_resource_get_version(resource), id); struct sl_host_extended_text_input* extended_text_input_host = new sl_host_extended_text_input(); extended_text_input_host->resource = extended_text_input_resource; extended_text_input_host->ctx = host->ctx; extended_text_input_host->proxy = zcr_text_input_extension_v1_get_extended_text_input( host->proxy, host_text_input->proxy); wl_resource_set_implementation( extended_text_input_resource, &sl_extended_text_input_implementation, extended_text_input_host, sl_destroy_host_extended_text_input); zcr_extended_text_input_v1_add_listener(extended_text_input_host->proxy, &sl_extended_text_input_listener, extended_text_input_host); } // NOLINT(whitespace/indent) static void sl_destroy_host_text_input_extension(struct wl_resource* resource) { struct sl_host_text_input_extension* host = static_cast( wl_resource_get_user_data(resource)); zcr_text_input_extension_v1_destroy(host->proxy); wl_resource_set_user_data(resource, NULL); delete host; } static struct zcr_text_input_extension_v1_interface sl_text_input_extension_implementation = { sl_text_input_extension_get_extended_text_input, }; static void sl_bind_host_text_input_extension(struct wl_client* client, void* data, uint32_t version, uint32_t id) { struct sl_context* ctx = (struct sl_context*)data; struct sl_text_input_extension* text_input_extension = ctx->text_input_extension; struct sl_host_text_input_extension* host = new sl_host_text_input_extension(); host->ctx = ctx; host->resource = wl_resource_create(client, &zcr_text_input_extension_v1_interface, std::min(version, kTextInputExtensionVersion), id); wl_resource_set_implementation(host->resource, &sl_text_input_extension_implementation, host, sl_destroy_host_text_input_extension); host->proxy = static_cast(wl_registry_bind( wl_display_get_registry(ctx->display), text_input_extension->id, &zcr_text_input_extension_v1_interface, wl_resource_get_version(host->resource))); zcr_text_input_extension_v1_set_user_data(host->proxy, host); } struct sl_global* sl_text_input_extension_global_create( struct sl_context* ctx) { return sl_global_create(ctx, &zcr_text_input_extension_v1_interface, kTextInputExtensionVersion, ctx, sl_bind_host_text_input_extension); } static void sl_text_input_x11_activate(wl_client* client, wl_resource* resource, wl_resource* text_input, wl_resource* seat, uint32_t x11_window_id) { struct sl_host_text_input* host_text_input = static_cast(wl_resource_get_user_data(text_input)); struct sl_host_seat* host_seat = static_cast(wl_resource_get_user_data(seat)); assert(host_text_input); assert(host_seat); struct sl_context* ctx = host_text_input->ctx; struct sl_window* window; wl_list_for_each(window, &ctx->windows, link) { if (window->id != x11_window_id) continue; if (!window->host_surface_id) return; struct wl_resource* host_window_resource = wl_client_get_object(ctx->client, window->host_surface_id); if (!host_window_resource) return; sl_host_surface* host_surface = static_cast( wl_resource_get_user_data(host_window_resource)); host_text_input->active_surface = host_surface; zwp_text_input_v1_activate(host_text_input->proxy, host_seat->proxy, host_surface->proxy); return; } } static const struct zcr_text_input_x11_v1_interface sl_text_input_x11_implementation = { sl_text_input_x11_activate, }; static void sl_bind_host_text_input_x11(struct wl_client* client, void* data, uint32_t version, uint32_t id) { // This exists only between sommelier and its clients and there is no proxy // to the host. For simplicity we don't use a sl_host_text_input_x11 // type as it is not needed. wl_resource* resource = wl_resource_create(client, &zcr_text_input_x11_v1_interface, std::min(version, kTextInputX11Version), id); wl_resource_set_implementation(resource, &sl_text_input_x11_implementation, nullptr, nullptr); } struct sl_global* sl_text_input_x11_global_create(struct sl_context* ctx) { return sl_global_create(ctx, &zcr_text_input_x11_v1_interface, kTextInputX11Version, ctx, sl_bind_host_text_input_x11); }