29 #include "../../idlib/precompiled.h"
35 #define DINPUT_BUFFERSIZE 256
37 #define CHAR_FIRSTREPEAT 200
38 #define CHAR_REPEAT 100
50 static DIOBJECTDATAFORMAT rgodf[] = {
51 { &GUID_XAxis, FIELD_OFFSET(
MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
52 { &GUID_YAxis, FIELD_OFFSET(
MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
53 { &GUID_ZAxis, FIELD_OFFSET(
MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
54 { 0, FIELD_OFFSET(
MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
55 { 0, FIELD_OFFSET(
MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
56 { 0, FIELD_OFFSET(
MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
57 { 0, FIELD_OFFSET(
MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
62 static const unsigned char s_scantokey[256] = {
65 0, 27,
'1',
'2',
'3',
'4',
'5',
'6',
67 'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
69 'd',
'f',
'g',
'h',
'j',
'k',
'l',
';',
70 '\'',
'`',
K_SHIFT,
'\\',
'z',
'x',
'c',
'v',
77 0, 0, 0, 0, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0,
80 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 27,
'!',
'@',
'#',
'$',
'%',
'^',
84 'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
86 'd',
'f',
'g',
'h',
'j',
'k',
'l',
';',
87 '\'',
'~',
K_SHIFT,
'\\',
'z',
'x',
'c',
'v',
94 0, 0, 0, 0, 0, 0, 0, 0,
95 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0,
97 0, 0, 0, 0, 0, 0, 0, 0
100 static const unsigned char s_scantokey_german[256] = {
103 0, 27,
'1',
'2',
'3',
'4',
'5',
'6',
105 'q',
'w',
'e',
'r',
't',
'z',
'u',
'i',
107 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'[',
108 ']',
'`',
K_SHIFT,
'#',
'y',
'x',
'c',
'v',
115 0, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 27,
'1',
'2',
'3',
'4',
'5',
'6',
122 'q',
'w',
'e',
'r',
't',
'z',
'u',
'i',
124 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'[',
125 ']',
'`',
K_SHIFT,
'#',
'y',
'x',
'c',
'v',
132 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0,
134 0, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 0, 0, 0, 0
138 static const unsigned char s_scantokey_french[256] = {
141 0, 27,
'1',
'2',
'3',
'4',
'5',
'6',
143 'a',
'z',
'e',
'r',
't',
'y',
'u',
'i',
145 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'm',
146 'ù' '`', K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '&', 'é', '\"', '\'', '(', '-',
'è', '_', 'ç', 'à', '°', '+', K_BACKSPACE, 9, // 0
'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'ù', 0, K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_spanish[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ',
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'`',
K_SHIFT,
'*',
'w',
'x',
'c',
'v',
153 0, 0, 0, 0, 0, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0,
158 0, 27,
'&',
'é' '\"', '\'', '(', '-',
'è', '_', 'ç', 'à', '°', '+', K_BACKSPACE, 9, // 0
'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'ù', 0, K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_spanish[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ',
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'\"',
'\'',
'(',
'-',
159 'è' '_', 'ç', 'à', '°', '+', K_BACKSPACE, 9, // 0
'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'ù', 0, K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_spanish[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ',
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'_',
'ç' 'à', '°', '+', K_BACKSPACE, 9, // 0
'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'ù', 0, K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_spanish[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ',
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'à' '°', '+', K_BACKSPACE, 9, // 0
'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'ù', 0, K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_spanish[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ',
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'°',
'+',
K_BACKSPACE, 9,
160 'a',
'z',
'e',
'r',
't',
'y',
'u',
'i',
162 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'm',
163 'ù' 0, K_SHIFT, '*', 'w', 'x', 'c', 'v', // 2
'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_spanish[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ',
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
, 0,
K_SHIFT,
'*',
'w',
'x',
'c',
'v',
170 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0,
172 0, 0, 0, 0, 0, 0, 0, 0,
173 0, 0, 0, 0, 0, 0, 0, 0
176 static const unsigned char s_scantokey_spanish[256] = {
179 0, 27,
'1',
'2',
'3',
'4',
'5',
'6',
181 'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
183 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'ñ'
'´', 'º', K_SHIFT, 'ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
184 '´',
'º',
K_SHIFT,
'ç' 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '·', '$', '%', '&',
'/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
'¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char s_scantokey_italian[256] = {
// 0 1 2 3 4 5 6 7
// 8 9 A B C D E F
0, 27, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '\'', 'ì', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'z',
'x',
'c',
'v',
191 0, 0, 0, 0, 0, 0, 0, 0,
192 0, 0, 0, 0, 0, 0, 0, 0,
193 0, 0, 0, 0, 0, 0, 0, 0,
194 0, 0, 0, 0, 0, 0, 0, 0,
196 0, 27,
'!',
'\"',
'·',
'$',
'%',
'&',
198 'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
200 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'Ñ',
201 '¨',
'ª',
K_SHIFT,
'Ç',
'z',
'x',
'c',
'v',
208 0, 0, 0, 0, 0, 0, 0, 0,
209 0, 0, 0, 0, 0, 0, 0, 0,
210 0, 0, 0, 0, 0, 0, 0, 0,
211 0, 0, 0, 0, 0, 0, 0, 0
214 static const unsigned char s_scantokey_italian[256] = {
217 0, 27,
'1',
'2',
'3',
'4',
'5',
'6',
218 '7',
'8',
'9',
'0',
'\'',
'ì' K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'è', '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
K_BACKSPACE, 9,
219 'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
220 'o',
'p',
'è' '+', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò',
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'+',
K_ENTER,
K_CTRL,
'a',
's',
221 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'ò'
'à', '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
222 'à' '\\', K_SHIFT, 'ù', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'\\',
K_SHIFT,
'ù' 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 7
// shifted
0, 27, '!', '\"', '£', '$', '%', '&',
'/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', 'é', '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'z',
'x',
'c',
'v',
229 0, 0, 0, 0, 0, 0, 0, 0,
230 0, 0, 0, 0, 0, 0, 0, 0,
231 0, 0, 0, 0, 0, 0, 0, 0,
232 0, 0, 0, 0, 0, 0, 0, 0,
234 0, 27,
'!',
'\"',
'£',
'$',
'%',
'&',
236 'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
237 'o',
'p',
'é' '*', K_ENTER,K_CTRL, 'a', 's', // 1
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ç',
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
'*',
K_ENTER,
K_CTRL,
'a',
's',
238 'd',
'f',
'g',
'h',
'j',
'k',
'l',
'ç'
'°', '|', K_SHIFT, '§', 'z', 'x', 'c', 'v', // 2
'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCROLL, K_HOME,
K_UPARROW, K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 // 7
};
static const unsigned char *keyScanTable = s_scantokey;
// this should be part of the scantables and the scan tables should be 512 bytes
// (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
// scan tables now might introduce bugs in tested code. Since we only need to fix
// the right-alt case for non-US keyboards, we're just using a special-case table
// for it. Eventually, the tables above should be fixed to handle all possible
// scan codes instead of just the first 128.
static unsigned char rightAltKey = K_ALT;
#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
static DIDATAFORMAT df = {
sizeof(DIDATAFORMAT), // this structure
sizeof(DIOBJECTDATAFORMAT), // size of object data format
DIDF_RELAXIS, // absolute axis coordinates
sizeof(MYDATA), // device data size
NUM_OBJECTS, // number of objects
rgodf, // and here they are
};
/*
============================================================
DIRECT INPUT KEYBOARD CONTROL
============================================================
*/
bool IN_StartupKeyboard( void ) {
HRESULT hr;
bool bExclusive;
bool bForeground;
bool bImmediate;
bool bDisableWindowsKey;
DWORD dwCoopFlags;
if (!win32.g_pdi) {
common->Printf("keyboard: DirectInput has not been started\n");
return false;
}
if (win32.g_pKeyboard) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
// Detrimine where the buffer would like to be allocated
bExclusive = false;
bForeground = true;
bImmediate = false;
bDisableWindowsKey = true;
if( bExclusive )
dwCoopFlags = DISCL_EXCLUSIVE;
else
dwCoopFlags = DISCL_NONEXCLUSIVE;
if( bForeground )
dwCoopFlags |= DISCL_FOREGROUND;
else
dwCoopFlags |= DISCL_BACKGROUND;
// Disabling the windows key is only allowed only if we are in foreground nonexclusive
if( bDisableWindowsKey && !bExclusive && bForeground )
dwCoopFlags |= DISCL_NOWINKEY;
// Obtain an interface to the system keyboard device.
if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
common->Printf("keyboard: couldn't find a keyboard device\n");
return false;
}
// Set the data format to "keyboard format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing an array
// of 256 bytes to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
return false;
// Set the cooperativity level to let DirectInput know how
// this device should interact with the system and with other
// DirectInput applications.
hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
return false;
}
if( FAILED(hr) ) {
return false;
}
if( !bImmediate ) {
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
return false;
}
// Acquire the newly created device
win32.g_pKeyboard->Acquire();
common->Printf( "keyboard: DirectInput initialized.\n");
return true;
}
/*
=======
MapKey
Map from windows to quake keynums
FIXME: scan code tables should include the upper 128 scan codes instead
of having to special-case them here. The current code makes it difficult
to special-case conversions for non-US keyboards. Currently the only
special-case is for right alt.
=======
*/
int IN_DIMapKey (int key) {
if ( key>=128 ) {
switch ( key ) {
case DIK_HOME:
return K_HOME;
case DIK_UPARROW:
return K_UPARROW;
case DIK_PGUP:
return K_PGUP;
case DIK_LEFTARROW:
return K_LEFTARROW;
case DIK_RIGHTARROW:
return K_RIGHTARROW;
case DIK_END:
return K_END;
case DIK_DOWNARROW:
return K_DOWNARROW;
case DIK_PGDN:
return K_PGDN;
case DIK_INSERT:
return K_INS;
case DIK_DELETE:
return K_DEL;
case DIK_RMENU:
return rightAltKey;
case DIK_RCONTROL:
return K_CTRL;
case DIK_NUMPADENTER:
return K_KP_ENTER;
case DIK_NUMPADEQUALS:
return K_KP_EQUALS;
case DIK_PAUSE:
return K_PAUSE;
case DIK_DIVIDE:
return K_KP_SLASH;
case DIK_LWIN:
return K_LWIN;
case DIK_RWIN:
return K_RWIN;
case DIK_APPS:
return K_MENU;
case DIK_SYSRQ:
return K_PRINT_SCR;
default:
return 0;
}
} else {
switch (key) {
case DIK_NUMPAD7:
return K_KP_HOME;
case DIK_NUMPAD8:
return K_KP_UPARROW;
case DIK_NUMPAD9:
return K_KP_PGUP;
case DIK_NUMPAD4:
return K_KP_LEFTARROW;
case DIK_NUMPAD5:
return K_KP_5;
case DIK_NUMPAD6:
return K_KP_RIGHTARROW;
case DIK_NUMPAD1:
return K_KP_END;
case DIK_NUMPAD2:
return K_KP_DOWNARROW;
case DIK_NUMPAD3:
return K_KP_PGDN;
case DIK_NUMPAD0:
return K_KP_INS;
case DIK_DECIMAL:
return K_KP_DEL;
case DIK_SUBTRACT:
return K_KP_MINUS;
case DIK_ADD:
return K_KP_PLUS;
case DIK_NUMLOCK:
return K_KP_NUMLOCK;
case DIK_MULTIPLY:
return K_KP_STAR;
default:
return keyScanTable[key];
}
}
}
/*
==========================
IN_DeactivateKeyboard
==========================
*/
void IN_DeactivateKeyboard( void ) {
if (!win32.g_pKeyboard) {
return;
}
win32.g_pKeyboard->Unacquire( );
}
/*
============================================================
DIRECT INPUT MOUSE CONTROL
============================================================
*/
/*
========================
IN_InitDirectInput
========================
*/
void IN_InitDirectInput( void ) {
HRESULT hr;
common->Printf( "Initializing DirectInput...\n" );
if ( win32.g_pdi != NULL ) {
win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
win32.g_pdi = NULL;
}
// Register with the DirectInput subsystem and get a pointer
// to a IDirectInput interface we can use.
// Create the base DirectInput object
if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
common->Printf ("DirectInputCreate failed\n");
}
}
/*
========================
IN_InitDIMouse
========================
*/
bool IN_InitDIMouse( void ) {
HRESULT hr;
if ( win32.g_pdi == NULL) {
return false;
}
// obtain an interface to the system mouse device.
hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't open DI mouse device\n");
return false;
}
// Set the data format to "mouse format" - a predefined data format
//
// A data format specifies which controls on a device we
// are interested in, and how they should be reported.
//
// This tells DirectInput that we will be passing a
// DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
common->Printf ("mouse: Couldn't set DI mouse format\n");
return false;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
common->Printf ("mouse: Couldn't set DI coop level\n");
return false;
}
// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
//
// DirectInput uses unbuffered I/O (buffer size = 0) by default.
// If you want to read buffered data, you need to set a nonzero
// buffer size.
//
// Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
//
// The buffer size is a DWORD property associated with the device.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
common->Printf ("mouse: Couldn't set DI buffersize\n");
return false;
}
IN_ActivateMouse();
// clear any pending samples
Sys_PollMouseInputEvents();
common->Printf( "mouse: DirectInput initialized.\n");
return true;
}
/*
==========================
IN_ActivateMouse
==========================
*/
void IN_ActivateMouse( void ) {
int i;
HRESULT hr;
if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
return;
}
win32.mouseGrabbed = true;
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( false ) < 0 ) {
break;
}
}
// we may fail to reacquire if the window has been recreated
hr = win32.g_pMouse->Acquire();
if (FAILED(hr)) {
return;
}
// set the cooperativity level.
hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
}
/*
==========================
IN_DeactivateMouse
==========================
*/
void IN_DeactivateMouse( void ) {
int i;
if (!win32.g_pMouse || !win32.mouseGrabbed ) {
return;
}
win32.g_pMouse->Unacquire();
for ( i = 0; i < 10; i++ ) {
if ( ::ShowCursor( true ) >= 0 ) {
break;
}
}
win32.mouseGrabbed = false;
}
/*
==========================
IN_DeactivateMouseIfWindowed
==========================
*/
void IN_DeactivateMouseIfWindowed( void ) {
if ( !win32.cdsFullscreen ) {
IN_DeactivateMouse();
}
}
/*
============================================================
MOUSE CONTROL
============================================================
*/
/*
===========
Sys_ShutdownInput
===========
*/
void Sys_ShutdownInput( void ) {
IN_DeactivateMouse();
IN_DeactivateKeyboard();
if ( win32.g_pKeyboard ) {
win32.g_pKeyboard->Release();
win32.g_pKeyboard = NULL;
}
if ( win32.g_pMouse ) {
win32.g_pMouse->Release();
win32.g_pMouse = NULL;
}
if ( win32.g_pdi ) {
win32.g_pdi->Release();
win32.g_pdi = NULL;
}
}
/*
===========
Sys_InitInput
===========
*/
void Sys_InitInput( void ) {
common->Printf ("\n------- Input Initialization -------\n");
IN_InitDirectInput();
if ( win32.in_mouse.GetBool() ) {
IN_InitDIMouse();
// don't grab the mouse on initialization
Sys_GrabMouseCursor( false );
} else {
common->Printf ("Mouse control not active.\n");
}
IN_StartupKeyboard();
common->Printf ("------------------------------------\n");
win32.in_mouse.ClearModified();
}
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
if ( lang.Length() == 0 ) {
lang = "english";
}
if ( lang.Icmp( "english" ) == 0 ) {
keyScanTable = s_scantokey;
// the only reason that english right alt binds as K_ALT is so that
// users who were using right-alt before the patch don't suddenly find
// that only left-alt is working.
rightAltKey = K_ALT;
} else if ( lang.Icmp( "spanish" ) == 0 ) {
keyScanTable = s_scantokey_spanish;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "french" ) == 0 ) {
keyScanTable = s_scantokey_french;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "german" ) == 0 ) {
keyScanTable = s_scantokey_german;
rightAltKey = K_RIGHT_ALT;
} else if ( lang.Icmp( "italian" ) == 0 ) {
keyScanTable = s_scantokey_italian;
rightAltKey = K_RIGHT_ALT;
}
}
/*
==================
Sys_GetScanTable
==================
*/
const unsigned char *Sys_GetScanTable( void ) {
return keyScanTable;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return keyScanTable[41 + ( shifted ? 128 : 0 )];
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
void IN_Frame( void ) {
bool shouldGrab = true;
if ( !win32.in_mouse.GetBool() ) {
shouldGrab = false;
}
// if fullscreen, we always want the mouse
if ( !win32.cdsFullscreen ) {
if ( win32.mouseReleased ) {
shouldGrab = false;
}
if ( win32.movingWindow ) {
shouldGrab = false;
}
if ( !win32.activeApp ) {
shouldGrab = false;
}
}
if ( shouldGrab != win32.mouseGrabbed ) {
if ( win32.mouseGrabbed ) {
IN_DeactivateMouse();
} else {
IN_ActivateMouse();
#if 0 // if we can't reacquire, try reinitializing
if ( !IN_InitDIMouse() ) {
win32.in_mouse.SetBool( false );
return;
}
#endif
}
}
}
void Sys_GrabMouseCursor( bool grabIt ) {
#ifndef ID_DEDICATED
win32.mouseReleased = !grabIt;
if ( !grabIt ) {
// release it right now
IN_Frame();
}
#endif
}
//=====================================================================================
static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
static int diFetch;
static byte toggleFetch[2][ 256 ];
#if 1
// I tried doing the full-state get to address a keyboard problem on one system,
// but it didn't make any difference
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
polled_didod, &dwElements, 0 );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
//Bug 951: The following command really clears the garbage input.
//The original will still process keys in the buffer and was causing
//some problems.
win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
dwElements = 0;
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
return dwElements;
}
#else
/*
====================
Sys_PollKeyboardInputEvents
Fake events by getting the entire device state
and checking transitions
====================
*/
int Sys_PollKeyboardInputEvents( void ) {
HRESULT hr;
if( win32.g_pKeyboard == NULL ) {
return 0;
}
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
if( hr != DI_OK )
{
// We got an error or we got DI_BUFFEROVERFLOW.
//
// Either way, it means that continuous contact with the
// device has been lost, either due to an external
// interruption, or because the buffer overflowed
// and some events were lost.
hr = win32.g_pKeyboard->Acquire();
// nuke the garbage
if (!FAILED(hr)) {
hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
}
// hr may be DIERR_OTHERAPPHASPRIO or other errors. This
// may occur when the app is minimized or in the process of
// switching, so just try again later
}
if( FAILED(hr) ) {
return 0;
}
// build faked events
int numChanges = 0;
for ( int i = 0 ; i < 256 ; i++ ) {
if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
polled_didod[ numChanges ].dwOfs = i;
polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
numChanges++;
}
}
diFetch ^= 1;
return numChanges;
}
#endif
/*
====================
Sys_PollKeyboardInputEvents
====================
*/
int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
state = (polled_didod[ n ].dwData & 0x80) == 0x80;
if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
// for windows, add a keydown event for print screen here, since
// windows doesn't send keydown events to the WndProc for this key.
// ctrl and alt are handled here to get around windows sending ctrl and
// alt messages when the right-alt is pressed on non-US 102 keyboards.
Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
}
return ch;
}
void Sys_EndKeyboardInputEvents( void ) {
}
void Sys_QueMouseEvents( int dwElements ) {
int i, value;
for( i = 0; i < dwElements; i++ ) {
if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
value = (polled_didod[i].dwData & 0x80) == 0x80;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
} else {
switch (polled_didod[i].dwOfs) {
case DIMOFS_X:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
break;
case DIMOFS_Y:
value = polled_didod[i].dwData;
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
break;
case DIMOFS_Z:
value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
value = abs( value );
while( value-- > 0 ) {
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
}
break;
}
}
}
}
//=====================================================================================
int Sys_PollMouseInputEvents( void ) {
DWORD dwElements;
HRESULT hr;
if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
return 0;
}
dwElements = DINPUT_BUFFERSIZE;
hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
if( hr != DI_OK ) {
hr = win32.g_pMouse->Acquire();
// clear the garbage
if (!FAILED(hr)) {
win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
}
}
if( FAILED(hr) ) {
return 0;
}
Sys_QueMouseEvents( dwElements );
return dwElements;
}
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
int diaction = polled_didod[n].dwOfs;
if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
value = (polled_didod[n].dwData & 0x80) == 0x80;
action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
return 1;
}
switch( diaction ) {
case DIMOFS_X:
value = polled_didod[n].dwData;
action = M_DELTAX;
return 1;
case DIMOFS_Y:
value = polled_didod[n].dwData;
action = M_DELTAY;
return 1;
case DIMOFS_Z:
// mouse wheel actions are impulses, without a specific up / down
value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
action = M_DELTAZ;
// a value of zero here should never happen
if ( value == 0 ) {
return 0;
}
return 1;
}
return 0;
}
void Sys_EndMouseInputEvents( void ) { }
unsigned char Sys_MapCharForKey( int key ) {
return (unsigned char)key;
}
,
239 '°',
'|',
K_SHIFT,
'§',
'z',
'x',
'c',
'v',
246 0, 0, 0, 0, 0, 0, 0, 0,
247 0, 0, 0, 0, 0, 0, 0, 0,
248 0, 0, 0, 0, 0, 0, 0, 0,
249 0, 0, 0, 0, 0, 0, 0, 0
254 static const unsigned char *keyScanTable = s_scantokey;
262 static unsigned char rightAltKey =
K_ALT;
264 #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
266 static DIDATAFORMAT df = {
267 sizeof(DIDATAFORMAT),
268 sizeof(DIOBJECTDATAFORMAT),
288 bool bDisableWindowsKey;
292 common->
Printf(
"keyboard: DirectInput has not been started\n");
305 bDisableWindowsKey =
true;
308 dwCoopFlags = DISCL_EXCLUSIVE;
310 dwCoopFlags = DISCL_NONEXCLUSIVE;
313 dwCoopFlags |= DISCL_FOREGROUND;
315 dwCoopFlags |= DISCL_BACKGROUND;
318 if( bDisableWindowsKey && !bExclusive && bForeground )
319 dwCoopFlags |= DISCL_NOWINKEY;
323 common->
Printf(
"keyboard: couldn't find a keyboard device\n");
341 if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
342 common->
Printf(
"keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
362 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
363 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
364 dipdw.diph.dwObj = 0;
365 dipdw.diph.dwHow = DIPH_DEVICE;
368 if( FAILED( hr =
win32.
g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
375 common->
Printf(
"keyboard: DirectInput initialized.\n");
418 case DIK_NUMPADENTER:
420 case DIK_NUMPADEQUALS:
470 return keyScanTable[key];
515 if ( FAILED( hr = DirectInput8Create( GetModuleHandle(
NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (
void**)&
win32.
g_pdi,
NULL ) ) ) {
536 common->
Printf (
"mouse: Couldn't open DI mouse device\n");
547 if( FAILED( hr =
win32.
g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
548 common->
Printf (
"mouse: Couldn't set DI mouse format\n");
571 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
572 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
573 dipdw.diph.dwObj = 0;
574 dipdw.diph.dwHow = DIPH_DEVICE;
577 if( FAILED( hr =
win32.
g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
606 for ( i = 0; i < 10; i++ ) {
607 if ( ::ShowCursor(
false ) < 0 ) {
636 for ( i = 0; i < 10; i++ ) {
637 if ( ::ShowCursor(
true ) >= 0 ) {
694 common->
Printf (
"\n------- Input Initialization -------\n");
704 common->
Printf (
"------------------------------------\n");
715 if ( lang.
Length() == 0 ) {
718 if ( lang.
Icmp(
"english" ) == 0 ) {
719 keyScanTable = s_scantokey;
724 }
else if ( lang.
Icmp(
"spanish" ) == 0 ) {
725 keyScanTable = s_scantokey_spanish;
727 }
else if ( lang.
Icmp(
"french" ) == 0 ) {
728 keyScanTable = s_scantokey_french;
730 }
else if ( lang.
Icmp(
"german" ) == 0 ) {
731 keyScanTable = s_scantokey_german;
733 }
else if ( lang.
Icmp(
"italian" ) == 0 ) {
734 keyScanTable = s_scantokey_italian;
754 return keyScanTable[41 + ( shifted ? 128 : 0 )];
765 bool shouldGrab =
true;
789 #if 0 // if we can't reacquire, try reinitializing
815 static byte toggleFetch[2][ 256 ];
837 polled_didod, &dwElements, 0 );
887 hr =
win32.
g_pKeyboard->GetDeviceState(
sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
900 hr =
win32.
g_pKeyboard->GetDeviceState(
sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
914 for (
int i = 0 ;
i < 256 ;
i++ ) {
915 if ( toggleFetch[0][
i] != toggleFetch[1][
i] ) {
916 polled_didod[ numChanges ].dwOfs =
i;
917 polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][
i] ? 0x80 : 0;
936 state = (polled_didod[
n ].dwData & 0x80) == 0x80;
954 for( i = 0; i < dwElements; i++ ) {
955 if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
956 value = (polled_didod[
i].dwData & 0x80) == 0x80;
959 switch (polled_didod[i].dwOfs) {
961 value = polled_didod[
i].dwData;
965 value = polled_didod[
i].dwData;
969 value = ( (
int) polled_didod[i].dwData ) / WHEEL_DELTA;
971 value = abs( value );
972 while( value-- > 0 ) {
993 hr =
win32.
g_pMouse->GetDeviceData(
sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
999 win32.
g_pMouse->GetDeviceData(
sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
1013 int diaction = polled_didod[
n].dwOfs;
1015 if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
1016 value = (polled_didod[
n].dwData & 0x80) == 0x80;
1017 action =
M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
1021 switch( diaction ) {
1023 value = polled_didod[
n].dwData;
1027 value = polled_didod[
n].dwData;
1032 value = ( (
int) polled_didod[n].dwData ) / WHEEL_DELTA;
1046 return (
unsigned char)key;
GLsizei const GLfloat * value
idCVarSystem * cvarSystem
int Icmp(const char *text) const
LPDIRECTINPUTDEVICE8 g_pMouse
virtual const char * GetCVarString(const char *name) const =0
void Sys_QueEvent(int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr)
virtual void Printf(const char *fmt,...) id_attribute((format(printf
LPDIRECTINPUTDEVICE8 g_pKeyboard
void SetBool(const bool value)
LPDIRECTSOUND IUnknown FAR *typedef HRESULT(FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID *