Placate gcc's `-Wall' warnings.
[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 return 0; /* shouldn't happen */
1146 }
1147
1148 static void show_mouseptr(int show)
1149 {
1150 static int cursor_visible = 1;
1151 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1152 show = 1;
1153 if (cursor_visible && !show)
1154 ShowCursor(FALSE);
1155 else if (!cursor_visible && show)
1156 ShowCursor(TRUE);
1157 cursor_visible = show;
1158 }
1159
1160 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1161 WPARAM wParam, LPARAM lParam)
1162 {
1163 HDC hdc;
1164 static int ignore_size = FALSE;
1165 static int ignore_clip = FALSE;
1166 static int just_reconfigged = FALSE;
1167 static int resizing = FALSE;
1168 static int need_backend_resize = FALSE;
1169 static int defered_resize = FALSE;
1170
1171 switch (message) {
1172 case WM_TIMER:
1173 if (pending_netevent)
1174 enact_pending_netevent();
1175 if (inbuf_head)
1176 term_out();
1177 noise_regular();
1178 HideCaret(hwnd);
1179 term_update();
1180 ShowCaret(hwnd);
1181 if (cfg.ping_interval > 0) {
1182 time_t now;
1183 time(&now);
1184 if (now - last_movement > cfg.ping_interval) {
1185 back->special(TS_PING);
1186 last_movement = now;
1187 }
1188 }
1189 return 0;
1190 case WM_CREATE:
1191 break;
1192 case WM_CLOSE:
1193 show_mouseptr(1);
1194 if (!cfg.warn_on_close || session_closed ||
1195 MessageBox(hwnd,
1196 "Are you sure you want to close this session?",
1197 "PuTTY Exit Confirmation",
1198 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1199 DestroyWindow(hwnd);
1200 return 0;
1201 case WM_DESTROY:
1202 show_mouseptr(1);
1203 PostQuitMessage(0);
1204 return 0;
1205 case WM_SYSCOMMAND:
1206 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1207 case IDM_SHOWLOG:
1208 showeventlog(hwnd);
1209 break;
1210 case IDM_NEWSESS:
1211 case IDM_DUPSESS:
1212 case IDM_SAVEDSESS:
1213 {
1214 char b[2048];
1215 char c[30], *cl;
1216 int freecl = FALSE;
1217 STARTUPINFO si;
1218 PROCESS_INFORMATION pi;
1219 HANDLE filemap = NULL;
1220
1221 if (wParam == IDM_DUPSESS) {
1222 /*
1223 * Allocate a file-mapping memory chunk for the
1224 * config structure.
1225 */
1226 SECURITY_ATTRIBUTES sa;
1227 Config *p;
1228
1229 sa.nLength = sizeof(sa);
1230 sa.lpSecurityDescriptor = NULL;
1231 sa.bInheritHandle = TRUE;
1232 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1233 &sa,
1234 PAGE_READWRITE,
1235 0, sizeof(Config), NULL);
1236 if (filemap) {
1237 p = (Config *) MapViewOfFile(filemap,
1238 FILE_MAP_WRITE,
1239 0, 0, sizeof(Config));
1240 if (p) {
1241 *p = cfg; /* structure copy */
1242 UnmapViewOfFile(p);
1243 }
1244 }
1245 sprintf(c, "putty &%p", filemap);
1246 cl = c;
1247 } else if (wParam == IDM_SAVEDSESS) {
1248 char *session =
1249 sessions[(lParam - IDM_SAVED_MIN) / 16];
1250 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1251 if (!cl)
1252 cl = NULL; /* not a very important failure mode */
1253 else {
1254 sprintf(cl, "putty @%s", session);
1255 freecl = TRUE;
1256 }
1257 } else
1258 cl = NULL;
1259
1260 GetModuleFileName(NULL, b, sizeof(b) - 1);
1261 si.cb = sizeof(si);
1262 si.lpReserved = NULL;
1263 si.lpDesktop = NULL;
1264 si.lpTitle = NULL;
1265 si.dwFlags = 0;
1266 si.cbReserved2 = 0;
1267 si.lpReserved2 = NULL;
1268 CreateProcess(b, cl, NULL, NULL, TRUE,
1269 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1270
1271 if (filemap)
1272 CloseHandle(filemap);
1273 if (freecl)
1274 sfree(cl);
1275 }
1276 break;
1277 case IDM_RECONF:
1278 {
1279 int prev_alwaysontop = cfg.alwaysontop;
1280 int prev_sunken_edge = cfg.sunken_edge;
1281 char oldlogfile[FILENAME_MAX];
1282 int oldlogtype;
1283 int need_setwpos = FALSE;
1284 int old_fwidth, old_fheight;
1285
1286 strcpy(oldlogfile, cfg.logfilename);
1287 oldlogtype = cfg.logtype;
1288 old_fwidth = font_width;
1289 old_fheight = font_height;
1290 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1291
1292 if (!do_reconfig(hwnd))
1293 break;
1294
1295 if (strcmp(oldlogfile, cfg.logfilename) ||
1296 oldlogtype != cfg.logtype) {
1297 logfclose(); /* reset logging */
1298 logfopen();
1299 }
1300
1301 just_reconfigged = TRUE;
1302 deinit_fonts();
1303 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1304 und_mode = UND_FONT;
1305 init_fonts(0);
1306 sfree(logpal);
1307 /*
1308 * Flush the line discipline's edit buffer in the
1309 * case where local editing has just been disabled.
1310 */
1311 ldisc_send(NULL, 0);
1312 if (pal)
1313 DeleteObject(pal);
1314 logpal = NULL;
1315 pal = NULL;
1316 cfgtopalette();
1317 init_palette();
1318
1319 /* Enable or disable the scroll bar, etc */
1320 {
1321 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1322 LONG nexflag, exflag =
1323 GetWindowLong(hwnd, GWL_EXSTYLE);
1324
1325 nexflag = exflag;
1326 if (cfg.alwaysontop != prev_alwaysontop) {
1327 if (cfg.alwaysontop) {
1328 nexflag |= WS_EX_TOPMOST;
1329 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1330 SWP_NOMOVE | SWP_NOSIZE);
1331 } else {
1332 nexflag &= ~(WS_EX_TOPMOST);
1333 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1334 SWP_NOMOVE | SWP_NOSIZE);
1335 }
1336 }
1337 if (cfg.sunken_edge)
1338 nexflag |= WS_EX_CLIENTEDGE;
1339 else
1340 nexflag &= ~(WS_EX_CLIENTEDGE);
1341
1342 nflg = flag;
1343 if (cfg.scrollbar)
1344 nflg |= WS_VSCROLL;
1345 else
1346 nflg &= ~WS_VSCROLL;
1347 if (cfg.locksize)
1348 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1349 else
1350 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1351
1352 if (nflg != flag || nexflag != exflag) {
1353 RECT cr, wr;
1354
1355 if (nflg != flag)
1356 SetWindowLong(hwnd, GWL_STYLE, nflg);
1357 if (nexflag != exflag)
1358 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1359
1360 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1361
1362 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1363 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1364 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1365 | SWP_FRAMECHANGED);
1366
1367 GetWindowRect(hwnd, &wr);
1368 GetClientRect(hwnd, &cr);
1369 extra_width =
1370 wr.right - wr.left - cr.right + cr.left;
1371 extra_height =
1372 wr.bottom - wr.top - cr.bottom + cr.top;
1373 need_setwpos = TRUE;
1374 }
1375 }
1376
1377 if (cfg.height != rows ||
1378 cfg.width != cols ||
1379 old_fwidth != font_width ||
1380 old_fheight != font_height ||
1381 cfg.savelines != savelines ||
1382 cfg.sunken_edge != prev_sunken_edge)
1383 need_setwpos = TRUE;
1384
1385 if (IsZoomed(hwnd)) {
1386 int w, h;
1387 RECT cr;
1388 if (need_setwpos)
1389 defered_resize = TRUE;
1390
1391 GetClientRect(hwnd, &cr);
1392 w = cr.right - cr.left;
1393 h = cr.bottom - cr.top;
1394 w = w / font_width;
1395 if (w < 1)
1396 w = 1;
1397 h = h / font_height;
1398 if (h < 1)
1399 h = 1;
1400
1401 term_size(h, w, cfg.savelines);
1402 InvalidateRect(hwnd, NULL, TRUE);
1403 back->size();
1404 } else {
1405 term_size(cfg.height, cfg.width, cfg.savelines);
1406 InvalidateRect(hwnd, NULL, TRUE);
1407 if (need_setwpos) {
1408 SetWindowPos(hwnd, NULL, 0, 0,
1409 extra_width + font_width * cfg.width,
1410 extra_height +
1411 font_height * cfg.height,
1412 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1413 SWP_NOMOVE | SWP_NOZORDER);
1414 }
1415 }
1416 /* Oops */
1417 if (cfg.locksize && IsZoomed(hwnd))
1418 force_normal(hwnd);
1419 set_title(cfg.wintitle);
1420 if (IsIconic(hwnd)) {
1421 SetWindowText(hwnd,
1422 cfg.win_name_always ? window_name :
1423 icon_name);
1424 }
1425 }
1426 break;
1427 case IDM_COPYALL:
1428 term_copyall();
1429 break;
1430 case IDM_CLRSB:
1431 term_clrsb();
1432 break;
1433 case IDM_RESET:
1434 term_pwron();
1435 break;
1436 case IDM_TEL_AYT:
1437 back->special(TS_AYT);
1438 break;
1439 case IDM_TEL_BRK:
1440 back->special(TS_BRK);
1441 break;
1442 case IDM_TEL_SYNCH:
1443 back->special(TS_SYNCH);
1444 break;
1445 case IDM_TEL_EC:
1446 back->special(TS_EC);
1447 break;
1448 case IDM_TEL_EL:
1449 back->special(TS_EL);
1450 break;
1451 case IDM_TEL_GA:
1452 back->special(TS_GA);
1453 break;
1454 case IDM_TEL_NOP:
1455 back->special(TS_NOP);
1456 break;
1457 case IDM_TEL_ABORT:
1458 back->special(TS_ABORT);
1459 break;
1460 case IDM_TEL_AO:
1461 back->special(TS_AO);
1462 break;
1463 case IDM_TEL_IP:
1464 back->special(TS_IP);
1465 break;
1466 case IDM_TEL_SUSP:
1467 back->special(TS_SUSP);
1468 break;
1469 case IDM_TEL_EOR:
1470 back->special(TS_EOR);
1471 break;
1472 case IDM_TEL_EOF:
1473 back->special(TS_EOF);
1474 break;
1475 case IDM_ABOUT:
1476 showabout(hwnd);
1477 break;
1478 default:
1479 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1480 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1481 }
1482 }
1483 break;
1484
1485 #define X_POS(l) ((int)(short)LOWORD(l))
1486 #define Y_POS(l) ((int)(short)HIWORD(l))
1487
1488 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1489 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1490 #define WHEEL_DELTA 120
1491 case WM_MOUSEWHEEL:
1492 {
1493 wheel_accumulator += (short) HIWORD(wParam);
1494 wParam = LOWORD(wParam);
1495
1496 /* process events when the threshold is reached */
1497 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1498 int b;
1499
1500 /* reduce amount for next time */
1501 if (wheel_accumulator > 0) {
1502 b = MBT_WHEEL_UP;
1503 wheel_accumulator -= WHEEL_DELTA;
1504 } else if (wheel_accumulator < 0) {
1505 b = MBT_WHEEL_DOWN;
1506 wheel_accumulator += WHEEL_DELTA;
1507 } else
1508 break;
1509
1510 if (send_raw_mouse) {
1511 /* send a mouse-down followed by a mouse up */
1512 term_mouse(b,
1513 MA_CLICK,
1514 TO_CHR_X(X_POS(lParam)),
1515 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1516 wParam & MK_CONTROL);
1517 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1518 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1519 wParam & MK_CONTROL);
1520 } else {
1521 /* trigger a scroll */
1522 term_scroll(0,
1523 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1524 }
1525 }
1526 return 0;
1527 }
1528 case WM_LBUTTONDOWN:
1529 case WM_MBUTTONDOWN:
1530 case WM_RBUTTONDOWN:
1531 case WM_LBUTTONUP:
1532 case WM_MBUTTONUP:
1533 case WM_RBUTTONUP:
1534 {
1535 int button, press;
1536 switch (message) {
1537 case WM_LBUTTONDOWN:
1538 button = MBT_LEFT;
1539 press = 1;
1540 break;
1541 case WM_MBUTTONDOWN:
1542 button = MBT_MIDDLE;
1543 press = 1;
1544 break;
1545 case WM_RBUTTONDOWN:
1546 button = MBT_RIGHT;
1547 press = 1;
1548 break;
1549 case WM_LBUTTONUP:
1550 button = MBT_LEFT;
1551 press = 0;
1552 break;
1553 case WM_MBUTTONUP:
1554 button = MBT_MIDDLE;
1555 press = 0;
1556 break;
1557 case WM_RBUTTONUP:
1558 button = MBT_RIGHT;
1559 press = 0;
1560 break;
1561 default:
1562 button = press = 0; /* shouldn't happen */
1563 }
1564 show_mouseptr(1);
1565 if (press) {
1566 click(button,
1567 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1568 wParam & MK_SHIFT, wParam & MK_CONTROL);
1569 SetCapture(hwnd);
1570 } else {
1571 term_mouse(button, MA_RELEASE,
1572 TO_CHR_X(X_POS(lParam)),
1573 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1574 wParam & MK_CONTROL);
1575 ReleaseCapture();
1576 }
1577 }
1578 return 0;
1579 case WM_MOUSEMOVE:
1580 show_mouseptr(1);
1581 /*
1582 * Add the mouse position and message time to the random
1583 * number noise.
1584 */
1585 noise_ultralight(lParam);
1586
1587 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1588 Mouse_Button b;
1589 if (wParam & MK_LBUTTON)
1590 b = MBT_SELECT;
1591 else if (wParam & MK_MBUTTON)
1592 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1593 else
1594 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1595 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1596 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1597 wParam & MK_CONTROL);
1598 }
1599 return 0;
1600 case WM_NCMOUSEMOVE:
1601 show_mouseptr(1);
1602 noise_ultralight(lParam);
1603 return 0;
1604 case WM_IGNORE_CLIP:
1605 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1606 break;
1607 case WM_DESTROYCLIPBOARD:
1608 if (!ignore_clip)
1609 term_deselect();
1610 ignore_clip = FALSE;
1611 return 0;
1612 case WM_PAINT:
1613 {
1614 PAINTSTRUCT p;
1615 HideCaret(hwnd);
1616 hdc = BeginPaint(hwnd, &p);
1617 if (pal) {
1618 SelectPalette(hdc, pal, TRUE);
1619 RealizePalette(hdc);
1620 }
1621 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1622 p.rcPaint.right, p.rcPaint.bottom);
1623 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1624 SelectObject(hdc, GetStockObject(WHITE_PEN));
1625 EndPaint(hwnd, &p);
1626 ShowCaret(hwnd);
1627 }
1628 return 0;
1629 case WM_NETEVENT:
1630 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1631 * but the only one that's likely to try to overload us is FD_READ.
1632 * This means buffering just one is fine.
1633 */
1634 if (pending_netevent)
1635 enact_pending_netevent();
1636
1637 pending_netevent = TRUE;
1638 pend_netevent_wParam = wParam;
1639 pend_netevent_lParam = lParam;
1640 time(&last_movement);
1641 return 0;
1642 case WM_SETFOCUS:
1643 has_focus = TRUE;
1644 CreateCaret(hwnd, caretbm, font_width, font_height);
1645 ShowCaret(hwnd);
1646 compose_state = 0;
1647 term_out();
1648 term_update();
1649 break;
1650 case WM_KILLFOCUS:
1651 show_mouseptr(1);
1652 has_focus = FALSE;
1653 DestroyCaret();
1654 term_out();
1655 term_update();
1656 break;
1657 case WM_IGNORE_SIZE:
1658 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1659 break;
1660 case WM_ENTERSIZEMOVE:
1661 EnableSizeTip(1);
1662 resizing = TRUE;
1663 need_backend_resize = FALSE;
1664 break;
1665 case WM_EXITSIZEMOVE:
1666 EnableSizeTip(0);
1667 resizing = FALSE;
1668 if (need_backend_resize)
1669 back->size();
1670 break;
1671 case WM_SIZING:
1672 {
1673 int width, height, w, h, ew, eh;
1674 LPRECT r = (LPRECT) lParam;
1675
1676 width = r->right - r->left - extra_width;
1677 height = r->bottom - r->top - extra_height;
1678 w = (width + font_width / 2) / font_width;
1679 if (w < 1)
1680 w = 1;
1681 h = (height + font_height / 2) / font_height;
1682 if (h < 1)
1683 h = 1;
1684 UpdateSizeTip(hwnd, w, h);
1685 ew = width - w * font_width;
1686 eh = height - h * font_height;
1687 if (ew != 0) {
1688 if (wParam == WMSZ_LEFT ||
1689 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1690 r->left += ew;
1691 else
1692 r->right -= ew;
1693 }
1694 if (eh != 0) {
1695 if (wParam == WMSZ_TOP ||
1696 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1697 r->top += eh;
1698 else
1699 r->bottom -= eh;
1700 }
1701 if (ew || eh)
1702 return 1;
1703 else
1704 return 0;
1705 }
1706 /* break; (never reached) */
1707 case WM_SIZE:
1708 if (wParam == SIZE_MINIMIZED) {
1709 SetWindowText(hwnd,
1710 cfg.win_name_always ? window_name : icon_name);
1711 break;
1712 }
1713 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1714 SetWindowText(hwnd, window_name);
1715 if (!ignore_size) {
1716 int width, height, w, h;
1717 #if 0 /* we have fixed this using WM_SIZING now */
1718 int ew, eh;
1719 #endif
1720
1721 width = LOWORD(lParam);
1722 height = HIWORD(lParam);
1723 w = width / font_width;
1724 if (w < 1)
1725 w = 1;
1726 h = height / font_height;
1727 if (h < 1)
1728 h = 1;
1729 #if 0 /* we have fixed this using WM_SIZING now */
1730 ew = width - w * font_width;
1731 eh = height - h * font_height;
1732 if (ew != 0 || eh != 0) {
1733 RECT r;
1734 GetWindowRect(hwnd, &r);
1735 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1736 SetWindowPos(hwnd, NULL, 0, 0,
1737 r.right - r.left - ew, r.bottom - r.top - eh,
1738 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1739 }
1740 #endif
1741 if (w != cols || h != rows || just_reconfigged) {
1742 term_invalidate();
1743 term_size(h, w, cfg.savelines);
1744 /*
1745 * Don't call back->size in mid-resize. (To prevent
1746 * massive numbers of resize events getting sent
1747 * down the connection during an NT opaque drag.)
1748 */
1749 if (!resizing)
1750 back->size();
1751 else {
1752 need_backend_resize = TRUE;
1753 cfg.height = h;
1754 cfg.width = w;
1755 }
1756 just_reconfigged = FALSE;
1757 }
1758 }
1759 if (wParam == SIZE_RESTORED && defered_resize) {
1760 defered_resize = FALSE;
1761 SetWindowPos(hwnd, NULL, 0, 0,
1762 extra_width + font_width * cfg.width,
1763 extra_height + font_height * cfg.height,
1764 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1765 SWP_NOMOVE | SWP_NOZORDER);
1766 }
1767 ignore_size = FALSE;
1768 return 0;
1769 case WM_VSCROLL:
1770 switch (LOWORD(wParam)) {
1771 case SB_BOTTOM:
1772 term_scroll(-1, 0);
1773 break;
1774 case SB_TOP:
1775 term_scroll(+1, 0);
1776 break;
1777 case SB_LINEDOWN:
1778 term_scroll(0, +1);
1779 break;
1780 case SB_LINEUP:
1781 term_scroll(0, -1);
1782 break;
1783 case SB_PAGEDOWN:
1784 term_scroll(0, +rows / 2);
1785 break;
1786 case SB_PAGEUP:
1787 term_scroll(0, -rows / 2);
1788 break;
1789 case SB_THUMBPOSITION:
1790 case SB_THUMBTRACK:
1791 term_scroll(1, HIWORD(wParam));
1792 break;
1793 }
1794 break;
1795 case WM_PALETTECHANGED:
1796 if ((HWND) wParam != hwnd && pal != NULL) {
1797 HDC hdc = get_ctx();
1798 if (hdc) {
1799 if (RealizePalette(hdc) > 0)
1800 UpdateColors(hdc);
1801 free_ctx(hdc);
1802 }
1803 }
1804 break;
1805 case WM_QUERYNEWPALETTE:
1806 if (pal != NULL) {
1807 HDC hdc = get_ctx();
1808 if (hdc) {
1809 if (RealizePalette(hdc) > 0)
1810 UpdateColors(hdc);
1811 free_ctx(hdc);
1812 return TRUE;
1813 }
1814 }
1815 return FALSE;
1816 case WM_KEYDOWN:
1817 case WM_SYSKEYDOWN:
1818 case WM_KEYUP:
1819 case WM_SYSKEYUP:
1820 /*
1821 * Add the scan code and keypress timing to the random
1822 * number noise.
1823 */
1824 noise_ultralight(lParam);
1825
1826 /*
1827 * We don't do TranslateMessage since it disassociates the
1828 * resulting CHAR message from the KEYDOWN that sparked it,
1829 * which we occasionally don't want. Instead, we process
1830 * KEYDOWN, and call the Win32 translator functions so that
1831 * we get the translations under _our_ control.
1832 */
1833 {
1834 unsigned char buf[20];
1835 int len;
1836
1837 if (wParam == VK_PROCESSKEY) {
1838 MSG m;
1839 m.hwnd = hwnd;
1840 m.message = WM_KEYDOWN;
1841 m.wParam = wParam;
1842 m.lParam = lParam & 0xdfff;
1843 TranslateMessage(&m);
1844 } else {
1845 len = TranslateKey(message, wParam, lParam, buf);
1846 if (len == -1)
1847 return DefWindowProc(hwnd, message, wParam, lParam);
1848 ldisc_send(buf, len);
1849
1850 if (len > 0)
1851 show_mouseptr(0);
1852 }
1853 }
1854 return 0;
1855 case WM_INPUTLANGCHANGE:
1856 {
1857 /* wParam == Font number */
1858 /* lParam == Locale */
1859 char lbuf[20];
1860 HKL NewInputLocale = (HKL) lParam;
1861
1862 // lParam == GetKeyboardLayout(0);
1863
1864 GetLocaleInfo(LOWORD(NewInputLocale),
1865 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1866
1867 kbd_codepage = atoi(lbuf);
1868 }
1869 break;
1870 case WM_IME_CHAR:
1871 if (wParam & 0xFF00) {
1872 unsigned char buf[2];
1873
1874 buf[1] = wParam;
1875 buf[0] = wParam >> 8;
1876 lpage_send(kbd_codepage, buf, 2);
1877 } else {
1878 char c = (unsigned char) wParam;
1879 lpage_send(kbd_codepage, &c, 1);
1880 }
1881 return (0);
1882 case WM_CHAR:
1883 case WM_SYSCHAR:
1884 /*
1885 * Nevertheless, we are prepared to deal with WM_CHAR
1886 * messages, should they crop up. So if someone wants to
1887 * post the things to us as part of a macro manoeuvre,
1888 * we're ready to cope.
1889 */
1890 {
1891 char c = (unsigned char)wParam;
1892 lpage_send(CP_ACP, &c, 1);
1893 }
1894 return 0;
1895 case WM_SETCURSOR:
1896 if (send_raw_mouse) {
1897 SetCursor(LoadCursor(NULL, IDC_ARROW));
1898 return TRUE;
1899 }
1900 }
1901
1902 return DefWindowProc(hwnd, message, wParam, lParam);
1903 }
1904
1905 /*
1906 * Move the system caret. (We maintain one, even though it's
1907 * invisible, for the benefit of blind people: apparently some
1908 * helper software tracks the system caret, so we should arrange to
1909 * have one.)
1910 */
1911 void sys_cursor(int x, int y)
1912 {
1913 if (has_focus)
1914 SetCaretPos(x * font_width, y * font_height);
1915 }
1916
1917 /*
1918 * Draw a line of text in the window, at given character
1919 * coordinates, in given attributes.
1920 *
1921 * We are allowed to fiddle with the contents of `text'.
1922 */
1923 void do_text(Context ctx, int x, int y, char *text, int len,
1924 unsigned long attr, int lattr)
1925 {
1926 COLORREF fg, bg, t;
1927 int nfg, nbg, nfont;
1928 HDC hdc = ctx;
1929 RECT line_box;
1930 int force_manual_underline = 0;
1931 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1932 int char_width = fnt_width;
1933 int text_adjust = 0;
1934 static int *IpDx = 0, IpDxLEN = 0;
1935
1936 if (attr & ATTR_WIDE)
1937 char_width *= 2;
1938
1939 if (len > IpDxLEN || IpDx[0] != char_width) {
1940 int i;
1941 if (len > IpDxLEN) {
1942 sfree(IpDx);
1943 IpDx = smalloc((len + 16) * sizeof(int));
1944 IpDxLEN = (len + 16);
1945 }
1946 for (i = 0; i < IpDxLEN; i++)
1947 IpDx[i] = char_width;
1948 }
1949
1950 x *= fnt_width;
1951 y *= font_height;
1952
1953 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
1954 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
1955 attr ^= ATTR_CUR_XOR;
1956 }
1957
1958 nfont = 0;
1959 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
1960 /* Assume a poorman font is borken in other ways too. */
1961 lattr = LATTR_WIDE;
1962 } else
1963 switch (lattr) {
1964 case LATTR_NORM:
1965 break;
1966 case LATTR_WIDE:
1967 nfont |= FONT_WIDE;
1968 break;
1969 default:
1970 nfont |= FONT_WIDE + FONT_HIGH;
1971 break;
1972 }
1973
1974 /* Special hack for the VT100 linedraw glyphs. */
1975 if ((attr & CSET_MASK) == 0x2300) {
1976 if (!dbcs_screenfont &&
1977 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
1978 switch ((unsigned char) (text[0])) {
1979 case 0xBA:
1980 text_adjust = -2 * font_height / 5;
1981 break;
1982 case 0xBB:
1983 text_adjust = -1 * font_height / 5;
1984 break;
1985 case 0xBC:
1986 text_adjust = font_height / 5;
1987 break;
1988 case 0xBD:
1989 text_adjust = 2 * font_height / 5;
1990 break;
1991 }
1992 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
1993 text_adjust *= 2;
1994 attr &= ~CSET_MASK;
1995 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
1996 attr |= (unitab_xterm['q'] & CSET_MASK);
1997 if (attr & ATTR_UNDER) {
1998 attr &= ~ATTR_UNDER;
1999 force_manual_underline = 1;
2000 }
2001 }
2002 }
2003
2004 /* Anything left as an original character set is unprintable. */
2005 if (DIRECT_CHAR(attr)) {
2006 attr &= ~CSET_MASK;
2007 attr |= 0xFF00;
2008 memset(text, 0xFF, len);
2009 }
2010
2011 /* OEM CP */
2012 if ((attr & CSET_MASK) == ATTR_OEMCP)
2013 nfont |= FONT_OEM;
2014
2015 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2016 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2017 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2018 nfont |= FONT_BOLD;
2019 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2020 nfont |= FONT_UNDERLINE;
2021 another_font(nfont);
2022 if (!fonts[nfont]) {
2023 if (nfont & FONT_UNDERLINE)
2024 force_manual_underline = 1;
2025 /* Don't do the same for manual bold, it could be bad news. */
2026
2027 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2028 }
2029 another_font(nfont);
2030 if (!fonts[nfont])
2031 nfont = FONT_NORMAL;
2032 if (attr & ATTR_REVERSE) {
2033 t = nfg;
2034 nfg = nbg;
2035 nbg = t;
2036 }
2037 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2038 nfg++;
2039 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2040 nbg++;
2041 fg = colours[nfg];
2042 bg = colours[nbg];
2043 SelectObject(hdc, fonts[nfont]);
2044 SetTextColor(hdc, fg);
2045 SetBkColor(hdc, bg);
2046 SetBkMode(hdc, OPAQUE);
2047 line_box.left = x;
2048 line_box.top = y;
2049 line_box.right = x + char_width * len;
2050 line_box.bottom = y + font_height;
2051
2052 /* We're using a private area for direct to font. (512 chars.) */
2053 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2054 /* Ho Hum, dbcs fonts are a PITA! */
2055 /* To display on W9x I have to convert to UCS */
2056 static wchar_t *uni_buf = 0;
2057 static int uni_len = 0;
2058 int nlen;
2059 if (len > uni_len) {
2060 sfree(uni_buf);
2061 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2062 }
2063 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2064 text, len, uni_buf, uni_len);
2065
2066 if (nlen <= 0)
2067 return; /* Eeek! */
2068
2069 ExtTextOutW(hdc, x,
2070 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2071 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2072 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2073 SetBkMode(hdc, TRANSPARENT);
2074 ExtTextOutW(hdc, x - 1,
2075 y - font_height * (lattr ==
2076 LATTR_BOT) + text_adjust,
2077 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2078 }
2079 } else if (DIRECT_FONT(attr)) {
2080 ExtTextOut(hdc, x,
2081 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2082 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2083 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2084 SetBkMode(hdc, TRANSPARENT);
2085
2086 /* GRR: This draws the character outside it's box and can leave
2087 * 'droppings' even with the clip box! I suppose I could loop it
2088 * one character at a time ... yuk.
2089 *
2090 * Or ... I could do a test print with "W", and use +1 or -1 for this
2091 * shift depending on if the leftmost column is blank...
2092 */
2093 ExtTextOut(hdc, x - 1,
2094 y - font_height * (lattr ==
2095 LATTR_BOT) + text_adjust,
2096 ETO_CLIPPED, &line_box, text, len, IpDx);
2097 }
2098 } else {
2099 /* And 'normal' unicode characters */
2100 static WCHAR *wbuf = NULL;
2101 static int wlen = 0;
2102 int i;
2103 if (wlen < len) {
2104 sfree(wbuf);
2105 wlen = len;
2106 wbuf = smalloc(wlen * sizeof(WCHAR));
2107 }
2108 for (i = 0; i < len; i++)
2109 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2110
2111 ExtTextOutW(hdc, x,
2112 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2113 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2114
2115 /* And the shadow bold hack. */
2116 if (bold_mode == BOLD_SHADOW) {
2117 SetBkMode(hdc, TRANSPARENT);
2118 ExtTextOutW(hdc, x - 1,
2119 y - font_height * (lattr ==
2120 LATTR_BOT) + text_adjust,
2121 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2122 }
2123 }
2124 if (lattr != LATTR_TOP && (force_manual_underline ||
2125 (und_mode == UND_LINE
2126 && (attr & ATTR_UNDER)))) {
2127 HPEN oldpen;
2128 int dec = descent;
2129 if (lattr == LATTR_BOT)
2130 dec = dec * 2 - font_height;
2131
2132 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2133 MoveToEx(hdc, x, y + dec, NULL);
2134 LineTo(hdc, x + len * char_width, y + dec);
2135 oldpen = SelectObject(hdc, oldpen);
2136 DeleteObject(oldpen);
2137 }
2138 }
2139
2140 void do_cursor(Context ctx, int x, int y, char *text, int len,
2141 unsigned long attr, int lattr)
2142 {
2143
2144 int fnt_width;
2145 int char_width;
2146 HDC hdc = ctx;
2147 int ctype = cfg.cursor_type;
2148
2149 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2150 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2151 do_text(ctx, x, y, text, len, attr, lattr);
2152 return;
2153 }
2154 ctype = 2;
2155 attr |= TATTR_RIGHTCURS;
2156 }
2157
2158 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2159 if (attr & ATTR_WIDE)
2160 char_width *= 2;
2161 x *= fnt_width;
2162 y *= font_height;
2163
2164 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2165 POINT pts[5];
2166 HPEN oldpen;
2167 pts[0].x = pts[1].x = pts[4].x = x;
2168 pts[2].x = pts[3].x = x + char_width - 1;
2169 pts[0].y = pts[3].y = pts[4].y = y;
2170 pts[1].y = pts[2].y = y + font_height - 1;
2171 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2172 Polyline(hdc, pts, 5);
2173 oldpen = SelectObject(hdc, oldpen);
2174 DeleteObject(oldpen);
2175 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2176 int startx, starty, dx, dy, length, i;
2177 if (ctype == 1) {
2178 startx = x;
2179 starty = y + descent;
2180 dx = 1;
2181 dy = 0;
2182 length = char_width;
2183 } else {
2184 int xadjust = 0;
2185 if (attr & TATTR_RIGHTCURS)
2186 xadjust = char_width - 1;
2187 startx = x + xadjust;
2188 starty = y;
2189 dx = 0;
2190 dy = 1;
2191 length = font_height;
2192 }
2193 if (attr & TATTR_ACTCURS) {
2194 HPEN oldpen;
2195 oldpen =
2196 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2197 MoveToEx(hdc, startx, starty, NULL);
2198 LineTo(hdc, startx + dx * length, starty + dy * length);
2199 oldpen = SelectObject(hdc, oldpen);
2200 DeleteObject(oldpen);
2201 } else {
2202 for (i = 0; i < length; i++) {
2203 if (i % 2 == 0) {
2204 SetPixel(hdc, startx, starty, colours[23]);
2205 }
2206 startx += dx;
2207 starty += dy;
2208 }
2209 }
2210 }
2211 }
2212
2213 /*
2214 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2215 * codes. Returns number of bytes used or zero to drop the message
2216 * or -1 to forward the message to windows.
2217 */
2218 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2219 unsigned char *output)
2220 {
2221 BYTE keystate[256];
2222 int scan, left_alt = 0, key_down, shift_state;
2223 int r, i, code;
2224 unsigned char *p = output;
2225 static int alt_state = 0;
2226 static int alt_sum = 0;
2227
2228 HKL kbd_layout = GetKeyboardLayout(0);
2229
2230 static WORD keys[3];
2231 static int compose_char = 0;
2232 static WPARAM compose_key = 0;
2233
2234 r = GetKeyboardState(keystate);
2235 if (!r)
2236 memset(keystate, 0, sizeof(keystate));
2237 else {
2238 #if 0
2239 #define SHOW_TOASCII_RESULT
2240 { /* Tell us all about key events */
2241 static BYTE oldstate[256];
2242 static int first = 1;
2243 static int scan;
2244 int ch;
2245 if (first)
2246 memcpy(oldstate, keystate, sizeof(oldstate));
2247 first = 0;
2248
2249 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2250 debug(("+"));
2251 } else if ((HIWORD(lParam) & KF_UP)
2252 && scan == (HIWORD(lParam) & 0xFF)) {
2253 debug((". U"));
2254 } else {
2255 debug((".\n"));
2256 if (wParam >= VK_F1 && wParam <= VK_F20)
2257 debug(("K_F%d", wParam + 1 - VK_F1));
2258 else
2259 switch (wParam) {
2260 case VK_SHIFT:
2261 debug(("SHIFT"));
2262 break;
2263 case VK_CONTROL:
2264 debug(("CTRL"));
2265 break;
2266 case VK_MENU:
2267 debug(("ALT"));
2268 break;
2269 default:
2270 debug(("VK_%02x", wParam));
2271 }
2272 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2273 debug(("*"));
2274 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2275
2276 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2277 if (ch >= ' ' && ch <= '~')
2278 debug((", '%c'", ch));
2279 else if (ch)
2280 debug((", $%02x", ch));
2281
2282 if (keys[0])
2283 debug((", KB0=%02x", keys[0]));
2284 if (keys[1])
2285 debug((", KB1=%02x", keys[1]));
2286 if (keys[2])
2287 debug((", KB2=%02x", keys[2]));
2288
2289 if ((keystate[VK_SHIFT] & 0x80) != 0)
2290 debug((", S"));
2291 if ((keystate[VK_CONTROL] & 0x80) != 0)
2292 debug((", C"));
2293 if ((HIWORD(lParam) & KF_EXTENDED))
2294 debug((", E"));
2295 if ((HIWORD(lParam) & KF_UP))
2296 debug((", U"));
2297 }
2298
2299 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2300 else if ((HIWORD(lParam) & KF_UP))
2301 oldstate[wParam & 0xFF] ^= 0x80;
2302 else
2303 oldstate[wParam & 0xFF] ^= 0x81;
2304
2305 for (ch = 0; ch < 256; ch++)
2306 if (oldstate[ch] != keystate[ch])
2307 debug((", M%02x=%02x", ch, keystate[ch]));
2308
2309 memcpy(oldstate, keystate, sizeof(oldstate));
2310 }
2311 #endif
2312
2313 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2314 keystate[VK_RMENU] = keystate[VK_MENU];
2315 }
2316
2317
2318 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2319 if ((cfg.funky_type == 3 ||
2320 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2321 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2322
2323 wParam = VK_EXECUTE;
2324
2325 /* UnToggle NUMLock */
2326 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2327 keystate[VK_NUMLOCK] ^= 1;
2328 }
2329
2330 /* And write back the 'adjusted' state */
2331 SetKeyboardState(keystate);
2332 }
2333
2334 /* Disable Auto repeat if required */
2335 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2336 return 0;
2337
2338 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2339 left_alt = 1;
2340
2341 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2342
2343 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2344 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2345 if (cfg.ctrlaltkeys)
2346 keystate[VK_MENU] = 0;
2347 else {
2348 keystate[VK_RMENU] = 0x80;
2349 left_alt = 0;
2350 }
2351 }
2352
2353 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2354 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2355 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2356
2357 /* Note if AltGr was pressed and if it was used as a compose key */
2358 if (!compose_state) {
2359 compose_key = 0x100;
2360 if (cfg.compose_key) {
2361 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2362 compose_key = wParam;
2363 }
2364 if (wParam == VK_APPS)
2365 compose_key = wParam;
2366 }
2367
2368 if (wParam == compose_key) {
2369 if (compose_state == 0
2370 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2371 1;
2372 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2373 compose_state = 2;
2374 else
2375 compose_state = 0;
2376 } else if (compose_state == 1 && wParam != VK_CONTROL)
2377 compose_state = 0;
2378
2379 /*
2380 * Record that we pressed key so the scroll window can be reset, but
2381 * be careful to avoid Shift-UP/Down
2382 */
2383 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2384 seen_key_event = 1;
2385 }
2386
2387 /* Make sure we're not pasting */
2388 if (key_down)
2389 term_nopaste();
2390
2391 if (compose_state > 1 && left_alt)
2392 compose_state = 0;
2393
2394 /* Sanitize the number pad if not using a PC NumPad */
2395 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2396 && cfg.funky_type != 2)
2397 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2398 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2399 int nParam = 0;
2400 switch (wParam) {
2401 case VK_INSERT:
2402 nParam = VK_NUMPAD0;
2403 break;
2404 case VK_END:
2405 nParam = VK_NUMPAD1;
2406 break;
2407 case VK_DOWN:
2408 nParam = VK_NUMPAD2;
2409 break;
2410 case VK_NEXT:
2411 nParam = VK_NUMPAD3;
2412 break;
2413 case VK_LEFT:
2414 nParam = VK_NUMPAD4;
2415 break;
2416 case VK_CLEAR:
2417 nParam = VK_NUMPAD5;
2418 break;
2419 case VK_RIGHT:
2420 nParam = VK_NUMPAD6;
2421 break;
2422 case VK_HOME:
2423 nParam = VK_NUMPAD7;
2424 break;
2425 case VK_UP:
2426 nParam = VK_NUMPAD8;
2427 break;
2428 case VK_PRIOR:
2429 nParam = VK_NUMPAD9;
2430 break;
2431 case VK_DELETE:
2432 nParam = VK_DECIMAL;
2433 break;
2434 }
2435 if (nParam) {
2436 if (keystate[VK_NUMLOCK] & 1)
2437 shift_state |= 1;
2438 wParam = nParam;
2439 }
2440 }
2441 }
2442
2443 /* If a key is pressed and AltGr is not active */
2444 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2445 /* Okay, prepare for most alts then ... */
2446 if (left_alt)
2447 *p++ = '\033';
2448
2449 /* Lets see if it's a pattern we know all about ... */
2450 if (wParam == VK_PRIOR && shift_state == 1) {
2451 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2452 return 0;
2453 }
2454 if (wParam == VK_NEXT && shift_state == 1) {
2455 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2456 return 0;
2457 }
2458 if (wParam == VK_INSERT && shift_state == 1) {
2459 term_mouse(MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
2460 term_mouse(MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
2461 return 0;
2462 }
2463 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2464 return -1;
2465 }
2466 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2467 alt_state = 0;
2468 PostMessage(hwnd, WM_CHAR, ' ', 0);
2469 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2470 return -1;
2471 }
2472 /* Control-Numlock for app-keypad mode switch */
2473 if (wParam == VK_PAUSE && shift_state == 2) {
2474 app_keypad_keys ^= 1;
2475 return 0;
2476 }
2477
2478 /* Nethack keypad */
2479 if (cfg.nethack_keypad && !left_alt) {
2480 switch (wParam) {
2481 case VK_NUMPAD1:
2482 *p++ = shift_state ? 'B' : 'b';
2483 return p - output;
2484 case VK_NUMPAD2:
2485 *p++ = shift_state ? 'J' : 'j';
2486 return p - output;
2487 case VK_NUMPAD3:
2488 *p++ = shift_state ? 'N' : 'n';
2489 return p - output;
2490 case VK_NUMPAD4:
2491 *p++ = shift_state ? 'H' : 'h';
2492 return p - output;
2493 case VK_NUMPAD5:
2494 *p++ = shift_state ? '.' : '.';
2495 return p - output;
2496 case VK_NUMPAD6:
2497 *p++ = shift_state ? 'L' : 'l';
2498 return p - output;
2499 case VK_NUMPAD7:
2500 *p++ = shift_state ? 'Y' : 'y';
2501 return p - output;
2502 case VK_NUMPAD8:
2503 *p++ = shift_state ? 'K' : 'k';
2504 return p - output;
2505 case VK_NUMPAD9:
2506 *p++ = shift_state ? 'U' : 'u';
2507 return p - output;
2508 }
2509 }
2510
2511 /* Application Keypad */
2512 if (!left_alt) {
2513 int xkey = 0;
2514
2515 if (cfg.funky_type == 3 ||
2516 (cfg.funky_type <= 1 &&
2517 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2518 case VK_EXECUTE:
2519 xkey = 'P';
2520 break;
2521 case VK_DIVIDE:
2522 xkey = 'Q';
2523 break;
2524 case VK_MULTIPLY:
2525 xkey = 'R';
2526 break;
2527 case VK_SUBTRACT:
2528 xkey = 'S';
2529 break;
2530 }
2531 if (app_keypad_keys && !cfg.no_applic_k)
2532 switch (wParam) {
2533 case VK_NUMPAD0:
2534 xkey = 'p';
2535 break;
2536 case VK_NUMPAD1:
2537 xkey = 'q';
2538 break;
2539 case VK_NUMPAD2:
2540 xkey = 'r';
2541 break;
2542 case VK_NUMPAD3:
2543 xkey = 's';
2544 break;
2545 case VK_NUMPAD4:
2546 xkey = 't';
2547 break;
2548 case VK_NUMPAD5:
2549 xkey = 'u';
2550 break;
2551 case VK_NUMPAD6:
2552 xkey = 'v';
2553 break;
2554 case VK_NUMPAD7:
2555 xkey = 'w';
2556 break;
2557 case VK_NUMPAD8:
2558 xkey = 'x';
2559 break;
2560 case VK_NUMPAD9:
2561 xkey = 'y';
2562 break;
2563
2564 case VK_DECIMAL:
2565 xkey = 'n';
2566 break;
2567 case VK_ADD:
2568 if (cfg.funky_type == 2) {
2569 if (shift_state)
2570 xkey = 'l';
2571 else
2572 xkey = 'k';
2573 } else if (shift_state)
2574 xkey = 'm';
2575 else
2576 xkey = 'l';
2577 break;
2578
2579 case VK_DIVIDE:
2580 if (cfg.funky_type == 2)
2581 xkey = 'o';
2582 break;
2583 case VK_MULTIPLY:
2584 if (cfg.funky_type == 2)
2585 xkey = 'j';
2586 break;
2587 case VK_SUBTRACT:
2588 if (cfg.funky_type == 2)
2589 xkey = 'm';
2590 break;
2591
2592 case VK_RETURN:
2593 if (HIWORD(lParam) & KF_EXTENDED)
2594 xkey = 'M';
2595 break;
2596 }
2597 if (xkey) {
2598 if (vt52_mode) {
2599 if (xkey >= 'P' && xkey <= 'S')
2600 p += sprintf((char *) p, "\x1B%c", xkey);
2601 else
2602 p += sprintf((char *) p, "\x1B?%c", xkey);
2603 } else
2604 p += sprintf((char *) p, "\x1BO%c", xkey);
2605 return p - output;
2606 }
2607 }
2608
2609 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2610 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2611 *p++ = 0;
2612 return -2;
2613 }
2614 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2615 *p++ = 0x1B;
2616 *p++ = '[';
2617 *p++ = 'Z';
2618 return p - output;
2619 }
2620 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2621 *p++ = 0;
2622 return p - output;
2623 }
2624 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2625 *p++ = 160;
2626 return p - output;
2627 }
2628 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2629 *p++ = 3;
2630 *p++ = 0;
2631 return -2;
2632 }
2633 if (wParam == VK_PAUSE) { /* Break/Pause */
2634 *p++ = 26;
2635 *p++ = 0;
2636 return -2;
2637 }
2638 /* Control-2 to Control-8 are special */
2639 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2640 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2641 return p - output;
2642 }
2643 if (shift_state == 2 && wParam == 0xBD) {
2644 *p++ = 0x1F;
2645 return p - output;
2646 }
2647 if (shift_state == 2 && wParam == 0xDF) {
2648 *p++ = 0x1C;
2649 return p - output;
2650 }
2651 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2652 *p++ = '\r';
2653 *p++ = '\n';
2654 return p - output;
2655 }
2656
2657 /*
2658 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2659 * for integer decimal nn.)
2660 *
2661 * We also deal with the weird ones here. Linux VCs replace F1
2662 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2663 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2664 * respectively.
2665 */
2666 code = 0;
2667 switch (wParam) {
2668 case VK_F1:
2669 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2670 break;
2671 case VK_F2:
2672 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2673 break;
2674 case VK_F3:
2675 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2676 break;
2677 case VK_F4:
2678 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2679 break;
2680 case VK_F5:
2681 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2682 break;
2683 case VK_F6:
2684 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2685 break;
2686 case VK_F7:
2687 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2688 break;
2689 case VK_F8:
2690 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2691 break;
2692 case VK_F9:
2693 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2694 break;
2695 case VK_F10:
2696 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2697 break;
2698 case VK_F11:
2699 code = 23;
2700 break;
2701 case VK_F12:
2702 code = 24;
2703 break;
2704 case VK_F13:
2705 code = 25;
2706 break;
2707 case VK_F14:
2708 code = 26;
2709 break;
2710 case VK_F15:
2711 code = 28;
2712 break;
2713 case VK_F16:
2714 code = 29;
2715 break;
2716 case VK_F17:
2717 code = 31;
2718 break;
2719 case VK_F18:
2720 code = 32;
2721 break;
2722 case VK_F19:
2723 code = 33;
2724 break;
2725 case VK_F20:
2726 code = 34;
2727 break;
2728 case VK_HOME:
2729 code = 1;
2730 break;
2731 case VK_INSERT:
2732 code = 2;
2733 break;
2734 case VK_DELETE:
2735 code = 3;
2736 break;
2737 case VK_END:
2738 code = 4;
2739 break;
2740 case VK_PRIOR:
2741 code = 5;
2742 break;
2743 case VK_NEXT:
2744 code = 6;
2745 break;
2746 }
2747 /* Reorder edit keys to physical order */
2748 if (cfg.funky_type == 3 && code <= 6)
2749 code = "\0\2\1\4\5\3\6"[code];
2750
2751 if (vt52_mode && code > 0 && code <= 6) {
2752 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2753 return p - output;
2754 }
2755
2756 if (cfg.funky_type == 5 && code >= 11 && code <= 34) {
2757 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2758 int index = 0;
2759 switch (wParam) {
2760 case VK_F1: index = 0; break;
2761 case VK_F2: index = 1; break;
2762 case VK_F3: index = 2; break;
2763 case VK_F4: index = 3; break;
2764 case VK_F5: index = 4; break;
2765 case VK_F6: index = 5; break;
2766 case VK_F7: index = 6; break;
2767 case VK_F8: index = 7; break;
2768 case VK_F9: index = 8; break;
2769 case VK_F10: index = 9; break;
2770 case VK_F11: index = 10; break;
2771 case VK_F12: index = 11; break;
2772 }
2773 if (keystate[VK_SHIFT] & 0x80) index += 12;
2774 if (keystate[VK_CONTROL] & 0x80) index += 24;
2775 p += sprintf((char *) p, "\x1B[%c", codes[index]);
2776 return p - output;
2777 }
2778 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2779 int offt = 0;
2780 if (code > 15)
2781 offt++;
2782 if (code > 21)
2783 offt++;
2784 if (vt52_mode)
2785 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2786 else
2787 p +=
2788 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2789 return p - output;
2790 }
2791 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2792 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2793 return p - output;
2794 }
2795 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2796 if (vt52_mode)
2797 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2798 else
2799 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2800 return p - output;
2801 }
2802 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2803 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2804 return p - output;
2805 }
2806 if (code) {
2807 p += sprintf((char *) p, "\x1B[%d~", code);
2808 return p - output;
2809 }
2810
2811 /*
2812 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2813 * some reason seems to send VK_CLEAR to Windows...).
2814 */
2815 {
2816 char xkey = 0;
2817 switch (wParam) {
2818 case VK_UP:
2819 xkey = 'A';
2820 break;
2821 case VK_DOWN:
2822 xkey = 'B';
2823 break;
2824 case VK_RIGHT:
2825 xkey = 'C';
2826 break;
2827 case VK_LEFT:
2828 xkey = 'D';
2829 break;
2830 case VK_CLEAR:
2831 xkey = 'G';
2832 break;
2833 }
2834 if (xkey) {
2835 if (vt52_mode)
2836 p += sprintf((char *) p, "\x1B%c", xkey);
2837 else {
2838 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2839 /* VT100 & VT102 manuals both state the app cursor keys
2840 * only work if the app keypad is on.
2841 */
2842 if (!app_keypad_keys)
2843 app_flg = 0;
2844 /* Useful mapping of Ctrl-arrows */
2845 if (shift_state == 2)
2846 app_flg = !app_flg;
2847
2848 if (app_flg)
2849 p += sprintf((char *) p, "\x1BO%c", xkey);
2850 else
2851 p += sprintf((char *) p, "\x1B[%c", xkey);
2852 }
2853 return p - output;
2854 }
2855 }
2856
2857 /*
2858 * Finally, deal with Return ourselves. (Win95 seems to
2859 * foul it up when Alt is pressed, for some reason.)
2860 */
2861 if (wParam == VK_RETURN) { /* Return */
2862 *p++ = 0x0D;
2863 *p++ = 0;
2864 return -2;
2865 }
2866
2867 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2868 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2869 else
2870 alt_sum = 0;
2871 }
2872
2873 /* Okay we've done everything interesting; let windows deal with
2874 * the boring stuff */
2875 {
2876 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2877 #ifdef SHOW_TOASCII_RESULT
2878 if (r == 1 && !key_down) {
2879 if (alt_sum) {
2880 if (utf || dbcs_screenfont)
2881 debug((", (U+%04x)", alt_sum));
2882 else
2883 debug((", LCH(%d)", alt_sum));
2884 } else {
2885 debug((", ACH(%d)", keys[0]));
2886 }
2887 } else if (r > 0) {
2888 int r1;
2889 debug((", ASC("));
2890 for (r1 = 0; r1 < r; r1++) {
2891 debug(("%s%d", r1 ? "," : "", keys[r1]));
2892 }
2893 debug((")"));
2894 }
2895 #endif
2896 if (r > 0) {
2897 WCHAR keybuf;
2898 p = output;
2899 for (i = 0; i < r; i++) {
2900 unsigned char ch = (unsigned char) keys[i];
2901
2902 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2903 compose_char = ch;
2904 compose_state++;
2905 continue;
2906 }
2907 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2908 int nc;
2909 compose_state = 0;
2910
2911 if ((nc = check_compose(compose_char, ch)) == -1) {
2912 MessageBeep(MB_ICONHAND);
2913 return 0;
2914 }
2915 keybuf = nc;
2916 luni_send(&keybuf, 1);
2917 continue;
2918 }
2919
2920 compose_state = 0;
2921
2922 if (!key_down) {
2923 if (alt_sum) {
2924 if (utf || dbcs_screenfont) {
2925 keybuf = alt_sum;
2926 luni_send(&keybuf, 1);
2927 } else {
2928 ch = (char) alt_sum;
2929 ldisc_send(&ch, 1);
2930 }
2931 alt_sum = 0;
2932 } else
2933 lpage_send(kbd_codepage, &ch, 1);
2934 } else {
2935 static char cbuf[] = "\033 ";
2936 cbuf[1] = ch;
2937 lpage_send(kbd_codepage, cbuf + !left_alt,
2938 1 + !!left_alt);
2939 }
2940 }
2941
2942 /* This is so the ALT-Numpad and dead keys work correctly. */
2943 keys[0] = 0;
2944
2945 return p - output;
2946 }
2947 /* If we're definitly not building up an ALT-54321 then clear it */
2948 if (!left_alt)
2949 keys[0] = 0;
2950 /* If we will be using alt_sum fix the 256s */
2951 else if (keys[0] && (utf || dbcs_screenfont))
2952 keys[0] = 10;
2953 }
2954
2955 /* ALT alone may or may not want to bring up the System menu */
2956 if (wParam == VK_MENU) {
2957 if (cfg.alt_only) {
2958 if (message == WM_SYSKEYDOWN)
2959 alt_state = 1;
2960 else if (message == WM_SYSKEYUP && alt_state)
2961 PostMessage(hwnd, WM_CHAR, ' ', 0);
2962 if (message == WM_SYSKEYUP)
2963 alt_state = 0;
2964 } else
2965 return 0;
2966 } else
2967 alt_state = 0;
2968
2969 return -1;
2970 }
2971
2972 void set_title(char *title)
2973 {
2974 sfree(window_name);
2975 window_name = smalloc(1 + strlen(title));
2976 strcpy(window_name, title);
2977 if (cfg.win_name_always || !IsIconic(hwnd))
2978 SetWindowText(hwnd, title);
2979 }
2980
2981 void set_icon(char *title)
2982 {
2983 sfree(icon_name);
2984 icon_name = smalloc(1 + strlen(title));
2985 strcpy(icon_name, title);
2986 if (!cfg.win_name_always && IsIconic(hwnd))
2987 SetWindowText(hwnd, title);
2988 }
2989
2990 void set_sbar(int total, int start, int page)
2991 {
2992 SCROLLINFO si;
2993
2994 if (!cfg.scrollbar)
2995 return;
2996
2997 si.cbSize = sizeof(si);
2998 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2999 si.nMin = 0;
3000 si.nMax = total - 1;
3001 si.nPage = page;
3002 si.nPos = start;
3003 if (hwnd)
3004 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3005 }
3006
3007 Context get_ctx(void)
3008 {
3009 HDC hdc;
3010 if (hwnd) {
3011 hdc = GetDC(hwnd);
3012 if (hdc && pal)
3013 SelectPalette(hdc, pal, FALSE);
3014 return hdc;
3015 } else
3016 return NULL;
3017 }
3018
3019 void free_ctx(Context ctx)
3020 {
3021 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3022 ReleaseDC(hwnd, ctx);
3023 }
3024
3025 static void real_palette_set(int n, int r, int g, int b)
3026 {
3027 if (pal) {
3028 logpal->palPalEntry[n].peRed = r;
3029 logpal->palPalEntry[n].peGreen = g;
3030 logpal->palPalEntry[n].peBlue = b;
3031 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3032 colours[n] = PALETTERGB(r, g, b);
3033 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3034 } else
3035 colours[n] = RGB(r, g, b);
3036 }
3037
3038 void palette_set(int n, int r, int g, int b)
3039 {
3040 static const int first[21] = {
3041 0, 2, 4, 6, 8, 10, 12, 14,
3042 1, 3, 5, 7, 9, 11, 13, 15,
3043 16, 17, 18, 20, 22
3044 };
3045 real_palette_set(first[n], r, g, b);
3046 if (first[n] >= 18)
3047 real_palette_set(first[n] + 1, r, g, b);
3048 if (pal) {
3049 HDC hdc = get_ctx();
3050 UnrealizeObject(pal);
3051 RealizePalette(hdc);
3052 free_ctx(hdc);
3053 }
3054 }
3055
3056 void palette_reset(void)
3057 {
3058 int i;
3059
3060 for (i = 0; i < NCOLOURS; i++) {
3061 if (pal) {
3062 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3063 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3064 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3065 logpal->palPalEntry[i].peFlags = 0;
3066 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3067 defpal[i].rgbtGreen,
3068 defpal[i].rgbtBlue);
3069 } else
3070 colours[i] = RGB(defpal[i].rgbtRed,
3071 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3072 }
3073
3074 if (pal) {
3075 HDC hdc;
3076 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3077 hdc = get_ctx();
3078 RealizePalette(hdc);
3079 free_ctx(hdc);
3080 }
3081 }
3082
3083 void write_aclip(char *data, int len, int must_deselect)
3084 {
3085 HGLOBAL clipdata;
3086 void *lock;
3087
3088 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3089 if (!clipdata)
3090 return;
3091 lock = GlobalLock(clipdata);
3092 if (!lock)
3093 return;
3094 memcpy(lock, data, len);
3095 ((unsigned char *) lock)[len] = 0;
3096 GlobalUnlock(clipdata);
3097
3098 if (!must_deselect)
3099 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3100
3101 if (OpenClipboard(hwnd)) {
3102 EmptyClipboard();
3103 SetClipboardData(CF_TEXT, clipdata);
3104 CloseClipboard();
3105 } else
3106 GlobalFree(clipdata);
3107
3108 if (!must_deselect)
3109 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3110 }
3111
3112 /*
3113 * Note: unlike write_aclip() this will not append a nul.
3114 */
3115 void write_clip(wchar_t * data, int len, int must_deselect)
3116 {
3117 HGLOBAL clipdata;
3118 HGLOBAL clipdata2;
3119 int len2;
3120 void *lock, *lock2;
3121
3122 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3123
3124 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3125 len * sizeof(wchar_t));
3126 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3127
3128 if (!clipdata || !clipdata2) {
3129 if (clipdata)
3130 GlobalFree(clipdata);
3131 if (clipdata2)
3132 GlobalFree(clipdata2);
3133 return;
3134 }
3135 if (!(lock = GlobalLock(clipdata)))
3136 return;
3137 if (!(lock2 = GlobalLock(clipdata2)))
3138 return;
3139
3140 memcpy(lock, data, len * sizeof(wchar_t));
3141 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3142
3143 GlobalUnlock(clipdata);
3144 GlobalUnlock(clipdata2);
3145
3146 if (!must_deselect)
3147 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3148
3149 if (OpenClipboard(hwnd)) {
3150 EmptyClipboard();
3151 SetClipboardData(CF_UNICODETEXT, clipdata);
3152 SetClipboardData(CF_TEXT, clipdata2);
3153 CloseClipboard();
3154 } else {
3155 GlobalFree(clipdata);
3156 GlobalFree(clipdata2);
3157 }
3158
3159 if (!must_deselect)
3160 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3161 }
3162
3163 void get_clip(wchar_t ** p, int *len)
3164 {
3165 static HGLOBAL clipdata = NULL;
3166 static wchar_t *converted = 0;
3167 wchar_t *p2;
3168
3169 if (converted) {
3170 sfree(converted);
3171 converted = 0;
3172 }
3173 if (!p) {
3174 if (clipdata)
3175 GlobalUnlock(clipdata);
3176 clipdata = NULL;
3177 return;
3178 } else if (OpenClipboard(NULL)) {
3179 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3180 CloseClipboard();
3181 *p = GlobalLock(clipdata);
3182 if (*p) {
3183 for (p2 = *p; *p2; p2++);
3184 *len = p2 - *p;
3185 return;
3186 }
3187 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3188 char *s;
3189 int i;
3190 CloseClipboard();
3191 s = GlobalLock(clipdata);
3192 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3193 *p = converted = smalloc(i * sizeof(wchar_t));
3194 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3195 *len = i - 1;
3196 return;
3197 } else
3198 CloseClipboard();
3199 }
3200
3201 *p = NULL;
3202 *len = 0;
3203 }
3204
3205 #if 0
3206 /*
3207 * Move `lines' lines from position `from' to position `to' in the
3208 * window.
3209 */
3210 void optimised_move(int to, int from, int lines)
3211 {
3212 RECT r;
3213 int min, max;
3214
3215 min = (to < from ? to : from);
3216 max = to + from - min;
3217
3218 r.left = 0;
3219 r.right = cols * font_width;
3220 r.top = min * font_height;
3221 r.bottom = (max + lines) * font_height;
3222 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3223 }
3224 #endif
3225
3226 /*
3227 * Print a message box and perform a fatal exit.
3228 */
3229 void fatalbox(char *fmt, ...)
3230 {
3231 va_list ap;
3232 char stuff[200];
3233
3234 va_start(ap, fmt);
3235 vsprintf(stuff, fmt, ap);
3236 va_end(ap);
3237 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3238 exit(1);
3239 }
3240
3241 /*
3242 * Beep.
3243 */
3244 void beep(int mode)
3245 {
3246 if (mode == BELL_DEFAULT) {
3247 /*
3248 * For MessageBeep style bells, we want to be careful of
3249 * timing, because they don't have the nice property of
3250 * PlaySound bells that each one cancels the previous
3251 * active one. So we limit the rate to one per 50ms or so.
3252 */
3253 static long lastbeep = 0;
3254 long beepdiff;
3255
3256 beepdiff = GetTickCount() - lastbeep;
3257 if (beepdiff >= 0 && beepdiff < 50)
3258 return;
3259 MessageBeep(MB_OK);
3260 /*
3261 * The above MessageBeep call takes time, so we record the
3262 * time _after_ it finishes rather than before it starts.
3263 */
3264 lastbeep = GetTickCount();
3265 } else if (mode == BELL_WAVEFILE) {
3266 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3267 char buf[sizeof(cfg.bell_wavefile) + 80];
3268 sprintf(buf, "Unable to play sound file\n%s\n"
3269 "Using default sound instead", cfg.bell_wavefile);
3270 MessageBox(hwnd, buf, "PuTTY Sound Error",
3271 MB_OK | MB_ICONEXCLAMATION);
3272 cfg.beep = BELL_DEFAULT;
3273 }
3274 }
3275 }