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