X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/5def752285da767c159c1ef88fe7f927dc5286f2..79bf227ba7ba02e32ac710621b672e2789f9ef50:/window.c diff --git a/window.c b/window.c index cda2d1d8..e720ed3c 100644 --- a/window.c +++ b/window.c @@ -1,23 +1,3 @@ -#include -#include -#include -#include -#include -#ifndef AUTO_WINSOCK -#ifdef WINSOCK_TWO -#include -#else -#include -#endif -#endif - -#ifndef NO_MULTIMON -#if WINVER < 0x0500 -#define COMPILE_MULTIMON_STUBS -#include -#endif -#endif - #include #include #include @@ -27,43 +7,47 @@ #define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "terminal.h" -#include "winstuff.h" #include "storage.h" #include "win_res.h" +#ifndef NO_MULTIMON +#if WINVER < 0x0500 +#define COMPILE_MULTIMON_STUBS +#include +#endif +#endif + +#include +#include +#include +#include + #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_HELP 0x0140 #define IDM_ABOUT 0x0150 #define IDM_SAVEDSESS 0x0160 #define IDM_COPYALL 0x0170 #define IDM_FULLSCREEN 0x0180 +#define IDM_PASTE 0x0190 #define IDM_SESSLGP 0x0250 /* log type printable */ #define IDM_SESSLGA 0x0260 /* log type all chars */ #define IDM_SESSLGE 0x0270 /* log end */ + +#define IDM_SPECIAL_MIN 0x0400 +#define IDM_SPECIAL_MAX 0x0800 + #define IDM_SAVED_MIN 0x1000 #define IDM_SAVED_MAX 0x2000 #define WM_IGNORE_CLIP (WM_XUSER + 2) #define WM_FULLSCR_ON_MAX (WM_XUSER + 3) +#define WM_AGENT_CALLBACK (WM_XUSER + 4) /* Needed for Chinese support and apparently not always defined. */ #ifndef VK_PROCESSKEY @@ -78,16 +62,17 @@ #define WHEEL_DELTA 120 #endif +static Mouse_Button translate_button(Mouse_Button button); 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 systopalette(void); static void init_palette(void); static void init_fonts(int, int); static void another_font(int); static void deinit_fonts(void); static void set_input_locale(HKL); -static int do_mouse_wheel_msg(UINT message, WPARAM wParam, LPARAM lParam); static int is_full_screen(void); static void make_full_screen(void); @@ -115,10 +100,33 @@ static time_t last_movement = 0; static int caret_x = -1, caret_y = -1; +static int kbd_codepage; + static void *ldisc; static Backend *back; static void *backhandle; -static Terminal *term; + +static struct unicode_data ucsdata; +static int session_closed; + +static const struct telnet_special *specials; + +static struct { + HMENU menu; + int specials_submenu_pos; +} popup_menus[2]; +enum { SYSMENU, CTXMENU }; + +Config cfg; /* exported to windlg.c */ + +extern struct sesslist sesslist; /* imported from windlg.c */ + +struct agent_callback { + void (*callback)(void *, void *, int); + void *callback_ctx; + void *data; + int len; +}; #define FONT_NORMAL 0 #define FONT_BOLD 1 @@ -167,10 +175,6 @@ static char *window_name, *icon_name; static int compose_state = 0; -static int wsa_started = 0; - -static OSVERSIONINFO osVersion; - static UINT wm_mousewheel = WM_MOUSEWHEEL; /* Dummy routine, only required in plink. */ @@ -180,9 +184,6 @@ void ldisc_update(void *frontend, int echo, int edit) int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { - static char appname[] = "PuTTY"; - WORD winsock_ver; - WSADATA wsadata; WNDCLASS wndclass; MSG msg; int guess_width, guess_height; @@ -190,20 +191,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) hinst = inst; flags = FLAG_VERBOSE | FLAG_INTERACTIVE; - winsock_ver = MAKEWORD(1, 1); - if (WSAStartup(winsock_ver, &wsadata)) { - MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error", - MB_OK | MB_ICONEXCLAMATION); - return 1; - } - if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { - MessageBox(NULL, "WinSock version is incompatible with 1.1", - "WinSock Error", MB_OK | MB_ICONEXCLAMATION); - WSACleanup(); - return 1; - } - wsa_started = 1; - /* WISHLIST: maybe allow config tweaking even if winsock not present? */ sk_init(); InitCommonControls(); @@ -212,14 +199,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * config box. */ defuse_showwindow(); + if (!init_winver()) { - ZeroMemory(&osVersion, sizeof(osVersion)); - osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); - if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) { - MessageBox(NULL, "Windows refuses to report a version", - "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION); - return 1; - } + char *str = dupprintf("%s Fatal Error", appname); + MessageBox(NULL, "Windows refuses to report a version", + str, MB_OK | MB_ICONEXCLAMATION); + sfree(str); + return 1; } /* @@ -265,8 +251,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) char *p; int got_host = 0; - default_protocol = DEFAULT_PROTOCOL; - default_port = DEFAULT_PORT; + default_protocol = be_default_protocol; + /* Find the appropriate default port. */ + { + int i; + default_port = 0; /* illegal */ + for (i = 0; backends[i].backend != NULL; i++) + if (backends[i].protocol == default_protocol) { + default_port = backends[i].backend->default_port; + break; + } + } cfg.logtype = LGTYP_NONE; do_defaults(NULL, &cfg); @@ -289,8 +284,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) p[i] = '\0'; do_defaults(p + 1, &cfg); if (!*cfg.host && !do_config()) { - WSACleanup(); - return 0; + cleanup_exit(0); } } else if (*p == '&') { /* @@ -308,8 +302,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) UnmapViewOfFile(cp); CloseHandle(filemap); } else if (!do_config()) { - WSACleanup(); - return 0; + cleanup_exit(0); } } else { /* @@ -325,7 +318,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) char *p = argv[i]; int ret; - ret = cmdline_process_param(p, i+1init((void *)term, &backhandle, - cfg.host, cfg.port, &realhost, cfg.tcp_nodelay); + error = back->init(NULL, &backhandle, &cfg, + cfg.host, cfg.port, &realhost, cfg.tcp_nodelay, + cfg.tcp_keepalives); + back->provide_logctx(backhandle, logctx); if (error) { + char *str = dupprintf("%s Error", appname); sprintf(msg, "Unable to open connection to\n" "%.800s\n" "%s", cfg.host, error); - MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK); + MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); + sfree(str); return 0; } window_name = icon_name = NULL; if (*cfg.wintitle) { title = cfg.wintitle; } else { - sprintf(msg, "%s - PuTTY", realhost); + sprintf(msg, "%s - %s", realhost, appname); title = msg; } sfree(realhost); - set_title(title); - set_icon(title); + set_title(NULL, title); + set_icon(NULL, title); } /* @@ -634,7 +642,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* * Set up a line discipline. */ - ldisc = ldisc_create(term, back, backhandle, NULL); + ldisc = ldisc_create(&cfg, term, back, backhandle, NULL); session_closed = FALSE; @@ -649,57 +657,51 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Set up the session-control options on the system menu. */ { - HMENU m = GetSystemMenu(hwnd, FALSE); - HMENU p, s; - int i; + HMENU s, m; + int i, j; + char *str; + + popup_menus[SYSMENU].menu = GetSystemMenu(hwnd, FALSE); + popup_menus[CTXMENU].menu = CreatePopupMenu(); + AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste"); - AppendMenu(m, MF_SEPARATOR, 0, 0); - if (cfg.protocol == PROT_TELNET) { - p = CreateMenu(); - AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There"); - AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break"); - AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch"); - AppendMenu(p, MF_SEPARATOR, 0, 0); - AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character"); - AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line"); - AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead"); - AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation"); - AppendMenu(p, MF_SEPARATOR, 0, 0); - AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process"); - AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output"); - AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process"); - AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process"); - AppendMenu(p, MF_SEPARATOR, 0, 0); - 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_SEPARATOR, 0, 0); - } - 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++) + get_sesslist(&sesslist, TRUE); + for (i = 1; + i < ((sesslist.nsessions < 256) ? sesslist.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_COPYALL, "C&opy All to Clipboard"); - 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, (cfg.resize_action == RESIZE_DISABLED) ? - MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen"); - AppendMenu(m, MF_SEPARATOR, 0, 0); - if (help_path) - AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help"); - AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY"); + sesslist.sessions[i]); + + for (j = 0; j < lenof(popup_menus); j++) { + m = popup_menus[j].menu; + + AppendMenu(m, MF_SEPARATOR, 0, 0); + popup_menus[j].specials_submenu_pos = GetMenuItemCount(m); + 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"); + 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_COPYALL, "C&opy All to Clipboard"); + 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, (cfg.resize_action == RESIZE_DISABLED) ? + MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen"); + AppendMenu(m, MF_SEPARATOR, 0, 0); + if (help_path) + AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help"); + str = dupprintf("&About %s", appname); + AppendMenu(m, MF_ENABLED, IDM_ABOUT, str); + sfree(str); + } } + update_specials_menu(NULL); + /* * Set up the initial input locale. */ @@ -708,7 +710,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* * Open the initial log file if there is one. */ - logfopen(); + logfopen(logctx); /* * Finally show the window! @@ -761,9 +763,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (pending_netevent) { enact_pending_netevent(); - /* Force the cursor blink on */ - term_blink(term, 1); - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) continue; } @@ -824,8 +823,6 @@ void cleanup_exit(int code) if (pal) DeleteObject(pal); sk_cleanup(); - if (wsa_started) - WSACleanup(); if (cfg.protocol == PROT_SSH) { random_save_seed(); @@ -852,8 +849,8 @@ char *do_select(SOCKET skt, int startup) } if (!hwnd) return "do_select(): internal error (hwnd==NULL)"; - if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) { - switch (WSAGetLastError()) { + if (p_WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) { + switch (p_WSAGetLastError()) { case WSAENETDOWN: return "Network is down"; default: @@ -864,9 +861,46 @@ char *do_select(SOCKET skt, int startup) } /* + * Update the Special Commands submenu. + */ +void update_specials_menu(void *frontend) +{ + HMENU m = GetSystemMenu(hwnd, FALSE); + int menu_already_exists = (specials != NULL); + int i, j; + + specials = back->get_specials(backhandle); + if (specials) { + HMENU p = CreateMenu(); + for (i = 0; specials[i].name; i++) { + assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX); + if (*specials[i].name) + AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i, + specials[i].name); + else + AppendMenu(p, MF_SEPARATOR, 0, 0); + } + for (j = 0; j < lenof(popup_menus); j++) { + if (menu_already_exists) + DeleteMenu(popup_menus[j].menu, + popup_menus[j].specials_submenu_pos, + MF_BYPOSITION); + else + InsertMenu(popup_menus[j].menu, + popup_menus[j].specials_submenu_pos, + MF_BYPOSITION | MF_SEPARATOR, 0, 0); + InsertMenu(popup_menus[j].menu, + popup_menus[j].specials_submenu_pos, + MF_BYPOSITION | MF_POPUP | MF_ENABLED, + (UINT) p, "Special Command"); + } + } +} + +/* * set or clear the "raw mouse message" mode */ -void set_raw_mouse_mode(int activate) +void set_raw_mouse_mode(void *frontend, int activate) { activate = activate && !cfg.no_mouse_rep; send_raw_mouse = activate; @@ -876,20 +910,25 @@ void set_raw_mouse_mode(int activate) /* * Print a message box and close the connection. */ -void connection_fatal(char *fmt, ...) +void connection_fatal(void *frontend, char *fmt, ...) { va_list ap; - char stuff[200]; + char *stuff, morestuff[100]; va_start(ap, fmt); - vsprintf(stuff, fmt, ap); + stuff = dupvprintf(fmt, ap); va_end(ap); - MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK); - if (cfg.close_on_exit == COE_ALWAYS) + sprintf(morestuff, "%.70s Fatal Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); + sfree(stuff); + + if (cfg.close_on_exit == FORCE_ON) PostQuitMessage(1); else { session_closed = TRUE; - SetWindowText(hwnd, "PuTTY (inactive)"); + sprintf(morestuff, "%.70s (inactive)", appname); + set_icon(NULL, morestuff); + set_title(NULL, morestuff); } } @@ -899,12 +938,14 @@ void connection_fatal(char *fmt, ...) void cmdline_error(char *fmt, ...) { va_list ap; - char stuff[200]; + char *stuff, morestuff[100]; va_start(ap, fmt); - vsprintf(stuff, fmt, ap); + stuff = dupvprintf(fmt, ap); va_end(ap); - MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK); + sprintf(morestuff, "%.70s Command Line Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); + sfree(stuff); exit(1); } @@ -929,13 +970,16 @@ static void enact_pending_netevent(void) if (ret == 0 && !session_closed) { /* Abnormal exits will already have set session_closed and taken * appropriate action. */ - if (cfg.close_on_exit == COE_ALWAYS || - cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0); + if (cfg.close_on_exit == FORCE_ON || + cfg.close_on_exit == AUTO) PostQuitMessage(0); else { + char morestuff[100]; session_closed = TRUE; - SetWindowText(hwnd, "PuTTY (inactive)"); + sprintf(morestuff, "%.70s (inactive)", appname); + set_icon(NULL, morestuff); + set_title(NULL, morestuff); MessageBox(hwnd, "Connection closed by remote host", - "PuTTY", MB_OK | MB_ICONINFORMATION); + appname, MB_OK | MB_ICONINFORMATION); } } } @@ -959,6 +1003,38 @@ static void cfgtopalette(void) defpal[i].rgbtGreen = cfg.colours[w][1]; defpal[i].rgbtBlue = cfg.colours[w][2]; } + + /* Override with system colours if appropriate */ + if (cfg.system_colour) + systopalette(); +} + +/* + * Override bit of defpal with colours from the system. + * (NB that this takes a copy the system colours at the time this is called, + * so subsequent colour scheme changes don't take effect. To fix that we'd + * probably want to be using GetSysColorBrush() and the like.) + */ +static void systopalette(void) +{ + int i; + static const struct { int nIndex; int norm; int bold; } or[] = + { + { COLOR_WINDOWTEXT, 16, 17 }, /* Default Foreground */ + { COLOR_WINDOW, 18, 19 }, /* Default Background */ + { COLOR_HIGHLIGHTTEXT, 20, 21 }, /* Cursor Text */ + { COLOR_HIGHLIGHT, 22, 23 }, /* Cursor Colour */ + }; + + for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) { + COLORREF colour = GetSysColor(or[i].nIndex); + defpal[or[i].norm].rgbtRed = + defpal[or[i].bold].rgbtRed = GetRValue(colour); + defpal[or[i].norm].rgbtGreen = + defpal[or[i].bold].rgbtGreen = GetGValue(colour); + defpal[or[i].norm].rgbtBlue = + defpal[or[i].bold].rgbtBlue = GetBValue(colour); + } } /* @@ -970,6 +1046,10 @@ static void init_palette(void) HDC hdc = GetDC(hwnd); if (hdc) { if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { + /* + * This is a genuine case where we must use smalloc + * because the snew macros can't cope. + */ logpal = smalloc(sizeof(*logpal) - sizeof(logpal->palPalEntry) + NCOLOURS * sizeof(PALETTEENTRY)); @@ -1002,6 +1082,36 @@ static void init_palette(void) } /* + * This is a wrapper to ExtTextOut() to force Windows to display + * the precise glyphs we give it. Otherwise it would do its own + * bidi and Arabic shaping, and we would end up uncertain which + * characters it had put where. + */ +static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc, + unsigned short *lpString, UINT cbCount, + CONST INT *lpDx) +{ + + GCP_RESULTSW gcpr; + char *buffer = snewn(cbCount*2+2, char); + char *classbuffer = snewn(cbCount, char); + memset(&gcpr, 0, sizeof(gcpr)); + memset(buffer, 0, cbCount*2+2); + memset(classbuffer, GCPCLASS_NEUTRAL, cbCount); + + gcpr.lStructSize = sizeof(gcpr); + gcpr.lpGlyphs = (void *)buffer; + gcpr.lpClass = classbuffer; + gcpr.nGlyphs = cbCount; + + GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr, + FLI_MASK | GCP_CLASSIN); + + ExtTextOut(hdc, x, y, ETO_GLYPH_INDEX | ETO_CLIPPED | ETO_OPAQUE, lprc, + buffer, cbCount, lpDx); +} + +/* * Initialise all the fonts we will need initially. There may be as many as * three or as few as one. The other (poentially) twentyone fonts are done * if/when they are needed. @@ -1033,7 +1143,7 @@ static void init_fonts(int pick_width, int pick_height) bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; und_mode = UND_FONT; - if (cfg.fontisbold) { + if (cfg.font.isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1046,7 +1156,7 @@ static void init_fonts(int pick_width, int pick_height) if (pick_height) font_height = pick_height; else { - font_height = cfg.fontheight; + font_height = cfg.font.height; if (font_height > 0) { font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); @@ -1058,9 +1168,9 @@ static void init_fonts(int pick_width, int pick_height) 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) + FIXED_PITCH | FF_DONTCARE, cfg.font.name) - f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE); + f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE); lfont.lfHeight = font_height; lfont.lfWidth = font_width; @@ -1070,12 +1180,12 @@ static void init_fonts(int pick_width, int pick_height) lfont.lfItalic = FALSE; lfont.lfUnderline = FALSE; lfont.lfStrikeOut = FALSE; - lfont.lfCharSet = cfg.fontcharset; + lfont.lfCharSet = cfg.font.charset; lfont.lfOutPrecision = OUT_DEFAULT_PRECIS; lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; lfont.lfQuality = DEFAULT_QUALITY; lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; - strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE); + strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE); SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); @@ -1098,19 +1208,18 @@ static void init_fonts(int pick_width, int pick_height) /* !!! Yes the next line is right */ if (cset == OEM_CHARSET) - font_codepage = GetOEMCP(); + ucsdata.font_codepage = GetOEMCP(); else - if (TranslateCharsetInfo - ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage = - info.ciACP; + if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET)) + ucsdata.font_codepage = info.ciACP; else - font_codepage = -1; + ucsdata.font_codepage = -1; - GetCPInfo(font_codepage, &cpinfo); - dbcs_screenfont = (cpinfo.MaxCharSize > 1); + GetCPInfo(ucsdata.font_codepage, &cpinfo); + ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); } - f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE); + f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE); /* * Some fonts, e.g. 9-pt Courier, draw their underlines @@ -1161,7 +1270,7 @@ static void init_fonts(int pick_width, int pick_height) } if (bold_mode == BOLD_FONT) { - f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE); + f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE); } #undef f @@ -1195,7 +1304,7 @@ static void init_fonts(int pick_width, int pick_height) } fontflag[0] = fontflag[1] = fontflag[2] = 1; - init_ucs(); + init_ucs(&cfg, &ucsdata); } static void another_font(int fontno) @@ -1212,7 +1321,7 @@ static void another_font(int fontno) if (basefont != fontno && !fontflag[basefont]) another_font(basefont); - if (cfg.fontisbold) { + if (cfg.font.isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1220,10 +1329,10 @@ static void another_font(int fontno) fw_bold = FW_BOLD; } - c = cfg.fontcharset; + c = cfg.font.charset; w = fw_dontcare; u = FALSE; - s = cfg.font; + s = cfg.font.name; x = font_width; if (fontno & FONT_WIDE) @@ -1257,7 +1366,7 @@ static void deinit_fonts(void) } } -void request_resize(int w, int h) +void request_resize(void *frontend, int w, int h) { int width, height; @@ -1533,7 +1642,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) if (send_raw_mouse && !(cfg.mouse_override && shift)) { lastbtn = MBT_NOTHING; - term_mouse(term, b, MA_CLICK, x, y, shift, ctrl, alt); + term_mouse(term, b, translate_button(b), MA_CLICK, + x, y, shift, ctrl, alt); return; } @@ -1546,7 +1656,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) lastact = MA_CLICK; } if (lastact != MA_NOTHING) - term_mouse(term, b, lastact, x, y, shift, ctrl, alt); + term_mouse(term, b, translate_button(b), lastact, + x, y, shift, ctrl, alt); lasttime = thistime; } @@ -1554,14 +1665,14 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT) * into a cooked one (SELECT, EXTEND, PASTE). */ -Mouse_Button translate_button(Mouse_Button button) +static Mouse_Button translate_button(Mouse_Button button) { if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) - return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND; + return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND; if (button == MBT_RIGHT) - return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE; + return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE; return 0; /* shouldn't happen */ } @@ -1635,18 +1746,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_CREATE: break; case WM_CLOSE: - show_mouseptr(1); - 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); + { + char *str; + show_mouseptr(1); + str = dupprintf("%s Exit Confirmation", appname); + if (!cfg.warn_on_close || session_closed || + MessageBox(hwnd, + "Are you sure you want to close this session?", + str, MB_ICONWARNING | MB_OKCANCEL) == IDOK) + DestroyWindow(hwnd); + sfree(str); + } return 0; case WM_DESTROY: show_mouseptr(1); PostQuitMessage(0); return 0; + case WM_COMMAND: case WM_SYSCOMMAND: switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ case IDM_SHOWLOG: @@ -1690,10 +1806,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, sprintf(c, "putty &%p", filemap); cl = c; } else if (wParam == IDM_SAVEDSESS) { - if ((lParam - IDM_SAVED_MIN) / 16 < nsessions) { + if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) { char *session = - sessions[(lParam - IDM_SAVED_MIN) / 16]; - cl = smalloc(16 + strlen(session)); + sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16]; + cl = snewn(16 + strlen(session), char); /* 8, but play safe */ if (!cl) cl = NULL; @@ -1748,11 +1864,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } } - if (strcmp(prev_cfg.logfilename, cfg.logfilename) || - prev_cfg.logtype != cfg.logtype) { - logfclose(); /* reset logging */ - logfopen(); - } + /* Pass new config data to the logging module */ + log_reconfig(logctx, &cfg); sfree(logpal); /* @@ -1767,8 +1880,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, cfgtopalette(); init_palette(); - /* Give terminal a heads-up on miscellaneous stuff */ - term_reconfig(term); + /* Pass new config data to the terminal */ + term_reconfig(term, &cfg); + + /* Pass new config data to the back end */ + back->reconfig(backhandle, &cfg); /* Screen size changed ? */ if (cfg.height != prev_cfg.height || @@ -1841,18 +1957,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, init_lvl = 2; } - set_title(cfg.wintitle); + set_title(NULL, cfg.wintitle); if (IsIconic(hwnd)) { SetWindowText(hwnd, cfg.win_name_always ? window_name : icon_name); } - if (strcmp(cfg.font, prev_cfg.font) != 0 || + if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 || strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 || - cfg.fontisbold != prev_cfg.fontisbold || - cfg.fontheight != prev_cfg.fontheight || - cfg.fontcharset != prev_cfg.fontcharset || + cfg.font.isbold != prev_cfg.font.isbold || + cfg.font.height != prev_cfg.font.height || + cfg.font.charset != prev_cfg.font.charset || cfg.vtmode != prev_cfg.vtmode || cfg.bold_colour != prev_cfg.bold_colour || cfg.resize_action == RESIZE_DISABLED || @@ -1868,6 +1984,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case IDM_COPYALL: term_copyall(term); break; + case IDM_PASTE: + term_do_paste(term); + break; case IDM_CLRSB: term_clrsb(term); break; @@ -1875,58 +1994,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, term_pwron(term); ldisc_send(ldisc, NULL, 0, 0); break; - case IDM_TEL_AYT: - back->special(backhandle, TS_AYT); - net_pending_errors(); - break; - case IDM_TEL_BRK: - back->special(backhandle, TS_BRK); - net_pending_errors(); - break; - case IDM_TEL_SYNCH: - back->special(backhandle, TS_SYNCH); - net_pending_errors(); - break; - case IDM_TEL_EC: - back->special(backhandle, TS_EC); - net_pending_errors(); - break; - case IDM_TEL_EL: - back->special(backhandle, TS_EL); - net_pending_errors(); - break; - case IDM_TEL_GA: - back->special(backhandle, TS_GA); - net_pending_errors(); - break; - case IDM_TEL_NOP: - back->special(backhandle, TS_NOP); - net_pending_errors(); - break; - case IDM_TEL_ABORT: - back->special(backhandle, TS_ABORT); - net_pending_errors(); - break; - case IDM_TEL_AO: - back->special(backhandle, TS_AO); - net_pending_errors(); - break; - case IDM_TEL_IP: - back->special(backhandle, TS_IP); - net_pending_errors(); - break; - case IDM_TEL_SUSP: - back->special(backhandle, TS_SUSP); - net_pending_errors(); - break; - case IDM_TEL_EOR: - back->special(backhandle, TS_EOR); - net_pending_errors(); - break; - case IDM_TEL_EOF: - back->special(backhandle, TS_EOF); - net_pending_errors(); - break; case IDM_ABOUT: showabout(hwnd); break; @@ -1961,6 +2028,22 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) { SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam); } + if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) { + int i = (wParam - IDM_SPECIAL_MIN) / 0x10; + int j; + /* + * Ensure we haven't been sent a bogus SYSCOMMAND + * which would cause us to reference invalid memory + * and crash. Perhaps I'm just too paranoid here. + */ + for (j = 0; j < i; j++) + if (!specials || !specials[j].name) + break; + if (j == i) { + back->special(backhandle, specials[i].code); + net_pending_errors(); + } + } } break; @@ -1975,6 +2058,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: + if (message == WM_RBUTTONDOWN && + ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) { + POINT cursorpos; + + show_mouseptr(1); /* make sure pointer is visible */ + GetCursorPos(&cursorpos); + TrackPopupMenu(popup_menus[CTXMENU].menu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, + cursorpos.x, cursorpos.y, + 0, hwnd, NULL); + break; + } { int button, press; @@ -2013,11 +2108,42 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * window, we put up the System menu instead of doing * selection. */ - if (is_full_screen() && press && button == MBT_LEFT && - X_POS(lParam) == 0 && Y_POS(lParam) == 0) { - SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0); - return 0; + { + char mouse_on_hotspot = 0; + POINT pt; + + GetCursorPos(&pt); +#ifndef NO_MULTIMON + { + HMONITOR mon; + MONITORINFO mi; + + mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL); + + if (mon != NULL) { + mi.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(mon, &mi); + + if (mi.rcMonitor.left == pt.x && + mi.rcMonitor.top == pt.y) { + mouse_on_hotspot = 1; + } + CloseHandle(mon); + } + } +#else + if (pt.x == 0 && pt.y == 0) { + mouse_on_hotspot = 1; + } +#endif + if (is_full_screen() && press && + button == MBT_LEFT && mouse_on_hotspot) { + SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, + MAKELPARAM(pt.x, pt.y)); + return 0; + } } + if (press) { click(button, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), @@ -2025,7 +2151,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, is_alt_pressed()); SetCapture(hwnd); } else { - term_mouse(term, button, MA_RELEASE, + term_mouse(term, button, translate_button(button), MA_RELEASE, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); @@ -2050,7 +2176,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, b = MBT_MIDDLE; else b = MBT_RIGHT; - term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)), + term_mouse(term, b, translate_button(b), MA_DRAG, + TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); } @@ -2058,7 +2185,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_NCMOUSEMOVE: show_mouseptr(1); noise_ultralight(lParam); - return 0; + break; case WM_IGNORE_CLIP: ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */ break; @@ -2080,7 +2207,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, (p.rcPaint.left-offset_width)/font_width, (p.rcPaint.top-offset_height)/font_height, (p.rcPaint.right-offset_width-1)/font_width, - (p.rcPaint.bottom-offset_height-1)/font_height); + (p.rcPaint.bottom-offset_height-1)/font_height, + is_alt_pressed()); if (p.fErase || p.rcPaint.left < offset_width || @@ -2187,7 +2315,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * 1) Keep the sizetip uptodate * 2) Make sure the window size is _stepped_ in units of the font size. */ - if (cfg.resize_action != RESIZE_FONT && !alt_pressed) { + if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) { int width, height, w, h, ew, eh; LPRECT r = (LPRECT) lParam; @@ -2348,7 +2476,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * down the connection during an NT opaque drag.) */ if (resizing) { - if (cfg.resize_action != RESIZE_FONT && !alt_pressed) { + if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) { need_backend_resize = TRUE; w = (width-cfg.window_border*2) / font_width; if (w < 1) w = 1; @@ -2391,7 +2519,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case WM_PALETTECHANGED: if ((HWND) wParam != hwnd && pal != NULL) { - HDC hdc = get_ctx(); + HDC hdc = get_ctx(NULL); if (hdc) { if (RealizePalette(hdc) > 0) UpdateColors(hdc); @@ -2401,7 +2529,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case WM_QUERYNEWPALETTE: if (pal != NULL) { - HDC hdc = get_ctx(); + HDC hdc = get_ctx(NULL); if (hdc) { if (RealizePalette(hdc) > 0) UpdateColors(hdc); @@ -2499,7 +2627,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (n > 0) { int i; - buff = (char*) smalloc(n); + buff = snewn(n, char); ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n); /* * Jaeyoun Chung reports that Korean character @@ -2550,9 +2678,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SetCursor(LoadCursor(NULL, IDC_ARROW)); return TRUE; } + break; + case WM_AGENT_CALLBACK: + { + struct agent_callback *c = (struct agent_callback *)lParam; + c->callback(c->callback_ctx, c->data, c->len); + sfree(c); + } + return 0; default: if (message == wm_mousewheel || message == WM_MOUSEWHEEL) { - int shift_pressed=0, control_pressed=0, alt_pressed=0; + int shift_pressed=0, control_pressed=0; if (message == WM_MOUSEWHEEL) { wheel_accumulator += (short)HIWORD(wParam); @@ -2584,12 +2720,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (send_raw_mouse && !(cfg.mouse_override && shift_pressed)) { /* send a mouse-down followed by a mouse up */ - term_mouse(term, b, + term_mouse(term, b, translate_button(b), MA_CLICK, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), shift_pressed, control_pressed, is_alt_pressed()); - term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)), + term_mouse(term, b, translate_button(b), + MA_RELEASE, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), shift_pressed, control_pressed, is_alt_pressed()); } else { @@ -2612,7 +2749,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * helper software tracks the system caret, so we should arrange to * have one.) */ -void sys_cursor(int x, int y) +void sys_cursor(void *frontend, int x, int y) { int cx, cy; @@ -2686,7 +2823,7 @@ void do_text(Context ctx, int x, int y, char *text, int len, int i; if (len > IpDxLEN) { sfree(IpDx); - IpDx = smalloc((len + 16) * sizeof(int)); + IpDx = snewn(len + 16, int); IpDxLEN = (len + 16); } for (i = 0; i < IpDxLEN; i++) @@ -2745,8 +2882,8 @@ void do_text(Context ctx, int x, int y, char *text, int len, if (lattr == LATTR_TOP || lattr == LATTR_BOT) text_adjust *= 2; attr &= ~CSET_MASK; - text[0] = (char) (unitab_xterm['q'] & CHAR_MASK); - attr |= (unitab_xterm['q'] & CSET_MASK); + text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK); + attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK); if (attr & ATTR_UNDER) { attr &= ~ATTR_UNDER; force_manual_underline = 1; @@ -2765,8 +2902,10 @@ void do_text(Context ctx, int x, int y, char *text, int len, if ((attr & CSET_MASK) == ATTR_OEMCP) nfont |= FONT_OEM; - nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); - nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); + nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); + nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0); + nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); + nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0); if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD)) nfont |= FONT_BOLD; if (und_mode == UND_FONT && (attr & ATTR_UNDER)) @@ -2788,9 +2927,9 @@ void do_text(Context ctx, int x, int y, char *text, int len, nbg = t; } if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) - nfg++; + nfg |= 1; if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) - nbg++; + nbg |= 1; fg = colours[nfg]; bg = colours[nbg]; SelectObject(hdc, fonts[nfont]); @@ -2807,7 +2946,7 @@ void do_text(Context ctx, int x, int y, char *text, int len, line_box.right = font_width*term->cols+offset_width; /* We're using a private area for direct to font. (512 chars.) */ - if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) { + if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) { /* Ho Hum, dbcs fonts are a PITA! */ /* To display on W9x I have to convert to UCS */ static wchar_t *uni_buf = 0; @@ -2815,20 +2954,21 @@ void do_text(Context ctx, int x, int y, char *text, int len, int nlen, mptr; if (len > uni_len) { sfree(uni_buf); - uni_buf = smalloc((uni_len = len) * sizeof(wchar_t)); + uni_len = len; + uni_buf = snewn(uni_len, wchar_t); } for(nlen = mptr = 0; mptr= ' ' && (uc&CHAR_MASK)<= '~') @@ -3061,6 +3205,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, HKL kbd_layout = GetKeyboardLayout(0); + /* keys is for ToAsciiEx. There's some ick here, see below. */ static WORD keys[3]; static int compose_char = 0; static WPARAM compose_key = 0; @@ -3186,8 +3331,6 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } } - alt_pressed = (left_alt && key_down); - scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF)); shift_state = ((keystate[VK_SHIFT] & 0x80) != 0) + ((keystate[VK_CONTROL] & 0x80) != 0) * 2; @@ -3277,10 +3420,18 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); return 0; } + if (wParam == VK_PRIOR && shift_state == 2) { + SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); + return 0; + } if (wParam == VK_NEXT && shift_state == 1) { SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); return 0; } + if (wParam == VK_NEXT && shift_state == 2) { + SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); + return 0; + } if (wParam == VK_INSERT && shift_state == 1) { term_do_paste(term); return 0; @@ -3475,7 +3626,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, *p++ = "\000\033\034\035\036\037\177"[wParam - '2']; return p - output; } - if (shift_state == 2 && wParam == 0xBD) { + if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) { *p++ = 0x1F; return p - output; } @@ -3483,6 +3634,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, *p++ = 0x1C; return p - output; } + if (shift_state == 3 && wParam == 0xDE) { + *p++ = 0x1E; /* Ctrl-~ == Ctrl-^ in xterm at least */ + return p - output; + } if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) { *p++ = '\r'; *p++ = '\n'; @@ -3742,11 +3897,31 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, keystate[VK_CAPITAL] = 0; } - r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout); + /* XXX how do we know what the max size of the keys array should + * be is? There's indication on MS' website of an Inquire/InquireEx + * functioning returning a KBINFO structure which tells us. */ + if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) { + /* XXX 'keys' parameter is declared in MSDN documentation as + * 'LPWORD lpChar'. + * The experience of a French user indicates that on + * Win98, WORD[] should be passed in, but on Win2K, it should + * be BYTE[]. German WinXP and my Win2K with "US International" + * driver corroborate this. + * Experimentally I've conditionalised the behaviour on the + * Win9x/NT split, but I suspect it's worse than that. + * See wishlist item `win-dead-keys' for more horrible detail + * and speculations. */ + BYTE keybs[3]; + int i; + r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout); + for (i=0; i<3; i++) keys[i] = keybs[i]; + } else { + r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout); + } #ifdef SHOW_TOASCII_RESULT if (r == 1 && !key_down) { if (alt_sum) { - if (in_utf(term) || dbcs_screenfont) + if (in_utf(term) || ucsdata.dbcs_screenfont) debug((", (U+%04x)", alt_sum)); else debug((", LCH(%d)", alt_sum)); @@ -3799,7 +3974,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (!key_down) { if (alt_sum) { - if (in_utf(term) || dbcs_screenfont) { + if (in_utf(term) || ucsdata.dbcs_screenfont) { keybuf = alt_sum; term_seen_key_event(term); luni_send(ldisc, &keybuf, 1, 1); @@ -3818,9 +3993,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, ldisc_send(ldisc, &ch, 1, 1); } alt_sum = 0; - } else + } else { term_seen_key_event(term); lpage_send(ldisc, kbd_codepage, &ch, 1, 1); + } } else { if(capsOn && ch < 0x80) { WCHAR cbuf[2]; @@ -3849,7 +4025,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (!left_alt) keys[0] = 0; /* If we will be using alt_sum fix the 256s */ - else if (keys[0] && (in_utf(term) || dbcs_screenfont)) + else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) keys[0] = 10; } @@ -3866,7 +4042,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return -1; } -void request_paste(void) +void request_paste(void *frontend) { /* * In Windows, pasting is synchronous: we can read the @@ -3876,25 +4052,25 @@ void request_paste(void) term_do_paste(term); } -void set_title(char *title) +void set_title(void *frontend, char *title) { sfree(window_name); - window_name = smalloc(1 + strlen(title)); + window_name = snewn(1 + strlen(title), char); strcpy(window_name, title); if (cfg.win_name_always || !IsIconic(hwnd)) SetWindowText(hwnd, title); } -void set_icon(char *title) +void set_icon(void *frontend, char *title) { sfree(icon_name); - icon_name = smalloc(1 + strlen(title)); + icon_name = snewn(1 + strlen(title), char); strcpy(icon_name, title); if (!cfg.win_name_always && IsIconic(hwnd)) SetWindowText(hwnd, title); } -void set_sbar(int total, int start, int page) +void set_sbar(void *frontend, int total, int start, int page) { SCROLLINFO si; @@ -3911,7 +4087,7 @@ void set_sbar(int total, int start, int page) SetScrollInfo(hwnd, SB_VERT, &si, TRUE); } -Context get_ctx(void) +Context get_ctx(void *frontend) { HDC hdc; if (hwnd) { @@ -3942,7 +4118,7 @@ static void real_palette_set(int n, int r, int g, int b) colours[n] = RGB(r, g, b); } -void palette_set(int n, int r, int g, int b) +void palette_set(void *frontend, int n, int r, int g, int b) { static const int first[21] = { 0, 2, 4, 6, 8, 10, 12, 14, @@ -3953,14 +4129,14 @@ void palette_set(int n, int r, int g, int b) if (first[n] >= 18) real_palette_set(first[n] + 1, r, g, b); if (pal) { - HDC hdc = get_ctx(); + HDC hdc = get_ctx(frontend); UnrealizeObject(pal); RealizePalette(hdc); free_ctx(hdc); } } -void palette_reset(void) +void palette_reset(void *frontend) { int i; @@ -3981,13 +4157,13 @@ void palette_reset(void) if (pal) { HDC hdc; SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry); - hdc = get_ctx(); + hdc = get_ctx(frontend); RealizePalette(hdc); free_ctx(hdc); } } -void write_aclip(char *data, int len, int must_deselect) +void write_aclip(void *frontend, char *data, int len, int must_deselect) { HGLOBAL clipdata; void *lock; @@ -4019,7 +4195,7 @@ void write_aclip(char *data, int len, int must_deselect) /* * Note: unlike write_aclip() this will not append a nul. */ -void write_clip(wchar_t * data, int len, int must_deselect) +void write_clip(void *frontend, wchar_t * data, int len, int must_deselect) { HGLOBAL clipdata, clipdata2, clipdata3; int len2; @@ -4058,10 +4234,10 @@ void write_clip(wchar_t * data, int len, int must_deselect) get_unitab(CP_ACP, unitab, 0); - rtfsize = 100 + strlen(cfg.font); - rtf = smalloc(rtfsize); + rtfsize = 100 + strlen(cfg.font.name); + rtf = snewn(rtfsize, char); sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0", - GetACP(), cfg.font); + GetACP(), cfg.font.name); rtflen = strlen(rtf); /* @@ -4124,7 +4300,7 @@ void write_clip(wchar_t * data, int len, int must_deselect) if (rtfsize < rtflen + totallen + 3) { rtfsize = rtflen + totallen + 512; - rtf = srealloc(rtf, rtfsize); + rtf = sresize(rtf, rtfsize, char); } strcpy(rtf + rtflen, before); rtflen += blen; @@ -4182,7 +4358,7 @@ void write_clip(wchar_t * data, int len, int must_deselect) SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0); } -void get_clip(wchar_t ** p, int *len) +void get_clip(void *frontend, wchar_t ** p, int *len) { static HGLOBAL clipdata = NULL; static wchar_t *converted = 0; @@ -4212,7 +4388,7 @@ void get_clip(wchar_t ** p, int *len) CloseClipboard(); s = GlobalLock(clipdata); i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0); - *p = converted = smalloc(i * sizeof(wchar_t)); + *p = converted = snewn(i, wchar_t); MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i); *len = i - 1; return; @@ -4229,7 +4405,7 @@ void get_clip(wchar_t ** p, int *len) * Move `lines' lines from position `from' to position `to' in the * window. */ -void optimised_move(int to, int from, int lines) +void optimised_move(void *frontend, int to, int from, int lines) { RECT r; int min, max; @@ -4251,12 +4427,14 @@ void optimised_move(int to, int from, int lines) void fatalbox(char *fmt, ...) { va_list ap; - char stuff[200]; + char *stuff, morestuff[100]; va_start(ap, fmt); - vsprintf(stuff, fmt, ap); + stuff = dupvprintf(fmt, ap); va_end(ap); - MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK); + sprintf(morestuff, "%.70s Fatal Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); + sfree(stuff); cleanup_exit(1); } @@ -4266,13 +4444,15 @@ void fatalbox(char *fmt, ...) void modalfatalbox(char *fmt, ...) { va_list ap; - char stuff[200]; + char *stuff, morestuff[100]; va_start(ap, fmt); - vsprintf(stuff, fmt, ap); + stuff = dupvprintf(fmt, ap); va_end(ap); - MessageBox(hwnd, stuff, "PuTTY Fatal Error", + sprintf(morestuff, "%.70s Fatal Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); + sfree(stuff); cleanup_exit(1); } @@ -4315,7 +4495,7 @@ static void flash_window(int mode) /* * Beep. */ -void beep(int mode) +void beep(void *frontend, int mode) { if (mode == BELL_DEFAULT) { /* @@ -4337,14 +4517,34 @@ void beep(int mode) */ lastbeep = GetTickCount(); } else if (mode == BELL_WAVEFILE) { - if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) { - char buf[sizeof(cfg.bell_wavefile) + 80]; + if (!PlaySound(cfg.bell_wavefile.path, NULL, + SND_ASYNC | SND_FILENAME)) { + char buf[sizeof(cfg.bell_wavefile.path) + 80]; + char otherbuf[100]; sprintf(buf, "Unable to play sound file\n%s\n" - "Using default sound instead", cfg.bell_wavefile); - MessageBox(hwnd, buf, "PuTTY Sound Error", + "Using default sound instead", cfg.bell_wavefile.path); + sprintf(otherbuf, "%.70s Sound Error", appname); + MessageBox(hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); cfg.beep = BELL_DEFAULT; } + } else if (mode == BELL_PCSPEAKER) { + static long lastbeep = 0; + long beepdiff; + + beepdiff = GetTickCount() - lastbeep; + if (beepdiff >= 0 && beepdiff < 50) + return; + + /* + * We must beep in different ways depending on whether this + * is a 95-series or NT-series OS. + */ + if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) + Beep(800, 100); + else + MessageBeep(-1); + lastbeep = GetTickCount(); } /* Otherwise, either visual bell or disabled; do nothing here */ if (!term->has_focus) { @@ -4356,7 +4556,7 @@ void beep(int mode) * Minimise or restore the window in response to a server-side * request. */ -void set_iconic(int iconic) +void set_iconic(void *frontend, int iconic) { if (IsIconic(hwnd)) { if (!iconic) @@ -4370,7 +4570,7 @@ void set_iconic(int iconic) /* * Move the window in response to a server-side request. */ -void move_window(int x, int y) +void move_window(void *frontend, int x, int y) { if (cfg.resize_action == RESIZE_DISABLED || cfg.resize_action == RESIZE_FONT || @@ -4384,7 +4584,7 @@ void move_window(int x, int y) * Move the window to the top or bottom of the z-order in response * to a server-side request. */ -void set_zorder(int top) +void set_zorder(void *frontend, int top) { if (cfg.alwaysontop) return; /* ignore */ @@ -4395,7 +4595,7 @@ void set_zorder(int top) /* * Refresh the window in response to a server-side request. */ -void refresh_window(void) +void refresh_window(void *frontend) { InvalidateRect(hwnd, NULL, TRUE); } @@ -4404,7 +4604,7 @@ void refresh_window(void) * Maximise or restore the window in response to a server-side * request. */ -void set_zoomed(int zoomed) +void set_zoomed(void *frontend, int zoomed) { if (IsZoomed(hwnd)) { if (!zoomed) @@ -4418,7 +4618,7 @@ void set_zoomed(int zoomed) /* * Report whether the window is iconic, for terminal reports. */ -int is_iconic(void) +int is_iconic(void *frontend) { return IsIconic(hwnd); } @@ -4426,7 +4626,7 @@ int is_iconic(void) /* * Report the window's position, for terminal reports. */ -void get_window_pos(int *x, int *y) +void get_window_pos(void *frontend, int *x, int *y) { RECT r; GetWindowRect(hwnd, &r); @@ -4437,7 +4637,7 @@ void get_window_pos(int *x, int *y) /* * Report the window's pixel size, for terminal reports. */ -void get_window_pixels(int *x, int *y) +void get_window_pixels(void *frontend, int *x, int *y) { RECT r; GetWindowRect(hwnd, &r); @@ -4448,7 +4648,7 @@ void get_window_pixels(int *x, int *y) /* * Return the window or icon title. */ -char *get_window_title(int icon) +char *get_window_title(void *frontend, int icon) { return icon ? icon_name : window_name; } @@ -4456,7 +4656,7 @@ char *get_window_title(int icon) /* * See if we're in full-screen mode. */ -int is_full_screen() +static int is_full_screen() { if (!IsZoomed(hwnd)) return FALSE; @@ -4470,7 +4670,7 @@ int is_full_screen() * one monitor is present. */ static int get_fullscreen_rect(RECT * ss) { -#ifdef MONITOR_DEFAULTTONEAREST +#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON) HMONITOR mon; MONITORINFO mi; mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); @@ -4495,7 +4695,7 @@ static int get_fullscreen_rect(RECT * ss) * Go full-screen. This should only be called when we are already * maximised. */ -void make_full_screen() +static void make_full_screen() { DWORD style; RECT ss; @@ -4529,7 +4729,7 @@ void make_full_screen() /* * Clear the full-screen attributes. */ -void clear_full_screen() +static void clear_full_screen() { DWORD oldstyle, style; @@ -4559,7 +4759,7 @@ void clear_full_screen() /* * Toggle full-screen mode. */ -void flip_full_screen() +static void flip_full_screen() { if (is_full_screen()) { ShowWindow(hwnd, SW_RESTORE); @@ -4581,3 +4781,19 @@ void frontend_keypress(void *handle) */ return; } + +int from_backend(void *frontend, int is_stderr, const char *data, int len) +{ + return term_data(term, is_stderr, data, len); +} + +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) +{ + struct agent_callback *c = snew(struct agent_callback); + c->callback = callback; + c->callback_ctx = callback_ctx; + c->data = data; + c->len = len; + PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c); +}