Peter Schellenbach's patch: re-implement the PuTTY cryptographic
[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 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 }