/* -*- C++ -*-
 *
 *  ONScripter_event.cpp - Event handler of ONScripter
 *
 *  Copyright (c) 2001-2020 Ogapee. All rights reserved.
 *
 *  ogapee@aqua.dti2.ne.jp
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "ONScripter.h"
#if defined(LINUX)
#include <sys/types.h>
#include <sys/wait.h>
#endif

#define ONS_TIMER_EVENT (SDL_USEREVENT)
#define ONS_MUSIC_EVENT (SDL_USEREVENT + 1)
#define ONS_CDAUDIO_EVENT (SDL_USEREVENT + 2)
#define ONS_MIDI_EVENT (SDL_USEREVENT + 3)
#define ONS_CHUNK_EVENT (SDL_USEREVENT + 4)
#define ONS_BREAK_EVENT (SDL_USEREVENT + 5)
#define ONS_BGMFADE_EVENT (SDL_USEREVENT + 6)

// This sets up the fade event flag for use in bgm fadeout and fadein.
#define BGM_FADEOUT 0
#define BGM_FADEIN 1

#define EDIT_MODE_PREFIX "[EDIT MODE]  "
#define EDIT_SELECT_STRING "MP3 vol (m)  SE vol (s)  Voice vol (v)  Numeric variable (n)"

static SDL_TimerID timer_id = NULL;
SDL_TimerID timer_cdaudio_id = NULL;
SDL_TimerID timer_bgmfade_id = NULL;

bool ext_music_play_once_flag = false;

/* **************************************** *
 * Callback functions
 * **************************************** */
extern "C" void musicFinishCallback()
{
    SDL_Event event;
    event.type = ONS_MUSIC_EVENT;
    SDL_PushEvent(&event);
}

extern "C" Uint32 SDLCALL timerCallback(Uint32 interval, void* param)
{
    SDL_RemoveTimer(timer_id);
    timer_id = NULL;

    SDL_Event event;
    event.type = ONS_TIMER_EVENT;
    SDL_PushEvent(&event);

    return 0;
}

extern "C" Uint32 cdaudioCallback(Uint32 interval, void* param)
{
    SDL_RemoveTimer(timer_cdaudio_id);
    timer_cdaudio_id = NULL;

    SDL_Event event;
    event.type = ONS_CDAUDIO_EVENT;
    SDL_PushEvent(&event);

    return interval;
}

extern "C" Uint32 SDLCALL bgmfadeCallback(Uint32 interval, void* param)
{
    SDL_Event event;
    event.type = ONS_BGMFADE_EVENT;
    event.user.code = (param == NULL) ? BGM_FADEOUT : BGM_FADEIN;
    SDL_PushEvent(&event);

    return interval;
}

/* **************************************** *
 * OS Dependent Input Translation
 * **************************************** */

SDLKey transKey(SDLKey key)
{
#if defined(IPODLINUX)
    switch (key) {
    case SDLK_m:
        key = SDLK_UP;
        break; /* Menu                   */
    case SDLK_d:
        key = SDLK_DOWN;
        break; /* Play/Pause             */
    case SDLK_f:
        key = SDLK_RIGHT;
        break; /* Fast forward           */
    case SDLK_w:
        key = SDLK_LEFT;
        break; /* Rewind                 */
    case SDLK_RETURN:
        key = SDLK_RETURN;
        break; /* Action                 */
    case SDLK_h:
        key = SDLK_ESCAPE;
        break; /* Hold                   */
    case SDLK_r:
        key = SDLK_UNKNOWN;
        break; /* Wheel clockwise        */
    case SDLK_l:
        key = SDLK_UNKNOWN;
        break; /* Wheel counterclockwise */
    default:
        break;
    }
#endif
#if defined(WINCE)
    switch (key) {
    case SDLK_UP:
        key = SDLK_UP;
        break; /* vkUP    */
    case SDLK_DOWN:
        key = SDLK_DOWN;
        break; /* vkDOWN  */
    case SDLK_LEFT:
        key = SDLK_LCTRL;
        break; /* vkLEFT  */
    case SDLK_RIGHT:
        key = SDLK_RETURN;
        break; /* vkRIGHT */
    case SDLK_KP0:
        key = SDLK_q;
        break; /* vkStart */
    case SDLK_KP1:
        key = SDLK_RETURN;
        break; /* vkA     */
    case SDLK_KP2:
        key = SDLK_SPACE;
        break; /* vkB     */
    case SDLK_KP3:
        key = SDLK_ESCAPE;
        break; /* vkC     */
    default:
        break;
    }
#endif
    return key;
}

SDLKey transJoystickButton(Uint8 button)
{
#if defined(PSP)
    SDLKey button_map[] = {SDLK_ESCAPE, /* TRIANGLE */
                           SDLK_RETURN, /* CIRCLE   */
                           SDLK_SPACE, /* CROSS    */
                           SDLK_RCTRL, /* SQUARE   */
                           SDLK_o, /* LTRIGGER */
                           SDLK_s, /* RTRIGGER */
                           SDLK_DOWN, /* DOWN     */
                           SDLK_LEFT, /* LEFT     */
                           SDLK_UP, /* UP       */
                           SDLK_RIGHT, /* RIGHT    */
                           SDLK_0, /* SELECT   */
                           SDLK_a, /* START    */
                           SDLK_UNKNOWN, /* HOME     */ /* kernel mode only */
                           SDLK_UNKNOWN,
                           /* HOLD     */};
    return button_map[button];
#elif defined(__PS3__)
    SDLKey button_map[] = {
        SDLK_0, /* SELECT   */
        SDLK_UNKNOWN, /* L3       */
        SDLK_UNKNOWN, /* R3       */
        SDLK_a, /* START    */
        SDLK_UP, /* UP       */
        SDLK_RIGHT, /* RIGHT    */
        SDLK_DOWN, /* DOWN     */
        SDLK_LEFT, /* LEFT     */
        SDLK_SPACE, /* L2       */
        SDLK_RETURN, /* R2       */
        SDLK_o, /* L1       */
        SDLK_s, /* R1       */
        SDLK_ESCAPE, /* TRIANGLE */
        SDLK_RETURN, /* CIRCLE   */
        SDLK_SPACE, /* CROSS    */
        SDLK_RCTRL, /* SQUARE   */
        SDLK_UNKNOWN, /* PS       */
        SDLK_UNKNOWN,
        SDLK_UNKNOWN,
    };
    return button_map[button];
#elif defined(GP2X)
    SDLKey button_map[] = {
        SDLK_UP, /* UP        */
        SDLK_UNKNOWN, /* UPLEFT    */
        SDLK_LEFT, /* LEFT      */
        SDLK_UNKNOWN, /* DOWNLEFT  */
        SDLK_DOWN, /* DOWN      */
        SDLK_UNKNOWN, /* DOWNRIGHT */
        SDLK_RIGHT, /* RIGHT     */
        SDLK_UNKNOWN, /* UPRIGHT   */
        SDLK_a, /* START     */
        SDLK_0, /* SELECT    */
        SDLK_o, /* L         */
        SDLK_s, /* R         */
        SDLK_RCTRL, /* A         */
        SDLK_RETURN, /* B         */
        SDLK_SPACE, /* X         */
        SDLK_ESCAPE, /* Y         */
        SDLK_UNKNOWN, /* VOLUP     */
        SDLK_UNKNOWN, /* VOLDOWN   */
        SDLK_UNKNOWN, /* STICK     */
    };
    return button_map[button];
#endif
    return SDLK_UNKNOWN;
}

