+/*
+ * window.c - the PuTTY(tel) main program, which runs a PuTTY terminal
+ * emulator and backend in a window.
+ */
+
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
+#include <limits.h>
#include <assert.h>
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#define IDM_COPYALL 0x0170
#define IDM_FULLSCREEN 0x0180
#define IDM_PASTE 0x0190
+#define IDM_SPECIALSEP 0x0200
#define IDM_SPECIAL_MIN 0x0400
#define IDM_SPECIAL_MAX 0x0800
/* Maximum number of sessions on saved-session submenu */
#define MENU_SAVED_MAX ((IDM_SAVED_MAX-IDM_SAVED_MIN) / MENU_SAVED_STEP)
-#define WM_IGNORE_CLIP (WM_XUSER + 2)
-#define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
-#define WM_AGENT_CALLBACK (WM_XUSER + 4)
+#define WM_IGNORE_CLIP (WM_APP + 2)
+#define WM_FULLSCR_ON_MAX (WM_APP + 3)
+#define WM_AGENT_CALLBACK (WM_APP + 4)
/* Needed for Chinese support and apparently not always defined. */
#ifndef VK_PROCESSKEY
static void another_font(int);
static void deinit_fonts(void);
static void set_input_locale(HKL);
+static void update_savedsess_menu(void);
static int is_full_screen(void);
static void make_full_screen(void);
static void *backhandle;
static struct unicode_data ucsdata;
-static int session_closed;
+static int must_close_session, session_closed;
+static int reconfiguring = FALSE;
-static const struct telnet_special *specials;
-static int n_specials;
+static const struct telnet_special *specials = NULL;
+static HMENU specials_menu = NULL;
+static int n_specials = 0;
#define TIMING_TIMER_ID 1234
static long timing_next_time;
static struct {
HMENU menu;
- int specials_submenu_pos;
} popup_menus[2];
enum { SYSMENU, CTXMENU };
+static HMENU savedsess_menu;
Config cfg; /* exported to windlg.c */
-extern struct sesslist sesslist; /* imported from windlg.c */
+static struct sesslist sesslist; /* for saved-session menu */
struct agent_callback {
void (*callback)(void *, void *, int);
} und_mode;
static int descent;
-#define NCOLOURS 24
-static COLORREF colours[NCOLOURS];
+#define NCFGCOLOURS 22
+#define NEXTCOLOURS 240
+#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
+static COLORREF colours[NALLCOLOURS];
static HPALETTE pal;
static LPLOGPALETTE logpal;
-static RGBTRIPLE defpal[NCOLOURS];
-
-static HWND hwnd;
+static RGBTRIPLE defpal[NALLCOLOURS];
static HBITMAP caretbm;
static int send_raw_mouse = 0;
static int wheel_accumulator = 0;
+static int busy_status = BUSY_NOT;
+
static char *window_name, *icon_name;
static int compose_state = 0;
{
}
+char *get_ttymode(void *frontend, const char *mode)
+{
+ return term_get_ttymode(term, mode);
+}
+
static void start_backend(void)
{
const char *error;
if (error) {
char *str = dupprintf("%s Error", appname);
sprintf(msg, "Unable to open connection to\n"
- "%.800s\n" "%s", cfg.host, error);
+ "%.800s\n" "%s", cfg_dest(&cfg), error);
MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
sfree(str);
exit(0);
DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
}
+ must_close_session = FALSE;
session_closed = FALSE;
}
int guess_width, guess_height;
hinst = inst;
+ hwnd = NULL;
flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
sk_init();
osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
- /*
- * See if we can find our Help file.
- */
- {
- 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.hlp");
- if ( (fp = fopen(b, "r")) != NULL) {
- help_path = dupstr(b);
- fclose(fp);
- } else
- help_path = NULL;
- strcpy(r, "putty.cnt");
- if ( (fp = fopen(b, "r")) != NULL) {
- help_has_contents = TRUE;
- fclose(fp);
- } else
- help_has_contents = FALSE;
- }
+ init_help();
/*
* Process the command line.
i--;
p[i] = '\0';
do_defaults(p + 1, &cfg);
- if (!*cfg.host && !do_config()) {
+ if (!cfg_launchable(&cfg) && !do_config()) {
cleanup_exit(0);
}
} else if (*p == '&') {
i++; /* skip next argument */
} else if (ret == 1) {
continue; /* nothing further needs doing */
- } else if (!strcmp(p, "-cleanup")) {
+ } else if (!strcmp(p, "-cleanup") ||
+ !strcmp(p, "-cleanup-during-uninstall")) {
/*
* `putty -cleanup'. Remove all registry
* entries associated with PuTTY, and also find
* and delete the random seed file.
*/
char *s1, *s2;
- s1 = dupprintf("This procedure will remove ALL Registry\n"
- "entries associated with %s, and will\n"
- "also remove the random seed file.\n"
- "\n"
- "THIS PROCESS WILL DESTROY YOUR SAVED\n"
- "SESSIONS. Are you really sure you want\n"
- "to continue?", appname);
- s2 = dupprintf("%s Warning", appname);
- if (MessageBox(NULL, s1, s2,
- MB_YESNO | MB_ICONWARNING) == IDYES) {
+ /* Are we being invoked from an uninstaller? */
+ if (!strcmp(p, "-cleanup-during-uninstall")) {
+ s1 = dupprintf("Remove saved sessions and random seed file?\n"
+ "\n"
+ "If you hit Yes, ALL Registry entries associated\n"
+ "with %s will be removed, as well as the\n"
+ "random seed file. THIS PROCESS WILL\n"
+ "DESTROY YOUR SAVED SESSIONS.\n"
+ "(This only affects the currently logged-in user.)\n"
+ "\n"
+ "If you hit No, uninstallation will proceed, but\n"
+ "saved sessions etc will be left on the machine.",
+ appname);
+ s2 = dupprintf("%s Uninstallation", appname);
+ } else {
+ s1 = dupprintf("This procedure will remove ALL Registry entries\n"
+ "associated with %s, and will also remove\n"
+ "the random seed file. (This only affects the\n"
+ "currently logged-in user.)\n"
+ "\n"
+ "THIS PROCESS WILL DESTROY YOUR SAVED SESSIONS.\n"
+ "Are you really sure you want to continue?",
+ appname);
+ s2 = dupprintf("%s Warning", appname);
+ }
+ if (message_box(s1, s2,
+ MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2,
+ HELPCTXID(option_cleanup)) == IDYES) {
cleanup_all();
}
sfree(s1);
sfree(s2);
exit(0);
+ } else if (!strcmp(p, "-pgpfp")) {
+ pgp_fingerprints();
+ exit(1);
} else if (*p != '-') {
char *q = p;
if (got_host) {
cmdline_run_saved(&cfg);
- if (!*cfg.host && !do_config()) {
+ if (!cfg_launchable(&cfg) && !do_config()) {
cleanup_exit(0);
}
}
/*
- * Trim a colon suffix off the hostname if it's there.
+ * Trim a colon suffix off the hostname if it's there. In
+ * order to protect IPv6 address literals against this
+ * treatment, we do not do this if there's _more_ than one
+ * colon.
*/
- cfg.host[strcspn(cfg.host, ":")] = '\0';
+ {
+ char *c = strchr(cfg.host, ':');
+
+ if (c) {
+ char *d = strchr(c+1, ':');
+ if (!d)
+ *c = '\0';
+ }
+ }
/*
* Remove any remaining whitespace from the hostname.
RegisterClass(&wndclass);
}
- hwnd = NULL;
-
memset(&ucsdata, 0, sizeof(ucsdata));
cfgtopalette();
* 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.
+ * timer_change_notify() which will expect hwnd to exist.)
*/
term = term_init(&cfg, &ucsdata, NULL);
logctx = log_init(NULL, &cfg);
* Set up the session-control options on the system menu.
*/
{
- HMENU s, m;
- int i, j;
+ HMENU m;
+ int j;
char *str;
popup_menus[SYSMENU].menu = GetSystemMenu(hwnd, FALSE);
popup_menus[CTXMENU].menu = CreatePopupMenu();
AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste");
- s = CreateMenu();
+ savedsess_menu = CreateMenu();
get_sesslist(&sesslist, TRUE);
- /* skip sesslist.sessions[0] == Default Settings */
- for (i = 1;
- i < ((sesslist.nsessions <= MENU_SAVED_MAX+1) ? sesslist.nsessions
- : MENU_SAVED_MAX+1);
- i++)
- AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (i-1)*MENU_SAVED_STEP,
- sesslist.sessions[i]);
+ update_savedsess_menu();
for (j = 0; j < lenof(popup_menus); j++) {
m = popup_menus[j].menu;
AppendMenu(m, MF_SEPARATOR, 0, 0);
- popup_menus[j].specials_submenu_pos = GetMenuItemCount(m);
AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
AppendMenu(m, MF_SEPARATOR, 0, 0);
AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
- AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
+ AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) savedsess_menu,
+ "Sa&ved Sessions");
AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
AppendMenu(m, MF_SEPARATOR, 0, 0);
AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
AppendMenu(m, (cfg.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);
set_input_locale(GetKeyboardLayout(0));
/*
- * Open the initial log file if there is one.
- */
- logfopen(logctx);
-
- /*
* Finally show the window!
*/
ShowWindow(hwnd, show);
term_set_focus(term, GetForegroundWindow() == hwnd);
UpdateWindow(hwnd);
- if (GetMessage(&msg, NULL, 0, 0) == 1) {
- while (msg.message != WM_QUIT) {
+ while (1) {
+ HANDLE *handles;
+ int nhandles, n;
+
+ handles = handle_get_events(&nhandles);
+
+ n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, INFINITE,
+ QS_ALLINPUT);
+
+ if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
+ handle_got_event(handles[n - WAIT_OBJECT_0]);
+ sfree(handles);
+ if (must_close_session)
+ close_session();
+ continue;
+ }
+
+ sfree(handles);
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT)
+ goto finished; /* two-level break */
+
if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
DispatchMessage(&msg);
/* Send the paste buffer if there's anything to send */
* we've delayed, reading the socket, writing, and repainting
* the window.
*/
- if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- continue;
-
- /* The messages seem unreliable; especially if we're being tricky */
- term_set_focus(term, GetForegroundWindow() == hwnd);
+ if (must_close_session)
+ close_session();
+ }
- net_pending_errors();
+ /* The messages seem unreliable; especially if we're being tricky */
+ term_set_focus(term, GetForegroundWindow() == hwnd);
- /* There's no point rescanning everything in the message queue
- * so we do an apparently unnecessary wait here
- */
- WaitMessage();
- if (GetMessage(&msg, NULL, 0, 0) != 1)
- break;
- }
+ net_pending_errors();
}
+ finished:
cleanup_exit(msg.wParam); /* this doesn't return... */
return msg.wParam; /* ... but optimiser doesn't know */
}
crypto_wrapup();
#endif
}
+ shutdown_help();
exit(code);
}
}
/*
+ * Refresh the saved-session submenu from `sesslist'.
+ */
+static void update_savedsess_menu(void)
+{
+ int i;
+ while (DeleteMenu(savedsess_menu, 0, MF_BYPOSITION)) ;
+ /* skip sesslist.sessions[0] == Default Settings */
+ for (i = 1;
+ i < ((sesslist.nsessions <= MENU_SAVED_MAX+1) ? sesslist.nsessions
+ : MENU_SAVED_MAX+1);
+ i++)
+ AppendMenu(savedsess_menu, MF_ENABLED,
+ IDM_SAVED_MIN + (i-1)*MENU_SAVED_STEP,
+ sesslist.sessions[i]);
+}
+
+/*
* Update the Special Commands submenu.
*/
void update_specials_menu(void *frontend)
{
- HMENU p;
- int menu_already_exists = (specials != NULL);
+ HMENU new_menu;
int i, j;
if (back)
* here's a lame "stack" that will do for now. */
HMENU saved_menu = NULL;
int nesting = 1;
- p = CreatePopupMenu();
+ new_menu = CreatePopupMenu();
for (i = 0; nesting > 0; i++) {
assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
switch (specials[i].code) {
case TS_SEP:
- AppendMenu(p, MF_SEPARATOR, 0, 0);
+ AppendMenu(new_menu, MF_SEPARATOR, 0, 0);
break;
case TS_SUBMENU:
assert(nesting < 2);
nesting++;
- saved_menu = p; /* XXX lame stacking */
- p = CreatePopupMenu();
+ saved_menu = new_menu; /* XXX lame stacking */
+ new_menu = CreatePopupMenu();
AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
- (UINT) p, specials[i].name);
+ (UINT) new_menu, specials[i].name);
break;
case TS_EXITMENU:
nesting--;
if (nesting) {
- p = saved_menu; /* XXX lame stacking */
+ new_menu = saved_menu; /* XXX lame stacking */
saved_menu = NULL;
}
break;
default:
- AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
+ AppendMenu(new_menu, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
specials[i].name);
break;
}
/* Squirrel the highest special. */
n_specials = i - 1;
} else {
- p = NULL;
+ new_menu = NULL;
n_specials = 0;
}
for (j = 0; j < lenof(popup_menus); j++) {
- if (menu_already_exists) {
+ if (specials_menu) {
/* XXX does this free up all submenus? */
- DeleteMenu(popup_menus[j].menu,
- popup_menus[j].specials_submenu_pos,
- MF_BYPOSITION);
- DeleteMenu(popup_menus[j].menu,
- popup_menus[j].specials_submenu_pos,
- MF_BYPOSITION);
+ DeleteMenu(popup_menus[j].menu, specials_menu, MF_BYCOMMAND);
+ DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND);
}
- if (specials) {
- InsertMenu(popup_menus[j].menu,
- popup_menus[j].specials_submenu_pos,
- MF_BYPOSITION | MF_SEPARATOR, 0, 0);
- InsertMenu(popup_menus[j].menu,
- popup_menus[j].specials_submenu_pos,
- MF_BYPOSITION | MF_POPUP | MF_ENABLED,
- (UINT) p, "S&pecial Command");
+ if (new_menu) {
+ InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
+ MF_BYCOMMAND | MF_POPUP | MF_ENABLED,
+ (UINT) new_menu, "S&pecial Command");
+ InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
+ MF_BYCOMMAND | MF_SEPARATOR, IDM_SPECIALSEP, 0);
}
}
+ specials_menu = new_menu;
+}
+
+static void update_mouse_pointer(void)
+{
+ LPTSTR curstype;
+ int force_visible = FALSE;
+ static int forced_visible = FALSE;
+ switch (busy_status) {
+ case BUSY_NOT:
+ if (send_raw_mouse)
+ curstype = IDC_ARROW;
+ else
+ curstype = IDC_IBEAM;
+ break;
+ case BUSY_WAITING:
+ curstype = IDC_APPSTARTING; /* this may be an abuse */
+ force_visible = TRUE;
+ break;
+ case BUSY_CPU:
+ curstype = IDC_WAIT;
+ force_visible = TRUE;
+ break;
+ default:
+ assert(0);
+ }
+ {
+ HCURSOR cursor = LoadCursor(NULL, curstype);
+ SetClassLongPtr(hwnd, GCLP_HCURSOR, (LONG_PTR)cursor);
+ SetCursor(cursor); /* force redraw of cursor at current posn */
+ }
+ if (force_visible != forced_visible) {
+ /* We want some cursor shapes to be visible always.
+ * Along with show_mouseptr(), this manages the ShowCursor()
+ * counter such that if we switch back to a non-force_visible
+ * cursor, the previous visibility state is restored. */
+ ShowCursor(force_visible);
+ forced_visible = force_visible;
+ }
+}
+
+void set_busy_status(void *frontend, int status)
+{
+ busy_status = status;
+ update_mouse_pointer();
}
/*
{
activate = activate && !cfg.no_mouse_rep;
send_raw_mouse = activate;
- SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
+ update_mouse_pointer();
}
/*
if (cfg.close_on_exit == FORCE_ON)
PostQuitMessage(1);
else {
- close_session();
+ must_close_session = TRUE;
}
}
{
static int reentering = 0;
extern int select_result(WPARAM, LPARAM);
- int ret;
if (reentering)
return; /* don't unpend the pending */
reentering = 1;
- ret = select_result(wParam, lParam);
+ select_result(wParam, lParam);
reentering = 0;
-
- if (ret == 0 && !session_closed) {
- /* Abnormal exits will already have set session_closed and taken
- * appropriate action. */
- if (cfg.close_on_exit == FORCE_ON ||
- cfg.close_on_exit == AUTO) PostQuitMessage(0);
- else {
- close_session();
- session_closed = TRUE;
- MessageBox(hwnd, "Connection closed by remote host",
- appname, MB_OK | MB_ICONINFORMATION);
- }
- }
}
/*
{
int i;
static const int ww[] = {
- 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21,
- 0, 1, 2, 3, 4, 4, 5, 5
+ 256, 257, 258, 259, 260, 261,
+ 0, 8, 1, 9, 2, 10, 3, 11,
+ 4, 12, 5, 13, 6, 14, 7, 15
};
- for (i = 0; i < 24; i++) {
+ for (i = 0; i < 22; i++) {
int w = ww[i];
- defpal[i].rgbtRed = cfg.colours[w][0];
- defpal[i].rgbtGreen = cfg.colours[w][1];
- defpal[i].rgbtBlue = cfg.colours[w][2];
+ defpal[w].rgbtRed = cfg.colours[i][0];
+ defpal[w].rgbtGreen = cfg.colours[i][1];
+ defpal[w].rgbtBlue = cfg.colours[i][2];
+ }
+ for (i = 0; i < NEXTCOLOURS; i++) {
+ if (i < 216) {
+ int r = i / 36, g = (i / 6) % 6, b = i % 6;
+ defpal[i+16].rgbtRed = r ? r * 40 + 55 : 0;
+ defpal[i+16].rgbtGreen = g ? g * 40 + 55 : 0;
+ defpal[i+16].rgbtBlue = b ? b * 40 + 55 : 0;
+ } else {
+ int shade = i - 216;
+ shade = shade * 10 + 8;
+ defpal[i+16].rgbtRed = defpal[i+16].rgbtGreen =
+ defpal[i+16].rgbtBlue = shade;
+ }
}
/* Override with system colours if appropriate */
int i;
static const struct { int nIndex; int norm; int bold; } or[] =
{
- { COLOR_WINDOWTEXT, 16, 17 }, /* Default Foreground */
- { COLOR_WINDOW, 18, 19 }, /* Default Background */
- { COLOR_HIGHLIGHTTEXT, 20, 21 }, /* Cursor Text */
- { COLOR_HIGHLIGHT, 22, 23 }, /* Cursor Colour */
+ { COLOR_WINDOWTEXT, 256, 257 }, /* Default Foreground */
+ { COLOR_WINDOW, 258, 259 }, /* Default Background */
+ { COLOR_HIGHLIGHTTEXT, 260, 260 }, /* Cursor Text */
+ { COLOR_HIGHLIGHT, 261, 261 }, /* Cursor Colour */
};
for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) {
*/
logpal = smalloc(sizeof(*logpal)
- sizeof(logpal->palPalEntry)
- + NCOLOURS * sizeof(PALETTEENTRY));
+ + NALLCOLOURS * sizeof(PALETTEENTRY));
logpal->palVersion = 0x300;
- logpal->palNumEntries = NCOLOURS;
- for (i = 0; i < NCOLOURS; i++) {
+ logpal->palNumEntries = NALLCOLOURS;
+ for (i = 0; i < NALLCOLOURS; i++) {
logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
ReleaseDC(hwnd, hdc);
}
if (pal)
- for (i = 0; i < NCOLOURS; i++)
+ for (i = 0; i < NALLCOLOURS; i++)
colours[i] = PALETTERGB(defpal[i].rgbtRed,
defpal[i].rgbtGreen,
defpal[i].rgbtBlue);
else
- for (i = 0; i < NCOLOURS; i++)
+ for (i = 0; i < NALLCOLOURS; i++)
colours[i] = RGB(defpal[i].rgbtRed,
defpal[i].rgbtGreen, defpal[i].rgbtBlue);
}
unsigned short *lpString, UINT cbCount,
CONST INT *lpDx, int opaque)
{
-
+#ifdef __LCC__
+ /*
+ * The LCC include files apparently don't supply the
+ * GCP_RESULTSW type, but we can make do with GCP_RESULTS
+ * proper: the differences aren't important to us (the only
+ * variable-width string parameter is one we don't use anyway).
+ */
+ GCP_RESULTS gcpr;
+#else
GCP_RESULTSW gcpr;
+#endif
char *buffer = snewn(cbCount*2+2, char);
char *classbuffer = snewn(cbCount, char);
memset(&gcpr, 0, sizeof(gcpr));
}
/*
+ * 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;
+ RECT newrc;
+
+#ifdef FIXME_REMOVE_BEFORE_CHECKIN
+int k;
+debug(("general_textout: %d,%d", x, y));
+for(k=0;k<cbCount;k++)debug((" U+%04X", lpString[k]));
+debug(("\n rect: [%d,%d %d,%d]", lprc->left, lprc->top, lprc->right, lprc->bottom));
+debug(("\n"));
+#endif
+
+ xp = xn = x;
+
+ for (i = 0; i < cbCount ;) {
+ int rtl = is_rtl(lpString[i]);
+
+ xn += lpDx[i];
+
+ for (j = i+1; j < 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) {
+ newrc.left = lprc->left + xp - x;
+ newrc.right = lprc->left + xn - x;
+ newrc.top = lprc->top;
+ newrc.bottom = lprc->bottom;
+#ifdef FIXME_REMOVE_BEFORE_CHECKIN
+{
+int k;
+debug((" exact_textout: %d,%d", xp, y));
+for(k=0;k<j-i;k++)debug((" U+%04X", lpString[i+k]));
+debug(("\n rect: [%d,%d %d,%d]\n", newrc.left, newrc.top, newrc.right, newrc.bottom));
+}
+#endif
+ exact_textout(hdc, xp, y, &newrc, lpString+i, j-i, lpDx+i, opaque);
+ } else {
+#ifdef FIXME_REMOVE_BEFORE_CHECKIN
+{
+int k;
+debug((" ExtTextOut : %d,%d", xp, y));
+for(k=0;k<j-i;k++)debug((" U+%04X", lpString[i+k]));
+debug(("\n rect: [%d,%d %d,%d]\n", newrc.left, newrc.top, newrc.right, newrc.bottom));
+}
+#endif
+ newrc.left = lprc->left + xp - x;
+ newrc.right = lprc->left + xn - x;
+ newrc.top = lprc->top;
+ newrc.bottom = lprc->bottom;
+ ExtTextOutW(hdc, xp, y, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
+ &newrc, lpString+i, j-i, lpDx+i);
+ }
+
+ i = j;
+ xp = xn;
+ }
+
+#ifdef FIXME_REMOVE_BEFORE_CHECKIN
+debug(("general_textout: done, xn=%d\n", xn));
+#endif
+ assert(xn - x == lprc->right - lprc->left);
+}
+
+/*
* Initialise all the fonts we will need initially. There may be as many as
* three or as few as one. The other (poentially) twentyone fonts are done
* if/when they are needed.
#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, DEFAULT_QUALITY, \
+ CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), \
FIXED_PITCH | FF_DONTCARE, cfg.font.name)
f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
- lfont.lfHeight = font_height;
- lfont.lfWidth = font_width;
- lfont.lfEscapement = 0;
- lfont.lfOrientation = 0;
- lfont.lfWeight = fw_dontcare;
- lfont.lfItalic = FALSE;
- lfont.lfUnderline = FALSE;
- lfont.lfStrikeOut = FALSE;
- lfont.lfCharSet = cfg.font.charset;
- lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
- lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- lfont.lfQuality = DEFAULT_QUALITY;
- lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
- strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE);
-
SelectObject(hdc, fonts[FONT_NORMAL]);
GetTextMetrics(hdc, &tm);
+ GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont);
+
if (pick_width == 0 || pick_height == 0) {
font_height = tm.tmHeight;
font_width = tm.tmAveCharWidth;
fonts[fontno] =
CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
- CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality),
FIXED_PITCH | FF_DONTCARE, s);
fontflag[fontno] = 1;
#endif
}
} else {
- if ( font_width != win_width/term->cols ||
- font_height != win_height/term->rows) {
+ 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.
*/
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;
static int resizing;
-void notify_remote_exit(void *fe) { /* stub not needed in this frontend */ }
+void notify_remote_exit(void *fe)
+{
+ int exitcode;
+
+ if (!session_closed &&
+ (exitcode = back->exitcode(backhandle)) >= 0) {
+ /* 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)) {
+ PostQuitMessage(0);
+ } else {
+ must_close_session = TRUE;
+ session_closed = TRUE;
+ /* exitcode == INT_MAX indicates that the connection was closed
+ * by a fatal error, so an error box will be coming our way and
+ * we should not generate this informational one. */
+ if (exitcode != INT_MAX)
+ MessageBox(hwnd, "Connection closed by remote host",
+ appname, MB_OK | MB_ICONINFORMATION);
+ }
+ }
+}
void timer_change_notify(long next)
{
if (!cfg.warn_on_close || session_closed ||
MessageBox(hwnd,
"Are you sure you want to close this session?",
- str, MB_ICONWARNING | MB_OKCANCEL) == IDOK)
+ str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1)
+ == IDOK)
DestroyWindow(hwnd);
sfree(str);
}
show_mouseptr(1);
PostQuitMessage(0);
return 0;
+ case WM_INITMENUPOPUP:
+ if ((HMENU)wParam == savedsess_menu) {
+ /* About to pop up Saved Sessions sub-menu.
+ * Refresh the session list. */
+ get_sesslist(&sesslist, FALSE); /* free */
+ get_sesslist(&sesslist, TRUE);
+ update_savedsess_menu();
+ return 0;
+ }
+ break;
case WM_COMMAND:
case WM_SYSCOMMAND:
switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
char b[2048];
char c[30], *cl;
int freecl = FALSE;
+ BOOL inherit_handles;
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE filemap = NULL;
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) {
+ if (filemap && filemap != INVALID_HANDLE_VALUE) {
p = (Config *) MapViewOfFile(filemap,
FILE_MAP_WRITE,
0, 0, sizeof(Config));
UnmapViewOfFile(p);
}
}
+ inherit_handles = TRUE;
sprintf(c, "putty &%p", filemap);
cl = c;
} else if (wParam == IDM_SAVEDSESS) {
char *session = sesslist.sessions[sessno];
/* XXX spaces? quotes? "-load"? */
cl = dupprintf("putty @%s", session);
+ inherit_handles = FALSE;
freecl = TRUE;
} else
break;
- } else
+ } else /* IDM_NEWSESS */ {
cl = NULL;
+ inherit_handles = FALSE;
+ }
GetModuleFileName(NULL, b, sizeof(b) - 1);
si.cb = sizeof(si);
si.dwFlags = 0;
si.cbReserved2 = 0;
si.lpReserved2 = NULL;
- CreateProcess(b, cl, NULL, NULL, TRUE,
+ CreateProcess(b, cl, NULL, NULL, inherit_handles,
NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
if (filemap)
case IDM_RESTART:
if (!back) {
logevent(NULL, "----- Session restarted -----");
+ term_pwron(term, FALSE);
start_backend();
}
{
Config prev_cfg;
int init_lvl = 1;
+ int reconfig_result;
+
+ if (reconfiguring)
+ break;
+ else
+ reconfiguring = TRUE;
GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
prev_cfg = cfg;
- if (!do_reconfig(hwnd))
+ reconfig_result =
+ do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0);
+ reconfiguring = FALSE;
+ if (!reconfig_result)
break;
{
/* Enable or disable the scroll bar, etc */
{
- LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
+ LONG nflg, flag = GetWindowLongPtr(hwnd, GWL_STYLE);
LONG nexflag, exflag =
- GetWindowLong(hwnd, GWL_EXSTYLE);
+ GetWindowLongPtr(hwnd, GWL_EXSTYLE);
nexflag = exflag;
if (cfg.alwaysontop != prev_cfg.alwaysontop) {
if (nflg != flag || nexflag != exflag) {
if (nflg != flag)
- SetWindowLong(hwnd, GWL_STYLE, nflg);
+ SetWindowLongPtr(hwnd, GWL_STYLE, nflg);
if (nexflag != exflag)
- SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, nexflag);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOCOPYBITS |
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 ||
term_clrsb(term);
break;
case IDM_RESET:
- term_pwron(term);
+ term_pwron(term, TRUE);
if (ldisc)
ldisc_send(ldisc, NULL, 0, 0);
break;
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:
/*
RealizePalette(hdc);
}
+ /*
+ * We have to be careful about term_paint(). It will
+ * set a bunch of character cells to INVALID and then
+ * call do_paint(), which will redraw those cells and
+ * _then mark them as done_. This may not be accurate:
+ * when painting in WM_PAINT context we are restricted
+ * to the rectangle which has just been exposed - so if
+ * that only covers _part_ of a character cell and the
+ * rest of it was already visible, that remainder will
+ * not be redrawn at all. Accordingly, we must not
+ * paint any character cell in a WM_PAINT context which
+ * already has a pending update due to terminal output.
+ * The simplest solution to this - and many, many
+ * thanks to Hung-Te Lin for working all this out - is
+ * not to do any actual painting at _all_ if there's a
+ * pending terminal update: just mark the relevant
+ * character cells as INVALID and wait for the
+ * scheduled full update to sort it out.
+ *
+ * I have a suspicion this isn't the _right_ solution.
+ * An alternative approach would be to have terminal.c
+ * separately track what _should_ be on the terminal
+ * screen and what _is_ on the terminal screen, and
+ * have two completely different types of redraw (one
+ * for full updates, which syncs the former with the
+ * terminal itself, and one for WM_PAINT which syncs
+ * the latter with the former); yet another possibility
+ * would be to have the Windows front end do what the
+ * GTK one already does, and maintain a bitmap of the
+ * current terminal appearance so that WM_PAINT becomes
+ * completely trivial. However, this should do for now.
+ */
term_paint(term, hdc,
(p.rcPaint.left-offset_width)/font_width,
(p.rcPaint.top-offset_height)/font_height,
(p.rcPaint.right-offset_width-1)/font_width,
(p.rcPaint.bottom-offset_height-1)/font_height,
- TRUE);
+ !term->window_update_pending);
if (p.fErase ||
p.rcPaint.left < offset_width ||
HBRUSH fillcolour, oldbrush;
HPEN edge, oldpen;
fillcolour = CreateSolidBrush (
- colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
+ colours[ATTR_DEFBG>>ATTR_BGSHIFT]);
oldbrush = SelectObject(hdc, fillcolour);
edge = CreatePen(PS_SOLID, 0,
- colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
+ colours[ATTR_DEFBG>>ATTR_BGSHIFT]);
oldpen = SelectObject(hdc, edge);
/*
Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
p.rcPaint.right, p.rcPaint.bottom);
- // SelectClipRgn(hdc, NULL);
+ /* SelectClipRgn(hdc, NULL); */
SelectObject(hdc, oldbrush);
DeleteObject(fillcolour);
unsigned char buf[20];
int len;
- if (wParam == VK_PROCESSKEY) {
- MSG m;
- m.hwnd = hwnd;
- m.message = WM_KEYDOWN;
- m.wParam = wParam;
- m.lParam = lParam & 0xdfff;
- TranslateMessage(&m);
+ if (wParam == VK_PROCESSKEY) { /* IME PROCESS key */
+ if (message == WM_KEYDOWN) {
+ MSG m;
+ m.hwnd = hwnd;
+ m.message = WM_KEYDOWN;
+ m.wParam = wParam;
+ m.lParam = lParam & 0xdfff;
+ TranslateMessage(&m);
+ } else break; /* pass to Windows for default processing */
} else {
len = TranslateKey(message, wParam, lParam, buf);
if (len == -1)
set_input_locale((HKL)lParam);
sys_cursor_update();
break;
- case WM_IME_NOTIFY:
- if(wParam == IMN_SETOPENSTATUS) {
+ case WM_IME_STARTCOMPOSITION:
+ {
HIMC hImc = ImmGetContext(hwnd);
ImmSetCompositionFont(hImc, &lfont);
ImmReleaseContext(hwnd, hImc);
- return 0;
}
break;
case WM_IME_COMPOSITION:
lpage_send(ldisc, CP_ACP, &c, 1, 1);
}
return 0;
- case WM_SETCURSOR:
- if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
- SetCursor(LoadCursor(NULL, IDC_ARROW));
- return TRUE;
- }
- break;
case WM_SYSCOLORCHANGE:
if (cfg.system_colour) {
/* Refresh palette from system colours. */
}
}
+ /*
+ * Any messages we don't process completely above are passed through to
+ * DefWindowProc() for default processing.
+ */
return DefWindowProc(hwnd, message, wParam, lParam);
}
y += offset_height;
if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
- attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
- attr ^= ATTR_CUR_XOR;
+ 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);
}
nfont = 0;
nfont |= FONT_OEM;
nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
- nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
- nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
nfont |= FONT_BOLD;
if (und_mode == UND_FONT && (attr & ATTR_UNDER))
nfg = nbg;
nbg = t;
}
- if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
- nfg |= 1;
- if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
- nbg |= 1;
+ if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) {
+ if (nfg < 16) nfg |= 8;
+ else if (nfg >= 256) nfg |= 1;
+ }
+ if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) {
+ if (nbg < 16) nbg |= 8;
+ else if (nbg >= 256) nbg |= 1;
+ }
fg = colours[nfg];
bg = colours[nbg];
SelectObject(hdc, fonts[nfont]);
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);
- */
+ general_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
+ &line_box, wbuf, len, IpDx, !(attr & TATTR_COMBINING));
/* And the shadow bold hack. */
if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
pts[2].x = pts[3].x = x + char_width - 1;
pts[0].y = pts[3].y = pts[4].y = y;
pts[1].y = pts[2].y = y + font_height - 1;
- oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
+ oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));
Polyline(hdc, pts, 5);
oldpen = SelectObject(hdc, oldpen);
DeleteObject(oldpen);
if (attr & TATTR_ACTCURS) {
HPEN oldpen;
oldpen =
- SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
+ SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));
MoveToEx(hdc, startx, starty, NULL);
LineTo(hdc, startx + dx * length, starty + dy * length);
oldpen = SelectObject(hdc, oldpen);
} else {
for (i = 0; i < length; i++) {
if (i % 2 == 0) {
- SetPixel(hdc, startx, starty, colours[23]);
+ SetPixel(hdc, startx, starty, colours[261]);
}
startx += dx;
starty += dy;
if (cfg.nethack_keypad && !left_alt) {
switch (wParam) {
case VK_NUMPAD1:
- *p++ = shift_state ? 'B' : 'b';
+ *p++ = "bB\002\002"[shift_state & 3];
return p - output;
case VK_NUMPAD2:
- *p++ = shift_state ? 'J' : 'j';
+ *p++ = "jJ\012\012"[shift_state & 3];
return p - output;
case VK_NUMPAD3:
- *p++ = shift_state ? 'N' : 'n';
+ *p++ = "nN\016\016"[shift_state & 3];
return p - output;
case VK_NUMPAD4:
- *p++ = shift_state ? 'H' : 'h';
+ *p++ = "hH\010\010"[shift_state & 3];
return p - output;
case VK_NUMPAD5:
*p++ = shift_state ? '.' : '.';
return p - output;
case VK_NUMPAD6:
- *p++ = shift_state ? 'L' : 'l';
+ *p++ = "lL\014\014"[shift_state & 3];
return p - output;
case VK_NUMPAD7:
- *p++ = shift_state ? 'Y' : 'y';
+ *p++ = "yY\031\031"[shift_state & 3];
return p - output;
case VK_NUMPAD8:
- *p++ = shift_state ? 'K' : 'k';
+ *p++ = "kK\013\013"[shift_state & 3];
return p - output;
case VK_NUMPAD9:
- *p++ = shift_state ? 'U' : 'u';
+ *p++ = "uU\025\025"[shift_state & 3];
return p - output;
}
}
logpal->palPalEntry[n].peBlue = b;
logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
colours[n] = PALETTERGB(r, g, b);
- SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
+ SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
} else
colours[n] = RGB(r, g, b);
}
void palette_set(void *frontend, int n, int r, int g, int b)
{
- static const int first[21] = {
- 0, 2, 4, 6, 8, 10, 12, 14,
- 1, 3, 5, 7, 9, 11, 13, 15,
- 16, 17, 18, 20, 22
- };
- real_palette_set(first[n], r, g, b);
- if (first[n] >= 18)
- real_palette_set(first[n] + 1, r, g, b);
+ if (n >= 16)
+ n += 256 - 16;
+ if (n > NALLCOLOURS)
+ return;
+ real_palette_set(n, r, g, b);
if (pal) {
HDC hdc = get_ctx(frontend);
UnrealizeObject(pal);
RealizePalette(hdc);
free_ctx(hdc);
+ } else {
+ if (n == (ATTR_DEFBG>>ATTR_BGSHIFT))
+ /* If Default Background changes, we need to ensure any
+ * space between the text area and the window border is
+ * redrawn. */
+ InvalidateRect(hwnd, NULL, TRUE);
}
}
{
int i;
- for (i = 0; i < NCOLOURS; i++) {
+ /* And this */
+ for (i = 0; i < NALLCOLOURS; i++) {
if (pal) {
logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
if (pal) {
HDC hdc;
- SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
+ SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
hdc = get_ctx(frontend);
RealizePalette(hdc);
free_ctx(hdc);
+ } else {
+ /* Default Background may have changed. Ensure any space between
+ * text area and window border is redrawn. */
+ InvalidateRect(hwnd, NULL, TRUE);
}
}
/*
* Note: unlike write_aclip() this will not append a nul.
*/
-void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
+void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)
{
HGLOBAL clipdata, clipdata2, clipdata3;
int len2;
int rtfsize = 0;
int multilen, blen, alen, totallen, i;
char before[16], after[4];
+ int fgcolour, lastfgcolour = 0;
+ int bgcolour, lastbgcolour = 0;
+ int attrBold, lastAttrBold = 0;
+ int attrUnder, lastAttrUnder = 0;
+ int palette[NALLCOLOURS];
+ int numcolours;
get_unitab(CP_ACP, unitab, 0);
rtfsize = 100 + strlen(cfg.font.name);
rtf = snewn(rtfsize, char);
- sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
- GetACP(), cfg.font.name);
- rtflen = strlen(rtf);
+ rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d",
+ cfg.font.name, cfg.font.height*2);
+
+ /*
+ * Add colour palette
+ * {\colortbl ;\red255\green0\blue0;\red0\green0\blue128;}
+ */
+
+ /*
+ * First - Determine all colours in use
+ * o Foregound and background colours share the same palette
+ */
+ if (attr) {
+ memset(palette, 0, sizeof(palette));
+ for (i = 0; i < (len-1); i++) {
+ fgcolour = ((attr[i] & ATTR_FGMASK) >> ATTR_FGSHIFT);
+ bgcolour = ((attr[i] & ATTR_BGMASK) >> ATTR_BGSHIFT);
+
+ if (attr[i] & ATTR_REVERSE) {
+ int tmpcolour = fgcolour; /* Swap foreground and background */
+ fgcolour = bgcolour;
+ bgcolour = tmpcolour;
+ }
+
+ if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) {
+ if (fgcolour < 8) /* ANSI colours */
+ fgcolour += 8;
+ else if (fgcolour >= 256) /* Default colours */
+ fgcolour ++;
+ }
+
+ if (attr[i] & ATTR_BLINK) {
+ if (bgcolour < 8) /* ANSI colours */
+ bgcolour += 8;
+ else if (bgcolour >= 256) /* Default colours */
+ bgcolour ++;
+ }
+
+ palette[fgcolour]++;
+ palette[bgcolour]++;
+ }
+
+ /*
+ * Next - Create a reduced palette
+ */
+ numcolours = 0;
+ for (i = 0; i < NALLCOLOURS; i++) {
+ if (palette[i] != 0)
+ palette[i] = ++numcolours;
+ }
+
+ /*
+ * Finally - Write the colour table
+ */
+ rtf = sresize(rtf, rtfsize + (numcolours * 25), char);
+ strcat(rtf, "{\\colortbl ;");
+ rtflen = strlen(rtf);
+
+ for (i = 0; i < NALLCOLOURS; i++) {
+ if (palette[i] != 0) {
+ rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;", defpal[i].rgbtRed, defpal[i].rgbtGreen, defpal[i].rgbtBlue);
+ }
+ }
+ strcpy(&rtf[rtflen], "}");
+ rtflen ++;
+ }
/*
* We want to construct a piece of RTF that specifies the
tdata[tindex+1] == '\n') {
tindex++;
uindex++;
+ }
+
+ /*
+ * Set text attributes
+ */
+ if (attr) {
+ if (rtfsize < rtflen + 64) {
+ rtfsize = rtflen + 512;
+ rtf = sresize(rtf, rtfsize, char);
+ }
+
+ /*
+ * Determine foreground and background colours
+ */
+ fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT);
+ bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT);
+
+ if (attr[tindex] & ATTR_REVERSE) {
+ int tmpcolour = fgcolour; /* Swap foreground and background */
+ fgcolour = bgcolour;
+ bgcolour = tmpcolour;
+ }
+
+ if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) {
+ if (fgcolour < 8) /* ANSI colours */
+ fgcolour += 8;
+ else if (fgcolour >= 256) /* Default colours */
+ fgcolour ++;
+ }
+
+ if (attr[tindex] & ATTR_BLINK) {
+ if (bgcolour < 8) /* ANSI colours */
+ bgcolour += 8;
+ else if (bgcolour >= 256) /* Default colours */
+ bgcolour ++;
+ }
+
+ /*
+ * Collect other attributes
+ */
+ if (bold_mode != BOLD_COLOURS)
+ attrBold = attr[tindex] & ATTR_BOLD;
+ else
+ attrBold = 0;
+
+ attrUnder = attr[tindex] & ATTR_UNDER;
+
+ /*
+ * Reverse video
+ * o If video isn't reversed, ignore colour attributes for default foregound
+ * or background.
+ * o Special case where bolded text is displayed using the default foregound
+ * and background colours - force to bolded RTF.
+ */
+ if (!(attr[tindex] & ATTR_REVERSE)) {
+ if (bgcolour >= 256) /* Default color */
+ bgcolour = -1; /* No coloring */
+
+ if (fgcolour >= 256) { /* Default colour */
+ if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1)
+ attrBold = ATTR_BOLD; /* Emphasize text with bold attribute */
+
+ fgcolour = -1; /* No coloring */
+ }
+ }
+
+ /*
+ * Write RTF text attributes
+ */
+ if (lastfgcolour != fgcolour) {
+ lastfgcolour = fgcolour;
+ rtflen += sprintf(&rtf[rtflen], "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0);
+ }
+
+ if (lastbgcolour != bgcolour) {
+ lastbgcolour = bgcolour;
+ rtflen += sprintf(&rtf[rtflen], "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0);
+ }
+
+ if (lastAttrBold != attrBold) {
+ lastAttrBold = attrBold;
+ rtflen += sprintf(&rtf[rtflen], "%s", attrBold ? "\\b " : "\\b0 ");
+ }
+
+ if (lastAttrUnder != attrUnder) {
+ lastAttrUnder = attrUnder;
+ rtflen += sprintf(&rtf[rtflen], "%s", attrUnder ? "\\ul " : "\\ulnone ");
+ }
}
+
if (unitab[tdata[tindex]] == udata[uindex]) {
multilen = 1;
before[0] = '\0';
uindex++;
}
- strcpy(rtf + rtflen, "}");
- rtflen += 2;
+ rtf[rtflen++] = '}'; /* Terminate RTF stream */
+ rtf[rtflen++] = '\0';
+ rtf[rtflen++] = '\0';
clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
- strcpy(lock3, rtf);
+ memcpy(lock3, rtf, rtflen);
GlobalUnlock(clipdata3);
}
sfree(rtf);
/*
* Beep.
*/
-void beep(void *frontend, int mode)
+void do_beep(void *frontend, int mode)
{
if (mode == BELL_DEFAULT) {
/*
{
if (!IsZoomed(hwnd))
return FALSE;
- if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CAPTION)
return FALSE;
return TRUE;
}
return;
/* Remove the window furniture. */
- style = GetWindowLong(hwnd, GWL_STYLE);
+ style = GetWindowLongPtr(hwnd, GWL_STYLE);
style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
if (cfg.scrollbar_in_fullscreen)
style |= WS_VSCROLL;
else
style &= ~WS_VSCROLL;
- SetWindowLong(hwnd, GWL_STYLE, style);
+ SetWindowLongPtr(hwnd, GWL_STYLE, style);
/* Resize ourselves to exactly cover the nearest monitor. */
get_fullscreen_rect(&ss);
ss.bottom - ss.top,
SWP_FRAMECHANGED);
+ /* We may have changed size as a result */
+
+ reset_window(0);
+
/* Tick the menu item in the System menu. */
CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
MF_CHECKED);
DWORD oldstyle, style;
/* Reinstate the window furniture. */
- style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
+ style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE);
style |= WS_CAPTION | WS_BORDER;
if (cfg.resize_action == RESIZE_DISABLED)
style &= ~WS_THICKFRAME;
else
style &= ~WS_VSCROLL;
if (style != oldstyle) {
- SetWindowLong(hwnd, GWL_STYLE, style);
+ SetWindowLongPtr(hwnd, GWL_STYLE, style);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_FRAMECHANGED);
return term_data(term, is_stderr, data, len);
}
+int from_backend_untrusted(void *frontend, const char *data, int len)
+{
+ return term_data_untrusted(term, data, len);
+}
+
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
+{
+ int ret;
+ ret = cmdline_get_passwd_input(p, in, inlen);
+ if (ret == -1)
+ ret = term_get_userpass_input(term, p, in, inlen);
+ return ret;
+}
+
void agent_schedule_callback(void (*callback)(void *, void *, int),
void *callback_ctx, void *data, int len)
{