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