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