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