#include "terminal.h"
#include "storage.h"
#include "win_res.h"
+#include "winsecur.h"
#ifndef NO_MULTIMON
#include <multimon.h>
#define WHEEL_DELTA 120
#endif
+/* VK_PACKET, used to send Unicode characters in WM_KEYDOWNs */
+#ifndef VK_PACKET
+#define VK_PACKET 0xE7
+#endif
+
static Mouse_Button translate_button(Mouse_Button button);
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
static void deinit_fonts(void);
static void set_input_locale(HKL);
static void update_savedsess_menu(void);
-static void init_flashwindow(void);
+static void init_winfuncs(void);
static int is_full_screen(void);
static void make_full_screen(void);
static int was_zoomed = 0;
static int prev_rows, prev_cols;
-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 get_fullscreen_rect(RECT * ss);
static void *backhandle;
static struct unicode_data ucsdata;
-static int must_close_session, session_closed;
+static int session_closed;
static int reconfiguring = FALSE;
static const struct telnet_special *specials = NULL;
enum { SYSMENU, CTXMENU };
static HMENU savedsess_menu;
+struct wm_netevent_params {
+ /* Used to pass data to wm_netevent_callback */
+ WPARAM wParam;
+ LPARAM lParam;
+};
+
Conf *conf; /* exported to windlg.c */
static void conf_cache_data(void);
#define FONT_OEMUND 0x22
#define FONT_OEMBOLDUND 0x23
-#define FONT_MAXNO 0x2F
+#define FONT_MAXNO 0x40
#define FONT_SHIFT 5
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;
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 */
+
+const int share_can_be_downstream = TRUE;
+const int share_can_be_upstream = TRUE;
+
/* Dummy routine, only required in plink. */
-void ldisc_update(void *frontend, int echo, int edit)
+void frontend_echoedit_update(void *frontend, int echo, int edit)
{
}
DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
}
- must_close_session = FALSE;
session_closed = FALSE;
}
-static void close_session(void)
+static void close_session(void *ignored_context)
{
char morestuff[100];
int i;
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;
init_help();
- init_flashwindow();
+ init_winfuncs();
conf = conf_new();
return 1;
}
+ /*
+ * Protect our process
+ */
+ {
+#ifndef UNPROTECT
+ char *error = NULL;
+ if (! setprocessacl(error)) {
+ char *message = dupprintf("Could not restrict process ACL: %s",
+ error);
+ logevent(NULL, message);
+ sfree(message);
+ sfree(error);
+ }
+#endif
+ }
/*
* Process the command line.
*/
q += 2;
conf_set_int(conf, CONF_protocol, PROT_TELNET);
p = q;
- while (*p && *p != ':' && *p != '/')
- p++;
+ p += host_strcspn(p, ":/");
c = *p;
if (*p)
*p++ = '\0';
}
}
- /*
- * Trim off a colon suffix if it's there.
- */
- host[strcspn(host, ":")] = '\0';
+ /*
+ * Trim a colon suffix off the hostname if it's there. In
+ * order to protect unbracketed IPv6 address literals
+ * against this treatment, we do not do this if there's
+ * _more_ than one colon.
+ */
+ {
+ char *c = host_strchr(host, ':');
+
+ if (c) {
+ char *d = host_strchr(c+1, ':');
+ if (!d)
+ *c = '\0';
+ }
+ }
/*
* Remove any remaining whitespace.
}
if (!prev) {
+ WNDCLASSW wndclass;
+
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = appname;
+ wndclass.lpszClassName = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
- RegisterClass(&wndclass);
+ RegisterClassW(&wndclass);
}
memset(&ucsdata, 0, sizeof(ucsdata));
{
int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
int exwinmode = 0;
+ wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
if (!conf_get_int(conf, CONF_scrollbar))
winmode &= ~(WS_VSCROLL);
if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
exwinmode |= WS_EX_TOPMOST;
if (conf_get_int(conf, CONF_sunken_edge))
exwinmode |= WS_EX_CLIENTEDGE;
- hwnd = CreateWindowEx(exwinmode, appname, appname,
- winmode, CW_USEDEFAULT, CW_USEDEFAULT,
- guess_width, guess_height,
- NULL, NULL, inst, NULL);
+ hwnd = CreateWindowExW(exwinmode, uappname, uappname,
+ winmode, CW_USEDEFAULT, CW_USEDEFAULT,
+ guess_width, guess_height,
+ NULL, NULL, inst, NULL);
+ sfree(uappname);
}
+ /*
+ * 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
conf_get_int(conf, CONF_width),
conf_get_int(conf, CONF_savelines));
- /*
- * Initialise the fonts, simultaneously correcting the guesses
- * for font_{width,height}.
- */
- init_fonts(0,0);
-
/*
* Correct the guesses for extra_{width,height}.
*/
while (1) {
HANDLE *handles;
int nhandles, n;
+ DWORD timeout;
+
+ if (toplevel_callback_pending() ||
+ PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+ /*
+ * If we have anything we'd like to do immediately, set
+ * the timeout for MsgWaitForMultipleObjects to zero so
+ * that we'll only do a quick check of our handles and
+ * then get on with whatever that was.
+ *
+ * One such option is a pending toplevel callback. The
+ * other is a non-empty Windows message queue, which you'd
+ * think we could leave to MsgWaitForMultipleObjects to
+ * check for us along with all the handles, but in fact we
+ * can't because once PeekMessage in one iteration of this
+ * loop has removed a message from the queue, the whole
+ * queue is considered uninteresting by the next
+ * invocation of MWFMO. So we check ourselves whether the
+ * message queue is non-empty, and if so, set this timeout
+ * to zero to ensure MWFMO doesn't block.
+ */
+ timeout = 0;
+ } else {
+ timeout = INFINITE;
+ /* The messages seem unreliable; especially if we're being tricky */
+ term_set_focus(term, GetForegroundWindow() == hwnd);
+ }
handles = handle_get_events(&nhandles);
- n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, INFINITE,
- QS_ALLINPUT);
+ n = MsgWaitForMultipleObjects(nhandles, handles, FALSE,
+ timeout, 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();
} else
sfree(handles);
- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ while (PeekMessageW(&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 */
- term_paste(term);
- /* If there's nothing new in the queue then we can do everything
- * we've delayed, reading the socket, writing, and repainting
- * the window.
- */
- if (must_close_session)
- close_session();
- }
+ DispatchMessageW(&msg);
- /* The messages seem unreliable; especially if we're being tricky */
- term_set_focus(term, GetForegroundWindow() == hwnd);
-
- if (pending_netevent)
- enact_pending_netevent();
+ /*
+ * WM_NETEVENT messages seem to jump ahead of others in
+ * the message queue. I'm not sure why; the docs for
+ * PeekMessage mention that messages are prioritised in
+ * some way, but I'm unclear on which priorities go where.
+ *
+ * Anyway, in practice I observe that WM_NETEVENT seems to
+ * jump to the head of the queue, which means that if we
+ * were to only process one message every time round this
+ * loop, we'd get nothing but NETEVENTs if the server
+ * flooded us with data, and stop responding to any other
+ * kind of window message. So instead, we keep on round
+ * this loop until we've consumed at least one message
+ * that _isn't_ a NETEVENT, or run out of messages
+ * completely (whichever comes first). And we don't go to
+ * run_toplevel_callbacks (which is where the netevents
+ * are actually processed, causing fresh NETEVENT messages
+ * to appear) until we've done this.
+ */
+ if (msg.message != WM_NETEVENT)
+ break;
+ }
- net_pending_errors();
+ run_toplevel_callbacks();
}
finished:
/*
* Print a message box and close the connection.
*/
-void connection_fatal(void *frontend, char *fmt, ...)
+void connection_fatal(void *frontend, const char *fmt, ...)
{
va_list ap;
char *stuff, morestuff[100];
if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON)
PostQuitMessage(1);
else {
- must_close_session = TRUE;
+ queue_toplevel_callback(close_session, NULL);
}
}
/*
* Report an error at the command-line parsing stage.
*/
-void cmdline_error(char *fmt, ...)
+void cmdline_error(const char *fmt, ...)
{
va_list ap;
char *stuff, morestuff[100];
/*
* Actually do the job requested by a WM_NETEVENT
*/
-static void enact_pending_netevent(void)
+static void wm_netevent_callback(void *vctx)
{
- static int reentering = 0;
- extern int select_result(WPARAM, LPARAM);
-
- if (reentering)
- return; /* don't unpend the pending */
-
- pending_netevent = FALSE;
-
- reentering = 1;
- select_result(pend_netevent_wParam, pend_netevent_lParam);
- reentering = 0;
+ struct wm_netevent_params *params = (struct wm_netevent_params *)vctx;
+ select_result(params->wParam, params->lParam);
+ sfree(vctx);
}
/*
for (i = 0; i < FONT_MAXNO; i++)
fonts[i] = NULL;
- bold_mode = conf_get_int(conf, CONF_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;
font = conf_get_fontspec(conf, CONF_font);
}
}
- if (bold_mode == BOLD_FONT) {
+ if (bold_font_mode == BOLD_FONT) {
f(FONT_BOLD, font->charset, fw_bold, FALSE);
}
#undef f
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;
}
(close_on_exit == AUTO && exitcode != INT_MAX)) {
PostQuitMessage(0);
} else {
- must_close_session = TRUE;
+ queue_toplevel_callback(close_session, NULL);
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
}
}
-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;
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)) {
unsigned int sessno = ((lParam - IDM_SAVED_MIN)
/ MENU_SAVED_STEP) + 1;
if (sessno < (unsigned)sesslist.nsessions) {
- char *session = sesslist.sessions[sessno];
+ const char *session = sesslist.sessions[sessno];
cl = dupprintf("putty @%s", session);
inherit_handles = FALSE;
freecl = TRUE;
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);
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();
* 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 (ldisc) {
+ ldisc_configure(ldisc, conf);
+ ldisc_echoedit_update(ldisc);
+ }
if (pal)
DeleteObject(pal);
logpal = NULL;
{
FontSpec *font = conf_get_fontspec(conf, CONF_font);
FontSpec *prev_font = conf_get_fontspec(prev_conf,
- CONF_font);
+ CONF_font);
if (!strcmp(font->name, prev_font->name) ||
!strcmp(conf_get_str(conf, CONF_line_codepage),
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_colour) !=
- conf_get_int(prev_conf, CONF_bold_colour) ||
+ 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,
InvalidateRect(hwnd, NULL, TRUE);
reset_window(init_lvl);
- net_pending_errors();
conf_free(prev_conf);
}
case IDM_RESET:
term_pwron(term, TRUE);
if (ldisc)
- ldisc_send(ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(ldisc);
break;
case IDM_ABOUT:
showabout(hwnd);
break;
if (back)
back->special(backhandle, specials[i].code);
- net_pending_errors();
}
}
break;
}
return 0;
case WM_NETEVENT:
- /* 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();
+ {
+ /*
+ * To protect against re-entrancy when Windows's recv()
+ * immediately triggers a new WSAAsyncSelect window
+ * message, we don't call select_result directly from this
+ * handler but instead wait until we're back out at the
+ * top level of the message loop.
+ */
+ struct wm_netevent_params *params =
+ snew(struct wm_netevent_params);
+ params->wParam = wParam;
+ params->lParam = lParam;
+ queue_toplevel_callback(wm_netevent_callback, params);
+ }
return 0;
case WM_SETFOCUS:
term_set_focus(term, TRUE);
h = height / font_height;
if (h < 1) h = 1;
- term_size(term, h, w, conf_get_int(conf, CONF_savelines));
+ 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) {
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;
unsigned char buf[20];
int len;
- if (wParam == VK_PROCESSKEY) { /* IME PROCESS key */
+ if (wParam == VK_PROCESSKEY || /* IME PROCESS key */
+ wParam == VK_PACKET) { /* 'this key is a Unicode char' */
if (message == WM_KEYDOWN) {
MSG m;
m.hwnd = hwnd;
} else {
len = TranslateKey(message, wParam, lParam, buf);
if (len == -1)
- return DefWindowProc(hwnd, message, wParam, lParam);
+ return DefWindowProcW(hwnd, message, wParam, lParam);
if (len != 0) {
- /*
- * Interrupt an ongoing paste. I'm not sure
- * this is sensible, but for the moment it's
- * preferable to having to faff about buffering
- * things.
- */
- term_nopaste(term);
-
/*
* We need not bother about stdin backlogs
* here, because in GUI PuTTY we can't do
}
}
}
- net_pending_errors();
return 0;
case WM_INPUTLANGCHANGE:
/* wParam == Font number */
* 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);
}
* we're ready to cope.
*/
{
- char c = (unsigned char)wParam;
- term_seen_key_event(term);
- if (ldisc)
- lpage_send(ldisc, CP_ACP, &c, 1, 1);
+ static wchar_t pending_surrogate = 0;
+ wchar_t c = wParam;
+
+ if (IS_HIGH_SURROGATE(c)) {
+ pending_surrogate = c;
+ } else if (IS_SURROGATE_PAIR(pending_surrogate, c)) {
+ wchar_t pair[2];
+ pair[0] = pending_surrogate;
+ pair[1] = c;
+ term_seen_key_event(term);
+ luni_send(ldisc, pair, 2, 1);
+ } else if (!IS_SURROGATE(c)) {
+ term_seen_key_event(term);
+ luni_send(ldisc, &c, 1, 1);
+ }
}
return 0;
case WM_SYSCOLORCHANGE:
} else
break;
- if (send_raw_mouse && shift_pressed &&
- !(conf_get_int(conf, CONF_mouse_override))) {
+ if (send_raw_mouse &&
+ !(conf_get_int(conf, CONF_mouse_override) &&
+ shift_pressed)) {
/* Mouse wheel position is in screen coordinates for
* some reason */
POINT p;
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 */
* Any messages we don't process completely above are passed through to
* DefWindowProc() for default processing.
*/
- return DefWindowProc(hwnd, message, wParam, lParam);
+ return DefWindowProcW(hwnd, message, wParam, lParam);
}
/*
int text_adjust = 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;
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 (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])) {
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;
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;
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;
}
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)
opaque = TRUE; /* start by erasing the rectangle */
for (remaining = len; remaining > 0;
- text += len, remaining -= len, x += char_width * len) {
+ text += len, remaining -= len, x += char_width * len2) {
len = (maxlen < remaining ? maxlen : remaining);
-
- if (len > lpDx_len) {
- if (len > lpDx_len) {
- lpDx_len = len * 9 / 8 + 16;
- lpDx = sresize(lpDx, lpDx_len, int);
- }
+ /* 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;
}
+
+ if (len > lpDx_len) {
+ lpDx_len = len * 9 / 8 + 16;
+ lpDx = sresize(lpDx, lpDx_len, int);
+
+ if (lpDx_maybe) lpDx_maybe = lpDx;
+ }
+
{
int i;
- for (i = 0; i < len; 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;
+ }
+ }
}
/* We're using a private area for direct to font. (512 chars.) */
ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
&line_box, uni_buf, nlen,
lpDx_maybe);
- if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+ if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
SetBkMode(hdc, TRANSPARENT);
ExtTextOutW(hdc, x + xoffset - 1,
y - font_height * (lattr ==
y - font_height * (lattr == LATTR_BOT) + text_adjust,
ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
&line_box, directbuf, len, lpDx_maybe);
- if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+ if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
SetBkMode(hdc, TRANSPARENT);
/* GRR: This draws the character outside its box and
opaque && !(attr & TATTR_COMBINING));
/* And the shadow bold hack. */
- if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+ if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
SetBkMode(hdc, TRANSPARENT);
ExtTextOutW(hdc, x + xoffset - 1,
y - font_height * (lattr ==
{
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;
}
return ibuf;
}
+DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
+DECL_WINDOWS_FUNCTION(static, BOOL, ToUnicodeEx,
+ (UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL));
+
+static void init_winfuncs(void)
+{
+ HMODULE user32_module = load_system32_dll("user32.dll");
+ GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
+ GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx);
+}
+
/*
* Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
* codes. Returns number of bytes used, zero to drop the message,
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_keycode = 0;
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"));
/* XXX how do we know what the max size of the keys array should
* be is? There's indication on MS' website of an Inquire/InquireEx
* functioning returning a KBINFO structure which tells us. */
- if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT && p_ToUnicodeEx) {
+ r = p_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
* 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) {
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((")"));
}
if (r > 0) {
WCHAR keybuf;
- /*
- * Interrupt an ongoing paste. I'm not sure this is
- * sensible, but for the moment it's preferable to
- * having to faff about buffering things.
- */
- term_nopaste(term);
-
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;
}
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
} 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;
}
/*
{
if (n >= 16)
n += 256 - 16;
- if (n > NALLCOLOURS)
+ if (n >= NALLCOLOURS)
return;
real_palette_set(n, r, g, b);
if (pal) {
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);
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 */
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 */
/*
* Collect other attributes
*/
- if (bold_mode != BOLD_COLOURS)
+ if (bold_font_mode != BOLD_NONE)
attrBold = attr[tindex] & ATTR_BOLD;
else
attrBold = 0;
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 */
/*
* Print a message box and perform a fatal exit.
*/
-void fatalbox(char *fmt, ...)
+void fatalbox(const char *fmt, ...)
{
va_list ap;
char *stuff, morestuff[100];
/*
* Print a modal (Really Bad) message box and perform a fatal exit.
*/
-void modalfatalbox(char *fmt, ...)
+void modalfatalbox(const char *fmt, ...)
{
va_list ap;
char *stuff, morestuff[100];
cleanup_exit(1);
}
-DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
-
-static void init_flashwindow(void)
+/*
+ * Print a message box and don't close the connection.
+ */
+void nonfatal(const char *fmt, ...)
{
- HMODULE user32_module = load_system32_dll("user32.dll");
- GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
+ 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);
}
static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout)
* Timer for platforms where we must maintain window flashing manually
* (e.g., Win95).
*/
-static void flash_window_timer(void *ctx, long now)
+static void flash_window_timer(void *ctx, unsigned long now)
{
- if (flashing && now - next_flash >= 0) {
+ if (flashing && now == next_flash) {
flash_window(1);
}
}
return TRUE; /* do respond to incoming EOF with outgoing */
}
-int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
+int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen)
{
int ret;
ret = cmdline_get_passwd_input(p, in, inlen);