#include <windows.h>
+#include <imm.h>
#include <commctrl.h>
+#ifndef AUTO_WINSOCK
+#ifdef WINSOCK_TWO
+#include <winsock2.h>
+#else
#include <winsock.h>
+#endif
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
+#include <time.h>
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
+#include "winstuff.h"
+#include "storage.h"
#include "win_res.h"
#define IDM_SHOWLOG 0x0010
#define IDM_TEL_EOF 0x0130
#define IDM_ABOUT 0x0140
#define IDM_SAVEDSESS 0x0150
+#define IDM_COPYALL 0x0160
#define IDM_SAVED_MIN 0x1000
#define IDM_SAVED_MAX 0x2000
#define WM_IGNORE_SIZE (WM_XUSER + 1)
#define WM_IGNORE_CLIP (WM_XUSER + 2)
-#define WM_IGNORE_KEYMENU (WM_XUSER + 3)
+
+/* Needed for Chinese support and apparently not always defined. */
+#ifndef VK_PROCESSKEY
+#define VK_PROCESSKEY 0xE5
+#endif
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
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;
static char *window_name, *icon_name;
+static int compose_state = 0;
+
+/* Dummy routine, only required in plink. */
+void ldisc_update(int echo, int edit) {}
+
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
static char appname[] = "PuTTY";
WORD winsock_ver;
MSG msg;
int guess_width, guess_height;
- putty_inst = inst;
+ hinst = inst;
flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
winsock_ver = MAKEWORD(1, 1);
return 1;
}
/* WISHLIST: maybe allow config tweaking even if winsock not present? */
+ sk_init();
InitCommonControls();
+ /* Ensure a Maximize setting in Explorer doesn't maximise the
+ * config box. */
+ defuse_showwindow();
+
/*
* Process the command line.
*/
default_protocol = DEFAULT_PROTOCOL;
default_port = DEFAULT_PORT;
+ cfg.logtype = LGTYP_NONE;
- do_defaults(NULL);
+ do_defaults(NULL, &cfg);
p = cmdline;
while (*p && isspace(*p)) p++;
tolower(p[2]) == 'h') {
default_protocol = cfg.protocol = PROT_SSH;
default_port = cfg.port = 22;
- } else if (q == p + 3 &&
- tolower(p[0]) == 'l' &&
- 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);
+ int i = strlen(p);
+ while (i > 1 && isspace(p[i-1]))
+ i--;
+ p[i] = '\0';
+ do_defaults (p+1, &cfg);
if (!*cfg.host && !do_config()) {
WSACleanup();
return 0;
return 0;
}
}
+
+ /* See if host is of the form user@host */
+ if (cfg.host[0] != '\0') {
+ char *atsign = strchr(cfg.host, '@');
+ /* Make sure we're not overflowing the user field */
+ if (atsign) {
+ if (atsign-cfg.host < sizeof cfg.username) {
+ strncpy (cfg.username, cfg.host, atsign-cfg.host);
+ cfg.username[atsign-cfg.host] = '\0';
+ }
+ memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
+ }
+ }
}
/*
}
}
- ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
+ /* Check for invalid Port number (i.e. zero) */
+ if (cfg.port == 0) {
+ MessageBox(NULL, "Invalid Port Number",
+ "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
+ WSACleanup();
+ return 1;
+ }
if (!prev) {
wndclass.style = 0;
}
{
- int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
- if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
- if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
- hwnd = CreateWindow (appname, appname,
- winmode,
- CW_USEDEFAULT, CW_USEDEFAULT,
- guess_width, guess_height,
- NULL, NULL, inst, NULL);
- }
+ int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
+ int exwinmode = 0;
+ if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
+ if (cfg.locksize) winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
+ if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
+ hwnd = CreateWindowEx (exwinmode, appname, appname,
+ winmode, CW_USEDEFAULT, CW_USEDEFAULT,
+ guess_width, guess_height,
+ NULL, NULL, inst, NULL);
+ }
/*
* Initialise the fonts, simultaneously correcting the guesses
SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
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 = smalloc(size);
+ memset(bits, 0, size);
+ caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
+ sfree(bits);
+ }
+ CreateCaret(hwnd, caretbm, font_width, font_height);
+
/*
* Initialise the scroll bar.
*/
*/
{
char *error;
- char msg[1024];
+ char msg[1024], *title;
char *realhost;
- error = back->init (hwnd, cfg.host, cfg.port, &realhost);
+ error = back->init (cfg.host, cfg.port, &realhost);
if (error) {
sprintf(msg, "Unable to open connection:\n%s", error);
MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
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;
}
AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
AppendMenu (m, MF_SEPARATOR, 0, 0);
- AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
+ AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
s = CreateMenu();
get_sesslist(TRUE);
for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
- AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
+ AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
AppendMenu (m, MF_SEPARATOR, 0, 0);
+ AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
AppendMenu (m, MF_SEPARATOR, 0, 0);
*/
ShowWindow (hwnd, show);
+ /*
+ * Open the initial log file if there is one.
+ */
+ logfopen();
+
/*
* Set the palette up.
*/
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.
*
}
if(!timer_id)
timer_id = SetTimer(hwnd, 1, 20, NULL);
- DispatchMessage (&msg);
+ if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
+ 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, 100, 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;
}
}
return msg.wParam;
}
+/*
+ * Set up, or shut down, an AsyncSelect. Called from winnet.c.
+ */
+char *do_select(SOCKET skt, int startup) {
+ int msg, events;
+ if (startup) {
+ msg = WM_NETEVENT;
+ events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
+ } else {
+ msg = events = 0;
+ }
+ if (!hwnd)
+ return "do_select(): internal error (hwnd==NULL)";
+ if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
+ switch (WSAGetLastError()) {
+ case WSAENETDOWN: return "Network is down";
+ default: return "WSAAsyncSelect(): unknown error";
+ }
+ }
+ return NULL;
+}
+
/*
* Print a message box and close the connection.
*/
vsprintf(stuff, fmt, ap);
va_end(ap);
MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
- if (cfg.close_on_exit)
+ if (cfg.close_on_exit == COE_ALWAYS)
PostQuitMessage(1);
else {
session_closed = TRUE;
* Actually do the job requested by a WM_NETEVENT
*/
static void enact_pending_netevent(void) {
- int i;
static int reentering = 0;
+ extern int select_result(WPARAM, LPARAM);
+ int ret;
if (reentering)
return; /* don't unpend the pending */
pending_netevent = FALSE;
reentering = 1;
- i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
+ ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
reentering = 0;
- if (i < 0) {
- char buf[1024];
- switch (WSABASEERR + (-i) % 10000) {
- case WSAECONNRESET:
- sprintf(buf, "Connection reset by peer");
- break;
- default:
- sprintf(buf, "Unexpected network error %d", -i);
- break;
- }
- connection_fatal(buf);
- }
- if (i <= 0) {
- if (cfg.close_on_exit)
+ if (ret == 0 && !session_closed) {
+ /* Abnormal exits will already have set session_closed and taken
+ * appropriate action. */
+ if (cfg.close_on_exit == COE_ALWAYS ||
+ cfg.close_on_exit == COE_NORMAL)
PostQuitMessage(0);
else {
- session_closed = TRUE;
- MessageBox(hwnd, "Connection closed by remote host",
- "PuTTY", MB_OK | MB_ICONINFORMATION);
- SetWindowText (hwnd, "PuTTY (inactive)");
+ session_closed = TRUE;
+ SetWindowText (hwnd, "PuTTY (inactive)");
+ MessageBox(hwnd, "Connection closed by remote host",
+ "PuTTY", MB_OK | MB_ICONINFORMATION);
}
}
}
if (cfg.fontisbold) {
fw_dontcare = FW_BOLD;
- fw_bold = FW_BLACK;
+ fw_bold = FW_HEAVY;
} else {
fw_dontcare = FW_DONTCARE;
fw_bold = FW_BOLD;
lasttime = thistime;
}
+static void show_mouseptr(int show) {
+ static int cursor_visible = 1;
+ if (!cfg.hide_mouseptr) /* override if this feature disabled */
+ show = 1;
+ if (cursor_visible && !show)
+ ShowCursor(FALSE);
+ else if (!cursor_visible && show)
+ ShowCursor(TRUE);
+ cursor_visible = show;
+}
+
static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
static int ignore_size = FALSE;
static int ignore_clip = FALSE;
- static int ignore_keymenu = TRUE;
static int just_reconfigged = FALSE;
+ static int resizing = FALSE;
+ static int need_backend_resize = FALSE;
switch (message) {
case WM_TIMER:
enact_pending_netevent();
if (inbuf_head)
term_out();
+ noise_regular();
+ HideCaret(hwnd);
term_update();
+ ShowCaret(hwnd);
+ if (cfg.ping_interval > 0)
+ {
+ time_t now;
+ time(&now);
+ if (now-last_movement > cfg.ping_interval)
+ {
+ back->special(TS_PING);
+ last_movement = now;
+ }
+ }
return 0;
case WM_CREATE:
break;
case WM_CLOSE:
+ show_mouseptr(1);
if (!cfg.warn_on_close || session_closed ||
MessageBox(hwnd, "Are you sure you want to close this session?",
"PuTTY Exit Confirmation",
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
+ show_mouseptr(1);
PostQuitMessage (0);
return 0;
case WM_SYSCOMMAND:
switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
- case SC_KEYMENU:
- if (ignore_keymenu)
- return 0; /* don't put up system menu on Alt */
- break;
case IDM_SHOWLOG:
showeventlog(hwnd);
break;
cl = c;
} else if (wParam == IDM_SAVEDSESS) {
char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
- cl = malloc(16 + strlen(session)); /* 8, but play safe */
+ cl = smalloc(16 + strlen(session)); /* 8, but play safe */
if (!cl)
cl = NULL; /* not a very important failure mode */
else {
if (filemap)
CloseHandle(filemap);
if (freecl)
- free(cl);
+ sfree(cl);
}
break;
- case IDM_RECONF:
- if (!do_reconfig(hwnd))
- break;
- just_reconfigged = TRUE;
- {
- int i;
- for (i=0; i<8; i++)
- if (fonts[i])
- DeleteObject(fonts[i]);
- }
- bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
- und_mode = UND_FONT;
- init_fonts(0);
- sfree(logpal);
- /* Telnet will change local echo -> remote if the remote asks */
- if (cfg.protocol != PROT_TELNET)
- ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
- if (pal)
- DeleteObject(pal);
- logpal = NULL;
- pal = NULL;
- cfgtopalette();
- init_palette();
-
- /* Enable or disable the scroll bar, etc */
- {
- LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
-
- nflg = flag;
- if (cfg.scrollbar) nflg |= WS_VSCROLL;
- else nflg &= ~WS_VSCROLL;
- if (cfg.locksize)
- nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
- else
- nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
-
- if (nflg != flag)
- {
- RECT cr, wr;
-
- SetWindowLong(hwnd, GWL_STYLE, nflg);
- SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
- SetWindowPos(hwnd, NULL, 0,0,0,0,
- SWP_NOACTIVATE|SWP_NOCOPYBITS|
- SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
- SWP_FRAMECHANGED);
-
- GetWindowRect (hwnd, &wr);
- GetClientRect (hwnd, &cr);
- extra_width = wr.right - wr.left - cr.right + cr.left;
- extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
+ case IDM_RECONF:
+ {
+ int prev_alwaysontop = cfg.alwaysontop;
+ char oldlogfile[FILENAME_MAX];
+ int oldlogtype;
+ int need_setwpos = FALSE;
+ int old_fwidth, old_fheight;
+
+ strcpy(oldlogfile, cfg.logfilename);
+ oldlogtype = cfg.logtype;
+ cfg.width = cols;
+ cfg.height = rows;
+ old_fwidth = font_width;
+ old_fheight = font_height;
+ GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
+
+ if (!do_reconfig(hwnd))
+ break;
+
+ if (strcmp(oldlogfile, cfg.logfilename) ||
+ oldlogtype != cfg.logtype) {
+ logfclose(); /* reset logging */
+ logfopen();
}
- }
- term_size(cfg.height, cfg.width, cfg.savelines);
- InvalidateRect(hwnd, NULL, TRUE);
- SetWindowPos (hwnd, NULL, 0, 0,
- extra_width + font_width * cfg.width,
- extra_height + font_height * cfg.height,
- SWP_NOACTIVATE | SWP_NOCOPYBITS |
- SWP_NOMOVE | SWP_NOZORDER);
- if (IsIconic(hwnd)) {
- SetWindowText (hwnd,
- cfg.win_name_always ? window_name : icon_name);
- }
- break;
- case IDM_CLRSB:
- term_clrsb();
- break;
- case IDM_RESET:
- term_pwron();
+ just_reconfigged = TRUE;
+ {
+ int i;
+ for (i=0; i<8; i++)
+ if (fonts[i])
+ DeleteObject(fonts[i]);
+ }
+ bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
+ und_mode = UND_FONT;
+ init_fonts(0);
+ sfree(logpal);
+ /*
+ * Flush the line discipline's edit buffer in the
+ * case where local editing has just been disabled.
+ */
+ ldisc_send(NULL, 0);
+ if (pal)
+ DeleteObject(pal);
+ logpal = NULL;
+ pal = NULL;
+ cfgtopalette();
+ init_palette();
+
+ /* Enable or disable the scroll bar, etc */
+ {
+ LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
+ LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
+
+ nexflag = exflag;
+ if (cfg.alwaysontop != prev_alwaysontop) {
+ if (cfg.alwaysontop) {
+ nexflag = WS_EX_TOPMOST;
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ } else {
+ nexflag = 0;
+ SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+
+ nflg = flag;
+ if (cfg.scrollbar) nflg |= WS_VSCROLL;
+ else nflg &= ~WS_VSCROLL;
+ if (cfg.locksize)
+ nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
+ else
+ nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
+
+ if (nflg != flag || nexflag != exflag)
+ {
+ RECT cr, wr;
+
+ if (nflg != flag)
+ SetWindowLong(hwnd, GWL_STYLE, nflg);
+ if (nexflag != exflag)
+ SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
+
+ SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
+
+ SetWindowPos(hwnd, NULL, 0,0,0,0,
+ SWP_NOACTIVATE|SWP_NOCOPYBITS|
+ SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
+ SWP_FRAMECHANGED);
+
+ GetWindowRect (hwnd, &wr);
+ GetClientRect (hwnd, &cr);
+ extra_width = wr.right - wr.left - cr.right + cr.left;
+ extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
+ }
+ }
+
+ if (cfg.height != rows ||
+ cfg.width != cols ||
+ old_fwidth != font_width ||
+ old_fheight != font_height ||
+ cfg.savelines != savelines)
+ need_setwpos = TRUE;
+ term_size(cfg.height, cfg.width, cfg.savelines);
+ InvalidateRect(hwnd, NULL, TRUE);
+ if (need_setwpos) {
+ force_normal(hwnd);
+ SetWindowPos (hwnd, NULL, 0, 0,
+ extra_width + font_width * cfg.width,
+ extra_height + font_height * cfg.height,
+ SWP_NOACTIVATE | SWP_NOCOPYBITS |
+ SWP_NOMOVE | SWP_NOZORDER);
+ }
+ set_title(cfg.wintitle);
+ if (IsIconic(hwnd)) {
+ SetWindowText (hwnd,
+ cfg.win_name_always ? window_name : icon_name);
+ }
+ }
+ break;
+ case IDM_COPYALL:
+ term_copyall();
break;
- case IDM_TEL_AYT: back->special (TS_AYT); break;
+ case IDM_CLRSB:
+ term_clrsb();
+ break;
+ case IDM_RESET:
+ term_pwron();
+ break;
+ case IDM_TEL_AYT: back->special (TS_AYT); break;
case IDM_TEL_BRK: back->special (TS_BRK); break;
case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
case IDM_TEL_EC: back->special (TS_EC); break;
#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
case WM_LBUTTONDOWN:
+ show_mouseptr(1);
click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)));
SetCapture(hwnd);
return 0;
case WM_LBUTTONUP:
+ show_mouseptr(1);
term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)));
ReleaseCapture();
return 0;
case WM_MBUTTONDOWN:
+ show_mouseptr(1);
SetCapture(hwnd);
click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)));
return 0;
case WM_MBUTTONUP:
+ show_mouseptr(1);
term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
MA_RELEASE, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)));
ReleaseCapture();
return 0;
case WM_RBUTTONDOWN:
+ show_mouseptr(1);
SetCapture(hwnd);
click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)));
return 0;
case WM_RBUTTONUP:
+ show_mouseptr(1);
term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
MA_RELEASE, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)));
ReleaseCapture();
return 0;
case WM_MOUSEMOVE:
+ show_mouseptr(1);
/*
* Add the mouse position and message time to the random
- * number noise, if we're using ssh.
+ * number noise.
*/
- if (cfg.protocol == PROT_SSH)
- noise_ultralight(lParam);
+ noise_ultralight(lParam);
if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
Mouse_Button b;
case WM_IGNORE_CLIP:
ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
break;
- case WM_IGNORE_KEYMENU:
- ignore_keymenu = wParam; /* do or don't ignore SC_KEYMENU */
- break;
case WM_DESTROYCLIPBOARD:
if (!ignore_clip)
term_deselect();
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, font_width, font_height);
+ ShowCaret(hwnd);
+ compose_state = 0;
term_out();
term_update();
break;
case WM_KILLFOCUS:
+ show_mouseptr(1);
has_focus = FALSE;
+ DestroyCaret();
term_out();
term_update();
break;
break;
case WM_ENTERSIZEMOVE:
EnableSizeTip(1);
+ resizing = TRUE;
+ need_backend_resize = FALSE;
break;
case WM_EXITSIZEMOVE:
EnableSizeTip(0);
+ resizing = FALSE;
+ if (need_backend_resize)
+ back->size();
break;
case WM_SIZING:
{
if (w != cols || h != rows || just_reconfigged) {
term_invalidate();
term_size (h, w, cfg.savelines);
- back->size();
+ /*
+ * Don't call back->size in mid-resize. (To prevent
+ * massive numbers of resize events getting sent
+ * down the connection during an NT opaque drag.)
+ */
+ if (!resizing)
+ back->size();
+ else
+ need_backend_resize = TRUE;
just_reconfigged = FALSE;
}
}
case WM_SYSKEYUP:
/*
* Add the scan code and keypress timing to the random
- * number noise, if we're using ssh.
+ * number noise.
*/
- if (cfg.protocol == PROT_SSH)
- noise_ultralight(lParam);
+ noise_ultralight(lParam);
/*
* We don't do TranslateMessage since it disassociates the
unsigned char buf[20];
int len;
- len = TranslateKey (message, wParam, lParam, buf);
- if (len == -1)
- return DefWindowProc (hwnd, message, wParam, lParam);
- ldisc->send (buf, len);
+ if (wParam==VK_PROCESSKEY) {
+ MSG m;
+ m.hwnd = hwnd;
+ m.message = WM_KEYDOWN;
+ m.wParam = wParam;
+ m.lParam = lParam & 0xdfff;
+ TranslateMessage(&m);
+ } else {
+ len = TranslateKey (message, wParam, lParam, buf);
+ if (len == -1)
+ return DefWindowProc (hwnd, message, wParam, lParam);
+ ldisc_send (buf, len);
+
+ if (len > 0)
+ show_mouseptr(0);
+ }
}
return 0;
+ case WM_IME_CHAR:
+ {
+ unsigned char buf[2];
+
+ buf[1] = wParam;
+ buf[0] = wParam >> 8;
+ ldisc_send (buf, 2);
+ }
case WM_CHAR:
case WM_SYSCHAR:
/*
*/
{
char c = xlat_kbd2tty((unsigned char)wParam);
- ldisc->send (&c, 1);
+ ldisc_send (&c, 1);
}
return 0;
}
return DefWindowProc (hwnd, message, wParam, lParam);
}
+/*
+ * 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.
x *= fnt_width;
y *= font_height;
- if (attr & ATTR_ACTCURS) {
+ if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
attr ^= ATTR_CUR_XOR;
}
}
}
- 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:
oldpen = SelectObject (hdc, oldpen);
DeleteObject (oldpen);
}
- if (attr & ATTR_PASCURS) {
+ if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
POINT pts[5];
HPEN oldpen;
pts[0].x = pts[1].x = pts[4].x = x;
oldpen = SelectObject (hdc, oldpen);
DeleteObject (oldpen);
}
+ if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
+ int startx, starty, dx, dy, length, i;
+ if (cfg.cursor_type == 1) {
+ startx = x; starty = y+descent;
+ dx = 1; dy = 0; length = fnt_width;
+ } else {
+ int xadjust = 0;
+ if (attr & ATTR_RIGHTCURS)
+ xadjust = fnt_width-1;
+ startx = x+xadjust; starty = y;
+ dx = 0; dy = 1; length = font_height;
+ }
+ if (attr & ATTR_ACTCURS) {
+ HPEN oldpen;
+ oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
+ MoveToEx (hdc, startx, starty, NULL);
+ LineTo (hdc, startx+dx*length, starty+dy*length);
+ oldpen = SelectObject (hdc, oldpen);
+ DeleteObject (oldpen);
+ } else {
+ for (i = 0; i < length; i++) {
+ if (i % 2 == 0) {
+ SetPixel(hdc, startx, starty, colours[23]);
+ }
+ startx += dx; starty += dy;
+ }
+ }
+ }
}
static int check_compose(int first, int second) {
static int recurse = 0;
int nc = -1;
- if(0)
- {
- char buf[256];
- char * p;
- sprintf(buf, "cc(%d,%d)", first, second);
- for(p=buf; *p; p++)
- c_write1(*p);
- }
-
for(c=composetbl; *c; c++) {
if( (*c)[0] == first && (*c)[1] == second)
{
* codes. Returns number of bytes used or zero to drop the message
* or -1 to forward the message to windows.
*/
-static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
+static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
+ unsigned char *output) {
BYTE keystate[256];
int scan, left_alt = 0, key_down, shift_state;
int r, i, code;
unsigned char * p = output;
+ static int alt_state = 0;
-static WORD keys[3];
-static int compose_state = 0;
-static int compose_char = 0;
-static WPARAM compose_key = 0;
+ HKL kbd_layout = GetKeyboardLayout(0);
+ static WORD keys[3];
+ static int compose_char = 0;
+ static WPARAM compose_key = 0;
+
r = GetKeyboardState(keystate);
if (!r) memset(keystate, 0, sizeof(keystate));
else
{
- /* Note if AltGr was pressed and if it was used as a compose key */
- if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
- {
+#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 = MapVirtualKeyEx(wParam, 2, kbd_layout);
+ 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
+
+ if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
keystate[VK_RMENU] = keystate[VK_MENU];
- if (!compose_state) compose_key = wParam;
}
- if (wParam == VK_APPS && !compose_state)
- compose_key = wParam;
- if (wParam == compose_key)
- {
- if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
- compose_state = 1;
- else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
- compose_state = 2;
- else
- compose_state = 0;
+ /* Note if AltGr was pressed and if it was used as a compose key */
+ if (cfg.compose_key) {
+ if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
+ {
+ if (!compose_state) compose_key = wParam;
+ }
+ if (wParam == VK_APPS && !compose_state)
+ compose_key = wParam;
+
+ if (wParam == compose_key)
+ {
+ if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
+ compose_state = 1;
+ else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
+ compose_state = 2;
+ else
+ compose_state = 0;
+ }
+ else if (compose_state==1 && wParam != VK_CONTROL)
+ compose_state = 0;
+ } else {
+ compose_state = 0;
}
- else if (compose_state==1 && wParam != VK_CONTROL)
- 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 && !cfg.no_applic_k))
&& wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
wParam = VK_EXECUTE;
if (compose_state>1 && left_alt) compose_state = 0;
/* 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 )
+ if( left_alt || (app_keypad_keys && !cfg.no_applic_k
+ && cfg.funky_type != 2)
+ || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
{
if ((HIWORD(lParam)&KF_EXTENDED) == 0)
{
SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
return 0;
}
+ if (wParam == VK_INSERT && shift_state == 1) {
+ term_mouse (MB_PASTE, MA_CLICK, 0, 0);
+ term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
+ return 0;
+ }
if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
return -1;
}
if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
-
- SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0);
+ alt_state = 0;
+ PostMessage(hwnd, WM_CHAR, ' ', 0);
SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
- SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 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 && !cfg.no_applic_k)) 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;
}
- if(app_keypad_keys) switch(wParam) {
+ if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
case VK_NUMPAD0: xkey = 'p'; break;
case VK_NUMPAD1: xkey = 'q'; break;
case VK_NUMPAD2: xkey = 'r'; 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)) {
{
if (vt52_mode)
p += sprintf((char *)p, "\x1B%c", xkey);
- else if (app_cursor_keys)
+ else if (app_cursor_keys && !cfg.no_applic_c)
p += sprintf((char *)p, "\x1BO%c", xkey);
else
p += sprintf((char *)p, "\x1B[%c", xkey);
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
if(cfg.xlat_capslockcyr)
keystate[VK_CAPITAL] = 0;
- r = ToAscii (wParam, scan, keystate, keys, 0);
+ r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
if(r>0)
{
p = output;
if ((nc=check_compose(compose_char,ch)) == -1)
{
- c_write1('\007');
+ MessageBeep(MB_ICONHAND);
return 0;
}
*p++ = xlat_kbd2tty((unsigned char)nc);
}
}
- /* This stops ALT press-release doing a 'COMMAND MENU' function */
-#if 0
- if (message == WM_SYSKEYUP && wParam == VK_MENU)
- {
- keystate[VK_MENU] = 0;
- return 0;
+ /* ALT alone may or may not want to bring up the System menu */
+ if (wParam == VK_MENU) {
+ if (cfg.alt_only) {
+ if (message == WM_SYSKEYDOWN)
+ alt_state = 1;
+ else if (message == WM_SYSKEYUP && alt_state)
+ PostMessage(hwnd, WM_CHAR, ' ', 0);
+ if (message == WM_SYSKEYUP)
+ alt_state = 0;
+ } else
+ return 0;
}
-#endif
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) {