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