SDL_KeyboardEvent transJoystickAxis(SDL_JoyAxisEvent& jaxis)
{
    static int old_axis = -1;

    SDL_KeyboardEvent event;

    SDLKey axis_map[] = {SDLK_LEFT, /* AL-LEFT  */
                         SDLK_RIGHT, /* AL-RIGHT */
                         SDLK_UP, /* AL-UP    */
                         SDLK_DOWN /* AL-DOWN  */};

    int axis = -1;
    /* rerofumi: Jan.15.2007 */
    /* ps3's pad has 0x1b axis (with analog button) */
    if (jaxis.axis < 2) {
        axis = ((3200 > jaxis.value) && (jaxis.value > -3200) ? -1 : (jaxis.axis * 2 + (jaxis.value > 0 ? 1 : 0)));
    }

    if (axis != old_axis) {
        if (axis == -1) {
            event.type = SDL_KEYUP;
            event.keysym.sym = axis_map[old_axis];
        }
        else {
            event.type = SDL_KEYDOWN;
            event.keysym.sym = axis_map[axis];
        }
        old_axis = axis;
    }
    else {
        event.keysym.sym = SDLK_UNKNOWN;
    }

    return event;
}

void ONScripter::flushEventSub(SDL_Event& event)
{
    if (event.type == ONS_MUSIC_EVENT) {
        if (music_play_loop_flag ||
            (cd_play_loop_flag && !cdaudio_flag)) {
            stopBGM(true);
            if (music_file_name) {
                playSound(music_file_name, SOUND_MUSIC, true);
                Mix_SetMusicPosition(music_loopback_offset);
            }
            else
                playCDAudio();
        }
        else {
            stopBGM(false);
        }
    }
    else if ((event.type == ONS_BGMFADE_EVENT) &&
             (event.user.code == BGM_FADEOUT)) {
        Uint32 cur_fade_duration = mp3fadeout_duration_internal;
        if (skip_mode & (SKIP_NORMAL | SKIP_TO_EOP) ||
            ctrl_pressed_status) {
            cur_fade_duration = 0;
            Mix_VolumeMusic(0);
        }
        Uint32 tmp = SDL_GetTicks() - mp3fade_start;
        if (tmp < cur_fade_duration) {
            tmp = cur_fade_duration - tmp;
            tmp *= music_volume;
            tmp /= cur_fade_duration;

            Mix_VolumeMusic(tmp * MIX_MAX_VOLUME / 100);
        }
        else {
            char* ext = NULL;
            if (fadeout_music_file_name) ext = strrchr(fadeout_music_file_name, '.');
            if (ext && (strcmp(ext + 1, "OGG") && strcmp(ext + 1, "ogg"))) {
                // set break event to return to script processing when playing music other than ogg
                SDL_Event event;
                event.type = ONS_BREAK_EVENT;
                SDL_PushEvent(&event);
            }

            stopBGM(false);
        }
    }
    else if ((event.type == ONS_BGMFADE_EVENT) &&
             (event.user.code == BGM_FADEIN)) {
        Uint32 cur_fade_duration = mp3fadein_duration_internal;
        if (skip_mode & (SKIP_NORMAL | SKIP_TO_EOP) ||
            ctrl_pressed_status) {
            cur_fade_duration = 0;
            Mix_VolumeMusic(music_volume * MIX_MAX_VOLUME / 100);
        }
        Uint32 tmp = SDL_GetTicks() - mp3fade_start;
        if (tmp < cur_fade_duration) {
            tmp *= music_volume;
            tmp /= cur_fade_duration;

            Mix_VolumeMusic(tmp * MIX_MAX_VOLUME / 100);
        }
        else {
            if (timer_bgmfade_id) SDL_RemoveTimer(timer_bgmfade_id);
            timer_bgmfade_id = NULL;
            mp3fadeout_duration_internal = 0;

            char* ext = NULL;
            if (music_file_name) ext = strrchr(music_file_name, '.');
            if (ext && (strcmp(ext + 1, "OGG") && strcmp(ext + 1, "ogg"))) {
                // set break event to return to script processing when playing music other than ogg
                SDL_Event event;
                event.type = ONS_BREAK_EVENT;
                SDL_PushEvent(&event);
            }
        }
    }
    else if (event.type == ONS_CDAUDIO_EVENT) {
        if (cd_play_loop_flag) {
            stopBGM(true);
            playCDAudio();
        }
        else {
            stopBGM(false);
        }
    }
    else if (event.type == ONS_MIDI_EVENT) {
        ext_music_play_once_flag = !midi_play_loop_flag;
        Mix_FreeMusic(midi_info);
        playMIDI(midi_play_loop_flag);
    }
    else if (event.type == ONS_CHUNK_EVENT) { // for processing btntim2 and automode correctly
        if (wave_sample[event.user.code]) {
            Mix_FreeChunk(wave_sample[event.user.code]);
            wave_sample[event.user.code] = NULL;
            if (event.user.code == MIX_LOOPBGM_CHANNEL0 &&
                loop_bgm_name[1] &&
                wave_sample[MIX_LOOPBGM_CHANNEL1])
                Mix_PlayChannel(MIX_LOOPBGM_CHANNEL1,
                                wave_sample[MIX_LOOPBGM_CHANNEL1], -1);
        }
    }
}

void ONScripter::flushEvent()
{
    SDL_Event event;
    while (SDL_PollEvent(&event))
        flushEventSub(event);
}

void ONScripter::removeEvent(int type)
{
    SDL_Event event;
#if SDL_VERSION_ATLEAST(1, 3, 0)
    while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, type, type) > 0)
        ;
