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