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