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