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;
*/
{
char *p;
+ int got_host = 0;
default_protocol = DEFAULT_PROTOCOL;
default_port = DEFAULT_PORT;
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]))
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<argc?argv[i+1]:NULL, 1);
+ if (ret == -2) {
+ cmdline_error("option \"%s\" requires an argument", p);
+ } else if (ret == 2) {
+ i++; /* skip next argument */
+ } else if (ret == 1) {
+ continue; /* nothing further needs doing */
+ } else if (!strcmp(p, "-cleanup")) {
+ /*
+ * `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);
+ } else if (*p != '-') {
+ char *q = p;
+ if (got_host) {
+ /*
+ * If we already have a host name, treat
+ * this argument as a port number. NB we
+ * have to treat this as a saved -P
+ * argument, so that it will be deferred
+ * until it's a good moment to run it.
+ */
+ int ret = cmdline_process_param("-P", p, 1);
+ assert(ret == 2);
+ } else if (!strncmp(q, "telnet:", 7)) {
+ /*
+ * If the hostname starts with "telnet:",
+ * set the protocol to Telnet and process
+ * the string as a Telnet URL.
+ */
+ 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';
+ got_host = 1;
+ } else {
+ /*
+ * Otherwise, treat this argument as a host
+ * name.
+ */
+ while (*p && !isspace(*p))
+ p++;
+ if (*p)
+ *p++ = '\0';
+ strncpy(cfg.host, q, sizeof(cfg.host) - 1);
+ cfg.host[sizeof(cfg.host) - 1] = '\0';
+ got_host = 1;
+ }
+ }
}
}
+ cmdline_run_saved();
+
+ if (!*cfg.host && !do_config()) {
+ WSACleanup();
+ return 0;
+ }
+
/*
* Trim leading whitespace off the hostname if it's there.
*/
* Trim a colon suffix off the hostname if it's there.
*/
cfg.host[strcspn(cfg.host, ":")] = '\0';
+
+ /*
+ * Remove any remaining whitespace from the hostname.
+ */
+ {
+ int p1 = 0, p2 = 0;
+ while (cfg.host[p2] != '\0') {
+ if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
+ cfg.host[p1] = cfg.host[p2];
+ p1++;
+ }
+ p2++;
+ }
+ cfg.host[p1] = '\0';
+ }
}
/*
guess_height = extra_height + font_height * rows;
{
RECT r;
- HWND w = GetDesktopWindow();
- GetWindowRect(w, &r);
+ get_fullscreen_rect(&r);
if (guess_width > r.right - r.left)
guess_width = r.right - r.left;
if (guess_height > r.bottom - r.top)
timer_id = 0;
}
HideCaret(hwnd);
- if (GetCapture() != hwnd)
+ if (GetCapture() != hwnd ||
+ (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
term_out();
term_update();
ShowCaret(hwnd);
}
}
- cleanup_exit(msg.wParam);
+ cleanup_exit(msg.wParam); /* this doesn't return... */
+ return msg.wParam; /* ... but optimiser doesn't know */
}
/*
*/
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));
}
}
/*
+ * 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)
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;
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;
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,
case WM_TIMER:
if (pending_netevent)
enact_pending_netevent();
- if (GetCapture() != hwnd)
+ if (GetCapture() != hwnd ||
+ (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
term_out();
noise_regular();
HideCaret(hwnd);
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 ||
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,
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) {
* messages. We _have_ to buffer everything
* we're sent.
*/
+ term_seen_key_event();
ldisc_send(buf, len, 1);
show_mouseptr(0);
}
* luni_send() covering the whole of buff. So
* instead we luni_send the characters one by one.
*/
- for (i = 0; i < n; i += 2)
+ term_seen_key_event();
+ for (i = 0; i < n; i += 2) {
luni_send((unsigned short *)(buff+i), 1, 1);
+ }
free(buff);
}
ImmReleaseContext(hwnd, hIMC);
buf[1] = wParam;
buf[0] = wParam >> 8;
+ term_seen_key_event();
lpage_send(kbd_codepage, buf, 2, 1);
} else {
char c = (unsigned char) wParam;
+ term_seen_key_event();
lpage_send(kbd_codepage, &c, 1, 1);
}
return (0);
*/
{
char c = (unsigned char)wParam;
+ term_seen_key_event();
lpage_send(CP_ACP, &c, 1, 1);
}
return 0;
} else if (compose_state == 1 && wParam != VK_CONTROL)
compose_state = 0;
- /*
- * Record that we pressed key so the scroll window can be reset, but
- * be careful to avoid Shift-UP/Down
- */
- if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
- wParam != VK_MENU && wParam != VK_CONTROL) {
- seen_key_event = 1;
- }
-
if (compose_state > 1 && left_alt)
compose_state = 0;
*p++ = 0;
return -2;
}
+ if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
+ /* We do the opposite of what is configured */
+ *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
+ *p++ = 0;
+ return -2;
+ }
if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
*p++ = 0x1B;
*p++ = '[';
return 0;
}
keybuf = nc;
+ term_seen_key_event();
luni_send(&keybuf, 1, 1);
continue;
}
if (alt_sum) {
if (in_utf || dbcs_screenfont) {
keybuf = alt_sum;
+ term_seen_key_event();
luni_send(&keybuf, 1, 1);
} else {
ch = (char) alt_sum;
* messages. We _have_ to buffer
* everything we're sent.
*/
+ term_seen_key_event();
ldisc_send(&ch, 1, 1);
}
alt_sum = 0;
} else
+ term_seen_key_event();
lpage_send(kbd_codepage, &ch, 1, 1);
} else {
if(capsOn && ch < 0x80) {
WCHAR cbuf[2];
cbuf[0] = 27;
cbuf[1] = xlat_uskbd2cyrllic(ch);
+ term_seen_key_event();
luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
} else {
char cbuf[2];
cbuf[0] = '\033';
cbuf[1] = ch;
+ term_seen_key_event();
lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
}
}
return -1;
}
+void request_paste(void)
+{
+ /*
+ * In Windows, pasting is synchronous: we can read the
+ * clipboard with no difficulty, so request_paste() can just go
+ * ahead and paste.
+ */
+ term_do_paste();
+}
+
void set_title(char *title)
{
sfree(window_name);
}
/*
+ * Print a modal (Really Bad) message box and perform a fatal exit.
+ */
+void modalfatalbox(char *fmt, ...)
+{
+ va_list ap;
+ char stuff[200];
+
+ va_start(ap, fmt);
+ vsprintf(stuff, fmt, ap);
+ va_end(ap);
+ MessageBox(hwnd, stuff, "PuTTY Fatal Error",
+ MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+ cleanup_exit(1);
+}
+
+/*
* Manage window caption / taskbar flashing, if enabled.
* 0 = stop, 1 = maintain, 2 = start
*/
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.
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);
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,