]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
e9419d7931f37058b12962863297c70082aa3ce3
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <commctrl.h>
3 #include <winsock.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <ctype.h>
7
8 #define PUTTY_DO_GLOBALS                       /* actually _define_ globals */
9 #include "putty.h"
10 #include "win_res.h"
11
12 #define IDM_SHOWLOG   0x0010
13 #define IDM_NEWSESS   0x0020
14 #define IDM_DUPSESS   0x0030
15 #define IDM_RECONF    0x0040
16 #define IDM_CLRSB     0x0050
17 #define IDM_RESET     0x0060
18 #define IDM_TEL_AYT   0x0070
19 #define IDM_TEL_BRK   0x0080
20 #define IDM_TEL_SYNCH 0x0090
21 #define IDM_TEL_EC    0x00a0
22 #define IDM_TEL_EL    0x00b0
23 #define IDM_TEL_GA    0x00c0
24 #define IDM_TEL_NOP   0x00d0
25 #define IDM_TEL_ABORT 0x00e0
26 #define IDM_TEL_AO    0x00f0
27 #define IDM_TEL_IP    0x0100
28 #define IDM_TEL_SUSP  0x0110
29 #define IDM_TEL_EOR   0x0120
30 #define IDM_TEL_EOF   0x0130
31 #define IDM_ABOUT     0x0140
32 #define IDM_SAVEDSESS 0x0150
33
34 #define IDM_SAVED_MIN 0x1000
35 #define IDM_SAVED_MAX 0x2000
36
37 #define WM_IGNORE_SIZE (WM_XUSER + 1)
38 #define WM_IGNORE_CLIP (WM_XUSER + 2)
39
40 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
41 static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output);
42 static void cfgtopalette(void);
43 static void init_palette(void);
44 static void init_fonts(int);
45
46 static int extra_width, extra_height;
47
48 static int pending_netevent = 0;
49 static WPARAM pend_netevent_wParam = 0;
50 static LPARAM pend_netevent_lParam = 0;
51 static void enact_pending_netevent(void);
52
53 #define FONT_NORMAL 0
54 #define FONT_BOLD 1
55 #define FONT_UNDERLINE 2
56 #define FONT_BOLDUND 3
57 #define FONT_OEM 4
58 #define FONT_OEMBOLD 5
59 #define FONT_OEMBOLDUND 6
60 #define FONT_OEMUND 7
61 static HFONT fonts[8];
62 static enum {
63     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
64 } bold_mode;
65 static enum {
66     UND_LINE, UND_FONT
67 } und_mode;
68 static int descent;
69
70 #define NCOLOURS 24
71 static COLORREF colours[NCOLOURS];
72 static HPALETTE pal;
73 static LPLOGPALETTE logpal;
74 static RGBTRIPLE defpal[NCOLOURS];
75
76 static HWND hwnd;
77
78 static int dbltime, lasttime, lastact;
79 static Mouse_Button lastbtn;
80
81 static char *window_name, *icon_name;
82
83 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
84     static char appname[] = "PuTTY";
85     WORD winsock_ver;
86     WSADATA wsadata;
87     WNDCLASS wndclass;
88     MSG msg;
89     int guess_width, guess_height;
90
91     putty_inst = inst;
92
93     winsock_ver = MAKEWORD(1, 1);
94     if (WSAStartup(winsock_ver, &wsadata)) {
95         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
96                    MB_OK | MB_ICONEXCLAMATION);
97         return 1;
98     }
99     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
100         MessageBox(NULL, "WinSock version is incompatible with 1.1",
101                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
102         WSACleanup();
103         return 1;
104     }
105     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
106
107     InitCommonControls();
108
109     /*
110      * Process the command line.
111      */
112     {
113         char *p;
114
115         default_protocol = DEFAULT_PROTOCOL;
116         default_port = DEFAULT_PORT;
117
118         do_defaults(NULL);
119
120         p = cmdline;
121         while (*p && isspace(*p)) p++;
122
123         /*
124          * Process command line options first. Yes, this can be
125          * done better, and it will be as soon as I have the
126          * energy...
127          */
128         while (*p == '-') {
129             char *q = p + strcspn(p, " \t");
130             p++;
131             if (q == p + 3 &&
132                 tolower(p[0]) == 's' &&
133                 tolower(p[1]) == 's' &&
134                 tolower(p[2]) == 'h') {
135                 default_protocol = cfg.protocol = PROT_SSH;
136                 default_port = cfg.port = 22;
137             } else if (q == p + 3 &&
138                 tolower(p[0]) == 'l' &&
139                 tolower(p[1]) == 'o' &&
140                 tolower(p[2]) == 'g') {
141                 logfile = "putty.log";
142             }
143             p = q + strspn(q, " \t");
144         }
145
146         /*
147          * An initial @ means to activate a saved session.
148          */
149         if (*p == '@') {
150             do_defaults (p+1);
151             if (!*cfg.host && !do_config()) {
152                 WSACleanup();
153                 return 0;
154             }
155         } else if (*p == '&') {
156             /*
157              * An initial & means we've been given a command line
158              * containing the hex value of a HANDLE for a file
159              * mapping object, which we must then extract as a
160              * config.
161              */
162             HANDLE filemap;
163             Config *cp;
164             if (sscanf(p+1, "%p", &filemap) == 1 &&
165                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
166                                     0, 0, sizeof(Config))) != NULL) {
167                 cfg = *cp;
168                 UnmapViewOfFile(cp);
169                 CloseHandle(filemap);
170             } else if (!do_config()) {
171                 WSACleanup();
172                 return 0;
173             }
174         } else if (*p) {
175             char *q = p;
176             /*
177              * If the hostname starts with "telnet://", set the
178              * protocol to Telnet and process the string as a
179              * Telnet URL.
180              */
181             if (!strncmp(q, "telnet://", 9)) {
182                 q += 9;
183                 cfg.protocol = PROT_TELNET;
184                 p = q;
185                 while (*p && *p != ':') p++;
186                 if (*p) {
187                     *p++ = '\0';
188                     cfg.port = atoi(p);
189                 } else
190                     cfg.port = -1;
191                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
192                 cfg.host[sizeof(cfg.host)-1] = '\0';
193             } else {
194                 while (*p && !isspace(*p)) p++;
195                 if (*p)
196                     *p++ = '\0';
197                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
198                 cfg.host[sizeof(cfg.host)-1] = '\0';
199                 while (*p && isspace(*p)) p++;
200                 if (*p)
201                     cfg.port = atoi(p);
202                 else
203                     cfg.port = -1;
204             }
205         } else {
206             if (!do_config()) {
207                 WSACleanup();
208                 return 0;
209             }
210         }
211     }
212
213     /*
214      * Select protocol. This is farmed out into a table in a
215      * separate file to enable an ssh-free variant.
216      */
217     {
218         int i;
219         back = NULL;
220         for (i = 0; backends[i].backend != NULL; i++)
221             if (backends[i].protocol == cfg.protocol) {
222                 back = backends[i].backend;
223                 break;
224             }
225         if (back == NULL) {
226             MessageBox(NULL, "Unsupported protocol number found",
227                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
228             WSACleanup();
229             return 1;
230         }
231     }
232
233     ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
234
235     if (!prev) {
236         wndclass.style         = 0;
237         wndclass.lpfnWndProc   = WndProc;
238         wndclass.cbClsExtra    = 0;
239         wndclass.cbWndExtra    = 0;
240         wndclass.hInstance     = inst;
241         wndclass.hIcon         = LoadIcon (inst,
242                                            MAKEINTRESOURCE(IDI_MAINICON));
243         wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
244         wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
245         wndclass.lpszMenuName  = NULL;
246         wndclass.lpszClassName = appname;
247
248         RegisterClass (&wndclass);
249     }
250
251     hwnd = NULL;
252
253     savelines = cfg.savelines;
254     term_init();
255
256     cfgtopalette();
257
258     /*
259      * Guess some defaults for the window size. This all gets
260      * updated later, so we don't really care too much. However, we
261      * do want the font width/height guesses to correspond to a
262      * large font rather than a small one...
263      */
264     
265     font_width = 10;
266     font_height = 20;
267     extra_width = 25;
268     extra_height = 28;
269     term_size (cfg.height, cfg.width, cfg.savelines);
270     guess_width = extra_width + font_width * cols;
271     guess_height = extra_height + font_height * rows;
272     {
273         RECT r;
274         HWND w = GetDesktopWindow();
275         GetWindowRect (w, &r);
276         if (guess_width > r.right - r.left)
277             guess_width = r.right - r.left;
278         if (guess_height > r.bottom - r.top)
279             guess_height = r.bottom - r.top;
280     }
281
282     hwnd = CreateWindow (appname, appname,
283                          WS_OVERLAPPEDWINDOW | WS_VSCROLL,
284                          CW_USEDEFAULT, CW_USEDEFAULT,
285                          guess_width, guess_height,
286                          NULL, NULL, inst, NULL);
287
288     /*
289      * Initialise the fonts, simultaneously correcting the guesses
290      * for font_{width,height}.
291      */
292     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
293     und_mode = UND_FONT;
294     init_fonts(0);
295
296     /*
297      * Correct the guesses for extra_{width,height}.
298      */
299     {
300         RECT cr, wr;
301         GetWindowRect (hwnd, &wr);
302         GetClientRect (hwnd, &cr);
303         extra_width = wr.right - wr.left - cr.right + cr.left;
304         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
305     }
306
307     /*
308      * Resize the window, now we know what size we _really_ want it
309      * to be.
310      */
311     guess_width = extra_width + font_width * cols;
312     guess_height = extra_height + font_height * rows;
313     SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
314     SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
315                   SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
316
317     /*
318      * Initialise the scroll bar.
319      */
320     {
321         SCROLLINFO si;
322
323         si.cbSize = sizeof(si);
324         si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
325         si.nMin = 0;
326         si.nMax = rows-1;
327         si.nPage = rows;
328         si.nPos = 0;
329         SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
330     }
331
332     /*
333      * Start up the telnet connection.
334      */
335     {
336         char *error;
337         char msg[1024];
338         char *realhost;
339
340         error = back->init (hwnd, cfg.host, cfg.port, &realhost);
341         if (error) {
342             sprintf(msg, "Unable to open connection:\n%s", error);
343             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
344             return 0;
345         }
346         window_name = icon_name = NULL;
347         sprintf(msg, "%s - PuTTY", realhost);
348         set_title (msg);
349         set_icon (msg);
350     }
351
352     session_closed = FALSE;
353
354     /*
355      * Set up the input and output buffers.
356      */
357     inbuf_reap = inbuf_head = 0;
358     outbuf_reap = outbuf_head = 0;
359
360     /* 
361      * Choose unscroll method
362      */
363     unscroll_event = US_DISP;
364
365     /*
366      * Prepare the mouse handler.
367      */
368     lastact = MA_NOTHING;
369     lastbtn = MB_NOTHING;
370     dbltime = GetDoubleClickTime();
371
372     /*
373      * Set up the session-control options on the system menu.
374      */
375     {
376         HMENU m = GetSystemMenu (hwnd, FALSE);
377         HMENU p,s;
378         int i;
379
380         AppendMenu (m, MF_SEPARATOR, 0, 0);
381         if (cfg.protocol == PROT_TELNET) {
382             p = CreateMenu();
383             AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
384             AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
385             AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
386             AppendMenu (p, MF_SEPARATOR, 0, 0);
387             AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
388             AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
389             AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
390             AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
391             AppendMenu (p, MF_SEPARATOR, 0, 0);
392             AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
393             AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
394             AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
395             AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
396             AppendMenu (p, MF_SEPARATOR, 0, 0);
397             AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
398             AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
399             AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
400             AppendMenu (m, MF_SEPARATOR, 0, 0);
401         }
402         AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
403         AppendMenu (m, MF_SEPARATOR, 0, 0);
404         AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
405         AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
406         s = CreateMenu();
407         get_sesslist(TRUE);
408         for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
409           AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
410         AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
411         AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
412         AppendMenu (m, MF_SEPARATOR, 0, 0);
413         AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
414         AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
415         AppendMenu (m, MF_SEPARATOR, 0, 0);
416         AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
417     }
418
419     /*
420      * Finally show the window!
421      */
422     ShowWindow (hwnd, show);
423
424     /*
425      * Set the palette up.
426      */
427     pal = NULL;
428     logpal = NULL;
429     init_palette();
430
431     has_focus = (GetForegroundWindow() == hwnd);
432     UpdateWindow (hwnd);
433
434     {
435         int timer_id = 0, long_timer = 0;
436
437         while (GetMessage (&msg, NULL, 0, 0) == 1) {
438             /* Sometimes DispatchMessage calls routines that use their own
439              * GetMessage loop, setup this timer so we get some control back.
440              *
441              * Also call term_update() from the timer so that if the host
442              * is sending data flat out we still do redraws.
443              */
444             if(timer_id && long_timer) {
445                 KillTimer(hwnd, timer_id);
446                 long_timer = timer_id = 0;
447             }
448             if(!timer_id)
449                 timer_id = SetTimer(hwnd, 1, 20, NULL);
450             DispatchMessage (&msg);
451
452             /* This is too fast, but I'll leave it for now 'cause it shows
453              * how often term_update is called (far too often at times!)
454              */
455             term_blink(0);
456
457             /* If there's nothing new in the queue then we can do everything
458              * we've delayed, reading the socket, writing, and repainting
459              * the window.
460              */
461             if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
462                 if (pending_netevent) {
463                     enact_pending_netevent();
464
465                     term_blink(1);
466                 }
467             } else continue;
468             if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
469                 if (timer_id) {
470                     KillTimer(hwnd, timer_id);
471                     timer_id = 0;
472                 }
473                 if (inbuf_reap != inbuf_head)
474                     term_out();
475                 term_update();
476                 timer_id = SetTimer(hwnd, 1, 500, NULL);
477                 long_timer = 1;
478             }
479         }
480     }
481
482     /*
483      * Clean up.
484      */
485     {
486         int i;
487         for (i=0; i<8; i++)
488             if (fonts[i])
489                 DeleteObject(fonts[i]);
490     }
491     sfree(logpal);
492     if (pal)
493         DeleteObject(pal);
494     WSACleanup();
495
496     if (cfg.protocol == PROT_SSH) {
497         random_save_seed();
498 #ifdef MSCRYPTOAPI
499         crypto_wrapup();
500 #endif
501     }
502
503     return msg.wParam;
504 }
505
506 /*
507  * Actually do the job requested by a WM_NETEVENT
508  */
509 static void enact_pending_netevent(void) {
510     int i;
511     pending_netevent = FALSE;
512     i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
513
514     if (i < 0) {
515         char buf[1024];
516         switch (WSABASEERR + (-i) % 10000) {
517           case WSAECONNRESET:
518             sprintf(buf, "Connection reset by peer");
519             break;
520           default:
521             sprintf(buf, "Unexpected network error %d", -i);
522             break;
523         }
524         MessageBox(hwnd, buf, "PuTTY Fatal Error",
525                    MB_ICONERROR | MB_OK);
526         PostQuitMessage(1);
527     } else if (i == 0) {
528         if (cfg.close_on_exit)
529             PostQuitMessage(0);
530         else {
531             session_closed = TRUE;
532             MessageBox(hwnd, "Connection closed by remote host",
533                        "PuTTY", MB_OK | MB_ICONINFORMATION);
534             SetWindowText (hwnd, "PuTTY (inactive)");
535         }
536     }
537 }
538
539 /*
540  * Copy the colour palette from the configuration data into defpal.
541  * This is non-trivial because the colour indices are different.
542  */
543 static void cfgtopalette(void) {
544     int i;
545     static const int ww[] = {
546         6, 7, 8, 9, 10, 11, 12, 13,
547         14, 15, 16, 17, 18, 19, 20, 21,
548         0, 1, 2, 3, 4, 4, 5, 5
549     };
550
551     for (i=0; i<24; i++) {
552         int w = ww[i];
553         defpal[i].rgbtRed = cfg.colours[w][0];
554         defpal[i].rgbtGreen = cfg.colours[w][1];
555         defpal[i].rgbtBlue = cfg.colours[w][2];
556     }
557 }
558
559 /*
560  * Set up the colour palette.
561  */
562 static void init_palette(void) {
563     int i;
564     HDC hdc = GetDC (hwnd);
565     if (hdc) {
566         if (cfg.try_palette &&
567             GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
568             logpal = smalloc(sizeof(*logpal)
569                              - sizeof(logpal->palPalEntry)
570                              + NCOLOURS * sizeof(PALETTEENTRY));
571             logpal->palVersion = 0x300;
572             logpal->palNumEntries = NCOLOURS;
573             for (i = 0; i < NCOLOURS; i++) {
574                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
575                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
576                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
577                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
578             }
579             pal = CreatePalette (logpal);
580             if (pal) {
581                 SelectPalette (hdc, pal, FALSE);
582                 RealizePalette (hdc);
583                 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
584                                FALSE);
585             }
586         }
587         ReleaseDC (hwnd, hdc);
588     }
589     if (pal)
590         for (i=0; i<NCOLOURS; i++)
591             colours[i] = PALETTERGB(defpal[i].rgbtRed,
592                                     defpal[i].rgbtGreen,
593                                     defpal[i].rgbtBlue);
594     else
595         for(i=0; i<NCOLOURS; i++)
596             colours[i] = RGB(defpal[i].rgbtRed,
597                              defpal[i].rgbtGreen,
598                              defpal[i].rgbtBlue);
599 }
600
601 /*
602  * Initialise all the fonts we will need. There may be as many as
603  * eight or as few as one. We also:
604  *
605  * - check the font width and height, correcting our guesses if
606  *   necessary.
607  *
608  * - verify that the bold font is the same width as the ordinary
609  *   one, and engage shadow bolding if not.
610  * 
611  * - verify that the underlined font is the same width as the
612  *   ordinary one (manual underlining by means of line drawing can
613  *   be done in a pinch).
614  */
615 static void init_fonts(int pick_width) {
616     TEXTMETRIC tm;
617     int i;
618     int fsize[8];
619     HDC hdc;
620     int fw_dontcare, fw_bold;
621     int firstchar = ' ';
622
623 #ifdef CHECKOEMFONT
624 font_messup:
625 #endif
626     for (i=0; i<8; i++)
627         fonts[i] = NULL;
628
629     if (cfg.fontisbold) {
630         fw_dontcare = FW_BOLD;
631         fw_bold = FW_BLACK;
632    } else {
633         fw_dontcare = FW_DONTCARE;
634         fw_bold = FW_BOLD;
635     }
636
637     hdc = GetDC(hwnd);
638
639     font_height = cfg.fontheight;
640     font_width = pick_width;
641
642 #define f(i,c,w,u) \
643     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
644                            c, OUT_DEFAULT_PRECIS, \
645                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
646                            FIXED_PITCH | FF_DONTCARE, cfg.font)
647
648     if (cfg.vtmode != VT_OEMONLY) {
649         f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
650
651         SelectObject (hdc, fonts[FONT_NORMAL]);
652         GetTextMetrics(hdc, &tm); 
653         font_height = tm.tmHeight;
654         font_width = tm.tmAveCharWidth;
655
656         f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
657
658         if (bold_mode == BOLD_FONT) {
659             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
660             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
661         }
662
663         if (cfg.vtmode == VT_OEMANSI) {
664             f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
665             f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
666
667             if (bold_mode == BOLD_FONT) {
668                 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
669                 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
670             }
671         }
672     }
673     else
674     {
675         f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
676
677         SelectObject (hdc, fonts[FONT_OEM]);
678         GetTextMetrics(hdc, &tm); 
679         font_height = tm.tmHeight;
680         font_width = tm.tmAveCharWidth;
681
682         f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
683
684         if (bold_mode == BOLD_FONT) {
685             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
686             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
687         }
688     }
689 #undef f
690
691     descent = tm.tmAscent + 1;
692     if (descent >= font_height)
693         descent = font_height - 1;
694     firstchar = tm.tmFirstChar;
695
696     for (i=0; i<8; i++) {
697         if (fonts[i]) {
698             if (SelectObject (hdc, fonts[i]) &&
699                 GetTextMetrics(hdc, &tm) )
700                  fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
701             else fsize[i] = -i;
702         }
703         else fsize[i] = -i;
704     }
705
706     ReleaseDC (hwnd, hdc);
707
708     /* ... This is wrong in OEM only mode */
709     if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
710         (bold_mode == BOLD_FONT &&
711          fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
712         und_mode = UND_LINE;
713         DeleteObject (fonts[FONT_UNDERLINE]);
714         if (bold_mode == BOLD_FONT)
715             DeleteObject (fonts[FONT_BOLDUND]);
716     }
717
718     if (bold_mode == BOLD_FONT &&
719         fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
720         bold_mode = BOLD_SHADOW;
721         DeleteObject (fonts[FONT_BOLD]);
722         if (und_mode == UND_FONT)
723             DeleteObject (fonts[FONT_BOLDUND]);
724     }
725
726 #ifdef CHECKOEMFONT
727     /* With the fascist font painting it doesn't matter if the linedraw font
728      * isn't exactly the right size anymore so we don't have to check this.
729      */
730     if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
731         if( cfg.fontcharset == OEM_CHARSET )
732         {
733             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
734                    "different sizes. Using OEM-only mode instead",
735                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
736             cfg.vtmode = VT_OEMONLY;
737         }
738         else if( firstchar < ' ' )
739         {
740             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
741                    "different sizes. Using XTerm mode instead",
742                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
743             cfg.vtmode = VT_XWINDOWS;
744         }
745         else
746         {
747             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
748                    "different sizes. Using ISO8859-1 mode instead",
749                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
750             cfg.vtmode = VT_POORMAN;
751         }
752
753         for (i=0; i<8; i++)
754             if (fonts[i])
755                 DeleteObject (fonts[i]);
756         goto font_messup;
757     }
758 #endif
759 }
760
761 void request_resize (int w, int h, int refont) {
762     int width, height;
763     
764 #ifdef CHECKOEMFONT
765     /* Don't do this in OEMANSI, you may get disable messages */
766     if (refont && w != cols && (cols==80 || cols==132)
767           && cfg.vtmode != VT_OEMANSI)
768 #else
769     if (refont && w != cols && (cols==80 || cols==132))
770 #endif
771     {
772        /* If font width too big for screen should we shrink the font more ? */
773         if (w==132)
774             font_width = ((font_width*cols+w/2)/w);
775         else
776             font_width = 0;
777         {
778             int i;
779             for (i=0; i<8; i++)
780                 if (fonts[i])
781                     DeleteObject(fonts[i]);
782         }
783         bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
784         und_mode = UND_FONT;
785         init_fonts(font_width);
786     }
787
788     width = extra_width + font_width * w;
789     height = extra_height + font_height * h;
790
791     SetWindowPos (hwnd, NULL, 0, 0, width, height,
792                   SWP_NOACTIVATE | SWP_NOCOPYBITS |
793                   SWP_NOMOVE | SWP_NOZORDER);
794 }
795
796 static void click (Mouse_Button b, int x, int y) {
797     int thistime = GetMessageTime();
798
799     if (lastbtn == b && thistime - lasttime < dbltime) {
800         lastact = (lastact == MA_CLICK ? MA_2CLK :
801                    lastact == MA_2CLK ? MA_3CLK :
802                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
803     } else {
804         lastbtn = b;
805         lastact = MA_CLICK;
806     }
807     if (lastact != MA_NOTHING)
808         term_mouse (b, lastact, x, y);
809     lasttime = thistime;
810 }
811
812 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
813                                  WPARAM wParam, LPARAM lParam) {
814     HDC hdc;
815     static int ignore_size = FALSE;
816     static int ignore_clip = FALSE;
817     static int just_reconfigged = FALSE;
818
819     switch (message) {
820       case WM_TIMER:
821         if (pending_netevent)
822             enact_pending_netevent();
823         if (inbuf_reap != inbuf_head)
824             term_out();
825         term_update();
826         return 0;
827       case WM_CREATE:
828         break;
829       case WM_CLOSE:
830         if (!cfg.warn_on_close || session_closed ||
831             MessageBox(hwnd, "Are you sure you want to close this session?",
832                        "PuTTY Exit Confirmation",
833                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
834             DestroyWindow(hwnd);
835         return 0;
836       case WM_DESTROY:
837         PostQuitMessage (0);
838         return 0;
839       case WM_SYSCOMMAND:
840         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
841           case IDM_SHOWLOG:
842             showeventlog(hwnd);
843             break;
844           case IDM_NEWSESS:
845           case IDM_DUPSESS:
846           case IDM_SAVEDSESS:
847             {
848                 char b[2048];
849                 char c[30], *cl;
850                 int freecl = FALSE;
851                 STARTUPINFO si;
852                 PROCESS_INFORMATION pi;
853                 HANDLE filemap = NULL;
854
855                 if (wParam == IDM_DUPSESS) {
856                     /*
857                      * Allocate a file-mapping memory chunk for the
858                      * config structure.
859                      */
860                     SECURITY_ATTRIBUTES sa;
861                     Config *p;
862
863                     sa.nLength = sizeof(sa);
864                     sa.lpSecurityDescriptor = NULL;
865                     sa.bInheritHandle = TRUE;
866                     filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
867                                                 &sa,
868                                                 PAGE_READWRITE,
869                                                 0,
870                                                 sizeof(Config),
871                                                 NULL);
872                     if (filemap) {
873                         p = (Config *)MapViewOfFile(filemap,
874                                                     FILE_MAP_WRITE,
875                                                     0, 0, sizeof(Config));
876                         if (p) {
877                             *p = cfg;  /* structure copy */
878                             UnmapViewOfFile(p);
879                         }
880                     }
881                     sprintf(c, "putty &%p", filemap);
882                     cl = c;
883                 } else if (wParam == IDM_SAVEDSESS) {
884                     char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
885                     cl = malloc(16 + strlen(session)); /* 8, but play safe */
886                     if (!cl)
887                         cl = NULL;     /* not a very important failure mode */
888                     else {
889                         sprintf(cl, "putty @%s", session);
890                         freecl = TRUE;
891                     }
892                 } else
893                     cl = NULL;
894
895                 GetModuleFileName (NULL, b, sizeof(b)-1);
896                 si.cb = sizeof(si);
897                 si.lpReserved = NULL;
898                 si.lpDesktop = NULL;
899                 si.lpTitle = NULL;
900                 si.dwFlags = 0;
901                 si.cbReserved2 = 0;
902                 si.lpReserved2 = NULL;
903                 CreateProcess (b, cl, NULL, NULL, TRUE,
904                                NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
905
906                 if (filemap)
907                     CloseHandle(filemap);
908                 if (freecl)
909                     free(cl);
910             }
911             break;
912           case IDM_RECONF:
913             if (!do_reconfig(hwnd))
914                 break;
915             just_reconfigged = TRUE;
916             {
917                 int i;
918                 for (i=0; i<8; i++)
919                     if (fonts[i])
920                         DeleteObject(fonts[i]);
921             }
922             bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
923             und_mode = UND_FONT;
924             init_fonts(0);
925             sfree(logpal);
926             /* Telnet will change local echo -> remote if the remote asks */
927             if (cfg.protocol != PROT_TELNET)
928                 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
929             if (pal)
930                 DeleteObject(pal);
931             logpal = NULL;
932             pal = NULL;
933             cfgtopalette();
934             init_palette();
935             term_size(cfg.height, cfg.width, cfg.savelines);
936             InvalidateRect(hwnd, NULL, TRUE);
937             SetWindowPos (hwnd, NULL, 0, 0,
938                           extra_width + font_width * cfg.width,
939                           extra_height + font_height * cfg.height,
940                           SWP_NOACTIVATE | SWP_NOCOPYBITS |
941                           SWP_NOMOVE | SWP_NOZORDER);
942             if (IsIconic(hwnd)) {
943                 SetWindowText (hwnd,
944                                cfg.win_name_always ? window_name : icon_name);
945             }
946             break;
947           case IDM_CLRSB:
948             term_clrsb();
949             break;
950           case IDM_RESET:
951             term_pwron();
952             break;
953           case IDM_TEL_AYT: back->special (TS_AYT); break;
954           case IDM_TEL_BRK: back->special (TS_BRK); break;
955           case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
956           case IDM_TEL_EC: back->special (TS_EC); break;
957           case IDM_TEL_EL: back->special (TS_EL); break;
958           case IDM_TEL_GA: back->special (TS_GA); break;
959           case IDM_TEL_NOP: back->special (TS_NOP); break;
960           case IDM_TEL_ABORT: back->special (TS_ABORT); break;
961           case IDM_TEL_AO: back->special (TS_AO); break;
962           case IDM_TEL_IP: back->special (TS_IP); break;
963           case IDM_TEL_SUSP: back->special (TS_SUSP); break;
964           case IDM_TEL_EOR: back->special (TS_EOR); break;
965           case IDM_TEL_EOF: back->special (TS_EOF); break;
966           case IDM_ABOUT:
967             showabout (hwnd);
968             break;
969         default:
970           if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
971             SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
972           }
973         }
974         break;
975
976 #define X_POS(l) ((int)(short)LOWORD(l))
977 #define Y_POS(l) ((int)(short)HIWORD(l))
978
979 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
980 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
981
982       case WM_LBUTTONDOWN:
983         click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
984                TO_CHR_Y(Y_POS(lParam)));
985         SetCapture(hwnd);
986         return 0;
987       case WM_LBUTTONUP:
988         term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
989                     TO_CHR_Y(Y_POS(lParam)));
990         ReleaseCapture();
991         return 0;
992       case WM_MBUTTONDOWN:
993         SetCapture(hwnd);
994         click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
995                TO_CHR_X(X_POS(lParam)),
996                TO_CHR_Y(Y_POS(lParam)));
997         return 0;
998       case WM_MBUTTONUP:
999         term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1000                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1001                     TO_CHR_Y(Y_POS(lParam)));
1002         ReleaseCapture();
1003         return 0;
1004       case WM_RBUTTONDOWN:
1005         SetCapture(hwnd);
1006         click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1007                TO_CHR_X(X_POS(lParam)),
1008                TO_CHR_Y(Y_POS(lParam)));
1009         return 0;
1010       case WM_RBUTTONUP:
1011         term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1012                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1013                     TO_CHR_Y(Y_POS(lParam)));
1014         ReleaseCapture();
1015         return 0;
1016       case WM_MOUSEMOVE:
1017         /*
1018          * Add the mouse position and message time to the random
1019          * number noise, if we're using ssh.
1020          */
1021         if (cfg.protocol == PROT_SSH)
1022             noise_ultralight(lParam);
1023
1024         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1025             Mouse_Button b;
1026             if (wParam & MK_LBUTTON)
1027                 b = MB_SELECT;
1028             else if (wParam & MK_MBUTTON)
1029                 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1030             else
1031                 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1032             term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1033                         TO_CHR_Y(Y_POS(lParam)));
1034         }
1035         return 0;
1036       case WM_IGNORE_CLIP:
1037         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1038         break;
1039       case WM_DESTROYCLIPBOARD:
1040         if (!ignore_clip)
1041             term_deselect();
1042         ignore_clip = FALSE;
1043         return 0;
1044       case WM_PAINT:
1045         {
1046             PAINTSTRUCT p;
1047             hdc = BeginPaint (hwnd, &p);
1048             if (pal) {
1049                 SelectPalette (hdc, pal, TRUE);
1050                 RealizePalette (hdc);
1051             }
1052             term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1053                         p.rcPaint.right, p.rcPaint.bottom);
1054             SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1055             SelectObject (hdc, GetStockObject(WHITE_PEN));
1056             EndPaint (hwnd, &p);
1057         }
1058         return 0;
1059       case WM_NETEVENT:
1060         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1061          * but the only one that's likely to try to overload us is FD_READ.
1062          * This means buffering just one is fine.
1063          */
1064         if (pending_netevent)
1065             enact_pending_netevent();
1066
1067         pending_netevent = TRUE;
1068         pend_netevent_wParam=wParam;
1069         pend_netevent_lParam=lParam;
1070         return 0;
1071       case WM_SETFOCUS:
1072         has_focus = TRUE;
1073         term_out();
1074         term_update();
1075         break;
1076       case WM_KILLFOCUS:
1077         has_focus = FALSE;
1078         term_out();
1079         term_update();
1080         break;
1081       case WM_IGNORE_SIZE:
1082         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1083         break;
1084       case WM_ENTERSIZEMOVE:
1085         EnableSizeTip(1);
1086         break;
1087       case WM_EXITSIZEMOVE:
1088         EnableSizeTip(0);
1089         break;
1090       case WM_SIZING:
1091         {
1092             int width, height, w, h, ew, eh;
1093             LPRECT r = (LPRECT)lParam;
1094
1095             width = r->right - r->left - extra_width;
1096             height = r->bottom - r->top - extra_height;
1097             w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1098             h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1099             UpdateSizeTip(hwnd, w, h);
1100             ew = width - w * font_width;
1101             eh = height - h * font_height;
1102             if (ew != 0) {
1103                 if (wParam == WMSZ_LEFT ||
1104                     wParam == WMSZ_BOTTOMLEFT ||
1105                     wParam == WMSZ_TOPLEFT)
1106                     r->left += ew;
1107                 else
1108                     r->right -= ew;
1109             }
1110             if (eh != 0) {
1111                 if (wParam == WMSZ_TOP ||
1112                     wParam == WMSZ_TOPRIGHT ||
1113                     wParam == WMSZ_TOPLEFT)
1114                     r->top += eh;
1115                 else
1116                     r->bottom -= eh;
1117             }
1118             if (ew || eh)
1119                 return 1;
1120             else
1121                 return 0;
1122         }
1123         /* break;  (never reached) */
1124       case WM_SIZE:
1125         if (wParam == SIZE_MINIMIZED) {
1126             SetWindowText (hwnd,
1127                            cfg.win_name_always ? window_name : icon_name);
1128             break;
1129         }
1130         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1131             SetWindowText (hwnd, window_name);
1132         if (!ignore_size) {
1133             int width, height, w, h;
1134 #if 0 /* we have fixed this using WM_SIZING now */
1135             int ew, eh;
1136 #endif
1137
1138             width = LOWORD(lParam);
1139             height = HIWORD(lParam);
1140             w = width / font_width; if (w < 1) w = 1;
1141             h = height / font_height; if (h < 1) h = 1;
1142 #if 0 /* we have fixed this using WM_SIZING now */
1143             ew = width - w * font_width;
1144             eh = height - h * font_height;
1145             if (ew != 0 || eh != 0) {
1146                 RECT r;
1147                 GetWindowRect (hwnd, &r);
1148                 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1149                 SetWindowPos (hwnd, NULL, 0, 0,
1150                               r.right - r.left - ew, r.bottom - r.top - eh,
1151                               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1152             }
1153 #endif
1154             if (w != cols || h != rows || just_reconfigged) {
1155                 term_invalidate();
1156                 term_size (h, w, cfg.savelines);
1157                 back->size();
1158                 just_reconfigged = FALSE;
1159             }
1160         }
1161         ignore_size = FALSE;
1162         return 0;
1163       case WM_VSCROLL:
1164         switch (LOWORD(wParam)) {
1165           case SB_BOTTOM: term_scroll(-1, 0); break;
1166           case SB_TOP: term_scroll(+1, 0); break;
1167           case SB_LINEDOWN: term_scroll (0, +1); break;
1168           case SB_LINEUP: term_scroll (0, -1); break;
1169           case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1170           case SB_PAGEUP: term_scroll (0, -rows/2); break;
1171           case SB_THUMBPOSITION: case SB_THUMBTRACK:
1172             term_scroll (1, HIWORD(wParam)); break;
1173         }
1174         break; 
1175      case WM_PALETTECHANGED:
1176         if ((HWND) wParam != hwnd && pal != NULL) {
1177             HDC hdc = get_ctx();
1178             if (hdc) {
1179                 if (RealizePalette (hdc) > 0)
1180                     UpdateColors (hdc);
1181                 free_ctx (hdc);
1182             }
1183         }
1184         break;
1185       case WM_QUERYNEWPALETTE:
1186         if (pal != NULL) {
1187             HDC hdc = get_ctx();
1188             if (hdc) {
1189                 if (RealizePalette (hdc) > 0)
1190                     UpdateColors (hdc);
1191                 free_ctx (hdc);
1192                 return TRUE;
1193             }
1194         }
1195         return FALSE;
1196       case WM_KEYDOWN:
1197       case WM_SYSKEYDOWN:
1198         /*
1199          * Add the scan code and keypress timing to the random
1200          * number noise, if we're using ssh.
1201          */
1202         if (cfg.protocol == PROT_SSH)
1203             noise_ultralight(lParam);
1204
1205         /*
1206          * We don't do TranslateMessage since it disassociates the
1207          * resulting CHAR message from the KEYDOWN that sparked it,
1208          * which we occasionally don't want. Instead, we process
1209          * KEYDOWN, and call the Win32 translator functions so that
1210          * we get the translations under _our_ control.
1211          */
1212         {
1213             unsigned char buf[20];
1214             int len;
1215
1216             len = TranslateKey (wParam, lParam, buf);
1217             if (len == -1)
1218                 return DefWindowProc (hwnd, message, wParam, lParam);
1219             ldisc->send (buf, len);
1220         }
1221         return 0;
1222       case WM_KEYUP:
1223       case WM_SYSKEYUP:
1224         /*
1225          * We handle KEYUP ourselves in order to distinghish left
1226          * and right Alt or Control keys, which Windows won't do
1227          * right if left to itself. See also the special processing
1228          * at the top of TranslateKey.
1229          */
1230         {
1231             BYTE keystate[256];
1232             int ret = GetKeyboardState(keystate);
1233             if (ret && wParam == VK_MENU) {
1234                 if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
1235                 else keystate[VK_LMENU] = 0;
1236                 SetKeyboardState (keystate);
1237             }
1238             if (ret && wParam == VK_CONTROL) {
1239                 if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
1240                 else keystate[VK_LCONTROL] = 0;
1241                 SetKeyboardState (keystate);
1242             }
1243         }
1244         /*
1245          * We don't return here, in order to allow Windows to do
1246          * its own KEYUP processing as well.
1247          */
1248         break;
1249       case WM_CHAR:
1250       case WM_SYSCHAR:
1251         /*
1252          * Nevertheless, we are prepared to deal with WM_CHAR
1253          * messages, should they crop up. So if someone wants to
1254          * post the things to us as part of a macro manoeuvre,
1255          * we're ready to cope.
1256          */
1257         {
1258             char c = xlat_kbd2tty((unsigned char)wParam);
1259             ldisc->send (&c, 1);
1260         }
1261         return 0;
1262     }
1263
1264     return DefWindowProc (hwnd, message, wParam, lParam);
1265 }
1266
1267 /*
1268  * Draw a line of text in the window, at given character
1269  * coordinates, in given attributes.
1270  *
1271  * We are allowed to fiddle with the contents of `text'.
1272  */
1273 void do_text (Context ctx, int x, int y, char *text, int len,
1274               unsigned long attr) {
1275     int lattr = 0;      /* Will be arg later for line attribute type */
1276     COLORREF fg, bg, t;
1277     int nfg, nbg, nfont;
1278     HDC hdc = ctx;
1279     RECT line_box;
1280     int force_manual_underline = 0;
1281 static int *IpDx = 0, IpDxLEN = 0;;
1282
1283     if (len>IpDxLEN || IpDx[0] != font_width*(1+!lattr)) {
1284         int i;
1285         if (len>IpDxLEN) {
1286             sfree(IpDx);
1287             IpDx = smalloc((len+16)*sizeof(int));
1288             IpDxLEN = (len+16);
1289         }
1290         for(i=0; i<len; i++)
1291             IpDx[i] = font_width;
1292     }
1293
1294     x *= font_width;
1295     y *= font_height;
1296
1297     if (lattr) x *= 2;
1298
1299     if (attr & ATTR_ACTCURS) {
1300         attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
1301         attr ^= ATTR_CUR_XOR;
1302     }
1303
1304     nfont = 0;
1305     if (cfg.vtmode == VT_OEMONLY)
1306         nfont |= FONT_OEM;
1307
1308     /*
1309      * Map high-half characters in order to approximate ISO using
1310      * OEM character set. No characters are missing if the OEM codepage
1311      * is CP850.
1312      */
1313     if (nfont & FONT_OEM) {
1314         int i;
1315         for (i=0; i<len; i++)
1316             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1317 #if 0
1318                 /* This is CP850 ... perfect translation */
1319                 static const char oemhighhalf[] =
1320                     "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1321                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1322                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1323                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1324                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1325                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1326                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1327                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1328                     "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1329                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1330                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1331                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1332                     ;
1333 #endif
1334                 /* This is CP437 ... junk translation */
1335                 static const unsigned char oemhighhalf[] = {
1336                     0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1337                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1338                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1339                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1340                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1341                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1342                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1343                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1344                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1345                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1346                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1347                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1348                 };
1349
1350                 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1351             }
1352     }
1353
1354     if (attr & ATTR_GBCHR) {
1355         int i;
1356         /*
1357          * GB mapping: map # to pound, and everything else stays
1358          * normal.
1359          */
1360         for (i=0; i<len; i++)
1361             if (text[i] == '#')
1362                 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1363     } else if (attr & ATTR_LINEDRW) {
1364         int i;
1365         /* ISO 8859-1 */
1366         static const char poorman[] =
1367             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1368
1369         /* CP437 */
1370         static const char oemmap_437[] =
1371             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1372             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1373
1374         /* CP850 */
1375         static const char oemmap_850[] =
1376             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1377             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1378
1379         /* Poor windows font ... eg: windows courier */
1380         static const char oemmap[] =
1381             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1382             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1383
1384         /*
1385          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1386          * VT100 line drawing chars; everything else stays normal.
1387          */
1388         switch (cfg.vtmode) {
1389           case VT_XWINDOWS:
1390             for (i=0; i<len; i++)
1391                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1392                     text[i] += '\x01' - '\x60';
1393             break;
1394           case VT_OEMANSI:
1395             /* Make sure we actually have an OEM font */
1396             if (fonts[nfont|FONT_OEM]) { 
1397           case VT_OEMONLY:
1398                 nfont |= FONT_OEM;
1399                 for (i=0; i<len; i++)
1400                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1401                         text[i] = oemmap[(unsigned char)text[i] - 0x60];
1402                 break;
1403             }
1404           case VT_POORMAN:
1405             for (i=0; i<len; i++)
1406                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1407                     text[i] = poorman[(unsigned char)text[i] - 0x60];
1408             break;
1409         }
1410     }
1411
1412     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1413     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1414     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1415         nfont |= FONT_BOLD;
1416     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1417         nfont |= FONT_UNDERLINE;
1418     if (!fonts[nfont]) 
1419     {
1420         if (nfont&FONT_UNDERLINE)
1421             force_manual_underline = 1;
1422         /* Don't do the same for manual bold, it could be bad news. */
1423
1424         nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1425     }
1426     if (attr & ATTR_REVERSE) {
1427         t = nfg; nfg = nbg; nbg = t;
1428     }
1429     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1430         nfg++;
1431     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1432         nbg++;
1433     fg = colours[nfg];
1434     bg = colours[nbg];
1435     SelectObject (hdc, fonts[nfont]);
1436     SetTextColor (hdc, fg);
1437     SetBkColor (hdc, bg);
1438     SetBkMode (hdc, OPAQUE);
1439     line_box.left   = x;
1440     line_box.top    = y;
1441     line_box.right  = x+font_width*len;
1442     line_box.bottom = y+font_height;
1443     ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1444     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1445         SetBkMode (hdc, TRANSPARENT);
1446
1447        /* GRR: This draws the character outside it's box and can leave
1448         * 'droppings' even with the clip box! I suppose I could loop it
1449         * one character at a time ... yuk. */
1450         ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1451     }
1452     if (force_manual_underline || 
1453             (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1454         HPEN oldpen;
1455         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1456         MoveToEx (hdc, x, y+descent, NULL);
1457         LineTo (hdc, x+len*font_width, y+descent);
1458         oldpen = SelectObject (hdc, oldpen);
1459         DeleteObject (oldpen);
1460     }
1461     if (attr & ATTR_PASCURS) {
1462         POINT pts[5];
1463         HPEN oldpen;
1464         pts[0].x = pts[1].x = pts[4].x = x;
1465         pts[2].x = pts[3].x = x+font_width-1;
1466         pts[0].y = pts[3].y = pts[4].y = y;
1467         pts[1].y = pts[2].y = y+font_height-1;
1468         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1469         Polyline (hdc, pts, 5);
1470         oldpen = SelectObject (hdc, oldpen);
1471         DeleteObject (oldpen);
1472     }
1473 }
1474
1475 /*
1476  * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
1477  * codes. Returns number of bytes used.
1478  */
1479 static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
1480     unsigned char *p = output;
1481     BYTE keystate[256];
1482     int ret, code;
1483     int cancel_alt = FALSE;
1484
1485     /*
1486      * Get hold of the keyboard state, because we'll need it a few
1487      * times shortly.
1488      */
1489     ret = GetKeyboardState(keystate);
1490
1491     /* 
1492      * Record that we pressed key so the scroll window can be reset, but
1493      * be careful to avoid Shift-UP/Down
1494      */
1495     if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1496         seen_key_event = 1; 
1497     }
1498
1499     /* 
1500      * Windows does not always want to distinguish left and right
1501      * Alt or Control keys. Thus we keep track of them ourselves.
1502      * See also the WM_KEYUP handler.
1503      */
1504     if (wParam == VK_MENU) {
1505         if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
1506         else keystate[VK_LMENU] = 0x80;
1507         SetKeyboardState (keystate);
1508         return 0;
1509     }
1510     if (wParam == VK_CONTROL) {
1511         if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
1512         else keystate[VK_LCONTROL] = 0x80;
1513         SetKeyboardState (keystate);
1514         return 0;
1515     }
1516
1517     /*
1518      * Prepend ESC, and cancel ALT, if ALT was pressed at the time
1519      * and it wasn't AltGr.
1520      */
1521     if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
1522         *p++ = 0x1B;
1523         cancel_alt = TRUE;
1524     }
1525
1526     /*
1527      * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
1528      * so we do it first.
1529      */
1530     if (cfg.nethack_keypad) {
1531         int shift = keystate[VK_SHIFT] & 0x80;
1532         /*
1533          * NB the shifted versions only work with numlock off.
1534          */
1535         switch ( (lParam >> 16) & 0x1FF ) {
1536           case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
1537           case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
1538           case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
1539           case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
1540           case 0x04C: *p++ = '.'; return p - output;
1541           case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
1542           case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
1543           case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
1544           case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
1545           case 0x053: *p++ = '.'; return p - output;
1546         }
1547     }
1548
1549     /*
1550      * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
1551      * events: we'll deal with those now.
1552      */
1553     if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
1554         SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1555         return 0;
1556     }
1557     if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
1558         SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1559         return 0;
1560     }
1561     if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
1562         return -1;
1563     }
1564     if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
1565         SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1566         return -1;
1567     }
1568
1569     /*
1570      * In general, the strategy is to see what the Windows keymap
1571      * translation has to say for itself, and then process function
1572      * keys and suchlike ourselves if that fails. But first we must
1573      * deal with the small number of special cases which the
1574      * Windows keymap translator thinks it can do but gets wrong.
1575      *
1576      * First special case: we might want the Backspace key to send
1577      * 0x7F not 0x08.
1578      */
1579     if (wParam == VK_BACK) {
1580         *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1581         return p - output;
1582     }
1583
1584     /*
1585      * Control-Space should send ^@ (0x00), not Space.
1586      */
1587     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
1588         *p++ = 0x00;
1589         return p - output;
1590     }
1591
1592     if (app_keypad_keys) {
1593         /*
1594          * If we're in applications keypad mode, we have to process it
1595          * before char-map translation, because it will pre-empt lots
1596          * of stuff, even if NumLock is off.
1597          */
1598         if (ret) {
1599             /*
1600              * Hack to ensure NumLock doesn't interfere with
1601              * perception of Shift for Keypad Plus. I don't pretend
1602              * to understand this, but it seems to work as is.
1603              * Leave it alone, or die.
1604              */
1605             keystate[VK_NUMLOCK] = 0;
1606             SetKeyboardState (keystate);
1607             GetKeyboardState (keystate);
1608         }
1609         switch ( (lParam >> 16) & 0x1FF ) {
1610           case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
1611           case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
1612           case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
1613           case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
1614           case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
1615           case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
1616           case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
1617           case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
1618           case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
1619           case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
1620           case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
1621             p += sprintf((char *)p,
1622                          (ret && (keystate[VK_SHIFT] & 0x80)) ?
1623                          "\x1BOm" : "\x1BOl");
1624             return p - output;
1625           case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
1626           case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
1627           case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
1628           case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
1629           case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
1630           case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
1631         }
1632     }
1633
1634     /*
1635      * Shift-Tab should send ESC [ Z.
1636      */
1637     if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_TAB) {
1638         *p++ = 0x1B;                   /* ESC */
1639         *p++ = '[';
1640         *p++ = 'Z';
1641         return p - output;
1642     }
1643
1644     /*
1645      * Before doing Windows charmap translation, remove LeftALT
1646      * from the keymap, since its sole effect should be to prepend
1647      * ESC, which we've already done. Note that removal of LeftALT
1648      * has to happen _after_ the above call to SetKeyboardState, or
1649      * dire things will befall.
1650      */
1651     if (cancel_alt) {
1652         keystate[VK_MENU] = keystate[VK_RMENU];
1653         keystate[VK_LMENU] = 0;
1654     }
1655
1656     /*
1657      * Attempt the Windows char-map translation.
1658      */
1659     if (ret) {
1660         WORD chr;
1661         int r;
1662         BOOL capsOn=keystate[VK_CAPITAL] !=0;
1663
1664         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1665         if(cfg.xlat_capslockcyr)
1666             keystate[VK_CAPITAL] = 0;
1667
1668         r = ToAscii (wParam, (lParam >> 16) & 0xFF,
1669                      keystate, &chr, 0);
1670
1671         if(capsOn)
1672             chr = xlat_latkbd2win((unsigned char)(chr & 0xFF));
1673         if (r == 1) {
1674             *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF));
1675             return p - output;
1676         }
1677     }
1678
1679     /*
1680      * OK, we haven't had a key code from the keymap translation.
1681      * We'll try our various special cases and function keys, and
1682      * then give up. (There's nothing wrong with giving up:
1683      * Scrollock, Pause/Break, and of course the various buckybit
1684      * keys all produce KEYDOWN events that we really _do_ want to
1685      * ignore.)
1686      */
1687
1688     /*
1689      * Control-2 should return ^@ (0x00), Control-6 should return
1690      * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
1691      * the DOS keyboard handling did it, and we have nothing better
1692      * to do with the key combo in question, we'll also map
1693      * Control-Backquote to ^\ (0x1C).
1694      *
1695      * In addition a real VT100 maps Ctrl-3/4/5/7 and 8.
1696      */
1697     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
1698         *p++ = 0x00;
1699         return p - output;
1700     }
1701     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '3') {
1702         *p++ = 0x1B;
1703         return p - output;
1704     }
1705     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '4') {
1706         *p++ = 0x1C;
1707         return p - output;
1708     }
1709     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '5') {
1710         *p++ = 0x1D;
1711         return p - output;
1712     }
1713     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
1714         *p++ = 0x1E;
1715         return p - output;
1716     }
1717     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '7') {
1718         *p++ = 0x1F;
1719         return p - output;
1720     }
1721     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '8') {
1722         *p++ = 0x7F;
1723         return p - output;
1724     }
1725     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
1726         *p++ = 0x1F;
1727         return p - output;
1728     }
1729     if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
1730         *p++ = 0x1C;
1731         return p - output;
1732     }
1733
1734     /*
1735      * First, all the keys that do tilde codes. (ESC '[' nn '~',
1736      * for integer decimal nn.)
1737      *
1738      * We also deal with the weird ones here. Linux VCs replace F1
1739      * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1740      * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1741      * respectively.
1742      */
1743     code = 0;
1744     switch (wParam) {
1745       case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1746       case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1747       case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1748       case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1749       case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1750       case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1751       case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1752       case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1753       case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1754       case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1755       case VK_F11: code = 23; break;
1756       case VK_F12: code = 24; break;
1757       case VK_HOME: code = 1; break;
1758       case VK_INSERT: code = 2; break;
1759       case VK_DELETE: code = 3; break;
1760       case VK_END: code = 4; break;
1761       case VK_PRIOR: code = 5; break;
1762       case VK_NEXT: code = 6; break;
1763     }
1764     if (cfg.linux_funkeys && code >= 11 && code <= 15) {
1765         p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1766         return p - output;
1767     }
1768     if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1769         p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1770         return p - output;
1771     }
1772     if (code) {
1773         p += sprintf((char *)p, "\x1B[%d~", code);
1774         return p - output;
1775     }
1776
1777     /*
1778      * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1779      * some reason seems to send VK_CLEAR to Windows...).
1780      */
1781     switch (wParam) {
1782       case VK_UP:
1783         p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
1784         return p - output;
1785       case VK_DOWN:
1786         p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
1787         return p - output;
1788       case VK_RIGHT:
1789         p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
1790         return p - output;
1791       case VK_LEFT:
1792         p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
1793         return p - output;
1794       case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
1795     }
1796
1797     return 0;
1798 }
1799
1800 void set_title (char *title) {
1801     sfree (window_name);
1802     window_name = smalloc(1+strlen(title));
1803     strcpy (window_name, title);
1804     if (cfg.win_name_always || !IsIconic(hwnd))
1805         SetWindowText (hwnd, title);
1806 }
1807
1808 void set_icon (char *title) {
1809     sfree (icon_name);
1810     icon_name = smalloc(1+strlen(title));
1811     strcpy (icon_name, title);
1812     if (!cfg.win_name_always && IsIconic(hwnd))
1813         SetWindowText (hwnd, title);
1814 }
1815
1816 void set_sbar (int total, int start, int page) {
1817     SCROLLINFO si;
1818     si.cbSize = sizeof(si);
1819     si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
1820     si.nMin = 0;
1821     si.nMax = total - 1;
1822     si.nPage = page;
1823     si.nPos = start;
1824     if (hwnd)
1825         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
1826 }
1827
1828 Context get_ctx(void) {
1829     HDC hdc;
1830     if (hwnd) {
1831         hdc = GetDC (hwnd);
1832         if (hdc && pal)
1833             SelectPalette (hdc, pal, FALSE);
1834         return hdc;
1835     } else
1836         return NULL;
1837 }
1838
1839 void free_ctx (Context ctx) {
1840     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
1841     ReleaseDC (hwnd, ctx);
1842 }
1843
1844 static void real_palette_set (int n, int r, int g, int b) {
1845     if (pal) {
1846         logpal->palPalEntry[n].peRed = r;
1847         logpal->palPalEntry[n].peGreen = g;
1848         logpal->palPalEntry[n].peBlue = b;
1849         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
1850         colours[n] = PALETTERGB(r, g, b);
1851         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1852     } else
1853         colours[n] = RGB(r, g, b);
1854 }
1855
1856 void palette_set (int n, int r, int g, int b) {
1857     static const int first[21] = {
1858         0, 2, 4, 6, 8, 10, 12, 14,
1859         1, 3, 5, 7, 9, 11, 13, 15,
1860         16, 17, 18, 20, 22
1861     };
1862     real_palette_set (first[n], r, g, b);
1863     if (first[n] >= 18)
1864         real_palette_set (first[n]+1, r, g, b);
1865     if (pal) {
1866         HDC hdc = get_ctx();
1867         UnrealizeObject (pal);
1868         RealizePalette (hdc);
1869         free_ctx (hdc);
1870     }
1871 }
1872
1873 void palette_reset (void) {
1874     int i;
1875
1876     for (i = 0; i < NCOLOURS; i++) {
1877         if (pal) {
1878             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1879             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1880             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1881             logpal->palPalEntry[i].peFlags = 0;
1882             colours[i] = PALETTERGB(defpal[i].rgbtRed,
1883                                     defpal[i].rgbtGreen,
1884                                     defpal[i].rgbtBlue);
1885         } else
1886             colours[i] = RGB(defpal[i].rgbtRed,
1887                              defpal[i].rgbtGreen,
1888                              defpal[i].rgbtBlue);
1889     }
1890
1891     if (pal) {
1892         HDC hdc;
1893         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
1894         hdc = get_ctx();
1895         RealizePalette (hdc);
1896         free_ctx (hdc);
1897     }
1898 }
1899
1900 void write_clip (void *data, int len) {
1901     HGLOBAL clipdata;
1902     void *lock;
1903
1904     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1905     if (!clipdata)
1906         return;
1907     lock = GlobalLock (clipdata);
1908     if (!lock)
1909         return;
1910     memcpy (lock, data, len);
1911     ((unsigned char *) lock) [len] = 0;
1912     GlobalUnlock (clipdata);
1913
1914     SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
1915     if (OpenClipboard (hwnd)) {
1916         EmptyClipboard();
1917         SetClipboardData (CF_TEXT, clipdata);
1918         CloseClipboard();
1919     } else
1920         GlobalFree (clipdata);
1921     SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
1922 }
1923
1924 void get_clip (void **p, int *len) {
1925     static HGLOBAL clipdata = NULL;
1926
1927     if (!p) {
1928         if (clipdata)
1929             GlobalUnlock (clipdata);
1930         clipdata = NULL;
1931         return;
1932     } else {
1933         if (OpenClipboard (NULL)) {
1934             clipdata = GetClipboardData (CF_TEXT);
1935             CloseClipboard();
1936             if (clipdata) {
1937                 *p = GlobalLock (clipdata);
1938                 if (*p) {
1939                     *len = strlen(*p);
1940                     return;
1941                 }
1942             }
1943         }
1944     }
1945
1946     *p = NULL;
1947     *len = 0;
1948 }
1949
1950 /*
1951  * Move `lines' lines from position `from' to position `to' in the
1952  * window.
1953  */
1954 void optimised_move (int to, int from, int lines) {
1955     RECT r;
1956     int min, max;
1957
1958     min = (to < from ? to : from);
1959     max = to + from - min;
1960
1961     r.left = 0; r.right = cols * font_width;
1962     r.top = min * font_height; r.bottom = (max+lines) * font_height;
1963     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
1964 }
1965
1966 /*
1967  * Print a message box and perform a fatal exit.
1968  */
1969 void fatalbox(char *fmt, ...) {
1970     va_list ap;
1971     char stuff[200];
1972
1973     va_start(ap, fmt);
1974     vsprintf(stuff, fmt, ap);
1975     va_end(ap);
1976     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
1977     exit(1);
1978 }
1979
1980 /*
1981  * Beep.
1982  */
1983 void beep(void) {
1984     MessageBeep(MB_OK);
1985 }