+++ /dev/null
-/*\r
- * gtkwin.c: the main code that runs a PuTTY terminal emulator and\r
- * backend in a GTK window.\r
- */\r
-\r
-#define _GNU_SOURCE\r
-\r
-#include <string.h>\r
-#include <assert.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <signal.h>\r
-#include <stdio.h>\r
-#include <time.h>\r
-#include <errno.h>\r
-#include <fcntl.h>\r
-#include <unistd.h>\r
-#include <sys/types.h>\r
-#include <sys/wait.h>\r
-#include <gtk/gtk.h>\r
-#include <gdk/gdkkeysyms.h>\r
-#include <gdk/gdkx.h>\r
-#include <X11/Xlib.h>\r
-#include <X11/Xutil.h>\r
-#include <X11/Xatom.h>\r
-\r
-#define PUTTY_DO_GLOBALS /* actually _define_ globals */\r
-\r
-#include "putty.h"\r
-#include "terminal.h"\r
-#include "gtkfont.h"\r
-\r
-#define CAT2(x,y) x ## y\r
-#define CAT(x,y) CAT2(x,y)\r
-#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}\r
-\r
-#if GTK_CHECK_VERSION(2,0,0)\r
-ASSERT(sizeof(long) <= sizeof(gsize));\r
-#define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)\r
-#define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)\r
-#else /* Gtk 1.2 */\r
-ASSERT(sizeof(long) <= sizeof(gpointer));\r
-#define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))\r
-#define GPOINTER_TO_LONG(p) ((long)(p))\r
-#endif\r
-\r
-/* Colours come in two flavours: configurable, and xterm-extended. */\r
-#define NCFGCOLOURS (lenof(((Config *)0)->colours))\r
-#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */\r
-#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)\r
-\r
-GdkAtom compound_text_atom, utf8_string_atom;\r
-\r
-extern char **pty_argv; /* declared in pty.c */\r
-extern int use_pty_argv;\r
-\r
-/*\r
- * Timers are global across all sessions (even if we were handling\r
- * multiple sessions, which we aren't), so the current timer ID is\r
- * a global variable.\r
- */\r
-static guint timer_id = 0;\r
-\r
-struct gui_data {\r
- GtkWidget *window, *area, *sbar;\r
- GtkBox *hbox;\r
- GtkAdjustment *sbar_adjust;\r
- GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2,\r
- *restartitem;\r
- GtkWidget *sessionsmenu;\r
- GdkPixmap *pixmap;\r
- unifont *fonts[4]; /* normal, bold, wide, widebold */\r
- int xpos, ypos, gotpos, gravity;\r
- GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor;\r
- GdkColor cols[NALLCOLOURS];\r
- GdkColormap *colmap;\r
- wchar_t *pastein_data;\r
- int direct_to_font;\r
- int pastein_data_len;\r
- char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8;\r
- int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len;\r
- int font_width, font_height;\r
- int width, height;\r
- int ignore_sbar;\r
- int mouseptr_visible;\r
- int busy_status;\r
- guint term_paste_idle_id;\r
- guint term_exit_idle_id;\r
- int alt_keycode;\r
- int alt_digits;\r
- char wintitle[sizeof(((Config *)0)->wintitle)];\r
- char icontitle[sizeof(((Config *)0)->wintitle)];\r
- int master_fd, master_func_id;\r
- void *ldisc;\r
- Backend *back;\r
- void *backhandle;\r
- Terminal *term;\r
- void *logctx;\r
- int exited;\r
- struct unicode_data ucsdata;\r
- Config cfg;\r
- void *eventlogstuff;\r
- char *progname, **gtkargvstart;\r
- int ngtkargs;\r
- guint32 input_event_time; /* Timestamp of the most recent input event. */\r
- int reconfiguring;\r
-};\r
-\r
-struct draw_ctx {\r
- GdkGC *gc;\r
- struct gui_data *inst;\r
-};\r
-\r
-static int send_raw_mouse;\r
-\r
-static char *app_name = "pterm";\r
-\r
-static void start_backend(struct gui_data *inst);\r
-\r
-char *x_get_default(const char *key)\r
-{\r
- return XGetDefault(GDK_DISPLAY(), app_name, key);\r
-}\r
-\r
-void connection_fatal(void *frontend, char *p, ...)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
-\r
- va_list ap;\r
- char *msg;\r
- va_start(ap, p);\r
- msg = dupvprintf(p, ap);\r
- va_end(ap);\r
- inst->exited = TRUE;\r
- fatal_message_box(inst->window, msg);\r
- sfree(msg);\r
- if (inst->cfg.close_on_exit == FORCE_ON)\r
- cleanup_exit(1);\r
-}\r
-\r
-/*\r
- * Default settings that are specific to pterm.\r
- */\r
-FontSpec platform_default_fontspec(const char *name)\r
-{\r
- FontSpec ret;\r
- if (!strcmp(name, "Font"))\r
- strcpy(ret.name, "server:fixed");\r
- else\r
- *ret.name = '\0';\r
- return ret;\r
-}\r
-\r
-Filename platform_default_filename(const char *name)\r
-{\r
- Filename ret;\r
- if (!strcmp(name, "LogFileName"))\r
- strcpy(ret.path, "putty.log");\r
- else\r
- *ret.path = '\0';\r
- return ret;\r
-}\r
-\r
-char *platform_default_s(const char *name)\r
-{\r
- if (!strcmp(name, "SerialLine"))\r
- return dupstr("/dev/ttyS0");\r
- return NULL;\r
-}\r
-\r
-int platform_default_i(const char *name, int def)\r
-{\r
- if (!strcmp(name, "CloseOnExit"))\r
- return 2; /* maps to FORCE_ON after painful rearrangement :-( */\r
- if (!strcmp(name, "WinNameAlways"))\r
- return 0; /* X natively supports icon titles, so use 'em by default */\r
- return def;\r
-}\r
-\r
-/* Dummy routine, only required in plink. */\r
-void ldisc_update(void *frontend, int echo, int edit)\r
-{\r
-}\r
-\r
-char *get_ttymode(void *frontend, const char *mode)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return term_get_ttymode(inst->term, mode);\r
-}\r
-\r
-int from_backend(void *frontend, int is_stderr, const char *data, int len)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return term_data(inst->term, is_stderr, data, len);\r
-}\r
-\r
-int from_backend_untrusted(void *frontend, const char *data, int len)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return term_data_untrusted(inst->term, data, len);\r
-}\r
-\r
-int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
-{\r
- struct gui_data *inst = (struct gui_data *)p->frontend;\r
- int ret;\r
- ret = cmdline_get_passwd_input(p, in, inlen);\r
- if (ret == -1)\r
- ret = term_get_userpass_input(inst->term, p, in, inlen);\r
- return ret;\r
-}\r
-\r
-void logevent(void *frontend, const char *string)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
-\r
- log_eventlog(inst->logctx, string);\r
-\r
- logevent_dlg(inst->eventlogstuff, string);\r
-}\r
-\r
-int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
-\r
- if (which)\r
- return inst->font_height;\r
- else\r
- return inst->font_width;\r
-}\r
-\r
-/*\r
- * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)\r
- * into a cooked one (SELECT, EXTEND, PASTE).\r
- * \r
- * In Unix, this is not configurable; the X button arrangement is\r
- * rock-solid across all applications, everyone has a three-button\r
- * mouse or a means of faking it, and there is no need to switch\r
- * buttons around at all.\r
- */\r
-static Mouse_Button translate_button(Mouse_Button button)\r
-{\r
- /* struct gui_data *inst = (struct gui_data *)frontend; */\r
-\r
- if (button == MBT_LEFT)\r
- return MBT_SELECT;\r
- if (button == MBT_MIDDLE)\r
- return MBT_PASTE;\r
- if (button == MBT_RIGHT)\r
- return MBT_EXTEND;\r
- return 0; /* shouldn't happen */\r
-}\r
-\r
-/*\r
- * Return the top-level GtkWindow associated with a particular\r
- * front end instance.\r
- */\r
-void *get_window(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return inst->window;\r
-}\r
-\r
-/*\r
- * Minimise or restore the window in response to a server-side\r
- * request.\r
- */\r
-void set_iconic(void *frontend, int iconic)\r
-{\r
- /*\r
- * GTK 1.2 doesn't know how to do this.\r
- */\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- if (iconic)\r
- gtk_window_iconify(GTK_WINDOW(inst->window));\r
- else\r
- gtk_window_deiconify(GTK_WINDOW(inst->window));\r
-#endif\r
-}\r
-\r
-/*\r
- * Move the window in response to a server-side request.\r
- */\r
-void move_window(void *frontend, int x, int y)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- /*\r
- * I assume that when the GTK version of this call is available\r
- * we should use it. Not sure how it differs from the GDK one,\r
- * though.\r
- */\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_window_move(GTK_WINDOW(inst->window), x, y);\r
-#else\r
- gdk_window_move(inst->window->window, x, y);\r
-#endif\r
-}\r
-\r
-/*\r
- * Move the window to the top or bottom of the z-order in response\r
- * to a server-side request.\r
- */\r
-void set_zorder(void *frontend, int top)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- if (top)\r
- gdk_window_raise(inst->window->window);\r
- else\r
- gdk_window_lower(inst->window->window);\r
-}\r
-\r
-/*\r
- * Refresh the window in response to a server-side request.\r
- */\r
-void refresh_window(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- term_invalidate(inst->term);\r
-}\r
-\r
-/*\r
- * Maximise or restore the window in response to a server-side\r
- * request.\r
- */\r
-void set_zoomed(void *frontend, int zoomed)\r
-{\r
- /*\r
- * GTK 1.2 doesn't know how to do this.\r
- */\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- if (zoomed)\r
- gtk_window_maximize(GTK_WINDOW(inst->window));\r
- else\r
- gtk_window_unmaximize(GTK_WINDOW(inst->window));\r
-#endif\r
-}\r
-\r
-/*\r
- * Report whether the window is iconic, for terminal reports.\r
- */\r
-int is_iconic(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return !gdk_window_is_viewable(inst->window->window);\r
-}\r
-\r
-/*\r
- * Report the window's position, for terminal reports.\r
- */\r
-void get_window_pos(void *frontend, int *x, int *y)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- /*\r
- * I assume that when the GTK version of this call is available\r
- * we should use it. Not sure how it differs from the GDK one,\r
- * though.\r
- */\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_window_get_position(GTK_WINDOW(inst->window), x, y);\r
-#else\r
- gdk_window_get_position(inst->window->window, x, y);\r
-#endif\r
-}\r
-\r
-/*\r
- * Report the window's pixel size, for terminal reports.\r
- */\r
-void get_window_pixels(void *frontend, int *x, int *y)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- /*\r
- * I assume that when the GTK version of this call is available\r
- * we should use it. Not sure how it differs from the GDK one,\r
- * though.\r
- */\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_window_get_size(GTK_WINDOW(inst->window), x, y);\r
-#else\r
- gdk_window_get_size(inst->window->window, x, y);\r
-#endif\r
-}\r
-\r
-/*\r
- * Return the window or icon title.\r
- */\r
-char *get_window_title(void *frontend, int icon)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return icon ? inst->icontitle : inst->wintitle;\r
-}\r
-\r
-gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- if (!inst->exited && inst->cfg.warn_on_close) {\r
- if (!reallyclose(inst))\r
- return TRUE;\r
- }\r
- return FALSE;\r
-}\r
-\r
-static void update_mouseptr(struct gui_data *inst)\r
-{\r
- switch (inst->busy_status) {\r
- case BUSY_NOT:\r
- if (!inst->mouseptr_visible) {\r
- gdk_window_set_cursor(inst->area->window, inst->blankcursor);\r
- } else if (send_raw_mouse) {\r
- gdk_window_set_cursor(inst->area->window, inst->rawcursor);\r
- } else {\r
- gdk_window_set_cursor(inst->area->window, inst->textcursor);\r
- }\r
- break;\r
- case BUSY_WAITING: /* XXX can we do better? */\r
- case BUSY_CPU:\r
- /* We always display these cursors. */\r
- gdk_window_set_cursor(inst->area->window, inst->waitcursor);\r
- break;\r
- default:\r
- assert(0);\r
- }\r
-}\r
-\r
-static void show_mouseptr(struct gui_data *inst, int show)\r
-{\r
- if (!inst->cfg.hide_mouseptr)\r
- show = 1;\r
- inst->mouseptr_visible = show;\r
- update_mouseptr(inst);\r
-}\r
-\r
-void draw_backing_rect(struct gui_data *inst)\r
-{\r
- GdkGC *gc = gdk_gc_new(inst->area->window);\r
- gdk_gc_set_foreground(gc, &inst->cols[258]); /* default background */\r
- gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,\r
- inst->cfg.width * inst->font_width + 2*inst->cfg.window_border,\r
- inst->cfg.height * inst->font_height + 2*inst->cfg.window_border);\r
- gdk_gc_unref(gc);\r
-}\r
-\r
-gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- int w, h, need_size = 0;\r
-\r
- /*\r
- * See if the terminal size has changed, in which case we must\r
- * let the terminal know.\r
- */\r
- w = (event->width - 2*inst->cfg.window_border) / inst->font_width;\r
- h = (event->height - 2*inst->cfg.window_border) / inst->font_height;\r
- if (w != inst->width || h != inst->height) {\r
- inst->cfg.width = inst->width = w;\r
- inst->cfg.height = inst->height = h;\r
- need_size = 1;\r
- }\r
-\r
- if (inst->pixmap) {\r
- gdk_pixmap_unref(inst->pixmap);\r
- inst->pixmap = NULL;\r
- }\r
-\r
- inst->pixmap = gdk_pixmap_new(widget->window,\r
- (inst->cfg.width * inst->font_width +\r
- 2*inst->cfg.window_border),\r
- (inst->cfg.height * inst->font_height +\r
- 2*inst->cfg.window_border), -1);\r
-\r
- draw_backing_rect(inst);\r
-\r
- if (need_size && inst->term) {\r
- term_size(inst->term, h, w, inst->cfg.savelines);\r
- }\r
-\r
- if (inst->term)\r
- term_invalidate(inst->term);\r
-\r
- return TRUE;\r
-}\r
-\r
-gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
-\r
- /*\r
- * Pass the exposed rectangle to terminal.c, which will call us\r
- * back to do the actual painting.\r
- */\r
- if (inst->pixmap) {\r
- gdk_draw_pixmap(widget->window,\r
- widget->style->fg_gc[GTK_WIDGET_STATE(widget)],\r
- inst->pixmap,\r
- event->area.x, event->area.y,\r
- event->area.x, event->area.y,\r
- event->area.width, event->area.height);\r
- }\r
- return TRUE;\r
-}\r
-\r
-#define KEY_PRESSED(k) \\r
- (inst->keystate[(k) / 32] & (1 << ((k) % 32)))\r
-\r
-gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- char output[256];\r
- wchar_t ucsoutput[2];\r
- int ucsval, start, end, special, output_charset, use_ucsoutput;\r
-\r
- /* Remember the timestamp. */\r
- inst->input_event_time = event->time;\r
-\r
- /* By default, nothing is generated. */\r
- end = start = 0;\r
- special = use_ucsoutput = FALSE;\r
- output_charset = CS_ISO8859_1;\r
-\r
- /*\r
- * If Alt is being released after typing an Alt+numberpad\r
- * sequence, we should generate the code that was typed.\r
- * \r
- * Note that we only do this if more than one key was actually\r
- * pressed - I don't think Alt+NumPad4 should be ^D or that\r
- * Alt+NumPad3 should be ^C, for example. There's no serious\r
- * inconvenience in having to type a zero before a single-digit\r
- * character code.\r
- */\r
- if (event->type == GDK_KEY_RELEASE &&\r
- (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||\r
- event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) &&\r
- inst->alt_keycode >= 0 && inst->alt_digits > 1) {\r
-#ifdef KEY_DEBUGGING\r
- printf("Alt key up, keycode = %d\n", inst->alt_keycode);\r
-#endif\r
- /*\r
- * FIXME: we might usefully try to do something clever here\r
- * about interpreting the generated key code in a way that's\r
- * appropriate to the line code page.\r
- */\r
- output[0] = inst->alt_keycode;\r
- end = 1;\r
- goto done;\r
- }\r
-\r
- if (event->type == GDK_KEY_PRESS) {\r
-#ifdef KEY_DEBUGGING\r
- {\r
- int i;\r
- printf("keypress: keyval = %04x, state = %08x; string =",\r
- event->keyval, event->state);\r
- for (i = 0; event->string[i]; i++)\r
- printf(" %02x", (unsigned char) event->string[i]);\r
- printf("\n");\r
- }\r
-#endif\r
-\r
- /*\r
- * NYI: Compose key (!!! requires Unicode faff before even trying)\r
- */\r
-\r
- /*\r
- * If Alt has just been pressed, we start potentially\r
- * accumulating an Alt+numberpad code. We do this by\r
- * setting alt_keycode to -1 (nothing yet but plausible).\r
- */\r
- if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||\r
- event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R)) {\r
- inst->alt_keycode = -1;\r
- inst->alt_digits = 0;\r
- goto done; /* this generates nothing else */\r
- }\r
-\r
- /*\r
- * If we're seeing a numberpad key press with Mod1 down,\r
- * consider adding it to alt_keycode if that's sensible.\r
- * Anything _else_ with Mod1 down cancels any possibility\r
- * of an ALT keycode: we set alt_keycode to -2.\r
- */\r
- if ((event->state & GDK_MOD1_MASK) && inst->alt_keycode != -2) {\r
- int digit = -1;\r
- switch (event->keyval) {\r
- case GDK_KP_0: case GDK_KP_Insert: digit = 0; break;\r
- case GDK_KP_1: case GDK_KP_End: digit = 1; break;\r
- case GDK_KP_2: case GDK_KP_Down: digit = 2; break;\r
- case GDK_KP_3: case GDK_KP_Page_Down: digit = 3; break;\r
- case GDK_KP_4: case GDK_KP_Left: digit = 4; break;\r
- case GDK_KP_5: case GDK_KP_Begin: digit = 5; break;\r
- case GDK_KP_6: case GDK_KP_Right: digit = 6; break;\r
- case GDK_KP_7: case GDK_KP_Home: digit = 7; break;\r
- case GDK_KP_8: case GDK_KP_Up: digit = 8; break;\r
- case GDK_KP_9: case GDK_KP_Page_Up: digit = 9; break;\r
- }\r
- if (digit < 0)\r
- inst->alt_keycode = -2; /* it's invalid */\r
- else {\r
-#ifdef KEY_DEBUGGING\r
- printf("Adding digit %d to keycode %d", digit,\r
- inst->alt_keycode);\r
-#endif\r
- if (inst->alt_keycode == -1)\r
- inst->alt_keycode = digit; /* one-digit code */\r
- else\r
- inst->alt_keycode = inst->alt_keycode * 10 + digit;\r
- inst->alt_digits++;\r
-#ifdef KEY_DEBUGGING\r
- printf(" gives new code %d\n", inst->alt_keycode);\r
-#endif\r
- /* Having used this digit, we now do nothing more with it. */\r
- goto done;\r
- }\r
- }\r
-\r
- /*\r
- * Shift-PgUp and Shift-PgDn don't even generate keystrokes\r
- * at all.\r
- */\r
- if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {\r
- term_scroll(inst->term, 0, -inst->cfg.height/2);\r
- return TRUE;\r
- }\r
- if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) {\r
- term_scroll(inst->term, 0, -1);\r
- return TRUE;\r
- }\r
- if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {\r
- term_scroll(inst->term, 0, +inst->cfg.height/2);\r
- return TRUE;\r
- }\r
- if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) {\r
- term_scroll(inst->term, 0, +1);\r
- return TRUE;\r
- }\r
-\r
- /*\r
- * Neither does Shift-Ins.\r
- */\r
- if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) {\r
- request_paste(inst);\r
- return TRUE;\r
- }\r
-\r
- special = FALSE;\r
- use_ucsoutput = FALSE;\r
-\r
- /* ALT+things gives leading Escape. */\r
- output[0] = '\033';\r
-#if !GTK_CHECK_VERSION(2,0,0)\r
- /*\r
- * In vanilla X, and hence also GDK 1.2, the string received\r
- * as part of a keyboard event is assumed to be in\r
- * ISO-8859-1. (Seems woefully shortsighted in i18n terms,\r
- * but it's true: see the man page for XLookupString(3) for\r
- * confirmation.)\r
- */\r
- output_charset = CS_ISO8859_1;\r
- strncpy(output+1, event->string, lenof(output)-1);\r
-#else\r
- /*\r
- * GDK 2.0 arranges to have done some translation for us: in\r
- * GDK 2.0, event->string is encoded in the current locale.\r
- *\r
- * (However, it's also deprecated; we really ought to be\r
- * using a GTKIMContext.)\r
- *\r
- * So we use the standard C library function mbstowcs() to\r
- * convert from the current locale into Unicode; from there\r
- * we can convert to whatever PuTTY is currently working in.\r
- * (In fact I convert straight back to UTF-8 from\r
- * wide-character Unicode, for the sake of simplicity: that\r
- * way we can still use exactly the same code to manipulate\r
- * the string, such as prefixing ESC.)\r
- */\r
- output_charset = CS_UTF8;\r
- {\r
- wchar_t widedata[32], *wp;\r
- int wlen;\r
- int ulen;\r
-\r
- wlen = mb_to_wc(DEFAULT_CODEPAGE, 0,\r
- event->string, strlen(event->string),\r
- widedata, lenof(widedata)-1);\r
-\r
- wp = widedata;\r
- ulen = charset_from_unicode(&wp, &wlen, output+1, lenof(output)-2,\r
- CS_UTF8, NULL, NULL, 0);\r
- output[1+ulen] = '\0';\r
- }\r
-#endif\r
-\r
- if (!output[1] &&\r
- (ucsval = keysym_to_unicode(event->keyval)) >= 0) {\r
- ucsoutput[0] = '\033';\r
- ucsoutput[1] = ucsval;\r
- use_ucsoutput = TRUE;\r
- end = 2;\r
- } else {\r
- output[lenof(output)-1] = '\0';\r
- end = strlen(output);\r
- }\r
- if (event->state & GDK_MOD1_MASK) {\r
- start = 0;\r
- if (end == 1) end = 0;\r
- } else\r
- start = 1;\r
-\r
- /* Control-` is the same as Control-\ (unless gtk has a better idea) */\r
- if (!output[1] && event->keyval == '`' &&\r
- (event->state & GDK_CONTROL_MASK)) {\r
- output[1] = '\x1C';\r
- use_ucsoutput = FALSE;\r
- end = 2;\r
- }\r
-\r
- /* Control-Break sends a Break special to the backend */\r
- if (event->keyval == GDK_Break &&\r
- (event->state & GDK_CONTROL_MASK)) {\r
- if (inst->back)\r
- inst->back->special(inst->backhandle, TS_BRK);\r
- return TRUE;\r
- }\r
-\r
- /* We handle Return ourselves, because it needs to be flagged as\r
- * special to ldisc. */\r
- if (event->keyval == GDK_Return) {\r
- output[1] = '\015';\r
- use_ucsoutput = FALSE;\r
- end = 2;\r
- special = TRUE;\r
- }\r
-\r
- /* Control-2, Control-Space and Control-@ are NUL */\r
- if (!output[1] &&\r
- (event->keyval == ' ' || event->keyval == '2' ||\r
- event->keyval == '@') &&\r
- (event->state & (GDK_SHIFT_MASK |\r
- GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {\r
- output[1] = '\0';\r
- use_ucsoutput = FALSE;\r
- end = 2;\r
- }\r
-\r
- /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */\r
- if (!output[1] && event->keyval == ' ' &&\r
- (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==\r
- (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {\r
- output[1] = '\240';\r
- output_charset = CS_ISO8859_1;\r
- use_ucsoutput = FALSE;\r
- end = 2;\r
- }\r
-\r
- /* We don't let GTK tell us what Backspace is! We know better. */\r
- if (event->keyval == GDK_BackSpace &&\r
- !(event->state & GDK_SHIFT_MASK)) {\r
- output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08';\r
- use_ucsoutput = FALSE;\r
- end = 2;\r
- special = TRUE;\r
- }\r
- /* For Shift Backspace, do opposite of what is configured. */\r
- if (event->keyval == GDK_BackSpace &&\r
- (event->state & GDK_SHIFT_MASK)) {\r
- output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F';\r
- use_ucsoutput = FALSE;\r
- end = 2;\r
- special = TRUE;\r
- }\r
-\r
- /* Shift-Tab is ESC [ Z */\r
- if (event->keyval == GDK_ISO_Left_Tab ||\r
- (event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {\r
- end = 1 + sprintf(output+1, "\033[Z");\r
- use_ucsoutput = FALSE;\r
- }\r
- /* And normal Tab is Tab, if the keymap hasn't already told us.\r
- * (Curiously, at least one version of the MacOS 10.5 X server\r
- * doesn't translate Tab for us. */\r
- if (event->keyval == GDK_Tab && end <= 1) {\r
- output[1] = '\t';\r
- end = 2;\r
- }\r
-\r
- /*\r
- * NetHack keypad mode.\r
- */\r
- if (inst->cfg.nethack_keypad) {\r
- char *keys = NULL;\r
- switch (event->keyval) {\r
- case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break;\r
- case GDK_KP_2: case GDK_KP_Down: keys = "jJ\012"; break;\r
- case GDK_KP_3: case GDK_KP_Page_Down: keys = "nN\016"; break;\r
- case GDK_KP_4: case GDK_KP_Left: keys = "hH\010"; break;\r
- case GDK_KP_5: case GDK_KP_Begin: keys = "..."; break;\r
- case GDK_KP_6: case GDK_KP_Right: keys = "lL\014"; break;\r
- case GDK_KP_7: case GDK_KP_Home: keys = "yY\031"; break;\r
- case GDK_KP_8: case GDK_KP_Up: keys = "kK\013"; break;\r
- case GDK_KP_9: case GDK_KP_Page_Up: keys = "uU\025"; break;\r
- }\r
- if (keys) {\r
- end = 2;\r
- if (event->state & GDK_CONTROL_MASK)\r
- output[1] = keys[2];\r
- else if (event->state & GDK_SHIFT_MASK)\r
- output[1] = keys[1];\r
- else\r
- output[1] = keys[0];\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- }\r
-\r
- /*\r
- * Application keypad mode.\r
- */\r
- if (inst->term->app_keypad_keys && !inst->cfg.no_applic_k) {\r
- int xkey = 0;\r
- switch (event->keyval) {\r
- case GDK_Num_Lock: xkey = 'P'; break;\r
- case GDK_KP_Divide: xkey = 'Q'; break;\r
- case GDK_KP_Multiply: xkey = 'R'; break;\r
- case GDK_KP_Subtract: xkey = 'S'; break;\r
- /*\r
- * Keypad + is tricky. It covers a space that would\r
- * be taken up on the VT100 by _two_ keys; so we\r
- * let Shift select between the two. Worse still,\r
- * in xterm function key mode we change which two...\r
- */\r
- case GDK_KP_Add:\r
- if (inst->cfg.funky_type == FUNKY_XTERM) {\r
- if (event->state & GDK_SHIFT_MASK)\r
- xkey = 'l';\r
- else\r
- xkey = 'k';\r
- } else if (event->state & GDK_SHIFT_MASK)\r
- xkey = 'm';\r
- else\r
- xkey = 'l';\r
- break;\r
- case GDK_KP_Enter: xkey = 'M'; break;\r
- case GDK_KP_0: case GDK_KP_Insert: xkey = 'p'; break;\r
- case GDK_KP_1: case GDK_KP_End: xkey = 'q'; break;\r
- case GDK_KP_2: case GDK_KP_Down: xkey = 'r'; break;\r
- case GDK_KP_3: case GDK_KP_Page_Down: xkey = 's'; break;\r
- case GDK_KP_4: case GDK_KP_Left: xkey = 't'; break;\r
- case GDK_KP_5: case GDK_KP_Begin: xkey = 'u'; break;\r
- case GDK_KP_6: case GDK_KP_Right: xkey = 'v'; break;\r
- case GDK_KP_7: case GDK_KP_Home: xkey = 'w'; break;\r
- case GDK_KP_8: case GDK_KP_Up: xkey = 'x'; break;\r
- case GDK_KP_9: case GDK_KP_Page_Up: xkey = 'y'; break;\r
- case GDK_KP_Decimal: case GDK_KP_Delete: xkey = 'n'; break;\r
- }\r
- if (xkey) {\r
- if (inst->term->vt52_mode) {\r
- if (xkey >= 'P' && xkey <= 'S')\r
- end = 1 + sprintf(output+1, "\033%c", xkey);\r
- else\r
- end = 1 + sprintf(output+1, "\033?%c", xkey);\r
- } else\r
- end = 1 + sprintf(output+1, "\033O%c", xkey);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- }\r
-\r
- /*\r
- * Next, all the keys that do tilde codes. (ESC '[' nn '~',\r
- * for integer decimal nn.)\r
- *\r
- * We also deal with the weird ones here. Linux VCs replace F1\r
- * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but\r
- * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w\r
- * respectively.\r
- */\r
- {\r
- int code = 0;\r
- switch (event->keyval) {\r
- case GDK_F1:\r
- code = (event->state & GDK_SHIFT_MASK ? 23 : 11);\r
- break;\r
- case GDK_F2:\r
- code = (event->state & GDK_SHIFT_MASK ? 24 : 12);\r
- break;\r
- case GDK_F3:\r
- code = (event->state & GDK_SHIFT_MASK ? 25 : 13);\r
- break;\r
- case GDK_F4:\r
- code = (event->state & GDK_SHIFT_MASK ? 26 : 14);\r
- break;\r
- case GDK_F5:\r
- code = (event->state & GDK_SHIFT_MASK ? 28 : 15);\r
- break;\r
- case GDK_F6:\r
- code = (event->state & GDK_SHIFT_MASK ? 29 : 17);\r
- break;\r
- case GDK_F7:\r
- code = (event->state & GDK_SHIFT_MASK ? 31 : 18);\r
- break;\r
- case GDK_F8:\r
- code = (event->state & GDK_SHIFT_MASK ? 32 : 19);\r
- break;\r
- case GDK_F9:\r
- code = (event->state & GDK_SHIFT_MASK ? 33 : 20);\r
- break;\r
- case GDK_F10:\r
- code = (event->state & GDK_SHIFT_MASK ? 34 : 21);\r
- break;\r
- case GDK_F11:\r
- code = 23;\r
- break;\r
- case GDK_F12:\r
- code = 24;\r
- break;\r
- case GDK_F13:\r
- code = 25;\r
- break;\r
- case GDK_F14:\r
- code = 26;\r
- break;\r
- case GDK_F15:\r
- code = 28;\r
- break;\r
- case GDK_F16:\r
- code = 29;\r
- break;\r
- case GDK_F17:\r
- code = 31;\r
- break;\r
- case GDK_F18:\r
- code = 32;\r
- break;\r
- case GDK_F19:\r
- code = 33;\r
- break;\r
- case GDK_F20:\r
- code = 34;\r
- break;\r
- }\r
- if (!(event->state & GDK_CONTROL_MASK)) switch (event->keyval) {\r
- case GDK_Home: case GDK_KP_Home:\r
- code = 1;\r
- break;\r
- case GDK_Insert: case GDK_KP_Insert:\r
- code = 2;\r
- break;\r
- case GDK_Delete: case GDK_KP_Delete:\r
- code = 3;\r
- break;\r
- case GDK_End: case GDK_KP_End:\r
- code = 4;\r
- break;\r
- case GDK_Page_Up: case GDK_KP_Page_Up:\r
- code = 5;\r
- break;\r
- case GDK_Page_Down: case GDK_KP_Page_Down:\r
- code = 6;\r
- break;\r
- }\r
- /* Reorder edit keys to physical order */\r
- if (inst->cfg.funky_type == FUNKY_VT400 && code <= 6)\r
- code = "\0\2\1\4\5\3\6"[code];\r
-\r
- if (inst->term->vt52_mode && code > 0 && code <= 6) {\r
- end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
-\r
- if (inst->cfg.funky_type == FUNKY_SCO && /* SCO function keys */\r
- code >= 11 && code <= 34) {\r
- char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";\r
- int index = 0;\r
- switch (event->keyval) {\r
- case GDK_F1: index = 0; break;\r
- case GDK_F2: index = 1; break;\r
- case GDK_F3: index = 2; break;\r
- case GDK_F4: index = 3; break;\r
- case GDK_F5: index = 4; break;\r
- case GDK_F6: index = 5; break;\r
- case GDK_F7: index = 6; break;\r
- case GDK_F8: index = 7; break;\r
- case GDK_F9: index = 8; break;\r
- case GDK_F10: index = 9; break;\r
- case GDK_F11: index = 10; break;\r
- case GDK_F12: index = 11; break;\r
- }\r
- if (event->state & GDK_SHIFT_MASK) index += 12;\r
- if (event->state & GDK_CONTROL_MASK) index += 24;\r
- end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- if (inst->cfg.funky_type == FUNKY_SCO && /* SCO small keypad */\r
- code >= 1 && code <= 6) {\r
- char codes[] = "HL.FIG";\r
- if (code == 3) {\r
- output[1] = '\x7F';\r
- end = 2;\r
- } else {\r
- end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);\r
- }\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) &&\r
- code >= 11 && code <= 24) {\r
- int offt = 0;\r
- if (code > 15)\r
- offt++;\r
- if (code > 21)\r
- offt++;\r
- if (inst->term->vt52_mode)\r
- end = 1 + sprintf(output+1,\r
- "\x1B%c", code + 'P' - 11 - offt);\r
- else\r
- end = 1 + sprintf(output+1,\r
- "\x1BO%c", code + 'P' - 11 - offt);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {\r
- end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {\r
- if (inst->term->vt52_mode)\r
- end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);\r
- else\r
- end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) {\r
- end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- if (code) {\r
- end = 1 + sprintf(output+1, "\x1B[%d~", code);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- }\r
-\r
- /*\r
- * Cursor keys. (This includes the numberpad cursor keys,\r
- * if we haven't already done them due to app keypad mode.)\r
- * \r
- * Here we also process un-numlocked un-appkeypadded KP5,\r
- * which sends ESC [ G.\r
- */\r
- {\r
- int xkey = 0;\r
- switch (event->keyval) {\r
- case GDK_Up: case GDK_KP_Up: xkey = 'A'; break;\r
- case GDK_Down: case GDK_KP_Down: xkey = 'B'; break;\r
- case GDK_Right: case GDK_KP_Right: xkey = 'C'; break;\r
- case GDK_Left: case GDK_KP_Left: xkey = 'D'; break;\r
- case GDK_Begin: case GDK_KP_Begin: xkey = 'G'; break;\r
- }\r
- if (xkey) {\r
- end = 1 + format_arrow_key(output+1, inst->term, xkey,\r
- event->state & GDK_CONTROL_MASK);\r
- use_ucsoutput = FALSE;\r
- goto done;\r
- }\r
- }\r
- goto done;\r
- }\r
-\r
- done:\r
-\r
- if (end-start > 0) {\r
-#ifdef KEY_DEBUGGING\r
- int i;\r
- printf("generating sequence:");\r
- for (i = start; i < end; i++)\r
- printf(" %02x", (unsigned char) output[i]);\r
- printf("\n");\r
-#endif\r
-\r
- if (special) {\r
- /*\r
- * For special control characters, the character set\r
- * should never matter.\r
- */\r
- output[end] = '\0'; /* NUL-terminate */\r
- if (inst->ldisc)\r
- ldisc_send(inst->ldisc, output+start, -2, 1);\r
- } else if (!inst->direct_to_font) {\r
- if (!use_ucsoutput) {\r
- if (inst->ldisc)\r
- lpage_send(inst->ldisc, output_charset, output+start,\r
- end-start, 1);\r
- } else {\r
- /*\r
- * We generated our own Unicode key data from the\r
- * keysym, so use that instead.\r
- */\r
- if (inst->ldisc)\r
- luni_send(inst->ldisc, ucsoutput+start, end-start, 1);\r
- }\r
- } else {\r
- /*\r
- * In direct-to-font mode, we just send the string\r
- * exactly as we received it.\r
- */\r
- if (inst->ldisc)\r
- ldisc_send(inst->ldisc, output+start, end-start, 1);\r
- }\r
-\r
- show_mouseptr(inst, 0);\r
- term_seen_key_event(inst->term);\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-gboolean button_internal(struct gui_data *inst, guint32 timestamp,\r
- GdkEventType type, guint ebutton, guint state,\r
- gdouble ex, gdouble ey)\r
-{\r
- int shift, ctrl, alt, x, y, button, act;\r
-\r
- /* Remember the timestamp. */\r
- inst->input_event_time = timestamp;\r
-\r
- show_mouseptr(inst, 1);\r
-\r
- if (ebutton == 4 && type == GDK_BUTTON_PRESS) {\r
- term_scroll(inst->term, 0, -5);\r
- return TRUE;\r
- }\r
- if (ebutton == 5 && type == GDK_BUTTON_PRESS) {\r
- term_scroll(inst->term, 0, +5);\r
- return TRUE;\r
- }\r
-\r
- shift = state & GDK_SHIFT_MASK;\r
- ctrl = state & GDK_CONTROL_MASK;\r
- alt = state & GDK_MOD1_MASK;\r
-\r
- if (ebutton == 3 && ctrl) {\r
- gtk_menu_popup(GTK_MENU(inst->menu), NULL, NULL, NULL, NULL,\r
- ebutton, timestamp);\r
- return TRUE;\r
- }\r
-\r
- if (ebutton == 1)\r
- button = MBT_LEFT;\r
- else if (ebutton == 2)\r
- button = MBT_MIDDLE;\r
- else if (ebutton == 3)\r
- button = MBT_RIGHT;\r
- else\r
- return FALSE; /* don't even know what button! */\r
-\r
- switch (type) {\r
- case GDK_BUTTON_PRESS: act = MA_CLICK; break;\r
- case GDK_BUTTON_RELEASE: act = MA_RELEASE; break;\r
- case GDK_2BUTTON_PRESS: act = MA_2CLK; break;\r
- case GDK_3BUTTON_PRESS: act = MA_3CLK; break;\r
- default: return FALSE; /* don't know this event type */\r
- }\r
-\r
- if (send_raw_mouse && !(inst->cfg.mouse_override && shift) &&\r
- act != MA_CLICK && act != MA_RELEASE)\r
- return TRUE; /* we ignore these in raw mouse mode */\r
-\r
- x = (ex - inst->cfg.window_border) / inst->font_width;\r
- y = (ey - inst->cfg.window_border) / inst->font_height;\r
-\r
- term_mouse(inst->term, button, translate_button(button), act,\r
- x, y, shift, ctrl, alt);\r
-\r
- return TRUE;\r
-}\r
-\r
-gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- return button_internal(inst, event->time, event->type, event->button,\r
- event->state, event->x, event->y);\r
-}\r
-\r
-#if GTK_CHECK_VERSION(2,0,0)\r
-/*\r
- * In GTK 2, mouse wheel events have become a new type of event.\r
- * This handler translates them back into button-4 and button-5\r
- * presses so that I don't have to change my old code too much :-)\r
- */\r
-gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- guint button;\r
-\r
- if (event->direction == GDK_SCROLL_UP)\r
- button = 4;\r
- else if (event->direction == GDK_SCROLL_DOWN)\r
- button = 5;\r
- else\r
- return FALSE;\r
-\r
- return button_internal(inst, event->time, GDK_BUTTON_PRESS,\r
- button, event->state, event->x, event->y);\r
-}\r
-#endif\r
-\r
-gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- int shift, ctrl, alt, x, y, button;\r
-\r
- /* Remember the timestamp. */\r
- inst->input_event_time = event->time;\r
-\r
- show_mouseptr(inst, 1);\r
-\r
- shift = event->state & GDK_SHIFT_MASK;\r
- ctrl = event->state & GDK_CONTROL_MASK;\r
- alt = event->state & GDK_MOD1_MASK;\r
- if (event->state & GDK_BUTTON1_MASK)\r
- button = MBT_LEFT;\r
- else if (event->state & GDK_BUTTON2_MASK)\r
- button = MBT_MIDDLE;\r
- else if (event->state & GDK_BUTTON3_MASK)\r
- button = MBT_RIGHT;\r
- else\r
- return FALSE; /* don't even know what button! */\r
-\r
- x = (event->x - inst->cfg.window_border) / inst->font_width;\r
- y = (event->y - inst->cfg.window_border) / inst->font_height;\r
-\r
- term_mouse(inst->term, button, translate_button(button), MA_DRAG,\r
- x, y, shift, ctrl, alt);\r
-\r
- return TRUE;\r
-}\r
-\r
-void frontend_keypress(void *handle)\r
-{\r
- struct gui_data *inst = (struct gui_data *)handle;\r
-\r
- /*\r
- * If our child process has exited but not closed, terminate on\r
- * any keypress.\r
- */\r
- if (inst->exited)\r
- exit(0);\r
-}\r
-\r
-static gint idle_exit_func(gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- int exitcode;\r
-\r
- if (!inst->exited &&\r
- (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) {\r
- inst->exited = TRUE;\r
- if (inst->cfg.close_on_exit == FORCE_ON ||\r
- (inst->cfg.close_on_exit == AUTO && exitcode == 0))\r
- gtk_main_quit(); /* just go */\r
- if (inst->ldisc) {\r
- ldisc_free(inst->ldisc);\r
- inst->ldisc = NULL;\r
- }\r
- if (inst->back) {\r
- inst->back->free(inst->backhandle);\r
- inst->backhandle = NULL;\r
- inst->back = NULL;\r
- term_provide_resize_fn(inst->term, NULL, NULL);\r
- update_specials_menu(inst);\r
- }\r
- gtk_widget_set_sensitive(inst->restartitem, TRUE);\r
- }\r
-\r
- gtk_idle_remove(inst->term_exit_idle_id);\r
- return TRUE;\r
-}\r
-\r
-void notify_remote_exit(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
-\r
- inst->term_exit_idle_id = gtk_idle_add(idle_exit_func, inst);\r
-}\r
-\r
-static gint timer_trigger(gpointer data)\r
-{\r
- long now = GPOINTER_TO_LONG(data);\r
- long next;\r
- long ticks;\r
-\r
- if (run_timers(now, &next)) {\r
- ticks = next - GETTICKCOUNT();\r
- timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger,\r
- LONG_TO_GPOINTER(next));\r
- }\r
-\r
- /*\r
- * Never let a timer resume. If we need another one, we've\r
- * asked for it explicitly above.\r
- */\r
- return FALSE;\r
-}\r
-\r
-void timer_change_notify(long next)\r
-{\r
- long ticks;\r
-\r
- if (timer_id)\r
- gtk_timeout_remove(timer_id);\r
-\r
- ticks = next - GETTICKCOUNT();\r
- if (ticks <= 0)\r
- ticks = 1; /* just in case */\r
-\r
- timer_id = gtk_timeout_add(ticks, timer_trigger,\r
- LONG_TO_GPOINTER(next));\r
-}\r
-\r
-void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)\r
-{\r
- /*\r
- * We must process exceptional notifications before ordinary\r
- * readability ones, or we may go straight past the urgent\r
- * marker.\r
- */\r
- if (condition & GDK_INPUT_EXCEPTION)\r
- select_result(sourcefd, 4);\r
- if (condition & GDK_INPUT_READ)\r
- select_result(sourcefd, 1);\r
- if (condition & GDK_INPUT_WRITE)\r
- select_result(sourcefd, 2);\r
-}\r
-\r
-void destroy(GtkWidget *widget, gpointer data)\r
-{\r
- gtk_main_quit();\r
-}\r
-\r
-gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- term_set_focus(inst->term, event->in);\r
- term_update(inst->term);\r
- show_mouseptr(inst, 1);\r
- return FALSE;\r
-}\r
-\r
-void set_busy_status(void *frontend, int status)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- inst->busy_status = status;\r
- update_mouseptr(inst);\r
-}\r
-\r
-/*\r
- * set or clear the "raw mouse message" mode\r
- */\r
-void set_raw_mouse_mode(void *frontend, int activate)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- activate = activate && !inst->cfg.no_mouse_rep;\r
- send_raw_mouse = activate;\r
- update_mouseptr(inst);\r
-}\r
-\r
-void request_resize(void *frontend, int w, int h)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- int large_x, large_y;\r
- int offset_x, offset_y;\r
- int area_x, area_y;\r
- GtkRequisition inner, outer;\r
-\r
- /*\r
- * This is a heinous hack dreamed up by the gnome-terminal\r
- * people to get around a limitation in gtk. The problem is\r
- * that in order to set the size correctly we really need to be\r
- * calling gtk_window_resize - but that needs to know the size\r
- * of the _whole window_, not the drawing area. So what we do\r
- * is to set an artificially huge size request on the drawing\r
- * area, recompute the resulting size request on the window,\r
- * and look at the difference between the two. That gives us\r
- * the x and y offsets we need to translate drawing area size\r
- * into window size for real, and then we call\r
- * gtk_window_resize.\r
- */\r
-\r
- /*\r
- * We start by retrieving the current size of the whole window.\r
- * Adding a bit to _that_ will give us a value we can use as a\r
- * bogus size request which guarantees to be bigger than the\r
- * current size of the drawing area.\r
- */\r
- get_window_pixels(inst, &large_x, &large_y);\r
- large_x += 32;\r
- large_y += 32;\r
-\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_widget_set_size_request(inst->area, large_x, large_y);\r
-#else\r
- gtk_widget_set_usize(inst->area, large_x, large_y);\r
-#endif\r
- gtk_widget_size_request(inst->area, &inner);\r
- gtk_widget_size_request(inst->window, &outer);\r
-\r
- offset_x = outer.width - inner.width;\r
- offset_y = outer.height - inner.height;\r
-\r
- area_x = inst->font_width * w + 2*inst->cfg.window_border;\r
- area_y = inst->font_height * h + 2*inst->cfg.window_border;\r
-\r
- /*\r
- * Now we must set the size request on the drawing area back to\r
- * something sensible before we commit the real resize. Best\r
- * way to do this, I think, is to set it to what the size is\r
- * really going to end up being.\r
- */\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_widget_set_size_request(inst->area, area_x, area_y);\r
- gtk_window_resize(GTK_WINDOW(inst->window),\r
- area_x + offset_x, area_y + offset_y);\r
-#else\r
- gtk_widget_set_usize(inst->area, area_x, area_y);\r
- gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), area_x, area_y);\r
- /*\r
- * I can no longer remember what this call to\r
- * gtk_container_dequeue_resize_handler is for. It was\r
- * introduced in r3092 with no comment, and the commit log\r
- * message was uninformative. I'm _guessing_ its purpose is to\r
- * prevent gratuitous resize processing on the window given\r
- * that we're about to resize it anyway, but I have no idea\r
- * why that's so incredibly vital.\r
- * \r
- * I've tried removing the call, and nothing seems to go\r
- * wrong. I've backtracked to r3092 and tried removing the\r
- * call there, and still nothing goes wrong. So I'm going to\r
- * adopt the working hypothesis that it's superfluous; I won't\r
- * actually remove it from the GTK 1.2 code, but I won't\r
- * attempt to replicate its functionality in the GTK 2 code\r
- * above.\r
- */\r
- gtk_container_dequeue_resize_handler(GTK_CONTAINER(inst->window));\r
- gdk_window_resize(inst->window->window,\r
- area_x + offset_x, area_y + offset_y);\r
-#endif\r
-}\r
-\r
-static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)\r
-{\r
- gboolean success[1];\r
-\r
- inst->cols[n].red = r * 0x0101;\r
- inst->cols[n].green = g * 0x0101;\r
- inst->cols[n].blue = b * 0x0101;\r
-\r
- gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);\r
- gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,\r
- FALSE, TRUE, success);\r
- if (!success[0])\r
- g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname,\r
- n, r, g, b);\r
-}\r
-\r
-void set_window_background(struct gui_data *inst)\r
-{\r
- if (inst->area && inst->area->window)\r
- gdk_window_set_background(inst->area->window, &inst->cols[258]);\r
- if (inst->window && inst->window->window)\r
- gdk_window_set_background(inst->window->window, &inst->cols[258]);\r
-}\r
-\r
-void palette_set(void *frontend, int n, int r, int g, int b)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- if (n >= 16)\r
- n += 256 - 16;\r
- if (n > NALLCOLOURS)\r
- return;\r
- real_palette_set(inst, n, r, g, b);\r
- if (n == 258) {\r
- /* Default Background changed. Ensure space between text area and\r
- * window border is redrawn */\r
- set_window_background(inst);\r
- draw_backing_rect(inst);\r
- gtk_widget_queue_draw(inst->area);\r
- }\r
-}\r
-\r
-void palette_reset(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- /* This maps colour indices in inst->cfg to those used in inst->cols. */\r
- static const int ww[] = {\r
- 256, 257, 258, 259, 260, 261,\r
- 0, 8, 1, 9, 2, 10, 3, 11,\r
- 4, 12, 5, 13, 6, 14, 7, 15\r
- };\r
- gboolean success[NALLCOLOURS];\r
- int i;\r
-\r
- assert(lenof(ww) == NCFGCOLOURS);\r
-\r
- if (!inst->colmap) {\r
- inst->colmap = gdk_colormap_get_system();\r
- } else {\r
- gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS);\r
- }\r
-\r
- for (i = 0; i < NCFGCOLOURS; i++) {\r
- inst->cols[ww[i]].red = inst->cfg.colours[i][0] * 0x0101;\r
- inst->cols[ww[i]].green = inst->cfg.colours[i][1] * 0x0101;\r
- inst->cols[ww[i]].blue = inst->cfg.colours[i][2] * 0x0101;\r
- }\r
-\r
- for (i = 0; i < NEXTCOLOURS; i++) {\r
- if (i < 216) {\r
- int r = i / 36, g = (i / 6) % 6, b = i % 6;\r
- inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0;\r
- inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0;\r
- inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0;\r
- } else {\r
- int shade = i - 216;\r
- shade = shade * 0x0a0a + 0x0808;\r
- inst->cols[i+16].red = inst->cols[i+16].green =\r
- inst->cols[i+16].blue = shade;\r
- }\r
- }\r
-\r
- gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS,\r
- FALSE, TRUE, success);\r
- for (i = 0; i < NALLCOLOURS; i++) {\r
- if (!success[i])\r
- g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",\r
- appname, i, inst->cfg.colours[i][0],\r
- inst->cfg.colours[i][1], inst->cfg.colours[i][2]);\r
- }\r
-\r
- /* Since Default Background may have changed, ensure that space\r
- * between text area and window border is refreshed. */\r
- set_window_background(inst);\r
- if (inst->area && inst->area->window) {\r
- draw_backing_rect(inst);\r
- gtk_widget_queue_draw(inst->area);\r
- }\r
-}\r
-\r
-/* Ensure that all the cut buffers exist - according to the ICCCM, we must\r
- * do this before we start using cut buffers.\r
- */\r
-void init_cutbuffers()\r
-{\r
- unsigned char empty[] = "";\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0);\r
- XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
- XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);\r
-}\r
-\r
-/* Store the data in a cut-buffer. */\r
-void store_cutbuffer(char * ptr, int len)\r
-{\r
- /* ICCCM says we must rotate the buffers before storing to buffer 0. */\r
- XRotateBuffers(GDK_DISPLAY(), 1);\r
- XStoreBytes(GDK_DISPLAY(), ptr, len);\r
-}\r
-\r
-/* Retrieve data from a cut-buffer.\r
- * Returned data needs to be freed with XFree().\r
- */\r
-char * retrieve_cutbuffer(int * nbytes)\r
-{\r
- char * ptr;\r
- ptr = XFetchBytes(GDK_DISPLAY(), nbytes);\r
- if (*nbytes <= 0 && ptr != 0) {\r
- XFree(ptr);\r
- ptr = 0;\r
- }\r
- return ptr;\r
-}\r
-\r
-void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- if (inst->pasteout_data)\r
- sfree(inst->pasteout_data);\r
- if (inst->pasteout_data_ctext)\r
- sfree(inst->pasteout_data_ctext);\r
- if (inst->pasteout_data_utf8)\r
- sfree(inst->pasteout_data_utf8);\r
-\r
- /*\r
- * Set up UTF-8 and compound text paste data. This only happens\r
- * if we aren't in direct-to-font mode using the D800 hack.\r
- */\r
- if (!inst->direct_to_font) {\r
- wchar_t *tmp = data;\r
- int tmplen = len;\r
- XTextProperty tp;\r
- char *list[1];\r
-\r
- inst->pasteout_data_utf8 = snewn(len*6, char);\r
- inst->pasteout_data_utf8_len = len*6;\r
- inst->pasteout_data_utf8_len =\r
- charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,\r
- inst->pasteout_data_utf8_len,\r
- CS_UTF8, NULL, NULL, 0);\r
- if (inst->pasteout_data_utf8_len == 0) {\r
- sfree(inst->pasteout_data_utf8);\r
- inst->pasteout_data_utf8 = NULL;\r
- } else {\r
- inst->pasteout_data_utf8 =\r
- sresize(inst->pasteout_data_utf8,\r
- inst->pasteout_data_utf8_len + 1, char);\r
- inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0';\r
- }\r
-\r
- /*\r
- * Now let Xlib convert our UTF-8 data into compound text.\r
- */\r
- list[0] = inst->pasteout_data_utf8;\r
- if (Xutf8TextListToTextProperty(GDK_DISPLAY(), list, 1,\r
- XCompoundTextStyle, &tp) == 0) {\r
- inst->pasteout_data_ctext = snewn(tp.nitems+1, char);\r
- memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);\r
- inst->pasteout_data_ctext_len = tp.nitems;\r
- XFree(tp.value);\r
- } else {\r
- inst->pasteout_data_ctext = NULL;\r
- inst->pasteout_data_ctext_len = 0;\r
- }\r
- } else {\r
- inst->pasteout_data_utf8 = NULL;\r
- inst->pasteout_data_utf8_len = 0;\r
- inst->pasteout_data_ctext = NULL;\r
- inst->pasteout_data_ctext_len = 0;\r
- }\r
-\r
- inst->pasteout_data = snewn(len*6, char);\r
- inst->pasteout_data_len = len*6;\r
- inst->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0,\r
- data, len, inst->pasteout_data,\r
- inst->pasteout_data_len,\r
- NULL, NULL, NULL);\r
- if (inst->pasteout_data_len == 0) {\r
- sfree(inst->pasteout_data);\r
- inst->pasteout_data = NULL;\r
- } else {\r
- inst->pasteout_data =\r
- sresize(inst->pasteout_data, inst->pasteout_data_len, char);\r
- }\r
-\r
- store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len);\r
-\r
- if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,\r
- inst->input_event_time)) {\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_selection_clear_targets(inst->area, GDK_SELECTION_PRIMARY);\r
-#endif\r
- gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,\r
- GDK_SELECTION_TYPE_STRING, 1);\r
- if (inst->pasteout_data_ctext)\r
- gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,\r
- compound_text_atom, 1);\r
- if (inst->pasteout_data_utf8)\r
- gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,\r
- utf8_string_atom, 1);\r
- }\r
-\r
- if (must_deselect)\r
- term_deselect(inst->term);\r
-}\r
-\r
-void selection_get(GtkWidget *widget, GtkSelectionData *seldata,\r
- guint info, guint time_stamp, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- if (seldata->target == utf8_string_atom)\r
- gtk_selection_data_set(seldata, seldata->target, 8,\r
- (unsigned char *)inst->pasteout_data_utf8,\r
- inst->pasteout_data_utf8_len);\r
- else if (seldata->target == compound_text_atom)\r
- gtk_selection_data_set(seldata, seldata->target, 8,\r
- (unsigned char *)inst->pasteout_data_ctext,\r
- inst->pasteout_data_ctext_len);\r
- else\r
- gtk_selection_data_set(seldata, seldata->target, 8,\r
- (unsigned char *)inst->pasteout_data,\r
- inst->pasteout_data_len);\r
-}\r
-\r
-gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,\r
- gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
-\r
- term_deselect(inst->term);\r
- if (inst->pasteout_data)\r
- sfree(inst->pasteout_data);\r
- if (inst->pasteout_data_ctext)\r
- sfree(inst->pasteout_data_ctext);\r
- if (inst->pasteout_data_utf8)\r
- sfree(inst->pasteout_data_utf8);\r
- inst->pasteout_data = NULL;\r
- inst->pasteout_data_len = 0;\r
- inst->pasteout_data_ctext = NULL;\r
- inst->pasteout_data_ctext_len = 0;\r
- inst->pasteout_data_utf8 = NULL;\r
- inst->pasteout_data_utf8_len = 0;\r
- return TRUE;\r
-}\r
-\r
-void request_paste(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- /*\r
- * In Unix, pasting is asynchronous: all we can do at the\r
- * moment is to call gtk_selection_convert(), and when the data\r
- * comes back _then_ we can call term_do_paste().\r
- */\r
-\r
- if (!inst->direct_to_font) {\r
- /*\r
- * First we attempt to retrieve the selection as a UTF-8\r
- * string (which we will convert to the correct code page\r
- * before sending to the session, of course). If that\r
- * fails, selection_received() will be informed and will\r
- * fall back to an ordinary string.\r
- */\r
- gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
- utf8_string_atom,\r
- inst->input_event_time);\r
- } else {\r
- /*\r
- * If we're in direct-to-font mode, we disable UTF-8\r
- * pasting, and go straight to ordinary string data.\r
- */\r
- gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
- GDK_SELECTION_TYPE_STRING,\r
- inst->input_event_time);\r
- }\r
-}\r
-\r
-gint idle_paste_func(gpointer data); /* forward ref */\r
-\r
-void selection_received(GtkWidget *widget, GtkSelectionData *seldata,\r
- guint time, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- XTextProperty tp;\r
- char **list;\r
- char *text;\r
- int length, count, ret;\r
- int free_list_required = 0;\r
- int free_required = 0;\r
- int charset;\r
-\r
- if (seldata->target == utf8_string_atom && seldata->length <= 0) {\r
- /*\r
- * Failed to get a UTF-8 selection string. Try compound\r
- * text next.\r
- */\r
- gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
- compound_text_atom,\r
- inst->input_event_time);\r
- return;\r
- }\r
-\r
- if (seldata->target == compound_text_atom && seldata->length <= 0) {\r
- /*\r
- * Failed to get UTF-8 or compound text. Try an ordinary\r
- * string.\r
- */\r
- gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
- GDK_SELECTION_TYPE_STRING,\r
- inst->input_event_time);\r
- return;\r
- }\r
-\r
- /*\r
- * If we have data, but it's not of a type we can deal with,\r
- * we have to ignore the data.\r
- */\r
- if (seldata->length > 0 &&\r
- seldata->type != GDK_SELECTION_TYPE_STRING &&\r
- seldata->type != compound_text_atom &&\r
- seldata->type != utf8_string_atom)\r
- return;\r
-\r
- /*\r
- * If we have no data, try looking in a cut buffer.\r
- */\r
- if (seldata->length <= 0) {\r
- text = retrieve_cutbuffer(&length);\r
- if (length == 0)\r
- return;\r
- /* Xterm is rumoured to expect Latin-1, though I havn't checked the\r
- * source, so use that as a de-facto standard. */\r
- charset = CS_ISO8859_1;\r
- free_required = 1;\r
- } else {\r
- /*\r
- * Convert COMPOUND_TEXT into UTF-8.\r
- */\r
- if (seldata->type == compound_text_atom) {\r
- tp.value = seldata->data;\r
- tp.encoding = (Atom) seldata->type;\r
- tp.format = seldata->format;\r
- tp.nitems = seldata->length;\r
- ret = Xutf8TextPropertyToTextList(GDK_DISPLAY(), &tp,\r
- &list, &count);\r
- if (ret != 0 || count != 1) {\r
- /*\r
- * Compound text failed; fall back to STRING.\r
- */\r
- gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
- GDK_SELECTION_TYPE_STRING,\r
- inst->input_event_time);\r
- return;\r
- }\r
- text = list[0];\r
- length = strlen(list[0]);\r
- charset = CS_UTF8;\r
- free_list_required = 1;\r
- } else {\r
- text = (char *)seldata->data;\r
- length = seldata->length;\r
- charset = (seldata->type == utf8_string_atom ?\r
- CS_UTF8 : inst->ucsdata.line_codepage);\r
- }\r
- }\r
-\r
- if (inst->pastein_data)\r
- sfree(inst->pastein_data);\r
-\r
- inst->pastein_data = snewn(length, wchar_t);\r
- inst->pastein_data_len = length;\r
- inst->pastein_data_len =\r
- mb_to_wc(charset, 0, text, length,\r
- inst->pastein_data, inst->pastein_data_len);\r
-\r
- term_do_paste(inst->term);\r
-\r
- if (term_paste_pending(inst->term))\r
- inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);\r
-\r
- if (free_list_required)\r
- XFreeStringList(list);\r
- if (free_required)\r
- XFree(text);\r
-}\r
-\r
-gint idle_paste_func(gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
-\r
- if (term_paste_pending(inst->term))\r
- term_paste(inst->term);\r
- else\r
- gtk_idle_remove(inst->term_paste_idle_id);\r
-\r
- return TRUE;\r
-}\r
-\r
-\r
-void get_clip(void *frontend, wchar_t ** p, int *len)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
-\r
- if (p) {\r
- *p = inst->pastein_data;\r
- *len = inst->pastein_data_len;\r
- }\r
-}\r
-\r
-static void set_window_titles(struct gui_data *inst)\r
-{\r
- /*\r
- * We must always call set_icon_name after calling set_title,\r
- * since set_title will write both names. Irritating, but such\r
- * is life.\r
- */\r
- gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);\r
- if (!inst->cfg.win_name_always)\r
- gdk_window_set_icon_name(inst->window->window, inst->icontitle);\r
-}\r
-\r
-void set_title(void *frontend, char *title)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- strncpy(inst->wintitle, title, lenof(inst->wintitle));\r
- inst->wintitle[lenof(inst->wintitle)-1] = '\0';\r
- set_window_titles(inst);\r
-}\r
-\r
-void set_icon(void *frontend, char *title)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- strncpy(inst->icontitle, title, lenof(inst->icontitle));\r
- inst->icontitle[lenof(inst->icontitle)-1] = '\0';\r
- set_window_titles(inst);\r
-}\r
-\r
-void set_sbar(void *frontend, int total, int start, int page)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- if (!inst->cfg.scrollbar)\r
- return;\r
- inst->sbar_adjust->lower = 0;\r
- inst->sbar_adjust->upper = total;\r
- inst->sbar_adjust->value = start;\r
- inst->sbar_adjust->page_size = page;\r
- inst->sbar_adjust->step_increment = 1;\r
- inst->sbar_adjust->page_increment = page/2;\r
- inst->ignore_sbar = TRUE;\r
- gtk_adjustment_changed(inst->sbar_adjust);\r
- inst->ignore_sbar = FALSE;\r
-}\r
-\r
-void scrollbar_moved(GtkAdjustment *adj, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
-\r
- if (!inst->cfg.scrollbar)\r
- return;\r
- if (!inst->ignore_sbar)\r
- term_scroll(inst->term, 1, (int)adj->value);\r
-}\r
-\r
-void sys_cursor(void *frontend, int x, int y)\r
-{\r
- /*\r
- * This is meaningless under X.\r
- */\r
-}\r
-\r
-/*\r
- * This is still called when mode==BELL_VISUAL, even though the\r
- * visual bell is handled entirely within terminal.c, because we\r
- * may want to perform additional actions on any kind of bell (for\r
- * example, taskbar flashing in Windows).\r
- */\r
-void do_beep(void *frontend, int mode)\r
-{\r
- if (mode == BELL_DEFAULT)\r
- gdk_beep();\r
-}\r
-\r
-int char_width(Context ctx, int uc)\r
-{\r
- /*\r
- * Under X, any fixed-width font really _is_ fixed-width.\r
- * Double-width characters will be dealt with using a separate\r
- * font. For the moment we can simply return 1.\r
- * \r
- * FIXME: but is that also true of Pango?\r
- */\r
- return 1;\r
-}\r
-\r
-Context get_ctx(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- struct draw_ctx *dctx;\r
-\r
- if (!inst->area->window)\r
- return NULL;\r
-\r
- dctx = snew(struct draw_ctx);\r
- dctx->inst = inst;\r
- dctx->gc = gdk_gc_new(inst->area->window);\r
- return dctx;\r
-}\r
-\r
-void free_ctx(Context ctx)\r
-{\r
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
- /* struct gui_data *inst = dctx->inst; */\r
- GdkGC *gc = dctx->gc;\r
- gdk_gc_unref(gc);\r
- sfree(dctx);\r
-}\r
-\r
-/*\r
- * Draw a line of text in the window, at given character\r
- * coordinates, in given attributes.\r
- *\r
- * We are allowed to fiddle with the contents of `text'.\r
- */\r
-void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,\r
- unsigned long attr, int lattr)\r
-{\r
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
- struct gui_data *inst = dctx->inst;\r
- GdkGC *gc = dctx->gc;\r
- int ncombining, combining;\r
- int nfg, nbg, t, fontid, shadow, rlen, widefactor, bold;\r
- int monochrome = gtk_widget_get_visual(inst->area)->depth == 1;\r
-\r
- if (attr & TATTR_COMBINING) {\r
- ncombining = len;\r
- len = 1;\r
- } else\r
- ncombining = 1;\r
-\r
- nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT);\r
- nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT);\r
- if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) {\r
- t = nfg;\r
- nfg = nbg;\r
- nbg = t;\r
- }\r
- if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) {\r
- if (nfg < 16) nfg |= 8;\r
- else if (nfg >= 256) nfg |= 1;\r
- }\r
- if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) {\r
- if (nbg < 16) nbg |= 8;\r
- else if (nbg >= 256) nbg |= 1;\r
- }\r
- if ((attr & TATTR_ACTCURS) && !monochrome) {\r
- nfg = 260;\r
- nbg = 261;\r
- }\r
-\r
- fontid = shadow = 0;\r
-\r
- if (attr & ATTR_WIDE) {\r
- widefactor = 2;\r
- fontid |= 2;\r
- } else {\r
- widefactor = 1;\r
- }\r
-\r
- if ((attr & ATTR_BOLD) && !inst->cfg.bold_colour) {\r
- bold = 1;\r
- fontid |= 1;\r
- } else {\r
- bold = 0;\r
- }\r
-\r
- if (!inst->fonts[fontid]) {\r
- int i;\r
- /*\r
- * Fall back through font ids with subsets of this one's\r
- * set bits, in order.\r
- */\r
- for (i = fontid; i-- > 0 ;) {\r
- if (i & ~fontid)\r
- continue; /* some other bit is set */\r
- if (inst->fonts[i]) {\r
- fontid = i;\r
- break;\r
- }\r
- }\r
- assert(inst->fonts[fontid]); /* we should at least have hit zero */\r
- }\r
-\r
- if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
- x *= 2;\r
- if (x >= inst->term->cols)\r
- return;\r
- if (x + len*2*widefactor > inst->term->cols)\r
- len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */\r
- rlen = len * 2;\r
- } else\r
- rlen = len;\r
-\r
- {\r
- GdkRectangle r;\r
-\r
- r.x = x*inst->font_width+inst->cfg.window_border;\r
- r.y = y*inst->font_height+inst->cfg.window_border;\r
- r.width = rlen*widefactor*inst->font_width;\r
- r.height = inst->font_height;\r
- gdk_gc_set_clip_rectangle(gc, &r);\r
- }\r
-\r
- gdk_gc_set_foreground(gc, &inst->cols[nbg]);\r
- gdk_draw_rectangle(inst->pixmap, gc, 1,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border,\r
- rlen*widefactor*inst->font_width, inst->font_height);\r
-\r
- gdk_gc_set_foreground(gc, &inst->cols[nfg]);\r
- {\r
- gchar *gcs;\r
-\r
- /*\r
- * FIXME: this length is hardwired on the assumption that\r
- * conversions from wide to multibyte characters will\r
- * never generate more than 10 bytes for a single wide\r
- * character.\r
- */\r
- gcs = snewn(len*10+1, gchar);\r
-\r
- for (combining = 0; combining < ncombining; combining++) {\r
- int mblen = wc_to_mb(inst->fonts[fontid]->real_charset, 0,\r
- text + combining, len, gcs, len*10+1, ".",\r
- NULL, NULL);\r
- unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid],\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,\r
- gcs, mblen, widefactor > 1, bold, inst->font_width);\r
- }\r
-\r
- sfree(gcs);\r
- }\r
-\r
- if (attr & ATTR_UNDER) {\r
- int uheight = inst->fonts[0]->ascent + 1;\r
- if (uheight >= inst->font_height)\r
- uheight = inst->font_height - 1;\r
- gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height + uheight + inst->cfg.window_border,\r
- (x+len)*widefactor*inst->font_width-1+inst->cfg.window_border,\r
- y*inst->font_height + uheight + inst->cfg.window_border);\r
- }\r
-\r
- if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
- /*\r
- * I can't find any plausible StretchBlt equivalent in the\r
- * X server, so I'm going to do this the slow and painful\r
- * way. This will involve repeated calls to\r
- * gdk_draw_pixmap() to stretch the text horizontally. It's\r
- * O(N^2) in time and O(N) in network bandwidth, but you\r
- * try thinking of a better way. :-(\r
- */\r
- int i;\r
- for (i = 0; i < len * widefactor * inst->font_width; i++) {\r
- gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,\r
- x*inst->font_width+inst->cfg.window_border + 2*i,\r
- y*inst->font_height+inst->cfg.window_border,\r
- x*inst->font_width+inst->cfg.window_border + 2*i+1,\r
- y*inst->font_height+inst->cfg.window_border,\r
- len * widefactor * inst->font_width - i, inst->font_height);\r
- }\r
- len *= 2;\r
- if ((lattr & LATTR_MODE) != LATTR_WIDE) {\r
- int dt, db;\r
- /* Now stretch vertically, in the same way. */\r
- if ((lattr & LATTR_MODE) == LATTR_BOT)\r
- dt = 0, db = 1;\r
- else\r
- dt = 1, db = 0;\r
- for (i = 0; i < inst->font_height; i+=2) {\r
- gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border+dt*i+db,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border+dt*(i+1),\r
- len * widefactor * inst->font_width, inst->font_height-i-1);\r
- }\r
- }\r
- }\r
-}\r
-\r
-void do_text(Context ctx, int x, int y, wchar_t *text, int len,\r
- unsigned long attr, int lattr)\r
-{\r
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
- struct gui_data *inst = dctx->inst;\r
- GdkGC *gc = dctx->gc;\r
- int widefactor;\r
-\r
- do_text_internal(ctx, x, y, text, len, attr, lattr);\r
-\r
- if (attr & ATTR_WIDE) {\r
- widefactor = 2;\r
- } else {\r
- widefactor = 1;\r
- }\r
-\r
- if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
- x *= 2;\r
- if (x >= inst->term->cols)\r
- return;\r
- if (x + len*2*widefactor > inst->term->cols)\r
- len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */\r
- len *= 2;\r
- }\r
-\r
- gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border,\r
- len*widefactor*inst->font_width, inst->font_height);\r
-}\r
-\r
-void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,\r
- unsigned long attr, int lattr)\r
-{\r
- struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
- struct gui_data *inst = dctx->inst;\r
- GdkGC *gc = dctx->gc;\r
-\r
- int active, passive, widefactor;\r
-\r
- if (attr & TATTR_PASCURS) {\r
- attr &= ~TATTR_PASCURS;\r
- passive = 1;\r
- } else\r
- passive = 0;\r
- if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) {\r
- attr &= ~TATTR_ACTCURS;\r
- active = 1;\r
- } else\r
- active = 0;\r
- do_text_internal(ctx, x, y, text, len, attr, lattr);\r
-\r
- if (attr & TATTR_COMBINING)\r
- len = 1;\r
-\r
- if (attr & ATTR_WIDE) {\r
- widefactor = 2;\r
- } else {\r
- widefactor = 1;\r
- }\r
-\r
- if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
- x *= 2;\r
- if (x >= inst->term->cols)\r
- return;\r
- if (x + len*2*widefactor > inst->term->cols)\r
- len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */\r
- len *= 2;\r
- }\r
-\r
- if (inst->cfg.cursor_type == 0) {\r
- /*\r
- * An active block cursor will already have been done by\r
- * the above do_text call, so we only need to do anything\r
- * if it's passive.\r
- */\r
- if (passive) {\r
- gdk_gc_set_foreground(gc, &inst->cols[261]);\r
- gdk_draw_rectangle(inst->pixmap, gc, 0,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border,\r
- len*widefactor*inst->font_width-1, inst->font_height-1);\r
- }\r
- } else {\r
- int uheight;\r
- int startx, starty, dx, dy, length, i;\r
-\r
- int char_width;\r
-\r
- if ((attr & ATTR_WIDE) || (lattr & LATTR_MODE) != LATTR_NORM)\r
- char_width = 2*inst->font_width;\r
- else\r
- char_width = inst->font_width;\r
-\r
- if (inst->cfg.cursor_type == 1) {\r
- uheight = inst->fonts[0]->ascent + 1;\r
- if (uheight >= inst->font_height)\r
- uheight = inst->font_height - 1;\r
-\r
- startx = x * inst->font_width + inst->cfg.window_border;\r
- starty = y * inst->font_height + inst->cfg.window_border + uheight;\r
- dx = 1;\r
- dy = 0;\r
- length = len * widefactor * char_width;\r
- } else {\r
- int xadjust = 0;\r
- if (attr & TATTR_RIGHTCURS)\r
- xadjust = char_width - 1;\r
- startx = x * inst->font_width + inst->cfg.window_border + xadjust;\r
- starty = y * inst->font_height + inst->cfg.window_border;\r
- dx = 0;\r
- dy = 1;\r
- length = inst->font_height;\r
- }\r
-\r
- gdk_gc_set_foreground(gc, &inst->cols[261]);\r
- if (passive) {\r
- for (i = 0; i < length; i++) {\r
- if (i % 2 == 0) {\r
- gdk_draw_point(inst->pixmap, gc, startx, starty);\r
- }\r
- startx += dx;\r
- starty += dy;\r
- }\r
- } else if (active) {\r
- gdk_draw_line(inst->pixmap, gc, startx, starty,\r
- startx + (length-1) * dx, starty + (length-1) * dy);\r
- } /* else no cursor (e.g., blinked off) */\r
- }\r
-\r
- gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border,\r
- x*inst->font_width+inst->cfg.window_border,\r
- y*inst->font_height+inst->cfg.window_border,\r
- len*widefactor*inst->font_width, inst->font_height);\r
-}\r
-\r
-GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)\r
-{\r
- /*\r
- * Truly hideous hack: GTK doesn't allow us to set the mouse\r
- * cursor foreground and background colours unless we've _also_\r
- * created our own cursor from bitmaps. Therefore, I need to\r
- * load the `cursor' font and draw glyphs from it on to\r
- * pixmaps, in order to construct my cursors with the fg and bg\r
- * I want. This is a gross hack, but it's more self-contained\r
- * than linking in Xlib to find the X window handle to\r
- * inst->area and calling XRecolorCursor, and it's more\r
- * futureproof than hard-coding the shapes as bitmap arrays.\r
- */\r
- static GdkFont *cursor_font = NULL;\r
- GdkPixmap *source, *mask;\r
- GdkGC *gc;\r
- GdkColor cfg = { 0, 65535, 65535, 65535 };\r
- GdkColor cbg = { 0, 0, 0, 0 };\r
- GdkColor dfg = { 1, 65535, 65535, 65535 };\r
- GdkColor dbg = { 0, 0, 0, 0 };\r
- GdkCursor *ret;\r
- gchar text[2];\r
- gint lb, rb, wid, asc, desc, w, h, x, y;\r
-\r
- if (cursor_val == -2) {\r
- gdk_font_unref(cursor_font);\r
- return NULL;\r
- }\r
-\r
- if (cursor_val >= 0 && !cursor_font) {\r
- cursor_font = gdk_font_load("cursor");\r
- if (cursor_font)\r
- gdk_font_ref(cursor_font);\r
- }\r
-\r
- /*\r
- * Get the text extent of the cursor in question. We use the\r
- * mask character for this, because it's typically slightly\r
- * bigger than the main character.\r
- */\r
- if (cursor_val >= 0) {\r
- text[1] = '\0';\r
- text[0] = (char)cursor_val + 1;\r
- gdk_string_extents(cursor_font, text, &lb, &rb, &wid, &asc, &desc);\r
- w = rb-lb; h = asc+desc; x = -lb; y = asc;\r
- } else {\r
- w = h = 1;\r
- x = y = 0;\r
- }\r
-\r
- source = gdk_pixmap_new(NULL, w, h, 1);\r
- mask = gdk_pixmap_new(NULL, w, h, 1);\r
-\r
- /*\r
- * Draw the mask character on the mask pixmap.\r
- */\r
- gc = gdk_gc_new(mask);\r
- gdk_gc_set_foreground(gc, &dbg);\r
- gdk_draw_rectangle(mask, gc, 1, 0, 0, w, h);\r
- if (cursor_val >= 0) {\r
- text[1] = '\0';\r
- text[0] = (char)cursor_val + 1;\r
- gdk_gc_set_foreground(gc, &dfg);\r
- gdk_draw_text(mask, cursor_font, gc, x, y, text, 1);\r
- }\r
- gdk_gc_unref(gc);\r
-\r
- /*\r
- * Draw the main character on the source pixmap.\r
- */\r
- gc = gdk_gc_new(source);\r
- gdk_gc_set_foreground(gc, &dbg);\r
- gdk_draw_rectangle(source, gc, 1, 0, 0, w, h);\r
- if (cursor_val >= 0) {\r
- text[1] = '\0';\r
- text[0] = (char)cursor_val;\r
- gdk_gc_set_foreground(gc, &dfg);\r
- gdk_draw_text(source, cursor_font, gc, x, y, text, 1);\r
- }\r
- gdk_gc_unref(gc);\r
-\r
- /*\r
- * Create the cursor.\r
- */\r
- ret = gdk_cursor_new_from_pixmap(source, mask, &cfg, &cbg, x, y);\r
-\r
- /*\r
- * Clean up.\r
- */\r
- gdk_pixmap_unref(source);\r
- gdk_pixmap_unref(mask);\r
-\r
- return ret;\r
-}\r
-\r
-void modalfatalbox(char *p, ...)\r
-{\r
- va_list ap;\r
- fprintf(stderr, "FATAL ERROR: ");\r
- va_start(ap, p);\r
- vfprintf(stderr, p, ap);\r
- va_end(ap);\r
- fputc('\n', stderr);\r
- exit(1);\r
-}\r
-\r
-void cmdline_error(char *p, ...)\r
-{\r
- va_list ap;\r
- fprintf(stderr, "%s: ", appname);\r
- va_start(ap, p);\r
- vfprintf(stderr, p, ap);\r
- va_end(ap);\r
- fputc('\n', stderr);\r
- exit(1);\r
-}\r
-\r
-char *get_x_display(void *frontend)\r
-{\r
- return gdk_get_display();\r
-}\r
-\r
-long get_windowid(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
- return (long)GDK_WINDOW_XWINDOW(inst->area->window);\r
-}\r
-\r
-static void help(FILE *fp) {\r
- if(fprintf(fp,\r
-"pterm option summary:\n"\r
-"\n"\r
-" --display DISPLAY Specify X display to use (note '--')\n"\r
-" -name PREFIX Prefix when looking up resources (default: pterm)\n"\r
-" -fn FONT Normal text font\n"\r
-" -fb FONT Bold text font\n"\r
-" -geometry GEOMETRY Position and size of window (size in characters)\n"\r
-" -sl LINES Number of lines of scrollback\n"\r
-" -fg COLOUR, -bg COLOUR Foreground/background colour\n"\r
-" -bfg COLOUR, -bbg COLOUR Foreground/background bold colour\n"\r
-" -cfg COLOUR, -bfg COLOUR Foreground/background cursor colour\n"\r
-" -T TITLE Window title\n"\r
-" -ut, +ut Do(default) or do not update utmp\n"\r
-" -ls, +ls Do(default) or do not make shell a login shell\n"\r
-" -sb, +sb Do(default) or do not display a scrollbar\n"\r
-" -log PATH Log all output to a file\n"\r
-" -nethack Map numeric keypad to hjklyubn direction keys\n"\r
-" -xrm RESOURCE-STRING Set an X resource\n"\r
-" -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n"\r
- ) < 0 || fflush(fp) < 0) {\r
- perror("output error");\r
- exit(1);\r
- }\r
-}\r
-\r
-int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,\r
- struct gui_data *inst, Config *cfg)\r
-{\r
- int err = 0;\r
- char *val;\r
-\r
- /*\r
- * Macros to make argument handling easier. Note that because\r
- * they need to call `continue', they cannot be contained in\r
- * the usual do {...} while (0) wrapper to make them\r
- * syntactically single statements; hence it is not legal to\r
- * use one of these macros as an unbraced statement between\r
- * `if' and `else'.\r
- */\r
-#define EXPECTS_ARG { \\r
- if (--argc <= 0) { \\r
- err = 1; \\r
- fprintf(stderr, "%s: %s expects an argument\n", appname, p); \\r
- continue; \\r
- } else \\r
- val = *++argv; \\r
-}\r
-#define SECOND_PASS_ONLY { if (!do_everything) continue; }\r
-\r
- while (--argc > 0) {\r
- char *p = *++argv;\r
- int ret;\r
-\r
- /*\r
- * Shameless cheating. Debian requires all X terminal\r
- * emulators to support `-T title'; but\r
- * cmdline_process_param will eat -T (it means no-pty) and\r
- * complain that pterm doesn't support it. So, in pterm\r
- * only, we convert -T into -title.\r
- */\r
- if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&\r
- !strcmp(p, "-T"))\r
- p = "-title";\r
-\r
- ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
- do_everything ? 1 : -1, cfg);\r
-\r
- if (ret == -2) {\r
- cmdline_error("option \"%s\" requires an argument", p);\r
- } else if (ret == 2) {\r
- --argc, ++argv; /* skip next argument */\r
- continue;\r
- } else if (ret == 1) {\r
- continue;\r
- }\r
-\r
- if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->font.name, val, sizeof(cfg->font.name));\r
- cfg->font.name[sizeof(cfg->font.name)-1] = '\0';\r
-\r
- } else if (!strcmp(p, "-fb")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->boldfont.name, val, sizeof(cfg->boldfont.name));\r
- cfg->boldfont.name[sizeof(cfg->boldfont.name)-1] = '\0';\r
-\r
- } else if (!strcmp(p, "-fw")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->widefont.name, val, sizeof(cfg->widefont.name));\r
- cfg->widefont.name[sizeof(cfg->widefont.name)-1] = '\0';\r
-\r
- } else if (!strcmp(p, "-fwb")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->wideboldfont.name, val, sizeof(cfg->wideboldfont.name));\r
- cfg->wideboldfont.name[sizeof(cfg->wideboldfont.name)-1] = '\0';\r
-\r
- } else if (!strcmp(p, "-cs")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->line_codepage, val, sizeof(cfg->line_codepage));\r
- cfg->line_codepage[sizeof(cfg->line_codepage)-1] = '\0';\r
-\r
- } else if (!strcmp(p, "-geometry")) {\r
- int flags, x, y;\r
- unsigned int w, h;\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
-\r
- flags = XParseGeometry(val, &x, &y, &w, &h);\r
- if (flags & WidthValue)\r
- cfg->width = (int)w;\r
- if (flags & HeightValue)\r
- cfg->height = (int)h;\r
-\r
- if (flags & (XValue | YValue)) {\r
- inst->xpos = x;\r
- inst->ypos = y;\r
- inst->gotpos = TRUE;\r
- inst->gravity = ((flags & XNegative ? 1 : 0) |\r
- (flags & YNegative ? 2 : 0));\r
- }\r
-\r
- } else if (!strcmp(p, "-sl")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- cfg->savelines = atoi(val);\r
-\r
- } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||\r
- !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||\r
- !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {\r
- GdkColor col;\r
-\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- if (!gdk_color_parse(val, &col)) {\r
- err = 1;\r
- fprintf(stderr, "%s: unable to parse colour \"%s\"\n",\r
- appname, val);\r
- } else {\r
- int index;\r
- index = (!strcmp(p, "-fg") ? 0 :\r
- !strcmp(p, "-bg") ? 2 :\r
- !strcmp(p, "-bfg") ? 1 :\r
- !strcmp(p, "-bbg") ? 3 :\r
- !strcmp(p, "-cfg") ? 4 :\r
- !strcmp(p, "-cbg") ? 5 : -1);\r
- assert(index != -1);\r
- cfg->colours[index][0] = col.red / 256;\r
- cfg->colours[index][1] = col.green / 256;\r
- cfg->colours[index][2] = col.blue / 256;\r
- }\r
-\r
- } else if (use_pty_argv && !strcmp(p, "-e")) {\r
- /* This option swallows all further arguments. */\r
- if (!do_everything)\r
- break;\r
-\r
- if (--argc > 0) {\r
- int i;\r
- pty_argv = snewn(argc+1, char *);\r
- ++argv;\r
- for (i = 0; i < argc; i++)\r
- pty_argv[i] = argv[i];\r
- pty_argv[argc] = NULL;\r
- break; /* finished command-line processing */\r
- } else\r
- err = 1, fprintf(stderr, "%s: -e expects an argument\n",\r
- appname);\r
-\r
- } else if (!strcmp(p, "-title")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->wintitle, val, sizeof(cfg->wintitle));\r
- cfg->wintitle[sizeof(cfg->wintitle)-1] = '\0';\r
-\r
- } else if (!strcmp(p, "-log")) {\r
- EXPECTS_ARG;\r
- SECOND_PASS_ONLY;\r
- strncpy(cfg->logfilename.path, val, sizeof(cfg->logfilename.path));\r
- cfg->logfilename.path[sizeof(cfg->logfilename.path)-1] = '\0';\r
- cfg->logtype = LGTYP_DEBUG;\r
-\r
- } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {\r
- SECOND_PASS_ONLY;\r
- cfg->stamp_utmp = 0;\r
-\r
- } else if (!strcmp(p, "-ut")) {\r
- SECOND_PASS_ONLY;\r
- cfg->stamp_utmp = 1;\r
-\r
- } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {\r
- SECOND_PASS_ONLY;\r
- cfg->login_shell = 0;\r
-\r
- } else if (!strcmp(p, "-ls")) {\r
- SECOND_PASS_ONLY;\r
- cfg->login_shell = 1;\r
-\r
- } else if (!strcmp(p, "-nethack")) {\r
- SECOND_PASS_ONLY;\r
- cfg->nethack_keypad = 1;\r
-\r
- } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {\r
- SECOND_PASS_ONLY;\r
- cfg->scrollbar = 0;\r
-\r
- } else if (!strcmp(p, "-sb")) {\r
- SECOND_PASS_ONLY;\r
- cfg->scrollbar = 0;\r
-\r
- } else if (!strcmp(p, "-name")) {\r
- EXPECTS_ARG;\r
- app_name = val;\r
-\r
- } else if (!strcmp(p, "-xrm")) {\r
- EXPECTS_ARG;\r
- provide_xrm_string(val);\r
-\r
- } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {\r
- help(stdout);\r
- exit(0);\r
-\r
- } else if (!strcmp(p, "-pgpfp")) {\r
- pgp_fingerprints();\r
- exit(1);\r
-\r
- } else if(p[0] != '-' && (!do_everything ||\r
- process_nonoption_arg(p, cfg,\r
- allow_launch))) {\r
- /* do nothing */\r
-\r
- } else {\r
- err = 1;\r
- fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);\r
- }\r
- }\r
-\r
- return err;\r
-}\r
-\r
-int uxsel_input_add(int fd, int rwx) {\r
- int flags = 0;\r
- if (rwx & 1) flags |= GDK_INPUT_READ;\r
- if (rwx & 2) flags |= GDK_INPUT_WRITE;\r
- if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;\r
- assert(flags);\r
- return gdk_input_add(fd, flags, fd_input_func, NULL);\r
-}\r
-\r
-void uxsel_input_remove(int id) {\r
- gdk_input_remove(id);\r
-}\r
-\r
-void setup_fonts_ucs(struct gui_data *inst)\r
-{\r
- if (inst->fonts[0])\r
- unifont_destroy(inst->fonts[0]);\r
- if (inst->fonts[1])\r
- unifont_destroy(inst->fonts[1]);\r
- if (inst->fonts[2])\r
- unifont_destroy(inst->fonts[2]);\r
- if (inst->fonts[3])\r
- unifont_destroy(inst->fonts[3]);\r
-\r
- inst->fonts[0] = unifont_create(inst->area, inst->cfg.font.name,\r
- FALSE, FALSE,\r
- inst->cfg.shadowboldoffset,\r
- inst->cfg.shadowbold);\r
- if (!inst->fonts[0]) {\r
- fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,\r
- inst->cfg.font.name);\r
- exit(1);\r
- }\r
-\r
- if (inst->cfg.shadowbold || !inst->cfg.boldfont.name[0]) {\r
- inst->fonts[1] = NULL;\r
- } else {\r
- inst->fonts[1] = unifont_create(inst->area, inst->cfg.boldfont.name,\r
- FALSE, TRUE,\r
- inst->cfg.shadowboldoffset,\r
- inst->cfg.shadowbold);\r
- if (!inst->fonts[1]) {\r
- fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,\r
- inst->cfg.boldfont.name);\r
- exit(1);\r
- }\r
- }\r
-\r
- if (inst->cfg.widefont.name[0]) {\r
- inst->fonts[2] = unifont_create(inst->area, inst->cfg.widefont.name,\r
- TRUE, FALSE,\r
- inst->cfg.shadowboldoffset,\r
- inst->cfg.shadowbold);\r
- if (!inst->fonts[2]) {\r
- fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,\r
- inst->cfg.widefont.name);\r
- exit(1);\r
- }\r
- } else {\r
- inst->fonts[2] = NULL;\r
- }\r
-\r
- if (inst->cfg.shadowbold || !inst->cfg.wideboldfont.name[0]) {\r
- inst->fonts[3] = NULL;\r
- } else {\r
- inst->fonts[3] = unifont_create(inst->area,\r
- inst->cfg.wideboldfont.name, TRUE,\r
- TRUE, inst->cfg.shadowboldoffset,\r
- inst->cfg.shadowbold);\r
- if (!inst->fonts[3]) {\r
- fprintf(stderr, "%s: unable to load wide bold font \"%s\"\n", appname,\r
- inst->cfg.boldfont.name);\r
- exit(1);\r
- }\r
- }\r
-\r
- inst->font_width = inst->fonts[0]->width;\r
- inst->font_height = inst->fonts[0]->height;\r
-\r
- inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage,\r
- inst->cfg.utf8_override,\r
- inst->fonts[0]->public_charset,\r
- inst->cfg.vtmode);\r
-}\r
-\r
-void set_geom_hints(struct gui_data *inst)\r
-{\r
- GdkGeometry geom;\r
- geom.min_width = inst->font_width + 2*inst->cfg.window_border;\r
- geom.min_height = inst->font_height + 2*inst->cfg.window_border;\r
- geom.max_width = geom.max_height = -1;\r
- geom.base_width = 2*inst->cfg.window_border;\r
- geom.base_height = 2*inst->cfg.window_border;\r
- geom.width_inc = inst->font_width;\r
- geom.height_inc = inst->font_height;\r
- geom.min_aspect = geom.max_aspect = 0;\r
- gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,\r
- GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |\r
- GDK_HINT_RESIZE_INC);\r
-}\r
-\r
-void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- term_clrsb(inst->term);\r
-}\r
-\r
-void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- term_pwron(inst->term, TRUE);\r
- if (inst->ldisc)\r
- ldisc_send(inst->ldisc, NULL, 0, 0);\r
-}\r
-\r
-void copy_all_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- term_copyall(inst->term);\r
-}\r
-\r
-void special_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- int code = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),\r
- "user-data"));\r
-\r
- if (inst->back)\r
- inst->back->special(inst->backhandle, code);\r
-}\r
-\r
-void about_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- about_box(inst->window);\r
-}\r
-\r
-void event_log_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- showeventlog(inst->eventlogstuff, inst->window);\r
-}\r
-\r
-void change_settings_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- /* This maps colour indices in inst->cfg to those used in inst->cols. */\r
- static const int ww[] = {\r
- 256, 257, 258, 259, 260, 261,\r
- 0, 8, 1, 9, 2, 10, 3, 11,\r
- 4, 12, 5, 13, 6, 14, 7, 15\r
- };\r
- struct gui_data *inst = (struct gui_data *)data;\r
- char *title = dupcat(appname, " Reconfiguration", NULL);\r
- Config cfg2, oldcfg;\r
- int i, need_size;\r
-\r
- assert(lenof(ww) == NCFGCOLOURS);\r
-\r
- if (inst->reconfiguring)\r
- return;\r
- else\r
- inst->reconfiguring = TRUE;\r
-\r
- cfg2 = inst->cfg; /* structure copy */\r
-\r
- if (do_config_box(title, &cfg2, 1,\r
- inst->back?inst->back->cfg_info(inst->backhandle):0)) {\r
-\r
- oldcfg = inst->cfg; /* structure copy */\r
- inst->cfg = cfg2; /* structure copy */\r
-\r
- /* Pass new config data to the logging module */\r
- log_reconfig(inst->logctx, &cfg2);\r
- /*\r
- * Flush the line discipline's edit buffer in the case\r
- * where local editing has just been disabled.\r
- */\r
- if (inst->ldisc)\r
- ldisc_send(inst->ldisc, NULL, 0, 0);\r
- /* Pass new config data to the terminal */\r
- term_reconfig(inst->term, &cfg2);\r
- /* Pass new config data to the back end */\r
- if (inst->back)\r
- inst->back->reconfig(inst->backhandle, &cfg2);\r
-\r
- /*\r
- * Just setting inst->cfg is sufficient to cause colour\r
- * setting changes to appear on the next ESC]R palette\r
- * reset. But we should also check whether any colour\r
- * settings have been changed, and revert the ones that\r
- * have to the new default, on the assumption that the user\r
- * is most likely to want an immediate update.\r
- */\r
- for (i = 0; i < NCFGCOLOURS; i++) {\r
- if (oldcfg.colours[i][0] != cfg2.colours[i][0] ||\r
- oldcfg.colours[i][1] != cfg2.colours[i][1] ||\r
- oldcfg.colours[i][2] != cfg2.colours[i][2]) {\r
- real_palette_set(inst, ww[i], cfg2.colours[i][0],\r
- cfg2.colours[i][1],\r
- cfg2.colours[i][2]);\r
-\r
- /*\r
- * If the default background has changed, we must\r
- * repaint the space in between the window border\r
- * and the text area.\r
- */\r
- if (i == 258) {\r
- set_window_background(inst);\r
- draw_backing_rect(inst);\r
- }\r
- }\r
- }\r
-\r
- /*\r
- * If the scrollbar needs to be shown, hidden, or moved\r
- * from one end to the other of the window, do so now.\r
- */\r
- if (oldcfg.scrollbar != cfg2.scrollbar) {\r
- if (cfg2.scrollbar)\r
- gtk_widget_show(inst->sbar);\r
- else\r
- gtk_widget_hide(inst->sbar);\r
- }\r
- if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) {\r
- gtk_box_reorder_child(inst->hbox, inst->sbar,\r
- cfg2.scrollbar_on_left ? 0 : 1);\r
- }\r
-\r
- /*\r
- * Change the window title, if required.\r
- */\r
- if (strcmp(oldcfg.wintitle, cfg2.wintitle))\r
- set_title(inst, cfg2.wintitle);\r
- set_window_titles(inst);\r
-\r
- /*\r
- * Redo the whole tangled fonts and Unicode mess if\r
- * necessary.\r
- */\r
- if (strcmp(oldcfg.font.name, cfg2.font.name) ||\r
- strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) ||\r
- strcmp(oldcfg.widefont.name, cfg2.widefont.name) ||\r
- strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) ||\r
- strcmp(oldcfg.line_codepage, cfg2.line_codepage) ||\r
- oldcfg.vtmode != cfg2.vtmode ||\r
- oldcfg.shadowbold != cfg2.shadowbold) {\r
- setup_fonts_ucs(inst);\r
- need_size = 1;\r
- } else\r
- need_size = 0;\r
-\r
- /*\r
- * Resize the window.\r
- */\r
- if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height ||\r
- oldcfg.window_border != cfg2.window_border || need_size) {\r
- set_geom_hints(inst);\r
- request_resize(inst, cfg2.width, cfg2.height);\r
- } else {\r
- /*\r
- * The above will have caused a call to term_size() for\r
- * us if it happened. If the user has fiddled with only\r
- * the scrollback size, the above will not have\r
- * happened and we will need an explicit term_size()\r
- * here.\r
- */\r
- if (oldcfg.savelines != cfg2.savelines)\r
- term_size(inst->term, inst->term->rows, inst->term->cols,\r
- cfg2.savelines);\r
- }\r
-\r
- term_invalidate(inst->term);\r
-\r
- /*\r
- * We do an explicit full redraw here to ensure the window\r
- * border has been redrawn as well as the text area.\r
- */\r
- gtk_widget_queue_draw(inst->area);\r
- }\r
- sfree(title);\r
- inst->reconfiguring = FALSE;\r
-}\r
-\r
-void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)\r
-{\r
- /*\r
- * Re-execing ourself is not an exact science under Unix. I do\r
- * the best I can by using /proc/self/exe if available and by\r
- * assuming argv[0] can be found on $PATH if not.\r
- * \r
- * Note that we also have to reconstruct the elements of the\r
- * original argv which gtk swallowed, since the user wants the\r
- * new session to appear on the same X display as the old one.\r
- */\r
- char **args;\r
- va_list ap;\r
- int i, n;\r
- int pid;\r
-\r
- /*\r
- * Collect the arguments with which to re-exec ourself.\r
- */\r
- va_start(ap, fd_to_close);\r
- n = 2; /* progname and terminating NULL */\r
- n += inst->ngtkargs;\r
- while (va_arg(ap, char *) != NULL)\r
- n++;\r
- va_end(ap);\r
-\r
- args = snewn(n, char *);\r
- args[0] = inst->progname;\r
- args[n-1] = NULL;\r
- for (i = 0; i < inst->ngtkargs; i++)\r
- args[i+1] = inst->gtkargvstart[i];\r
-\r
- i++;\r
- va_start(ap, fd_to_close);\r
- while ((args[i++] = va_arg(ap, char *)) != NULL);\r
- va_end(ap);\r
-\r
- assert(i == n);\r
-\r
- /*\r
- * Do the double fork.\r
- */\r
- pid = fork();\r
- if (pid < 0) {\r
- perror("fork");\r
- return;\r
- }\r
-\r
- if (pid == 0) {\r
- int pid2 = fork();\r
- if (pid2 < 0) {\r
- perror("fork");\r
- _exit(1);\r
- } else if (pid2 > 0) {\r
- /*\r
- * First child has successfully forked second child. My\r
- * Work Here Is Done. Note the use of _exit rather than\r
- * exit: the latter appears to cause destroy messages\r
- * to be sent to the X server. I suspect gtk uses\r
- * atexit.\r
- */\r
- _exit(0);\r
- }\r
-\r
- /*\r
- * If we reach here, we are the second child, so we now\r
- * actually perform the exec.\r
- */\r
- if (fd_to_close >= 0)\r
- close(fd_to_close);\r
-\r
- execv("/proc/self/exe", args);\r
- execvp(inst->progname, args);\r
- perror("exec");\r
- _exit(127);\r
-\r
- } else {\r
- int status;\r
- waitpid(pid, &status, 0);\r
- }\r
-\r
-}\r
-\r
-void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)\r
-{\r
- struct gui_data *inst = (struct gui_data *)gdata;\r
- /*\r
- * For this feature we must marshal cfg and (possibly) pty_argv\r
- * into a byte stream, create a pipe, and send this byte stream\r
- * to the child through the pipe.\r
- */\r
- int i, ret, size;\r
- char *data;\r
- char option[80];\r
- int pipefd[2];\r
-\r
- if (pipe(pipefd) < 0) {\r
- perror("pipe");\r
- return;\r
- }\r
-\r
- size = sizeof(inst->cfg);\r
- if (use_pty_argv && pty_argv) {\r
- for (i = 0; pty_argv[i]; i++)\r
- size += strlen(pty_argv[i]) + 1;\r
- }\r
-\r
- data = snewn(size, char);\r
- memcpy(data, &inst->cfg, sizeof(inst->cfg));\r
- if (use_pty_argv && pty_argv) {\r
- int p = sizeof(inst->cfg);\r
- for (i = 0; pty_argv[i]; i++) {\r
- strcpy(data + p, pty_argv[i]);\r
- p += strlen(pty_argv[i]) + 1;\r
- }\r
- assert(p == size);\r
- }\r
-\r
- sprintf(option, "---[%d,%d]", pipefd[0], size);\r
- fcntl(pipefd[0], F_SETFD, 0);\r
- fork_and_exec_self(inst, pipefd[1], option, NULL);\r
- close(pipefd[0]);\r
-\r
- i = ret = 0;\r
- while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)\r
- i += ret;\r
- if (ret < 0)\r
- perror("write to pipe");\r
- close(pipefd[1]);\r
- sfree(data);\r
-}\r
-\r
-int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg)\r
-{\r
- int fd, i, ret, size;\r
- char *data;\r
-\r
- if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {\r
- fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);\r
- exit(1);\r
- }\r
-\r
- data = snewn(size, char);\r
- i = ret = 0;\r
- while (i < size && (ret = read(fd, data + i, size - i)) > 0)\r
- i += ret;\r
- if (ret < 0) {\r
- perror("read from pipe");\r
- exit(1);\r
- } else if (i < size) {\r
- fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",\r
- appname);\r
- exit(1);\r
- }\r
-\r
- memcpy(cfg, data, sizeof(Config));\r
- if (use_pty_argv && size > sizeof(Config)) {\r
- int n = 0;\r
- i = sizeof(Config);\r
- while (i < size) {\r
- while (i < size && data[i]) i++;\r
- if (i >= size) {\r
- fprintf(stderr, "%s: malformed Duplicate Session data\n",\r
- appname);\r
- exit(1);\r
- }\r
- i++;\r
- n++;\r
- }\r
- pty_argv = snewn(n+1, char *);\r
- pty_argv[n] = NULL;\r
- n = 0;\r
- i = sizeof(Config);\r
- while (i < size) {\r
- char *p = data + i;\r
- while (i < size && data[i]) i++;\r
- assert(i < size);\r
- i++;\r
- pty_argv[n++] = dupstr(p);\r
- }\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-void new_session_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
-\r
- fork_and_exec_self(inst, -1, NULL);\r
-}\r
-\r
-void restart_session_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
-\r
- if (!inst->back) {\r
- logevent(inst, "----- Session restarted -----");\r
- term_pwron(inst->term, FALSE);\r
- start_backend(inst);\r
- inst->exited = FALSE;\r
- }\r
-}\r
-\r
-void saved_session_menuitem(GtkMenuItem *item, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- char *str = (char *)gtk_object_get_data(GTK_OBJECT(item), "user-data");\r
-\r
- fork_and_exec_self(inst, -1, "-load", str, NULL);\r
-}\r
-\r
-void saved_session_freedata(GtkMenuItem *item, gpointer data)\r
-{\r
- char *str = (char *)gtk_object_get_data(GTK_OBJECT(item), "user-data");\r
-\r
- sfree(str);\r
-}\r
-\r
-static void update_savedsess_menu(GtkMenuItem *menuitem, gpointer data)\r
-{\r
- struct gui_data *inst = (struct gui_data *)data;\r
- struct sesslist sesslist;\r
- int i;\r
-\r
- gtk_container_foreach(GTK_CONTAINER(inst->sessionsmenu),\r
- (GtkCallback)gtk_widget_destroy, NULL);\r
-\r
- get_sesslist(&sesslist, TRUE);\r
- /* skip sesslist.sessions[0] == Default Settings */\r
- for (i = 1; i < sesslist.nsessions; i++) {\r
- GtkWidget *menuitem =\r
- gtk_menu_item_new_with_label(sesslist.sessions[i]);\r
- gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);\r
- gtk_widget_show(menuitem);\r
- gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",\r
- dupstr(sesslist.sessions[i]));\r
- gtk_signal_connect(GTK_OBJECT(menuitem), "activate",\r
- GTK_SIGNAL_FUNC(saved_session_menuitem),\r
- inst);\r
- gtk_signal_connect(GTK_OBJECT(menuitem), "destroy",\r
- GTK_SIGNAL_FUNC(saved_session_freedata),\r
- inst);\r
- }\r
- if (sesslist.nsessions <= 1) {\r
- GtkWidget *menuitem =\r
- gtk_menu_item_new_with_label("(No sessions)");\r
- gtk_widget_set_sensitive(menuitem, FALSE);\r
- gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);\r
- gtk_widget_show(menuitem);\r
- }\r
- get_sesslist(&sesslist, FALSE); /* free up */\r
-}\r
-\r
-void set_window_icon(GtkWidget *window, const char *const *const *icon,\r
- int n_icon)\r
-{\r
- GdkPixmap *iconpm;\r
- GdkBitmap *iconmask;\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- GList *iconlist;\r
- int n;\r
-#endif\r
-\r
- if (!n_icon)\r
- return;\r
-\r
- gtk_widget_realize(window);\r
- iconpm = gdk_pixmap_create_from_xpm_d(window->window, &iconmask,\r
- NULL, (gchar **)icon[0]);\r
- gdk_window_set_icon(window->window, NULL, iconpm, iconmask);\r
-\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- iconlist = NULL;\r
- for (n = 0; n < n_icon; n++) {\r
- iconlist =\r
- g_list_append(iconlist,\r
- gdk_pixbuf_new_from_xpm_data((const gchar **)\r
- icon[n]));\r
- }\r
- gdk_window_set_icon_list(window->window, iconlist);\r
-#endif\r
-}\r
-\r
-void update_specials_menu(void *frontend)\r
-{\r
- struct gui_data *inst = (struct gui_data *)frontend;\r
-\r
- const struct telnet_special *specials;\r
-\r
- if (inst->back)\r
- specials = inst->back->get_specials(inst->backhandle);\r
- else\r
- specials = NULL;\r
-\r
- /* I believe this disposes of submenus too. */\r
- gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),\r
- (GtkCallback)gtk_widget_destroy, NULL);\r
- if (specials) {\r
- int i;\r
- GtkWidget *menu = inst->specialsmenu;\r
- /* A lame "stack" for submenus that will do for now. */\r
- GtkWidget *saved_menu = NULL;\r
- int nesting = 1;\r
- for (i = 0; nesting > 0; i++) {\r
- GtkWidget *menuitem = NULL;\r
- switch (specials[i].code) {\r
- case TS_SUBMENU:\r
- assert (nesting < 2);\r
- saved_menu = menu; /* XXX lame stacking */\r
- menu = gtk_menu_new();\r
- menuitem = gtk_menu_item_new_with_label(specials[i].name);\r
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);\r
- gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);\r
- gtk_widget_show(menuitem);\r
- menuitem = NULL;\r
- nesting++;\r
- break;\r
- case TS_EXITMENU:\r
- nesting--;\r
- if (nesting) {\r
- menu = saved_menu; /* XXX lame stacking */\r
- saved_menu = NULL;\r
- }\r
- break;\r
- case TS_SEP:\r
- menuitem = gtk_menu_item_new();\r
- break;\r
- default:\r
- menuitem = gtk_menu_item_new_with_label(specials[i].name);\r
- gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",\r
- GINT_TO_POINTER(specials[i].code));\r
- gtk_signal_connect(GTK_OBJECT(menuitem), "activate",\r
- GTK_SIGNAL_FUNC(special_menuitem), inst);\r
- break;\r
- }\r
- if (menuitem) {\r
- gtk_container_add(GTK_CONTAINER(menu), menuitem);\r
- gtk_widget_show(menuitem);\r
- }\r
- }\r
- gtk_widget_show(inst->specialsitem1);\r
- gtk_widget_show(inst->specialsitem2);\r
- } else {\r
- gtk_widget_hide(inst->specialsitem1);\r
- gtk_widget_hide(inst->specialsitem2);\r
- }\r
-}\r
-\r
-static void start_backend(struct gui_data *inst)\r
-{\r
- extern Backend *select_backend(Config *cfg);\r
- char *realhost;\r
- const char *error;\r
-\r
- inst->back = select_backend(&inst->cfg);\r
-\r
- error = inst->back->init((void *)inst, &inst->backhandle,\r
- &inst->cfg, inst->cfg.host, inst->cfg.port,\r
- &realhost, inst->cfg.tcp_nodelay,\r
- inst->cfg.tcp_keepalives);\r
-\r
- if (error) {\r
- char *msg = dupprintf("Unable to open connection to %s:\n%s",\r
- inst->cfg.host, error);\r
- inst->exited = TRUE;\r
- fatal_message_box(inst->window, msg);\r
- sfree(msg);\r
- exit(0);\r
- }\r
-\r
- if (inst->cfg.wintitle[0]) {\r
- set_title(inst, inst->cfg.wintitle);\r
- set_icon(inst, inst->cfg.wintitle);\r
- } else {\r
- char *title = make_default_wintitle(realhost);\r
- set_title(inst, title);\r
- set_icon(inst, title);\r
- sfree(title);\r
- }\r
- sfree(realhost);\r
-\r
- inst->back->provide_logctx(inst->backhandle, inst->logctx);\r
-\r
- term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);\r
-\r
- inst->ldisc =\r
- ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle,\r
- inst);\r
-\r
- gtk_widget_set_sensitive(inst->restartitem, FALSE);\r
-}\r
-\r
-int pt_main(int argc, char **argv)\r
-{\r
- extern int cfgbox(Config *cfg);\r
- struct gui_data *inst;\r
-\r
- /*\r
- * Create an instance structure and initialise to zeroes\r
- */\r
- inst = snew(struct gui_data);\r
- memset(inst, 0, sizeof(*inst));\r
- inst->alt_keycode = -1; /* this one needs _not_ to be zero */\r
- inst->busy_status = BUSY_NOT;\r
-\r
- /* defer any child exit handling until we're ready to deal with\r
- * it */\r
- block_signal(SIGCHLD, 1);\r
-\r
- inst->progname = argv[0];\r
- /*\r
- * Copy the original argv before letting gtk_init fiddle with\r
- * it. It will be required later.\r
- */\r
- {\r
- int i, oldargc;\r
- inst->gtkargvstart = snewn(argc-1, char *);\r
- for (i = 1; i < argc; i++)\r
- inst->gtkargvstart[i-1] = dupstr(argv[i]);\r
- oldargc = argc;\r
- gtk_init(&argc, &argv);\r
- inst->ngtkargs = oldargc - argc;\r
- }\r
-\r
- if (argc > 1 && !strncmp(argv[1], "---", 3)) {\r
- read_dupsession_data(inst, &inst->cfg, argv[1]);\r
- /* Splatter this argument so it doesn't clutter a ps listing */\r
- memset(argv[1], 0, strlen(argv[1]));\r
- } else {\r
- /* By default, we bring up the config dialog, rather than launching\r
- * a session. This gets set to TRUE if something happens to change\r
- * that (e.g., a hostname is specified on the command-line). */\r
- int allow_launch = FALSE;\r
- if (do_cmdline(argc, argv, 0, &allow_launch, inst, &inst->cfg))\r
- exit(1); /* pre-defaults pass to get -class */\r
- do_defaults(NULL, &inst->cfg);\r
- if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg))\r
- exit(1); /* post-defaults, do everything */\r
-\r
- cmdline_run_saved(&inst->cfg);\r
-\r
- if (loaded_session)\r
- allow_launch = TRUE;\r
-\r
- if ((!allow_launch || !cfg_launchable(&inst->cfg)) &&\r
- !cfgbox(&inst->cfg))\r
- exit(0); /* config box hit Cancel */\r
- }\r
-\r
- if (!compound_text_atom)\r
- compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);\r
- if (!utf8_string_atom)\r
- utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);\r
-\r
- inst->area = gtk_drawing_area_new();\r
-\r
- setup_fonts_ucs(inst);\r
- init_cutbuffers();\r
-\r
- inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);\r
- if (inst->cfg.winclass[0])\r
- gtk_window_set_wmclass(GTK_WINDOW(inst->window),\r
- inst->cfg.winclass, inst->cfg.winclass);\r
-\r
- /*\r
- * Set up the colour map.\r
- */\r
- palette_reset(inst);\r
-\r
- inst->width = inst->cfg.width;\r
- inst->height = inst->cfg.height;\r
-\r
- gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),\r
- inst->font_width * inst->cfg.width + 2*inst->cfg.window_border,\r
- inst->font_height * inst->cfg.height + 2*inst->cfg.window_border);\r
- inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));\r
- inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);\r
- inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));\r
- /*\r
- * We always create the scrollbar; it remains invisible if\r
- * unwanted, so we can pop it up quickly if it suddenly becomes\r
- * desirable.\r
- */\r
- if (inst->cfg.scrollbar_on_left)\r
- gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);\r
- gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);\r
- if (!inst->cfg.scrollbar_on_left)\r
- gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);\r
-\r
- gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));\r
-\r
- set_geom_hints(inst);\r
-\r
- gtk_widget_show(inst->area);\r
- if (inst->cfg.scrollbar)\r
- gtk_widget_show(inst->sbar);\r
- else\r
- gtk_widget_hide(inst->sbar);\r
- gtk_widget_show(GTK_WIDGET(inst->hbox));\r
-\r
- if (inst->gotpos) {\r
- int x = inst->xpos, y = inst->ypos;\r
- GtkRequisition req;\r
- gtk_widget_size_request(GTK_WIDGET(inst->window), &req);\r
- if (inst->gravity & 1) x += gdk_screen_width() - req.width;\r
- if (inst->gravity & 2) y += gdk_screen_height() - req.height;\r
- gtk_window_set_position(GTK_WINDOW(inst->window), GTK_WIN_POS_NONE);\r
- gtk_widget_set_uposition(GTK_WIDGET(inst->window), x, y);\r
- }\r
-\r
- gtk_signal_connect(GTK_OBJECT(inst->window), "destroy",\r
- GTK_SIGNAL_FUNC(destroy), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->window), "delete_event",\r
- GTK_SIGNAL_FUNC(delete_window), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->window), "key_press_event",\r
- GTK_SIGNAL_FUNC(key_event), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->window), "key_release_event",\r
- GTK_SIGNAL_FUNC(key_event), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->window), "focus_in_event",\r
- GTK_SIGNAL_FUNC(focus_event), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->window), "focus_out_event",\r
- GTK_SIGNAL_FUNC(focus_event), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "configure_event",\r
- GTK_SIGNAL_FUNC(configure_area), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event",\r
- GTK_SIGNAL_FUNC(expose_area), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "button_press_event",\r
- GTK_SIGNAL_FUNC(button_event), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "button_release_event",\r
- GTK_SIGNAL_FUNC(button_event), inst);\r
-#if GTK_CHECK_VERSION(2,0,0)\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "scroll_event",\r
- GTK_SIGNAL_FUNC(scroll_event), inst);\r
-#endif\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "motion_notify_event",\r
- GTK_SIGNAL_FUNC(motion_event), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received",\r
- GTK_SIGNAL_FUNC(selection_received), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get",\r
- GTK_SIGNAL_FUNC(selection_get), inst);\r
- gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",\r
- GTK_SIGNAL_FUNC(selection_clear), inst);\r
- if (inst->cfg.scrollbar)\r
- gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",\r
- GTK_SIGNAL_FUNC(scrollbar_moved), inst);\r
- gtk_widget_add_events(GTK_WIDGET(inst->area),\r
- GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |\r
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |\r
- GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK);\r
-\r
- {\r
- extern const char *const *const main_icon[];\r
- extern const int n_main_icon;\r
- set_window_icon(inst->window, main_icon, n_main_icon);\r
- }\r
-\r
- gtk_widget_show(inst->window);\r
-\r
- set_window_background(inst);\r
-\r
- /*\r
- * Set up the Ctrl+rightclick context menu.\r
- */\r
- {\r
- GtkWidget *menuitem;\r
- char *s;\r
- extern const int use_event_log, new_session, saved_sessions;\r
-\r
- inst->menu = gtk_menu_new();\r
-\r
-#define MKMENUITEM(title, func) do \\r
- { \\r
- menuitem = gtk_menu_item_new_with_label(title); \\r
- gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \\r
- gtk_widget_show(menuitem); \\r
- gtk_signal_connect(GTK_OBJECT(menuitem), "activate", \\r
- GTK_SIGNAL_FUNC(func), inst); \\r
- } while (0)\r
-\r
-#define MKSUBMENU(title) do \\r
- { \\r
- menuitem = gtk_menu_item_new_with_label(title); \\r
- gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \\r
- gtk_widget_show(menuitem); \\r
- } while (0)\r
-\r
-#define MKSEP() do \\r
- { \\r
- menuitem = gtk_menu_item_new(); \\r
- gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \\r
- gtk_widget_show(menuitem); \\r
- } while (0)\r
-\r
- if (new_session)\r
- MKMENUITEM("New Session...", new_session_menuitem);\r
- MKMENUITEM("Restart Session", restart_session_menuitem);\r
- inst->restartitem = menuitem;\r
- gtk_widget_set_sensitive(inst->restartitem, FALSE);\r
- MKMENUITEM("Duplicate Session", dup_session_menuitem);\r
- if (saved_sessions) {\r
- inst->sessionsmenu = gtk_menu_new();\r
- /* sessionsmenu will be updated when it's invoked */\r
- /* XXX is this the right way to do dynamic menus in Gtk? */\r
- MKMENUITEM("Saved Sessions", update_savedsess_menu);\r
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),\r
- inst->sessionsmenu);\r
- }\r
- MKSEP();\r
- MKMENUITEM("Change Settings...", change_settings_menuitem);\r
- MKSEP();\r
- if (use_event_log)\r
- MKMENUITEM("Event Log", event_log_menuitem);\r
- MKSUBMENU("Special Commands");\r
- inst->specialsmenu = gtk_menu_new();\r
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->specialsmenu);\r
- inst->specialsitem1 = menuitem;\r
- MKSEP();\r
- inst->specialsitem2 = menuitem;\r
- gtk_widget_hide(inst->specialsitem1);\r
- gtk_widget_hide(inst->specialsitem2);\r
- MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);\r
- MKMENUITEM("Reset Terminal", reset_terminal_menuitem);\r
- MKMENUITEM("Copy All", copy_all_menuitem);\r
- MKSEP();\r
- s = dupcat("About ", appname, NULL);\r
- MKMENUITEM(s, about_menuitem);\r
- sfree(s);\r
-#undef MKMENUITEM\r
-#undef MKSUBMENU\r
-#undef MKSEP\r
- }\r
-\r
- inst->textcursor = make_mouse_ptr(inst, GDK_XTERM);\r
- inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR);\r
- inst->waitcursor = make_mouse_ptr(inst, GDK_WATCH);\r
- inst->blankcursor = make_mouse_ptr(inst, -1);\r
- make_mouse_ptr(inst, -2); /* clean up cursor font */\r
- inst->currcursor = inst->textcursor;\r
- show_mouseptr(inst, 1);\r
-\r
- inst->eventlogstuff = eventlogstuff_new();\r
-\r
- inst->term = term_init(&inst->cfg, &inst->ucsdata, inst);\r
- inst->logctx = log_init(inst, &inst->cfg);\r
- term_provide_logctx(inst->term, inst->logctx);\r
-\r
- uxsel_init();\r
-\r
- term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines);\r
-\r
- start_backend(inst);\r
-\r
- ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */\r
-\r
- /* now we're reday to deal with the child exit handler being\r
- * called */\r
- block_signal(SIGCHLD, 0);\r
-\r
- /*\r
- * Block SIGPIPE: if we attempt Duplicate Session or similar\r
- * and it falls over in some way, we certainly don't want\r
- * SIGPIPE terminating the main pterm/PuTTY. Note that we do\r
- * this _after_ (at least pterm) forks off its child process,\r
- * since the child wants SIGPIPE handled in the usual way.\r
- */\r
- block_signal(SIGPIPE, 1);\r
-\r
- inst->exited = FALSE;\r
-\r
- gtk_main();\r
-\r
- return 0;\r
-}\r