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