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