Blame src/ibuskeymap.c

Packit Service 1d8f1c
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
Packit Service 1d8f1c
/* vim:set et sts=4: */
Packit Service 1d8f1c
/* IBus - The Input Bus
Packit Service 1d8f1c
 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
Packit Service 1d8f1c
 * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
Packit Service 1d8f1c
 * Copyright (C) 2008-2018 Red Hat, Inc.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is free software; you can redistribute it and/or
Packit Service 1d8f1c
 * modify it under the terms of the GNU Lesser General Public
Packit Service 1d8f1c
 * License as published by the Free Software Foundation; either
Packit Service 1d8f1c
 * version 2.1 of the License, or (at your option) any later version.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is distributed in the hope that it will be useful,
Packit Service 1d8f1c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 1d8f1c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 1d8f1c
 * Lesser General Public License for more details.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * You should have received a copy of the GNU Lesser General Public
Packit Service 1d8f1c
 * License along with this library; if not, write to the Free Software
Packit Service 1d8f1c
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
Packit Service 1d8f1c
 * USA
Packit Service 1d8f1c
 */
Packit Service 1d8f1c
#include <errno.h>
Packit Service 1d8f1c
#include <string.h>
Packit Service 1d8f1c
#include <stdio.h>
Packit Service 1d8f1c
#include <stdlib.h>
Packit Service 1d8f1c
#include <glib/gstdio.h>
Packit Service 1d8f1c
#include "ibuskeys.h"
Packit Service 1d8f1c
#include "ibuskeysyms.h"
Packit Service 1d8f1c
#include "ibuskeymap.h"
Packit Service 1d8f1c
Packit Service 1d8f1c
typedef guint KEYMAP[256][7];
Packit Service 1d8f1c
/* functions prototype */
Packit Service 1d8f1c
static void         ibus_keymap_destroy         (IBusKeymap             *keymap);
Packit Service 1d8f1c
static gboolean     ibus_keymap_load            (const gchar            *name,
Packit Service 1d8f1c
                                                 KEYMAP                  keymap);
Packit Service 1d8f1c
static GHashTable   *keymaps = NULL;
Packit Service 1d8f1c
Packit Service 1d8f1c
G_DEFINE_TYPE (IBusKeymap, ibus_keymap, IBUS_TYPE_OBJECT)
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_keymap_class_init (IBusKeymapClass *class)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
Packit Service 1d8f1c
Packit Service 1d8f1c
    object_class->destroy = (IBusObjectDestroyFunc) ibus_keymap_destroy;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_keymap_init (IBusKeymap *keymap)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gint i;
Packit Service 1d8f1c
    keymap->name = NULL;