#else
    while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(type)) > 0)
        ;
#endif
}

void ONScripter::removeBGMFadeEvent()
{
    removeEvent(ONS_BGMFADE_EVENT);
}

void ONScripter::waitEventSub(int count)
{
    next_time = count;
    timerEvent(true);

    runEventLoop();
    removeEvent(ONS_BREAK_EVENT);
}

bool ONScripter::waitEvent(int count)
{
    if (count > 0) count += SDL_GetTicks();

    while (1) {
        waitEventSub(count);
        if (system_menu_mode == SYSTEM_NULL ||
            script_h.isExternalScript()) break;
        int ret = executeSystemCall();
        if (ret == 1)
            return true;
        else if (ret == 2)
            return false;
    }

    return false;
}

void midiCallback(int sig)
{
#if defined(LINUX)
    int status;
    wait(&status);
#endif
    if (!ext_music_play_once_flag) {
        SDL_Event event;
        event.type = ONS_MIDI_EVENT;
        SDL_PushEvent(&event);
    }
}

extern "C" void waveCallback(int channel)
{
    SDL_Event event;
    event.type = ONS_CHUNK_EVENT;
    event.user.code = channel;
    SDL_PushEvent(&event);
}

bool ONScripter::trapHandler()
{
    if (event_mode & WAIT_BUTTON_MODE ||
        event_mode & WAIT_TEXT_MODE) return false;

    if (trap_mode & TRAP_STOP) {
        trap_mode |= TRAP_CLICKED;
        return false;
    }

    trap_mode = TRAP_NONE;
    setCurrentLabel(trap_dist);
    current_button_state.button = 0; // to escape from screen effect

    return true;
}

/* **************************************** *
 * Event handlers
 * **************************************** */
bool ONScripter::mouseMoveEvent(SDL_MouseMotionEvent* event)
{
    current_button_state.x = event->x * screen_width / screen_device_width;
    current_button_state.y = event->y * screen_width / screen_device_width;

    if (event_mode & WAIT_BUTTON_MODE) {
        mouseOverCheck(current_button_state.x, current_button_state.y);
        if (getmouseover_flag &&
            current_over_button >= getmouseover_lower &&
            current_over_button <= getmouseover_upper) {
            current_button_state.button = current_over_button;
            return true;
        }
    }

    return false;
}

bool ONScripter::mousePressEvent(SDL_MouseButtonEvent* event)
{
    if (variable_edit_mode) return false;

    if (automode_flag) {
        automode_flag = false;
        return false;
    }

    if ((event->button == SDL_BUTTON_RIGHT && trap_mode & TRAP_RIGHT_CLICK) ||
        (event->button == SDL_BUTTON_LEFT && trap_mode & TRAP_LEFT_CLICK)) {
        if (trapHandler()) return true;
    }

    current_button_state.x = event->x * screen_width / screen_device_width;
    current_button_state.y = event->y * screen_width / screen_device_width;
    current_button_state.down_flag = false;
    skip_mode &= ~SKIP_NORMAL;

    if (event->button == SDL_BUTTON_RIGHT &&
        event->type == SDL_MOUSEBUTTONUP &&
        ((rmode_flag && event_mode & WAIT_TEXT_MODE) ||
         (event_mode & (WAIT_BUTTON_MODE | WAIT_RCLICK_MODE)))) {
        current_button_state.button = -1;
        sprintf(current_button_state.str, "RCLICK");
        if (event_mode & WAIT_TEXT_MODE) {
            if (root_rmenu_link.next)
                system_menu_mode = SYSTEM_MENU;
            else
                system_menu_mode = SYSTEM_WINDOWERASE;
        }
    }
    else if (event->button == SDL_BUTTON_LEFT &&
             (event->type == SDL_MOUSEBUTTONUP || btndown_flag)) {
        current_button_state.button = current_over_button;
        if (current_over_button == -1) {
            if (!bexec_flag) current_button_state.button = 0;
            sprintf(current_button_state.str, "LCLICK");
        }
        else {
            sprintf(current_button_state.str, "S%d", current_over_button);
            if (bexec_flag && current_button_link) {
                ButtonLink* cbl = current_button_link;
                if (current_button_link->exbtn_ctl[2]) {
                    SDL_Rect check_src_rect = cbl->image_rect;
                    SDL_Rect check_dst_rect = {0, 0, 0, 0};
                    decodeExbtnControl(cbl->exbtn_ctl[2], &check_src_rect, &check_dst_rect);
                }
                else {
                    sprite_info[cbl->sprite_no].visible = true;
                    sprite_info[cbl->sprite_no].setCell(2);
                    dirty_rect.add(cbl->image_rect);
                }
                flush(refreshMode());
            }
        }

        if (event->type == SDL_MOUSEBUTTONDOWN)
            current_button_state.down_flag = true;
    }
#if SDL_VERSION_ATLEAST(1, 2, 5)
    else if (event->button == SDL_BUTTON_WHEELUP &&
             (bexec_flag ||
              (event_mode & WAIT_TEXT_MODE) ||
              (usewheel_flag && event_mode & WAIT_BUTTON_MODE) ||
              system_menu_mode == SYSTEM_LOOKBACK)) {
        current_button_state.button = -2;
        sprintf(current_button_state.str, "WHEELUP");
        if (event_mode & WAIT_TEXT_MODE) system_menu_mode = SYSTEM_LOOKBACK;
    }
    else if (event->button == SDL_BUTTON_WHEELDOWN &&
             (bexec_flag ||
              (enable_wheeldown_advance_flag && event_mode & WAIT_TEXT_MODE) ||
              (usewheel_flag && event_mode & WAIT_BUTTON_MODE) ||
              system_menu_mode == SYSTEM_LOOKBACK)) {
        if (event_mode & WAIT_TEXT_MODE)
            current_button_state.button = 0;
        else
            current_button_state.button = -3;
        sprintf(current_button_state.str, "WHEELDOWN");
    }
#endif
    else if (getmclick_flag && event->button == SDL_BUTTON_MIDDLE) {
        current_button_state.button = -70;
        sprintf(current_button_state.str, "MCLICK");
    }
    else
        return false;

    if (event_mode & (WAIT_INPUT_MODE | WAIT_BUTTON_MODE)) {
        if (!(event_mode & (WAIT_TEXT_MODE)))
            skip_mode |= SKIP_TO_EOL;
        playClickVoice();

        return true;
    }

    return false;
}

