mirror of
https://github.com/brl/mutter.git
synced 2024-11-24 00:50:42 -05:00
4d75de006c
It's possible for a released pointer to have repick / set_focus on it as part of sync_input_focus. When the pointer is actually re-init'd, it will memset 0, which can cause corruption as our destroy listener has already been added. Released devices should be idempotent, so just make sure method calls on them don't have any effect.
621 lines
19 KiB
C
621 lines
19 KiB
C
/*
|
|
* Wayland Support
|
|
*
|
|
* Copyright (C) 2013 Intel Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Copyright © 2010-2011 Intel Corporation
|
|
* Copyright © 2008-2011 Kristian Høgsberg
|
|
* Copyright © 2012 Collabora, Ltd.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* The file is based on src/input.c from Weston */
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include "config.h"
|
|
|
|
#include <glib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
|
|
#include "meta-wayland-private.h"
|
|
|
|
static void meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard);
|
|
static void notify_modifiers (MetaWaylandKeyboard *keyboard, uint32_t serial,
|
|
uint32_t mods_depressed, uint32_t mods_latched,
|
|
uint32_t mods_locked, uint32_t group);
|
|
|
|
static void
|
|
unbind_resource (struct wl_resource *resource)
|
|
{
|
|
wl_list_remove (wl_resource_get_link (resource));
|
|
}
|
|
|
|
static int
|
|
create_anonymous_file (off_t size,
|
|
GError **error)
|
|
{
|
|
static const char template[] = "mutter-shared-XXXXXX";
|
|
char *path;
|
|
int fd, flags;
|
|
|
|
fd = g_file_open_tmp (template, &path, error);
|
|
|
|
if (fd == -1)
|
|
return -1;
|
|
|
|
unlink (path);
|
|
g_free (path);
|
|
|
|
flags = fcntl (fd, F_GETFD);
|
|
if (flags == -1)
|
|
goto err;
|
|
|
|
if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
|
goto err;
|
|
|
|
if (ftruncate (fd, size) < 0)
|
|
goto err;
|
|
|
|
return fd;
|
|
|
|
err:
|
|
g_set_error_literal (error,
|
|
G_FILE_ERROR,
|
|
g_file_error_from_errno (errno),
|
|
strerror (errno));
|
|
close (fd);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
inform_clients_of_new_keymap (MetaWaylandKeyboard *keyboard)
|
|
{
|
|
struct wl_resource *keyboard_resource;
|
|
|
|
wl_resource_for_each (keyboard_resource, &keyboard->resource_list)
|
|
{
|
|
wl_keyboard_send_keymap (keyboard_resource,
|
|
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
|
keyboard->xkb_info.keymap_fd,
|
|
keyboard->xkb_info.keymap_size);
|
|
}
|
|
wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list)
|
|
{
|
|
wl_keyboard_send_keymap (keyboard_resource,
|
|
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
|
keyboard->xkb_info.keymap_fd,
|
|
keyboard->xkb_info.keymap_size);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard,
|
|
struct xkb_keymap *keymap)
|
|
{
|
|
MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info;
|
|
GError *error = NULL;
|
|
char *keymap_str;
|
|
size_t previous_size;
|
|
|
|
if (keymap == NULL)
|
|
{
|
|
g_warning ("Attempting to set null keymap (compilation probably failed)");
|
|
return;
|
|
}
|
|
|
|
xkb_keymap_unref (xkb_info->keymap);
|
|
xkb_info->keymap = xkb_keymap_ref (keymap);
|
|
|
|
meta_wayland_keyboard_update_xkb_state (keyboard);
|
|
|
|
keymap_str = xkb_map_get_as_string (xkb_info->keymap);
|
|
if (keymap_str == NULL)
|
|
{
|
|
g_warning ("failed to get string version of keymap");
|
|
return;
|
|
}
|
|
previous_size = xkb_info->keymap_size;
|
|
xkb_info->keymap_size = strlen (keymap_str) + 1;
|
|
|
|
if (xkb_info->keymap_fd >= 0)
|
|
close (xkb_info->keymap_fd);
|
|
|
|
xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error);
|
|
if (xkb_info->keymap_fd < 0)
|
|
{
|
|
g_warning ("creating a keymap file for %lu bytes failed: %s",
|
|
(unsigned long) xkb_info->keymap_size,
|
|
error->message);
|
|
g_clear_error (&error);
|
|
goto err_keymap_str;
|
|
}
|
|
|
|
if (xkb_info->keymap_area)
|
|
munmap (xkb_info->keymap_area, previous_size);
|
|
|
|
xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, xkb_info->keymap_fd, 0);
|
|
if (xkb_info->keymap_area == MAP_FAILED)
|
|
{
|
|
g_warning ("failed to mmap() %lu bytes\n",
|
|
(unsigned long) xkb_info->keymap_size);
|
|
goto err_dev_zero;
|
|
}
|
|
strcpy (xkb_info->keymap_area, keymap_str);
|
|
free (keymap_str);
|
|
|
|
inform_clients_of_new_keymap (keyboard);
|
|
|
|
notify_modifiers (keyboard,
|
|
wl_display_next_serial (keyboard->display),
|
|
xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_DEPRESSED),
|
|
xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LATCHED),
|
|
xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LOCKED),
|
|
xkb_state_serialize_layout (xkb_info->state, XKB_STATE_LAYOUT_EFFECTIVE));
|
|
return;
|
|
|
|
err_dev_zero:
|
|
close (xkb_info->keymap_fd);
|
|
xkb_info->keymap_fd = -1;
|
|
err_keymap_str:
|
|
free (keymap_str);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
keyboard_handle_focus_surface_destroy (struct wl_listener *listener, void *data)
|
|
{
|
|
MetaWaylandKeyboard *keyboard = wl_container_of (listener, keyboard, focus_surface_listener);
|
|
|
|
meta_wayland_keyboard_set_focus (keyboard, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
notify_key (MetaWaylandKeyboard *keyboard,
|
|
uint32_t time, uint32_t key, uint32_t state)
|
|
{
|
|
struct wl_resource *resource;
|
|
struct wl_list *l;
|
|
|
|
l = &keyboard->focus_resource_list;
|
|
if (!wl_list_empty (l))
|
|
{
|
|
struct wl_client *client = wl_resource_get_client (keyboard->focus_surface->resource);
|
|
struct wl_display *display = wl_client_get_display (client);
|
|
uint32_t serial = wl_display_next_serial (display);
|
|
|
|
wl_resource_for_each (resource, l)
|
|
{
|
|
wl_keyboard_send_key (resource, serial, time, key, state);
|
|
}
|
|
}
|
|
|
|
/* Eat the key events if we have a focused surface. */
|
|
return (keyboard->focus_surface != NULL);
|
|
}
|
|
|
|
static void
|
|
notify_modifiers (MetaWaylandKeyboard *keyboard, uint32_t serial,
|
|
uint32_t mods_depressed, uint32_t mods_latched,
|
|
uint32_t mods_locked, uint32_t group)
|
|
{
|
|
struct wl_resource *resource;
|
|
struct wl_list *l;
|
|
|
|
l = &keyboard->focus_resource_list;
|
|
wl_resource_for_each (resource, l)
|
|
{
|
|
wl_keyboard_send_modifiers (resource, serial, mods_depressed,
|
|
mods_latched, mods_locked, group);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard)
|
|
{
|
|
MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info;
|
|
xkb_mod_mask_t latched, locked, group;
|
|
|
|
/* Preserve latched/locked modifiers state */
|
|
if (xkb_info->state)
|
|
{
|
|
latched = xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LATCHED);
|
|
locked = xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LOCKED);
|
|
group = xkb_state_serialize_layout (xkb_info->state, XKB_STATE_LAYOUT_EFFECTIVE);
|
|
xkb_state_unref (xkb_info->state);
|
|
}
|
|
else
|
|
latched = locked = group = 0;
|
|
|
|
xkb_info->state = xkb_state_new (xkb_info->keymap);
|
|
|
|
if (latched || locked || group)
|
|
xkb_state_update_mask (xkb_info->state, 0, latched, locked, 0, 0, group);
|
|
}
|
|
|
|
static void
|
|
notify_key_repeat_for_resource (MetaWaylandKeyboard *keyboard,
|
|
struct wl_resource *keyboard_resource)
|
|
{
|
|
if (wl_resource_get_version (keyboard_resource) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
|
|
{
|
|
gboolean repeat;
|
|
unsigned int delay, rate;
|
|
|
|
repeat = g_settings_get_boolean (keyboard->settings, "repeat");
|
|
|
|
if (repeat)
|
|
{
|
|
unsigned int interval;
|
|
interval = g_settings_get_uint (keyboard->settings, "repeat-interval");
|
|
/* Our setting is in the milliseconds between keys. "rate" is the number
|
|
* of keys per second. */
|
|
rate = (1000 / interval);
|
|
delay = g_settings_get_uint (keyboard->settings, "delay");
|
|
}
|
|
else
|
|
{
|
|
rate = 0;
|
|
delay = 0;
|
|
}
|
|
|
|
wl_keyboard_send_repeat_info (keyboard_resource, rate, delay);
|
|
}
|
|
}
|
|
|
|
static void
|
|
notify_key_repeat (MetaWaylandKeyboard *keyboard)
|
|
{
|
|
struct wl_resource *keyboard_resource;
|
|
|
|
wl_resource_for_each (keyboard_resource, &keyboard->resource_list)
|
|
{
|
|
notify_key_repeat_for_resource (keyboard, keyboard_resource);
|
|
}
|
|
|
|
wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list)
|
|
{
|
|
notify_key_repeat_for_resource (keyboard, keyboard_resource);
|
|
}
|
|
}
|
|
|
|
static void
|
|
settings_changed (GSettings *settings,
|
|
const char *key,
|
|
gpointer data)
|
|
{
|
|
MetaWaylandKeyboard *keyboard = data;
|
|
|
|
notify_key_repeat (keyboard);
|
|
}
|
|
|
|
void
|
|
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
|
|
struct wl_display *display)
|
|
{
|
|
memset (keyboard, 0, sizeof *keyboard);
|
|
|
|
keyboard->display = display;
|
|
|
|
wl_list_init (&keyboard->resource_list);
|
|
wl_list_init (&keyboard->focus_resource_list);
|
|
|
|
keyboard->focus_surface_listener.notify = keyboard_handle_focus_surface_destroy;
|
|
|
|
wl_array_init (&keyboard->pressed_keys);
|
|
|
|
keyboard->xkb_info.keymap_fd = -1;
|
|
|
|
keyboard->settings = g_settings_new ("org.gnome.settings-daemon.peripherals.keyboard");
|
|
g_signal_connect (keyboard->settings, "changed",
|
|
G_CALLBACK (settings_changed), keyboard);
|
|
|
|
meta_wayland_keyboard_take_keymap (keyboard,
|
|
meta_backend_get_keymap (meta_get_backend ()));
|
|
}
|
|
|
|
static void
|
|
meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
|
|
{
|
|
xkb_keymap_unref (xkb_info->keymap);
|
|
xkb_state_unref (xkb_info->state);
|
|
|
|
if (xkb_info->keymap_area)
|
|
munmap (xkb_info->keymap_area, xkb_info->keymap_size);
|
|
if (xkb_info->keymap_fd >= 0)
|
|
close (xkb_info->keymap_fd);
|
|
}
|
|
|
|
void
|
|
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard)
|
|
{
|
|
meta_wayland_keyboard_set_focus (keyboard, NULL);
|
|
meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
|
|
|
|
/* XXX: What about keyboard->resource_list? */
|
|
wl_array_release (&keyboard->pressed_keys);
|
|
|
|
g_object_unref (keyboard->settings);
|
|
|
|
keyboard->display = NULL;
|
|
}
|
|
|
|
static void
|
|
update_pressed_keys (struct wl_array *keys,
|
|
uint32_t evdev_code,
|
|
gboolean is_press)
|
|
{
|
|
uint32_t *end = (void *) ((char *) keys->data + keys->size);
|
|
uint32_t *k;
|
|
|
|
if (is_press)
|
|
{
|
|
/* Make sure we don't already have this key. */
|
|
for (k = keys->data; k < end; k++)
|
|
if (*k == evdev_code)
|
|
return;
|
|
|
|
/* Otherwise add the key to the list of pressed keys */
|
|
k = wl_array_add (keys, sizeof (*k));
|
|
*k = evdev_code;
|
|
}
|
|
else
|
|
{
|
|
/* Remove the key from the array */
|
|
for (k = keys->data; k < end; k++)
|
|
if (*k == evdev_code)
|
|
{
|
|
*k = *(end - 1);
|
|
keys->size -= sizeof (*k);
|
|
return;
|
|
}
|
|
|
|
g_warning ("unexpected key release event for key 0x%x", evdev_code);
|
|
}
|
|
}
|
|
|
|
static guint
|
|
evdev_code (const ClutterKeyEvent *event)
|
|
{
|
|
/* clutter-xkb-utils.c adds a fixed offset of 8 to go into XKB's
|
|
* range, so we do the reverse here. */
|
|
return event->hardware_keycode - 8;
|
|
}
|
|
|
|
void
|
|
meta_wayland_keyboard_update (MetaWaylandKeyboard *keyboard,
|
|
const ClutterKeyEvent *event)
|
|
{
|
|
gboolean is_press = event->type == CLUTTER_KEY_PRESS;
|
|
struct xkb_state *state = keyboard->xkb_info.state;
|
|
enum xkb_state_component changed_state;
|
|
|
|
update_pressed_keys (&keyboard->pressed_keys, evdev_code (event), is_press);
|
|
|
|
changed_state = xkb_state_update_key (state,
|
|
event->hardware_keycode,
|
|
is_press ? XKB_KEY_DOWN : XKB_KEY_UP);
|
|
if (changed_state == 0)
|
|
return;
|
|
|
|
notify_modifiers (keyboard,
|
|
wl_display_next_serial (keyboard->display),
|
|
xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED),
|
|
xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED),
|
|
xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED),
|
|
xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE));
|
|
}
|
|
|
|
gboolean
|
|
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
|
|
const ClutterKeyEvent *event)
|
|
{
|
|
gboolean is_press = event->type == CLUTTER_KEY_PRESS;
|
|
gboolean handled;
|
|
|
|
/* Synthetic key events are for autorepeat. Ignore those, as
|
|
* autorepeat in Wayland is done on the client side. */
|
|
if (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC)
|
|
return FALSE;
|
|
|
|
meta_verbose ("Handling key %s event code %d\n",
|
|
is_press ? "press" : "release",
|
|
event->hardware_keycode);
|
|
|
|
handled = notify_key (keyboard, event->time, evdev_code (event), is_press);
|
|
|
|
if (handled)
|
|
meta_verbose ("Sent event to wayland client\n");
|
|
else
|
|
meta_verbose ("No wayland surface is focused, continuing normal operation\n");
|
|
|
|
return handled;
|
|
}
|
|
|
|
static void
|
|
move_resources (struct wl_list *destination, struct wl_list *source)
|
|
{
|
|
wl_list_insert_list (destination, source);
|
|
wl_list_init (source);
|
|
}
|
|
|
|
static void
|
|
move_resources_for_client (struct wl_list *destination,
|
|
struct wl_list *source,
|
|
struct wl_client *client)
|
|
{
|
|
struct wl_resource *resource, *tmp;
|
|
wl_resource_for_each_safe (resource, tmp, source)
|
|
{
|
|
if (wl_resource_get_client (resource) == client)
|
|
{
|
|
wl_list_remove (wl_resource_get_link (resource));
|
|
wl_list_insert (destination, wl_resource_get_link (resource));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
broadcast_focus (MetaWaylandKeyboard *keyboard,
|
|
struct wl_resource *resource)
|
|
{
|
|
struct xkb_state *state = keyboard->xkb_info.state;
|
|
|
|
wl_keyboard_send_modifiers (resource, keyboard->focus_serial,
|
|
xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED),
|
|
xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED),
|
|
xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED),
|
|
xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE));
|
|
wl_keyboard_send_enter (resource, keyboard->focus_serial,
|
|
keyboard->focus_surface->resource,
|
|
&keyboard->pressed_keys);
|
|
}
|
|
|
|
void
|
|
meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
|
|
MetaWaylandSurface *surface)
|
|
{
|
|
if (keyboard->display == NULL)
|
|
return;
|
|
|
|
if (keyboard->focus_surface == surface)
|
|
return;
|
|
|
|
if (keyboard->focus_surface != NULL)
|
|
{
|
|
struct wl_resource *resource;
|
|
struct wl_list *l;
|
|
|
|
l = &keyboard->focus_resource_list;
|
|
if (!wl_list_empty (l))
|
|
{
|
|
struct wl_client *client = wl_resource_get_client (keyboard->focus_surface->resource);
|
|
struct wl_display *display = wl_client_get_display (client);
|
|
uint32_t serial = wl_display_next_serial (display);
|
|
|
|
wl_resource_for_each (resource, l)
|
|
{
|
|
wl_keyboard_send_leave (resource, serial, keyboard->focus_surface->resource);
|
|
}
|
|
|
|
move_resources (&keyboard->resource_list, &keyboard->focus_resource_list);
|
|
}
|
|
|
|
wl_list_remove (&keyboard->focus_surface_listener.link);
|
|
keyboard->focus_surface = NULL;
|
|
}
|
|
|
|
if (surface != NULL)
|
|
{
|
|
struct wl_resource *resource;
|
|
struct wl_list *l;
|
|
|
|
keyboard->focus_surface = surface;
|
|
wl_resource_add_destroy_listener (keyboard->focus_surface->resource, &keyboard->focus_surface_listener);
|
|
|
|
move_resources_for_client (&keyboard->focus_resource_list,
|
|
&keyboard->resource_list,
|
|
wl_resource_get_client (keyboard->focus_surface->resource));
|
|
|
|
l = &keyboard->focus_resource_list;
|
|
if (!wl_list_empty (l))
|
|
{
|
|
struct wl_client *client = wl_resource_get_client (keyboard->focus_surface->resource);
|
|
struct wl_display *display = wl_client_get_display (client);
|
|
keyboard->focus_serial = wl_display_next_serial (display);
|
|
|
|
wl_resource_for_each (resource, l)
|
|
{
|
|
broadcast_focus (keyboard, resource);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct wl_client *
|
|
meta_wayland_keyboard_get_focus_client (MetaWaylandKeyboard *keyboard)
|
|
{
|
|
if (keyboard->focus_surface)
|
|
return wl_resource_get_client (keyboard->focus_surface->resource);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
keyboard_release (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
static const struct wl_keyboard_interface keyboard_interface = {
|
|
keyboard_release,
|
|
};
|
|
|
|
void
|
|
meta_wayland_keyboard_create_new_resource (MetaWaylandKeyboard *keyboard,
|
|
struct wl_client *client,
|
|
struct wl_resource *seat_resource,
|
|
uint32_t id)
|
|
{
|
|
struct wl_resource *cr;
|
|
|
|
cr = wl_resource_create (client, &wl_keyboard_interface, wl_resource_get_version (seat_resource), id);
|
|
wl_resource_set_implementation (cr, &keyboard_interface, keyboard, unbind_resource);
|
|
wl_list_insert (&keyboard->resource_list, wl_resource_get_link (cr));
|
|
|
|
wl_keyboard_send_keymap (cr,
|
|
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
|
keyboard->xkb_info.keymap_fd,
|
|
keyboard->xkb_info.keymap_size);
|
|
|
|
notify_key_repeat_for_resource (keyboard, cr);
|
|
|
|
if (keyboard->focus_surface && wl_resource_get_client (keyboard->focus_surface->resource) == client)
|
|
broadcast_focus (keyboard, cr);
|
|
}
|