view src/video/photon/SDL_ph_events.c @ 2288:e9bcf004c324

Merged fix for bug #482 from SDL 1.2 revision 3492.
author Sam Lantinga <slouken@libsdl.org>
date Sat, 29 Dec 2007 20:13:23 +0000
parents c121d94672cb
children 99210400e8b9
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    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 St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

/* Handle the event stream, converting photon events into SDL events */

#include <stdio.h>
#include <setjmp.h>
#include <sys/time.h>

#include <Ph.h>
#include <photon/PkKeyDef.h>

#include "SDL.h"
#include "SDL_syswm.h"
#include "../SDL_sysvideo.h"
#include "../../events/SDL_sysevents.h"
#include "../../events/SDL_events_c.h"
#include "SDL_ph_video.h"
#include "SDL_ph_modes_c.h"
#include "SDL_ph_image_c.h"
#include "SDL_ph_events_c.h"
#include "SDL_phyuv_c.h"

/* The translation tables from a photon keysym to a SDL keysym */
static SDLKey ODD_keymap[256];
static SDLKey MISC_keymap[0xFF + 1];
SDL_keysym *ph_TranslateKey(PhKeyEvent_t * key, SDL_keysym * keysym);

/* Check to see if this is a repeated key.
   (idea shamelessly lifted from GII -- thanks guys! :) */
static int
ph_WarpedMotion(_THIS, PhEvent_t * winEvent)
{
    PhRect_t *rect = PhGetRects(winEvent);

    int centre_x, centre_y;
    int dx, dy;
    short abs_x, abs_y;
    int posted;

    centre_x = SDL_VideoSurface->w / 2;
    centre_y = SDL_VideoSurface->h / 2;

    dx = rect->ul.x - centre_x;
    dy = rect->ul.y - centre_y;

    posted = SDL_PrivateMouseMotion(0, 1, dx, dy);

    /* Move mouse cursor to middle of the window */
    PtGetAbsPosition(window, &abs_x, &abs_y);
    PhMoveCursorAbs(PhInputGroup(NULL), abs_x + centre_x, abs_y + centre_y);

    return (posted);
}

/* Control which motion flags the window has set, a flags value of -1 sets
 * MOTION_BUTTON and MOTION_NOBUTTON */

static void
set_motion_sensitivity(_THIS, unsigned int flags)
{
    int rid;
    int fields = Ph_EV_PTR_MOTION_BUTTON | Ph_EV_PTR_MOTION_NOBUTTON;
    PhRegion_t region;

    if (window) {
        rid = PtWidgetRid(window);
        if (rid != 0 && PhRegionQuery(rid, &region, NULL, NULL, 0) == 0) {
            region.events_sense =
                (region.events_sense & ~fields) | (flags & fields);
            PhRegionChange(Ph_REGION_EV_SENSE, 0, &region, NULL, NULL);
        }
    }
}

/* Convert the photon button state value to an SDL value */
static Uint8
ph2sdl_mousebutton(unsigned short button_state)
{
    Uint8 mouse_button = 0;

    if (button_state & Ph_BUTTON_SELECT)
        mouse_button |= SDL_BUTTON_LEFT;
    if (button_state & Ph_BUTTON_MENU)
        mouse_button |= SDL_BUTTON_RIGHT;
    if (button_state & Ph_BUTTON_ADJUST)
        mouse_button |= SDL_BUTTON_MIDDLE;

    return (mouse_button);
}

