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