]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
89a8c11882431c54d7c66de4f3fd743357093f57
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <commctrl.h>
3 #ifndef AUTO_WINSOCK
4 #ifdef WINSOCK_TWO
5 #include <winsock2.h>
6 #else
7 #include <winsock.h>
8 #endif
9 #endif
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <time.h>
14
15 #define PUTTY_DO_GLOBALS                       /* actually _define_ globals */
16 #include "putty.h"
17 #include "winstuff.h"
18 #include "storage.h"
19 #include "win_res.h"
20
21 #define IDM_SHOWLOG   0x0010
22 #define IDM_NEWSESS   0x0020
23 #define IDM_DUPSESS   0x0030
24 #define IDM_RECONF    0x0040
25 #define IDM_CLRSB     0x0050
26 #define IDM_RESET     0x0060
27 #define IDM_TEL_AYT   0x0070
28 #define IDM_TEL_BRK   0x0080
29 #define IDM_TEL_SYNCH 0x0090
30 #define IDM_TEL_EC    0x00a0
31 #define IDM_TEL_EL    0x00b0
32 #define IDM_TEL_GA    0x00c0
33 #define IDM_TEL_NOP   0x00d0
34 #define IDM_TEL_ABORT 0x00e0
35 #define IDM_TEL_AO    0x00f0
36 #define IDM_TEL_IP    0x0100
37 #define IDM_TEL_SUSP  0x0110
38 #define IDM_TEL_EOR   0x0120
39 #define IDM_TEL_EOF   0x0130
40 #define IDM_ABOUT     0x0140
41 #define IDM_SAVEDSESS 0x0150
42
43 #define IDM_SAVED_MIN 0x1000
44 #define IDM_SAVED_MAX 0x2000
45
46 #define WM_IGNORE_SIZE (WM_XUSER + 1)
47 #define WM_IGNORE_CLIP (WM_XUSER + 2)
48
49 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
50 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
51 static void cfgtopalette(void);
52 static void init_palette(void);
53 static void init_fonts(int);
54
55 static int extra_width, extra_height;
56
57 static int pending_netevent = 0;
58 static WPARAM pend_netevent_wParam = 0;
59 static LPARAM pend_netevent_lParam = 0;
60 static void enact_pending_netevent(void);
61
62 static time_t last_movement = 0;
63
64 #define FONT_NORMAL 0
65 #define FONT_BOLD 1
66 #define FONT_UNDERLINE 2
67 #define FONT_BOLDUND 3
68 #define FONT_OEM 4
69 #define FONT_OEMBOLD 5
70 #define FONT_OEMBOLDUND 6
71 #define FONT_OEMUND 7
72 static HFONT fonts[8];
73 static int font_needs_hand_underlining;
74 static enum {
75     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
76 } bold_mode;
77 static enum {
78     UND_LINE, UND_FONT
79 } und_mode;
80 static int descent;
81
82 #define NCOLOURS 24
83 static COLORREF colours[NCOLOURS];
84 static HPALETTE pal;
85 static LPLOGPALETTE logpal;
86 static RGBTRIPLE defpal[NCOLOURS];
87
88 static HWND hwnd;
89
90 static HBITMAP caretbm;
91
92 static int dbltime, lasttime, lastact;
93 static Mouse_Button lastbtn;
94
95 static char *window_name, *icon_name;
96
97 static Ldisc *real_ldisc;
98
99 void begin_session(void) {
100     ldisc = real_ldisc;
101 }
102
103 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
104     static char appname[] = "PuTTY";
105     WORD winsock_ver;
106     WSADATA wsadata;
107     WNDCLASS wndclass;
108     MSG msg;
109     int guess_width, guess_height;
110
111     hinst = inst;
112     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
113
114     winsock_ver = MAKEWORD(1, 1);
115     if (WSAStartup(winsock_ver, &wsadata)) {
116         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
117                    MB_OK | MB_ICONEXCLAMATION);
118         return 1;
119     }
120     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
121         MessageBox(NULL, "WinSock version is incompatible with 1.1",
122                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
123         WSACleanup();
124         return 1;
125     }
126     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
127
128     InitCommonControls();
129
130     /* Ensure a Maximize setting in Explorer doesn't maximise the
131      * config box. */
132     defuse_showwindow();
133
134     /*
135      * Process the command line.
136      */
137     {
138         char *p;
139
140         default_protocol = DEFAULT_PROTOCOL;
141         default_port = DEFAULT_PORT;
142
143         do_defaults(NULL, &cfg);
144
145         p = cmdline;
146         while (*p && isspace(*p)) p++;
147
148         /*
149          * Process command line options first. Yes, this can be
150          * done better, and it will be as soon as I have the
151          * energy...
152          */
153         while (*p == '-') {
154             char *q = p + strcspn(p, " \t");
155             p++;
156             if (q == p + 3 &&
157                 tolower(p[0]) == 's' &&
158                 tolower(p[1]) == 's' &&
159                 tolower(p[2]) == 'h') {
160                 default_protocol = cfg.protocol = PROT_SSH;
161                 default_port = cfg.port = 22;
162             } else if (q == p + 3 &&
163                 tolower(p[0]) == 'l' &&
164                 tolower(p[1]) == 'o' &&
165                 tolower(p[2]) == 'g') {
166                 logfile = "putty.log";
167             } else if (q == p + 7 &&
168                 tolower(p[0]) == 'c' &&
169                 tolower(p[1]) == 'l' &&
170                 tolower(p[2]) == 'e' &&
171                 tolower(p[3]) == 'a' &&
172                 tolower(p[4]) == 'n' &&
173                 tolower(p[5]) == 'u' &&
174                 tolower(p[6]) == 'p') {
175                 /*
176                  * `putty -cleanup'. Remove all registry entries
177                  * associated with PuTTY, and also find and delete
178                  * the random seed file.
179                  */
180                 if (MessageBox(NULL,
181                                "This procedure will remove ALL Registry\n"
182                                "entries associated with PuTTY, and will\n"
183                                "also remove the PuTTY random seed file.\n"
184                                "\n"
185                                "THIS PROCESS WILL DESTROY YOUR SAVED\n"
186                                "SESSIONS. Are you really sure you want\n"
187                                "to continue?",
188                                "PuTTY Warning",
189                                MB_YESNO | MB_ICONWARNING) == IDYES) {
190                     cleanup_all();
191                 }
192                 exit(0);
193             }
194             p = q + strspn(q, " \t");
195         }
196
197         /*
198          * An initial @ means to activate a saved session.
199          */
200         if (*p == '@') {
201             do_defaults (p+1, &cfg);
202             if (!*cfg.host && !do_config()) {
203                 WSACleanup();
204                 return 0;
205             }
206         } else if (*p == '&') {
207             /*
208              * An initial & means we've been given a command line
209              * containing the hex value of a HANDLE for a file
210              * mapping object, which we must then extract as a
211              * config.
212              */
213             HANDLE filemap;
214             Config *cp;
215             if (sscanf(p+1, "%p", &filemap) == 1 &&
216                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
217                                     0, 0, sizeof(Config))) != NULL) {
218                 cfg = *cp;
219                 UnmapViewOfFile(cp);
220                 CloseHandle(filemap);
221             } else if (!do_config()) {
222                 WSACleanup();
223                 return 0;
224             }
225         } else if (*p) {
226             char *q = p;
227             /*
228              * If the hostname starts with "telnet:", set the
229              * protocol to Telnet and process the string as a
230              * Telnet URL.
231              */
232             if (!strncmp(q, "telnet:", 7)) {
233                 char c;
234
235                 q += 7;
236                 if (q[0] == '/' && q[1] == '/')
237                     q += 2;
238                 cfg.protocol = PROT_TELNET;
239                 p = q;
240                 while (*p && *p != ':' && *p != '/') p++;
241                 c = *p;
242                 if (*p)
243                     *p++ = '\0';
244                 if (c == ':')
245                     cfg.port = atoi(p);
246                 else
247                     cfg.port = -1;
248                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
249                 cfg.host[sizeof(cfg.host)-1] = '\0';
250             } else {
251                 while (*p && !isspace(*p)) p++;
252                 if (*p)
253                     *p++ = '\0';
254                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
255                 cfg.host[sizeof(cfg.host)-1] = '\0';
256                 while (*p && isspace(*p)) p++;
257                 if (*p)
258                     cfg.port = atoi(p);
259                 else
260                     cfg.port = -1;
261             }
262         } else {
263             if (!do_config()) {
264                 WSACleanup();
265                 return 0;
266             }
267         }
268
269         /* See if host is of the form user@host */
270         if (cfg.host[0] != '\0') {
271             char *atsign = strchr(cfg.host, '@');
272             /* Make sure we're not overflowing the user field */
273             if (atsign) {
274                 if (atsign-cfg.host < sizeof cfg.username) {
275                     strncpy (cfg.username, cfg.host, atsign-cfg.host);
276                     cfg.username[atsign-cfg.host] = '\0';
277                 }
278                 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
279             }
280         }
281     }
282
283     /*
284      * Select protocol. This is farmed out into a table in a
285      * separate file to enable an ssh-free variant.
286      */
287     {
288         int i;
289         back = NULL;
290         for (i = 0; backends[i].backend != NULL; i++)
291             if (backends[i].protocol == cfg.protocol) {
292                 back = backends[i].backend;
293                 break;
294             }
295         if (back == NULL) {
296             MessageBox(NULL, "Unsupported protocol number found",
297                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
298             WSACleanup();
299             return 1;
300         }
301     }
302
303     real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
304     /* To start with, we use the simple line discipline, so we can
305      * type passwords etc without fear of them being echoed... */
306     ldisc = &ldisc_simple;
307
308     if (!prev) {
309         wndclass.style         = 0;
310         wndclass.lpfnWndProc   = WndProc;
311         wndclass.cbClsExtra    = 0;
312         wndclass.cbWndExtra    = 0;
313         wndclass.hInstance     = inst;
314         wndclass.hIcon         = LoadIcon (inst,
315                                            MAKEINTRESOURCE(IDI_MAINICON));
316         wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
317         wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
318         wndclass.lpszMenuName  = NULL;
319         wndclass.lpszClassName = appname;
320
321         RegisterClass (&wndclass);
322     }
323
324     hwnd = NULL;
325
326     savelines = cfg.savelines;
327     term_init();
328
329     cfgtopalette();
330
331     /*
332      * Guess some defaults for the window size. This all gets
333      * updated later, so we don't really care too much. However, we
334      * do want the font width/height guesses to correspond to a
335      * large font rather than a small one...
336      */
337     
338     font_width = 10;
339     font_height = 20;
340     extra_width = 25;
341     extra_height = 28;
342     term_size (cfg.height, cfg.width, cfg.savelines);
343     guess_width = extra_width + font_width * cols;
344     guess_height = extra_height + font_height * rows;
345     {
346         RECT r;
347         HWND w = GetDesktopWindow();
348         GetWindowRect (w, &r);
349         if (guess_width > r.right - r.left)
350             guess_width = r.right - r.left;
351         if (guess_height > r.bottom - r.top)
352             guess_height = r.bottom - r.top;
353     }
354
355     {
356        int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
357        if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
358        if (cfg.locksize)   winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
359        hwnd = CreateWindow (appname, appname,
360                             winmode,
361                             CW_USEDEFAULT, CW_USEDEFAULT,
362                             guess_width, guess_height,
363                             NULL, NULL, inst, NULL);
364     }
365
366     /*
367      * Initialise the fonts, simultaneously correcting the guesses
368      * for font_{width,height}.
369      */
370     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
371     und_mode = UND_FONT;
372     init_fonts(0);
373
374     /*
375      * Correct the guesses for extra_{width,height}.
376      */
377     {
378         RECT cr, wr;
379         GetWindowRect (hwnd, &wr);
380         GetClientRect (hwnd, &cr);
381         extra_width = wr.right - wr.left - cr.right + cr.left;
382         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
383     }
384
385     /*
386      * Resize the window, now we know what size we _really_ want it
387      * to be.
388      */
389     guess_width = extra_width + font_width * cols;
390     guess_height = extra_height + font_height * rows;
391     SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
392     SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
393                   SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
394
395     /*
396      * Set up a caret bitmap, with no content.
397      */
398     {
399         char *bits;
400         int size = (font_width+15)/16 * 2 * font_height; 
401         bits = calloc(size, 1);
402         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
403         free(bits);
404     }
405
406     /*
407      * Initialise the scroll bar.
408      */
409     {
410         SCROLLINFO si;
411
412         si.cbSize = sizeof(si);
413         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
414         si.nMin = 0;
415         si.nMax = rows-1;
416         si.nPage = rows;
417         si.nPos = 0;
418         SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
419     }
420
421     /*
422      * Start up the telnet connection.
423      */
424     {
425         char *error;
426         char msg[1024], *title;
427         char *realhost;
428
429         error = back->init (hwnd, cfg.host, cfg.port, &realhost);
430         if (error) {
431             sprintf(msg, "Unable to open connection:\n%s", error);
432             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
433             return 0;
434         }
435         window_name = icon_name = NULL;
436         if (*cfg.wintitle) {
437             title = cfg.wintitle;
438         } else {
439             sprintf(msg, "%s - PuTTY", realhost);
440             title = msg;
441         }
442         set_title (title);
443         set_icon (title);
444     }
445
446     session_closed = FALSE;
447
448     /*
449      * Set up the input and output buffers.
450      */
451     inbuf_head = 0;
452     outbuf_reap = outbuf_head = 0;
453
454     /*
455      * Prepare the mouse handler.
456      */
457     lastact = MA_NOTHING;
458     lastbtn = MB_NOTHING;
459     dbltime = GetDoubleClickTime();
460
461     /*
462      * Set up the session-control options on the system menu.
463      */
464     {
465         HMENU m = GetSystemMenu (hwnd, FALSE);
466         HMENU p,s;
467         int i;
468
469         AppendMenu (m, MF_SEPARATOR, 0, 0);
470         if (cfg.protocol == PROT_TELNET) {
471             p = CreateMenu();
472             AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
473             AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
474             AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
475             AppendMenu (p, MF_SEPARATOR, 0, 0);
476             AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
477             AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
478             AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
479             AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
480             AppendMenu (p, MF_SEPARATOR, 0, 0);
481             AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
482             AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
483             AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
484             AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
485             AppendMenu (p, MF_SEPARATOR, 0, 0);
486             AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
487             AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
488             AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
489             AppendMenu (m, MF_SEPARATOR, 0, 0);
490         }
491         AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
492         AppendMenu (m, MF_SEPARATOR, 0, 0);
493         AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
494         AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
495         s = CreateMenu();
496         get_sesslist(TRUE);
497         for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
498           AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
499         AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
500         AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
501         AppendMenu (m, MF_SEPARATOR, 0, 0);
502         AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
503         AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
504         AppendMenu (m, MF_SEPARATOR, 0, 0);
505         AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
506     }
507
508     /*
509      * Finally show the window!
510      */
511     ShowWindow (hwnd, show);
512
513     /*
514      * Set the palette up.
515      */
516     pal = NULL;
517     logpal = NULL;
518     init_palette();
519
520     has_focus = (GetForegroundWindow() == hwnd);
521     UpdateWindow (hwnd);
522
523     if (GetMessage (&msg, NULL, 0, 0) == 1)
524     {
525         int timer_id = 0, long_timer = 0;
526
527         while (msg.message != WM_QUIT) {
528             /* Sometimes DispatchMessage calls routines that use their own
529              * GetMessage loop, setup this timer so we get some control back.
530              *
531              * Also call term_update() from the timer so that if the host
532              * is sending data flat out we still do redraws.
533              */
534             if(timer_id && long_timer) {
535                 KillTimer(hwnd, timer_id);
536                 long_timer = timer_id = 0;
537             }
538             if(!timer_id)
539                 timer_id = SetTimer(hwnd, 1, 20, NULL);
540             DispatchMessage (&msg);
541
542             /* Make sure we blink everything that needs it. */
543             term_blink(0);
544
545             /* Send the paste buffer if there's anything to send */
546             term_paste();
547
548             /* If there's nothing new in the queue then we can do everything
549              * we've delayed, reading the socket, writing, and repainting
550              * the window.
551              */
552             if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
553                 continue;
554
555             if (pending_netevent) {
556                 enact_pending_netevent();
557
558                 /* Force the cursor blink on */
559                 term_blink(1);
560
561                 if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
562                     continue;
563             }
564
565             /* Okay there is now nothing to do so we make sure the screen is
566              * completely up to date then tell windows to call us in a little 
567              * while.
568              */
569             if (timer_id) {
570                 KillTimer(hwnd, timer_id);
571                 timer_id = 0;
572             }
573             HideCaret(hwnd);
574             if (inbuf_head)
575                 term_out();
576             term_update();
577             ShowCaret(hwnd);
578             if (!has_focus)
579                timer_id = SetTimer(hwnd, 1, 59500, NULL);
580             else
581                timer_id = SetTimer(hwnd, 1, 250, NULL);
582             long_timer = 1;
583         
584             /* There's no point rescanning everything in the message queue
585              * so we do an apperently unneccesary wait here 
586              */
587             WaitMessage();
588             if (GetMessage (&msg, NULL, 0, 0) != 1)
589                 break;
590         }
591     }
592
593     /*
594      * Clean up.
595      */
596     {
597         int i;
598         for (i=0; i<8; i++)
599             if (fonts[i])
600                 DeleteObject(fonts[i]);
601     }
602     sfree(logpal);
603     if (pal)
604         DeleteObject(pal);
605     WSACleanup();
606
607     if (cfg.protocol == PROT_SSH) {
608         random_save_seed();
609 #ifdef MSCRYPTOAPI
610         crypto_wrapup();
611 #endif
612     }
613
614     return msg.wParam;
615 }
616
617 /*
618  * Print a message box and close the connection.
619  */
620 void connection_fatal(char *fmt, ...) {
621     va_list ap;
622     char stuff[200];
623
624     va_start(ap, fmt);
625     vsprintf(stuff, fmt, ap);
626     va_end(ap);
627     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
628     if (cfg.close_on_exit)
629         PostQuitMessage(1);
630     else {
631         session_closed = TRUE;
632         SetWindowText (hwnd, "PuTTY (inactive)");
633     }
634 }
635
636 /*
637  * Actually do the job requested by a WM_NETEVENT
638  */
639 static void enact_pending_netevent(void) {
640     int i;
641     static int reentering = 0;
642
643     if (reentering)
644         return;                        /* don't unpend the pending */
645
646     pending_netevent = FALSE;
647
648     reentering = 1;
649     i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
650     reentering = 0;
651
652     if (i < 0) {
653         char buf[1024];
654         switch (WSABASEERR + (-i) % 10000) {
655           case WSAECONNRESET:
656             sprintf(buf, "Connection reset by peer");
657             break;
658           case WSAECONNABORTED:
659             sprintf(buf, "Connection aborted");
660             break;
661           default:
662             sprintf(buf, "Unexpected network error %d", -i);
663             break;
664         }
665         connection_fatal(buf);
666     }
667     if (i <= 0) {
668         if (cfg.close_on_exit)
669             PostQuitMessage(0);
670         else {
671             session_closed = TRUE;
672             MessageBox(hwnd, "Connection closed by remote host",
673                        "PuTTY", MB_OK | MB_ICONINFORMATION);
674             SetWindowText (hwnd, "PuTTY (inactive)");
675         }
676     }
677 }
678
679 /*
680  * Copy the colour palette from the configuration data into defpal.
681  * This is non-trivial because the colour indices are different.
682  */
683 static void cfgtopalette(void) {
684     int i;
685     static const int ww[] = {
686         6, 7, 8, 9, 10, 11, 12, 13,
687         14, 15, 16, 17, 18, 19, 20, 21,
688         0, 1, 2, 3, 4, 4, 5, 5
689     };
690
691     for (i=0; i<24; i++) {
692         int w = ww[i];
693         defpal[i].rgbtRed = cfg.colours[w][0];
694         defpal[i].rgbtGreen = cfg.colours[w][1];
695         defpal[i].rgbtBlue = cfg.colours[w][2];
696     }
697 }
698
699 /*
700  * Set up the colour palette.
701  */
702 static void init_palette(void) {
703     int i;
704     HDC hdc = GetDC (hwnd);
705     if (hdc) {
706         if (cfg.try_palette &&
707             GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
708             logpal = smalloc(sizeof(*logpal)
709                              - sizeof(logpal->palPalEntry)
710                              + NCOLOURS * sizeof(PALETTEENTRY));
711             logpal->palVersion = 0x300;
712             logpal->palNumEntries = NCOLOURS;
713             for (i = 0; i < NCOLOURS; i++) {
714                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
715                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
716                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
717                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
718             }
719             pal = CreatePalette (logpal);
720             if (pal) {
721                 SelectPalette (hdc, pal, FALSE);
722                 RealizePalette (hdc);
723                 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
724                                FALSE);
725             }
726         }
727         ReleaseDC (hwnd, hdc);
728     }
729     if (pal)
730         for (i=0; i<NCOLOURS; i++)
731             colours[i] = PALETTERGB(defpal[i].rgbtRed,
732                                     defpal[i].rgbtGreen,
733                                     defpal[i].rgbtBlue);
734     else
735         for(i=0; i<NCOLOURS; i++)
736             colours[i] = RGB(defpal[i].rgbtRed,
737                              defpal[i].rgbtGreen,
738                              defpal[i].rgbtBlue);
739 }
740
741 /*
742  * Initialise all the fonts we will need. There may be as many as
743  * eight or as few as one. We also:
744  *
745  * - check the font width and height, correcting our guesses if
746  *   necessary.
747  *
748  * - verify that the bold font is the same width as the ordinary
749  *   one, and engage shadow bolding if not.
750  * 
751  * - verify that the underlined font is the same width as the
752  *   ordinary one (manual underlining by means of line drawing can
753  *   be done in a pinch).
754  */
755 static void init_fonts(int pick_width) {
756     TEXTMETRIC tm;
757     int i;
758     int fsize[8];
759     HDC hdc;
760     int fw_dontcare, fw_bold;
761     int firstchar = ' ';
762
763 #ifdef CHECKOEMFONT
764 font_messup:
765 #endif
766     for (i=0; i<8; i++)
767         fonts[i] = NULL;
768
769     if (cfg.fontisbold) {
770         fw_dontcare = FW_BOLD;
771         fw_bold = FW_HEAVY;
772    } else {
773         fw_dontcare = FW_DONTCARE;
774         fw_bold = FW_BOLD;
775     }
776
777     hdc = GetDC(hwnd);
778
779     font_height = cfg.fontheight;
780     font_width = pick_width;
781
782 #define f(i,c,w,u) \
783     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
784                            c, OUT_DEFAULT_PRECIS, \
785                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
786                            FIXED_PITCH | FF_DONTCARE, cfg.font)
787
788     if (cfg.vtmode != VT_OEMONLY) {
789         f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
790
791         SelectObject (hdc, fonts[FONT_NORMAL]);
792         GetTextMetrics(hdc, &tm); 
793         font_height = tm.tmHeight;
794         font_width = tm.tmAveCharWidth;
795
796         f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
797
798         /*
799          * Some fonts, e.g. 9-pt Courier, draw their underlines
800          * outside their character cell. We successfully prevent
801          * screen corruption by clipping the text output, but then
802          * we lose the underline completely. Here we try to work
803          * out whether this is such a font, and if it is, we set a
804          * flag that causes underlines to be drawn by hand.
805          *
806          * Having tried other more sophisticated approaches (such
807          * as examining the TEXTMETRIC structure or requesting the
808          * height of a string), I think we'll do this the brute
809          * force way: we create a small bitmap, draw an underlined
810          * space on it, and test to see whether any pixels are
811          * foreground-coloured. (Since we expect the underline to
812          * go all the way across the character cell, we only search
813          * down a single column of the bitmap, half way across.)
814          */
815         {
816             HDC und_dc;
817             HBITMAP und_bm, und_oldbm;
818             int i, gotit;
819             COLORREF c;
820
821             und_dc = CreateCompatibleDC(hdc);
822             und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
823             und_oldbm = SelectObject(und_dc, und_bm);
824             SelectObject(und_dc, fonts[FONT_UNDERLINE]);
825             SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
826             SetTextColor (und_dc, RGB(255,255,255));
827             SetBkColor (und_dc, RGB(0,0,0));
828             SetBkMode (und_dc, OPAQUE);
829             ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
830             gotit = FALSE;
831             for (i = 0; i < font_height; i++) {
832                 c = GetPixel(und_dc, font_width/2, i);
833                 if (c != RGB(0,0,0))
834                     gotit = TRUE;
835             }
836             SelectObject(und_dc, und_oldbm);
837             DeleteObject(und_bm);
838             DeleteDC(und_dc);
839             font_needs_hand_underlining = !gotit;
840         }
841
842         if (bold_mode == BOLD_FONT) {
843             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
844             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
845         }
846
847         if (cfg.vtmode == VT_OEMANSI) {
848             f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
849             f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
850
851             if (bold_mode == BOLD_FONT) {
852                 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
853                 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
854             }
855         }
856     }
857     else
858     {
859         f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
860
861         SelectObject (hdc, fonts[FONT_OEM]);
862         GetTextMetrics(hdc, &tm); 
863         font_height = tm.tmHeight;
864         font_width = tm.tmAveCharWidth;
865
866         f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
867
868         if (bold_mode == BOLD_FONT) {
869             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
870             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
871         }
872     }
873 #undef f
874
875     descent = tm.tmAscent + 1;
876     if (descent >= font_height)
877         descent = font_height - 1;
878     firstchar = tm.tmFirstChar;
879
880     for (i=0; i<8; i++) {
881         if (fonts[i]) {
882             if (SelectObject (hdc, fonts[i]) &&
883                 GetTextMetrics(hdc, &tm) )
884                  fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
885             else fsize[i] = -i;
886         }
887         else fsize[i] = -i;
888     }
889
890     ReleaseDC (hwnd, hdc);
891
892     /* ... This is wrong in OEM only mode */
893     if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
894         (bold_mode == BOLD_FONT &&
895          fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
896         und_mode = UND_LINE;
897         DeleteObject (fonts[FONT_UNDERLINE]);
898         if (bold_mode == BOLD_FONT)
899             DeleteObject (fonts[FONT_BOLDUND]);
900     }
901
902     if (bold_mode == BOLD_FONT &&
903         fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
904         bold_mode = BOLD_SHADOW;
905         DeleteObject (fonts[FONT_BOLD]);
906         if (und_mode == UND_FONT)
907             DeleteObject (fonts[FONT_BOLDUND]);
908     }
909
910 #ifdef CHECKOEMFONT
911     /* With the fascist font painting it doesn't matter if the linedraw font
912      * isn't exactly the right size anymore so we don't have to check this.
913      */
914     if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
915         if( cfg.fontcharset == OEM_CHARSET )
916         {
917             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
918                    "different sizes. Using OEM-only mode instead",
919                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
920             cfg.vtmode = VT_OEMONLY;
921         }
922         else if( firstchar < ' ' )
923         {
924             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
925                    "different sizes. Using XTerm mode instead",
926                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
927             cfg.vtmode = VT_XWINDOWS;
928         }
929         else
930         {
931             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
932                    "different sizes. Using ISO8859-1 mode instead",
933                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
934             cfg.vtmode = VT_POORMAN;
935         }
936
937         for (i=0; i<8; i++)
938             if (fonts[i])
939                 DeleteObject (fonts[i]);
940         goto font_messup;
941     }
942 #endif
943 }
944
945 void request_resize (int w, int h, int refont) {
946     int width, height;
947
948     /* If the window is maximized supress resizing attempts */
949     if(IsZoomed(hwnd)) return;
950     
951 #ifdef CHECKOEMFONT
952     /* Don't do this in OEMANSI, you may get disable messages */
953     if (refont && w != cols && (cols==80 || cols==132)
954           && cfg.vtmode != VT_OEMANSI)
955 #else
956     if (refont && w != cols && (cols==80 || cols==132))
957 #endif
958     {
959        /* If font width too big for screen should we shrink the font more ? */
960         if (w==132)
961             font_width = ((font_width*cols+w/2)/w);
962         else
963             font_width = 0;
964         {
965             int i;
966             for (i=0; i<8; i++)
967                 if (fonts[i])
968                     DeleteObject(fonts[i]);
969         }
970         bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
971         und_mode = UND_FONT;
972         init_fonts(font_width);
973     }
974     else
975     {
976        static int first_time = 1;
977        static RECT ss;
978
979        switch(first_time)
980        {
981        case 1:
982              /* Get the size of the screen */
983              if (GetClientRect(GetDesktopWindow(),&ss))
984                 /* first_time = 0 */;
985              else { first_time = 2; break; }
986        case 0:
987              /* Make sure the values are sane */
988              width  = (ss.right-ss.left-extra_width  ) / font_width;
989              height = (ss.bottom-ss.top-extra_height ) / font_height;
990
991              if (w>width)  w=width;
992              if (h>height) h=height;
993              if (w<15) w = 15;
994              if (h<1) w = 1;
995        }
996     }
997
998     width = extra_width + font_width * w;
999     height = extra_height + font_height * h;
1000
1001     SetWindowPos (hwnd, NULL, 0, 0, width, height,
1002                   SWP_NOACTIVATE | SWP_NOCOPYBITS |
1003                   SWP_NOMOVE | SWP_NOZORDER);
1004 }
1005
1006 static void click (Mouse_Button b, int x, int y) {
1007     int thistime = GetMessageTime();
1008
1009     if (lastbtn == b && thistime - lasttime < dbltime) {
1010         lastact = (lastact == MA_CLICK ? MA_2CLK :
1011                    lastact == MA_2CLK ? MA_3CLK :
1012                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1013     } else {
1014         lastbtn = b;
1015         lastact = MA_CLICK;
1016     }
1017     if (lastact != MA_NOTHING)
1018         term_mouse (b, lastact, x, y);
1019     lasttime = thistime;
1020 }
1021
1022 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
1023                                  WPARAM wParam, LPARAM lParam) {
1024     HDC hdc;
1025     static int ignore_size = FALSE;
1026     static int ignore_clip = FALSE;
1027     static int just_reconfigged = FALSE;
1028     static int resizing = FALSE;
1029
1030     switch (message) {
1031       case WM_TIMER:
1032         if (pending_netevent)
1033             enact_pending_netevent();
1034         if (inbuf_head)
1035             term_out();
1036         HideCaret(hwnd);
1037         term_update();
1038         ShowCaret(hwnd);
1039         if (cfg.ping_interval > 0)
1040         {
1041            time_t now;
1042            time(&now);
1043            if (now-last_movement > cfg.ping_interval * 60 - 10)
1044            {
1045               back->special(TS_PING);
1046               last_movement = now;
1047            }
1048         }
1049         return 0;
1050       case WM_CREATE:
1051         break;
1052       case WM_CLOSE:
1053         if (!cfg.warn_on_close || session_closed ||
1054             MessageBox(hwnd, "Are you sure you want to close this session?",
1055                        "PuTTY Exit Confirmation",
1056                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1057             DestroyWindow(hwnd);
1058         return 0;
1059       case WM_DESTROY:
1060         PostQuitMessage (0);
1061         return 0;
1062       case WM_SYSCOMMAND:
1063         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1064           case IDM_SHOWLOG:
1065             showeventlog(hwnd);
1066             break;
1067           case IDM_NEWSESS:
1068           case IDM_DUPSESS:
1069           case IDM_SAVEDSESS:
1070             {
1071                 char b[2048];
1072                 char c[30], *cl;
1073                 int freecl = FALSE;
1074                 STARTUPINFO si;
1075                 PROCESS_INFORMATION pi;
1076                 HANDLE filemap = NULL;
1077
1078                 if (wParam == IDM_DUPSESS) {
1079                     /*
1080                      * Allocate a file-mapping memory chunk for the
1081                      * config structure.
1082                      */
1083                     SECURITY_ATTRIBUTES sa;
1084                     Config *p;
1085
1086                     sa.nLength = sizeof(sa);
1087                     sa.lpSecurityDescriptor = NULL;
1088                     sa.bInheritHandle = TRUE;
1089                     filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1090                                                 &sa,
1091                                                 PAGE_READWRITE,
1092                                                 0,
1093                                                 sizeof(Config),
1094                                                 NULL);
1095                     if (filemap) {
1096                         p = (Config *)MapViewOfFile(filemap,
1097                                                     FILE_MAP_WRITE,
1098                                                     0, 0, sizeof(Config));
1099                         if (p) {
1100                             *p = cfg;  /* structure copy */
1101                             UnmapViewOfFile(p);
1102                         }
1103                     }
1104                     sprintf(c, "putty &%p", filemap);
1105                     cl = c;
1106                 } else if (wParam == IDM_SAVEDSESS) {
1107                     char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1108                     cl = malloc(16 + strlen(session)); /* 8, but play safe */
1109                     if (!cl)
1110                         cl = NULL;     /* not a very important failure mode */
1111                     else {
1112                         sprintf(cl, "putty @%s", session);
1113                         freecl = TRUE;
1114                     }
1115                 } else
1116                     cl = NULL;
1117
1118                 GetModuleFileName (NULL, b, sizeof(b)-1);
1119                 si.cb = sizeof(si);
1120                 si.lpReserved = NULL;
1121                 si.lpDesktop = NULL;
1122                 si.lpTitle = NULL;
1123                 si.dwFlags = 0;
1124                 si.cbReserved2 = 0;
1125                 si.lpReserved2 = NULL;
1126                 CreateProcess (b, cl, NULL, NULL, TRUE,
1127                                NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1128
1129                 if (filemap)
1130                     CloseHandle(filemap);
1131                 if (freecl)
1132                     free(cl);
1133             }
1134             break;
1135           case IDM_RECONF:
1136             if (!do_reconfig(hwnd))
1137                 break;
1138             just_reconfigged = TRUE;
1139             {
1140                 int i;
1141                 for (i=0; i<8; i++)
1142                     if (fonts[i])
1143                         DeleteObject(fonts[i]);
1144             }
1145             bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1146             und_mode = UND_FONT;
1147             init_fonts(0);
1148             sfree(logpal);
1149             /* Telnet will change local echo -> remote if the remote asks */
1150             if (cfg.protocol != PROT_TELNET)
1151                 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1152             if (pal)
1153                 DeleteObject(pal);
1154             logpal = NULL;
1155             pal = NULL;
1156             cfgtopalette();
1157             init_palette();
1158
1159             /* Enable or disable the scroll bar, etc */
1160             {
1161                 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1162
1163                 nflg = flag;
1164                 if (cfg.scrollbar) nflg |=  WS_VSCROLL;
1165                 else               nflg &= ~WS_VSCROLL;
1166                 if (cfg.locksize) 
1167                    nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1168                 else              
1169                    nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1170
1171                 if (nflg != flag)
1172                 {
1173                     RECT cr, wr;
1174
1175                     SetWindowLong(hwnd, GWL_STYLE, nflg);
1176                     SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1177                     SetWindowPos(hwnd, NULL, 0,0,0,0,
1178                          SWP_NOACTIVATE|SWP_NOCOPYBITS|
1179                          SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1180                          SWP_FRAMECHANGED);
1181
1182                     GetWindowRect (hwnd, &wr);
1183                     GetClientRect (hwnd, &cr);
1184                     extra_width = wr.right - wr.left - cr.right + cr.left;
1185                     extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1186                 }
1187             }
1188
1189             term_size(cfg.height, cfg.width, cfg.savelines);
1190             InvalidateRect(hwnd, NULL, TRUE);
1191             SetWindowPos (hwnd, NULL, 0, 0,
1192                           extra_width + font_width * cfg.width,
1193                           extra_height + font_height * cfg.height,
1194                           SWP_NOACTIVATE | SWP_NOCOPYBITS |
1195                           SWP_NOMOVE | SWP_NOZORDER);
1196             if (IsIconic(hwnd)) {
1197                 SetWindowText (hwnd,
1198                                cfg.win_name_always ? window_name : icon_name);
1199             }
1200             break;
1201           case IDM_CLRSB:
1202             term_clrsb();
1203             break;
1204           case IDM_RESET:
1205             term_pwron();
1206             break;
1207           case IDM_TEL_AYT: back->special (TS_AYT); break;
1208           case IDM_TEL_BRK: back->special (TS_BRK); break;
1209           case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1210           case IDM_TEL_EC: back->special (TS_EC); break;
1211           case IDM_TEL_EL: back->special (TS_EL); break;
1212           case IDM_TEL_GA: back->special (TS_GA); break;
1213           case IDM_TEL_NOP: back->special (TS_NOP); break;
1214           case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1215           case IDM_TEL_AO: back->special (TS_AO); break;
1216           case IDM_TEL_IP: back->special (TS_IP); break;
1217           case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1218           case IDM_TEL_EOR: back->special (TS_EOR); break;
1219           case IDM_TEL_EOF: back->special (TS_EOF); break;
1220           case IDM_ABOUT:
1221             showabout (hwnd);
1222             break;
1223         default:
1224           if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1225             SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1226           }
1227         }
1228         break;
1229
1230 #define X_POS(l) ((int)(short)LOWORD(l))
1231 #define Y_POS(l) ((int)(short)HIWORD(l))
1232
1233 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1234 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1235
1236       case WM_LBUTTONDOWN:
1237         click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1238                TO_CHR_Y(Y_POS(lParam)));
1239         SetCapture(hwnd);
1240         return 0;
1241       case WM_LBUTTONUP:
1242         term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1243                     TO_CHR_Y(Y_POS(lParam)));
1244         ReleaseCapture();
1245         return 0;
1246       case WM_MBUTTONDOWN:
1247         SetCapture(hwnd);
1248         click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1249                TO_CHR_X(X_POS(lParam)),
1250                TO_CHR_Y(Y_POS(lParam)));
1251         return 0;
1252       case WM_MBUTTONUP:
1253         term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1254                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1255                     TO_CHR_Y(Y_POS(lParam)));
1256         ReleaseCapture();
1257         return 0;
1258       case WM_RBUTTONDOWN:
1259         SetCapture(hwnd);
1260         click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1261                TO_CHR_X(X_POS(lParam)),
1262                TO_CHR_Y(Y_POS(lParam)));
1263         return 0;
1264       case WM_RBUTTONUP:
1265         term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1266                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1267                     TO_CHR_Y(Y_POS(lParam)));
1268         ReleaseCapture();
1269         return 0;
1270       case WM_MOUSEMOVE:
1271         /*
1272          * Add the mouse position and message time to the random
1273          * number noise, if we're using ssh.
1274          */
1275         if (cfg.protocol == PROT_SSH)
1276             noise_ultralight(lParam);
1277
1278         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1279             Mouse_Button b;
1280             if (wParam & MK_LBUTTON)
1281                 b = MB_SELECT;
1282             else if (wParam & MK_MBUTTON)
1283                 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1284             else
1285                 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1286             term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1287                         TO_CHR_Y(Y_POS(lParam)));
1288         }
1289         return 0;
1290       case WM_IGNORE_CLIP:
1291         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1292         break;
1293       case WM_DESTROYCLIPBOARD:
1294         if (!ignore_clip)
1295             term_deselect();
1296         ignore_clip = FALSE;
1297         return 0;
1298       case WM_PAINT:
1299         {
1300             PAINTSTRUCT p;
1301             HideCaret(hwnd);
1302             hdc = BeginPaint (hwnd, &p);
1303             if (pal) {
1304                 SelectPalette (hdc, pal, TRUE);
1305                 RealizePalette (hdc);
1306             }
1307             term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1308                         p.rcPaint.right, p.rcPaint.bottom);
1309             SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1310             SelectObject (hdc, GetStockObject(WHITE_PEN));
1311             EndPaint (hwnd, &p);
1312             ShowCaret(hwnd);
1313         }
1314         return 0;
1315       case WM_NETEVENT:
1316         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1317          * but the only one that's likely to try to overload us is FD_READ.
1318          * This means buffering just one is fine.
1319          */
1320         if (pending_netevent)
1321             enact_pending_netevent();
1322
1323         pending_netevent = TRUE;
1324         pend_netevent_wParam=wParam;
1325         pend_netevent_lParam=lParam;
1326         time(&last_movement);
1327         return 0;
1328       case WM_SETFOCUS:
1329         has_focus = TRUE;
1330         CreateCaret(hwnd, caretbm, 0, 0);
1331         ShowCaret(hwnd);
1332         term_out();
1333         term_update();
1334         break;
1335       case WM_KILLFOCUS:
1336         has_focus = FALSE;
1337         DestroyCaret();
1338         term_out();
1339         term_update();
1340         break;
1341       case WM_IGNORE_SIZE:
1342         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1343         break;
1344       case WM_ENTERSIZEMOVE:
1345         EnableSizeTip(1);
1346         resizing = TRUE;
1347         break;
1348       case WM_EXITSIZEMOVE:
1349         EnableSizeTip(0);
1350         resizing = FALSE;
1351         back->size();
1352         break;
1353       case WM_SIZING:
1354         {
1355             int width, height, w, h, ew, eh;
1356             LPRECT r = (LPRECT)lParam;
1357
1358             width = r->right - r->left - extra_width;
1359             height = r->bottom - r->top - extra_height;
1360             w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1361             h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1362             UpdateSizeTip(hwnd, w, h);
1363             ew = width - w * font_width;
1364             eh = height - h * font_height;
1365             if (ew != 0) {
1366                 if (wParam == WMSZ_LEFT ||
1367                     wParam == WMSZ_BOTTOMLEFT ||
1368                     wParam == WMSZ_TOPLEFT)
1369                     r->left += ew;
1370                 else
1371                     r->right -= ew;
1372             }
1373             if (eh != 0) {
1374                 if (wParam == WMSZ_TOP ||
1375                     wParam == WMSZ_TOPRIGHT ||
1376                     wParam == WMSZ_TOPLEFT)
1377                     r->top += eh;
1378                 else
1379                     r->bottom -= eh;
1380             }
1381             if (ew || eh)
1382                 return 1;
1383             else
1384                 return 0;
1385         }
1386         /* break;  (never reached) */
1387       case WM_SIZE:
1388         if (wParam == SIZE_MINIMIZED) {
1389             SetWindowText (hwnd,
1390                            cfg.win_name_always ? window_name : icon_name);
1391             break;
1392         }
1393         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1394             SetWindowText (hwnd, window_name);
1395         if (!ignore_size) {
1396             int width, height, w, h;
1397 #if 0 /* we have fixed this using WM_SIZING now */
1398             int ew, eh;
1399 #endif
1400
1401             width = LOWORD(lParam);
1402             height = HIWORD(lParam);
1403             w = width / font_width; if (w < 1) w = 1;
1404             h = height / font_height; if (h < 1) h = 1;
1405 #if 0 /* we have fixed this using WM_SIZING now */
1406             ew = width - w * font_width;
1407             eh = height - h * font_height;
1408             if (ew != 0 || eh != 0) {
1409                 RECT r;
1410                 GetWindowRect (hwnd, &r);
1411                 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1412                 SetWindowPos (hwnd, NULL, 0, 0,
1413                               r.right - r.left - ew, r.bottom - r.top - eh,
1414                               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1415             }
1416 #endif
1417             if (w != cols || h != rows || just_reconfigged) {
1418                 term_invalidate();
1419                 term_size (h, w, cfg.savelines);
1420                 /*
1421                  * Don't call back->size in mid-resize. (To prevent
1422                  * massive numbers of resize events getting sent
1423                  * down the connection during an NT opaque drag.)
1424                  */
1425                 if (!resizing)
1426                     back->size();
1427                 just_reconfigged = FALSE;
1428             }
1429         }
1430         ignore_size = FALSE;
1431         return 0;
1432       case WM_VSCROLL:
1433         switch (LOWORD(wParam)) {
1434           case SB_BOTTOM: term_scroll(-1, 0); break;
1435           case SB_TOP: term_scroll(+1, 0); break;
1436           case SB_LINEDOWN: term_scroll (0, +1); break;
1437           case SB_LINEUP: term_scroll (0, -1); break;
1438           case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1439           case SB_PAGEUP: term_scroll (0, -rows/2); break;
1440           case SB_THUMBPOSITION: case SB_THUMBTRACK:
1441             term_scroll (1, HIWORD(wParam)); break;
1442         }
1443         break; 
1444      case WM_PALETTECHANGED:
1445         if ((HWND) wParam != hwnd && pal != NULL) {
1446             HDC hdc = get_ctx();
1447             if (hdc) {
1448                 if (RealizePalette (hdc) > 0)
1449                     UpdateColors (hdc);
1450                 free_ctx (hdc);
1451             }
1452         }
1453         break;
1454       case WM_QUERYNEWPALETTE:
1455         if (pal != NULL) {
1456             HDC hdc = get_ctx();
1457             if (hdc) {
1458                 if (RealizePalette (hdc) > 0)
1459                     UpdateColors (hdc);
1460                 free_ctx (hdc);
1461                 return TRUE;
1462             }
1463         }
1464         return FALSE;
1465       case WM_KEYDOWN:
1466       case WM_SYSKEYDOWN:
1467       case WM_KEYUP:
1468       case WM_SYSKEYUP:
1469         /*
1470          * Add the scan code and keypress timing to the random
1471          * number noise, if we're using ssh.
1472          */
1473         if (cfg.protocol == PROT_SSH)
1474             noise_ultralight(lParam);
1475
1476         /*
1477          * We don't do TranslateMessage since it disassociates the
1478          * resulting CHAR message from the KEYDOWN that sparked it,
1479          * which we occasionally don't want. Instead, we process
1480          * KEYDOWN, and call the Win32 translator functions so that
1481          * we get the translations under _our_ control.
1482          */
1483         {
1484             unsigned char buf[20];
1485             int len;
1486
1487             len = TranslateKey (message, wParam, lParam, buf);
1488             if (len == -1)
1489                 return DefWindowProc (hwnd, message, wParam, lParam);
1490             ldisc->send (buf, len);
1491         }
1492         return 0;
1493       case WM_CHAR:
1494       case WM_SYSCHAR:
1495         /*
1496          * Nevertheless, we are prepared to deal with WM_CHAR
1497          * messages, should they crop up. So if someone wants to
1498          * post the things to us as part of a macro manoeuvre,
1499          * we're ready to cope.
1500          */
1501         {
1502             char c = xlat_kbd2tty((unsigned char)wParam);
1503             ldisc->send (&c, 1);
1504         }
1505         return 0;
1506     }
1507
1508     return DefWindowProc (hwnd, message, wParam, lParam);
1509 }
1510
1511 /*
1512  * Move the system caret. (We maintain one, even though it's
1513  * invisible, for the benefit of blind people: apparently some
1514  * helper software tracks the system caret, so we should arrange to
1515  * have one.)
1516  */
1517 void sys_cursor(int x, int y) {
1518     SetCaretPos(x * font_width, y * font_height);
1519 }
1520
1521 /*
1522  * Draw a line of text in the window, at given character
1523  * coordinates, in given attributes.
1524  *
1525  * We are allowed to fiddle with the contents of `text'.
1526  */
1527 void do_text (Context ctx, int x, int y, char *text, int len,
1528               unsigned long attr, int lattr) {
1529     COLORREF fg, bg, t;
1530     int nfg, nbg, nfont;
1531     HDC hdc = ctx;
1532     RECT line_box;
1533     int force_manual_underline = 0;
1534     int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1535     static int *IpDx = 0, IpDxLEN = 0;;
1536
1537     if (len>IpDxLEN || IpDx[0] != fnt_width) {
1538         int i;
1539         if (len>IpDxLEN) {
1540             sfree(IpDx);
1541             IpDx = smalloc((len+16)*sizeof(int));
1542             IpDxLEN = (len+16);
1543         }
1544         for(i=0; i<IpDxLEN; i++)
1545             IpDx[i] = fnt_width;
1546     }
1547
1548     x *= fnt_width;
1549     y *= font_height;
1550
1551     if (attr & ATTR_ACTCURS) {
1552         attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1553         attr ^= ATTR_CUR_XOR;
1554     }
1555
1556     nfont = 0;
1557     if (cfg.vtmode == VT_OEMONLY)
1558         nfont |= FONT_OEM;
1559
1560     /*
1561      * Map high-half characters in order to approximate ISO using
1562      * OEM character set. No characters are missing if the OEM codepage
1563      * is CP850.
1564      */
1565     if (nfont & FONT_OEM) {
1566         int i;
1567         for (i=0; i<len; i++)
1568             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1569 #if 0
1570                 /* This is CP850 ... perfect translation */
1571                 static const char oemhighhalf[] =
1572                     "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1573                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1574                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1575                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1576                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1577                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1578                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1579                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1580                     "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1581                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1582                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1583                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1584                     ;
1585 #endif
1586                 /* This is CP437 ... junk translation */
1587                 static const unsigned char oemhighhalf[] = {
1588                     0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1589                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1590                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1591                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1592                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1593                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1594                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1595                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1596                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1597                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1598                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1599                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1600                 };
1601
1602                 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1603             }
1604     }
1605
1606     if (attr & ATTR_LINEDRW) {
1607         int i;
1608         /* ISO 8859-1 */
1609         static const char poorman[] =
1610             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1611
1612         /* CP437 */
1613         static const char oemmap_437[] =
1614             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1615             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1616
1617         /* CP850 */
1618         static const char oemmap_850[] =
1619             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1620             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1621
1622         /* Poor windows font ... eg: windows courier */
1623         static const char oemmap[] =
1624             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1625             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1626
1627         /*
1628          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1629          * VT100 line drawing chars; everything else stays normal.
1630          *
1631          * Actually '_' maps to space too, but that's done before.
1632          */
1633         switch (cfg.vtmode) {
1634           case VT_XWINDOWS:
1635             for (i=0; i<len; i++)
1636                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1637                     text[i] += '\x01' - '\x60';
1638             break;
1639           case VT_OEMANSI:
1640             /* Make sure we actually have an OEM font */
1641             if (fonts[nfont|FONT_OEM]) { 
1642           case VT_OEMONLY:
1643                 nfont |= FONT_OEM;
1644                 for (i=0; i<len; i++)
1645                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1646                         text[i] = oemmap[(unsigned char)text[i] - 0x60];
1647                 break;
1648             }
1649           case VT_POORMAN:
1650             for (i=0; i<len; i++)
1651                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1652                     text[i] = poorman[(unsigned char)text[i] - 0x60];
1653             break;
1654         }
1655     }
1656
1657     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1658     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1659     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1660         nfont |= FONT_BOLD;
1661     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1662         nfont |= FONT_UNDERLINE;
1663     if (!fonts[nfont]) 
1664     {
1665         if (nfont&FONT_UNDERLINE)
1666             force_manual_underline = 1;
1667         /* Don't do the same for manual bold, it could be bad news. */
1668
1669         nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1670     }
1671     if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1672         force_manual_underline = 1;
1673     if (attr & ATTR_REVERSE) {
1674         t = nfg; nfg = nbg; nbg = t;
1675     }
1676     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1677         nfg++;
1678     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1679         nbg++;
1680     fg = colours[nfg];
1681     bg = colours[nbg];
1682     SelectObject (hdc, fonts[nfont]);
1683     SetTextColor (hdc, fg);
1684     SetBkColor (hdc, bg);
1685     SetBkMode (hdc, OPAQUE);
1686     line_box.left   = x;
1687     line_box.top    = y;
1688     line_box.right  = x+fnt_width*len;
1689     line_box.bottom = y+font_height;
1690     ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1691     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1692         SetBkMode (hdc, TRANSPARENT);
1693
1694        /* GRR: This draws the character outside it's box and can leave
1695         * 'droppings' even with the clip box! I suppose I could loop it
1696         * one character at a time ... yuk. 
1697         * 
1698         * Or ... I could do a test print with "W", and use +1 or -1 for this
1699         * shift depending on if the leftmost column is blank...
1700         */
1701         ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1702     }
1703     if (force_manual_underline || 
1704             (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1705         HPEN oldpen;
1706         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1707         MoveToEx (hdc, x, y+descent, NULL);
1708         LineTo (hdc, x+len*fnt_width, y+descent);
1709         oldpen = SelectObject (hdc, oldpen);
1710         DeleteObject (oldpen);
1711     }
1712     if (attr & ATTR_PASCURS) {
1713         POINT pts[5];
1714         HPEN oldpen;
1715         pts[0].x = pts[1].x = pts[4].x = x;
1716         pts[2].x = pts[3].x = x+fnt_width-1;
1717         pts[0].y = pts[3].y = pts[4].y = y;
1718         pts[1].y = pts[2].y = y+font_height-1;
1719         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1720         Polyline (hdc, pts, 5);
1721         oldpen = SelectObject (hdc, oldpen);
1722         DeleteObject (oldpen);
1723     }
1724 }
1725
1726 static int check_compose(int first, int second) {
1727
1728     static char * composetbl[] = {
1729        "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1730        "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1731        "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1732        "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1733        "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1734        "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1735        "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1736        "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1737        "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1738        "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1739        "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1740        "\"uü", "'yý", "htþ", "\"yÿ",
1741     0};
1742
1743     char ** c;
1744     static int recurse = 0;
1745     int nc = -1;
1746
1747     if(0)
1748     {
1749         char buf[256];
1750         char * p;
1751         sprintf(buf, "cc(%d,%d)", first, second);
1752         for(p=buf; *p; p++)
1753             c_write1(*p);
1754     }
1755
1756     for(c=composetbl; *c; c++) {
1757         if( (*c)[0] == first && (*c)[1] == second)
1758         {
1759             return (*c)[2] & 0xFF;
1760         }
1761     }
1762
1763     if(recurse==0)
1764     {
1765         recurse=1;
1766         nc = check_compose(second, first);
1767         if(nc == -1)
1768             nc = check_compose(toupper(first), toupper(second));
1769         if(nc == -1)
1770             nc = check_compose(toupper(second), toupper(first));
1771         recurse=0;
1772     }
1773     return nc;
1774 }
1775
1776
1777 /*
1778  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1779  * codes. Returns number of bytes used or zero to drop the message
1780  * or -1 to forward the message to windows.
1781  */
1782 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1783     BYTE keystate[256];
1784     int  scan, left_alt = 0, key_down, shift_state;
1785     int  r, i, code;
1786     unsigned char * p = output;
1787
1788     static WORD keys[3];
1789     static int compose_state = 0;
1790     static int compose_char = 0;
1791     static WPARAM compose_key = 0;
1792
1793     r = GetKeyboardState(keystate);
1794     if (!r) memset(keystate, 0, sizeof(keystate));
1795     else
1796     {
1797 #if 0
1798        {  /* Tell us all about key events */
1799           static BYTE oldstate[256];
1800           static int first = 1;
1801           static int scan;
1802           int ch;
1803           if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1804           first=0;
1805
1806           if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1807              debug(("+"));
1808           } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1809              debug((". U"));
1810           } else {
1811              debug((".\n"));
1812              if (wParam >= VK_F1 && wParam <= VK_F20 )
1813                 debug(("K_F%d", wParam+1-VK_F1));
1814              else switch(wParam)
1815              {
1816              case VK_SHIFT:   debug(("SHIFT")); break;
1817              case VK_CONTROL: debug(("CTRL")); break;
1818              case VK_MENU:    debug(("ALT")); break;
1819              default:         debug(("VK_%02x", wParam));
1820              }
1821              if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1822                 debug(("*"));
1823              debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1824
1825              ch = MapVirtualKey(wParam, 2);
1826              if (ch>=' ' && ch<='~') debug((", '%c'", ch));
1827              else if (ch)            debug((", $%02x", ch));
1828
1829              if (keys[0]) debug((", KB0=%02x", keys[0]));
1830              if (keys[1]) debug((", KB1=%02x", keys[1]));
1831              if (keys[2]) debug((", KB2=%02x", keys[2]));
1832
1833              if ( (keystate[VK_SHIFT]&0x80)!=0)     debug((", S"));
1834              if ( (keystate[VK_CONTROL]&0x80)!=0)   debug((", C"));
1835              if ( (HIWORD(lParam)&KF_EXTENDED) )    debug((", E"));
1836              if ( (HIWORD(lParam)&KF_UP) )          debug((", U"));
1837           }
1838
1839           if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1840              ;
1841           else if ( (HIWORD(lParam)&KF_UP) ) 
1842              oldstate[wParam&0xFF] ^= 0x80;
1843           else
1844              oldstate[wParam&0xFF] ^= 0x81;
1845
1846           for(ch=0; ch<256; ch++)
1847              if (oldstate[ch] != keystate[ch])
1848                 debug((", M%02x=%02x", ch, keystate[ch]));
1849
1850           memcpy(oldstate, keystate, sizeof(oldstate));
1851        }
1852 #endif
1853
1854         /* Note if AltGr was pressed and if it was used as a compose key */
1855         if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1856         {
1857             keystate[VK_RMENU] = keystate[VK_MENU];
1858             if (!compose_state) compose_key = wParam;
1859         }
1860         if (wParam == VK_APPS && !compose_state)
1861             compose_key = wParam;
1862
1863         if (wParam == compose_key)
1864         {
1865             if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1866                compose_state = 1;
1867             else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1868                compose_state = 2;
1869             else
1870                compose_state = 0;
1871         }
1872         else if (compose_state==1 && wParam != VK_CONTROL)
1873            compose_state = 0;
1874
1875         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1876         if ( (cfg.funky_type == 3 || (cfg.funky_type <= 1 && app_keypad_keys))
1877               && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1878
1879             wParam = VK_EXECUTE;
1880
1881             /* UnToggle NUMLock */
1882             if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1883                 keystate[VK_NUMLOCK] ^= 1;
1884         }
1885
1886         /* And write back the 'adjusted' state */
1887         SetKeyboardState (keystate);
1888     }
1889
1890     /* Disable Auto repeat if required */
1891     if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1892        return 0;
1893
1894     if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1895         left_alt = 1;
1896
1897     key_down = ((HIWORD(lParam)&KF_UP)==0);
1898
1899     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1900     if (left_alt && (keystate[VK_CONTROL]&0x80))
1901         keystate[VK_MENU] = 0;
1902
1903     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1904     shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1905                 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1906
1907     /* 
1908      * Record that we pressed key so the scroll window can be reset, but
1909      * be careful to avoid Shift-UP/Down
1910      */
1911     if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1912         seen_key_event = 1; 
1913     }
1914
1915     /* Make sure we're not pasting */
1916     if (key_down) term_nopaste();
1917
1918     if (compose_state>1 && left_alt) compose_state = 0;
1919
1920     /* Sanitize the number pad if not using a PC NumPad */
1921     if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1922         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
1923     {
1924         if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1925         {
1926             int nParam = 0;
1927             switch(wParam)
1928             {
1929             case VK_INSERT:     nParam = VK_NUMPAD0; break;
1930             case VK_END:        nParam = VK_NUMPAD1; break;
1931             case VK_DOWN:       nParam = VK_NUMPAD2; break;
1932             case VK_NEXT:       nParam = VK_NUMPAD3; break;
1933             case VK_LEFT:       nParam = VK_NUMPAD4; break;
1934             case VK_CLEAR:      nParam = VK_NUMPAD5; break;
1935             case VK_RIGHT:      nParam = VK_NUMPAD6; break;
1936             case VK_HOME:       nParam = VK_NUMPAD7; break;
1937             case VK_UP:         nParam = VK_NUMPAD8; break;
1938             case VK_PRIOR:      nParam = VK_NUMPAD9; break;
1939             case VK_DELETE:     nParam = VK_DECIMAL; break;
1940             }
1941             if (nParam)
1942             {
1943                 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1944                 wParam = nParam;
1945             }
1946         }
1947     }
1948
1949     /* If a key is pressed and AltGr is not active */
1950     if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1951     {
1952         /* Okay, prepare for most alts then ...*/
1953         if (left_alt) *p++ = '\033';
1954
1955         /* Lets see if it's a pattern we know all about ... */
1956         if (wParam == VK_PRIOR && shift_state == 1) {
1957             SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1958             return 0;
1959         }
1960         if (wParam == VK_NEXT && shift_state == 1) {
1961             SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1962             return 0;
1963         }
1964         if (wParam == VK_INSERT && shift_state == 1) {
1965             term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1966             term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1967             return 0;
1968         }
1969         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1970             return -1;
1971         }
1972         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1973             
1974             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1975             return -1;
1976         }
1977         /* Control-Numlock for app-keypad mode switch */
1978         if (wParam == VK_PAUSE && shift_state == 2) {
1979             app_keypad_keys ^= 1;
1980             return 0;
1981         }
1982
1983         /* Nethack keypad */
1984         if (cfg.nethack_keypad && !left_alt) {
1985            switch(wParam) {
1986                case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1987                case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1988                case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1989                case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1990                case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1991                case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1992                case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1993                case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1994                case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1995            }
1996         }
1997
1998         /* Application Keypad */
1999         if (!left_alt) {
2000            int xkey = 0;
2001
2002            if ( cfg.funky_type == 3 ||
2003               ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) {
2004                case VK_EXECUTE: xkey = 'P'; break;
2005                case VK_DIVIDE:  xkey = 'Q'; break;
2006                case VK_MULTIPLY:xkey = 'R'; break;
2007                case VK_SUBTRACT:xkey = 'S'; break;
2008            }
2009            if(app_keypad_keys) switch(wParam) {
2010                case VK_NUMPAD0: xkey = 'p'; break;
2011                case VK_NUMPAD1: xkey = 'q'; break;
2012                case VK_NUMPAD2: xkey = 'r'; break;
2013                case VK_NUMPAD3: xkey = 's'; break;
2014                case VK_NUMPAD4: xkey = 't'; break;
2015                case VK_NUMPAD5: xkey = 'u'; break;
2016                case VK_NUMPAD6: xkey = 'v'; break;
2017                case VK_NUMPAD7: xkey = 'w'; break;
2018                case VK_NUMPAD8: xkey = 'x'; break;
2019                case VK_NUMPAD9: xkey = 'y'; break;
2020
2021                case VK_DECIMAL: xkey = 'n'; break;
2022                case VK_ADD:     if(cfg.funky_type==2) { 
2023                                     if(shift_state) xkey = 'l';
2024                                     else            xkey = 'k';
2025                                 } else if(shift_state)  xkey = 'm'; 
2026                                   else                  xkey = 'l';
2027                                 break;
2028
2029                case VK_DIVIDE:  if(cfg.funky_type==2) xkey = 'o'; break;
2030                case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2031                case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2032
2033                case VK_RETURN:
2034                                 if (HIWORD(lParam)&KF_EXTENDED)
2035                                     xkey = 'M';
2036                                 break;
2037             }
2038             if(xkey)
2039             {
2040                 if (vt52_mode)
2041                 {
2042                     if (xkey>='P' && xkey<='S')
2043                         p += sprintf((char *)p, "\x1B%c", xkey); 
2044                     else
2045                         p += sprintf((char *)p, "\x1B?%c", xkey); 
2046                 }
2047                 else 
2048                     p += sprintf((char *)p, "\x1BO%c", xkey); 
2049                 return p - output;
2050             }
2051         }
2052
2053         if (wParam == VK_BACK && shift_state == 0 )     /* Backspace */
2054         {
2055             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2056             return p-output;
2057         }
2058         if (wParam == VK_TAB && shift_state == 1 )      /* Shift tab */
2059         {
2060             *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2061         }
2062         if (wParam == VK_SPACE && shift_state == 2 )    /* Ctrl-Space */
2063         {
2064             *p++ = 0; return p - output;
2065         }
2066         if (wParam == VK_SPACE && shift_state == 3 )    /* Ctrl-Shift-Space */
2067         {
2068             *p++ = 160; return p - output;
2069         }
2070         if (wParam == VK_CANCEL && shift_state == 2 )   /* Ctrl-Break */
2071         {
2072             *p++ = 3; return p - output;
2073         }
2074         /* Control-2 to Control-8 are special */
2075         if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2076         {
2077             *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2078             return p - output;
2079         }
2080         if (shift_state == 2 && wParam == 0xBD) {
2081             *p++ = 0x1F;
2082             return p - output;
2083         }
2084         if (shift_state == 2 && wParam == 0xDF) {
2085             *p++ = 0x1C;
2086             return p - output;
2087         }
2088         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2089             *p++ = '\r'; *p++ = '\n';
2090             return p - output;
2091         }
2092
2093         /*
2094          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2095          * for integer decimal nn.)
2096          *
2097          * We also deal with the weird ones here. Linux VCs replace F1
2098          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2099          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2100          * respectively.
2101          */
2102         code = 0;
2103         switch (wParam) {
2104           case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2105           case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2106           case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2107           case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2108           case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2109           case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2110           case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2111           case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2112           case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2113           case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2114           case VK_F11: code = 23; break;
2115           case VK_F12: code = 24; break;
2116           case VK_F13: code = 25; break;
2117           case VK_F14: code = 26; break;
2118           case VK_F15: code = 28; break;
2119           case VK_F16: code = 29; break;
2120           case VK_F17: code = 31; break;
2121           case VK_F18: code = 32; break;
2122           case VK_F19: code = 33; break;
2123           case VK_F20: code = 34; break;
2124           case VK_HOME: code = 1; break;
2125           case VK_INSERT: code = 2; break;
2126           case VK_DELETE: code = 3; break;
2127           case VK_END: code = 4; break;
2128           case VK_PRIOR: code = 5; break;
2129           case VK_NEXT: code = 6; break;
2130         }
2131         /* Reorder edit keys to physical order */
2132         if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2133
2134         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2135             p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2136             return p - output;
2137         }
2138         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2139             if (vt52_mode)
2140                 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2141             else
2142                 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2143             return p - output;
2144         }
2145         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2146             p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2147             return p - output;
2148         }
2149         if (code) {
2150             p += sprintf((char *)p, "\x1B[%d~", code);
2151             return p - output;
2152         }
2153
2154         /*
2155          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2156          * some reason seems to send VK_CLEAR to Windows...).
2157          */
2158         {
2159             char xkey = 0;
2160             switch (wParam) {
2161                 case VK_UP:     xkey = 'A'; break;
2162                 case VK_DOWN:   xkey = 'B'; break;
2163                 case VK_RIGHT:  xkey = 'C'; break;
2164                 case VK_LEFT:   xkey = 'D'; break;
2165                 case VK_CLEAR:  xkey = 'G'; break;
2166             }
2167             if (xkey)
2168             {
2169                 if (vt52_mode)
2170                     p += sprintf((char *)p, "\x1B%c", xkey);
2171                 else if (app_cursor_keys)
2172                     p += sprintf((char *)p, "\x1BO%c", xkey);
2173                 else
2174                     p += sprintf((char *)p, "\x1B[%c", xkey);
2175                 return p - output;
2176             }
2177         }
2178
2179         /*
2180          * Finally, deal with Return ourselves. (Win95 seems to
2181          * foul it up when Alt is pressed, for some reason.)
2182          */
2183         if (wParam == VK_RETURN)       /* Return */
2184         {
2185             *p++ = 0x0D;
2186             return p-output;
2187         }
2188     }
2189
2190     /* Okay we've done everything interesting; let windows deal with 
2191      * the boring stuff */
2192     {
2193         BOOL capsOn=keystate[VK_CAPITAL] !=0;
2194
2195         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2196         if(cfg.xlat_capslockcyr)
2197             keystate[VK_CAPITAL] = 0;
2198
2199         r = ToAscii (wParam, scan, keystate, keys, 0);
2200         if(r>0)
2201         {
2202             p = output;
2203             for(i=0; i<r; i++)
2204             {
2205                 unsigned char ch = (unsigned char)keys[i];
2206
2207                 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2208                     compose_char = ch;
2209                     compose_state ++;
2210                     continue;
2211                 }
2212                 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2213                     int nc;
2214                     compose_state = 0;
2215
2216                     if ((nc=check_compose(compose_char,ch)) == -1)
2217                     {
2218                         c_write1('\007');
2219                         return 0;
2220                     }
2221                     *p++ = xlat_kbd2tty((unsigned char)nc);
2222                     return p-output;
2223                 }
2224
2225                 compose_state = 0;
2226
2227                 if( left_alt && key_down ) *p++ = '\033';
2228                 if (!key_down)
2229                     *p++ = ch;
2230                 else
2231                 {
2232                     if(capsOn)
2233                         ch = xlat_latkbd2win(ch);
2234                     *p++ = xlat_kbd2tty(ch);
2235                 }
2236             }
2237
2238             /* This is so the ALT-Numpad and dead keys work correctly. */
2239             keys[0] = 0;
2240
2241             return p-output;
2242         }
2243     }
2244
2245     /* This stops ALT press-release doing a 'COMMAND MENU' function */
2246     if (message == WM_SYSKEYUP && wParam == VK_MENU) 
2247         return 0;
2248
2249     return -1;
2250 }
2251
2252 void set_title (char *title) {
2253     sfree (window_name);
2254     window_name = smalloc(1+strlen(title));
2255     strcpy (window_name, title);
2256     if (cfg.win_name_always || !IsIconic(hwnd))
2257         SetWindowText (hwnd, title);
2258 }
2259
2260 void set_icon (char *title) {
2261     sfree (icon_name);
2262     icon_name = smalloc(1+strlen(title));
2263     strcpy (icon_name, title);
2264     if (!cfg.win_name_always && IsIconic(hwnd))
2265         SetWindowText (hwnd, title);
2266 }
2267
2268 void set_sbar (int total, int start, int page) {
2269     SCROLLINFO si;
2270
2271     if (!cfg.scrollbar) return;
2272
2273     si.cbSize = sizeof(si);
2274     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2275     si.nMin = 0;
2276     si.nMax = total - 1;
2277     si.nPage = page;
2278     si.nPos = start;
2279     if (hwnd)
2280         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2281 }
2282
2283 Context get_ctx(void) {
2284     HDC hdc;
2285     if (hwnd) {
2286         hdc = GetDC (hwnd);
2287         if (hdc && pal)
2288             SelectPalette (hdc, pal, FALSE);
2289         return hdc;
2290     } else
2291         return NULL;
2292 }
2293
2294 void free_ctx (Context ctx) {
2295     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2296     ReleaseDC (hwnd, ctx);
2297 }
2298
2299 static void real_palette_set (int n, int r, int g, int b) {
2300     if (pal) {
2301         logpal->palPalEntry[n].peRed = r;
2302         logpal->palPalEntry[n].peGreen = g;
2303         logpal->palPalEntry[n].peBlue = b;
2304         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2305         colours[n] = PALETTERGB(r, g, b);
2306         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2307     } else
2308         colours[n] = RGB(r, g, b);
2309 }
2310
2311 void palette_set (int n, int r, int g, int b) {
2312     static const int first[21] = {
2313         0, 2, 4, 6, 8, 10, 12, 14,
2314         1, 3, 5, 7, 9, 11, 13, 15,
2315         16, 17, 18, 20, 22
2316     };
2317     real_palette_set (first[n], r, g, b);
2318     if (first[n] >= 18)
2319         real_palette_set (first[n]+1, r, g, b);
2320     if (pal) {
2321         HDC hdc = get_ctx();
2322         UnrealizeObject (pal);
2323         RealizePalette (hdc);
2324         free_ctx (hdc);
2325     }
2326 }
2327
2328 void palette_reset (void) {
2329     int i;
2330
2331     for (i = 0; i < NCOLOURS; i++) {
2332         if (pal) {
2333             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2334             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2335             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2336             logpal->palPalEntry[i].peFlags = 0;
2337             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2338                                     defpal[i].rgbtGreen,
2339                                     defpal[i].rgbtBlue);
2340         } else
2341             colours[i] = RGB(defpal[i].rgbtRed,
2342                              defpal[i].rgbtGreen,
2343                              defpal[i].rgbtBlue);
2344     }
2345
2346     if (pal) {
2347         HDC hdc;
2348         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2349         hdc = get_ctx();
2350         RealizePalette (hdc);
2351         free_ctx (hdc);
2352     }
2353 }
2354
2355 void write_clip (void *data, int len, int must_deselect) {
2356     HGLOBAL clipdata;
2357     void *lock;
2358
2359     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2360     if (!clipdata)
2361         return;
2362     lock = GlobalLock (clipdata);
2363     if (!lock)
2364         return;
2365     memcpy (lock, data, len);
2366     ((unsigned char *) lock) [len] = 0;
2367     GlobalUnlock (clipdata);
2368
2369     if (!must_deselect)
2370         SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2371
2372     if (OpenClipboard (hwnd)) {
2373         EmptyClipboard();
2374         SetClipboardData (CF_TEXT, clipdata);
2375         CloseClipboard();
2376     } else
2377         GlobalFree (clipdata);
2378
2379     if (!must_deselect)
2380         SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2381 }
2382
2383 void get_clip (void **p, int *len) {
2384     static HGLOBAL clipdata = NULL;
2385
2386     if (!p) {
2387         if (clipdata)
2388             GlobalUnlock (clipdata);
2389         clipdata = NULL;
2390         return;
2391     } else {
2392         if (OpenClipboard (NULL)) {
2393             clipdata = GetClipboardData (CF_TEXT);
2394             CloseClipboard();
2395             if (clipdata) {
2396                 *p = GlobalLock (clipdata);
2397                 if (*p) {
2398                     *len = strlen(*p);
2399                     return;
2400                 }
2401             }
2402         }
2403     }
2404
2405     *p = NULL;
2406     *len = 0;
2407 }
2408
2409 /*
2410  * Move `lines' lines from position `from' to position `to' in the
2411  * window.
2412  */
2413 void optimised_move (int to, int from, int lines) {
2414     RECT r;
2415     int min, max;
2416
2417     min = (to < from ? to : from);
2418     max = to + from - min;
2419
2420     r.left = 0; r.right = cols * font_width;
2421     r.top = min * font_height; r.bottom = (max+lines) * font_height;
2422     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2423 }
2424
2425 /*
2426  * Print a message box and perform a fatal exit.
2427  */
2428 void fatalbox(char *fmt, ...) {
2429     va_list ap;
2430     char stuff[200];
2431
2432     va_start(ap, fmt);
2433     vsprintf(stuff, fmt, ap);
2434     va_end(ap);
2435     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2436     exit(1);
2437 }
2438
2439 /*
2440  * Beep.
2441  */
2442 void beep(int errorbeep) {
2443     static long last_beep = 0;
2444     long now, beep_diff;
2445
2446     now = GetTickCount();
2447     beep_diff = now-last_beep;
2448
2449     /* Make sure we only respond to one beep per packet or so */
2450     if (beep_diff>=0 && beep_diff<50)
2451         return;
2452
2453     if(errorbeep)
2454        MessageBeep(MB_ICONHAND);
2455     else
2456        MessageBeep(MB_OK);
2457
2458     last_beep = GetTickCount();
2459 }