7db845e4fb069e869a278ca919cf3b5d080b41df
[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 (cfg.compose_key) {
1874 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1875 {
1876 keystate[VK_RMENU] = keystate[VK_MENU];
1877 if (!compose_state) compose_key = wParam;
1878 }
1879 if (wParam == VK_APPS && !compose_state)
1880 compose_key = wParam;
1881
1882 if (wParam == compose_key)
1883 {
1884 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1885 compose_state = 1;
1886 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1887 compose_state = 2;
1888 else
1889 compose_state = 0;
1890 }
1891 else if (compose_state==1 && wParam != VK_CONTROL)
1892 compose_state = 0;
1893 } else
1894 compose_state = 0;
1895
1896 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1897 if ( (cfg.funky_type == 3 || (cfg.funky_type <= 1 && app_keypad_keys))
1898 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1899
1900 wParam = VK_EXECUTE;
1901
1902 /* UnToggle NUMLock */
1903 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1904 keystate[VK_NUMLOCK] ^= 1;
1905 }
1906
1907 /* And write back the 'adjusted' state */
1908 SetKeyboardState (keystate);
1909 }
1910
1911 /* Disable Auto repeat if required */
1912 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1913 return 0;
1914
1915 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1916 left_alt = 1;
1917
1918 key_down = ((HIWORD(lParam)&KF_UP)==0);
1919
1920 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1921 if (left_alt && (keystate[VK_CONTROL]&0x80))
1922 keystate[VK_MENU] = 0;
1923
1924 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1925 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1926 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1927
1928 /*
1929 * Record that we pressed key so the scroll window can be reset, but
1930 * be careful to avoid Shift-UP/Down
1931 */
1932 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1933 seen_key_event = 1;
1934 }
1935
1936 /* Make sure we're not pasting */
1937 if (key_down) term_nopaste();
1938
1939 if (compose_state>1 && left_alt) compose_state = 0;
1940
1941 /* Sanitize the number pad if not using a PC NumPad */
1942 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1943 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
1944 {
1945 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1946 {
1947 int nParam = 0;
1948 switch(wParam)
1949 {
1950 case VK_INSERT: nParam = VK_NUMPAD0; break;
1951 case VK_END: nParam = VK_NUMPAD1; break;
1952 case VK_DOWN: nParam = VK_NUMPAD2; break;
1953 case VK_NEXT: nParam = VK_NUMPAD3; break;
1954 case VK_LEFT: nParam = VK_NUMPAD4; break;
1955 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1956 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1957 case VK_HOME: nParam = VK_NUMPAD7; break;
1958 case VK_UP: nParam = VK_NUMPAD8; break;
1959 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1960 case VK_DELETE: nParam = VK_DECIMAL; break;
1961 }
1962 if (nParam)
1963 {
1964 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1965 wParam = nParam;
1966 }
1967 }
1968 }
1969
1970 /* If a key is pressed and AltGr is not active */
1971 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1972 {
1973 /* Okay, prepare for most alts then ...*/
1974 if (left_alt) *p++ = '\033';
1975
1976 /* Lets see if it's a pattern we know all about ... */
1977 if (wParam == VK_PRIOR && shift_state == 1) {
1978 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1979 return 0;
1980 }
1981 if (wParam == VK_NEXT && shift_state == 1) {
1982 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1983 return 0;
1984 }
1985 if (wParam == VK_INSERT && shift_state == 1) {
1986 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1987 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1988 return 0;
1989 }
1990 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1991 return -1;
1992 }
1993 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1994
1995 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1996 return -1;
1997 }
1998 /* Control-Numlock for app-keypad mode switch */
1999 if (wParam == VK_PAUSE && shift_state == 2) {
2000 app_keypad_keys ^= 1;
2001 return 0;
2002 }
2003
2004 /* Nethack keypad */
2005 if (cfg.nethack_keypad && !left_alt) {
2006 switch(wParam) {
2007 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2008 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2009 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2010 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2011 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2012 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2013 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2014 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2015 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2016 }
2017 }
2018
2019 /* Application Keypad */
2020 if (!left_alt) {
2021 int xkey = 0;
2022
2023 if ( cfg.funky_type == 3 ||
2024 ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) {
2025 case VK_EXECUTE: xkey = 'P'; break;
2026 case VK_DIVIDE: xkey = 'Q'; break;
2027 case VK_MULTIPLY:xkey = 'R'; break;
2028 case VK_SUBTRACT:xkey = 'S'; break;
2029 }
2030 if(app_keypad_keys) switch(wParam) {
2031 case VK_NUMPAD0: xkey = 'p'; break;
2032 case VK_NUMPAD1: xkey = 'q'; break;
2033 case VK_NUMPAD2: xkey = 'r'; break;
2034 case VK_NUMPAD3: xkey = 's'; break;
2035 case VK_NUMPAD4: xkey = 't'; break;
2036 case VK_NUMPAD5: xkey = 'u'; break;
2037 case VK_NUMPAD6: xkey = 'v'; break;
2038 case VK_NUMPAD7: xkey = 'w'; break;
2039 case VK_NUMPAD8: xkey = 'x'; break;
2040 case VK_NUMPAD9: xkey = 'y'; break;
2041
2042 case VK_DECIMAL: xkey = 'n'; break;
2043 case VK_ADD: if(cfg.funky_type==2) {
2044 if(shift_state) xkey = 'l';
2045 else xkey = 'k';
2046 } else if(shift_state) xkey = 'm';
2047 else xkey = 'l';
2048 break;
2049
2050 case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
2051 case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2052 case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2053
2054 case VK_RETURN:
2055 if (HIWORD(lParam)&KF_EXTENDED)
2056 xkey = 'M';
2057 break;
2058 }
2059 if(xkey)
2060 {
2061 if (vt52_mode)
2062 {
2063 if (xkey>='P' && xkey<='S')
2064 p += sprintf((char *)p, "\x1B%c", xkey);
2065 else
2066 p += sprintf((char *)p, "\x1B?%c", xkey);
2067 }
2068 else
2069 p += sprintf((char *)p, "\x1BO%c", xkey);
2070 return p - output;
2071 }
2072 }
2073
2074 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
2075 {
2076 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2077 return p-output;
2078 }
2079 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
2080 {
2081 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2082 }
2083 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
2084 {
2085 *p++ = 0; return p - output;
2086 }
2087 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
2088 {
2089 *p++ = 160; return p - output;
2090 }
2091 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
2092 {
2093 *p++ = 3; return p - output;
2094 }
2095 /* Control-2 to Control-8 are special */
2096 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2097 {
2098 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2099 return p - output;
2100 }
2101 if (shift_state == 2 && wParam == 0xBD) {
2102 *p++ = 0x1F;
2103 return p - output;
2104 }
2105 if (shift_state == 2 && wParam == 0xDF) {
2106 *p++ = 0x1C;
2107 return p - output;
2108 }
2109 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2110 *p++ = '\r'; *p++ = '\n';
2111 return p - output;
2112 }
2113
2114 /*
2115 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2116 * for integer decimal nn.)
2117 *
2118 * We also deal with the weird ones here. Linux VCs replace F1
2119 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2120 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2121 * respectively.
2122 */
2123 code = 0;
2124 switch (wParam) {
2125 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2126 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2127 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2128 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2129 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2130 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2131 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2132 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2133 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2134 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2135 case VK_F11: code = 23; break;
2136 case VK_F12: code = 24; break;
2137 case VK_F13: code = 25; break;
2138 case VK_F14: code = 26; break;
2139 case VK_F15: code = 28; break;
2140 case VK_F16: code = 29; break;
2141 case VK_F17: code = 31; break;
2142 case VK_F18: code = 32; break;
2143 case VK_F19: code = 33; break;
2144 case VK_F20: code = 34; break;
2145 case VK_HOME: code = 1; break;
2146 case VK_INSERT: code = 2; break;
2147 case VK_DELETE: code = 3; break;
2148 case VK_END: code = 4; break;
2149 case VK_PRIOR: code = 5; break;
2150 case VK_NEXT: code = 6; break;
2151 }
2152 /* Reorder edit keys to physical order */
2153 if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2154
2155 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2156 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2157 return p - output;
2158 }
2159 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2160 if (vt52_mode)
2161 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2162 else
2163 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2164 return p - output;
2165 }
2166 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2167 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2168 return p - output;
2169 }
2170 if (code) {
2171 p += sprintf((char *)p, "\x1B[%d~", code);
2172 return p - output;
2173 }
2174
2175 /*
2176 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2177 * some reason seems to send VK_CLEAR to Windows...).
2178 */
2179 {
2180 char xkey = 0;
2181 switch (wParam) {
2182 case VK_UP: xkey = 'A'; break;
2183 case VK_DOWN: xkey = 'B'; break;
2184 case VK_RIGHT: xkey = 'C'; break;
2185 case VK_LEFT: xkey = 'D'; break;
2186 case VK_CLEAR: xkey = 'G'; break;
2187 }
2188 if (xkey)
2189 {
2190 if (vt52_mode)
2191 p += sprintf((char *)p, "\x1B%c", xkey);
2192 else if (app_cursor_keys)
2193 p += sprintf((char *)p, "\x1BO%c", xkey);
2194 else
2195 p += sprintf((char *)p, "\x1B[%c", xkey);
2196 return p - output;
2197 }
2198 }
2199
2200 /*
2201 * Finally, deal with Return ourselves. (Win95 seems to
2202 * foul it up when Alt is pressed, for some reason.)
2203 */
2204 if (wParam == VK_RETURN) /* Return */
2205 {
2206 *p++ = 0x0D;
2207 return p-output;
2208 }
2209 }
2210
2211 /* Okay we've done everything interesting; let windows deal with
2212 * the boring stuff */
2213 {
2214 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2215
2216 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2217 if(cfg.xlat_capslockcyr)
2218 keystate[VK_CAPITAL] = 0;
2219
2220 r = ToAscii (wParam, scan, keystate, keys, 0);
2221 if(r>0)
2222 {
2223 p = output;
2224 for(i=0; i<r; i++)
2225 {
2226 unsigned char ch = (unsigned char)keys[i];
2227
2228 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2229 compose_char = ch;
2230 compose_state ++;
2231 continue;
2232 }
2233 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2234 int nc;
2235 compose_state = 0;
2236
2237 if ((nc=check_compose(compose_char,ch)) == -1)
2238 {
2239 MessageBeep(MB_ICONHAND);
2240 return 0;
2241 }
2242 *p++ = xlat_kbd2tty((unsigned char)nc);
2243 return p-output;
2244 }
2245
2246 compose_state = 0;
2247
2248 if( left_alt && key_down ) *p++ = '\033';
2249 if (!key_down)
2250 *p++ = ch;
2251 else
2252 {
2253 if(capsOn)
2254 ch = xlat_latkbd2win(ch);
2255 *p++ = xlat_kbd2tty(ch);
2256 }
2257 }
2258
2259 /* This is so the ALT-Numpad and dead keys work correctly. */
2260 keys[0] = 0;
2261
2262 return p-output;
2263 }
2264 }
2265
2266 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2267 if (!cfg.alt_only) {
2268 if (message == WM_SYSKEYUP && wParam == VK_MENU)
2269 return 0;
2270 }
2271
2272 return -1;
2273 }
2274
2275 void set_title (char *title) {
2276 sfree (window_name);
2277 window_name = smalloc(1+strlen(title));
2278 strcpy (window_name, title);
2279 if (cfg.win_name_always || !IsIconic(hwnd))
2280 SetWindowText (hwnd, title);
2281 }
2282
2283 void set_icon (char *title) {
2284 sfree (icon_name);
2285 icon_name = smalloc(1+strlen(title));
2286 strcpy (icon_name, title);
2287 if (!cfg.win_name_always && IsIconic(hwnd))
2288 SetWindowText (hwnd, title);
2289 }
2290
2291 void set_sbar (int total, int start, int page) {
2292 SCROLLINFO si;
2293
2294 if (!cfg.scrollbar) return;
2295
2296 si.cbSize = sizeof(si);
2297 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2298 si.nMin = 0;
2299 si.nMax = total - 1;
2300 si.nPage = page;
2301 si.nPos = start;
2302 if (hwnd)
2303 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2304 }
2305
2306 Context get_ctx(void) {
2307 HDC hdc;
2308 if (hwnd) {
2309 hdc = GetDC (hwnd);
2310 if (hdc && pal)
2311 SelectPalette (hdc, pal, FALSE);
2312 return hdc;
2313 } else
2314 return NULL;
2315 }
2316
2317 void free_ctx (Context ctx) {
2318 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2319 ReleaseDC (hwnd, ctx);
2320 }
2321
2322 static void real_palette_set (int n, int r, int g, int b) {
2323 if (pal) {
2324 logpal->palPalEntry[n].peRed = r;
2325 logpal->palPalEntry[n].peGreen = g;
2326 logpal->palPalEntry[n].peBlue = b;
2327 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2328 colours[n] = PALETTERGB(r, g, b);
2329 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2330 } else
2331 colours[n] = RGB(r, g, b);
2332 }
2333
2334 void palette_set (int n, int r, int g, int b) {
2335 static const int first[21] = {
2336 0, 2, 4, 6, 8, 10, 12, 14,
2337 1, 3, 5, 7, 9, 11, 13, 15,
2338 16, 17, 18, 20, 22
2339 };
2340 real_palette_set (first[n], r, g, b);
2341 if (first[n] >= 18)
2342 real_palette_set (first[n]+1, r, g, b);
2343 if (pal) {
2344 HDC hdc = get_ctx();
2345 UnrealizeObject (pal);
2346 RealizePalette (hdc);
2347 free_ctx (hdc);
2348 }
2349 }
2350
2351 void palette_reset (void) {
2352 int i;
2353
2354 for (i = 0; i < NCOLOURS; i++) {
2355 if (pal) {
2356 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2357 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2358 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2359 logpal->palPalEntry[i].peFlags = 0;
2360 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2361 defpal[i].rgbtGreen,
2362 defpal[i].rgbtBlue);
2363 } else
2364 colours[i] = RGB(defpal[i].rgbtRed,
2365 defpal[i].rgbtGreen,
2366 defpal[i].rgbtBlue);
2367 }
2368
2369 if (pal) {
2370 HDC hdc;
2371 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2372 hdc = get_ctx();
2373 RealizePalette (hdc);
2374 free_ctx (hdc);
2375 }
2376 }
2377
2378 void write_clip (void *data, int len, int must_deselect) {
2379 HGLOBAL clipdata;
2380 void *lock;
2381
2382 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2383 if (!clipdata)
2384 return;
2385 lock = GlobalLock (clipdata);
2386 if (!lock)
2387 return;
2388 memcpy (lock, data, len);
2389 ((unsigned char *) lock) [len] = 0;
2390 GlobalUnlock (clipdata);
2391
2392 if (!must_deselect)
2393 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2394
2395 if (OpenClipboard (hwnd)) {
2396 EmptyClipboard();
2397 SetClipboardData (CF_TEXT, clipdata);
2398 CloseClipboard();
2399 } else
2400 GlobalFree (clipdata);
2401
2402 if (!must_deselect)
2403 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2404 }
2405
2406 void get_clip (void **p, int *len) {
2407 static HGLOBAL clipdata = NULL;
2408
2409 if (!p) {
2410 if (clipdata)
2411 GlobalUnlock (clipdata);
2412 clipdata = NULL;
2413 return;
2414 } else {
2415 if (OpenClipboard (NULL)) {
2416 clipdata = GetClipboardData (CF_TEXT);
2417 CloseClipboard();
2418 if (clipdata) {
2419 *p = GlobalLock (clipdata);
2420 if (*p) {
2421 *len = strlen(*p);
2422 return;
2423 }
2424 }
2425 }
2426 }
2427
2428 *p = NULL;
2429 *len = 0;
2430 }
2431
2432 /*
2433 * Move `lines' lines from position `from' to position `to' in the
2434 * window.
2435 */
2436 void optimised_move (int to, int from, int lines) {
2437 RECT r;
2438 int min, max;
2439
2440 min = (to < from ? to : from);
2441 max = to + from - min;
2442
2443 r.left = 0; r.right = cols * font_width;
2444 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2445 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2446 }
2447
2448 /*
2449 * Print a message box and perform a fatal exit.
2450 */
2451 void fatalbox(char *fmt, ...) {
2452 va_list ap;
2453 char stuff[200];
2454
2455 va_start(ap, fmt);
2456 vsprintf(stuff, fmt, ap);
2457 va_end(ap);
2458 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2459 exit(1);
2460 }
2461
2462 /*
2463 * Beep.
2464 */
2465 void beep(int errorbeep) {
2466 static long last_beep = 0;
2467 long now, beep_diff;
2468
2469 now = GetTickCount();
2470 beep_diff = now-last_beep;
2471
2472 /* Make sure we only respond to one beep per packet or so */
2473 if (beep_diff>=0 && beep_diff<50)
2474 return;
2475
2476 if(errorbeep)
2477 MessageBeep(MB_ICONHAND);
2478 else
2479 MessageBeep(MB_OK);
2480
2481 last_beep = GetTickCount();
2482 }