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