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