2019-12-22 19:55:47 +00:00
|
|
|
/*
|
|
|
|
** i_input.cpp
|
|
|
|
** Handles input from keyboard, mouse, and joystick
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 1998-2009 Randy Heit
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
// DI3 only supports up to 4 mouse buttons, and I want the joystick to
|
|
|
|
// be read using DirectInput instead of winmm.
|
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#define __BYTEBOOL__
|
|
|
|
#ifndef __GNUC__
|
|
|
|
#define INITGUID
|
|
|
|
#endif
|
|
|
|
#define DIRECTINPUT_VERSION 0x800
|
|
|
|
#include <windows.h>
|
|
|
|
#include <dbt.h>
|
|
|
|
#include <dinput.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(disable:4244)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Compensate for w32api's lack
|
|
|
|
#ifndef WM_WTSSESSION_CHANGE
|
|
|
|
#define WM_WTSSESSION_CHANGE 0x02B1
|
|
|
|
#define WTS_CONSOLE_CONNECT 1
|
|
|
|
#define WTS_CONSOLE_DISCONNECT 2
|
|
|
|
#define WTS_SESSION_LOCK 7
|
|
|
|
#define WTS_SESSION_UNLOCK 8
|
|
|
|
#endif
|
|
|
|
#ifndef PBT_APMSUSPEND
|
|
|
|
// w32api does not #define the PBT_ macros in winuser.h like the PSDK does
|
|
|
|
#include <pbt.h>
|
|
|
|
#endif
|
|
|
|
#ifndef GET_RAWINPUT_CODE_WPARAM
|
|
|
|
#define GET_RAWINPUT_CODE_WPARAM(wParam) ((wParam) & 0xff)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "m_argv.h"
|
|
|
|
#include "i_input.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "i_sound.h"
|
|
|
|
#include "d_gui.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
#include "s_soundinternal.h"
|
|
|
|
#include "gameconfigfile.h"
|
|
|
|
#include "hardware.h"
|
|
|
|
#include "d_event.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "printf.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "c_buttons.h"
|
|
|
|
#include "gamecontrol.h"
|
|
|
|
|
|
|
|
// Compensate for w32api's lack
|
|
|
|
#ifndef GET_XBUTTON_WPARAM
|
|
|
|
#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
#define INGAME_PRIORITY_CLASS NORMAL_PRIORITY_CLASS
|
|
|
|
#else
|
|
|
|
//#define INGAME_PRIORITY_CLASS HIGH_PRIORITY_CLASS
|
|
|
|
#define INGAME_PRIORITY_CLASS NORMAL_PRIORITY_CLASS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
FJoystickCollection *JoyDevices[NUM_JOYDEVICES];
|
|
|
|
|
|
|
|
|
|
|
|
extern HINSTANCE g_hInst;
|
|
|
|
extern DWORD SessionID;
|
|
|
|
|
|
|
|
static HMODULE DInputDLL;
|
|
|
|
|
|
|
|
extern FMouse *Mouse;
|
|
|
|
extern FKeyboard *Keyboard;
|
2020-01-08 00:00:57 +00:00
|
|
|
extern bool ToggleFullscreen;
|
2019-12-22 19:55:47 +00:00
|
|
|
|
|
|
|
bool VidResizing;
|
|
|
|
|
|
|
|
extern BOOL vidactive;
|
|
|
|
extern HWND Window, ConWindow;
|
|
|
|
|
|
|
|
EXTERN_CVAR (String, language)
|
|
|
|
EXTERN_CVAR (Bool, lookstrafe)
|
|
|
|
EXTERN_CVAR (Bool, use_joystick)
|
|
|
|
EXTERN_CVAR (Bool, use_mouse)
|
|
|
|
|
|
|
|
static int WheelDelta;
|
|
|
|
extern bool CursorState;
|
|
|
|
|
|
|
|
extern BOOL paused;
|
|
|
|
static bool noidle = false;
|
|
|
|
|
|
|
|
LPDIRECTINPUT8 g_pdi;
|
|
|
|
LPDIRECTINPUT g_pdi3;
|
|
|
|
|
2020-01-08 00:00:57 +00:00
|
|
|
extern bool AppActive;
|
2019-12-22 19:55:47 +00:00
|
|
|
|
|
|
|
int SessionState = 0;
|
|
|
|
int BlockMouseMove;
|
|
|
|
|
|
|
|
static bool EventHandlerResultForNativeMouse;
|
|
|
|
|
|
|
|
|
|
|
|
CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
CVAR (Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
|
|
|
extern int chatmodeon;
|
|
|
|
|
|
|
|
|
|
|
|
void I_SetMouseCapture()
|
|
|
|
{
|
|
|
|
SetCapture(Window);
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_ReleaseMouseCapture()
|
|
|
|
{
|
|
|
|
ReleaseCapture();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
|
|
|
|
{
|
|
|
|
event_t ev = { EV_GUI_Event };
|
|
|
|
|
|
|
|
*result = 0;
|
|
|
|
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
case WM_SYSKEYDOWN:
|
|
|
|
case WM_KEYUP:
|
|
|
|
case WM_SYSKEYUP:
|
|
|
|
if (message == WM_KEYUP || message == WM_SYSKEYUP)
|
|
|
|
{
|
|
|
|
ev.subtype = EV_GUI_KeyUp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ev.subtype = (lParam & 0x40000000) ? EV_GUI_KeyRepeat : EV_GUI_KeyDown;
|
|
|
|
}
|
|
|
|
if (GetKeyState(VK_SHIFT) & 0x8000) ev.data3 |= GKM_SHIFT;
|
|
|
|
if (GetKeyState(VK_CONTROL) & 0x8000) ev.data3 |= GKM_CTRL;
|
|
|
|
if (GetKeyState(VK_MENU) & 0x8000) ev.data3 |= GKM_ALT;
|
|
|
|
if (wParam == VK_PROCESSKEY)
|
|
|
|
{ // Use the scan code to determine the real virtual-key code.
|
|
|
|
// ImmGetVirtualKey() will supposedly do this, but it just returns
|
|
|
|
// VK_PROCESSKEY again.
|
|
|
|
wParam = MapVirtualKey((lParam >> 16) & 255, 1);
|
|
|
|
}
|
|
|
|
if ( (ev.data1 = MapVirtualKey(wParam, 2)) )
|
|
|
|
{
|
|
|
|
D_PostEvent(&ev);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
case VK_PRIOR: ev.data1 = GK_PGUP; break;
|
|
|
|
case VK_NEXT: ev.data1 = GK_PGDN; break;
|
|
|
|
case VK_END: ev.data1 = GK_END; break;
|
|
|
|
case VK_HOME: ev.data1 = GK_HOME; break;
|
|
|
|
case VK_LEFT: ev.data1 = GK_LEFT; break;
|
|
|
|
case VK_RIGHT: ev.data1 = GK_RIGHT; break;
|
|
|
|
case VK_UP: ev.data1 = GK_UP; break;
|
|
|
|
case VK_DOWN: ev.data1 = GK_DOWN; break;
|
|
|
|
case VK_DELETE: ev.data1 = GK_DEL; break;
|
|
|
|
case VK_ESCAPE: ev.data1 = GK_ESCAPE; break;
|
|
|
|
case VK_F1: ev.data1 = GK_F1; break;
|
|
|
|
case VK_F2: ev.data1 = GK_F2; break;
|
|
|
|
case VK_F3: ev.data1 = GK_F3; break;
|
|
|
|
case VK_F4: ev.data1 = GK_F4; break;
|
|
|
|
case VK_F5: ev.data1 = GK_F5; break;
|
|
|
|
case VK_F6: ev.data1 = GK_F6; break;
|
|
|
|
case VK_F7: ev.data1 = GK_F7; break;
|
|
|
|
case VK_F8: ev.data1 = GK_F8; break;
|
|
|
|
case VK_F9: ev.data1 = GK_F9; break;
|
|
|
|
case VK_F10: ev.data1 = GK_F10; break;
|
|
|
|
case VK_F11: ev.data1 = GK_F11; break;
|
|
|
|
case VK_F12: ev.data1 = GK_F12; break;
|
|
|
|
case VK_BROWSER_BACK: ev.data1 = GK_BACK; break;
|
|
|
|
}
|
|
|
|
if (ev.data1 != 0)
|
|
|
|
{
|
|
|
|
D_PostEvent(&ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Return false for key downs so that we can handle special hotkeys
|
|
|
|
// in the main WndProc.
|
|
|
|
return ev.subtype == EV_GUI_KeyUp;
|
|
|
|
|
|
|
|
case WM_CHAR:
|
|
|
|
case WM_SYSCHAR:
|
|
|
|
if (wParam >= ' ') // only send displayable characters
|
|
|
|
{
|
|
|
|
ev.subtype = EV_GUI_Char;
|
|
|
|
ev.data1 = wParam;
|
|
|
|
ev.data2 = (message == WM_SYSCHAR);
|
|
|
|
D_PostEvent(&ev);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
case WM_RBUTTONDOWN:
|
|
|
|
case WM_RBUTTONUP:
|
|
|
|
case WM_MBUTTONDOWN:
|
|
|
|
case WM_MBUTTONUP:
|
|
|
|
case WM_XBUTTONDOWN:
|
|
|
|
case WM_XBUTTONUP:
|
|
|
|
case WM_MOUSEMOVE:
|
|
|
|
if (message >= WM_LBUTTONDOWN && message <= WM_LBUTTONDBLCLK)
|
|
|
|
{
|
|
|
|
ev.subtype = message - WM_LBUTTONDOWN + EV_GUI_LButtonDown;
|
|
|
|
}
|
|
|
|
else if (message >= WM_RBUTTONDOWN && message <= WM_RBUTTONDBLCLK)
|
|
|
|
{
|
|
|
|
ev.subtype = message - WM_RBUTTONDOWN + EV_GUI_RButtonDown;
|
|
|
|
}
|
|
|
|
else if (message >= WM_MBUTTONDOWN && message <= WM_MBUTTONDBLCLK)
|
|
|
|
{
|
|
|
|
ev.subtype = message - WM_MBUTTONDOWN + EV_GUI_MButtonDown;
|
|
|
|
}
|
|
|
|
else if (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONUP)
|
|
|
|
{
|
|
|
|
ev.subtype = message - WM_XBUTTONDOWN + EV_GUI_BackButtonDown;
|
|
|
|
if (GET_XBUTTON_WPARAM(wParam) == 2)
|
|
|
|
{
|
|
|
|
ev.subtype += EV_GUI_FwdButtonDown - EV_GUI_BackButtonDown;
|
|
|
|
}
|
|
|
|
else if (GET_XBUTTON_WPARAM(wParam) != 1)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (message == WM_MOUSEMOVE)
|
|
|
|
{
|
|
|
|
ev.subtype = EV_GUI_MouseMove;
|
|
|
|
if (BlockMouseMove > 0) return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev.data1 = LOWORD(lParam);
|
|
|
|
ev.data2 = HIWORD(lParam);
|
|
|
|
if (screen != NULL)
|
|
|
|
{
|
|
|
|
screen->ScaleCoordsFromWindow(ev.data1, ev.data2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wParam & MK_SHIFT) ev.data3 |= GKM_SHIFT;
|
|
|
|
if (wParam & MK_CONTROL) ev.data3 |= GKM_CTRL;
|
|
|
|
if (GetKeyState(VK_MENU) & 0x8000) ev.data3 |= GKM_ALT;
|
|
|
|
|
|
|
|
if (use_mouse) D_PostEvent(&ev);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Note: If the mouse is grabbed, it sends the mouse wheel events itself.
|
|
|
|
case WM_MOUSEWHEEL:
|
|
|
|
if (!use_mouse) return false;
|
|
|
|
if (wParam & MK_SHIFT) ev.data3 |= GKM_SHIFT;
|
|
|
|
if (wParam & MK_CONTROL) ev.data3 |= GKM_CTRL;
|
|
|
|
if (GetKeyState(VK_MENU) & 0x8000) ev.data3 |= GKM_ALT;
|
|
|
|
WheelDelta += (SHORT)HIWORD(wParam);
|
|
|
|
if (WheelDelta < 0)
|
|
|
|
{
|
|
|
|
ev.subtype = EV_GUI_WheelDown;
|
|
|
|
while (WheelDelta <= -WHEEL_DELTA)
|
|
|
|
{
|
|
|
|
D_PostEvent(&ev);
|
|
|
|
WheelDelta += WHEEL_DELTA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ev.subtype = EV_GUI_WheelUp;
|
|
|
|
while (WheelDelta >= WHEEL_DELTA)
|
|
|
|
{
|
|
|
|
D_PostEvent(&ev);
|
|
|
|
WheelDelta -= WHEEL_DELTA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CallHook(FInputDevice *device, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
|
|
|
|
{
|
|
|
|
if (device == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*result = 0;
|
|
|
|
return device->WndProcHook(hWnd, message, wParam, lParam, result);
|
|
|
|
}
|
|
|
|
|
2020-01-03 19:13:44 +00:00
|
|
|
void GetRefreshRate(HWND hWnd)
|
|
|
|
{
|
|
|
|
HMONITOR moni = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
|
|
|
|
MONITORINFOEXA moninf;
|
|
|
|
moninf.cbSize = sizeof(moninf);
|
|
|
|
if (GetMonitorInfoA(moni, (LPMONITORINFO)&moninf))
|
|
|
|
{
|
|
|
|
DEVMODEA dm;
|
|
|
|
dm.dmSize = sizeof(DEVMODEA);
|
|
|
|
if (EnumDisplaySettingsA(moninf.szDevice, ENUM_CURRENT_SETTINGS, &dm))
|
|
|
|
{
|
|
|
|
refreshfreq = dm.dmDisplayFrequency;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 19:55:47 +00:00
|
|
|
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
LRESULT result;
|
|
|
|
|
|
|
|
if (message == WM_INPUT)
|
|
|
|
{
|
|
|
|
UINT size;
|
|
|
|
|
|
|
|
if (!GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) &&
|
|
|
|
size != 0)
|
|
|
|
{
|
|
|
|
uint8_t *buffer = (uint8_t *)alloca(size);
|
|
|
|
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size)
|
|
|
|
{
|
|
|
|
int code = GET_RAWINPUT_CODE_WPARAM(wParam);
|
|
|
|
if (Keyboard == NULL || !Keyboard->ProcessRawInput((RAWINPUT *)buffer, code))
|
|
|
|
{
|
|
|
|
if (Mouse == NULL || !Mouse->ProcessRawInput((RAWINPUT *)buffer, code))
|
|
|
|
{
|
|
|
|
if (JoyDevices[INPUT_RawPS2] != NULL)
|
|
|
|
{
|
|
|
|
JoyDevices[INPUT_RawPS2]->ProcessRawInput((RAWINPUT *)buffer, code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CallHook(Keyboard, hWnd, message, wParam, lParam, &result))
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (CallHook(Mouse, hWnd, message, wParam, lParam, &result))
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
|
|
|
{
|
|
|
|
if (CallHook(JoyDevices[i], hWnd, message, wParam, lParam, &result))
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (GUICapture && GUIWndProcHook(hWnd, message, wParam, lParam, &result))
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if ((gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL) && message == WM_LBUTTONDOWN)
|
|
|
|
{
|
|
|
|
if (GUIWndProcHook(hWnd, message, wParam, lParam, &result))
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case WM_DESTROY:
|
|
|
|
SetPriorityClass (GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
|
|
|
PostQuitMessage (0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_HOTKEY:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
|
|
|
|
|
|
|
case WM_SETTINGCHANGE:
|
|
|
|
// If regional settings were changed, reget preferred languages
|
|
|
|
if (wParam == 0 && lParam != 0 && strcmp ((const char *)lParam, "intl") == 0)
|
|
|
|
{
|
|
|
|
language.Callback ();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_KILLFOCUS:
|
|
|
|
I_CheckNativeMouse (true, false); // Make sure mouse gets released right away
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SETFOCUS:
|
2020-01-03 19:13:44 +00:00
|
|
|
GetRefreshRate(hWnd);
|
2019-12-24 11:59:26 +00:00
|
|
|
I_CheckNativeMouse (false, false); // This cannot call the event handler. Doing it from here is unsafe.
|
2019-12-22 19:55:47 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SETCURSOR:
|
|
|
|
if (!CursorState)
|
|
|
|
{
|
|
|
|
SetCursor(NULL); // turn off window cursor
|
|
|
|
return TRUE; // Prevent Windows from setting cursor to window class cursor
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SIZE:
|
|
|
|
InvalidateRect (Window, NULL, FALSE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SYSKEYDOWN:
|
|
|
|
// Pressing Alt+Enter can toggle between fullscreen and windowed.
|
|
|
|
if (wParam == VK_RETURN && k_allowfullscreentoggle && !(lParam & 0x40000000))
|
|
|
|
{
|
|
|
|
ToggleFullscreen = !ToggleFullscreen;
|
|
|
|
}
|
|
|
|
// Pressing Alt+F4 quits the program.
|
|
|
|
if (wParam == VK_F4 && !(lParam & 0x40000000))
|
|
|
|
{
|
|
|
|
PostQuitMessage(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SYSCOMMAND:
|
|
|
|
// Prevent activation of the window menu with Alt+Space
|
|
|
|
if ((wParam & 0xFFF0) != SC_KEYMENU)
|
|
|
|
{
|
|
|
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_DISPLAYCHANGE:
|
2020-01-03 19:13:44 +00:00
|
|
|
GetRefreshRate(hWnd);
|
|
|
|
// fall through
|
2019-12-22 19:55:47 +00:00
|
|
|
case WM_STYLECHANGED:
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
|
|
|
|
case WM_GETMINMAXINFO:
|
|
|
|
if (screen && !VidResizing)
|
|
|
|
{
|
|
|
|
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
|
|
|
|
if (screen->IsFullscreen())
|
|
|
|
{
|
|
|
|
RECT rect = { 0, 0, screen->GetWidth(), screen->GetHeight() };
|
|
|
|
AdjustWindowRectEx(&rect, WS_VISIBLE | WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW);
|
|
|
|
mmi->ptMinTrackSize.x = rect.right - rect.left;
|
|
|
|
mmi->ptMinTrackSize.y = rect.bottom - rect.top;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RECT rect = { 0, 0, VID_MIN_WIDTH, VID_MIN_HEIGHT };
|
|
|
|
AdjustWindowRectEx(&rect, GetWindowLongW(hWnd, GWL_STYLE), FALSE, GetWindowLongW(hWnd, GWL_EXSTYLE));
|
|
|
|
mmi->ptMinTrackSize.x = rect.right - rect.left;
|
|
|
|
mmi->ptMinTrackSize.y = rect.bottom - rect.top;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_ACTIVATEAPP:
|
|
|
|
AppActive = wParam == TRUE;
|
|
|
|
if (wParam)
|
|
|
|
{
|
|
|
|
SetPriorityClass (GetCurrentProcess (), INGAME_PRIORITY_CLASS);
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
else if (!noidle && !netgame)
|
|
|
|
{
|
|
|
|
SetPriorityClass (GetCurrentProcess (), IDLE_PRIORITY_CLASS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
S_SetSoundPaused ((!!i_soundinbackground) || wParam);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_WTSSESSION_CHANGE:
|
|
|
|
case WM_POWERBROADCAST:
|
|
|
|
{
|
|
|
|
int oldstate = SessionState;
|
|
|
|
|
|
|
|
if (message == WM_WTSSESSION_CHANGE && lParam == (LPARAM)SessionID)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
OutputDebugStringA ("SessionID matched\n");
|
|
|
|
#endif
|
|
|
|
// When using fast user switching, XP will lock a session before
|
|
|
|
// disconnecting it, and the session will be unlocked before reconnecting it.
|
|
|
|
// For our purposes, video output will only happen when the session is
|
|
|
|
// both unlocked and connected (that is, SessionState is 0).
|
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
case WTS_SESSION_LOCK:
|
|
|
|
SessionState |= 1;
|
|
|
|
break;
|
|
|
|
case WTS_SESSION_UNLOCK:
|
|
|
|
SessionState &= ~1;
|
|
|
|
break;
|
|
|
|
case WTS_CONSOLE_DISCONNECT:
|
|
|
|
SessionState |= 2;
|
|
|
|
break;
|
|
|
|
case WTS_CONSOLE_CONNECT:
|
|
|
|
SessionState &= ~2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (message == WM_POWERBROADCAST)
|
|
|
|
{
|
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
case PBT_APMSUSPEND:
|
|
|
|
SessionState |= 4;
|
|
|
|
break;
|
|
|
|
case PBT_APMRESUMESUSPEND:
|
|
|
|
SessionState &= ~4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GSnd != NULL)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
// Do we actually need this here?
|
|
|
|
if (!oldstate && SessionState)
|
|
|
|
{
|
|
|
|
GSnd->SuspendSound ();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
|
|
char foo[256];
|
|
|
|
snprintf (foo, countof(foo), "Session Change: %ld %d\n", (long)lParam, (int)wParam);
|
|
|
|
OutputDebugStringA (foo);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_ERASEBKGND:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case WM_DEVICECHANGE:
|
|
|
|
if (wParam == DBT_DEVNODES_CHANGED ||
|
|
|
|
wParam == DBT_DEVICEARRIVAL ||
|
|
|
|
wParam == DBT_CONFIGCHANGED)
|
|
|
|
{
|
|
|
|
event_t ev = { EV_DeviceChange };
|
|
|
|
D_PostEvent(&ev);
|
|
|
|
}
|
|
|
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool I_InitInput (void *hwnd)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
Printf ("I_InitInput\n");
|
|
|
|
|
|
|
|
noidle = !!Args->CheckParm ("-noidle");
|
|
|
|
g_pdi = NULL;
|
|
|
|
g_pdi3 = NULL;
|
|
|
|
|
|
|
|
// Try for DirectInput 8 first, then DirectInput 3 for NT 4's benefit.
|
|
|
|
DInputDLL = LoadLibraryA("dinput8.dll");
|
|
|
|
if (DInputDLL != NULL)
|
|
|
|
{
|
|
|
|
typedef HRESULT (WINAPI *blah)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN);
|
|
|
|
blah di8c = (blah)GetProcAddress(DInputDLL, "DirectInput8Create");
|
|
|
|
if (di8c != NULL)
|
|
|
|
{
|
|
|
|
hr = di8c(g_hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&g_pdi, NULL);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
Printf(TEXTCOLOR_ORANGE "DirectInput8Create failed: %08lx\n", hr);
|
|
|
|
g_pdi = NULL; // Just to be sure DirectInput8Create didn't change it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf(TEXTCOLOR_ORANGE "Could not find DirectInput8Create in dinput8.dll\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_pdi == NULL)
|
|
|
|
{
|
|
|
|
if (DInputDLL != NULL)
|
|
|
|
{
|
|
|
|
FreeLibrary(DInputDLL);
|
|
|
|
}
|
|
|
|
DInputDLL = LoadLibraryA ("dinput.dll");
|
|
|
|
if (DInputDLL == NULL)
|
|
|
|
{
|
|
|
|
I_FatalError ("Could not load dinput.dll: %08lx", GetLastError());
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef HRESULT (WINAPI *blah)(HINSTANCE, DWORD, LPDIRECTINPUT*, LPUNKNOWN);
|
|
|
|
#ifdef UNICODE
|
|
|
|
blah dic = (blah)GetProcAddress (DInputDLL, "DirectInputCreateW");
|
|
|
|
#else
|
|
|
|
blah dic = (blah)GetProcAddress(DInputDLL, "DirectInputCreateA");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (dic == NULL)
|
|
|
|
{
|
|
|
|
I_FatalError ("dinput.dll is corrupt");
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = dic (g_hInst, 0x0300, &g_pdi3, NULL);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
I_FatalError ("DirectInputCreate failed: %08lx", hr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Printf ("I_StartupMouse\n");
|
|
|
|
I_StartupMouse();
|
|
|
|
|
|
|
|
Printf ("I_StartupKeyboard\n");
|
|
|
|
I_StartupKeyboard();
|
|
|
|
|
|
|
|
Printf ("I_StartupXInput\n");
|
|
|
|
I_StartupXInput();
|
|
|
|
|
|
|
|
Printf ("I_StartupRawPS2\n");
|
|
|
|
I_StartupRawPS2();
|
|
|
|
|
|
|
|
Printf ("I_StartupDirectInputJoystick\n");
|
|
|
|
I_StartupDirectInputJoystick();
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Free all input resources
|
|
|
|
void I_ShutdownInput ()
|
|
|
|
{
|
|
|
|
if (Keyboard != NULL)
|
|
|
|
{
|
|
|
|
delete Keyboard;
|
|
|
|
Keyboard = NULL;
|
|
|
|
}
|
|
|
|
if (Mouse != NULL)
|
|
|
|
{
|
|
|
|
delete Mouse;
|
|
|
|
Mouse = NULL;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
|
|
|
{
|
|
|
|
if (JoyDevices[i] != NULL)
|
|
|
|
{
|
|
|
|
delete JoyDevices[i];
|
|
|
|
JoyDevices[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (g_pdi)
|
|
|
|
{
|
|
|
|
g_pdi->Release ();
|
|
|
|
g_pdi = NULL;
|
|
|
|
}
|
|
|
|
if (g_pdi3)
|
|
|
|
{
|
|
|
|
g_pdi3->Release ();
|
|
|
|
g_pdi3 = NULL;
|
|
|
|
}
|
|
|
|
if (DInputDLL != NULL)
|
|
|
|
{
|
|
|
|
FreeLibrary (DInputDLL);
|
|
|
|
DInputDLL = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_GetEvent ()
|
|
|
|
{
|
|
|
|
MSG mess;
|
|
|
|
|
|
|
|
// Briefly enter an alertable state so that if a secondary thread
|
|
|
|
// crashed, we will execute the APC it sent now.
|
|
|
|
SleepEx (0, TRUE);
|
|
|
|
|
|
|
|
while (PeekMessage (&mess, NULL, 0, 0, PM_REMOVE))
|
|
|
|
{
|
|
|
|
if (mess.message == WM_QUIT)
|
|
|
|
throw ExitEvent(mess.wParam);
|
|
|
|
|
|
|
|
if (GUICapture)
|
|
|
|
{
|
|
|
|
TranslateMessage (&mess);
|
|
|
|
}
|
|
|
|
DispatchMessage (&mess);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Keyboard != NULL)
|
|
|
|
{
|
|
|
|
Keyboard->ProcessInput();
|
|
|
|
}
|
|
|
|
if (Mouse != NULL)
|
|
|
|
{
|
|
|
|
Mouse->ProcessInput();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// I_StartTic
|
|
|
|
//
|
|
|
|
void I_StartTic ()
|
|
|
|
{
|
2019-12-24 11:59:26 +00:00
|
|
|
I_StartFrame();
|
2019-12-22 19:55:47 +00:00
|
|
|
BlockMouseMove--;
|
|
|
|
buttonMap.ResetButtonTriggers ();
|
2019-12-23 09:53:58 +00:00
|
|
|
I_CheckNativeMouse (false, false);
|
2019-12-22 19:55:47 +00:00
|
|
|
I_GetEvent ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// I_StartFrame
|
|
|
|
//
|
|
|
|
void I_StartFrame ()
|
|
|
|
{
|
|
|
|
if (use_joystick)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
|
|
|
{
|
|
|
|
if (JoyDevices[i] != NULL)
|
|
|
|
{
|
|
|
|
JoyDevices[i]->ProcessInput();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_GetAxes(float axes[NUM_JOYAXIS])
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_JOYAXIS; ++i)
|
|
|
|
{
|
|
|
|
axes[i] = 0;
|
|
|
|
}
|
|
|
|
if (use_joystick)
|
|
|
|
{
|
|
|
|
for (i = 0; i < NUM_JOYDEVICES; ++i)
|
|
|
|
{
|
|
|
|
if (JoyDevices[i] != NULL)
|
|
|
|
{
|
|
|
|
JoyDevices[i]->AddAxes(axes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_GetJoysticks(TArray<IJoystickConfig *> &sticks)
|
|
|
|
{
|
|
|
|
sticks.Clear();
|
|
|
|
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
|
|
|
{
|
|
|
|
if (JoyDevices[i] != NULL)
|
|
|
|
{
|
|
|
|
JoyDevices[i]->GetDevices(sticks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a new controller was added, returns a pointer to it.
|
|
|
|
IJoystickConfig *I_UpdateDeviceList()
|
|
|
|
{
|
|
|
|
IJoystickConfig *newone = NULL;
|
|
|
|
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
|
|
|
{
|
|
|
|
if (JoyDevices[i] != NULL)
|
|
|
|
{
|
|
|
|
IJoystickConfig *thisnewone = JoyDevices[i]->Rescan();
|
|
|
|
if (newone == NULL)
|
|
|
|
{
|
|
|
|
newone = thisnewone;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newone;
|
|
|
|
}
|
|
|
|
|
|
|
|
void I_PutInClipboard (const char *str)
|
|
|
|
{
|
|
|
|
if (str == NULL || !OpenClipboard (Window))
|
|
|
|
return;
|
|
|
|
EmptyClipboard ();
|
|
|
|
|
|
|
|
auto wstr = WideString(str);
|
|
|
|
HGLOBAL cliphandle = GlobalAlloc (GMEM_DDESHARE, wstr.length() * 2 + 2);
|
|
|
|
if (cliphandle != NULL)
|
|
|
|
{
|
|
|
|
wchar_t *ptr = (wchar_t *)GlobalLock (cliphandle);
|
|
|
|
wcscpy (ptr, wstr.c_str());
|
|
|
|
GlobalUnlock (cliphandle);
|
|
|
|
SetClipboardData (CF_UNICODETEXT, cliphandle);
|
|
|
|
}
|
|
|
|
CloseClipboard ();
|
|
|
|
}
|
|
|
|
|
|
|
|
FString I_GetFromClipboard (bool return_nothing)
|
|
|
|
{
|
|
|
|
FString retstr;
|
|
|
|
HGLOBAL cliphandle;
|
|
|
|
wchar_t *clipstr;
|
|
|
|
|
|
|
|
if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (Window))
|
|
|
|
return retstr;
|
|
|
|
|
|
|
|
cliphandle = GetClipboardData (CF_UNICODETEXT);
|
|
|
|
if (cliphandle != nullptr)
|
|
|
|
{
|
|
|
|
clipstr = (wchar_t *)GlobalLock (cliphandle);
|
|
|
|
if (clipstr != nullptr)
|
|
|
|
{
|
|
|
|
// Convert CR-LF pairs to just LF.
|
|
|
|
retstr = clipstr;
|
|
|
|
GlobalUnlock(clipstr);
|
|
|
|
retstr.Substitute("\r\n", "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseClipboard ();
|
|
|
|
return retstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FInputDevice - Destructor
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FInputDevice::~FInputDevice()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FInputDevice :: ProcessInput
|
|
|
|
//
|
|
|
|
// Gives subclasses an opportunity to do input handling that doesn't involve
|
|
|
|
// window messages.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FInputDevice::ProcessInput()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FInputDevice :: ProcessRawInput
|
|
|
|
//
|
|
|
|
// Gives subclasses a chance to handle WM_INPUT messages. This is not part
|
|
|
|
// of WndProcHook so that we only need to fill the RAWINPUT buffer once
|
|
|
|
// per message and be sure it gets cleaned up properly.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FInputDevice::ProcessRawInput(RAWINPUT *raw, int code)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FInputDevice :: WndProcHook
|
|
|
|
//
|
|
|
|
// Gives subclasses a chance to intercept window messages.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FInputDevice::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|