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";
156 } else if (q == p + 7 &&
157 tolower(p[0]) == 'c' &&
158 tolower(p[1]) == 'l' &&
159 tolower(p[2]) == 'e' &&
160 tolower(p[3]) == 'a' &&
161 tolower(p[4]) == 'n' &&
162 tolower(p[5]) == 'u' &&
163 tolower(p[6]) == 'p') {
165 * `putty -cleanup'. Remove all registry entries
166 * associated with PuTTY, and also find and delete
167 * the random seed file.
170 "This procedure will remove ALL Registry\n"
171 "entries associated with PuTTY, and will\n"
172 "also remove the PuTTY random seed file.\n"
174 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
175 "SESSIONS. Are you really sure you want\n"
178 MB_YESNO | MB_ICONWARNING) == IDYES) {
179 random_destroy_seed();
184 p = q + strspn(q, " \t");
188 * An initial @ means to activate a saved session.
192 if (!*cfg.host && !do_config()) {
196 } else if (*p == '&') {
198 * An initial & means we've been given a command line
199 * containing the hex value of a HANDLE for a file
200 * mapping object, which we must then extract as a
205 if (sscanf(p+1, "%p", &filemap) == 1 &&
206 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
207 0, 0, sizeof(Config))) != NULL) {
210 CloseHandle(filemap);
211 } else if (!do_config()) {
218 * If the hostname starts with "telnet:", set the
219 * protocol to Telnet and process the string as a
222 if (!strncmp(q, "telnet:", 7)) {
226 if (q[0] == '/' && q[1] == '/')
228 cfg.protocol = PROT_TELNET;
230 while (*p && *p != ':' && *p != '/') p++;
238 strncpy (cfg.host, q, sizeof(cfg.host)-1);
239 cfg.host[sizeof(cfg.host)-1] = '\0';
241 while (*p && !isspace(*p)) p++;
244 strncpy (cfg.host, q, sizeof(cfg.host)-1);
245 cfg.host[sizeof(cfg.host)-1] = '\0';
246 while (*p && isspace(*p)) p++;
259 /* See if host is of the form user@host */
260 if (cfg.host[0] != '\0') {
261 char *atsign = strchr(cfg.host, '@');
262 /* Make sure we're not overflowing the user field */
264 if (atsign-cfg.host < sizeof cfg.username) {
265 strncpy (cfg.username, cfg.host, atsign-cfg.host);
266 cfg.username[atsign-cfg.host] = '\0';
268 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
274 * Select protocol. This is farmed out into a table in a
275 * separate file to enable an ssh-free variant.
280 for (i = 0; backends[i].backend != NULL; i++)
281 if (backends[i].protocol == cfg.protocol) {
282 back = backends[i].backend;
286 MessageBox(NULL, "Unsupported protocol number found",
287 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
293 real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
294 /* To start with, we use the simple line discipline, so we can
295 * type passwords etc without fear of them being echoed... */
296 ldisc = &ldisc_simple;
300 wndclass.lpfnWndProc = WndProc;
301 wndclass.cbClsExtra = 0;
302 wndclass.cbWndExtra = 0;
303 wndclass.hInstance = inst;
304 wndclass.hIcon = LoadIcon (inst,
305 MAKEINTRESOURCE(IDI_MAINICON));
306 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
307 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
308 wndclass.lpszMenuName = NULL;
309 wndclass.lpszClassName = appname;
311 RegisterClass (&wndclass);
316 savelines = cfg.savelines;
322 * Guess some defaults for the window size. This all gets
323 * updated later, so we don't really care too much. However, we
324 * do want the font width/height guesses to correspond to a
325 * large font rather than a small one...
332 term_size (cfg.height, cfg.width, cfg.savelines);
333 guess_width = extra_width + font_width * cols;
334 guess_height = extra_height + font_height * rows;
337 HWND w = GetDesktopWindow();
338 GetWindowRect (w, &r);
339 if (guess_width > r.right - r.left)
340 guess_width = r.right - r.left;
341 if (guess_height > r.bottom - r.top)
342 guess_height = r.bottom - r.top;
346 int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
347 if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
348 if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
349 hwnd = CreateWindow (appname, appname,
351 CW_USEDEFAULT, CW_USEDEFAULT,
352 guess_width, guess_height,
353 NULL, NULL, inst, NULL);
357 * Initialise the fonts, simultaneously correcting the guesses
358 * for font_{width,height}.
360 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
365 * Correct the guesses for extra_{width,height}.
369 GetWindowRect (hwnd, &wr);
370 GetClientRect (hwnd, &cr);
371 extra_width = wr.right - wr.left - cr.right + cr.left;
372 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
376 * Resize the window, now we know what size we _really_ want it
379 guess_width = extra_width + font_width * cols;
380 guess_height = extra_height + font_height * rows;
381 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
382 SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
383 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
386 * Initialise the scroll bar.
391 si.cbSize = sizeof(si);
392 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
397 SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
401 * Start up the telnet connection.
408 error = back->init (hwnd, cfg.host, cfg.port, &realhost);
410 sprintf(msg, "Unable to open connection:\n%s", error);
411 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
414 window_name = icon_name = NULL;
415 sprintf(msg, "%s - PuTTY", realhost);
420 session_closed = FALSE;
423 * Set up the input and output buffers.
426 outbuf_reap = outbuf_head = 0;
429 * Prepare the mouse handler.
431 lastact = MA_NOTHING;
432 lastbtn = MB_NOTHING;
433 dbltime = GetDoubleClickTime();
436 * Set up the session-control options on the system menu.
439 HMENU m = GetSystemMenu (hwnd, FALSE);
443 AppendMenu (m, MF_SEPARATOR, 0, 0);
444 if (cfg.protocol == PROT_TELNET) {
446 AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
447 AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
448 AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
449 AppendMenu (p, MF_SEPARATOR, 0, 0);
450 AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
451 AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
452 AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
453 AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
454 AppendMenu (p, MF_SEPARATOR, 0, 0);
455 AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
456 AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
457 AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
458 AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
459 AppendMenu (p, MF_SEPARATOR, 0, 0);
460 AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
461 AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
462 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
463 AppendMenu (m, MF_SEPARATOR, 0, 0);
465 AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
466 AppendMenu (m, MF_SEPARATOR, 0, 0);
467 AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
468 AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
471 for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
472 AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
473 AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
474 AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
475 AppendMenu (m, MF_SEPARATOR, 0, 0);
476 AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
477 AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
478 AppendMenu (m, MF_SEPARATOR, 0, 0);
479 AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
483 * Finally show the window!
485 ShowWindow (hwnd, show);
488 * Set the palette up.
494 has_focus = (GetForegroundWindow() == hwnd);
498 int timer_id = 0, long_timer = 0;
500 while (GetMessage (&msg, NULL, 0, 0) == 1) {
501 /* Sometimes DispatchMessage calls routines that use their own
502 * GetMessage loop, setup this timer so we get some control back.
504 * Also call term_update() from the timer so that if the host
505 * is sending data flat out we still do redraws.
507 if(timer_id && long_timer) {
508 KillTimer(hwnd, timer_id);
509 long_timer = timer_id = 0;
512 timer_id = SetTimer(hwnd, 1, 20, NULL);
513 DispatchMessage (&msg);
515 /* This is too fast, but I'll leave it for now 'cause it shows
516 * how often term_update is called (far too often at times!)
520 /* Send the paste buffer if there's anything to send */
523 /* If there's nothing new in the queue then we can do everything
524 * we've delayed, reading the socket, writing, and repainting
527 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
528 if (pending_netevent) {
529 enact_pending_netevent();
534 if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
536 KillTimer(hwnd, timer_id);
543 timer_id = SetTimer(hwnd, 1, 2000, NULL);
544 else if (cfg.blinktext)
545 timer_id = SetTimer(hwnd, 1, 250, NULL);
547 timer_id = SetTimer(hwnd, 1, 500, NULL);
560 DeleteObject(fonts[i]);
567 if (cfg.protocol == PROT_SSH) {
578 * Print a message box and close the connection.
580 void connection_fatal(char *fmt, ...) {
585 vsprintf(stuff, fmt, ap);
587 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
588 if (cfg.close_on_exit)
591 session_closed = TRUE;
592 SetWindowText (hwnd, "PuTTY (inactive)");
597 * Actually do the job requested by a WM_NETEVENT
599 static void enact_pending_netevent(void) {
601 static int reentering = 0;
604 return; /* don't unpend the pending */
606 pending_netevent = FALSE;
609 i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
614 switch (WSABASEERR + (-i) % 10000) {
616 sprintf(buf, "Connection reset by peer");
619 sprintf(buf, "Unexpected network error %d", -i);
622 connection_fatal(buf);
625 if (cfg.close_on_exit)
628 session_closed = TRUE;
629 MessageBox(hwnd, "Connection closed by remote host",
630 "PuTTY", MB_OK | MB_ICONINFORMATION);
631 SetWindowText (hwnd, "PuTTY (inactive)");
637 * Copy the colour palette from the configuration data into defpal.
638 * This is non-trivial because the colour indices are different.
640 static void cfgtopalette(void) {
642 static const int ww[] = {
643 6, 7, 8, 9, 10, 11, 12, 13,
644 14, 15, 16, 17, 18, 19, 20, 21,
645 0, 1, 2, 3, 4, 4, 5, 5
648 for (i=0; i<24; i++) {
650 defpal[i].rgbtRed = cfg.colours[w][0];
651 defpal[i].rgbtGreen = cfg.colours[w][1];
652 defpal[i].rgbtBlue = cfg.colours[w][2];
657 * Set up the colour palette.
659 static void init_palette(void) {
661 HDC hdc = GetDC (hwnd);
663 if (cfg.try_palette &&
664 GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
665 logpal = smalloc(sizeof(*logpal)
666 - sizeof(logpal->palPalEntry)
667 + NCOLOURS * sizeof(PALETTEENTRY));
668 logpal->palVersion = 0x300;
669 logpal->palNumEntries = NCOLOURS;
670 for (i = 0; i < NCOLOURS; i++) {
671 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
672 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
673 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
674 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
676 pal = CreatePalette (logpal);
678 SelectPalette (hdc, pal, FALSE);
679 RealizePalette (hdc);
680 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
684 ReleaseDC (hwnd, hdc);
687 for (i=0; i<NCOLOURS; i++)
688 colours[i] = PALETTERGB(defpal[i].rgbtRed,
692 for(i=0; i<NCOLOURS; i++)
693 colours[i] = RGB(defpal[i].rgbtRed,
699 * Initialise all the fonts we will need. There may be as many as
700 * eight or as few as one. We also:
702 * - check the font width and height, correcting our guesses if
705 * - verify that the bold font is the same width as the ordinary
706 * one, and engage shadow bolding if not.
708 * - verify that the underlined font is the same width as the
709 * ordinary one (manual underlining by means of line drawing can
710 * be done in a pinch).
712 static void init_fonts(int pick_width) {
717 int fw_dontcare, fw_bold;
726 if (cfg.fontisbold) {
727 fw_dontcare = FW_BOLD;
730 fw_dontcare = FW_DONTCARE;
736 font_height = cfg.fontheight;
737 font_width = pick_width;
740 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
741 c, OUT_DEFAULT_PRECIS, \
742 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
743 FIXED_PITCH | FF_DONTCARE, cfg.font)
745 if (cfg.vtmode != VT_OEMONLY) {
746 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
748 SelectObject (hdc, fonts[FONT_NORMAL]);
749 GetTextMetrics(hdc, &tm);
750 font_height = tm.tmHeight;
751 font_width = tm.tmAveCharWidth;
753 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
756 * Some fonts, e.g. 9-pt Courier, draw their underlines
757 * outside their character cell. We successfully prevent
758 * screen corruption by clipping the text output, but then
759 * we lose the underline completely. Here we try to work
760 * out whether this is such a font, and if it is, we set a
761 * flag that causes underlines to be drawn by hand.
763 * Having tried other more sophisticated approaches (such
764 * as examining the TEXTMETRIC structure or requesting the
765 * height of a string), I think we'll do this the brute
766 * force way: we create a small bitmap, draw an underlined
767 * space on it, and test to see whether any pixels are
768 * foreground-coloured. (Since we expect the underline to
769 * go all the way across the character cell, we only search
770 * down a single column of the bitmap, half way across.)
774 HBITMAP und_bm, und_oldbm;
778 und_dc = CreateCompatibleDC(hdc);
779 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
780 und_oldbm = SelectObject(und_dc, und_bm);
781 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
782 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
783 SetTextColor (und_dc, RGB(255,255,255));
784 SetBkColor (und_dc, RGB(0,0,0));
785 SetBkMode (und_dc, OPAQUE);
786 ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
788 for (i = 0; i < font_height; i++) {
789 c = GetPixel(und_dc, font_width/2, i);
793 SelectObject(und_dc, und_oldbm);
794 DeleteObject(und_bm);
796 font_needs_hand_underlining = !gotit;
799 if (bold_mode == BOLD_FONT) {
800 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
801 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
804 if (cfg.vtmode == VT_OEMANSI) {
805 f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
806 f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
808 if (bold_mode == BOLD_FONT) {
809 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
810 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
816 f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
818 SelectObject (hdc, fonts[FONT_OEM]);
819 GetTextMetrics(hdc, &tm);
820 font_height = tm.tmHeight;
821 font_width = tm.tmAveCharWidth;
823 f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
825 if (bold_mode == BOLD_FONT) {
826 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
827 f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
832 descent = tm.tmAscent + 1;
833 if (descent >= font_height)
834 descent = font_height - 1;
835 firstchar = tm.tmFirstChar;
837 for (i=0; i<8; i++) {
839 if (SelectObject (hdc, fonts[i]) &&
840 GetTextMetrics(hdc, &tm) )
841 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
847 ReleaseDC (hwnd, hdc);
849 /* ... This is wrong in OEM only mode */
850 if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
851 (bold_mode == BOLD_FONT &&
852 fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
854 DeleteObject (fonts[FONT_UNDERLINE]);
855 if (bold_mode == BOLD_FONT)
856 DeleteObject (fonts[FONT_BOLDUND]);
859 if (bold_mode == BOLD_FONT &&
860 fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
861 bold_mode = BOLD_SHADOW;
862 DeleteObject (fonts[FONT_BOLD]);
863 if (und_mode == UND_FONT)
864 DeleteObject (fonts[FONT_BOLDUND]);
868 /* With the fascist font painting it doesn't matter if the linedraw font
869 * isn't exactly the right size anymore so we don't have to check this.
871 if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
872 if( cfg.fontcharset == OEM_CHARSET )
874 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
875 "different sizes. Using OEM-only mode instead",
876 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
877 cfg.vtmode = VT_OEMONLY;
879 else if( firstchar < ' ' )
881 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
882 "different sizes. Using XTerm mode instead",
883 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
884 cfg.vtmode = VT_XWINDOWS;
888 MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
889 "different sizes. Using ISO8859-1 mode instead",
890 "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
891 cfg.vtmode = VT_POORMAN;
896 DeleteObject (fonts[i]);
902 void request_resize (int w, int h, int refont) {
905 /* If the window is maximized supress resizing attempts */
906 if(IsZoomed(hwnd)) return;
909 /* Don't do this in OEMANSI, you may get disable messages */
910 if (refont && w != cols && (cols==80 || cols==132)
911 && cfg.vtmode != VT_OEMANSI)
913 if (refont && w != cols && (cols==80 || cols==132))
916 /* If font width too big for screen should we shrink the font more ? */
918 font_width = ((font_width*cols+w/2)/w);
925 DeleteObject(fonts[i]);
927 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
929 init_fonts(font_width);
933 static int first_time = 1;
939 /* Get the size of the screen */
940 if (GetClientRect(GetDesktopWindow(),&ss))
941 /* first_time = 0 */;
942 else { first_time = 2; break; }
944 /* Make sure the values are sane */
945 width = (ss.right-ss.left-extra_width ) / font_width;
946 height = (ss.bottom-ss.top-extra_height ) / font_height;
948 if (w>width) w=width;
949 if (h>height) h=height;
955 width = extra_width + font_width * w;
956 height = extra_height + font_height * h;
958 SetWindowPos (hwnd, NULL, 0, 0, width, height,
959 SWP_NOACTIVATE | SWP_NOCOPYBITS |
960 SWP_NOMOVE | SWP_NOZORDER);
963 static void click (Mouse_Button b, int x, int y) {
964 int thistime = GetMessageTime();
966 if (lastbtn == b && thistime - lasttime < dbltime) {
967 lastact = (lastact == MA_CLICK ? MA_2CLK :
968 lastact == MA_2CLK ? MA_3CLK :
969 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
974 if (lastact != MA_NOTHING)
975 term_mouse (b, lastact, x, y);
979 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
980 WPARAM wParam, LPARAM lParam) {
982 static int ignore_size = FALSE;
983 static int ignore_clip = FALSE;
984 static int just_reconfigged = FALSE;
985 static int resizing = FALSE;
989 if (pending_netevent)
990 enact_pending_netevent();
998 if (!cfg.warn_on_close || session_closed ||
999 MessageBox(hwnd, "Are you sure you want to close this session?",
1000 "PuTTY Exit Confirmation",
1001 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1002 DestroyWindow(hwnd);
1005 PostQuitMessage (0);
1008 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1020 PROCESS_INFORMATION pi;
1021 HANDLE filemap = NULL;
1023 if (wParam == IDM_DUPSESS) {
1025 * Allocate a file-mapping memory chunk for the
1028 SECURITY_ATTRIBUTES sa;
1031 sa.nLength = sizeof(sa);
1032 sa.lpSecurityDescriptor = NULL;
1033 sa.bInheritHandle = TRUE;
1034 filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1041 p = (Config *)MapViewOfFile(filemap,
1043 0, 0, sizeof(Config));
1045 *p = cfg; /* structure copy */
1049 sprintf(c, "putty &%p", filemap);
1051 } else if (wParam == IDM_SAVEDSESS) {
1052 char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1053 cl = malloc(16 + strlen(session)); /* 8, but play safe */
1055 cl = NULL; /* not a very important failure mode */
1057 sprintf(cl, "putty @%s", session);
1063 GetModuleFileName (NULL, b, sizeof(b)-1);
1065 si.lpReserved = NULL;
1066 si.lpDesktop = NULL;
1070 si.lpReserved2 = NULL;
1071 CreateProcess (b, cl, NULL, NULL, TRUE,
1072 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1075 CloseHandle(filemap);
1081 if (!do_reconfig(hwnd))
1083 just_reconfigged = TRUE;
1088 DeleteObject(fonts[i]);
1090 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1091 und_mode = UND_FONT;
1094 /* Telnet will change local echo -> remote if the remote asks */
1095 if (cfg.protocol != PROT_TELNET)
1096 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1104 /* Enable or disable the scroll bar, etc */
1106 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1109 if (cfg.scrollbar) nflg |= WS_VSCROLL;
1110 else nflg &= ~WS_VSCROLL;
1112 nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1114 nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1120 SetWindowLong(hwnd, GWL_STYLE, nflg);
1121 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1122 SetWindowPos(hwnd, NULL, 0,0,0,0,
1123 SWP_NOACTIVATE|SWP_NOCOPYBITS|
1124 SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1127 GetWindowRect (hwnd, &wr);
1128 GetClientRect (hwnd, &cr);
1129 extra_width = wr.right - wr.left - cr.right + cr.left;
1130 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1134 term_size(cfg.height, cfg.width, cfg.savelines);
1135 InvalidateRect(hwnd, NULL, TRUE);
1136 SetWindowPos (hwnd, NULL, 0, 0,
1137 extra_width + font_width * cfg.width,
1138 extra_height + font_height * cfg.height,
1139 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1140 SWP_NOMOVE | SWP_NOZORDER);
1141 if (IsIconic(hwnd)) {
1142 SetWindowText (hwnd,
1143 cfg.win_name_always ? window_name : icon_name);
1152 case IDM_TEL_AYT: back->special (TS_AYT); break;
1153 case IDM_TEL_BRK: back->special (TS_BRK); break;
1154 case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1155 case IDM_TEL_EC: back->special (TS_EC); break;
1156 case IDM_TEL_EL: back->special (TS_EL); break;
1157 case IDM_TEL_GA: back->special (TS_GA); break;
1158 case IDM_TEL_NOP: back->special (TS_NOP); break;
1159 case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1160 case IDM_TEL_AO: back->special (TS_AO); break;
1161 case IDM_TEL_IP: back->special (TS_IP); break;
1162 case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1163 case IDM_TEL_EOR: back->special (TS_EOR); break;
1164 case IDM_TEL_EOF: back->special (TS_EOF); break;
1169 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1170 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1175 #define X_POS(l) ((int)(short)LOWORD(l))
1176 #define Y_POS(l) ((int)(short)HIWORD(l))
1178 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1179 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1181 case WM_LBUTTONDOWN:
1182 click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1183 TO_CHR_Y(Y_POS(lParam)));
1187 term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1188 TO_CHR_Y(Y_POS(lParam)));
1191 case WM_MBUTTONDOWN:
1193 click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1194 TO_CHR_X(X_POS(lParam)),
1195 TO_CHR_Y(Y_POS(lParam)));
1198 term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1199 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1200 TO_CHR_Y(Y_POS(lParam)));
1203 case WM_RBUTTONDOWN:
1205 click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1206 TO_CHR_X(X_POS(lParam)),
1207 TO_CHR_Y(Y_POS(lParam)));
1210 term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1211 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1212 TO_CHR_Y(Y_POS(lParam)));
1217 * Add the mouse position and message time to the random
1218 * number noise, if we're using ssh.
1220 if (cfg.protocol == PROT_SSH)
1221 noise_ultralight(lParam);
1223 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1225 if (wParam & MK_LBUTTON)
1227 else if (wParam & MK_MBUTTON)
1228 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1230 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1231 term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1232 TO_CHR_Y(Y_POS(lParam)));
1235 case WM_IGNORE_CLIP:
1236 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1238 case WM_DESTROYCLIPBOARD:
1241 ignore_clip = FALSE;
1246 hdc = BeginPaint (hwnd, &p);
1248 SelectPalette (hdc, pal, TRUE);
1249 RealizePalette (hdc);
1251 term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1252 p.rcPaint.right, p.rcPaint.bottom);
1253 SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1254 SelectObject (hdc, GetStockObject(WHITE_PEN));
1255 EndPaint (hwnd, &p);
1259 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1260 * but the only one that's likely to try to overload us is FD_READ.
1261 * This means buffering just one is fine.
1263 if (pending_netevent)
1264 enact_pending_netevent();
1266 pending_netevent = TRUE;
1267 pend_netevent_wParam=wParam;
1268 pend_netevent_lParam=lParam;
1280 case WM_IGNORE_SIZE:
1281 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1283 case WM_ENTERSIZEMOVE:
1287 case WM_EXITSIZEMOVE:
1294 int width, height, w, h, ew, eh;
1295 LPRECT r = (LPRECT)lParam;
1297 width = r->right - r->left - extra_width;
1298 height = r->bottom - r->top - extra_height;
1299 w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1300 h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1301 UpdateSizeTip(hwnd, w, h);
1302 ew = width - w * font_width;
1303 eh = height - h * font_height;
1305 if (wParam == WMSZ_LEFT ||
1306 wParam == WMSZ_BOTTOMLEFT ||
1307 wParam == WMSZ_TOPLEFT)
1313 if (wParam == WMSZ_TOP ||
1314 wParam == WMSZ_TOPRIGHT ||
1315 wParam == WMSZ_TOPLEFT)
1325 /* break; (never reached) */
1327 if (wParam == SIZE_MINIMIZED) {
1328 SetWindowText (hwnd,
1329 cfg.win_name_always ? window_name : icon_name);
1332 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1333 SetWindowText (hwnd, window_name);
1335 int width, height, w, h;
1336 #if 0 /* we have fixed this using WM_SIZING now */
1340 width = LOWORD(lParam);
1341 height = HIWORD(lParam);
1342 w = width / font_width; if (w < 1) w = 1;
1343 h = height / font_height; if (h < 1) h = 1;
1344 #if 0 /* we have fixed this using WM_SIZING now */
1345 ew = width - w * font_width;
1346 eh = height - h * font_height;
1347 if (ew != 0 || eh != 0) {
1349 GetWindowRect (hwnd, &r);
1350 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1351 SetWindowPos (hwnd, NULL, 0, 0,
1352 r.right - r.left - ew, r.bottom - r.top - eh,
1353 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1356 if (w != cols || h != rows || just_reconfigged) {
1358 term_size (h, w, cfg.savelines);
1360 * Don't call back->size in mid-resize. (To prevent
1361 * massive numbers of resize events getting sent
1362 * down the connection during an NT opaque drag.)
1366 just_reconfigged = FALSE;
1369 ignore_size = FALSE;
1372 switch (LOWORD(wParam)) {
1373 case SB_BOTTOM: term_scroll(-1, 0); break;
1374 case SB_TOP: term_scroll(+1, 0); break;
1375 case SB_LINEDOWN: term_scroll (0, +1); break;
1376 case SB_LINEUP: term_scroll (0, -1); break;
1377 case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1378 case SB_PAGEUP: term_scroll (0, -rows/2); break;
1379 case SB_THUMBPOSITION: case SB_THUMBTRACK:
1380 term_scroll (1, HIWORD(wParam)); break;
1383 case WM_PALETTECHANGED:
1384 if ((HWND) wParam != hwnd && pal != NULL) {
1385 HDC hdc = get_ctx();
1387 if (RealizePalette (hdc) > 0)
1393 case WM_QUERYNEWPALETTE:
1395 HDC hdc = get_ctx();
1397 if (RealizePalette (hdc) > 0)
1409 * Add the scan code and keypress timing to the random
1410 * number noise, if we're using ssh.
1412 if (cfg.protocol == PROT_SSH)
1413 noise_ultralight(lParam);
1416 * We don't do TranslateMessage since it disassociates the
1417 * resulting CHAR message from the KEYDOWN that sparked it,
1418 * which we occasionally don't want. Instead, we process
1419 * KEYDOWN, and call the Win32 translator functions so that
1420 * we get the translations under _our_ control.
1423 unsigned char buf[20];
1426 len = TranslateKey (message, wParam, lParam, buf);
1428 return DefWindowProc (hwnd, message, wParam, lParam);
1429 ldisc->send (buf, len);
1435 * Nevertheless, we are prepared to deal with WM_CHAR
1436 * messages, should they crop up. So if someone wants to
1437 * post the things to us as part of a macro manoeuvre,
1438 * we're ready to cope.
1441 char c = xlat_kbd2tty((unsigned char)wParam);
1442 ldisc->send (&c, 1);
1447 return DefWindowProc (hwnd, message, wParam, lParam);
1451 * Draw a line of text in the window, at given character
1452 * coordinates, in given attributes.
1454 * We are allowed to fiddle with the contents of `text'.
1456 void do_text (Context ctx, int x, int y, char *text, int len,
1457 unsigned long attr, int lattr) {
1459 int nfg, nbg, nfont;
1462 int force_manual_underline = 0;
1463 int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1464 static int *IpDx = 0, IpDxLEN = 0;;
1466 if (len>IpDxLEN || IpDx[0] != fnt_width) {
1470 IpDx = smalloc((len+16)*sizeof(int));
1473 for(i=0; i<IpDxLEN; i++)
1474 IpDx[i] = fnt_width;
1480 if (attr & ATTR_ACTCURS) {
1481 attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1482 attr ^= ATTR_CUR_XOR;
1486 if (cfg.vtmode == VT_OEMONLY)
1490 * Map high-half characters in order to approximate ISO using
1491 * OEM character set. No characters are missing if the OEM codepage
1494 if (nfont & FONT_OEM) {
1496 for (i=0; i<len; i++)
1497 if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1499 /* This is CP850 ... perfect translation */
1500 static const char oemhighhalf[] =
1501 "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1502 "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1503 "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1504 "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1505 "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1506 "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1507 "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1508 "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1509 "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1510 "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1511 "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1512 "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1515 /* This is CP437 ... junk translation */
1516 static const unsigned char oemhighhalf[] = {
1517 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1518 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1519 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1520 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1521 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1522 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1523 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1524 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1525 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1526 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1527 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1528 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1531 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1535 if (attr & ATTR_GBCHR) {
1538 * GB mapping: map # to pound, and everything else stays
1541 for (i=0; i<len; i++)
1543 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1544 } else if (attr & ATTR_LINEDRW) {
1547 static const char poorman[] =
1548 "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1551 static const char oemmap_437[] =
1552 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1553 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1556 static const char oemmap_850[] =
1557 "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1558 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1560 /* Poor windows font ... eg: windows courier */
1561 static const char oemmap[] =
1562 "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1563 "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1566 * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1567 * VT100 line drawing chars; everything else stays normal.
1569 switch (cfg.vtmode) {
1571 for (i=0; i<len; i++)
1572 if (text[i] >= '\x60' && text[i] <= '\x7E')
1573 text[i] += '\x01' - '\x60';
1576 /* Make sure we actually have an OEM font */
1577 if (fonts[nfont|FONT_OEM]) {
1580 for (i=0; i<len; i++)
1581 if (text[i] >= '\x60' && text[i] <= '\x7E')
1582 text[i] = oemmap[(unsigned char)text[i] - 0x60];
1586 for (i=0; i<len; i++)
1587 if (text[i] >= '\x60' && text[i] <= '\x7E')
1588 text[i] = poorman[(unsigned char)text[i] - 0x60];
1593 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1594 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1595 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1597 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1598 nfont |= FONT_UNDERLINE;
1601 if (nfont&FONT_UNDERLINE)
1602 force_manual_underline = 1;
1603 /* Don't do the same for manual bold, it could be bad news. */
1605 nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1607 if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1608 force_manual_underline = 1;
1609 if (attr & ATTR_REVERSE) {
1610 t = nfg; nfg = nbg; nbg = t;
1612 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1614 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1618 SelectObject (hdc, fonts[nfont]);
1619 SetTextColor (hdc, fg);
1620 SetBkColor (hdc, bg);
1621 SetBkMode (hdc, OPAQUE);
1624 line_box.right = x+fnt_width*len;
1625 line_box.bottom = y+font_height;
1626 ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1627 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1628 SetBkMode (hdc, TRANSPARENT);
1630 /* GRR: This draws the character outside it's box and can leave
1631 * 'droppings' even with the clip box! I suppose I could loop it
1632 * one character at a time ... yuk.
1634 * Or ... I could do a test print with "W", and use +1 or -1 for this
1635 * shift depending on if the leftmost column is blank...
1637 ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1639 if (force_manual_underline ||
1640 (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1642 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1643 MoveToEx (hdc, x, y+descent, NULL);
1644 LineTo (hdc, x+len*fnt_width, y+descent);
1645 oldpen = SelectObject (hdc, oldpen);
1646 DeleteObject (oldpen);
1648 if (attr & ATTR_PASCURS) {
1651 pts[0].x = pts[1].x = pts[4].x = x;
1652 pts[2].x = pts[3].x = x+fnt_width-1;
1653 pts[0].y = pts[3].y = pts[4].y = y;
1654 pts[1].y = pts[2].y = y+font_height-1;
1655 oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1656 Polyline (hdc, pts, 5);
1657 oldpen = SelectObject (hdc, oldpen);
1658 DeleteObject (oldpen);
1662 static int check_compose(int first, int second) {
1664 static char * composetbl[] = {
1665 "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1666 "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1667 "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--", "RO®",
1668 "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1669 "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1670 "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1671 "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1672 "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1673 "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1674 "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1675 "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1676 "\"uü", "'yý", "htþ", "\"yÿ",
1680 static int recurse = 0;
1687 sprintf(buf, "cc(%d,%d)", first, second);
1692 for(c=composetbl; *c; c++) {
1693 if( (*c)[0] == first && (*c)[1] == second)
1695 return (*c)[2] & 0xFF;
1702 nc = check_compose(second, first);
1704 nc = check_compose(toupper(first), toupper(second));
1706 nc = check_compose(toupper(second), toupper(first));
1714 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1715 * codes. Returns number of bytes used or zero to drop the message
1716 * or -1 to forward the message to windows.
1718 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1720 int scan, left_alt = 0, key_down, shift_state;
1722 unsigned char * p = output;
1724 static WORD keys[3];
1725 static int compose_state = 0;
1726 static int compose_char = 0;
1727 static WPARAM compose_key = 0;
1729 r = GetKeyboardState(keystate);
1730 if (!r) memset(keystate, 0, sizeof(keystate));
1733 /* Note if AltGr was pressed and if it was used as a compose key */
1734 if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1736 keystate[VK_RMENU] = keystate[VK_MENU];
1737 if (!compose_state) compose_key = wParam;
1739 if (wParam == VK_APPS && !compose_state)
1740 compose_key = wParam;
1742 if (wParam == compose_key)
1744 if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1746 else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1751 else if (compose_state==1 && wParam != VK_CONTROL)
1754 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1755 if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1756 && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1758 wParam = VK_EXECUTE;
1760 /* UnToggle NUMLock */
1761 if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1762 keystate[VK_NUMLOCK] ^= 1;
1765 /* And write back the 'adjusted' state */
1766 SetKeyboardState (keystate);
1769 /* Disable Auto repeat if required */
1770 if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1773 if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1776 key_down = ((HIWORD(lParam)&KF_UP)==0);
1778 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1779 if (left_alt && (keystate[VK_CONTROL]&0x80))
1780 keystate[VK_MENU] = 0;
1782 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1783 shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1784 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1787 * Record that we pressed key so the scroll window can be reset, but
1788 * be careful to avoid Shift-UP/Down
1790 if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1794 /* Make sure we're not pasting */
1795 if (key_down) term_nopaste();
1797 if (compose_state>1 && left_alt) compose_state = 0;
1799 /* Sanitize the number pad if not using a PC NumPad */
1800 if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1801 || cfg.nethack_keypad || compose_state )
1803 if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1808 case VK_INSERT: nParam = VK_NUMPAD0; break;
1809 case VK_END: nParam = VK_NUMPAD1; break;
1810 case VK_DOWN: nParam = VK_NUMPAD2; break;
1811 case VK_NEXT: nParam = VK_NUMPAD3; break;
1812 case VK_LEFT: nParam = VK_NUMPAD4; break;
1813 case VK_CLEAR: nParam = VK_NUMPAD5; break;
1814 case VK_RIGHT: nParam = VK_NUMPAD6; break;
1815 case VK_HOME: nParam = VK_NUMPAD7; break;
1816 case VK_UP: nParam = VK_NUMPAD8; break;
1817 case VK_PRIOR: nParam = VK_NUMPAD9; break;
1818 case VK_DELETE: nParam = VK_DECIMAL; break;
1822 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1828 /* If a key is pressed and AltGr is not active */
1829 if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1831 /* Okay, prepare for most alts then ...*/
1832 if (left_alt) *p++ = '\033';
1834 /* Lets see if it's a pattern we know all about ... */
1835 if (wParam == VK_PRIOR && shift_state == 1) {
1836 SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1839 if (wParam == VK_NEXT && shift_state == 1) {
1840 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1843 if (wParam == VK_INSERT && shift_state == 1) {
1844 term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1845 term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1848 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1851 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1853 SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1857 /* Nethack keypad */
1858 if (cfg.nethack_keypad && !left_alt) {
1860 case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1861 case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1862 case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1863 case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1864 case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1865 case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1866 case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1867 case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1868 case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1872 /* Application Keypad */
1876 if ( cfg.funky_type == 0 ||
1877 ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1878 case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1879 case VK_DIVIDE: xkey = 'Q'; break;
1880 case VK_MULTIPLY:xkey = 'R'; break;
1881 case VK_SUBTRACT:xkey = 'S'; break;
1883 if(app_keypad_keys) switch(wParam) {
1884 case VK_NUMPAD0: xkey = 'p'; break;
1885 case VK_NUMPAD1: xkey = 'q'; break;
1886 case VK_NUMPAD2: xkey = 'r'; break;
1887 case VK_NUMPAD3: xkey = 's'; break;
1888 case VK_NUMPAD4: xkey = 't'; break;
1889 case VK_NUMPAD5: xkey = 'u'; break;
1890 case VK_NUMPAD6: xkey = 'v'; break;
1891 case VK_NUMPAD7: xkey = 'w'; break;
1892 case VK_NUMPAD8: xkey = 'x'; break;
1893 case VK_NUMPAD9: xkey = 'y'; break;
1895 case VK_DECIMAL: xkey = 'n'; break;
1896 case VK_ADD: if(shift_state) xkey = 'm';
1900 if (HIWORD(lParam)&KF_EXTENDED)
1908 if (xkey>='P' && xkey<='S')
1909 p += sprintf((char *)p, "\x1B%c", xkey);
1911 p += sprintf((char *)p, "\x1B?%c", xkey);
1914 p += sprintf((char *)p, "\x1BO%c", xkey);
1919 if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */
1921 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1924 if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */
1926 *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1928 if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */
1930 *p++ = 0; return p - output;
1932 if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */
1934 *p++ = 160; return p - output;
1936 if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */
1938 *p++ = 3; return p - output;
1940 /* Control-2 to Control-8 are special */
1941 if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1943 *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1946 if (shift_state == 2 && wParam == 0xBD) {
1950 if (shift_state == 2 && wParam == 0xDF) {
1954 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1955 *p++ = '\r'; *p++ = '\n';
1960 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1961 * for integer decimal nn.)
1963 * We also deal with the weird ones here. Linux VCs replace F1
1964 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1965 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1970 case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1971 case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1972 case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1973 case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1974 case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1975 case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1976 case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1977 case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1978 case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1979 case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1980 case VK_F11: code = 23; break;
1981 case VK_F12: code = 24; break;
1982 case VK_F13: code = 25; break;
1983 case VK_F14: code = 26; break;
1984 case VK_F15: code = 28; break;
1985 case VK_F16: code = 29; break;
1986 case VK_F17: code = 31; break;
1987 case VK_F18: code = 32; break;
1988 case VK_F19: code = 33; break;
1989 case VK_F20: code = 34; break;
1990 case VK_HOME: code = 1; break;
1991 case VK_INSERT: code = 2; break;
1992 case VK_DELETE: code = 3; break;
1993 case VK_END: code = 4; break;
1994 case VK_PRIOR: code = 5; break;
1995 case VK_NEXT: code = 6; break;
1997 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1998 p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2001 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2002 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2005 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2006 p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2010 p += sprintf((char *)p, "\x1B[%d~", code);
2015 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2016 * some reason seems to send VK_CLEAR to Windows...).
2021 case VK_UP: xkey = 'A'; break;
2022 case VK_DOWN: xkey = 'B'; break;
2023 case VK_RIGHT: xkey = 'C'; break;
2024 case VK_LEFT: xkey = 'D'; break;
2025 case VK_CLEAR: xkey = 'G'; break;
2030 p += sprintf((char *)p, "\x1B%c", xkey);
2031 else if (app_cursor_keys)
2032 p += sprintf((char *)p, "\x1BO%c", xkey);
2034 p += sprintf((char *)p, "\x1B[%c", xkey);
2040 /* Okay we've done everything interesting; let windows deal with
2041 * the boring stuff */
2043 BOOL capsOn=keystate[VK_CAPITAL] !=0;
2045 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2046 if(cfg.xlat_capslockcyr)
2047 keystate[VK_CAPITAL] = 0;
2049 r = ToAscii (wParam, scan, keystate, keys, 0);
2055 unsigned char ch = (unsigned char)keys[i];
2057 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2062 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2066 if ((nc=check_compose(compose_char,ch)) == -1)
2071 *p++ = xlat_kbd2tty((unsigned char)nc);
2077 if( left_alt && key_down ) *p++ = '\033';
2083 ch = xlat_latkbd2win(ch);
2084 *p++ = xlat_kbd2tty(ch);
2088 /* This is so the ALT-Numpad and dead keys work correctly. */
2095 /* This stops ALT press-release doing a 'COMMAND MENU' function */
2096 if (message == WM_SYSKEYUP && wParam == VK_MENU)
2098 keystate[VK_MENU] = 0;
2105 void set_title (char *title) {
2106 sfree (window_name);
2107 window_name = smalloc(1+strlen(title));
2108 strcpy (window_name, title);
2109 if (cfg.win_name_always || !IsIconic(hwnd))
2110 SetWindowText (hwnd, title);
2113 void set_icon (char *title) {
2115 icon_name = smalloc(1+strlen(title));
2116 strcpy (icon_name, title);
2117 if (!cfg.win_name_always && IsIconic(hwnd))
2118 SetWindowText (hwnd, title);
2121 void set_sbar (int total, int start, int page) {
2124 if (!cfg.scrollbar) return;
2126 si.cbSize = sizeof(si);
2127 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2129 si.nMax = total - 1;
2133 SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2136 Context get_ctx(void) {
2141 SelectPalette (hdc, pal, FALSE);
2147 void free_ctx (Context ctx) {
2148 SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2149 ReleaseDC (hwnd, ctx);
2152 static void real_palette_set (int n, int r, int g, int b) {
2154 logpal->palPalEntry[n].peRed = r;
2155 logpal->palPalEntry[n].peGreen = g;
2156 logpal->palPalEntry[n].peBlue = b;
2157 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2158 colours[n] = PALETTERGB(r, g, b);
2159 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2161 colours[n] = RGB(r, g, b);
2164 void palette_set (int n, int r, int g, int b) {
2165 static const int first[21] = {
2166 0, 2, 4, 6, 8, 10, 12, 14,
2167 1, 3, 5, 7, 9, 11, 13, 15,
2170 real_palette_set (first[n], r, g, b);
2172 real_palette_set (first[n]+1, r, g, b);
2174 HDC hdc = get_ctx();
2175 UnrealizeObject (pal);
2176 RealizePalette (hdc);
2181 void palette_reset (void) {
2184 for (i = 0; i < NCOLOURS; i++) {
2186 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2187 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2188 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2189 logpal->palPalEntry[i].peFlags = 0;
2190 colours[i] = PALETTERGB(defpal[i].rgbtRed,
2191 defpal[i].rgbtGreen,
2192 defpal[i].rgbtBlue);
2194 colours[i] = RGB(defpal[i].rgbtRed,
2195 defpal[i].rgbtGreen,
2196 defpal[i].rgbtBlue);
2201 SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2203 RealizePalette (hdc);
2208 void write_clip (void *data, int len) {
2212 clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2215 lock = GlobalLock (clipdata);
2218 memcpy (lock, data, len);
2219 ((unsigned char *) lock) [len] = 0;
2220 GlobalUnlock (clipdata);
2222 SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2223 if (OpenClipboard (hwnd)) {
2225 SetClipboardData (CF_TEXT, clipdata);
2228 GlobalFree (clipdata);
2229 SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2232 void get_clip (void **p, int *len) {
2233 static HGLOBAL clipdata = NULL;
2237 GlobalUnlock (clipdata);
2241 if (OpenClipboard (NULL)) {
2242 clipdata = GetClipboardData (CF_TEXT);
2245 *p = GlobalLock (clipdata);
2259 * Move `lines' lines from position `from' to position `to' in the
2262 void optimised_move (int to, int from, int lines) {
2266 min = (to < from ? to : from);
2267 max = to + from - min;
2269 r.left = 0; r.right = cols * font_width;
2270 r.top = min * font_height; r.bottom = (max+lines) * font_height;
2271 ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2275 * Print a message box and perform a fatal exit.
2277 void fatalbox(char *fmt, ...) {
2282 vsprintf(stuff, fmt, ap);
2284 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2291 void beep(int errorbeep) {
2292 static long last_beep = 0;
2293 long now, beep_diff;
2295 now = GetTickCount();
2296 beep_diff = now-last_beep;
2298 /* Make sure we only respond to one beep per packet or so */
2299 if (beep_diff>=0 && beep_diff<50)
2303 MessageBeep(MB_ICONHAND);
2307 last_beep = GetTickCount();