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