X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/b08600c44afcea44391d6a351e5b4107a1d8309d..0c33d3a6483e2493a29d662afc79a8c86dc6dfc5:/windows/window.c diff --git a/windows/window.c b/windows/window.c index 4855de54..b477d6ae 100644 --- a/windows/window.c +++ b/windows/window.c @@ -10,6 +10,10 @@ #include #include +#ifndef NO_MULTIMON +#define COMPILE_MULTIMON_STUBS +#endif + #define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "terminal.h" @@ -17,11 +21,8 @@ #include "win_res.h" #ifndef NO_MULTIMON -#if WINVER < 0x0500 -#define COMPILE_MULTIMON_STUBS #include #endif -#endif #include #include @@ -60,6 +61,7 @@ #define WM_IGNORE_CLIP (WM_APP + 2) #define WM_FULLSCR_ON_MAX (WM_APP + 3) #define WM_AGENT_CALLBACK (WM_APP + 4) +#define WM_GOT_CLIPDATA (WM_APP + 6) /* Needed for Chinese support and apparently not always defined. */ #ifndef VK_PROCESSKEY @@ -78,7 +80,7 @@ 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 conftopalette(void); static void systopalette(void); static void init_palette(void); static void init_fonts(int, int); @@ -86,24 +88,28 @@ static void another_font(int); static void deinit_fonts(void); static void set_input_locale(HKL); static void update_savedsess_menu(void); +static void init_flashwindow(void); static int is_full_screen(void); static void make_full_screen(void); static void clear_full_screen(void); static void flip_full_screen(void); +static int process_clipdata(HGLOBAL clipdata, int unicode); /* Window layout information */ static void reset_window(int); static int extra_width, extra_height; -static int font_width, font_height, font_dualwidth; +static int font_width, font_height, font_dualwidth, font_varpitch; static int offset_width, offset_height; static int was_zoomed = 0; static int prev_rows, prev_cols; -static void enact_netevent(WPARAM, LPARAM); +static int pending_netevent = 0; +static WPARAM pend_netevent_wParam = 0; +static LPARAM pend_netevent_lParam = 0; +static void enact_pending_netevent(void); static void flash_window(int mode); static void sys_cursor_update(void); -static int is_shift_pressed(void); static int get_fullscreen_rect(RECT * ss); static int caret_x = -1, caret_y = -1; @@ -122,6 +128,9 @@ static const struct telnet_special *specials = NULL; static HMENU specials_menu = NULL; static int n_specials = 0; +static wchar_t *clipboard_contents; +static size_t clipboard_length; + #define TIMING_TIMER_ID 1234 static long timing_next_time; @@ -131,7 +140,11 @@ static struct { enum { SYSMENU, CTXMENU }; static HMENU savedsess_menu; -Config cfg; /* exported to windlg.c */ +Conf *conf; /* exported to windlg.c */ + +static void conf_cache_data(void); +int cursor_type; +int vtmode; static struct sesslist sesslist; /* for saved-session menu */ @@ -161,8 +174,9 @@ static HFONT fonts[FONT_MAXNO]; static LOGFONT lfont; static int fontflag[FONT_MAXNO]; static enum { - BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT -} bold_mode; + BOLD_NONE, BOLD_SHADOW, BOLD_FONT +} bold_font_mode; +static int bold_colours; static enum { UND_LINE, UND_FONT } und_mode; @@ -193,6 +207,12 @@ static int compose_state = 0; static UINT wm_mousewheel = WM_MOUSEWHEEL; +#define IS_HIGH_VARSEL(wch1, wch2) \ + ((wch1) == 0xDB40 && ((wch2) >= 0xDD00 && (wch2) <= 0xDDEF)) +#define IS_LOW_VARSEL(wch) \ + (((wch) >= 0x180B && (wch) <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR */ \ + ((wch) >= 0xFE00 && (wch) <= 0xFE0F)) /* VARIATION SELECTOR 1-16 */ + /* Dummy routine, only required in plink. */ void ldisc_update(void *frontend, int echo, int edit) { @@ -214,12 +234,7 @@ static void start_backend(void) * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ - back = NULL; - for (i = 0; backends[i].backend != NULL; i++) - if (backends[i].protocol == cfg.protocol) { - back = backends[i].backend; - break; - } + back = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (back == NULL) { char *str = dupprintf("%s Internal Error", appname); MessageBox(NULL, "Unsupported protocol number found", @@ -228,22 +243,24 @@ static void start_backend(void) cleanup_exit(1); } - error = back->init(NULL, &backhandle, &cfg, - cfg.host, cfg.port, &realhost, cfg.tcp_nodelay, - cfg.tcp_keepalives); + error = back->init(NULL, &backhandle, conf, + conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), + &realhost, + conf_get_int(conf, CONF_tcp_nodelay), + conf_get_int(conf, CONF_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_dest(&cfg), error); + "%.800s\n" "%s", conf_dest(conf), error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); exit(0); } window_name = icon_name = NULL; - if (*cfg.wintitle) { - title = cfg.wintitle; - } else { + title = conf_get_str(conf, CONF_wintitle); + if (!*title) { sprintf(msg, "%s - %s", realhost, appname); title = msg; } @@ -259,7 +276,7 @@ static void start_backend(void) /* * Set up a line discipline. */ - ldisc = ldisc_create(&cfg, term, back, backhandle, NULL); + ldisc = ldisc_create(conf, term, back, backhandle, NULL); /* * Destroy the Restart Session menu item. (This will return @@ -272,6 +289,7 @@ static void start_backend(void) DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND); } + must_close_session = FALSE; session_closed = FALSE; } @@ -293,6 +311,7 @@ static void close_session(void) back->free(backhandle); backhandle = NULL; back = NULL; + term_provide_resize_fn(term, NULL, NULL); update_specials_menu(NULL); } @@ -305,12 +324,22 @@ static void close_session(void) InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED, IDM_RESTART, "&Restart Session"); } + + /* + * Unset the 'must_close_session' flag, or else we'll come + * straight back here the next time we go round the main message + * loop - which, worse still, will be immediately (without + * blocking) because we've just triggered a WM_SETTEXT by the + * window title change above. + */ + must_close_session = FALSE; } int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; MSG msg; + HRESULT hr; int guess_width, guess_height; hinst = inst; @@ -344,30 +373,22 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT)) wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG"); + init_help(); + + init_flashwindow(); + + conf = conf_new(); + /* - * See if we can find our Help file. + * Initialize COM. */ - { - char b[2048], *p, *q, *r; - FILE *fp; - GetModuleFileName(NULL, b, sizeof(b) - 1); - r = b; - p = strrchr(b, '\\'); - if (p && p >= r) r = p+1; - q = strrchr(b, ':'); - if (q && q >= r) r = q+1; - strcpy(r, PUTTY_HELP_FILE); - if ( (fp = fopen(b, "r")) != NULL) { - help_path = dupstr(b); - fclose(fp); - } else - help_path = NULL; - strcpy(r, PUTTY_HELP_CONTENTS); - if ( (fp = fopen(b, "r")) != NULL) { - help_has_contents = TRUE; - fclose(fp); - } else - help_has_contents = FALSE; + hr = CoInitialize(NULL); + if (hr != S_OK && hr != S_FALSE) { + char *str = dupprintf("%s Fatal Error", appname); + MessageBox(NULL, "Failed to initialize COM subsystem", + str, MB_OK | MB_ICONEXCLAMATION); + sfree(str); + return 1; } /* @@ -376,60 +397,71 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { char *p; int got_host = 0; + /* By default, we bring up the config dialog, rather than launching + * a session. This gets set to TRUE if something happens to change + * that (e.g., a hostname is specified on the command-line). */ + int allow_launch = FALSE; default_protocol = be_default_protocol; /* Find the appropriate default port. */ { - int i; + Backend *b = backend_from_proto(default_protocol); 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; - } + if (b) + default_port = b->default_port; } - cfg.logtype = LGTYP_NONE; + conf_set_int(conf, CONF_logtype, LGTYP_NONE); - do_defaults(NULL, &cfg); + do_defaults(NULL, conf); p = cmdline; /* * Process a couple of command-line options which are more - * easily dealt with before the line is broken up into - * words. These are the soon-to-be-defunct @sessionname and - * the internal-use-only &sharedmemoryhandle, neither of - * which are combined with anything else. + * easily dealt with before the line is broken up into words. + * These are the old-fashioned but convenient @sessionname and + * the internal-use-only &sharedmemoryhandle, neither of which + * are combined with anything else. */ while (*p && isspace(*p)) p++; if (*p == '@') { + /* + * An initial @ means that the whole of the rest of the + * command line should be treated as the name of a saved + * session, with _no quoting or escaping_. This makes it a + * very convenient means of automated saved-session + * launching, via IDM_SAVEDSESS or Windows 7 jump lists. + */ int i = strlen(p); while (i > 1 && isspace(p[i - 1])) i--; p[i] = '\0'; - do_defaults(p + 1, &cfg); - if (!cfg_launchable(&cfg) && !do_config()) { + do_defaults(p + 1, conf); + if (!conf_launchable(conf) && !do_config()) { cleanup_exit(0); } + allow_launch = TRUE; /* allow it to be launched directly */ } else if (*p == '&') { /* * An initial & means we've been given a command line * containing the hex value of a HANDLE for a file - * mapping object, which we must then extract as a - * config. + * mapping object, which we must then interpret as a + * serialised Conf. */ HANDLE filemap; - Config *cp; - if (sscanf(p + 1, "%p", &filemap) == 1 && + void *cp; + unsigned cpsize; + if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 && (cp = MapViewOfFile(filemap, FILE_MAP_READ, - 0, 0, sizeof(Config))) != NULL) { - cfg = *cp; + 0, 0, cpsize)) != NULL) { + conf_deserialise(conf, cp, cpsize); UnmapViewOfFile(cp); CloseHandle(filemap); } else if (!do_config()) { cleanup_exit(0); } + allow_launch = TRUE; } else { /* * Otherwise, break up the command line and deal with @@ -445,7 +477,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) int ret; ret = cmdline_process_param(p, i+1 r.right - r.left) guess_width = r.right - r.left; if (guess_height > r.bottom - r.top) @@ -664,13 +685,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL; int exwinmode = 0; - if (!cfg.scrollbar) + if (!conf_get_int(conf, CONF_scrollbar)) winmode &= ~(WS_VSCROLL); - if (cfg.resize_action == RESIZE_DISABLED) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); - if (cfg.alwaysontop) + if (conf_get_int(conf, CONF_alwaysontop)) exwinmode |= WS_EX_TOPMOST; - if (cfg.sunken_edge) + if (conf_get_int(conf, CONF_sunken_edge)) exwinmode |= WS_EX_CLIENTEDGE; hwnd = CreateWindowEx(exwinmode, appname, appname, winmode, CW_USEDEFAULT, CW_USEDEFAULT, @@ -679,21 +700,23 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } /* + * Initialise the fonts, simultaneously correcting the guesses + * for font_{width,height}. + */ + init_fonts(0,0); + + /* * Initialise the terminal. (We have to do this _after_ * creating the window, since the terminal is the first thing * which will call schedule_timer(), which will in turn call * timer_change_notify() which will expect hwnd to exist.) */ - term = term_init(&cfg, &ucsdata, NULL); - logctx = log_init(NULL, &cfg); + term = term_init(conf, &ucsdata, NULL); + logctx = log_init(NULL, conf); term_provide_logctx(term, logctx); - term_size(term, cfg.height, cfg.width, cfg.savelines); - - /* - * Initialise the fonts, simultaneously correcting the guesses - * for font_{width,height}. - */ - init_fonts(0,0); + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); /* * Correct the guesses for extra_{width,height}. @@ -702,7 +725,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) RECT cr, wr; GetWindowRect(hwnd, &wr); GetClientRect(hwnd, &cr); - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = conf_get_int(conf, CONF_window_border); extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; } @@ -783,10 +806,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) 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, (conf_get_int(conf, CONF_resize_action) + == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED, + IDM_FULLSCREEN, "&Full Screen"); AppendMenu(m, MF_SEPARATOR, 0, 0); - if (help_path) + if (has_help()) AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help"); str = dupprintf("&About %s", appname); AppendMenu(m, MF_ENABLED, IDM_ABOUT, str); @@ -831,10 +855,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) sfree(handles); if (must_close_session) close_session(); - continue; - } - - sfree(handles); + } else + sfree(handles); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) @@ -855,6 +877,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* The messages seem unreliable; especially if we're being tricky */ term_set_focus(term, GetForegroundWindow() == hwnd); + if (pending_netevent) + enact_pending_netevent(); + net_pending_errors(); } @@ -877,12 +902,16 @@ void cleanup_exit(int code) DeleteObject(pal); sk_cleanup(); - if (cfg.protocol == PROT_SSH) { + if (conf_get_int(conf, CONF_protocol) == PROT_SSH) { random_save_seed(); #ifdef MSCRYPTOAPI crypto_wrapup(); #endif } + shutdown_help(); + + /* Clean up COM. */ + CoUninitialize(); exit(code); } @@ -928,6 +957,8 @@ static void update_savedsess_menu(void) AppendMenu(savedsess_menu, MF_ENABLED, IDM_SAVED_MIN + (i-1)*MENU_SAVED_STEP, sesslist.sessions[i]); + if (sesslist.nsessions <= 1) + AppendMenu(savedsess_menu, MF_GRAYED, IDM_SAVED_MIN, "(No sessions)"); } /* @@ -986,7 +1017,7 @@ void update_specials_menu(void *frontend) for (j = 0; j < lenof(popup_menus); j++) { if (specials_menu) { /* XXX does this free up all submenus? */ - DeleteMenu(popup_menus[j].menu, specials_menu, MF_BYCOMMAND); + DeleteMenu(popup_menus[j].menu, (UINT)specials_menu, MF_BYCOMMAND); DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND); } if (new_menu) { @@ -1049,7 +1080,7 @@ void set_busy_status(void *frontend, int status) */ void set_raw_mouse_mode(void *frontend, int activate) { - activate = activate && !cfg.no_mouse_rep; + activate = activate && !conf_get_int(conf, CONF_no_mouse_rep); send_raw_mouse = activate; update_mouse_pointer(); } @@ -1069,7 +1100,7 @@ void connection_fatal(void *frontend, char *fmt, ...) MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); sfree(stuff); - if (cfg.close_on_exit == FORCE_ON) + if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON) PostQuitMessage(1); else { must_close_session = TRUE; @@ -1096,7 +1127,7 @@ void cmdline_error(char *fmt, ...) /* * Actually do the job requested by a WM_NETEVENT */ -static void enact_netevent(WPARAM wParam, LPARAM lParam) +static void enact_pending_netevent(void) { static int reentering = 0; extern int select_result(WPARAM, LPARAM); @@ -1104,8 +1135,10 @@ static void enact_netevent(WPARAM wParam, LPARAM lParam) if (reentering) return; /* don't unpend the pending */ + pending_netevent = FALSE; + reentering = 1; - select_result(wParam, lParam); + select_result(pend_netevent_wParam, pend_netevent_lParam); reentering = 0; } @@ -1113,7 +1146,7 @@ static void enact_netevent(WPARAM wParam, LPARAM lParam) * Copy the colour palette from the configuration data into defpal. * This is non-trivial because the colour indices are different. */ -static void cfgtopalette(void) +static void conftopalette(void) { int i; static const int ww[] = { @@ -1124,9 +1157,9 @@ static void cfgtopalette(void) for (i = 0; i < 22; i++) { int w = ww[i]; - defpal[w].rgbtRed = cfg.colours[i][0]; - defpal[w].rgbtGreen = cfg.colours[i][1]; - defpal[w].rgbtBlue = cfg.colours[i][2]; + defpal[w].rgbtRed = conf_get_int_int(conf, CONF_colours, i*3+0); + defpal[w].rgbtGreen = conf_get_int_int(conf, CONF_colours, i*3+1); + defpal[w].rgbtBlue = conf_get_int_int(conf, CONF_colours, i*3+2); } for (i = 0; i < NEXTCOLOURS; i++) { if (i < 216) { @@ -1143,7 +1176,7 @@ static void cfgtopalette(void) } /* Override with system colours if appropriate */ - if (cfg.system_colour) + if (conf_get_int(conf, CONF_system_colour)) systopalette(); } @@ -1183,7 +1216,8 @@ static void init_palette(void) int i; HDC hdc = GetDC(hwnd); if (hdc) { - if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { + if (conf_get_int(conf, CONF_try_palette) && + GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { /* * This is a genuine case where we must use smalloc * because the snew macros can't cope. @@ -1248,9 +1282,8 @@ static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc, gcpr.lStructSize = sizeof(gcpr); gcpr.lpGlyphs = (void *)buffer; - gcpr.lpClass = classbuffer; + gcpr.lpClass = (void *)classbuffer; gcpr.nGlyphs = cbCount; - GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr, FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC); @@ -1260,8 +1293,95 @@ static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc, } /* + * The exact_textout() wrapper, unfortunately, destroys the useful + * Windows `font linking' behaviour: automatic handling of Unicode + * code points not supported in this font by falling back to a font + * which does contain them. Therefore, we adopt a multi-layered + * approach: for any potentially-bidi text, we use exact_textout(), + * and for everything else we use a simple ExtTextOut as we did + * before exact_textout() was introduced. + */ +static void general_textout(HDC hdc, int x, int y, CONST RECT *lprc, + unsigned short *lpString, UINT cbCount, + CONST INT *lpDx, int opaque) +{ + int i, j, xp, xn; + int bkmode = 0, got_bkmode = FALSE; + + xp = xn = x; + + for (i = 0; i < (int)cbCount ;) { + int rtl = is_rtl(lpString[i]); + + xn += lpDx[i]; + + for (j = i+1; j < (int)cbCount; j++) { + if (rtl != is_rtl(lpString[j])) + break; + xn += lpDx[j]; + } + + /* + * Now [i,j) indicates a maximal substring of lpString + * which should be displayed using the same textout + * function. + */ + if (rtl) { + exact_textout(hdc, xp, y, lprc, lpString+i, j-i, + font_varpitch ? NULL : lpDx+i, opaque); + } else { + ExtTextOutW(hdc, xp, y, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), + lprc, lpString+i, j-i, + font_varpitch ? NULL : lpDx+i); + } + + i = j; + xp = xn; + + bkmode = GetBkMode(hdc); + got_bkmode = TRUE; + SetBkMode(hdc, TRANSPARENT); + opaque = FALSE; + } + + if (got_bkmode) + SetBkMode(hdc, bkmode); +} + +static int get_font_width(HDC hdc, const TEXTMETRIC *tm) +{ + int ret; + /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ + if (!(tm->tmPitchAndFamily & TMPF_FIXED_PITCH)) { + ret = tm->tmAveCharWidth; + } else { +#define FIRST '0' +#define LAST '9' + ABCFLOAT widths[LAST-FIRST + 1]; + int j; + + font_varpitch = TRUE; + font_dualwidth = TRUE; + if (GetCharABCWidthsFloat(hdc, FIRST, LAST, widths)) { + ret = 0; + for (j = 0; j < lenof(widths); j++) { + int width = (int)(0.5 + widths[j].abcfA + + widths[j].abcfB + widths[j].abcfC); + if (ret < width) + ret = width; + } + } else { + ret = tm->tmMaxCharWidth; + } +#undef FIRST +#undef LAST + } + return ret; +} + +/* * 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 + * three or as few as one. The other (potentially) twenty-one fonts are done * if/when they are needed. * * We also: @@ -1280,18 +1400,23 @@ static void init_fonts(int pick_width, int pick_height) { TEXTMETRIC tm; CPINFO cpinfo; + FontSpec *font; int fontsize[3]; int i; + int quality; HDC hdc; int fw_dontcare, fw_bold; for (i = 0; i < FONT_MAXNO; i++) fonts[i] = NULL; - bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT; + bold_font_mode = conf_get_int(conf, CONF_bold_style) & 1 ? + BOLD_FONT : BOLD_NONE; + bold_colours = conf_get_int(conf, CONF_bold_style) & 2 ? TRUE : FALSE; und_mode = UND_FONT; - if (cfg.font.isbold) { + font = conf_get_fontspec(conf, CONF_font); + if (font->isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1304,7 +1429,7 @@ static void init_fonts(int pick_width, int pick_height) if (pick_height) font_height = pick_height; else { - font_height = cfg.font.height; + font_height = font->height; if (font_height > 0) { font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); @@ -1312,24 +1437,32 @@ static void init_fonts(int pick_width, int pick_height) } font_width = pick_width; + quality = conf_get_int(conf, CONF_font_quality); #define f(i,c,w,u) \ fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \ c, OUT_DEFAULT_PRECIS, \ - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), \ - FIXED_PITCH | FF_DONTCARE, cfg.font.name) + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), \ + FIXED_PITCH | FF_DONTCARE, font->name) - f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE); + f(FONT_NORMAL, font->charset, fw_dontcare, FALSE); SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont); + /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ + if (!(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) { + font_varpitch = FALSE; + font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth); + } else { + font_varpitch = TRUE; + font_dualwidth = TRUE; + } if (pick_width == 0 || pick_height == 0) { font_height = tm.tmHeight; - font_width = tm.tmAveCharWidth; + font_width = get_font_width(hdc, &tm); } - font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth); #ifdef RDB_DEBUG_PATCH debug(23, "Primary font H=%d, AW=%d, MW=%d", @@ -1354,7 +1487,7 @@ static void init_fonts(int pick_width, int pick_height) ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); } - f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE); + f(FONT_UNDERLINE, font->charset, fw_dontcare, TRUE); /* * Some fonts, e.g. 9-pt Courier, draw their underlines @@ -1404,8 +1537,8 @@ static void init_fonts(int pick_width, int pick_height) } } - if (bold_mode == BOLD_FONT) { - f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE); + if (bold_font_mode == BOLD_FONT) { + f(FONT_BOLD, font->charset, fw_bold, FALSE); } #undef f @@ -1416,7 +1549,7 @@ static void init_fonts(int pick_width, int pick_height) for (i = 0; i < 3; i++) { if (fonts[i]) { if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm)) - fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight; + fontsize[i] = get_font_width(hdc, &tm) + 256 * tm.tmHeight; else fontsize[i] = -i; } else @@ -1431,23 +1564,24 @@ static void init_fonts(int pick_width, int pick_height) fonts[FONT_UNDERLINE] = 0; } - if (bold_mode == BOLD_FONT && + if (bold_font_mode == BOLD_FONT && fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) { - bold_mode = BOLD_SHADOW; + bold_font_mode = BOLD_SHADOW; DeleteObject(fonts[FONT_BOLD]); fonts[FONT_BOLD] = 0; } fontflag[0] = fontflag[1] = fontflag[2] = 1; - init_ucs(&cfg, &ucsdata); + init_ucs(conf, &ucsdata); } static void another_font(int fontno) { int basefont; - int fw_dontcare, fw_bold; + int fw_dontcare, fw_bold, quality; int c, u, w, x; char *s; + FontSpec *font; if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno]) return; @@ -1456,7 +1590,9 @@ static void another_font(int fontno) if (basefont != fontno && !fontflag[basefont]) another_font(basefont); - if (cfg.font.isbold) { + font = conf_get_fontspec(conf, CONF_font); + + if (font->isbold) { fw_dontcare = FW_BOLD; fw_bold = FW_HEAVY; } else { @@ -1464,10 +1600,10 @@ static void another_font(int fontno) fw_bold = FW_BOLD; } - c = cfg.font.charset; + c = font->charset; w = fw_dontcare; u = FALSE; - s = cfg.font.name; + s = font->name; x = font_width; if (fontno & FONT_WIDE) @@ -1481,11 +1617,13 @@ static void another_font(int fontno) if (fontno & FONT_UNDERLINE) u = TRUE; + quality = conf_get_int(conf, CONF_font_quality); + fonts[fontno] = CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w, FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), - FIXED_PITCH | FF_DONTCARE, s); + CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), + DEFAULT_PITCH | FF_DONTCARE, s); fontflag[fontno] = 1; } @@ -1507,11 +1645,11 @@ void request_resize(void *frontend, int w, int h) /* If the window is maximized supress resizing attempts */ if (IsZoomed(hwnd)) { - if (cfg.resize_action == RESIZE_TERM) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) return; } - if (cfg.resize_action == RESIZE_DISABLED) return; + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return; if (h == term->rows && w == term->cols) return; /* Sanity checks ... */ @@ -1542,9 +1680,10 @@ void request_resize(void *frontend, int w, int h) } } - term_size(term, h, w, cfg.savelines); + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); - if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) { + if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT && + !IsZoomed(hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; @@ -1565,7 +1704,7 @@ static void reset_window(int reinit) { * This function doesn't like to change the terminal size but if the * font size is locked that may be it's only soluion. */ - int win_width, win_height; + int win_width, win_height, resize_action, window_border; RECT cr, wr; #ifdef RDB_DEBUG_PATCH @@ -1579,7 +1718,11 @@ static void reset_window(int reinit) { win_width = cr.right - cr.left; win_height = cr.bottom - cr.top; - if (cfg.resize_action == RESIZE_DISABLED) reinit = 2; + resize_action = conf_get_int(conf, CONF_resize_action); + window_border = conf_get_int(conf, CONF_window_border); + + if (resize_action == RESIZE_DISABLED) + reinit = 2; /* Are we being forced to reload the fonts ? */ if (reinit>1) { @@ -1614,9 +1757,9 @@ static void reset_window(int reinit) { extra_width = wr.right - wr.left - cr.right + cr.left; extra_height = wr.bottom - wr.top - cr.bottom + cr.top; - if (cfg.resize_action != RESIZE_TERM) { - if ( font_width != win_width/term->cols || - font_height != win_height/term->rows) { + if (resize_action != RESIZE_TERM) { + if (font_width != win_width/term->cols || + font_height != win_height/term->rows) { deinit_fonts(); init_fonts(win_width/term->cols, win_height/term->rows); offset_width = (win_width-font_width*term->cols)/2; @@ -1628,13 +1771,13 @@ static void reset_window(int reinit) { #endif } } else { - if ( font_width * term->cols != win_width || - font_height * term->rows != win_height) { + if (font_width * term->cols != win_width || + font_height * term->rows != win_height) { /* Our only choice at this point is to change the * size of the terminal; Oh well. */ term_size(term, win_height/font_height, win_width/font_width, - cfg.savelines); + conf_get_int(conf, CONF_savelines)); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; InvalidateRect(hwnd, NULL, TRUE); @@ -1654,7 +1797,7 @@ static void reset_window(int reinit) { debug((27, "reset_window() -> Forced re-init")); #endif - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -1679,10 +1822,10 @@ static void reset_window(int reinit) { * window. But that may be too big for the screen which forces us * to change the terminal. */ - if ((cfg.resize_action == RESIZE_TERM && reinit<=0) || - (cfg.resize_action == RESIZE_EITHER && reinit<0) || + if ((resize_action == RESIZE_TERM && reinit<=0) || + (resize_action == RESIZE_EITHER && reinit<0) || reinit>0) { - offset_width = offset_height = cfg.window_border; + offset_width = offset_height = window_border; extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -1699,7 +1842,7 @@ static void reset_window(int reinit) { /* Grrr too big */ if ( term->rows > height || term->cols > width ) { - if (cfg.resize_action == RESIZE_EITHER) { + if (resize_action == RESIZE_EITHER) { /* Make the font the biggest we can */ if (term->cols > width) font_width = (ss.right - ss.left - extra_width) @@ -1716,7 +1859,8 @@ static void reset_window(int reinit) { } else { if ( height > term->rows ) height = term->rows; if ( width > term->cols ) width = term->cols; - term_size(term, height, width, cfg.savelines); + term_size(term, height, width, + conf_get_int(conf, CONF_savelines)); #ifdef RDB_DEBUG_PATCH debug((27, "reset_window() -> term resize to (%d,%d)", height, width)); @@ -1741,12 +1885,12 @@ static void reset_window(int reinit) { /* We're allowed to or must change the font but do we want to ? */ - if (font_width != (win_width-cfg.window_border*2)/term->cols || - font_height != (win_height-cfg.window_border*2)/term->rows) { + if (font_width != (win_width-window_border*2)/term->cols || + font_height != (win_height-window_border*2)/term->rows) { deinit_fonts(); - init_fonts((win_width-cfg.window_border*2)/term->cols, - (win_height-cfg.window_border*2)/term->rows); + init_fonts((win_width-window_border*2)/term->cols, + (win_height-window_border*2)/term->rows); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; @@ -1775,7 +1919,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt) { int thistime = GetMessageTime(); - if (send_raw_mouse && !(cfg.mouse_override && shift)) { + if (send_raw_mouse && + !(shift && conf_get_int(conf, CONF_mouse_override))) { lastbtn = MBT_NOTHING; term_mouse(term, b, translate_button(b), MA_CLICK, x, y, shift, ctrl, alt); @@ -1805,9 +1950,11 @@ static Mouse_Button translate_button(Mouse_Button button) if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) - return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND; + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_PASTE : MBT_EXTEND; if (button == MBT_RIGHT) - return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE; + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_EXTEND : MBT_PASTE; return 0; /* shouldn't happen */ } @@ -1816,8 +1963,8 @@ static void show_mouseptr(int show) /* NB that the counter in ShowCursor() is also frobbed by * update_mouse_pointer() */ static int cursor_visible = 1; - if (!cfg.hide_mouseptr) /* override if this feature disabled */ - show = 1; + if (!conf_get_int(conf, CONF_hide_mouseptr)) + show = 1; /* override if this feature disabled */ if (cursor_visible && !show) ShowCursor(FALSE); else if (!cursor_visible && show) @@ -1838,29 +1985,19 @@ static int is_alt_pressed(void) return FALSE; } -static int is_shift_pressed(void) -{ - BYTE keystate[256]; - int r = GetKeyboardState(keystate); - if (!r) - return FALSE; - if (keystate[VK_SHIFT] & 0x80) - return TRUE; - return FALSE; -} - static int resizing; void notify_remote_exit(void *fe) { - int exitcode; + int exitcode, close_on_exit; if (!session_closed && (exitcode = back->exitcode(backhandle)) >= 0) { + close_on_exit = conf_get_int(conf, CONF_close_on_exit); /* Abnormal exits will already have set session_closed and taken * appropriate action. */ - if (cfg.close_on_exit == FORCE_ON || - (cfg.close_on_exit == AUTO && exitcode != INT_MAX)) { + if (close_on_exit == FORCE_ON || + (close_on_exit == AUTO && exitcode != INT_MAX)) { PostQuitMessage(0); } else { must_close_session = TRUE; @@ -1875,15 +2012,26 @@ void notify_remote_exit(void *fe) } } -void timer_change_notify(long next) +void timer_change_notify(unsigned long next) { - long ticks = next - GETTICKCOUNT(); - if (ticks <= 0) ticks = 1; /* just in case */ + unsigned long now = GETTICKCOUNT(); + long ticks; + if (now - next < INT_MAX) + ticks = 0; + else + ticks = next - now; KillTimer(hwnd, TIMING_TIMER_ID); SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL); timing_next_time = next; } +static void conf_cache_data(void) +{ + /* Cache some items from conf to speed lookups in very hot code */ + cursor_type = conf_get_int(conf, CONF_cursor_type); + vtmode = conf_get_int(conf, CONF_vtmode); +} + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -1891,12 +2039,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, static int ignore_clip = FALSE; static int need_backend_resize = FALSE; static int fullscr_on_max = FALSE; + static int processed_resize = FALSE; static UINT last_mousemove = 0; + int resize_action; switch (message) { case WM_TIMER: if ((UINT_PTR)wParam == TIMING_TIMER_ID) { - long next; + unsigned long next; KillTimer(hwnd, TIMING_TIMER_ID); if (run_timers(timing_next_time, &next)) { @@ -1912,7 +2062,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, char *str; show_mouseptr(1); str = dupprintf("%s Exit Confirmation", appname); - if (!cfg.warn_on_close || session_closed || + if (session_closed || !conf_get_int(conf, CONF_warn_on_close) || MessageBox(hwnd, "Are you sure you want to close this session?", str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) @@ -1959,33 +2109,33 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * config structure. */ SECURITY_ATTRIBUTES sa; - Config *p; + void *p; + int size; + + size = conf_serialised_size(conf); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; - filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF, + filemap = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, - 0, sizeof(Config), NULL); - if (filemap) { - p = (Config *) MapViewOfFile(filemap, - FILE_MAP_WRITE, - 0, 0, sizeof(Config)); + 0, size, NULL); + if (filemap && filemap != INVALID_HANDLE_VALUE) { + p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size); if (p) { - *p = cfg; /* structure copy */ + conf_serialise(conf, p); UnmapViewOfFile(p); } } inherit_handles = TRUE; - sprintf(c, "putty &%p", filemap); + sprintf(c, "putty &%p:%u", filemap, (unsigned)size); cl = c; } else if (wParam == IDM_SAVEDSESS) { unsigned int sessno = ((lParam - IDM_SAVED_MIN) / MENU_SAVED_STEP) + 1; - if (sessno < sesslist.nsessions) { + if (sessno < (unsigned)sesslist.nsessions) { char *session = sesslist.sessions[sessno]; - /* XXX spaces? quotes? "-load"? */ cl = dupprintf("putty @%s", session); inherit_handles = FALSE; freecl = TRUE; @@ -2006,6 +2156,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, si.lpReserved2 = NULL; CreateProcess(b, cl, NULL, NULL, inherit_handles, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); if (filemap) CloseHandle(filemap); @@ -2023,7 +2175,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case IDM_RECONF: { - Config prev_cfg; + Conf *prev_conf; int init_lvl = 1; int reconfig_result; @@ -2032,60 +2184,78 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, else reconfiguring = TRUE; - GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle)); - prev_cfg = cfg; + /* + * Copy the current window title into the stored + * previous configuration, so that doing nothing to + * the window title field in the config box doesn't + * reset the title to its startup state. + */ + conf_set_str(conf, CONF_wintitle, window_name); + + prev_conf = conf_copy(conf); reconfig_result = do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0); reconfiguring = FALSE; - if (!reconfig_result) + if (!reconfig_result) { + conf_free(prev_conf); break; + } + conf_cache_data(); + + resize_action = conf_get_int(conf, CONF_resize_action); { /* Disable full-screen if resizing forbidden */ - HMENU m = GetSystemMenu (hwnd, FALSE); - EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND | - (cfg.resize_action == RESIZE_DISABLED) - ? MF_GRAYED : MF_ENABLED); + int i; + for (i = 0; i < lenof(popup_menus); i++) + EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, + MF_BYCOMMAND | + (resize_action == RESIZE_DISABLED) + ? MF_GRAYED : MF_ENABLED); /* Gracefully unzoom if necessary */ - if (IsZoomed(hwnd) && - (cfg.resize_action == RESIZE_DISABLED)) { + if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED)) ShowWindow(hwnd, SW_RESTORE); - } } /* Pass new config data to the logging module */ - log_reconfig(logctx, &cfg); + log_reconfig(logctx, conf); sfree(logpal); /* * Flush the line discipline's edit buffer in the * case where local editing has just been disabled. */ + ldisc_configure(ldisc, conf); if (ldisc) ldisc_send(ldisc, NULL, 0, 0); if (pal) DeleteObject(pal); logpal = NULL; pal = NULL; - cfgtopalette(); + conftopalette(); init_palette(); /* Pass new config data to the terminal */ - term_reconfig(term, &cfg); + term_reconfig(term, conf); /* Pass new config data to the back end */ if (back) - back->reconfig(backhandle, &cfg); + back->reconfig(backhandle, conf); /* Screen size changed ? */ - if (cfg.height != prev_cfg.height || - cfg.width != prev_cfg.width || - cfg.savelines != prev_cfg.savelines || - cfg.resize_action == RESIZE_FONT || - (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || - cfg.resize_action == RESIZE_DISABLED) - term_size(term, cfg.height, cfg.width, cfg.savelines); + if (conf_get_int(conf, CONF_height) != + conf_get_int(prev_conf, CONF_height) || + conf_get_int(conf, CONF_width) != + conf_get_int(prev_conf, CONF_width) || + conf_get_int(conf, CONF_savelines) != + conf_get_int(prev_conf, CONF_savelines) || + resize_action == RESIZE_FONT || + (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || + resize_action == RESIZE_DISABLED) + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); /* Enable or disable the scroll bar, etc */ { @@ -2094,8 +2264,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, GetWindowLongPtr(hwnd, GWL_EXSTYLE); nexflag = exflag; - if (cfg.alwaysontop != prev_cfg.alwaysontop) { - if (cfg.alwaysontop) { + if (conf_get_int(conf, CONF_alwaysontop) != + conf_get_int(prev_conf, CONF_alwaysontop)) { + if (conf_get_int(conf, CONF_alwaysontop)) { nexflag |= WS_EX_TOPMOST; SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -2105,25 +2276,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SWP_NOMOVE | SWP_NOSIZE); } } - if (cfg.sunken_edge) + if (conf_get_int(conf, CONF_sunken_edge)) nexflag |= WS_EX_CLIENTEDGE; else nexflag &= ~(WS_EX_CLIENTEDGE); nflg = flag; - if (is_full_screen() ? - cfg.scrollbar_in_fullscreen : cfg.scrollbar) + if (conf_get_int(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : + CONF_scrollbar)) nflg |= WS_VSCROLL; else nflg &= ~WS_VSCROLL; - if (cfg.resize_action == RESIZE_DISABLED || + if (resize_action == RESIZE_DISABLED || is_full_screen()) nflg &= ~WS_THICKFRAME; else nflg |= WS_THICKFRAME; - if (cfg.resize_action == RESIZE_DISABLED) + if (resize_action == RESIZE_DISABLED) nflg &= ~WS_MAXIMIZEBOX; else nflg |= WS_MAXIMIZEBOX; @@ -2144,41 +2316,54 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } /* Oops */ - if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { + if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { force_normal(hwnd); init_lvl = 2; } - set_title(NULL, cfg.wintitle); + set_title(NULL, conf_get_str(conf, CONF_wintitle)); if (IsIconic(hwnd)) { SetWindowText(hwnd, - cfg.win_name_always ? window_name : - icon_name); + conf_get_int(conf, CONF_win_name_always) ? + window_name : icon_name); } - if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 || - strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 || - cfg.font.isbold != prev_cfg.font.isbold || - cfg.font.height != prev_cfg.font.height || - cfg.font.charset != prev_cfg.font.charset || - cfg.font_quality != prev_cfg.font_quality || - cfg.vtmode != prev_cfg.vtmode || - cfg.bold_colour != prev_cfg.bold_colour || - cfg.resize_action == RESIZE_DISABLED || - cfg.resize_action == RESIZE_EITHER || - (cfg.resize_action != prev_cfg.resize_action)) - init_lvl = 2; + { + FontSpec *font = conf_get_fontspec(conf, CONF_font); + FontSpec *prev_font = conf_get_fontspec(prev_conf, + CONF_font); + + if (!strcmp(font->name, prev_font->name) || + !strcmp(conf_get_str(conf, CONF_line_codepage), + conf_get_str(prev_conf, CONF_line_codepage)) || + font->isbold != prev_font->isbold || + font->height != prev_font->height || + font->charset != prev_font->charset || + conf_get_int(conf, CONF_font_quality) != + conf_get_int(prev_conf, CONF_font_quality) || + conf_get_int(conf, CONF_vtmode) != + conf_get_int(prev_conf, CONF_vtmode) || + conf_get_int(conf, CONF_bold_style) != + conf_get_int(prev_conf, CONF_bold_style) || + resize_action == RESIZE_DISABLED || + resize_action == RESIZE_EITHER || + resize_action != conf_get_int(prev_conf, + CONF_resize_action)) + init_lvl = 2; + } InvalidateRect(hwnd, NULL, TRUE); reset_window(init_lvl); net_pending_errors(); + + conf_free(prev_conf); } break; case IDM_COPYALL: term_copyall(term); break; case IDM_PASTE: - term_do_paste(term); + request_paste(NULL); break; case IDM_CLRSB: term_clrsb(term); @@ -2192,8 +2377,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, showabout(hwnd); break; case IDM_HELP: - WinHelp(hwnd, help_path, - help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0); + launch_help(hwnd, NULL); break; case SC_MOUSEMENU: /* @@ -2250,7 +2434,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_MBUTTONUP: case WM_RBUTTONUP: if (message == WM_RBUTTONDOWN && - ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) { + ((wParam & MK_CONTROL) || + (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) { POINT cursorpos; show_mouseptr(1); /* make sure pointer is visible */ @@ -2267,26 +2452,32 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, switch (message) { case WM_LBUTTONDOWN: button = MBT_LEFT; + wParam |= MK_LBUTTON; press = 1; break; case WM_MBUTTONDOWN: button = MBT_MIDDLE; + wParam |= MK_MBUTTON; press = 1; break; case WM_RBUTTONDOWN: button = MBT_RIGHT; + wParam |= MK_RBUTTON; press = 1; break; case WM_LBUTTONUP: button = MBT_LEFT; + wParam &= ~MK_LBUTTON; press = 0; break; case WM_MBUTTONUP: button = MBT_MIDDLE; + wParam &= ~MK_MBUTTON; press = 0; break; case WM_RBUTTONUP: button = MBT_RIGHT; + wParam &= ~MK_RBUTTON; press = 0; break; default: @@ -2345,7 +2536,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); - ReleaseCapture(); + if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) + ReleaseCapture(); } } return 0; @@ -2505,7 +2697,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } return 0; case WM_NETEVENT: - enact_netevent(wParam, lParam); + /* 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; + if (WSAGETSELECTEVENT(lParam) != FD_READ) + enact_pending_netevent(); + net_pending_errors(); return 0; case WM_SETFOCUS: @@ -2538,7 +2742,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, debug((27, "WM_EXITSIZEMOVE")); #endif if (need_backend_resize) { - term_size(term, cfg.height, cfg.width, cfg.savelines); + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); InvalidateRect(hwnd, NULL, TRUE); } break; @@ -2548,12 +2754,15 @@ 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 && !is_alt_pressed()) { + resize_action = conf_get_int(conf, CONF_resize_action); + if (resize_action == RESIZE_TERM || + (resize_action == RESIZE_EITHER && !is_alt_pressed())) { int width, height, w, h, ew, eh; LPRECT r = (LPRECT) lParam; - if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER && - (cfg.height != term->rows || cfg.width != term->cols )) { + if (!need_backend_resize && resize_action == RESIZE_EITHER && + (conf_get_int(conf, CONF_height) != term->rows || + conf_get_int(conf, CONF_width) != term->cols)) { /* * Great! It seems that both the terminal size and the * font size have been changed and the user is now dragging. @@ -2562,11 +2771,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * font size! * * This would be easier but it seems to be too confusing. - - term_size(term, cfg.height, cfg.width, cfg.savelines); - reset_window(2); */ - cfg.height=term->rows; cfg.width=term->cols; + conf_set_int(conf, CONF_height, term->rows); + conf_set_int(conf, CONF_width, term->cols); InvalidateRect(hwnd, NULL, TRUE); need_backend_resize = TRUE; @@ -2603,8 +2810,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, return 0; } else { int width, height, w, h, rv = 0; - int ex_width = extra_width + (cfg.window_border - offset_width) * 2; - int ex_height = extra_height + (cfg.window_border - offset_height) * 2; + int window_border = conf_get_int(conf, CONF_window_border); + int ex_width = extra_width + (window_border - offset_width) * 2; + int ex_height = extra_height + (window_border - offset_height) * 2; LPRECT r = (LPRECT) lParam; width = r->right - r->left - ex_width; @@ -2640,6 +2848,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, sys_cursor_update(); break; case WM_SIZE: + resize_action = conf_get_int(conf, CONF_resize_action); #ifdef RDB_DEBUG_PATCH debug((27, "WM_SIZE %s (%d,%d)", (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED": @@ -2651,75 +2860,116 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, #endif if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, - cfg.win_name_always ? window_name : icon_name); + conf_get_int(conf, CONF_win_name_always) ? + window_name : icon_name); if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) SetWindowText(hwnd, window_name); - if (wParam == SIZE_RESTORED) + if (wParam == SIZE_RESTORED) { + processed_resize = FALSE; clear_full_screen(); + if (processed_resize) { + /* + * Inhibit normal processing of this WM_SIZE; a + * secondary one was triggered just now by + * clear_full_screen which contained the correct + * client area size. + */ + return 0; + } + } if (wParam == SIZE_MAXIMIZED && fullscr_on_max) { fullscr_on_max = FALSE; + processed_resize = FALSE; make_full_screen(); + if (processed_resize) { + /* + * Inhibit normal processing of this WM_SIZE; a + * secondary one was triggered just now by + * make_full_screen which contained the correct client + * area size. + */ + return 0; + } } - if (cfg.resize_action == RESIZE_DISABLED) { + processed_resize = TRUE; + + if (resize_action == RESIZE_DISABLED) { /* A resize, well it better be a minimize. */ reset_window(-1); } else { int width, height, w, h; + int window_border = conf_get_int(conf, CONF_window_border); width = LOWORD(lParam); height = HIWORD(lParam); - if (!resizing) { - if (wParam == SIZE_MAXIMIZED && !was_zoomed) { - was_zoomed = 1; - prev_rows = term->rows; - prev_cols = term->cols; - if (cfg.resize_action == RESIZE_TERM) { - w = width / font_width; - if (w < 1) w = 1; - h = height / font_height; - if (h < 1) h = 1; - - term_size(term, h, w, cfg.savelines); - } - reset_window(0); - } else if (wParam == SIZE_RESTORED && was_zoomed) { - was_zoomed = 0; - if (cfg.resize_action == RESIZE_TERM) - term_size(term, prev_rows, prev_cols, cfg.savelines); - if (cfg.resize_action != RESIZE_FONT) - reset_window(2); - else - reset_window(0); - } - /* This is an unexpected resize, these will normally happen - * if the window is too large. Probably either the user - * selected a huge font or the screen size has changed. - * - * This is also called with minimize. - */ - else reset_window(-1); - } - - /* - * Don't call back->size in mid-resize. (To prevent - * massive numbers of resize events getting sent - * down the connection during an NT opaque drag.) - */ - if (resizing) { - if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) { + if (wParam == SIZE_MAXIMIZED && !was_zoomed) { + was_zoomed = 1; + prev_rows = term->rows; + prev_cols = term->cols; + if (resize_action == RESIZE_TERM) { + w = width / font_width; + if (w < 1) w = 1; + h = height / font_height; + if (h < 1) h = 1; + + if (resizing) { + /* + * As below, if we're in the middle of an + * interactive resize we don't call + * back->size. In Windows 7, this case can + * arise in maximisation as well via the Aero + * snap UI. + */ + need_backend_resize = TRUE; + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); + } else { + term_size(term, h, w, + conf_get_int(conf, CONF_savelines)); + } + } + reset_window(0); + } else if (wParam == SIZE_RESTORED && was_zoomed) { + was_zoomed = 0; + if (resize_action == RESIZE_TERM) { + w = (width-window_border*2) / font_width; + if (w < 1) w = 1; + h = (height-window_border*2) / font_height; + if (h < 1) h = 1; + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); + reset_window(2); + } else if (resize_action != RESIZE_FONT) + reset_window(2); + else + reset_window(0); + } else if (wParam == SIZE_MINIMIZED) { + /* do nothing */ + } else if (resize_action == RESIZE_TERM || + (resize_action == RESIZE_EITHER && + !is_alt_pressed())) { + w = (width-window_border*2) / font_width; + if (w < 1) w = 1; + h = (height-window_border*2) / font_height; + if (h < 1) h = 1; + + if (resizing) { + /* + * Don't call back->size in mid-resize. (To + * prevent massive numbers of resize events + * getting sent down the connection during an NT + * opaque drag.) + */ need_backend_resize = TRUE; - w = (width-cfg.window_border*2) / font_width; - if (w < 1) w = 1; - h = (height-cfg.window_border*2) / font_height; - if (h < 1) h = 1; - - cfg.height = h; - cfg.width = w; - } else - reset_window(0); + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); + } else { + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); + } + } else { + reset_window(0); } } sys_cursor_update(); @@ -2746,7 +2996,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case SB_THUMBPOSITION: case SB_THUMBTRACK: - term_scroll(term, 1, HIWORD(wParam)); + /* + * Use GetScrollInfo instead of HIWORD(wParam) to get + * 32-bit scroll position. + */ + { + SCROLLINFO si; + + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_VERT, &si) == 0) + si.nTrackPos = HIWORD(wParam); + term_scroll(term, 1, si.nTrackPos); + } break; } break; @@ -2871,9 +3133,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * instead we luni_send the characters one by one. */ term_seen_key_event(term); - for (i = 0; i < n; i += 2) { - if (ldisc) + /* don't divide SURROGATE PAIR */ + if (ldisc) { + for (i = 0; i < n; i += 2) { + WCHAR hs = *(unsigned short *)(buff+i); + if (IS_HIGH_SURROGATE(hs) && i+2 < n) { + WCHAR ls = *(unsigned short *)(buff+i+2); + if (IS_LOW_SURROGATE(ls)) { + luni_send(ldisc, (unsigned short *)(buff+i), 2, 1); + i += 2; + continue; + } + } luni_send(ldisc, (unsigned short *)(buff+i), 1, 1); + } } free(buff); } @@ -2913,7 +3186,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } return 0; case WM_SYSCOLORCHANGE: - if (cfg.system_colour) { + if (conf_get_int(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ /* XXX actually this zaps the entire palette. */ systopalette(); @@ -2929,6 +3202,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, sfree(c); } return 0; + case WM_GOT_CLIPDATA: + if (process_clipdata((HGLOBAL)lParam, wParam)) + term_do_paste(term); + return 0; default: if (message == wm_mousewheel || message == WM_MOUSEWHEEL) { int shift_pressed=0, control_pressed=0; @@ -2961,17 +3238,24 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; if (send_raw_mouse && - !(cfg.mouse_override && shift_pressed)) { - /* send a mouse-down followed by a mouse up */ - 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, translate_button(b), - MA_RELEASE, TO_CHR_X(X_POS(lParam)), - TO_CHR_Y(Y_POS(lParam)), shift_pressed, - control_pressed, is_alt_pressed()); + !(conf_get_int(conf, CONF_mouse_override) && + shift_pressed)) { + /* Mouse wheel position is in screen coordinates for + * some reason */ + POINT p; + p.x = X_POS(lParam); p.y = Y_POS(lParam); + if (ScreenToClient(hwnd, &p)) { + /* send a mouse-down followed by a mouse up */ + term_mouse(term, b, translate_button(b), + MA_CLICK, + TO_CHR_X(p.x), + TO_CHR_Y(p.y), shift_pressed, + control_pressed, is_alt_pressed()); + term_mouse(term, b, translate_button(b), + MA_RELEASE, TO_CHR_X(p.x), + TO_CHR_Y(p.y), shift_pressed, + control_pressed, is_alt_pressed()); + } /* else: not sure when this can fail */ } else { /* trigger a scroll */ term_scroll(term, 0, @@ -3060,7 +3344,13 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, int force_manual_underline = 0; int fnt_width, char_width; int text_adjust = 0; - static int *IpDx = 0, IpDxLEN = 0; + int xoffset = 0; + int maxlen, remaining, opaque; + int is_cursor = FALSE; + static int *lpDx = NULL; + static int lpDx_len = 0; + int *lpDx_maybe; + int len2; /* for SURROGATE PAIR */ lattr &= LATTR_MODE; @@ -3069,17 +3359,6 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, if (attr & ATTR_WIDE) char_width *= 2; - if (len > IpDxLEN || IpDx[0] != char_width) { - int i; - if (len > IpDxLEN) { - sfree(IpDx); - IpDx = snewn(len + 16, int); - IpDxLEN = (len + 16); - } - for (i = 0; i < IpDxLEN; i++) - IpDx[i] = char_width; - } - /* Only want the left half of double width lines */ if (lattr != LATTR_NORM && x*2 >= term->cols) return; @@ -3089,17 +3368,15 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, x += offset_width; y += offset_height; - if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) { + if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) { attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS); - if (bold_mode == BOLD_COLOURS) - attr &= ~ATTR_BOLD; - /* cursor fg and bg */ attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT); + is_cursor = TRUE; } nfont = 0; - if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) { + if (vtmode == VT_POORMAN && lattr != LATTR_NORM) { /* Assume a poorman font is borken in other ways too. */ lattr = LATTR_WIDE; } else @@ -3116,6 +3393,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, if (attr & ATTR_NARROW) nfont |= FONT_NARROW; +#ifdef USES_VTLINE_HACK /* Special hack for the VT100 linedraw glyphs. */ if (text[0] >= 0x23BA && text[0] <= 0x23BD) { switch ((unsigned char) (text[0])) { @@ -3140,9 +3418,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, force_manual_underline = 1; } } +#endif /* Anything left as an original character set is unprintable. */ - if (DIRECT_CHAR(text[0])) { + if (DIRECT_CHAR(text[0]) && + (len < 2 || !IS_SURROGATE_PAIR(text[0], text[1]))) { int i; for (i = 0; i < len; i++) text[i] = 0xFFFD; @@ -3154,7 +3434,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); - if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD)) + if (bold_font_mode == BOLD_FONT && (attr & ATTR_BOLD)) nfont |= FONT_BOLD; if (und_mode == UND_FONT && (attr & ATTR_UNDER)) nfont |= FONT_UNDERLINE; @@ -3174,11 +3454,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, nfg = nbg; nbg = t; } - if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) { + if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) { if (nfg < 16) nfg |= 8; else if (nfg >= 256) nfg |= 1; } - if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) { + if (bold_colours && (attr & ATTR_BLINK)) { if (nbg < 16) nbg |= 8; else if (nbg >= 256) nbg |= 1; } @@ -3195,120 +3475,218 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, line_box.top = y; line_box.right = x + char_width * len; line_box.bottom = y + font_height; + /* adjust line_box.right for SURROGATE PAIR & VARIATION SELECTOR */ + { + int i; + int rc_width = 0; + for (i = 0; i < len ; i++) { + if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { + i++; + } else if (i+1 < len && IS_SURROGATE_PAIR(text[i], text[i+1])) { + rc_width += char_width; + i++; + } else if (IS_LOW_VARSEL(text[i])) { + /* do nothing */ + } else { + rc_width += char_width; + } + } + line_box.right = line_box.left + rc_width; + } /* Only want the left half of double width lines */ if (line_box.right > font_width*term->cols+offset_width) line_box.right = font_width*term->cols+offset_width; - /* We're using a private area for direct to font. (512 chars.) */ - if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) { - /* Ho Hum, dbcs fonts are a PITA! */ - /* To display on W9x I have to convert to UCS */ - static wchar_t *uni_buf = 0; - static int uni_len = 0; - int nlen, mptr; - if (len > uni_len) { - sfree(uni_buf); - uni_len = len; - uni_buf = snewn(uni_len, wchar_t); - } + if (font_varpitch) { + /* + * If we're using a variable-pitch font, we unconditionally + * draw the glyphs one at a time and centre them in their + * character cells (which means in particular that we must + * disable the lpDx mechanism). This gives slightly odd but + * generally reasonable results. + */ + xoffset = char_width / 2; + SetTextAlign(hdc, TA_TOP | TA_CENTER | TA_NOUPDATECP); + lpDx_maybe = NULL; + maxlen = 1; + } else { + /* + * In a fixed-pitch font, we draw the whole string in one go + * in the normal way. + */ + xoffset = 0; + SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP); + lpDx_maybe = lpDx; + maxlen = len; + } - for(nlen = mptr = 0; mptr 0; + text += len, remaining -= len, x += char_width * len2) { + len = (maxlen < remaining ? maxlen : remaining); + /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ + len2 = len; + if (maxlen == 1) { + if (remaining >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) + len++; + if (remaining-len >= 1 && IS_LOW_VARSEL(text[len])) + len++; + else if (remaining-len >= 2 && + IS_HIGH_VARSEL(text[len], text[len+1])) + len += 2; + } - IpDx[0] = -1; - } else if (DIRECT_FONT(text[0])) { - static char *directbuf = NULL; - static int directlen = 0; - int i; - if (len > directlen) { - directlen = len; - directbuf = sresize(directbuf, directlen, char); + if (len > lpDx_len) { + lpDx_len = len * 9 / 8 + 16; + lpDx = sresize(lpDx, lpDx_len, int); + + if (lpDx_maybe) lpDx_maybe = lpDx; } - for (i = 0; i < len; i++) - directbuf[i] = text[i] & 0xFF; + { + int i; + /* only last char has dx width in SURROGATE PAIR and + * VARIATION sequence */ + for (i = 0; i < len; i++) { + lpDx[i] = char_width; + if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) { + if (i > 0) lpDx[i-1] = 0; + lpDx[i] = 0; + i++; + lpDx[i] = char_width; + } else if (i+1 < len && IS_SURROGATE_PAIR(text[i],text[i+1])) { + lpDx[i] = 0; + i++; + lpDx[i] = char_width; + } else if (IS_LOW_VARSEL(text[i])) { + if (i > 0) lpDx[i-1] = 0; + lpDx[i] = char_width; + } + } + } - ExtTextOut(hdc, x, - y - font_height * (lattr == LATTR_BOT) + text_adjust, - ETO_CLIPPED | ETO_OPAQUE, &line_box, directbuf, len, IpDx); - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { - SetBkMode(hdc, TRANSPARENT); + /* We're using a private area for direct to font. (512 chars.) */ + if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) { + /* Ho Hum, dbcs fonts are a PITA! */ + /* To display on W9x I have to convert to UCS */ + static wchar_t *uni_buf = 0; + static int uni_len = 0; + int nlen, mptr; + if (len > uni_len) { + sfree(uni_buf); + uni_len = len; + uni_buf = snewn(uni_len, wchar_t); + } - /* 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 - font_height * (lattr == - LATTR_BOT) + text_adjust, - ETO_CLIPPED, &line_box, directbuf, len, IpDx); - } - } else { - /* And 'normal' unicode characters */ - static WCHAR *wbuf = NULL; - static int wlen = 0; - int i; + for(nlen = mptr = 0; mptr directlen) { + directlen = len; + directbuf = sresize(directbuf, directlen, char); + } - for (i = 0; i < len; i++) - wbuf[i] = text[i]; - - /* print Glyphs as they are, without Windows' Shaping*/ - exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust, - &line_box, wbuf, len, IpDx, !(attr & TATTR_COMBINING)); -/* ExtTextOutW(hdc, x, - y - font_height * (lattr == LATTR_BOT) + text_adjust, - ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx); - */ + for (i = 0; i < len; i++) + directbuf[i] = text[i] & 0xFF; + + ExtTextOut(hdc, x + xoffset, + y - font_height * (lattr == LATTR_BOT) + text_adjust, + ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), + &line_box, directbuf, len, lpDx_maybe); + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + SetBkMode(hdc, TRANSPARENT); + + /* GRR: This draws the character outside its 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 + xoffset - 1, + y - font_height * (lattr == + LATTR_BOT) + text_adjust, + ETO_CLIPPED, &line_box, directbuf, len, lpDx_maybe); + } + } else { + /* And 'normal' unicode characters */ + static WCHAR *wbuf = NULL; + static int wlen = 0; + int i; + + if (wlen < len) { + sfree(wbuf); + wlen = len; + wbuf = snewn(wlen, WCHAR); + } - /* And the shadow bold hack. */ - if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { - SetBkMode(hdc, TRANSPARENT); - ExtTextOutW(hdc, x - 1, - y - font_height * (lattr == - LATTR_BOT) + text_adjust, - ETO_CLIPPED, &line_box, wbuf, len, IpDx); - } + for (i = 0; i < len; i++) + wbuf[i] = text[i]; + + /* print Glyphs as they are, without Windows' Shaping*/ + general_textout(hdc, x + xoffset, + y - font_height * (lattr==LATTR_BOT) + text_adjust, + &line_box, wbuf, len, lpDx, + opaque && !(attr & TATTR_COMBINING)); + + /* And the shadow bold hack. */ + if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) { + SetBkMode(hdc, TRANSPARENT); + ExtTextOutW(hdc, x + xoffset - 1, + y - font_height * (lattr == + LATTR_BOT) + text_adjust, + ETO_CLIPPED, &line_box, wbuf, len, lpDx_maybe); + } + } + + /* + * If we're looping round again, stop erasing the background + * rectangle. + */ + SetBkMode(hdc, TRANSPARENT); + opaque = FALSE; } if (lattr != LATTR_TOP && (force_manual_underline || (und_mode == UND_LINE @@ -3319,8 +3697,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, dec = dec * 2 - font_height; oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg)); - MoveToEx(hdc, x, y + dec, NULL); - LineTo(hdc, x + len * char_width, y + dec); + MoveToEx(hdc, line_box.left, line_box.top + dec, NULL); + LineTo(hdc, line_box.right, line_box.top + dec); oldpen = SelectObject(hdc, oldpen); DeleteObject(oldpen); } @@ -3334,9 +3712,35 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len, { if (attr & TATTR_COMBINING) { unsigned long a = 0; - attr &= ~TATTR_COMBINING; + int len0 = 1; + /* don't divide SURROGATE PAIR and VARIATION SELECTOR */ + if (len >= 2 && IS_SURROGATE_PAIR(text[0], text[1])) + len0 = 2; + if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) { + attr &= ~TATTR_COMBINING; + do_text_internal(ctx, x, y, text, len0+1, attr, lattr); + text += len0+1; + len -= len0+1; + a = TATTR_COMBINING; + } else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) { + attr &= ~TATTR_COMBINING; + do_text_internal(ctx, x, y, text, len0+2, attr, lattr); + text += len0+2; + len -= len0+2; + a = TATTR_COMBINING; + } else { + attr &= ~TATTR_COMBINING; + } + while (len--) { - do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) { + do_text_internal(ctx, x, y, text, 2, attr | a, lattr); + len--; + text++; + } else { + do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + } + text++; a = TATTR_COMBINING; } @@ -3351,7 +3755,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, int fnt_width; int char_width; HDC hdc = ctx; - int ctype = cfg.cursor_type; + int ctype = cursor_type; lattr &= LATTR_MODE; @@ -3484,8 +3888,9 @@ int char_width(Context ctx, int uc) { /* * 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. + * codes. Returns number of bytes used, zero to drop the message, + * -1 to forward the message to Windows, or another negative number + * to indicate a NUL-terminated "special" string. */ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) @@ -3495,13 +3900,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, int r, i, code; unsigned char *p = output; static int alt_sum = 0; + int funky_type = conf_get_int(conf, CONF_funky_type); + int no_applic_k = conf_get_int(conf, CONF_no_applic_k); + int ctrlaltkeys = conf_get_int(conf, CONF_ctrlaltkeys); + int nethack_keypad = conf_get_int(conf, CONF_nethack_keypad); HKL kbd_layout = GetKeyboardLayout(0); - /* keys is for ToAsciiEx. There's some ick here, see below. */ - static WORD keys[3]; + static wchar_t keys_unicode[3]; static int compose_char = 0; - static WPARAM compose_key = 0; + static WPARAM compose_keycode = 0; r = GetKeyboardState(keystate); if (!r) @@ -3551,12 +3959,12 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, else if (ch) debug((", $%02x", ch)); - if (keys[0]) - debug((", KB0=%02x", keys[0])); - if (keys[1]) - debug((", KB1=%02x", keys[1])); - if (keys[2]) - debug((", KB2=%02x", keys[2])); + if (keys_unicode[0]) + debug((", KB0=%04x", keys_unicode[0])); + if (keys_unicode[1]) + debug((", KB1=%04x", keys_unicode[1])); + if (keys_unicode[2]) + debug((", KB2=%04x", keys_unicode[2])); if ((keystate[VK_SHIFT] & 0x80) != 0) debug((", S")); @@ -3588,9 +3996,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Nastyness with NUMLock - Shift-NUMLock is left alone though */ - if ((cfg.funky_type == FUNKY_VT400 || - (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys && - !cfg.no_applic_k)) + if ((funky_type == FUNKY_VT400 || + (funky_type <= FUNKY_LINUX && term->app_keypad_keys && + !no_applic_k)) && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) { wParam = VK_EXECUTE; @@ -3616,7 +4024,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */ if (left_alt && (keystate[VK_CONTROL] & 0x80)) { - if (cfg.ctrlaltkeys) + if (ctrlaltkeys) keystate[VK_MENU] = 0; else { keystate[VK_RMENU] = 0x80; @@ -3630,16 +4038,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Note if AltGr was pressed and if it was used as a compose key */ if (!compose_state) { - compose_key = 0x100; - if (cfg.compose_key) { + compose_keycode = 0x100; + if (conf_get_int(conf, CONF_compose_key)) { if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) - compose_key = wParam; + compose_keycode = wParam; } if (wParam == VK_APPS) - compose_key = wParam; + compose_keycode = wParam; } - if (wParam == compose_key) { + if (wParam == compose_keycode) { if (compose_state == 0 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state = 1; @@ -3654,9 +4062,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, compose_state = 0; /* Sanitize the number pad if not using a PC NumPad */ - if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k - && cfg.funky_type != FUNKY_XTERM) - || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) { + if (left_alt || (term->app_keypad_keys && !no_applic_k + && funky_type != FUNKY_XTERM) + || funky_type == FUNKY_VT400 || nethack_keypad || compose_state) { if ((HIWORD(lParam) & KF_EXTENDED) == 0) { int nParam = 0; switch (wParam) { @@ -3725,19 +4133,25 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); return 0; } + if ((wParam == VK_PRIOR || wParam == VK_NEXT) && shift_state == 3) { + term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1)); + return 0; + } if (wParam == VK_INSERT && shift_state == 1) { - term_do_paste(term); + request_paste(NULL); return 0; } - if (left_alt && wParam == VK_F4 && cfg.alt_f4) { + if (left_alt && wParam == VK_F4 && conf_get_int(conf, CONF_alt_f4)) { return -1; } - if (left_alt && wParam == VK_SPACE && cfg.alt_space) { + if (left_alt && wParam == VK_SPACE && conf_get_int(conf, + CONF_alt_space)) { SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } - if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter && - (cfg.resize_action != RESIZE_DISABLED)) { + if (left_alt && wParam == VK_RETURN && + conf_get_int(conf, CONF_fullscreenonaltenter) && + (conf_get_int(conf, CONF_resize_action) != RESIZE_DISABLED)) { if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) flip_full_screen(); return -1; @@ -3749,7 +4163,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } /* Nethack keypad */ - if (cfg.nethack_keypad && !left_alt) { + if (nethack_keypad && !left_alt) { switch (wParam) { case VK_NUMPAD1: *p++ = "bB\002\002"[shift_state & 3]; @@ -3785,9 +4199,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (!left_alt) { int xkey = 0; - if (cfg.funky_type == FUNKY_VT400 || - (cfg.funky_type <= FUNKY_LINUX && - term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) { + if (funky_type == FUNKY_VT400 || + (funky_type <= FUNKY_LINUX && + term->app_keypad_keys && !no_applic_k)) switch (wParam) { case VK_EXECUTE: xkey = 'P'; break; @@ -3801,7 +4215,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, xkey = 'S'; break; } - if (term->app_keypad_keys && !cfg.no_applic_k) + if (term->app_keypad_keys && !no_applic_k) switch (wParam) { case VK_NUMPAD0: xkey = 'p'; @@ -3838,7 +4252,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, xkey = 'n'; break; case VK_ADD: - if (cfg.funky_type == FUNKY_XTERM) { + if (funky_type == FUNKY_XTERM) { if (shift_state) xkey = 'l'; else @@ -3850,15 +4264,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; case VK_DIVIDE: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'o'; break; case VK_MULTIPLY: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'j'; break; case VK_SUBTRACT: - if (cfg.funky_type == FUNKY_XTERM) + if (funky_type == FUNKY_XTERM) xkey = 'm'; break; @@ -3880,13 +4294,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } if (wParam == VK_BACK && shift_state == 0) { /* Backspace */ - *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08); + *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x7F : 0x08); *p++ = 0; return -2; } if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */ /* We do the opposite of what is configured */ - *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F); + *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x08 : 0x7F); *p++ = 0; return -2; } @@ -3905,9 +4319,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return p - output; } if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */ - *p++ = 3; - *p++ = 0; - return -2; + if (back) + back->special(backhandle, TS_BRK); + return 0; } if (wParam == VK_PAUSE) { /* Break/Pause */ *p++ = 26; @@ -3923,7 +4337,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, *p++ = 0x1F; return p - output; } - if (shift_state == 2 && wParam == 0xDF) { + if (shift_state == 2 && (wParam == 0xDF || wParam == 0xDC)) { *p++ = 0x1C; return p - output; } @@ -4030,7 +4444,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; } /* Reorder edit keys to physical order */ - if (cfg.funky_type == FUNKY_VT400 && code <= 6) + if (funky_type == FUNKY_VT400 && code <= 6) code = "\0\2\1\4\5\3\6"[code]; if (term->vt52_mode && code > 0 && code <= 6) { @@ -4038,8 +4452,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return p - output; } - if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */ - code >= 11 && code <= 34) { + if (funky_type == FUNKY_SCO && code >= 11 && code <= 34) { + /* SCO function keys */ char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; int index = 0; switch (wParam) { @@ -4061,7 +4475,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, p += sprintf((char *) p, "\x1B[%c", codes[index]); return p - output; } - if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */ + if (funky_type == FUNKY_SCO && /* SCO small keypad */ code >= 1 && code <= 6) { char codes[] = "HL.FIG"; if (code == 3) { @@ -4071,7 +4485,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } return p - output; } - if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { + if ((term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) { int offt = 0; if (code > 15) offt++; @@ -4084,18 +4498,19 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt); return p - output; } - if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { + if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11); return p - output; } - if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { + if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", code + 'P' - 11); else p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11); return p - output; } - if (cfg.rxvt_homeend && (code == 1 || code == 4)) { + if ((code == 1 || code == 4) && + conf_get_int(conf, CONF_rxvt_homeend)) { p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw"); return p - output; } @@ -4128,37 +4543,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, break; } if (xkey) { - if (term->vt52_mode) - p += sprintf((char *) p, "\x1B%c", xkey); - else { - int app_flg = (term->app_cursor_keys && !cfg.no_applic_c); -#if 0 - /* - * RDB: VT100 & VT102 manuals both state the - * app cursor keys only work if the app keypad - * is on. - * - * SGT: That may well be true, but xterm - * disagrees and so does at least one - * application, so I've #if'ed this out and the - * behaviour is back to PuTTY's original: app - * cursor and app keypad are independently - * switchable modes. If anyone complains about - * _this_ I'll have to put in a configurable - * option. - */ - if (!term->app_keypad_keys) - app_flg = 0; -#endif - /* Useful mapping of Ctrl-arrows */ - if (shift_state == 2) - app_flg = !app_flg; - - if (app_flg) - p += sprintf((char *) p, "\x1BO%c", xkey); - else - p += sprintf((char *) p, "\x1B[%c", xkey); - } + p += format_arrow_key(p, term, xkey, shift_state); return p - output; } } @@ -4185,7 +4570,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, BOOL capsOn=0; /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ - if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) { + if(keystate[VK_CAPITAL] != 0 && + conf_get_int(conf, CONF_xlat_capslockcyr)) { capsOn= !left_alt; keystate[VK_CAPITAL] = 0; } @@ -4194,6 +4580,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * 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) { + r = ToUnicodeEx(wParam, scan, keystate, keys_unicode, + lenof(keys_unicode), 0, kbd_layout); + } else { /* XXX 'keys' parameter is declared in MSDN documentation as * 'LPWORD lpChar'. * The experience of a French user indicates that on @@ -4204,12 +4593,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * 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 { + static WORD keys[3]; + static BYTE keysb[3]; r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout); + if (r > 0) { + for (i = 0; i < r; i++) { + keysb[i] = (BYTE)keys[i]; + } + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)keysb, r, + keys_unicode, lenof(keys_unicode)); + } } #ifdef SHOW_TOASCII_RESULT if (r == 1 && !key_down) { @@ -4219,13 +4613,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, else debug((", LCH(%d)", alt_sum)); } else { - debug((", ACH(%d)", keys[0])); + debug((", ACH(%d)", keys_unicode[0])); } } else if (r > 0) { int r1; debug((", ASC(")); for (r1 = 0; r1 < r; r1++) { - debug(("%s%d", r1 ? "," : "", keys[r1])); + debug(("%s%d", r1 ? "," : "", keys_unicode[r1])); } debug((")")); } @@ -4242,18 +4636,18 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, p = output; for (i = 0; i < r; i++) { - unsigned char ch = (unsigned char) keys[i]; + wchar_t wch = keys_unicode[i]; - if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') { - compose_char = ch; + if (compose_state == 2 && wch >= ' ' && wch < 0x80) { + compose_char = wch; compose_state++; continue; } - if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') { + if (compose_state == 3 && wch >= ' ' && wch < 0x80) { int nc; compose_state = 0; - if ((nc = check_compose(compose_char, ch)) == -1) { + if ((nc = check_compose(compose_char, wch)) == -1) { MessageBeep(MB_ICONHAND); return 0; } @@ -4274,7 +4668,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (ldisc) luni_send(ldisc, &keybuf, 1, 1); } else { - ch = (char) alt_sum; + char ch = (char) alt_sum; /* * We need not bother about stdin * backlogs here, because in GUI PuTTY @@ -4292,40 +4686,39 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } else { term_seen_key_event(term); if (ldisc) - lpage_send(ldisc, kbd_codepage, &ch, 1, 1); + luni_send(ldisc, &wch, 1, 1); } } else { - if(capsOn && ch < 0x80) { + if(capsOn && wch < 0x80) { WCHAR cbuf[2]; cbuf[0] = 27; - cbuf[1] = xlat_uskbd2cyrllic(ch); + cbuf[1] = xlat_uskbd2cyrllic(wch); term_seen_key_event(term); if (ldisc) luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1); } else { - char cbuf[2]; + WCHAR cbuf[2]; cbuf[0] = '\033'; - cbuf[1] = ch; + cbuf[1] = wch; term_seen_key_event(term); if (ldisc) - lpage_send(ldisc, kbd_codepage, - cbuf+!left_alt, 1+!!left_alt, 1); + luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, 1); } } show_mouseptr(0); } /* This is so the ALT-Numpad and dead keys work correctly. */ - keys[0] = 0; + keys_unicode[0] = 0; return p - output; } /* If we're definitly not building up an ALT-54321 then clear it */ if (!left_alt) - keys[0] = 0; + keys_unicode[0] = 0; /* If we will be using alt_sum fix the 256s */ - else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) - keys[0] = 10; + else if (keys_unicode[0] && (in_utf(term) || ucsdata.dbcs_screenfont)) + keys_unicode[0] = 10; } /* @@ -4335,28 +4728,18 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, * we return -1, which means Windows will give the keystroke * its default handling (i.e. bring up the System menu). */ - if (wParam == VK_MENU && !cfg.alt_only) + if (wParam == VK_MENU && !conf_get_int(conf, CONF_alt_only)) return 0; return -1; } -void request_paste(void *frontend) -{ - /* - * In Windows, pasting is synchronous: we can read the - * clipboard with no difficulty, so request_paste() can just go - * ahead and paste. - */ - term_do_paste(term); -} - void set_title(void *frontend, char *title) { sfree(window_name); window_name = snewn(1 + strlen(title), char); strcpy(window_name, title); - if (cfg.win_name_always || !IsIconic(hwnd)) + if (conf_get_int(conf, CONF_win_name_always) || !IsIconic(hwnd)) SetWindowText(hwnd, title); } @@ -4365,7 +4748,7 @@ void set_icon(void *frontend, char *title) sfree(icon_name); icon_name = snewn(1 + strlen(title), char); strcpy(icon_name, title); - if (!cfg.win_name_always && IsIconic(hwnd)) + if (!conf_get_int(conf, CONF_win_name_always) && IsIconic(hwnd)) SetWindowText(hwnd, title); } @@ -4373,7 +4756,8 @@ void set_sbar(void *frontend, int total, int start, int page) { SCROLLINFO si; - if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar) + if (!conf_get_int(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : CONF_scrollbar)) return; si.cbSize = sizeof(si); @@ -4521,15 +4905,22 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des GlobalFree(clipdata2); return; } - if (!(lock = GlobalLock(clipdata))) + if (!(lock = GlobalLock(clipdata))) { + GlobalFree(clipdata); + GlobalFree(clipdata2); return; - if (!(lock2 = GlobalLock(clipdata2))) + } + if (!(lock2 = GlobalLock(clipdata2))) { + GlobalUnlock(clipdata); + GlobalFree(clipdata); + GlobalFree(clipdata2); return; + } memcpy(lock, data, len * sizeof(wchar_t)); WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL); - if (cfg.rtf_paste) { + if (conf_get_int(conf, CONF_rtf_paste)) { wchar_t unitab[256]; char *rtf = NULL; unsigned char *tdata = (unsigned char *)lock2; @@ -4544,13 +4935,14 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des int attrUnder, lastAttrUnder = 0; int palette[NALLCOLOURS]; int numcolours; + FontSpec *font = conf_get_fontspec(conf, CONF_font); get_unitab(CP_ACP, unitab, 0); - rtfsize = 100 + strlen(cfg.font.name); + rtfsize = 100 + strlen(font->name); rtf = snewn(rtfsize, char); rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d", - cfg.font.name, cfg.font.height*2); + font->name, font->height*2); /* * Add colour palette @@ -4573,7 +4965,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = tmpcolour; } - if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) { + if (bold_colours && (attr[i] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ @@ -4664,7 +5056,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = tmpcolour; } - if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) { + if (bold_colours && (attr[tindex] & ATTR_BOLD)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ @@ -4681,7 +5073,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des /* * Collect other attributes */ - if (bold_mode != BOLD_COLOURS) + if (bold_font_mode != BOLD_NONE) attrBold = attr[tindex] & ATTR_BOLD; else attrBold = 0; @@ -4700,7 +5092,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des bgcolour = -1; /* No coloring */ if (fgcolour >= 256) { /* Default colour */ - if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1) + if (bold_colours && (fgcolour & 1) && bgcolour == -1) attrBold = ATTR_BOLD; /* Emphasize text with bold attribute */ fgcolour = -1; /* No coloring */ @@ -4824,46 +5216,89 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0); } -void get_clip(void *frontend, wchar_t ** p, int *len) +static DWORD WINAPI clipboard_read_threadfunc(void *param) { - static HGLOBAL clipdata = NULL; - static wchar_t *converted = 0; - wchar_t *p2; + HWND hwnd = (HWND)param; + HGLOBAL clipdata; - if (converted) { - sfree(converted); - converted = 0; - } - if (!p) { - if (clipdata) - GlobalUnlock(clipdata); - clipdata = NULL; - return; - } else if (OpenClipboard(NULL)) { + if (OpenClipboard(NULL)) { if ((clipdata = GetClipboardData(CF_UNICODETEXT))) { - CloseClipboard(); - *p = GlobalLock(clipdata); - if (*p) { - for (p2 = *p; *p2; p2++); - *len = p2 - *p; - return; - } - } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) { - char *s; - int i; - CloseClipboard(); - s = GlobalLock(clipdata); + SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)1, (LPARAM)clipdata); + } else if ((clipdata = GetClipboardData(CF_TEXT))) { + SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)0, (LPARAM)clipdata); + } + CloseClipboard(); + } + + return 0; +} + +static int process_clipdata(HGLOBAL clipdata, int unicode) +{ + sfree(clipboard_contents); + clipboard_contents = NULL; + clipboard_length = 0; + + if (unicode) { + wchar_t *p = GlobalLock(clipdata); + wchar_t *p2; + + if (p) { + /* Unwilling to rely on Windows having wcslen() */ + for (p2 = p; *p2; p2++); + clipboard_length = p2 - p; + clipboard_contents = snewn(clipboard_length + 1, wchar_t); + memcpy(clipboard_contents, p, clipboard_length * sizeof(wchar_t)); + clipboard_contents[clipboard_length] = L'\0'; + return TRUE; + } + } else { + char *s = GlobalLock(clipdata); + int i; + + if (s) { i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0); - *p = converted = snewn(i, wchar_t); - MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i); - *len = i - 1; - return; - } else - CloseClipboard(); + clipboard_contents = snewn(i, wchar_t); + MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, + clipboard_contents, i); + clipboard_length = i - 1; + clipboard_contents[clipboard_length] = L'\0'; + return TRUE; + } } - *p = NULL; - *len = 0; + return FALSE; +} + +void request_paste(void *frontend) +{ + /* + * I always thought pasting was synchronous in Windows; the + * clipboard access functions certainly _look_ synchronous, + * unlike the X ones. But in fact it seems that in some + * situations the contents of the clipboard might not be + * immediately available, and the clipboard-reading functions + * may block. This leads to trouble if the application + * delivering the clipboard data has to get hold of it by - + * for example - talking over a network connection which is + * forwarded through this very PuTTY. + * + * Hence, we spawn a subthread to read the clipboard, and do + * our paste when it's finished. The thread will send a + * message back to our main window when it terminates, and + * that tells us it's OK to paste. + */ + DWORD in_threadid; /* required for Win9x */ + CreateThread(NULL, 0, clipboard_read_threadfunc, + hwnd, 0, &in_threadid); +} + +void get_clip(void *frontend, wchar_t **p, int *len) +{ + if (p) { + *p = clipboard_contents; + *len = clipboard_length; + } } #if 0 @@ -4922,13 +5357,56 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } +/* + * Print a message box and don't close the connection. + */ +void nonfatal(char *fmt, ...) +{ + va_list ap; + char *stuff, morestuff[100]; + + va_start(ap, fmt); + stuff = dupvprintf(fmt, ap); + va_end(ap); + sprintf(morestuff, "%.70s Error", appname); + MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); + sfree(stuff); +} + +DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO)); + +static void init_flashwindow(void) +{ + HMODULE user32_module = load_system32_dll("user32.dll"); + GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx); +} + +static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout) +{ + if (p_FlashWindowEx) { + FLASHWINFO fi; + fi.cbSize = sizeof(fi); + fi.hwnd = hwnd; + fi.dwFlags = dwFlags; + fi.uCount = uCount; + fi.dwTimeout = dwTimeout; + return (*p_FlashWindowEx)(&fi); + } + else + return FALSE; /* shrug */ +} + static void flash_window(int mode); static long next_flash; static int flashing = 0; -static void flash_window_timer(void *ctx, long now) +/* + * Timer for platforms where we must maintain window flashing manually + * (e.g., Win95). + */ +static void flash_window_timer(void *ctx, unsigned long now) { - if (flashing && now - next_flash >= 0) { + if (flashing && now == next_flash) { flash_window(1); } } @@ -4939,24 +5417,41 @@ static void flash_window_timer(void *ctx, long now) */ static void flash_window(int mode) { - if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) { + int beep_ind = conf_get_int(conf, CONF_beep_ind); + if ((mode == 0) || (beep_ind == B_IND_DISABLED)) { /* stop */ if (flashing) { - FlashWindow(hwnd, FALSE); flashing = 0; + if (p_FlashWindowEx) + flash_window_ex(FLASHW_STOP, 0, 0); + else + FlashWindow(hwnd, FALSE); } } else if (mode == 2) { /* start */ if (!flashing) { flashing = 1; - FlashWindow(hwnd, TRUE); - next_flash = schedule_timer(450, flash_window_timer, hwnd); + if (p_FlashWindowEx) { + /* For so-called "steady" mode, we use uCount=2, which + * seems to be the traditional number of flashes used + * by user notifications (e.g., by Explorer). + * uCount=0 appears to enable continuous flashing, per + * "flashing" mode, although I haven't seen this + * documented. */ + flash_window_ex(FLASHW_ALL | FLASHW_TIMER, + (beep_ind == B_IND_FLASH ? 0 : 2), + 0 /* system cursor blink rate */); + /* No need to schedule timer */ + } else { + FlashWindow(hwnd, TRUE); + next_flash = schedule_timer(450, flash_window_timer, hwnd); + } } - } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) { + } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) { /* maintain */ - if (flashing) { + if (flashing && !p_FlashWindowEx) { FlashWindow(hwnd, TRUE); /* toggle */ next_flash = schedule_timer(450, flash_window_timer, hwnd); } @@ -4988,16 +5483,17 @@ void do_beep(void *frontend, int mode) */ lastbeep = GetTickCount(); } else if (mode == BELL_WAVEFILE) { - if (!PlaySound(cfg.bell_wavefile.path, NULL, + Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile); + if (!PlaySound(bell_wavefile->path, NULL, SND_ASYNC | SND_FILENAME)) { - char buf[sizeof(cfg.bell_wavefile.path) + 80]; + char buf[sizeof(bell_wavefile->path) + 80]; char otherbuf[100]; sprintf(buf, "Unable to play sound file\n%s\n" - "Using default sound instead", cfg.bell_wavefile.path); + "Using default sound instead", bell_wavefile->path); sprintf(otherbuf, "%.70s Sound Error", appname); MessageBox(hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); - cfg.beep = BELL_DEFAULT; + conf_set_int(conf, CONF_beep, BELL_DEFAULT); } } else if (mode == BELL_PCSPEAKER) { static long lastbeep = 0; @@ -5043,8 +5539,9 @@ void set_iconic(void *frontend, int iconic) */ void move_window(void *frontend, int x, int y) { - if (cfg.resize_action == RESIZE_DISABLED || - cfg.resize_action == RESIZE_FONT || + int resize_action = conf_get_int(conf, CONF_resize_action); + if (resize_action == RESIZE_DISABLED || + resize_action == RESIZE_FONT || IsZoomed(hwnd)) return; @@ -5057,7 +5554,7 @@ void move_window(void *frontend, int x, int y) */ void set_zorder(void *frontend, int top) { - if (cfg.alwaysontop) + if (conf_get_int(conf, CONF_alwaysontop)) return; /* ignore */ SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -5179,7 +5676,7 @@ static void make_full_screen() /* Remove the window furniture. */ style = GetWindowLongPtr(hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); - if (cfg.scrollbar_in_fullscreen) + if (conf_get_int(conf, CONF_scrollbar_in_fullscreen)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; @@ -5196,9 +5693,12 @@ static void make_full_screen() reset_window(0); - /* Tick the menu item in the System menu. */ - CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN, - MF_CHECKED); + /* Tick the menu item in the System and context menus. */ + { + int i; + for (i = 0; i < lenof(popup_menus); i++) + CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_CHECKED); + } } /* @@ -5211,11 +5711,11 @@ static void clear_full_screen() /* Reinstate the window furniture. */ style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE); style |= WS_CAPTION | WS_BORDER; - if (cfg.resize_action == RESIZE_DISABLED) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) style &= ~WS_THICKFRAME; else style |= WS_THICKFRAME; - if (cfg.scrollbar) + if (conf_get_int(conf, CONF_scrollbar)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; @@ -5226,9 +5726,12 @@ static void clear_full_screen() SWP_FRAMECHANGED); } - /* Untick the menu item in the System menu. */ - CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN, - MF_UNCHECKED); + /* Untick the menu item in the System and context menus. */ + { + int i; + for (i = 0; i < lenof(popup_menus); i++) + CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_UNCHECKED); + } } /* @@ -5267,6 +5770,11 @@ int from_backend_untrusted(void *frontend, const char *data, int len) return term_data_untrusted(term, data, len); } +int from_backend_eof(void *frontend) +{ + return TRUE; /* do respond to incoming EOF with outgoing */ +} + int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int ret;