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