📄 pterm.c
字号:
/* * pterm - a fusion of the PuTTY terminal emulator with a Unix pty * back end, all running as a GTK application. Wish me luck. */#define _GNU_SOURCE#include <string.h>#include <assert.h>#include <stdlib.h>#include <string.h>#include <signal.h>#include <stdio.h>#include <time.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <gtk/gtk.h>#include <gdk/gdkkeysyms.h>#include <gdk/gdkx.h>#include <X11/Xlib.h>#include <X11/Xutil.h>#include <X11/Xatom.h>#define PUTTY_DO_GLOBALS /* actually _define_ globals */#include "putty.h"#include "terminal.h"#define CAT2(x,y) x ## y#define CAT(x,y) CAT2(x,y)#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}#define NCOLOURS (lenof(((Config *)0)->colours))GdkAtom compound_text_atom, utf8_string_atom;extern char **pty_argv; /* declared in pty.c */extern int use_pty_argv;struct gui_data { GtkWidget *window, *area, *sbar; GtkBox *hbox; GtkAdjustment *sbar_adjust; GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2, *restartitem; GtkWidget *sessionsmenu; GdkPixmap *pixmap; GdkFont *fonts[4]; /* normal, bold, wide, widebold */ struct { int charset; int is_wide; } fontinfo[4]; int xpos, ypos, gotpos, gravity; GdkCursor *rawcursor, *textcursor, *blankcursor, *currcursor; GdkColor cols[NCOLOURS]; GdkColormap *colmap; wchar_t *pastein_data; int direct_to_font; int pastein_data_len; char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8; int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len; int font_width, font_height; int width, height; int ignore_sbar; int mouseptr_visible; guint term_paste_idle_id; int alt_keycode; int alt_digits; char wintitle[sizeof(((Config *)0)->wintitle)]; char icontitle[sizeof(((Config *)0)->wintitle)]; int master_fd, master_func_id; void *ldisc; Backend *back; void *backhandle; Terminal *term; void *logctx; int exited; struct unicode_data ucsdata; Config cfg; void *eventlogstuff; char *progname, **gtkargvstart; int ngtkargs; guint32 input_event_time; /* Timestamp of the most recent input event. */};struct draw_ctx { GdkGC *gc; struct gui_data *inst;};static int send_raw_mouse;static char *app_name = "pterm";static void start_backend(struct gui_data *inst);char *x_get_default(const char *key){ return XGetDefault(GDK_DISPLAY(), app_name, key);}void connection_fatal(void *frontend, char *p, ...){ struct gui_data *inst = (struct gui_data *)frontend; va_list ap; char *msg; va_start(ap, p); msg = dupvprintf(p, ap); va_end(ap); inst->exited = TRUE; fatal_message_box(inst->window, msg); sfree(msg); if (inst->cfg.close_on_exit == FORCE_ON) cleanup_exit(1);}/* * Default settings that are specific to pterm. */FontSpec platform_default_fontspec(const char *name){ FontSpec ret; if (!strcmp(name, "Font")) strcpy(ret.name, "fixed"); else *ret.name = '\0'; return ret;}Filename platform_default_filename(const char *name){ Filename ret; if (!strcmp(name, "LogFileName")) strcpy(ret.path, "putty.log"); else *ret.path = '\0'; return ret;}char *platform_default_s(const char *name){ return NULL;}int platform_default_i(const char *name, int def){ if (!strcmp(name, "CloseOnExit")) return 2; /* maps to FORCE_ON after painful rearrangement :-( */ if (!strcmp(name, "WinNameAlways")) return 0; /* X natively supports icon titles, so use 'em by default */ return def;}void ldisc_update(void *frontend, int echo, int edit){ /* * This is a stub in pterm. If I ever produce a Unix * command-line ssh/telnet/rlogin client (i.e. a port of plink) * then it will require some termios manoeuvring analogous to * that in the Windows plink.c, but here it's meaningless. */}int from_backend(void *frontend, int is_stderr, const char *data, int len){ struct gui_data *inst = (struct gui_data *)frontend; return term_data(inst->term, is_stderr, data, len);}void logevent(void *frontend, const char *string){ struct gui_data *inst = (struct gui_data *)frontend; log_eventlog(inst->logctx, string); logevent_dlg(inst->eventlogstuff, string);}int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */{ struct gui_data *inst = (struct gui_data *)frontend; if (which) return inst->font_height; else return inst->font_width;}/* * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT) * into a cooked one (SELECT, EXTEND, PASTE). * * In Unix, this is not configurable; the X button arrangement is * rock-solid across all applications, everyone has a three-button * mouse or a means of faking it, and there is no need to switch * buttons around at all. */static Mouse_Button translate_button(Mouse_Button button){ /* struct gui_data *inst = (struct gui_data *)frontend; */ if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) return MBT_PASTE; if (button == MBT_RIGHT) return MBT_EXTEND; return 0; /* shouldn't happen */}/* * Return the top-level GtkWindow associated with a particular * front end instance. */void *get_window(void *frontend){ struct gui_data *inst = (struct gui_data *)frontend; return inst->window;}/* * Minimise or restore the window in response to a server-side * request. */void set_iconic(void *frontend, int iconic){ /* * GTK 1.2 doesn't know how to do this. */#if GTK_CHECK_VERSION(2,0,0) struct gui_data *inst = (struct gui_data *)frontend; if (iconic) gtk_window_iconify(GTK_WINDOW(inst->window)); else gtk_window_deiconify(GTK_WINDOW(inst->window));#endif}/* * Move the window in response to a server-side request. */void move_window(void *frontend, int x, int y){ struct gui_data *inst = (struct gui_data *)frontend; /* * I assume that when the GTK version of this call is available * we should use it. Not sure how it differs from the GDK one, * though. */#if GTK_CHECK_VERSION(2,0,0) gtk_window_move(GTK_WINDOW(inst->window), x, y);#else gdk_window_move(inst->window->window, x, y);#endif}/* * Move the window to the top or bottom of the z-order in response * to a server-side request. */void set_zorder(void *frontend, int top){ struct gui_data *inst = (struct gui_data *)frontend; if (top) gdk_window_raise(inst->window->window); else gdk_window_lower(inst->window->window);}/* * Refresh the window in response to a server-side request. */void refresh_window(void *frontend){ struct gui_data *inst = (struct gui_data *)frontend; term_invalidate(inst->term);}/* * Maximise or restore the window in response to a server-side * request. */void set_zoomed(void *frontend, int zoomed){ /* * GTK 1.2 doesn't know how to do this. */#if GTK_CHECK_VERSION(2,0,0) struct gui_data *inst = (struct gui_data *)frontend; if (iconic) gtk_window_maximize(GTK_WINDOW(inst->window)); else gtk_window_unmaximize(GTK_WINDOW(inst->window));#endif}/* * Report whether the window is iconic, for terminal reports. */int is_iconic(void *frontend){ struct gui_data *inst = (struct gui_data *)frontend; return !gdk_window_is_viewable(inst->window->window);}/* * Report the window's position, for terminal reports. */void get_window_pos(void *frontend, int *x, int *y){ struct gui_data *inst = (struct gui_data *)frontend; /* * I assume that when the GTK version of this call is available * we should use it. Not sure how it differs from the GDK one, * though. */#if GTK_CHECK_VERSION(2,0,0) gtk_window_get_position(GTK_WINDOW(inst->window), x, y);#else gdk_window_get_position(inst->window->window, x, y);#endif}/* * Report the window's pixel size, for terminal reports. */void get_window_pixels(void *frontend, int *x, int *y){ struct gui_data *inst = (struct gui_data *)frontend; /* * I assume that when the GTK version of this call is available * we should use it. Not sure how it differs from the GDK one, * though. */#if GTK_CHECK_VERSION(2,0,0) gtk_window_get_size(GTK_WINDOW(inst->window), x, y);#else gdk_window_get_size(inst->window->window, x, y);#endif}/* * Return the window or icon title. */char *get_window_title(void *frontend, int icon){ struct gui_data *inst = (struct gui_data *)frontend; return icon ? inst->icontitle : inst->wintitle;}gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data){ struct gui_data *inst = (struct gui_data *)data; if (!inst->exited && inst->cfg.warn_on_close) { if (!reallyclose(inst)) return TRUE; } return FALSE;}static void show_mouseptr(struct gui_data *inst, int show){ if (!inst->cfg.hide_mouseptr) show = 1; if (show) gdk_window_set_cursor(inst->area->window, inst->currcursor); else gdk_window_set_cursor(inst->area->window, inst->blankcursor); inst->mouseptr_visible = show;}void draw_backing_rect(struct gui_data *inst){ GdkGC *gc = gdk_gc_new(inst->area->window); gdk_gc_set_foreground(gc, &inst->cols[18]); /* default background */ gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0, inst->cfg.width * inst->font_width + 2*inst->cfg.window_border, inst->cfg.height * inst->font_height + 2*inst->cfg.window_border); gdk_gc_unref(gc);}gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data){ struct gui_data *inst = (struct gui_data *)data; int w, h, need_size = 0; /* * See if the terminal size has changed, in which case we must * let the terminal know. */ w = (event->width - 2*inst->cfg.window_border) / inst->font_width; h = (event->height - 2*inst->cfg.window_border) / inst->font_height; if (w != inst->width || h != inst->height) { inst->cfg.width = inst->width = w; inst->cfg.height = inst->height = h; need_size = 1; } if (inst->pixmap) { gdk_pixmap_unref(inst->pixmap); inst->pixmap = NULL; } inst->pixmap = gdk_pixmap_new(widget->window, (inst->cfg.width * inst->font_width + 2*inst->cfg.window_border), (inst->cfg.height * inst->font_height + 2*inst->cfg.window_border), -1); draw_backing_rect(inst); if (need_size && inst->term) { term_size(inst->term, h, w, inst->cfg.savelines); } if (inst->term) term_invalidate(inst->term); return TRUE;}gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data){ struct gui_data *inst = (struct gui_data *)data; /* * Pass the exposed rectangle to terminal.c, which will call us * back to do the actual painting. */ if (inst->pixmap) { gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], inst->pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); } return TRUE;}#define KEY_PRESSED(k) \ (inst->keystate[(k) / 32] & (1 << ((k) % 32)))gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data){ struct gui_data *inst = (struct gui_data *)data; char output[32]; wchar_t ucsoutput[2]; int ucsval, start, end, special, use_ucsoutput; /* Remember the timestamp. */ inst->input_event_time = event->time;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -