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