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