mirror of
https://gitlab.freedesktop.org/wayland/weston
synced 2024-09-06 09:56:14 +00:00
![Antonio Borneo](/assets/img/avatar_default.png)
The printf() format specifier "%m" is a glibc extension to print the string returned by strerror(errno). While supported by other libraries (e.g. uClibc and musl), it is not widely portable. In Weston code the format string is often passed to a logging function that calls other syscalls before the conversion of "%m" takes place. If one of such syscall modifies the value in errno, the conversion of "%m" will incorrectly report the error string corresponding to the new value of errno. Remove all the occurrences of the specifier "%m" in Weston code by using directly the string returned by strerror(errno). While there, fix some minor indentation issue. Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
527 lines
13 KiB
C
527 lines
13 KiB
C
/*
|
|
* 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 <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
#include "window.h"
|
|
#include "input-method-unstable-v1-client-protocol.h"
|
|
#include "shared/helpers.h"
|
|
|
|
enum compose_state {
|
|
state_normal,
|
|
state_compose
|
|
};
|
|
|
|
struct compose_seq {
|
|
uint32_t keys[4];
|
|
|
|
const char *text;
|
|
};
|
|
|
|
struct simple_im;
|
|
|
|
typedef void (*keyboard_input_key_handler_t)(struct simple_im *keyboard,
|
|
uint32_t serial,
|
|
uint32_t time, uint32_t key, uint32_t unicode,
|
|
enum wl_keyboard_key_state state);
|
|
|
|
struct simple_im {
|
|
struct zwp_input_method_v1 *input_method;
|
|
struct zwp_input_method_context_v1 *context;
|
|
struct wl_display *display;
|
|
struct wl_registry *registry;
|
|
struct wl_keyboard *keyboard;
|
|
enum compose_state compose_state;
|
|
struct compose_seq compose_seq;
|
|
|
|
struct xkb_context *xkb_context;
|
|
|
|
uint32_t modifiers;
|
|
|
|
struct xkb_keymap *keymap;
|
|
struct xkb_state *state;
|
|
xkb_mod_mask_t control_mask;
|
|
xkb_mod_mask_t alt_mask;
|
|
xkb_mod_mask_t shift_mask;
|
|
|
|
keyboard_input_key_handler_t key_handler;
|
|
|
|
uint32_t serial;
|
|
};
|
|
|
|
static const struct compose_seq compose_seqs[] = {
|
|
{ { XKB_KEY_quotedbl, XKB_KEY_A, 0 }, "Ä" },
|
|
{ { XKB_KEY_quotedbl, XKB_KEY_O, 0 }, "Ö" },
|
|
{ { XKB_KEY_quotedbl, XKB_KEY_U, 0 }, "Ü" },
|
|
{ { XKB_KEY_quotedbl, XKB_KEY_a, 0 }, "ä" },
|
|
{ { XKB_KEY_quotedbl, XKB_KEY_o, 0 }, "ö" },
|
|
{ { XKB_KEY_quotedbl, XKB_KEY_u, 0 }, "ü" },
|
|
{ { XKB_KEY_apostrophe, XKB_KEY_A, 0 }, "Á" },
|
|
{ { XKB_KEY_apostrophe, XKB_KEY_a, 0 }, "á" },
|
|
{ { XKB_KEY_slash, XKB_KEY_O, 0 }, "Ø" },
|
|
{ { XKB_KEY_slash, XKB_KEY_o, 0 }, "ø" },
|
|
{ { XKB_KEY_less, XKB_KEY_3, 0 }, "♥" },
|
|
{ { XKB_KEY_A, XKB_KEY_A, 0 }, "Å" },
|
|
{ { XKB_KEY_A, XKB_KEY_E, 0 }, "Æ" },
|
|
{ { XKB_KEY_O, XKB_KEY_C, 0 }, "©" },
|
|
{ { XKB_KEY_O, XKB_KEY_R, 0 }, "®" },
|
|
{ { XKB_KEY_s, XKB_KEY_s, 0 }, "ß" },
|
|
{ { XKB_KEY_a, XKB_KEY_e, 0 }, "æ" },
|
|
{ { XKB_KEY_a, XKB_KEY_a, 0 }, "å" },
|
|
};
|
|
|
|
static const uint32_t ignore_keys_on_compose[] = {
|
|
XKB_KEY_Shift_L,
|
|
XKB_KEY_Shift_R
|
|
};
|
|
|
|
static void
|
|
handle_surrounding_text(void *data,
|
|
struct zwp_input_method_context_v1 *context,
|
|
const char *text,
|
|
uint32_t cursor,
|
|
uint32_t anchor)
|
|
{
|
|
fprintf(stderr, "Surrounding text updated: %s\n", text);
|
|
}
|
|
|
|
static void
|
|
handle_reset(void *data,
|
|
struct zwp_input_method_context_v1 *context)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
|
|
fprintf(stderr, "Reset pre-edit buffer\n");
|
|
|
|
keyboard->compose_state = state_normal;
|
|
}
|
|
|
|
static void
|
|
handle_content_type(void *data,
|
|
struct zwp_input_method_context_v1 *context,
|
|
uint32_t hint,
|
|
uint32_t purpose)
|
|
{
|
|
}
|
|
|
|
static void
|
|
handle_invoke_action(void *data,
|
|
struct zwp_input_method_context_v1 *context,
|
|
uint32_t button,
|
|
uint32_t index)
|
|
{
|
|
}
|
|
|
|
static void
|
|
handle_commit_state(void *data,
|
|
struct zwp_input_method_context_v1 *context,
|
|
uint32_t serial)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
|
|
keyboard->serial = serial;
|
|
}
|
|
|
|
static void
|
|
handle_preferred_language(void *data,
|
|
struct zwp_input_method_context_v1 *context,
|
|
const char *language)
|
|
{
|
|
}
|
|
|
|
static const struct zwp_input_method_context_v1_listener input_method_context_listener = {
|
|
handle_surrounding_text,
|
|
handle_reset,
|
|
handle_content_type,
|
|
handle_invoke_action,
|
|
handle_commit_state,
|
|
handle_preferred_language
|
|
};
|
|
|
|
static void
|
|
input_method_keyboard_keymap(void *data,
|
|
struct wl_keyboard *wl_keyboard,
|
|
uint32_t format,
|
|
int32_t fd,
|
|
uint32_t size)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
char *map_str;
|
|
|
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
|
if (map_str == MAP_FAILED) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
keyboard->keymap =
|
|
xkb_keymap_new_from_string(keyboard->xkb_context,
|
|
map_str,
|
|
XKB_KEYMAP_FORMAT_TEXT_V1,
|
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
|
|
munmap(map_str, size);
|
|
close(fd);
|
|
|
|
if (!keyboard->keymap) {
|
|
fprintf(stderr, "Failed to compile keymap\n");
|
|
return;
|
|
}
|
|
|
|
keyboard->state = xkb_state_new(keyboard->keymap);
|
|
if (!keyboard->state) {
|
|
fprintf(stderr, "Failed to create XKB state\n");
|
|
xkb_keymap_unref(keyboard->keymap);
|
|
return;
|
|
}
|
|
|
|
keyboard->control_mask =
|
|
1 << xkb_keymap_mod_get_index(keyboard->keymap, "Control");
|
|
keyboard->alt_mask =
|
|
1 << xkb_keymap_mod_get_index(keyboard->keymap, "Mod1");
|
|
keyboard->shift_mask =
|
|
1 << xkb_keymap_mod_get_index(keyboard->keymap, "Shift");
|
|
}
|
|
|
|
static void
|
|
input_method_keyboard_key(void *data,
|
|
struct wl_keyboard *wl_keyboard,
|
|
uint32_t serial,
|
|
uint32_t time,
|
|
uint32_t key,
|
|
uint32_t state_w)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
uint32_t code;
|
|
uint32_t num_syms;
|
|
const xkb_keysym_t *syms;
|
|
xkb_keysym_t sym;
|
|
enum wl_keyboard_key_state state = state_w;
|
|
|
|
if (!keyboard->state)
|
|
return;
|
|
|
|
code = key + 8;
|
|
num_syms = xkb_state_key_get_syms(keyboard->state, code, &syms);
|
|
|
|
sym = XKB_KEY_NoSymbol;
|
|
if (num_syms == 1)
|
|
sym = syms[0];
|
|
|
|
if (keyboard->key_handler)
|
|
(*keyboard->key_handler)(keyboard, serial, time, key, sym,
|
|
state);
|
|
}
|
|
|
|
static void
|
|
input_method_keyboard_modifiers(void *data,
|
|
struct wl_keyboard *wl_keyboard,
|
|
uint32_t serial,
|
|
uint32_t mods_depressed,
|
|
uint32_t mods_latched,
|
|
uint32_t mods_locked,
|
|
uint32_t group)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
struct zwp_input_method_context_v1 *context = keyboard->context;
|
|
xkb_mod_mask_t mask;
|
|
|
|
xkb_state_update_mask(keyboard->state, mods_depressed,
|
|
mods_latched, mods_locked, 0, 0, group);
|
|
mask = xkb_state_serialize_mods(keyboard->state,
|
|
XKB_STATE_MODS_DEPRESSED |
|
|
XKB_STATE_MODS_LATCHED);
|
|
|
|
keyboard->modifiers = 0;
|
|
if (mask & keyboard->control_mask)
|
|
keyboard->modifiers |= MOD_CONTROL_MASK;
|
|
if (mask & keyboard->alt_mask)
|
|
keyboard->modifiers |= MOD_ALT_MASK;
|
|
if (mask & keyboard->shift_mask)
|
|
keyboard->modifiers |= MOD_SHIFT_MASK;
|
|
|
|
zwp_input_method_context_v1_modifiers(context, serial,
|
|
mods_depressed, mods_depressed,
|
|
mods_latched, group);
|
|
}
|
|
|
|
static const struct wl_keyboard_listener input_method_keyboard_listener = {
|
|
input_method_keyboard_keymap,
|
|
NULL, /* enter */
|
|
NULL, /* leave */
|
|
input_method_keyboard_key,
|
|
input_method_keyboard_modifiers
|
|
};
|
|
|
|
static void
|
|
input_method_activate(void *data,
|
|
struct zwp_input_method_v1 *input_method,
|
|
struct zwp_input_method_context_v1 *context)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
|
|
if (keyboard->context)
|
|
zwp_input_method_context_v1_destroy(keyboard->context);
|
|
|
|
keyboard->compose_state = state_normal;
|
|
|
|
keyboard->serial = 0;
|
|
|
|
keyboard->context = context;
|
|
zwp_input_method_context_v1_add_listener(context,
|
|
&input_method_context_listener,
|
|
keyboard);
|
|
keyboard->keyboard = zwp_input_method_context_v1_grab_keyboard(context);
|
|
wl_keyboard_add_listener(keyboard->keyboard,
|
|
&input_method_keyboard_listener,
|
|
keyboard);
|
|
}
|
|
|
|
static void
|
|
input_method_deactivate(void *data,
|
|
struct zwp_input_method_v1 *input_method,
|
|
struct zwp_input_method_context_v1 *context)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
|
|
if (!keyboard->context)
|
|
return;
|
|
|
|
zwp_input_method_context_v1_destroy(keyboard->context);
|
|
keyboard->context = NULL;
|
|
}
|
|
|
|
static const struct zwp_input_method_v1_listener input_method_listener = {
|
|
input_method_activate,
|
|
input_method_deactivate
|
|
};
|
|
|
|
static void
|
|
registry_handle_global(void *data, struct wl_registry *registry,
|
|
uint32_t name, const char *interface, uint32_t version)
|
|
{
|
|
struct simple_im *keyboard = data;
|
|
|
|
if (!strcmp(interface, "zwp_input_method_v1")) {
|
|
keyboard->input_method =
|
|
wl_registry_bind(registry, name,
|
|
&zwp_input_method_v1_interface, 1);
|
|
zwp_input_method_v1_add_listener(keyboard->input_method,
|
|
&input_method_listener, keyboard);
|
|
}
|
|
}
|
|
|
|
static void
|
|
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
|
uint32_t name)
|
|
{
|
|
}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
registry_handle_global,
|
|
registry_handle_global_remove
|
|
};
|
|
|
|
static int
|
|
compare_compose_keys(const void *c1, const void *c2)
|
|
{
|
|
const struct compose_seq *cs1 = c1;
|
|
const struct compose_seq *cs2 = c2;
|
|
int i;
|
|
|
|
for (i = 0; cs1->keys[i] != 0 && cs2->keys[i] != 0; i++) {
|
|
if (cs1->keys[i] != cs2->keys[i])
|
|
return cs1->keys[i] - cs2->keys[i];
|
|
}
|
|
|
|
if (cs1->keys[i] == cs2->keys[i]
|
|
|| cs1->keys[i] == 0)
|
|
return 0;
|
|
|
|
return cs1->keys[i] - cs2->keys[i];
|
|
}
|
|
|
|
static void
|
|
simple_im_key_handler(struct simple_im *keyboard,
|
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t sym,
|
|
enum wl_keyboard_key_state state)
|
|
{
|
|
struct zwp_input_method_context_v1 *context = keyboard->context;
|
|
char text[64];
|
|
|
|
if (sym == XKB_KEY_Multi_key &&
|
|
state == WL_KEYBOARD_KEY_STATE_RELEASED &&
|
|
keyboard->compose_state == state_normal) {
|
|
keyboard->compose_state = state_compose;
|
|
memset(&keyboard->compose_seq, 0, sizeof(struct compose_seq));
|
|
return;
|
|
}
|
|
|
|
if (keyboard->compose_state == state_compose) {
|
|
uint32_t i = 0;
|
|
struct compose_seq *cs;
|
|
|
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
|
return;
|
|
|
|
for (i = 0; i < ARRAY_LENGTH(ignore_keys_on_compose); i++) {
|
|
if (sym == ignore_keys_on_compose[i]) {
|
|
zwp_input_method_context_v1_key(context,
|
|
keyboard->serial,
|
|
time,
|
|
key,
|
|
state);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; keyboard->compose_seq.keys[i] != 0; i++);
|
|
|
|
keyboard->compose_seq.keys[i] = sym;
|
|
|
|
cs = bsearch (&keyboard->compose_seq, compose_seqs,
|
|
ARRAY_LENGTH(compose_seqs),
|
|
sizeof(compose_seqs[0]), compare_compose_keys);
|
|
|
|
if (cs) {
|
|
if (cs->keys[i + 1] == 0) {
|
|
zwp_input_method_context_v1_preedit_cursor(keyboard->context,
|
|
0);
|
|
zwp_input_method_context_v1_preedit_string(keyboard->context,
|
|
keyboard->serial,
|
|
"", "");
|
|
zwp_input_method_context_v1_cursor_position(keyboard->context,
|
|
0, 0);
|
|
zwp_input_method_context_v1_commit_string(keyboard->context,
|
|
keyboard->serial,
|
|
cs->text);
|
|
keyboard->compose_state = state_normal;
|
|
} else {
|
|
uint32_t j = 0, idx = 0;
|
|
|
|
for (; j <= i; j++) {
|
|
idx += xkb_keysym_to_utf8(cs->keys[j], text + idx, sizeof(text) - idx);
|
|
}
|
|
|
|
zwp_input_method_context_v1_preedit_cursor(keyboard->context,
|
|
strlen(text));
|
|
zwp_input_method_context_v1_preedit_string(keyboard->context,
|
|
keyboard->serial,
|
|
text,
|
|
text);
|
|
}
|
|
} else {
|
|
uint32_t j = 0, idx = 0;
|
|
|
|
for (; j <= i; j++) {
|
|
idx += xkb_keysym_to_utf8(keyboard->compose_seq.keys[j], text + idx, sizeof(text) - idx);
|
|
}
|
|
zwp_input_method_context_v1_preedit_cursor(keyboard->context,
|
|
0);
|
|
zwp_input_method_context_v1_preedit_string(keyboard->context,
|
|
keyboard->serial,
|
|
"", "");
|
|
zwp_input_method_context_v1_cursor_position(keyboard->context,
|
|
0, 0);
|
|
zwp_input_method_context_v1_commit_string(keyboard->context,
|
|
keyboard->serial,
|
|
text);
|
|
keyboard->compose_state = state_normal;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0) {
|
|
zwp_input_method_context_v1_key(context, serial, time, key, state);
|
|
return;
|
|
}
|
|
|
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
|
return;
|
|
|
|
zwp_input_method_context_v1_cursor_position(keyboard->context,
|
|
0, 0);
|
|
zwp_input_method_context_v1_commit_string(keyboard->context,
|
|
keyboard->serial,
|
|
text);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct simple_im simple_im;
|
|
int ret = 0;
|
|
|
|
memset(&simple_im, 0, sizeof(simple_im));
|
|
|
|
simple_im.display = wl_display_connect(NULL);
|
|
if (simple_im.display == NULL) {
|
|
fprintf(stderr, "Failed to connect to server: %s\n",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
simple_im.registry = wl_display_get_registry(simple_im.display);
|
|
wl_registry_add_listener(simple_im.registry,
|
|
®istry_listener, &simple_im);
|
|
wl_display_roundtrip(simple_im.display);
|
|
if (simple_im.input_method == NULL) {
|
|
fprintf(stderr, "No input_method global\n");
|
|
return -1;
|
|
}
|
|
|
|
simple_im.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
if (simple_im.xkb_context == NULL) {
|
|
fprintf(stderr, "Failed to create XKB context\n");
|
|
return -1;
|
|
}
|
|
|
|
simple_im.context = NULL;
|
|
simple_im.key_handler = simple_im_key_handler;
|
|
|
|
while (ret != -1)
|
|
ret = wl_display_dispatch(simple_im.display);
|
|
|
|
if (ret == -1) {
|
|
fprintf(stderr, "Dispatch error: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|