Packit Service 1d8f1c
    for (i = 0; i < sizeof (keymap->keymap) / sizeof (guint); i++) {
Packit Service 1d8f1c
        ((guint *)keymap->keymap)[i] = IBUS_KEY_VoidSymbol;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_keymap_destroy (IBusKeymap *keymap)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    if (keymap->name != NULL) {
Packit Service 1d8f1c
        g_free (keymap->name);
Packit Service 1d8f1c
        keymap->name = NULL;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    IBUS_OBJECT_CLASS (ibus_keymap_parent_class)->destroy ((IBusObject *)keymap);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
#define SKIP_SPACE(p)   \
Packit Service 1d8f1c
    while (*p == ' ') p++;
Packit Service 1d8f1c
Packit Service 1d8f1c
static gboolean
Packit Service 1d8f1c
ibus_keymap_parse_line (gchar  *str,
Packit Service 1d8f1c
                        KEYMAP  keymap)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gchar *p1, *p2, ch;
Packit Service 1d8f1c
    gint i;
Packit Service 1d8f1c
    guint keycode;
Packit Service 1d8f1c
    guint keysym;
Packit Service 1d8f1c
Packit Service 1d8f1c
    const struct {
Packit Service 1d8f1c
        const gchar *prefix;
Packit Service 1d8f1c
        const gint len;
Packit Service 1d8f1c
    } prefix [] = {
Packit Service 1d8f1c
        { "keycode ", sizeof ("keycode ") - 1 },
Packit Service 1d8f1c
        { "shift keycode ", sizeof ("shift keycode ") - 1 },
Packit Service 1d8f1c
        { "capslock keycode ", sizeof ("capslock keycode ") - 1 },
Packit Service 1d8f1c
        { "shift capslock keycode ", sizeof ("shift capslock keycode ") - 1 },
Packit Service 1d8f1c
        { "altgr keycode ", sizeof ("altgr keycode ") - 1},
Packit Service 1d8f1c
        { "shift altgr keycode ", sizeof ("shift altgr keycode ") - 1},
Packit Service 1d8f1c
        { "numlock keycode ", sizeof ("numlock keycode ") - 1},
Packit Service 1d8f1c
    };
Packit Service 1d8f1c
Packit Service 1d8f1c
    p1 = str;
Packit Service 1d8f1c
Packit Service 1d8f1c
    SKIP_SPACE(p1);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (*p1 == '#')
Packit Service 1d8f1c
        return TRUE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (strncmp (p1, "include ", sizeof ("include ") - 1) == 0) {
Packit Service 1d8f1c
        p1 += sizeof ("include ") - 1;
Packit Service 1d8f1c
        for (p2 = p1; *p2 != '\n'; p2++);
Packit Service 1d8f1c
        *p2 = '\0';
Packit Service 1d8f1c
        return ibus_keymap_load (p1, keymap);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    for (i = 0; i < sizeof (prefix) / sizeof (prefix[0]); i++) {
Packit Service 1d8f1c
        if (strncmp (p1, prefix[i].prefix, prefix[i].len) == 0) {
Packit Service 1d8f1c
            p1 += prefix[i].len;
Packit Service 1d8f1c
            break;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (i >= sizeof (prefix) / sizeof (prefix[0]))
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    keycode = (guint) strtoul (p1, &p2, 10);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (keycode == 0 && p1 == p2)
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    if ((int) keycode < 0 || keycode > 255)
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    p1 = p2;
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (*p1++ != ' ')
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    if (*p1++ != '=')
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    if (*p1++ != ' ')
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    for (p2 = p1; *p2 != '\n' && *p2 != ' '; p2++);
Packit Service 1d8f1c
    *p2 = '\0'; p2++;
Packit Service 1d8f1c
Packit Service 1d8f1c
    keysym = ibus_keyval_from_name (p1);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (keysym == IBUS_KEY_VoidSymbol)
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* Do not assign *p1 to g_ascii_isalpha() directly for the syntax check */
Packit Service 1d8f1c
    if (i == 0 &&
Packit Service 1d8f1c
        strncmp (p2, "addupper", sizeof ("addupper") - 1) == 0 &&
rpm-build 0a7332
        (ch = *p1) && (ch >= 0) && g_ascii_isalpha (ch)) {
Packit Service 1d8f1c
        gchar buf[] = "a";
Packit Service 1d8f1c
        buf[0] = g_ascii_toupper(ch);
Packit Service 1d8f1c
        keymap[keycode][0] = keymap[keycode][3] = keysym;
Packit Service 1d8f1c
        keymap[keycode][1] = keymap[keycode][2] = ibus_keyval_from_name (buf);
Packit Service 1d8f1c
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    else {
Packit Service 1d8f1c
        keymap[keycode][i] = keysym;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    return TRUE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static gboolean
Packit Service 1d8f1c
ibus_keymap_load (const gchar *name,
Packit Service 1d8f1c
                  KEYMAP       keymap)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    const gchar *envstr;
Packit Service 1d8f1c
    gchar *fname;
Packit Service 1d8f1c
    FILE *pf;
Packit Service 1d8f1c
    gchar buf[256];
Packit Service 1d8f1c
    gint lineno;
Packit Service 1d8f1c
Packit Service 1d8f1c
Packit Service 1d8f1c
    if ((envstr = g_getenv ("IBUS_KEYMAP_PATH")) != NULL)
Packit Service 1d8f1c
        fname = g_build_filename (envstr, name, NULL);
Packit Service 1d8f1c
    else
Packit Service 1d8f1c
        fname = g_build_filename (IBUS_DATA_DIR, "keymaps", name, NULL);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (fname == NULL) {
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    pf = g_fopen (fname, "r");
Packit Service 1d8f1c
    g_free (fname);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (pf == NULL) {
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    lineno = 0;
Packit Service 1d8f1c
    while (fgets (buf, sizeof (buf), pf) != NULL) {
Packit Service 1d8f1c
        lineno ++;
Packit Service 1d8f1c
        if (!ibus_keymap_parse_line (buf, keymap)) {
Packit Service 1d8f1c
            g_warning ("parse %s failed on %d line", name, lineno);
Packit Service 1d8f1c
            lineno = -1;
Packit Service 1d8f1c
            break;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    fclose (pf);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (lineno == -1) {
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    return TRUE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
void
Packit Service 1d8f1c
ibus_keymap_fill (KEYMAP keymap)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gint i;
Packit Service 1d8f1c
    for (i = 0; i < 256; i++) {
Packit Service 1d8f1c
        /* fill shift */
Packit Service 1d8f1c
        if (keymap[i][1] == IBUS_KEY_VoidSymbol)
Packit Service 1d8f1c
            keymap[i][1] = keymap[i][0];
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* fill capslock */
Packit Service 1d8f1c
        if (keymap[i][2] == IBUS_KEY_VoidSymbol)
Packit Service 1d8f1c
            keymap[i][2] = keymap[i][0];
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* fill shift capslock */
Packit Service 1d8f1c
        if (keymap[i][3] == IBUS_KEY_VoidSymbol)
Packit Service 1d8f1c
            keymap[i][3] = keymap[i][1];
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* fill altgr */
Packit Service 1d8f1c
        if (keymap[i][4] == IBUS_KEY_VoidSymbol)
Packit Service 1d8f1c
            keymap[i][4] = keymap[i][0];
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* fill shift altgr */
Packit Service 1d8f1c
        if (keymap[i][5] == IBUS_KEY_VoidSymbol)
Packit Service 1d8f1c
            keymap[i][5] = keymap[i][1];
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
_keymap_destroy_cb (IBusKeymap *keymap,
Packit Service 1d8f1c
                    gpointer    user_data)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_hash_table_remove (keymaps, keymap->name);
Packit Service 1d8f1c
    g_object_unref (keymap);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
IBusKeymap *
Packit Service 1d8f1c
ibus_keymap_new (const gchar *name)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    return ibus_keymap_get (name);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
IBusKeymap *
Packit Service 1d8f1c
ibus_keymap_get (const gchar *name)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_assert (name != NULL);
Packit Service 1d8f1c
Packit Service 1d8f1c
    IBusKeymap *keymap;
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (keymaps == NULL) {
Packit Service 1d8f1c
        keymaps = g_hash_table_new_full (g_str_hash,
Packit Service 1d8f1c
                                         g_str_equal,
Packit Service 1d8f1c
                                         (GDestroyNotify) g_free,
Packit Service 1d8f1c
                                         (GDestroyNotify)  g_object_unref);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    keymap = (IBusKeymap *) g_hash_table_lookup (keymaps, name);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (keymap == NULL) {
Packit Service 1d8f1c
        keymap = g_object_new (IBUS_TYPE_KEYMAP, NULL);
Packit Service 1d8f1c
        g_object_ref_sink (keymap);
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (ibus_keymap_load (name, keymap->keymap)) {
Packit Service 1d8f1c
            ibus_keymap_fill (keymap->keymap);
Packit Service 1d8f1c
            keymap->name = g_strdup (name);
Packit Service 1d8f1c
            g_hash_table_insert (keymaps, g_strdup (keymap->name), keymap);
Packit Service 1d8f1c
Packit Service 1d8f1c
            g_signal_connect (keymap, "destroy", G_CALLBACK (_keymap_destroy_cb), NULL);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        else {
Packit Service 1d8f1c
            g_object_unref (keymap);
Packit Service 1d8f1c
            keymap = NULL;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    if (keymap != NULL)
Packit Service 1d8f1c
        g_object_ref_sink (keymap);
Packit Service 1d8f1c
    return keymap;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
guint32
Packit Service 1d8f1c
ibus_keymap_lookup_keysym (IBusKeymap *keymap,
Packit Service 1d8f1c
                           guint16     keycode,
Packit Service 1d8f1c
                           guint32     state)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_assert (IBUS_IS_KEYMAP (keymap));
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (keycode < 256) {
Packit Service 1d8f1c
        /* numlock */
Packit Service 1d8f1c
        if ((state & IBUS_MOD2_MASK) &&
Packit Service 1d8f1c
            (keymap->keymap[keycode][6] != IBUS_KEY_VoidSymbol)) {
Packit Service 1d8f1c
            return keymap->keymap[keycode][6];
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        state &= IBUS_SHIFT_MASK | IBUS_LOCK_MASK | IBUS_MOD5_MASK;
Packit Service 1d8f1c
Packit Service 1d8f1c
        switch (state) {
Packit Service 1d8f1c
        case 0:
Packit Service 1d8f1c
            return keymap->keymap[keycode][0];
Packit Service 1d8f1c
        case IBUS_SHIFT_MASK:
Packit Service 1d8f1c
            return keymap->keymap[keycode][1];
Packit Service 1d8f1c
        case IBUS_LOCK_MASK:
Packit Service 1d8f1c
            return keymap->keymap[keycode][2];
Packit Service 1d8f1c
        case IBUS_SHIFT_MASK | IBUS_LOCK_MASK:
Packit Service 1d8f1c
            return keymap->keymap[keycode][3];
Packit Service 1d8f1c
        case IBUS_MOD5_MASK:
Packit Service 1d8f1c
        case IBUS_MOD5_MASK | IBUS_LOCK_MASK:
Packit Service 1d8f1c
            return keymap->keymap[keycode][4];
Packit Service 1d8f1c
        case IBUS_MOD5_MASK | IBUS_SHIFT_MASK:
Packit Service 1d8f1c
        case IBUS_MOD5_MASK | IBUS_LOCK_MASK | IBUS_SHIFT_MASK:
Packit Service 1d8f1c
            return keymap->keymap[keycode][5];
Packit Service 1d8f1c
        default:
Packit Service 1d8f1c
            break;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    return IBUS_KEY_VoidSymbol;
Packit Service 1d8f1c
}