/*************************************************************************
 *
 * SDL_keybd.c - Keyboard initialisation module of FB test program.
 *
 * $Id$
 *
 * Copyright 2004-2014 Citrix Systems, Inc.  All Rights Reserved.
 *
 *************************************************************************/

#include "citrix.h"

#include <unistd.h>
#include <stdio.h>

#include <SDL/SDL_events.h>
#include <SDL/SDL_keyboard.h>

#include "mainloop.h"
#include "simplified.h"
#include "mlooplog.h"
#include "xkeytran.h"
 
#include "CTX_SDL_internal.h"

//#define RESIZE_TEST /* Keys F1, F2, F3 generate resize requests. */

/* Table to translate SDL keycodes to ICA special key codes.
 * Indexed by SDLKey value and based at SDLK_KP0.
 */

static SpecialKeys special_table[] = {
    /* Numeric keypad */

    ICAKEY_KP_0,        /* SDLK_KP0		= 256 */
    ICAKEY_KP_1,        /* SDLK_KP1		= 257 */
    ICAKEY_KP_2,        /* SDLK_KP2		= 258 */
    ICAKEY_KP_3,        /* SDLK_KP3		= 259 */
    ICAKEY_KP_4,        /* SDLK_KP4		= 260 */
    ICAKEY_KP_5,        /* SDLK_KP5		= 261 */
    ICAKEY_KP_6,        /* SDLK_KP6		= 262 */
    ICAKEY_KP_7,        /* SDLK_KP7		= 263 */
    ICAKEY_KP_8,        /* SDLK_KP8		= 264 */
    ICAKEY_KP_9,        /* SDLK_KP9		= 265 */
    ICAKEY_KP_DELETE,   /* SDLK_KP_PERIOD	= 266 */
    ICAKEY_KP_DIVIDE,   /* SDLK_KP_DIVIDE	= 267 */
    ICAKEY_KP_MULT,     /* SDLK_KP_MULTIPLY	= 268 */
    ICAKEY_KP_MINUS,    /* SDLK_KP_MINUS	= 269 */
    ICAKEY_KP_PLUS,     /* SDLK_KP_PLUS		= 270 */
    ICAKEY_KP_ENTER,    /* SDLK_KP_ENTER	= 271 */
    NO_TRAN + '=',      /* SDLK_KP_EQUALS	= 272 */

    /* Arrows + Home/End pad */

    ICAKEY_UP,          /* SDLK_UP		= 273 */
    ICAKEY_DOWN,        /* SDLK_DOWN		= 274 */
    ICAKEY_RIGHT,       /* SDLK_RIGHT		= 275 */
    ICAKEY_LEFT,        /* SDLK_LEFT		= 276 */
    ICAKEY_INSERT,      /* SDLK_INSERT		= 277 */
    ICAKEY_HOME,        /* SDLK_HOME		= 278 */
    ICAKEY_END,         /* SDLK_END		= 279 */
    ICAKEY_PAGEUP,      /* SDLK_PAGEUP		= 280 */
    ICAKEY_PAGEDOWN,    /* SDLK_PAGEDOWN	= 281 */

    /* Function keys */

    ICAKEY_F1,          /* SDLK_F1		= 282 */
    ICAKEY_F2,          /* SDLK_F2		= 283 */
    ICAKEY_F3,          /* SDLK_F3		= 284 */
    ICAKEY_F4,          /* SDLK_F4		= 285 */
    ICAKEY_F5,          /* SDLK_F5		= 286 */
    ICAKEY_F6,          /* SDLK_F6		= 287 */
    ICAKEY_F7,          /* SDLK_F7		= 288 */
    ICAKEY_F8,          /* SDLK_F8		= 289 */
    ICAKEY_F9,          /* SDLK_F9		= 290 */
    ICAKEY_F10,         /* SDLK_F10		= 291 */
    ICAKEY_F11,         /* SDLK_F11		= 292 */
    ICAKEY_F12,         /* SDLK_F12		= 293 */
    ICAKEY_F13,         /* SDLK_F13		= 294 */
    ICAKEY_F14,         /* SDLK_F14		= 295 */
    ICAKEY_F15,         /* SDLK_F15		= 296 */
    ICAKEY_F16,         /* undefined		= 297 */
    ICAKEY_F17,         /* undefined		= 298 */
    ICAKEY_F18,         /* undefined		= 299 */

    /* Key state modifier keys */

    ICAKEY_NUM_LOCK,    /* SDLK_NUMLOCK		= 300 */
    ICAKEY_CAPS_LOCK,   /* SDLK_CAPSLOCK	= 301 */
    ICAKEY_SCROLL_LOCK, /* SDLK_SCROLLOCK	= 302 */
    ICAKEY_SHIFT_R,     /* SDLK_RSHIFT		= 303 */
    ICAKEY_SHIFT_L,     /* SDLK_LSHIFT		= 304 */
    ICAKEY_CONTROL_R,   /* SDLK_RCTRL		= 305 */
    ICAKEY_CONTROL_L,   /* SDLK_LCTRL		= 306 */
    ICAKEY_ALT_R,       /* SDLK_RALT		= 307 */
    ICAKEY_ALT_L,       /* SDLK_LALT		= 308 */
    ICAKEY_ALT_R,       /* SDLK_RMETA		= 309 */
    ICAKEY_ALT_L,       /* SDLK_LMETA		= 310 */
    ICAKEY_WINDOWS_L,   /* SDLK_LSUPER		= 311   Left "Windows" key */
    ICAKEY_WINDOWS_R,	/* SDLK_RSUPER		= 312   Right "Windows" key */
    ICAKEY_ALT_R,       /* SDLK_MODE		= 313   "Alt Gr" key */
    ICAKEY_APPLICATION, /* SDLK_COMPOSE		= 314   Multi-key/compose ?? */

    /* Miscellaneous function keys */

    ICAKEY_F1,          /* SDLK_HELP		= 315 ?? */
    ICAKEY_PRINT_SCREEN,/* SDLK_PRINT		= 316 */
    ICAKEY_PRINT_SCREEN,/* SDLK_SYSREQ		= 317 */
    ICAKEY_PAUSE,       /* SDLK_BREAK		= 318 */
    ICAKEY_APPLICATION, /* SDLK_MENU		= 319 */
    ICAKEY_APPLICATION, /* SDLK_POWER		= 320   Macintosh power key ?*/
    NO_TRAN + 0x20ac,   /* SDLK_EURO		= 321 */
    NO_TRAN + 'Z' - 32, /* SDLK_UNDO		= 322   Atari keyboard */
};