void ONScripter::variableEditMode(SDL_KeyboardEvent* event)
{
    int i;
    const char* var_name;
    char var_index[12];

    switch (event->keysym.sym) {
    case SDLK_m:
        if (variable_edit_mode != EDIT_SELECT_MODE) return;
        variable_edit_mode = EDIT_MP3_VOLUME_MODE;
        variable_edit_num = music_volume;
        break;

    case SDLK_s:
        if (variable_edit_mode != EDIT_SELECT_MODE) return;
        variable_edit_mode = EDIT_SE_VOLUME_MODE;
        variable_edit_num = se_volume;
        break;

    case SDLK_v:
        if (variable_edit_mode != EDIT_SELECT_MODE) return;
        variable_edit_mode = EDIT_VOICE_VOLUME_MODE;
        variable_edit_num = voice_volume;
        break;

    case SDLK_n:
        if (variable_edit_mode != EDIT_SELECT_MODE) return;
        variable_edit_mode = EDIT_VARIABLE_INDEX_MODE;
        variable_edit_num = 0;
        break;

    case SDLK_9:
    case SDLK_KP9:
        variable_edit_num = variable_edit_num * 10 + 9;
        break;
    case SDLK_8:
    case SDLK_KP8:
        variable_edit_num = variable_edit_num * 10 + 8;
        break;
    case SDLK_7:
    case SDLK_KP7:
        variable_edit_num = variable_edit_num * 10 + 7;
        break;
    case SDLK_6:
    case SDLK_KP6:
        variable_edit_num = variable_edit_num * 10 + 6;
        break;
    case SDLK_5:
    case SDLK_KP5:
        variable_edit_num = variable_edit_num * 10 + 5;
        break;
    case SDLK_4:
    case SDLK_KP4:
        variable_edit_num = variable_edit_num * 10 + 4;
        break;
    case SDLK_3:
    case SDLK_KP3:
        variable_edit_num = variable_edit_num * 10 + 3;
        break;
    case SDLK_2:
    case SDLK_KP2:
        variable_edit_num = variable_edit_num * 10 + 2;
        break;
    case SDLK_1:
    case SDLK_KP1:
        variable_edit_num = variable_edit_num * 10 + 1;
        break;
    case SDLK_0:
    case SDLK_KP0:
        variable_edit_num = variable_edit_num * 10 + 0;
        break;

    case SDLK_MINUS:
    case SDLK_KP_MINUS:
        if (variable_edit_mode == EDIT_VARIABLE_NUM_MODE && variable_edit_num == 0) variable_edit_sign = -1;
        break;

    case SDLK_BACKSPACE:
        if (variable_edit_num)
            variable_edit_num /= 10;
        else if (variable_edit_sign == -1)
            variable_edit_sign = 1;
        break;

    case SDLK_RETURN:
    case SDLK_KP_ENTER:
        switch (variable_edit_mode) {
        case EDIT_VARIABLE_INDEX_MODE:
            variable_edit_index = variable_edit_num;
            variable_edit_num = script_h.getVariableData(variable_edit_index).num;
            if (variable_edit_num < 0) {
                variable_edit_num = -variable_edit_num;
                variable_edit_sign = -1;
            }
            else {
                variable_edit_sign = 1;
            }
            break;

        case EDIT_VARIABLE_NUM_MODE:
            script_h.setNumVariable(variable_edit_index, variable_edit_sign * variable_edit_num);
            break;

        case EDIT_MP3_VOLUME_MODE:
            music_volume = variable_edit_num;
            Mix_VolumeMusic(music_volume * MIX_MAX_VOLUME / 100);
            break;

        case EDIT_SE_VOLUME_MODE:
            se_volume = variable_edit_num;
            for (i = 1; i < ONS_MIX_CHANNELS; i++)
                if (wave_sample[i]) Mix_Volume(i, se_volume * MIX_MAX_VOLUME / 100);
            if (wave_sample[MIX_LOOPBGM_CHANNEL0]) Mix_Volume(MIX_LOOPBGM_CHANNEL0, se_volume * MIX_MAX_VOLUME / 100);
            if (wave_sample[MIX_LOOPBGM_CHANNEL1]) Mix_Volume(MIX_LOOPBGM_CHANNEL1, se_volume * MIX_MAX_VOLUME / 100);
            break;

        case EDIT_VOICE_VOLUME_MODE:
            voice_volume = variable_edit_num;
            if (wave_sample[0]) Mix_Volume(0, se_volume * MIX_MAX_VOLUME / 100);

        default:
            break;
        }
        if (variable_edit_mode == EDIT_VARIABLE_INDEX_MODE)
            variable_edit_mode = EDIT_VARIABLE_NUM_MODE;
        else
            variable_edit_mode = EDIT_SELECT_MODE;
        break;

    case SDLK_ESCAPE:
        if (variable_edit_mode == EDIT_SELECT_MODE) {
            variable_edit_mode = NOT_EDIT_MODE;
            SDL_WM_SetCaption(DEFAULT_WM_TITLE, DEFAULT_WM_ICON);
            SDL_Delay(100);
            SDL_WM_SetCaption(wm_title_string, wm_icon_string);
            return;
        }
        variable_edit_mode = EDIT_SELECT_MODE;

    default:
        break;
    }

    if (variable_edit_mode == EDIT_SELECT_MODE) {
        sprintf(wm_edit_string, "%s%s", EDIT_MODE_PREFIX, EDIT_SELECT_STRING);
    }
    else if (variable_edit_mode == EDIT_VARIABLE_INDEX_MODE) {
        sprintf(wm_edit_string, "%s%s%d", EDIT_MODE_PREFIX, "Variable Index?  %", variable_edit_sign * variable_edit_num);
    }
    else if (variable_edit_mode >= EDIT_VARIABLE_NUM_MODE) {
        int p = 0;

        switch (variable_edit_mode) {
        case EDIT_VARIABLE_NUM_MODE:
            sprintf(var_index, "%%%d", variable_edit_index);
            var_name = var_index;
            p = script_h.getVariableData(variable_edit_index).num;
            break;

        case EDIT_MP3_VOLUME_MODE:
            var_name = "MP3 Volume";
            p = music_volume;
            break;

        case EDIT_VOICE_VOLUME_MODE:
            var_name = "Voice Volume";
            p = voice_volume;
            break;

        case EDIT_SE_VOLUME_MODE:
            var_name = "Sound effect Volume";
            p = se_volume;
            break;

        default:
            var_name = "";
        }
        sprintf(wm_edit_string, "%sCurrent %s=%d  New value? %s%d",
                EDIT_MODE_PREFIX, var_name, p, (variable_edit_sign == 1) ? "" : "-", variable_edit_num);
    }

    SDL_WM_SetCaption(wm_edit_string, wm_icon_string);
}