static int
ph_DispatchEvent(_THIS)
{
    int posted;
    PhRect_t *rect;
    PhPointerEvent_t *pointerEvent;
    PhKeyEvent_t *keyEvent;
    PhWindowEvent_t *winEvent;
    int i, buttons;
    SDL_Rect sdlrects[PH_SDL_MAX_RECTS];

    posted = 0;

    switch (phevent->type) {
    case Ph_EV_BOUNDARY:
        {
            if (phevent->subtype == Ph_EV_PTR_ENTER) {
                posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
            } else if (phevent->subtype == Ph_EV_PTR_LEAVE) {
                posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
            }
        }
        break;

    case Ph_EV_PTR_MOTION_BUTTON:
    case Ph_EV_PTR_MOTION_NOBUTTON:
        {
            if (SDL_VideoSurface) {
                pointerEvent = PhGetData(phevent);
                rect = PhGetRects(phevent);

                if (mouse_relative) {
                    posted = ph_WarpedMotion(this, phevent);
                } else {
                    posted =
                        SDL_PrivateMouseMotion(0, 0, rect->ul.x, rect->ul.y);
                }
            }
        }
        break;

    case Ph_EV_BUT_PRESS:
        {
            pointerEvent = PhGetData(phevent);
            buttons = ph2sdl_mousebutton(pointerEvent->buttons);
            if (buttons != 0) {
                posted = SDL_PrivateMouseButton(SDL_PRESSED, buttons, 0, 0);
            }
        }
        break;

    case Ph_EV_BUT_RELEASE:
        {
            pointerEvent = PhGetData(phevent);
            buttons = ph2sdl_mousebutton(pointerEvent->buttons);
            if (phevent->subtype == Ph_EV_RELEASE_REAL && buttons != 0) {
                posted = SDL_PrivateMouseButton(SDL_RELEASED, buttons, 0, 0);
            } else if (phevent->subtype == Ph_EV_RELEASE_PHANTOM) {
                /* If the mouse is outside the window,
                 * only a phantom release event is sent, so
                 * check if the window doesn't have mouse focus.
                 * Not perfect, maybe checking the mouse button
                 * state for Ph_EV_BOUNDARY events would be
                 * better. */
                if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS) == 0) {
                    posted =
                        SDL_PrivateMouseButton(SDL_RELEASED, buttons, 0, 0);
                }
            }
        }
        break;

    case Ph_EV_WM:
        {
            winEvent = PhGetData(phevent);

            /* losing focus */
            if ((winEvent->event_f == Ph_WM_FOCUS)
                && (winEvent->event_state == Ph_WM_EVSTATE_FOCUSLOST)) {
                set_motion_sensitivity(this, Ph_EV_PTR_MOTION_BUTTON);
                posted = SDL_PrivateAppActive(0, SDL_APPINPUTFOCUS);
            }
            /* gaining focus */
            else if ((winEvent->event_f == Ph_WM_FOCUS)
                     && (winEvent->event_state == Ph_WM_EVSTATE_FOCUS)) {
                set_motion_sensitivity(this, -1);
                posted = SDL_PrivateAppActive(1, SDL_APPINPUTFOCUS);
            }
            /* request quit */
            else if (winEvent->event_f == Ph_WM_CLOSE) {
                posted = SDL_PrivateQuit();
            }
            /* request hide/unhide */
            else if (winEvent->event_f == Ph_WM_HIDE) {
                if (currently_hided) {
                    /* got unhide window event                                */
                    /* TODO: restore application's palette if in palette mode */
                    currently_hided = 0;
                } else {
                    /* got hide window event                                  */
                    /* TODO: restore original palette if in palette mode      */
                    currently_hided = 1;
                }
            }
            /* request to resize */
            else if (winEvent->event_f == Ph_WM_RESIZE) {
                currently_maximized = 0;
#if (_NTO_VERSION < 630)
                SDL_PrivateResize(winEvent->size.w + 1, winEvent->size.h + 1);
#else
                /* QNX 6.3.0 have this bug fixed */
                SDL_PrivateResize(winEvent->size.w, winEvent->size.h);
#endif /* _NTO_VERSION */
            }
            /* request to move */
            else if (winEvent->event_f == Ph_WM_MOVE) {
                if (current_overlay != NULL) {
                    int lockedstate = current_overlay->hwdata->locked;
                    int chromastate = current_overlay->hwdata->ischromakey;
                    int error;
                    SDL_Rect src, dst;

                    current_overlay->hwdata->locked = 1;
                    src.x = 0;
                    src.y = 0;
                    src.w = current_overlay->w;
                    src.y = current_overlay->h;
                    dst.x = current_overlay->hwdata->CurrentViewPort.pos.x;
                    dst.y = current_overlay->hwdata->CurrentViewPort.pos.y;
                    dst.w = current_overlay->hwdata->CurrentViewPort.size.w;
                    dst.h = current_overlay->hwdata->CurrentViewPort.size.h;
                    current_overlay->hwdata->ischromakey = 0;
                    error =
                        ph_DisplayYUVOverlay(this, current_overlay,
                                             &src, &dst);
                    if (!error) {
                        current_overlay->hwdata->ischromakey = chromastate;
                        current_overlay->hwdata->locked = lockedstate;
                    }
                }
            }
            /* maximize request */
            else if (winEvent->event_f == Ph_WM_MAX) {
                /* window already moved and resized here */
                currently_maximized = 1;
            }
            /* restore request */
            else if (winEvent->event_f == Ph_WM_RESTORE) {
                /* window already moved and resized here */
                currently_maximized = 0;
            }
        }
        break;

        /* window has been resized, moved or removed */
    case Ph_EV_EXPOSE:
        {
            if (phevent->num_rects != 0) {
                int numrects;

                if (SDL_VideoSurface) {
                    rect = PhGetRects(phevent);
                    if (phevent->num_rects > PH_SDL_MAX_RECTS) {
                        /* sorry, buffers underrun, we'll update only first PH_SDL_MAX_RECTS rects */
                        numrects = PH_SDL_MAX_RECTS;
                    }

                    for (i = 0; i < phevent->num_rects; i++) {
                        sdlrects[i].x = rect[i].ul.x;
                        sdlrects[i].y = rect[i].ul.y;
                        sdlrects[i].w = rect[i].lr.x - rect[i].ul.x + 1;
                        sdlrects[i].h = rect[i].lr.y - rect[i].ul.y + 1;
                    }

                    this->UpdateRects(this, phevent->num_rects, sdlrects);

                    if (current_overlay != NULL) {
                        int lockedstate = current_overlay->hwdata->locked;
                        int error;
                        SDL_Rect src, dst;

                        current_overlay->hwdata->locked = 1;
                        src.x = 0;
                        src.y = 0;
                        src.w = current_overlay->w;
                        src.y = current_overlay->h;
                        dst.x =
                            current_overlay->hwdata->CurrentViewPort.pos.x;
                        dst.y =
                            current_overlay->hwdata->CurrentViewPort.pos.y;
                        dst.w =
                            current_overlay->hwdata->CurrentViewPort.size.w;
                        dst.h =
                            current_overlay->hwdata->CurrentViewPort.size.h;
                        current_overlay->hwdata->forcedredraw = 1;
                        error =
                            ph_DisplayYUVOverlay(this,
                                                 current_overlay, &src, &dst);
                        if (!error) {
                            current_overlay->hwdata->forcedredraw = 0;
                            current_overlay->hwdata->locked = lockedstate;
                        }
                    }
                }
            }
        }
        break;

    case Ph_EV_KEY:
        {
            SDL_keysym keysym;

            posted = 0;

            keyEvent = PhGetData(phevent);

            if (Pk_KF_Key_Down & keyEvent->key_flags) {
                /* split the wheel events from real key events */
                if ((keyEvent->key_cap == Pk_Up)
                    && (keyEvent->key_scan == 0)
                    && ((keyEvent->key_flags & Pk_KF_Scan_Valid) ==
                        Pk_KF_Scan_Valid)) {
                    posted =
                        SDL_PrivateMouseButton(SDL_PRESSED,
                                               SDL_BUTTON_WHEELUP, 0, 0);
                    break;
                }
                if ((keyEvent->key_cap == Pk_Down)
                    && (keyEvent->key_scan == 0)
                    && ((keyEvent->key_flags & Pk_KF_Scan_Valid) ==
                        Pk_KF_Scan_Valid)) {
                    posted =
                        SDL_PrivateMouseButton(SDL_PRESSED,
                                               SDL_BUTTON_WHEELDOWN, 0, 0);
                    break;
                }
                posted =
                    SDL_PrivateKeyboard(SDL_PRESSED,
                                        ph_TranslateKey(keyEvent, &keysym));
            } else {            /* must be key release */

                /* split the wheel events from real key events */
                if ((keyEvent->key_cap == Pk_Up)
                    && (keyEvent->key_scan == 0)
                    && ((keyEvent->key_flags & Pk_KF_Scan_Valid) ==
                        Pk_KF_Scan_Valid)) {
                    posted =
                        SDL_PrivateMouseButton(SDL_RELEASED,
                                               SDL_BUTTON_WHEELUP, 0, 0);
                    break;
                }
                if ((keyEvent->key_cap == Pk_Down)
                    && (keyEvent->key_scan == 0)
                    && ((keyEvent->key_flags & Pk_KF_Scan_Valid) ==
                        Pk_KF_Scan_Valid)) {
                    posted =
                        SDL_PrivateMouseButton(SDL_RELEASED,
                                               SDL_BUTTON_WHEELDOWN, 0, 0);
                    break;
                }
                posted =
                    SDL_PrivateKeyboard(SDL_RELEASED,
                                        ph_TranslateKey(keyEvent, &keysym));
            }
        }
        break;

    case Ph_EV_INFO:
        {
            if (phevent->subtype == Ph_OFFSCREEN_INVALID) {
                unsigned long *EvInfoData;

                EvInfoData = (unsigned long *) PhGetData(phevent);

                switch (*EvInfoData) {
                case Pg_VIDEO_MODE_SWITCHED:
                    {
                    }
                    break;
                case Pg_ENTERED_DIRECT:
                    {
                    }
                    break;
                case Pg_EXITED_DIRECT:
                    {
                    }
                    break;
                case Pg_DRIVER_STARTED:
                    {
                    }
                    break;
                }
            }
        }
        break;
    }

    return (posted);
}

