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