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