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