void ONScripter::shiftCursorOnButton(int diff)
{
    int num;
    ButtonLink* button = root_button_link.next;
    for (num = 0; button; num++)
        button = button->next;

    shortcut_mouse_line += diff;
    if (shortcut_mouse_line < 0)
        shortcut_mouse_line = num - 1;
    else if (shortcut_mouse_line >= num)
        shortcut_mouse_line = 0;

    button = root_button_link.next;
    for (int i = 0; i < shortcut_mouse_line; i++)
        button = button->next;

    if (button) {
        int x = button->select_rect.x + button->select_rect.w / 2;
        int y = button->select_rect.y + button->select_rect.h / 2;
        if (x < 0)
            x = 0;
        else if (x >= screen_width)
            x = screen_width - 1;
        if (y < 0)
            y = 0;
        else if (y >= screen_height)
            y = screen_height - 1;
        x = x * screen_device_width / screen_width;
        y = y * screen_device_width / screen_width;
        shift_over_button = button->no;
        SDL_WarpMouse(x, y);
    }
}

bool ONScripter::keyDownEvent(SDL_KeyboardEvent* event)
{
    if (event->keysym.sym == SDLK_ESCAPE) {
        current_button_state.event_type = SDL_MOUSEBUTTONDOWN;
        current_button_state.event_button = SDL_BUTTON_RIGHT;
    }
    else if (event->keysym.sym == SDLK_KP_ENTER) {
        current_button_state.event_type = SDL_MOUSEBUTTONDOWN;
        current_button_state.event_button = SDL_BUTTON_LEFT;
    }
    else if (event->keysym.sym == SDLK_LEFT) {
        current_button_state.event_type = SDL_MOUSEBUTTONDOWN;
        current_button_state.event_button = SDL_BUTTON_WHEELUP;
    }
    else if (event->keysym.sym == SDLK_RIGHT) {
        current_button_state.event_type = SDL_MOUSEBUTTONDOWN;
        current_button_state.event_button = SDL_BUTTON_WHEELDOWN;
    }

    switch (event->keysym.sym) {
    case SDLK_RCTRL:
        ctrl_pressed_status |= 0x01;
        sprintf(current_button_state.str, "CTRL");
        goto ctrl_pressed;
    case SDLK_LCTRL:
        ctrl_pressed_status |= 0x02;
        sprintf(current_button_state.str, "CTRL");
        goto ctrl_pressed;
    case SDLK_RSHIFT:
        shift_pressed_status |= 0x01;
        break;
    case SDLK_LSHIFT:
        shift_pressed_status |= 0x02;
        break;
    default:
        break;
    }

    return false;

ctrl_pressed:
    current_button_state.button = 0;
    playClickVoice();

    return true;
}

void ONScripter::keyUpEvent(SDL_KeyboardEvent* event)
{
    if (event->keysym.sym == SDLK_ESCAPE) {
        current_button_state.event_type = SDL_MOUSEBUTTONUP;
        current_button_state.event_button = SDL_BUTTON_RIGHT;
    }
    else if (event->keysym.sym == SDLK_KP_ENTER) {
        current_button_state.event_type = SDL_MOUSEBUTTONUP;
        current_button_state.event_button = SDL_BUTTON_LEFT;
    }
    else if (event->keysym.sym == SDLK_LEFT) {
        current_button_state.event_type = SDL_MOUSEBUTTONUP;
        current_button_state.event_button = SDL_BUTTON_WHEELUP;
    }
    else if (event->keysym.sym == SDLK_RIGHT) {
        current_button_state.event_type = SDL_MOUSEBUTTONUP;
        current_button_state.event_button = SDL_BUTTON_WHEELDOWN;
    }

    switch (event->keysym.sym) {
    case SDLK_RCTRL:
        ctrl_pressed_status &= ~0x01;
        break;
    case SDLK_LCTRL:
        ctrl_pressed_status &= ~0x02;
        break;
    case SDLK_RSHIFT:
        shift_pressed_status &= ~0x01;
        break;
    case SDLK_LSHIFT:
        shift_pressed_status &= ~0x02;
        break;
    default:
        break;
    }
}

