X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/1a6f78fe2deea81bff8c2d14d6078a51e0c57b60..ab21de779596591471529758bbf6d54b3176ea29:/window.c diff --git a/window.c b/window.c index 829abe79..aa6fda5d 100644 --- a/window.c +++ b/window.c @@ -3,43 +3,53 @@ #include #include #include +#include #define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "win_res.h" -#define IDM_SHOWLOG 501 -#define IDM_NEWSESS 502 -#define IDM_DUPSESS 503 -#define IDM_RECONF 504 -#define IDM_CLRSB 505 -#define IDM_RESET 506 -#define IDM_TEL_AYT 507 -#define IDM_TEL_BRK 508 -#define IDM_TEL_SYNCH 509 -#define IDM_TEL_EC 510 -#define IDM_TEL_EL 511 -#define IDM_TEL_GA 512 -#define IDM_TEL_NOP 513 -#define IDM_TEL_ABORT 514 -#define IDM_TEL_AO 515 -#define IDM_TEL_IP 516 -#define IDM_TEL_SUSP 517 -#define IDM_TEL_EOR 518 -#define IDM_TEL_EOF 519 -#define IDM_ABOUT 520 - -#define WM_IGNORE_SIZE (WM_USER + 2) -#define WM_IGNORE_CLIP (WM_USER + 3) - -static int WINAPI WndProc (HWND, UINT, WPARAM, LPARAM); +#define IDM_SHOWLOG 0x0010 +#define IDM_NEWSESS 0x0020 +#define IDM_DUPSESS 0x0030 +#define IDM_RECONF 0x0040 +#define IDM_CLRSB 0x0050 +#define IDM_RESET 0x0060 +#define IDM_TEL_AYT 0x0070 +#define IDM_TEL_BRK 0x0080 +#define IDM_TEL_SYNCH 0x0090 +#define IDM_TEL_EC 0x00a0 +#define IDM_TEL_EL 0x00b0 +#define IDM_TEL_GA 0x00c0 +#define IDM_TEL_NOP 0x00d0 +#define IDM_TEL_ABORT 0x00e0 +#define IDM_TEL_AO 0x00f0 +#define IDM_TEL_IP 0x0100 +#define IDM_TEL_SUSP 0x0110 +#define IDM_TEL_EOR 0x0120 +#define IDM_TEL_EOF 0x0130 +#define IDM_ABOUT 0x0140 +#define IDM_SAVEDSESS 0x0150 + +#define IDM_SAVED_MIN 0x1000 +#define IDM_SAVED_MAX 0x2000 + +#define WM_IGNORE_SIZE (WM_XUSER + 1) +#define WM_IGNORE_CLIP (WM_XUSER + 2) + +static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output); static void cfgtopalette(void); static void init_palette(void); -static void init_fonts(void); +static void init_fonts(int); static int extra_width, extra_height; +static int pending_netevent = 0; +static WPARAM pend_netevent_wParam = 0; +static LPARAM pend_netevent_lParam = 0; +static void enact_pending_netevent(void); + #define FONT_NORMAL 0 #define FONT_BOLD 1 #define FONT_UNDERLINE 2 @@ -78,6 +88,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; int guess_width, guess_height; + putty_inst = inst; + winsock_ver = MAKEWORD(1, 1); if (WSAStartup(winsock_ver, &wsadata)) { MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error", @@ -100,12 +112,38 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { { char *p; + default_protocol = DEFAULT_PROTOCOL; + default_port = DEFAULT_PORT; + do_defaults(NULL); p = cmdline; while (*p && isspace(*p)) p++; /* + * Process command line options first. Yes, this can be + * done better, and it will be as soon as I have the + * energy... + */ + while (*p == '-') { + char *q = p + strcspn(p, " \t"); + p++; + if (q == p + 3 && + tolower(p[0]) == 's' && + tolower(p[1]) == 's' && + tolower(p[2]) == 'h') { + default_protocol = cfg.protocol = PROT_SSH; + default_port = cfg.port = 22; + } else if (q == p + 3 && + tolower(p[0]) == 'l' && + tolower(p[1]) == 'o' && + tolower(p[2]) == 'g') { + logfile = "putty.log"; + } + p = q + strspn(q, " \t"); + } + + /* * An initial @ means to activate a saved session. */ if (*p == '@') { @@ -123,7 +161,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { */ HANDLE filemap; Config *cp; - if (sscanf(p+1, "%x", &filemap) == 1 && + if (sscanf(p+1, "%p", &filemap) == 1 && (cp = MapViewOfFile(filemap, FILE_MAP_READ, 0, 0, sizeof(Config))) != NULL) { cfg = *cp; @@ -135,16 +173,39 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } } else if (*p) { char *q = p; - while (*p && !isspace(*p)) p++; - if (*p) - *p++ = '\0'; - strncpy (cfg.host, q, sizeof(cfg.host)-1); - cfg.host[sizeof(cfg.host)-1] = '\0'; - while (*p && isspace(*p)) p++; - if (*p) - cfg.port = atoi(p); - else - cfg.port = -1; + /* + * If the hostname starts with "telnet://", set the + * protocol to Telnet and process the string as a + * Telnet URL. + */ + if (!strncmp(q, "telnet://", 9)) { + char c; + + q += 9; + cfg.protocol = PROT_TELNET; + p = q; + while (*p && *p != ':' && *p != '/') p++; + c = *p; + if (*p) + *p++ = '\0'; + if (c == ':') + cfg.port = atoi(p); + else + cfg.port = -1; + strncpy (cfg.host, q, sizeof(cfg.host)-1); + cfg.host[sizeof(cfg.host)-1] = '\0'; + } else { + while (*p && !isspace(*p)) p++; + if (*p) + *p++ = '\0'; + strncpy (cfg.host, q, sizeof(cfg.host)-1); + cfg.host[sizeof(cfg.host)-1] = '\0'; + while (*p && isspace(*p)) p++; + if (*p) + cfg.port = atoi(p); + else + cfg.port = -1; + } } else { if (!do_config()) { WSACleanup(); @@ -153,7 +214,27 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } } - back = (cfg.protocol == PROT_SSH ? &ssh_backend : &telnet_backend); + /* + * Select protocol. This is farmed out into a table in a + * separate file to enable an ssh-free variant. + */ + { + int i; + back = NULL; + for (i = 0; backends[i].backend != NULL; i++) + if (backends[i].protocol == cfg.protocol) { + back = backends[i].backend; + break; + } + if (back == NULL) { + MessageBox(NULL, "Unsupported protocol number found", + "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION); + WSACleanup(); + return 1; + } + } + + ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple); if (!prev) { wndclass.style = 0; @@ -163,7 +244,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { wndclass.hInstance = inst; wndclass.hIcon = LoadIcon (inst, MAKEINTRESOURCE(IDI_MAINICON)); - wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM); wndclass.hbrBackground = GetStockObject (BLACK_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = appname; @@ -214,7 +295,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { */ bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; und_mode = UND_FONT; - init_fonts(); + init_fonts(0); /* * Correct the guesses for extra_{width,height}. @@ -267,17 +348,24 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { return 0; } window_name = icon_name = NULL; - sprintf(msg, "PuTTY: %s", realhost); + sprintf(msg, "%s - PuTTY", realhost); set_title (msg); set_icon (msg); } + session_closed = FALSE; + /* * Set up the input and output buffers. */ inbuf_reap = inbuf_head = 0; outbuf_reap = outbuf_head = 0; + /* + * Choose unscroll method + */ + unscroll_event = US_DISP; + /* * Prepare the mouse handler. */ @@ -290,7 +378,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { */ { HMENU m = GetSystemMenu (hwnd, FALSE); - HMENU p; + HMENU p,s; + int i; AppendMenu (m, MF_SEPARATOR, 0, 0); if (cfg.protocol == PROT_TELNET) { @@ -312,17 +401,23 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record"); AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File"); AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command"); - AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Show Negotiation"); AppendMenu (m, MF_SEPARATOR, 0, 0); } - AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "New Session"); - AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "Duplicate Session"); - AppendMenu (m, MF_ENABLED, IDM_RECONF, "Change Settings"); + AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log"); AppendMenu (m, MF_SEPARATOR, 0, 0); - AppendMenu (m, MF_ENABLED, IDM_CLRSB, "Clear Scrollback"); - AppendMenu (m, MF_ENABLED, IDM_RESET, "Reset Terminal"); + AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session"); + AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session"); + s = CreateMenu(); + get_sesslist(TRUE); + for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++) + AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]); + AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions"); + AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings"); AppendMenu (m, MF_SEPARATOR, 0, 0); - AppendMenu (m, MF_ENABLED, IDM_ABOUT, "About PuTTY"); + AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback"); + AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal"); + AppendMenu (m, MF_SEPARATOR, 0, 0); + AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY"); } /* @@ -340,13 +435,52 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { has_focus = (GetForegroundWindow() == hwnd); UpdateWindow (hwnd); - while (GetMessage (&msg, NULL, 0, 0)) { - DispatchMessage (&msg); - if (inbuf_reap != inbuf_head) - term_out(); - /* In idle moments, do a full screen update */ - if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) - term_update(); + { + int timer_id = 0, long_timer = 0; + + while (GetMessage (&msg, NULL, 0, 0) == 1) { + /* Sometimes DispatchMessage calls routines that use their own + * GetMessage loop, setup this timer so we get some control back. + * + * Also call term_update() from the timer so that if the host + * is sending data flat out we still do redraws. + */ + if(timer_id && long_timer) { + KillTimer(hwnd, timer_id); + long_timer = timer_id = 0; + } + if(!timer_id) + timer_id = SetTimer(hwnd, 1, 20, NULL); + DispatchMessage (&msg); + + /* This is too fast, but I'll leave it for now 'cause it shows + * how often term_update is called (far too often at times!) + */ + term_blink(0); + + /* If there's nothing new in the queue then we can do everything + * we've delayed, reading the socket, writing, and repainting + * the window. + */ + if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (pending_netevent) { + enact_pending_netevent(); + + term_blink(1); + } + } else continue; + if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (timer_id) { + KillTimer(hwnd, timer_id); + timer_id = 0; + } + if (inbuf_reap != inbuf_head) + term_out(); + term_update(); + timer_id = SetTimer(hwnd, 1, 500, NULL); + long_timer = 1; + } + } } /* @@ -363,13 +497,50 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { DeleteObject(pal); WSACleanup(); - if (cfg.protocol == PROT_SSH) + if (cfg.protocol == PROT_SSH) { random_save_seed(); +#ifdef MSCRYPTOAPI + crypto_wrapup(); +#endif + } return msg.wParam; } /* + * Actually do the job requested by a WM_NETEVENT + */ +static void enact_pending_netevent(void) { + int i; + pending_netevent = FALSE; + i = back->msg (pend_netevent_wParam, pend_netevent_lParam); + + if (i < 0) { + char buf[1024]; + switch (WSABASEERR + (-i) % 10000) { + case WSAECONNRESET: + sprintf(buf, "Connection reset by peer"); + break; + default: + sprintf(buf, "Unexpected network error %d", -i); + break; + } + MessageBox(hwnd, buf, "PuTTY Fatal Error", + MB_ICONERROR | MB_OK); + PostQuitMessage(1); + } else if (i == 0) { + if (cfg.close_on_exit) + PostQuitMessage(0); + else { + session_closed = TRUE; + MessageBox(hwnd, "Connection closed by remote host", + "PuTTY", MB_OK | MB_ICONINFORMATION); + SetWindowText (hwnd, "PuTTY (inactive)"); + } + } +} + +/* * Copy the colour palette from the configuration data into defpal. * This is non-trivial because the colour indices are different. */ @@ -444,17 +615,18 @@ static void init_palette(void) { * - verify that the underlined font is the same width as the * ordinary one (manual underlining by means of line drawing can * be done in a pinch). - * - * - verify, in OEM/ANSI combined mode, that the OEM and ANSI base - * fonts are the same size, and shift to OEM-only mode if not. */ -static void init_fonts(void) { +static void init_fonts(int pick_width) { TEXTMETRIC tm; - int i, j; - int widths[5]; + int i; + int fsize[8]; HDC hdc; int fw_dontcare, fw_bold; + int firstchar = ' '; +#ifdef CHECKOEMFONT +font_messup: +#endif for (i=0; i<8; i++) fonts[i] = NULL; @@ -466,61 +638,81 @@ static void init_fonts(void) { fw_bold = FW_BOLD; } + hdc = GetDC(hwnd); + + font_height = cfg.fontheight; + font_width = pick_width; + #define f(i,c,w,u) \ - fonts[i] = CreateFont (cfg.fontheight, 0, 0, 0, w, FALSE, u, FALSE, \ + fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \ c, OUT_DEFAULT_PRECIS, \ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \ FIXED_PITCH | FF_DONTCARE, cfg.font) + if (cfg.vtmode != VT_OEMONLY) { - f(FONT_NORMAL, ANSI_CHARSET, fw_dontcare, FALSE); - f(FONT_UNDERLINE, ANSI_CHARSET, fw_dontcare, TRUE); - } - if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) { - f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE); - f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE); - } - if (bold_mode == BOLD_FONT) { - if (cfg.vtmode != VT_OEMONLY) { - f(FONT_BOLD, ANSI_CHARSET, fw_bold, FALSE); - f(FONT_BOLDUND, ANSI_CHARSET, fw_bold, TRUE); + f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE); + + SelectObject (hdc, fonts[FONT_NORMAL]); + GetTextMetrics(hdc, &tm); + font_height = tm.tmHeight; + font_width = tm.tmAveCharWidth; + + f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE); + + if (bold_mode == BOLD_FONT) { + f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE); + f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE); } - if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) { - f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE); - f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE); + + if (cfg.vtmode == VT_OEMANSI) { + f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE); + f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE); + + if (bold_mode == BOLD_FONT) { + f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE); + f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE); + } + } + } + else + { + f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE); + + SelectObject (hdc, fonts[FONT_OEM]); + GetTextMetrics(hdc, &tm); + font_height = tm.tmHeight; + font_width = tm.tmAveCharWidth; + + f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE); + + if (bold_mode == BOLD_FONT) { + f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE); + f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE); } - } else { - fonts[FONT_BOLD] = fonts[FONT_BOLDUND] = NULL; - fonts[FONT_OEMBOLD] = fonts[FONT_OEMBOLDUND] = NULL; } #undef f - hdc = GetDC(hwnd); - - if (cfg.vtmode == VT_OEMONLY) - j = 4; - else - j = 0; - - for (i=0; i<(cfg.vtmode == VT_OEMANSI ? 5 : 4); i++) { - if (fonts[i+j]) { - SelectObject (hdc, fonts[i+j]); - GetTextMetrics(hdc, &tm); - if (i == 0 || i == 4) { - font_height = tm.tmHeight; - font_width = tm.tmAveCharWidth; - descent = tm.tmAscent + 1; - if (descent >= font_height) - descent = font_height - 1; - } - widths[i] = tm.tmAveCharWidth; + descent = tm.tmAscent + 1; + if (descent >= font_height) + descent = font_height - 1; + firstchar = tm.tmFirstChar; + + for (i=0; i<8; i++) { + if (fonts[i]) { + if (SelectObject (hdc, fonts[i]) && + GetTextMetrics(hdc, &tm) ) + fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight; + else fsize[i] = -i; } + else fsize[i] = -i; } ReleaseDC (hwnd, hdc); - if (widths[FONT_UNDERLINE] != widths[FONT_NORMAL] || + /* ... This is wrong in OEM only mode */ + if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] || (bold_mode == BOLD_FONT && - widths[FONT_BOLDUND] != widths[FONT_BOLD])) { + fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) { und_mode = UND_LINE; DeleteObject (fonts[FONT_UNDERLINE]); if (bold_mode == BOLD_FONT) @@ -528,27 +720,77 @@ static void init_fonts(void) { } if (bold_mode == BOLD_FONT && - widths[FONT_BOLD] != widths[FONT_NORMAL]) { + fsize[FONT_BOLD] != fsize[FONT_NORMAL]) { bold_mode = BOLD_SHADOW; DeleteObject (fonts[FONT_BOLD]); if (und_mode == UND_FONT) DeleteObject (fonts[FONT_BOLDUND]); } - if (cfg.vtmode == VT_OEMANSI && widths[FONT_OEM] != widths[FONT_NORMAL]) { - MessageBox(NULL, "The OEM and ANSI versions of this font are\n" +#ifdef CHECKOEMFONT + /* With the fascist font painting it doesn't matter if the linedraw font + * isn't exactly the right size anymore so we don't have to check this. + */ + if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) { + if( cfg.fontcharset == OEM_CHARSET ) + { + MessageBox(NULL, "The OEM and ANSI versions of this font are\n" "different sizes. Using OEM-only mode instead", "Font Size Mismatch", MB_ICONINFORMATION | MB_OK); - cfg.vtmode = VT_OEMONLY; - for (i=0; i<4; i++) + cfg.vtmode = VT_OEMONLY; + } + else if( firstchar < ' ' ) + { + MessageBox(NULL, "The OEM and ANSI versions of this font are\n" + "different sizes. Using XTerm mode instead", + "Font Size Mismatch", MB_ICONINFORMATION | MB_OK); + cfg.vtmode = VT_XWINDOWS; + } + else + { + MessageBox(NULL, "The OEM and ANSI versions of this font are\n" + "different sizes. Using ISO8859-1 mode instead", + "Font Size Mismatch", MB_ICONINFORMATION | MB_OK); + cfg.vtmode = VT_POORMAN; + } + + for (i=0; i<8; i++) if (fonts[i]) DeleteObject (fonts[i]); + goto font_messup; } +#endif } -void request_resize (int w, int h) { - int width = extra_width + font_width * w; - int height = extra_height + font_height * h; +void request_resize (int w, int h, int refont) { + int width, height; + +#ifdef CHECKOEMFONT + /* Don't do this in OEMANSI, you may get disable messages */ + if (refont && w != cols && (cols==80 || cols==132) + && cfg.vtmode != VT_OEMANSI) +#else + if (refont && w != cols && (cols==80 || cols==132)) +#endif + { + /* If font width too big for screen should we shrink the font more ? */ + if (w==132) + font_width = ((font_width*cols+w/2)/w); + else + font_width = 0; + { + int i; + for (i=0; i<8; i++) + if (fonts[i]) + DeleteObject(fonts[i]); + } + bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; + und_mode = UND_FONT; + init_fonts(font_width); + } + + width = extra_width + font_width * w; + height = extra_height + font_height * h; SetWindowPos (hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOCOPYBITS | @@ -571,29 +813,45 @@ static void click (Mouse_Button b, int x, int y) { lasttime = thistime; } -static int WINAPI WndProc (HWND hwnd, UINT message, - WPARAM wParam, LPARAM lParam) { +static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) { HDC hdc; static int ignore_size = FALSE; static int ignore_clip = FALSE; static int just_reconfigged = FALSE; switch (message) { + case WM_TIMER: + if (pending_netevent) + enact_pending_netevent(); + if (inbuf_reap != inbuf_head) + term_out(); + term_update(); + return 0; case WM_CREATE: break; + case WM_CLOSE: + if (!cfg.warn_on_close || session_closed || + MessageBox(hwnd, "Are you sure you want to close this session?", + "PuTTY Exit Confirmation", + MB_ICONWARNING | MB_OKCANCEL) == IDOK) + DestroyWindow(hwnd); + return 0; case WM_DESTROY: PostQuitMessage (0); return 0; case WM_SYSCOMMAND: - switch (wParam) { + switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ case IDM_SHOWLOG: - shownegot(hwnd); + showeventlog(hwnd); break; case IDM_NEWSESS: case IDM_DUPSESS: + case IDM_SAVEDSESS: { char b[2048]; char c[30], *cl; + int freecl = FALSE; STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE filemap = NULL; @@ -624,8 +882,17 @@ static int WINAPI WndProc (HWND hwnd, UINT message, UnmapViewOfFile(p); } } - sprintf(c, "putty &%08x", filemap); + sprintf(c, "putty &%p", filemap); cl = c; + } else if (wParam == IDM_SAVEDSESS) { + char *session = sessions[(lParam - IDM_SAVED_MIN) / 16]; + cl = malloc(16 + strlen(session)); /* 8, but play safe */ + if (!cl) + cl = NULL; /* not a very important failure mode */ + else { + sprintf(cl, "putty @%s", session); + freecl = TRUE; + } } else cl = NULL; @@ -642,6 +909,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message, if (filemap) CloseHandle(filemap); + if (freecl) + free(cl); } break; case IDM_RECONF: @@ -656,8 +925,11 @@ static int WINAPI WndProc (HWND hwnd, UINT message, } bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; und_mode = UND_FONT; - init_fonts(); + init_fonts(0); sfree(logpal); + /* Telnet will change local echo -> remote if the remote asks */ + if (cfg.protocol != PROT_TELNET) + ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple); if (pal) DeleteObject(pal); logpal = NULL; @@ -698,6 +970,10 @@ static int WINAPI WndProc (HWND hwnd, UINT message, case IDM_ABOUT: showabout (hwnd); break; + default: + if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) { + SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam); + } } break; @@ -727,8 +1003,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message, term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND, MA_RELEASE, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam))); - return 0; ReleaseCapture(); + return 0; case WM_RBUTTONDOWN: SetCapture(hwnd); click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE, @@ -785,31 +1061,16 @@ static int WINAPI WndProc (HWND hwnd, UINT message, } return 0; case WM_NETEVENT: - { - int i = back->msg (wParam, lParam); - if (i < 0) { - char buf[1024]; - switch (WSABASEERR + (-i) % 10000) { - case WSAECONNRESET: - sprintf(buf, "Connection reset by peer"); - break; - default: - sprintf(buf, "Unexpected network error %d", -i); - break; - } - MessageBox(hwnd, buf, "PuTTY Fatal Error", - MB_ICONERROR | MB_OK); - PostQuitMessage(1); - } else if (i == 0) { - if (cfg.close_on_exit) - PostQuitMessage(0); - else { - MessageBox(hwnd, "Connection closed by remote host", - "PuTTY", MB_OK | MB_ICONINFORMATION); - SetWindowText (hwnd, "PuTTY (inactive)"); - } - } - } + /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc + * but the only one that's likely to try to overload us is FD_READ. + * This means buffering just one is fine. + */ + if (pending_netevent) + enact_pending_netevent(); + + pending_netevent = TRUE; + pend_netevent_wParam=wParam; + pend_netevent_lParam=lParam; return 0; case WM_SETFOCUS: has_focus = TRUE; @@ -824,6 +1085,12 @@ static int WINAPI WndProc (HWND hwnd, UINT message, case WM_IGNORE_SIZE: ignore_size = TRUE; /* don't panic on next WM_SIZE msg */ break; + case WM_ENTERSIZEMOVE: + EnableSizeTip(1); + break; + case WM_EXITSIZEMOVE: + EnableSizeTip(0); + break; case WM_SIZING: { int width, height, w, h, ew, eh; @@ -833,6 +1100,7 @@ static int WINAPI WndProc (HWND hwnd, UINT message, height = r->bottom - r->top - extra_height; w = (width + font_width/2) / font_width; if (w < 1) w = 1; h = (height + font_height/2) / font_height; if (h < 1) h = 1; + UpdateSizeTip(hwnd, w, h); ew = width - w * font_width; eh = height - h * font_height; if (ew != 0) { @@ -856,7 +1124,7 @@ static int WINAPI WndProc (HWND hwnd, UINT message, else return 0; } - break; + /* break; (never reached) */ case WM_SIZE: if (wParam == SIZE_MINIMIZED) { SetWindowText (hwnd, @@ -950,9 +1218,38 @@ static int WINAPI WndProc (HWND hwnd, UINT message, int len; len = TranslateKey (wParam, lParam, buf); - back->send (buf, len); + if (len == -1) + return DefWindowProc (hwnd, message, wParam, lParam); + ldisc->send (buf, len); } return 0; + case WM_KEYUP: + case WM_SYSKEYUP: + /* + * We handle KEYUP ourselves in order to distinghish left + * and right Alt or Control keys, which Windows won't do + * right if left to itself. See also the special processing + * at the top of TranslateKey. + */ + { + BYTE keystate[256]; + int ret = GetKeyboardState(keystate); + if (ret && wParam == VK_MENU) { + if (lParam & 0x1000000) keystate[VK_RMENU] = 0; + else keystate[VK_LMENU] = 0; + SetKeyboardState (keystate); + } + if (ret && wParam == VK_CONTROL) { + if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0; + else keystate[VK_LCONTROL] = 0; + SetKeyboardState (keystate); + } + } + /* + * We don't return here, in order to allow Windows to do + * its own KEYUP processing as well. + */ + break; case WM_CHAR: case WM_SYSCHAR: /* @@ -962,8 +1259,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message, * we're ready to cope. */ { - char c = wParam; - back->send (&c, 1); + char c = xlat_kbd2tty((unsigned char)wParam); + ldisc->send (&c, 1); } return 0; } @@ -979,13 +1276,30 @@ static int WINAPI WndProc (HWND hwnd, UINT message, */ void do_text (Context ctx, int x, int y, char *text, int len, unsigned long attr) { + int lattr = 0; /* Will be arg later for line attribute type */ COLORREF fg, bg, t; int nfg, nbg, nfont; HDC hdc = ctx; + RECT line_box; + int force_manual_underline = 0; +static int *IpDx = 0, IpDxLEN = 0;; + + if (len>IpDxLEN || IpDx[0] != font_width*(1+!lattr)) { + int i; + if (len>IpDxLEN) { + sfree(IpDx); + IpDx = smalloc((len+16)*sizeof(int)); + IpDxLEN = (len+16); + } + for(i=0; i= '\xA0' && text[i] <= '\xFF') { +#if 0 + /* This is CP850 ... perfect translation */ static const char oemhighhalf[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */ "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */ "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */ "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */ - "\xB7\xB5\xB6\x41\x8E\x8F\x92\x80" /* C0-C7 */ - "\xD4\x90\xD2\xD3\x49\xD6\xD7\xD8" /* C8-CF */ + "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */ + "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */ "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */ "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */ "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */ @@ -1018,6 +1334,23 @@ void do_text (Context ctx, int x, int y, char *text, int len, "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */ "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */ ; +#endif + /* This is CP437 ... junk translation */ + static const unsigned char oemhighhalf[] = { + 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15, + 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4, + 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa, + 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8, + 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80, + 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78, + 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1, + 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87, + 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, + 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6, + 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98 + }; + text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0]; } } @@ -1033,8 +1366,21 @@ void do_text (Context ctx, int x, int y, char *text, int len, text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3'; } else if (attr & ATTR_LINEDRW) { int i; + /* ISO 8859-1 */ static const char poorman[] = "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7"; + + /* CP437 */ + static const char oemmap_437[] = + "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5" + "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA"; + + /* CP850 */ + static const char oemmap_850[] = + "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5" + "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA"; + + /* Poor windows font ... eg: windows courier */ static const char oemmap[] = "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5" "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA"; @@ -1050,12 +1396,15 @@ void do_text (Context ctx, int x, int y, char *text, int len, text[i] += '\x01' - '\x60'; break; case VT_OEMANSI: + /* Make sure we actually have an OEM font */ + if (fonts[nfont|FONT_OEM]) { case VT_OEMONLY: - nfont |= FONT_OEM; - for (i=0; i= '\x60' && text[i] <= '\x7E') - text[i] = oemmap[(unsigned char)text[i] - 0x60]; - break; + nfont |= FONT_OEM; + for (i=0; i= '\x60' && text[i] <= '\x7E') + text[i] = oemmap[(unsigned char)text[i] - 0x60]; + break; + } case VT_POORMAN: for (i=0; i= '\x60' && text[i] <= '\x7E') @@ -1070,23 +1419,42 @@ void do_text (Context ctx, int x, int y, char *text, int len, nfont |= FONT_BOLD; if (und_mode == UND_FONT && (attr & ATTR_UNDER)) nfont |= FONT_UNDERLINE; + if (!fonts[nfont]) + { + if (nfont&FONT_UNDERLINE) + force_manual_underline = 1; + /* Don't do the same for manual bold, it could be bad news. */ + + nfont &= ~(FONT_BOLD|FONT_UNDERLINE); + } if (attr & ATTR_REVERSE) { t = nfg; nfg = nbg; nbg = t; } if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) nfg++; + if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) + nbg++; fg = colours[nfg]; bg = colours[nbg]; SelectObject (hdc, fonts[nfont]); SetTextColor (hdc, fg); SetBkColor (hdc, bg); SetBkMode (hdc, OPAQUE); - TextOut (hdc, x, y, text, len); + line_box.left = x; + line_box.top = y; + line_box.right = x+font_width*len; + line_box.bottom = y+font_height; + ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx); if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { SetBkMode (hdc, TRANSPARENT); - TextOut (hdc, x-1, y, text, len); + + /* GRR: This draws the character outside it's box and can leave + * 'droppings' even with the clip box! I suppose I could loop it + * one character at a time ... yuk. */ + ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx); } - if (und_mode == UND_LINE && (attr & ATTR_UNDER)) { + if (force_manual_underline || + (und_mode == UND_LINE && (attr & ATTR_UNDER))) { HPEN oldpen; oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg)); MoveToEx (hdc, x, y+descent, NULL); @@ -1116,18 +1484,71 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { unsigned char *p = output; BYTE keystate[256]; int ret, code; + int cancel_alt = FALSE; /* - * Prepend ESC if ALT was pressed at the time. + * Get hold of the keyboard state, because we'll need it a few + * times shortly. */ - if (lParam & 0x20000000) + ret = GetKeyboardState(keystate); + + /* + * Record that we pressed key so the scroll window can be reset, but + * be careful to avoid Shift-UP/Down + */ + if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) { + seen_key_event = 1; + } + + /* + * Windows does not always want to distinguish left and right + * Alt or Control keys. Thus we keep track of them ourselves. + * See also the WM_KEYUP handler. + */ + if (wParam == VK_MENU) { + if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80; + else keystate[VK_LMENU] = 0x80; + SetKeyboardState (keystate); + return 0; + } + if (wParam == VK_CONTROL) { + if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80; + else keystate[VK_LCONTROL] = 0x80; + SetKeyboardState (keystate); + return 0; + } + + /* + * Prepend ESC, and cancel ALT, if ALT was pressed at the time + * and it wasn't AltGr. + */ + if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) { *p++ = 0x1B; + cancel_alt = TRUE; + } /* - * Get hold of the keyboard state, because we'll need it a few - * times shortly. + * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn, + * so we do it first. */ - ret = GetKeyboardState(keystate); + if (cfg.nethack_keypad) { + int shift = keystate[VK_SHIFT] & 0x80; + /* + * NB the shifted versions only work with numlock off. + */ + switch ( (lParam >> 16) & 0x1FF ) { + case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output; + case 0x048: *p++ = shift ? 'K' : 'k'; return p - output; + case 0x049: *p++ = shift ? 'U' : 'u'; return p - output; + case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output; + case 0x04C: *p++ = '.'; return p - output; + case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output; + case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output; + case 0x050: *p++ = shift ? 'J' : 'j'; return p - output; + case 0x051: *p++ = shift ? 'N' : 'n'; return p - output; + case 0x053: *p++ = '.'; return p - output; + } + } /* * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window @@ -1141,9 +1562,12 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); return 0; } - if ((lParam & 0x20000000) && wParam == VK_F4) { - SendMessage (hwnd, WM_DESTROY, 0, 0); - return 0; + if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) { + return -1; + } + if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) { + SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); + return -1; } /* @@ -1169,12 +1593,12 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { return p - output; } - /* - * If we're in applications keypad mode, we have to process it - * before char-map translation, because it will pre-empt lots - * of stuff, even if NumLock is off. - */ if (app_keypad_keys) { + /* + * If we're in applications keypad mode, we have to process it + * before char-map translation, because it will pre-empt lots + * of stuff, even if NumLock is off. + */ if (ret) { /* * Hack to ensure NumLock doesn't interfere with @@ -1212,23 +1636,46 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { } /* - * Before doing Windows charmap translation, remove ALT from - * the keymap, since its sole effect should be to prepend ESC, - * which we've already done. Note that removal of ALT has to - * happen _after_ the above call to SetKeyboardState, or dire - * things will befall. + * Shift-Tab should send ESC [ Z. */ - keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0; + if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_TAB) { + *p++ = 0x1B; /* ESC */ + *p++ = '['; + *p++ = 'Z'; + return p - output; + } + + /* + * Before doing Windows charmap translation, remove LeftALT + * from the keymap, since its sole effect should be to prepend + * ESC, which we've already done. Note that removal of LeftALT + * has to happen _after_ the above call to SetKeyboardState, or + * dire things will befall. + */ + if (cancel_alt) { + keystate[VK_MENU] = keystate[VK_RMENU]; + keystate[VK_LMENU] = 0; + } /* * Attempt the Windows char-map translation. */ if (ret) { WORD chr; - int r = ToAscii (wParam, (lParam >> 16) & 0xFF, - keystate, &chr, 0); + int r; + BOOL capsOn=keystate[VK_CAPITAL] !=0; + + /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ + if(cfg.xlat_capslockcyr) + keystate[VK_CAPITAL] = 0; + + r = ToAscii (wParam, (lParam >> 16) & 0xFF, + keystate, &chr, 0); + + if(capsOn) + chr = xlat_latkbd2win((unsigned char)(chr & 0xFF)); if (r == 1) { - *p++ = chr & 0xFF; + *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF)); return p - output; } } @@ -1248,15 +1695,37 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { * the DOS keyboard handling did it, and we have nothing better * to do with the key combo in question, we'll also map * Control-Backquote to ^\ (0x1C). + * + * In addition a real VT100 maps Ctrl-3/4/5/7 and 8. */ if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') { *p++ = 0x00; return p - output; } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '3') { + *p++ = 0x1B; + return p - output; + } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '4') { + *p++ = 0x1C; + return p - output; + } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '5') { + *p++ = 0x1D; + return p - output; + } if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') { *p++ = 0x1E; return p - output; } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '7') { + *p++ = 0x1F; + return p - output; + } + if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '8') { + *p++ = 0x7F; + return p - output; + } if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) { *p++ = 0x1F; return p - output; @@ -1360,7 +1829,7 @@ void set_sbar (int total, int start, int page) { SetScrollInfo (hwnd, SB_VERT, &si, TRUE); } -Context get_ctx() { +Context get_ctx(void) { HDC hdc; if (hwnd) { hdc = GetDC (hwnd); @@ -1488,11 +1957,10 @@ void get_clip (void **p, int *len) { */ void optimised_move (int to, int from, int lines) { RECT r; - int min, max, d; + int min, max; min = (to < from ? to : from); max = to + from - min; - d = max - min; r.left = 0; r.right = cols * font_width; r.top = min * font_height; r.bottom = (max+lines) * font_height;