SDL-mirror/src/video/win32/SDL_win32events.c

960 lines
24 KiB
C
Raw Normal View History

/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997-2006 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Sam Lantinga
slouken@libsdl.org
*/
#include "SDL_config.h"
#include "SDL_win32video.h"
#include "SDL_version.h"
#include "SDL_syswm.h"
#include "SDL_vkeys.h"
#include "../../events/SDL_events_c.h"
/*#define WMMSG_DEBUG*/
#ifdef WMMSG_DEBUG
#include <stdio.h>
#include "wmmsg.h"
#endif
/* Masks for processing the windows KEYDOWN and KEYUP messages */
#define REPEATED_KEYMASK (1<<30)
#define EXTENDED_KEYMASK (1<<24)
static SDLKey
TranslateKey(WPARAM vkey)
{
SDLKey key;
/* FIXME: Assign vkey directly to key if in ASCII range */
switch (vkey) {
case VK_BACK:
key = SDLK_BACKSPACE;
break;
case VK_TAB:
key = SDLK_TAB;
break;
case VK_CLEAR:
key = SDLK_CLEAR;
break;
case VK_RETURN:
key = SDLK_RETURN;
break;
case VK_PAUSE:
key = SDLK_PAUSE;
break;
case VK_ESCAPE:
key = SDLK_ESCAPE;
break;
case VK_SPACE:
key = SDLK_SPACE;
break;
case VK_APOSTROPHE:
key = SDLK_QUOTE;
break;
case VK_COMMA:
key = SDLK_COMMA;
break;
case VK_MINUS:
key = SDLK_MINUS;
break;
case VK_PERIOD:
key = SDLK_PERIOD;
break;
case VK_SLASH:
key = SDLK_SLASH;
break;
case VK_0:
key = SDLK_0;
break;
case VK_1:
key = SDLK_1;
break;
case VK_2:
key = SDLK_2;
break;
case VK_3:
key = SDLK_3;
break;
case VK_4:
key = SDLK_4;
break;
case VK_5:
key = SDLK_5;
break;
case VK_6:
key = SDLK_6;
break;
case VK_7:
key = SDLK_7;
break;
case VK_8:
key = SDLK_8;
break;
case VK_9:
key = SDLK_9;
break;
case VK_SEMICOLON:
key = SDLK_SEMICOLON;
break;
case VK_EQUALS:
key = SDLK_EQUALS;
break;
case VK_LBRACKET:
key = SDLK_LEFTBRACKET;
break;
case VK_BACKSLASH:
key = SDLK_BACKSLASH;
break;
case VK_OEM_102:
key = SDLK_LESS;
break;
case VK_RBRACKET:
key = SDLK_RIGHTBRACKET;
break;
case VK_GRAVE:
key = SDLK_BACKQUOTE;
break;
case VK_BACKTICK:
key = SDLK_BACKQUOTE;
break;
case VK_A:
key = SDLK_a;
break;
case VK_B:
key = SDLK_b;
break;
case VK_C:
key = SDLK_c;
break;
case VK_D:
key = SDLK_d;
break;
case VK_E:
key = SDLK_e;
break;
case VK_F:
key = SDLK_f;
break;
case VK_G:
key = SDLK_g;
break;
case VK_H:
key = SDLK_h;
break;
case VK_I:
key = SDLK_i;
break;
case VK_J:
key = SDLK_j;
break;
case VK_K:
key = SDLK_k;
break;
case VK_L:
key = SDLK_l;
break;
case VK_M:
key = SDLK_m;
break;
case VK_N:
key = SDLK_n;
break;
case VK_O:
key = SDLK_o;
break;
case VK_P:
key = SDLK_p;
break;
case VK_Q:
key = SDLK_q;
break;
case VK_R:
key = SDLK_r;
break;
case VK_S:
key = SDLK_s;
break;
case VK_T:
key = SDLK_t;
break;
case VK_U:
key = SDLK_u;
break;
case VK_V:
key = SDLK_v;
break;
case VK_W:
key = SDLK_w;
break;
case VK_X:
key = SDLK_x;
break;
case VK_Y:
key = SDLK_y;
break;
case VK_Z:
key = SDLK_z;
break;
case VK_DELETE:
key = SDLK_DELETE;
break;
case VK_NUMPAD0:
key = SDLK_KP0;
break;
case VK_NUMPAD1:
key = SDLK_KP1;
break;
case VK_NUMPAD2:
key = SDLK_KP2;
break;
case VK_NUMPAD3:
key = SDLK_KP3;
break;
case VK_NUMPAD4:
key = SDLK_KP4;
break;
case VK_NUMPAD5:
key = SDLK_KP5;
break;
case VK_NUMPAD6:
key = SDLK_KP6;
break;
case VK_NUMPAD7:
key = SDLK_KP7;
break;
case VK_NUMPAD8:
key = SDLK_KP8;
break;
case VK_NUMPAD9:
key = SDLK_KP9;
break;
case VK_DECIMAL:
key = SDLK_KP_PERIOD;
break;
case VK_DIVIDE:
key = SDLK_KP_DIVIDE;
break;
case VK_MULTIPLY:
key = SDLK_KP_MULTIPLY;
break;
case VK_SUBTRACT:
key = SDLK_KP_MINUS;
break;
case VK_ADD:
key = SDLK_KP_PLUS;
break;
case VK_UP:
key = SDLK_UP;
break;
case VK_DOWN:
key = SDLK_DOWN;
break;
case VK_RIGHT:
key = SDLK_RIGHT;
break;
case VK_LEFT:
key = SDLK_LEFT;
break;
case VK_INSERT:
key = SDLK_INSERT;
break;
case VK_HOME:
key = SDLK_HOME;
break;
case VK_END:
key = SDLK_END;
break;
case VK_PRIOR:
key = SDLK_PAGEUP;
break;
case VK_NEXT:
key = SDLK_PAGEDOWN;
break;
case VK_F1:
key = SDLK_F1;
break;
case VK_F2:
key = SDLK_F2;
break;
case VK_F3:
key = SDLK_F3;
break;
case VK_F4:
key = SDLK_F4;
break;
case VK_F5:
key = SDLK_F5;
break;
case VK_F6:
key = SDLK_F6;
break;
case VK_F7:
key = SDLK_F7;
break;
case VK_F8:
key = SDLK_F8;
break;
case VK_F9:
key = SDLK_F9;
break;
case VK_F10:
key = SDLK_F10;
break;
case VK_F11:
key = SDLK_F11;
break;
case VK_F12:
key = SDLK_F12;
break;
case VK_F13:
key = SDLK_F13;
break;
case VK_F14:
key = SDLK_F14;
break;
case VK_F15:
key = SDLK_F15;
break;
case VK_NUMLOCK:
key = SDLK_NUMLOCK;
break;
case VK_CAPITAL:
key = SDLK_CAPSLOCK;
break;
case VK_SCROLL:
key = SDLK_SCROLLOCK;
break;
case VK_RSHIFT:
key = SDLK_RSHIFT;
break;
case VK_LSHIFT:
key = SDLK_LSHIFT;
break;
case VK_RCONTROL:
key = SDLK_RCTRL;
break;
case VK_LCONTROL:
key = SDLK_LCTRL;
break;
case VK_RMENU:
key = SDLK_RALT;
break;
case VK_LMENU:
key = SDLK_LALT;
break;
case VK_RWIN:
key = SDLK_RSUPER;
break;
case VK_LWIN:
key = SDLK_LSUPER;
break;
case VK_HELP:
key = SDLK_HELP;
break;
case VK_PRINT:
key = SDLK_PRINT;
break;
case VK_SNAPSHOT:
key = SDLK_PRINT;
break;
case VK_CANCEL:
key = SDLK_BREAK;
break;
case VK_APPS:
key = SDLK_MENU;
break;
default:
key = SDLK_UNKNOWN;
break;
}
return key;
}
LRESULT CALLBACK
WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
SDL_WindowData *data;
/* Get the window data for the window */
data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
if (!data) {
return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
}
#ifdef WMMSG_DEBUG
{
FILE *log = fopen("wmmsg.txt", "a");
fprintf(log, "Received windows message: %p ", hwnd);
if (msg > MAX_WMMSG) {
fprintf(log, "%d", msg);
} else {
fprintf(log, "%s", wmtab[msg]);
}
fprintf(log, " -- 0x%X, 0x%X\n", wParam, lParam);
fclose(log);
}
#endif
/* Send a SDL_SYSWMEVENT if the application wants them */
if (SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE) {
SDL_SysWMmsg wmmsg;
SDL_VERSION(&wmmsg.version);
wmmsg.hwnd = hwnd;
wmmsg.msg = msg;
wmmsg.wParam = wParam;
wmmsg.lParam = lParam;
SDL_SendSysWMEvent(&wmmsg);
}
switch (msg) {
case WM_SHOWWINDOW:
{
if (wParam) {
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_SHOWN, 0,
0);
} else {
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_HIDDEN, 0,
0);
}
}
break;
case WM_ACTIVATE:
{
int index;
SDL_Keyboard *keyboard;
BOOL minimized;
minimized = HIWORD(wParam);
index = data->videodata->keyboard;
keyboard = SDL_GetKeyboard(index);
if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_SHOWN,
0, 0);
SDL_SendWindowEvent(data->windowID,
SDL_WINDOWEVENT_RESTORED, 0, 0);
if (IsZoomed(hwnd)) {
SDL_SendWindowEvent(data->windowID,
SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
}
if (keyboard && keyboard->focus != data->windowID) {
SDL_SetKeyboardFocus(index, data->windowID);
}
/* FIXME: Update keyboard state */
} else {
if (keyboard && keyboard->focus == data->windowID) {
SDL_SetKeyboardFocus(index, 0);
}
if (minimized) {
SDL_SendWindowEvent(data->windowID,
SDL_WINDOWEVENT_MINIMIZED, 0, 0);
}
}
return (0);
}
break;
case WM_MOUSEMOVE:
{
int index;
SDL_Mouse *mouse;
int x, y;
index = data->videodata->mouse;
mouse = SDL_GetMouse(index);
if (mouse->focus != data->windowID) {
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
TrackMouseEvent(&tme);
SDL_SetMouseFocus(index, data->windowID);
}
/* mouse has moved within the window */
x = LOWORD(lParam);
y = HIWORD(lParam);
if (mouse->relative_mode) {
int w, h;
POINT center;
SDL_GetWindowSize(data->windowID, &w, &h);
center.x = (w / 2);
center.y = (h / 2);
x -= center.x;
y -= center.y;
if (x || y) {
ClientToScreen(hwnd, &center);
SetCursorPos(center.x, center.y);
SDL_SendMouseMotion(index, 1, x, y);
}
} else {
SDL_SendMouseMotion(index, 0, x, y);
}
}
return (0);
case WM_MOUSELEAVE:
{
int index;
SDL_Mouse *mouse;
index = data->videodata->mouse;
mouse = SDL_GetMouse(index);
if (mouse->focus == data->windowID) {
SDL_SetMouseFocus(index, 0);
}
}
return (0);
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
{
int index;
SDL_Mouse *mouse;
Uint8 button, state;
/* DJM:
We want the SDL window to take focus so that
it acts like a normal windows "component"
(e.g. gains keyboard focus on a mouse click).
*/
SetFocus(hwnd);
index = data->videodata->mouse;
mouse = SDL_GetMouse(index);
/* Figure out which button to use */
switch (msg) {
case WM_LBUTTONDOWN:
button = SDL_BUTTON_LEFT;
state = SDL_PRESSED;
break;
case WM_LBUTTONUP:
button = SDL_BUTTON_LEFT;
state = SDL_RELEASED;
break;
case WM_MBUTTONDOWN:
button = SDL_BUTTON_MIDDLE;
state = SDL_PRESSED;
break;
case WM_MBUTTONUP:
button = SDL_BUTTON_MIDDLE;
state = SDL_RELEASED;
break;
case WM_RBUTTONDOWN:
button = SDL_BUTTON_RIGHT;
state = SDL_PRESSED;
break;
case WM_RBUTTONUP:
button = SDL_BUTTON_RIGHT;
state = SDL_RELEASED;
break;
default:
/* Eh? Unknown button? */
return (0);
}
if (state == SDL_PRESSED) {
/* Grab mouse so we get up events */
if (++data->mouse_pressed > 0) {
SetCapture(hwnd);
}
} else {
/* Release mouse after all up events */
if (--data->mouse_pressed <= 0) {
ReleaseCapture();
data->mouse_pressed = 0;
}
}
if (!mouse->relative_mode) {
int x, y;
x = LOWORD(lParam);
y = HIWORD(lParam);
SDL_SendMouseMotion(index, 0, x, y);
}
SDL_SendMouseButton(index, state, button);
}
return (0);
case WM_MOUSEWHEEL:
{
int index;
int motion = (short) HIWORD(wParam);
index = data->videodata->mouse;
SDL_SendMouseWheel(index, motion);
}
return (0);
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
int index;
/* Ignore repeated keys */
if (lParam & REPEATED_KEYMASK) {
return (0);
}
index = data->videodata->keyboard;
switch (wParam) {
case VK_CONTROL:
if (lParam & EXTENDED_KEYMASK)
wParam = VK_RCONTROL;
else
wParam = VK_LCONTROL;
break;
case VK_SHIFT:
/* EXTENDED trick doesn't work here */
{
Uint8 *state = SDL_GetKeyState(NULL);
if (state[SDLK_LSHIFT] == SDL_RELEASED
&& (GetKeyState(VK_LSHIFT) & 0x8000)) {
wParam = VK_LSHIFT;
} else if (state[SDLK_RSHIFT] == SDL_RELEASED
&& (GetKeyState(VK_RSHIFT) & 0x8000)) {
wParam = VK_RSHIFT;
} else {
/* Probably a key repeat */
return (0);
}
}
break;
case VK_MENU:
if (lParam & EXTENDED_KEYMASK)
wParam = VK_RMENU;
else
wParam = VK_LMENU;
break;
}
SDL_SendKeyboardKey(index, SDL_PRESSED, (Uint8) HIWORD(lParam),
TranslateKey(wParam));
}
return (0);
case WM_SYSKEYUP:
case WM_KEYUP:
{
int index;
index = data->videodata->keyboard;
switch (wParam) {
case VK_CONTROL:
if (lParam & EXTENDED_KEYMASK)
wParam = VK_RCONTROL;
else
wParam = VK_LCONTROL;
break;
case VK_SHIFT:
/* EXTENDED trick doesn't work here */
{
Uint8 *state = SDL_GetKeyState(NULL);
if (state[SDLK_LSHIFT] == SDL_PRESSED
&& !(GetKeyState(VK_LSHIFT) & 0x8000)) {
wParam = VK_LSHIFT;
} else if (state[SDLK_RSHIFT] == SDL_PRESSED
&& !(GetKeyState(VK_RSHIFT) & 0x8000)) {
wParam = VK_RSHIFT;
} else {
/* Probably a key repeat */
return (0);
}
}
break;
case VK_MENU:
if (lParam & EXTENDED_KEYMASK)
wParam = VK_RMENU;
else
wParam = VK_LMENU;
break;
}
/* Windows only reports keyup for print screen */
if (wParam == VK_SNAPSHOT
&& SDL_GetKeyState(NULL)[SDLK_PRINT] == SDL_RELEASED) {
SDL_SendKeyboardKey(index, SDL_PRESSED,
(Uint8) HIWORD(lParam),
TranslateKey(wParam));
}
SDL_SendKeyboardKey(index, SDL_RELEASED, (Uint8) HIWORD(lParam),
TranslateKey(wParam));
}
return (0);
case WM_GETMINMAXINFO:
{
MINMAXINFO *info;
RECT size;
int x, y;
int w, h;
int style;
/* If we allow resizing, let the resize happen naturally */
if (SDL_GetWindowFlags(data->windowID) & SDL_WINDOW_RESIZABLE) {
return (0);
}
/* Get the current position of our window */
GetWindowRect(hwnd, &size);
x = size.left;
y = size.top;
/* Calculate current size of our window */
SDL_GetWindowSize(data->windowID, &w, &h);
size.top = 0;
size.left = 0;
size.bottom = h;
size.right = w;
/* DJM - according to the docs for GetMenu(), the
return value is undefined if hwnd is a child window.
Aparently it's too difficult for MS to check
inside their function, so I have to do it here.
*/
style = GetWindowLong(hwnd, GWL_STYLE);
AdjustWindowRect(&size,
style,
style & WS_CHILDWINDOW ? FALSE : GetMenu(hwnd) !=
NULL);
w = size.right - size.left;
h = size.bottom - size.top;
/* Fix our size to the current size */
info = (MINMAXINFO *) lParam;
info->ptMaxSize.x = w;
info->ptMaxSize.y = h;
info->ptMaxPosition.x = x;
info->ptMaxPosition.y = y;
info->ptMinTrackSize.x = w;
info->ptMinTrackSize.y = h;
info->ptMaxTrackSize.x = w;
info->ptMaxTrackSize.y = h;
}
return (0);
case WM_WINDOWPOSCHANGED:
{
RECT rect;
int x, y;
int w, h;
Uint32 window_flags;
GetClientRect(hwnd, &rect);
ClientToScreen(hwnd, (LPPOINT) & rect);
ClientToScreen(hwnd, (LPPOINT) & rect + 1);
window_flags = SDL_GetWindowFlags(data->windowID);
if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
(window_flags & SDL_WINDOW_INPUT_FOCUS)) {
ClipCursor(&rect);
}
x = rect.left;
y = rect.top;
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_MOVED, x, y);
w = rect.right - rect.left;
h = rect.bottom - rect.top;
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_RESIZED, w,
h);
}
break;
case WM_SETCURSOR:
{
/*
Uint16 hittest;
hittest = LOWORD(lParam);
if (hittest == HTCLIENT) {
SetCursor(SDL_hcursor);
return (TRUE);
}
*/
}
break;
/* We are about to get palette focus! */
case WM_QUERYNEWPALETTE:
{
/*
WIN_RealizePalette(current_video);
return (TRUE);
*/
}
break;
/* Another application changed the palette */
case WM_PALETTECHANGED:
{
/*
WIN_PaletteChanged(current_video, (HWND) wParam);
*/
}
break;
/* We were occluded, refresh our display */
case WM_PAINT:
{
RECT rect;
if (GetUpdateRect(hwnd, &rect, FALSE)) {
ValidateRect(hwnd, &rect);
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_EXPOSED,
0, 0);
}
}
return (0);
/* We'll do our own drawing, prevent flicker */
case WM_ERASEBKGND:
{
}
return (1);
case WM_SYSCOMMAND:
{
/* Don't start the screensaver or blank the monitor in fullscreen apps */
if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
(wParam & 0xFFF0) == SC_MONITORPOWER) {
if (SDL_GetWindowFlags(data->windowID) &
SDL_WINDOW_FULLSCREEN) {
return (0);
}
}
}
break;
case WM_CLOSE:
{
SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_CLOSE, 0, 0);
}
return (0);
}
return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
}
void
WIN_PumpEvents(_THIS)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
static int app_registered = 0;
LPTSTR SDL_Appname = NULL;
Uint32 SDL_Appstyle = 0;
HINSTANCE SDL_Instance = NULL;
/* Register the class for this application */
int
SDL_RegisterApp(char *name, Uint32 style, void *hInst)
{
WNDCLASS class;
/* Only do this once... */
if (app_registered) {
++app_registered;
return (0);
}
if (!name && !SDL_Appname) {
name = "SDL_app";
SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
}
if (name) {
SDL_Appname = WIN_UTF8ToString(name);
SDL_Appstyle = style;
SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
}
/* Register the application class */
class.hCursor = NULL;
class.hIcon = LoadImage(SDL_Instance, SDL_Appname,
IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
class.lpszMenuName = NULL;
class.lpszClassName = SDL_Appname;
class.hbrBackground = NULL;
class.hInstance = SDL_Instance;
class.style = SDL_Appstyle;
class.lpfnWndProc = DefWindowProc;
class.cbWndExtra = 0;
class.cbClsExtra = 0;
if (!RegisterClass(&class)) {
SDL_SetError("Couldn't register application class");
return (-1);
}
app_registered = 1;
return (0);
}
/* Unregisters the windowclass registered in SDL_RegisterApp above. */
void
SDL_UnregisterApp()
{
WNDCLASS class;
/* SDL_RegisterApp might not have been called before */
if (!app_registered) {
return;
}
--app_registered;
if (app_registered == 0) {
/* Check for any registered window classes. */
if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
UnregisterClass(SDL_Appname, SDL_Instance);
}
SDL_free(SDL_Appname);
SDL_Appname = NULL;
}
}
/* Sets an error message based on GetLastError() */
void
WIN_SetError(const char *prefix)
{
TCHAR buffer[1024];
char *message;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(), 0, buffer, SDL_arraysize(buffer), NULL);
message = WIN_StringToUTF8(buffer);
SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ":" : "", message);
SDL_free(message);
}
/* vi: set ts=4 sw=4 expandtab: */