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