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