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