/* Local structure to hold details of pressed keys - similar to keysdown.c. */

static struct pressed {
    struct pressed     *next;
    Uint8               scancode;
    Uint8               is_special;
    Uint16              value;
} *down_list, *free_list;

/* Handle an SDL keyboard event. */

void handle_SDL_keyboard(SDL_KeyboardEvent *ev)
{
    SpecialKeys                         special;
    enum {Unknown, Unicode, Special}    key_type;
    unsigned short                      uc;
    int                                 flags, indx;
    struct pressed                     *p;


    flags = (ev->type == SDL_KEYUP) ? WND_UNICODECHAR_KEYUP : 0;

#ifdef DEBUG
    {
        static char         visible[] = ": ' '";

        visible[3] = ev->keysym.sym;
        MLoopTrace("Got scan %d %s symbol %d unicode %#x%s\n",
               ev->keysym.scancode,
               (ev->type == SDL_KEYUP) ? "up" : "down",
               ev->keysym.sym, ev->keysym.unicode,
               (ev->keysym.sym >= ' ' && ev->keysym.sym < 128) ?
                   visible : "");
    }
#endif /* DEBUG */

    if (ev->type == SDL_KEYUP) {
        struct pressed **pp;

       /* SDL does not provide Unicode translations for releases,
         * so we send what was sent for the matching key press.
         * That means releases with no press are ignored, probably rightly.
         */

        pp = &down_list;
        while ((p = *pp)) {
            if (p->scancode == ev->keysym.scancode) {
                *pp = p->next;
                p->next = free_list;
                free_list = p;
                break;
            }
            pp = &p->next;
        }
        if (p) {
            /* Found record of key press. */

            uc = p->value;
        } else {
            return;     /* Random key release. */
        }
    } else {
        /* Key pressed. */

        key_type = Unknown;
        if (ev->keysym.sym < 128) {
            /* Treat as ASCII code, except that SDL likes to report some
             * non-character keys as control characters.  Translate those back.
             */

            if (((ev->keysym.sym > 0 && ev->keysym.sym < ' ') ||
                 ev->keysym.sym == 127) &&
                (ev->keysym.mod & KMOD_CTRL) == 0) {
                /* A control character, but no control key is down. */

                key_type = Special;             /* For now ... */
                switch (ev->keysym.sym) {
                case 8:
                    uc = ICAKEY_BACKSPACE;
                    break;
                case 9:
                    uc = ICAKEY_TAB;
                    break;
                case 13:
                    uc = ICAKEY_ENTER;
                    break;
                case 19:    /* Control-S! */
                    uc = ICAKEY_PAUSE;
                    break;
                case 27:
                    uc = ICAKEY_ESCAPE;
                    break;
                case 127:
                    uc = ICAKEY_DELETE;
                    break;
                default:
                    key_type = Unicode;
                    uc = ev->keysym.sym;
                    break;
                }
            }
        } else if (ev->keysym.sym < SDLK_LAST) {
            /* One of SDL's special keys. */

            indx = ev->keysym.sym - SDLK_KP0;
            if (indx >= 0 &&
                indx < (sizeof special_table) / (sizeof special_table[0])) {
                /* Probably an ICA special key */

                special = special_table[indx];
                if (special < NO_TRAN) {
                    uc = special;
                    key_type = Special;
#ifdef RESIZE_TEST
                    if (uc == ICAKEY_F1) {
                        Send_session_resize(400, 400, TRUE);  /* Make small. */
                        return;
                    } else if (uc == ICAKEY_F2) {
                        Send_session_resize(800, 600, TRUE);  /* Medium. */
                        return;
                    } else if (uc == ICAKEY_F3) {
                        Send_session_resize(1000, 800, TRUE); /* Larger. */
                        return;
                    }
#endif
                } else {
                    key_type = Unicode;
                    uc =  special - NO_TRAN;    /* Unicode table entries. */
                }
            }
        }

        if (key_type == Unknown) {
            uc = ev->keysym.unicode;
            if (uc > 0)
                key_type = Unicode;     /* Use Unicode value. */
            else
                return;                 /* No value found. */
        }

        /* This keystroke will be forwarded to the server, so record what
         * we sent for use when the key is released.
         */

        p = free_list;
        if (p)
            free_list = p->next;
        else
            p = malloc(sizeof *p);

        if (p) {
            p->next = down_list;
            down_list = p;
            p->scancode = ev->keysym.scancode;
            p->is_special = (key_type == Special);
            p->value = uc;
        }
    }
        
    if (p->is_special)
        flags |= WND_UNICODECHAR_SPECIAL;
    Send_unicode(uc, flags);
}