bool ONScripter::keyPressEvent(SDL_KeyboardEvent* event)
{
    current_button_state.button = 0;
    current_button_state.down_flag = false;
    if (automode_flag) {
        automode_flag = false;
        return false;
    }

    if (event->type == SDL_KEYUP) {
        if (variable_edit_mode) {
            variableEditMode(event);
            return false;
        }

        if (edit_flag && event->keysym.sym == SDLK_z) {
            variable_edit_mode = EDIT_SELECT_MODE;
            variable_edit_sign = 1;
            variable_edit_num = 0;
            sprintf(wm_edit_string, "%s%s", EDIT_MODE_PREFIX, EDIT_SELECT_STRING);
            SDL_WM_SetCaption(wm_edit_string, wm_icon_string);
        }
    }

    if (event->type == SDL_KEYUP)
        skip_mode &= ~SKIP_NORMAL;

    if (shift_pressed_status && event->keysym.sym == SDLK_q && current_mode == NORMAL_MODE) {
        endCommand();
    }

    if ((trap_mode & TRAP_LEFT_CLICK) &&
        (event->keysym.sym == SDLK_RETURN ||
         event->keysym.sym == SDLK_KP_ENTER ||
         event->keysym.sym == SDLK_SPACE)) {
        if (trapHandler()) return true;
    }
    else if ((trap_mode & TRAP_RIGHT_CLICK) &&
             (event->keysym.sym == SDLK_ESCAPE)) {
        if (trapHandler()) return true;
    }

    if (event_mode & WAIT_BUTTON_MODE &&
        (((event->type == SDL_KEYUP || btndown_flag) &&
          ((!getenter_flag && event->keysym.sym == SDLK_RETURN) ||
           (!getenter_flag && event->keysym.sym == SDLK_KP_ENTER))) ||
         ((spclclk_flag || !useescspc_flag) && event->keysym.sym == SDLK_SPACE))) {
        if (event->keysym.sym == SDLK_RETURN ||
            event->keysym.sym == SDLK_KP_ENTER ||
            (spclclk_flag && event->keysym.sym == SDLK_SPACE)) {
            current_button_state.button = current_over_button;
            if (current_over_button == -1) {
                if (!bexec_flag) current_button_state.button = 0;
                sprintf(current_button_state.str, "RETURN");
            }
            else {
                sprintf(current_button_state.str, "S%d", current_over_button);
                if (bexec_flag && current_button_link) {
                    ButtonLink* cbl = current_button_link;
                    if (current_button_link->exbtn_ctl[2]) {
                        SDL_Rect check_src_rect = cbl->image_rect;
                        SDL_Rect check_dst_rect = {0, 0, 0, 0};
                        decodeExbtnControl(cbl->exbtn_ctl[2], &check_src_rect, &check_dst_rect);
                    }
                    else {
                        sprite_info[cbl->sprite_no].visible = true;
                        sprite_info[cbl->sprite_no].setCell(2);
                        dirty_rect.add(cbl->image_rect);
                    }
                    flush(refreshMode());
                }
            }
            if (event->type == SDL_KEYDOWN)
                current_button_state.down_flag = true;
        }
        else {
            current_button_state.button = -1;
            if (!bexec_flag) current_button_state.button = 0;
            sprintf(current_button_state.str, "SPACE");
        }
        playClickVoice();

        return true;
    }

    if (event->type == SDL_KEYDOWN) return false;

    if ((event_mode & (WAIT_INPUT_MODE | WAIT_BUTTON_MODE)) &&
        (autoclick_time == 0 || (event_mode & WAIT_BUTTON_MODE))) {
        if (!useescspc_flag && event->keysym.sym == SDLK_ESCAPE) {
            current_button_state.button = -1;
            sprintf(current_button_state.str, "RCLICK");
            if (rmode_flag && event_mode & WAIT_TEXT_MODE) {
                if (root_rmenu_link.next)
                    system_menu_mode = SYSTEM_MENU;
                else
                    system_menu_mode = SYSTEM_WINDOWERASE;
            }
        }
        else if (useescspc_flag && event->keysym.sym == SDLK_ESCAPE) {
            current_button_state.button = -10;
        }
        else if (!spclclk_flag && useescspc_flag && event->keysym.sym == SDLK_SPACE) {
            current_button_state.button = -11;
        }
        else if (((!getcursor_flag && event->keysym.sym == SDLK_LEFT) ||
                  event->keysym.sym == SDLK_h) &&
                 (event_mode & WAIT_TEXT_MODE ||
                  (usewheel_flag && !getcursor_flag && event_mode & WAIT_BUTTON_MODE) ||
                  system_menu_mode == SYSTEM_LOOKBACK)) {
            current_button_state.button = -2;
            sprintf(current_button_state.str, "WHEELUP");
            if (event_mode & WAIT_TEXT_MODE) system_menu_mode = SYSTEM_LOOKBACK;
        }
        else if (((!getcursor_flag && event->keysym.sym == SDLK_RIGHT) ||
                  event->keysym.sym == SDLK_l) &&
                 ((enable_wheeldown_advance_flag && event_mode & WAIT_TEXT_MODE) ||
                  (usewheel_flag && event_mode & WAIT_BUTTON_MODE) ||
                  system_menu_mode == SYSTEM_LOOKBACK)) {
            if (event_mode & WAIT_TEXT_MODE)
                current_button_state.button = 0;
            else
                current_button_state.button = -3;
            sprintf(current_button_state.str, "WHEELDOWN");
        }
        else if (((!getcursor_flag && event->keysym.sym == SDLK_UP) ||
                  event->keysym.sym == SDLK_k ||
                  event->keysym.sym == SDLK_p) &&
                 event_mode & WAIT_BUTTON_MODE) {
            shiftCursorOnButton(1);
            return false;
        }
        else if (((!getcursor_flag && event->keysym.sym == SDLK_DOWN) ||
                  event->keysym.sym == SDLK_j ||
                  event->keysym.sym == SDLK_n) &&
                 event_mode & WAIT_BUTTON_MODE) {
            shiftCursorOnButton(-1);
            return false;
        }
        else if (getpageup_flag && event->keysym.sym == SDLK_PAGEUP) {
            current_button_state.button = -12;
            sprintf(current_button_state.str, "PAGEUP");
        }
        else if (getpagedown_flag && event->keysym.sym == SDLK_PAGEDOWN) {
            current_button_state.button = -13;
            sprintf(current_button_state.str, "PAGEDOWN");
        }
        else if ((getenter_flag && event->keysym.sym == SDLK_RETURN) ||
                 (getenter_flag && event->keysym.sym == SDLK_KP_ENTER)) {
            current_button_state.button = -19;
        }
        else if (gettab_flag && event->keysym.sym == SDLK_TAB) {
            current_button_state.button = -20;
        }
        else if (getcursor_flag && event->keysym.sym == SDLK_UP) {
            current_button_state.button = -40;
            sprintf(current_button_state.str, "UP");
        }
        else if (getcursor_flag && event->keysym.sym == SDLK_RIGHT) {
            current_button_state.button = -41;
            sprintf(current_button_state.str, "RIGHT");
        }
        else if (getcursor_flag && event->keysym.sym == SDLK_DOWN) {
            current_button_state.button = -42;
            sprintf(current_button_state.str, "DOWN");
        }
        else if (getcursor_flag && event->keysym.sym == SDLK_LEFT) {
            current_button_state.button = -43;
            sprintf(current_button_state.str, "LEFT");
        }
        else if (getinsert_flag && event->keysym.sym == SDLK_INSERT) {
            current_button_state.button = -50;
        }
        else if (getzxc_flag && event->keysym.sym == SDLK_z) {
            current_button_state.button = -51;
        }
        else if (getzxc_flag && event->keysym.sym == SDLK_x) {
            current_button_state.button = -52;
        }
        else if (getzxc_flag && event->keysym.sym == SDLK_c) {
            current_button_state.button = -53;
        }
        else if (getfunction_flag &&
                 event->keysym.sym >= SDLK_F1 && event->keysym.sym <= SDLK_F12) {
            current_button_state.button = -21 - (event->keysym.sym - SDLK_F1);
            sprintf(current_button_state.str, "F%d", event->keysym.sym - SDLK_F1 + 1);
        }
        else if (bexec_flag &&
                 event->keysym.sym >= SDLK_0 && event->keysym.sym <= SDLK_9) {
            current_button_state.button = -1; // dummy
            sprintf(current_button_state.str, "%d", event->keysym.sym - SDLK_0);
        }
        else if (bexec_flag &&
                 event->keysym.sym >= SDLK_a && event->keysym.sym <= SDLK_z) {
            current_button_state.button = -1; // dummy
            sprintf(current_button_state.str, "%c", 'A' + event->keysym.sym - SDLK_a);
        }
        else if (bexec_flag &&
                 (event->keysym.sym == SDLK_RSHIFT || event->keysym.sym == SDLK_LSHIFT)) {
            current_button_state.button = -1; // dummy
            sprintf(current_button_state.str, "SHIFT");
        }

        if (current_button_state.button != 0)
            return true;
    }

    if (event_mode & WAIT_INPUT_MODE &&
        (autoclick_time == 0 || (event_mode & WAIT_BUTTON_MODE))) {
        if (event->keysym.sym == SDLK_RETURN ||
            event->keysym.sym == SDLK_KP_ENTER ||
            event->keysym.sym == SDLK_SPACE) {
            if (!(event_mode & WAIT_TEXT_MODE))
                skip_mode |= SKIP_TO_EOL;
            playClickVoice();

            return true;
        }
    }

    if (event_mode & WAIT_INPUT_MODE) {
        if (event->keysym.sym == SDLK_s && !automode_flag) {
            skip_mode |= SKIP_NORMAL;
            printf("toggle skip to true\n");

            return true;
        }
        else if (event->keysym.sym == SDLK_o) {
            if (skip_mode & SKIP_TO_EOP)
                skip_mode &= ~SKIP_TO_EOP;
            else
                skip_mode |= SKIP_TO_EOP;
            printf("toggle draw one page flag to %s\n", (skip_mode & SKIP_TO_EOP ? "true" : "false"));
            if (skip_mode & SKIP_TO_EOP)
                return true;
        }
        else if (event->keysym.sym == SDLK_a && !automode_flag) {
            automode_flag = true;
            skip_mode &= ~SKIP_NORMAL;
            printf("change to automode\n");

            return true;
        }
        else if (event->keysym.sym == SDLK_0) {
            if (++text_speed_no > 2) text_speed_no = 0;
            sentence_font.wait_time = -1;
        }
        else if (event->keysym.sym == SDLK_1) {
            text_speed_no = 0;
            sentence_font.wait_time = -1;
        }
        else if (event->keysym.sym == SDLK_2) {
            text_speed_no = 1;
            sentence_font.wait_time = -1;
        }
        else if (event->keysym.sym == SDLK_3) {
            text_speed_no = 2;
            sentence_font.wait_time = -1;
        }
    }

    if (event_mode & (WAIT_INPUT_MODE | WAIT_BUTTON_MODE)) {
        if (event->keysym.sym == SDLK_f) {
            if (fullscreen_mode)
                menu_windowCommand();
            else
                menu_fullCommand();
        }
    }

    return false;
}

