]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/win32/win_input.cpp
hello world
[icculus/iodoom3.git] / neo / sys / win32 / win_input.cpp
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
73         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
74         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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
90         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
91         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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
111         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
112         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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
128         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
129         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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 
147         'b',        'n',    ',',       ';',        ':',    '!',         K_SHIFT,  K_KP_STAR,
148         K_ALT,      ' ',    K_CAPSLOCK,K_F1,       K_F2,   K_F3,        K_F4,     K_F5,  // 3
149         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
150         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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,    '&',       'é',        '\"',    '\'',         '(',      '-', 
159         'è',        '_',    'ç',       'à',        '°',    '+',         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 
164         'b',        'n',    ',',       ';',        ':',    '!',         K_SHIFT,  K_KP_STAR,
165         K_ALT,      ' ',    K_CAPSLOCK,K_F1,       K_F2,   K_F3,        K_F4,     K_F5,  // 3
166         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
167         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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',      'ñ', 
184         '´',        'º',    K_SHIFT,   'ç',        '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
187         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
188         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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
204         K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
205         K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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 
219                 'q',        'w',    'e',       'r',        't',    'y',         'u',      'i', 
220                 'o',        'p',    'è',       '+',        K_ENTER,K_CTRL,      'a',      's',   // 1 
221                 'd',        'f',    'g',       'h',        'j',    'k',         'l',      'ò', 
222                 'à',        '\\',    K_SHIFT,   'ù',        '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
225                 K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
226                 K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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 
238                 'd',        'f',    'g',       'h',        'j',    'k',         'l',      'ç', 
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
242                 K_F6,       K_F7,   K_F8,      K_F9,       K_F10,  K_PAUSE,     K_SCROLL, K_HOME, 
243                 K_UPARROW,  K_PGUP, K_KP_MINUS,K_LEFTARROW,K_KP_5, K_RIGHTARROW,K_KP_PLUS,K_END, // 4
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();
298                 win32.g_pKeyboard = NULL;
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
582         IN_ActivateMouse();
583
584         // clear any pending samples
585         Sys_PollMouseInputEvents();
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
601         if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
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 */
649 void IN_DeactivateMouseIfWindowed( void ) {
650         if ( !win32.cdsFullscreen ) {
651                 IN_DeactivateMouse();
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 ) {
670         IN_DeactivateMouse();
671         IN_DeactivateKeyboard();
672         if ( win32.g_pKeyboard ) {
673                 win32.g_pKeyboard->Release();
674                 win32.g_pKeyboard = NULL;
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");
695         IN_InitDirectInput();
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         }
703         IN_StartupKeyboard();
704         common->Printf ("------------------------------------\n");
705         win32.in_mouse.ClearModified();
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 ) {
785                         IN_DeactivateMouse();
786                 } else {
787                         IN_ActivateMouse();
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 */
827 int Sys_PollKeyboardInputEvents( void ) {
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
948 void Sys_EndKeyboardInputEvents( void ) {
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
984 int Sys_PollMouseInputEvents( void ) {
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 }