/* perform a blocking read if no events available */
int
ph_Pending(_THIS)
{
    /* Flush the display connection and look to see if events are queued */
    PgFlush();

    while (1) {
        switch (PhEventPeek(phevent, EVENT_SIZE)) {
        case Ph_EVENT_MSG:
            return 1;
        case -1:
            SDL_SetError("ph_Pending(): PhEventNext failed.\n");
            return 0;
        default:
            return 0;
        }
    }

    /* Oh well, nothing is ready .. */
    return (0);
}

void
ph_PumpEvents(_THIS)
{
    /* Flush the display connection and look to see if events are queued */
    PgFlush();

    while (ph_Pending(this)) {
        PtEventHandler(phevent);
        ph_DispatchEvent(this);
    }
}

void
ph_InitKeymap(void)
{
    int i;

    /* Odd keys used in international keyboards */
    for (i = 0; i < SDL_arraysize(ODD_keymap); ++i) {
        ODD_keymap[i] = SDLK_UNKNOWN;
    }

    /* Map the miscellaneous keys */
    for (i = 0; i < SDL_arraysize(MISC_keymap); ++i) {
        MISC_keymap[i] = SDLK_UNKNOWN;
    }

    MISC_keymap[Pk_BackSpace & 0xFF] = SDLK_BACKSPACE;
    MISC_keymap[Pk_Tab & 0xFF] = SDLK_TAB;
    MISC_keymap[Pk_Clear & 0xFF] = SDLK_CLEAR;
    MISC_keymap[Pk_Return & 0xFF] = SDLK_RETURN;
    MISC_keymap[Pk_Pause & 0xFF] = SDLK_PAUSE;
    MISC_keymap[Pk_Escape & 0xFF] = SDLK_ESCAPE;
    MISC_keymap[Pk_Delete & 0xFF] = SDLK_DELETE;

    MISC_keymap[Pk_KP_0 & 0xFF] = SDLK_KP0;
    MISC_keymap[Pk_KP_1 & 0xFF] = SDLK_KP1;
    MISC_keymap[Pk_KP_2 & 0xFF] = SDLK_KP2;
    MISC_keymap[Pk_KP_3 & 0xFF] = SDLK_KP3;
    MISC_keymap[Pk_KP_4 & 0xFF] = SDLK_KP4;
    MISC_keymap[Pk_KP_5 & 0xFF] = SDLK_KP5;
    MISC_keymap[Pk_KP_6 & 0xFF] = SDLK_KP6;
    MISC_keymap[Pk_KP_7 & 0xFF] = SDLK_KP7;
    MISC_keymap[Pk_KP_8 & 0xFF] = SDLK_KP8;
    MISC_keymap[Pk_KP_9 & 0xFF] = SDLK_KP9;

    MISC_keymap[Pk_KP_Decimal & 0xFF] = SDLK_KP_PERIOD;
    MISC_keymap[Pk_KP_Divide & 0xFF] = SDLK_KP_DIVIDE;
    MISC_keymap[Pk_KP_Multiply & 0xFF] = SDLK_KP_MULTIPLY;
    MISC_keymap[Pk_KP_Subtract & 0xFF] = SDLK_KP_MINUS;
    MISC_keymap[Pk_KP_Add & 0xFF] = SDLK_KP_PLUS;
    MISC_keymap[Pk_KP_Enter & 0xFF] = SDLK_KP_ENTER;
    MISC_keymap[Pk_KP_Equal & 0xFF] = SDLK_KP_EQUALS;

    MISC_keymap[Pk_Up & 0xFF] = SDLK_UP;
    MISC_keymap[Pk_Down & 0xFF] = SDLK_DOWN;
    MISC_keymap[Pk_Right & 0xFF] = SDLK_RIGHT;
    MISC_keymap[Pk_Left & 0xFF] = SDLK_LEFT;
    MISC_keymap[Pk_Insert & 0xFF] = SDLK_INSERT;
    MISC_keymap[Pk_Home & 0xFF] = SDLK_HOME;
    MISC_keymap[Pk_End & 0xFF] = SDLK_END;
    MISC_keymap[Pk_Pg_Up & 0xFF] = SDLK_PAGEUP;
    MISC_keymap[Pk_Pg_Down & 0xFF] = SDLK_PAGEDOWN;

    MISC_keymap[Pk_F1 & 0xFF] = SDLK_F1;
    MISC_keymap[Pk_F2 & 0xFF] = SDLK_F2;
    MISC_keymap[Pk_F3 & 0xFF] = SDLK_F3;
    MISC_keymap[Pk_F4 & 0xFF] = SDLK_F4;
    MISC_keymap[Pk_F5 & 0xFF] = SDLK_F5;
    MISC_keymap[Pk_F6 & 0xFF] = SDLK_F6;
    MISC_keymap[Pk_F7 & 0xFF] = SDLK_F7;
    MISC_keymap[Pk_F8 & 0xFF] = SDLK_F8;
    MISC_keymap[Pk_F9 & 0xFF] = SDLK_F9;
    MISC_keymap[Pk_F10 & 0xFF] = SDLK_F10;
    MISC_keymap[Pk_F11 & 0xFF] = SDLK_F11;
    MISC_keymap[Pk_F12 & 0xFF] = SDLK_F12;
    MISC_keymap[Pk_F13 & 0xFF] = SDLK_F13;
    MISC_keymap[Pk_F14 & 0xFF] = SDLK_F14;
    MISC_keymap[Pk_F15 & 0xFF] = SDLK_F15;

    MISC_keymap[Pk_Num_Lock & 0xFF] = SDLK_NUMLOCK;
    MISC_keymap[Pk_Caps_Lock & 0xFF] = SDLK_CAPSLOCK;
    MISC_keymap[Pk_Scroll_Lock & 0xFF] = SDLK_SCROLLOCK;
    MISC_keymap[Pk_Shift_R & 0xFF] = SDLK_RSHIFT;
    MISC_keymap[Pk_Shift_L & 0xFF] = SDLK_LSHIFT;
    MISC_keymap[Pk_Control_R & 0xFF] = SDLK_RCTRL;
    MISC_keymap[Pk_Control_L & 0xFF] = SDLK_LCTRL;
    MISC_keymap[Pk_Alt_R & 0xFF] = SDLK_RALT;
    MISC_keymap[Pk_Alt_L & 0xFF] = SDLK_LALT;
    MISC_keymap[Pk_Meta_R & 0xFF] = SDLK_RMETA;
    MISC_keymap[Pk_Meta_L & 0xFF] = SDLK_LMETA;
    MISC_keymap[Pk_Super_L & 0xFF] = SDLK_LSUPER;
    MISC_keymap[Pk_Super_R & 0xFF] = SDLK_RSUPER;
    MISC_keymap[Pk_Mode_switch & 0xFF] = SDLK_MODE;     /* "Alt Gr" key    */

    MISC_keymap[Pk_Help & 0xFF] = SDLK_HELP;
    MISC_keymap[Pk_Print & 0xFF] = SDLK_PRINT;
    MISC_keymap[Pk_Break & 0xFF] = SDLK_BREAK;
    MISC_keymap[Pk_Menu & 0xFF] = SDLK_MENU;    /* Windows "Menu" key */

    MISC_keymap[Pk_Hyper_R & 0xFF] = SDLK_RSUPER;       /* Right "Windows" */

    /* Left "Windows" key, but it can't be catched by application */
    MISC_keymap[Pk_Hyper_L & 0xFF] = SDLK_LSUPER;
}