void ONScripter::timerEvent(bool init_flag)
{
    int current_time = SDL_GetTicks();
    int remaining_time = next_time;
    if (next_time > 0) {
        remaining_time -= current_time;
        if (remaining_time < 0) remaining_time = 0;
    }

    // trick to increase frame rate
    if (remaining_time == 0 && !init_flag) {
        SDL_Event event;
        event.type = ONS_BREAK_EVENT;
        SDL_PushEvent(&event);
        return;
    }

    int duration = 0;
    if (event_mode & WAIT_TIMER_MODE) {
        proceedAnimation(current_time);
        duration = calcDurationToNextAnimation() - current_time;
        if (duration < 0) duration = 0;
    }

    // trick to increase frame rate
    if (remaining_time == 0 && init_flag) {
        SDL_Event event;
        event.type = ONS_BREAK_EVENT;
        SDL_PushEvent(&event);
        return;
    }

    if (duration > 0) {
        if (duration > remaining_time && remaining_time > 0)
            duration = remaining_time;

        if (timer_id) SDL_RemoveTimer(timer_id);
        timer_id = SDL_AddTimer(duration, timerCallback, NULL);
    }
    else if (remaining_time > 0) {
        if (timer_id) SDL_RemoveTimer(timer_id);
        timer_id = SDL_AddTimer(remaining_time, timerCallback, NULL);
    }
}

