#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
+#include <time.h>
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
+#include "storage.h"
#include "win_res.h"
#define IDM_SHOWLOG 0x0010
static LPARAM pend_netevent_lParam = 0;
static void enact_pending_netevent(void);
+static time_t last_movement = 0;
+
#define FONT_NORMAL 0
#define FONT_BOLD 1
#define FONT_UNDERLINE 2
static HWND hwnd;
+static HBITMAP caretbm;
+
static int dbltime, lasttime, lastact;
static Mouse_Button lastbtn;
default_protocol = DEFAULT_PROTOCOL;
default_port = DEFAULT_PORT;
- do_defaults(NULL);
+ do_defaults(NULL, &cfg);
p = cmdline;
while (*p && isspace(*p)) p++;
tolower(p[1]) == 'o' &&
tolower(p[2]) == 'g') {
logfile = "putty.log";
+ } 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 == '@') {
- do_defaults (p+1);
+ do_defaults (p+1, &cfg);
if (!*cfg.host && !do_config()) {
WSACleanup();
return 0;
SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
/*
+ * Set up a caret bitmap, with no content.
+ */
+ {
+ char *bits;
+ int size = (font_width+15)/16 * 2 * font_height;
+ bits = calloc(size, 1);
+ caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
+ free(bits);
+ }
+
+ /*
* Initialise the scroll bar.
*/
{
*/
{
char *error;
- char msg[1024];
+ char msg[1024], *title;
char *realhost;
error = back->init (hwnd, cfg.host, cfg.port, &realhost);
return 0;
}
window_name = icon_name = NULL;
- sprintf(msg, "%s - PuTTY", realhost);
- set_title (msg);
- set_icon (msg);
+ if (*cfg.wintitle) {
+ title = cfg.wintitle;
+ } else {
+ sprintf(msg, "%s - PuTTY", realhost);
+ title = msg;
+ }
+ set_title (title);
+ set_icon (title);
}
session_closed = FALSE;
has_focus = (GetForegroundWindow() == hwnd);
UpdateWindow (hwnd);
+ if (GetMessage (&msg, NULL, 0, 0) == 1)
{
int timer_id = 0, long_timer = 0;
- while (GetMessage (&msg, NULL, 0, 0) == 1) {
+ while (msg.message != WM_QUIT) {
/* Sometimes DispatchMessage calls routines that use their own
* GetMessage loop, setup this timer so we get some control back.
*
timer_id = SetTimer(hwnd, 1, 20, NULL);
DispatchMessage (&msg);
- /* This is too fast, but I'll leave it for now 'cause it shows
- * how often term_update is called (far too often at times!)
- */
+ /* Make sure we blink everything that needs it. */
term_blink(0);
/* Send the paste buffer if there's anything to send */
* we've delayed, reading the socket, writing, and repainting
* the window.
*/
- if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
- if (pending_netevent) {
- enact_pending_netevent();
+ if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+ continue;
- term_blink(1);
- }
- } else continue;
- if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
- if (timer_id) {
- KillTimer(hwnd, timer_id);
- timer_id = 0;
- }
- if (inbuf_head)
- term_out();
- term_update();
- if (!has_focus)
- timer_id = SetTimer(hwnd, 1, 2000, NULL);
- else if (cfg.blinktext)
- timer_id = SetTimer(hwnd, 1, 250, NULL);
- else
- timer_id = SetTimer(hwnd, 1, 500, NULL);
- long_timer = 1;
+ if (pending_netevent) {
+ enact_pending_netevent();
+
+ /* Force the cursor blink on */
+ term_blink(1);
+
+ if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+ continue;
+ }
+
+ /* Okay there is now nothing to do so we make sure the screen is
+ * completely up to date then tell windows to call us in a little
+ * while.
+ */
+ if (timer_id) {
+ KillTimer(hwnd, timer_id);
+ timer_id = 0;
}
+ HideCaret(hwnd);
+ if (inbuf_head)
+ term_out();
+ term_update();
+ ShowCaret(hwnd);
+ if (!has_focus)
+ timer_id = SetTimer(hwnd, 1, 59500, NULL);
+ else
+ timer_id = SetTimer(hwnd, 1, 250, NULL);
+ long_timer = 1;
+
+ /* There's no point rescanning everything in the message queue
+ * so we do an apperently unneccesary wait here
+ */
+ WaitMessage();
+ if (GetMessage (&msg, NULL, 0, 0) != 1)
+ break;
}
}
case WSAECONNRESET:
sprintf(buf, "Connection reset by peer");
break;
+ case WSAECONNABORTED:
+ sprintf(buf, "Connection aborted");
+ break;
default:
sprintf(buf, "Unexpected network error %d", -i);
break;
if (cfg.fontisbold) {
fw_dontcare = FW_BOLD;
- fw_bold = FW_BLACK;
+ fw_bold = FW_HEAVY;
} else {
fw_dontcare = FW_DONTCARE;
fw_bold = FW_BOLD;
enact_pending_netevent();
if (inbuf_head)
term_out();
+ HideCaret(hwnd);
term_update();
+ ShowCaret(hwnd);
+ if (cfg.ping_interval > 0)
+ {
+ time_t now;
+ time(&now);
+ if (now-last_movement > cfg.ping_interval * 60 - 10)
+ {
+ back->special(TS_PING);
+ last_movement = now;
+ }
+ }
return 0;
case WM_CREATE:
break;
case WM_PAINT:
{
PAINTSTRUCT p;
+ HideCaret(hwnd);
hdc = BeginPaint (hwnd, &p);
if (pal) {
SelectPalette (hdc, pal, TRUE);
SelectObject (hdc, GetStockObject(SYSTEM_FONT));
SelectObject (hdc, GetStockObject(WHITE_PEN));
EndPaint (hwnd, &p);
+ ShowCaret(hwnd);
}
return 0;
case WM_NETEVENT:
pending_netevent = TRUE;
pend_netevent_wParam=wParam;
pend_netevent_lParam=lParam;
+ time(&last_movement);
return 0;
case WM_SETFOCUS:
has_focus = TRUE;
+ CreateCaret(hwnd, caretbm, 0, 0);
+ ShowCaret(hwnd);
term_out();
term_update();
break;
case WM_KILLFOCUS:
has_focus = FALSE;
+ DestroyCaret();
term_out();
term_update();
break;
}
/*
+ * Move the system caret. (We maintain one, even though it's
+ * invisible, for the benefit of blind people: apparently some
+ * helper software tracks the system caret, so we should arrange to
+ * have one.)
+ */
+void sys_cursor(int x, int y) {
+ SetCaretPos(x * font_width, y * font_height);
+}
+
+/*
* Draw a line of text in the window, at given character
* coordinates, in given attributes.
*
}
}
- if (attr & ATTR_GBCHR) {
- int i;
- /*
- * GB mapping: map # to pound, and everything else stays
- * normal.
- */
- for (i=0; i<len; i++)
- if (text[i] == '#')
- text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
- } else if (attr & ATTR_LINEDRW) {
+ if (attr & ATTR_LINEDRW) {
int i;
/* ISO 8859-1 */
static const char poorman[] =
/*
* Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
* VT100 line drawing chars; everything else stays normal.
+ *
+ * Actually '_' maps to space too, but that's done before.
*/
switch (cfg.vtmode) {
case VT_XWINDOWS:
int r, i, code;
unsigned char * p = output;
-static WORD keys[3];
-static int compose_state = 0;
-static int compose_char = 0;
-static WPARAM compose_key = 0;
+ static WORD keys[3];
+ static int compose_state = 0;
+ static int compose_char = 0;
+ static WPARAM compose_key = 0;
r = GetKeyboardState(keystate);
if (!r) memset(keystate, 0, sizeof(keystate));
else
{
+#if 0
+ { /* Tell us all about key events */
+ static BYTE oldstate[256];
+ static int first = 1;
+ static int scan;
+ int ch;
+ if(first) memcpy(oldstate, keystate, sizeof(oldstate));
+ first=0;
+
+ if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
+ debug(("+"));
+ } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
+ debug((". U"));
+ } else {
+ debug((".\n"));
+ if (wParam >= VK_F1 && wParam <= VK_F20 )
+ debug(("K_F%d", wParam+1-VK_F1));
+ else switch(wParam)
+ {
+ case VK_SHIFT: debug(("SHIFT")); break;
+ case VK_CONTROL: debug(("CTRL")); break;
+ case VK_MENU: debug(("ALT")); break;
+ default: debug(("VK_%02x", wParam));
+ }
+ if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
+ debug(("*"));
+ debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
+
+ ch = MapVirtualKey(wParam, 2);
+ if (ch>=' ' && ch<='~') debug((", '%c'", ch));
+ 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 ( (keystate[VK_SHIFT]&0x80)!=0) debug((", S"));
+ if ( (keystate[VK_CONTROL]&0x80)!=0) debug((", C"));
+ if ( (HIWORD(lParam)&KF_EXTENDED) ) debug((", E"));
+ if ( (HIWORD(lParam)&KF_UP) ) debug((", U"));
+ }
+
+ if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
+ ;
+ else if ( (HIWORD(lParam)&KF_UP) )
+ oldstate[wParam&0xFF] ^= 0x80;
+ else
+ oldstate[wParam&0xFF] ^= 0x81;
+
+ for(ch=0; ch<256; ch++)
+ if (oldstate[ch] != keystate[ch])
+ debug((", M%02x=%02x", ch, keystate[ch]));
+
+ memcpy(oldstate, keystate, sizeof(oldstate));
+ }
+#endif
+
/* Note if AltGr was pressed and if it was used as a compose key */
if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
{
compose_state = 0;
/* Nastyness with NUMLock - Shift-NUMLock is left alone though */
- if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
+ if ( (cfg.funky_type == 3 || (cfg.funky_type <= 1 && app_keypad_keys))
&& wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
wParam = VK_EXECUTE;
/* Sanitize the number pad if not using a PC NumPad */
if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
- || cfg.nethack_keypad || compose_state )
+ || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
{
if ((HIWORD(lParam)&KF_EXTENDED) == 0)
{
SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
return -1;
}
+ /* Control-Numlock for app-keypad mode switch */
+ if (wParam == VK_PAUSE && shift_state == 2) {
+ app_keypad_keys ^= 1;
+ return 0;
+ }
/* Nethack keypad */
if (cfg.nethack_keypad && !left_alt) {
if (!left_alt) {
int xkey = 0;
- if ( cfg.funky_type == 0 ||
- ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
- case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
+ if ( cfg.funky_type == 3 ||
+ ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) {
+ case VK_EXECUTE: xkey = 'P'; break;
case VK_DIVIDE: xkey = 'Q'; break;
case VK_MULTIPLY:xkey = 'R'; break;
case VK_SUBTRACT:xkey = 'S'; break;
case VK_NUMPAD9: xkey = 'y'; break;
case VK_DECIMAL: xkey = 'n'; break;
- case VK_ADD: if(shift_state) xkey = 'm';
- else xkey = 'l';
+ case VK_ADD: if(cfg.funky_type==2) {
+ if(shift_state) xkey = 'l';
+ else xkey = 'k';
+ } else if(shift_state) xkey = 'm';
+ else xkey = 'l';
break;
+
+ case VK_DIVIDE: if(cfg.funky_type==2) xkey = 'o'; break;
+ case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
+ case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
+
case VK_RETURN:
if (HIWORD(lParam)&KF_EXTENDED)
xkey = 'M';
case VK_PRIOR: code = 5; break;
case VK_NEXT: code = 6; break;
}
+ /* Reorder edit keys to physical order */
+ if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
+
if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
return p - output;
}
if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
- p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
+ if (vt52_mode)
+ p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
+ else
+ p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
return p - output;
}
if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
return p - output;
}
}
+
+ /*
+ * Finally, deal with Return ourselves. (Win95 seems to
+ * foul it up when Alt is pressed, for some reason.)
+ */
+ if (wParam == VK_RETURN) /* Return */
+ {
+ *p++ = 0x0D;
+ return p-output;
+ }
}
/* Okay we've done everything interesting; let windows deal with
/* This stops ALT press-release doing a 'COMMAND MENU' function */
if (message == WM_SYSKEYUP && wParam == VK_MENU)
- {
- keystate[VK_MENU] = 0;
return 0;
- }
return -1;
}
}
}
-void write_clip (void *data, int len) {
+void write_clip (void *data, int len, int must_deselect) {
HGLOBAL clipdata;
void *lock;
((unsigned char *) lock) [len] = 0;
GlobalUnlock (clipdata);
- SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+ if (!must_deselect)
+ SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+
if (OpenClipboard (hwnd)) {
EmptyClipboard();
SetClipboardData (CF_TEXT, clipdata);
CloseClipboard();
} else
GlobalFree (clipdata);
- SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
+
+ if (!must_deselect)
+ SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
}
void get_clip (void **p, int *len) {