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