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