static unsigned long cap;

SDL_keysym *
ph_TranslateKey(PhKeyEvent_t * key, SDL_keysym * keysym)
{
    /* 'sym' is set to the value of the key with modifiers applied to it.
       This member is valid only if Pk_KF_Sym_Valid is set in the key_flags.
       We will assume it is valid. */

    /* FIXME: This needs to check whether the cap & scancode is valid */

    cap = key->key_cap;

    switch (cap >> 8) {
    case 0x00:                 /* Latin 1 */
    case 0x01:                 /* Latin 2 */
    case 0x02:                 /* Latin 3 */
    case 0x03:                 /* Latin 4 */
    case 0x04:                 /* Katakana */
    case 0x05:                 /* Arabic */
    case 0x06:                 /* Cyrillic */
    case 0x07:                 /* Greek */
    case 0x08:                 /* Technical */
    case 0x0A:                 /* Publishing */
    case 0x0C:                 /* Hebrew */
    case 0x0D:                 /* Thai */
        keysym->sym = (SDLKey) (cap & 0xFF);
        /* Map capital letter syms to lowercase */
        if ((keysym->sym >= 'A') && (keysym->sym <= 'Z'))
            keysym->sym += ('a' - 'A');
        break;
    case 0xF0:
        keysym->sym = MISC_keymap[cap & 0xFF];
        break;
    default:
        keysym->sym = SDLK_UNKNOWN;
        break;
    }

    keysym->scancode = key->key_scan;
    keysym->unicode = 0;

    if (SDL_TranslateUNICODE) {
        char utf8[MB_CUR_MAX];
        int utf8len;
        wchar_t unicode;

        switch (keysym->scancode) {
            /* Esc key */
        case 0x01:
            keysym->unicode = 27;
            break;
            /* BackSpace key */
        case 0x0E:
            keysym->unicode = 127;
            break;
            /* Enter key */
        case 0x1C:
            keysym->unicode = 10;
            break;
        default:
            utf8len = PhKeyToMb(utf8, key);
            if (utf8len > 0) {
                utf8len = mbtowc(&unicode, utf8, utf8len);
                if (utf8len > 0) {
                    keysym->unicode = unicode;
                }
            }
            break;
        }

    }

    return (keysym);
}

void
ph_InitOSKeymap(_THIS)
{
    ph_InitKeymap();
}

/* vi: set ts=4 sw=4 expandtab: */