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