📄 gtkwin.c
字号:
/*
* gtkwin.c: the main code that runs a PuTTY terminal emulator and
* backend in a GTK window.
*/
#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)}
/* Colours come in two flavours: configurable, and xterm-extended. */
#define NCFGCOLOURS (lenof(((Config *)0)->colours))
#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
GdkAtom compound_text_atom, utf8_string_atom;
extern char **pty_argv; /* declared in pty.c */
extern int use_pty_argv;
/*
* Timers are global across all sessions (even if we were handling
* multiple sessions, which we aren't), so the current timer ID is
* a global variable.
*/
static guint timer_id = 0;
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, *waitcursor, *currcursor;
GdkColor cols[NALLCOLOURS];
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;
int busy_status;
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. */
int reconfiguring;
};
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)
{
if (!strcmp(name, "SerialLine"))
return dupstr("/dev/ttyS0");
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.
*/
}
char *get_ttymode(void *frontend, const char *mode)
{
struct gui_data *inst = (struct gui_data *)frontend;
return term_get_ttymode(inst->term, mode);
}
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);
}
int from_backend_untrusted(void *frontend, const char *data, int len)
{
struct gui_data *inst = (struct gui_data *)frontend;
return term_data_untrusted(inst->term, data, len);
}
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
struct gui_data *inst = (struct gui_data *)p->frontend;
int ret;
ret = cmdline_get_passwd_input(p, in, inlen);
if (ret == -1)
ret = term_get_userpass_input(inst->term, p, in, inlen);
return ret;
}
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 update_mouseptr(struct gui_data *inst)
{
switch (inst->busy_status) {
case BUSY_NOT:
if (!inst->mouseptr_visible) {
gdk_window_set_cursor(inst->area->window, inst->blankcursor);
} else if (send_raw_mouse) {
gdk_window_set_cursor(inst->area->window, inst->rawcursor);
} else {
gdk_window_set_cursor(inst->area->window, inst->textcursor);
}
break;
case BUSY_WAITING: /* XXX can we do better? */
case BUSY_CPU:
/* We always display these cursors. */
gdk_window_set_cursor(inst->area->window, inst->waitcursor);
break;
default:
assert(0);
}
}
static void show_mouseptr(struct gui_data *inst, int show)
{
if (!inst->cfg.hide_mouseptr)
show = 1;
inst->mouseptr_visible = show;
update_mouseptr(inst);
}
void draw_backing_rect(struct gui_data *inst)
{
GdkGC *gc = gdk_gc_new(inst->area->window);
gdk_gc_set_foreground(gc, &inst->cols[258]); /* 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -