cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_windowsevents.c (37406B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21#include "../../SDL_internal.h"
     22
     23#if SDL_VIDEO_DRIVER_WINDOWS
     24
     25#include "SDL_windowsvideo.h"
     26#include "SDL_windowsshape.h"
     27#include "SDL_syswm.h"
     28#include "SDL_timer.h"
     29#include "SDL_vkeys.h"
     30#include "../../events/SDL_events_c.h"
     31#include "../../events/SDL_touch_c.h"
     32#include "../../events/scancodes_windows.h"
     33#include "SDL_assert.h"
     34
     35/* Dropfile support */
     36#include <shellapi.h>
     37
     38/* For GET_X_LPARAM, GET_Y_LPARAM. */
     39#include <windowsx.h>
     40
     41/* #define WMMSG_DEBUG */
     42#ifdef WMMSG_DEBUG
     43#include <stdio.h>
     44#include "wmmsg.h"
     45#endif
     46
     47/* Masks for processing the windows KEYDOWN and KEYUP messages */
     48#define REPEATED_KEYMASK    (1<<30)
     49#define EXTENDED_KEYMASK    (1<<24)
     50
     51#define VK_ENTER    10          /* Keypad Enter ... no VKEY defined? */
     52#ifndef VK_OEM_NEC_EQUAL
     53#define VK_OEM_NEC_EQUAL 0x92
     54#endif
     55
     56/* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
     57#ifndef WM_XBUTTONDOWN
     58#define WM_XBUTTONDOWN 0x020B
     59#endif
     60#ifndef WM_XBUTTONUP
     61#define WM_XBUTTONUP 0x020C
     62#endif
     63#ifndef GET_XBUTTON_WPARAM
     64#define GET_XBUTTON_WPARAM(w) (HIWORD(w))
     65#endif
     66#ifndef WM_INPUT
     67#define WM_INPUT 0x00ff
     68#endif
     69#ifndef WM_TOUCH
     70#define WM_TOUCH 0x0240
     71#endif
     72#ifndef WM_MOUSEHWHEEL
     73#define WM_MOUSEHWHEEL 0x020E
     74#endif
     75#ifndef WM_UNICHAR
     76#define WM_UNICHAR 0x0109
     77#endif
     78
     79static SDL_Scancode
     80WindowsScanCodeToSDLScanCode(LPARAM lParam, WPARAM wParam)
     81{
     82    SDL_Scancode code;
     83    char bIsExtended;
     84    int nScanCode = (lParam >> 16) & 0xFF;
     85
     86    /* 0x45 here to work around both pause and numlock sharing the same scancode, so use the VK key to tell them apart */
     87    if (nScanCode == 0 || nScanCode == 0x45) {
     88        switch(wParam) {
     89        case VK_CLEAR: return SDL_SCANCODE_CLEAR;
     90        case VK_MODECHANGE: return SDL_SCANCODE_MODE;
     91        case VK_SELECT: return SDL_SCANCODE_SELECT;
     92        case VK_EXECUTE: return SDL_SCANCODE_EXECUTE;
     93        case VK_HELP: return SDL_SCANCODE_HELP;
     94        case VK_PAUSE: return SDL_SCANCODE_PAUSE;
     95        case VK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR;
     96
     97        case VK_F13: return SDL_SCANCODE_F13;
     98        case VK_F14: return SDL_SCANCODE_F14;
     99        case VK_F15: return SDL_SCANCODE_F15;
    100        case VK_F16: return SDL_SCANCODE_F16;
    101        case VK_F17: return SDL_SCANCODE_F17;
    102        case VK_F18: return SDL_SCANCODE_F18;
    103        case VK_F19: return SDL_SCANCODE_F19;
    104        case VK_F20: return SDL_SCANCODE_F20;
    105        case VK_F21: return SDL_SCANCODE_F21;
    106        case VK_F22: return SDL_SCANCODE_F22;
    107        case VK_F23: return SDL_SCANCODE_F23;
    108        case VK_F24: return SDL_SCANCODE_F24;
    109
    110        case VK_OEM_NEC_EQUAL: return SDL_SCANCODE_KP_EQUALS;
    111        case VK_BROWSER_BACK: return SDL_SCANCODE_AC_BACK;
    112        case VK_BROWSER_FORWARD: return SDL_SCANCODE_AC_FORWARD;
    113        case VK_BROWSER_REFRESH: return SDL_SCANCODE_AC_REFRESH;
    114        case VK_BROWSER_STOP: return SDL_SCANCODE_AC_STOP;
    115        case VK_BROWSER_SEARCH: return SDL_SCANCODE_AC_SEARCH;
    116        case VK_BROWSER_FAVORITES: return SDL_SCANCODE_AC_BOOKMARKS;
    117        case VK_BROWSER_HOME: return SDL_SCANCODE_AC_HOME;
    118        case VK_VOLUME_MUTE: return SDL_SCANCODE_AUDIOMUTE;
    119        case VK_VOLUME_DOWN: return SDL_SCANCODE_VOLUMEDOWN;
    120        case VK_VOLUME_UP: return SDL_SCANCODE_VOLUMEUP;
    121
    122        case VK_MEDIA_NEXT_TRACK: return SDL_SCANCODE_AUDIONEXT;
    123        case VK_MEDIA_PREV_TRACK: return SDL_SCANCODE_AUDIOPREV;
    124        case VK_MEDIA_STOP: return SDL_SCANCODE_AUDIOSTOP;
    125        case VK_MEDIA_PLAY_PAUSE: return SDL_SCANCODE_AUDIOPLAY;
    126        case VK_LAUNCH_MAIL: return SDL_SCANCODE_MAIL;
    127        case VK_LAUNCH_MEDIA_SELECT: return SDL_SCANCODE_MEDIASELECT;
    128
    129        case VK_OEM_102: return SDL_SCANCODE_NONUSBACKSLASH;
    130
    131        case VK_ATTN: return SDL_SCANCODE_SYSREQ;
    132        case VK_CRSEL: return SDL_SCANCODE_CRSEL;
    133        case VK_EXSEL: return SDL_SCANCODE_EXSEL;
    134        case VK_OEM_CLEAR: return SDL_SCANCODE_CLEAR;
    135
    136        case VK_LAUNCH_APP1: return SDL_SCANCODE_APP1;
    137        case VK_LAUNCH_APP2: return SDL_SCANCODE_APP2;
    138
    139        default: return SDL_SCANCODE_UNKNOWN;
    140        }
    141    }
    142
    143    if (nScanCode > 127)
    144        return SDL_SCANCODE_UNKNOWN;
    145
    146    code = windows_scancode_table[nScanCode];
    147
    148    bIsExtended = (lParam & (1 << 24)) != 0;
    149    if (!bIsExtended) {
    150        switch (code) {
    151        case SDL_SCANCODE_HOME:
    152            return SDL_SCANCODE_KP_7;
    153        case SDL_SCANCODE_UP:
    154            return SDL_SCANCODE_KP_8;
    155        case SDL_SCANCODE_PAGEUP:
    156            return SDL_SCANCODE_KP_9;
    157        case SDL_SCANCODE_LEFT:
    158            return SDL_SCANCODE_KP_4;
    159        case SDL_SCANCODE_RIGHT:
    160            return SDL_SCANCODE_KP_6;
    161        case SDL_SCANCODE_END:
    162            return SDL_SCANCODE_KP_1;
    163        case SDL_SCANCODE_DOWN:
    164            return SDL_SCANCODE_KP_2;
    165        case SDL_SCANCODE_PAGEDOWN:
    166            return SDL_SCANCODE_KP_3;
    167        case SDL_SCANCODE_INSERT:
    168            return SDL_SCANCODE_KP_0;
    169        case SDL_SCANCODE_DELETE:
    170            return SDL_SCANCODE_KP_PERIOD;
    171        case SDL_SCANCODE_PRINTSCREEN:
    172            return SDL_SCANCODE_KP_MULTIPLY;
    173        default:
    174            break;
    175        }
    176    } else {
    177        switch (code) {
    178        case SDL_SCANCODE_RETURN:
    179            return SDL_SCANCODE_KP_ENTER;
    180        case SDL_SCANCODE_LALT:
    181            return SDL_SCANCODE_RALT;
    182        case SDL_SCANCODE_LCTRL:
    183            return SDL_SCANCODE_RCTRL;
    184        case SDL_SCANCODE_SLASH:
    185            return SDL_SCANCODE_KP_DIVIDE;
    186        case SDL_SCANCODE_CAPSLOCK:
    187            return SDL_SCANCODE_KP_PLUS;
    188        default:
    189            break;
    190        }
    191    }
    192
    193    return code;
    194}
    195
    196
    197void
    198WIN_CheckWParamMouseButton(SDL_bool bwParamMousePressed, SDL_bool bSDLMousePressed, SDL_WindowData *data, Uint8 button)
    199{
    200    if (data->focus_click_pending && button == SDL_BUTTON_LEFT && !bwParamMousePressed) {
    201        data->focus_click_pending = SDL_FALSE;
    202        WIN_UpdateClipCursor(data->window);
    203    }
    204
    205    if (bwParamMousePressed && !bSDLMousePressed) {
    206        SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
    207    } else if (!bwParamMousePressed && bSDLMousePressed) {
    208        SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button);
    209    }
    210}
    211
    212/*
    213* Some windows systems fail to send a WM_LBUTTONDOWN sometimes, but each mouse move contains the current button state also
    214*  so this funciton reconciles our view of the world with the current buttons reported by windows
    215*/
    216void
    217WIN_CheckWParamMouseButtons(WPARAM wParam, SDL_WindowData *data)
    218{
    219    if (wParam != data->mouse_button_flags) {
    220        Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
    221        WIN_CheckWParamMouseButton((wParam & MK_LBUTTON), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
    222        WIN_CheckWParamMouseButton((wParam & MK_MBUTTON), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
    223        WIN_CheckWParamMouseButton((wParam & MK_RBUTTON), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
    224        WIN_CheckWParamMouseButton((wParam & MK_XBUTTON1), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
    225        WIN_CheckWParamMouseButton((wParam & MK_XBUTTON2), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
    226        data->mouse_button_flags = wParam;
    227    }
    228}
    229
    230
    231void
    232WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data)
    233{
    234    if (rawButtons != data->mouse_button_flags) {
    235        Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
    236        if ((rawButtons & RI_MOUSE_BUTTON_1_DOWN))
    237            WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
    238        if ((rawButtons & RI_MOUSE_BUTTON_1_UP))
    239            WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
    240        if ((rawButtons & RI_MOUSE_BUTTON_2_DOWN))
    241            WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
    242        if ((rawButtons & RI_MOUSE_BUTTON_2_UP))
    243            WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
    244        if ((rawButtons & RI_MOUSE_BUTTON_3_DOWN))
    245            WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
    246        if ((rawButtons & RI_MOUSE_BUTTON_3_UP))
    247            WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
    248        if ((rawButtons & RI_MOUSE_BUTTON_4_DOWN))
    249            WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
    250        if ((rawButtons & RI_MOUSE_BUTTON_4_UP))
    251            WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
    252        if ((rawButtons & RI_MOUSE_BUTTON_5_DOWN))
    253            WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
    254        if ((rawButtons & RI_MOUSE_BUTTON_5_UP))
    255            WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
    256        data->mouse_button_flags = rawButtons;
    257    }
    258}
    259
    260void
    261WIN_CheckAsyncMouseRelease(SDL_WindowData *data)
    262{
    263    Uint32 mouseFlags;
    264    SHORT keyState;
    265
    266    /* mouse buttons may have changed state here, we need to resync them,
    267       but we will get a WM_MOUSEMOVE right away which will fix things up if in non raw mode also
    268    */
    269    mouseFlags = SDL_GetMouseState(NULL, NULL);
    270
    271    keyState = GetAsyncKeyState(VK_LBUTTON);
    272    if (!(keyState & 0x8000)) {
    273        WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
    274    }
    275    keyState = GetAsyncKeyState(VK_RBUTTON);
    276    if (!(keyState & 0x8000)) {
    277        WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
    278    }
    279    keyState = GetAsyncKeyState(VK_MBUTTON);
    280    if (!(keyState & 0x8000)) {
    281        WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
    282    }
    283    keyState = GetAsyncKeyState(VK_XBUTTON1);
    284    if (!(keyState & 0x8000)) {
    285        WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
    286    }
    287    keyState = GetAsyncKeyState(VK_XBUTTON2);
    288    if (!(keyState & 0x8000)) {
    289        WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
    290    }
    291    data->mouse_button_flags = 0;
    292}
    293
    294SDL_FORCE_INLINE BOOL
    295WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text)
    296{
    297    if (codepoint <= 0x7F) {
    298        text[0] = (char) codepoint;
    299        text[1] = '\0';
    300    } else if (codepoint <= 0x7FF) {
    301        text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
    302        text[1] = 0x80 | (char) (codepoint & 0x3F);
    303        text[2] = '\0';
    304    } else if (codepoint <= 0xFFFF) {
    305        text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
    306        text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
    307        text[2] = 0x80 | (char) (codepoint & 0x3F);
    308        text[3] = '\0';
    309    } else if (codepoint <= 0x10FFFF) {
    310        text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
    311        text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
    312        text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
    313        text[3] = 0x80 | (char) (codepoint & 0x3F);
    314        text[4] = '\0';
    315    } else {
    316        return SDL_FALSE;
    317    }
    318    return SDL_TRUE;
    319}
    320
    321LRESULT CALLBACK
    322WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    323{
    324    SDL_WindowData *data;
    325    LRESULT returnCode = -1;
    326
    327    /* Send a SDL_SYSWMEVENT if the application wants them */
    328    if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
    329        SDL_SysWMmsg wmmsg;
    330
    331        SDL_VERSION(&wmmsg.version);
    332        wmmsg.subsystem = SDL_SYSWM_WINDOWS;
    333        wmmsg.msg.win.hwnd = hwnd;
    334        wmmsg.msg.win.msg = msg;
    335        wmmsg.msg.win.wParam = wParam;
    336        wmmsg.msg.win.lParam = lParam;
    337        SDL_SendSysWMEvent(&wmmsg);
    338    }
    339
    340    /* Get the window data for the window */
    341    data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
    342    if (!data) {
    343        return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
    344    }
    345
    346#ifdef WMMSG_DEBUG
    347    {
    348        char message[1024];
    349        if (msg > MAX_WMMSG) {
    350            SDL_snprintf(message, sizeof(message), "Received windows message: %p UNKNOWN (%d) -- 0x%X, 0x%X\n", hwnd, msg, wParam, lParam);
    351        } else {
    352            SDL_snprintf(message, sizeof(message), "Received windows message: %p %s -- 0x%X, 0x%X\n", hwnd, wmtab[msg], wParam, lParam);
    353        }
    354        OutputDebugStringA(message);
    355    }
    356#endif /* WMMSG_DEBUG */
    357
    358    if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata))
    359        return 0;
    360
    361    switch (msg) {
    362
    363    case WM_SHOWWINDOW:
    364        {
    365            if (wParam) {
    366                SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
    367            } else {
    368                SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
    369            }
    370        }
    371        break;
    372
    373    case WM_ACTIVATE:
    374        {
    375            POINT cursorPos;
    376            BOOL minimized;
    377
    378            minimized = HIWORD(wParam);
    379            if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
    380                data->focus_click_pending = (GetAsyncKeyState(VK_LBUTTON) != 0);
    381
    382                SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
    383                if (SDL_GetKeyboardFocus() != data->window) {
    384                    SDL_SetKeyboardFocus(data->window);
    385                }
    386                
    387                GetCursorPos(&cursorPos);
    388                ScreenToClient(hwnd, &cursorPos);
    389                SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
    390                
    391                WIN_CheckAsyncMouseRelease(data);
    392
    393                /*
    394                 * FIXME: Update keyboard state
    395                 */
    396                WIN_CheckClipboardUpdate(data->videodata);
    397            } else {
    398                if (SDL_GetKeyboardFocus() == data->window) {
    399                    SDL_SetKeyboardFocus(NULL);
    400                }
    401
    402                ClipCursor(NULL);
    403            }
    404        }
    405        returnCode = 0;
    406        break;
    407
    408    case WM_MOUSEMOVE:
    409        {
    410            SDL_Mouse *mouse = SDL_GetMouse();
    411            if (!mouse->relative_mode || mouse->relative_mode_warp) {
    412                SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
    413            }
    414        }
    415        /* don't break here, fall through to check the wParam like the button presses */
    416    case WM_LBUTTONUP:
    417    case WM_RBUTTONUP:
    418    case WM_MBUTTONUP:
    419    case WM_XBUTTONUP:
    420    case WM_LBUTTONDOWN:
    421    case WM_LBUTTONDBLCLK:
    422    case WM_RBUTTONDOWN:
    423    case WM_RBUTTONDBLCLK:
    424    case WM_MBUTTONDOWN:
    425    case WM_MBUTTONDBLCLK:
    426    case WM_XBUTTONDOWN:
    427    case WM_XBUTTONDBLCLK:
    428        {
    429            SDL_Mouse *mouse = SDL_GetMouse();
    430            if (!mouse->relative_mode || mouse->relative_mode_warp) {
    431                WIN_CheckWParamMouseButtons(wParam, data);
    432            }
    433        }
    434        break;
    435
    436    case WM_INPUT:
    437        {
    438            SDL_Mouse *mouse = SDL_GetMouse();
    439            HRAWINPUT hRawInput = (HRAWINPUT)lParam;
    440            RAWINPUT inp;
    441            UINT size = sizeof(inp);
    442            const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
    443            const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
    444
    445            if (!isRelative || mouse->focus != data->window) {
    446                if (!isCapture) {
    447                    break;
    448                }
    449            }
    450
    451            GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
    452
    453            /* Mouse data */
    454            if (inp.header.dwType == RIM_TYPEMOUSE) {
    455                if (isRelative) {
    456                    RAWMOUSE* rawmouse = &inp.data.mouse;
    457
    458                    if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
    459                        SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY);
    460                    } else {
    461                        /* synthesize relative moves from the abs position */
    462                        static SDL_Point initialMousePoint;
    463                        if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
    464                            initialMousePoint.x = rawmouse->lLastX;
    465                            initialMousePoint.y = rawmouse->lLastY;
    466                        }
    467
    468                        SDL_SendMouseMotion(data->window, 0, 1, (int)(rawmouse->lLastX-initialMousePoint.x), (int)(rawmouse->lLastY-initialMousePoint.y) );
    469
    470                        initialMousePoint.x = rawmouse->lLastX;
    471                        initialMousePoint.y = rawmouse->lLastY;
    472                    }
    473                    WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data);
    474                } else if (isCapture) {
    475                    /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
    476                    POINT pt;
    477                    GetCursorPos(&pt);
    478                    if (WindowFromPoint(pt) != hwnd) {  /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
    479                        ScreenToClient(hwnd, &pt);
    480                        SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y);
    481                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
    482                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
    483                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
    484                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
    485                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
    486                    }
    487                } else {
    488                    SDL_assert(0 && "Shouldn't happen");
    489                }
    490            }
    491        }
    492        break;
    493
    494    case WM_MOUSEWHEEL:
    495        {
    496            static short s_AccumulatedMotion;
    497
    498            s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam);
    499            if (s_AccumulatedMotion > 0) {
    500                while (s_AccumulatedMotion >= WHEEL_DELTA) {
    501                    SDL_SendMouseWheel(data->window, 0, 0, 1);
    502                    s_AccumulatedMotion -= WHEEL_DELTA;
    503                }
    504            } else {
    505                while (s_AccumulatedMotion <= -WHEEL_DELTA) {
    506                    SDL_SendMouseWheel(data->window, 0, 0, -1);
    507                    s_AccumulatedMotion += WHEEL_DELTA;
    508                }
    509            }
    510        }
    511        break;
    512
    513    case WM_MOUSEHWHEEL:
    514        {
    515            static short s_AccumulatedMotion;
    516
    517            s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam);
    518            if (s_AccumulatedMotion > 0) {
    519                while (s_AccumulatedMotion >= WHEEL_DELTA) {
    520                    SDL_SendMouseWheel(data->window, 0, 1, 0);
    521                    s_AccumulatedMotion -= WHEEL_DELTA;
    522                }
    523            } else {
    524                while (s_AccumulatedMotion <= -WHEEL_DELTA) {
    525                    SDL_SendMouseWheel(data->window, 0, -1, 0);
    526                    s_AccumulatedMotion += WHEEL_DELTA;
    527                }
    528            }
    529        }
    530        break;
    531
    532#ifdef WM_MOUSELEAVE
    533    case WM_MOUSELEAVE:
    534        if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
    535            if (!IsIconic(hwnd)) {
    536                POINT cursorPos;
    537                GetCursorPos(&cursorPos);
    538                ScreenToClient(hwnd, &cursorPos);
    539                SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
    540            }
    541            SDL_SetMouseFocus(NULL);
    542        }
    543        returnCode = 0;
    544        break;
    545#endif /* WM_MOUSELEAVE */
    546
    547    case WM_KEYDOWN:
    548    case WM_SYSKEYDOWN:
    549        {
    550            SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
    551            if (code != SDL_SCANCODE_UNKNOWN) {
    552                SDL_SendKeyboardKey(SDL_PRESSED, code);
    553            }
    554        }
    555        if (msg == WM_KEYDOWN) {
    556            BYTE keyboardState[256];
    557            char text[5];
    558            UINT32 utf32 = 0;
    559
    560            GetKeyboardState(keyboardState);
    561            if (ToUnicode(wParam, (lParam >> 16) & 0xff, keyboardState, (LPWSTR)&utf32, 1, 0) > 0) {
    562                WORD repetition;
    563                for (repetition = lParam & 0xffff; repetition > 0; repetition--) {
    564                    WIN_ConvertUTF32toUTF8(utf32, text);
    565                    SDL_SendKeyboardText(text);
    566                }
    567            }
    568        }
    569        returnCode = 0;
    570        break;
    571
    572    case WM_SYSKEYUP:
    573    case WM_KEYUP:
    574        {
    575            SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
    576            const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
    577
    578            /* Detect relevant keyboard shortcuts */
    579            if (keyboardState[SDL_SCANCODE_LALT] == SDL_PRESSED || keyboardState[SDL_SCANCODE_RALT] == SDL_PRESSED) {
    580                /* ALT+F4: Close window */
    581                if (code == SDL_SCANCODE_F4) {
    582                    SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
    583                }
    584            }
    585
    586            if (code != SDL_SCANCODE_UNKNOWN) {
    587                if (code == SDL_SCANCODE_PRINTSCREEN &&
    588                    keyboardState[code] == SDL_RELEASED) {
    589                    SDL_SendKeyboardKey(SDL_PRESSED, code);
    590                }
    591                SDL_SendKeyboardKey(SDL_RELEASED, code);
    592            }
    593        }
    594        returnCode = 0;
    595        break;
    596
    597    case WM_UNICHAR:
    598    case WM_CHAR:
    599        /* Ignore WM_CHAR messages that come from TranslateMessage(), since we handle WM_KEY* messages directly */
    600        returnCode = 0;
    601        break;
    602
    603#ifdef WM_INPUTLANGCHANGE
    604    case WM_INPUTLANGCHANGE:
    605        {
    606            WIN_UpdateKeymap();
    607        }
    608        returnCode = 1;
    609        break;
    610#endif /* WM_INPUTLANGCHANGE */
    611
    612    case WM_NCLBUTTONDOWN:
    613        {
    614            data->in_title_click = SDL_TRUE;
    615        }
    616        break;
    617
    618    case WM_CAPTURECHANGED:
    619        {
    620            data->in_title_click = SDL_FALSE;
    621
    622            /* The mouse may have been released during a modal loop */
    623            WIN_CheckAsyncMouseRelease(data);
    624        }
    625        break;
    626
    627#ifdef WM_GETMINMAXINFO
    628    case WM_GETMINMAXINFO:
    629        {
    630            MINMAXINFO *info;
    631            RECT size;
    632            int x, y;
    633            int w, h;
    634            int min_w, min_h;
    635            int max_w, max_h;
    636            int style;
    637            BOOL menu;
    638            BOOL constrain_max_size;
    639
    640            if (SDL_IsShapedWindow(data->window))
    641                Win32_ResizeWindowShape(data->window);
    642
    643            /* If this is an expected size change, allow it */
    644            if (data->expected_resize) {
    645                break;
    646            }
    647
    648            /* Get the current position of our window */
    649            GetWindowRect(hwnd, &size);
    650            x = size.left;
    651            y = size.top;
    652
    653            /* Calculate current size of our window */
    654            SDL_GetWindowSize(data->window, &w, &h);
    655            SDL_GetWindowMinimumSize(data->window, &min_w, &min_h);
    656            SDL_GetWindowMaximumSize(data->window, &max_w, &max_h);
    657
    658            /* Store in min_w and min_h difference between current size and minimal
    659               size so we don't need to call AdjustWindowRectEx twice */
    660            min_w -= w;
    661            min_h -= h;
    662            if (max_w && max_h) {
    663                max_w -= w;
    664                max_h -= h;
    665                constrain_max_size = TRUE;
    666            } else {
    667                constrain_max_size = FALSE;
    668            }
    669
    670            size.top = 0;
    671            size.left = 0;
    672            size.bottom = h;
    673            size.right = w;
    674
    675            style = GetWindowLong(hwnd, GWL_STYLE);
    676            /* DJM - according to the docs for GetMenu(), the
    677               return value is undefined if hwnd is a child window.
    678               Apparently it's too difficult for MS to check
    679               inside their function, so I have to do it here.
    680             */
    681            menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
    682            AdjustWindowRectEx(&size, style, menu, 0);
    683            w = size.right - size.left;
    684            h = size.bottom - size.top;
    685
    686            /* Fix our size to the current size */
    687            info = (MINMAXINFO *) lParam;
    688            if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
    689                info->ptMinTrackSize.x = w + min_w;
    690                info->ptMinTrackSize.y = h + min_h;
    691                if (constrain_max_size) {
    692                    info->ptMaxTrackSize.x = w + max_w;
    693                    info->ptMaxTrackSize.y = h + max_h;
    694                }
    695            } else {
    696                info->ptMaxSize.x = w;
    697                info->ptMaxSize.y = h;
    698                info->ptMaxPosition.x = x;
    699                info->ptMaxPosition.y = y;
    700                info->ptMinTrackSize.x = w;
    701                info->ptMinTrackSize.y = h;
    702                info->ptMaxTrackSize.x = w;
    703                info->ptMaxTrackSize.y = h;
    704            }
    705        }
    706        returnCode = 0;
    707        break;
    708#endif /* WM_GETMINMAXINFO */
    709
    710    case WM_WINDOWPOSCHANGED:
    711        {
    712            RECT rect;
    713            int x, y;
    714            int w, h;
    715            
    716            if (data->in_border_change) {
    717                break;
    718            }
    719
    720            if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
    721                break;
    722            }
    723            ClientToScreen(hwnd, (LPPOINT) & rect);
    724            ClientToScreen(hwnd, (LPPOINT) & rect + 1);
    725
    726            WIN_UpdateClipCursor(data->window);
    727
    728            x = rect.left;
    729            y = rect.top;
    730            SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
    731
    732            w = rect.right - rect.left;
    733            h = rect.bottom - rect.top;
    734            SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
    735                                h);
    736        }
    737        break;
    738
    739    case WM_SIZE:
    740        {
    741            switch (wParam) {
    742            case SIZE_MAXIMIZED:
    743                SDL_SendWindowEvent(data->window,
    744                    SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
    745                break;
    746            case SIZE_MINIMIZED:
    747                SDL_SendWindowEvent(data->window,
    748                    SDL_WINDOWEVENT_MINIMIZED, 0, 0);
    749                break;
    750            default:
    751                SDL_SendWindowEvent(data->window,
    752                    SDL_WINDOWEVENT_RESTORED, 0, 0);
    753                break;
    754            }
    755        }
    756        break;
    757
    758    case WM_SETCURSOR:
    759        {
    760            Uint16 hittest;
    761
    762            hittest = LOWORD(lParam);
    763            if (hittest == HTCLIENT) {
    764                SetCursor(SDL_cursor);
    765                returnCode = TRUE;
    766            } else if (!g_WindowFrameUsableWhileCursorHidden && !SDL_cursor) {
    767                SetCursor(NULL);
    768                returnCode = TRUE;
    769            }
    770        }
    771        break;
    772
    773        /* We were occluded, refresh our display */
    774    case WM_PAINT:
    775        {
    776            RECT rect;
    777            if (GetUpdateRect(hwnd, &rect, FALSE)) {
    778                ValidateRect(hwnd, NULL);
    779                SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
    780                                    0, 0);
    781            }
    782        }
    783        returnCode = 0;
    784        break;
    785
    786        /* We'll do our own drawing, prevent flicker */
    787    case WM_ERASEBKGND:
    788        {
    789        }
    790        return (1);
    791
    792#if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
    793    case WM_SYSCOMMAND:
    794        {
    795            /* Don't start the screensaver or blank the monitor in fullscreen apps */
    796            if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
    797                (wParam & 0xFFF0) == SC_MONITORPOWER) {
    798                if (SDL_GetVideoDevice()->suspend_screensaver) {
    799                    return (0);
    800                }
    801            }
    802        }
    803        break;
    804#endif /* System has screensaver support */
    805
    806    case WM_CLOSE:
    807        {
    808            SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
    809        }
    810        returnCode = 0;
    811        break;
    812
    813    case WM_TOUCH:
    814        {
    815            UINT i, num_inputs = LOWORD(wParam);
    816            PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs);
    817            if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) {
    818                RECT rect;
    819                float x, y;
    820
    821                if (!GetClientRect(hwnd, &rect) ||
    822                    (rect.right == rect.left && rect.bottom == rect.top)) {
    823                    if (inputs) {
    824                        SDL_stack_free(inputs);
    825                    }
    826                    break;
    827                }
    828                ClientToScreen(hwnd, (LPPOINT) & rect);
    829                ClientToScreen(hwnd, (LPPOINT) & rect + 1);
    830                rect.top *= 100;
    831                rect.left *= 100;
    832                rect.bottom *= 100;
    833                rect.right *= 100;
    834
    835                for (i = 0; i < num_inputs; ++i) {
    836                    PTOUCHINPUT input = &inputs[i];
    837
    838                    const SDL_TouchID touchId = (SDL_TouchID)((size_t)input->hSource);
    839                    if (!SDL_GetTouch(touchId)) {
    840                        if (SDL_AddTouch(touchId, "") < 0) {
    841                            continue;
    842                        }
    843                    }
    844
    845                    /* Get the normalized coordinates for the window */
    846                    x = (float)(input->x - rect.left)/(rect.right - rect.left);
    847                    y = (float)(input->y - rect.top)/(rect.bottom - rect.top);
    848
    849                    if (input->dwFlags & TOUCHEVENTF_DOWN) {
    850                        SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f);
    851                    }
    852                    if (input->dwFlags & TOUCHEVENTF_MOVE) {
    853                        SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f);
    854                    }
    855                    if (input->dwFlags & TOUCHEVENTF_UP) {
    856                        SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f);
    857                    }
    858                }
    859            }
    860            SDL_stack_free(inputs);
    861
    862            data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam);
    863            return 0;
    864        }
    865        break;
    866
    867    case WM_DROPFILES:
    868        {
    869            UINT i;
    870            HDROP drop = (HDROP) wParam;
    871            UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
    872            for (i = 0; i < count; ++i) {
    873                UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
    874                LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
    875                if (buffer) {
    876                    if (DragQueryFile(drop, i, buffer, size)) {
    877                        char *file = WIN_StringToUTF8(buffer);
    878                        SDL_SendDropFile(file);
    879                        SDL_free(file);
    880                    }
    881                    SDL_stack_free(buffer);
    882                }
    883            }
    884            DragFinish(drop);
    885            return 0;
    886        }
    887        break;
    888
    889    case WM_NCHITTEST:
    890        {
    891            SDL_Window *window = data->window;
    892            if (window->hit_test) {
    893                POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) };
    894                if (ScreenToClient(hwnd, &winpoint)) {
    895                    const SDL_Point point = { (int) winpoint.x, (int) winpoint.y };
    896                    const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
    897                    switch (rc) {
    898                        case SDL_HITTEST_DRAGGABLE: return HTCAPTION;
    899                        case SDL_HITTEST_RESIZE_TOPLEFT: return HTTOPLEFT;
    900                        case SDL_HITTEST_RESIZE_TOP: return HTTOP;
    901                        case SDL_HITTEST_RESIZE_TOPRIGHT: return HTTOPRIGHT;
    902                        case SDL_HITTEST_RESIZE_RIGHT: return HTRIGHT;
    903                        case SDL_HITTEST_RESIZE_BOTTOMRIGHT: return HTBOTTOMRIGHT;
    904                        case SDL_HITTEST_RESIZE_BOTTOM: return HTBOTTOM;
    905                        case SDL_HITTEST_RESIZE_BOTTOMLEFT: return HTBOTTOMLEFT;
    906                        case SDL_HITTEST_RESIZE_LEFT: return HTLEFT;
    907                        case SDL_HITTEST_NORMAL: return HTCLIENT;
    908                    }
    909                }
    910                /* If we didn't return, this will call DefWindowProc below. */
    911            }
    912        }
    913        break;
    914
    915    }
    916
    917    /* If there's a window proc, assume it's going to handle messages */
    918    if (data->wndproc) {
    919        return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
    920    } else if (returnCode >= 0) {
    921        return returnCode;
    922    } else {
    923        return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
    924    }
    925}
    926
    927void
    928WIN_PumpEvents(_THIS)
    929{
    930    const Uint8 *keystate;
    931    MSG msg;
    932    DWORD start_ticks = GetTickCount();
    933
    934    if (g_WindowsEnableMessageLoop) {
    935        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    936            /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */
    937            TranslateMessage(&msg);
    938            DispatchMessage(&msg);
    939
    940            /* Make sure we don't busy loop here forever if there are lots of events coming in */
    941            if (SDL_TICKS_PASSED(msg.time, start_ticks)) {
    942                break;
    943            }
    944        }
    945    }
    946
    947    /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one.
    948       You won't get a KEYUP until both are released, and that keyup will only be for the second
    949       key you released. Take heroic measures and check the keystate as of the last handled event,
    950       and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */
    951    keystate = SDL_GetKeyboardState(NULL);
    952    if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
    953        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
    954    }
    955    if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
    956        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT);
    957    }
    958}
    959
    960static int app_registered = 0;
    961LPTSTR SDL_Appname = NULL;
    962Uint32 SDL_Appstyle = 0;
    963HINSTANCE SDL_Instance = NULL;
    964
    965/* Register the class for this application */
    966int
    967SDL_RegisterApp(char *name, Uint32 style, void *hInst)
    968{
    969    WNDCLASS class;
    970
    971    /* Only do this once... */
    972    if (app_registered) {
    973        ++app_registered;
    974        return (0);
    975    }
    976    if (!name && !SDL_Appname) {
    977        name = "SDL_app";
    978#if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC)
    979        SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
    980#endif
    981        SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
    982    }
    983
    984    if (name) {
    985        SDL_Appname = WIN_UTF8ToString(name);
    986        SDL_Appstyle = style;
    987        SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
    988    }
    989
    990    /* Register the application class */
    991    class.hCursor = NULL;
    992    class.hIcon =
    993        LoadImage(SDL_Instance, SDL_Appname, IMAGE_ICON, 0, 0,
    994                  LR_DEFAULTCOLOR);
    995    class.lpszMenuName = NULL;
    996    class.lpszClassName = SDL_Appname;
    997    class.hbrBackground = NULL;
    998    class.hInstance = SDL_Instance;
    999    class.style = SDL_Appstyle;
   1000    class.lpfnWndProc = WIN_WindowProc;
   1001    class.cbWndExtra = 0;
   1002    class.cbClsExtra = 0;
   1003    if (!RegisterClass(&class)) {
   1004        return SDL_SetError("Couldn't register application class");
   1005    }
   1006
   1007    app_registered = 1;
   1008    return 0;
   1009}
   1010
   1011/* Unregisters the windowclass registered in SDL_RegisterApp above. */
   1012void
   1013SDL_UnregisterApp()
   1014{
   1015    WNDCLASS class;
   1016
   1017    /* SDL_RegisterApp might not have been called before */
   1018    if (!app_registered) {
   1019        return;
   1020    }
   1021    --app_registered;
   1022    if (app_registered == 0) {
   1023        /* Check for any registered window classes. */
   1024        if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
   1025            UnregisterClass(SDL_Appname, SDL_Instance);
   1026        }
   1027        SDL_free(SDL_Appname);
   1028        SDL_Appname = NULL;
   1029    }
   1030}
   1031
   1032#endif /* SDL_VIDEO_DRIVER_WINDOWS */
   1033
   1034/* vi: set ts=4 sw=4 expandtab: */