1 /*	SCCS Id: @(#)nhdefkey.c	3.4	$Date: 2003/12/11 09:49:08 $   */
2 /* Copyright (c) NetHack PC Development Team 2003                      */
3 /* NetHack may be freely redistributed.  See license for details.      */
4 
5 /*
6  * This is the default NetHack keystroke processing.
7  * It can be built as a run-time loadable dll (nhdefkey.dll).
8  * Alternative keystroke handlers can be built using the
9  * entry points in this file as a template.
10  *
11  * Use the defaults.nh "altkeyhandler" option to set a
12  * different dll name (without the ".DLL" extension) to
13  * get different processing. Ensure that the dll referenced
14  * in defaults.nh exists in the same directory as NetHack in
15  * order for it to load successfully.
16  *
17  */
18 
19 static char where_to_get_source[] = "http://www.nethack.org/";
20 static char author[] = "The NetHack Development Team";
21 
22 #include "hack.h"
23 #include "wintty.h"
24 #include "win32api.h"
25 
26 extern HANDLE hConIn;
27 extern INPUT_RECORD ir;
28 char dllname[512];
29 char *shortdllname;
30 
31 int FDECL(__declspec(dllexport) __stdcall
32 ProcessKeystroke, (HANDLE hConIn, INPUT_RECORD *ir,
33     boolean *valid, BOOLEAN_P numberpad, int portdebug));
34 
DllMain(HINSTANCE hInstance,DWORD fdwReason,PVOID pvReserved)35 int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
36 {
37 	char dlltmpname[512];
38 	char *tmp = dlltmpname, *tmp2;
39 	*(tmp + GetModuleFileName(hInstance, tmp, 511)) = '\0';
40 	(void)strcpy(dllname, tmp);
41 	tmp2 = strrchr(dllname, '\\');
42 	if (tmp2) {
43 		tmp2++;
44 		shortdllname = tmp2;
45 	}
46 	return TRUE;
47 }
48 
49 /*
50  *  Keyboard translation tables.
51  *  (Adopted from the MSDOS port)
52  */
53 
54 #define KEYPADLO	0x47
55 #define KEYPADHI	0x53
56 
57 #define PADKEYS 	(KEYPADHI - KEYPADLO + 1)
58 #define iskeypad(x)	(KEYPADLO <= (x) && (x) <= KEYPADHI)
59 
60 /*
61  * Keypad keys are translated to the normal values below.
62  * Shifted keypad keys are translated to the
63  *    shift values below.
64  */
65 
66 static const struct pad {
67 	uchar normal, shift, cntrl;
68 } keypad[PADKEYS] = {
69 			{'y', 'Y', C('y')},		/* 7 */
70 			{'k', 'K', C('k')},		/* 8 */
71 			{'u', 'U', C('u')},		/* 9 */
72 			{'m', C('p'), C('p')},		/* - */
73 			{'h', 'H', C('h')},		/* 4 */
74 			{'g', 'G', 'g'},		/* 5 */
75 			{'l', 'L', C('l')},		/* 6 */
76 			{'+', 'P', C('p')},		/* + */
77 			{'b', 'B', C('b')},		/* 1 */
78 			{'j', 'J', C('j')},		/* 2 */
79 			{'n', 'N', C('n')},		/* 3 */
80 			{'i', 'I', C('i')},		/* Ins */
81 			{'.', ':', ':'}			/* Del */
82 }, numpad[PADKEYS] = {
83 			{'7', M('7'), '7'},		/* 7 */
84 			{'8', M('8'), '8'},		/* 8 */
85 			{'9', M('9'), '9'},		/* 9 */
86 			{'m', C('p'), C('p')},		/* - */
87 			{'4', M('4'), '4'},		/* 4 */
88 			{'5', M('5'), '5'},		/* 5 */
89 			{'6', M('6'), '6'},		/* 6 */
90 			{'+', 'P', C('p')},		/* + */
91 			{'1', M('1'), '1'},		/* 1 */
92 			{'2', M('2'), '2'},		/* 2 */
93 			{'3', M('3'), '3'},		/* 3 */
94 			{'0', M('0'), '0'},		/* Ins */
95 			{'.', ':', ':'}			/* Del */
96 };
97 
98 #define inmap(x,vk)	(((x) > 'A' && (x) < 'Z') || (vk) == 0xBF || (x) == '2')
99 
100 static BYTE KeyState[256];
101 
102 int __declspec(dllexport) __stdcall
103 ProcessKeystroke(hConIn,ir, valid, numberpad, portdebug)
104 HANDLE hConIn;
105 INPUT_RECORD *ir;
106 boolean *valid;
107 boolean numberpad;
108 int portdebug;
109 {
110 	int metaflags = 0, k = 0;
111 	int keycode, vk;
112 	unsigned char ch, pre_ch, mk = 0;
113 	unsigned short int scan;
114 	unsigned long shiftstate;
115 	int altseq = 0;
116 	const struct pad *kpad;
117 
118 	shiftstate = 0L;
119 	ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar;
120 	scan  = ir->Event.KeyEvent.wVirtualScanCode;
121 	vk    = ir->Event.KeyEvent.wVirtualKeyCode;
122 	keycode = MapVirtualKey(vk, 2);
123 	shiftstate = ir->Event.KeyEvent.dwControlKeyState;
124 	KeyState[VK_SHIFT]   = (shiftstate & SHIFT_PRESSED) ? 0x81 : 0;
125 	KeyState[VK_CONTROL] = (shiftstate & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) ?
126 				0x81 : 0;
127 	KeyState[VK_CAPITAL] = (shiftstate & CAPSLOCK_ON) ? 0x81 : 0;
128 
129 	if (shiftstate & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) {
130 		if (ch || inmap(keycode,vk)) altseq = 1;
131 		else altseq = -1;	/* invalid altseq */
132 	}
133 	if (ch || (iskeypad(scan)) || (altseq > 0))
134 		*valid = TRUE;
135 	/* if (!valid) return 0; */
136     	/*
137 	 * shiftstate can be checked to see if various special
138          * keys were pressed at the same time as the key.
139          * Currently we are using the ALT & SHIFT & CONTROLS.
140          *
141          *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
142          *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
143          *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
144          *           CAPSLOCK_ON, ENHANCED_KEY
145          *
146          * are all valid bit masks to use on shiftstate.
147          * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
148          *      left control key was pressed with the keystroke.
149          */
150         if (iskeypad(scan)) {
151             kpad = numberpad ? numpad : keypad;
152             if (shiftstate & SHIFT_PRESSED) {
153                 ch = kpad[scan - KEYPADLO].shift;
154             }
155             else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
156                 ch = kpad[scan - KEYPADLO].cntrl;
157             }
158             else {
159                 ch = kpad[scan - KEYPADLO].normal;
160             }
161         }
162         else if (altseq > 0) { /* ALT sequence */
163 		if (vk == 0xBF) ch = M('?');
164 		else ch = M(tolower(keycode));
165         }
166 	/* Attempt to work better with international keyboards. */
167 	else {
168 		WORD chr[2];
169 		k = ToAscii(vk, scan, KeyState, chr, 0);
170 		if (k <= 2)
171 		    switch(k) {
172 			case 2:  /* two characters */
173 				ch = (unsigned char)chr[1];
174 				*valid = TRUE;
175 				break;
176 			case 1:  /* one character */
177 				ch = (unsigned char)chr[0];
178 				*valid = TRUE;
179 				break;
180 			case 0:  /* no translation */
181 			default: /* negative */
182 				*valid = FALSE;
183 		    }
184 	}
185 	if (ch == '\r') ch = '\n';
186 #ifdef PORT_DEBUG
187 	if (portdebug) {
188 		char buf[BUFSZ];
189 		Sprintf(buf,
190 	"PORTDEBUG (%s): ch=%u, sc=%u, vk=%d, pre=%d, sh=0x%X, ta=%d (ESC to end)",
191 			shortdllname, ch, scan, vk, pre_ch, shiftstate, k);
192 		fprintf(stdout, "\n%s", buf);
193 	}
194 #endif
195 	return ch;
196 }
197 
198 
199 int __declspec(dllexport) __stdcall
200 NHkbhit(hConIn, ir)
201 HANDLE hConIn;
202 INPUT_RECORD *ir;
203 {
204 	int done = 0;	/* true =  "stop searching"        */
205 	int retval;	/* true =  "we had a match"        */
206 	DWORD count;
207 	unsigned short int scan;
208 	unsigned char ch;
209 	unsigned long shiftstate;
210 	int altseq = 0, keycode, vk;
211 	done = 0;
212 	retval = 0;
213 	while (!done)
214 	{
215 	    count = 0;
216 	    PeekConsoleInput(hConIn,ir,1,&count);
217 	    if (count > 0) {
218 		if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) {
219 			ch    = ir->Event.KeyEvent.uChar.AsciiChar;
220 			scan  = ir->Event.KeyEvent.wVirtualScanCode;
221 			shiftstate = ir->Event.KeyEvent.dwControlKeyState;
222 			vk = ir->Event.KeyEvent.wVirtualKeyCode;
223 			keycode = MapVirtualKey(vk, 2);
224 			if (shiftstate & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) {
225 				if  (ch || inmap(keycode,vk)) altseq = 1;
226 				else altseq = -1;	/* invalid altseq */
227 			}
228 			if (ch || iskeypad(scan) || altseq) {
229 				done = 1;	    /* Stop looking         */
230 				retval = 1;         /* Found what we sought */
231 			} else {
232 				/* Strange Key event; let's purge it to avoid trouble */
233 				ReadConsoleInput(hConIn,ir,1,&count);
234 			}
235 
236 		}
237 		else if ((ir->EventType == MOUSE_EVENT &&
238 		  (ir->Event.MouseEvent.dwButtonState & MOUSEMASK))) {
239 			done = 1;
240 			retval = 1;
241 		}
242 
243 		else /* Discard it, it's an insignificant event */
244 			ReadConsoleInput(hConIn,ir,1,&count);
245 		} else  /* There are no events in console event queue */ {
246 		done = 1;	  /* Stop looking               */
247 		retval = 0;
248 	    }
249 	}
250 	return retval;
251 }
252 
253 int __declspec(dllexport) __stdcall
254 CheckInput(hConIn, ir, count, numpad, mode, mod, cc)
255 HANDLE hConIn;
256 INPUT_RECORD *ir;
257 DWORD *count;
258 boolean numpad;
259 int mode;
260 int *mod;
261 coord *cc;
262 {
263 	int ch;
264 	boolean valid = 0, done = 0;
265 	while (!done) {
266 		ReadConsoleInput(hConIn,ir,1,count);
267 		if (mode == 0) {
268 		    if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) {
269 			ch = ProcessKeystroke(hConIn, ir, &valid, numpad, 0);
270 			done = valid;
271 		    }
272 		} else {
273 		    if (count > 0) {
274 			if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) {
275 			    ch = ProcessKeystroke(hConIn, ir, &valid, numpad, 0);
276 			    if (valid) return ch;
277 			} else if (ir->EventType == MOUSE_EVENT) {
278 			    if ((ir->Event.MouseEvent.dwEventFlags == 0) &&
279 		   	        (ir->Event.MouseEvent.dwButtonState & MOUSEMASK)) {
280 			  	    cc->x = ir->Event.MouseEvent.dwMousePosition.X + 1;
281 			  	    cc->y = ir->Event.MouseEvent.dwMousePosition.Y - 1;
282 
283 				    if (ir->Event.MouseEvent.dwButtonState & LEFTBUTTON)
284 		  	       		*mod = CLICK_1;
285 				    else if (ir->Event.MouseEvent.dwButtonState & RIGHTBUTTON)
286 					*mod = CLICK_2;
287 #if 0	/* middle button */
288 				    else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON)
289 			      		*mod = CLICK_3;
290 #endif
291 			           return 0;
292 			    }
293 	        	}
294 		    } else
295 			done = 1;
296 		}
297 	}
298 	return mode ? 0 : ch;
299 }
300 
301 int __declspec(dllexport) __stdcall
302 SourceWhere(buf)
303 char **buf;
304 {
305 	if (!buf) return 0;
306 	*buf = where_to_get_source;
307 	return 1;
308 }
309 
310 int __declspec(dllexport) __stdcall
311 SourceAuthor(buf)
312 char **buf;
313 {
314 	if (!buf) return 0;
315 	*buf = author;
316 	return 1;
317 }
318 
319 int __declspec(dllexport) __stdcall
320 KeyHandlerName(buf, full)
321 char **buf;
322 int full;
323 {
324 	if (!buf) return 0;
325 	if (full) *buf = dllname;
326 	else *buf = shortdllname;
327 	return 1;
328 }
329 
330