doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
win_input.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "win_local.h"
33 
34 
35 #define DINPUT_BUFFERSIZE 256
36 
37 #define CHAR_FIRSTREPEAT 200
38 #define CHAR_REPEAT 100
39 
40 typedef struct MYDATA {
41  LONG lX; // X axis goes here
42  LONG lY; // Y axis goes here
43  LONG lZ; // Z axis goes here
44  BYTE bButtonA; // One button goes here
45  BYTE bButtonB; // Another button goes here
46  BYTE bButtonC; // Another button goes here
47  BYTE bButtonD; // Another button goes here
48 } MYDATA;
49 
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,},
58 };
59 
60 //==========================================================================
61 
62 static const unsigned char s_scantokey[256] = {
63 // 0 1 2 3 4 5 6 7
64 // 8 9 A B C D E F
65  0, 27, '1', '2', '3', '4', '5', '6',
66  '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0
67  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
68  'o', 'p', '[', ']', K_ENTER,K_CTRL, 'a', 's', // 1
69  'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
70  '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2
71  'b', 'n', 'm', ',', '.', '/', K_SHIFT, K_KP_STAR,
72  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
75  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, 0, K_F11,
76  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
77  0, 0, 0, 0, 0, 0, 0, 0,
78  0, 0, 0, 0, 0, 0, 0, 0, // 6
79  0, 0, 0, 0, 0, 0, 0, 0,
80  0, 0, 0, 0, 0, 0, 0, 0, // 7
81 // shifted
82  0, 27, '!', '@', '#', '$', '%', '^',
83  '&', '*', '(', ')', '_', '+', K_BACKSPACE, 9, // 0
84  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
85  'o', 'p', '[', ']', K_ENTER,K_CTRL, 'a', 's', // 1
86  'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
87  '\'', '~', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2
88  'b', 'n', 'm', ',', '.', '/', K_SHIFT, K_KP_STAR,
89  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
92  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, 0, K_F11,
93  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
94  0, 0, 0, 0, 0, 0, 0, 0,
95  0, 0, 0, 0, 0, 0, 0, 0, // 6
96  0, 0, 0, 0, 0, 0, 0, 0,
97  0, 0, 0, 0, 0, 0, 0, 0 // 7
98 };
99 
100 static const unsigned char s_scantokey_german[256] = {
101 // 0 1 2 3 4 5 6 7
102 // 8 9 A B C D E F
103  0, 27, '1', '2', '3', '4', '5', '6',
104  '7', '8', '9', '0', '?', '\'', K_BACKSPACE, 9, // 0
105  'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',
106  'o', 'p', '=', '+', K_ENTER,K_CTRL, 'a', 's', // 1
107  'd', 'f', 'g', 'h', 'j', 'k', 'l', '[',
108  ']', '`', K_SHIFT, '#', 'y', 'x', 'c', 'v', // 2
109  'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
110  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
113  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
114  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
115  0, 0, 0, 0, 0, 0, 0, 0,
116  0, 0, 0, 0, 0, 0, 0, 0, // 6
117  0, 0, 0, 0, 0, 0, 0, 0,
118  0, 0, 0, 0, 0, 0, 0, 0, // 7
119 // shifted
120  0, 27, '1', '2', '3', '4', '5', '6',
121  '7', '8', '9', '0', '?', '\'', K_BACKSPACE, 9, // 0
122  'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',
123  'o', 'p', '=', '+', K_ENTER,K_CTRL, 'a', 's', // 1
124  'd', 'f', 'g', 'h', 'j', 'k', 'l', '[',
125  ']', '`', K_SHIFT, '#', 'y', 'x', 'c', 'v', // 2
126  'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
127  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
130  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
131  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
132  0, 0, 0, 0, 0, 0, 0, 0,
133  0, 0, 0, 0, 0, 0, 0, 0, // 6
134  0, 0, 0, 0, 0, 0, 0, 0,
135  0, 0, 0, 0, 0, 0, 0, 0 // 7
136 };
137 
138 static const unsigned char s_scantokey_french[256] = {
139 // 0 1 2 3 4 5 6 7
140 // 8 9 A B C D E F
141  0, 27, '1', '2', '3', '4', '5', '6',
142  '7', '8', '9', '0', ')', '=', K_BACKSPACE, 9, // 0
143  'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
144  'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
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', // 2
147  'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
148  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
151  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
152  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
153  0, 0, 0, 0, 0, 0, 0, 0,
154  0, 0, 0, 0, 0, 0, 0, 0, // 6
155  0, 0, 0, 0, 0, 0, 0, 0,
156  0, 0, 0, 0, 0, 0, 0, 0, // 7
157 // shifted
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, // 0
160  'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
161  'o', 'p', '^', '$', K_ENTER,K_CTRL, 'q', 's', // 1
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', // 2
164  'b', 'n', ',', ';', ':', '!', K_SHIFT, K_KP_STAR,
165  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
168  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
169  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
170  0, 0, 0, 0, 0, 0, 0, 0,
171  0, 0, 0, 0, 0, 0, 0, 0, // 6
172  0, 0, 0, 0, 0, 0, 0, 0,
173  0, 0, 0, 0, 0, 0, 0, 0 // 7
174 };
175 
176 static const unsigned char s_scantokey_spanish[256] = {
177 // 0 1 2 3 4 5 6 7
178 // 8 9 A B C D E F
179  0, 27, '1', '2', '3', '4', '5', '6',
180  '7', '8', '9', '0', '\'', '¡', K_BACKSPACE, 9, // 0
181  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
182  'o', 'p', '`', '+', K_ENTER,K_CTRL, 'a', 's', // 1
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', // 2
185  'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
186  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
189  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
190  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
191  0, 0, 0, 0, 0, 0, 0, 0,
192  0, 0, 0, 0, 0, 0, 0, 0, // 6
193  0, 0, 0, 0, 0, 0, 0, 0,
194  0, 0, 0, 0, 0, 0, 0, 0, // 7
195 // shifted
196  0, 27, '!', '\"', '·', '$', '%', '&',
197  '/', '(', ')', '=', '?', '¿', K_BACKSPACE, 9, // 0
198  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
199  'o', 'p', '^', '*', K_ENTER,K_CTRL, 'a', 's', // 1
200  'd', 'f', 'g', 'h', 'j', 'k', 'l', 'Ñ',
201  '¨', 'ª', K_SHIFT, 'Ç', 'z', 'x', 'c', 'v', // 2
202  'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
203  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
206  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
207  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
208  0, 0, 0, 0, 0, 0, 0, 0,
209  0, 0, 0, 0, 0, 0, 0, 0, // 6
210  0, 0, 0, 0, 0, 0, 0, 0,
211  0, 0, 0, 0, 0, 0, 0, 0 // 7
212 };
213 
214 static const unsigned char s_scantokey_italian[256] = {
215 // 0 1 2 3 4 5 6 7
216 // 8 9 A B C D E F
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, // 0
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', // 1
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', // 2
223  'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
224  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
227  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
228  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
229  0, 0, 0, 0, 0, 0, 0, 0,
230  0, 0, 0, 0, 0, 0, 0, 0, // 6
231  0, 0, 0, 0, 0, 0, 0, 0,
232  0, 0, 0, 0, 0, 0, 0, 0, // 7
233 // shifted
234  0, 27, '!', '\"', '£', '$', '%', '&',
235  '/', '(', ')', '=', '?', '^', K_BACKSPACE, 9, // 0
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', // 1
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', // 2
240  'b', 'n', 'm', ',', '.', '-', K_SHIFT, K_KP_STAR,
241  K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3
244  K_DOWNARROW,K_PGDN, K_INS, K_DEL, 0, 0, '<', K_F11,
245  K_F12, 0, 0, K_LWIN, K_RWIN, K_MENU, 0, 0, // 5
246  0, 0, 0, 0, 0, 0, 0, 0,
247  0, 0, 0, 0, 0, 0, 0, 0, // 6
248  0, 0, 0, 0, 0, 0, 0, 0,
249  0, 0, 0, 0, 0, 0, 0, 0 // 7
250 
251 
252 };
253 
254 static const unsigned char *keyScanTable = s_scantokey;
255 
256 // this should be part of the scantables and the scan tables should be 512 bytes
257 // (256 scan codes, shifted and unshifted). Changing everything to use 512 byte
258 // scan tables now might introduce bugs in tested code. Since we only need to fix
259 // the right-alt case for non-US keyboards, we're just using a special-case table
260 // for it. Eventually, the tables above should be fixed to handle all possible
261 // scan codes instead of just the first 128.
262 static unsigned char rightAltKey = K_ALT;
263 
264 #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
265 
266 static DIDATAFORMAT df = {
267  sizeof(DIDATAFORMAT), // this structure
268  sizeof(DIOBJECTDATAFORMAT), // size of object data format
269  DIDF_RELAXIS, // absolute axis coordinates
270  sizeof(MYDATA), // device data size
271  NUM_OBJECTS, // number of objects
272  rgodf, // and here they are
273 };
274 
275 /*
276 ============================================================
277 
278 DIRECT INPUT KEYBOARD CONTROL
279 
280 ============================================================
281 */
282 
283 bool IN_StartupKeyboard( void ) {
284  HRESULT hr;
285  bool bExclusive;
286  bool bForeground;
287  bool bImmediate;
288  bool bDisableWindowsKey;
289  DWORD dwCoopFlags;
290 
291  if (!win32.g_pdi) {
292  common->Printf("keyboard: DirectInput has not been started\n");
293  return false;
294  }
295 
296  if (win32.g_pKeyboard) {
297  win32.g_pKeyboard->Release();
299  }
300 
301  // Detrimine where the buffer would like to be allocated
302  bExclusive = false;
303  bForeground = true;
304  bImmediate = false;
305  bDisableWindowsKey = true;
306 
307  if( bExclusive )
308  dwCoopFlags = DISCL_EXCLUSIVE;
309  else
310  dwCoopFlags = DISCL_NONEXCLUSIVE;
311 
312  if( bForeground )
313  dwCoopFlags |= DISCL_FOREGROUND;
314  else
315  dwCoopFlags |= DISCL_BACKGROUND;
316 
317  // Disabling the windows key is only allowed only if we are in foreground nonexclusive
318  if( bDisableWindowsKey && !bExclusive && bForeground )
319  dwCoopFlags |= DISCL_NOWINKEY;
320 
321  // Obtain an interface to the system keyboard device.
322  if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
323  common->Printf("keyboard: couldn't find a keyboard device\n");
324  return false;
325  }
326 
327  // Set the data format to "keyboard format" - a predefined data format
328  //
329  // A data format specifies which controls on a device we
330  // are interested in, and how they should be reported.
331  //
332  // This tells DirectInput that we will be passing an array
333  // of 256 bytes to IDirectInputDevice::GetDeviceState.
334  if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
335  return false;
336 
337  // Set the cooperativity level to let DirectInput know how
338  // this device should interact with the system and with other
339  // DirectInput applications.
340  hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
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");
343  return false;
344  }
345 
346  if( FAILED(hr) ) {
347  return false;
348  }
349 
350  if( !bImmediate ) {
351  // IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
352  //
353  // DirectInput uses unbuffered I/O (buffer size = 0) by default.
354  // If you want to read buffered data, you need to set a nonzero
355  // buffer size.
356  //
357  // Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
358  //
359  // The buffer size is a DWORD property associated with the device.
360  DIPROPDWORD dipdw;
361 
362  dipdw.diph.dwSize = sizeof(DIPROPDWORD);
363  dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
364  dipdw.diph.dwObj = 0;
365  dipdw.diph.dwHow = DIPH_DEVICE;
366  dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
367 
368  if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
369  return false;
370  }
371 
372  // Acquire the newly created device
373  win32.g_pKeyboard->Acquire();
374 
375  common->Printf( "keyboard: DirectInput initialized.\n");
376  return true;
377 }
378 
379 /*
380 =======
381 MapKey
382 
383 Map from windows to quake keynums
384 
385 FIXME: scan code tables should include the upper 128 scan codes instead
386  of having to special-case them here. The current code makes it difficult
387  to special-case conversions for non-US keyboards. Currently the only
388  special-case is for right alt.
389 =======
390 */
391 int IN_DIMapKey (int key) {
392  if ( key>=128 ) {
393  switch ( key ) {
394  case DIK_HOME:
395  return K_HOME;
396  case DIK_UPARROW:
397  return K_UPARROW;
398  case DIK_PGUP:
399  return K_PGUP;
400  case DIK_LEFTARROW:
401  return K_LEFTARROW;
402  case DIK_RIGHTARROW:
403  return K_RIGHTARROW;
404  case DIK_END:
405  return K_END;
406  case DIK_DOWNARROW:
407  return K_DOWNARROW;
408  case DIK_PGDN:
409  return K_PGDN;
410  case DIK_INSERT:
411  return K_INS;
412  case DIK_DELETE:
413  return K_DEL;
414  case DIK_RMENU:
415  return rightAltKey;
416  case DIK_RCONTROL:
417  return K_CTRL;
418  case DIK_NUMPADENTER:
419  return K_KP_ENTER;
420  case DIK_NUMPADEQUALS:
421  return K_KP_EQUALS;
422  case DIK_PAUSE:
423  return K_PAUSE;
424  case DIK_DIVIDE:
425  return K_KP_SLASH;
426  case DIK_LWIN:
427  return K_LWIN;
428  case DIK_RWIN:
429  return K_RWIN;
430  case DIK_APPS:
431  return K_MENU;
432  case DIK_SYSRQ:
433  return K_PRINT_SCR;
434  default:
435  return 0;
436  }
437  } else {
438  switch (key) {
439  case DIK_NUMPAD7:
440  return K_KP_HOME;
441  case DIK_NUMPAD8:
442  return K_KP_UPARROW;
443  case DIK_NUMPAD9:
444  return K_KP_PGUP;
445  case DIK_NUMPAD4:
446  return K_KP_LEFTARROW;
447  case DIK_NUMPAD5:
448  return K_KP_5;
449  case DIK_NUMPAD6:
450  return K_KP_RIGHTARROW;
451  case DIK_NUMPAD1:
452  return K_KP_END;
453  case DIK_NUMPAD2:
454  return K_KP_DOWNARROW;
455  case DIK_NUMPAD3:
456  return K_KP_PGDN;
457  case DIK_NUMPAD0:
458  return K_KP_INS;
459  case DIK_DECIMAL:
460  return K_KP_DEL;
461  case DIK_SUBTRACT:
462  return K_KP_MINUS;
463  case DIK_ADD:
464  return K_KP_PLUS;
465  case DIK_NUMLOCK:
466  return K_KP_NUMLOCK;
467  case DIK_MULTIPLY:
468  return K_KP_STAR;
469  default:
470  return keyScanTable[key];
471  }
472  }
473 }
474 
475 
476 /*
477 ==========================
478 IN_DeactivateKeyboard
479 ==========================
480 */
481 void IN_DeactivateKeyboard( void ) {
482  if (!win32.g_pKeyboard) {
483  return;
484  }
485  win32.g_pKeyboard->Unacquire( );
486 }
487 
488 /*
489 ============================================================
490 
491 DIRECT INPUT MOUSE CONTROL
492 
493 ============================================================
494 */
495 
496 /*
497 ========================
498 IN_InitDirectInput
499 ========================
500 */
501 
502 void IN_InitDirectInput( void ) {
503  HRESULT hr;
504 
505  common->Printf( "Initializing DirectInput...\n" );
506 
507  if ( win32.g_pdi != NULL ) {
508  win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
509  win32.g_pdi = NULL;
510  }
511 
512  // Register with the DirectInput subsystem and get a pointer
513  // to a IDirectInput interface we can use.
514  // Create the base DirectInput object
515  if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
516  common->Printf ("DirectInputCreate failed\n");
517  }
518 }
519 
520 /*
521 ========================
522 IN_InitDIMouse
523 ========================
524 */
525 bool IN_InitDIMouse( void ) {
526  HRESULT hr;
527 
528  if ( win32.g_pdi == NULL) {
529  return false;
530  }
531 
532  // obtain an interface to the system mouse device.
533  hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
534 
535  if (FAILED(hr)) {
536  common->Printf ("mouse: Couldn't open DI mouse device\n");
537  return false;
538  }
539 
540  // Set the data format to "mouse format" - a predefined data format
541  //
542  // A data format specifies which controls on a device we
543  // are interested in, and how they should be reported.
544  //
545  // This tells DirectInput that we will be passing a
546  // DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
547  if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
548  common->Printf ("mouse: Couldn't set DI mouse format\n");
549  return false;
550  }
551 
552  // set the cooperativity level.
553  hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
554 
555  if (FAILED(hr)) {
556  common->Printf ("mouse: Couldn't set DI coop level\n");
557  return false;
558  }
559 
560 
561  // IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
562  //
563  // DirectInput uses unbuffered I/O (buffer size = 0) by default.
564  // If you want to read buffered data, you need to set a nonzero
565  // buffer size.
566  //
567  // Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
568  //
569  // The buffer size is a DWORD property associated with the device.
570  DIPROPDWORD dipdw;
571  dipdw.diph.dwSize = sizeof(DIPROPDWORD);
572  dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
573  dipdw.diph.dwObj = 0;
574  dipdw.diph.dwHow = DIPH_DEVICE;
575  dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
576 
577  if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
578  common->Printf ("mouse: Couldn't set DI buffersize\n");
579  return false;
580  }
581 
583 
584  // clear any pending samples
586 
587  common->Printf( "mouse: DirectInput initialized.\n");
588  return true;
589 }
590 
591 
592 /*
593 ==========================
594 IN_ActivateMouse
595 ==========================
596 */
597 void IN_ActivateMouse( void ) {
598  int i;
599  HRESULT hr;
600 
602  return;
603  }
604 
605  win32.mouseGrabbed = true;
606  for ( i = 0; i < 10; i++ ) {
607  if ( ::ShowCursor( false ) < 0 ) {
608  break;
609  }
610  }
611 
612  // we may fail to reacquire if the window has been recreated
613  hr = win32.g_pMouse->Acquire();
614  if (FAILED(hr)) {
615  return;
616  }
617 
618  // set the cooperativity level.
619  hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
620 }
621 
622 /*
623 ==========================
624 IN_DeactivateMouse
625 ==========================
626 */
627 void IN_DeactivateMouse( void ) {
628  int i;
629 
630  if (!win32.g_pMouse || !win32.mouseGrabbed ) {
631  return;
632  }
633 
634  win32.g_pMouse->Unacquire();
635 
636  for ( i = 0; i < 10; i++ ) {
637  if ( ::ShowCursor( true ) >= 0 ) {
638  break;
639  }
640  }
641  win32.mouseGrabbed = false;
642 }
643 
644 /*
645 ==========================
646 IN_DeactivateMouseIfWindowed
647 ==========================
648 */
650  if ( !win32.cdsFullscreen ) {
652  }
653 }
654 
655 /*
656 ============================================================
657 
658  MOUSE CONTROL
659 
660 ============================================================
661 */
662 
663 
664 /*
665 ===========
666 Sys_ShutdownInput
667 ===========
668 */
669 void Sys_ShutdownInput( void ) {
672  if ( win32.g_pKeyboard ) {
673  win32.g_pKeyboard->Release();
675  }
676 
677  if ( win32.g_pMouse ) {
678  win32.g_pMouse->Release();
679  win32.g_pMouse = NULL;
680  }
681 
682  if ( win32.g_pdi ) {
683  win32.g_pdi->Release();
684  win32.g_pdi = NULL;
685  }
686 }
687 
688 /*
689 ===========
690 Sys_InitInput
691 ===========
692 */
693 void Sys_InitInput( void ) {
694  common->Printf ("\n------- Input Initialization -------\n");
696  if ( win32.in_mouse.GetBool() ) {
697  IN_InitDIMouse();
698  // don't grab the mouse on initialization
699  Sys_GrabMouseCursor( false );
700  } else {
701  common->Printf ("Mouse control not active.\n");
702  }
704  common->Printf ("------------------------------------\n");
706 }
707 
708 /*
709 ===========
710 Sys_InitScanTable
711 ===========
712 */
713 void Sys_InitScanTable( void ) {
714  idStr lang = cvarSystem->GetCVarString( "sys_lang" );
715  if ( lang.Length() == 0 ) {
716  lang = "english";
717  }
718  if ( lang.Icmp( "english" ) == 0 ) {
719  keyScanTable = s_scantokey;
720  // the only reason that english right alt binds as K_ALT is so that
721  // users who were using right-alt before the patch don't suddenly find
722  // that only left-alt is working.
723  rightAltKey = K_ALT;
724  } else if ( lang.Icmp( "spanish" ) == 0 ) {
725  keyScanTable = s_scantokey_spanish;
726  rightAltKey = K_RIGHT_ALT;
727  } else if ( lang.Icmp( "french" ) == 0 ) {
728  keyScanTable = s_scantokey_french;
729  rightAltKey = K_RIGHT_ALT;
730  } else if ( lang.Icmp( "german" ) == 0 ) {
731  keyScanTable = s_scantokey_german;
732  rightAltKey = K_RIGHT_ALT;
733  } else if ( lang.Icmp( "italian" ) == 0 ) {
734  keyScanTable = s_scantokey_italian;
735  rightAltKey = K_RIGHT_ALT;
736  }
737 }
738 
739 /*
740 ==================
741 Sys_GetScanTable
742 ==================
743 */
744 const unsigned char *Sys_GetScanTable( void ) {
745  return keyScanTable;
746 }
747 
748 /*
749 ===============
750 Sys_GetConsoleKey
751 ===============
752 */
753 unsigned char Sys_GetConsoleKey( bool shifted ) {
754  return keyScanTable[41 + ( shifted ? 128 : 0 )];
755 }
756 
757 /*
758 ==================
759 IN_Frame
760 
761 Called every frame, even if not generating commands
762 ==================
763 */
764 void IN_Frame( void ) {
765  bool shouldGrab = true;
766 
767  if ( !win32.in_mouse.GetBool() ) {
768  shouldGrab = false;
769  }
770  // if fullscreen, we always want the mouse
771  if ( !win32.cdsFullscreen ) {
772  if ( win32.mouseReleased ) {
773  shouldGrab = false;
774  }
775  if ( win32.movingWindow ) {
776  shouldGrab = false;
777  }
778  if ( !win32.activeApp ) {
779  shouldGrab = false;
780  }
781  }
782 
783  if ( shouldGrab != win32.mouseGrabbed ) {
784  if ( win32.mouseGrabbed ) {
786  } else {
788 
789 #if 0 // if we can't reacquire, try reinitializing
790  if ( !IN_InitDIMouse() ) {
791  win32.in_mouse.SetBool( false );
792  return;
793  }
794 #endif
795  }
796  }
797 }
798 
799 
800 void Sys_GrabMouseCursor( bool grabIt ) {
801 #ifndef ID_DEDICATED
802  win32.mouseReleased = !grabIt;
803  if ( !grabIt ) {
804  // release it right now
805  IN_Frame();
806  }
807 #endif
808 }
809 
810 //=====================================================================================
811 
812 static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
813 
814 static int diFetch;
815 static byte toggleFetch[2][ 256 ];
816 
817 
818 #if 1
819 // I tried doing the full-state get to address a keyboard problem on one system,
820 // but it didn't make any difference
821 
822 /*
823 ====================
824 Sys_PollKeyboardInputEvents
825 ====================
826 */
828  DWORD dwElements;
829  HRESULT hr;
830 
831  if( win32.g_pKeyboard == NULL ) {
832  return 0;
833  }
834 
835  dwElements = DINPUT_BUFFERSIZE;
836  hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
837  polled_didod, &dwElements, 0 );
838  if( hr != DI_OK )
839  {
840  // We got an error or we got DI_BUFFEROVERFLOW.
841  //
842  // Either way, it means that continuous contact with the
843  // device has been lost, either due to an external
844  // interruption, or because the buffer overflowed
845  // and some events were lost.
846  hr = win32.g_pKeyboard->Acquire();
847 
848 
849 
850  // nuke the garbage
851  if (!FAILED(hr)) {
852  //Bug 951: The following command really clears the garbage input.
853  //The original will still process keys in the buffer and was causing
854  //some problems.
855  win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
856  dwElements = 0;
857  }
858  // hr may be DIERR_OTHERAPPHASPRIO or other errors. This
859  // may occur when the app is minimized or in the process of
860  // switching, so just try again later
861  }
862 
863  if( FAILED(hr) ) {
864  return 0;
865  }
866 
867  return dwElements;
868 }
869 
870 #else
871 
872 /*
873 ====================
874 Sys_PollKeyboardInputEvents
875 
876 Fake events by getting the entire device state
877 and checking transitions
878 ====================
879 */
880 int Sys_PollKeyboardInputEvents( void ) {
881  HRESULT hr;
882 
883  if( win32.g_pKeyboard == NULL ) {
884  return 0;
885  }
886 
887  hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
888  if( hr != DI_OK )
889  {
890  // We got an error or we got DI_BUFFEROVERFLOW.
891  //
892  // Either way, it means that continuous contact with the
893  // device has been lost, either due to an external
894  // interruption, or because the buffer overflowed
895  // and some events were lost.
896  hr = win32.g_pKeyboard->Acquire();
897 
898  // nuke the garbage
899  if (!FAILED(hr)) {
900  hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
901  }
902  // hr may be DIERR_OTHERAPPHASPRIO or other errors. This
903  // may occur when the app is minimized or in the process of
904  // switching, so just try again later
905  }
906 
907  if( FAILED(hr) ) {
908  return 0;
909  }
910 
911  // build faked events
912  int numChanges = 0;
913 
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;
918  numChanges++;
919  }
920  }
921 
922  diFetch ^= 1;
923 
924  return numChanges;
925 }
926 
927 #endif
928 
929 /*
930 ====================
931 Sys_PollKeyboardInputEvents
932 ====================
933 */
934 int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
935  ch = IN_DIMapKey( polled_didod[ n ].dwOfs );
936  state = (polled_didod[ n ].dwData & 0x80) == 0x80;
937  if ( ch == K_PRINT_SCR || ch == K_CTRL || ch == K_ALT || ch == K_RIGHT_ALT ) {
938  // for windows, add a keydown event for print screen here, since
939  // windows doesn't send keydown events to the WndProc for this key.
940  // ctrl and alt are handled here to get around windows sending ctrl and
941  // alt messages when the right-alt is pressed on non-US 102 keyboards.
942  Sys_QueEvent( GetTickCount(), SE_KEY, ch, state, 0, NULL );
943  }
944  return ch;
945 }
946 
947 
949 }
950 
951 void Sys_QueMouseEvents( int dwElements ) {
952  int i, value;
953 
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;
957  Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, K_MOUSE1 + ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 ), value, 0, NULL );
958  } else {
959  switch (polled_didod[i].dwOfs) {
960  case DIMOFS_X:
961  value = polled_didod[i].dwData;
962  Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, value, 0, 0, NULL );
963  break;
964  case DIMOFS_Y:
965  value = polled_didod[i].dwData;
966  Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_MOUSE, 0, value, 0, NULL );
967  break;
968  case DIMOFS_Z:
969  value = ( (int) polled_didod[i].dwData ) / WHEEL_DELTA;
970  int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
971  value = abs( value );
972  while( value-- > 0 ) {
973  Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, true, 0, NULL );
974  Sys_QueEvent( polled_didod[i].dwTimeStamp, SE_KEY, key, false, 0, NULL );
975  }
976  break;
977  }
978  }
979  }
980 }
981 
982 //=====================================================================================
983 
985  DWORD dwElements;
986  HRESULT hr;
987 
988  if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
989  return 0;
990  }
991 
992  dwElements = DINPUT_BUFFERSIZE;
993  hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
994 
995  if( hr != DI_OK ) {
996  hr = win32.g_pMouse->Acquire();
997  // clear the garbage
998  if (!FAILED(hr)) {
999  win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
1000  }
1001  }
1002 
1003  if( FAILED(hr) ) {
1004  return 0;
1005  }
1006 
1007  Sys_QueMouseEvents( dwElements );
1008 
1009  return dwElements;
1010 }
1011 
1012 int Sys_ReturnMouseInputEvent( const int n, int &action, int &value ) {
1013  int diaction = polled_didod[n].dwOfs;
1014 
1015  if ( diaction >= DIMOFS_BUTTON0 && diaction <= DIMOFS_BUTTON7 ) {
1016  value = (polled_didod[n].dwData & 0x80) == 0x80;
1017  action = M_ACTION1 + ( diaction - DIMOFS_BUTTON0 );
1018  return 1;
1019  }
1020 
1021  switch( diaction ) {
1022  case DIMOFS_X:
1023  value = polled_didod[n].dwData;
1024  action = M_DELTAX;
1025  return 1;
1026  case DIMOFS_Y:
1027  value = polled_didod[n].dwData;
1028  action = M_DELTAY;
1029  return 1;
1030  case DIMOFS_Z:
1031  // mouse wheel actions are impulses, without a specific up / down
1032  value = ( (int) polled_didod[n].dwData ) / WHEEL_DELTA;
1033  action = M_DELTAZ;
1034  // a value of zero here should never happen
1035  if ( value == 0 ) {
1036  return 0;
1037  }
1038  return 1;
1039  }
1040  return 0;
1041 }
1042 
1043 void Sys_EndMouseInputEvents( void ) { }
1044 
1045 unsigned char Sys_MapCharForKey( int key ) {
1046  return (unsigned char)key;
1047 }
BYTE bButtonA
Definition: win_input.cpp:44
Definition: KeyInput.h:78
Definition: KeyInput.h:83
GLsizei const GLfloat * value
Definition: glext.h:3614
Definition: KeyInput.h:82
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
LONG lX
Definition: win_input.cpp:41
Definition: KeyInput.h:88
void Sys_QueMouseEvents(int dwElements)
Definition: win_input.cpp:951
bool activeApp
Definition: win_local.h:104
static idCVar in_mouse
Definition: win_local.h:140
void IN_InitDirectInput(void)
Definition: win_input.cpp:502
void Sys_InitScanTable(void)
Definition: win_input.cpp:713
Definition: KeyInput.h:86
Definition: KeyInput.h:74
int Length(void) const
Definition: Str.h:702
void IN_DeactivateKeyboard(void)
Definition: win_input.cpp:481
DWORD
Definition: win_qgl.cpp:61
Definition: KeyInput.h:85
GLenum GLsizei n
Definition: glext.h:3705
case const int
Definition: Callbacks.cpp:52
Definition: KeyInput.h:81
void Sys_InitInput(void)
Definition: win_input.cpp:693
int i
Definition: process.py:33
Definition: KeyInput.h:91
int Icmp(const char *text) const
Definition: Str.h:667
bool cdsFullscreen
Definition: win_local.h:131
Definition: KeyInput.h:90
Definition: KeyInput.h:80
bool IN_InitDIMouse(void)
Definition: win_input.cpp:525
LPDIRECTINPUTDEVICE8 g_pMouse
Definition: win_local.h:158
Definition: KeyInput.h:73
int Sys_PollKeyboardInputEvents(void)
Definition: win_input.cpp:827
idCommon * common
Definition: Common.cpp:206
BYTE bButtonC
Definition: win_input.cpp:46
#define NULL
Definition: Lib.h:88
virtual const char * GetCVarString(const char *name) const =0
const unsigned char * Sys_GetScanTable(void)
Definition: win_input.cpp:744
bool IN_StartupKeyboard(void)
Definition: win_input.cpp:283
void Sys_QueEvent(int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr)
Definition: win_main.cpp:731
void IN_DeactivateMouse(void)
Definition: win_input.cpp:627
Definition: KeyInput.h:84
int Sys_PollMouseInputEvents(void)
Definition: win_input.cpp:984
BYTE bButtonB
Definition: win_input.cpp:45
virtual void Printf(const char *fmt,...) id_attribute((format(printf
void Sys_EndMouseInputEvents(void)
Definition: win_input.cpp:1043
Definition: KeyInput.h:87
void IN_DeactivateMouseIfWindowed(void)
Definition: win_input.cpp:649
struct MYDATA MYDATA
void IN_ActivateMouse(void)
Definition: win_input.cpp:597
unsigned char Sys_MapCharForKey(int key)
Definition: win_input.cpp:1045
void Sys_ShutdownInput(void)
Definition: win_input.cpp:669
long LONG
LPDIRECTINPUTDEVICE8 g_pKeyboard
Definition: win_local.h:159
bool mouseGrabbed
Definition: win_local.h:107
bool GetBool(void) const
Definition: CVarSystem.h:142
void ClearModified(void)
Definition: CVarSystem.h:139
Definition: KeyInput.h:70
unsigned char byte
Definition: Lib.h:75
Definition: Str.h:116
int IN_DIMapKey(int key)
Definition: win_input.cpp:391
void Sys_EndKeyboardInputEvents(void)
Definition: win_input.cpp:948
LONG lY
Definition: win_input.cpp:42
void SetBool(const bool value)
Definition: CVarSystem.h:147
#define NUM_OBJECTS
Definition: win_input.cpp:264
int Sys_ReturnKeyboardInputEvent(const int n, int &ch, bool &state)
Definition: win_input.cpp:934
#define DINPUT_BUFFERSIZE
Definition: win_input.cpp:35
Win32Vars_t win32
Definition: win_main.cpp:65
int Sys_ReturnMouseInputEvent(const int n, int &action, int &value)
Definition: win_input.cpp:1012
LONG lZ
Definition: win_input.cpp:43
BYTE bButtonD
Definition: win_input.cpp:47
LPDIRECTINPUT8 g_pdi
Definition: win_local.h:157
bool mouseReleased
Definition: win_local.h:105
unsigned char Sys_GetConsoleKey(bool shifted)
Definition: win_input.cpp:753
void Sys_GrabMouseCursor(bool grabIt)
Definition: win_input.cpp:800
bool movingWindow
Definition: win_local.h:106
Definition: KeyInput.h:89
void IN_Frame(void)
Definition: win_input.cpp:764
LPDIRECTSOUND IUnknown FAR *typedef HRESULT(FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID *