X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/1bb542b2d891036ca5e9a80c2ce64621bb9318be..6f34e365eed04c1b4ebcf6d90511dd9e4400880a:/window.c diff --git a/window.c b/window.c index 0d2ddb66..be4b4e8d 100644 --- a/window.c +++ b/window.c @@ -3,43 +3,54 @@ #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); -static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output); +#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) +#define WM_IGNORE_KEYMENU (WM_XUSER + 3) + +static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); +static int TranslateKey(UINT message, 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 @@ -49,6 +60,7 @@ static int extra_width, extra_height; #define FONT_OEMBOLDUND 6 #define FONT_OEMUND 7 static HFONT fonts[8]; +static int font_needs_hand_underlining; static enum { BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT } bold_mode; @@ -70,6 +82,12 @@ static Mouse_Button lastbtn; static char *window_name, *icon_name; +static Ldisc *real_ldisc; + +void begin_session(void) { + ldisc = real_ldisc; +} + int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { static char appname[] = "PuTTY"; WORD winsock_ver; @@ -78,6 +96,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; int guess_width, guess_height; + putty_inst = inst; + flags = FLAG_VERBOSE | FLAG_INTERACTIVE; + winsock_ver = MAKEWORD(1, 1); if (WSAStartup(winsock_ver, &wsadata)) { MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error", @@ -100,12 +121,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 +170,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 +182,41 @@ 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:", 7)) { + char c; + + q += 7; + if (q[0] == '/' && q[1] == '/') + q += 2; + 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 +225,30 @@ 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; + } + } + + real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple); + /* To start with, we use the simple line discipline, so we can + * type passwords etc without fear of them being echoed... */ + ldisc = &ldisc_simple; if (!prev) { wndclass.style = 0; @@ -202,11 +297,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { guess_height = r.bottom - r.top; } - hwnd = CreateWindow (appname, appname, - WS_OVERLAPPEDWINDOW | WS_VSCROLL, - CW_USEDEFAULT, CW_USEDEFAULT, - guess_width, guess_height, - NULL, NULL, inst, NULL); + { + int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL; + if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL); + if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX); + hwnd = CreateWindow (appname, appname, + winmode, + CW_USEDEFAULT, CW_USEDEFAULT, + guess_width, guess_height, + NULL, NULL, inst, NULL); + } /* * Initialise the fonts, simultaneously correcting the guesses @@ -214,7 +314,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}. @@ -244,7 +344,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { SCROLLINFO si; si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL; + si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = rows-1; si.nPage = rows; @@ -267,15 +367,17 @@ 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; + inbuf_head = 0; outbuf_reap = outbuf_head = 0; /* @@ -290,7 +392,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 +415,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_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_CLRSB, "Clear Scrollback"); - AppendMenu (m, MF_ENABLED, IDM_RESET, "Reset Terminal"); + 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"); + AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY"); } /* @@ -340,13 +449,60 @@ 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); + + /* Send the paste buffer if there's anything to send */ + term_paste(); + + /* 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_head) + term_out(); + term_update(); + if (!has_focus) + timer_id = SetTimer(hwnd, 1, 2000, NULL); + else if (cfg.blinktext) + timer_id = SetTimer(hwnd, 1, 250, NULL); + else + timer_id = SetTimer(hwnd, 1, 500, NULL); + long_timer = 1; + } + } } /* @@ -363,13 +519,76 @@ 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; } /* + * Print a message box and close the connection. + */ +void connection_fatal(char *fmt, ...) { + va_list ap; + char stuff[200]; + + va_start(ap, fmt); + vsprintf(stuff, fmt, ap); + va_end(ap); + MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK); + if (cfg.close_on_exit) + PostQuitMessage(1); + else { + session_closed = TRUE; + SetWindowText (hwnd, "PuTTY (inactive)"); + } +} + +/* + * Actually do the job requested by a WM_NETEVENT + */ +static void enact_pending_netevent(void) { + int i; + static int reentering = 0; + + if (reentering) + return; /* don't unpend the pending */ + + pending_netevent = FALSE; + + reentering = 1; + i = back->msg (pend_netevent_wParam, pend_netevent_lParam); + reentering = 0; + + 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; + } + connection_fatal(buf); + } + 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 +663,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 +686,125 @@ 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); + + /* + * Some fonts, e.g. 9-pt Courier, draw their underlines + * outside their character cell. We successfully prevent + * screen corruption by clipping the text output, but then + * we lose the underline completely. Here we try to work + * out whether this is such a font, and if it is, we set a + * flag that causes underlines to be drawn by hand. + * + * Having tried other more sophisticated approaches (such + * as examining the TEXTMETRIC structure or requesting the + * height of a string), I think we'll do this the brute + * force way: we create a small bitmap, draw an underlined + * space on it, and test to see whether any pixels are + * foreground-coloured. (Since we expect the underline to + * go all the way across the character cell, we only search + * down a single column of the bitmap, half way across.) + */ + { + HDC und_dc; + HBITMAP und_bm, und_oldbm; + int i, gotit; + COLORREF c; + + und_dc = CreateCompatibleDC(hdc); + und_bm = CreateCompatibleBitmap(hdc, font_width, font_height); + und_oldbm = SelectObject(und_dc, und_bm); + SelectObject(und_dc, fonts[FONT_UNDERLINE]); + SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP); + SetTextColor (und_dc, RGB(255,255,255)); + SetBkColor (und_dc, RGB(0,0,0)); + SetBkMode (und_dc, OPAQUE); + ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL); + gotit = FALSE; + for (i = 0; i < font_height; i++) { + c = GetPixel(und_dc, font_width/2, i); + if (c != RGB(0,0,0)) + gotit = TRUE; + } + SelectObject(und_dc, und_oldbm); + DeleteObject(und_bm); + DeleteDC(und_dc); + font_needs_hand_underlining = !gotit; + } + + 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 +812,103 @@ 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; + + /* If the window is maximized supress resizing attempts */ + if(IsZoomed(hwnd)) return; + +#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); + } + else + { + static int first_time = 1; + static RECT ss; + + switch(first_time) + { + case 1: + /* Get the size of the screen */ + if (GetClientRect(GetDesktopWindow(),&ss)) + /* first_time = 0 */; + else { first_time = 2; break; } + case 0: + /* Make sure the values are sane */ + width = (ss.right-ss.left-extra_width ) / font_width; + height = (ss.bottom-ss.top-extra_height ) / font_height; + + if (w>width) w=width; + if (h>height) h=height; + if (w<15) w = 15; + if (h<1) w = 1; + } + } + + 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 +931,50 @@ 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 ignore_keymenu = TRUE; static int just_reconfigged = FALSE; switch (message) { + case WM_TIMER: + if (pending_netevent) + enact_pending_netevent(); + if (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 SC_KEYMENU: + if (ignore_keymenu) + return 0; /* don't put up system menu on Alt */ + break; 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 +1005,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 +1032,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message, if (filemap) CloseHandle(filemap); + if (freecl) + free(cl); } break; case IDM_RECONF: @@ -656,14 +1048,48 @@ 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; pal = NULL; cfgtopalette(); init_palette(); + + /* Enable or disable the scroll bar, etc */ + { + LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE); + + nflg = flag; + if (cfg.scrollbar) nflg |= WS_VSCROLL; + else nflg &= ~WS_VSCROLL; + if (cfg.locksize) + nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX); + else + nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX); + + if (nflg != flag) + { + RECT cr, wr; + + SetWindowLong(hwnd, GWL_STYLE, nflg); + SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0); + SetWindowPos(hwnd, NULL, 0,0,0,0, + SWP_NOACTIVATE|SWP_NOCOPYBITS| + SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER| + SWP_FRAMECHANGED); + + GetWindowRect (hwnd, &wr); + GetClientRect (hwnd, &cr); + extra_width = wr.right - wr.left - cr.right + cr.left; + extra_height = wr.bottom - wr.top - cr.bottom + cr.top; + } + } + term_size(cfg.height, cfg.width, cfg.savelines); InvalidateRect(hwnd, NULL, TRUE); SetWindowPos (hwnd, NULL, 0, 0, @@ -698,6 +1124,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 +1157,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, @@ -764,6 +1194,9 @@ static int WINAPI WndProc (HWND hwnd, UINT message, case WM_IGNORE_CLIP: ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */ break; + case WM_IGNORE_KEYMENU: + ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */ + break; case WM_DESTROYCLIPBOARD: if (!ignore_clip) term_deselect(); @@ -785,31 +1218,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 +1242,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 +1257,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 +1281,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, @@ -931,6 +1356,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message, return FALSE; case WM_KEYDOWN: case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: /* * Add the scan code and keypress timing to the random * number noise, if we're using ssh. @@ -949,37 +1376,12 @@ static int WINAPI WndProc (HWND hwnd, UINT message, unsigned char buf[20]; int len; - len = TranslateKey (wParam, lParam, buf); - back->send (buf, len); + len = TranslateKey (message, wParam, lParam, buf); + 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: /* @@ -989,8 +1391,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; } @@ -1005,16 +1407,31 @@ static int WINAPI WndProc (HWND hwnd, UINT message, * We are allowed to fiddle with the contents of `text'. */ void do_text (Context ctx, int x, int y, char *text, int len, - unsigned long attr) { + unsigned long attr, int lattr) { COLORREF fg, bg, t; int nfg, nbg, nfont; HDC hdc = ctx; + RECT line_box; + int force_manual_underline = 0; + int fnt_width = font_width*(1+(lattr!=LATTR_NORM)); + static int *IpDx = 0, IpDxLEN = 0;; - x *= font_width; + if (len>IpDxLEN || IpDx[0] != fnt_width) { + 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 */ @@ -1045,6 +1464,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]; } } @@ -1060,8 +1496,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"; @@ -1077,12 +1526,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') @@ -1097,27 +1549,52 @@ 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 (font_needs_hand_underlining && (attr & ATTR_UNDER)) + force_manual_underline = 1; 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+fnt_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. + * + * Or ... I could do a test print with "W", and use +1 or -1 for this + * shift depending on if the leftmost column is blank... + */ + 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); - LineTo (hdc, x+len*font_width, y+descent); + LineTo (hdc, x+len*fnt_width, y+descent); oldpen = SelectObject (hdc, oldpen); DeleteObject (oldpen); } @@ -1125,7 +1602,7 @@ void do_text (Context ctx, int x, int y, char *text, int len, POINT pts[5]; HPEN oldpen; pts[0].x = pts[1].x = pts[4].x = x; - pts[2].x = pts[3].x = x+font_width-1; + pts[2].x = pts[3].x = x+fnt_width-1; pts[0].y = pts[3].y = pts[4].y = y; pts[1].y = pts[2].y = y+font_height-1; oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23])); @@ -1135,253 +1612,446 @@ void do_text (Context ctx, int x, int y, char *text, int len, } } +static int check_compose(int first, int second) { + + static char * composetbl[] = { + "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢", + "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§", + "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®", + "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸", + "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ", + "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË", + "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ", + "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ", + "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç", + "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ", + "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû", + "\"uü", "'yý", "htþ", "\"yÿ", + 0}; + + char ** c; + static int recurse = 0; + int nc = -1; + + if(0) + { + char buf[256]; + char * p; + sprintf(buf, "cc(%d,%d)", first, second); + for(p=buf; *p; p++) + c_write1(*p); + } + + for(c=composetbl; *c; c++) { + if( (*c)[0] == first && (*c)[1] == second) + { + return (*c)[2] & 0xFF; + } + } + + if(recurse==0) + { + recurse=1; + nc = check_compose(second, first); + if(nc == -1) + nc = check_compose(toupper(first), toupper(second)); + if(nc == -1) + nc = check_compose(toupper(second), toupper(first)); + recurse=0; + } + return nc; +} + + /* - * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII - * codes. Returns number of bytes used. + * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII + * codes. Returns number of bytes used or zero to drop the message + * or -1 to forward the message to windows. */ -static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) { - unsigned char *p = output; +static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) { BYTE keystate[256]; - int ret, code; - int cancel_alt = FALSE; + int scan, left_alt = 0, key_down, shift_state; + int r, i, code; + unsigned char * p = output; - /* - * Get hold of the keyboard state, because we'll need it a few - * times shortly. - */ - ret = GetKeyboardState(keystate); +static WORD keys[3]; +static int compose_state = 0; +static int compose_char = 0; +static WPARAM compose_key = 0; - /* - * 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; - } + r = GetKeyboardState(keystate); + if (!r) memset(keystate, 0, sizeof(keystate)); + else + { + /* Note if AltGr was pressed and if it was used as a compose key */ + if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) + { + keystate[VK_RMENU] = keystate[VK_MENU]; + if (!compose_state) compose_key = wParam; + } + if (wParam == VK_APPS && !compose_state) + compose_key = wParam; - /* - * 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; - } + if (wParam == compose_key) + { + if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0) + compose_state = 1; + else if (compose_state == 1 && (HIWORD(lParam)&KF_UP)) + compose_state = 2; + else + compose_state = 0; + } + else if (compose_state==1 && wParam != VK_CONTROL) + compose_state = 0; - /* - * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window - * events: we'll deal with those now. - */ - if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) { - SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0); - return 0; - } - if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) { - SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); - return 0; - } - if ((lParam & 0x20000000) && wParam == VK_F4) { - SendMessage (hwnd, WM_DESTROY, 0, 0); - return 0; + /* Nastyness with NUMLock - Shift-NUMLock is left alone though */ + if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys)) + && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) { + + wParam = VK_EXECUTE; + + /* UnToggle NUMLock */ + if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0) + keystate[VK_NUMLOCK] ^= 1; + } + + /* And write back the 'adjusted' state */ + SetKeyboardState (keystate); } - /* - * In general, the strategy is to see what the Windows keymap - * translation has to say for itself, and then process function - * keys and suchlike ourselves if that fails. But first we must - * deal with the small number of special cases which the - * Windows keymap translator thinks it can do but gets wrong. - * - * First special case: we might want the Backspace key to send - * 0x7F not 0x08. + /* Disable Auto repeat if required */ + if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) + return 0; + + if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0) + left_alt = 1; + + key_down = ((HIWORD(lParam)&KF_UP)==0); + + /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */ + if (left_alt && (keystate[VK_CONTROL]&0x80)) + keystate[VK_MENU] = 0; + + scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF)); + shift_state = ((keystate[VK_SHIFT]&0x80)!=0) + + ((keystate[VK_CONTROL]&0x80)!=0)*2; + + /* + * Record that we pressed key so the scroll window can be reset, but + * be careful to avoid Shift-UP/Down */ - if (wParam == VK_BACK) { - *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08); - return p - output; + if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) { + seen_key_event = 1; } - /* - * Control-Space should send ^@ (0x00), not Space. - */ - if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) { - *p++ = 0x00; - return p - output; + /* Make sure we're not pasting */ + if (key_down) term_nopaste(); + + if (compose_state>1 && left_alt) compose_state = 0; + + /* Sanitize the number pad if not using a PC NumPad */ + if( left_alt || (app_keypad_keys && cfg.funky_type != 2) + || cfg.nethack_keypad || compose_state ) + { + if ((HIWORD(lParam)&KF_EXTENDED) == 0) + { + int nParam = 0; + switch(wParam) + { + case VK_INSERT: nParam = VK_NUMPAD0; break; + case VK_END: nParam = VK_NUMPAD1; break; + case VK_DOWN: nParam = VK_NUMPAD2; break; + case VK_NEXT: nParam = VK_NUMPAD3; break; + case VK_LEFT: nParam = VK_NUMPAD4; break; + case VK_CLEAR: nParam = VK_NUMPAD5; break; + case VK_RIGHT: nParam = VK_NUMPAD6; break; + case VK_HOME: nParam = VK_NUMPAD7; break; + case VK_UP: nParam = VK_NUMPAD8; break; + case VK_PRIOR: nParam = VK_NUMPAD9; break; + case VK_DELETE: nParam = VK_DECIMAL; break; + } + if (nParam) + { + if (keystate[VK_NUMLOCK]&1) shift_state |= 1; + wParam = nParam; + } + } } - /* - * 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 (ret) { - /* - * Hack to ensure NumLock doesn't interfere with - * perception of Shift for Keypad Plus. I don't pretend - * to understand this, but it seems to work as is. - * Leave it alone, or die. - */ - keystate[VK_NUMLOCK] = 0; - SetKeyboardState (keystate); - GetKeyboardState (keystate); + /* If a key is pressed and AltGr is not active */ + if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state) + { + /* Okay, prepare for most alts then ...*/ + if (left_alt) *p++ = '\033'; + + /* Lets see if it's a pattern we know all about ... */ + if (wParam == VK_PRIOR && shift_state == 1) { + SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0); + return 0; } - switch ( (lParam >> 16) & 0x1FF ) { - case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output; - case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output; - case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output; - case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output; - case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output; - case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output; - case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output; - case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output; - case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output; - case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output; - case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */ - p += sprintf((char *)p, - (ret && (keystate[VK_SHIFT] & 0x80)) ? - "\x1BOm" : "\x1BOl"); - return p - output; - case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output; - case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output; - case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output; - case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output; - case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output; - case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output; + if (wParam == VK_NEXT && shift_state == 1) { + SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); + return 0; + } + if (left_alt && wParam == VK_F4 && cfg.alt_f4) { + return -1; + } + if (left_alt && wParam == VK_SPACE && cfg.alt_space) { + + SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0); + SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); + SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 0); + return -1; } - } - /* - * 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; - } + /* Nethack keypad */ + if (cfg.nethack_keypad && !left_alt) { + switch(wParam) { + case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output; + case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output; + case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output; + case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output; + case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output; + case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output; + case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output; + case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output; + case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output; + } + } - /* - * Attempt the Windows char-map translation. - */ - if (ret) { - WORD chr; - int r = ToAscii (wParam, (lParam >> 16) & 0xFF, - keystate, &chr, 0); - if (r == 1) { - *p++ = chr & 0xFF; + /* Application Keypad */ + if (!left_alt) { + int xkey = 0; + + if ( cfg.funky_type == 0 || + ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) { + case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break; + case VK_DIVIDE: xkey = 'Q'; break; + case VK_MULTIPLY:xkey = 'R'; break; + case VK_SUBTRACT:xkey = 'S'; break; + } + if(app_keypad_keys) switch(wParam) { + case VK_NUMPAD0: xkey = 'p'; break; + case VK_NUMPAD1: xkey = 'q'; break; + case VK_NUMPAD2: xkey = 'r'; break; + case VK_NUMPAD3: xkey = 's'; break; + case VK_NUMPAD4: xkey = 't'; break; + case VK_NUMPAD5: xkey = 'u'; break; + case VK_NUMPAD6: xkey = 'v'; break; + case VK_NUMPAD7: xkey = 'w'; break; + case VK_NUMPAD8: xkey = 'x'; break; + case VK_NUMPAD9: xkey = 'y'; break; + + case VK_DECIMAL: xkey = 'n'; break; + case VK_ADD: if(shift_state) xkey = 'm'; + else xkey = 'l'; + break; + case VK_RETURN: + if (HIWORD(lParam)&KF_EXTENDED) + xkey = 'M'; + break; + } + if(xkey) + { + if (vt52_mode) + { + if (xkey>='P' && xkey<='S') + p += sprintf((char *)p, "\x1B%c", xkey); + else + p += sprintf((char *)p, "\x1B?%c", xkey); + } + else + p += sprintf((char *)p, "\x1BO%c", xkey); + return p - output; + } + } + + if (wParam == VK_BACK && shift_state == 0 ) /* Backspace */ + { + *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08); + return p-output; + } + if (wParam == VK_TAB && shift_state == 1 ) /* Shift tab */ + { + *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output; + } + if (wParam == VK_SPACE && shift_state == 2 ) /* Ctrl-Space */ + { + *p++ = 0; return p - output; + } + if (wParam == VK_SPACE && shift_state == 3 ) /* Ctrl-Shift-Space */ + { + *p++ = 160; return p - output; + } + if (wParam == VK_CANCEL && shift_state == 2 ) /* Ctrl-Break */ + { + *p++ = 3; return p - output; + } + /* Control-2 to Control-8 are special */ + if (shift_state == 2 && wParam >= '2' && wParam <= '8') + { + *p++ = "\000\033\034\035\036\037\177"[wParam-'2']; + return p - output; + } + if (shift_state == 2 && wParam == 0xBD) { + *p++ = 0x1F; + return p - output; + } + if (shift_state == 2 && wParam == 0xDF) { + *p++ = 0x1C; + return p - output; + } + if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) { + *p++ = '\r'; *p++ = '\n'; return p - output; } - } - /* - * OK, we haven't had a key code from the keymap translation. - * We'll try our various special cases and function keys, and - * then give up. (There's nothing wrong with giving up: - * Scrollock, Pause/Break, and of course the various buckybit - * keys all produce KEYDOWN events that we really _do_ want to - * ignore.) - */ + /* + * Next, all the keys that do tilde codes. (ESC '[' nn '~', + * for integer decimal nn.) + * + * We also deal with the weird ones here. Linux VCs replace F1 + * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but + * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w + * respectively. + */ + code = 0; + switch (wParam) { + case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break; + case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break; + case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break; + case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break; + case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break; + case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break; + case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break; + case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break; + case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break; + case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break; + case VK_F11: code = 23; break; + case VK_F12: code = 24; break; + case VK_F13: code = 25; break; + case VK_F14: code = 26; break; + case VK_F15: code = 28; break; + case VK_F16: code = 29; break; + case VK_F17: code = 31; break; + case VK_F18: code = 32; break; + case VK_F19: code = 33; break; + case VK_F20: code = 34; break; + case VK_HOME: code = 1; break; + case VK_INSERT: code = 2; break; + case VK_DELETE: code = 3; break; + case VK_END: code = 4; break; + case VK_PRIOR: code = 5; break; + case VK_NEXT: code = 6; break; + } + if (cfg.funky_type == 1 && code >= 11 && code <= 15) { + p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11); + return p - output; + } + if (cfg.funky_type == 2 && code >= 11 && code <= 14) { + p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11); + return p - output; + } + if (cfg.rxvt_homeend && (code == 1 || code == 4)) { + p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw"); + return p - output; + } + if (code) { + p += sprintf((char *)p, "\x1B[%d~", code); + return p - output; + } - /* - * Control-2 should return ^@ (0x00), Control-6 should return - * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since - * 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). - */ - if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') { - *p++ = 0x00; - return p - output; - } - if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') { - *p++ = 0x1E; - return p - output; - } - if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) { - *p++ = 0x1F; - return p - output; - } - if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) { - *p++ = 0x1C; - return p - output; + /* + * Now the remaining keys (arrows and Keypad 5. Keypad 5 for + * some reason seems to send VK_CLEAR to Windows...). + */ + { + char xkey = 0; + switch (wParam) { + case VK_UP: xkey = 'A'; break; + case VK_DOWN: xkey = 'B'; break; + case VK_RIGHT: xkey = 'C'; break; + case VK_LEFT: xkey = 'D'; break; + case VK_CLEAR: xkey = 'G'; break; + } + if (xkey) + { + if (vt52_mode) + p += sprintf((char *)p, "\x1B%c", xkey); + else if (app_cursor_keys) + p += sprintf((char *)p, "\x1BO%c", xkey); + else + p += sprintf((char *)p, "\x1B[%c", xkey); + return p - output; + } + } } - /* - * First, all the keys that do tilde codes. (ESC '[' nn '~', - * for integer decimal nn.) - * - * We also deal with the weird ones here. Linux VCs replace F1 - * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but - * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w - * respectively. - */ - code = 0; - switch (wParam) { - case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break; - case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break; - case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break; - case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break; - case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break; - case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break; - case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break; - case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break; - case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break; - case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break; - case VK_F11: code = 23; break; - case VK_F12: code = 24; break; - case VK_HOME: code = 1; break; - case VK_INSERT: code = 2; break; - case VK_DELETE: code = 3; break; - case VK_END: code = 4; break; - case VK_PRIOR: code = 5; break; - case VK_NEXT: code = 6; break; - } - if (cfg.linux_funkeys && code >= 11 && code <= 15) { - p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11); - return p - output; - } - if (cfg.rxvt_homeend && (code == 1 || code == 4)) { - p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw"); - return p - output; - } - if (code) { - p += sprintf((char *)p, "\x1B[%d~", code); - return p - output; + /* Okay we've done everything interesting; let windows deal with + * the boring stuff */ + { + 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, scan, keystate, keys, 0); + if(r>0) + { + p = output; + for(i=0; i' ') { + compose_char = ch; + compose_state ++; + continue; + } + if (compose_state==3 && (ch&0x80) == 0 && ch>' ') { + int nc; + compose_state = 0; + + if ((nc=check_compose(compose_char,ch)) == -1) + { + c_write1('\007'); + return 0; + } + *p++ = xlat_kbd2tty((unsigned char)nc); + return p-output; + } + + compose_state = 0; + + if( left_alt && key_down ) *p++ = '\033'; + if (!key_down) + *p++ = ch; + else + { + if(capsOn) + ch = xlat_latkbd2win(ch); + *p++ = xlat_kbd2tty(ch); + } + } + + /* This is so the ALT-Numpad and dead keys work correctly. */ + keys[0] = 0; + + return p-output; + } } - /* - * Now the remaining keys (arrows and Keypad 5. Keypad 5 for - * some reason seems to send VK_CLEAR to Windows...). - */ - switch (wParam) { - case VK_UP: - p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A"); - return p - output; - case VK_DOWN: - p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B"); - return p - output; - case VK_RIGHT: - p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C"); - return p - output; - case VK_LEFT: - p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D"); - return p - output; - case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output; + /* This stops ALT press-release doing a 'COMMAND MENU' function */ +#if 0 + if (message == WM_SYSKEYUP && wParam == VK_MENU) + { + keystate[VK_MENU] = 0; + return 0; } +#endif - return 0; + return -1; } void set_title (char *title) { @@ -1402,8 +2072,11 @@ void set_icon (char *title) { void set_sbar (int total, int start, int page) { SCROLLINFO si; + + if (!cfg.scrollbar) return; + si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL; + si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = total - 1; si.nPage = page; @@ -1412,7 +2085,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); @@ -1540,11 +2213,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; @@ -1568,6 +2240,21 @@ void fatalbox(char *fmt, ...) { /* * Beep. */ -void beep(void) { - MessageBeep(MB_OK); +void beep(int errorbeep) { + static long last_beep = 0; + long now, beep_diff; + + now = GetTickCount(); + beep_diff = now-last_beep; + + /* Make sure we only respond to one beep per packet or so */ + if (beep_diff>=0 && beep_diff<50) + return; + + if(errorbeep) + MessageBeep(MB_ICONHAND); + else + MessageBeep(MB_OK); + + last_beep = GetTickCount(); }