weston/src/text-backend.c
Rob Bradford ead3ef8c77 text-backend: remove the weston_seat destruction listener on destroy
Prior to freeing the memory in which the link node for the signal is
emedded we should remove the link node from the list to prevent the list
from being corrupted.

https://bugs.freedesktop.org/show_bug.cgi?id=67231
2013-07-29 16:36:58 -07:00

961 lines
27 KiB
C

/*
* Copyright © 2012 Openismus GmbH
* Copyright © 2012 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "compositor.h"
#include "text-server-protocol.h"
#include "input-method-server-protocol.h"
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;
uint32_t input_panel_visible;
};
struct text_input_manager {
struct wl_global *text_input_manager_global;
struct wl_listener destroy_listener;
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 *model;
struct wl_list link;
struct wl_listener keyboard_focus_listener;
int focus_listener_initialized;
struct input_method_context *context;
struct text_backend *text_backend;
};
struct input_method_context {
struct wl_resource *resource;
struct text_input *model;
struct input_method *input_method;
struct wl_list link;
struct wl_resource *keyboard;
};
struct text_backend {
struct weston_compositor *compositor;
struct {
char *path;
struct wl_resource *binding;
struct weston_process process;
struct wl_client *client;
unsigned deathcount;
uint32_t deathstamp;
} input_method;
struct wl_listener seat_created_listener;
struct wl_listener destroy_listener;
};
static void input_method_context_create(struct text_input *model,
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_text_input(struct text_input *text_input,
struct input_method *input_method)
{
struct weston_compositor *ec = text_input->ec;
if (input_method->model == text_input) {
if (input_method->context && input_method->input_method_binding) {
input_method_context_end_keyboard_grab(input_method->context);
wl_input_method_send_deactivate(input_method->input_method_binding,
input_method->context->resource);
}
wl_list_remove(&input_method->link);
input_method->model = NULL;
input_method->context = NULL;
wl_signal_emit(&ec->hide_input_panel_signal, ec);
wl_text_input_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_text_input(text_input, 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;
wl_input_method_context_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 = weston_seat->input_method;
struct text_input *old = weston_seat->input_method->model;
struct weston_compositor *ec = text_input->ec;
if (old == text_input)
return;
if (old) {
deactivate_text_input(old,
weston_seat->input_method);
}
input_method->model = 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);
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);
}
wl_text_input_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 text_input *text_input = wl_resource_get_user_data(resource);
struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
deactivate_text_input(text_input,
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;
wl_input_method_context_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;
wl_input_method_context_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;
wl_input_method_context_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;
wl_input_method_context_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 = 1;
if (!wl_list_empty(&text_input->input_methods)) {
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 = 0;
if (!wl_list_empty(&text_input->input_methods))
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;
wl_input_method_context_send_preferred_language(input_method->context->resource,
language);
}
}
static const struct wl_text_input_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 = calloc(1, sizeof *text_input);
text_input->resource =
wl_resource_create(client, &wl_text_input_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;
wl_list_init(&text_input->input_methods);
};
static const struct wl_text_input_manager_interface text_input_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,
&wl_text_input_manager_interface, 1, id);
if (resource)
wl_resource_set_implementation(resource,
&text_input_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_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 = calloc(1, sizeof *text_input_manager);
text_input_manager->ec = ec;
text_input_manager->text_input_manager_global =
wl_global_create(ec->wl_display,
&wl_text_input_manager_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);
wl_text_input_send_commit_string(context->model->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);
wl_text_input_send_preedit_string(context->model->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);
wl_text_input_send_preedit_styling(context->model->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);
wl_text_input_send_preedit_cursor(context->model->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);
wl_text_input_send_delete_surrounding_text(context->model->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);
wl_text_input_send_cursor_position(context->model->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);
wl_text_input_send_modifiers_map(context->model->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);
wl_text_input_send_keysym(context->model->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,
uint32_t time, uint32_t key, uint32_t state_w)
{
struct weston_keyboard *keyboard = grab->keyboard;
struct wl_display *display;
uint32_t serial;
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);
wl_keyboard_send_key(keyboard->input_method_resource,
serial, time, 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 const struct weston_keyboard_grab_interface input_method_context_grab = {
input_method_context_grab_key,
input_method_context_grab_modifier,
};
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 = seat->keyboard;
cr = wl_resource_create(client, &wl_keyboard_interface, 1, id);
wl_resource_set_implementation(cr, NULL, context, unbind_keyboard);
context->keyboard = cr;
wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
seat->xkb_info.keymap_fd,
seat->xkb_info.keymap_size);
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 = seat->keyboard;
struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
default_grab->interface->key(default_grab, time, 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 = seat->keyboard;
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);
wl_text_input_send_language(context->model->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);
wl_text_input_send_text_direction(context->model->resource, serial, direction);
}
static const struct wl_input_method_context_interface input_method_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);
}
free(context);
}
static void
input_method_context_create(struct text_input *model,
struct input_method *input_method)
{
struct input_method_context *context;
struct wl_resource *binding;
if (!input_method->input_method_binding)
return;
context = calloc(1, sizeof *context);
if (context == NULL)
return;
binding = input_method->input_method_binding;
context->resource =
wl_resource_create(wl_resource_get_client(binding),
&wl_input_method_context_interface, 1, 0);
wl_resource_set_implementation(context->resource,
&input_method_context_implementation,
context, destroy_input_method_context);
context->model = model;
context->input_method = input_method;
input_method->context = context;
wl_input_method_send_activate(binding, context->resource);
}
static void
input_method_context_end_keyboard_grab(struct input_method_context *context)
{
struct weston_keyboard_grab *grab =
&context->input_method->seat->keyboard->input_method_grab;
struct weston_keyboard *keyboard = grab->keyboard;
if (!grab->keyboard)
return;
if (grab->keyboard->grab == grab)
weston_keyboard_end_grab(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);
struct text_backend *text_backend = input_method->text_backend;
input_method->input_method_binding = NULL;
input_method->context = NULL;
text_backend->input_method.binding = 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, &wl_input_method_interface, 1, id);
if (input_method->input_method_binding != NULL) {
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"interface object already bound");
wl_resource_destroy(resource);
return;
}
if (text_backend->input_method.client != client) {
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"permission to bind input_method denied");
wl_resource_destroy(resource);
return;
}
wl_resource_set_implementation(resource, NULL, input_method,
unbind_input_method);
input_method->input_method_binding = resource;
text_backend->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->model)
deactivate_text_input(input_method->model, 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->model)
return;
if (!surface || input_method->model->surface != surface)
deactivate_text_input(input_method->model,
input_method);
}
static void
input_method_init_seat(struct weston_seat *seat)
{
if (seat->input_method->focus_listener_initialized)
return;
if (seat->keyboard) {
seat->input_method->keyboard_focus_listener.notify = handle_keyboard_focus;
wl_signal_add(&seat->keyboard->focus_signal, &seat->input_method->keyboard_focus_listener);
seat->keyboard->input_method_grab.interface = &input_method_context_grab;
}
seat->input_method->focus_listener_initialized = 1;
}
static void launch_input_method(struct text_backend *text_backend);
static void
handle_input_method_sigchld(struct weston_process *process, int status)
{
uint32_t time;
struct text_backend *text_backend =
container_of(process, struct text_backend, input_method.process);
text_backend->input_method.process.pid = 0;
text_backend->input_method.client = NULL;
/* if input_method dies more than 5 times in 10 seconds, give up */
time = weston_compositor_get_time();
if (time - text_backend->input_method.deathstamp > 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 died, giving up.\n");
return;
}
weston_log("input_method died, respawning...\n");
launch_input_method(text_backend);
}
static void
launch_input_method(struct text_backend *text_backend)
{
if (text_backend->input_method.binding)
return;
if (!text_backend->input_method.path)
return;
if (text_backend->input_method.process.pid != 0)
return;
text_backend->input_method.client = weston_client_launch(text_backend->compositor,
&text_backend->input_method.process,
text_backend->input_method.path,
handle_input_method_sigchld);
if (!text_backend->input_method.client)
weston_log("not able to start %s\n", text_backend->input_method.path);
}
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);
struct input_method *input_method;
struct weston_compositor *ec = seat->compositor;
input_method = calloc(1, sizeof *input_method);
input_method->seat = seat;
input_method->model = NULL;
input_method->focus_listener_initialized = 0;
input_method->context = NULL;
input_method->text_backend = text_backend;
input_method->input_method_global =
wl_global_create(ec->wl_display, &wl_input_method_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;
launch_input_method(text_backend);
}
static void
text_backend_configuration(struct text_backend *text_backend)
{
struct weston_config_section *section;
section = weston_config_get_section(text_backend->compositor->config,
"input-method", NULL, NULL);
weston_config_section_get_string(section, "path",
&text_backend->input_method.path,
LIBEXECDIR "/weston-keyboard");
}
static void
text_backend_notifier_destroy(struct wl_listener *listener, void *data)
{
struct text_backend *text_backend =
container_of(listener, struct text_backend, destroy_listener);
if (text_backend->input_method.client)
wl_client_destroy(text_backend->input_method.client);
free(text_backend->input_method.path);
free(text_backend);
}
WL_EXPORT int
text_backend_init(struct weston_compositor *ec)
{
struct text_backend *text_backend;
text_backend = calloc(1, sizeof(*text_backend));
text_backend->compositor = ec;
text_backend->seat_created_listener.notify = handle_seat_created;
wl_signal_add(&ec->seat_created_signal,
&text_backend->seat_created_listener);
text_backend->destroy_listener.notify = text_backend_notifier_destroy;
wl_signal_add(&ec->destroy_signal, &text_backend->destroy_listener);
text_backend_configuration(text_backend);
text_input_manager_create(ec);
return 0;
}