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