📄 window.c
字号:
/*
* window.c - the PuTTY(tel) main program, which runs a PuTTY terminal
* emulator and backend in a window.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <limits.h>
#include <assert.h>
#ifndef NO_MULTIMON
#define COMPILE_MULTIMON_STUBS
#endif
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
#include "terminal.h"
#include "storage.h"
#include "win_res.h"
#ifndef NO_MULTIMON
#include <multimon.h>
#endif
#include <imm.h>
#include <commctrl.h>
#include <richedit.h>
#include <mmsystem.h>
/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
* wParam are used by Windows, and should be masked off, so we shouldn't
* attempt to store information in them. Hence all these identifiers have
* the low 4 bits clear. Also, identifiers should < 0xF000. */
#define IDM_SHOWLOG 0x0010
#define IDM_NEWSESS 0x0020
#define IDM_DUPSESS 0x0030
#define IDM_RESTART 0x0040
#define IDM_RECONF 0x0050
#define IDM_CLRSB 0x0060
#define IDM_RESET 0x0070
#define IDM_HELP 0x0140
#define IDM_ABOUT 0x0150
#define IDM_SAVEDSESS 0x0160
#define IDM_COPYALL 0x0170
#define IDM_FULLSCREEN 0x0180
#define IDM_PASTE 0x0190
#define IDM_SPECIALSEP 0x0200
#define IDM_SPECIAL_MIN 0x0400
#define IDM_SPECIAL_MAX 0x0800
#define IDM_SAVED_MIN 0x1000
#define IDM_SAVED_MAX 0x5000
#define MENU_SAVED_STEP 16
/* Maximum number of sessions on saved-session submenu */
#define MENU_SAVED_MAX ((IDM_SAVED_MAX-IDM_SAVED_MIN) / MENU_SAVED_STEP)
#define WM_IGNORE_CLIP (WM_APP + 2)
#define WM_FULLSCR_ON_MAX (WM_APP + 3)
#define WM_AGENT_CALLBACK (WM_APP + 4)
/* Needed for Chinese support and apparently not always defined. */
#ifndef VK_PROCESSKEY
#define VK_PROCESSKEY 0xE5
#endif
/* Mouse wheel support. */
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
#endif
#ifndef WHEEL_DELTA
#define WHEEL_DELTA 120
#endif
static Mouse_Button translate_button(Mouse_Button button);
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
unsigned char *output);
static void cfgtopalette(void);
static void systopalette(void);
static void init_palette(void);
static void init_fonts(int, int);
static void another_font(int);
static void deinit_fonts(void);
static void set_input_locale(HKL);
static void update_savedsess_menu(void);
static void init_flashwindow(void);
static int is_full_screen(void);
static void make_full_screen(void);
static void clear_full_screen(void);
static void flip_full_screen(void);
/* Window layout information */
static void reset_window(int);
static int extra_width, extra_height;
static int font_width, font_height, font_dualwidth;
static int offset_width, offset_height;
static int was_zoomed = 0;
static int prev_rows, prev_cols;
static int pending_netevent = 0;
static WPARAM pend_netevent_wParam = 0;
static LPARAM pend_netevent_lParam = 0;
static void enact_pending_netevent(void);
static void flash_window(int mode);
static void sys_cursor_update(void);
static int is_shift_pressed(void);
static int get_fullscreen_rect(RECT * ss);
static int caret_x = -1, caret_y = -1;
static int kbd_codepage;
static void *ldisc;
static Backend *back;
static void *backhandle;
static struct unicode_data ucsdata;
static int must_close_session, session_closed;
static int reconfiguring = FALSE;
static const struct telnet_special *specials = NULL;
static HMENU specials_menu = NULL;
static int n_specials = 0;
#define TIMING_TIMER_ID 1234
static long timing_next_time;
static struct {
HMENU menu;
} popup_menus[2];
enum { SYSMENU, CTXMENU };
static HMENU savedsess_menu;
Config cfg; /* exported to windlg.c */
static struct sesslist sesslist; /* for saved-session menu */
struct agent_callback {
void (*callback)(void *, void *, int);
void *callback_ctx;
void *data;
int len;
};
#define FONT_NORMAL 0
#define FONT_BOLD 1
#define FONT_UNDERLINE 2
#define FONT_BOLDUND 3
#define FONT_WIDE 0x04
#define FONT_HIGH 0x08
#define FONT_NARROW 0x10
#define FONT_OEM 0x20
#define FONT_OEMBOLD 0x21
#define FONT_OEMUND 0x22
#define FONT_OEMBOLDUND 0x23
#define FONT_MAXNO 0x2F
#define FONT_SHIFT 5
static HFONT fonts[FONT_MAXNO];
static LOGFONT lfont;
static int fontflag[FONT_MAXNO];
static enum {
BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
} bold_mode;
static enum {
UND_LINE, UND_FONT
} und_mode;
static int descent;
#define NCFGCOLOURS 22
#define NEXTCOLOURS 240
#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
static COLORREF colours[NALLCOLOURS];
static HPALETTE pal;
static LPLOGPALETTE logpal;
static RGBTRIPLE defpal[NALLCOLOURS];
static HBITMAP caretbm;
static int dbltime, lasttime, lastact;
static Mouse_Button lastbtn;
/* this allows xterm-style mouse handling. */
static int send_raw_mouse = 0;
static int wheel_accumulator = 0;
static int busy_status = BUSY_NOT;
static char *window_name, *icon_name;
static int compose_state = 0;
static UINT wm_mousewheel = WM_MOUSEWHEEL;
/* Dummy routine, only required in plink. */
void ldisc_update(void *frontend, int echo, int edit)
{
}
char *get_ttymode(void *frontend, const char *mode)
{
return term_get_ttymode(term, mode);
}
static void start_backend(void)
{
const char *error;
char msg[1024], *title;
char *realhost;
int i;
/*
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
back = NULL;
for (i = 0; backends[i].backend != NULL; i++)
if (backends[i].protocol == cfg.protocol) {
back = backends[i].backend;
break;
}
if (back == NULL) {
char *str = dupprintf("%s Internal Error", appname);
MessageBox(NULL, "Unsupported protocol number found",
str, MB_OK | MB_ICONEXCLAMATION);
sfree(str);
cleanup_exit(1);
}
error = back->init(NULL, &backhandle, &cfg,
cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
cfg.tcp_keepalives);
back->provide_logctx(backhandle, logctx);
if (error) {
char *str = dupprintf("%s Error", appname);
sprintf(msg, "Unable to open connection to\n"
"%.800s\n" "%s", cfg_dest(&cfg), error);
MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
sfree(str);
exit(0);
}
window_name = icon_name = NULL;
if (*cfg.wintitle) {
title = cfg.wintitle;
} else {
sprintf(msg, "%s - %s", realhost, appname);
title = msg;
}
sfree(realhost);
set_title(NULL, title);
set_icon(NULL, title);
/*
* Connect the terminal to the backend for resize purposes.
*/
term_provide_resize_fn(term, back->size, backhandle);
/*
* Set up a line discipline.
*/
ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
/*
* Destroy the Restart Session menu item. (This will return
* failure if it's already absent, as it will be the very first
* time we call this function. We ignore that, because as long
* as the menu item ends up not being there, we don't care
* whether it was us who removed it or not!)
*/
for (i = 0; i < lenof(popup_menus); i++) {
DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
}
must_close_session = FALSE;
session_closed = FALSE;
}
static void close_session(void)
{
char morestuff[100];
int i;
session_closed = TRUE;
sprintf(morestuff, "%.70s (inactive)", appname);
set_icon(NULL, morestuff);
set_title(NULL, morestuff);
if (ldisc) {
ldisc_free(ldisc);
ldisc = NULL;
}
if (back) {
back->free(backhandle);
backhandle = NULL;
back = NULL;
term_provide_resize_fn(term, NULL, NULL);
update_specials_menu(NULL);
}
/*
* Show the Restart Session menu item. Do a precautionary
* delete first to ensure we never end up with more than one.
*/
for (i = 0; i < lenof(popup_menus); i++) {
DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED,
IDM_RESTART, "&Restart Session");
}
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{
WNDCLASS wndclass;
MSG msg;
int guess_width, guess_height;
hinst = inst;
hwnd = NULL;
flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
sk_init();
InitCommonControls();
/* Ensure a Maximize setting in Explorer doesn't maximise the
* config box. */
defuse_showwindow();
if (!init_winver())
{
char *str = dupprintf("%s Fatal Error", appname);
MessageBox(NULL, "Windows refuses to report a version",
str, MB_OK | MB_ICONEXCLAMATION);
sfree(str);
return 1;
}
/*
* If we're running a version of Windows that doesn't support
* WM_MOUSEWHEEL, find out what message number we should be
* using instead.
*/
if (osVersion.dwMajorVersion < 4 ||
(osVersion.dwMajorVersion == 4 &&
osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
init_help();
init_flashwindow();
/*
* Process the command line.
*/
{
char *p;
int got_host = 0;
/* By default, we bring up the config dialog, rather than launching
* a session. This gets set to TRUE if something happens to change
* that (e.g., a hostname is specified on the command-line). */
int allow_launch = FALSE;
default_protocol = be_default_protocol;
/* Find the appropriate default port. */
{
int i;
default_port = 0; /* illegal */
for (i = 0; backends[i].backend != NULL; i++)
if (backends[i].protocol == default_protocol) {
default_port = backends[i].backend->default_port;
break;
}
}
cfg.logtype = LGTYP_NONE;
do_defaults(NULL, &cfg);
p = cmdline;
/*
* Process a couple of command-line options which are more
* easily dealt with before the line is broken up into
* words. These are the soon-to-be-defunct @sessionname and
* the internal-use-only &sharedmemoryhandle, neither of
* which are combined with anything else.
*/
while (*p && isspace(*p))
p++;
if (*p == '@') {
int i = strlen(p);
while (i > 1 && isspace(p[i - 1]))
i--;
p[i] = '\0';
do_defaults(p + 1, &cfg);
if (!cfg_launchable(&cfg) && !do_config()) {
cleanup_exit(0);
}
allow_launch = TRUE; /* allow it to be launched directly */
} else if (*p == '&') {
/*
* An initial & means we've been given a command line
* containing the hex value of a HANDLE for a file
* mapping object, which we must then extract as a
* config.
*/
HANDLE filemap;
Config *cp;
if (sscanf(p + 1, "%p", &filemap) == 1 &&
(cp = MapViewOfFile(filemap, FILE_MAP_READ,
0, 0, sizeof(Config))) != NULL) {
cfg = *cp;
UnmapViewOfFile(cp);
CloseHandle(filemap);
} else if (!do_config()) {
cleanup_exit(0);
}
allow_launch = TRUE;
} else {
/*
* Otherwise, break up the command line and deal with
* it sensibly.
*/
int argc, i;
char **argv;
split_into_argv(cmdline, &argc, &argv, NULL);
for (i = 0; i < argc; i++) {
char *p = argv[i];
int ret;
ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
1, &cfg);
if (ret == -2) {
cmdline_error("option \"%s\" requires an argument", p);
} else if (ret == 2) {
i++; /* skip next argument */
} else if (ret == 1) {
continue; /* nothing further needs doing */
} else if (!strcmp(p, "-cleanup") ||
!strcmp(p, "-cleanup-during-uninstall")) {
/*
* `putty -cleanup'. Remove all registry
* entries associated with PuTTY, and also find
* and delete the random seed file.
*/
char *s1, *s2;
/* Are we being invoked from an uninstaller? */
if (!strcmp(p, "-cleanup-during-uninstall")) {
s1 = dupprintf("Remove saved sessions and random seed file?\n"
"\n"
"If you hit Yes, ALL Registry entries associated\n"
"with %s will be removed, as well as the\n"
"random seed file. THIS PROCESS WILL\n"
"DESTROY YOUR SAVED SESSIONS.\n"
"(This only affects the currently logged-in user.)\n"
"\n"
"If you hit No, uninstallation will proceed, but\n"
"saved sessions etc will be left on the machine.",
appname);
s2 = dupprintf("%s Uninstallation", appname);
} else {
s1 = dupprintf("This procedure will remove ALL Registry entries\n"
"associated with %s, and will also remove\n"
"the random seed file. (This only affects the\n"
"currently logged-in user.)\n"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -