aa6fda5df7674b2f4b2eee2b9460c83a8f0cff6f
[u/mdw/putty] / 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 }