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