3d5d9476e3
The main idea is to make libweston users use the form #include <libweston/libweston.h> instead of the plain #include <compositor.h> which is prone to name conflicts. This is reflected both in the installed files, and the internal header search paths so that Weston would use the exact same form as an external project using libweston would. The public headers are moved under a new top-level directory include/ to make them clearly stand out as special (public API). Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
1100 lines
29 KiB
C
1100 lines
29 KiB
C
/*
|
|
* Copyright © 2012 Openismus GmbH
|
|
* Copyright © 2012 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include <libweston/libweston.h>
|
|
#include "weston.h"
|
|
#include "text-input-unstable-v1-server-protocol.h"
|
|
#include "input-method-unstable-v1-server-protocol.h"
|
|
#include "shared/helpers.h"
|
|
#include "shared/timespec-util.h"
|
|
|
|
struct text_input_manager;
|
|
struct input_method;
|
|
struct input_method_context;
|
|
struct text_backend;
|
|
|
|
struct text_input {
|
|
struct wl_resource *resource;
|
|
|
|
struct weston_compositor *ec;
|
|
|
|
struct wl_list input_methods;
|
|
|
|
struct weston_surface *surface;
|
|
|
|
pixman_box32_t cursor_rectangle;
|
|
|
|
bool input_panel_visible;
|
|
|
|
struct text_input_manager *manager;
|
|
};
|
|
|
|
struct text_input_manager {
|
|
struct wl_global *text_input_manager_global;
|
|
struct wl_listener destroy_listener;
|
|
|
|
struct text_input *current_text_input;
|
|
|
|
struct weston_compositor *ec;
|
|
};
|
|
|
|
struct input_method {
|
|
struct wl_resource *input_method_binding;
|
|
struct wl_global *input_method_global;
|
|
struct wl_listener destroy_listener;
|
|
|
|
struct weston_seat *seat;
|
|
struct text_input *input;
|
|
|
|
struct wl_list link;
|
|
|
|
struct wl_listener keyboard_focus_listener;
|
|
|
|
bool focus_listener_initialized;
|
|
|
|
struct input_method_context *context;
|
|
|
|
struct text_backend *text_backend;
|
|
};
|
|
|
|
struct input_method_context {
|
|
struct wl_resource *resource;
|
|
|
|
struct text_input *input;
|
|
struct input_method *input_method;
|
|
|
|
struct wl_resource *keyboard;
|
|
};
|
|
|
|
struct text_backend {
|
|
struct weston_compositor *compositor;
|
|
|
|
struct {
|
|
char *path;
|
|
struct wl_client *client;
|
|
|
|
unsigned deathcount;
|
|
struct timespec deathstamp;
|
|
} input_method;
|
|
|
|
struct wl_listener client_listener;
|
|
struct wl_listener seat_created_listener;
|
|
};
|
|
|
|
static void
|
|
input_method_context_create(struct text_input *input,
|
|
struct input_method *input_method);
|
|
static void
|
|
input_method_context_end_keyboard_grab(struct input_method_context *context);
|
|
|
|
static void
|
|
input_method_init_seat(struct weston_seat *seat);
|
|
|
|
static void
|
|
deactivate_input_method(struct input_method *input_method)
|
|
{
|
|
struct text_input *text_input = input_method->input;
|
|
struct weston_compositor *ec = text_input->ec;
|
|
|
|
if (input_method->context && input_method->input_method_binding) {
|
|
input_method_context_end_keyboard_grab(input_method->context);
|
|
zwp_input_method_v1_send_deactivate(
|
|
input_method->input_method_binding,
|
|
input_method->context->resource);
|
|
input_method->context->input = NULL;
|
|
}
|
|
|
|
wl_list_remove(&input_method->link);
|
|
input_method->input = NULL;
|
|
input_method->context = NULL;
|
|
|
|
if (wl_list_empty(&text_input->input_methods) &&
|
|
text_input->input_panel_visible &&
|
|
text_input->manager->current_text_input == text_input) {
|
|
wl_signal_emit(&ec->hide_input_panel_signal, ec);
|
|
text_input->input_panel_visible = false;
|
|
}
|
|
|
|
if (text_input->manager->current_text_input == text_input)
|
|
text_input->manager->current_text_input = NULL;
|
|
|
|
zwp_text_input_v1_send_leave(text_input->resource);
|
|
}
|
|
|
|
static void
|
|
destroy_text_input(struct wl_resource *resource)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link)
|
|
deactivate_input_method(input_method);
|
|
|
|
free(text_input);
|
|
}
|
|
|
|
static void
|
|
text_input_set_surrounding_text(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
const char *text,
|
|
uint32_t cursor,
|
|
uint32_t anchor)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link) {
|
|
if (!input_method->context)
|
|
continue;
|
|
zwp_input_method_context_v1_send_surrounding_text(
|
|
input_method->context->resource, text, cursor, anchor);
|
|
}
|
|
}
|
|
|
|
static void
|
|
text_input_activate(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_resource *seat,
|
|
struct wl_resource *surface)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
|
|
struct input_method *input_method;
|
|
struct weston_compositor *ec = text_input->ec;
|
|
struct text_input *current;
|
|
|
|
if (!weston_seat)
|
|
return;
|
|
|
|
input_method = weston_seat->input_method;
|
|
if (input_method->input == text_input)
|
|
return;
|
|
|
|
if (input_method->input)
|
|
deactivate_input_method(input_method);
|
|
|
|
input_method->input = text_input;
|
|
wl_list_insert(&text_input->input_methods, &input_method->link);
|
|
input_method_init_seat(weston_seat);
|
|
|
|
text_input->surface = wl_resource_get_user_data(surface);
|
|
|
|
input_method_context_create(text_input, input_method);
|
|
|
|
current = text_input->manager->current_text_input;
|
|
|
|
if (current && current != text_input) {
|
|
current->input_panel_visible = false;
|
|
wl_signal_emit(&ec->hide_input_panel_signal, ec);
|
|
}
|
|
|
|
if (text_input->input_panel_visible) {
|
|
wl_signal_emit(&ec->show_input_panel_signal,
|
|
text_input->surface);
|
|
wl_signal_emit(&ec->update_input_panel_signal,
|
|
&text_input->cursor_rectangle);
|
|
}
|
|
text_input->manager->current_text_input = text_input;
|
|
|
|
zwp_text_input_v1_send_enter(text_input->resource,
|
|
text_input->surface->resource);
|
|
}
|
|
|
|
static void
|
|
text_input_deactivate(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_resource *seat)
|
|
{
|
|
struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
|
|
|
|
if (weston_seat && weston_seat->input_method->input)
|
|
deactivate_input_method(weston_seat->input_method);
|
|
}
|
|
|
|
static void
|
|
text_input_reset(struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link) {
|
|
if (!input_method->context)
|
|
continue;
|
|
zwp_input_method_context_v1_send_reset(
|
|
input_method->context->resource);
|
|
}
|
|
}
|
|
|
|
static void
|
|
text_input_set_cursor_rectangle(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t x,
|
|
int32_t y,
|
|
int32_t width,
|
|
int32_t height)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct weston_compositor *ec = text_input->ec;
|
|
|
|
text_input->cursor_rectangle.x1 = x;
|
|
text_input->cursor_rectangle.y1 = y;
|
|
text_input->cursor_rectangle.x2 = x + width;
|
|
text_input->cursor_rectangle.y2 = y + height;
|
|
|
|
wl_signal_emit(&ec->update_input_panel_signal,
|
|
&text_input->cursor_rectangle);
|
|
}
|
|
|
|
static void
|
|
text_input_set_content_type(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t hint,
|
|
uint32_t purpose)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link) {
|
|
if (!input_method->context)
|
|
continue;
|
|
zwp_input_method_context_v1_send_content_type(
|
|
input_method->context->resource, hint, purpose);
|
|
}
|
|
}
|
|
|
|
static void
|
|
text_input_invoke_action(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t button,
|
|
uint32_t index)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link) {
|
|
if (!input_method->context)
|
|
continue;
|
|
zwp_input_method_context_v1_send_invoke_action(
|
|
input_method->context->resource, button, index);
|
|
}
|
|
}
|
|
|
|
static void
|
|
text_input_commit_state(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link) {
|
|
if (!input_method->context)
|
|
continue;
|
|
zwp_input_method_context_v1_send_commit_state(
|
|
input_method->context->resource, serial);
|
|
}
|
|
}
|
|
|
|
static void
|
|
text_input_show_input_panel(struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct weston_compositor *ec = text_input->ec;
|
|
|
|
text_input->input_panel_visible = true;
|
|
|
|
if (!wl_list_empty(&text_input->input_methods) &&
|
|
text_input == text_input->manager->current_text_input) {
|
|
wl_signal_emit(&ec->show_input_panel_signal,
|
|
text_input->surface);
|
|
wl_signal_emit(&ec->update_input_panel_signal,
|
|
&text_input->cursor_rectangle);
|
|
}
|
|
}
|
|
|
|
static void
|
|
text_input_hide_input_panel(struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct weston_compositor *ec = text_input->ec;
|
|
|
|
text_input->input_panel_visible = false;
|
|
|
|
if (!wl_list_empty(&text_input->input_methods) &&
|
|
text_input == text_input->manager->current_text_input)
|
|
wl_signal_emit(&ec->hide_input_panel_signal, ec);
|
|
}
|
|
|
|
static void
|
|
text_input_set_preferred_language(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
const char *language)
|
|
{
|
|
struct text_input *text_input = wl_resource_get_user_data(resource);
|
|
struct input_method *input_method, *next;
|
|
|
|
wl_list_for_each_safe(input_method, next,
|
|
&text_input->input_methods, link) {
|
|
if (!input_method->context)
|
|
continue;
|
|
zwp_input_method_context_v1_send_preferred_language(
|
|
input_method->context->resource, language);
|
|
}
|
|
}
|
|
|
|
static const struct zwp_text_input_v1_interface text_input_implementation = {
|
|
text_input_activate,
|
|
text_input_deactivate,
|
|
text_input_show_input_panel,
|
|
text_input_hide_input_panel,
|
|
text_input_reset,
|
|
text_input_set_surrounding_text,
|
|
text_input_set_content_type,
|
|
text_input_set_cursor_rectangle,
|
|
text_input_set_preferred_language,
|
|
text_input_commit_state,
|
|
text_input_invoke_action
|
|
};
|
|
|
|
static void text_input_manager_create_text_input(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t id)
|
|
{
|
|
struct text_input_manager *text_input_manager =
|
|
wl_resource_get_user_data(resource);
|
|
struct text_input *text_input;
|
|
|
|
text_input = zalloc(sizeof *text_input);
|
|
if (text_input == NULL)
|
|
return;
|
|
|
|
text_input->resource =
|
|
wl_resource_create(client, &zwp_text_input_v1_interface, 1, id);
|
|
wl_resource_set_implementation(text_input->resource,
|
|
&text_input_implementation,
|
|
text_input, destroy_text_input);
|
|
|
|
text_input->ec = text_input_manager->ec;
|
|
text_input->manager = text_input_manager;
|
|
|
|
wl_list_init(&text_input->input_methods);
|
|
};
|
|
|
|
static const struct zwp_text_input_manager_v1_interface manager_implementation = {
|
|
text_input_manager_create_text_input
|
|
};
|
|
|
|
static void
|
|
bind_text_input_manager(struct wl_client *client,
|
|
void *data,
|
|
uint32_t version,
|
|
uint32_t id)
|
|
{
|
|
struct text_input_manager *text_input_manager = data;
|
|
struct wl_resource *resource;
|
|
|
|
/* No checking for duplicate binding necessary. */
|
|
resource =
|
|
wl_resource_create(client,
|
|
&zwp_text_input_manager_v1_interface, 1, id);
|
|
if (resource)
|
|
wl_resource_set_implementation(resource,
|
|
&manager_implementation,
|
|
text_input_manager, NULL);
|
|
}
|
|
|
|
static void
|
|
text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
|
|
{
|
|
struct text_input_manager *text_input_manager =
|
|
container_of(listener,
|
|
struct text_input_manager,
|
|
destroy_listener);
|
|
|
|
wl_list_remove(&text_input_manager->destroy_listener.link);
|
|
wl_global_destroy(text_input_manager->text_input_manager_global);
|
|
|
|
free(text_input_manager);
|
|
}
|
|
|
|
static void
|
|
text_input_manager_create(struct weston_compositor *ec)
|
|
{
|
|
struct text_input_manager *text_input_manager;
|
|
|
|
text_input_manager = zalloc(sizeof *text_input_manager);
|
|
if (text_input_manager == NULL)
|
|
return;
|
|
|
|
text_input_manager->ec = ec;
|
|
|
|
text_input_manager->text_input_manager_global =
|
|
wl_global_create(ec->wl_display,
|
|
&zwp_text_input_manager_v1_interface, 1,
|
|
text_input_manager, bind_text_input_manager);
|
|
|
|
text_input_manager->destroy_listener.notify =
|
|
text_input_manager_notifier_destroy;
|
|
wl_signal_add(&ec->destroy_signal,
|
|
&text_input_manager->destroy_listener);
|
|
}
|
|
|
|
static void
|
|
input_method_context_destroy(struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy(resource);
|
|
}
|
|
|
|
static void
|
|
input_method_context_commit_string(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
const char *text)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_commit_string(context->input->resource,
|
|
serial, text);
|
|
}
|
|
|
|
static void
|
|
input_method_context_preedit_string(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
const char *text,
|
|
const char *commit)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_preedit_string(context->input->resource,
|
|
serial, text, commit);
|
|
}
|
|
|
|
static void
|
|
input_method_context_preedit_styling(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t index,
|
|
uint32_t length,
|
|
uint32_t style)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_preedit_styling(context->input->resource,
|
|
index, length, style);
|
|
}
|
|
|
|
static void
|
|
input_method_context_preedit_cursor(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t cursor)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_preedit_cursor(context->input->resource,
|
|
cursor);
|
|
}
|
|
|
|
static void
|
|
input_method_context_delete_surrounding_text(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t index,
|
|
uint32_t length)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_delete_surrounding_text(
|
|
context->input->resource, index, length);
|
|
}
|
|
|
|
static void
|
|
input_method_context_cursor_position(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t index,
|
|
int32_t anchor)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_cursor_position(context->input->resource,
|
|
index, anchor);
|
|
}
|
|
|
|
static void
|
|
input_method_context_modifiers_map(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_array *map)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_modifiers_map(context->input->resource,
|
|
map);
|
|
}
|
|
|
|
static void
|
|
input_method_context_keysym(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
uint32_t time,
|
|
uint32_t sym,
|
|
uint32_t state,
|
|
uint32_t modifiers)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_keysym(context->input->resource,
|
|
serial, time,
|
|
sym, state, modifiers);
|
|
}
|
|
|
|
static void
|
|
unbind_keyboard(struct wl_resource *resource)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
input_method_context_end_keyboard_grab(context);
|
|
context->keyboard = NULL;
|
|
}
|
|
|
|
static void
|
|
input_method_context_grab_key(struct weston_keyboard_grab *grab,
|
|
const struct timespec *time, uint32_t key,
|
|
uint32_t state_w)
|
|
{
|
|
struct weston_keyboard *keyboard = grab->keyboard;
|
|
struct wl_display *display;
|
|
uint32_t serial;
|
|
uint32_t msecs;
|
|
|
|
if (!keyboard->input_method_resource)
|
|
return;
|
|
|
|
display = wl_client_get_display(
|
|
wl_resource_get_client(keyboard->input_method_resource));
|
|
serial = wl_display_next_serial(display);
|
|
msecs = timespec_to_msec(time);
|
|
wl_keyboard_send_key(keyboard->input_method_resource,
|
|
serial, msecs, key, state_w);
|
|
}
|
|
|
|
static void
|
|
input_method_context_grab_modifier(struct weston_keyboard_grab *grab,
|
|
uint32_t serial,
|
|
uint32_t mods_depressed,
|
|
uint32_t mods_latched,
|
|
uint32_t mods_locked,
|
|
uint32_t group)
|
|
{
|
|
struct weston_keyboard *keyboard = grab->keyboard;
|
|
|
|
if (!keyboard->input_method_resource)
|
|
return;
|
|
|
|
wl_keyboard_send_modifiers(keyboard->input_method_resource,
|
|
serial, mods_depressed, mods_latched,
|
|
mods_locked, group);
|
|
}
|
|
|
|
static void
|
|
input_method_context_grab_cancel(struct weston_keyboard_grab *grab)
|
|
{
|
|
weston_keyboard_end_grab(grab->keyboard);
|
|
}
|
|
|
|
static const struct weston_keyboard_grab_interface input_method_context_grab = {
|
|
input_method_context_grab_key,
|
|
input_method_context_grab_modifier,
|
|
input_method_context_grab_cancel,
|
|
};
|
|
|
|
static void
|
|
input_method_context_grab_keyboard(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t id)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
struct wl_resource *cr;
|
|
struct weston_seat *seat = context->input_method->seat;
|
|
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
|
|
|
cr = wl_resource_create(client, &wl_keyboard_interface, 1, id);
|
|
wl_resource_set_implementation(cr, NULL, context, unbind_keyboard);
|
|
|
|
context->keyboard = cr;
|
|
|
|
weston_keyboard_send_keymap(keyboard, cr);
|
|
|
|
if (keyboard->grab != &keyboard->default_grab) {
|
|
weston_keyboard_end_grab(keyboard);
|
|
}
|
|
weston_keyboard_start_grab(keyboard, &keyboard->input_method_grab);
|
|
keyboard->input_method_resource = cr;
|
|
}
|
|
|
|
static void
|
|
input_method_context_key(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
uint32_t time,
|
|
uint32_t key,
|
|
uint32_t state_w)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
struct weston_seat *seat = context->input_method->seat;
|
|
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
|
struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
|
|
struct timespec ts;
|
|
|
|
timespec_from_msec(&ts, time);
|
|
|
|
default_grab->interface->key(default_grab, &ts, key, state_w);
|
|
}
|
|
|
|
static void
|
|
input_method_context_modifiers(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
uint32_t mods_depressed,
|
|
uint32_t mods_latched,
|
|
uint32_t mods_locked,
|
|
uint32_t group)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
struct weston_seat *seat = context->input_method->seat;
|
|
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
|
struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
|
|
|
|
default_grab->interface->modifiers(default_grab,
|
|
serial, mods_depressed,
|
|
mods_latched, mods_locked,
|
|
group);
|
|
}
|
|
|
|
static void
|
|
input_method_context_language(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
const char *language)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_language(context->input->resource,
|
|
serial, language);
|
|
}
|
|
|
|
static void
|
|
input_method_context_text_direction(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t serial,
|
|
uint32_t direction)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->input)
|
|
zwp_text_input_v1_send_text_direction(context->input->resource,
|
|
serial, direction);
|
|
}
|
|
|
|
|
|
static const struct zwp_input_method_context_v1_interface context_implementation = {
|
|
input_method_context_destroy,
|
|
input_method_context_commit_string,
|
|
input_method_context_preedit_string,
|
|
input_method_context_preedit_styling,
|
|
input_method_context_preedit_cursor,
|
|
input_method_context_delete_surrounding_text,
|
|
input_method_context_cursor_position,
|
|
input_method_context_modifiers_map,
|
|
input_method_context_keysym,
|
|
input_method_context_grab_keyboard,
|
|
input_method_context_key,
|
|
input_method_context_modifiers,
|
|
input_method_context_language,
|
|
input_method_context_text_direction
|
|
};
|
|
|
|
static void
|
|
destroy_input_method_context(struct wl_resource *resource)
|
|
{
|
|
struct input_method_context *context =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (context->keyboard)
|
|
wl_resource_destroy(context->keyboard);
|
|
|
|
if (context->input_method && context->input_method->context == context)
|
|
context->input_method->context = NULL;
|
|
|
|
free(context);
|
|
}
|
|
|
|
static void
|
|
input_method_context_create(struct text_input *input,
|
|
struct input_method *input_method)
|
|
{
|
|
struct input_method_context *context;
|
|
struct wl_resource *binding;
|
|
|
|
if (!input_method->input_method_binding)
|
|
return;
|
|
|
|
context = zalloc(sizeof *context);
|
|
if (context == NULL)
|
|
return;
|
|
|
|
binding = input_method->input_method_binding;
|
|
context->resource =
|
|
wl_resource_create(wl_resource_get_client(binding),
|
|
&zwp_input_method_context_v1_interface,
|
|
1, 0);
|
|
wl_resource_set_implementation(context->resource,
|
|
&context_implementation,
|
|
context, destroy_input_method_context);
|
|
|
|
context->input = input;
|
|
context->input_method = input_method;
|
|
input_method->context = context;
|
|
|
|
|
|
zwp_input_method_v1_send_activate(binding, context->resource);
|
|
}
|
|
|
|
static void
|
|
input_method_context_end_keyboard_grab(struct input_method_context *context)
|
|
{
|
|
struct weston_keyboard_grab *grab;
|
|
struct weston_keyboard *keyboard;
|
|
|
|
keyboard = weston_seat_get_keyboard(context->input_method->seat);
|
|
if (!keyboard)
|
|
return;
|
|
|
|
grab = &keyboard->input_method_grab;
|
|
keyboard = grab->keyboard;
|
|
if (!keyboard)
|
|
return;
|
|
|
|
if (keyboard->grab == grab)
|
|
weston_keyboard_end_grab(keyboard);
|
|
|
|
keyboard->input_method_resource = NULL;
|
|
}
|
|
|
|
static void
|
|
unbind_input_method(struct wl_resource *resource)
|
|
{
|
|
struct input_method *input_method = wl_resource_get_user_data(resource);
|
|
|
|
input_method->input_method_binding = NULL;
|
|
input_method->context = NULL;
|
|
}
|
|
|
|
static void
|
|
bind_input_method(struct wl_client *client,
|
|
void *data,
|
|
uint32_t version,
|
|
uint32_t id)
|
|
{
|
|
struct input_method *input_method = data;
|
|
struct text_backend *text_backend = input_method->text_backend;
|
|
struct wl_resource *resource;
|
|
|
|
resource =
|
|
wl_resource_create(client,
|
|
&zwp_input_method_v1_interface, 1, id);
|
|
|
|
if (input_method->input_method_binding != NULL) {
|
|
wl_resource_post_error(resource,
|
|
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
|
"interface object already bound");
|
|
return;
|
|
}
|
|
|
|
if (text_backend->input_method.client != client) {
|
|
wl_resource_post_error(resource,
|
|
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
|
"permission to bind "
|
|
"input_method denied");
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation(resource, NULL, input_method,
|
|
unbind_input_method);
|
|
input_method->input_method_binding = resource;
|
|
}
|
|
|
|
static void
|
|
input_method_notifier_destroy(struct wl_listener *listener, void *data)
|
|
{
|
|
struct input_method *input_method =
|
|
container_of(listener, struct input_method, destroy_listener);
|
|
|
|
if (input_method->input)
|
|
deactivate_input_method(input_method);
|
|
|
|
wl_global_destroy(input_method->input_method_global);
|
|
wl_list_remove(&input_method->destroy_listener.link);
|
|
|
|
free(input_method);
|
|
}
|
|
|
|
static void
|
|
handle_keyboard_focus(struct wl_listener *listener, void *data)
|
|
{
|
|
struct weston_keyboard *keyboard = data;
|
|
struct input_method *input_method =
|
|
container_of(listener, struct input_method,
|
|
keyboard_focus_listener);
|
|
struct weston_surface *surface = keyboard->focus;
|
|
|
|
if (!input_method->input)
|
|
return;
|
|
|
|
if (!surface || input_method->input->surface != surface)
|
|
deactivate_input_method(input_method);
|
|
}
|
|
|
|
static void
|
|
input_method_init_seat(struct weston_seat *seat)
|
|
{
|
|
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
|
|
|
if (seat->input_method->focus_listener_initialized)
|
|
return;
|
|
|
|
if (keyboard) {
|
|
seat->input_method->keyboard_focus_listener.notify =
|
|
handle_keyboard_focus;
|
|
wl_signal_add(&keyboard->focus_signal,
|
|
&seat->input_method->keyboard_focus_listener);
|
|
keyboard->input_method_grab.interface =
|
|
&input_method_context_grab;
|
|
}
|
|
|
|
seat->input_method->focus_listener_initialized = true;
|
|
}
|
|
|
|
static void launch_input_method(struct text_backend *text_backend);
|
|
|
|
static void
|
|
respawn_input_method_process(struct text_backend *text_backend)
|
|
{
|
|
struct timespec time;
|
|
int64_t tdiff;
|
|
|
|
/* if input_method dies more than 5 times in 10 seconds, give up */
|
|
weston_compositor_get_time(&time);
|
|
tdiff = timespec_sub_to_msec(&time,
|
|
&text_backend->input_method.deathstamp);
|
|
if (tdiff > 10000) {
|
|
text_backend->input_method.deathstamp = time;
|
|
text_backend->input_method.deathcount = 0;
|
|
}
|
|
|
|
text_backend->input_method.deathcount++;
|
|
if (text_backend->input_method.deathcount > 5) {
|
|
weston_log("input_method disconnected, giving up.\n");
|
|
return;
|
|
}
|
|
|
|
weston_log("input_method disconnected, respawning...\n");
|
|
launch_input_method(text_backend);
|
|
}
|
|
|
|
static void
|
|
input_method_client_notifier(struct wl_listener *listener, void *data)
|
|
{
|
|
struct text_backend *text_backend;
|
|
|
|
text_backend = container_of(listener, struct text_backend,
|
|
client_listener);
|
|
|
|
text_backend->input_method.client = NULL;
|
|
respawn_input_method_process(text_backend);
|
|
}
|
|
|
|
static void
|
|
launch_input_method(struct text_backend *text_backend)
|
|
{
|
|
if (!text_backend->input_method.path)
|
|
return;
|
|
|
|
if (strcmp(text_backend->input_method.path, "") == 0)
|
|
return;
|
|
|
|
text_backend->input_method.client =
|
|
weston_client_start(text_backend->compositor,
|
|
text_backend->input_method.path);
|
|
|
|
if (!text_backend->input_method.client) {
|
|
weston_log("not able to start %s\n",
|
|
text_backend->input_method.path);
|
|
return;
|
|
}
|
|
|
|
text_backend->client_listener.notify = input_method_client_notifier;
|
|
wl_client_add_destroy_listener(text_backend->input_method.client,
|
|
&text_backend->client_listener);
|
|
}
|
|
|
|
static void
|
|
text_backend_seat_created(struct text_backend *text_backend,
|
|
struct weston_seat *seat)
|
|
{
|
|
struct input_method *input_method;
|
|
struct weston_compositor *ec = seat->compositor;
|
|
|
|
input_method = zalloc(sizeof *input_method);
|
|
if (input_method == NULL)
|
|
return;
|
|
|
|
input_method->seat = seat;
|
|
input_method->input = NULL;
|
|
input_method->focus_listener_initialized = false;
|
|
input_method->context = NULL;
|
|
input_method->text_backend = text_backend;
|
|
|
|
input_method->input_method_global =
|
|
wl_global_create(ec->wl_display,
|
|
&zwp_input_method_v1_interface, 1,
|
|
input_method, bind_input_method);
|
|
|
|
input_method->destroy_listener.notify = input_method_notifier_destroy;
|
|
wl_signal_add(&seat->destroy_signal, &input_method->destroy_listener);
|
|
|
|
seat->input_method = input_method;
|
|
}
|
|
|
|
static void
|
|
handle_seat_created(struct wl_listener *listener, void *data)
|
|
{
|
|
struct weston_seat *seat = data;
|
|
struct text_backend *text_backend =
|
|
container_of(listener, struct text_backend,
|
|
seat_created_listener);
|
|
|
|
text_backend_seat_created(text_backend, seat);
|
|
}
|
|
|
|
static void
|
|
text_backend_configuration(struct text_backend *text_backend)
|
|
{
|
|
struct weston_config *config = wet_get_config(text_backend->compositor);
|
|
struct weston_config_section *section;
|
|
char *client;
|
|
|
|
section = weston_config_get_section(config,
|
|
"input-method", NULL, NULL);
|
|
client = wet_get_libexec_path("weston-keyboard");
|
|
weston_config_section_get_string(section, "path",
|
|
&text_backend->input_method.path,
|
|
client);
|
|
free(client);
|
|
}
|
|
|
|
WL_EXPORT void
|
|
text_backend_destroy(struct text_backend *text_backend)
|
|
{
|
|
wl_list_remove(&text_backend->seat_created_listener.link);
|
|
|
|
if (text_backend->input_method.client) {
|
|
/* disable respawn */
|
|
wl_list_remove(&text_backend->client_listener.link);
|
|
wl_client_destroy(text_backend->input_method.client);
|
|
}
|
|
|
|
free(text_backend->input_method.path);
|
|
free(text_backend);
|
|
}
|
|
|
|
WL_EXPORT struct text_backend *
|
|
text_backend_init(struct weston_compositor *ec)
|
|
{
|
|
struct text_backend *text_backend;
|
|
struct weston_seat *seat;
|
|
|
|
text_backend = zalloc(sizeof(*text_backend));
|
|
if (text_backend == NULL)
|
|
return NULL;
|
|
|
|
text_backend->compositor = ec;
|
|
|
|
text_backend_configuration(text_backend);
|
|
|
|
wl_list_for_each(seat, &ec->seat_list, link)
|
|
text_backend_seat_created(text_backend, seat);
|
|
text_backend->seat_created_listener.notify = handle_seat_created;
|
|
wl_signal_add(&ec->seat_created_signal,
|
|
&text_backend->seat_created_listener);
|
|
|
|
text_input_manager_create(ec);
|
|
|
|
launch_input_method(text_backend);
|
|
|
|
return text_backend;
|
|
}
|