void ONScripter::runEventLoop()
{
    SDL_Event event, tmp_event;

    while (SDL_WaitEvent(&event)) {
#if defined(USE_SMPEG)
        // required to repeat the movie
        if (layer_smpeg_sample)
            SMPEG_status(layer_smpeg_sample);
#endif
        bool ret = false;
        // ignore continous SDL_MOUSEMOTION
        while (event.type == SDL_MOUSEMOTION) {
#if SDL_VERSION_ATLEAST(1, 3, 0)
            if (SDL_PeepEvents(&tmp_event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 0) break;
            if (tmp_event.type != SDL_MOUSEMOTION) break;
            SDL_PeepEvents(&tmp_event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
#else
            if (SDL_PeepEvents(&tmp_event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS) == 0) break;
            if (tmp_event.type != SDL_MOUSEMOTION) break;
            SDL_PeepEvents(&tmp_event, 1, SDL_GETEVENT, SDL_ALLEVENTS);
#endif
            event = tmp_event;
        }

        switch (event.type) {
#if defined(IOS) // || defined(ANDROID)
        case SDL_FINGERMOTION: {
            SDL_Touch* touch = SDL_GetTouch(event.tfinger.touchId);
            tmp_event.motion.x = device_width * event.tfinger.x / touch->xres - (device_width - screen_device_width) / 2;
            tmp_event.motion.y = device_height * event.tfinger.y / touch->yres - (device_height - screen_device_height) / 2;
            if (mouseMoveEvent(&tmp_event.motion)) return;
            if (btndown_flag) {
                event.button.type = SDL_MOUSEBUTTONDOWN;
                event.button.button = SDL_BUTTON_LEFT;
                if (touch->num_fingers >= 2)
                    event.button.button = SDL_BUTTON_RIGHT;
                event.button.x = tmp_event.motion.x;
                event.button.y = tmp_event.motion.y;
                ret = mousePressEvent(&event.button);
                if (ret) return;
            }
        } break;
        case SDL_FINGERDOWN: {
            SDL_Touch* touch = SDL_GetTouch(event.tfinger.touchId);
            tmp_event.motion.x = device_width * event.tfinger.x / touch->xres - (device_width - screen_device_width) / 2;
            tmp_event.motion.y = device_height * event.tfinger.y / touch->yres - (device_height - screen_device_height) / 2;
            if (mouseMoveEvent(&tmp_event.motion)) return;
        }
            if (btndown_flag) {
                SDL_Touch* touch = SDL_GetTouch(event.tfinger.touchId);
                tmp_event.button.type = SDL_MOUSEBUTTONDOWN;
                tmp_event.button.button = SDL_BUTTON_LEFT;
                if (touch->num_fingers >= 2)
                    tmp_event.button.button = SDL_BUTTON_RIGHT;
                tmp_event.button.x = device_width * event.tfinger.x / touch->xres - (device_width - screen_device_width) / 2;
                tmp_event.button.y = device_height * event.tfinger.y / touch->yres - (device_height - screen_device_height) / 2;
                ret = mousePressEvent(&tmp_event.button);
            }
            {
                SDL_Touch* touch = SDL_GetTouch(event.tfinger.touchId);
                num_fingers = touch->num_fingers;
                if (num_fingers >= 3) {
                    tmp_event.key.keysym.sym = SDLK_LCTRL;
                    ret |= keyDownEvent(&tmp_event.key);
                }
            }
            if (ret) return;
            break;
        case SDL_FINGERUP:
            if (num_fingers == 0) break;
            {
                SDL_Touch* touch = SDL_GetTouch(event.tfinger.touchId);
                tmp_event.button.type = SDL_MOUSEBUTTONUP;
                tmp_event.button.button = SDL_BUTTON_LEFT;
                if (touch->num_fingers >= 1)
                    tmp_event.button.button = SDL_BUTTON_RIGHT;
                tmp_event.button.x = device_width * event.tfinger.x / touch->xres - (device_width - screen_device_width) / 2;
                tmp_event.button.y = device_height * event.tfinger.y / touch->yres - (device_height - screen_device_height) / 2;
                ret = mousePressEvent(&tmp_event.button);
            }
            tmp_event.key.keysym.sym = SDLK_LCTRL;
            keyUpEvent(&tmp_event.key);
            num_fingers = 0;
            if (ret) return;
            break;
#else
        case SDL_MOUSEMOTION:
            if (mouseMoveEvent(&event.motion)) return;
            if (btndown_flag) {
                if (event.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT))
                    tmp_event.button.button = SDL_BUTTON_LEFT;
                else if (event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
                    tmp_event.button.button = SDL_BUTTON_RIGHT;
                else
                    break;

                tmp_event.button.type = SDL_MOUSEBUTTONDOWN;
                tmp_event.button.x = event.motion.x;
                tmp_event.button.y = event.motion.y;
                ret = mousePressEvent(&tmp_event.button);
                if (ret) return;
            }
            break;

        case SDL_MOUSEBUTTONDOWN:
            current_button_state.event_type = event.type;
            current_button_state.event_button = event.button.button;
            if (!btndown_flag) break;
        case SDL_MOUSEBUTTONUP:
            current_button_state.event_type = event.type;
            current_button_state.event_button = event.button.button;
            ret = mousePressEvent(&event.button);
            if (ret) return;
            break;
#endif
        case SDL_JOYBUTTONDOWN:
            event.key.type = SDL_KEYDOWN;
            event.key.keysym.sym = transJoystickButton(event.jbutton.button);
            if (event.key.keysym.sym == SDLK_UNKNOWN)
                break;

        case SDL_KEYDOWN:
            event.key.keysym.sym = transKey(event.key.keysym.sym);
            ret = keyDownEvent(&event.key);
            if (btndown_flag)
                ret |= keyPressEvent(&event.key);
            if (ret) return;
            break;

        case SDL_JOYBUTTONUP:
            event.key.type = SDL_KEYUP;
            event.key.keysym.sym = transJoystickButton(event.jbutton.button);
            if (event.key.keysym.sym == SDLK_UNKNOWN)
                break;

        case SDL_KEYUP:
            event.key.keysym.sym = transKey(event.key.keysym.sym);
            keyUpEvent(&event.key);
            ret = keyPressEvent(&event.key);
            if (ret) return;
            break;

        case SDL_JOYAXISMOTION: {
            SDL_KeyboardEvent ke = transJoystickAxis(event.jaxis);
            if (ke.keysym.sym != SDLK_UNKNOWN) {
                if (ke.type == SDL_KEYDOWN) {
                    keyDownEvent(&ke);
                    if (btndown_flag)
                        keyPressEvent(&ke);
                }
                else if (ke.type == SDL_KEYUP) {
                    keyUpEvent(&ke);
                    keyPressEvent(&ke);
                }
            }
            break;
        }

        case ONS_TIMER_EVENT:
            timerEvent(false);
            break;

        case ONS_MUSIC_EVENT:
        case ONS_BGMFADE_EVENT:
        case ONS_CDAUDIO_EVENT:
        case ONS_MIDI_EVENT:
            flushEventSub(event);
            break;

        case ONS_CHUNK_EVENT:
            flushEventSub(event);
            // printf("ONS_CHUNK_EVENT %d: %x %d %x\n", event.user.code, wave_sample[0], automode_flag, event_mode);
            if (event.user.code != 0 ||
                !(event_mode & WAIT_VOICE_MODE)) break;

            event_mode &= ~WAIT_VOICE_MODE;

        case ONS_BREAK_EVENT:
            if (event_mode & WAIT_VOICE_MODE && wave_sample[0]) {
                next_time = -1;
                timerEvent(false);
                break;
            }

            if (automode_flag || autoclick_time > 0)
                current_button_state.button = 0;
            else if (usewheel_flag) {
                current_button_state.button = -5;
                sprintf(current_button_state.str, "TIMEOUT");
            }
            else {
                current_button_state.button = -2;
                sprintf(current_button_state.str, "TIMEOUT");
            }

            if (event_mode & (WAIT_INPUT_MODE | WAIT_BUTTON_MODE) &&
                (clickstr_state == CLICK_WAIT ||
                 clickstr_state == CLICK_NEWPAGE))
                playClickVoice();

            return;

        case SDL_ACTIVEEVENT:
            if (!event.active.gain) {
                // the mouse cursor leaves the window
                SDL_MouseMotionEvent mevent;
                mevent.x = screen_device_width;
                mevent.y = screen_device_height;
                mouseMoveEvent(&mevent);
                break;
            }
#ifdef ANDROID
            if (event.active.state == SDL_APPACTIVE) {
                screen_surface = SDL_SetVideoMode(screen_width, screen_height, screen_bpp, DEFAULT_VIDEO_SURFACE_FLAG);
                SDL_SetSurfaceBlendMode(screen_surface, SDL_BLENDMODE_NONE);
                repaintCommand();
                break;
            }
#endif
        case SDL_VIDEOEXPOSE:
#ifdef USE_SDL_RENDERER
            SDL_RenderPresent(renderer);
#else
            SDL_UpdateRect(screen_surface, 0, 0, screen_width, screen_height);
#endif
            break;

        case SDL_QUIT:
            endCommand();
            break;

        default:
            break;
        }
    }
}
