OSDN Git Service

upgrade to 3.6.2
[jnethack/source.git] / sys / winnt / nhraykey.c
1 /* NetHack 3.6  nhraykey.c      $NHDT-Date: 1457207047 2016/03/05 19:44:07 $  $NHDT-Branch: chasonr $:$NHDT-Revision: 1.16 $ */
2 /* Copyright (c) NetHack PC Development Team 2003                      */
3 /* NetHack may be freely redistributed.  See license for details.      */
4
5 /*
6  * Keystroke handling contributed by Ray Chason.
7  * The following text was written by Ray Chason.
8  *
9  * The problem
10  * ===========
11  *
12  * The console-mode Nethack wants both keyboard and mouse input.  The
13  * problem is that the Windows API provides no easy way to get mouse input
14  * and also keyboard input properly translated according to the user's
15  * chosen keyboard layout.
16  *
17  * The ReadConsoleInput function returns a stream of keyboard and mouse
18  * events.  Nethack is interested in those events that represent a key
19  * pressed, or a click on a mouse button.  The keyboard events from
20  * ReadConsoleInput are not translated according to the keyboard layout,
21  * and do not take into account the shift, control, or alt keys.
22  *
23  * The PeekConsoleInput function works similarly to ReadConsoleInput,
24  * except that it does not remove an event from the queue and it returns
25  * instead of blocking when the queue is empty.
26  *
27  * A program can also use ReadConsole to get a properly translated stream
28  * of characters.  Unfortunately, ReadConsole does not return mouse events,
29  * does not distinguish the keypad from the main keyboard, does not return
30  * keys shifted with Alt, and does not even return the ESC key when
31  * pressed.
32  *
33  * We want both the functionality of ReadConsole and the functionality of
34  * ReadConsoleInput.  But Microsoft didn't seem to think of that.
35  *
36  *
37  * The solution, in the original code
38  * ==================================
39  *
40  * The original 3.4.1 distribution tries to get proper keyboard translation
41  * by passing keyboard events to the ToAscii function.  This works, to some
42  * extent -- it takes the shift key into account, and it processes dead
43  * keys properly.  But it doesn't take non-US keyboards into account.  It
44  * appears that ToAscii is meant for windowed applications, and does not
45  * have enough information to do its job properly in a console application.
46  *
47  *
48  * The Finnish keyboard patch
49  * ==========================
50  *
51  * This patch adds the "subkeyvalue" option to the defaults.nh file.  The
52  * user can then add OPTIONS=sukeyvalue:171/92, for instance, to replace
53  * the 171 character with 92, which is \.  This works, once properly
54  * configured, but places too much burden on the user.  It also bars the
55  * use of the substituted characters in naming objects or monsters.
56  *
57  *
58  * The solution presented here
59  * ===========================
60  *
61  * The best way I could find to combine the functionality of ReadConsole
62  * with that of ReadConsoleInput is simple in concept.  First, call
63  * PeekConsoleInput to get the first event.  If it represents a key press,
64  * call ReadConsole to retrieve the key.  Otherwise, pop it off the queue
65  * with ReadConsoleInput and, if it's a mouse click, return it as such.
66  *
67  * But the Devil, as they say, is in the details.  The problem is in
68  * recognizing an event that ReadConsole will return as a key.  We don't
69  * want to call ReadConsole unless we know that it will immediately return:
70  * if it blocks, the mouse and the Alt sequences will cease to function
71  * until it returns.
72  *
73  * Separating process_keystroke into two functions, one for commands and a
74  * new one, process_keystroke2, for answering prompts, makes the job a lot
75  * easier.  process_keystroke2 doesn't have to worry about mouse events or
76  * Alt sequences, and so the consequences are minor if ReadConsole blocks.
77  * process_keystroke, OTOH, never needs to return a non-ASCII character
78  * that was read from ReadConsole; it returns bytes with the high bit set
79  * only in response to an Alt sequence.
80  *
81  * So in process_keystroke, before calling ReadConsole, a bogus key event
82  * is pushed on the queue.  This event causes ReadConsole to return, even
83  * if there was no other character available.  Because the bogus key has
84  * the eighth bit set, it is filtered out.  This is not done in
85  * process_keystroke2, because that would render dead keys unusable.
86  *
87  * A separate process_keystroke2 can also process the numeric keypad in a
88  * way that makes sense for prompts:  just return the corresponding symbol,
89  * and pay no mind to number_pad or the num lock key.
90  *
91  * The recognition of Alt sequences is modified, to support the use of
92  * characters generated with the AltGr key.  A keystroke is an Alt sequence
93  * if an Alt key is seen that can't be an AltGr (since an AltGr sequence
94  * could be a character, and in some layouts it could even be an ASCII
95  * character).  This recognition is different on NT-based and 95-based
96  * Windows:
97  *
98  *    * On NT-based Windows, AltGr signals as right Alt and left Ctrl
99  *      together.  So an Alt sequence is recognized if either Alt key is
100  *      pressed and if right Alt and left Ctrl are not both present.  This
101  *      is true even if the keyboard in use does not have an AltGr key, and
102  *      uses right Alt for AltGr.
103  *
104  *    * On 95-based Windows, with a keyboard that lacks the AltGr key, the
105  *      right Alt key is used instead.  But it still signals as right Alt,
106  *      without left Ctrl.  There is no way for the application to know
107  *      whether right Alt is Alt or AltGr, and so it is always assumed
108  *      to be AltGr.  This means that Alt sequences must be formed with
109  *      left Alt.
110  *
111  * So the patch processes keystrokes as follows:
112  *
113  *     * If the scan and virtual key codes are both 0, it's the bogus key,
114  *       and we ignore it.
115  *
116  *     * Keys on the numeric keypad are processed for commands as in the
117  *       unpatched Nethack, and for prompts by returning the ASCII
118  *       character, even if the num lock is off.
119  *
120  *     * Alt sequences are processed for commands as in the unpatched
121  *       Nethack, and ignored for prompts.
122  *
123  *     * Control codes are returned as received, because ReadConsole will
124  *       not return the ESC key.
125  *
126  *     * Other key-down events are passed to ReadConsole.  The use of
127  *       ReadConsole is different for commands than for prompts:
128  *
129  *       o For commands, the bogus key is pushed onto the queue before
130  *         ReadConsole is called.  On return, non-ASCII characters are
131  *         filtered, so they are not mistaken for Alt sequences; this also
132  *         filters the bogus key.
133  *
134  *       o For prompts, the bogus key is not used, because that would
135  *         interfere with dead keys.  Eight bit characters may be returned,
136  *         and are coded in the configured code page.
137  *
138  *
139  * Possible improvements
140  * =====================
141  *
142  * Some possible improvements remain:
143  *
144  *     * Integrate the existing Finnish keyboard patch, for use with non-
145  *       QWERTY layouts such as the German QWERTZ keyboard or Dvorak.
146  *
147  *     * Fix the keyboard glitches in the graphical version.  Namely, dead
148  *       keys don't work, and input comes in as ISO-8859-1 but is displayed
149  *       as code page 437 if IBMgraphics is set on startup.
150  *
151  *     * Transform incoming text to ISO-8859-1, for full compatibility with
152  *       the graphical version.
153  *
154  *     * After pushing the bogus key and calling ReadConsole, check to see
155  *       if we got the bogus key; if so, and an Alt is pressed, process the
156  *       event as an Alt sequence.
157  *
158  */
159
160 static char where_to_get_source[] = "http://www.nethack.org/";
161 static char author[] = "Ray Chason";
162
163 #include "win32api.h"
164 #include "hack.h"
165 #include "wintty.h"
166
167 extern HANDLE hConIn;
168 extern INPUT_RECORD ir;
169 char dllname[512];
170 char *shortdllname;
171
172 int FDECL(__declspec(dllexport) __stdcall ProcessKeystroke,
173           (HANDLE hConIn, INPUT_RECORD *ir, boolean *valid,
174            BOOLEAN_P numberpad, int portdebug));
175
176 static INPUT_RECORD bogus_key;
177
178 int WINAPI
179 DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
180 {
181     char dlltmpname[512];
182     char *tmp = dlltmpname, *tmp2;
183     *(tmp + GetModuleFileName(hInstance, tmp, 511)) = '\0';
184     (void) strcpy(dllname, tmp);
185     tmp2 = strrchr(dllname, '\\');
186     if (tmp2) {
187         tmp2++;
188         shortdllname = tmp2;
189     }
190     /* A bogus key that will be filtered when received, to keep ReadConsole
191      * from blocking */
192     bogus_key.EventType = KEY_EVENT;
193     bogus_key.Event.KeyEvent.bKeyDown = 1;
194     bogus_key.Event.KeyEvent.wRepeatCount = 1;
195     bogus_key.Event.KeyEvent.wVirtualKeyCode = 0;
196     bogus_key.Event.KeyEvent.wVirtualScanCode = 0;
197     bogus_key.Event.KeyEvent.uChar.AsciiChar = (uchar) 0x80;
198     bogus_key.Event.KeyEvent.dwControlKeyState = 0;
199     return TRUE;
200 }
201
202 /*
203  *  Keyboard translation tables.
204  *  (Adopted from the MSDOS port)
205  */
206
207 #define KEYPADLO 0x47
208 #define KEYPADHI 0x53
209
210 #define PADKEYS (KEYPADHI - KEYPADLO + 1)
211 #define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI)
212 #define isnumkeypad(x) \
213     (KEYPADLO <= (x) && (x) <= 0x51 && (x) != 0x4A && (x) != 0x4E)
214
215 /*
216  * Keypad keys are translated to the normal values below.
217  * Shifted keypad keys are translated to the
218  *    shift values below.
219  */
220
221 static const struct pad {
222     uchar normal, shift, cntrl;
223 } keypad[PADKEYS] =
224     {
225       { 'y', 'Y', C('y') },    /* 7 */
226       { 'k', 'K', C('k') },    /* 8 */
227       { 'u', 'U', C('u') },    /* 9 */
228       { 'm', C('p'), C('p') }, /* - */
229       { 'h', 'H', C('h') },    /* 4 */
230       { 'g', 'G', 'g' },       /* 5 */
231       { 'l', 'L', C('l') },    /* 6 */
232       { '+', 'P', C('p') },    /* + */
233       { 'b', 'B', C('b') },    /* 1 */
234       { 'j', 'J', C('j') },    /* 2 */
235       { 'n', 'N', C('n') },    /* 3 */
236       { 'i', 'I', C('i') },    /* Ins */
237       { '.', ':', ':' }        /* Del */
238     },
239   numpad[PADKEYS] = {
240       { '7', M('7'), '7' },    /* 7 */
241       { '8', M('8'), '8' },    /* 8 */
242       { '9', M('9'), '9' },    /* 9 */
243       { 'm', C('p'), C('p') }, /* - */
244       { '4', M('4'), '4' },    /* 4 */
245       { 'g', 'G', 'g' },       /* 5 */
246       { '6', M('6'), '6' },    /* 6 */
247       { '+', 'P', C('p') },    /* + */
248       { '1', M('1'), '1' },    /* 1 */
249       { '2', M('2'), '2' },    /* 2 */
250       { '3', M('3'), '3' },    /* 3 */
251       { 'i', 'I', C('i') },    /* Ins */
252       { '.', ':', ':' }        /* Del */
253   };
254
255 #define inmap(x, vk) (((x) > 'A' && (x) < 'Z') || (vk) == 0xBF || (x) == '2')
256
257 /* Use process_keystroke for key commands, process_keystroke2 for prompts */
258 /* int FDECL(process_keystroke, (INPUT_RECORD *ir, boolean *valid, int
259  * portdebug)); */
260 int FDECL(process_keystroke2, (HANDLE, INPUT_RECORD *ir, boolean *valid));
261 static int FDECL(is_altseq, (unsigned long shiftstate));
262
263 static int
264 is_altseq(shiftstate)
265 unsigned long shiftstate;
266 {
267     /* We need to distinguish the Alt keys from the AltGr key.
268      * On NT-based Windows, AltGr signals as right Alt and left Ctrl together;
269      * on 95-based Windows, AltGr signals as right Alt only.
270      * So on NT, we signal Alt if either Alt is pressed and left Ctrl is not,
271      * and on 95, we signal Alt for left Alt only. */
272     switch (shiftstate
273             & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) {
274     case LEFT_ALT_PRESSED:
275     case LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED:
276         return 1;
277
278     case RIGHT_ALT_PRESSED:
279     case RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED:
280         return (GetVersion() & 0x80000000) == 0;
281
282     default:
283         return 0;
284     }
285 }
286
287 int __declspec(dllexport) __stdcall ProcessKeystroke(hConIn, ir, valid,
288                                                      numberpad, portdebug)
289 HANDLE hConIn;
290 INPUT_RECORD *ir;
291 boolean *valid;
292 boolean numberpad;
293 int portdebug;
294 {
295     int metaflags = 0, k = 0;
296     int keycode, vk;
297     unsigned char ch, pre_ch, mk = 0;
298     unsigned short int scan;
299     unsigned long shiftstate;
300     int altseq = 0;
301     const struct pad *kpad;
302     DWORD count;
303
304     shiftstate = 0L;
305     ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar;
306     scan = ir->Event.KeyEvent.wVirtualScanCode;
307     vk = ir->Event.KeyEvent.wVirtualKeyCode;
308     keycode = MapVirtualKey(vk, 2);
309     shiftstate = ir->Event.KeyEvent.dwControlKeyState;
310     if (scan == 0 && vk == 0) {
311         /* It's the bogus_key */
312         ReadConsoleInput(hConIn, ir, 1, &count);
313         *valid = FALSE;
314         return 0;
315     }
316
317     if (is_altseq(shiftstate)) {
318         if (ch || inmap(keycode, vk))
319             altseq = 1;
320         else
321             altseq = -1; /* invalid altseq */
322     }
323     if (ch || (iskeypad(scan)) || (altseq > 0))
324         *valid = TRUE;
325     /* if (!valid) return 0; */
326     /*
327      * shiftstate can be checked to see if various special
328      * keys were pressed at the same time as the key.
329      * Currently we are using the ALT & SHIFT & CONTROLS.
330      *
331      *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
332      *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
333      *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
334      *           CAPSLOCK_ON, ENHANCED_KEY
335      *
336      * are all valid bit masks to use on shiftstate.
337      * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
338      *      left control key was pressed with the keystroke.
339      */
340     if (iskeypad(scan)) {
341         ReadConsoleInput(hConIn, ir, 1, &count);
342         kpad = numberpad ? numpad : keypad;
343         if (shiftstate & SHIFT_PRESSED) {
344             ch = kpad[scan - KEYPADLO].shift;
345         } else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
346             ch = kpad[scan - KEYPADLO].cntrl;
347         } else {
348             ch = kpad[scan - KEYPADLO].normal;
349         }
350     } else if (altseq > 0) { /* ALT sequence */
351         ReadConsoleInput(hConIn, ir, 1, &count);
352         if (vk == 0xBF)
353             ch = M('?');
354         else
355             ch = M(tolower((uchar) keycode));
356     } else if (ch < 32 && !isnumkeypad(scan)) {
357         /* Control code; ReadConsole seems to filter some of these,
358          * including ESC */
359         ReadConsoleInput(hConIn, ir, 1, &count);
360     }
361     /* Attempt to work better with international keyboards. */
362     else {
363         CHAR ch2;
364         DWORD written;
365         /* The bogus_key guarantees that ReadConsole will return,
366          * and does not itself do anything */
367         WriteConsoleInput(hConIn, &bogus_key, 1, &written);
368         ReadConsole(hConIn, &ch2, 1, &count, NULL);
369         /* Prevent high characters from being interpreted as alt
370          * sequences; also filter the bogus_key */
371         if (ch2 & 0x80)
372             *valid = FALSE;
373         else
374             ch = ch2;
375         if (ch == 0)
376             *valid = FALSE;
377     }
378     if (ch == '\r')
379         ch = '\n';
380 #ifdef PORT_DEBUG
381     if (portdebug) {
382         char buf[BUFSZ];
383         Sprintf(buf, "PORTDEBUG: ch=%u, scan=%u, vk=%d, pre=%d, "
384                      "shiftstate=0x%X (ESC to end)\n",
385                 ch, scan, vk, pre_ch, shiftstate);
386         fprintf(stdout, "\n%s", buf);
387     }
388 #endif
389     return ch;
390 }
391
392 int
393 process_keystroke2(hConIn, ir, valid)
394 HANDLE hConIn;
395 INPUT_RECORD *ir;
396 boolean *valid;
397 {
398     /* Use these values for the numeric keypad */
399     static const char keypad_nums[] = "789-456+1230.";
400
401     unsigned char ch;
402     int vk;
403     unsigned short int scan;
404     unsigned long shiftstate;
405     int altseq;
406     DWORD count;
407
408     ch = ir->Event.KeyEvent.uChar.AsciiChar;
409     vk = ir->Event.KeyEvent.wVirtualKeyCode;
410     scan = ir->Event.KeyEvent.wVirtualScanCode;
411     shiftstate = ir->Event.KeyEvent.dwControlKeyState;
412
413     if (scan == 0 && vk == 0) {
414         /* It's the bogus_key */
415         ReadConsoleInput(hConIn, ir, 1, &count);
416         *valid = FALSE;
417         return 0;
418     }
419
420     altseq = is_altseq(shiftstate);
421     if (ch || (iskeypad(scan)) || altseq)
422         *valid = TRUE;
423     /* if (!valid) return 0; */
424     /*
425      * shiftstate can be checked to see if various special
426      * keys were pressed at the same time as the key.
427      * Currently we are using the ALT & SHIFT & CONTROLS.
428      *
429      *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
430      *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
431      *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
432      *           CAPSLOCK_ON, ENHANCED_KEY
433      *
434      * are all valid bit masks to use on shiftstate.
435      * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
436      *      left control key was pressed with the keystroke.
437      */
438     if (iskeypad(scan) && !altseq) {
439         ReadConsoleInput(hConIn, ir, 1, &count);
440         ch = keypad_nums[scan - KEYPADLO];
441     } else if (ch < 32 && !isnumkeypad(scan)) {
442         /* Control code; ReadConsole seems to filter some of these,
443          * including ESC */
444         ReadConsoleInput(hConIn, ir, 1, &count);
445     }
446     /* Attempt to work better with international keyboards. */
447     else {
448         CHAR ch2;
449         ReadConsole(hConIn, &ch2, 1, &count, NULL);
450         ch = ch2 & 0xFF;
451         if (ch == 0)
452             *valid = FALSE;
453     }
454     if (ch == '\r')
455         ch = '\n';
456     return ch;
457 }
458
459 int __declspec(dllexport) __stdcall CheckInput(hConIn, ir, count, numpad,
460                                                mode, mod, cc)
461 HANDLE hConIn;
462 INPUT_RECORD *ir;
463 DWORD *count;
464 int mode;
465 int *mod;
466 boolean numpad;
467 coord *cc;
468 {
469 #if defined(SAFERHANGUP)
470     DWORD dwWait;
471 #endif
472     int ch;
473     boolean valid = 0, done = 0;
474     while (!done) {
475         *count = 0;
476         dwWait = WaitForSingleObject(hConIn, INFINITE);
477 #if defined(SAFERHANGUP)
478         if (dwWait == WAIT_FAILED)
479             return '\033';
480 #endif
481         PeekConsoleInput(hConIn, ir, 1, count);
482         if (mode == 0) {
483             if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) {
484                 ch = process_keystroke2(hConIn, ir, &valid);
485                 done = valid;
486             } else
487                 ReadConsoleInput(hConIn, ir, 1, count);
488         } else {
489             ch = 0;
490             if (count > 0) {
491                 if (ir->EventType == KEY_EVENT
492                     && ir->Event.KeyEvent.bKeyDown) {
493                     ch = ProcessKeystroke(hConIn, ir, &valid, numpad,
494 #ifdef PORTDEBUG
495                                           1);
496 #else
497                                           0);
498 #endif
499                     if (valid)
500                         return ch;
501                 } else {
502                     ReadConsoleInput(hConIn, ir, 1, count);
503                     if (ir->EventType == MOUSE_EVENT) {
504                         if ((ir->Event.MouseEvent.dwEventFlags == 0)
505                             && (ir->Event.MouseEvent.dwButtonState
506                                 & MOUSEMASK)) {
507                             cc->x =
508                                 ir->Event.MouseEvent.dwMousePosition.X + 1;
509                             cc->y =
510                                 ir->Event.MouseEvent.dwMousePosition.Y - 1;
511
512                             if (ir->Event.MouseEvent.dwButtonState
513                                 & LEFTBUTTON)
514                                 *mod = CLICK_1;
515                             else if (ir->Event.MouseEvent.dwButtonState
516                                      & RIGHTBUTTON)
517                                 *mod = CLICK_2;
518 #if 0 /* middle button */                              
519                                 else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON)
520                                         *mod = CLICK_3;
521 #endif
522                             return 0;
523                         }
524                     }
525 #if 0
526                         /* We ignore these types of console events */
527                         else if (ir->EventType == FOCUS_EVENT) {
528                         }
529                         else if (ir->EventType == MENU_EVENT) {
530                         }
531 #endif
532                 }
533             } else
534                 done = 1;
535         }
536     }
537     *mod = 0;
538     return ch;
539 }
540
541 int __declspec(dllexport) __stdcall NHkbhit(hConIn, ir)
542 HANDLE hConIn;
543 INPUT_RECORD *ir;
544 {
545     int done = 0; /* true =  "stop searching"        */
546     int retval;   /* true =  "we had a match"        */
547     DWORD count;
548     unsigned short int scan;
549     unsigned char ch;
550     unsigned long shiftstate;
551     int altseq = 0, keycode, vk;
552     done = 0;
553     retval = 0;
554     while (!done) {
555         count = 0;
556         PeekConsoleInput(hConIn, ir, 1, &count);
557         if (count > 0) {
558             if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) {
559                 ch = ir->Event.KeyEvent.uChar.AsciiChar;
560                 scan = ir->Event.KeyEvent.wVirtualScanCode;
561                 shiftstate = ir->Event.KeyEvent.dwControlKeyState;
562                 vk = ir->Event.KeyEvent.wVirtualKeyCode;
563                 if (scan == 0 && vk == 0) {
564                     /* It's the bogus_key.  Discard it */
565                     ReadConsoleInput(hConIn,ir,1,&count);
566                 } else {
567                     keycode = MapVirtualKey(vk, 2);
568                     if (is_altseq(shiftstate)) {
569                         if (ch || inmap(keycode, vk))
570                             altseq = 1;
571                         else
572                             altseq = -1; /* invalid altseq */
573                     }
574                     if (ch || iskeypad(scan) || altseq) {
575                         done = 1;   /* Stop looking         */
576                         retval = 1; /* Found what we sought */
577                     } else {
578                         /* Strange Key event; let's purge it to avoid trouble */
579                         ReadConsoleInput(hConIn, ir, 1, &count);
580                     }
581                 }
582
583             } else if ((ir->EventType == MOUSE_EVENT
584                         && (ir->Event.MouseEvent.dwButtonState
585                             & MOUSEMASK))) {
586                 done = 1;
587                 retval = 1;
588             }
589
590             else /* Discard it, it's an insignificant event */
591                 ReadConsoleInput(hConIn, ir, 1, &count);
592         } else /* There are no events in console event queue */ {
593             done = 1; /* Stop looking               */
594             retval = 0;
595         }
596     }
597     return retval;
598 }
599
600 int __declspec(dllexport) __stdcall SourceWhere(buf)
601 char **buf;
602 {
603     if (!buf)
604         return 0;
605     *buf = where_to_get_source;
606     return 1;
607 }
608
609 int __declspec(dllexport) __stdcall SourceAuthor(buf)
610 char **buf;
611 {
612     if (!buf)
613         return 0;
614     *buf = author;
615     return 1;
616 }
617
618 int __declspec(dllexport) __stdcall KeyHandlerName(buf, full)
619 char **buf;
620 int full;
621 {
622     if (!buf)
623         return 0;
624     if (full)
625         *buf = dllname;
626     else
627         *buf = shortdllname;
628     return 1;
629 }