13cf177c90d6ef6fc301608929a8c86378acfc99
[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 #define WM_IGNORE_KEYMENU (WM_XUSER + 3)
40
41 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
42 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
43 static void cfgtopalette(void);
44 static void init_palette(void);
45 static void init_fonts(int);
46
47 static int extra_width, extra_height;
48
49 static int pending_netevent = 0;
50 static WPARAM pend_netevent_wParam = 0;
51 static LPARAM pend_netevent_lParam = 0;
52 static void enact_pending_netevent(void);
53
54 #define FONT_NORMAL 0
55 #define FONT_BOLD 1
56 #define FONT_UNDERLINE 2
57 #define FONT_BOLDUND 3
58 #define FONT_OEM 4
59 #define FONT_OEMBOLD 5
60 #define FONT_OEMBOLDUND 6
61 #define FONT_OEMUND 7
62 static HFONT fonts[8];
63 static int font_needs_hand_underlining;
64 static enum {
65 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
66 } bold_mode;
67 static enum {
68 UND_LINE, UND_FONT
69 } und_mode;
70 static int descent;
71
72 #define NCOLOURS 24
73 static COLORREF colours[NCOLOURS];
74 static HPALETTE pal;
75 static LPLOGPALETTE logpal;
76 static RGBTRIPLE defpal[NCOLOURS];
77
78 static HWND hwnd;
79
80 static int dbltime, lasttime, lastact;
81 static Mouse_Button lastbtn;
82
83 static char *window_name, *icon_name;
84
85 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
86 static char appname[] = "PuTTY";
87 WORD winsock_ver;
88 WSADATA wsadata;
89 WNDCLASS wndclass;
90 MSG msg;
91 int guess_width, guess_height;
92
93 putty_inst = inst;
94 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
95
96 winsock_ver = MAKEWORD(1, 1);
97 if (WSAStartup(winsock_ver, &wsadata)) {
98 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
99 MB_OK | MB_ICONEXCLAMATION);
100 return 1;
101 }
102 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
103 MessageBox(NULL, "WinSock version is incompatible with 1.1",
104 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
105 WSACleanup();
106 return 1;
107 }
108 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
109
110 InitCommonControls();
111
112 /*
113 * Process the command line.
114 */
115 {
116 char *p;
117
118 default_protocol = DEFAULT_PROTOCOL;
119 default_port = DEFAULT_PORT;
120
121 do_defaults(NULL);
122
123 p = cmdline;
124 while (*p && isspace(*p)) p++;
125
126 /*
127 * Process command line options first. Yes, this can be
128 * done better, and it will be as soon as I have the
129 * energy...
130 */
131 while (*p == '-') {
132 char *q = p + strcspn(p, " \t");
133 p++;
134 if (q == p + 3 &&
135 tolower(p[0]) == 's' &&
136 tolower(p[1]) == 's' &&
137 tolower(p[2]) == 'h') {
138 default_protocol = cfg.protocol = PROT_SSH;
139 default_port = cfg.port = 22;
140 } else if (q == p + 3 &&
141 tolower(p[0]) == 'l' &&
142 tolower(p[1]) == 'o' &&
143 tolower(p[2]) == 'g') {
144 logfile = "putty.log";
145 }
146 p = q + strspn(q, " \t");
147 }
148
149 /*
150 * An initial @ means to activate a saved session.
151 */
152 if (*p == '@') {
153 do_defaults (p+1);
154 if (!*cfg.host && !do_config()) {
155 WSACleanup();
156 return 0;
157 }
158 } else if (*p == '&') {
159 /*
160 * An initial & means we've been given a command line
161 * containing the hex value of a HANDLE for a file
162 * mapping object, which we must then extract as a
163 * config.
164 */
165 HANDLE filemap;
166 Config *cp;
167 if (sscanf(p+1, "%p", &filemap) == 1 &&
168 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
169 0, 0, sizeof(Config))) != NULL) {
170 cfg = *cp;
171 UnmapViewOfFile(cp);
172 CloseHandle(filemap);
173 } else if (!do_config()) {
174 WSACleanup();
175 return 0;
176 }
177 } else if (*p) {
178 char *q = p;
179 /*
180 * If the hostname starts with "telnet:", set the
181 * protocol to Telnet and process the string as a
182 * Telnet URL.
183 */
184 if (!strncmp(q, "telnet:", 7)) {
185 char c;
186
187 q += 7;
188 if (q[0] == '/' && q[1] == '/')
189 q += 2;
190 cfg.protocol = PROT_TELNET;
191 p = q;
192 while (*p && *p != ':' && *p != '/') p++;
193 c = *p;
194 if (*p)
195 *p++ = '\0';
196 if (c == ':')
197 cfg.port = atoi(p);
198 else
199 cfg.port = -1;
200 strncpy (cfg.host, q, sizeof(cfg.host)-1);
201 cfg.host[sizeof(cfg.host)-1] = '\0';
202 } else {
203 while (*p && !isspace(*p)) p++;
204 if (*p)
205 *p++ = '\0';
206 strncpy (cfg.host, q, sizeof(cfg.host)-1);
207 cfg.host[sizeof(cfg.host)-1] = '\0';
208 while (*p && isspace(*p)) p++;
209 if (*p)
210 cfg.port = atoi(p);
211 else
212 cfg.port = -1;
213 }
214 } else {
215 if (!do_config()) {
216 WSACleanup();
217 return 0;
218 }
219 }
220 }
221
222 /*
223 * Select protocol. This is farmed out into a table in a
224 * separate file to enable an ssh-free variant.
225 */
226 {
227 int i;
228 back = NULL;
229 for (i = 0; backends[i].backend != NULL; i++)
230 if (backends[i].protocol == cfg.protocol) {
231 back = backends[i].backend;
232 break;
233 }
234 if (back == NULL) {
235 MessageBox(NULL, "Unsupported protocol number found",
236 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
237 WSACleanup();
238 return 1;
239 }
240 }
241
242 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
243
244 if (!prev) {
245 wndclass.style = 0;
246 wndclass.lpfnWndProc = WndProc;
247 wndclass.cbClsExtra = 0;
248 wndclass.cbWndExtra = 0;
249 wndclass.hInstance = inst;
250 wndclass.hIcon = LoadIcon (inst,
251 MAKEINTRESOURCE(IDI_MAINICON));
252 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
253 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
254 wndclass.lpszMenuName = NULL;
255 wndclass.lpszClassName = appname;
256
257 RegisterClass (&wndclass);
258 }
259
260 hwnd = NULL;
261
262 savelines = cfg.savelines;
263 term_init();
264
265 cfgtopalette();
266
267 /*
268 * Guess some defaults for the window size. This all gets
269 * updated later, so we don't really care too much. However, we
270 * do want the font width/height guesses to correspond to a
271 * large font rather than a small one...
272 */
273
274 font_width = 10;
275 font_height = 20;
276 extra_width = 25;
277 extra_height = 28;
278 term_size (cfg.height, cfg.width, cfg.savelines);
279 guess_width = extra_width + font_width * cols;
280 guess_height = extra_height + font_height * rows;
281 {
282 RECT r;
283 HWND w = GetDesktopWindow();
284 GetWindowRect (w, &r);
285 if (guess_width > r.right - r.left)
286 guess_width = r.right - r.left;
287 if (guess_height > r.bottom - r.top)
288 guess_height = r.bottom - r.top;
289 }
290
291 {
292 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
293 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
294 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
295 hwnd = CreateWindow (appname, appname,
296 winmode,
297 CW_USEDEFAULT, CW_USEDEFAULT,
298 guess_width, guess_height,
299 NULL, NULL, inst, NULL);
300 }
301
302 /*
303 * Initialise the fonts, simultaneously correcting the guesses
304 * for font_{width,height}.
305 */
306 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
307 und_mode = UND_FONT;
308 init_fonts(0);
309
310 /*
311 * Correct the guesses for extra_{width,height}.
312 */
313 {
314 RECT cr, wr;
315 GetWindowRect (hwnd, &wr);
316 GetClientRect (hwnd, &cr);
317 extra_width = wr.right - wr.left - cr.right + cr.left;
318 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
319 }
320
321 /*
322 * Resize the window, now we know what size we _really_ want it
323 * to be.
324 */
325 guess_width = extra_width + font_width * cols;
326 guess_height = extra_height + font_height * rows;
327 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
328 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
329 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
330
331 /*
332 * Initialise the scroll bar.
333 */
334 {
335 SCROLLINFO si;
336
337 si.cbSize = sizeof(si);
338 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
339 si.nMin = 0;
340 si.nMax = rows-1;
341 si.nPage = rows;
342 si.nPos = 0;
343 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
344 }
345
346 /*
347 * Start up the telnet connection.
348 */
349 {
350 char *error;
351 char msg[1024];
352 char *realhost;
353
354 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
355 if (error) {
356 sprintf(msg, "Unable to open connection:\n%s", error);
357 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
358 return 0;
359 }
360 window_name = icon_name = NULL;
361 sprintf(msg, "%s - PuTTY", realhost);
362 set_title (msg);
363 set_icon (msg);
364 }
365
366 session_closed = FALSE;
367
368 /*
369 * Set up the input and output buffers.
370 */
371 inbuf_head = 0;
372 outbuf_reap = outbuf_head = 0;
373
374 /*
375 * Prepare the mouse handler.
376 */
377 lastact = MA_NOTHING;
378 lastbtn = MB_NOTHING;
379 dbltime = GetDoubleClickTime();
380
381 /*
382 * Set up the session-control options on the system menu.
383 */
384 {
385 HMENU m = GetSystemMenu (hwnd, FALSE);
386 HMENU p,s;
387 int i;
388
389 AppendMenu (m, MF_SEPARATOR, 0, 0);
390 if (cfg.protocol == PROT_TELNET) {
391 p = CreateMenu();
392 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
393 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
394 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
395 AppendMenu (p, MF_SEPARATOR, 0, 0);
396 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
397 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
398 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
399 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
400 AppendMenu (p, MF_SEPARATOR, 0, 0);
401 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
402 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
403 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
404 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
405 AppendMenu (p, MF_SEPARATOR, 0, 0);
406 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
407 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
408 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
409 AppendMenu (m, MF_SEPARATOR, 0, 0);
410 }
411 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
412 AppendMenu (m, MF_SEPARATOR, 0, 0);
413 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
414 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
415 s = CreateMenu();
416 get_sesslist(TRUE);
417 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
418 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
419 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
420 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
421 AppendMenu (m, MF_SEPARATOR, 0, 0);
422 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
423 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
424 AppendMenu (m, MF_SEPARATOR, 0, 0);
425 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
426 }
427
428 /*
429 * Finally show the window!
430 */
431 ShowWindow (hwnd, show);
432
433 /*
434 * Set the palette up.
435 */
436 pal = NULL;
437 logpal = NULL;
438 init_palette();
439
440 has_focus = (GetForegroundWindow() == hwnd);
441 UpdateWindow (hwnd);
442
443 {
444 int timer_id = 0, long_timer = 0;
445
446 while (GetMessage (&msg, NULL, 0, 0) == 1) {
447 /* Sometimes DispatchMessage calls routines that use their own
448 * GetMessage loop, setup this timer so we get some control back.
449 *
450 * Also call term_update() from the timer so that if the host
451 * is sending data flat out we still do redraws.
452 */
453 if(timer_id && long_timer) {
454 KillTimer(hwnd, timer_id);
455 long_timer = timer_id = 0;
456 }
457 if(!timer_id)
458 timer_id = SetTimer(hwnd, 1, 20, NULL);
459 DispatchMessage (&msg);
460
461 /* This is too fast, but I'll leave it for now 'cause it shows
462 * how often term_update is called (far too often at times!)
463 */
464 term_blink(0);
465
466 /* Send the paste buffer if there's anything to send */
467 term_paste();
468
469 /* If there's nothing new in the queue then we can do everything
470 * we've delayed, reading the socket, writing, and repainting
471 * the window.
472 */
473 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
474 if (pending_netevent) {
475 enact_pending_netevent();
476
477 term_blink(1);
478 }
479 } else continue;
480 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
481 if (timer_id) {
482 KillTimer(hwnd, timer_id);
483 timer_id = 0;
484 }
485 if (inbuf_head)
486 term_out();
487 term_update();
488 if (!has_focus)
489 timer_id = SetTimer(hwnd, 1, 2000, NULL);
490 else if (cfg.blinktext)
491 timer_id = SetTimer(hwnd, 1, 250, NULL);
492 else
493 timer_id = SetTimer(hwnd, 1, 500, NULL);
494 long_timer = 1;
495 }
496 }
497 }
498
499 /*
500 * Clean up.
501 */
502 {
503 int i;
504 for (i=0; i<8; i++)
505 if (fonts[i])
506 DeleteObject(fonts[i]);
507 }
508 sfree(logpal);
509 if (pal)
510 DeleteObject(pal);
511 WSACleanup();
512
513 if (cfg.protocol == PROT_SSH) {
514 random_save_seed();
515 #ifdef MSCRYPTOAPI
516 crypto_wrapup();
517 #endif
518 }
519
520 return msg.wParam;
521 }
522
523 /*
524 * Actually do the job requested by a WM_NETEVENT
525 */
526 static void enact_pending_netevent(void) {
527 int i;
528 static int reentering = 0;
529
530 if (reentering)
531 return; /* don't unpend the pending */
532
533 pending_netevent = FALSE;
534
535 reentering = 1;
536 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
537 reentering = 0;
538
539 if (i < 0) {
540 char buf[1024];
541 switch (WSABASEERR + (-i) % 10000) {
542 case WSAECONNRESET:
543 sprintf(buf, "Connection reset by peer");
544 break;
545 default:
546 sprintf(buf, "Unexpected network error %d", -i);
547 break;
548 }
549 MessageBox(hwnd, buf, "PuTTY Fatal Error",
550 MB_ICONERROR | MB_OK);
551 PostQuitMessage(1);
552 } else if (i == 0) {
553 if (cfg.close_on_exit)
554 PostQuitMessage(0);
555 else {
556 session_closed = TRUE;
557 MessageBox(hwnd, "Connection closed by remote host",
558 "PuTTY", MB_OK | MB_ICONINFORMATION);
559 SetWindowText (hwnd, "PuTTY (inactive)");
560 }
561 }
562 }
563
564 /*
565 * Copy the colour palette from the configuration data into defpal.
566 * This is non-trivial because the colour indices are different.
567 */
568 static void cfgtopalette(void) {
569 int i;
570 static const int ww[] = {
571 6, 7, 8, 9, 10, 11, 12, 13,
572 14, 15, 16, 17, 18, 19, 20, 21,
573 0, 1, 2, 3, 4, 4, 5, 5
574 };
575
576 for (i=0; i<24; i++) {
577 int w = ww[i];
578 defpal[i].rgbtRed = cfg.colours[w][0];
579 defpal[i].rgbtGreen = cfg.colours[w][1];
580 defpal[i].rgbtBlue = cfg.colours[w][2];
581 }
582 }
583
584 /*
585 * Set up the colour palette.
586 */
587 static void init_palette(void) {
588 int i;
589 HDC hdc = GetDC (hwnd);
590 if (hdc) {
591 if (cfg.try_palette &&
592 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
593 logpal = smalloc(sizeof(*logpal)
594 - sizeof(logpal->palPalEntry)
595 + NCOLOURS * sizeof(PALETTEENTRY));
596 logpal->palVersion = 0x300;
597 logpal->palNumEntries = NCOLOURS;
598 for (i = 0; i < NCOLOURS; i++) {
599 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
600 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
601 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
602 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
603 }
604 pal = CreatePalette (logpal);
605 if (pal) {
606 SelectPalette (hdc, pal, FALSE);
607 RealizePalette (hdc);
608 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
609 FALSE);
610 }
611 }
612 ReleaseDC (hwnd, hdc);
613 }
614 if (pal)
615 for (i=0; i<NCOLOURS; i++)
616 colours[i] = PALETTERGB(defpal[i].rgbtRed,
617 defpal[i].rgbtGreen,
618 defpal[i].rgbtBlue);
619 else
620 for(i=0; i<NCOLOURS; i++)
621 colours[i] = RGB(defpal[i].rgbtRed,
622 defpal[i].rgbtGreen,
623 defpal[i].rgbtBlue);
624 }
625
626 /*
627 * Initialise all the fonts we will need. There may be as many as
628 * eight or as few as one. We also:
629 *
630 * - check the font width and height, correcting our guesses if
631 * necessary.
632 *
633 * - verify that the bold font is the same width as the ordinary
634 * one, and engage shadow bolding if not.
635 *
636 * - verify that the underlined font is the same width as the
637 * ordinary one (manual underlining by means of line drawing can
638 * be done in a pinch).
639 */
640 static void init_fonts(int pick_width) {
641 TEXTMETRIC tm;
642 int i;
643 int fsize[8];
644 HDC hdc;
645 int fw_dontcare, fw_bold;
646 int firstchar = ' ';
647
648 #ifdef CHECKOEMFONT
649 font_messup:
650 #endif
651 for (i=0; i<8; i++)
652 fonts[i] = NULL;
653
654 if (cfg.fontisbold) {
655 fw_dontcare = FW_BOLD;
656 fw_bold = FW_BLACK;
657 } else {
658 fw_dontcare = FW_DONTCARE;
659 fw_bold = FW_BOLD;
660 }
661
662 hdc = GetDC(hwnd);
663
664 font_height = cfg.fontheight;
665 font_width = pick_width;
666
667 #define f(i,c,w,u) \
668 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
669 c, OUT_DEFAULT_PRECIS, \
670 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
671 FIXED_PITCH | FF_DONTCARE, cfg.font)
672
673 if (cfg.vtmode != VT_OEMONLY) {
674 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
675
676 SelectObject (hdc, fonts[FONT_NORMAL]);
677 GetTextMetrics(hdc, &tm);
678 font_height = tm.tmHeight;
679 font_width = tm.tmAveCharWidth;
680
681 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
682
683 /*
684 * Some fonts, e.g. 9-pt Courier, draw their underlines
685 * outside their character cell. We successfully prevent
686 * screen corruption by clipping the text output, but then
687 * we lose the underline completely. Here we try to work
688 * out whether this is such a font, and if it is, we set a
689 * flag that causes underlines to be drawn by hand.
690 *
691 * Having tried other more sophisticated approaches (such
692 * as examining the TEXTMETRIC structure or requesting the
693 * height of a string), I think we'll do this the brute
694 * force way: we create a small bitmap, draw an underlined
695 * space on it, and test to see whether any pixels are
696 * foreground-coloured. (Since we expect the underline to
697 * go all the way across the character cell, we only search
698 * down a single column of the bitmap, half way across.)
699 */
700 {
701 HDC und_dc;
702 HBITMAP und_bm, und_oldbm;
703 int i, gotit;
704 COLORREF c;
705
706 und_dc = CreateCompatibleDC(hdc);
707 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
708 und_oldbm = SelectObject(und_dc, und_bm);
709 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
710 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
711 SetTextColor (und_dc, RGB(255,255,255));
712 SetBkColor (und_dc, RGB(0,0,0));
713 SetBkMode (und_dc, OPAQUE);
714 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
715 gotit = FALSE;
716 for (i = 0; i < font_height; i++) {
717 c = GetPixel(und_dc, font_width/2, i);
718 if (c != RGB(0,0,0))
719 gotit = TRUE;
720 }
721 SelectObject(und_dc, und_oldbm);
722 DeleteObject(und_bm);
723 DeleteDC(und_dc);
724 font_needs_hand_underlining = !gotit;
725 }
726
727 if (bold_mode == BOLD_FONT) {
728 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
729 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
730 }
731
732 if (cfg.vtmode == VT_OEMANSI) {
733 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
734 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
735
736 if (bold_mode == BOLD_FONT) {
737 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
738 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
739 }
740 }
741 }
742 else
743 {
744 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
745
746 SelectObject (hdc, fonts[FONT_OEM]);
747 GetTextMetrics(hdc, &tm);
748 font_height = tm.tmHeight;
749 font_width = tm.tmAveCharWidth;
750
751 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
752
753 if (bold_mode == BOLD_FONT) {
754 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
755 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
756 }
757 }
758 #undef f
759
760 descent = tm.tmAscent + 1;
761 if (descent >= font_height)
762 descent = font_height - 1;
763 firstchar = tm.tmFirstChar;
764
765 for (i=0; i<8; i++) {
766 if (fonts[i]) {
767 if (SelectObject (hdc, fonts[i]) &&
768 GetTextMetrics(hdc, &tm) )
769 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
770 else fsize[i] = -i;
771 }
772 else fsize[i] = -i;
773 }
774
775 ReleaseDC (hwnd, hdc);
776
777 /* ... This is wrong in OEM only mode */
778 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
779 (bold_mode == BOLD_FONT &&
780 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
781 und_mode = UND_LINE;
782 DeleteObject (fonts[FONT_UNDERLINE]);
783 if (bold_mode == BOLD_FONT)
784 DeleteObject (fonts[FONT_BOLDUND]);
785 }
786
787 if (bold_mode == BOLD_FONT &&
788 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
789 bold_mode = BOLD_SHADOW;
790 DeleteObject (fonts[FONT_BOLD]);
791 if (und_mode == UND_FONT)
792 DeleteObject (fonts[FONT_BOLDUND]);
793 }
794
795 #ifdef CHECKOEMFONT
796 /* With the fascist font painting it doesn't matter if the linedraw font
797 * isn't exactly the right size anymore so we don't have to check this.
798 */
799 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
800 if( cfg.fontcharset == OEM_CHARSET )
801 {
802 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
803 "different sizes. Using OEM-only mode instead",
804 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
805 cfg.vtmode = VT_OEMONLY;
806 }
807 else if( firstchar < ' ' )
808 {
809 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
810 "different sizes. Using XTerm mode instead",
811 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
812 cfg.vtmode = VT_XWINDOWS;
813 }
814 else
815 {
816 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
817 "different sizes. Using ISO8859-1 mode instead",
818 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
819 cfg.vtmode = VT_POORMAN;
820 }
821
822 for (i=0; i<8; i++)
823 if (fonts[i])
824 DeleteObject (fonts[i]);
825 goto font_messup;
826 }
827 #endif
828 }
829
830 void request_resize (int w, int h, int refont) {
831 int width, height;
832
833 /* If the window is maximized supress resizing attempts */
834 if(IsZoomed(hwnd)) return;
835
836 #ifdef CHECKOEMFONT
837 /* Don't do this in OEMANSI, you may get disable messages */
838 if (refont && w != cols && (cols==80 || cols==132)
839 && cfg.vtmode != VT_OEMANSI)
840 #else
841 if (refont && w != cols && (cols==80 || cols==132))
842 #endif
843 {
844 /* If font width too big for screen should we shrink the font more ? */
845 if (w==132)
846 font_width = ((font_width*cols+w/2)/w);
847 else
848 font_width = 0;
849 {
850 int i;
851 for (i=0; i<8; i++)
852 if (fonts[i])
853 DeleteObject(fonts[i]);
854 }
855 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
856 und_mode = UND_FONT;
857 init_fonts(font_width);
858 }
859 else
860 {
861 static int first_time = 1;
862 static RECT ss;
863
864 switch(first_time)
865 {
866 case 1:
867 /* Get the size of the screen */
868 if (GetClientRect(GetDesktopWindow(),&ss))
869 /* first_time = 0 */;
870 else { first_time = 2; break; }
871 case 0:
872 /* Make sure the values are sane */
873 width = (ss.right-ss.left-extra_width ) / font_width;
874 height = (ss.bottom-ss.top-extra_height ) / font_height;
875
876 if (w>width) w=width;
877 if (h>height) h=height;
878 if (w<15) w = 15;
879 if (h<1) w = 1;
880 }
881 }
882
883 width = extra_width + font_width * w;
884 height = extra_height + font_height * h;
885
886 SetWindowPos (hwnd, NULL, 0, 0, width, height,
887 SWP_NOACTIVATE | SWP_NOCOPYBITS |
888 SWP_NOMOVE | SWP_NOZORDER);
889 }
890
891 static void click (Mouse_Button b, int x, int y) {
892 int thistime = GetMessageTime();
893
894 if (lastbtn == b && thistime - lasttime < dbltime) {
895 lastact = (lastact == MA_CLICK ? MA_2CLK :
896 lastact == MA_2CLK ? MA_3CLK :
897 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
898 } else {
899 lastbtn = b;
900 lastact = MA_CLICK;
901 }
902 if (lastact != MA_NOTHING)
903 term_mouse (b, lastact, x, y);
904 lasttime = thistime;
905 }
906
907 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
908 WPARAM wParam, LPARAM lParam) {
909 HDC hdc;
910 static int ignore_size = FALSE;
911 static int ignore_clip = FALSE;
912 static int ignore_keymenu = TRUE;
913 static int just_reconfigged = FALSE;
914
915 switch (message) {
916 case WM_TIMER:
917 if (pending_netevent)
918 enact_pending_netevent();
919 if (inbuf_head)
920 term_out();
921 term_update();
922 return 0;
923 case WM_CREATE:
924 break;
925 case WM_CLOSE:
926 if (!cfg.warn_on_close || session_closed ||
927 MessageBox(hwnd, "Are you sure you want to close this session?",
928 "PuTTY Exit Confirmation",
929 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
930 DestroyWindow(hwnd);
931 return 0;
932 case WM_DESTROY:
933 PostQuitMessage (0);
934 return 0;
935 case WM_SYSCOMMAND:
936 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
937 case SC_KEYMENU:
938 if (ignore_keymenu)
939 return 0; /* don't put up system menu on Alt */
940 break;
941 case IDM_SHOWLOG:
942 showeventlog(hwnd);
943 break;
944 case IDM_NEWSESS:
945 case IDM_DUPSESS:
946 case IDM_SAVEDSESS:
947 {
948 char b[2048];
949 char c[30], *cl;
950 int freecl = FALSE;
951 STARTUPINFO si;
952 PROCESS_INFORMATION pi;
953 HANDLE filemap = NULL;
954
955 if (wParam == IDM_DUPSESS) {
956 /*
957 * Allocate a file-mapping memory chunk for the
958 * config structure.
959 */
960 SECURITY_ATTRIBUTES sa;
961 Config *p;
962
963 sa.nLength = sizeof(sa);
964 sa.lpSecurityDescriptor = NULL;
965 sa.bInheritHandle = TRUE;
966 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
967 &sa,
968 PAGE_READWRITE,
969 0,
970 sizeof(Config),
971 NULL);
972 if (filemap) {
973 p = (Config *)MapViewOfFile(filemap,
974 FILE_MAP_WRITE,
975 0, 0, sizeof(Config));
976 if (p) {
977 *p = cfg; /* structure copy */
978 UnmapViewOfFile(p);
979 }
980 }
981 sprintf(c, "putty &%p", filemap);
982 cl = c;
983 } else if (wParam == IDM_SAVEDSESS) {
984 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
985 cl = malloc(16 + strlen(session)); /* 8, but play safe */
986 if (!cl)
987 cl = NULL; /* not a very important failure mode */
988 else {
989 sprintf(cl, "putty @%s", session);
990 freecl = TRUE;
991 }
992 } else
993 cl = NULL;
994
995 GetModuleFileName (NULL, b, sizeof(b)-1);
996 si.cb = sizeof(si);
997 si.lpReserved = NULL;
998 si.lpDesktop = NULL;
999 si.lpTitle = NULL;
1000 si.dwFlags = 0;
1001 si.cbReserved2 = 0;
1002 si.lpReserved2 = NULL;
1003 CreateProcess (b, cl, NULL, NULL, TRUE,
1004 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1005
1006 if (filemap)
1007 CloseHandle(filemap);
1008 if (freecl)
1009 free(cl);
1010 }
1011 break;
1012 case IDM_RECONF:
1013 if (!do_reconfig(hwnd))
1014 break;
1015 just_reconfigged = TRUE;
1016 {
1017 int i;
1018 for (i=0; i<8; i++)
1019 if (fonts[i])
1020 DeleteObject(fonts[i]);
1021 }
1022 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1023 und_mode = UND_FONT;
1024 init_fonts(0);
1025 sfree(logpal);
1026 /* Telnet will change local echo -> remote if the remote asks */
1027 if (cfg.protocol != PROT_TELNET)
1028 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1029 if (pal)
1030 DeleteObject(pal);
1031 logpal = NULL;
1032 pal = NULL;
1033 cfgtopalette();
1034 init_palette();
1035
1036 /* Enable or disable the scroll bar, etc */
1037 {
1038 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1039
1040 nflg = flag;
1041 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1042 else nflg &= ~WS_VSCROLL;
1043 if (cfg.locksize)
1044 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1045 else
1046 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1047
1048 if (nflg != flag)
1049 {
1050 RECT cr, wr;
1051
1052 SetWindowLong(hwnd, GWL_STYLE, nflg);
1053 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1054 SetWindowPos(hwnd, NULL, 0,0,0,0,
1055 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1056 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1057 SWP_FRAMECHANGED);
1058
1059 GetWindowRect (hwnd, &wr);
1060 GetClientRect (hwnd, &cr);
1061 extra_width = wr.right - wr.left - cr.right + cr.left;
1062 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1063 }
1064 }
1065
1066 term_size(cfg.height, cfg.width, cfg.savelines);
1067 InvalidateRect(hwnd, NULL, TRUE);
1068 SetWindowPos (hwnd, NULL, 0, 0,
1069 extra_width + font_width * cfg.width,
1070 extra_height + font_height * cfg.height,
1071 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1072 SWP_NOMOVE | SWP_NOZORDER);
1073 if (IsIconic(hwnd)) {
1074 SetWindowText (hwnd,
1075 cfg.win_name_always ? window_name : icon_name);
1076 }
1077 break;
1078 case IDM_CLRSB:
1079 term_clrsb();
1080 break;
1081 case IDM_RESET:
1082 term_pwron();
1083 break;
1084 case IDM_TEL_AYT: back->special (TS_AYT); break;
1085 case IDM_TEL_BRK: back->special (TS_BRK); break;
1086 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1087 case IDM_TEL_EC: back->special (TS_EC); break;
1088 case IDM_TEL_EL: back->special (TS_EL); break;
1089 case IDM_TEL_GA: back->special (TS_GA); break;
1090 case IDM_TEL_NOP: back->special (TS_NOP); break;
1091 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1092 case IDM_TEL_AO: back->special (TS_AO); break;
1093 case IDM_TEL_IP: back->special (TS_IP); break;
1094 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1095 case IDM_TEL_EOR: back->special (TS_EOR); break;
1096 case IDM_TEL_EOF: back->special (TS_EOF); break;
1097 case IDM_ABOUT:
1098 showabout (hwnd);
1099 break;
1100 default:
1101 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1102 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1103 }
1104 }
1105 break;
1106
1107 #define X_POS(l) ((int)(short)LOWORD(l))
1108 #define Y_POS(l) ((int)(short)HIWORD(l))
1109
1110 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1111 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1112
1113 case WM_LBUTTONDOWN:
1114 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1115 TO_CHR_Y(Y_POS(lParam)));
1116 SetCapture(hwnd);
1117 return 0;
1118 case WM_LBUTTONUP:
1119 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1120 TO_CHR_Y(Y_POS(lParam)));
1121 ReleaseCapture();
1122 return 0;
1123 case WM_MBUTTONDOWN:
1124 SetCapture(hwnd);
1125 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1126 TO_CHR_X(X_POS(lParam)),
1127 TO_CHR_Y(Y_POS(lParam)));
1128 return 0;
1129 case WM_MBUTTONUP:
1130 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1131 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1132 TO_CHR_Y(Y_POS(lParam)));
1133 ReleaseCapture();
1134 return 0;
1135 case WM_RBUTTONDOWN:
1136 SetCapture(hwnd);
1137 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1138 TO_CHR_X(X_POS(lParam)),
1139 TO_CHR_Y(Y_POS(lParam)));
1140 return 0;
1141 case WM_RBUTTONUP:
1142 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1143 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1144 TO_CHR_Y(Y_POS(lParam)));
1145 ReleaseCapture();
1146 return 0;
1147 case WM_MOUSEMOVE:
1148 /*
1149 * Add the mouse position and message time to the random
1150 * number noise, if we're using ssh.
1151 */
1152 if (cfg.protocol == PROT_SSH)
1153 noise_ultralight(lParam);
1154
1155 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1156 Mouse_Button b;
1157 if (wParam & MK_LBUTTON)
1158 b = MB_SELECT;
1159 else if (wParam & MK_MBUTTON)
1160 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1161 else
1162 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1163 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1164 TO_CHR_Y(Y_POS(lParam)));
1165 }
1166 return 0;
1167 case WM_IGNORE_CLIP:
1168 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1169 break;
1170 case WM_IGNORE_KEYMENU:
1171 ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */
1172 break;
1173 case WM_DESTROYCLIPBOARD:
1174 if (!ignore_clip)
1175 term_deselect();
1176 ignore_clip = FALSE;
1177 return 0;
1178 case WM_PAINT:
1179 {
1180 PAINTSTRUCT p;
1181 hdc = BeginPaint (hwnd, &p);
1182 if (pal) {
1183 SelectPalette (hdc, pal, TRUE);
1184 RealizePalette (hdc);
1185 }
1186 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1187 p.rcPaint.right, p.rcPaint.bottom);
1188 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1189 SelectObject (hdc, GetStockObject(WHITE_PEN));
1190 EndPaint (hwnd, &p);
1191 }
1192 return 0;
1193 case WM_NETEVENT:
1194 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1195 * but the only one that's likely to try to overload us is FD_READ.
1196 * This means buffering just one is fine.
1197 */
1198 if (pending_netevent)
1199 enact_pending_netevent();
1200
1201 pending_netevent = TRUE;
1202 pend_netevent_wParam=wParam;
1203 pend_netevent_lParam=lParam;
1204 return 0;
1205 case WM_SETFOCUS:
1206 has_focus = TRUE;
1207 term_out();
1208 term_update();
1209 break;
1210 case WM_KILLFOCUS:
1211 has_focus = FALSE;
1212 term_out();
1213 term_update();
1214 break;
1215 case WM_IGNORE_SIZE:
1216 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1217 break;
1218 case WM_ENTERSIZEMOVE:
1219 EnableSizeTip(1);
1220 break;
1221 case WM_EXITSIZEMOVE:
1222 EnableSizeTip(0);
1223 break;
1224 case WM_SIZING:
1225 {
1226 int width, height, w, h, ew, eh;
1227 LPRECT r = (LPRECT)lParam;
1228
1229 width = r->right - r->left - extra_width;
1230 height = r->bottom - r->top - extra_height;
1231 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1232 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1233 UpdateSizeTip(hwnd, w, h);
1234 ew = width - w * font_width;
1235 eh = height - h * font_height;
1236 if (ew != 0) {
1237 if (wParam == WMSZ_LEFT ||
1238 wParam == WMSZ_BOTTOMLEFT ||
1239 wParam == WMSZ_TOPLEFT)
1240 r->left += ew;
1241 else
1242 r->right -= ew;
1243 }
1244 if (eh != 0) {
1245 if (wParam == WMSZ_TOP ||
1246 wParam == WMSZ_TOPRIGHT ||
1247 wParam == WMSZ_TOPLEFT)
1248 r->top += eh;
1249 else
1250 r->bottom -= eh;
1251 }
1252 if (ew || eh)
1253 return 1;
1254 else
1255 return 0;
1256 }
1257 /* break; (never reached) */
1258 case WM_SIZE:
1259 if (wParam == SIZE_MINIMIZED) {
1260 SetWindowText (hwnd,
1261 cfg.win_name_always ? window_name : icon_name);
1262 break;
1263 }
1264 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1265 SetWindowText (hwnd, window_name);
1266 if (!ignore_size) {
1267 int width, height, w, h;
1268 #if 0 /* we have fixed this using WM_SIZING now */
1269 int ew, eh;
1270 #endif
1271
1272 width = LOWORD(lParam);
1273 height = HIWORD(lParam);
1274 w = width / font_width; if (w < 1) w = 1;
1275 h = height / font_height; if (h < 1) h = 1;
1276 #if 0 /* we have fixed this using WM_SIZING now */
1277 ew = width - w * font_width;
1278 eh = height - h * font_height;
1279 if (ew != 0 || eh != 0) {
1280 RECT r;
1281 GetWindowRect (hwnd, &r);
1282 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1283 SetWindowPos (hwnd, NULL, 0, 0,
1284 r.right - r.left - ew, r.bottom - r.top - eh,
1285 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1286 }
1287 #endif
1288 if (w != cols || h != rows || just_reconfigged) {
1289 term_invalidate();
1290 term_size (h, w, cfg.savelines);
1291 back->size();
1292 just_reconfigged = FALSE;
1293 }
1294 }
1295 ignore_size = FALSE;
1296 return 0;
1297 case WM_VSCROLL:
1298 switch (LOWORD(wParam)) {
1299 case SB_BOTTOM: term_scroll(-1, 0); break;
1300 case SB_TOP: term_scroll(+1, 0); break;
1301 case SB_LINEDOWN: term_scroll (0, +1); break;
1302 case SB_LINEUP: term_scroll (0, -1); break;
1303 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1304 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1305 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1306 term_scroll (1, HIWORD(wParam)); break;
1307 }
1308 break;
1309 case WM_PALETTECHANGED:
1310 if ((HWND) wParam != hwnd && pal != NULL) {
1311 HDC hdc = get_ctx();
1312 if (hdc) {
1313 if (RealizePalette (hdc) > 0)
1314 UpdateColors (hdc);
1315 free_ctx (hdc);
1316 }
1317 }
1318 break;
1319 case WM_QUERYNEWPALETTE:
1320 if (pal != NULL) {
1321 HDC hdc = get_ctx();
1322 if (hdc) {
1323 if (RealizePalette (hdc) > 0)
1324 UpdateColors (hdc);
1325 free_ctx (hdc);
1326 return TRUE;
1327 }
1328 }
1329 return FALSE;
1330 case WM_KEYDOWN:
1331 case WM_SYSKEYDOWN:
1332 case WM_KEYUP:
1333 case WM_SYSKEYUP:
1334 /*
1335 * Add the scan code and keypress timing to the random
1336 * number noise, if we're using ssh.
1337 */
1338 if (cfg.protocol == PROT_SSH)
1339 noise_ultralight(lParam);
1340
1341 /*
1342 * We don't do TranslateMessage since it disassociates the
1343 * resulting CHAR message from the KEYDOWN that sparked it,
1344 * which we occasionally don't want. Instead, we process
1345 * KEYDOWN, and call the Win32 translator functions so that
1346 * we get the translations under _our_ control.
1347 */
1348 {
1349 unsigned char buf[20];
1350 int len;
1351
1352 len = TranslateKey (message, wParam, lParam, buf);
1353 if (len == -1)
1354 return DefWindowProc (hwnd, message, wParam, lParam);
1355 ldisc->send (buf, len);
1356 }
1357 return 0;
1358 case WM_CHAR:
1359 case WM_SYSCHAR:
1360 /*
1361 * Nevertheless, we are prepared to deal with WM_CHAR
1362 * messages, should they crop up. So if someone wants to
1363 * post the things to us as part of a macro manoeuvre,
1364 * we're ready to cope.
1365 */
1366 {
1367 char c = xlat_kbd2tty((unsigned char)wParam);
1368 ldisc->send (&c, 1);
1369 }
1370 return 0;
1371 }
1372
1373 return DefWindowProc (hwnd, message, wParam, lParam);
1374 }
1375
1376 /*
1377 * Draw a line of text in the window, at given character
1378 * coordinates, in given attributes.
1379 *
1380 * We are allowed to fiddle with the contents of `text'.
1381 */
1382 void do_text (Context ctx, int x, int y, char *text, int len,
1383 unsigned long attr, int lattr) {
1384 COLORREF fg, bg, t;
1385 int nfg, nbg, nfont;
1386 HDC hdc = ctx;
1387 RECT line_box;
1388 int force_manual_underline = 0;
1389 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1390 static int *IpDx = 0, IpDxLEN = 0;;
1391
1392 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1393 int i;
1394 if (len>IpDxLEN) {
1395 sfree(IpDx);
1396 IpDx = smalloc((len+16)*sizeof(int));
1397 IpDxLEN = (len+16);
1398 }
1399 for(i=0; i<IpDxLEN; i++)
1400 IpDx[i] = fnt_width;
1401 }
1402
1403 x *= fnt_width;
1404 y *= font_height;
1405
1406 if (attr & ATTR_ACTCURS) {
1407 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1408 attr ^= ATTR_CUR_XOR;
1409 }
1410
1411 nfont = 0;
1412 if (cfg.vtmode == VT_OEMONLY)
1413 nfont |= FONT_OEM;
1414
1415 /*
1416 * Map high-half characters in order to approximate ISO using
1417 * OEM character set. No characters are missing if the OEM codepage
1418 * is CP850.
1419 */
1420 if (nfont & FONT_OEM) {
1421 int i;
1422 for (i=0; i<len; i++)
1423 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1424 #if 0
1425 /* This is CP850 ... perfect translation */
1426 static const char oemhighhalf[] =
1427 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1428 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1429 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1430 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1431 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1432 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1433 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1434 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1435 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1436 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1437 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1438 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1439 ;
1440 #endif
1441 /* This is CP437 ... junk translation */
1442 static const unsigned char oemhighhalf[] = {
1443 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1444 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1445 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1446 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1447 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1448 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1449 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1450 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1451 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1452 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1453 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1454 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1455 };
1456
1457 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1458 }
1459 }
1460
1461 if (attr & ATTR_GBCHR) {
1462 int i;
1463 /*
1464 * GB mapping: map # to pound, and everything else stays
1465 * normal.
1466 */
1467 for (i=0; i<len; i++)
1468 if (text[i] == '#')
1469 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1470 } else if (attr & ATTR_LINEDRW) {
1471 int i;
1472 /* ISO 8859-1 */
1473 static const char poorman[] =
1474 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1475
1476 /* CP437 */
1477 static const char oemmap_437[] =
1478 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1479 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1480
1481 /* CP850 */
1482 static const char oemmap_850[] =
1483 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1484 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1485
1486 /* Poor windows font ... eg: windows courier */
1487 static const char oemmap[] =
1488 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1489 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1490
1491 /*
1492 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1493 * VT100 line drawing chars; everything else stays normal.
1494 */
1495 switch (cfg.vtmode) {
1496 case VT_XWINDOWS:
1497 for (i=0; i<len; i++)
1498 if (text[i] >= '\x60' && text[i] <= '\x7E')
1499 text[i] += '\x01' - '\x60';
1500 break;
1501 case VT_OEMANSI:
1502 /* Make sure we actually have an OEM font */
1503 if (fonts[nfont|FONT_OEM]) {
1504 case VT_OEMONLY:
1505 nfont |= FONT_OEM;
1506 for (i=0; i<len; i++)
1507 if (text[i] >= '\x60' && text[i] <= '\x7E')
1508 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1509 break;
1510 }
1511 case VT_POORMAN:
1512 for (i=0; i<len; i++)
1513 if (text[i] >= '\x60' && text[i] <= '\x7E')
1514 text[i] = poorman[(unsigned char)text[i] - 0x60];
1515 break;
1516 }
1517 }
1518
1519 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1520 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1521 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1522 nfont |= FONT_BOLD;
1523 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1524 nfont |= FONT_UNDERLINE;
1525 if (!fonts[nfont])
1526 {
1527 if (nfont&FONT_UNDERLINE)
1528 force_manual_underline = 1;
1529 /* Don't do the same for manual bold, it could be bad news. */
1530
1531 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1532 }
1533 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1534 force_manual_underline = 1;
1535 if (attr & ATTR_REVERSE) {
1536 t = nfg; nfg = nbg; nbg = t;
1537 }
1538 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1539 nfg++;
1540 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1541 nbg++;
1542 fg = colours[nfg];
1543 bg = colours[nbg];
1544 SelectObject (hdc, fonts[nfont]);
1545 SetTextColor (hdc, fg);
1546 SetBkColor (hdc, bg);
1547 SetBkMode (hdc, OPAQUE);
1548 line_box.left = x;
1549 line_box.top = y;
1550 line_box.right = x+fnt_width*len;
1551 line_box.bottom = y+font_height;
1552 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1553 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1554 SetBkMode (hdc, TRANSPARENT);
1555
1556 /* GRR: This draws the character outside it's box and can leave
1557 * 'droppings' even with the clip box! I suppose I could loop it
1558 * one character at a time ... yuk.
1559 *
1560 * Or ... I could do a test print with "W", and use +1 or -1 for this
1561 * shift depending on if the leftmost column is blank...
1562 */
1563 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1564 }
1565 if (force_manual_underline ||
1566 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1567 HPEN oldpen;
1568 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1569 MoveToEx (hdc, x, y+descent, NULL);
1570 LineTo (hdc, x+len*fnt_width, y+descent);
1571 oldpen = SelectObject (hdc, oldpen);
1572 DeleteObject (oldpen);
1573 }
1574 if (attr & ATTR_PASCURS) {
1575 POINT pts[5];
1576 HPEN oldpen;
1577 pts[0].x = pts[1].x = pts[4].x = x;
1578 pts[2].x = pts[3].x = x+fnt_width-1;
1579 pts[0].y = pts[3].y = pts[4].y = y;
1580 pts[1].y = pts[2].y = y+font_height-1;
1581 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1582 Polyline (hdc, pts, 5);
1583 oldpen = SelectObject (hdc, oldpen);
1584 DeleteObject (oldpen);
1585 }
1586 }
1587
1588 static int check_compose(int first, int second) {
1589
1590 static char * composetbl[] = {
1591 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1592 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1593 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1594 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1595 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1596 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1597 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1598 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1599 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1600 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1601 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1602 "\"uü", "'yý", "htþ", "\"yÿ",
1603 0};
1604
1605 char ** c;
1606 static int recurse = 0;
1607 int nc = -1;
1608
1609 if(0)
1610 {
1611 char buf[256];
1612 char * p;
1613 sprintf(buf, "cc(%d,%d)", first, second);
1614 for(p=buf; *p; p++)
1615 c_write1(*p);
1616 }
1617
1618 for(c=composetbl; *c; c++) {
1619 if( (*c)[0] == first && (*c)[1] == second)
1620 {
1621 return (*c)[2] & 0xFF;
1622 }
1623 }
1624
1625 if(recurse==0)
1626 {
1627 recurse=1;
1628 nc = check_compose(second, first);
1629 if(nc == -1)
1630 nc = check_compose(toupper(first), toupper(second));
1631 if(nc == -1)
1632 nc = check_compose(toupper(second), toupper(first));
1633 recurse=0;
1634 }
1635 return nc;
1636 }
1637
1638
1639 /*
1640 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1641 * codes. Returns number of bytes used or zero to drop the message
1642 * or -1 to forward the message to windows.
1643 */
1644 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1645 BYTE keystate[256];
1646 int scan, left_alt = 0, key_down, shift_state;
1647 int r, i, code;
1648 unsigned char * p = output;
1649
1650 static WORD keys[3];
1651 static int compose_state = 0;
1652 static int compose_char = 0;
1653 static WPARAM compose_key = 0;
1654
1655 r = GetKeyboardState(keystate);
1656 if (!r) memset(keystate, 0, sizeof(keystate));
1657 else
1658 {
1659 /* Note if AltGr was pressed and if it was used as a compose key */
1660 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1661 {
1662 keystate[VK_RMENU] = keystate[VK_MENU];
1663 if (!compose_state) compose_key = wParam;
1664 }
1665 if (wParam == VK_APPS && !compose_state)
1666 compose_key = wParam;
1667
1668 if (wParam == compose_key)
1669 {
1670 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1671 compose_state = 1;
1672 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1673 compose_state = 2;
1674 else
1675 compose_state = 0;
1676 }
1677 else if (compose_state==1 && wParam != VK_CONTROL)
1678 compose_state = 0;
1679
1680 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1681 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1682 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1683
1684 wParam = VK_EXECUTE;
1685
1686 /* UnToggle NUMLock */
1687 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1688 keystate[VK_NUMLOCK] ^= 1;
1689 }
1690
1691 /* And write back the 'adjusted' state */
1692 SetKeyboardState (keystate);
1693 }
1694
1695 /* Disable Auto repeat if required */
1696 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1697 return 0;
1698
1699 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1700 left_alt = 1;
1701
1702 key_down = ((HIWORD(lParam)&KF_UP)==0);
1703
1704 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1705 if (left_alt && (keystate[VK_CONTROL]&0x80))
1706 keystate[VK_MENU] = 0;
1707
1708 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1709 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1710 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1711
1712 /*
1713 * Record that we pressed key so the scroll window can be reset, but
1714 * be careful to avoid Shift-UP/Down
1715 */
1716 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1717 seen_key_event = 1;
1718 }
1719
1720 /* Make sure we're not pasting */
1721 if (key_down) term_nopaste();
1722
1723 if (compose_state>1 && left_alt) compose_state = 0;
1724
1725 /* Sanitize the number pad if not using a PC NumPad */
1726 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1727 || cfg.nethack_keypad || compose_state )
1728 {
1729 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1730 {
1731 int nParam = 0;
1732 switch(wParam)
1733 {
1734 case VK_INSERT: nParam = VK_NUMPAD0; break;
1735 case VK_END: nParam = VK_NUMPAD1; break;
1736 case VK_DOWN: nParam = VK_NUMPAD2; break;
1737 case VK_NEXT: nParam = VK_NUMPAD3; break;
1738 case VK_LEFT: nParam = VK_NUMPAD4; break;
1739 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1740 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1741 case VK_HOME: nParam = VK_NUMPAD7; break;
1742 case VK_UP: nParam = VK_NUMPAD8; break;
1743 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1744 case VK_DELETE: nParam = VK_DECIMAL; break;
1745 }
1746 if (nParam)
1747 {
1748 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1749 wParam = nParam;
1750 }
1751 }
1752 }
1753
1754 /* If a key is pressed and AltGr is not active */
1755 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1756 {
1757 /* Okay, prepare for most alts then ...*/
1758 if (left_alt) *p++ = '\033';
1759
1760 /* Lets see if it's a pattern we know all about ... */
1761 if (wParam == VK_PRIOR && shift_state == 1) {
1762 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1763 return 0;
1764 }
1765 if (wParam == VK_NEXT && shift_state == 1) {
1766 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1767 return 0;
1768 }
1769 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1770 return -1;
1771 }
1772 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1773
1774 SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0);
1775 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1776 SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 0);
1777 return -1;
1778 }
1779
1780 /* Nethack keypad */
1781 if (cfg.nethack_keypad && !left_alt) {
1782 switch(wParam) {
1783 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1784 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1785 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1786 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1787 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1788 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1789 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1790 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1791 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1792 }
1793 }
1794
1795 /* Application Keypad */
1796 if (!left_alt) {
1797 int xkey = 0;
1798
1799 if ( cfg.funky_type == 0 ||
1800 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1801 case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1802 case VK_DIVIDE: xkey = 'Q'; break;
1803 case VK_MULTIPLY:xkey = 'R'; break;
1804 case VK_SUBTRACT:xkey = 'S'; break;
1805 }
1806 if(app_keypad_keys) switch(wParam) {
1807 case VK_NUMPAD0: xkey = 'p'; break;
1808 case VK_NUMPAD1: xkey = 'q'; break;
1809 case VK_NUMPAD2: xkey = 'r'; break;
1810 case VK_NUMPAD3: xkey = 's'; break;
1811 case VK_NUMPAD4: xkey = 't'; break;
1812 case VK_NUMPAD5: xkey = 'u'; break;
1813 case VK_NUMPAD6: xkey = 'v'; break;
1814 case VK_NUMPAD7: xkey = 'w'; break;
1815 case VK_NUMPAD8: xkey = 'x'; break;
1816 case VK_NUMPAD9: xkey = 'y'; break;
1817
1818 case VK_DECIMAL: xkey = 'n'; break;
1819 case VK_ADD: if(shift_state) xkey = 'm';
1820 else xkey = 'l';
1821 break;
1822 case VK_RETURN:
1823 if (HIWORD(lParam)&KF_EXTENDED)
1824 xkey = 'M';
1825 break;
1826 }
1827 if(xkey)
1828 {
1829 if (vt52_mode)
1830 {
1831 if (xkey>='P' && xkey<='S')
1832 p += sprintf((char *)p, "\x1B%c", xkey);
1833 else
1834 p += sprintf((char *)p, "\x1B?%c", xkey);
1835 }
1836 else
1837 p += sprintf((char *)p, "\x1BO%c", xkey);
1838 return p - output;
1839 }
1840 }
1841
1842 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1843 {
1844 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1845 return p-output;
1846 }
1847 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1848 {
1849 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1850 }
1851 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1852 {
1853 *p++ = 0; return p - output;
1854 }
1855 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1856 {
1857 *p++ = 160; return p - output;
1858 }
1859 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1860 {
1861 *p++ = 3; return p - output;
1862 }
1863 /* Control-2 to Control-8 are special */
1864 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1865 {
1866 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1867 return p - output;
1868 }
1869 if (shift_state == 2 && wParam == 0xBD) {
1870 *p++ = 0x1F;
1871 return p - output;
1872 }
1873 if (shift_state == 2 && wParam == 0xDF) {
1874 *p++ = 0x1C;
1875 return p - output;
1876 }
1877 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1878 *p++ = '\r'; *p++ = '\n';
1879 return p - output;
1880 }
1881
1882 /*
1883 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1884 * for integer decimal nn.)
1885 *
1886 * We also deal with the weird ones here. Linux VCs replace F1
1887 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1888 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1889 * respectively.
1890 */
1891 code = 0;
1892 switch (wParam) {
1893 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1894 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1895 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1896 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1897 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1898 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1899 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1900 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1901 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1902 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1903 case VK_F11: code = 23; break;
1904 case VK_F12: code = 24; break;
1905 case VK_F13: code = 25; break;
1906 case VK_F14: code = 26; break;
1907 case VK_F15: code = 28; break;
1908 case VK_F16: code = 29; break;
1909 case VK_F17: code = 31; break;
1910 case VK_F18: code = 32; break;
1911 case VK_F19: code = 33; break;
1912 case VK_F20: code = 34; break;
1913 case VK_HOME: code = 1; break;
1914 case VK_INSERT: code = 2; break;
1915 case VK_DELETE: code = 3; break;
1916 case VK_END: code = 4; break;
1917 case VK_PRIOR: code = 5; break;
1918 case VK_NEXT: code = 6; break;
1919 }
1920 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1921 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1922 return p - output;
1923 }
1924 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1925 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1926 return p - output;
1927 }
1928 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1929 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1930 return p - output;
1931 }
1932 if (code) {
1933 p += sprintf((char *)p, "\x1B[%d~", code);
1934 return p - output;
1935 }
1936
1937 /*
1938 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1939 * some reason seems to send VK_CLEAR to Windows...).
1940 */
1941 {
1942 char xkey = 0;
1943 switch (wParam) {
1944 case VK_UP: xkey = 'A'; break;
1945 case VK_DOWN: xkey = 'B'; break;
1946 case VK_RIGHT: xkey = 'C'; break;
1947 case VK_LEFT: xkey = 'D'; break;
1948 case VK_CLEAR: xkey = 'G'; break;
1949 }
1950 if (xkey)
1951 {
1952 if (vt52_mode)
1953 p += sprintf((char *)p, "\x1B%c", xkey);
1954 else if (app_cursor_keys)
1955 p += sprintf((char *)p, "\x1BO%c", xkey);
1956 else
1957 p += sprintf((char *)p, "\x1B[%c", xkey);
1958 return p - output;
1959 }
1960 }
1961 }
1962
1963 /* Okay we've done everything interesting; let windows deal with
1964 * the boring stuff */
1965 {
1966 BOOL capsOn=keystate[VK_CAPITAL] !=0;
1967
1968 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1969 if(cfg.xlat_capslockcyr)
1970 keystate[VK_CAPITAL] = 0;
1971
1972 r = ToAscii (wParam, scan, keystate, keys, 0);
1973 if(r>0)
1974 {
1975 p = output;
1976 for(i=0; i<r; i++)
1977 {
1978 unsigned char ch = (unsigned char)keys[i];
1979
1980 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
1981 compose_char = ch;
1982 compose_state ++;
1983 continue;
1984 }
1985 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
1986 int nc;
1987 compose_state = 0;
1988
1989 if ((nc=check_compose(compose_char,ch)) == -1)
1990 {
1991 c_write1('\007');
1992 return 0;
1993 }
1994 *p++ = xlat_kbd2tty((unsigned char)nc);
1995 return p-output;
1996 }
1997
1998 compose_state = 0;
1999
2000 if( left_alt && key_down ) *p++ = '\033';
2001 if (!key_down)
2002 *p++ = ch;
2003 else
2004 {
2005 if(capsOn)
2006 ch = xlat_latkbd2win(ch);
2007 *p++ = xlat_kbd2tty(ch);
2008 }
2009 }
2010
2011 /* This is so the ALT-Numpad and dead keys work correctly. */
2012 keys[0] = 0;
2013
2014 return p-output;
2015 }
2016 }
2017
2018 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2019 #if 0
2020 if (message == WM_SYSKEYUP && wParam == VK_MENU)
2021 {
2022 keystate[VK_MENU] = 0;
2023 return 0;
2024 }
2025 #endif
2026
2027 return -1;
2028 }
2029
2030 void set_title (char *title) {
2031 sfree (window_name);
2032 window_name = smalloc(1+strlen(title));
2033 strcpy (window_name, title);
2034 if (cfg.win_name_always || !IsIconic(hwnd))
2035 SetWindowText (hwnd, title);
2036 }
2037
2038 void set_icon (char *title) {
2039 sfree (icon_name);
2040 icon_name = smalloc(1+strlen(title));
2041 strcpy (icon_name, title);
2042 if (!cfg.win_name_always && IsIconic(hwnd))
2043 SetWindowText (hwnd, title);
2044 }
2045
2046 void set_sbar (int total, int start, int page) {
2047 SCROLLINFO si;
2048
2049 if (!cfg.scrollbar) return;
2050
2051 si.cbSize = sizeof(si);
2052 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2053 si.nMin = 0;
2054 si.nMax = total - 1;
2055 si.nPage = page;
2056 si.nPos = start;
2057 if (hwnd)
2058 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2059 }
2060
2061 Context get_ctx(void) {
2062 HDC hdc;
2063 if (hwnd) {
2064 hdc = GetDC (hwnd);
2065 if (hdc && pal)
2066 SelectPalette (hdc, pal, FALSE);
2067 return hdc;
2068 } else
2069 return NULL;
2070 }
2071
2072 void free_ctx (Context ctx) {
2073 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2074 ReleaseDC (hwnd, ctx);
2075 }
2076
2077 static void real_palette_set (int n, int r, int g, int b) {
2078 if (pal) {
2079 logpal->palPalEntry[n].peRed = r;
2080 logpal->palPalEntry[n].peGreen = g;
2081 logpal->palPalEntry[n].peBlue = b;
2082 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2083 colours[n] = PALETTERGB(r, g, b);
2084 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2085 } else
2086 colours[n] = RGB(r, g, b);
2087 }
2088
2089 void palette_set (int n, int r, int g, int b) {
2090 static const int first[21] = {
2091 0, 2, 4, 6, 8, 10, 12, 14,
2092 1, 3, 5, 7, 9, 11, 13, 15,
2093 16, 17, 18, 20, 22
2094 };
2095 real_palette_set (first[n], r, g, b);
2096 if (first[n] >= 18)
2097 real_palette_set (first[n]+1, r, g, b);
2098 if (pal) {
2099 HDC hdc = get_ctx();
2100 UnrealizeObject (pal);
2101 RealizePalette (hdc);
2102 free_ctx (hdc);
2103 }
2104 }
2105
2106 void palette_reset (void) {
2107 int i;
2108
2109 for (i = 0; i < NCOLOURS; i++) {
2110 if (pal) {
2111 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2112 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2113 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2114 logpal->palPalEntry[i].peFlags = 0;
2115 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2116 defpal[i].rgbtGreen,
2117 defpal[i].rgbtBlue);
2118 } else
2119 colours[i] = RGB(defpal[i].rgbtRed,
2120 defpal[i].rgbtGreen,
2121 defpal[i].rgbtBlue);
2122 }
2123
2124 if (pal) {
2125 HDC hdc;
2126 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2127 hdc = get_ctx();
2128 RealizePalette (hdc);
2129 free_ctx (hdc);
2130 }
2131 }
2132
2133 void write_clip (void *data, int len) {
2134 HGLOBAL clipdata;
2135 void *lock;
2136
2137 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2138 if (!clipdata)
2139 return;
2140 lock = GlobalLock (clipdata);
2141 if (!lock)
2142 return;
2143 memcpy (lock, data, len);
2144 ((unsigned char *) lock) [len] = 0;
2145 GlobalUnlock (clipdata);
2146
2147 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2148 if (OpenClipboard (hwnd)) {
2149 EmptyClipboard();
2150 SetClipboardData (CF_TEXT, clipdata);
2151 CloseClipboard();
2152 } else
2153 GlobalFree (clipdata);
2154 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2155 }
2156
2157 void get_clip (void **p, int *len) {
2158 static HGLOBAL clipdata = NULL;
2159
2160 if (!p) {
2161 if (clipdata)
2162 GlobalUnlock (clipdata);
2163 clipdata = NULL;
2164 return;
2165 } else {
2166 if (OpenClipboard (NULL)) {
2167 clipdata = GetClipboardData (CF_TEXT);
2168 CloseClipboard();
2169 if (clipdata) {
2170 *p = GlobalLock (clipdata);
2171 if (*p) {
2172 *len = strlen(*p);
2173 return;
2174 }
2175 }
2176 }
2177 }
2178
2179 *p = NULL;
2180 *len = 0;
2181 }
2182
2183 /*
2184 * Move `lines' lines from position `from' to position `to' in the
2185 * window.
2186 */
2187 void optimised_move (int to, int from, int lines) {
2188 RECT r;
2189 int min, max;
2190
2191 min = (to < from ? to : from);
2192 max = to + from - min;
2193
2194 r.left = 0; r.right = cols * font_width;
2195 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2196 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2197 }
2198
2199 /*
2200 * Print a message box and perform a fatal exit.
2201 */
2202 void fatalbox(char *fmt, ...) {
2203 va_list ap;
2204 char stuff[200];
2205
2206 va_start(ap, fmt);
2207 vsprintf(stuff, fmt, ap);
2208 va_end(ap);
2209 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2210 exit(1);
2211 }
2212
2213 /*
2214 * Beep.
2215 */
2216 void beep(int errorbeep) {
2217 static long last_beep = 0;
2218 long now, beep_diff;
2219
2220 now = GetTickCount();
2221 beep_diff = now-last_beep;
2222
2223 /* Make sure we only respond to one beep per packet or so */
2224 if (beep_diff>=0 && beep_diff<50)
2225 return;
2226
2227 if(errorbeep)
2228 MessageBeep(MB_ICONHAND);
2229 else
2230 MessageBeep(MB_OK);
2231
2232 last_beep = GetTickCount();
2233 }