`realhost', passed back from all the backend init functions, was
[u/mdw/putty] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <mmsystem.h>
5 #ifndef AUTO_WINSOCK
6 #ifdef WINSOCK_TWO
7 #include <winsock2.h>
8 #else
9 #include <winsock.h>
10 #endif
11 #endif
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <time.h>
16
17 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
18 #include "putty.h"
19 #include "winstuff.h"
20 #include "storage.h"
21 #include "win_res.h"
22
23 #define IDM_SHOWLOG 0x0010
24 #define IDM_NEWSESS 0x0020
25 #define IDM_DUPSESS 0x0030
26 #define IDM_RECONF 0x0040
27 #define IDM_CLRSB 0x0050
28 #define IDM_RESET 0x0060
29 #define IDM_TEL_AYT 0x0070
30 #define IDM_TEL_BRK 0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC 0x00a0
33 #define IDM_TEL_EL 0x00b0
34 #define IDM_TEL_GA 0x00c0
35 #define IDM_TEL_NOP 0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO 0x00f0
38 #define IDM_TEL_IP 0x0100
39 #define IDM_TEL_SUSP 0x0110
40 #define IDM_TEL_EOR 0x0120
41 #define IDM_TEL_EOF 0x0130
42 #define IDM_ABOUT 0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL 0x0160
45
46 #define IDM_SESSLGP 0x0250 /* log type printable */
47 #define IDM_SESSLGA 0x0260 /* log type all chars */
48 #define IDM_SESSLGE 0x0270 /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
51
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
54
55 /* Needed for Chinese support and apparently not always defined. */
56 #ifndef VK_PROCESSKEY
57 #define VK_PROCESSKEY 0xE5
58 #endif
59
60 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
61 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
62 unsigned char *output);
63 static void cfgtopalette(void);
64 static void init_palette(void);
65 static void init_fonts(int);
66
67 static int extra_width, extra_height;
68
69 static int pending_netevent = 0;
70 static WPARAM pend_netevent_wParam = 0;
71 static LPARAM pend_netevent_lParam = 0;
72 static void enact_pending_netevent(void);
73
74 static time_t last_movement = 0;
75
76 #define FONT_NORMAL 0
77 #define FONT_BOLD 1
78 #define FONT_UNDERLINE 2
79 #define FONT_BOLDUND 3
80 #define FONT_OEM 4
81 #define FONT_OEMBOLD 5
82 #define FONT_OEMBOLDUND 6
83 #define FONT_OEMUND 7
84 static HFONT fonts[8];
85 static int font_needs_hand_underlining;
86 static enum {
87 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
88 } bold_mode;
89 static enum {
90 UND_LINE, UND_FONT
91 } und_mode;
92 static int descent;
93
94 #define NCOLOURS 24
95 static COLORREF colours[NCOLOURS];
96 static HPALETTE pal;
97 static LPLOGPALETTE logpal;
98 static RGBTRIPLE defpal[NCOLOURS];
99
100 static HWND hwnd;
101
102 static HBITMAP caretbm;
103
104 static int dbltime, lasttime, lastact;
105 static Mouse_Button lastbtn;
106
107 /* this allows xterm-style mouse handling. */
108 static int send_raw_mouse = 0;
109 static int wheel_accumulator = 0;
110
111 static char *window_name, *icon_name;
112
113 static int compose_state = 0;
114
115 /* Dummy routine, only required in plink. */
116 void ldisc_update(int echo, int edit)
117 {
118 }
119
120 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
121 {
122 static char appname[] = "PuTTY";
123 WORD winsock_ver;
124 WSADATA wsadata;
125 WNDCLASS wndclass;
126 MSG msg;
127 int guess_width, guess_height;
128
129 hinst = inst;
130 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
131
132 winsock_ver = MAKEWORD(1, 1);
133 if (WSAStartup(winsock_ver, &wsadata)) {
134 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
135 MB_OK | MB_ICONEXCLAMATION);
136 return 1;
137 }
138 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
139 MessageBox(NULL, "WinSock version is incompatible with 1.1",
140 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
141 WSACleanup();
142 return 1;
143 }
144 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
145 sk_init();
146
147 InitCommonControls();
148
149 /* Ensure a Maximize setting in Explorer doesn't maximise the
150 * config box. */
151 defuse_showwindow();
152
153 /*
154 * Process the command line.
155 */
156 {
157 char *p;
158
159 default_protocol = DEFAULT_PROTOCOL;
160 default_port = DEFAULT_PORT;
161 cfg.logtype = LGTYP_NONE;
162
163 do_defaults(NULL, &cfg);
164
165 p = cmdline;
166 while (*p && isspace(*p))
167 p++;
168
169 /*
170 * Process command line options first. Yes, this can be
171 * done better, and it will be as soon as I have the
172 * energy...
173 */
174 while (*p == '-') {
175 char *q = p + strcspn(p, " \t");
176 p++;
177 if (q == p + 3 &&
178 tolower(p[0]) == 's' &&
179 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
180 default_protocol = cfg.protocol = PROT_SSH;
181 default_port = cfg.port = 22;
182 } else if (q == p + 7 &&
183 tolower(p[0]) == 'c' &&
184 tolower(p[1]) == 'l' &&
185 tolower(p[2]) == 'e' &&
186 tolower(p[3]) == 'a' &&
187 tolower(p[4]) == 'n' &&
188 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
189 /*
190 * `putty -cleanup'. Remove all registry entries
191 * associated with PuTTY, and also find and delete
192 * the random seed file.
193 */
194 if (MessageBox(NULL,
195 "This procedure will remove ALL Registry\n"
196 "entries associated with PuTTY, and will\n"
197 "also remove the PuTTY random seed file.\n"
198 "\n"
199 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
200 "SESSIONS. Are you really sure you want\n"
201 "to continue?",
202 "PuTTY Warning",
203 MB_YESNO | MB_ICONWARNING) == IDYES) {
204 cleanup_all();
205 }
206 exit(0);
207 }
208 p = q + strspn(q, " \t");
209 }
210
211 /*
212 * An initial @ means to activate a saved session.
213 */
214 if (*p == '@') {
215 int i = strlen(p);
216 while (i > 1 && isspace(p[i - 1]))
217 i--;
218 p[i] = '\0';
219 do_defaults(p + 1, &cfg);
220 if (!*cfg.host && !do_config()) {
221 WSACleanup();
222 return 0;
223 }
224 } else if (*p == '&') {
225 /*
226 * An initial & means we've been given a command line
227 * containing the hex value of a HANDLE for a file
228 * mapping object, which we must then extract as a
229 * config.
230 */
231 HANDLE filemap;
232 Config *cp;
233 if (sscanf(p + 1, "%p", &filemap) == 1 &&
234 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
235 0, 0, sizeof(Config))) != NULL) {
236 cfg = *cp;
237 UnmapViewOfFile(cp);
238 CloseHandle(filemap);
239 } else if (!do_config()) {
240 WSACleanup();
241 return 0;
242 }
243 } else if (*p) {
244 char *q = p;
245 /*
246 * If the hostname starts with "telnet:", set the
247 * protocol to Telnet and process the string as a
248 * Telnet URL.
249 */
250 if (!strncmp(q, "telnet:", 7)) {
251 char c;
252
253 q += 7;
254 if (q[0] == '/' && q[1] == '/')
255 q += 2;
256 cfg.protocol = PROT_TELNET;
257 p = q;
258 while (*p && *p != ':' && *p != '/')
259 p++;
260 c = *p;
261 if (*p)
262 *p++ = '\0';
263 if (c == ':')
264 cfg.port = atoi(p);
265 else
266 cfg.port = -1;
267 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
268 cfg.host[sizeof(cfg.host) - 1] = '\0';
269 } else {
270 while (*p && !isspace(*p))
271 p++;
272 if (*p)
273 *p++ = '\0';
274 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
275 cfg.host[sizeof(cfg.host) - 1] = '\0';
276 while (*p && isspace(*p))
277 p++;
278 if (*p)
279 cfg.port = atoi(p);
280 else
281 cfg.port = -1;
282 }
283 } else {
284 if (!do_config()) {
285 WSACleanup();
286 return 0;
287 }
288 }
289
290 /* See if host is of the form user@host */
291 if (cfg.host[0] != '\0') {
292 char *atsign = strchr(cfg.host, '@');
293 /* Make sure we're not overflowing the user field */
294 if (atsign) {
295 if (atsign - cfg.host < sizeof cfg.username) {
296 strncpy(cfg.username, cfg.host, atsign - cfg.host);
297 cfg.username[atsign - cfg.host] = '\0';
298 }
299 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
300 }
301 }
302
303 /*
304 * Trim a colon suffix off the hostname if it's there.
305 */
306 cfg.host[strcspn(cfg.host, ":")] = '\0';
307 }
308
309 /*
310 * Select protocol. This is farmed out into a table in a
311 * separate file to enable an ssh-free variant.
312 */
313 {
314 int i;
315 back = NULL;
316 for (i = 0; backends[i].backend != NULL; i++)
317 if (backends[i].protocol == cfg.protocol) {
318 back = backends[i].backend;
319 break;
320 }
321 if (back == NULL) {
322 MessageBox(NULL, "Unsupported protocol number found",
323 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
324 WSACleanup();
325 return 1;
326 }
327 }
328
329 /* Check for invalid Port number (i.e. zero) */
330 if (cfg.port == 0) {
331 MessageBox(NULL, "Invalid Port Number",
332 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
333 WSACleanup();
334 return 1;
335 }
336
337 if (!prev) {
338 wndclass.style = 0;
339 wndclass.lpfnWndProc = WndProc;
340 wndclass.cbClsExtra = 0;
341 wndclass.cbWndExtra = 0;
342 wndclass.hInstance = inst;
343 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
344 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
345 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
346 wndclass.lpszMenuName = NULL;
347 wndclass.lpszClassName = appname;
348
349 RegisterClass(&wndclass);
350 }
351
352 hwnd = NULL;
353
354 savelines = cfg.savelines;
355 term_init();
356
357 cfgtopalette();
358
359 /*
360 * Guess some defaults for the window size. This all gets
361 * updated later, so we don't really care too much. However, we
362 * do want the font width/height guesses to correspond to a
363 * large font rather than a small one...
364 */
365
366 font_width = 10;
367 font_height = 20;
368 extra_width = 25;
369 extra_height = 28;
370 term_size(cfg.height, cfg.width, cfg.savelines);
371 guess_width = extra_width + font_width * cols;
372 guess_height = extra_height + font_height * rows;
373 {
374 RECT r;
375 HWND w = GetDesktopWindow();
376 GetWindowRect(w, &r);
377 if (guess_width > r.right - r.left)
378 guess_width = r.right - r.left;
379 if (guess_height > r.bottom - r.top)
380 guess_height = r.bottom - r.top;
381 }
382
383 {
384 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
385 int exwinmode = 0;
386 if (!cfg.scrollbar)
387 winmode &= ~(WS_VSCROLL);
388 if (cfg.locksize)
389 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
390 if (cfg.alwaysontop)
391 exwinmode |= WS_EX_TOPMOST;
392 if (cfg.sunken_edge)
393 exwinmode |= WS_EX_CLIENTEDGE;
394 hwnd = CreateWindowEx(exwinmode, appname, appname,
395 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
396 guess_width, guess_height,
397 NULL, NULL, inst, NULL);
398 }
399
400 /*
401 * Initialise the fonts, simultaneously correcting the guesses
402 * for font_{width,height}.
403 */
404 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
405 und_mode = UND_FONT;
406 init_fonts(0);
407
408 /*
409 * Correct the guesses for extra_{width,height}.
410 */
411 {
412 RECT cr, wr;
413 GetWindowRect(hwnd, &wr);
414 GetClientRect(hwnd, &cr);
415 extra_width = wr.right - wr.left - cr.right + cr.left;
416 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
417 }
418
419 /*
420 * Resize the window, now we know what size we _really_ want it
421 * to be.
422 */
423 guess_width = extra_width + font_width * cols;
424 guess_height = extra_height + font_height * rows;
425 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
426 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
427 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
428
429 /*
430 * Set up a caret bitmap, with no content.
431 */
432 {
433 char *bits;
434 int size = (font_width + 15) / 16 * 2 * font_height;
435 bits = smalloc(size);
436 memset(bits, 0, size);
437 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
438 sfree(bits);
439 }
440 CreateCaret(hwnd, caretbm, font_width, font_height);
441
442 /*
443 * Initialise the scroll bar.
444 */
445 {
446 SCROLLINFO si;
447
448 si.cbSize = sizeof(si);
449 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
450 si.nMin = 0;
451 si.nMax = rows - 1;
452 si.nPage = rows;
453 si.nPos = 0;
454 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
455 }
456
457 /*
458 * Start up the telnet connection.
459 */
460 {
461 char *error;
462 char msg[1024], *title;
463 char *realhost;
464
465 error = back->init(cfg.host, cfg.port, &realhost);
466 if (error) {
467 sprintf(msg, "Unable to open connection to\n"
468 "%.800s\n" "%s", cfg.host, error);
469 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
470 return 0;
471 }
472 window_name = icon_name = NULL;
473 if (*cfg.wintitle) {
474 title = cfg.wintitle;
475 } else {
476 sprintf(msg, "%s - PuTTY", realhost);
477 title = msg;
478 }
479 sfree(realhost);
480 set_title(title);
481 set_icon(title);
482 }
483
484 session_closed = FALSE;
485
486 /*
487 * Set up the input and output buffers.
488 */
489 inbuf_head = 0;
490 outbuf_reap = outbuf_head = 0;
491
492 /*
493 * Prepare the mouse handler.
494 */
495 lastact = MA_NOTHING;
496 lastbtn = MBT_NOTHING;
497 dbltime = GetDoubleClickTime();
498
499 /*
500 * Set up the session-control options on the system menu.
501 */
502 {
503 HMENU m = GetSystemMenu(hwnd, FALSE);
504 HMENU p, s;
505 int i;
506
507 AppendMenu(m, MF_SEPARATOR, 0, 0);
508 if (cfg.protocol == PROT_TELNET) {
509 p = CreateMenu();
510 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
511 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
512 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
513 AppendMenu(p, MF_SEPARATOR, 0, 0);
514 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
515 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
516 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
517 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
518 AppendMenu(p, MF_SEPARATOR, 0, 0);
519 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
520 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
521 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
522 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
523 AppendMenu(p, MF_SEPARATOR, 0, 0);
524 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
525 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
526 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
527 "Telnet Command");
528 AppendMenu(m, MF_SEPARATOR, 0, 0);
529 }
530 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
531 AppendMenu(m, MF_SEPARATOR, 0, 0);
532 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
533 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
534 s = CreateMenu();
535 get_sesslist(TRUE);
536 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
537 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
538 sessions[i]);
539 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
540 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
541 AppendMenu(m, MF_SEPARATOR, 0, 0);
542 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
543 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
544 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
545 AppendMenu(m, MF_SEPARATOR, 0, 0);
546 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
547 }
548
549 /*
550 * Finally show the window!
551 */
552 ShowWindow(hwnd, show);
553
554 /*
555 * Open the initial log file if there is one.
556 */
557 logfopen();
558
559 /*
560 * Set the palette up.
561 */
562 pal = NULL;
563 logpal = NULL;
564 init_palette();
565
566 has_focus = (GetForegroundWindow() == hwnd);
567 UpdateWindow(hwnd);
568
569 if (GetMessage(&msg, NULL, 0, 0) == 1) {
570 int timer_id = 0, long_timer = 0;
571
572 while (msg.message != WM_QUIT) {
573 /* Sometimes DispatchMessage calls routines that use their own
574 * GetMessage loop, setup this timer so we get some control back.
575 *
576 * Also call term_update() from the timer so that if the host
577 * is sending data flat out we still do redraws.
578 */
579 if (timer_id && long_timer) {
580 KillTimer(hwnd, timer_id);
581 long_timer = timer_id = 0;
582 }
583 if (!timer_id)
584 timer_id = SetTimer(hwnd, 1, 20, NULL);
585 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
586 DispatchMessage(&msg);
587
588 /* Make sure we blink everything that needs it. */
589 term_blink(0);
590
591 /* Send the paste buffer if there's anything to send */
592 term_paste();
593
594 /* If there's nothing new in the queue then we can do everything
595 * we've delayed, reading the socket, writing, and repainting
596 * the window.
597 */
598 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
599 continue;
600
601 if (pending_netevent) {
602 enact_pending_netevent();
603
604 /* Force the cursor blink on */
605 term_blink(1);
606
607 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
608 continue;
609 }
610
611 /* Okay there is now nothing to do so we make sure the screen is
612 * completely up to date then tell windows to call us in a little
613 * while.
614 */
615 if (timer_id) {
616 KillTimer(hwnd, timer_id);
617 timer_id = 0;
618 }
619 HideCaret(hwnd);
620 if (inbuf_head)
621 term_out();
622 term_update();
623 ShowCaret(hwnd);
624 if (in_vbell)
625 /* Hmm, term_update didn't want to do an update too soon ... */
626 timer_id = SetTimer(hwnd, 1, 50, NULL);
627 else if (!has_focus)
628 timer_id = SetTimer(hwnd, 1, 2000, NULL);
629 else
630 timer_id = SetTimer(hwnd, 1, 100, NULL);
631 long_timer = 1;
632
633 /* There's no point rescanning everything in the message queue
634 * so we do an apparently unnecessary wait here
635 */
636 WaitMessage();
637 if (GetMessage(&msg, NULL, 0, 0) != 1)
638 break;
639 }
640 }
641
642 /*
643 * Clean up.
644 */
645 {
646 int i;
647 for (i = 0; i < 8; i++)
648 if (fonts[i])
649 DeleteObject(fonts[i]);
650 }
651 sfree(logpal);
652 if (pal)
653 DeleteObject(pal);
654 WSACleanup();
655
656 if (cfg.protocol == PROT_SSH) {
657 random_save_seed();
658 #ifdef MSCRYPTOAPI
659 crypto_wrapup();
660 #endif
661 }
662
663 return msg.wParam;
664 }
665
666 /*
667 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
668 */
669 char *do_select(SOCKET skt, int startup)
670 {
671 int msg, events;
672 if (startup) {
673 msg = WM_NETEVENT;
674 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
675 } else {
676 msg = events = 0;
677 }
678 if (!hwnd)
679 return "do_select(): internal error (hwnd==NULL)";
680 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
681 switch (WSAGetLastError()) {
682 case WSAENETDOWN:
683 return "Network is down";
684 default:
685 return "WSAAsyncSelect(): unknown error";
686 }
687 }
688 return NULL;
689 }
690
691 /*
692 * set or clear the "raw mouse message" mode
693 */
694 void set_raw_mouse_mode(int activate)
695 {
696 send_raw_mouse = activate;
697 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
698 }
699
700 /*
701 * Print a message box and close the connection.
702 */
703 void connection_fatal(char *fmt, ...)
704 {
705 va_list ap;
706 char stuff[200];
707
708 va_start(ap, fmt);
709 vsprintf(stuff, fmt, ap);
710 va_end(ap);
711 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
712 if (cfg.close_on_exit == COE_ALWAYS)
713 PostQuitMessage(1);
714 else {
715 session_closed = TRUE;
716 SetWindowText(hwnd, "PuTTY (inactive)");
717 }
718 }
719
720 /*
721 * Actually do the job requested by a WM_NETEVENT
722 */
723 static void enact_pending_netevent(void)
724 {
725 static int reentering = 0;
726 extern int select_result(WPARAM, LPARAM);
727 int ret;
728
729 if (reentering)
730 return; /* don't unpend the pending */
731
732 pending_netevent = FALSE;
733
734 reentering = 1;
735 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
736 reentering = 0;
737
738 if (ret == 0 && !session_closed) {
739 /* Abnormal exits will already have set session_closed and taken
740 * appropriate action. */
741 if (cfg.close_on_exit == COE_ALWAYS ||
742 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
743 else {
744 session_closed = TRUE;
745 SetWindowText(hwnd, "PuTTY (inactive)");
746 MessageBox(hwnd, "Connection closed by remote host",
747 "PuTTY", MB_OK | MB_ICONINFORMATION);
748 }
749 }
750 }
751
752 /*
753 * Copy the colour palette from the configuration data into defpal.
754 * This is non-trivial because the colour indices are different.
755 */
756 static void cfgtopalette(void)
757 {
758 int i;
759 static const int ww[] = {
760 6, 7, 8, 9, 10, 11, 12, 13,
761 14, 15, 16, 17, 18, 19, 20, 21,
762 0, 1, 2, 3, 4, 4, 5, 5
763 };
764
765 for (i = 0; i < 24; i++) {
766 int w = ww[i];
767 defpal[i].rgbtRed = cfg.colours[w][0];
768 defpal[i].rgbtGreen = cfg.colours[w][1];
769 defpal[i].rgbtBlue = cfg.colours[w][2];
770 }
771 }
772
773 /*
774 * Set up the colour palette.
775 */
776 static void init_palette(void)
777 {
778 int i;
779 HDC hdc = GetDC(hwnd);
780 if (hdc) {
781 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
782 logpal = smalloc(sizeof(*logpal)
783 - sizeof(logpal->palPalEntry)
784 + NCOLOURS * sizeof(PALETTEENTRY));
785 logpal->palVersion = 0x300;
786 logpal->palNumEntries = NCOLOURS;
787 for (i = 0; i < NCOLOURS; i++) {
788 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
789 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
790 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
791 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
792 }
793 pal = CreatePalette(logpal);
794 if (pal) {
795 SelectPalette(hdc, pal, FALSE);
796 RealizePalette(hdc);
797 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
798 }
799 }
800 ReleaseDC(hwnd, hdc);
801 }
802 if (pal)
803 for (i = 0; i < NCOLOURS; i++)
804 colours[i] = PALETTERGB(defpal[i].rgbtRed,
805 defpal[i].rgbtGreen,
806 defpal[i].rgbtBlue);
807 else
808 for (i = 0; i < NCOLOURS; i++)
809 colours[i] = RGB(defpal[i].rgbtRed,
810 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
811 }
812
813 /*
814 * Initialise all the fonts we will need. There may be as many as
815 * eight or as few as one. We also:
816 *
817 * - check the font width and height, correcting our guesses if
818 * necessary.
819 *
820 * - verify that the bold font is the same width as the ordinary
821 * one, and engage shadow bolding if not.
822 *
823 * - verify that the underlined font is the same width as the
824 * ordinary one (manual underlining by means of line drawing can
825 * be done in a pinch).
826 */
827 static void init_fonts(int pick_width)
828 {
829 TEXTMETRIC tm;
830 int i;
831 int fsize[8];
832 HDC hdc;
833 int fw_dontcare, fw_bold;
834 int firstchar = ' ';
835
836 #ifdef CHECKOEMFONT
837 font_messup:
838 #endif
839 for (i = 0; i < 8; i++)
840 fonts[i] = NULL;
841
842 if (cfg.fontisbold) {
843 fw_dontcare = FW_BOLD;
844 fw_bold = FW_HEAVY;
845 } else {
846 fw_dontcare = FW_DONTCARE;
847 fw_bold = FW_BOLD;
848 }
849
850 hdc = GetDC(hwnd);
851
852 font_height = cfg.fontheight;
853 if (font_height > 0) {
854 font_height =
855 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
856 }
857 font_width = pick_width;
858
859 #define f(i,c,w,u) \
860 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
861 c, OUT_DEFAULT_PRECIS, \
862 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
863 FIXED_PITCH | FF_DONTCARE, cfg.font)
864
865 if (cfg.vtmode != VT_OEMONLY) {
866 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
867
868 SelectObject(hdc, fonts[FONT_NORMAL]);
869 GetTextMetrics(hdc, &tm);
870 font_height = tm.tmHeight;
871 font_width = tm.tmAveCharWidth;
872
873 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
874
875 /*
876 * Some fonts, e.g. 9-pt Courier, draw their underlines
877 * outside their character cell. We successfully prevent
878 * screen corruption by clipping the text output, but then
879 * we lose the underline completely. Here we try to work
880 * out whether this is such a font, and if it is, we set a
881 * flag that causes underlines to be drawn by hand.
882 *
883 * Having tried other more sophisticated approaches (such
884 * as examining the TEXTMETRIC structure or requesting the
885 * height of a string), I think we'll do this the brute
886 * force way: we create a small bitmap, draw an underlined
887 * space on it, and test to see whether any pixels are
888 * foreground-coloured. (Since we expect the underline to
889 * go all the way across the character cell, we only search
890 * down a single column of the bitmap, half way across.)
891 */
892 {
893 HDC und_dc;
894 HBITMAP und_bm, und_oldbm;
895 int i, gotit;
896 COLORREF c;
897
898 und_dc = CreateCompatibleDC(hdc);
899 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
900 und_oldbm = SelectObject(und_dc, und_bm);
901 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
902 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
903 SetTextColor(und_dc, RGB(255, 255, 255));
904 SetBkColor(und_dc, RGB(0, 0, 0));
905 SetBkMode(und_dc, OPAQUE);
906 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
907 gotit = FALSE;
908 for (i = 0; i < font_height; i++) {
909 c = GetPixel(und_dc, font_width / 2, i);
910 if (c != RGB(0, 0, 0))
911 gotit = TRUE;
912 }
913 SelectObject(und_dc, und_oldbm);
914 DeleteObject(und_bm);
915 DeleteDC(und_dc);
916 font_needs_hand_underlining = !gotit;
917 }
918
919 if (bold_mode == BOLD_FONT) {
920 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
921 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
922 }
923
924 if (cfg.vtmode == VT_OEMANSI) {
925 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
926 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
927
928 if (bold_mode == BOLD_FONT) {
929 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
930 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
931 }
932 }
933 } else {
934 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
935
936 SelectObject(hdc, fonts[FONT_OEM]);
937 GetTextMetrics(hdc, &tm);
938 font_height = tm.tmHeight;
939 font_width = tm.tmAveCharWidth;
940
941 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
942
943 if (bold_mode == BOLD_FONT) {
944 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
945 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
946 }
947 }
948 #undef f
949
950 descent = tm.tmAscent + 1;
951 if (descent >= font_height)
952 descent = font_height - 1;
953 firstchar = tm.tmFirstChar;
954
955 for (i = 0; i < 8; i++) {
956 if (fonts[i]) {
957 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
958 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
959 else
960 fsize[i] = -i;
961 } else
962 fsize[i] = -i;
963 }
964
965 ReleaseDC(hwnd, hdc);
966
967 /* ... This is wrong in OEM only mode */
968 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
969 (bold_mode == BOLD_FONT &&
970 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
971 und_mode = UND_LINE;
972 DeleteObject(fonts[FONT_UNDERLINE]);
973 if (bold_mode == BOLD_FONT)
974 DeleteObject(fonts[FONT_BOLDUND]);
975 }
976
977 if (bold_mode == BOLD_FONT && fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
978 bold_mode = BOLD_SHADOW;
979 DeleteObject(fonts[FONT_BOLD]);
980 if (und_mode == UND_FONT)
981 DeleteObject(fonts[FONT_BOLDUND]);
982 }
983 #ifdef CHECKOEMFONT
984 /* With the fascist font painting it doesn't matter if the linedraw font
985 * isn't exactly the right size anymore so we don't have to check this.
986 */
987 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL]) {
988 if (cfg.fontcharset == OEM_CHARSET) {
989 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
990 "different sizes. Using OEM-only mode instead",
991 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
992 cfg.vtmode = VT_OEMONLY;
993 } else if (firstchar < ' ') {
994 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
995 "different sizes. Using XTerm mode instead",
996 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
997 cfg.vtmode = VT_XWINDOWS;
998 } else {
999 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
1000 "different sizes. Using ISO8859-1 mode instead",
1001 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
1002 cfg.vtmode = VT_POORMAN;
1003 }
1004
1005 for (i = 0; i < 8; i++)
1006 if (fonts[i])
1007 DeleteObject(fonts[i]);
1008 goto font_messup;
1009 }
1010 #endif
1011 }
1012
1013 void request_resize(int w, int h, int refont)
1014 {
1015 int width, height;
1016
1017 /* If the window is maximized supress resizing attempts */
1018 if (IsZoomed(hwnd))
1019 return;
1020
1021 #ifdef CHECKOEMFONT
1022 /* Don't do this in OEMANSI, you may get disable messages */
1023 if (refont && w != cols && (cols == 80 || cols == 132)
1024 && cfg.vtmode != VT_OEMANSI)
1025 #else
1026 if (refont && w != cols && (cols == 80 || cols == 132))
1027 #endif
1028 {
1029 /* If font width too big for screen should we shrink the font more ? */
1030 if (w == 132)
1031 font_width = ((font_width * cols + w / 2) / w);
1032 else
1033 font_width = 0;
1034 {
1035 int i;
1036 for (i = 0; i < 8; i++)
1037 if (fonts[i])
1038 DeleteObject(fonts[i]);
1039 }
1040 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1041 und_mode = UND_FONT;
1042 init_fonts(font_width);
1043 } else {
1044 static int first_time = 1;
1045 static RECT ss;
1046
1047 switch (first_time) {
1048 case 1:
1049 /* Get the size of the screen */
1050 if (GetClientRect(GetDesktopWindow(), &ss))
1051 /* first_time = 0 */ ;
1052 else {
1053 first_time = 2;
1054 break;
1055 }
1056 case 0:
1057 /* Make sure the values are sane */
1058 width = (ss.right - ss.left - extra_width) / font_width;
1059 height = (ss.bottom - ss.top - extra_height) / font_height;
1060
1061 if (w > width)
1062 w = width;
1063 if (h > height)
1064 h = height;
1065 if (w < 15)
1066 w = 15;
1067 if (h < 1)
1068 w = 1;
1069 }
1070 }
1071
1072 width = extra_width + font_width * w;
1073 height = extra_height + font_height * h;
1074
1075 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1076 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1077 SWP_NOMOVE | SWP_NOZORDER);
1078 }
1079
1080 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1081 {
1082 int thistime = GetMessageTime();
1083
1084 if (send_raw_mouse) {
1085 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1086 return;
1087 }
1088
1089 if (lastbtn == b && thistime - lasttime < dbltime) {
1090 lastact = (lastact == MA_CLICK ? MA_2CLK :
1091 lastact == MA_2CLK ? MA_3CLK :
1092 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1093 } else {
1094 lastbtn = b;
1095 lastact = MA_CLICK;
1096 }
1097 if (lastact != MA_NOTHING)
1098 term_mouse(b, lastact, x, y, shift, ctrl);
1099 lasttime = thistime;
1100 }
1101
1102 /*
1103 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1104 * into a cooked one (SELECT, EXTEND, PASTE).
1105 */
1106 Mouse_Button translate_button(Mouse_Button button)
1107 {
1108 if (button == MBT_LEFT)
1109 return MBT_SELECT;
1110 if (button == MBT_MIDDLE)
1111 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1112 if (button == MBT_RIGHT)
1113 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1114 }
1115
1116 static void show_mouseptr(int show)
1117 {
1118 static int cursor_visible = 1;
1119 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1120 show = 1;
1121 if (cursor_visible && !show)
1122 ShowCursor(FALSE);
1123 else if (!cursor_visible && show)
1124 ShowCursor(TRUE);
1125 cursor_visible = show;
1126 }
1127
1128 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1129 WPARAM wParam, LPARAM lParam)
1130 {
1131 HDC hdc;
1132 static int ignore_size = FALSE;
1133 static int ignore_clip = FALSE;
1134 static int just_reconfigged = FALSE;
1135 static int resizing = FALSE;
1136 static int need_backend_resize = FALSE;
1137 static int defered_resize = FALSE;
1138
1139 switch (message) {
1140 case WM_TIMER:
1141 if (pending_netevent)
1142 enact_pending_netevent();
1143 if (inbuf_head)
1144 term_out();
1145 noise_regular();
1146 HideCaret(hwnd);
1147 term_update();
1148 ShowCaret(hwnd);
1149 if (cfg.ping_interval > 0) {
1150 time_t now;
1151 time(&now);
1152 if (now - last_movement > cfg.ping_interval) {
1153 back->special(TS_PING);
1154 last_movement = now;
1155 }
1156 }
1157 return 0;
1158 case WM_CREATE:
1159 break;
1160 case WM_CLOSE:
1161 show_mouseptr(1);
1162 if (!cfg.warn_on_close || session_closed ||
1163 MessageBox(hwnd,
1164 "Are you sure you want to close this session?",
1165 "PuTTY Exit Confirmation",
1166 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1167 DestroyWindow(hwnd);
1168 return 0;
1169 case WM_DESTROY:
1170 show_mouseptr(1);
1171 PostQuitMessage(0);
1172 return 0;
1173 case WM_SYSCOMMAND:
1174 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1175 case IDM_SHOWLOG:
1176 showeventlog(hwnd);
1177 break;
1178 case IDM_NEWSESS:
1179 case IDM_DUPSESS:
1180 case IDM_SAVEDSESS:
1181 {
1182 char b[2048];
1183 char c[30], *cl;
1184 int freecl = FALSE;
1185 STARTUPINFO si;
1186 PROCESS_INFORMATION pi;
1187 HANDLE filemap = NULL;
1188
1189 if (wParam == IDM_DUPSESS) {
1190 /*
1191 * Allocate a file-mapping memory chunk for the
1192 * config structure.
1193 */
1194 SECURITY_ATTRIBUTES sa;
1195 Config *p;
1196
1197 sa.nLength = sizeof(sa);
1198 sa.lpSecurityDescriptor = NULL;
1199 sa.bInheritHandle = TRUE;
1200 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1201 &sa,
1202 PAGE_READWRITE,
1203 0, sizeof(Config), NULL);
1204 if (filemap) {
1205 p = (Config *) MapViewOfFile(filemap,
1206 FILE_MAP_WRITE,
1207 0, 0, sizeof(Config));
1208 if (p) {
1209 *p = cfg; /* structure copy */
1210 UnmapViewOfFile(p);
1211 }
1212 }
1213 sprintf(c, "putty &%p", filemap);
1214 cl = c;
1215 } else if (wParam == IDM_SAVEDSESS) {
1216 char *session =
1217 sessions[(lParam - IDM_SAVED_MIN) / 16];
1218 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1219 if (!cl)
1220 cl = NULL; /* not a very important failure mode */
1221 else {
1222 sprintf(cl, "putty @%s", session);
1223 freecl = TRUE;
1224 }
1225 } else
1226 cl = NULL;
1227
1228 GetModuleFileName(NULL, b, sizeof(b) - 1);
1229 si.cb = sizeof(si);
1230 si.lpReserved = NULL;
1231 si.lpDesktop = NULL;
1232 si.lpTitle = NULL;
1233 si.dwFlags = 0;
1234 si.cbReserved2 = 0;
1235 si.lpReserved2 = NULL;
1236 CreateProcess(b, cl, NULL, NULL, TRUE,
1237 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1238
1239 if (filemap)
1240 CloseHandle(filemap);
1241 if (freecl)
1242 sfree(cl);
1243 }
1244 break;
1245 case IDM_RECONF:
1246 {
1247 int prev_alwaysontop = cfg.alwaysontop;
1248 int prev_sunken_edge = cfg.sunken_edge;
1249 char oldlogfile[FILENAME_MAX];
1250 int oldlogtype;
1251 int need_setwpos = FALSE;
1252 int old_fwidth, old_fheight;
1253
1254 strcpy(oldlogfile, cfg.logfilename);
1255 oldlogtype = cfg.logtype;
1256 old_fwidth = font_width;
1257 old_fheight = font_height;
1258 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1259
1260 if (!do_reconfig(hwnd))
1261 break;
1262
1263 if (strcmp(oldlogfile, cfg.logfilename) ||
1264 oldlogtype != cfg.logtype) {
1265 logfclose(); /* reset logging */
1266 logfopen();
1267 }
1268
1269 just_reconfigged = TRUE;
1270 {
1271 int i;
1272 for (i = 0; i < 8; i++)
1273 if (fonts[i])
1274 DeleteObject(fonts[i]);
1275 }
1276 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1277 und_mode = UND_FONT;
1278 init_fonts(0);
1279 sfree(logpal);
1280 /*
1281 * Flush the line discipline's edit buffer in the
1282 * case where local editing has just been disabled.
1283 */
1284 ldisc_send(NULL, 0);
1285 if (pal)
1286 DeleteObject(pal);
1287 logpal = NULL;
1288 pal = NULL;
1289 cfgtopalette();
1290 init_palette();
1291
1292 /* Enable or disable the scroll bar, etc */
1293 {
1294 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1295 LONG nexflag, exflag =
1296 GetWindowLong(hwnd, GWL_EXSTYLE);
1297
1298 nexflag = exflag;
1299 if (cfg.alwaysontop != prev_alwaysontop) {
1300 if (cfg.alwaysontop) {
1301 nexflag |= WS_EX_TOPMOST;
1302 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1303 SWP_NOMOVE | SWP_NOSIZE);
1304 } else {
1305 nexflag &= ~(WS_EX_TOPMOST);
1306 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1307 SWP_NOMOVE | SWP_NOSIZE);
1308 }
1309 }
1310 if (cfg.sunken_edge)
1311 nexflag |= WS_EX_CLIENTEDGE;
1312 else
1313 nexflag &= ~(WS_EX_CLIENTEDGE);
1314
1315 nflg = flag;
1316 if (cfg.scrollbar)
1317 nflg |= WS_VSCROLL;
1318 else
1319 nflg &= ~WS_VSCROLL;
1320 if (cfg.locksize)
1321 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1322 else
1323 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1324
1325 if (nflg != flag || nexflag != exflag) {
1326 RECT cr, wr;
1327
1328 if (nflg != flag)
1329 SetWindowLong(hwnd, GWL_STYLE, nflg);
1330 if (nexflag != exflag)
1331 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1332
1333 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1334
1335 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1336 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1337 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1338 | SWP_FRAMECHANGED);
1339
1340 GetWindowRect(hwnd, &wr);
1341 GetClientRect(hwnd, &cr);
1342 extra_width =
1343 wr.right - wr.left - cr.right + cr.left;
1344 extra_height =
1345 wr.bottom - wr.top - cr.bottom + cr.top;
1346 need_setwpos = TRUE;
1347 }
1348 }
1349
1350 if (cfg.height != rows ||
1351 cfg.width != cols ||
1352 old_fwidth != font_width ||
1353 old_fheight != font_height ||
1354 cfg.savelines != savelines ||
1355 cfg.sunken_edge != prev_sunken_edge)
1356 need_setwpos = TRUE;
1357
1358 if (IsZoomed(hwnd)) {
1359 int w, h;
1360 RECT cr;
1361 if (need_setwpos)
1362 defered_resize = TRUE;
1363
1364 GetClientRect(hwnd, &cr);
1365 w = cr.right - cr.left;
1366 h = cr.bottom - cr.top;
1367 w = w / font_width;
1368 if (w < 1)
1369 w = 1;
1370 h = h / font_height;
1371 if (h < 1)
1372 h = 1;
1373
1374 term_size(h, w, cfg.savelines);
1375 InvalidateRect(hwnd, NULL, TRUE);
1376 back->size();
1377 } else {
1378 term_size(cfg.height, cfg.width, cfg.savelines);
1379 InvalidateRect(hwnd, NULL, TRUE);
1380 if (need_setwpos) {
1381 SetWindowPos(hwnd, NULL, 0, 0,
1382 extra_width + font_width * cfg.width,
1383 extra_height +
1384 font_height * cfg.height,
1385 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1386 SWP_NOMOVE | SWP_NOZORDER);
1387 }
1388 }
1389 /* Oops */
1390 if (cfg.locksize && IsZoomed(hwnd))
1391 force_normal(hwnd);
1392 set_title(cfg.wintitle);
1393 if (IsIconic(hwnd)) {
1394 SetWindowText(hwnd,
1395 cfg.win_name_always ? window_name :
1396 icon_name);
1397 }
1398 }
1399 break;
1400 case IDM_COPYALL:
1401 term_copyall();
1402 break;
1403 case IDM_CLRSB:
1404 term_clrsb();
1405 break;
1406 case IDM_RESET:
1407 term_pwron();
1408 break;
1409 case IDM_TEL_AYT:
1410 back->special(TS_AYT);
1411 break;
1412 case IDM_TEL_BRK:
1413 back->special(TS_BRK);
1414 break;
1415 case IDM_TEL_SYNCH:
1416 back->special(TS_SYNCH);
1417 break;
1418 case IDM_TEL_EC:
1419 back->special(TS_EC);
1420 break;
1421 case IDM_TEL_EL:
1422 back->special(TS_EL);
1423 break;
1424 case IDM_TEL_GA:
1425 back->special(TS_GA);
1426 break;
1427 case IDM_TEL_NOP:
1428 back->special(TS_NOP);
1429 break;
1430 case IDM_TEL_ABORT:
1431 back->special(TS_ABORT);
1432 break;
1433 case IDM_TEL_AO:
1434 back->special(TS_AO);
1435 break;
1436 case IDM_TEL_IP:
1437 back->special(TS_IP);
1438 break;
1439 case IDM_TEL_SUSP:
1440 back->special(TS_SUSP);
1441 break;
1442 case IDM_TEL_EOR:
1443 back->special(TS_EOR);
1444 break;
1445 case IDM_TEL_EOF:
1446 back->special(TS_EOF);
1447 break;
1448 case IDM_ABOUT:
1449 showabout(hwnd);
1450 break;
1451 default:
1452 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1453 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1454 }
1455 }
1456 break;
1457
1458 #define X_POS(l) ((int)(short)LOWORD(l))
1459 #define Y_POS(l) ((int)(short)HIWORD(l))
1460
1461 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1462 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1463 #define WHEEL_DELTA 120
1464 case WM_MOUSEWHEEL:
1465 {
1466 wheel_accumulator += (short) HIWORD(wParam);
1467 wParam = LOWORD(wParam);
1468
1469 /* process events when the threshold is reached */
1470 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1471 int b;
1472
1473 /* reduce amount for next time */
1474 if (wheel_accumulator > 0) {
1475 b = MBT_WHEEL_UP;
1476 wheel_accumulator -= WHEEL_DELTA;
1477 } else if (wheel_accumulator < 0) {
1478 b = MBT_WHEEL_DOWN;
1479 wheel_accumulator += WHEEL_DELTA;
1480 } else
1481 break;
1482
1483 if (send_raw_mouse) {
1484 /* send a mouse-down followed by a mouse up */
1485 term_mouse(b,
1486 MA_CLICK,
1487 TO_CHR_X(X_POS(lParam)),
1488 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1489 wParam & MK_CONTROL);
1490 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1491 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1492 wParam & MK_CONTROL);
1493 } else {
1494 /* trigger a scroll */
1495 term_scroll(0,
1496 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1497 }
1498 }
1499 return 0;
1500 }
1501 case WM_LBUTTONDOWN:
1502 case WM_MBUTTONDOWN:
1503 case WM_RBUTTONDOWN:
1504 case WM_LBUTTONUP:
1505 case WM_MBUTTONUP:
1506 case WM_RBUTTONUP:
1507 {
1508 int button, press;
1509 switch (message) {
1510 case WM_LBUTTONDOWN:
1511 button = MBT_LEFT;
1512 press = 1;
1513 break;
1514 case WM_MBUTTONDOWN:
1515 button = MBT_MIDDLE;
1516 press = 1;
1517 break;
1518 case WM_RBUTTONDOWN:
1519 button = MBT_RIGHT;
1520 press = 1;
1521 break;
1522 case WM_LBUTTONUP:
1523 button = MBT_LEFT;
1524 press = 0;
1525 break;
1526 case WM_MBUTTONUP:
1527 button = MBT_MIDDLE;
1528 press = 0;
1529 break;
1530 case WM_RBUTTONUP:
1531 button = MBT_RIGHT;
1532 press = 0;
1533 break;
1534 }
1535 show_mouseptr(1);
1536 if (press) {
1537 click(button,
1538 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1539 wParam & MK_SHIFT, wParam & MK_CONTROL);
1540 SetCapture(hwnd);
1541 } else {
1542 term_mouse(button, MA_RELEASE,
1543 TO_CHR_X(X_POS(lParam)),
1544 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1545 wParam & MK_CONTROL);
1546 ReleaseCapture();
1547 }
1548 }
1549 return 0;
1550 case WM_MOUSEMOVE:
1551 show_mouseptr(1);
1552 /*
1553 * Add the mouse position and message time to the random
1554 * number noise.
1555 */
1556 noise_ultralight(lParam);
1557
1558 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1559 Mouse_Button b;
1560 if (wParam & MK_LBUTTON)
1561 b = MBT_SELECT;
1562 else if (wParam & MK_MBUTTON)
1563 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1564 else
1565 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1566 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1567 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1568 wParam & MK_CONTROL);
1569 }
1570 return 0;
1571 case WM_NCMOUSEMOVE:
1572 show_mouseptr(1);
1573 noise_ultralight(lParam);
1574 return 0;
1575 case WM_IGNORE_CLIP:
1576 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1577 break;
1578 case WM_DESTROYCLIPBOARD:
1579 if (!ignore_clip)
1580 term_deselect();
1581 ignore_clip = FALSE;
1582 return 0;
1583 case WM_PAINT:
1584 {
1585 PAINTSTRUCT p;
1586 HideCaret(hwnd);
1587 hdc = BeginPaint(hwnd, &p);
1588 if (pal) {
1589 SelectPalette(hdc, pal, TRUE);
1590 RealizePalette(hdc);
1591 }
1592 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1593 p.rcPaint.right, p.rcPaint.bottom);
1594 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1595 SelectObject(hdc, GetStockObject(WHITE_PEN));
1596 EndPaint(hwnd, &p);
1597 ShowCaret(hwnd);
1598 }
1599 return 0;
1600 case WM_NETEVENT:
1601 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1602 * but the only one that's likely to try to overload us is FD_READ.
1603 * This means buffering just one is fine.
1604 */
1605 if (pending_netevent)
1606 enact_pending_netevent();
1607
1608 pending_netevent = TRUE;
1609 pend_netevent_wParam = wParam;
1610 pend_netevent_lParam = lParam;
1611 time(&last_movement);
1612 return 0;
1613 case WM_SETFOCUS:
1614 has_focus = TRUE;
1615 CreateCaret(hwnd, caretbm, font_width, font_height);
1616 ShowCaret(hwnd);
1617 compose_state = 0;
1618 term_out();
1619 term_update();
1620 break;
1621 case WM_KILLFOCUS:
1622 show_mouseptr(1);
1623 has_focus = FALSE;
1624 DestroyCaret();
1625 term_out();
1626 term_update();
1627 break;
1628 case WM_IGNORE_SIZE:
1629 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1630 break;
1631 case WM_ENTERSIZEMOVE:
1632 EnableSizeTip(1);
1633 resizing = TRUE;
1634 need_backend_resize = FALSE;
1635 break;
1636 case WM_EXITSIZEMOVE:
1637 EnableSizeTip(0);
1638 resizing = FALSE;
1639 if (need_backend_resize)
1640 back->size();
1641 break;
1642 case WM_SIZING:
1643 {
1644 int width, height, w, h, ew, eh;
1645 LPRECT r = (LPRECT) lParam;
1646
1647 width = r->right - r->left - extra_width;
1648 height = r->bottom - r->top - extra_height;
1649 w = (width + font_width / 2) / font_width;
1650 if (w < 1)
1651 w = 1;
1652 h = (height + font_height / 2) / font_height;
1653 if (h < 1)
1654 h = 1;
1655 UpdateSizeTip(hwnd, w, h);
1656 ew = width - w * font_width;
1657 eh = height - h * font_height;
1658 if (ew != 0) {
1659 if (wParam == WMSZ_LEFT ||
1660 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1661 r->left += ew;
1662 else
1663 r->right -= ew;
1664 }
1665 if (eh != 0) {
1666 if (wParam == WMSZ_TOP ||
1667 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1668 r->top += eh;
1669 else
1670 r->bottom -= eh;
1671 }
1672 if (ew || eh)
1673 return 1;
1674 else
1675 return 0;
1676 }
1677 /* break; (never reached) */
1678 case WM_SIZE:
1679 if (wParam == SIZE_MINIMIZED) {
1680 SetWindowText(hwnd,
1681 cfg.win_name_always ? window_name : icon_name);
1682 break;
1683 }
1684 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1685 SetWindowText(hwnd, window_name);
1686 if (!ignore_size) {
1687 int width, height, w, h;
1688 #if 0 /* we have fixed this using WM_SIZING now */
1689 int ew, eh;
1690 #endif
1691
1692 width = LOWORD(lParam);
1693 height = HIWORD(lParam);
1694 w = width / font_width;
1695 if (w < 1)
1696 w = 1;
1697 h = height / font_height;
1698 if (h < 1)
1699 h = 1;
1700 #if 0 /* we have fixed this using WM_SIZING now */
1701 ew = width - w * font_width;
1702 eh = height - h * font_height;
1703 if (ew != 0 || eh != 0) {
1704 RECT r;
1705 GetWindowRect(hwnd, &r);
1706 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1707 SetWindowPos(hwnd, NULL, 0, 0,
1708 r.right - r.left - ew, r.bottom - r.top - eh,
1709 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1710 }
1711 #endif
1712 if (w != cols || h != rows || just_reconfigged) {
1713 term_invalidate();
1714 term_size(h, w, cfg.savelines);
1715 /*
1716 * Don't call back->size in mid-resize. (To prevent
1717 * massive numbers of resize events getting sent
1718 * down the connection during an NT opaque drag.)
1719 */
1720 if (!resizing)
1721 back->size();
1722 else {
1723 need_backend_resize = TRUE;
1724 cfg.height = h;
1725 cfg.width = w;
1726 }
1727 just_reconfigged = FALSE;
1728 }
1729 }
1730 if (wParam == SIZE_RESTORED && defered_resize) {
1731 defered_resize = FALSE;
1732 SetWindowPos(hwnd, NULL, 0, 0,
1733 extra_width + font_width * cfg.width,
1734 extra_height + font_height * cfg.height,
1735 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1736 SWP_NOMOVE | SWP_NOZORDER);
1737 }
1738 ignore_size = FALSE;
1739 return 0;
1740 case WM_VSCROLL:
1741 switch (LOWORD(wParam)) {
1742 case SB_BOTTOM:
1743 term_scroll(-1, 0);
1744 break;
1745 case SB_TOP:
1746 term_scroll(+1, 0);
1747 break;
1748 case SB_LINEDOWN:
1749 term_scroll(0, +1);
1750 break;
1751 case SB_LINEUP:
1752 term_scroll(0, -1);
1753 break;
1754 case SB_PAGEDOWN:
1755 term_scroll(0, +rows / 2);
1756 break;
1757 case SB_PAGEUP:
1758 term_scroll(0, -rows / 2);
1759 break;
1760 case SB_THUMBPOSITION:
1761 case SB_THUMBTRACK:
1762 term_scroll(1, HIWORD(wParam));
1763 break;
1764 }
1765 break;
1766 case WM_PALETTECHANGED:
1767 if ((HWND) wParam != hwnd && pal != NULL) {
1768 HDC hdc = get_ctx();
1769 if (hdc) {
1770 if (RealizePalette(hdc) > 0)
1771 UpdateColors(hdc);
1772 free_ctx(hdc);
1773 }
1774 }
1775 break;
1776 case WM_QUERYNEWPALETTE:
1777 if (pal != NULL) {
1778 HDC hdc = get_ctx();
1779 if (hdc) {
1780 if (RealizePalette(hdc) > 0)
1781 UpdateColors(hdc);
1782 free_ctx(hdc);
1783 return TRUE;
1784 }
1785 }
1786 return FALSE;
1787 case WM_KEYDOWN:
1788 case WM_SYSKEYDOWN:
1789 case WM_KEYUP:
1790 case WM_SYSKEYUP:
1791 /*
1792 * Add the scan code and keypress timing to the random
1793 * number noise.
1794 */
1795 noise_ultralight(lParam);
1796
1797 /*
1798 * We don't do TranslateMessage since it disassociates the
1799 * resulting CHAR message from the KEYDOWN that sparked it,
1800 * which we occasionally don't want. Instead, we process
1801 * KEYDOWN, and call the Win32 translator functions so that
1802 * we get the translations under _our_ control.
1803 */
1804 {
1805 unsigned char buf[20];
1806 int len;
1807
1808 if (wParam == VK_PROCESSKEY) {
1809 MSG m;
1810 m.hwnd = hwnd;
1811 m.message = WM_KEYDOWN;
1812 m.wParam = wParam;
1813 m.lParam = lParam & 0xdfff;
1814 TranslateMessage(&m);
1815 } else {
1816 len = TranslateKey(message, wParam, lParam, buf);
1817 if (len == -1)
1818 return DefWindowProc(hwnd, message, wParam, lParam);
1819 ldisc_send(buf, len);
1820
1821 if (len > 0)
1822 show_mouseptr(0);
1823 }
1824 }
1825 return 0;
1826 case WM_IME_CHAR:
1827 {
1828 unsigned char buf[2];
1829
1830 buf[1] = wParam;
1831 buf[0] = wParam >> 8;
1832 ldisc_send(buf, 2);
1833 }
1834 case WM_CHAR:
1835 case WM_SYSCHAR:
1836 /*
1837 * Nevertheless, we are prepared to deal with WM_CHAR
1838 * messages, should they crop up. So if someone wants to
1839 * post the things to us as part of a macro manoeuvre,
1840 * we're ready to cope.
1841 */
1842 {
1843 char c = xlat_kbd2tty((unsigned char) wParam);
1844 ldisc_send(&c, 1);
1845 }
1846 return 0;
1847 case WM_SETCURSOR:
1848 if (send_raw_mouse) {
1849 SetCursor(LoadCursor(NULL, IDC_ARROW));
1850 return TRUE;
1851 }
1852 }
1853
1854 return DefWindowProc(hwnd, message, wParam, lParam);
1855 }
1856
1857 /*
1858 * Move the system caret. (We maintain one, even though it's
1859 * invisible, for the benefit of blind people: apparently some
1860 * helper software tracks the system caret, so we should arrange to
1861 * have one.)
1862 */
1863 void sys_cursor(int x, int y)
1864 {
1865 if (has_focus)
1866 SetCaretPos(x * font_width, y * font_height);
1867 }
1868
1869 /*
1870 * Draw a line of text in the window, at given character
1871 * coordinates, in given attributes.
1872 *
1873 * We are allowed to fiddle with the contents of `text'.
1874 */
1875 void do_text(Context ctx, int x, int y, char *text, int len,
1876 unsigned long attr, int lattr)
1877 {
1878 COLORREF fg, bg, t;
1879 int nfg, nbg, nfont;
1880 HDC hdc = ctx;
1881 RECT line_box;
1882 int force_manual_underline = 0;
1883 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1884 static int *IpDx = 0, IpDxLEN = 0;;
1885
1886 if (len > IpDxLEN || IpDx[0] != fnt_width) {
1887 int i;
1888 if (len > IpDxLEN) {
1889 sfree(IpDx);
1890 IpDx = smalloc((len + 16) * sizeof(int));
1891 IpDxLEN = (len + 16);
1892 }
1893 for (i = 0; i < IpDxLEN; i++)
1894 IpDx[i] = fnt_width;
1895 }
1896
1897 x *= fnt_width;
1898 y *= font_height;
1899
1900 if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1901 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1902 attr ^= ATTR_CUR_XOR;
1903 }
1904
1905 nfont = 0;
1906 if (cfg.vtmode == VT_OEMONLY)
1907 nfont |= FONT_OEM;
1908
1909 /*
1910 * Map high-half characters in order to approximate ISO using
1911 * OEM character set. No characters are missing if the OEM codepage
1912 * is CP850.
1913 */
1914 if (nfont & FONT_OEM) {
1915 int i;
1916 for (i = 0; i < len; i++)
1917 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1918 #if 0
1919 /* This is CP850 ... perfect translation */
1920 static const char oemhighhalf[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1921 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1922 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1923 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1924 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1925 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1926 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1927 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1928 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1929 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1930 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1931 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1932 ;
1933 #endif
1934 /* This is CP437 ... junk translation */
1935 static const unsigned char oemhighhalf[] = {
1936 0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1937 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1938 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1939 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1940 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1941 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1942 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1943 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1944 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1945 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1946 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1947 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1948 };
1949
1950 text[i] = oemhighhalf[(unsigned char) text[i] - 0xA0];
1951 }
1952 }
1953
1954 if (attr & ATTR_LINEDRW) {
1955 int i;
1956 /* ISO 8859-1 */
1957 static const char poorman[] =
1958 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1959
1960 /* CP437 */
1961 static const char oemmap_437[] =
1962 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1963 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1964
1965 /* CP850 */
1966 static const char oemmap_850[] =
1967 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1968 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1969
1970 /* Poor windows font ... eg: windows courier */
1971 static const char oemmap[] =
1972 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1973 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1974
1975 /*
1976 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1977 * VT100 line drawing chars; everything else stays normal.
1978 *
1979 * Actually '_' maps to space too, but that's done before.
1980 */
1981 switch (cfg.vtmode) {
1982 case VT_XWINDOWS:
1983 for (i = 0; i < len; i++)
1984 if (text[i] >= '\x60' && text[i] <= '\x7E')
1985 text[i] += '\x01' - '\x60';
1986 break;
1987 case VT_OEMANSI:
1988 /* Make sure we actually have an OEM font */
1989 if (fonts[nfont | FONT_OEM]) {
1990 case VT_OEMONLY:
1991 nfont |= FONT_OEM;
1992 for (i = 0; i < len; i++)
1993 if (text[i] >= '\x60' && text[i] <= '\x7E')
1994 text[i] = oemmap[(unsigned char) text[i] - 0x60];
1995 break;
1996 }
1997 case VT_POORMAN:
1998 for (i = 0; i < len; i++)
1999 if (text[i] >= '\x60' && text[i] <= '\x7E')
2000 text[i] = poorman[(unsigned char) text[i] - 0x60];
2001 break;
2002 }
2003 }
2004
2005 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2006 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2007 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2008 nfont |= FONT_BOLD;
2009 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2010 nfont |= FONT_UNDERLINE;
2011 if (!fonts[nfont]) {
2012 if (nfont & FONT_UNDERLINE)
2013 force_manual_underline = 1;
2014 /* Don't do the same for manual bold, it could be bad news. */
2015
2016 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2017 }
2018 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
2019 force_manual_underline = 1;
2020 if (attr & ATTR_REVERSE) {
2021 t = nfg;
2022 nfg = nbg;
2023 nbg = t;
2024 }
2025 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2026 nfg++;
2027 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2028 nbg++;
2029 fg = colours[nfg];
2030 bg = colours[nbg];
2031 SelectObject(hdc, fonts[nfont]);
2032 SetTextColor(hdc, fg);
2033 SetBkColor(hdc, bg);
2034 SetBkMode(hdc, OPAQUE);
2035 line_box.left = x;
2036 line_box.top = y;
2037 line_box.right = x + fnt_width * len;
2038 line_box.bottom = y + font_height;
2039 ExtTextOut(hdc, x, y, ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len,
2040 IpDx);
2041 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2042 SetBkMode(hdc, TRANSPARENT);
2043
2044 /* GRR: This draws the character outside it's box and can leave
2045 * 'droppings' even with the clip box! I suppose I could loop it
2046 * one character at a time ... yuk.
2047 *
2048 * Or ... I could do a test print with "W", and use +1 or -1 for this
2049 * shift depending on if the leftmost column is blank...
2050 */
2051 ExtTextOut(hdc, x - 1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
2052 }
2053 if (force_manual_underline ||
2054 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
2055 HPEN oldpen;
2056 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2057 MoveToEx(hdc, x, y + descent, NULL);
2058 LineTo(hdc, x + len * fnt_width, y + descent);
2059 oldpen = SelectObject(hdc, oldpen);
2060 DeleteObject(oldpen);
2061 }
2062 if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
2063 POINT pts[5];
2064 HPEN oldpen;
2065 pts[0].x = pts[1].x = pts[4].x = x;
2066 pts[2].x = pts[3].x = x + fnt_width - 1;
2067 pts[0].y = pts[3].y = pts[4].y = y;
2068 pts[1].y = pts[2].y = y + font_height - 1;
2069 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2070 Polyline(hdc, pts, 5);
2071 oldpen = SelectObject(hdc, oldpen);
2072 DeleteObject(oldpen);
2073 }
2074 if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
2075 int startx, starty, dx, dy, length, i;
2076 if (cfg.cursor_type == 1) {
2077 startx = x;
2078 starty = y + descent;
2079 dx = 1;
2080 dy = 0;
2081 length = fnt_width;
2082 } else {
2083 int xadjust = 0;
2084 if (attr & ATTR_RIGHTCURS)
2085 xadjust = fnt_width - 1;
2086 startx = x + xadjust;
2087 starty = y;
2088 dx = 0;
2089 dy = 1;
2090 length = font_height;
2091 }
2092 if (attr & ATTR_ACTCURS) {
2093 HPEN oldpen;
2094 oldpen =
2095 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2096 MoveToEx(hdc, startx, starty, NULL);
2097 LineTo(hdc, startx + dx * length, starty + dy * length);
2098 oldpen = SelectObject(hdc, oldpen);
2099 DeleteObject(oldpen);
2100 } else {
2101 for (i = 0; i < length; i++) {
2102 if (i % 2 == 0) {
2103 SetPixel(hdc, startx, starty, colours[23]);
2104 }
2105 startx += dx;
2106 starty += dy;
2107 }
2108 }
2109 }
2110 }
2111
2112 static int check_compose(int first, int second)
2113 {
2114
2115 static char *composetbl[] = {
2116 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
2117 "C/¢",
2118 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
2119 "S!§",
2120 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
2121 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
2122 ",,¸",
2123 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
2124 "^AÂ",
2125 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
2126 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
2127 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
2128 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
2129 ",cç",
2130 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
2131 "~nñ",
2132 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
2133 "^uû",
2134 "\"uü", "'yý", "htþ", "\"yÿ",
2135 0
2136 };
2137
2138 char **c;
2139 static int recurse = 0;
2140 int nc = -1;
2141
2142 for (c = composetbl; *c; c++) {
2143 if ((*c)[0] == first && (*c)[1] == second) {
2144 return (*c)[2] & 0xFF;
2145 }
2146 }
2147
2148 if (recurse == 0) {
2149 recurse = 1;
2150 nc = check_compose(second, first);
2151 if (nc == -1)
2152 nc = check_compose(toupper(first), toupper(second));
2153 if (nc == -1)
2154 nc = check_compose(toupper(second), toupper(first));
2155 recurse = 0;
2156 }
2157 return nc;
2158 }
2159
2160
2161 /*
2162 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2163 * codes. Returns number of bytes used or zero to drop the message
2164 * or -1 to forward the message to windows.
2165 */
2166 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2167 unsigned char *output)
2168 {
2169 BYTE keystate[256];
2170 int scan, left_alt = 0, key_down, shift_state;
2171 int r, i, code;
2172 unsigned char *p = output;
2173 static int alt_state = 0;
2174
2175 HKL kbd_layout = GetKeyboardLayout(0);
2176
2177 static WORD keys[3];
2178 static int compose_char = 0;
2179 static WPARAM compose_key = 0;
2180
2181 r = GetKeyboardState(keystate);
2182 if (!r)
2183 memset(keystate, 0, sizeof(keystate));
2184 else {
2185 #if 0
2186 { /* Tell us all about key events */
2187 static BYTE oldstate[256];
2188 static int first = 1;
2189 static int scan;
2190 int ch;
2191 if (first)
2192 memcpy(oldstate, keystate, sizeof(oldstate));
2193 first = 0;
2194
2195 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2196 debug(("+"));
2197 } else if ((HIWORD(lParam) & KF_UP)
2198 && scan == (HIWORD(lParam) & 0xFF)) {
2199 debug((". U"));
2200 } else {
2201 debug((".\n"));
2202 if (wParam >= VK_F1 && wParam <= VK_F20)
2203 debug(("K_F%d", wParam + 1 - VK_F1));
2204 else
2205 switch (wParam) {
2206 case VK_SHIFT:
2207 debug(("SHIFT"));
2208 break;
2209 case VK_CONTROL:
2210 debug(("CTRL"));
2211 break;
2212 case VK_MENU:
2213 debug(("ALT"));
2214 break;
2215 default:
2216 debug(("VK_%02x", wParam));
2217 }
2218 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2219 debug(("*"));
2220 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2221
2222 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2223 if (ch >= ' ' && ch <= '~')
2224 debug((", '%c'", ch));
2225 else if (ch)
2226 debug((", $%02x", ch));
2227
2228 if (keys[0])
2229 debug((", KB0=%02x", keys[0]));
2230 if (keys[1])
2231 debug((", KB1=%02x", keys[1]));
2232 if (keys[2])
2233 debug((", KB2=%02x", keys[2]));
2234
2235 if ((keystate[VK_SHIFT] & 0x80) != 0)
2236 debug((", S"));
2237 if ((keystate[VK_CONTROL] & 0x80) != 0)
2238 debug((", C"));
2239 if ((HIWORD(lParam) & KF_EXTENDED))
2240 debug((", E"));
2241 if ((HIWORD(lParam) & KF_UP))
2242 debug((", U"));
2243 }
2244
2245 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2246 else if ((HIWORD(lParam) & KF_UP))
2247 oldstate[wParam & 0xFF] ^= 0x80;
2248 else
2249 oldstate[wParam & 0xFF] ^= 0x81;
2250
2251 for (ch = 0; ch < 256; ch++)
2252 if (oldstate[ch] != keystate[ch])
2253 debug((", M%02x=%02x", ch, keystate[ch]));
2254
2255 memcpy(oldstate, keystate, sizeof(oldstate));
2256 }
2257 #endif
2258
2259 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2260 keystate[VK_RMENU] = keystate[VK_MENU];
2261 }
2262
2263
2264 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2265 if ((cfg.funky_type == 3 ||
2266 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2267 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2268
2269 wParam = VK_EXECUTE;
2270
2271 /* UnToggle NUMLock */
2272 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2273 keystate[VK_NUMLOCK] ^= 1;
2274 }
2275
2276 /* And write back the 'adjusted' state */
2277 SetKeyboardState(keystate);
2278 }
2279
2280 /* Disable Auto repeat if required */
2281 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2282 return 0;
2283
2284 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2285 left_alt = 1;
2286
2287 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2288
2289 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2290 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2291 if (cfg.ctrlaltkeys)
2292 keystate[VK_MENU] = 0;
2293 else {
2294 keystate[VK_RMENU] = 0x80;
2295 left_alt = 0;
2296 }
2297 }
2298
2299 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2300 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2301 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2302
2303 /* Note if AltGr was pressed and if it was used as a compose key */
2304 if (!compose_state) {
2305 compose_key = 0x100;
2306 if (cfg.compose_key) {
2307 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2308 compose_key = wParam;
2309 }
2310 if (wParam == VK_APPS)
2311 compose_key = wParam;
2312 }
2313
2314 if (wParam == compose_key) {
2315 if (compose_state == 0
2316 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2317 1;
2318 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2319 compose_state = 2;
2320 else
2321 compose_state = 0;
2322 } else if (compose_state == 1 && wParam != VK_CONTROL)
2323 compose_state = 0;
2324
2325 /*
2326 * Record that we pressed key so the scroll window can be reset, but
2327 * be careful to avoid Shift-UP/Down
2328 */
2329 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2330 seen_key_event = 1;
2331 }
2332
2333 /* Make sure we're not pasting */
2334 if (key_down)
2335 term_nopaste();
2336
2337 if (compose_state > 1 && left_alt)
2338 compose_state = 0;
2339
2340 /* Sanitize the number pad if not using a PC NumPad */
2341 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2342 && cfg.funky_type != 2)
2343 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2344 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2345 int nParam = 0;
2346 switch (wParam) {
2347 case VK_INSERT:
2348 nParam = VK_NUMPAD0;
2349 break;
2350 case VK_END:
2351 nParam = VK_NUMPAD1;
2352 break;
2353 case VK_DOWN:
2354 nParam = VK_NUMPAD2;
2355 break;
2356 case VK_NEXT:
2357 nParam = VK_NUMPAD3;
2358 break;
2359 case VK_LEFT:
2360 nParam = VK_NUMPAD4;
2361 break;
2362 case VK_CLEAR:
2363 nParam = VK_NUMPAD5;
2364 break;
2365 case VK_RIGHT:
2366 nParam = VK_NUMPAD6;
2367 break;
2368 case VK_HOME:
2369 nParam = VK_NUMPAD7;
2370 break;
2371 case VK_UP:
2372 nParam = VK_NUMPAD8;
2373 break;
2374 case VK_PRIOR:
2375 nParam = VK_NUMPAD9;
2376 break;
2377 case VK_DELETE:
2378 nParam = VK_DECIMAL;
2379 break;
2380 }
2381 if (nParam) {
2382 if (keystate[VK_NUMLOCK] & 1)
2383 shift_state |= 1;
2384 wParam = nParam;
2385 }
2386 }
2387 }
2388
2389 /* If a key is pressed and AltGr is not active */
2390 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2391 /* Okay, prepare for most alts then ... */
2392 if (left_alt)
2393 *p++ = '\033';
2394
2395 /* Lets see if it's a pattern we know all about ... */
2396 if (wParam == VK_PRIOR && shift_state == 1) {
2397 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2398 return 0;
2399 }
2400 if (wParam == VK_NEXT && shift_state == 1) {
2401 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2402 return 0;
2403 }
2404 if (wParam == VK_INSERT && shift_state == 1) {
2405 term_mouse(MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
2406 term_mouse(MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
2407 return 0;
2408 }
2409 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2410 return -1;
2411 }
2412 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2413 alt_state = 0;
2414 PostMessage(hwnd, WM_CHAR, ' ', 0);
2415 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2416 return -1;
2417 }
2418 /* Control-Numlock for app-keypad mode switch */
2419 if (wParam == VK_PAUSE && shift_state == 2) {
2420 app_keypad_keys ^= 1;
2421 return 0;
2422 }
2423
2424 /* Nethack keypad */
2425 if (cfg.nethack_keypad && !left_alt) {
2426 switch (wParam) {
2427 case VK_NUMPAD1:
2428 *p++ = shift_state ? 'B' : 'b';
2429 return p - output;
2430 case VK_NUMPAD2:
2431 *p++ = shift_state ? 'J' : 'j';
2432 return p - output;
2433 case VK_NUMPAD3:
2434 *p++ = shift_state ? 'N' : 'n';
2435 return p - output;
2436 case VK_NUMPAD4:
2437 *p++ = shift_state ? 'H' : 'h';
2438 return p - output;
2439 case VK_NUMPAD5:
2440 *p++ = shift_state ? '.' : '.';
2441 return p - output;
2442 case VK_NUMPAD6:
2443 *p++ = shift_state ? 'L' : 'l';
2444 return p - output;
2445 case VK_NUMPAD7:
2446 *p++ = shift_state ? 'Y' : 'y';
2447 return p - output;
2448 case VK_NUMPAD8:
2449 *p++ = shift_state ? 'K' : 'k';
2450 return p - output;
2451 case VK_NUMPAD9:
2452 *p++ = shift_state ? 'U' : 'u';
2453 return p - output;
2454 }
2455 }
2456
2457 /* Application Keypad */
2458 if (!left_alt) {
2459 int xkey = 0;
2460
2461 if (cfg.funky_type == 3 ||
2462 (cfg.funky_type <= 1 &&
2463 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2464 case VK_EXECUTE:
2465 xkey = 'P';
2466 break;
2467 case VK_DIVIDE:
2468 xkey = 'Q';
2469 break;
2470 case VK_MULTIPLY:
2471 xkey = 'R';
2472 break;
2473 case VK_SUBTRACT:
2474 xkey = 'S';
2475 break;
2476 }
2477 if (app_keypad_keys && !cfg.no_applic_k)
2478 switch (wParam) {
2479 case VK_NUMPAD0:
2480 xkey = 'p';
2481 break;
2482 case VK_NUMPAD1:
2483 xkey = 'q';
2484 break;
2485 case VK_NUMPAD2:
2486 xkey = 'r';
2487 break;
2488 case VK_NUMPAD3:
2489 xkey = 's';
2490 break;
2491 case VK_NUMPAD4:
2492 xkey = 't';
2493 break;
2494 case VK_NUMPAD5:
2495 xkey = 'u';
2496 break;
2497 case VK_NUMPAD6:
2498 xkey = 'v';
2499 break;
2500 case VK_NUMPAD7:
2501 xkey = 'w';
2502 break;
2503 case VK_NUMPAD8:
2504 xkey = 'x';
2505 break;
2506 case VK_NUMPAD9:
2507 xkey = 'y';
2508 break;
2509
2510 case VK_DECIMAL:
2511 xkey = 'n';
2512 break;
2513 case VK_ADD:
2514 if (cfg.funky_type == 2) {
2515 if (shift_state)
2516 xkey = 'l';
2517 else
2518 xkey = 'k';
2519 } else if (shift_state)
2520 xkey = 'm';
2521 else
2522 xkey = 'l';
2523 break;
2524
2525 case VK_DIVIDE:
2526 if (cfg.funky_type == 2)
2527 xkey = 'o';
2528 break;
2529 case VK_MULTIPLY:
2530 if (cfg.funky_type == 2)
2531 xkey = 'j';
2532 break;
2533 case VK_SUBTRACT:
2534 if (cfg.funky_type == 2)
2535 xkey = 'm';
2536 break;
2537
2538 case VK_RETURN:
2539 if (HIWORD(lParam) & KF_EXTENDED)
2540 xkey = 'M';
2541 break;
2542 }
2543 if (xkey) {
2544 if (vt52_mode) {
2545 if (xkey >= 'P' && xkey <= 'S')
2546 p += sprintf((char *) p, "\x1B%c", xkey);
2547 else
2548 p += sprintf((char *) p, "\x1B?%c", xkey);
2549 } else
2550 p += sprintf((char *) p, "\x1BO%c", xkey);
2551 return p - output;
2552 }
2553 }
2554
2555 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2556 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2557 return p - output;
2558 }
2559 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2560 *p++ = 0x1B;
2561 *p++ = '[';
2562 *p++ = 'Z';
2563 return p - output;
2564 }
2565 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2566 *p++ = 0;
2567 return p - output;
2568 }
2569 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2570 *p++ = 160;
2571 return p - output;
2572 }
2573 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2574 *p++ = 3;
2575 return p - output;
2576 }
2577 if (wParam == VK_PAUSE) { /* Break/Pause */
2578 *p++ = 26;
2579 *p++ = 0;
2580 return -2;
2581 }
2582 /* Control-2 to Control-8 are special */
2583 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2584 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2585 return p - output;
2586 }
2587 if (shift_state == 2 && wParam == 0xBD) {
2588 *p++ = 0x1F;
2589 return p - output;
2590 }
2591 if (shift_state == 2 && wParam == 0xDF) {
2592 *p++ = 0x1C;
2593 return p - output;
2594 }
2595 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2596 *p++ = '\r';
2597 *p++ = '\n';
2598 return p - output;
2599 }
2600
2601 /*
2602 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2603 * for integer decimal nn.)
2604 *
2605 * We also deal with the weird ones here. Linux VCs replace F1
2606 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2607 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2608 * respectively.
2609 */
2610 code = 0;
2611 switch (wParam) {
2612 case VK_F1:
2613 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2614 break;
2615 case VK_F2:
2616 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2617 break;
2618 case VK_F3:
2619 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2620 break;
2621 case VK_F4:
2622 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2623 break;
2624 case VK_F5:
2625 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2626 break;
2627 case VK_F6:
2628 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2629 break;
2630 case VK_F7:
2631 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2632 break;
2633 case VK_F8:
2634 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2635 break;
2636 case VK_F9:
2637 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2638 break;
2639 case VK_F10:
2640 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2641 break;
2642 case VK_F11:
2643 code = 23;
2644 break;
2645 case VK_F12:
2646 code = 24;
2647 break;
2648 case VK_F13:
2649 code = 25;
2650 break;
2651 case VK_F14:
2652 code = 26;
2653 break;
2654 case VK_F15:
2655 code = 28;
2656 break;
2657 case VK_F16:
2658 code = 29;
2659 break;
2660 case VK_F17:
2661 code = 31;
2662 break;
2663 case VK_F18:
2664 code = 32;
2665 break;
2666 case VK_F19:
2667 code = 33;
2668 break;
2669 case VK_F20:
2670 code = 34;
2671 break;
2672 case VK_HOME:
2673 code = 1;
2674 break;
2675 case VK_INSERT:
2676 code = 2;
2677 break;
2678 case VK_DELETE:
2679 code = 3;
2680 break;
2681 case VK_END:
2682 code = 4;
2683 break;
2684 case VK_PRIOR:
2685 code = 5;
2686 break;
2687 case VK_NEXT:
2688 code = 6;
2689 break;
2690 }
2691 /* Reorder edit keys to physical order */
2692 if (cfg.funky_type == 3 && code <= 6)
2693 code = "\0\2\1\4\5\3\6"[code];
2694
2695 if (vt52_mode && code > 0 && code <= 6) {
2696 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2697 return p - output;
2698 }
2699
2700 if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2701 p += sprintf((char *) p, "\x1B[%c", code + 'M' - 11);
2702 return p - output;
2703 }
2704 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2705 int offt = 0;
2706 if (code > 15)
2707 offt++;
2708 if (code > 21)
2709 offt++;
2710 if (vt52_mode)
2711 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2712 else
2713 p +=
2714 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2715 return p - output;
2716 }
2717 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2718 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2719 return p - output;
2720 }
2721 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2722 if (vt52_mode)
2723 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2724 else
2725 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2726 return p - output;
2727 }
2728 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2729 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2730 return p - output;
2731 }
2732 if (code) {
2733 p += sprintf((char *) p, "\x1B[%d~", code);
2734 return p - output;
2735 }
2736
2737 /*
2738 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2739 * some reason seems to send VK_CLEAR to Windows...).
2740 */
2741 {
2742 char xkey = 0;
2743 switch (wParam) {
2744 case VK_UP:
2745 xkey = 'A';
2746 break;
2747 case VK_DOWN:
2748 xkey = 'B';
2749 break;
2750 case VK_RIGHT:
2751 xkey = 'C';
2752 break;
2753 case VK_LEFT:
2754 xkey = 'D';
2755 break;
2756 case VK_CLEAR:
2757 xkey = 'G';
2758 break;
2759 }
2760 if (xkey) {
2761 if (vt52_mode)
2762 p += sprintf((char *) p, "\x1B%c", xkey);
2763 else {
2764 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2765 /* VT100 & VT102 manuals both state the app cursor keys
2766 * only work if the app keypad is on.
2767 */
2768 if (!app_keypad_keys)
2769 app_flg = 0;
2770 /* Useful mapping of Ctrl-arrows */
2771 if (shift_state == 2)
2772 app_flg = !app_flg;
2773
2774 if (app_flg)
2775 p += sprintf((char *) p, "\x1BO%c", xkey);
2776 else
2777 p += sprintf((char *) p, "\x1B[%c", xkey);
2778 }
2779 return p - output;
2780 }
2781 }
2782
2783 /*
2784 * Finally, deal with Return ourselves. (Win95 seems to
2785 * foul it up when Alt is pressed, for some reason.)
2786 */
2787 if (wParam == VK_RETURN) { /* Return */
2788 *p++ = 0x0D;
2789 return p - output;
2790 }
2791 }
2792
2793 /* Okay we've done everything interesting; let windows deal with
2794 * the boring stuff */
2795 {
2796 BOOL capsOn = keystate[VK_CAPITAL] != 0;
2797
2798 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2799 if (cfg.xlat_capslockcyr)
2800 keystate[VK_CAPITAL] = 0;
2801
2802 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2803 if (r > 0) {
2804 p = output;
2805 for (i = 0; i < r; i++) {
2806 unsigned char ch = (unsigned char) keys[i];
2807
2808 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2809 compose_char = ch;
2810 compose_state++;
2811 continue;
2812 }
2813 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2814 int nc;
2815 compose_state = 0;
2816
2817 if ((nc = check_compose(compose_char, ch)) == -1) {
2818 MessageBeep(MB_ICONHAND);
2819 return 0;
2820 }
2821 *p++ = xlat_kbd2tty((unsigned char) nc);
2822 return p - output;
2823 }
2824
2825 compose_state = 0;
2826
2827 if (left_alt && key_down)
2828 *p++ = '\033';
2829 if (!key_down)
2830 *p++ = ch;
2831 else {
2832 if (capsOn)
2833 ch = xlat_latkbd2win(ch);
2834 *p++ = xlat_kbd2tty(ch);
2835 }
2836 }
2837
2838 /* This is so the ALT-Numpad and dead keys work correctly. */
2839 keys[0] = 0;
2840
2841 return p - output;
2842 }
2843 /* If we're definitly not building up an ALT-54321 then clear it */
2844 if (!left_alt)
2845 keys[0] = 0;
2846 }
2847
2848 /* ALT alone may or may not want to bring up the System menu */
2849 if (wParam == VK_MENU) {
2850 if (cfg.alt_only) {
2851 if (message == WM_SYSKEYDOWN)
2852 alt_state = 1;
2853 else if (message == WM_SYSKEYUP && alt_state)
2854 PostMessage(hwnd, WM_CHAR, ' ', 0);
2855 if (message == WM_SYSKEYUP)
2856 alt_state = 0;
2857 } else
2858 return 0;
2859 } else
2860 alt_state = 0;
2861
2862 return -1;
2863 }
2864
2865 void set_title(char *title)
2866 {
2867 sfree(window_name);
2868 window_name = smalloc(1 + strlen(title));
2869 strcpy(window_name, title);
2870 if (cfg.win_name_always || !IsIconic(hwnd))
2871 SetWindowText(hwnd, title);
2872 }
2873
2874 void set_icon(char *title)
2875 {
2876 sfree(icon_name);
2877 icon_name = smalloc(1 + strlen(title));
2878 strcpy(icon_name, title);
2879 if (!cfg.win_name_always && IsIconic(hwnd))
2880 SetWindowText(hwnd, title);
2881 }
2882
2883 void set_sbar(int total, int start, int page)
2884 {
2885 SCROLLINFO si;
2886
2887 if (!cfg.scrollbar)
2888 return;
2889
2890 si.cbSize = sizeof(si);
2891 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2892 si.nMin = 0;
2893 si.nMax = total - 1;
2894 si.nPage = page;
2895 si.nPos = start;
2896 if (hwnd)
2897 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2898 }
2899
2900 Context get_ctx(void)
2901 {
2902 HDC hdc;
2903 if (hwnd) {
2904 hdc = GetDC(hwnd);
2905 if (hdc && pal)
2906 SelectPalette(hdc, pal, FALSE);
2907 return hdc;
2908 } else
2909 return NULL;
2910 }
2911
2912 void free_ctx(Context ctx)
2913 {
2914 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
2915 ReleaseDC(hwnd, ctx);
2916 }
2917
2918 static void real_palette_set(int n, int r, int g, int b)
2919 {
2920 if (pal) {
2921 logpal->palPalEntry[n].peRed = r;
2922 logpal->palPalEntry[n].peGreen = g;
2923 logpal->palPalEntry[n].peBlue = b;
2924 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2925 colours[n] = PALETTERGB(r, g, b);
2926 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
2927 } else
2928 colours[n] = RGB(r, g, b);
2929 }
2930
2931 void palette_set(int n, int r, int g, int b)
2932 {
2933 static const int first[21] = {
2934 0, 2, 4, 6, 8, 10, 12, 14,
2935 1, 3, 5, 7, 9, 11, 13, 15,
2936 16, 17, 18, 20, 22
2937 };
2938 real_palette_set(first[n], r, g, b);
2939 if (first[n] >= 18)
2940 real_palette_set(first[n] + 1, r, g, b);
2941 if (pal) {
2942 HDC hdc = get_ctx();
2943 UnrealizeObject(pal);
2944 RealizePalette(hdc);
2945 free_ctx(hdc);
2946 }
2947 }
2948
2949 void palette_reset(void)
2950 {
2951 int i;
2952
2953 for (i = 0; i < NCOLOURS; i++) {
2954 if (pal) {
2955 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2956 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2957 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2958 logpal->palPalEntry[i].peFlags = 0;
2959 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2960 defpal[i].rgbtGreen,
2961 defpal[i].rgbtBlue);
2962 } else
2963 colours[i] = RGB(defpal[i].rgbtRed,
2964 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
2965 }
2966
2967 if (pal) {
2968 HDC hdc;
2969 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
2970 hdc = get_ctx();
2971 RealizePalette(hdc);
2972 free_ctx(hdc);
2973 }
2974 }
2975
2976 void write_clip(void *data, int len, int must_deselect)
2977 {
2978 HGLOBAL clipdata;
2979 void *lock;
2980
2981 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2982 if (!clipdata)
2983 return;
2984 lock = GlobalLock(clipdata);
2985 if (!lock)
2986 return;
2987 memcpy(lock, data, len);
2988 ((unsigned char *) lock)[len] = 0;
2989 GlobalUnlock(clipdata);
2990
2991 if (!must_deselect)
2992 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
2993
2994 if (OpenClipboard(hwnd)) {
2995 EmptyClipboard();
2996 SetClipboardData(CF_TEXT, clipdata);
2997 CloseClipboard();
2998 } else
2999 GlobalFree(clipdata);
3000
3001 if (!must_deselect)
3002 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3003 }
3004
3005 void get_clip(void **p, int *len)
3006 {
3007 static HGLOBAL clipdata = NULL;
3008
3009 if (!p) {
3010 if (clipdata)
3011 GlobalUnlock(clipdata);
3012 clipdata = NULL;
3013 return;
3014 } else {
3015 if (OpenClipboard(NULL)) {
3016 clipdata = GetClipboardData(CF_TEXT);
3017 CloseClipboard();
3018 if (clipdata) {
3019 *p = GlobalLock(clipdata);
3020 if (*p) {
3021 *len = strlen(*p);
3022 return;
3023 }
3024 }
3025 }
3026 }
3027
3028 *p = NULL;
3029 *len = 0;
3030 }
3031
3032 /*
3033 * Move `lines' lines from position `from' to position `to' in the
3034 * window.
3035 */
3036 void optimised_move(int to, int from, int lines)
3037 {
3038 RECT r;
3039 int min, max;
3040
3041 min = (to < from ? to : from);
3042 max = to + from - min;
3043
3044 r.left = 0;
3045 r.right = cols * font_width;
3046 r.top = min * font_height;
3047 r.bottom = (max + lines) * font_height;
3048 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3049 }
3050
3051 /*
3052 * Print a message box and perform a fatal exit.
3053 */
3054 void fatalbox(char *fmt, ...)
3055 {
3056 va_list ap;
3057 char stuff[200];
3058
3059 va_start(ap, fmt);
3060 vsprintf(stuff, fmt, ap);
3061 va_end(ap);
3062 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3063 exit(1);
3064 }
3065
3066 /*
3067 * Beep.
3068 */
3069 void beep(int mode)
3070 {
3071 if (mode == BELL_DEFAULT) {
3072 /*
3073 * For MessageBeep style bells, we want to be careful of
3074 * timing, because they don't have the nice property of
3075 * PlaySound bells that each one cancels the previous
3076 * active one. So we limit the rate to one per 50ms or so.
3077 */
3078 static long lastbeep = 0;
3079 long beepdiff;
3080
3081 beepdiff = GetTickCount() - lastbeep;
3082 if (beepdiff >= 0 && beepdiff < 50)
3083 return;
3084 MessageBeep(MB_OK);
3085 /*
3086 * The above MessageBeep call takes time, so we record the
3087 * time _after_ it finishes rather than before it starts.
3088 */
3089 lastbeep = GetTickCount();
3090 } else if (mode == BELL_WAVEFILE) {
3091 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3092 char buf[sizeof(cfg.bell_wavefile) + 80];
3093 sprintf(buf, "Unable to play sound file\n%s\n"
3094 "Using default sound instead", cfg.bell_wavefile);
3095 MessageBox(hwnd, buf, "PuTTY Sound Error",
3096 MB_OK | MB_ICONEXCLAMATION);
3097 cfg.beep = BELL_DEFAULT;
3098 }
3099 }
3100 }