X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/6f34e365eed04c1b4ebcf6d90511dd9e4400880a..0965bee0865fd8ea129b2de62a3c50e09c59a184:/window.c diff --git a/window.c b/window.c index be4b4e8d..cef6825f 100644 --- a/window.c +++ b/window.c @@ -1,12 +1,22 @@ #include +#include #include +#ifndef AUTO_WINSOCK +#ifdef WINSOCK_TWO +#include +#else #include +#endif +#endif #include #include #include +#include #define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" +#include "winstuff.h" +#include "storage.h" #include "win_res.h" #define IDM_SHOWLOG 0x0010 @@ -30,13 +40,18 @@ #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); @@ -51,6 +66,8 @@ static WPARAM pend_netevent_wParam = 0; 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 @@ -77,16 +94,17 @@ static RGBTRIPLE defpal[NCOLOURS]; static HWND hwnd; +static HBITMAP caretbm; + static int dbltime, lasttime, lastact; static Mouse_Button lastbtn; static char *window_name, *icon_name; -static Ldisc *real_ldisc; +static int compose_state = 0; -void begin_session(void) { - ldisc = real_ldisc; -} +/* 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"; @@ -96,7 +114,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; int guess_width, guess_height; - putty_inst = inst; + hinst = inst; flags = FLAG_VERBOSE | FLAG_INTERACTIVE; winsock_ver = MAKEWORD(1, 1); @@ -112,9 +130,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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. */ @@ -123,8 +146,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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++; @@ -143,11 +167,32 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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"); } @@ -156,7 +201,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { * 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; @@ -223,6 +272,19 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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)); + } + } } /* @@ -245,10 +307,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } } - real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple); - /* To start with, we use the simple line discipline, so we can - * type passwords etc without fear of them being echoed... */ - ldisc = &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; @@ -298,15 +363,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } { - 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 @@ -338,6 +404,19 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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. */ { @@ -357,19 +436,24 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { */ { 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; @@ -419,15 +503,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } 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); @@ -440,6 +525,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { ShowWindow (hwnd, show); /* + * Open the initial log file if there is one. + */ + logfopen(); + + /* * Set the palette up. */ pal = NULL; @@ -449,10 +539,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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. * @@ -465,11 +556,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } 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 */ @@ -479,29 +569,44 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { * 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; } } @@ -530,6 +635,28 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } /* + * 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. */ void connection_fatal(char *fmt, ...) { @@ -552,8 +679,9 @@ void connection_fatal(char *fmt, ...) { * 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 */ @@ -561,22 +689,10 @@ static void enact_pending_netevent(void) { 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 (ret == 0) { if (cfg.close_on_exit) PostQuitMessage(0); else { @@ -680,7 +796,7 @@ font_messup: if (cfg.fontisbold) { fw_dontcare = FW_BOLD; - fw_bold = FW_BLACK; + fw_bold = FW_HEAVY; } else { fw_dontcare = FW_DONTCARE; fw_bold = FW_BOLD; @@ -931,13 +1047,25 @@ static void click (Mouse_Button b, int x, int y) { 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: @@ -945,11 +1073,25 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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", @@ -957,14 +1099,11 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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; @@ -1009,7 +1148,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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 { @@ -1033,82 +1172,139 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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; @@ -1138,46 +1334,52 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, #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; @@ -1194,9 +1396,6 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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(); @@ -1205,6 +1404,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, case WM_PAINT: { PAINTSTRUCT p; + HideCaret(hwnd); hdc = BeginPaint (hwnd, &p); if (pal) { SelectPalette (hdc, pal, TRUE); @@ -1215,6 +1415,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, SelectObject (hdc, GetStockObject(SYSTEM_FONT)); SelectObject (hdc, GetStockObject(WHITE_PEN)); EndPaint (hwnd, &p); + ShowCaret(hwnd); } return 0; case WM_NETEVENT: @@ -1228,14 +1429,20 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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; @@ -1244,9 +1451,14 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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: { @@ -1315,7 +1527,15 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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; } } @@ -1360,10 +1580,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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 @@ -1376,12 +1595,32 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, 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: /* @@ -1392,7 +1631,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, */ { char c = xlat_kbd2tty((unsigned char)wParam); - ldisc->send (&c, 1); + ldisc_send (&c, 1); } return 0; } @@ -1401,6 +1640,16 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, } /* + * 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. * @@ -1430,7 +1679,7 @@ void do_text (Context ctx, int x, int y, char *text, int len, 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; } @@ -1485,16 +1734,7 @@ void do_text (Context ctx, int x, int y, char *text, int len, } } - if (attr & ATTR_GBCHR) { - int i; - /* - * GB mapping: map # to pound, and everything else stays - * normal. - */ - for (i=0; i= 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; @@ -1750,8 +2079,9 @@ static WPARAM compose_key = 0; 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) { @@ -1793,16 +2123,25 @@ static WPARAM compose_key = 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) { @@ -1823,14 +2162,15 @@ static WPARAM compose_key = 0; 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; @@ -1843,9 +2183,17 @@ static WPARAM compose_key = 0; 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'; @@ -1944,12 +2292,18 @@ static WPARAM compose_key = 0; 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)) { @@ -1978,13 +2332,23 @@ static WPARAM compose_key = 0; { 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 @@ -1996,7 +2360,7 @@ static WPARAM compose_key = 0; 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; @@ -2015,7 +2379,7 @@ static WPARAM compose_key = 0; if ((nc=check_compose(compose_char,ch)) == -1) { - c_write1('\007'); + MessageBeep(MB_ICONHAND); return 0; } *p++ = xlat_kbd2tty((unsigned char)nc); @@ -2042,14 +2406,18 @@ static WPARAM compose_key = 0; } } - /* 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; } @@ -2157,7 +2525,7 @@ void palette_reset (void) { } } -void write_clip (void *data, int len) { +void write_clip (void *data, int len, int must_deselect) { HGLOBAL clipdata; void *lock; @@ -2171,14 +2539,18 @@ void write_clip (void *data, int len) { ((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) {