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