/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- */
/*
 * Copyright (C) 2002,2003 Red Hat, Inc.
 *
 * Various fixes by VICE team.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "vice.h"

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <glib.h>
#include <gtk/gtk.h>
#include "caps.hh"
#include "debug.h"
#include "keymap.h"

#ifdef VTE_DEBUG
static void _vte_keysym_print(guint keyval, guint modifiers)
{
    g_printerr("Mapping ");
    if (modifiers & GDK_CONTROL_MASK) {
        g_printerr("Control+");
    }
    if (modifiers & VTE_META_MASK) {
        g_printerr("Meta+");
    }
    if (modifiers & VTE_NUMLOCK_MASK) {
        g_printerr("NumLock+");
    }
    if (modifiers & GDK_SHIFT_MASK) {
        g_printerr("Shift+");
    }
    g_printerr("%s" , gdk_keyval_name(keyval));
}
#else
static void _vte_keysym_print(guint keyval, guint modifiers)
{
}
#endif

enum _vte_cursor_mode {
    cursor_default =    1u << 0,
    cursor_app =        1u << 1
};

enum _vte_keypad_mode {
    keypad_default =    1u << 0,
    keypad_app =        1u << 1
};

#define cursor_all    (cursor_default | cursor_app)
#define keypad_all    (keypad_default | keypad_app)

struct _vte_keymap_entry {
    guint cursor_mode;
    guint keypad_mode;
    guint mod_mask;
    const char normal[8];
    gssize normal_length;
};

#define X_NULL ""

enum _vte_modifier_encoding_method {
    MODIFIER_ENCODING_NONE,
    MODIFIER_ENCODING_SHORT,
    MODIFIER_ENCODING_LONG,
};

static const struct _vte_keymap_entry _vte_keymap_GDK_space[] = {
    /* Control+Meta+space = ESC+NUL */
        {cursor_all, keypad_all, GDK_CONTROL_MASK | VTE_META_MASK, _VTE_CAP_ESC "\0", 2},
    /* Meta+space = ESC+" " */
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_ESC " ", 2},
    /* Control+space = NUL */
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\0", 1},
    /* Regular space. */
        {cursor_all, keypad_all, 0, " ", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Tab[] = {
    /* Shift+Tab = Back-Tab */
        {cursor_all, keypad_all, GDK_SHIFT_MASK, _VTE_CAP_CSI "Z", -1},
    /* Alt+Tab = Esc+Tab */
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_ESC "\t", -1},
    /* Regular tab. */
        {cursor_all, keypad_all, 0, "\t", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Return[] = {
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_ESC "\r", 2},
        {cursor_all, keypad_all, 0, "\r", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Escape[] = {
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_ESC _VTE_CAP_ESC, 2},
        {cursor_all, keypad_all, 0, _VTE_CAP_ESC, 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Insert[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "2~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_ISO_Left_Tab[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "Z", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_slash[] = {
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_ESC "/", 2},
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\037", 1},
        {cursor_all, keypad_all, 0, "/", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_question[] = {
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_ESC "?", 2},
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\177", 1},
        {cursor_all, keypad_all, 0, "?", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

/* Various numeric keys enter control characters. */
static const struct _vte_keymap_entry _vte_keymap_GDK_2[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\0", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_3[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\033", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_4[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\034", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_5[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\035", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_6[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\036", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_7[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\037", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_8[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\177", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};
static const struct _vte_keymap_entry _vte_keymap_GDK_Minus[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, "\037", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

/* Keys (potentially) affected by the cursor key mode. */
static const struct _vte_keymap_entry _vte_keymap_GDK_Home[] = {
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "H", -1},
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "H", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_End[] = {
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "F", -1},
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "F", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Page_Up[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "5~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Page_Down[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "6~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Up[] = {
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "A", -1},
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "A", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Down[] = {
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "B", -1},
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "B", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Right[] = {
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "C", -1},
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "C", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_Left[] = {
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "D", -1},
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "D", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

/* Keys (potentially) affected by the keypad key mode. */
static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Space[] = {
        {cursor_all, keypad_default, 0, " ", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 " ", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Tab[] = {
        {cursor_all, keypad_default, 0, "\t", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "I", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Enter[] = {
        {cursor_all, keypad_app, VTE_NUMLOCK_MASK, "\r", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "M", -1},
        {cursor_all, keypad_all, 0, "\r", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_F1[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "P", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_F2[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "Q", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_F3[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "R", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_F4[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "S", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Multiply[] = {
        {cursor_all, keypad_default, 0, "*", 1},
        {cursor_all, keypad_app, VTE_NUMLOCK_MASK, "*", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "j", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Add[] = {
        {cursor_all, keypad_default, 0, "+", 1},
        {cursor_all, keypad_app, VTE_NUMLOCK_MASK, "+", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "k", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Separator[] = {
        {cursor_all, keypad_default, 0, ",", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "l", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Subtract[] = {
        {cursor_all, keypad_default, 0, "-", 1},
        {cursor_all, keypad_app, VTE_NUMLOCK_MASK, "-", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "m", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Decimal_Delete[] = {
        {cursor_all, keypad_default, 0, ".", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "3~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Divide[] = {
        {cursor_all, keypad_default, 0, "/", 1},
        {cursor_all, keypad_app, VTE_NUMLOCK_MASK, "/", 1},
        {cursor_all, keypad_app, 0, _VTE_CAP_SS3 "o", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

/* GDK already separates keypad "0" from keypad "Insert", so the only time
 * we'll see this key is when NumLock is on. */
static const struct _vte_keymap_entry _vte_keymap_GDK_KP_0[] = {
        {cursor_all, keypad_all, 0, "0", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_1[] = {
        {cursor_all, keypad_all, 0, "1", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_2[] = {
        {cursor_all, keypad_all, 0, "2", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_3[] = {
        {cursor_all, keypad_all, 0, "3", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_4[] = {
        {cursor_all, keypad_all, 0, "4", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_5[] = {
        {cursor_all, keypad_all, 0, "5", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_6[] = {
        {cursor_all, keypad_all, 0, "6", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_7[] = {
        {cursor_all, keypad_all, 0, "7", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_8[] = {
        {cursor_all, keypad_all, 0, "8", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_9[] = {
        {cursor_all, keypad_all, 0, "9", 1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

/* These are the same keys as above, but without numlock. */
static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Insert[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "2~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_End[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "F", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "F", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Down[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "B", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "B", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Page_Down[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "6~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Left[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "D", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "D", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Begin[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "E", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "E", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Right[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "C", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "C", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Home[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "H", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "H", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Up[] = {
        {cursor_app, keypad_all, 0, _VTE_CAP_SS3 "A", -1},
        {cursor_default, keypad_all, 0, _VTE_CAP_CSI "A", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_KP_Page_Up[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "5~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F1[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, _VTE_CAP_CSI "P", -1},
        {cursor_all, keypad_all, GDK_SHIFT_MASK, _VTE_CAP_CSI "P", -1},
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_CSI "P", -1},
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "P", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F2[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, _VTE_CAP_CSI "Q", -1},
        {cursor_all, keypad_all, GDK_SHIFT_MASK, _VTE_CAP_CSI "Q", -1},
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_CSI "Q", -1},
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "Q", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F3[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, _VTE_CAP_CSI "R", -1},
        {cursor_all, keypad_all, GDK_SHIFT_MASK, _VTE_CAP_CSI "R", -1},
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_CSI "R", -1},
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "R", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F4[] = {
        {cursor_all, keypad_all, GDK_CONTROL_MASK, _VTE_CAP_CSI "S", -1},
        {cursor_all, keypad_all, GDK_SHIFT_MASK, _VTE_CAP_CSI "S", -1},
        {cursor_all, keypad_all, VTE_META_MASK, _VTE_CAP_CSI "S", -1},
        {cursor_all, keypad_all, 0, _VTE_CAP_SS3 "S", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F5[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "15~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F6[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "17~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F7[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "18~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F8[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "19~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F9[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "20~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F10[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "21~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F11[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "23~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F12[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "24~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F13[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "25~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F14[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "26~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F15[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "28~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F16[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "29~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F17[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "31~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F18[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "32~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F19[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "33~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F20[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "34~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F21[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "42~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F22[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "43~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F23[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "44~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F24[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "45~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F25[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "46~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F26[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "47~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F27[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "48~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F28[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "49~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F29[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "50~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F30[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "51~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F31[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "52~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F32[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "53~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F33[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "54~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F34[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "55~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_entry _vte_keymap_GDK_F35[] = {
        {cursor_all, keypad_all, 0, _VTE_CAP_CSI "56~", -1},
        {cursor_all, keypad_all, 0, X_NULL, 0},
};

static const struct _vte_keymap_group {
    guint keyval;
    const struct _vte_keymap_entry *entries;
} _vte_keymap[] = {
    {GDK_KEY_space,             _vte_keymap_GDK_space},
    {GDK_KEY_Return,            _vte_keymap_GDK_Return},
    {GDK_KEY_Escape,            _vte_keymap_GDK_Escape},
    {GDK_KEY_Tab,               _vte_keymap_GDK_Tab},
    {GDK_KEY_ISO_Left_Tab,      _vte_keymap_GDK_ISO_Left_Tab},
    {GDK_KEY_Home,              _vte_keymap_GDK_Home},
    {GDK_KEY_End,               _vte_keymap_GDK_End},
    {GDK_KEY_Insert,            _vte_keymap_GDK_Insert},
    {GDK_KEY_slash,             _vte_keymap_GDK_slash},
    {GDK_KEY_question,          _vte_keymap_GDK_question},
    /* GDK_Delete is all handled in code, due to funkiness. */
    {GDK_KEY_Page_Up,           _vte_keymap_GDK_Page_Up},
    {GDK_KEY_Page_Down,         _vte_keymap_GDK_Page_Down},

    {GDK_KEY_2,                 _vte_keymap_GDK_2},
    {GDK_KEY_3,                 _vte_keymap_GDK_3},
    {GDK_KEY_4,                 _vte_keymap_GDK_4},
    {GDK_KEY_5,                 _vte_keymap_GDK_5},
    {GDK_KEY_6,                 _vte_keymap_GDK_6},
    {GDK_KEY_7,                 _vte_keymap_GDK_7},
    {GDK_KEY_8,                 _vte_keymap_GDK_8},
    {GDK_KEY_minus,             _vte_keymap_GDK_Minus},

    {GDK_KEY_Up,                _vte_keymap_GDK_Up},
    {GDK_KEY_Down,              _vte_keymap_GDK_Down},
    {GDK_KEY_Right,             _vte_keymap_GDK_Right},
    {GDK_KEY_Left,              _vte_keymap_GDK_Left},

    {GDK_KEY_KP_Space,          _vte_keymap_GDK_KP_Space},
    {GDK_KEY_KP_Tab,            _vte_keymap_GDK_KP_Tab},
    {GDK_KEY_KP_Enter,          _vte_keymap_GDK_KP_Enter},
    {GDK_KEY_KP_F1,             _vte_keymap_GDK_KP_F1},
    {GDK_KEY_KP_F2,             _vte_keymap_GDK_KP_F2},
    {GDK_KEY_KP_F3,             _vte_keymap_GDK_KP_F3},
    {GDK_KEY_KP_F4,             _vte_keymap_GDK_KP_F4},
    {GDK_KEY_KP_Multiply,       _vte_keymap_GDK_KP_Multiply},
    {GDK_KEY_KP_Add,            _vte_keymap_GDK_KP_Add},
    {GDK_KEY_KP_Separator,      _vte_keymap_GDK_KP_Separator},
    {GDK_KEY_KP_Subtract,       _vte_keymap_GDK_KP_Subtract},
    {GDK_KEY_KP_Decimal,        _vte_keymap_GDK_KP_Decimal_Delete},
    {GDK_KEY_KP_Delete,         _vte_keymap_GDK_KP_Decimal_Delete},
    {GDK_KEY_KP_Divide,         _vte_keymap_GDK_KP_Divide},
    {GDK_KEY_KP_0,              _vte_keymap_GDK_KP_0},
    {GDK_KEY_KP_Insert,         _vte_keymap_GDK_KP_Insert},
    {GDK_KEY_KP_1,              _vte_keymap_GDK_KP_1},
    {GDK_KEY_KP_End,            _vte_keymap_GDK_KP_End},
    {GDK_KEY_KP_2,              _vte_keymap_GDK_KP_2},
    {GDK_KEY_KP_Down,           _vte_keymap_GDK_KP_Down},
    {GDK_KEY_KP_3,              _vte_keymap_GDK_KP_3},
    {GDK_KEY_KP_Page_Down,      _vte_keymap_GDK_KP_Page_Down},
    {GDK_KEY_KP_4,              _vte_keymap_GDK_KP_4},
    {GDK_KEY_KP_Left,           _vte_keymap_GDK_KP_Left},
    {GDK_KEY_KP_5,              _vte_keymap_GDK_KP_5},
    {GDK_KEY_KP_Begin,          _vte_keymap_GDK_KP_Begin},
    {GDK_KEY_KP_6,              _vte_keymap_GDK_KP_6},
    {GDK_KEY_KP_Right,          _vte_keymap_GDK_KP_Right},
    {GDK_KEY_KP_7,              _vte_keymap_GDK_KP_7},
    {GDK_KEY_KP_Home,           _vte_keymap_GDK_KP_Home},
    {GDK_KEY_KP_8,              _vte_keymap_GDK_KP_8},
    {GDK_KEY_KP_Up,             _vte_keymap_GDK_KP_Up},
    {GDK_KEY_KP_9,              _vte_keymap_GDK_KP_9},
    {GDK_KEY_KP_Page_Up,        _vte_keymap_GDK_KP_Page_Up},

    {GDK_KEY_F1,                _vte_keymap_GDK_F1},
    {GDK_KEY_F2,                _vte_keymap_GDK_F2},
    {GDK_KEY_F3,                _vte_keymap_GDK_F3},
    {GDK_KEY_F4,                _vte_keymap_GDK_F4},
    {GDK_KEY_F5,                _vte_keymap_GDK_F5},
    {GDK_KEY_F6,                _vte_keymap_GDK_F6},
    {GDK_KEY_F7,                _vte_keymap_GDK_F7},
    {GDK_KEY_F8,                _vte_keymap_GDK_F8},
    {GDK_KEY_F9,                _vte_keymap_GDK_F9},
    {GDK_KEY_F10,               _vte_keymap_GDK_F10},
    {GDK_KEY_F11,               _vte_keymap_GDK_F11},
    {GDK_KEY_F12,               _vte_keymap_GDK_F12},
    {GDK_KEY_F13,               _vte_keymap_GDK_F13},
    {GDK_KEY_F14,               _vte_keymap_GDK_F14},
    {GDK_KEY_F15,               _vte_keymap_GDK_F15},
    {GDK_KEY_F16,               _vte_keymap_GDK_F16},
    {GDK_KEY_F17,               _vte_keymap_GDK_F17},
    {GDK_KEY_F18,               _vte_keymap_GDK_F18},
    {GDK_KEY_F19,               _vte_keymap_GDK_F19},
    {GDK_KEY_F20,               _vte_keymap_GDK_F20},
    {GDK_KEY_F21,               _vte_keymap_GDK_F21},
    {GDK_KEY_F22,               _vte_keymap_GDK_F22},
    {GDK_KEY_F23,               _vte_keymap_GDK_F23},
    {GDK_KEY_F24,               _vte_keymap_GDK_F24},
    {GDK_KEY_F25,               _vte_keymap_GDK_F25},
    {GDK_KEY_F26,               _vte_keymap_GDK_F26},
    {GDK_KEY_F27,               _vte_keymap_GDK_F27},
    {GDK_KEY_F28,               _vte_keymap_GDK_F28},
    {GDK_KEY_F29,               _vte_keymap_GDK_F29},
    {GDK_KEY_F30,               _vte_keymap_GDK_F30},
    {GDK_KEY_F31,               _vte_keymap_GDK_F31},
    {GDK_KEY_F32,               _vte_keymap_GDK_F32},
    {GDK_KEY_F33,               _vte_keymap_GDK_F33},
    {GDK_KEY_F34,               _vte_keymap_GDK_F34},
    {GDK_KEY_F35,               _vte_keymap_GDK_F35},
};

/* Map the specified keyval/modifier setup, dependent on the mode, to
 * a literal string. */
void _vte_keymap_map(guint keyval,
                guint modifiers,
                gboolean app_cursor_keys,
                gboolean app_keypad_keys,
                char **normal,
                gssize *normal_length)
{
    gsize i;
    const struct _vte_keymap_entry *entries;
    enum _vte_cursor_mode cursor_mode;
    enum _vte_keypad_mode keypad_mode;

    g_return_if_fail(normal != NULL);
    g_return_if_fail(normal_length != NULL);

    _VTE_DEBUG_IF(VTE_DEBUG_KEYBOARD)
        _vte_keysym_print(keyval, modifiers);

    /* Start from scratch. */
    *normal = NULL;
    *normal_length = 0;

    /* Search for the list for this key. */
    entries = NULL;
    for (i = 0; i < G_N_ELEMENTS(_vte_keymap); i++) {
        if (_vte_keymap[i].keyval == keyval) {
            /* Found it! */
            entries = _vte_keymap[i].entries;
            break;
        }
    }
    if (entries == NULL) {
        _vte_debug_print(VTE_DEBUG_KEYBOARD, " (ignoring, no map for key).\n");
        return;
    }

    /* Build mode masks. */
    cursor_mode = app_cursor_keys ? cursor_app : cursor_default;
    keypad_mode = app_keypad_keys ? keypad_app : keypad_default;
    modifiers &= GDK_SHIFT_MASK | GDK_CONTROL_MASK | VTE_META_MASK | VTE_NUMLOCK_MASK;

    /* Search for the conditions. */
    for (i = 0; entries[i].normal_length; i++) {
        if ((entries[i].cursor_mode & cursor_mode) &&
            (entries[i].keypad_mode & keypad_mode)) {
            if ((modifiers & entries[i].mod_mask) == entries[i].mod_mask) {
                if (entries[i].normal_length != -1) {
                    *normal_length = entries[i].normal_length;
/* g_memdup2() is only available in GLib 2.67.3 and later */
#if GLIB_CHECK_VERSION(2,67,3)
                    *normal = (char*)g_memdup2(entries[i].normal,
                                            entries[i].normal_length);
#else
                    *normal = (char*)g_memdup(entries[i].normal,
                                            entries[i].normal_length);
#endif
                } else {
                    *normal_length = strlen(entries[i].normal);
                    *normal = g_strdup(entries[i].normal);
                }
                _vte_keymap_key_add_key_modifiers(keyval,
                                                    modifiers,
                                                    cursor_mode & cursor_app,
                                                    normal,
                                                    normal_length);
                _vte_debug_print(VTE_DEBUG_KEYBOARD,
                                    " to '%s'.\n",
                                    _vte_debug_sequence_to_string(*normal, *normal_length));
                return;
            }
        }
    }

    _vte_debug_print(VTE_DEBUG_KEYBOARD, " (ignoring, no match for modifier state).\n");
}

gboolean _vte_keymap_key_is_modifier(guint keyval)
{
    gboolean modifier = FALSE;
    /* Determine if this is just a modifier key. */
    switch (keyval) {
        case GDK_KEY_Alt_L:
        case GDK_KEY_Alt_R:
        case GDK_KEY_Caps_Lock:
        case GDK_KEY_Control_L:
        case GDK_KEY_Control_R:
        case GDK_KEY_Eisu_Shift:
        case GDK_KEY_Hyper_L:
        case GDK_KEY_Hyper_R:
        case GDK_KEY_ISO_First_Group:
        case GDK_KEY_ISO_First_Group_Lock:
        case GDK_KEY_ISO_Group_Latch:
        case GDK_KEY_ISO_Group_Lock:
        case GDK_KEY_ISO_Group_Shift:
        case GDK_KEY_ISO_Last_Group:
        case GDK_KEY_ISO_Last_Group_Lock:
        case GDK_KEY_ISO_Level2_Latch:
        case GDK_KEY_ISO_Level3_Latch:
        case GDK_KEY_ISO_Level3_Lock:
        case GDK_KEY_ISO_Level3_Shift:
        case GDK_KEY_ISO_Level5_Latch:
        case GDK_KEY_ISO_Level5_Lock:
        case GDK_KEY_ISO_Level5_Shift:
        case GDK_KEY_ISO_Lock:
        case GDK_KEY_ISO_Next_Group:
        case GDK_KEY_ISO_Next_Group_Lock:
        case GDK_KEY_ISO_Prev_Group:
        case GDK_KEY_ISO_Prev_Group_Lock:
        case GDK_KEY_Kana_Lock:
        case GDK_KEY_Kana_Shift:
        case GDK_KEY_Meta_L:
        case GDK_KEY_Meta_R:
        case GDK_KEY_ModeLock:
        case GDK_KEY_Num_Lock:
        case GDK_KEY_Scroll_Lock:
        case GDK_KEY_Shift_L:
        case GDK_KEY_Shift_Lock:
        case GDK_KEY_Shift_R:
        case GDK_KEY_Super_L:
        case GDK_KEY_Super_R:
            modifier = TRUE;
            break;
        default:
            modifier = FALSE;
            break;
    }
    return modifier;
}

static enum _vte_modifier_encoding_method
_vte_keymap_key_get_modifier_encoding_method(guint keyval)
{
    enum _vte_modifier_encoding_method method = MODIFIER_ENCODING_NONE;
    /* Determine if this key gets modifiers. */
    switch (keyval) {
        case GDK_KEY_Up:
        case GDK_KEY_Down:
        case GDK_KEY_Left:
        case GDK_KEY_Right:
        case GDK_KEY_Insert:
        case GDK_KEY_Delete:
        case GDK_KEY_Home:
        case GDK_KEY_End:
        case GDK_KEY_Page_Up:
        case GDK_KEY_Page_Down:
        case GDK_KEY_KP_Up:
        case GDK_KEY_KP_Down:
        case GDK_KEY_KP_Left:
        case GDK_KEY_KP_Right:
        case GDK_KEY_KP_Insert:
        case GDK_KEY_KP_Delete:
        case GDK_KEY_KP_Home:
        case GDK_KEY_KP_End:
        case GDK_KEY_KP_Page_Up:
        case GDK_KEY_KP_Page_Down:
        case GDK_KEY_KP_Begin:
        case GDK_KEY_F1:
        case GDK_KEY_F2:
        case GDK_KEY_F3:
        case GDK_KEY_F4:
        case GDK_KEY_F5:
        case GDK_KEY_F6:
        case GDK_KEY_F7:
        case GDK_KEY_F8:
        case GDK_KEY_F9:
        case GDK_KEY_F10:
        case GDK_KEY_F11:
        case GDK_KEY_F12:
        case GDK_KEY_F13:
        case GDK_KEY_F14:
        case GDK_KEY_F15:
        case GDK_KEY_F16:
        case GDK_KEY_F17:
        case GDK_KEY_F18:
        case GDK_KEY_F19:
        case GDK_KEY_F20:
        case GDK_KEY_F21:
        case GDK_KEY_F22:
        case GDK_KEY_F23:
        case GDK_KEY_F24:
        case GDK_KEY_F25:
        case GDK_KEY_F26:
        case GDK_KEY_F27:
        case GDK_KEY_F28:
        case GDK_KEY_F29:
        case GDK_KEY_F30:
        case GDK_KEY_F31:
        case GDK_KEY_F32:
        case GDK_KEY_F33:
        case GDK_KEY_F34:
        case GDK_KEY_F35:
            method = MODIFIER_ENCODING_LONG;
            break;
        case GDK_KEY_KP_Divide:
        case GDK_KEY_KP_Multiply:
        case GDK_KEY_KP_Subtract:
        case GDK_KEY_KP_Add:
        case GDK_KEY_KP_Enter:
            method = MODIFIER_ENCODING_SHORT;
            break;
        default:
            method = MODIFIER_ENCODING_NONE;
            break;
    }
    return method;
}

/* Prior and Next are ommitted for the SS3 to CSI switch below */
static gboolean is_cursor_key(guint keyval)
{
    switch (keyval) {
        case GDK_KEY_Home:
        case GDK_KEY_Left:
        case GDK_KEY_Up:
        case GDK_KEY_Right:
        case GDK_KEY_Down:
        case GDK_KEY_End:
        case GDK_KEY_Begin:

        case GDK_KEY_KP_Home:
        case GDK_KEY_KP_Left:
        case GDK_KEY_KP_Up:
        case GDK_KEY_KP_Right:
        case GDK_KEY_KP_Down:
        case GDK_KEY_KP_End:
        case GDK_KEY_KP_Begin:
            return TRUE;
        default:
            return FALSE;
    }
}

void _vte_keymap_key_add_key_modifiers(guint keyval,
                                        guint modifiers,
                                        gboolean cursor_app_mode,
                                        char **normal,
                                        gssize *normal_length)
{
    int modifier, offset;
    char *nnormal;
    enum _vte_modifier_encoding_method modifier_encoding_method;
    guint significant_modifiers;

    significant_modifiers = GDK_SHIFT_MASK |
                GDK_CONTROL_MASK |
                VTE_META_MASK;

    modifier_encoding_method = _vte_keymap_key_get_modifier_encoding_method(keyval);
    if (modifier_encoding_method == MODIFIER_ENCODING_NONE) {
        return;
    }

    switch (modifiers & significant_modifiers) {
        case 0:
            modifier = 0;
            break;
        case GDK_SHIFT_MASK:
            modifier = 2;
            break;
        case VTE_META_MASK:
            modifier = 3;
            break;
        case GDK_SHIFT_MASK | VTE_META_MASK:
            modifier = 4;
            break;
        case GDK_CONTROL_MASK:
            modifier = 5;
            break;
        case GDK_SHIFT_MASK | GDK_CONTROL_MASK:
            modifier = 6;
            break;
        case VTE_META_MASK | GDK_CONTROL_MASK:
            modifier = 7;
            break;
        case GDK_SHIFT_MASK | VTE_META_MASK | GDK_CONTROL_MASK:
            modifier = 8;
            break;
        default:
            modifier = 8;
            break;
    }

    if (modifier == 0) {
        return;
    }

    nnormal = g_new0(char, *normal_length + 4);
    memcpy(nnormal, *normal, *normal_length);
    if (strlen(nnormal) > 1) {
        /* SS3 should have no modifiers so make it CSI instead. See
         * http://cvsweb.xfree86.org/cvsweb/xc/programs/xterm/input.c.diff?r1=3.57&r2=3.58
         */
        if (cursor_app_mode &&
            g_str_has_prefix(nnormal, _VTE_CAP_SS3)
            && is_cursor_key(keyval)) {
            nnormal[1] = '[';
        }

        /* Get the offset of the last character. */
        offset = strlen(nnormal) - 1;
        if (g_ascii_isdigit(nnormal[offset - 1])) {
            /* Stuff a semicolon and the modifier in right before
             * that last character. */
            nnormal[offset + 2] = nnormal[offset];
            nnormal[offset + 1] = modifier + '0';
            nnormal[offset + 0] = ';';
            *normal_length += 2;
        } else if (modifier_encoding_method == MODIFIER_ENCODING_LONG) {
            /* Stuff a "1", a semicolon and the modifier in right
             * before that last character, matching Xterm most of the time. */
            nnormal[offset + 3] = nnormal[offset];
            nnormal[offset + 2] = modifier + '0';
            nnormal[offset + 1] = ';';
            nnormal[offset + 0] = '1';
            *normal_length += 3;
        } else {
            /* Stuff the modifier in right before that last
             * character, matching what people expect,
             * and what Xterm does with numpad math operators */
            nnormal[offset + 1] = nnormal[offset];
            nnormal[offset + 0] = modifier + '0';
            *normal_length += 1;
        }
        g_free(*normal);
        *normal = nnormal;
    } else {
        g_free(nnormal);
    }
}
