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