X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/fe50e8140a2dbb3ba357a0ab777f34e07d568c23..5b7ce734d2e126ebebf27b0a6ece1db672cd3eae:/window.c diff --git a/window.c b/window.c index 7fd371a3..8c2d96c9 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,5 @@ #include +#include #include #ifndef AUTO_WINSOCK #ifdef WINSOCK_TWO @@ -39,6 +40,7 @@ #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 @@ -46,6 +48,11 @@ #define WM_IGNORE_SIZE (WM_XUSER + 1) #define WM_IGNORE_CLIP (WM_XUSER + 2) +/* 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 void cfgtopalette(void); @@ -96,6 +103,8 @@ static char *window_name, *icon_name; static Ldisc *real_ldisc; +static int compose_state = 0; + void begin_session(void) { ldisc = real_ldisc; } @@ -124,6 +133,7 @@ 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(); @@ -304,6 +314,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { } } + /* 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; + } + 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... */ @@ -357,15 +375,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 @@ -402,10 +421,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { { char *bits; int size = (font_width+15)/16 * 2 * font_height; - bits = calloc(size, 1); + bits = smalloc(size); + memset(bits, 0, size); caretbm = CreateBitmap(font_width, font_height, 1, 1, bits); - free(bits); + sfree(bits); } + CreateCaret(hwnd, caretbm, font_width, font_height); /* * Initialise the scroll bar. @@ -430,7 +451,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { 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); @@ -494,15 +515,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); @@ -582,7 +604,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { if (!has_focus) timer_id = SetTimer(hwnd, 1, 59500, NULL); else - timer_id = SetTimer(hwnd, 1, 250, NULL); + timer_id = SetTimer(hwnd, 1, 100, NULL); long_timer = 1; /* There's no point rescanning everything in the message queue @@ -619,6 +641,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, ...) { @@ -641,8 +685,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 */ @@ -650,25 +695,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; - case WSAECONNABORTED: - sprintf(buf, "Connection aborted"); - 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 { @@ -1037,6 +1067,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, enact_pending_netevent(); if (inbuf_head) term_out(); + noise_regular(); HideCaret(hwnd); term_update(); ShowCaret(hwnd); @@ -1109,7 +1140,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 { @@ -1133,82 +1164,120 @@ 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); + case IDM_RECONF: + { + int prev_alwaysontop = cfg.alwaysontop; + int need_setwpos = FALSE; + cfg.width = cols; + cfg.height = rows; + 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); + 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 (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; + if (cfg.height != rows || + cfg.width != cols || + 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); } - } - - 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(); + 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; @@ -1274,10 +1343,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, case WM_MOUSEMOVE: /* * 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; @@ -1331,8 +1399,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, return 0; case WM_SETFOCUS: has_focus = TRUE; - CreateCaret(hwnd, caretbm, 0, 0); + CreateCaret(hwnd, caretbm, font_width, font_height); ShowCaret(hwnd); + compose_state = 0; term_out(); term_update(); break; @@ -1472,10 +1541,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 @@ -1488,12 +1556,29 @@ 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); + } } 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: /* @@ -1774,14 +1859,14 @@ static int check_compose(int first, int 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 WORD keys[3]; - static int compose_state = 0; static int compose_char = 0; static WPARAM compose_key = 0; @@ -1846,29 +1931,37 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha } #endif - /* Note if AltGr was pressed and if it was used as a compose key */ - if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) - { + 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 == 3 || (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; @@ -1913,7 +2006,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha 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) + 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) @@ -1965,7 +2059,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha return -1; } if (left_alt && wParam == VK_SPACE && cfg.alt_space) { - + PostMessage(hwnd, WM_CHAR, ' ', 0); SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } @@ -1995,13 +2089,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha int xkey = 0; if ( cfg.funky_type == 3 || - ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) { + ( 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; @@ -2163,7 +2258,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha { 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); @@ -2237,9 +2332,19 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha } } - /* This stops ALT press-release doing a 'COMMAND MENU' function */ - if (message == WM_SYSKEYUP && wParam == VK_MENU) - return 0; + /* ALT alone may or may not want to bring up the System menu */ + if (wParam == VK_MENU) { + if (cfg.alt_only) { + static int alt_state = 0; + 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; + } return -1; }