这是之前的 JoyToKey 的改进版,真正实用,我和 CY 两人用手柄对拳皇97,比键盘更容易放出招来(游戏是在不支持手柄的虚拟机中运行的)。
1/**//*
2JoyToKey.cpp
3
4Copyright (C) 2011, coreBugZJ, all rights reserved.
5
6版本:
72.0.0
8
9功能:
10将游戏手柄操作映射为键盘按键操作。
11
12原理:
13接收手柄消息,然后产生相应键盘消息。
14
15改进:
161.改进了手柄与键盘按键按下与释放的对应:手柄按键释放后才产生键盘按键释放消息,手柄按键按下后只产生键盘按键按下消息。
172.增加双手柄支持,可以同时使用两个手柄。
18
19使用时不必考虑手柄按键和键盘按键的对应关系,就如直接使用手柄一般使用。
20
21*/
22
23
24#include <Windows.h>
25#include <MMSystem.h>
26
27
28#pragma comment( lib, "winmm.lib" )
29
30
31 // 每 ELAPSE 毫秒检测手柄输入
32#define ELAPSE 50
33 // 手柄按键对应键盘按键
34#define UP1 ('W')
35#define DOWN1 ('S')
36#define LEFT1 ('A')
37#define RIGHT1 ('D')
38#define BTN11 ('U')
39#define BTN12 ('I')
40#define BTN13 ('J')
41#define BTN14 ('K')
42
43#define UP2 ('Z')
44#define DOWN2 ('X')
45#define LEFT2 ('C')
46#define RIGHT2 ('V')
47#define BTN21 ('B')
48#define BTN22 ('N')
49#define BTN23 ('M')
50#define BTN24 ('F')
51
52
53TCHAR gClassName[] = TEXT("JoyToKey");
54TCHAR gWndName[] = TEXT("JoyToKey -- coreBugZJ");
55
56
57VOID msgOut( HWND hWnd, TCHAR msg[], UINT msglen ) {
58 HDC hdc = ::GetDC( hWnd );
59 ::TextOut( hdc, 50, 20, msg, msglen );
60 ::ReleaseDC( hWnd, hdc );
61}
62
63#define KEYDOWN(k) ::keybd_event( k, ::MapVirtualKey(k,0), 0, 0 )
64#define KEYUP(k) ::keybd_event( k, ::MapVirtualKey(k,0), KEYEVENTF_KEYUP, 0 )
65
66LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
67 static JOYINFO ji1, ji2;
68 static DWORD id1 = 0xFFFF, id2 = 0xFFFF;
69 static JOYCAPS jc1, jc2;
70 static DWORD numDev;
71 static DWORD jxm1, jxl1, jxr1, jym1, jyt1, jyb1, x1, y1;
72 static DWORD jxm2, jxl2, jxr2, jym2, jyt2, jyb2, x2, y2;
73
74 DWORD x, y;
75 DWORD bf;
76
77 switch ( uMsg ) {
78 case WM_CREATE :
79 if ( 0 == (numDev = ::joyGetNumDevs()) ) {
80 ::MessageBox( hWnd, TEXT("0 == ::joyGetNumDevs()\n\n请检查手柄是否插好"), TEXT("Error"), MB_OK | MB_ICONERROR );
81 ::SendMessage( hWnd, WM_DESTROY, 0, 0 );
82 return 0;
83 }
84
85 // 手柄 1
86 if ( JOYERR_NOERROR != ::joyGetPos( JOYSTICKID1, &ji1 ) ) {
87 ::MessageBox( hWnd, TEXT("JOYERR_NOERROR != ::joyGetPos()\n\n请检查手柄是否插好"), TEXT("Error"), MB_OK | MB_ICONERROR );
88 ::SendMessage( hWnd, WM_DESTROY, 0, 0 );
89 return 0;
90 }
91 if ( JOYERR_NOERROR != ::joyGetDevCaps( JOYSTICKID1, &jc1, sizeof(jc1) ) ) {
92 ::MessageBox( hWnd, TEXT("JOYERR_NOERROR != ::joyGetDevCaps()\n\n请检查手柄是否插好"), TEXT("Error"), MB_OK | MB_ICONERROR );
93 ::SendMessage( hWnd, WM_DESTROY, 0, 0 );
94 return 0;
95 }
96 id1 = JOYSTICKID1;
97 jxm1 = ( jc1.wXmin + jc1.wXmax ) / 2;
98 jxl1 = ( jc1.wXmin + jxm1 ) / 2;
99 jxr1 = ( jc1.wXmax + jxm1 ) / 2;
100 jym1 = ( jc1.wYmin + jc1.wYmax ) / 2;
101 jyt1 = ( jc1.wYmin + jym1 ) / 2;
102 jyb1 = ( jc1.wYmax + jym1 ) / 2;
103 x1 = jxm1;
104 y1 = jym1;
105 ::joySetCapture( hWnd, id1, ELAPSE, TRUE );
106
107 // 手柄 2
108 if ( 2 > numDev ) {
109 return 0;
110 }
111 if ( JOYERR_NOERROR != ::joyGetPos( JOYSTICKID2, &ji2 ) ) {
112 return 0;
113 }
114 if ( JOYERR_NOERROR != ::joyGetDevCaps( JOYSTICKID2, &jc2, sizeof(jc2) ) ) {
115 return 0;
116 }
117 id2 = JOYSTICKID2;
118 jxm2 = ( jc2.wXmin + jc2.wXmax ) / 2;
119 jxl2 = ( jc2.wXmin + jxm2 ) / 2;
120 jxr2 = ( jc2.wXmax + jxm2 ) / 2;
121 jym2 = ( jc2.wYmin + jc2.wYmax ) / 2;
122 jyt2 = ( jc2.wYmin + jym2 ) / 2;
123 jyb2 = ( jc2.wYmax + jym2 ) / 2;
124 x2 = jxm2;
125 y2 = jym2;
126 ::joySetCapture( hWnd, id2, ELAPSE, TRUE );
127
128 return 0;
129
130 case MM_JOY1MOVE :
131 bf = wParam;
132 x = LOWORD(lParam);
133 y = HIWORD(lParam);
134
135 if ( x1 < jxl1 ) {
136 KEYUP(LEFT1);
137 ::msgOut( hWnd, TEXT("LEFT1 UP "), 12 );
138 }
139 if ( x1 > jxr1 ) {
140 KEYUP(RIGHT1);
141 ::msgOut( hWnd, TEXT("RIGHT1 UP "), 12 );
142 }
143 if ( y1 < jyt1 ) {
144 KEYUP(UP1);
145 ::msgOut( hWnd, TEXT("UP1 UP "), 12 );
146 }
147 if ( y1 > jyb1 ) {
148 KEYUP(DOWN1);
149 ::msgOut( hWnd, TEXT("DOWN1 UP "), 12 );
150 }
151
152 x1 = x;
153 y1 = y;
154 if ( x1 < jxl1 ) {
155 KEYDOWN(LEFT1);
156 ::msgOut( hWnd, TEXT("LEFT1 DOWN "), 12 );
157 }
158 if ( x1 > jxr1 ) {
159 KEYDOWN(RIGHT1);
160 ::msgOut( hWnd, TEXT("RIGHT1 DOWN "), 12 );
161 }
162 if ( y1 < jyt1 ) {
163 KEYDOWN(UP1);
164 ::msgOut( hWnd, TEXT("UP1 DOWN "), 12 );
165 }
166 if ( y1 > jyb1 ) {
167 KEYDOWN(DOWN1);
168 ::msgOut( hWnd, TEXT("DOWN1 DOWN "), 12 );
169 }
170 return 0;
171
172 case MM_JOY1BUTTONDOWN :
173 bf = wParam;
174 if ( (bf & JOY_BUTTON1CHG) && (bf & JOY_BUTTON1) ) {
175 KEYDOWN(BTN11);
176 ::msgOut( hWnd, TEXT("BTN11 DOWN "), 12 );
177 }
178 if ( (bf & JOY_BUTTON2CHG) && (bf & JOY_BUTTON2) ) {
179 KEYDOWN(BTN12);
180 ::msgOut( hWnd, TEXT("BTN12 DOWN "), 12 );
181 }
182 if ( (bf & JOY_BUTTON3CHG) && (bf & JOY_BUTTON3) ) {
183 KEYDOWN(BTN13);
184 ::msgOut( hWnd, TEXT("BTN13 DOWN "), 12 );
185 }
186 if ( (bf & JOY_BUTTON4CHG) && (bf & JOY_BUTTON4) ) {
187 KEYDOWN(BTN14);
188 ::msgOut( hWnd, TEXT("BTN14 DWON "), 12 );
189 }
190 return 0;
191
192 case MM_JOY1BUTTONUP :
193 bf = wParam;
194 if ( (bf & JOY_BUTTON1CHG) && (0 == (bf & JOY_BUTTON1)) ) {
195 KEYUP(BTN11);
196 ::msgOut( hWnd, TEXT("BTN11 UP "), 12 );
197 }
198 if ( (bf & JOY_BUTTON2CHG) && (0 == (bf & JOY_BUTTON2)) ) {
199 KEYUP(BTN12);
200 ::msgOut( hWnd, TEXT("BTN12 UP "), 12 );
201 }
202 if ( (bf & JOY_BUTTON3CHG) && (0 == (bf & JOY_BUTTON3)) ) {
203 KEYUP(BTN13);
204 ::msgOut( hWnd, TEXT("BTN13 UP "), 12 );
205 }
206 if ( (bf & JOY_BUTTON4CHG) && (0 == (bf & JOY_BUTTON4)) ) {
207 KEYUP(BTN14);
208 ::msgOut( hWnd, TEXT("BTN14 UP "), 12 );
209 }
210 return 0;
211
212 case MM_JOY2MOVE :
213 bf = wParam;
214 x = LOWORD(lParam);
215 y = HIWORD(lParam);
216
217 if ( x2 < jxl2 ) {
218 KEYUP(LEFT2);
219 ::msgOut( hWnd, TEXT("LEFT2 UP "), 12 );
220 }
221 if ( x2 > jxr2 ) {
222 KEYUP(RIGHT2);
223 ::msgOut( hWnd, TEXT("RIGHT2 UP "), 12 );
224 }
225 if ( y2 < jyt2 ) {
226 KEYUP(UP2);
227 ::msgOut( hWnd, TEXT("UP2 UP "), 12 );
228 }
229 if ( y2 > jyb2 ) {
230 KEYUP(DOWN2);
231 ::msgOut( hWnd, TEXT("DOWN2 UP "), 12 );
232 }
233
234 x2 = x;
235 y2 = y;
236 if ( x2 < jxl2 ) {
237 KEYDOWN(LEFT2);
238 ::msgOut( hWnd, TEXT("LEFT2 DOWN "), 12 );
239 }
240 if ( x2 > jxr2 ) {
241 KEYDOWN(RIGHT2);
242 ::msgOut( hWnd, TEXT("RIGHT2 DOWN "), 12 );
243 }
244 if ( y2 < jyt2 ) {
245 KEYDOWN(UP2);
246 ::msgOut( hWnd, TEXT("UP2 DOWN "), 12 );
247 }
248 if ( y2 > jyb2 ) {
249 KEYDOWN(DOWN2);
250 ::msgOut( hWnd, TEXT("DOWN2 DOWN "), 12 );
251 }
252 return 0;
253
254 case MM_JOY2BUTTONDOWN :
255 bf = wParam;
256 if ( (bf & JOY_BUTTON1CHG) && (bf & JOY_BUTTON1) ) {
257 KEYDOWN(BTN21);
258 ::msgOut( hWnd, TEXT("BTN21 DOWN "), 12 );
259 }
260 if ( (bf & JOY_BUTTON2CHG) && (bf & JOY_BUTTON2) ) {
261 KEYDOWN(BTN22);
262 ::msgOut( hWnd, TEXT("BTN22 DOWN "), 12 );
263 }
264 if ( (bf & JOY_BUTTON3CHG) && (bf & JOY_BUTTON3) ) {
265 KEYDOWN(BTN23);
266 ::msgOut( hWnd, TEXT("BTN23 DOWN "), 12 );
267 }
268 if ( (bf & JOY_BUTTON4CHG) && (bf & JOY_BUTTON4) ) {
269 KEYDOWN(BTN24);
270 ::msgOut( hWnd, TEXT("BTN24 DWON "), 12 );
271 }
272 return 0;
273
274 case MM_JOY2BUTTONUP :
275 bf = wParam;
276 if ( (bf & JOY_BUTTON1CHG) && (0 == (bf & JOY_BUTTON1)) ) {
277 KEYUP(BTN21);
278 ::msgOut( hWnd, TEXT("BTN21 UP "), 12 );
279 }
280 if ( (bf & JOY_BUTTON2CHG) && (0 == (bf & JOY_BUTTON2)) ) {
281 KEYUP(BTN22);
282 ::msgOut( hWnd, TEXT("BTN22 UP "), 12 );
283 }
284 if ( (bf & JOY_BUTTON3CHG) && (0 == (bf & JOY_BUTTON3)) ) {
285 KEYUP(BTN23);
286 ::msgOut( hWnd, TEXT("BTN23 UP "), 12 );
287 }
288 if ( (bf & JOY_BUTTON4CHG) && (0 == (bf & JOY_BUTTON4)) ) {
289 KEYUP(BTN24);
290 ::msgOut( hWnd, TEXT("BTN24 UP "), 12 );
291 }
292 return 0;
293
294 case WM_DESTROY :
295 if ( JOYSTICKID1 == id1 ) {
296 ::joyReleaseCapture( id1 );
297 }
298 if ( JOYSTICKID2 == id2 ) {
299 ::joyReleaseCapture( id2 );
300 }
301 ::PostQuitMessage( 0 );
302 return 0;
303 }
304
305 return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
306}
307
308INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmd, INT nShow ) {
309 WNDCLASSEX wc;
310 HWND hWnd;
311 MSG msg;
312
313 wc.cbClsExtra = 0;
314 wc.cbSize = sizeof(wc);
315 wc.cbWndExtra = 0;
316 wc.hbrBackground = (HBRUSH)::GetStockObject( WHITE_BRUSH );
317 wc.hCursor = ::LoadCursor( NULL, IDC_ARROW );
318 wc.hIcon = ::LoadIcon( NULL, IDI_APPLICATION );
319 wc.hIconSm = ::LoadIcon( NULL, IDI_APPLICATION );
320 wc.hInstance = hInst;
321 wc.lpfnWndProc = WndProc;
322 wc.lpszClassName = gClassName;
323 wc.lpszMenuName = NULL;
324 wc.style = 0;
325
326 if ( 0 == ::RegisterClassEx( &wc ) ) {
327 ::MessageBox( NULL, TEXT("RegisterClassEx Failed!"), TEXT("Error"), MB_OK | MB_ICONERROR );
328 return 0;
329 }
330
331 hWnd = ::CreateWindowEx( 0,
332 gClassName, gWndName,
333 WS_OVERLAPPEDWINDOW,
334 200, 200, 400, 100,
335 NULL, NULL,
336 hInst, NULL );
337 if ( NULL == hWnd ) {
338 ::MessageBox( NULL, TEXT("CreateWindowEx Failed!"), TEXT("Error"), MB_OK | MB_ICONERROR );
339 return 0;
340 }
341 ::ShowWindow( hWnd, nShow );
342 ::UpdateWindow( hWnd );
343
344 while ( ::GetMessage( &msg, NULL, 0, 0 ) ) {
345 ::TranslateMessage( &msg );
346 ::DispatchMessage( &msg );
347 }
348 return msg.wParam;
349}
350