14 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
18 #define IDM_SHOWLOG 0x0010
19 #define IDM_NEWSESS 0x0020
20 #define IDM_DUPSESS 0x0030
21 #define IDM_RECONF 0x0040
22 #define IDM_CLRSB 0x0050
23 #define IDM_RESET 0x0060
24 #define IDM_TEL_AYT 0x0070
25 #define IDM_TEL_BRK 0x0080
26 #define IDM_TEL_SYNCH 0x0090
27 #define IDM_TEL_EC 0x00a0
28 #define IDM_TEL_EL 0x00b0
29 #define IDM_TEL_GA 0x00c0
30 #define IDM_TEL_NOP 0x00d0
31 #define IDM_TEL_ABORT 0x00e0
32 #define IDM_TEL_AO 0x00f0
33 #define IDM_TEL_IP 0x0100
34 #define IDM_TEL_SUSP 0x0110
35 #define IDM_TEL_EOR 0x0120
36 #define IDM_TEL_EOF 0x0130
37 #define IDM_ABOUT 0x0140
38 #define IDM_SAVEDSESS 0x0150
40 #define IDM_SAVED_MIN 0x1000
41 #define IDM_SAVED_MAX 0x2000
43 #define WM_IGNORE_SIZE (WM_XUSER + 1)
44 #define WM_IGNORE_CLIP (WM_XUSER + 2)
46 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
47 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
48 static void cfgtopalette(void);
49 static void init_palette(void);
50 static void init_fonts(int);
52 static int extra_width, extra_height;
54 static int pending_netevent = 0;
55 static WPARAM pend_netevent_wParam = 0;
56 static LPARAM pend_netevent_lParam = 0;
57 static void enact_pending_netevent(void);
61 #define FONT_UNDERLINE 2
62 #define FONT_BOLDUND 3
64 #define FONT_OEMBOLD 5
65 #define FONT_OEMBOLDUND 6
67 static HFONT fonts[8];
68 static int font_needs_hand_underlining;
70 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
78 static COLORREF colours[NCOLOURS];
80 static LPLOGPALETTE logpal;
81 static RGBTRIPLE defpal[NCOLOURS];
85 static int dbltime, lasttime, lastact;
86 static Mouse_Button lastbtn;
88 static char *window_name, *icon_name;
90 static Ldisc *real_ldisc;
92 void begin_session(void) {
96 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
97 static char appname[] = "PuTTY";
102 int guess_width, guess_height;
105 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
107 winsock_ver = MAKEWORD(1, 1);
108 if (WSAStartup(winsock_ver, &wsadata)) {
109 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
110 MB_OK | MB_ICONEXCLAMATION);
113 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
114 MessageBox(NULL, "WinSock version is incompatible with 1.1",
115 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
119 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
121 InitCommonControls();
124 * Process the command line.
129 default_protocol = DEFAULT_PROTOCOL;
130 default_port = DEFAULT_PORT;
135 while (*p && isspace(*p)) p++;
138 * Process command line options first. Yes, this can be
139 * done better, and it will be as soon as I have the
143 char *q = p + strcspn(p, " \t");
146 tolower(p[0]) == 's' &&
147 tolower(p[1]) == 's' &&
148 tolower(p[2]) == 'h') {
149 default_protocol = cfg.protocol = PROT_SSH;
150 default_port = cfg.port = 22;
151 } else if (q == p + 3 &&
152 tolower(p[0]) == 'l' &&
153 tolower(p[1]) == 'o' &&
154 tolower(p[2]) == 'g') {
155 logfile = "putty.log";
157 p = q + strspn(q, " \t");
161 * An initial @ means to activate a saved session.
165 if (!*cfg.host && !do_config()) {
169 } else if (*p == '&') {
171 * An initial & means we've been given a command line
172 * containing the hex value of a HANDLE for a file
173 * mapping object, which we must then extract as a
178 if (sscanf(p+1, "%p", &filemap) == 1 &&
179 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
180 0, 0, sizeof(Config))) != NULL) {
183 CloseHandle(filemap);
184 } else if (!do_config()) {
191 * If the hostname starts with "telnet:", set the
192 * protocol to Telnet and process the string as a
195 if (!strncmp(q, "telnet:", 7)) {
199 if (q[0] == '/' && q[1] == '/')
201 cfg.protocol = PROT_TELNET;
203 while (*p && *p != ':' && *p != '/') p++;
211 strncpy (cfg.host, q, sizeof(cfg.host)-1);
212 cfg.host[sizeof(cfg.host)-1] = '\0';
214 while (*p && !isspace(*p)) p++;
217 strncpy (cfg.host, q, sizeof(cfg.host)-1);
218 cfg.host[sizeof(cfg.host)-1] = '\0';
219 while (*p && isspace(*p)) p++;
232 /* See if host is of the form user@host */
233 if (cfg.host[0] != '\0') {
234 char *atsign = strchr(cfg.host, '@');
235 /* Make sure we're not overflowing the user field */
237 if (atsign-cfg.host < sizeof cfg.username) {
238 strncpy (cfg.username, cfg.host, atsign-cfg.host);
239 cfg.username[atsign-cfg.host] = '\0';
241 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
247 * Select protocol. This is farmed out into a table in a
248 * separate file to enable an ssh-free variant.
253 for (i = 0; backends[i].backend != NULL; i++)
254 if (backends[i].protocol == cfg.protocol) {
255 back = backends[i].backend;
259 MessageBox(NULL, "Unsupported protocol number found",
260 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
266 real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
267 /* To start with, we use the simple line discipline, so we can
268 * type passwords etc without fear of them being echoed... */
269 ldisc = &ldisc_simple;
273 wndclass.lpfnWndProc = WndProc;
274 wndclass.cbClsExtra = 0;
275 wndclass.cbWndExtra = 0;
276 wndclass.hInstance = inst;
277 wndclass.hIcon = LoadIcon (inst,
278 MAKEINTRESOURCE(IDI_MAINICON));
279 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
280 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
281 wndclass.lpszMenuName = NULL;
282 wndclass.lpszClassName = appname;
284 RegisterClass (&wndclass);
289 savelines = cfg.savelines;
295 * Guess some defaults for the window size. This all gets
296 * updated later, so we don't really care too much. However, we
297 * do want the font width/height guesses to correspond to a
298 * large font rather than a small one...
305 term_size (cfg.height, cfg.width, cfg.savelines);
306 guess_width = extra_width + font_width * cols;
307 guess_height = extra_height + font_height * rows;
310 HWND w = GetDesktopWindow();
311 GetWindowRect (w, &r);
312 if (guess_width > r.right - r.left)
313 guess_width = r.right - r.left;
314 if (guess_height > r.bottom - r.top)
315 guess_height = r.bottom - r.top;
319 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
320 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
321 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
322 hwnd = CreateWindow (appname, appname,
324 CW_USEDEFAULT, CW_USEDEFAULT,
325 guess_width, guess_height,
326 NULL, NULL, inst, NULL);
330 * Initialise the fonts, simultaneously correcting the guesses
331 * for font_{width,height}.
333 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
338 * Correct the guesses for extra_{width,height}.
342 GetWindowRect (hwnd, &wr);
343 GetClientRect (hwnd, &cr);
344 extra_width = wr.right - wr.left - cr.right + cr.left;
345 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
349 * Resize the window, now we know what size we _really_ want it
352 guess_width = extra_width + font_width * cols;
353 guess_height = extra_height + font_height * rows;
354 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
355 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
356 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
359 * Initialise the scroll bar.
364 si.cbSize = sizeof(si);
365 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
370 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
374 * Start up the telnet connection.
381 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
383 sprintf(msg, "Unable to open connection:\n%s", error);
384 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
387 window_name = icon_name = NULL;
388 sprintf(msg, "%s - PuTTY", realhost);
393 session_closed = FALSE;
396 * Set up the input and output buffers.
399 outbuf_reap = outbuf_head = 0;
402 * Prepare the mouse handler.
404 lastact = MA_NOTHING;
405 lastbtn = MB_NOTHING;
406 dbltime = GetDoubleClickTime();
409 * Set up the session-control options on the system menu.
412 HMENU m = GetSystemMenu (hwnd, FALSE);
416 AppendMenu (m, MF_SEPARATOR, 0, 0);
417 if (cfg.protocol == PROT_TELNET) {
419 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
420 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
421 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
422 AppendMenu (p, MF_SEPARATOR, 0, 0);
423 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
424 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
425 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
426 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
427 AppendMenu (p, MF_SEPARATOR, 0, 0);
428 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
429 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
430 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
431 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
432 AppendMenu (p, MF_SEPARATOR, 0, 0);
433 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
434 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
435 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
436 AppendMenu (m, MF_SEPARATOR, 0, 0);
438 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
439 AppendMenu (m, MF_SEPARATOR, 0, 0);
440 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
441 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
444 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
445 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
446 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
447 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
448 AppendMenu (m, MF_SEPARATOR, 0, 0);
449 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
450 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
451 AppendMenu (m, MF_SEPARATOR, 0, 0);
452 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
456 * Finally show the window!
458 ShowWindow (hwnd, show);
461 * Set the palette up.
467 has_focus = (GetForegroundWindow() == hwnd);
471 int timer_id = 0, long_timer = 0;
473 while (GetMessage (&msg, NULL, 0, 0) == 1) {
474 /* Sometimes DispatchMessage calls routines that use their own
475 * GetMessage loop, setup this timer so we get some control back.
477 * Also call term_update() from the timer so that if the host
478 * is sending data flat out we still do redraws.
480 if(timer_id && long_timer) {
481 KillTimer(hwnd, timer_id);
482 long_timer = timer_id = 0;
485 timer_id = SetTimer(hwnd, 1, 20, NULL);
486 DispatchMessage (&msg);
488 /* This is too fast, but I'll leave it for now 'cause it shows
489 * how often term_update is called (far too often at times!)
493 /* Send the paste buffer if there's anything to send */
496 /* If there's nothing new in the queue then we can do everything
497 * we've delayed, reading the socket, writing, and repainting
500 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
501 if (pending_netevent) {
502 enact_pending_netevent();
507 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
509 KillTimer(hwnd, timer_id);
516 timer_id = SetTimer(hwnd, 1, 2000, NULL);
517 else if (cfg.blinktext)
518 timer_id = SetTimer(hwnd, 1, 250, NULL);
520 timer_id = SetTimer(hwnd, 1, 500, NULL);
533 DeleteObject(fonts[i]);
540 if (cfg.protocol == PROT_SSH) {
551 * Print a message box and close the connection.
553 void connection_fatal(char *fmt, ...) {
558 vsprintf(stuff, fmt, ap);
560 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
561 if (cfg.close_on_exit)
564 session_closed = TRUE;
565 SetWindowText (hwnd, "PuTTY (inactive)");
570 * Actually do the job requested by a WM_NETEVENT
572 static void enact_pending_netevent(void) {
574 static int reentering = 0;
577 return; /* don't unpend the pending */
579 pending_netevent = FALSE;
582 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
587 switch (WSABASEERR + (-i) % 10000) {
589 sprintf(buf, "Connection reset by peer");
592 sprintf(buf, "Unexpected network error %d", -i);
595 connection_fatal(buf);
598 if (cfg.close_on_exit)
601 session_closed = TRUE;
602 MessageBox(hwnd, "Connection closed by remote host",
603 "PuTTY", MB_OK | MB_ICONINFORMATION);
604 SetWindowText (hwnd, "PuTTY (inactive)");
610 * Copy the colour palette from the configuration data into defpal.
611 * This is non-trivial because the colour indices are different.
613 static void cfgtopalette(void) {
615 static const int ww[] = {
616 6, 7, 8, 9, 10, 11, 12, 13,
617 14, 15, 16, 17, 18, 19, 20, 21,
618 0, 1, 2, 3, 4, 4, 5, 5
621 for (i=0; i<24; i++) {
623 defpal[i].rgbtRed = cfg.colours[w][0];
624 defpal[i].rgbtGreen = cfg.colours[w][1];
625 defpal[i].rgbtBlue = cfg.colours[w][2];
630 * Set up the colour palette.
632 static void init_palette(void) {
634 HDC hdc = GetDC (hwnd);
636 if (cfg.try_palette &&
637 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
638 logpal = smalloc(sizeof(*logpal)
639 - sizeof(logpal->palPalEntry)
640 + NCOLOURS * sizeof(PALETTEENTRY));
641 logpal->palVersion = 0x300;
642 logpal->palNumEntries = NCOLOURS;
643 for (i = 0; i < NCOLOURS; i++) {
644 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
645 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
646 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
647 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
649 pal = CreatePalette (logpal);
651 SelectPalette (hdc, pal, FALSE);
652 RealizePalette (hdc);
653 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
657 ReleaseDC (hwnd, hdc);
660 for (i=0; i<NCOLOURS; i++)
661 colours[i] = PALETTERGB(defpal[i].rgbtRed,
665 for(i=0; i<NCOLOURS; i++)
666 colours[i] = RGB(defpal[i].rgbtRed,
672 * Initialise all the fonts we will need. There may be as many as
673 * eight or as few as one. We also:
675 * - check the font width and height, correcting our guesses if
678 * - verify that the bold font is the same width as the ordinary
679 * one, and engage shadow bolding if not.
681 * - verify that the underlined font is the same width as the
682 * ordinary one (manual underlining by means of line drawing can
683 * be done in a pinch).
685 static void init_fonts(int pick_width) {
690 int fw_dontcare, fw_bold;
699 if (cfg.fontisbold) {
700 fw_dontcare = FW_BOLD;
703 fw_dontcare = FW_DONTCARE;
709 font_height = cfg.fontheight;
710 font_width = pick_width;
713 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
714 c, OUT_DEFAULT_PRECIS, \
715 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
716 FIXED_PITCH | FF_DONTCARE, cfg.font)
718 if (cfg.vtmode != VT_OEMONLY) {
719 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
721 SelectObject (hdc, fonts[FONT_NORMAL]);
722 GetTextMetrics(hdc, &tm);
723 font_height = tm.tmHeight;
724 font_width = tm.tmAveCharWidth;
726 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
729 * Some fonts, e.g. 9-pt Courier, draw their underlines
730 * outside their character cell. We successfully prevent
731 * screen corruption by clipping the text output, but then
732 * we lose the underline completely. Here we try to work
733 * out whether this is such a font, and if it is, we set a
734 * flag that causes underlines to be drawn by hand.
736 * Having tried other more sophisticated approaches (such
737 * as examining the TEXTMETRIC structure or requesting the
738 * height of a string), I think we'll do this the brute
739 * force way: we create a small bitmap, draw an underlined
740 * space on it, and test to see whether any pixels are
741 * foreground-coloured. (Since we expect the underline to
742 * go all the way across the character cell, we only search
743 * down a single column of the bitmap, half way across.)
747 HBITMAP und_bm, und_oldbm;
751 und_dc = CreateCompatibleDC(hdc);
752 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
753 und_oldbm = SelectObject(und_dc, und_bm);
754 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
755 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
756 SetTextColor (und_dc, RGB(255,255,255));
757 SetBkColor (und_dc, RGB(0,0,0));
758 SetBkMode (und_dc, OPAQUE);
759 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
761 for (i = 0; i < font_height; i++) {
762 c = GetPixel(und_dc, font_width/2, i);
766 SelectObject(und_dc, und_oldbm);
767 DeleteObject(und_bm);
769 font_needs_hand_underlining = !gotit;
772 if (bold_mode == BOLD_FONT) {
773 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
774 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
777 if (cfg.vtmode == VT_OEMANSI) {
778 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
779 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
781 if (bold_mode == BOLD_FONT) {
782 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
783 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
789 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
791 SelectObject (hdc, fonts[FONT_OEM]);
792 GetTextMetrics(hdc, &tm);
793 font_height = tm.tmHeight;
794 font_width = tm.tmAveCharWidth;
796 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
798 if (bold_mode == BOLD_FONT) {
799 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
800 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
805 descent = tm.tmAscent + 1;
806 if (descent >= font_height)
807 descent = font_height - 1;
808 firstchar = tm.tmFirstChar;
810 for (i=0; i<8; i++) {
812 if (SelectObject (hdc, fonts[i]) &&
813 GetTextMetrics(hdc, &tm) )
814 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
820 ReleaseDC (hwnd, hdc);
822 /* ... This is wrong in OEM only mode */
823 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
824 (bold_mode == BOLD_FONT &&
825 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
827 DeleteObject (fonts[FONT_UNDERLINE]);
828 if (bold_mode == BOLD_FONT)
829 DeleteObject (fonts[FONT_BOLDUND]);
832 if (bold_mode == BOLD_FONT &&
833 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
834 bold_mode = BOLD_SHADOW;
835 DeleteObject (fonts[FONT_BOLD]);
836 if (und_mode == UND_FONT)
837 DeleteObject (fonts[FONT_BOLDUND]);
841 /* With the fascist font painting it doesn't matter if the linedraw font
842 * isn't exactly the right size anymore so we don't have to check this.
844 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
845 if( cfg.fontcharset == OEM_CHARSET )
847 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
848 "different sizes. Using OEM-only mode instead",
849 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
850 cfg.vtmode = VT_OEMONLY;
852 else if( firstchar < ' ' )
854 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
855 "different sizes. Using XTerm mode instead",
856 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
857 cfg.vtmode = VT_XWINDOWS;
861 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
862 "different sizes. Using ISO8859-1 mode instead",
863 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
864 cfg.vtmode = VT_POORMAN;
869 DeleteObject (fonts[i]);
875 void request_resize (int w, int h, int refont) {
878 /* If the window is maximized supress resizing attempts */
879 if(IsZoomed(hwnd)) return;
882 /* Don't do this in OEMANSI, you may get disable messages */
883 if (refont && w != cols && (cols==80 || cols==132)
884 && cfg.vtmode != VT_OEMANSI)
886 if (refont && w != cols && (cols==80 || cols==132))
889 /* If font width too big for screen should we shrink the font more ? */
891 font_width = ((font_width*cols+w/2)/w);
898 DeleteObject(fonts[i]);
900 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
902 init_fonts(font_width);
906 static int first_time = 1;
912 /* Get the size of the screen */
913 if (GetClientRect(GetDesktopWindow(),&ss))
914 /* first_time = 0 */;
915 else { first_time = 2; break; }
917 /* Make sure the values are sane */
918 width = (ss.right-ss.left-extra_width ) / font_width;
919 height = (ss.bottom-ss.top-extra_height ) / font_height;
921 if (w>width) w=width;
922 if (h>height) h=height;
928 width = extra_width + font_width * w;
929 height = extra_height + font_height * h;
931 SetWindowPos (hwnd, NULL, 0, 0, width, height,
932 SWP_NOACTIVATE | SWP_NOCOPYBITS |
933 SWP_NOMOVE | SWP_NOZORDER);
936 static void click (Mouse_Button b, int x, int y) {
937 int thistime = GetMessageTime();
939 if (lastbtn == b && thistime - lasttime < dbltime) {
940 lastact = (lastact == MA_CLICK ? MA_2CLK :
941 lastact == MA_2CLK ? MA_3CLK :
942 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
947 if (lastact != MA_NOTHING)
948 term_mouse (b, lastact, x, y);
952 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
953 WPARAM wParam, LPARAM lParam) {
955 static int ignore_size = FALSE;
956 static int ignore_clip = FALSE;
957 static int just_reconfigged = FALSE;
958 static int resizing = FALSE;
962 if (pending_netevent)
963 enact_pending_netevent();
971 if (!cfg.warn_on_close || session_closed ||
972 MessageBox(hwnd, "Are you sure you want to close this session?",
973 "PuTTY Exit Confirmation",
974 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
981 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
993 PROCESS_INFORMATION pi;
994 HANDLE filemap = NULL;
996 if (wParam == IDM_DUPSESS) {
998 * Allocate a file-mapping memory chunk for the
1001 SECURITY_ATTRIBUTES sa;
1004 sa.nLength = sizeof(sa);
1005 sa.lpSecurityDescriptor = NULL;
1006 sa.bInheritHandle = TRUE;
1007 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1014 p = (Config *)MapViewOfFile(filemap,
1016 0, 0, sizeof(Config));
1018 *p = cfg; /* structure copy */
1022 sprintf(c, "putty &%p", filemap);
1024 } else if (wParam == IDM_SAVEDSESS) {
1025 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1026 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1028 cl = NULL; /* not a very important failure mode */
1030 sprintf(cl, "putty @%s", session);
1036 GetModuleFileName (NULL, b, sizeof(b)-1);
1038 si.lpReserved = NULL;
1039 si.lpDesktop = NULL;
1043 si.lpReserved2 = NULL;
1044 CreateProcess (b, cl, NULL, NULL, TRUE,
1045 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1048 CloseHandle(filemap);
1054 if (!do_reconfig(hwnd))
1056 just_reconfigged = TRUE;
1061 DeleteObject(fonts[i]);
1063 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1064 und_mode = UND_FONT;
1067 /* Telnet will change local echo -> remote if the remote asks */
1068 if (cfg.protocol != PROT_TELNET)
1069 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1077 /* Enable or disable the scroll bar, etc */
1079 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1082 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1083 else nflg &= ~WS_VSCROLL;
1085 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1087 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1093 SetWindowLong(hwnd, GWL_STYLE, nflg);
1094 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1095 SetWindowPos(hwnd, NULL, 0,0,0,0,
1096 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1097 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1100 GetWindowRect (hwnd, &wr);
1101 GetClientRect (hwnd, &cr);
1102 extra_width = wr.right - wr.left - cr.right + cr.left;
1103 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1107 term_size(cfg.height, cfg.width, cfg.savelines);
1108 InvalidateRect(hwnd, NULL, TRUE);
1109 SetWindowPos (hwnd, NULL, 0, 0,
1110 extra_width + font_width * cfg.width,
1111 extra_height + font_height * cfg.height,
1112 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1113 SWP_NOMOVE | SWP_NOZORDER);
1114 if (IsIconic(hwnd)) {
1115 SetWindowText (hwnd,
1116 cfg.win_name_always ? window_name : icon_name);
1125 case IDM_TEL_AYT: back->special (TS_AYT); break;
1126 case IDM_TEL_BRK: back->special (TS_BRK); break;
1127 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1128 case IDM_TEL_EC: back->special (TS_EC); break;
1129 case IDM_TEL_EL: back->special (TS_EL); break;
1130 case IDM_TEL_GA: back->special (TS_GA); break;
1131 case IDM_TEL_NOP: back->special (TS_NOP); break;
1132 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1133 case IDM_TEL_AO: back->special (TS_AO); break;
1134 case IDM_TEL_IP: back->special (TS_IP); break;
1135 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1136 case IDM_TEL_EOR: back->special (TS_EOR); break;
1137 case IDM_TEL_EOF: back->special (TS_EOF); break;
1142 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1143 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1148 #define X_POS(l) ((int)(short)LOWORD(l))
1149 #define Y_POS(l) ((int)(short)HIWORD(l))
1151 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1152 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1154 case WM_LBUTTONDOWN:
1155 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1156 TO_CHR_Y(Y_POS(lParam)));
1160 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1161 TO_CHR_Y(Y_POS(lParam)));
1164 case WM_MBUTTONDOWN:
1166 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1167 TO_CHR_X(X_POS(lParam)),
1168 TO_CHR_Y(Y_POS(lParam)));
1171 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1172 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1173 TO_CHR_Y(Y_POS(lParam)));
1176 case WM_RBUTTONDOWN:
1178 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1179 TO_CHR_X(X_POS(lParam)),
1180 TO_CHR_Y(Y_POS(lParam)));
1183 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1184 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1185 TO_CHR_Y(Y_POS(lParam)));
1190 * Add the mouse position and message time to the random
1191 * number noise, if we're using ssh.
1193 if (cfg.protocol == PROT_SSH)
1194 noise_ultralight(lParam);
1196 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1198 if (wParam & MK_LBUTTON)
1200 else if (wParam & MK_MBUTTON)
1201 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1203 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1204 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1205 TO_CHR_Y(Y_POS(lParam)));
1208 case WM_IGNORE_CLIP:
1209 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1211 case WM_DESTROYCLIPBOARD:
1214 ignore_clip = FALSE;
1219 hdc = BeginPaint (hwnd, &p);
1221 SelectPalette (hdc, pal, TRUE);
1222 RealizePalette (hdc);
1224 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1225 p.rcPaint.right, p.rcPaint.bottom);
1226 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1227 SelectObject (hdc, GetStockObject(WHITE_PEN));
1228 EndPaint (hwnd, &p);
1232 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1233 * but the only one that's likely to try to overload us is FD_READ.
1234 * This means buffering just one is fine.
1236 if (pending_netevent)
1237 enact_pending_netevent();
1239 pending_netevent = TRUE;
1240 pend_netevent_wParam=wParam;
1241 pend_netevent_lParam=lParam;
1253 case WM_IGNORE_SIZE:
1254 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1256 case WM_ENTERSIZEMOVE:
1260 case WM_EXITSIZEMOVE:
1267 int width, height, w, h, ew, eh;
1268 LPRECT r = (LPRECT)lParam;
1270 width = r->right - r->left - extra_width;
1271 height = r->bottom - r->top - extra_height;
1272 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1273 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1274 UpdateSizeTip(hwnd, w, h);
1275 ew = width - w * font_width;
1276 eh = height - h * font_height;
1278 if (wParam == WMSZ_LEFT ||
1279 wParam == WMSZ_BOTTOMLEFT ||
1280 wParam == WMSZ_TOPLEFT)
1286 if (wParam == WMSZ_TOP ||
1287 wParam == WMSZ_TOPRIGHT ||
1288 wParam == WMSZ_TOPLEFT)
1298 /* break; (never reached) */
1300 if (wParam == SIZE_MINIMIZED) {
1301 SetWindowText (hwnd,
1302 cfg.win_name_always ? window_name : icon_name);
1305 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1306 SetWindowText (hwnd, window_name);
1308 int width, height, w, h;
1309 #if 0 /* we have fixed this using WM_SIZING now */
1313 width = LOWORD(lParam);
1314 height = HIWORD(lParam);
1315 w = width / font_width; if (w < 1) w = 1;
1316 h = height / font_height; if (h < 1) h = 1;
1317 #if 0 /* we have fixed this using WM_SIZING now */
1318 ew = width - w * font_width;
1319 eh = height - h * font_height;
1320 if (ew != 0 || eh != 0) {
1322 GetWindowRect (hwnd, &r);
1323 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1324 SetWindowPos (hwnd, NULL, 0, 0,
1325 r.right - r.left - ew, r.bottom - r.top - eh,
1326 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1329 if (w != cols || h != rows || just_reconfigged) {
1331 term_size (h, w, cfg.savelines);
1333 * Don't call back->size in mid-resize. (To prevent
1334 * massive numbers of resize events getting sent
1335 * down the connection during an NT opaque drag.)
1339 just_reconfigged = FALSE;
1342 ignore_size = FALSE;
1345 switch (LOWORD(wParam)) {
1346 case SB_BOTTOM: term_scroll(-1, 0); break;
1347 case SB_TOP: term_scroll(+1, 0); break;
1348 case SB_LINEDOWN: term_scroll (0, +1); break;
1349 case SB_LINEUP: term_scroll (0, -1); break;
1350 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1351 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1352 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1353 term_scroll (1, HIWORD(wParam)); break;
1356 case WM_PALETTECHANGED:
1357 if ((HWND) wParam != hwnd && pal != NULL) {
1358 HDC hdc = get_ctx();
1360 if (RealizePalette (hdc) > 0)
1366 case WM_QUERYNEWPALETTE:
1368 HDC hdc = get_ctx();
1370 if (RealizePalette (hdc) > 0)
1382 * Add the scan code and keypress timing to the random
1383 * number noise, if we're using ssh.
1385 if (cfg.protocol == PROT_SSH)
1386 noise_ultralight(lParam);
1389 * We don't do TranslateMessage since it disassociates the
1390 * resulting CHAR message from the KEYDOWN that sparked it,
1391 * which we occasionally don't want. Instead, we process
1392 * KEYDOWN, and call the Win32 translator functions so that
1393 * we get the translations under _our_ control.
1396 unsigned char buf[20];
1399 len = TranslateKey (message, wParam, lParam, buf);
1401 return DefWindowProc (hwnd, message, wParam, lParam);
1402 ldisc->send (buf, len);
1408 * Nevertheless, we are prepared to deal with WM_CHAR
1409 * messages, should they crop up. So if someone wants to
1410 * post the things to us as part of a macro manoeuvre,
1411 * we're ready to cope.
1414 char c = xlat_kbd2tty((unsigned char)wParam);
1415 ldisc->send (&c, 1);
1420 return DefWindowProc (hwnd, message, wParam, lParam);
1424 * Draw a line of text in the window, at given character
1425 * coordinates, in given attributes.
1427 * We are allowed to fiddle with the contents of `text'.
1429 void do_text (Context ctx, int x, int y, char *text, int len,
1430 unsigned long attr, int lattr) {
1432 int nfg, nbg, nfont;
1435 int force_manual_underline = 0;
1436 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1437 static int *IpDx = 0, IpDxLEN = 0;;
1439 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1443 IpDx = smalloc((len+16)*sizeof(int));
1446 for(i=0; i<IpDxLEN; i++)
1447 IpDx[i] = fnt_width;
1453 if (attr & ATTR_ACTCURS) {
1454 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1455 attr ^= ATTR_CUR_XOR;
1459 if (cfg.vtmode == VT_OEMONLY)
1463 * Map high-half characters in order to approximate ISO using
1464 * OEM character set. No characters are missing if the OEM codepage
1467 if (nfont & FONT_OEM) {
1469 for (i=0; i<len; i++)
1470 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1472 /* This is CP850 ... perfect translation */
1473 static const char oemhighhalf[] =
1474 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1475 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1476 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1477 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1478 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1479 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1480 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1481 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1482 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1483 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1484 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1485 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1488 /* This is CP437 ... junk translation */
1489 static const unsigned char oemhighhalf[] = {
1490 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1491 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1492 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1493 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1494 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1495 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1496 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1497 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1498 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1499 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1500 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1501 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1504 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1508 if (attr & ATTR_GBCHR) {
1511 * GB mapping: map # to pound, and everything else stays
1514 for (i=0; i<len; i++)
1516 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1517 } else if (attr & ATTR_LINEDRW) {
1520 static const char poorman[] =
1521 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1524 static const char oemmap_437[] =
1525 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1526 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1529 static const char oemmap_850[] =
1530 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1531 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1533 /* Poor windows font ... eg: windows courier */
1534 static const char oemmap[] =
1535 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1536 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1539 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1540 * VT100 line drawing chars; everything else stays normal.
1542 switch (cfg.vtmode) {
1544 for (i=0; i<len; i++)
1545 if (text[i] >= '\x60' && text[i] <= '\x7E')
1546 text[i] += '\x01' - '\x60';
1549 /* Make sure we actually have an OEM font */
1550 if (fonts[nfont|FONT_OEM]) {
1553 for (i=0; i<len; i++)
1554 if (text[i] >= '\x60' && text[i] <= '\x7E')
1555 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1559 for (i=0; i<len; i++)
1560 if (text[i] >= '\x60' && text[i] <= '\x7E')
1561 text[i] = poorman[(unsigned char)text[i] - 0x60];
1566 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1567 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1568 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1570 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1571 nfont |= FONT_UNDERLINE;
1574 if (nfont&FONT_UNDERLINE)
1575 force_manual_underline = 1;
1576 /* Don't do the same for manual bold, it could be bad news. */
1578 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1580 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1581 force_manual_underline = 1;
1582 if (attr & ATTR_REVERSE) {
1583 t = nfg; nfg = nbg; nbg = t;
1585 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1587 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1591 SelectObject (hdc, fonts[nfont]);
1592 SetTextColor (hdc, fg);
1593 SetBkColor (hdc, bg);
1594 SetBkMode (hdc, OPAQUE);
1597 line_box.right = x+fnt_width*len;
1598 line_box.bottom = y+font_height;
1599 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1600 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1601 SetBkMode (hdc, TRANSPARENT);
1603 /* GRR: This draws the character outside it's box and can leave
1604 * 'droppings' even with the clip box! I suppose I could loop it
1605 * one character at a time ... yuk.
1607 * Or ... I could do a test print with "W", and use +1 or -1 for this
1608 * shift depending on if the leftmost column is blank...
1610 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1612 if (force_manual_underline ||
1613 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1615 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1616 MoveToEx (hdc, x, y+descent, NULL);
1617 LineTo (hdc, x+len*fnt_width, y+descent);
1618 oldpen = SelectObject (hdc, oldpen);
1619 DeleteObject (oldpen);
1621 if (attr & ATTR_PASCURS) {
1624 pts[0].x = pts[1].x = pts[4].x = x;
1625 pts[2].x = pts[3].x = x+fnt_width-1;
1626 pts[0].y = pts[3].y = pts[4].y = y;
1627 pts[1].y = pts[2].y = y+font_height-1;
1628 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1629 Polyline (hdc, pts, 5);
1630 oldpen = SelectObject (hdc, oldpen);
1631 DeleteObject (oldpen);
1635 static int check_compose(int first, int second) {
1637 static char * composetbl[] = {
1638 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1639 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1640 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1641 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1642 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1643 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1644 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1645 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1646 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1647 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1648 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1649 "\"uü", "'yý", "htþ", "\"yÿ",
1653 static int recurse = 0;
1660 sprintf(buf, "cc(%d,%d)", first, second);
1665 for(c=composetbl; *c; c++) {
1666 if( (*c)[0] == first && (*c)[1] == second)
1668 return (*c)[2] & 0xFF;
1675 nc = check_compose(second, first);
1677 nc = check_compose(toupper(first), toupper(second));
1679 nc = check_compose(toupper(second), toupper(first));
1687 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1688 * codes. Returns number of bytes used or zero to drop the message
1689 * or -1 to forward the message to windows.
1691 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1693 int scan, left_alt = 0, key_down, shift_state;
1695 unsigned char * p = output;
1697 static WORD keys[3];
1698 static int compose_state = 0;
1699 static int compose_char = 0;
1700 static WPARAM compose_key = 0;
1702 r = GetKeyboardState(keystate);
1703 if (!r) memset(keystate, 0, sizeof(keystate));
1706 /* Note if AltGr was pressed and if it was used as a compose key */
1707 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1709 keystate[VK_RMENU] = keystate[VK_MENU];
1710 if (!compose_state) compose_key = wParam;
1712 if (wParam == VK_APPS && !compose_state)
1713 compose_key = wParam;
1715 if (wParam == compose_key)
1717 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1719 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1724 else if (compose_state==1 && wParam != VK_CONTROL)
1727 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1728 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1729 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1731 wParam = VK_EXECUTE;
1733 /* UnToggle NUMLock */
1734 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1735 keystate[VK_NUMLOCK] ^= 1;
1738 /* And write back the 'adjusted' state */
1739 SetKeyboardState (keystate);
1742 /* Disable Auto repeat if required */
1743 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1746 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1749 key_down = ((HIWORD(lParam)&KF_UP)==0);
1751 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1752 if (left_alt && (keystate[VK_CONTROL]&0x80))
1753 keystate[VK_MENU] = 0;
1755 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1756 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1757 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1760 * Record that we pressed key so the scroll window can be reset, but
1761 * be careful to avoid Shift-UP/Down
1763 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1767 /* Make sure we're not pasting */
1768 if (key_down) term_nopaste();
1770 if (compose_state>1 && left_alt) compose_state = 0;
1772 /* Sanitize the number pad if not using a PC NumPad */
1773 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1774 || cfg.nethack_keypad || compose_state )
1776 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1781 case VK_INSERT: nParam = VK_NUMPAD0; break;
1782 case VK_END: nParam = VK_NUMPAD1; break;
1783 case VK_DOWN: nParam = VK_NUMPAD2; break;
1784 case VK_NEXT: nParam = VK_NUMPAD3; break;
1785 case VK_LEFT: nParam = VK_NUMPAD4; break;
1786 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1787 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1788 case VK_HOME: nParam = VK_NUMPAD7; break;
1789 case VK_UP: nParam = VK_NUMPAD8; break;
1790 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1791 case VK_DELETE: nParam = VK_DECIMAL; break;
1795 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1801 /* If a key is pressed and AltGr is not active */
1802 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1804 /* Okay, prepare for most alts then ...*/
1805 if (left_alt) *p++ = '\033';
1807 /* Lets see if it's a pattern we know all about ... */
1808 if (wParam == VK_PRIOR && shift_state == 1) {
1809 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1812 if (wParam == VK_NEXT && shift_state == 1) {
1813 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1816 if (wParam == VK_INSERT && shift_state == 1) {
1817 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1818 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1821 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1824 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1826 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1830 /* Nethack keypad */
1831 if (cfg.nethack_keypad && !left_alt) {
1833 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1834 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1835 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1836 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1837 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1838 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1839 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1840 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1841 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1845 /* Application Keypad */
1849 if ( cfg.funky_type == 0 ||
1850 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1851 case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1852 case VK_DIVIDE: xkey = 'Q'; break;
1853 case VK_MULTIPLY:xkey = 'R'; break;
1854 case VK_SUBTRACT:xkey = 'S'; break;
1856 if(app_keypad_keys) switch(wParam) {
1857 case VK_NUMPAD0: xkey = 'p'; break;
1858 case VK_NUMPAD1: xkey = 'q'; break;
1859 case VK_NUMPAD2: xkey = 'r'; break;
1860 case VK_NUMPAD3: xkey = 's'; break;
1861 case VK_NUMPAD4: xkey = 't'; break;
1862 case VK_NUMPAD5: xkey = 'u'; break;
1863 case VK_NUMPAD6: xkey = 'v'; break;
1864 case VK_NUMPAD7: xkey = 'w'; break;
1865 case VK_NUMPAD8: xkey = 'x'; break;
1866 case VK_NUMPAD9: xkey = 'y'; break;
1868 case VK_DECIMAL: xkey = 'n'; break;
1869 case VK_ADD: if(shift_state) xkey = 'm';
1873 if (HIWORD(lParam)&KF_EXTENDED)
1881 if (xkey>='P' && xkey<='S')
1882 p += sprintf((char *)p, "\x1B%c", xkey);
1884 p += sprintf((char *)p, "\x1B?%c", xkey);
1887 p += sprintf((char *)p, "\x1BO%c", xkey);
1892 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1894 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1897 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1899 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1901 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1903 *p++ = 0; return p - output;
1905 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1907 *p++ = 160; return p - output;
1909 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1911 *p++ = 3; return p - output;
1913 /* Control-2 to Control-8 are special */
1914 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1916 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1919 if (shift_state == 2 && wParam == 0xBD) {
1923 if (shift_state == 2 && wParam == 0xDF) {
1927 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1928 *p++ = '\r'; *p++ = '\n';
1933 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1934 * for integer decimal nn.)
1936 * We also deal with the weird ones here. Linux VCs replace F1
1937 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1938 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1943 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1944 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1945 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1946 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1947 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1948 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1949 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1950 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1951 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1952 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1953 case VK_F11: code = 23; break;
1954 case VK_F12: code = 24; break;
1955 case VK_F13: code = 25; break;
1956 case VK_F14: code = 26; break;
1957 case VK_F15: code = 28; break;
1958 case VK_F16: code = 29; break;
1959 case VK_F17: code = 31; break;
1960 case VK_F18: code = 32; break;
1961 case VK_F19: code = 33; break;
1962 case VK_F20: code = 34; break;
1963 case VK_HOME: code = 1; break;
1964 case VK_INSERT: code = 2; break;
1965 case VK_DELETE: code = 3; break;
1966 case VK_END: code = 4; break;
1967 case VK_PRIOR: code = 5; break;
1968 case VK_NEXT: code = 6; break;
1970 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1971 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1974 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1975 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1978 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1979 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1983 p += sprintf((char *)p, "\x1B[%d~", code);
1988 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1989 * some reason seems to send VK_CLEAR to Windows...).
1994 case VK_UP: xkey = 'A'; break;
1995 case VK_DOWN: xkey = 'B'; break;
1996 case VK_RIGHT: xkey = 'C'; break;
1997 case VK_LEFT: xkey = 'D'; break;
1998 case VK_CLEAR: xkey = 'G'; break;
2003 p += sprintf((char *)p, "\x1B%c", xkey);
2004 else if (app_cursor_keys)
2005 p += sprintf((char *)p, "\x1BO%c", xkey);
2007 p += sprintf((char *)p, "\x1B[%c", xkey);
2013 /* Okay we've done everything interesting; let windows deal with
2014 * the boring stuff */
2016 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2018 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2019 if(cfg.xlat_capslockcyr)
2020 keystate[VK_CAPITAL] = 0;
2022 r = ToAscii (wParam, scan, keystate, keys, 0);
2028 unsigned char ch = (unsigned char)keys[i];
2030 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2035 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2039 if ((nc=check_compose(compose_char,ch)) == -1)
2044 *p++ = xlat_kbd2tty((unsigned char)nc);
2050 if( left_alt && key_down ) *p++ = '\033';
2056 ch = xlat_latkbd2win(ch);
2057 *p++ = xlat_kbd2tty(ch);
2061 /* This is so the ALT-Numpad and dead keys work correctly. */
2068 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2069 if (message == WM_SYSKEYUP && wParam == VK_MENU)
2071 keystate[VK_MENU] = 0;
2078 void set_title (char *title) {
2079 sfree (window_name);
2080 window_name = smalloc(1+strlen(title));
2081 strcpy (window_name, title);
2082 if (cfg.win_name_always || !IsIconic(hwnd))
2083 SetWindowText (hwnd, title);
2086 void set_icon (char *title) {
2088 icon_name = smalloc(1+strlen(title));
2089 strcpy (icon_name, title);
2090 if (!cfg.win_name_always && IsIconic(hwnd))
2091 SetWindowText (hwnd, title);
2094 void set_sbar (int total, int start, int page) {
2097 if (!cfg.scrollbar) return;
2099 si.cbSize = sizeof(si);
2100 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2102 si.nMax = total - 1;
2106 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2109 Context get_ctx(void) {
2114 SelectPalette (hdc, pal, FALSE);
2120 void free_ctx (Context ctx) {
2121 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2122 ReleaseDC (hwnd, ctx);
2125 static void real_palette_set (int n, int r, int g, int b) {
2127 logpal->palPalEntry[n].peRed = r;
2128 logpal->palPalEntry[n].peGreen = g;
2129 logpal->palPalEntry[n].peBlue = b;
2130 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2131 colours[n] = PALETTERGB(r, g, b);
2132 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2134 colours[n] = RGB(r, g, b);
2137 void palette_set (int n, int r, int g, int b) {
2138 static const int first[21] = {
2139 0, 2, 4, 6, 8, 10, 12, 14,
2140 1, 3, 5, 7, 9, 11, 13, 15,
2143 real_palette_set (first[n], r, g, b);
2145 real_palette_set (first[n]+1, r, g, b);
2147 HDC hdc = get_ctx();
2148 UnrealizeObject (pal);
2149 RealizePalette (hdc);
2154 void palette_reset (void) {
2157 for (i = 0; i < NCOLOURS; i++) {
2159 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2160 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2161 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2162 logpal->palPalEntry[i].peFlags = 0;
2163 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2164 defpal[i].rgbtGreen,
2165 defpal[i].rgbtBlue);
2167 colours[i] = RGB(defpal[i].rgbtRed,
2168 defpal[i].rgbtGreen,
2169 defpal[i].rgbtBlue);
2174 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2176 RealizePalette (hdc);
2181 void write_clip (void *data, int len) {
2185 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2188 lock = GlobalLock (clipdata);
2191 memcpy (lock, data, len);
2192 ((unsigned char *) lock) [len] = 0;
2193 GlobalUnlock (clipdata);
2195 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2196 if (OpenClipboard (hwnd)) {
2198 SetClipboardData (CF_TEXT, clipdata);
2201 GlobalFree (clipdata);
2202 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2205 void get_clip (void **p, int *len) {
2206 static HGLOBAL clipdata = NULL;
2210 GlobalUnlock (clipdata);
2214 if (OpenClipboard (NULL)) {
2215 clipdata = GetClipboardData (CF_TEXT);
2218 *p = GlobalLock (clipdata);
2232 * Move `lines' lines from position `from' to position `to' in the
2235 void optimised_move (int to, int from, int lines) {
2239 min = (to < from ? to : from);
2240 max = to + from - min;
2242 r.left = 0; r.right = cols * font_width;
2243 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2244 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2248 * Print a message box and perform a fatal exit.
2250 void fatalbox(char *fmt, ...) {
2255 vsprintf(stuff, fmt, ap);
2257 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2264 void beep(int errorbeep) {
2265 static long last_beep = 0;
2266 long now, beep_diff;
2268 now = GetTickCount();
2269 beep_diff = now-last_beep;
2271 /* Make sure we only respond to one beep per packet or so */
2272 if (beep_diff>=0 && beep_diff<50)
2276 MessageBeep(MB_ICONHAND);
2280 last_beep = GetTickCount();