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