X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/3f0f93880087c16c416f2701cd4efcc4dc9c4ed9..1b56c05584652dba13d7f4f54e7ac2560dab92fe:/window.c diff --git a/window.c b/window.c index 439495af..9ea6c018 100644 --- a/window.c +++ b/window.c @@ -11,10 +11,12 @@ #endif #endif +#ifndef NO_MULTIMON #if WINVER < 0x0500 #define COMPILE_MULTIMON_STUBS #include #endif +#endif #include #include @@ -104,9 +106,13 @@ 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 time_t last_movement = 0; +static int caret_x = -1, caret_y = -1; + #define FONT_NORMAL 0 #define FONT_BOLD 1 #define FONT_UNDERLINE 2 @@ -154,6 +160,8 @@ static char *window_name, *icon_name; static int compose_state = 0; +static int wsa_started = 0; + static OSVERSIONINFO osVersion; static UINT wm_mousewheel = WM_MOUSEWHEEL; @@ -187,6 +195,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) WSACleanup(); return 1; } + wsa_started = 1; /* WISHLIST: maybe allow config tweaking even if winsock not present? */ sk_init(); @@ -247,6 +256,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ { char *p; + int got_host = 0; default_protocol = DEFAULT_PROTOCOL; default_port = DEFAULT_PORT; @@ -255,54 +265,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) do_defaults(NULL, &cfg); p = cmdline; - while (*p && isspace(*p)) - p++; /* - * Process command line options first. Yes, this can be - * done better, and it will be as soon as I have the - * energy... + * Process a couple of command-line options which are more + * easily dealt with before the line is broken up into + * words. These are the soon-to-be-defunct @sessionname and + * the internal-use-only &sharedmemoryhandle, neither of + * which are combined with anything else. */ - while (*p == '-') { - char *q = p + strcspn(p, " \t"); + while (*p && isspace(*p)) p++; - if (q == p + 3 && - tolower(p[0]) == 's' && - tolower(p[1]) == 's' && tolower(p[2]) == 'h') { - default_protocol = cfg.protocol = PROT_SSH; - default_port = cfg.port = 22; - } else if (q == p + 7 && - tolower(p[0]) == 'c' && - tolower(p[1]) == 'l' && - tolower(p[2]) == 'e' && - tolower(p[3]) == 'a' && - tolower(p[4]) == 'n' && - tolower(p[5]) == 'u' && tolower(p[6]) == 'p') { - /* - * `putty -cleanup'. Remove all registry entries - * associated with PuTTY, and also find and delete - * the random seed file. - */ - if (MessageBox(NULL, - "This procedure will remove ALL Registry\n" - "entries associated with PuTTY, and will\n" - "also remove the PuTTY random seed file.\n" - "\n" - "THIS PROCESS WILL DESTROY YOUR SAVED\n" - "SESSIONS. Are you really sure you want\n" - "to continue?", - "PuTTY Warning", - MB_YESNO | MB_ICONWARNING) == IDYES) { - cleanup_all(); - } - exit(0); - } - p = q + strspn(q, " \t"); - } - - /* - * An initial @ means to activate a saved session. - */ if (*p == '@') { int i = strlen(p); while (i > 1 && isspace(p[i - 1])) @@ -332,53 +304,107 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) WSACleanup(); return 0; } - } else if (*p) { - char *q = p; + } else { /* - * If the hostname starts with "telnet:", set the - * protocol to Telnet and process the string as a - * Telnet URL. + * Otherwise, break up the command line and deal with + * it sensibly. */ - if (!strncmp(q, "telnet:", 7)) { - char c; - - q += 7; - if (q[0] == '/' && q[1] == '/') - q += 2; - cfg.protocol = PROT_TELNET; - p = q; - while (*p && *p != ':' && *p != '/') - p++; - c = *p; - if (*p) - *p++ = '\0'; - if (c == ':') - cfg.port = atoi(p); - else - cfg.port = -1; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - } else { - while (*p && !isspace(*p)) - p++; - if (*p) - *p++ = '\0'; - strncpy(cfg.host, q, sizeof(cfg.host) - 1); - cfg.host[sizeof(cfg.host) - 1] = '\0'; - while (*p && isspace(*p)) - p++; - if (*p) - cfg.port = atoi(p); - else - cfg.port = -1; - } - } else { - if (!do_config()) { - WSACleanup(); - return 0; + int argc, i; + char **argv; + + split_into_argv(cmdline, &argc, &argv, NULL); + + for (i = 0; i < argc; i++) { + char *p = argv[i]; + int ret; + + ret = cmdline_process_param(p, i+1 r.right - r.left) guess_width = r.right - r.left; if (guess_height > r.bottom - r.top) @@ -720,7 +745,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) timer_id = 0; } HideCaret(hwnd); - term_out(); + if (GetCapture() != hwnd || + (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed()))) + term_out(); term_update(); ShowCaret(hwnd); @@ -747,6 +774,15 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } } + cleanup_exit(msg.wParam); /* this doesn't return... */ + return msg.wParam; /* ... but optimiser doesn't know */ +} + +/* + * Clean up and exit. + */ +void cleanup_exit(int code) +{ /* * Clean up. */ @@ -754,7 +790,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) sfree(logpal); if (pal) DeleteObject(pal); - WSACleanup(); + sk_cleanup(); + if (wsa_started) + WSACleanup(); if (cfg.protocol == PROT_SSH) { random_save_seed(); @@ -763,7 +801,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) #endif } - return msg.wParam; + exit(code); } /* @@ -797,6 +835,7 @@ char *do_select(SOCKET skt, int startup) */ void set_raw_mouse_mode(int activate) { + activate = activate && !cfg.no_mouse_rep; send_raw_mouse = activate; SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM)); } @@ -822,6 +861,21 @@ void connection_fatal(char *fmt, ...) } /* + * Report an error at the command-line parsing stage. + */ +void cmdline_error(char *fmt, ...) +{ + va_list ap; + char stuff[200]; + + va_start(ap, fmt); + vsprintf(stuff, fmt, ap); + va_end(ap); + MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK); + exit(1); +} + +/* * Actually do the job requested by a WM_NETEVENT */ static void enact_pending_netevent(void) @@ -1191,7 +1245,7 @@ void request_resize(int w, int h) switch (first_time) { case 1: /* Get the size of the screen */ - if (GetClientRect(GetDesktopWindow(), &ss)) + if (get_fullscreen_rect(&ss)) /* first_time = 0 */ ; else { first_time = 2; @@ -1360,8 +1414,9 @@ static void reset_window(int reinit) { static RECT ss; int width, height; + + get_fullscreen_rect(&ss); - GetClientRect(GetDesktopWindow(), &ss); width = (ss.right - ss.left - extra_width) / font_width; height = (ss.bottom - ss.top - extra_height) / font_height; @@ -1500,6 +1555,17 @@ static int is_alt_pressed(void) return FALSE; } +static int is_shift_pressed(void) +{ + BYTE keystate[256]; + int r = GetKeyboardState(keystate); + if (!r) + return FALSE; + if (keystate[VK_SHIFT] & 0x80) + return TRUE; + return FALSE; +} + static int resizing; static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, @@ -1514,7 +1580,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_TIMER: if (pending_netevent) enact_pending_netevent(); - term_out(); + if (GetCapture() != hwnd || + (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed()))) + term_out(); noise_regular(); HideCaret(hwnd); term_update(); @@ -1664,6 +1732,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, cfgtopalette(); init_palette(); + /* Give terminal a heads-up on miscellaneous stuff */ + term_reconfig(); + /* Screen size changed ? */ if (cfg.height != prev_cfg.height || cfg.width != prev_cfg.width || @@ -1934,7 +2005,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, */ noise_ultralight(lParam); - if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) { + if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) && + GetCapture() == hwnd) { Mouse_Button b; if (wParam & MK_LBUTTON) b = MBT_LEFT; @@ -1989,6 +2061,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]); oldpen = SelectObject(hdc, edge); + /* + * Jordan Russell reports that this apparently + * ineffectual IntersectClipRect() call masks a + * Windows NT/2K bug causing strange display + * problems when the PuTTY window is taller than + * the primary monitor. It seems harmless enough... + */ + IntersectClipRect(hdc, + p.rcPaint.left, p.rcPaint.top, + p.rcPaint.right, p.rcPaint.bottom); + ExcludeClipRect(hdc, offset_width, offset_height, offset_width+font_width*cols, @@ -2039,6 +2122,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, show_mouseptr(1); has_focus = FALSE; DestroyCaret(); + caret_x = caret_y = -1; /* ensure caret is replaced next time */ term_out(); term_update(); break; @@ -2155,6 +2239,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_FULLSCR_ON_MAX: fullscr_on_max = TRUE; break; + case WM_MOVE: + sys_cursor_update(); + break; case WM_SIZE: #ifdef RDB_DEBUG_PATCH debug((27, "WM_SIZE %s (%d,%d)", @@ -2173,8 +2260,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (wParam == SIZE_RESTORED) clear_full_screen(); if (wParam == SIZE_MAXIMIZED && fullscr_on_max) { - make_full_screen(); fullscr_on_max = FALSE; + make_full_screen(); } if (cfg.resize_action == RESIZE_DISABLED) { @@ -2238,6 +2325,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, reset_window(0); } } + sys_cursor_update(); return 0; case WM_VSCROLL: switch (LOWORD(wParam)) { @@ -2347,6 +2435,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, /* wParam == Font number */ /* lParam == Locale */ set_input_locale((HKL)lParam); + sys_cursor_update(); break; case WM_IME_NOTIFY: if(wParam == IMN_SETOPENSTATUS) { @@ -2420,7 +2509,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, return TRUE; } default: - if (message == wm_mousewheel) { + if (message == wm_mousewheel || message == WM_MOUSEWHEEL) { int shift_pressed=0, control_pressed=0, alt_pressed=0; if (message == WM_MOUSEWHEEL) { @@ -2482,13 +2571,35 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, */ void sys_cursor(int x, int y) { + int cx, cy; + + if (!has_focus) return; + + /* + * Avoid gratuitously re-updating the cursor position and IMM + * window if there's no actual change required. + */ + cx = x * font_width + offset_width; + cy = y * font_height + offset_height; + if (cx == caret_x && cy == caret_y) + return; + caret_x = cx; + caret_y = cy; + + sys_cursor_update(); +} + +static void sys_cursor_update(void) +{ COMPOSITIONFORM cf; HIMC hIMC; if (!has_focus) return; - - SetCaretPos(x * font_width + offset_width, - y * font_height + offset_height); + + if (caret_x < 0 || caret_y < 0) + return; + + SetCaretPos(caret_x, caret_y); /* IMM calls on Win98 and beyond only */ if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */ @@ -2499,8 +2610,8 @@ void sys_cursor(int x, int y) /* we should have the IMM functions */ hIMC = ImmGetContext(hwnd); cf.dwStyle = CFS_POINT; - cf.ptCurrentPos.x = x * font_width + offset_width; - cf.ptCurrentPos.y = y * font_height + offset_height; + cf.ptCurrentPos.x = caret_x; + cf.ptCurrentPos.y = caret_y; ImmSetCompositionWindow(hIMC, &cf); ImmReleaseContext(hwnd, hIMC); @@ -4087,7 +4198,7 @@ void fatalbox(char *fmt, ...) vsprintf(stuff, fmt, ap); va_end(ap); MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK); - exit(1); + cleanup_exit(1); } /* @@ -4186,6 +4297,11 @@ void set_iconic(int iconic) */ void move_window(int x, int y) { + if (cfg.resize_action == RESIZE_DISABLED || + cfg.resize_action == RESIZE_FONT || + IsZoomed(hwnd)) + return; + SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } @@ -4274,6 +4390,32 @@ int is_full_screen() return TRUE; } +/* Get the rect/size of a full screen window using the nearest available + * monitor in multimon systems; default to something sensible if only + * one monitor is present. */ +static int get_fullscreen_rect(RECT * ss) +{ +#ifdef MONITOR_DEFAULTTONEAREST + HMONITOR mon; + MONITORINFO mi; + mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof(mi); + GetMonitorInfo(mon, &mi); + + /* structure copy */ + *ss = mi.rcMonitor; + return TRUE; +#else +/* could also use code like this: + ss->left = ss->top = 0; + ss->right = GetSystemMetrics(SM_CXSCREEN); + ss->bottom = GetSystemMetrics(SM_CYSCREEN); +*/ + return GetClientRect(GetDesktopWindow(), ss); +#endif +} + + /* * Go full-screen. This should only be called when we are already * maximised. @@ -4281,10 +4423,13 @@ int is_full_screen() void make_full_screen() { DWORD style; - int x, y, w, h; + RECT ss; assert(IsZoomed(hwnd)); + if (is_full_screen()) + return; + /* Remove the window furniture. */ style = GetWindowLong(hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); @@ -4295,24 +4440,11 @@ void make_full_screen() SetWindowLong(hwnd, GWL_STYLE, style); /* Resize ourselves to exactly cover the nearest monitor. */ -#ifdef MONITOR_DEFAULTTONEAREST - { - HMONITOR mon; - MONITORINFO mi; - mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof(mi); - GetMonitorInfo(mon, &mi); - x = mi.rcMonitor.left; - y = mi.rcMonitor.top; - w = mi.rcMonitor.right; - h = mi.rcMonitor.bottom; - } -#else - x = y = 0; - w = GetSystemMetrics(SM_CXSCREEN); - h = GetSystemMetrics(SM_CYSCREEN); -#endif - SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED); + get_fullscreen_rect(&ss); + SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top, + ss.right - ss.left, + ss.bottom - ss.top, + SWP_FRAMECHANGED); /* Tick the menu item in the System menu. */ CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,