X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=windows%2Fwindow.c;h=2bbdc263c9da91dad5f3682da3b38c24429708e4;hb=a063e522970946bf7d5dc052079d7773c0dee76d;hp=b477d6ae81fda4af6044bef6f6c798d0bdf1a9e3;hpb=b99bec3b02d79c6902f50cfa616eebaee43536f6;p=PuTTY.git diff --git a/windows/window.c b/windows/window.c index b477d6ae..2bbdc263 100644 --- a/windows/window.c +++ b/windows/window.c @@ -76,6 +76,11 @@ #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, @@ -88,7 +93,7 @@ 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 void init_winfuncs(void); static int is_full_screen(void); static void make_full_screen(void); @@ -104,10 +109,6 @@ static int offset_width, offset_height; 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); @@ -121,7 +122,7 @@ static Backend *back; 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; @@ -140,6 +141,12 @@ static struct { 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); @@ -168,7 +175,7 @@ struct agent_callback { #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; @@ -213,6 +220,9 @@ static UINT wm_mousewheel = WM_MOUSEWHEEL; (((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) { @@ -289,11 +299,10 @@ static void start_backend(void) 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; @@ -324,20 +333,10 @@ 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; @@ -375,7 +374,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) init_help(); - init_flashwindow(); + init_winfuncs(); conf = conf_new(); @@ -553,8 +552,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) 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'; @@ -617,10 +615,21 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } } - /* - * 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. @@ -640,6 +649,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } if (!prev) { + WNDCLASSW wndclass; + wndclass.style = 0; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; @@ -649,9 +660,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) 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)); @@ -685,6 +696,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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) @@ -693,10 +705,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) 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); } /* @@ -844,43 +857,76 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) 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: @@ -1103,7 +1149,7 @@ void connection_fatal(void *frontend, char *fmt, ...) if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON) PostQuitMessage(1); else { - must_close_session = TRUE; + queue_toplevel_callback(close_session, NULL); } } @@ -1127,19 +1173,11 @@ void cmdline_error(char *fmt, ...) /* * 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); } /* @@ -2000,7 +2038,7 @@ void notify_remote_exit(void *fe) (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 @@ -2226,9 +2264,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * Flush the line discipline's edit buffer in the * case where local editing has just been disabled. */ - ldisc_configure(ldisc, conf); - if (ldisc) + if (ldisc) { + ldisc_configure(ldisc, conf); ldisc_send(ldisc, NULL, 0, 0); + } if (pal) DeleteObject(pal); logpal = NULL; @@ -2354,7 +2393,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, InvalidateRect(hwnd, NULL, TRUE); reset_window(init_lvl); - net_pending_errors(); conf_free(prev_conf); } @@ -2417,7 +2455,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; if (back) back->special(backhandle, specials[i].code); - net_pending_errors(); } } break; @@ -2697,20 +2734,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } 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); @@ -3054,7 +3091,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, 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; @@ -3066,17 +3104,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } 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 @@ -3092,7 +3122,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } } } - net_pending_errors(); return 0; case WM_INPUTLANGCHANGE: /* wParam == Font number */ @@ -3179,10 +3208,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * 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: @@ -3251,10 +3291,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, 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 */ @@ -3271,7 +3307,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * 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); } /* @@ -3886,6 +3922,17 @@ int char_width(Context ctx, int uc) { 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, @@ -4579,9 +4626,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* 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'. @@ -4627,13 +4674,6 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, 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]; @@ -4805,7 +4845,7 @@ void palette_set(void *frontend, int n, int r, int g, int b) { if (n >= 16) n += 256 - 16; - if (n > NALLCOLOURS) + if (n >= NALLCOLOURS) return; real_palette_set(n, r, g, b); if (pal) { @@ -5373,14 +5413,6 @@ void nonfatal(char *fmt, ...) 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) {