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);
(((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)
init_help();
- init_flashwindow();
+ init_winfuncs();
conf = conf_new();
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 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.
+ * order to protect unbracketed IPv6 address literals
+ * against this treatment, we do not do this if there's
+ * _more_ than one colon.
*/
{
- char *c = strchr(host, ':');
+ char *c = host_strchr(host, ':');
if (c) {
- char *d = strchr(c+1, ':');
+ char *d = host_strchr(c+1, ':');
if (!d)
*c = '\0';
}
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)) {
+ if (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 */
- 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();
}
- /* 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();
+ run_toplevel_callbacks();
}
finished:
if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON)
PostQuitMessage(1);
else {
- must_close_session = TRUE;
+ queue_toplevel_callback(close_session, NULL);
}
}
/*
* 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);
}
/*
(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
* 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;
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);
return DefWindowProc(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 */
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 */
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,
/* 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) {
- r = ToUnicodeEx(wParam, scan, keystate, keys_unicode,
- lenof(keys_unicode), 0, kbd_layout);
+ 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'.
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++) {
wchar_t wch = keys_unicode[i];
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) {