]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkwin.c
2ec84d57f513caadb1c4db1d4243d5d076fbd4ea
[PuTTY.git] / unix / gtkwin.c
1 /*
2  * gtkwin.c: the main code that runs a PuTTY terminal emulator and
3  * backend in a GTK window.
4  */
5
6 #define _GNU_SOURCE
7
8 #include <string.h>
9 #include <assert.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <time.h>
15 #include <errno.h>
16 #include <locale.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <gtk/gtk.h>
22 #if !GTK_CHECK_VERSION(3,0,0)
23 #include <gdk/gdkkeysyms.h>
24 #endif
25 #ifndef NOT_X_WINDOWS
26 #include <gdk/gdkx.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/Xatom.h>
30 #endif
31
32 #if GTK_CHECK_VERSION(2,0,0)
33 #include <gtk/gtkimmodule.h>
34 #endif
35
36 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
37
38 #define MAY_REFER_TO_GTK_IN_HEADERS
39
40 #include "putty.h"
41 #include "terminal.h"
42 #include "gtkcompat.h"
43 #include "gtkfont.h"
44
45 #define CAT2(x,y) x ## y
46 #define CAT(x,y) CAT2(x,y)
47 #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
48
49 #if GTK_CHECK_VERSION(2,0,0)
50 ASSERT(sizeof(long) <= sizeof(gsize));
51 #define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
52 #define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
53 #else /* Gtk 1.2 */
54 ASSERT(sizeof(long) <= sizeof(gpointer));
55 #define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
56 #define GPOINTER_TO_LONG(p) ((long)(p))
57 #endif
58
59 /* Colours come in two flavours: configurable, and xterm-extended. */
60 #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
61 #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
62
63 GdkAtom compound_text_atom, utf8_string_atom;
64
65 extern char **pty_argv;        /* declared in pty.c */
66 extern int use_pty_argv;
67
68 /*
69  * Timers are global across all sessions (even if we were handling
70  * multiple sessions, which we aren't), so the current timer ID is
71  * a global variable.
72  */
73 static guint timer_id = 0;
74
75 struct gui_data {
76     GtkWidget *window, *area, *sbar;
77     GtkBox *hbox;
78     GtkAdjustment *sbar_adjust;
79     GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2,
80         *restartitem;
81     GtkWidget *sessionsmenu;
82 #ifndef NO_BACKING_PIXMAPS
83     GdkPixmap *pixmap;
84 #endif
85 #if GTK_CHECK_VERSION(2,0,0)
86     GtkIMContext *imc;
87 #endif
88     unifont *fonts[4];                 /* normal, bold, wide, widebold */
89 #if GTK_CHECK_VERSION(2,0,0)
90     const char *geometry;
91 #else
92     int xpos, ypos, gotpos, gravity;
93 #endif
94     GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor;
95     GdkColor cols[NALLCOLOURS];
96     GdkColormap *colmap;
97     wchar_t *pastein_data;
98     int direct_to_font;
99     int pastein_data_len;
100     char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8;
101     int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len;
102     int font_width, font_height;
103     int width, height;
104     int ignore_sbar;
105     int mouseptr_visible;
106     int busy_status;
107     guint toplevel_callback_idle_id;
108     int idle_fn_scheduled, quit_fn_scheduled;
109     int alt_keycode;
110     int alt_digits;
111     char *wintitle;
112     char *icontitle;
113     int master_fd, master_func_id;
114     void *ldisc;
115     Backend *back;
116     void *backhandle;
117     Terminal *term;
118     void *logctx;
119     int exited;
120     struct unicode_data ucsdata;
121     Conf *conf;
122     void *eventlogstuff;
123     char *progname, **gtkargvstart;
124     int ngtkargs;
125     guint32 input_event_time; /* Timestamp of the most recent input event. */
126     int reconfiguring;
127     /* Cached things out of conf that we refer to a lot */
128     int bold_style;
129     int window_border;
130     int cursor_type;
131     int drawtype;
132 };
133
134 static void cache_conf_values(struct gui_data *inst)
135 {
136     inst->bold_style = conf_get_int(inst->conf, CONF_bold_style);
137     inst->window_border = conf_get_int(inst->conf, CONF_window_border);
138     inst->cursor_type = conf_get_int(inst->conf, CONF_cursor_type);
139 }
140
141 struct draw_ctx {
142     struct gui_data *inst;
143     unifont_drawctx uctx;
144 };
145
146 static int send_raw_mouse;
147
148 static const char *app_name = "pterm";
149
150 static void start_backend(struct gui_data *inst);
151 static void exit_callback(void *vinst);
152
153 char *x_get_default(const char *key)
154 {
155 #ifndef NOT_X_WINDOWS
156     return XGetDefault(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
157                        app_name, key);
158 #else
159     return NULL;
160 #endif
161 }
162
163 void connection_fatal(void *frontend, const char *p, ...)
164 {
165     struct gui_data *inst = (struct gui_data *)frontend;
166
167     va_list ap;
168     char *msg;
169     va_start(ap, p);
170     msg = dupvprintf(p, ap);
171     va_end(ap);
172     fatal_message_box(inst->window, msg);
173     sfree(msg);
174
175     queue_toplevel_callback(exit_callback, inst);
176 }
177
178 /*
179  * Default settings that are specific to pterm.
180  */
181 FontSpec *platform_default_fontspec(const char *name)
182 {
183     if (!strcmp(name, "Font"))
184         return fontspec_new("server:fixed");
185     else
186         return fontspec_new("");
187 }
188
189 Filename *platform_default_filename(const char *name)
190 {
191     if (!strcmp(name, "LogFileName"))
192         return filename_from_str("putty.log");
193     else
194         return filename_from_str("");
195 }
196
197 char *platform_default_s(const char *name)
198 {
199     if (!strcmp(name, "SerialLine"))
200         return dupstr("/dev/ttyS0");
201     return NULL;
202 }
203
204 int platform_default_i(const char *name, int def)
205 {
206     if (!strcmp(name, "CloseOnExit"))
207         return 2;  /* maps to FORCE_ON after painful rearrangement :-( */
208     if (!strcmp(name, "WinNameAlways"))
209         return 0;  /* X natively supports icon titles, so use 'em by default */
210     return def;
211 }
212
213 /* Dummy routine, only required in plink. */
214 void frontend_echoedit_update(void *frontend, int echo, int edit)
215 {
216 }
217
218 char *get_ttymode(void *frontend, const char *mode)
219 {
220     struct gui_data *inst = (struct gui_data *)frontend;
221     return term_get_ttymode(inst->term, mode);
222 }
223
224 int from_backend(void *frontend, int is_stderr, const char *data, int len)
225 {
226     struct gui_data *inst = (struct gui_data *)frontend;
227     return term_data(inst->term, is_stderr, data, len);
228 }
229
230 int from_backend_untrusted(void *frontend, const char *data, int len)
231 {
232     struct gui_data *inst = (struct gui_data *)frontend;
233     return term_data_untrusted(inst->term, data, len);
234 }
235
236 int from_backend_eof(void *frontend)
237 {
238     return TRUE;   /* do respond to incoming EOF with outgoing */
239 }
240
241 int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen)
242 {
243     struct gui_data *inst = (struct gui_data *)p->frontend;
244     int ret;
245     ret = cmdline_get_passwd_input(p, in, inlen);
246     if (ret == -1)
247         ret = term_get_userpass_input(inst->term, p, in, inlen);
248     return ret;
249 }
250
251 void logevent(void *frontend, const char *string)
252 {
253     struct gui_data *inst = (struct gui_data *)frontend;
254
255     log_eventlog(inst->logctx, string);
256
257     logevent_dlg(inst->eventlogstuff, string);
258 }
259
260 int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
261 {
262     struct gui_data *inst = (struct gui_data *)frontend;
263
264     if (which)
265         return inst->font_height;
266     else
267         return inst->font_width;
268 }
269
270 /*
271  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
272  * into a cooked one (SELECT, EXTEND, PASTE).
273  * 
274  * In Unix, this is not configurable; the X button arrangement is
275  * rock-solid across all applications, everyone has a three-button
276  * mouse or a means of faking it, and there is no need to switch
277  * buttons around at all.
278  */
279 static Mouse_Button translate_button(Mouse_Button button)
280 {
281     /* struct gui_data *inst = (struct gui_data *)frontend; */
282
283     if (button == MBT_LEFT)
284         return MBT_SELECT;
285     if (button == MBT_MIDDLE)
286         return MBT_PASTE;
287     if (button == MBT_RIGHT)
288         return MBT_EXTEND;
289     return 0;                          /* shouldn't happen */
290 }
291
292 /*
293  * Return the top-level GtkWindow associated with a particular
294  * front end instance.
295  */
296 void *get_window(void *frontend)
297 {
298     struct gui_data *inst = (struct gui_data *)frontend;
299     return inst->window;
300 }
301
302 /*
303  * Minimise or restore the window in response to a server-side
304  * request.
305  */
306 void set_iconic(void *frontend, int iconic)
307 {
308     /*
309      * GTK 1.2 doesn't know how to do this.
310      */
311 #if GTK_CHECK_VERSION(2,0,0)
312     struct gui_data *inst = (struct gui_data *)frontend;
313     if (iconic)
314         gtk_window_iconify(GTK_WINDOW(inst->window));
315     else
316         gtk_window_deiconify(GTK_WINDOW(inst->window));
317 #endif
318 }
319
320 /*
321  * Move the window in response to a server-side request.
322  */
323 void move_window(void *frontend, int x, int y)
324 {
325     struct gui_data *inst = (struct gui_data *)frontend;
326     /*
327      * I assume that when the GTK version of this call is available
328      * we should use it. Not sure how it differs from the GDK one,
329      * though.
330      */
331 #if GTK_CHECK_VERSION(2,0,0)
332     gtk_window_move(GTK_WINDOW(inst->window), x, y);
333 #else
334     gdk_window_move(gtk_widget_get_window(inst->window), x, y);
335 #endif
336 }
337
338 /*
339  * Move the window to the top or bottom of the z-order in response
340  * to a server-side request.
341  */
342 void set_zorder(void *frontend, int top)
343 {
344     struct gui_data *inst = (struct gui_data *)frontend;
345     if (top)
346         gdk_window_raise(gtk_widget_get_window(inst->window));
347     else
348         gdk_window_lower(gtk_widget_get_window(inst->window));
349 }
350
351 /*
352  * Refresh the window in response to a server-side request.
353  */
354 void refresh_window(void *frontend)
355 {
356     struct gui_data *inst = (struct gui_data *)frontend;
357     term_invalidate(inst->term);
358 }
359
360 /*
361  * Maximise or restore the window in response to a server-side
362  * request.
363  */
364 void set_zoomed(void *frontend, int zoomed)
365 {
366     /*
367      * GTK 1.2 doesn't know how to do this.
368      */
369 #if GTK_CHECK_VERSION(2,0,0)
370     struct gui_data *inst = (struct gui_data *)frontend;
371     if (zoomed)
372         gtk_window_maximize(GTK_WINDOW(inst->window));
373     else
374         gtk_window_unmaximize(GTK_WINDOW(inst->window));
375 #endif
376 }
377
378 /*
379  * Report whether the window is iconic, for terminal reports.
380  */
381 int is_iconic(void *frontend)
382 {
383     struct gui_data *inst = (struct gui_data *)frontend;
384     return !gdk_window_is_viewable(gtk_widget_get_window(inst->window));
385 }
386
387 /*
388  * Report the window's position, for terminal reports.
389  */
390 void get_window_pos(void *frontend, int *x, int *y)
391 {
392     struct gui_data *inst = (struct gui_data *)frontend;
393     /*
394      * I assume that when the GTK version of this call is available
395      * we should use it. Not sure how it differs from the GDK one,
396      * though.
397      */
398 #if GTK_CHECK_VERSION(2,0,0)
399     gtk_window_get_position(GTK_WINDOW(inst->window), x, y);
400 #else
401     gdk_window_get_position(gtk_widget_get_window(inst->window), x, y);
402 #endif
403 }
404
405 /*
406  * Report the window's pixel size, for terminal reports.
407  */
408 void get_window_pixels(void *frontend, int *x, int *y)
409 {
410     struct gui_data *inst = (struct gui_data *)frontend;
411     /*
412      * I assume that when the GTK version of this call is available
413      * we should use it. Not sure how it differs from the GDK one,
414      * though.
415      */
416 #if GTK_CHECK_VERSION(2,0,0)
417     gtk_window_get_size(GTK_WINDOW(inst->window), x, y);
418 #else
419     gdk_window_get_size(gtk_widget_get_window(inst->window), x, y);
420 #endif
421 }
422
423 /*
424  * Return the window or icon title.
425  */
426 char *get_window_title(void *frontend, int icon)
427 {
428     struct gui_data *inst = (struct gui_data *)frontend;
429     return icon ? inst->icontitle : inst->wintitle;
430 }
431
432 gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
433 {
434     struct gui_data *inst = (struct gui_data *)data;
435     if (!inst->exited && conf_get_int(inst->conf, CONF_warn_on_close)) {
436         if (!reallyclose(inst))
437             return TRUE;
438     }
439     return FALSE;
440 }
441
442 static void update_mouseptr(struct gui_data *inst)
443 {
444     switch (inst->busy_status) {
445       case BUSY_NOT:
446         if (!inst->mouseptr_visible) {
447             gdk_window_set_cursor(gtk_widget_get_window(inst->area),
448                                   inst->blankcursor);
449         } else if (send_raw_mouse) {
450             gdk_window_set_cursor(gtk_widget_get_window(inst->area),
451                                   inst->rawcursor);
452         } else {
453             gdk_window_set_cursor(gtk_widget_get_window(inst->area),
454                                   inst->textcursor);
455         }
456         break;
457       case BUSY_WAITING:    /* XXX can we do better? */
458       case BUSY_CPU:
459         /* We always display these cursors. */
460         gdk_window_set_cursor(gtk_widget_get_window(inst->area),
461                               inst->waitcursor);
462         break;
463       default:
464         assert(0);
465     }
466 }
467
468 static void show_mouseptr(struct gui_data *inst, int show)
469 {
470     if (!conf_get_int(inst->conf, CONF_hide_mouseptr))
471         show = 1;
472     inst->mouseptr_visible = show;
473     update_mouseptr(inst);
474 }
475
476 static void draw_backing_rect(struct gui_data *inst);
477
478 gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
479 {
480     struct gui_data *inst = (struct gui_data *)data;
481     int w, h, need_size = 0;
482
483     /*
484      * See if the terminal size has changed, in which case we must
485      * let the terminal know.
486      */
487     w = (event->width - 2*inst->window_border) / inst->font_width;
488     h = (event->height - 2*inst->window_border) / inst->font_height;
489     if (w != inst->width || h != inst->height) {
490         inst->width = w;
491         inst->height = h;
492         conf_set_int(inst->conf, CONF_width, inst->width);
493         conf_set_int(inst->conf, CONF_height, inst->height);
494         need_size = 1;
495     }
496
497 #ifndef NO_BACKING_PIXMAPS
498     if (inst->pixmap) {
499         gdk_pixmap_unref(inst->pixmap);
500         inst->pixmap = NULL;
501     }
502
503     inst->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
504                                   (w * inst->font_width + 2*inst->window_border),
505                                   (h * inst->font_height + 2*inst->window_border), -1);
506 #endif
507
508     draw_backing_rect(inst);
509
510     if (need_size && inst->term) {
511         term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines));
512     }
513
514     if (inst->term)
515         term_invalidate(inst->term);
516
517 #if GTK_CHECK_VERSION(2,0,0)
518     gtk_im_context_set_client_window(inst->imc, gtk_widget_get_window(widget));
519 #endif
520
521     return TRUE;
522 }
523
524 #ifdef DRAW_TEXT_CAIRO
525 static void cairo_setup_dctx(struct draw_ctx *dctx)
526 {
527     cairo_get_matrix(dctx->uctx.u.cairo.cr,
528                      &dctx->uctx.u.cairo.origmatrix);
529     cairo_set_line_width(dctx->uctx.u.cairo.cr, 1.0);
530     cairo_set_line_cap(dctx->uctx.u.cairo.cr, CAIRO_LINE_CAP_SQUARE);
531     cairo_set_line_join(dctx->uctx.u.cairo.cr, CAIRO_LINE_JOIN_MITER);
532     /* This antialiasing setting appears to be ignored for Pango
533      * font rendering but honoured for stroking and filling paths;
534      * I don't quite understand the logic of that, but I won't
535      * complain since it's exactly what I happen to want */
536     cairo_set_antialias(dctx->uctx.u.cairo.cr, CAIRO_ANTIALIAS_NONE);
537 }
538 #endif
539
540 #if GTK_CHECK_VERSION(3,0,0)
541 static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
542 {
543     struct gui_data *inst = (struct gui_data *)data;
544
545     if (inst->term) {
546         struct draw_ctx adctx, *dctx = &adctx;
547         GdkRectangle dirtyrect;
548
549         dctx->inst = inst;
550         dctx->uctx.type = DRAWTYPE_CAIRO;
551         dctx->uctx.u.cairo.widget = widget;
552         dctx->uctx.u.cairo.cr = cr;
553         cairo_setup_dctx(dctx);
554
555         gdk_cairo_get_clip_rectangle(cr, &dirtyrect);
556
557         /*
558          * As in window.c, we clear the 'immediately' flag in the
559          * term_paint() call if the terminal has an update pending, in
560          * case we're constrained within this event to only draw on
561          * the exposed rectangle of the window. (Because if the whole
562          * of a character cell needs a redraw due to a terminal
563          * contents change, the last thing we want is to give it a
564          * _partial_ redraw here due to system-imposed clipping, and
565          * then have the next main terminal update believe it's been
566          * redrawn in full.)
567          *
568          * I don't actually know if GTK draw events will constrain us
569          * in this way, but it's best to be careful...
570          */
571         term_paint(inst->term, dctx,
572                    (dirtyrect.x - inst->window_border) / inst->font_width,
573                    (dirtyrect.y - inst->window_border) / inst->font_height,
574                    (dirtyrect.x + dirtyrect.width -
575                     inst->window_border) / inst->font_width,
576                    (dirtyrect.y + dirtyrect.height -
577                     inst->window_border) / inst->font_height,
578                    !inst->term->window_update_pending);
579     }
580
581     return TRUE;
582 }
583 #else
584 gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
585 {
586     struct gui_data *inst = (struct gui_data *)data;
587
588 #ifndef NO_BACKING_PIXMAPS
589     /*
590      * Pass the exposed rectangle to terminal.c, which will call us
591      * back to do the actual painting.
592      */
593     if (inst->pixmap) {
594         gdk_draw_pixmap(gtk_widget_get_window(widget),
595                         (gtk_widget_get_style(widget)->fg_gc
596                          [gtk_widget_get_state(widget)]),
597                         inst->pixmap,
598                         event->area.x, event->area.y,
599                         event->area.x, event->area.y,
600                         event->area.width, event->area.height);
601     }
602 #else
603     if (inst->term) {
604         Context ctx = get_ctx(inst);
605         term_paint(inst->term, ctx,
606                    (event->area.x - inst->window_border) / inst->font_width,
607                    (event->area.y - inst->window_border) / inst->font_height,
608                    (event->area.x + event->area.width -
609                     inst->window_border) / inst->font_width,
610                    (event->area.y + event->area.height -
611                     inst->window_border) / inst->font_height,
612                    !inst->term->window_update_pending);
613         free_ctx(ctx);
614     }
615 #endif
616
617     return TRUE;
618 }
619 #endif
620
621 #define KEY_PRESSED(k) \
622     (inst->keystate[(k) / 32] & (1 << ((k) % 32)))
623
624 gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
625 {
626     struct gui_data *inst = (struct gui_data *)data;
627     char output[256];
628     wchar_t ucsoutput[2];
629     int ucsval, start, end, special, output_charset, use_ucsoutput;
630     int nethack_mode, app_keypad_mode;
631
632     /* Remember the timestamp. */
633     inst->input_event_time = event->time;
634
635     /* By default, nothing is generated. */
636     end = start = 0;
637     special = use_ucsoutput = FALSE;
638     output_charset = CS_ISO8859_1;
639
640     /*
641      * If Alt is being released after typing an Alt+numberpad
642      * sequence, we should generate the code that was typed.
643      * 
644      * Note that we only do this if more than one key was actually
645      * pressed - I don't think Alt+NumPad4 should be ^D or that
646      * Alt+NumPad3 should be ^C, for example. There's no serious
647      * inconvenience in having to type a zero before a single-digit
648      * character code.
649      */
650     if (event->type == GDK_KEY_RELEASE) {
651         if ((event->keyval == GDK_KEY_Meta_L ||
652              event->keyval == GDK_KEY_Meta_R ||
653              event->keyval == GDK_KEY_Alt_L ||
654              event->keyval == GDK_KEY_Alt_R) &&
655             inst->alt_keycode >= 0 && inst->alt_digits > 1) {
656 #ifdef KEY_DEBUGGING
657             printf("Alt key up, keycode = %d\n", inst->alt_keycode);
658 #endif
659             /*
660              * FIXME: we might usefully try to do something clever here
661              * about interpreting the generated key code in a way that's
662              * appropriate to the line code page.
663              */
664             output[0] = inst->alt_keycode;
665             end = 1;
666             goto done;
667         }
668 #if GTK_CHECK_VERSION(2,0,0)
669         if (gtk_im_context_filter_keypress(inst->imc, event))
670             return TRUE;
671 #endif
672     }
673
674     if (event->type == GDK_KEY_PRESS) {
675 #ifdef KEY_DEBUGGING
676         {
677             int i;
678             printf("keypress: keyval = %04x, state = %08x; string =",
679                    event->keyval, event->state);
680             for (i = 0; event->string[i]; i++)
681                 printf(" %02x", (unsigned char) event->string[i]);
682             printf("\n");
683         }
684 #endif
685
686         /*
687          * NYI: Compose key (!!! requires Unicode faff before even trying)
688          */
689
690         /*
691          * If Alt has just been pressed, we start potentially
692          * accumulating an Alt+numberpad code. We do this by
693          * setting alt_keycode to -1 (nothing yet but plausible).
694          */
695         if ((event->keyval == GDK_KEY_Meta_L ||
696              event->keyval == GDK_KEY_Meta_R ||
697              event->keyval == GDK_KEY_Alt_L ||
698              event->keyval == GDK_KEY_Alt_R)) {
699             inst->alt_keycode = -1;
700             inst->alt_digits = 0;
701             goto done;                 /* this generates nothing else */
702         }
703
704         /*
705          * If we're seeing a numberpad key press with Mod1 down,
706          * consider adding it to alt_keycode if that's sensible.
707          * Anything _else_ with Mod1 down cancels any possibility
708          * of an ALT keycode: we set alt_keycode to -2.
709          */
710         if ((event->state & GDK_MOD1_MASK) && inst->alt_keycode != -2) {
711             int digit = -1;
712             switch (event->keyval) {
713               case GDK_KEY_KP_0: case GDK_KEY_KP_Insert: digit = 0; break;
714               case GDK_KEY_KP_1: case GDK_KEY_KP_End: digit = 1; break;
715               case GDK_KEY_KP_2: case GDK_KEY_KP_Down: digit = 2; break;
716               case GDK_KEY_KP_3: case GDK_KEY_KP_Page_Down: digit = 3; break;
717               case GDK_KEY_KP_4: case GDK_KEY_KP_Left: digit = 4; break;
718               case GDK_KEY_KP_5: case GDK_KEY_KP_Begin: digit = 5; break;
719               case GDK_KEY_KP_6: case GDK_KEY_KP_Right: digit = 6; break;
720               case GDK_KEY_KP_7: case GDK_KEY_KP_Home: digit = 7; break;
721               case GDK_KEY_KP_8: case GDK_KEY_KP_Up: digit = 8; break;
722               case GDK_KEY_KP_9: case GDK_KEY_KP_Page_Up: digit = 9; break;
723             }
724             if (digit < 0)
725                 inst->alt_keycode = -2;   /* it's invalid */
726             else {
727 #ifdef KEY_DEBUGGING
728                 printf("Adding digit %d to keycode %d", digit,
729                        inst->alt_keycode);
730 #endif
731                 if (inst->alt_keycode == -1)
732                     inst->alt_keycode = digit;   /* one-digit code */
733                 else
734                     inst->alt_keycode = inst->alt_keycode * 10 + digit;
735                 inst->alt_digits++;
736 #ifdef KEY_DEBUGGING
737                 printf(" gives new code %d\n", inst->alt_keycode);
738 #endif
739                 /* Having used this digit, we now do nothing more with it. */
740                 goto done;
741             }
742         }
743
744         /*
745          * Shift-PgUp and Shift-PgDn don't even generate keystrokes
746          * at all.
747          */
748         if (event->keyval == GDK_KEY_Page_Up &&
749             (event->state & GDK_SHIFT_MASK)) {
750             term_scroll(inst->term, 0, -inst->height/2);
751             return TRUE;
752         }
753         if (event->keyval == GDK_KEY_Page_Up &&
754             (event->state & GDK_CONTROL_MASK)) {
755             term_scroll(inst->term, 0, -1);
756             return TRUE;
757         }
758         if (event->keyval == GDK_KEY_Page_Down &&
759             (event->state & GDK_SHIFT_MASK)) {
760             term_scroll(inst->term, 0, +inst->height/2);
761             return TRUE;
762         }
763         if (event->keyval == GDK_KEY_Page_Down &&
764             (event->state & GDK_CONTROL_MASK)) {
765             term_scroll(inst->term, 0, +1);
766             return TRUE;
767         }
768
769         /*
770          * Neither does Shift-Ins.
771          */
772         if (event->keyval == GDK_KEY_Insert &&
773             (event->state & GDK_SHIFT_MASK)) {
774             request_paste(inst);
775             return TRUE;
776         }
777
778         special = FALSE;
779         use_ucsoutput = FALSE;
780
781         nethack_mode = conf_get_int(inst->conf, CONF_nethack_keypad);
782         app_keypad_mode = (inst->term->app_keypad_keys &&
783                            !conf_get_int(inst->conf, CONF_no_applic_k));
784
785         /* ALT+things gives leading Escape. */
786         output[0] = '\033';
787 #if !GTK_CHECK_VERSION(2,0,0)
788         /*
789          * In vanilla X, and hence also GDK 1.2, the string received
790          * as part of a keyboard event is assumed to be in
791          * ISO-8859-1. (Seems woefully shortsighted in i18n terms,
792          * but it's true: see the man page for XLookupString(3) for
793          * confirmation.)
794          */
795         output_charset = CS_ISO8859_1;
796         strncpy(output+1, event->string, lenof(output)-1);
797 #else
798         /*
799          * Most things can now be passed to
800          * gtk_im_context_filter_keypress without breaking anything
801          * below this point. An exception is the numeric keypad if
802          * we're in Nethack or application mode: the IM will eat
803          * numeric keypad presses if Num Lock is on, but we don't want
804          * it to.
805          */
806         if (app_keypad_mode &&
807             (event->keyval == GDK_KEY_Num_Lock ||
808              event->keyval == GDK_KEY_KP_Divide ||
809              event->keyval == GDK_KEY_KP_Multiply ||
810              event->keyval == GDK_KEY_KP_Subtract ||
811              event->keyval == GDK_KEY_KP_Add ||
812              event->keyval == GDK_KEY_KP_Enter ||
813              event->keyval == GDK_KEY_KP_0 ||
814              event->keyval == GDK_KEY_KP_Insert ||
815              event->keyval == GDK_KEY_KP_1 ||
816              event->keyval == GDK_KEY_KP_End ||
817              event->keyval == GDK_KEY_KP_2 ||
818              event->keyval == GDK_KEY_KP_Down ||
819              event->keyval == GDK_KEY_KP_3 ||
820              event->keyval == GDK_KEY_KP_Page_Down ||
821              event->keyval == GDK_KEY_KP_4 ||
822              event->keyval == GDK_KEY_KP_Left ||
823              event->keyval == GDK_KEY_KP_5 ||
824              event->keyval == GDK_KEY_KP_Begin ||
825              event->keyval == GDK_KEY_KP_6 ||
826              event->keyval == GDK_KEY_KP_Right ||
827              event->keyval == GDK_KEY_KP_7 ||
828              event->keyval == GDK_KEY_KP_Home ||
829              event->keyval == GDK_KEY_KP_8 ||
830              event->keyval == GDK_KEY_KP_Up ||
831              event->keyval == GDK_KEY_KP_9 ||
832              event->keyval == GDK_KEY_KP_Page_Up ||
833              event->keyval == GDK_KEY_KP_Decimal ||
834              event->keyval == GDK_KEY_KP_Delete)) {
835             /* app keypad; do nothing */
836         } else if (nethack_mode &&
837                    (event->keyval == GDK_KEY_KP_1 ||
838                     event->keyval == GDK_KEY_KP_End ||
839                     event->keyval == GDK_KEY_KP_2 ||
840                     event->keyval == GDK_KEY_KP_Down ||
841                     event->keyval == GDK_KEY_KP_3 ||
842                     event->keyval == GDK_KEY_KP_Page_Down ||
843                     event->keyval == GDK_KEY_KP_4 ||
844                     event->keyval == GDK_KEY_KP_Left ||
845                     event->keyval == GDK_KEY_KP_5 ||
846                     event->keyval == GDK_KEY_KP_Begin ||
847                     event->keyval == GDK_KEY_KP_6 ||
848                     event->keyval == GDK_KEY_KP_Right ||
849                     event->keyval == GDK_KEY_KP_7 ||
850                     event->keyval == GDK_KEY_KP_Home ||
851                     event->keyval == GDK_KEY_KP_8 ||
852                     event->keyval == GDK_KEY_KP_Up ||
853                     event->keyval == GDK_KEY_KP_9 ||
854                     event->keyval == GDK_KEY_KP_Page_Up)) {
855             /* nethack mode; do nothing */
856         } else {
857             if (gtk_im_context_filter_keypress(inst->imc, event))
858                 return TRUE;
859         }
860
861         /*
862          * GDK 2.0 arranges to have done some translation for us: in
863          * GDK 2.0, event->string is encoded in the current locale.
864          *
865          * So we use the standard C library function mbstowcs() to
866          * convert from the current locale into Unicode; from there
867          * we can convert to whatever PuTTY is currently working in.
868          * (In fact I convert straight back to UTF-8 from
869          * wide-character Unicode, for the sake of simplicity: that
870          * way we can still use exactly the same code to manipulate
871          * the string, such as prefixing ESC.)
872          */
873         output_charset = CS_UTF8;
874         {
875             wchar_t widedata[32];
876             const wchar_t *wp;
877             int wlen;
878             int ulen;
879
880             wlen = mb_to_wc(DEFAULT_CODEPAGE, 0,
881                             event->string, strlen(event->string),
882                             widedata, lenof(widedata)-1);
883
884             wp = widedata;
885             ulen = charset_from_unicode(&wp, &wlen, output+1, lenof(output)-2,
886                                         CS_UTF8, NULL, NULL, 0);
887             output[1+ulen] = '\0';
888         }
889 #endif
890
891         if (!output[1] &&
892             (ucsval = keysym_to_unicode(event->keyval)) >= 0) {
893             ucsoutput[0] = '\033';
894             ucsoutput[1] = ucsval;
895             use_ucsoutput = TRUE;
896             end = 2;
897         } else {
898             output[lenof(output)-1] = '\0';
899             end = strlen(output);
900         }
901         if (event->state & GDK_MOD1_MASK) {
902             start = 0;
903             if (end == 1) end = 0;
904         } else
905             start = 1;
906
907         /* Control-` is the same as Control-\ (unless gtk has a better idea) */
908         if (!output[1] && event->keyval == '`' &&
909             (event->state & GDK_CONTROL_MASK)) {
910             output[1] = '\x1C';
911             use_ucsoutput = FALSE;
912             end = 2;
913         }
914
915         /* Control-Break sends a Break special to the backend */
916         if (event->keyval == GDK_KEY_Break &&
917             (event->state & GDK_CONTROL_MASK)) {
918             if (inst->back)
919                 inst->back->special(inst->backhandle, TS_BRK);
920             return TRUE;
921         }
922
923         /* We handle Return ourselves, because it needs to be flagged as
924          * special to ldisc. */
925         if (event->keyval == GDK_KEY_Return) {
926             output[1] = '\015';
927             use_ucsoutput = FALSE;
928             end = 2;
929             special = TRUE;
930         }
931
932         /* Control-2, Control-Space and Control-@ are NUL */
933         if (!output[1] &&
934             (event->keyval == ' ' || event->keyval == '2' ||
935              event->keyval == '@') &&
936             (event->state & (GDK_SHIFT_MASK |
937                              GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {
938             output[1] = '\0';
939             use_ucsoutput = FALSE;
940             end = 2;
941         }
942
943         /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
944         if (!output[1] && event->keyval == ' ' &&
945             (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==
946             (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
947             output[1] = '\240';
948             output_charset = CS_ISO8859_1;
949             use_ucsoutput = FALSE;
950             end = 2;
951         }
952
953         /* We don't let GTK tell us what Backspace is! We know better. */
954         if (event->keyval == GDK_KEY_BackSpace &&
955             !(event->state & GDK_SHIFT_MASK)) {
956             output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ?
957                 '\x7F' : '\x08';
958             use_ucsoutput = FALSE;
959             end = 2;
960             special = TRUE;
961         }
962         /* For Shift Backspace, do opposite of what is configured. */
963         if (event->keyval == GDK_KEY_BackSpace &&
964             (event->state & GDK_SHIFT_MASK)) {
965             output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ?
966                 '\x08' : '\x7F';
967             use_ucsoutput = FALSE;
968             end = 2;
969             special = TRUE;
970         }
971
972         /* Shift-Tab is ESC [ Z */
973         if (event->keyval == GDK_KEY_ISO_Left_Tab ||
974             (event->keyval == GDK_KEY_Tab &&
975              (event->state & GDK_SHIFT_MASK))) {
976             end = 1 + sprintf(output+1, "\033[Z");
977             use_ucsoutput = FALSE;
978         }
979         /* And normal Tab is Tab, if the keymap hasn't already told us.
980          * (Curiously, at least one version of the MacOS 10.5 X server
981          * doesn't translate Tab for us. */
982         if (event->keyval == GDK_KEY_Tab && end <= 1) {
983             output[1] = '\t';
984             end = 2;
985         }
986
987         /*
988          * NetHack keypad mode.
989          */
990         if (nethack_mode) {
991             const char *keys = NULL;
992             switch (event->keyval) {
993               case GDK_KEY_KP_1: case GDK_KEY_KP_End:
994                 keys = "bB\002"; break;
995               case GDK_KEY_KP_2: case GDK_KEY_KP_Down:
996                 keys = "jJ\012"; break;
997               case GDK_KEY_KP_3: case GDK_KEY_KP_Page_Down:
998                 keys = "nN\016"; break;
999               case GDK_KEY_KP_4: case GDK_KEY_KP_Left:
1000                 keys = "hH\010"; break;
1001               case GDK_KEY_KP_5: case GDK_KEY_KP_Begin:
1002                 keys = "..."; break;
1003               case GDK_KEY_KP_6: case GDK_KEY_KP_Right:
1004                 keys = "lL\014"; break;
1005               case GDK_KEY_KP_7: case GDK_KEY_KP_Home:
1006                 keys = "yY\031"; break;
1007               case GDK_KEY_KP_8: case GDK_KEY_KP_Up:
1008                 keys = "kK\013"; break;
1009               case GDK_KEY_KP_9: case GDK_KEY_KP_Page_Up:
1010                 keys = "uU\025"; break;
1011             }
1012             if (keys) {
1013                 end = 2;
1014                 if (event->state & GDK_CONTROL_MASK)
1015                     output[1] = keys[2];
1016                 else if (event->state & GDK_SHIFT_MASK)
1017                     output[1] = keys[1];
1018                 else
1019                     output[1] = keys[0];
1020                 use_ucsoutput = FALSE;
1021                 goto done;
1022             }
1023         }
1024
1025         /*
1026          * Application keypad mode.
1027          */
1028         if (app_keypad_mode) {
1029             int xkey = 0;
1030             switch (event->keyval) {
1031               case GDK_KEY_Num_Lock: xkey = 'P'; break;
1032               case GDK_KEY_KP_Divide: xkey = 'Q'; break;
1033               case GDK_KEY_KP_Multiply: xkey = 'R'; break;
1034               case GDK_KEY_KP_Subtract: xkey = 'S'; break;
1035                 /*
1036                  * Keypad + is tricky. It covers a space that would
1037                  * be taken up on the VT100 by _two_ keys; so we
1038                  * let Shift select between the two. Worse still,
1039                  * in xterm function key mode we change which two...
1040                  */
1041               case GDK_KEY_KP_Add:
1042                 if (conf_get_int(inst->conf, CONF_funky_type) == FUNKY_XTERM) {
1043                     if (event->state & GDK_SHIFT_MASK)
1044                         xkey = 'l';
1045                     else
1046                         xkey = 'k';
1047                 } else if (event->state & GDK_SHIFT_MASK)
1048                         xkey = 'm';
1049                 else
1050                     xkey = 'l';
1051                 break;
1052               case GDK_KEY_KP_Enter: xkey = 'M'; break;
1053               case GDK_KEY_KP_0: case GDK_KEY_KP_Insert: xkey = 'p'; break;
1054               case GDK_KEY_KP_1: case GDK_KEY_KP_End: xkey = 'q'; break;
1055               case GDK_KEY_KP_2: case GDK_KEY_KP_Down: xkey = 'r'; break;
1056               case GDK_KEY_KP_3: case GDK_KEY_KP_Page_Down: xkey = 's'; break;
1057               case GDK_KEY_KP_4: case GDK_KEY_KP_Left: xkey = 't'; break;
1058               case GDK_KEY_KP_5: case GDK_KEY_KP_Begin: xkey = 'u'; break;
1059               case GDK_KEY_KP_6: case GDK_KEY_KP_Right: xkey = 'v'; break;
1060               case GDK_KEY_KP_7: case GDK_KEY_KP_Home: xkey = 'w'; break;
1061               case GDK_KEY_KP_8: case GDK_KEY_KP_Up: xkey = 'x'; break;
1062               case GDK_KEY_KP_9: case GDK_KEY_KP_Page_Up: xkey = 'y'; break;
1063               case GDK_KEY_KP_Decimal: case GDK_KEY_KP_Delete:
1064                 xkey = 'n'; break;
1065             }
1066             if (xkey) {
1067                 if (inst->term->vt52_mode) {
1068                     if (xkey >= 'P' && xkey <= 'S')
1069                         end = 1 + sprintf(output+1, "\033%c", xkey);
1070                     else
1071                         end = 1 + sprintf(output+1, "\033?%c", xkey);
1072                 } else
1073                     end = 1 + sprintf(output+1, "\033O%c", xkey);
1074                 use_ucsoutput = FALSE;
1075                 goto done;
1076             }
1077         }
1078
1079         /*
1080          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1081          * for integer decimal nn.)
1082          *
1083          * We also deal with the weird ones here. Linux VCs replace F1
1084          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1085          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1086          * respectively.
1087          */
1088         {
1089             int code = 0;
1090             int funky_type = conf_get_int(inst->conf, CONF_funky_type);
1091             switch (event->keyval) {
1092               case GDK_KEY_F1:
1093                 code = (event->state & GDK_SHIFT_MASK ? 23 : 11);
1094                 break;
1095               case GDK_KEY_F2:
1096                 code = (event->state & GDK_SHIFT_MASK ? 24 : 12);
1097                 break;
1098               case GDK_KEY_F3:
1099                 code = (event->state & GDK_SHIFT_MASK ? 25 : 13);
1100                 break;
1101               case GDK_KEY_F4:
1102                 code = (event->state & GDK_SHIFT_MASK ? 26 : 14);
1103                 break;
1104               case GDK_KEY_F5:
1105                 code = (event->state & GDK_SHIFT_MASK ? 28 : 15);
1106                 break;
1107               case GDK_KEY_F6:
1108                 code = (event->state & GDK_SHIFT_MASK ? 29 : 17);
1109                 break;
1110               case GDK_KEY_F7:
1111                 code = (event->state & GDK_SHIFT_MASK ? 31 : 18);
1112                 break;
1113               case GDK_KEY_F8:
1114                 code = (event->state & GDK_SHIFT_MASK ? 32 : 19);
1115                 break;
1116               case GDK_KEY_F9:
1117                 code = (event->state & GDK_SHIFT_MASK ? 33 : 20);
1118                 break;
1119               case GDK_KEY_F10:
1120                 code = (event->state & GDK_SHIFT_MASK ? 34 : 21);
1121                 break;
1122               case GDK_KEY_F11:
1123                 code = 23;
1124                 break;
1125               case GDK_KEY_F12:
1126                 code = 24;
1127                 break;
1128               case GDK_KEY_F13:
1129                 code = 25;
1130                 break;
1131               case GDK_KEY_F14:
1132                 code = 26;
1133                 break;
1134               case GDK_KEY_F15:
1135                 code = 28;
1136                 break;
1137               case GDK_KEY_F16:
1138                 code = 29;
1139                 break;
1140               case GDK_KEY_F17:
1141                 code = 31;
1142                 break;
1143               case GDK_KEY_F18:
1144                 code = 32;
1145                 break;
1146               case GDK_KEY_F19:
1147                 code = 33;
1148                 break;
1149               case GDK_KEY_F20:
1150                 code = 34;
1151                 break;
1152             }
1153             if (!(event->state & GDK_CONTROL_MASK)) switch (event->keyval) {
1154               case GDK_KEY_Home: case GDK_KEY_KP_Home:
1155                 code = 1;
1156                 break;
1157               case GDK_KEY_Insert: case GDK_KEY_KP_Insert:
1158                 code = 2;
1159                 break;
1160               case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
1161                 code = 3;
1162                 break;
1163               case GDK_KEY_End: case GDK_KEY_KP_End:
1164                 code = 4;
1165                 break;
1166               case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
1167                 code = 5;
1168                 break;
1169               case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
1170                 code = 6;
1171                 break;
1172             }
1173             /* Reorder edit keys to physical order */
1174             if (funky_type == FUNKY_VT400 && code <= 6)
1175                 code = "\0\2\1\4\5\3\6"[code];
1176
1177             if (inst->term->vt52_mode && code > 0 && code <= 6) {
1178                 end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);
1179                 use_ucsoutput = FALSE;
1180                 goto done;
1181             }
1182
1183             if (funky_type == FUNKY_SCO &&     /* SCO function keys */
1184                 code >= 11 && code <= 34) {
1185                 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
1186                 int index = 0;
1187                 switch (event->keyval) {
1188                   case GDK_KEY_F1: index = 0; break;
1189                   case GDK_KEY_F2: index = 1; break;
1190                   case GDK_KEY_F3: index = 2; break;
1191                   case GDK_KEY_F4: index = 3; break;
1192                   case GDK_KEY_F5: index = 4; break;
1193                   case GDK_KEY_F6: index = 5; break;
1194                   case GDK_KEY_F7: index = 6; break;
1195                   case GDK_KEY_F8: index = 7; break;
1196                   case GDK_KEY_F9: index = 8; break;
1197                   case GDK_KEY_F10: index = 9; break;
1198                   case GDK_KEY_F11: index = 10; break;
1199                   case GDK_KEY_F12: index = 11; break;
1200                 }
1201                 if (event->state & GDK_SHIFT_MASK) index += 12;
1202                 if (event->state & GDK_CONTROL_MASK) index += 24;
1203                 end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);
1204                 use_ucsoutput = FALSE;
1205                 goto done;
1206             }
1207             if (funky_type == FUNKY_SCO &&     /* SCO small keypad */
1208                 code >= 1 && code <= 6) {
1209                 char codes[] = "HL.FIG";
1210                 if (code == 3) {
1211                     output[1] = '\x7F';
1212                     end = 2;
1213                 } else {
1214                     end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);
1215                 }
1216                 use_ucsoutput = FALSE;
1217                 goto done;
1218             }
1219             if ((inst->term->vt52_mode || funky_type == FUNKY_VT100P) &&
1220                 code >= 11 && code <= 24) {
1221                 int offt = 0;
1222                 if (code > 15)
1223                     offt++;
1224                 if (code > 21)
1225                     offt++;
1226                 if (inst->term->vt52_mode)
1227                     end = 1 + sprintf(output+1,
1228                                       "\x1B%c", code + 'P' - 11 - offt);
1229                 else
1230                     end = 1 + sprintf(output+1,
1231                                       "\x1BO%c", code + 'P' - 11 - offt);
1232                 use_ucsoutput = FALSE;
1233                 goto done;
1234             }
1235             if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
1236                 end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
1237                 use_ucsoutput = FALSE;
1238                 goto done;
1239             }
1240             if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
1241                 if (inst->term->vt52_mode)
1242                     end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
1243                 else
1244                     end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);
1245                 use_ucsoutput = FALSE;
1246                 goto done;
1247             }
1248             if ((code == 1 || code == 4) &&
1249                 conf_get_int(inst->conf, CONF_rxvt_homeend)) {
1250                 end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
1251                 use_ucsoutput = FALSE;
1252                 goto done;
1253             }
1254             if (code) {
1255                 end = 1 + sprintf(output+1, "\x1B[%d~", code);
1256                 use_ucsoutput = FALSE;
1257                 goto done;
1258             }
1259         }
1260
1261         /*
1262          * Cursor keys. (This includes the numberpad cursor keys,
1263          * if we haven't already done them due to app keypad mode.)
1264          * 
1265          * Here we also process un-numlocked un-appkeypadded KP5,
1266          * which sends ESC [ G.
1267          */
1268         {
1269             int xkey = 0;
1270             switch (event->keyval) {
1271               case GDK_KEY_Up: case GDK_KEY_KP_Up: xkey = 'A'; break;
1272               case GDK_KEY_Down: case GDK_KEY_KP_Down: xkey = 'B'; break;
1273               case GDK_KEY_Right: case GDK_KEY_KP_Right: xkey = 'C'; break;
1274               case GDK_KEY_Left: case GDK_KEY_KP_Left: xkey = 'D'; break;
1275               case GDK_KEY_Begin: case GDK_KEY_KP_Begin: xkey = 'G'; break;
1276             }
1277             if (xkey) {
1278                 end = 1 + format_arrow_key(output+1, inst->term, xkey,
1279                                            event->state & GDK_CONTROL_MASK);
1280                 use_ucsoutput = FALSE;
1281                 goto done;
1282             }
1283         }
1284         goto done;
1285     }
1286
1287     done:
1288
1289     if (end-start > 0) {
1290 #ifdef KEY_DEBUGGING
1291         int i;
1292         printf("generating sequence:");
1293         for (i = start; i < end; i++)
1294             printf(" %02x", (unsigned char) output[i]);
1295         printf("\n");
1296 #endif
1297
1298         if (special) {
1299             /*
1300              * For special control characters, the character set
1301              * should never matter.
1302              */
1303             output[end] = '\0';        /* NUL-terminate */
1304             if (inst->ldisc)
1305                 ldisc_send(inst->ldisc, output+start, -2, 1);
1306         } else if (!inst->direct_to_font) {
1307             if (!use_ucsoutput) {
1308                 if (inst->ldisc)
1309                     lpage_send(inst->ldisc, output_charset, output+start,
1310                                end-start, 1);
1311             } else {
1312                 /*
1313                  * We generated our own Unicode key data from the
1314                  * keysym, so use that instead.
1315                  */
1316                 if (inst->ldisc)
1317                     luni_send(inst->ldisc, ucsoutput+start, end-start, 1);
1318             }
1319         } else {
1320             /*
1321              * In direct-to-font mode, we just send the string
1322              * exactly as we received it.
1323              */
1324             if (inst->ldisc)
1325                 ldisc_send(inst->ldisc, output+start, end-start, 1);
1326         }
1327
1328         show_mouseptr(inst, 0);
1329         term_seen_key_event(inst->term);
1330     }
1331
1332     return TRUE;
1333 }
1334
1335 #if GTK_CHECK_VERSION(2,0,0)
1336 void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data)
1337 {
1338     struct gui_data *inst = (struct gui_data *)data;
1339     if (inst->ldisc)
1340         lpage_send(inst->ldisc, CS_UTF8, str, strlen(str), 1);
1341     show_mouseptr(inst, 0);
1342     term_seen_key_event(inst->term);
1343 }
1344 #endif
1345
1346 gboolean button_internal(struct gui_data *inst, guint32 timestamp,
1347                          GdkEventType type, guint ebutton, guint state,
1348                          gdouble ex, gdouble ey)
1349 {
1350     int shift, ctrl, alt, x, y, button, act, raw_mouse_mode;
1351
1352     /* Remember the timestamp. */
1353     inst->input_event_time = timestamp;
1354
1355     show_mouseptr(inst, 1);
1356
1357     shift = state & GDK_SHIFT_MASK;
1358     ctrl = state & GDK_CONTROL_MASK;
1359     alt = state & GDK_MOD1_MASK;
1360
1361     raw_mouse_mode =
1362         send_raw_mouse && !(shift && conf_get_int(inst->conf,
1363                                                   CONF_mouse_override));
1364
1365     if (!raw_mouse_mode) {
1366         if (ebutton == 4 && type == GDK_BUTTON_PRESS) {
1367             term_scroll(inst->term, 0, -5);
1368             return TRUE;
1369         }
1370         if (ebutton == 5 && type == GDK_BUTTON_PRESS) {
1371             term_scroll(inst->term, 0, +5);
1372             return TRUE;
1373         }
1374     }
1375
1376     if (ebutton == 3 && ctrl) {
1377         gtk_menu_popup(GTK_MENU(inst->menu), NULL, NULL, NULL, NULL,
1378                        ebutton, timestamp);
1379         return TRUE;
1380     }
1381
1382     if (ebutton == 1)
1383         button = MBT_LEFT;
1384     else if (ebutton == 2)
1385         button = MBT_MIDDLE;
1386     else if (ebutton == 3)
1387         button = MBT_RIGHT;
1388     else if (ebutton == 4)
1389         button = MBT_WHEEL_UP;
1390     else if (ebutton == 5)
1391         button = MBT_WHEEL_DOWN;
1392     else
1393         return FALSE;                  /* don't even know what button! */
1394
1395     switch (type) {
1396       case GDK_BUTTON_PRESS: act = MA_CLICK; break;
1397       case GDK_BUTTON_RELEASE: act = MA_RELEASE; break;
1398       case GDK_2BUTTON_PRESS: act = MA_2CLK; break;
1399       case GDK_3BUTTON_PRESS: act = MA_3CLK; break;
1400       default: return FALSE;           /* don't know this event type */
1401     }
1402
1403     if (raw_mouse_mode && act != MA_CLICK && act != MA_RELEASE)
1404         return TRUE;                   /* we ignore these in raw mouse mode */
1405
1406     x = (ex - inst->window_border) / inst->font_width;
1407     y = (ey - inst->window_border) / inst->font_height;
1408
1409     term_mouse(inst->term, button, translate_button(button), act,
1410                x, y, shift, ctrl, alt);
1411
1412     return TRUE;
1413 }
1414
1415 gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
1416 {
1417     struct gui_data *inst = (struct gui_data *)data;
1418     return button_internal(inst, event->time, event->type, event->button,
1419                            event->state, event->x, event->y);
1420 }
1421
1422 #if GTK_CHECK_VERSION(2,0,0)
1423 /*
1424  * In GTK 2, mouse wheel events have become a new type of event.
1425  * This handler translates them back into button-4 and button-5
1426  * presses so that I don't have to change my old code too much :-)
1427  */
1428 gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1429 {
1430     struct gui_data *inst = (struct gui_data *)data;
1431     guint button;
1432
1433     if (event->direction == GDK_SCROLL_UP)
1434         button = 4;
1435     else if (event->direction == GDK_SCROLL_DOWN)
1436         button = 5;
1437     else
1438         return FALSE;
1439
1440     return button_internal(inst, event->time, GDK_BUTTON_PRESS,
1441                            button, event->state, event->x, event->y);
1442 }
1443 #endif
1444
1445 gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
1446 {
1447     struct gui_data *inst = (struct gui_data *)data;
1448     int shift, ctrl, alt, x, y, button;
1449
1450     /* Remember the timestamp. */
1451     inst->input_event_time = event->time;
1452
1453     show_mouseptr(inst, 1);
1454
1455     shift = event->state & GDK_SHIFT_MASK;
1456     ctrl = event->state & GDK_CONTROL_MASK;
1457     alt = event->state & GDK_MOD1_MASK;
1458     if (event->state & GDK_BUTTON1_MASK)
1459         button = MBT_LEFT;
1460     else if (event->state & GDK_BUTTON2_MASK)
1461         button = MBT_MIDDLE;
1462     else if (event->state & GDK_BUTTON3_MASK)
1463         button = MBT_RIGHT;
1464     else
1465         return FALSE;                  /* don't even know what button! */
1466
1467     x = (event->x - inst->window_border) / inst->font_width;
1468     y = (event->y - inst->window_border) / inst->font_height;
1469
1470     term_mouse(inst->term, button, translate_button(button), MA_DRAG,
1471                x, y, shift, ctrl, alt);
1472
1473     return TRUE;
1474 }
1475
1476 void frontend_keypress(void *handle)
1477 {
1478     struct gui_data *inst = (struct gui_data *)handle;
1479
1480     /*
1481      * If our child process has exited but not closed, terminate on
1482      * any keypress.
1483      */
1484     if (inst->exited)
1485         cleanup_exit(0);
1486 }
1487
1488 static void exit_callback(void *vinst)
1489 {
1490     struct gui_data *inst = (struct gui_data *)vinst;
1491     int exitcode, close_on_exit;
1492
1493     if (!inst->exited &&
1494         (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) {
1495         inst->exited = TRUE;
1496         close_on_exit = conf_get_int(inst->conf, CONF_close_on_exit);
1497         if (close_on_exit == FORCE_ON ||
1498             (close_on_exit == AUTO && exitcode == 0))
1499             gtk_main_quit();           /* just go */
1500         if (inst->ldisc) {
1501             ldisc_free(inst->ldisc);
1502             inst->ldisc = NULL;
1503         }
1504         inst->back->free(inst->backhandle);
1505         inst->backhandle = NULL;
1506         inst->back = NULL;
1507         term_provide_resize_fn(inst->term, NULL, NULL);
1508         update_specials_menu(inst);
1509         gtk_widget_set_sensitive(inst->restartitem, TRUE);
1510     }
1511 }
1512
1513 /*
1514  * Replacement code for the gtk_quit_add() function, which GTK2 - in
1515  * their unbounded wisdom - deprecated without providing any usable
1516  * replacement, and which we were using to ensure that our idle
1517  * function for toplevel callbacks was only run from the outermost
1518  * gtk_main().
1519  *
1520  * We maintain a global variable with a list of 'struct gui_data'
1521  * instances on which we should call inst_post_main() when a
1522  * subsidiary gtk_main() terminates; then we must make sure that all
1523  * our subsidiary calls to gtk_main() are followed by a call to
1524  * post_main().
1525  *
1526  * This is kind of overkill in the sense that at the time of writing
1527  * we don't actually ever run more than one 'struct gui_data' instance
1528  * in the same process, but we're _so nearly_ prepared to do that that
1529  * I want to remain futureproof against the possibility of doing so in
1530  * future.
1531  */
1532 struct post_main_context {
1533     struct post_main_context *next;
1534     struct gui_data *inst;
1535 };
1536 struct post_main_context *post_main_list_head = NULL;
1537 static void request_post_main(struct gui_data *inst)
1538 {
1539     struct post_main_context *node = snew(struct post_main_context);
1540     node->next = post_main_list_head;
1541     node->inst = inst;
1542     post_main_list_head = node;
1543 }
1544 static void inst_post_main(struct gui_data *inst);
1545 void post_main(void)
1546 {
1547     while (post_main_list_head) {
1548         struct post_main_context *node = post_main_list_head;
1549         post_main_list_head = node->next;
1550         inst_post_main(node->inst);
1551         sfree(node);
1552     }
1553 }
1554
1555 void notify_remote_exit(void *frontend)
1556 {
1557     struct gui_data *inst = (struct gui_data *)frontend;
1558
1559     queue_toplevel_callback(exit_callback, inst);
1560 }
1561
1562 static void notify_toplevel_callback(void *frontend);
1563
1564 static void inst_post_main(struct gui_data *inst)
1565 {
1566     if (gtk_main_level() == 1) {
1567         notify_toplevel_callback(inst);
1568         inst->quit_fn_scheduled = FALSE;
1569     } else {
1570         /* Apparently we're _still_ more than one level deep in
1571          * gtk_main() instances, so we'll need another callback for
1572          * when we get out of the next one. */
1573         request_post_main(inst);
1574     }
1575 }
1576
1577 static gint idle_toplevel_callback_func(gpointer data)
1578 {
1579     struct gui_data *inst = (struct gui_data *)data;
1580
1581     if (gtk_main_level() > 1) {
1582         /*
1583          * We don't run the callbacks if we're in the middle of a
1584          * subsidiary gtk_main. Instead, ask for a callback when we
1585          * get back out of the subsidiary main loop (if we haven't
1586          * already arranged one), so we can reschedule ourself then.
1587          */
1588         if (!inst->quit_fn_scheduled) {
1589             request_post_main(inst);
1590             inst->quit_fn_scheduled = TRUE;
1591         }
1592         /*
1593          * And unschedule this idle function, since we've now done
1594          * everything we can until the innermost gtk_main has quit and
1595          * can reschedule us with a chance of actually taking action.
1596          */
1597         if (inst->idle_fn_scheduled) { /* double-check, just in case */
1598             g_source_remove(inst->toplevel_callback_idle_id);
1599             inst->idle_fn_scheduled = FALSE;
1600         }
1601     } else {
1602         run_toplevel_callbacks();
1603     }
1604
1605     /*
1606      * If we've emptied our toplevel callback queue, unschedule
1607      * ourself. Otherwise, leave ourselves pending so we'll be called
1608      * again to deal with more callbacks after another round of the
1609      * event loop.
1610      */
1611     if (!toplevel_callback_pending() && inst->idle_fn_scheduled) {
1612         g_source_remove(inst->toplevel_callback_idle_id);
1613         inst->idle_fn_scheduled = FALSE;
1614     }
1615
1616     return TRUE;
1617 }
1618
1619 static void notify_toplevel_callback(void *frontend)
1620 {
1621     struct gui_data *inst = (struct gui_data *)frontend;
1622
1623     if (!inst->idle_fn_scheduled) {
1624         inst->toplevel_callback_idle_id =
1625             g_idle_add(idle_toplevel_callback_func, inst);
1626         inst->idle_fn_scheduled = TRUE;
1627     }
1628 }
1629
1630 static gint timer_trigger(gpointer data)
1631 {
1632     unsigned long now = GPOINTER_TO_LONG(data);
1633     unsigned long next, then;
1634     long ticks;
1635
1636     /*
1637      * Destroy the timer we got here on.
1638      */
1639     if (timer_id) {
1640         g_source_remove(timer_id);
1641         timer_id = 0;
1642     }
1643
1644     /*
1645      * run_timers() may cause a call to timer_change_notify, in which
1646      * case a new timer will already have been set up and left in
1647      * timer_id. If it hasn't, and run_timers reports that some timing
1648      * still needs to be done, we do it ourselves.
1649      */
1650     if (run_timers(now, &next) && !timer_id) {
1651         then = now;
1652         now = GETTICKCOUNT();
1653         if (now - then > next - then)
1654             ticks = 0;
1655         else
1656             ticks = next - now;
1657         timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
1658     }
1659
1660     /*
1661      * Returning FALSE means 'don't call this timer again', which
1662      * _should_ be redundant given that we removed it above, but just
1663      * in case, return FALSE anyway.
1664      */
1665     return FALSE;
1666 }
1667
1668 void timer_change_notify(unsigned long next)
1669 {
1670     long ticks;
1671
1672     if (timer_id)
1673         g_source_remove(timer_id);
1674
1675     ticks = next - GETTICKCOUNT();
1676     if (ticks <= 0)
1677         ticks = 1;                     /* just in case */
1678
1679     timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
1680 }
1681
1682 #if GTK_CHECK_VERSION(2,0,0)
1683 gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
1684                        gpointer data)
1685 {
1686     int sourcefd = g_io_channel_unix_get_fd(source);
1687     /*
1688      * We must process exceptional notifications before ordinary
1689      * readability ones, or we may go straight past the urgent
1690      * marker.
1691      */
1692     if (condition & G_IO_PRI)
1693         select_result(sourcefd, 4);
1694     if (condition & G_IO_IN)
1695         select_result(sourcefd, 1);
1696     if (condition & G_IO_OUT)
1697         select_result(sourcefd, 2);
1698
1699     return TRUE;
1700 }
1701 #else
1702 void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
1703 {
1704     if (condition & GDK_INPUT_EXCEPTION)
1705         select_result(sourcefd, 4);
1706     if (condition & GDK_INPUT_READ)
1707         select_result(sourcefd, 1);
1708     if (condition & GDK_INPUT_WRITE)
1709         select_result(sourcefd, 2);
1710 }
1711 #endif
1712
1713 void destroy(GtkWidget *widget, gpointer data)
1714 {
1715     gtk_main_quit();
1716 }
1717
1718 gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1719 {
1720     struct gui_data *inst = (struct gui_data *)data;
1721     term_set_focus(inst->term, event->in);
1722     term_update(inst->term);
1723     show_mouseptr(inst, 1);
1724     return FALSE;
1725 }
1726
1727 void set_busy_status(void *frontend, int status)
1728 {
1729     struct gui_data *inst = (struct gui_data *)frontend;
1730     inst->busy_status = status;
1731     update_mouseptr(inst);
1732 }
1733
1734 /*
1735  * set or clear the "raw mouse message" mode
1736  */
1737 void set_raw_mouse_mode(void *frontend, int activate)
1738 {
1739     struct gui_data *inst = (struct gui_data *)frontend;
1740     activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep);
1741     send_raw_mouse = activate;
1742     update_mouseptr(inst);
1743 }
1744
1745 void request_resize(void *frontend, int w, int h)
1746 {
1747     struct gui_data *inst = (struct gui_data *)frontend;
1748     int large_x, large_y;
1749     int offset_x, offset_y;
1750     int area_x, area_y;
1751     GtkRequisition inner, outer;
1752
1753     /*
1754      * This is a heinous hack dreamed up by the gnome-terminal
1755      * people to get around a limitation in gtk. The problem is
1756      * that in order to set the size correctly we really need to be
1757      * calling gtk_window_resize - but that needs to know the size
1758      * of the _whole window_, not the drawing area. So what we do
1759      * is to set an artificially huge size request on the drawing
1760      * area, recompute the resulting size request on the window,
1761      * and look at the difference between the two. That gives us
1762      * the x and y offsets we need to translate drawing area size
1763      * into window size for real, and then we call
1764      * gtk_window_resize.
1765      */
1766
1767     /*
1768      * We start by retrieving the current size of the whole window.
1769      * Adding a bit to _that_ will give us a value we can use as a
1770      * bogus size request which guarantees to be bigger than the
1771      * current size of the drawing area.
1772      */
1773     get_window_pixels(inst, &large_x, &large_y);
1774     large_x += 32;
1775     large_y += 32;
1776
1777     gtk_widget_set_size_request(inst->area, large_x, large_y);
1778     gtk_widget_size_request(inst->area, &inner);
1779     gtk_widget_size_request(inst->window, &outer);
1780
1781     offset_x = outer.width - inner.width;
1782     offset_y = outer.height - inner.height;
1783
1784     area_x = inst->font_width * w + 2*inst->window_border;
1785     area_y = inst->font_height * h + 2*inst->window_border;
1786
1787     /*
1788      * Now we must set the size request on the drawing area back to
1789      * something sensible before we commit the real resize. Best
1790      * way to do this, I think, is to set it to what the size is
1791      * really going to end up being.
1792      */
1793     gtk_widget_set_size_request(inst->area, area_x, area_y);
1794 #if GTK_CHECK_VERSION(2,0,0)
1795     gtk_window_resize(GTK_WINDOW(inst->window),
1796                       area_x + offset_x, area_y + offset_y);
1797 #else
1798     gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), area_x, area_y);
1799     /*
1800      * I can no longer remember what this call to
1801      * gtk_container_dequeue_resize_handler is for. It was
1802      * introduced in r3092 with no comment, and the commit log
1803      * message was uninformative. I'm _guessing_ its purpose is to
1804      * prevent gratuitous resize processing on the window given
1805      * that we're about to resize it anyway, but I have no idea
1806      * why that's so incredibly vital.
1807      * 
1808      * I've tried removing the call, and nothing seems to go
1809      * wrong. I've backtracked to r3092 and tried removing the
1810      * call there, and still nothing goes wrong. So I'm going to
1811      * adopt the working hypothesis that it's superfluous; I won't
1812      * actually remove it from the GTK 1.2 code, but I won't
1813      * attempt to replicate its functionality in the GTK 2 code
1814      * above.
1815      */
1816     gtk_container_dequeue_resize_handler(GTK_CONTAINER(inst->window));
1817     gdk_window_resize(gtk_widget_get_window(inst->window),
1818                       area_x + offset_x, area_y + offset_y);
1819 #endif
1820 }
1821
1822 static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)
1823 {
1824     gboolean success[1];
1825
1826     inst->cols[n].red = r * 0x0101;
1827     inst->cols[n].green = g * 0x0101;
1828     inst->cols[n].blue = b * 0x0101;
1829
1830     gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);
1831     gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,
1832                               FALSE, TRUE, success);
1833     if (!success[0])
1834         g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname,
1835                 n, r, g, b);
1836 }
1837
1838 void set_window_background(struct gui_data *inst)
1839 {
1840     if (inst->area && gtk_widget_get_window(inst->area))
1841         gdk_window_set_background(gtk_widget_get_window(inst->area),
1842                                   &inst->cols[258]);
1843     if (inst->window && gtk_widget_get_window(inst->window))
1844         gdk_window_set_background(gtk_widget_get_window(inst->window),
1845                                   &inst->cols[258]);
1846 }
1847
1848 void palette_set(void *frontend, int n, int r, int g, int b)
1849 {
1850     struct gui_data *inst = (struct gui_data *)frontend;
1851     if (n >= 16)
1852         n += 256 - 16;
1853     if (n >= NALLCOLOURS)
1854         return;
1855     real_palette_set(inst, n, r, g, b);
1856     if (n == 258) {
1857         /* Default Background changed. Ensure space between text area and
1858          * window border is redrawn */
1859         set_window_background(inst);
1860         draw_backing_rect(inst);
1861         gtk_widget_queue_draw(inst->area);
1862     }
1863 }
1864
1865 void palette_reset(void *frontend)
1866 {
1867     struct gui_data *inst = (struct gui_data *)frontend;
1868     /* This maps colour indices in inst->conf to those used in inst->cols. */
1869     static const int ww[] = {
1870         256, 257, 258, 259, 260, 261,
1871         0, 8, 1, 9, 2, 10, 3, 11,
1872         4, 12, 5, 13, 6, 14, 7, 15
1873     };
1874     gboolean success[NALLCOLOURS];
1875     int i;
1876
1877     assert(lenof(ww) == NCFGCOLOURS);
1878
1879     if (!inst->colmap) {
1880         inst->colmap = gdk_colormap_get_system();
1881     } else {
1882         gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS);
1883     }
1884
1885     for (i = 0; i < NCFGCOLOURS; i++) {
1886         inst->cols[ww[i]].red =
1887             conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101;
1888         inst->cols[ww[i]].green =
1889             conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101;
1890         inst->cols[ww[i]].blue = 
1891             conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101;
1892     }
1893
1894     for (i = 0; i < NEXTCOLOURS; i++) {
1895         if (i < 216) {
1896             int r = i / 36, g = (i / 6) % 6, b = i % 6;
1897             inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0;
1898             inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0;
1899             inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0;
1900         } else {
1901             int shade = i - 216;
1902             shade = shade * 0x0a0a + 0x0808;
1903             inst->cols[i+16].red = inst->cols[i+16].green =
1904                 inst->cols[i+16].blue = shade;
1905         }
1906     }
1907
1908     gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS,
1909                               FALSE, TRUE, success);
1910     for (i = 0; i < NALLCOLOURS; i++) {
1911         if (!success[i])
1912             g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
1913                     appname, i,
1914                     conf_get_int_int(inst->conf, CONF_colours, i*3+0),
1915                     conf_get_int_int(inst->conf, CONF_colours, i*3+1),
1916                     conf_get_int_int(inst->conf, CONF_colours, i*3+2));
1917     }
1918
1919     /* Since Default Background may have changed, ensure that space
1920      * between text area and window border is refreshed. */
1921     set_window_background(inst);
1922     if (inst->area && gtk_widget_get_window(inst->area)) {
1923         draw_backing_rect(inst);
1924         gtk_widget_queue_draw(inst->area);
1925     }
1926 }
1927
1928 /* Ensure that all the cut buffers exist - according to the ICCCM, we must
1929  * do this before we start using cut buffers.
1930  */
1931 void init_cutbuffers()
1932 {
1933 #ifndef NOT_X_WINDOWS
1934     unsigned char empty[] = "";
1935     Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1936     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1937                     XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0);
1938     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1939                     XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0);
1940     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1941                     XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0);
1942     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1943                     XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0);
1944     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1945                     XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0);
1946     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1947                     XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0);
1948     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1949                     XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0);
1950     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1951                     XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);
1952 #endif
1953 }
1954
1955 /* Store the data in a cut-buffer. */
1956 void store_cutbuffer(char * ptr, int len)
1957 {
1958 #ifndef NOT_X_WINDOWS
1959     Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1960     /* ICCCM says we must rotate the buffers before storing to buffer 0. */
1961     XRotateBuffers(disp, 1);
1962     XStoreBytes(disp, ptr, len);
1963 #endif
1964 }
1965
1966 /* Retrieve data from a cut-buffer.
1967  * Returned data needs to be freed with XFree().
1968  */
1969 char * retrieve_cutbuffer(int * nbytes)
1970 {
1971 #ifndef NOT_X_WINDOWS
1972     Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1973     char * ptr;
1974     ptr = XFetchBytes(disp, nbytes);
1975     if (*nbytes <= 0 && ptr != 0) {
1976         XFree(ptr);
1977         ptr = 0;
1978     }
1979     return ptr;
1980 #else
1981     return NULL;
1982 #endif
1983 }
1984
1985 void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)
1986 {
1987     struct gui_data *inst = (struct gui_data *)frontend;
1988     if (inst->pasteout_data)
1989         sfree(inst->pasteout_data);
1990     if (inst->pasteout_data_ctext)
1991         sfree(inst->pasteout_data_ctext);
1992     if (inst->pasteout_data_utf8)
1993         sfree(inst->pasteout_data_utf8);
1994
1995     /*
1996      * Set up UTF-8 and compound text paste data. This only happens
1997      * if we aren't in direct-to-font mode using the D800 hack.
1998      */
1999     if (!inst->direct_to_font) {
2000         const wchar_t *tmp = data;
2001         int tmplen = len;
2002 #ifndef NOT_X_WINDOWS
2003         XTextProperty tp;
2004         char *list[1];
2005         Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2006 #endif
2007
2008         inst->pasteout_data_utf8 = snewn(len*6, char);
2009         inst->pasteout_data_utf8_len = len*6;
2010         inst->pasteout_data_utf8_len =
2011             charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,
2012                                  inst->pasteout_data_utf8_len,
2013                                  CS_UTF8, NULL, NULL, 0);
2014         if (inst->pasteout_data_utf8_len == 0) {
2015             sfree(inst->pasteout_data_utf8);
2016             inst->pasteout_data_utf8 = NULL;
2017         } else {
2018             inst->pasteout_data_utf8 =
2019                 sresize(inst->pasteout_data_utf8,
2020                         inst->pasteout_data_utf8_len + 1, char);
2021             inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0';
2022         }
2023
2024         /*
2025          * Now let Xlib convert our UTF-8 data into compound text.
2026          */
2027 #ifndef NOT_X_WINDOWS
2028         list[0] = inst->pasteout_data_utf8;
2029         if (Xutf8TextListToTextProperty(disp, list, 1,
2030                                         XCompoundTextStyle, &tp) == 0) {
2031             inst->pasteout_data_ctext = snewn(tp.nitems+1, char);
2032             memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);
2033             inst->pasteout_data_ctext_len = tp.nitems;
2034             XFree(tp.value);
2035         } else
2036 #endif
2037         {
2038             inst->pasteout_data_ctext = NULL;
2039             inst->pasteout_data_ctext_len = 0;
2040         }
2041     } else {
2042         inst->pasteout_data_utf8 = NULL;
2043         inst->pasteout_data_utf8_len = 0;
2044         inst->pasteout_data_ctext = NULL;
2045         inst->pasteout_data_ctext_len = 0;
2046     }
2047
2048     inst->pasteout_data = snewn(len*6, char);
2049     inst->pasteout_data_len = len*6;
2050     inst->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0,
2051                                        data, len, inst->pasteout_data,
2052                                        inst->pasteout_data_len,
2053                                        NULL, NULL, NULL);
2054     if (inst->pasteout_data_len == 0) {
2055         sfree(inst->pasteout_data);
2056         inst->pasteout_data = NULL;
2057     } else {
2058         inst->pasteout_data =
2059             sresize(inst->pasteout_data, inst->pasteout_data_len, char);
2060     }
2061
2062     store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len);
2063
2064     if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
2065                                 inst->input_event_time)) {
2066 #if GTK_CHECK_VERSION(2,0,0)
2067         gtk_selection_clear_targets(inst->area, GDK_SELECTION_PRIMARY);
2068 #endif
2069         gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
2070                                  GDK_SELECTION_TYPE_STRING, 1);
2071         if (inst->pasteout_data_ctext)
2072             gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
2073                                      compound_text_atom, 1);
2074         if (inst->pasteout_data_utf8)
2075             gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
2076                                      utf8_string_atom, 1);
2077     }
2078
2079     if (must_deselect)
2080         term_deselect(inst->term);
2081 }
2082
2083 void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
2084                    guint info, guint time_stamp, gpointer data)
2085 {
2086     struct gui_data *inst = (struct gui_data *)data;
2087     GdkAtom target = gtk_selection_data_get_target(seldata);
2088     if (target == utf8_string_atom)
2089         gtk_selection_data_set(seldata, target, 8,
2090                                (unsigned char *)inst->pasteout_data_utf8,
2091                                inst->pasteout_data_utf8_len);
2092     else if (target == compound_text_atom)
2093         gtk_selection_data_set(seldata, target, 8,
2094                                (unsigned char *)inst->pasteout_data_ctext,
2095                                inst->pasteout_data_ctext_len);
2096     else
2097         gtk_selection_data_set(seldata, target, 8,
2098                                (unsigned char *)inst->pasteout_data,
2099                                inst->pasteout_data_len);
2100 }
2101
2102 gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
2103                      gpointer data)
2104 {
2105     struct gui_data *inst = (struct gui_data *)data;
2106
2107     term_deselect(inst->term);
2108     if (inst->pasteout_data)
2109         sfree(inst->pasteout_data);
2110     if (inst->pasteout_data_ctext)
2111         sfree(inst->pasteout_data_ctext);
2112     if (inst->pasteout_data_utf8)
2113         sfree(inst->pasteout_data_utf8);
2114     inst->pasteout_data = NULL;
2115     inst->pasteout_data_len = 0;
2116     inst->pasteout_data_ctext = NULL;
2117     inst->pasteout_data_ctext_len = 0;
2118     inst->pasteout_data_utf8 = NULL;
2119     inst->pasteout_data_utf8_len = 0;
2120     return TRUE;
2121 }
2122
2123 void request_paste(void *frontend)
2124 {
2125     struct gui_data *inst = (struct gui_data *)frontend;
2126     /*
2127      * In Unix, pasting is asynchronous: all we can do at the
2128      * moment is to call gtk_selection_convert(), and when the data
2129      * comes back _then_ we can call term_do_paste().
2130      */
2131
2132     if (!inst->direct_to_font) {
2133         /*
2134          * First we attempt to retrieve the selection as a UTF-8
2135          * string (which we will convert to the correct code page
2136          * before sending to the session, of course). If that
2137          * fails, selection_received() will be informed and will
2138          * fall back to an ordinary string.
2139          */
2140         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2141                               utf8_string_atom,
2142                               inst->input_event_time);
2143     } else {
2144         /*
2145          * If we're in direct-to-font mode, we disable UTF-8
2146          * pasting, and go straight to ordinary string data.
2147          */
2148         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2149                               GDK_SELECTION_TYPE_STRING,
2150                               inst->input_event_time);
2151     }
2152 }
2153
2154 gint idle_paste_func(gpointer data);   /* forward ref */
2155
2156 void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
2157                         guint time, gpointer data)
2158 {
2159     struct gui_data *inst = (struct gui_data *)data;
2160     char *text;
2161     int length;
2162 #ifndef NOT_X_WINDOWS
2163     char **list;
2164     int free_list_required = 0;
2165     int free_required = 0;
2166 #endif
2167     int charset;
2168     GdkAtom seldata_target = gtk_selection_data_get_target(seldata);
2169     GdkAtom seldata_type = gtk_selection_data_get_data_type(seldata);
2170     const guchar *seldata_data = gtk_selection_data_get_data(seldata);
2171     gint seldata_length = gtk_selection_data_get_length(seldata);
2172
2173     if (seldata_target == utf8_string_atom && seldata_length <= 0) {
2174         /*
2175          * Failed to get a UTF-8 selection string. Try compound
2176          * text next.
2177          */
2178         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2179                               compound_text_atom,
2180                               inst->input_event_time);
2181         return;
2182     }
2183
2184     if (seldata_target == compound_text_atom && seldata_length <= 0) {
2185         /*
2186          * Failed to get UTF-8 or compound text. Try an ordinary
2187          * string.
2188          */
2189         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2190                               GDK_SELECTION_TYPE_STRING,
2191                               inst->input_event_time);
2192         return;
2193     }
2194
2195     /*
2196      * If we have data, but it's not of a type we can deal with,
2197      * we have to ignore the data.
2198      */
2199     if (seldata_length > 0 &&
2200         seldata_type != GDK_SELECTION_TYPE_STRING &&
2201         seldata_type != compound_text_atom &&
2202         seldata_type != utf8_string_atom)
2203         return;
2204
2205     /*
2206      * If we have no data, try looking in a cut buffer.
2207      */
2208     if (seldata_length <= 0) {
2209 #ifndef NOT_X_WINDOWS
2210         text = retrieve_cutbuffer(&length);
2211         if (length == 0)
2212             return;
2213         /* Xterm is rumoured to expect Latin-1, though I havn't checked the
2214          * source, so use that as a de-facto standard. */
2215         charset = CS_ISO8859_1;
2216         free_required = 1;
2217 #else
2218         return;
2219 #endif
2220     } else {
2221         /*
2222          * Convert COMPOUND_TEXT into UTF-8.
2223          */
2224         if (seldata_type == compound_text_atom) {
2225 #ifndef NOT_X_WINDOWS
2226             XTextProperty tp;
2227             int ret, count;
2228             Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2229
2230             tp.value = (unsigned char *)seldata_data;
2231             tp.encoding = (Atom) seldata_type;
2232             tp.format = gtk_selection_data_get_format(seldata);
2233             tp.nitems = seldata_length;
2234             ret = Xutf8TextPropertyToTextList(disp, &tp, &list, &count);
2235             if (ret == 0 && count == 1) {
2236                 text = list[0];
2237                 length = strlen(list[0]);
2238                 charset = CS_UTF8;
2239                 free_list_required = 1;
2240             } else
2241 #endif
2242             {
2243                 /*
2244                  * Compound text failed; fall back to STRING.
2245                  */
2246                 gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2247                                       GDK_SELECTION_TYPE_STRING,
2248                                       inst->input_event_time);
2249                 return;
2250             }
2251         } else {
2252             text = (char *)seldata_data;
2253             length = seldata_length;
2254             charset = (seldata_type == utf8_string_atom ?
2255                        CS_UTF8 : inst->ucsdata.line_codepage);
2256         }
2257     }
2258
2259     if (inst->pastein_data)
2260         sfree(inst->pastein_data);
2261
2262     inst->pastein_data = snewn(length, wchar_t);
2263     inst->pastein_data_len = length;
2264     inst->pastein_data_len =
2265         mb_to_wc(charset, 0, text, length,
2266                  inst->pastein_data, inst->pastein_data_len);
2267
2268     term_do_paste(inst->term);
2269
2270 #ifndef NOT_X_WINDOWS
2271     if (free_list_required)
2272         XFreeStringList(list);
2273     if (free_required)
2274         XFree(text);
2275 #endif
2276 }
2277
2278 void get_clip(void *frontend, wchar_t ** p, int *len)
2279 {
2280     struct gui_data *inst = (struct gui_data *)frontend;
2281
2282     if (p) {
2283         *p = inst->pastein_data;
2284         *len = inst->pastein_data_len;
2285     }
2286 }
2287
2288 static void set_window_titles(struct gui_data *inst)
2289 {
2290     /*
2291      * We must always call set_icon_name after calling set_title,
2292      * since set_title will write both names. Irritating, but such
2293      * is life.
2294      */
2295     gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
2296     if (!conf_get_int(inst->conf, CONF_win_name_always))
2297         gdk_window_set_icon_name(gtk_widget_get_window(inst->window),
2298                                  inst->icontitle);
2299 }
2300
2301 void set_title(void *frontend, char *title)
2302 {
2303     struct gui_data *inst = (struct gui_data *)frontend;
2304     sfree(inst->wintitle);
2305     inst->wintitle = dupstr(title);
2306     set_window_titles(inst);
2307 }
2308
2309 void set_icon(void *frontend, char *title)
2310 {
2311     struct gui_data *inst = (struct gui_data *)frontend;
2312     sfree(inst->icontitle);
2313     inst->icontitle = dupstr(title);
2314     set_window_titles(inst);
2315 }
2316
2317 void set_title_and_icon(void *frontend, char *title, char *icon)
2318 {
2319     struct gui_data *inst = (struct gui_data *)frontend;
2320     sfree(inst->wintitle);
2321     inst->wintitle = dupstr(title);
2322     sfree(inst->icontitle);
2323     inst->icontitle = dupstr(icon);
2324     set_window_titles(inst);
2325 }
2326
2327 void set_sbar(void *frontend, int total, int start, int page)
2328 {
2329     struct gui_data *inst = (struct gui_data *)frontend;
2330     if (!conf_get_int(inst->conf, CONF_scrollbar))
2331         return;
2332     gtk_adjustment_set_lower(inst->sbar_adjust, 0);
2333     gtk_adjustment_set_upper(inst->sbar_adjust, total);
2334     gtk_adjustment_set_value(inst->sbar_adjust, start);
2335     gtk_adjustment_set_page_size(inst->sbar_adjust, page);
2336     gtk_adjustment_set_step_increment(inst->sbar_adjust, 1);
2337     gtk_adjustment_set_page_increment(inst->sbar_adjust, page/2);
2338     inst->ignore_sbar = TRUE;
2339     gtk_adjustment_changed(inst->sbar_adjust);
2340     inst->ignore_sbar = FALSE;
2341 }
2342
2343 void scrollbar_moved(GtkAdjustment *adj, gpointer data)
2344 {
2345     struct gui_data *inst = (struct gui_data *)data;
2346
2347     if (!conf_get_int(inst->conf, CONF_scrollbar))
2348         return;
2349     if (!inst->ignore_sbar)
2350         term_scroll(inst->term, 1, (int)gtk_adjustment_get_value(adj));
2351 }
2352
2353 void sys_cursor(void *frontend, int x, int y)
2354 {
2355     /*
2356      * This is meaningless under X.
2357      */
2358 }
2359
2360 /*
2361  * This is still called when mode==BELL_VISUAL, even though the
2362  * visual bell is handled entirely within terminal.c, because we
2363  * may want to perform additional actions on any kind of bell (for
2364  * example, taskbar flashing in Windows).
2365  */
2366 void do_beep(void *frontend, int mode)
2367 {
2368     if (mode == BELL_DEFAULT)
2369         gdk_beep();
2370 }
2371
2372 int char_width(Context ctx, int uc)
2373 {
2374     /*
2375      * Under X, any fixed-width font really _is_ fixed-width.
2376      * Double-width characters will be dealt with using a separate
2377      * font. For the moment we can simply return 1.
2378      * 
2379      * FIXME: but is that also true of Pango?
2380      */
2381     return 1;
2382 }
2383
2384 Context get_ctx(void *frontend)
2385 {
2386     struct gui_data *inst = (struct gui_data *)frontend;
2387     struct draw_ctx *dctx;
2388     GdkWindow *target;
2389
2390     if (!gtk_widget_get_window(inst->area))
2391         return NULL;
2392
2393 #ifndef NO_BACKING_PIXMAPS
2394     target = inst->pixmap;
2395 #else
2396     target = gtk_widget_get_window(inst->area);
2397 #endif
2398
2399     dctx = snew(struct draw_ctx);
2400     dctx->inst = inst;
2401     dctx->uctx.type = inst->drawtype;
2402 #ifdef DRAW_TEXT_GDK
2403     if (dctx->uctx.type == DRAWTYPE_GDK) {
2404         dctx->uctx.u.gdk.target = target;
2405         dctx->uctx.u.gdk.gc = gdk_gc_new(gtk_widget_get_window(inst->area));
2406     }
2407 #endif
2408 #ifdef DRAW_TEXT_CAIRO
2409     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2410         dctx->uctx.u.cairo.widget = GTK_WIDGET(inst->area);
2411         dctx->uctx.u.cairo.cr = gdk_cairo_create(target);
2412         cairo_setup_dctx(dctx);
2413     }
2414 #endif
2415     return dctx;
2416 }
2417
2418 void free_ctx(Context ctx)
2419 {
2420     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2421     /* struct gui_data *inst = dctx->inst; */
2422 #ifdef DRAW_TEXT_GDK
2423     if (dctx->uctx.type == DRAWTYPE_GDK) {
2424         gdk_gc_unref(dctx->uctx.u.gdk.gc);
2425     }
2426 #endif
2427 #ifdef DRAW_TEXT_CAIRO
2428     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2429         cairo_destroy(dctx->uctx.u.cairo.cr);
2430     }
2431 #endif
2432     sfree(dctx);
2433 }
2434
2435
2436 static void draw_update(struct draw_ctx *dctx, int x, int y, int w, int h)
2437 {
2438 #ifndef NO_BACKING_PIXMAPS
2439 #ifdef DRAW_TEXT_GDK
2440     if (dctx->uctx.type == DRAWTYPE_GDK) {
2441         gdk_draw_pixmap(gtk_widget_get_window(dctx->inst->area),
2442                         dctx->uctx.u.gdk.gc, dctx->inst->pixmap,
2443                         x, y, x, y, w, h);
2444     }
2445 #endif
2446 #ifdef DRAW_TEXT_CAIRO /* FIXME: and not GTK3 where a cairo_t is all we have */
2447     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2448         GdkGC *gc = gdk_gc_new(gtk_widget_get_window(dctx->inst->area));
2449         gdk_draw_pixmap(gtk_widget_get_window(dctx->inst->area),
2450                         gc, dctx->inst->pixmap, x, y, x, y, w, h);
2451         gdk_gc_unref(gc);
2452     }
2453 #endif
2454 #endif
2455 }
2456
2457 static void draw_set_colour(struct draw_ctx *dctx, int col)
2458 {
2459 #ifdef DRAW_TEXT_GDK
2460     if (dctx->uctx.type == DRAWTYPE_GDK) {
2461         gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[col]);
2462     }
2463 #endif
2464 #ifdef DRAW_TEXT_CAIRO
2465     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2466         cairo_set_source_rgb(dctx->uctx.u.cairo.cr,
2467                              dctx->inst->cols[col].red / 65535.0,
2468                              dctx->inst->cols[col].green / 65535.0,
2469                              dctx->inst->cols[col].blue / 65535.0);
2470     }
2471 #endif
2472 }
2473
2474 static void draw_rectangle(struct draw_ctx *dctx, int filled,
2475                            int x, int y, int w, int h)
2476 {
2477 #ifdef DRAW_TEXT_GDK
2478     if (dctx->uctx.type == DRAWTYPE_GDK) {
2479         gdk_draw_rectangle(dctx->uctx.u.gdk.target, dctx->uctx.u.gdk.gc,
2480                            filled, x, y, w, h);
2481     }
2482 #endif
2483 #ifdef DRAW_TEXT_CAIRO
2484     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2485         cairo_new_path(dctx->uctx.u.cairo.cr);
2486         if (filled) {
2487             cairo_rectangle(dctx->uctx.u.cairo.cr, x, y, w, h);
2488             cairo_fill(dctx->uctx.u.cairo.cr);
2489         } else {
2490             cairo_rectangle(dctx->uctx.u.cairo.cr,
2491                             x + 0.5, y + 0.5, w, h);
2492             cairo_close_path(dctx->uctx.u.cairo.cr);
2493             cairo_stroke(dctx->uctx.u.cairo.cr);
2494         }
2495     }
2496 #endif
2497 }
2498
2499 static void draw_clip(struct draw_ctx *dctx, int x, int y, int w, int h)
2500 {
2501 #ifdef DRAW_TEXT_GDK
2502     if (dctx->uctx.type == DRAWTYPE_GDK) {
2503         GdkRectangle r;
2504
2505         r.x = x;
2506         r.y = y;
2507         r.width = w;
2508         r.height = h;
2509
2510         gdk_gc_set_clip_rectangle(dctx->uctx.u.gdk.gc, &r);
2511     }
2512 #endif
2513 #ifdef DRAW_TEXT_CAIRO
2514     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2515         cairo_reset_clip(dctx->uctx.u.cairo.cr);
2516         cairo_new_path(dctx->uctx.u.cairo.cr);
2517         cairo_rectangle(dctx->uctx.u.cairo.cr, x, y, w, h);
2518         cairo_clip(dctx->uctx.u.cairo.cr);
2519     }
2520 #endif
2521 }
2522
2523 static void draw_point(struct draw_ctx *dctx, int x, int y)
2524 {
2525 #ifdef DRAW_TEXT_GDK
2526     if (dctx->uctx.type == DRAWTYPE_GDK) {
2527         gdk_draw_point(dctx->uctx.u.gdk.target, dctx->uctx.u.gdk.gc, x, y);
2528     }
2529 #endif
2530 #ifdef DRAW_TEXT_CAIRO
2531     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2532         cairo_new_path(dctx->uctx.u.cairo.cr);
2533         cairo_rectangle(dctx->uctx.u.cairo.cr, x, y, 1, 1);
2534         cairo_fill(dctx->uctx.u.cairo.cr);
2535     }
2536 #endif
2537 }
2538
2539 static void draw_line(struct draw_ctx *dctx, int x0, int y0, int x1, int y1)
2540 {
2541 #ifdef DRAW_TEXT_GDK
2542     if (dctx->uctx.type == DRAWTYPE_GDK) {
2543         gdk_draw_line(dctx->uctx.u.gdk.target, dctx->uctx.u.gdk.gc,
2544                       x0, y0, x1, y1);
2545     }
2546 #endif
2547 #ifdef DRAW_TEXT_CAIRO
2548     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2549         cairo_new_path(dctx->uctx.u.cairo.cr);
2550         cairo_move_to(dctx->uctx.u.cairo.cr, x0 + 0.5, y0 + 0.5);
2551         cairo_line_to(dctx->uctx.u.cairo.cr, x1 + 0.5, y1 + 0.5);
2552         cairo_stroke(dctx->uctx.u.cairo.cr);
2553     }
2554 #endif
2555 }
2556
2557 static void draw_stretch_before(struct draw_ctx *dctx, int x, int y,
2558                                 int w, int wdouble,
2559                                 int h, int hdouble, int hbothalf)
2560 {
2561 #ifdef DRAW_TEXT_CAIRO
2562     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2563         cairo_matrix_t matrix;
2564
2565         matrix.xy = 0;
2566         matrix.yx = 0;
2567
2568         if (wdouble) {
2569             matrix.xx = 2;
2570             matrix.x0 = -x;
2571         } else {
2572             matrix.xx = 1;
2573             matrix.x0 = 0;
2574         }
2575
2576         if (hdouble) {
2577             matrix.yy = 2;
2578             if (hbothalf) {
2579                 matrix.y0 = -(y+h);
2580             } else {
2581                 matrix.y0 = -y;
2582             }
2583         } else {
2584             matrix.yy = 1;
2585             matrix.y0 = 0;
2586         }
2587         cairo_transform(dctx->uctx.u.cairo.cr, &matrix);
2588     }
2589 #endif
2590 }
2591
2592 static void draw_stretch_after(struct draw_ctx *dctx, int x, int y,
2593                                int w, int wdouble,
2594                                int h, int hdouble, int hbothalf)
2595 {
2596 #ifdef DRAW_TEXT_GDK
2597 #ifndef NO_BACKING_PIXMAPS
2598     if (dctx->uctx.type == DRAWTYPE_GDK) {
2599         /*
2600          * I can't find any plausible StretchBlt equivalent in the X
2601          * server, so I'm going to do this the slow and painful way.
2602          * This will involve repeated calls to gdk_draw_pixmap() to
2603          * stretch the text horizontally. It's O(N^2) in time and O(N)
2604          * in network bandwidth, but you try thinking of a better way.
2605          * :-(
2606          */
2607         int i;
2608         if (wdouble) {
2609             for (i = 0; i < w; i++) {
2610                 gdk_draw_pixmap(dctx->uctx.u.gdk.target,
2611                                 dctx->uctx.u.gdk.gc,
2612                                 dctx->uctx.u.gdk.target,
2613                                 x + 2*i, y,
2614                                 x + 2*i+1, y,
2615                                 w - i, h);
2616             }
2617             w *= 2;
2618         }
2619
2620         if (hdouble) {
2621             int dt, db;
2622             /* Now stretch vertically, in the same way. */
2623             if (hbothalf)
2624                 dt = 0, db = 1;
2625             else
2626                 dt = 1, db = 0;
2627             for (i = 0; i < h; i += 2) {
2628                 gdk_draw_pixmap(dctx->uctx.u.gdk.target,
2629                                 dctx->uctx.u.gdk.gc,
2630                                 dctx->uctx.u.gdk.target,
2631                                 x, y + dt*i + db,
2632                                 x, y + dt*(i+1),
2633                                 w, h-i-1);
2634             }
2635         }
2636     }
2637 #else
2638 #error No way to implement stretching in GDK without a reliable backing pixmap
2639 #endif
2640 #endif /* DRAW_TEXT_GDK */
2641 #ifdef DRAW_TEXT_CAIRO
2642     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2643         cairo_set_matrix(dctx->uctx.u.cairo.cr,
2644                          &dctx->uctx.u.cairo.origmatrix);
2645     }
2646 #endif
2647 }
2648
2649 static void draw_backing_rect(struct gui_data *inst)
2650 {
2651     struct draw_ctx *dctx = get_ctx(inst);
2652     draw_set_colour(dctx, 258);
2653     draw_rectangle(dctx, 1, 0, 0,
2654                    inst->width * inst->font_width + 2*inst->window_border,
2655                    inst->height * inst->font_height + 2*inst->window_border);
2656     free_ctx(dctx);
2657 }
2658
2659 /*
2660  * Draw a line of text in the window, at given character
2661  * coordinates, in given attributes.
2662  *
2663  * We are allowed to fiddle with the contents of `text'.
2664  */
2665 void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
2666                       unsigned long attr, int lattr)
2667 {
2668     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2669     struct gui_data *inst = dctx->inst;
2670     int ncombining, combining;
2671     int nfg, nbg, t, fontid, shadow, rlen, widefactor, bold;
2672     int monochrome =
2673         gdk_visual_get_depth(gtk_widget_get_visual(inst->area)) == 1;
2674
2675     if (attr & TATTR_COMBINING) {
2676         ncombining = len;
2677         len = 1;
2678     } else
2679         ncombining = 1;
2680
2681     nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT);
2682     nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT);
2683     if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) {
2684         t = nfg;
2685         nfg = nbg;
2686         nbg = t;
2687     }
2688     if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) {
2689         if (nfg < 16) nfg |= 8;
2690         else if (nfg >= 256) nfg |= 1;
2691     }
2692     if ((inst->bold_style & 2) && (attr & ATTR_BLINK)) {
2693         if (nbg < 16) nbg |= 8;
2694         else if (nbg >= 256) nbg |= 1;
2695     }
2696     if ((attr & TATTR_ACTCURS) && !monochrome) {
2697         nfg = 260;
2698         nbg = 261;
2699     }
2700
2701     fontid = shadow = 0;
2702
2703     if (attr & ATTR_WIDE) {
2704         widefactor = 2;
2705         fontid |= 2;
2706     } else {
2707         widefactor = 1;
2708     }
2709
2710     if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) {
2711         bold = 1;
2712         fontid |= 1;
2713     } else {
2714         bold = 0;
2715     }
2716
2717     if (!inst->fonts[fontid]) {
2718         int i;
2719         /*
2720          * Fall back through font ids with subsets of this one's
2721          * set bits, in order.
2722          */
2723         for (i = fontid; i-- > 0 ;) {
2724             if (i & ~fontid)
2725                 continue;              /* some other bit is set */
2726             if (inst->fonts[i]) {
2727                 fontid = i;
2728                 break;
2729             }
2730         }
2731         assert(inst->fonts[fontid]);   /* we should at least have hit zero */
2732     }
2733
2734     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2735         x *= 2;
2736         if (x >= inst->term->cols)
2737             return;
2738         if (x + len*2*widefactor > inst->term->cols)
2739             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
2740         rlen = len * 2;
2741     } else
2742         rlen = len;
2743
2744     draw_clip(dctx,
2745               x*inst->font_width+inst->window_border,
2746               y*inst->font_height+inst->window_border,
2747               rlen*widefactor*inst->font_width,
2748               inst->font_height);
2749
2750     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2751         draw_stretch_before(dctx,
2752                             x*inst->font_width+inst->window_border,
2753                             y*inst->font_height+inst->window_border,
2754                             rlen*widefactor*inst->font_width, TRUE,
2755                             inst->font_height,
2756                             ((lattr & LATTR_MODE) != LATTR_WIDE),
2757                             ((lattr & LATTR_MODE) == LATTR_BOT));
2758     }
2759
2760     draw_set_colour(dctx, nbg);
2761     draw_rectangle(dctx, TRUE,
2762                    x*inst->font_width+inst->window_border,
2763                    y*inst->font_height+inst->window_border,
2764                    rlen*widefactor*inst->font_width, inst->font_height);
2765
2766     draw_set_colour(dctx, nfg);
2767     for (combining = 0; combining < ncombining; combining++) {
2768         unifont_draw_text(&dctx->uctx, inst->fonts[fontid],
2769                           x*inst->font_width+inst->window_border,
2770                           y*inst->font_height+inst->window_border+inst->fonts[0]->ascent,
2771                           text + combining, len, widefactor > 1,
2772                           bold, inst->font_width);
2773     }
2774
2775     if (attr & ATTR_UNDER) {
2776         int uheight = inst->fonts[0]->ascent + 1;
2777         if (uheight >= inst->font_height)
2778             uheight = inst->font_height - 1;
2779         draw_line(dctx, x*inst->font_width+inst->window_border,
2780                   y*inst->font_height + uheight + inst->window_border,
2781                   (x+len)*widefactor*inst->font_width-1+inst->window_border,
2782                   y*inst->font_height + uheight + inst->window_border);
2783     }
2784
2785     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2786         draw_stretch_after(dctx,
2787                            x*inst->font_width+inst->window_border,
2788                            y*inst->font_height+inst->window_border,
2789                            rlen*widefactor*inst->font_width, TRUE,
2790                            inst->font_height,
2791                            ((lattr & LATTR_MODE) != LATTR_WIDE),
2792                            ((lattr & LATTR_MODE) == LATTR_BOT));
2793     }
2794 }
2795
2796 void do_text(Context ctx, int x, int y, wchar_t *text, int len,
2797              unsigned long attr, int lattr)
2798 {
2799     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2800     struct gui_data *inst = dctx->inst;
2801     int widefactor;
2802
2803     do_text_internal(ctx, x, y, text, len, attr, lattr);
2804
2805     if (attr & ATTR_WIDE) {
2806         widefactor = 2;
2807     } else {
2808         widefactor = 1;
2809     }
2810
2811     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2812         x *= 2;
2813         if (x >= inst->term->cols)
2814             return;
2815         if (x + len*2*widefactor > inst->term->cols)
2816             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
2817         len *= 2;
2818     }
2819
2820     draw_update(dctx,
2821                 x*inst->font_width+inst->window_border,
2822                 y*inst->font_height+inst->window_border,
2823                 len*widefactor*inst->font_width, inst->font_height);
2824 }
2825
2826 void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
2827                unsigned long attr, int lattr)
2828 {
2829     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2830     struct gui_data *inst = dctx->inst;
2831
2832     int active, passive, widefactor;
2833
2834     if (attr & TATTR_PASCURS) {
2835         attr &= ~TATTR_PASCURS;
2836         passive = 1;
2837     } else
2838         passive = 0;
2839     if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) {
2840         attr &= ~TATTR_ACTCURS;
2841         active = 1;
2842     } else
2843         active = 0;
2844     do_text_internal(ctx, x, y, text, len, attr, lattr);
2845
2846     if (attr & TATTR_COMBINING)
2847         len = 1;
2848
2849     if (attr & ATTR_WIDE) {
2850         widefactor = 2;
2851     } else {
2852         widefactor = 1;
2853     }
2854
2855     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2856         x *= 2;
2857         if (x >= inst->term->cols)
2858             return;
2859         if (x + len*2*widefactor > inst->term->cols)
2860             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
2861         len *= 2;
2862     }
2863
2864     if (inst->cursor_type == 0) {
2865         /*
2866          * An active block cursor will already have been done by
2867          * the above do_text call, so we only need to do anything
2868          * if it's passive.
2869          */
2870         if (passive) {
2871             draw_set_colour(dctx, 261);
2872             draw_rectangle(dctx, FALSE,
2873                            x*inst->font_width+inst->window_border,
2874                            y*inst->font_height+inst->window_border,
2875                            len*widefactor*inst->font_width-1,
2876                            inst->font_height-1);
2877         }
2878     } else {
2879         int uheight;
2880         int startx, starty, dx, dy, length, i;
2881
2882         int char_width;
2883
2884         if ((attr & ATTR_WIDE) || (lattr & LATTR_MODE) != LATTR_NORM)
2885             char_width = 2*inst->font_width;
2886         else
2887             char_width = inst->font_width;
2888
2889         if (inst->cursor_type == 1) {
2890             uheight = inst->fonts[0]->ascent + 1;
2891             if (uheight >= inst->font_height)
2892                 uheight = inst->font_height - 1;
2893
2894             startx = x * inst->font_width + inst->window_border;
2895             starty = y * inst->font_height + inst->window_border + uheight;
2896             dx = 1;
2897             dy = 0;
2898             length = len * widefactor * char_width;
2899         } else {
2900             int xadjust = 0;
2901             if (attr & TATTR_RIGHTCURS)
2902                 xadjust = char_width - 1;
2903             startx = x * inst->font_width + inst->window_border + xadjust;
2904             starty = y * inst->font_height + inst->window_border;
2905             dx = 0;
2906             dy = 1;
2907             length = inst->font_height;
2908         }
2909
2910         draw_set_colour(dctx, 261);
2911         if (passive) {
2912             for (i = 0; i < length; i++) {
2913                 if (i % 2 == 0) {
2914                     draw_point(dctx, startx, starty);
2915                 }
2916                 startx += dx;
2917                 starty += dy;
2918             }
2919         } else if (active) {
2920             draw_line(dctx, startx, starty,
2921                       startx + (length-1) * dx, starty + (length-1) * dy);
2922         } /* else no cursor (e.g., blinked off) */
2923     }
2924
2925     draw_update(dctx,
2926                 x*inst->font_width+inst->window_border,
2927                 y*inst->font_height+inst->window_border,
2928                 len*widefactor*inst->font_width, inst->font_height);
2929
2930 #if GTK_CHECK_VERSION(2,0,0)
2931     {
2932         GdkRectangle cursorrect;
2933         cursorrect.x = x*inst->font_width+inst->window_border;
2934         cursorrect.y = y*inst->font_height+inst->window_border;
2935         cursorrect.width = len*widefactor*inst->font_width;
2936         cursorrect.height = inst->font_height;
2937         gtk_im_context_set_cursor_location(inst->imc, &cursorrect);
2938     }
2939 #endif
2940 }
2941
2942 GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
2943 {
2944     if (cursor_val == -1) {
2945 #if GTK_CHECK_VERSION(2,16,0)
2946         cursor_val = GDK_BLANK_CURSOR;
2947 #else
2948         /*
2949          * Work around absence of GDK_BLANK_CURSOR by inventing a
2950          * blank pixmap.
2951          */
2952         GdkCursor *ret;
2953         GdkColor bg = { 0, 0, 0, 0 };
2954         GdkPixmap *pm = gdk_pixmap_new(NULL, 1, 1, 1);
2955         GdkGC *gc = gdk_gc_new(pm);
2956         gdk_gc_set_foreground(gc, &bg);
2957         gdk_draw_rectangle(pm, gc, 1, 0, 0, 1, 1);
2958         gdk_gc_unref(gc);
2959         ret = gdk_cursor_new_from_pixmap(pm, pm, &bg, &bg, 1, 1);
2960         gdk_pixmap_unref(pm);
2961         return ret;
2962 #endif
2963     }
2964
2965     return gdk_cursor_new(cursor_val);
2966 }
2967
2968 void modalfatalbox(const char *p, ...)
2969 {
2970     va_list ap;
2971     fprintf(stderr, "FATAL ERROR: ");
2972     va_start(ap, p);
2973     vfprintf(stderr, p, ap);
2974     va_end(ap);
2975     fputc('\n', stderr);
2976     exit(1);
2977 }
2978
2979 void cmdline_error(const char *p, ...)
2980 {
2981     va_list ap;
2982     fprintf(stderr, "%s: ", appname);
2983     va_start(ap, p);
2984     vfprintf(stderr, p, ap);
2985     va_end(ap);
2986     fputc('\n', stderr);
2987     exit(1);
2988 }
2989
2990 char *get_x_display(void *frontend)
2991 {
2992     return gdk_get_display();
2993 }
2994
2995 #ifndef NOT_X_WINDOWS
2996 long get_windowid(void *frontend)
2997 {
2998     struct gui_data *inst = (struct gui_data *)frontend;
2999     return (long)GDK_WINDOW_XWINDOW(gtk_widget_get_window(inst->area));
3000 }
3001 #endif
3002
3003 static void help(FILE *fp) {
3004     if(fprintf(fp,
3005 "pterm option summary:\n"
3006 "\n"
3007 "  --display DISPLAY         Specify X display to use (note '--')\n"
3008 "  -name PREFIX              Prefix when looking up resources (default: pterm)\n"
3009 "  -fn FONT                  Normal text font\n"
3010 "  -fb FONT                  Bold text font\n"
3011 "  -geometry GEOMETRY        Position and size of window (size in characters)\n"
3012 "  -sl LINES                 Number of lines of scrollback\n"
3013 "  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"
3014 "  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"
3015 "  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"
3016 "  -T TITLE                  Window title\n"
3017 "  -ut, +ut                  Do(default) or do not update utmp\n"
3018 "  -ls, +ls                  Do(default) or do not make shell a login shell\n"
3019 "  -sb, +sb                  Do(default) or do not display a scrollbar\n"
3020 "  -log PATH                 Log all output to a file\n"
3021 "  -nethack                  Map numeric keypad to hjklyubn direction keys\n"
3022 "  -xrm RESOURCE-STRING      Set an X resource\n"
3023 "  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"
3024          ) < 0 || fflush(fp) < 0) {
3025         perror("output error");
3026         exit(1);
3027     }
3028 }
3029
3030 static void version(FILE *fp) {
3031     if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
3032         perror("output error");
3033         exit(1);
3034     }
3035 }
3036
3037 int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
3038                struct gui_data *inst, Conf *conf)
3039 {
3040     int err = 0;
3041     char *val;
3042
3043     /*
3044      * Macros to make argument handling easier. Note that because
3045      * they need to call `continue', they cannot be contained in
3046      * the usual do {...} while (0) wrapper to make them
3047      * syntactically single statements; hence it is not legal to
3048      * use one of these macros as an unbraced statement between
3049      * `if' and `else'.
3050      */
3051 #define EXPECTS_ARG { \
3052     if (--argc <= 0) { \
3053         err = 1; \
3054         fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
3055         continue; \
3056     } else \
3057         val = *++argv; \
3058 }
3059 #define SECOND_PASS_ONLY { if (!do_everything) continue; }
3060
3061     while (--argc > 0) {
3062         const char *p = *++argv;
3063         int ret;
3064
3065         /*
3066          * Shameless cheating. Debian requires all X terminal
3067          * emulators to support `-T title'; but
3068          * cmdline_process_param will eat -T (it means no-pty) and
3069          * complain that pterm doesn't support it. So, in pterm
3070          * only, we convert -T into -title.
3071          */
3072         if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
3073             !strcmp(p, "-T"))
3074             p = "-title";
3075
3076         ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
3077                                     do_everything ? 1 : -1, conf);
3078
3079         if (ret == -2) {
3080             cmdline_error("option \"%s\" requires an argument", p);
3081         } else if (ret == 2) {
3082             --argc, ++argv;            /* skip next argument */
3083             continue;
3084         } else if (ret == 1) {
3085             continue;
3086         }
3087
3088         if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
3089             FontSpec *fs;
3090             EXPECTS_ARG;
3091             SECOND_PASS_ONLY;
3092             fs = fontspec_new(val);
3093             conf_set_fontspec(conf, CONF_font, fs);
3094             fontspec_free(fs);
3095
3096         } else if (!strcmp(p, "-fb")) {
3097             FontSpec *fs;
3098             EXPECTS_ARG;
3099             SECOND_PASS_ONLY;
3100             fs = fontspec_new(val);
3101             conf_set_fontspec(conf, CONF_boldfont, fs);
3102             fontspec_free(fs);
3103
3104         } else if (!strcmp(p, "-fw")) {
3105             FontSpec *fs;
3106             EXPECTS_ARG;
3107             SECOND_PASS_ONLY;
3108             fs = fontspec_new(val);
3109             conf_set_fontspec(conf, CONF_widefont, fs);
3110             fontspec_free(fs);
3111
3112         } else if (!strcmp(p, "-fwb")) {
3113             FontSpec *fs;
3114             EXPECTS_ARG;
3115             SECOND_PASS_ONLY;
3116             fs = fontspec_new(val);
3117             conf_set_fontspec(conf, CONF_wideboldfont, fs);
3118             fontspec_free(fs);
3119
3120         } else if (!strcmp(p, "-cs")) {
3121             EXPECTS_ARG;
3122             SECOND_PASS_ONLY;
3123             conf_set_str(conf, CONF_line_codepage, val);
3124
3125         } else if (!strcmp(p, "-geometry")) {
3126             EXPECTS_ARG;
3127             SECOND_PASS_ONLY;
3128
3129 #if GTK_CHECK_VERSION(2,0,0)
3130             inst->geometry = val;
3131 #else
3132             /* On GTK 1, we have to do this using raw Xlib */
3133             {
3134                 int flags, x, y;
3135                 unsigned int w, h;
3136                 flags = XParseGeometry(val, &x, &y, &w, &h);
3137                 if (flags & WidthValue)
3138                     conf_set_int(conf, CONF_width, w);
3139                 if (flags & HeightValue)
3140                     conf_set_int(conf, CONF_height, h);
3141
3142                 if (flags & (XValue | YValue)) {
3143                     inst->xpos = x;
3144                     inst->ypos = y;
3145                     inst->gotpos = TRUE;
3146                     inst->gravity = ((flags & XNegative ? 1 : 0) |
3147                                      (flags & YNegative ? 2 : 0));
3148                 }
3149             }
3150 #endif
3151
3152         } else if (!strcmp(p, "-sl")) {
3153             EXPECTS_ARG;
3154             SECOND_PASS_ONLY;
3155             conf_set_int(conf, CONF_savelines, atoi(val));
3156
3157         } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
3158                    !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
3159                    !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
3160             GdkColor col;
3161
3162             EXPECTS_ARG;
3163             SECOND_PASS_ONLY;
3164             if (!gdk_color_parse(val, &col)) {
3165                 err = 1;
3166                 fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
3167                         appname, val);
3168             } else {
3169                 int index;
3170                 index = (!strcmp(p, "-fg") ? 0 :
3171                          !strcmp(p, "-bg") ? 2 :
3172                          !strcmp(p, "-bfg") ? 1 :
3173                          !strcmp(p, "-bbg") ? 3 :
3174                          !strcmp(p, "-cfg") ? 4 :
3175                          !strcmp(p, "-cbg") ? 5 : -1);
3176                 assert(index != -1);
3177                 conf_set_int_int(conf, CONF_colours, index*3+0, col.red / 256);
3178                 conf_set_int_int(conf, CONF_colours, index*3+1,col.green/ 256);
3179                 conf_set_int_int(conf, CONF_colours, index*3+2, col.blue/ 256);
3180             }
3181
3182         } else if (use_pty_argv && !strcmp(p, "-e")) {
3183             /* This option swallows all further arguments. */
3184             if (!do_everything)
3185                 break;
3186
3187             if (--argc > 0) {
3188                 int i;
3189                 pty_argv = snewn(argc+1, char *);
3190                 ++argv;
3191                 for (i = 0; i < argc; i++)
3192                     pty_argv[i] = argv[i];
3193                 pty_argv[argc] = NULL;
3194                 break;                 /* finished command-line processing */
3195             } else
3196                 err = 1, fprintf(stderr, "%s: -e expects an argument\n",
3197                                  appname);
3198
3199         } else if (!strcmp(p, "-title")) {
3200             EXPECTS_ARG;
3201             SECOND_PASS_ONLY;
3202             conf_set_str(conf, CONF_wintitle, val);
3203
3204         } else if (!strcmp(p, "-log")) {
3205             Filename *fn;
3206             EXPECTS_ARG;
3207             SECOND_PASS_ONLY;
3208             fn = filename_from_str(val);
3209             conf_set_filename(conf, CONF_logfilename, fn);
3210             conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
3211             filename_free(fn);
3212
3213         } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
3214             SECOND_PASS_ONLY;
3215             conf_set_int(conf, CONF_stamp_utmp, 0);
3216
3217         } else if (!strcmp(p, "-ut")) {
3218             SECOND_PASS_ONLY;
3219             conf_set_int(conf, CONF_stamp_utmp, 1);
3220
3221         } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
3222             SECOND_PASS_ONLY;
3223             conf_set_int(conf, CONF_login_shell, 0);
3224
3225         } else if (!strcmp(p, "-ls")) {
3226             SECOND_PASS_ONLY;
3227             conf_set_int(conf, CONF_login_shell, 1);
3228
3229         } else if (!strcmp(p, "-nethack")) {
3230             SECOND_PASS_ONLY;
3231             conf_set_int(conf, CONF_nethack_keypad, 1);
3232
3233         } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
3234             SECOND_PASS_ONLY;
3235             conf_set_int(conf, CONF_scrollbar, 0);
3236
3237         } else if (!strcmp(p, "-sb")) {
3238             SECOND_PASS_ONLY;
3239             conf_set_int(conf, CONF_scrollbar, 1);
3240
3241         } else if (!strcmp(p, "-name")) {
3242             EXPECTS_ARG;
3243             app_name = val;
3244
3245         } else if (!strcmp(p, "-xrm")) {
3246             EXPECTS_ARG;
3247             provide_xrm_string(val);
3248
3249         } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
3250             help(stdout);
3251             exit(0);
3252
3253         } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
3254             version(stdout);
3255             exit(0);
3256
3257         } else if (!strcmp(p, "-pgpfp")) {
3258             pgp_fingerprints();
3259             exit(1);
3260
3261         } else if(p[0] != '-' && (!do_everything ||
3262                                   process_nonoption_arg(p, conf,
3263                                                         allow_launch))) {
3264             /* do nothing */
3265
3266         } else {
3267             err = 1;
3268             fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
3269         }
3270     }
3271
3272     return err;
3273 }
3274
3275 struct uxsel_id {
3276 #if GTK_CHECK_VERSION(2,0,0)
3277     GIOChannel *chan;
3278     guint watch_id;
3279 #else
3280     int id;
3281 #endif
3282 };
3283
3284 uxsel_id *uxsel_input_add(int fd, int rwx) {
3285     uxsel_id *id = snew(uxsel_id);
3286
3287 #if GTK_CHECK_VERSION(2,0,0)
3288     int flags = 0;
3289     if (rwx & 1) flags |= G_IO_IN;
3290     if (rwx & 2) flags |= G_IO_OUT;
3291     if (rwx & 4) flags |= G_IO_PRI;
3292     id->chan = g_io_channel_unix_new(fd);
3293     g_io_channel_set_encoding(id->chan, NULL, NULL);
3294     id->watch_id = g_io_add_watch(id->chan, flags, fd_input_func, NULL);
3295 #else
3296     int flags = 0;
3297     if (rwx & 1) flags |= GDK_INPUT_READ;
3298     if (rwx & 2) flags |= GDK_INPUT_WRITE;
3299     if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
3300     assert(flags);
3301     id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
3302 #endif
3303
3304     return id;
3305 }
3306
3307 void uxsel_input_remove(uxsel_id *id) {
3308 #if GTK_CHECK_VERSION(2,0,0)
3309     g_source_remove(id->watch_id);
3310     g_io_channel_unref(id->chan);
3311 #else
3312     gdk_input_remove(id->id);
3313 #endif
3314     sfree(id);
3315 }
3316
3317 char *setup_fonts_ucs(struct gui_data *inst)
3318 {
3319     int shadowbold = conf_get_int(inst->conf, CONF_shadowbold);
3320     int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset);
3321     FontSpec *fs;
3322     unifont *fonts[4];
3323     int i;
3324
3325     fs = conf_get_fontspec(inst->conf, CONF_font);
3326     fonts[0] = multifont_create(inst->area, fs->name, FALSE, FALSE,
3327                                 shadowboldoffset, shadowbold);
3328     if (!fonts[0]) {
3329         return dupprintf("unable to load font \"%s\"", fs->name);
3330     }
3331
3332     fs = conf_get_fontspec(inst->conf, CONF_boldfont);
3333     if (shadowbold || !fs->name[0]) {
3334         fonts[1] = NULL;
3335     } else {
3336         fonts[1] = multifont_create(inst->area, fs->name, FALSE, TRUE,
3337                                     shadowboldoffset, shadowbold);
3338         if (!fonts[1]) {
3339             if (fonts[0])
3340                 unifont_destroy(fonts[0]);
3341             return dupprintf("unable to load bold font \"%s\"", fs->name);
3342         }
3343     }
3344
3345     fs = conf_get_fontspec(inst->conf, CONF_widefont);
3346     if (fs->name[0]) {
3347         fonts[2] = multifont_create(inst->area, fs->name, TRUE, FALSE,
3348                                     shadowboldoffset, shadowbold);
3349         if (!fonts[2]) {
3350             for (i = 0; i < 2; i++)
3351                 if (fonts[i])
3352                     unifont_destroy(fonts[i]);
3353             return dupprintf("unable to load wide font \"%s\"", fs->name);
3354         }
3355     } else {
3356         fonts[2] = NULL;
3357     }
3358
3359     fs = conf_get_fontspec(inst->conf, CONF_wideboldfont);
3360     if (shadowbold || !fs->name[0]) {
3361         fonts[3] = NULL;
3362     } else {
3363         fonts[3] = multifont_create(inst->area, fs->name, TRUE, TRUE,
3364                                     shadowboldoffset, shadowbold);
3365         if (!fonts[3]) {
3366             for (i = 0; i < 3; i++)
3367                 if (fonts[i])
3368                     unifont_destroy(fonts[i]);
3369             return dupprintf("unable to load wide bold font \"%s\"", fs->name);
3370         }
3371     }
3372
3373     /*
3374      * Now we've got past all the possible error conditions, we can
3375      * actually update our state.
3376      */
3377
3378     for (i = 0; i < 4; i++) {
3379         if (inst->fonts[i])
3380             unifont_destroy(inst->fonts[i]);
3381         inst->fonts[i] = fonts[i];
3382     }
3383
3384     inst->font_width = inst->fonts[0]->width;
3385     inst->font_height = inst->fonts[0]->height;
3386
3387     inst->direct_to_font = init_ucs(&inst->ucsdata,
3388                                     conf_get_str(inst->conf, CONF_line_codepage),
3389                                     conf_get_int(inst->conf, CONF_utf8_override),
3390                                     inst->fonts[0]->public_charset,
3391                                     conf_get_int(inst->conf, CONF_vtmode));
3392
3393     inst->drawtype = inst->fonts[0]->preferred_drawtype;
3394
3395     return NULL;
3396 }
3397
3398 void set_geom_hints(struct gui_data *inst)
3399 {
3400     GdkGeometry geom;
3401     geom.min_width = inst->font_width + 2*inst->window_border;
3402     geom.min_height = inst->font_height + 2*inst->window_border;
3403     geom.max_width = geom.max_height = -1;
3404     geom.base_width = 2*inst->window_border;
3405     geom.base_height = 2*inst->window_border;
3406     geom.width_inc = inst->font_width;
3407     geom.height_inc = inst->font_height;
3408     geom.min_aspect = geom.max_aspect = 0;
3409     gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,
3410                                   GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
3411                                   GDK_HINT_RESIZE_INC);
3412 }
3413
3414 void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)
3415 {
3416     struct gui_data *inst = (struct gui_data *)data;
3417     term_clrsb(inst->term);
3418 }
3419
3420 void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
3421 {
3422     struct gui_data *inst = (struct gui_data *)data;
3423     term_pwron(inst->term, TRUE);
3424     if (inst->ldisc)
3425         ldisc_echoedit_update(inst->ldisc);
3426 }
3427
3428 void copy_all_menuitem(GtkMenuItem *item, gpointer data)
3429 {
3430     struct gui_data *inst = (struct gui_data *)data;
3431     term_copyall(inst->term);
3432 }
3433
3434 void special_menuitem(GtkMenuItem *item, gpointer data)
3435 {
3436     struct gui_data *inst = (struct gui_data *)data;
3437     int code = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
3438                                                  "user-data"));
3439
3440     if (inst->back)
3441         inst->back->special(inst->backhandle, code);
3442 }
3443
3444 void about_menuitem(GtkMenuItem *item, gpointer data)
3445 {
3446     struct gui_data *inst = (struct gui_data *)data;
3447     about_box(inst->window);
3448 }
3449
3450 void event_log_menuitem(GtkMenuItem *item, gpointer data)
3451 {
3452     struct gui_data *inst = (struct gui_data *)data;
3453     showeventlog(inst->eventlogstuff, inst->window);
3454 }
3455
3456 void change_settings_menuitem(GtkMenuItem *item, gpointer data)
3457 {
3458     /* This maps colour indices in inst->conf to those used in inst->cols. */
3459     static const int ww[] = {
3460         256, 257, 258, 259, 260, 261,
3461         0, 8, 1, 9, 2, 10, 3, 11,
3462         4, 12, 5, 13, 6, 14, 7, 15
3463     };
3464     struct gui_data *inst = (struct gui_data *)data;
3465     char *title;
3466     Conf *oldconf, *newconf;
3467     int i, j, need_size;
3468
3469     assert(lenof(ww) == NCFGCOLOURS);
3470
3471     if (inst->reconfiguring)
3472       return;
3473     else
3474       inst->reconfiguring = TRUE;
3475
3476     title = dupcat(appname, " Reconfiguration", NULL);
3477
3478     oldconf = inst->conf;
3479     newconf = conf_copy(inst->conf);
3480
3481     if (do_config_box(title, newconf, 1,
3482                       inst->back?inst->back->cfg_info(inst->backhandle):0)) {
3483         inst->conf = newconf;
3484
3485         /* Pass new config data to the logging module */
3486         log_reconfig(inst->logctx, inst->conf);
3487         /*
3488          * Flush the line discipline's edit buffer in the case
3489          * where local editing has just been disabled.
3490          */
3491         if (inst->ldisc) {
3492             ldisc_configure(inst->ldisc, inst->conf);
3493             ldisc_echoedit_update(inst->ldisc);
3494         }
3495         /* Pass new config data to the terminal */
3496         term_reconfig(inst->term, inst->conf);
3497         /* Pass new config data to the back end */
3498         if (inst->back)
3499             inst->back->reconfig(inst->backhandle, inst->conf);
3500
3501         cache_conf_values(inst);
3502
3503         /*
3504          * Just setting inst->conf is sufficient to cause colour
3505          * setting changes to appear on the next ESC]R palette
3506          * reset. But we should also check whether any colour
3507          * settings have been changed, and revert the ones that have
3508          * to the new default, on the assumption that the user is
3509          * most likely to want an immediate update.
3510          */
3511         for (i = 0; i < NCFGCOLOURS; i++) {
3512             for (j = 0; j < 3; j++)
3513                 if (conf_get_int_int(oldconf, CONF_colours, i*3+j) !=
3514                     conf_get_int_int(newconf, CONF_colours, i*3+j))
3515                     break;
3516             if (j < 3) {
3517                 real_palette_set(inst, ww[i],
3518                                  conf_get_int_int(newconf,CONF_colours,i*3+0),
3519                                  conf_get_int_int(newconf,CONF_colours,i*3+1),
3520                                  conf_get_int_int(newconf,CONF_colours,i*3+2));
3521
3522                 /*
3523                  * If the default background has changed, we must
3524                  * repaint the space in between the window border
3525                  * and the text area.
3526                  */
3527                 if (ww[i] == 258) {
3528                     set_window_background(inst);
3529                     draw_backing_rect(inst);
3530                 }
3531             }
3532         }
3533
3534         /*
3535          * If the scrollbar needs to be shown, hidden, or moved
3536          * from one end to the other of the window, do so now.
3537          */
3538         if (conf_get_int(oldconf, CONF_scrollbar) !=
3539             conf_get_int(newconf, CONF_scrollbar)) {
3540             if (conf_get_int(newconf, CONF_scrollbar))
3541                 gtk_widget_show(inst->sbar);
3542             else
3543                 gtk_widget_hide(inst->sbar);
3544         }
3545         if (conf_get_int(oldconf, CONF_scrollbar_on_left) !=
3546             conf_get_int(newconf, CONF_scrollbar_on_left)) {
3547             gtk_box_reorder_child(inst->hbox, inst->sbar,
3548                                   conf_get_int(newconf, CONF_scrollbar_on_left)
3549                                   ? 0 : 1);
3550         }
3551
3552         /*
3553          * Change the window title, if required.
3554          */
3555         if (strcmp(conf_get_str(oldconf, CONF_wintitle),
3556                    conf_get_str(newconf, CONF_wintitle)))
3557             set_title(inst, conf_get_str(newconf, CONF_wintitle));
3558         set_window_titles(inst);
3559
3560         /*
3561          * Redo the whole tangled fonts and Unicode mess if
3562          * necessary.
3563          */
3564         need_size = FALSE;
3565         if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name,
3566                    conf_get_fontspec(newconf, CONF_font)->name) ||
3567             strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name,
3568                    conf_get_fontspec(newconf, CONF_boldfont)->name) ||
3569             strcmp(conf_get_fontspec(oldconf, CONF_widefont)->name,
3570                    conf_get_fontspec(newconf, CONF_widefont)->name) ||
3571             strcmp(conf_get_fontspec(oldconf, CONF_wideboldfont)->name,
3572                    conf_get_fontspec(newconf, CONF_wideboldfont)->name) ||
3573             strcmp(conf_get_str(oldconf, CONF_line_codepage),
3574                    conf_get_str(newconf, CONF_line_codepage)) ||
3575             conf_get_int(oldconf, CONF_utf8_override) !=
3576             conf_get_int(newconf, CONF_utf8_override) ||
3577             conf_get_int(oldconf, CONF_vtmode) !=
3578             conf_get_int(newconf, CONF_vtmode) ||
3579             conf_get_int(oldconf, CONF_shadowbold) !=
3580             conf_get_int(newconf, CONF_shadowbold) ||
3581             conf_get_int(oldconf, CONF_shadowboldoffset) !=
3582             conf_get_int(newconf, CONF_shadowboldoffset)) {
3583             char *errmsg = setup_fonts_ucs(inst);
3584             if (errmsg) {
3585                 char *msgboxtext =
3586                     dupprintf("Could not change fonts in terminal window: %s\n",
3587                               errmsg);
3588                 messagebox(inst->window, "Font setup error", msgboxtext,
3589                            string_width("Could not change fonts in terminal window:"),
3590                            "OK", 'o', +1, 1,
3591                            NULL);
3592                 sfree(msgboxtext);
3593                 sfree(errmsg);
3594             } else {
3595                 need_size = TRUE;
3596             }
3597         }
3598
3599         /*
3600          * Resize the window.
3601          */
3602         if (conf_get_int(oldconf, CONF_width) !=
3603             conf_get_int(newconf, CONF_width) ||
3604             conf_get_int(oldconf, CONF_height) !=
3605             conf_get_int(newconf, CONF_height) ||
3606             conf_get_int(oldconf, CONF_window_border) !=
3607             conf_get_int(newconf, CONF_window_border) ||
3608             need_size) {
3609             set_geom_hints(inst);
3610             request_resize(inst, conf_get_int(newconf, CONF_width),
3611                            conf_get_int(newconf, CONF_height));
3612         } else {
3613             /*
3614              * The above will have caused a call to term_size() for
3615              * us if it happened. If the user has fiddled with only
3616              * the scrollback size, the above will not have
3617              * happened and we will need an explicit term_size()
3618              * here.
3619              */
3620             if (conf_get_int(oldconf, CONF_savelines) !=
3621                 conf_get_int(newconf, CONF_savelines))
3622                 term_size(inst->term, inst->term->rows, inst->term->cols,
3623                           conf_get_int(newconf, CONF_savelines));
3624         }
3625
3626         term_invalidate(inst->term);
3627
3628         /*
3629          * We do an explicit full redraw here to ensure the window
3630          * border has been redrawn as well as the text area.
3631          */
3632         gtk_widget_queue_draw(inst->area);
3633
3634         conf_free(oldconf);
3635     } else {
3636         conf_free(newconf);
3637     }
3638     sfree(title);
3639     inst->reconfiguring = FALSE;
3640 }
3641
3642 void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)
3643 {
3644     /*
3645      * Re-execing ourself is not an exact science under Unix. I do
3646      * the best I can by using /proc/self/exe if available and by
3647      * assuming argv[0] can be found on $PATH if not.
3648      * 
3649      * Note that we also have to reconstruct the elements of the
3650      * original argv which gtk swallowed, since the user wants the
3651      * new session to appear on the same X display as the old one.
3652      */
3653     char **args;
3654     va_list ap;
3655     int i, n;
3656     int pid;
3657
3658     /*
3659      * Collect the arguments with which to re-exec ourself.
3660      */
3661     va_start(ap, fd_to_close);
3662     n = 2;                             /* progname and terminating NULL */
3663     n += inst->ngtkargs;
3664     while (va_arg(ap, char *) != NULL)
3665         n++;
3666     va_end(ap);
3667
3668     args = snewn(n, char *);
3669     args[0] = inst->progname;
3670     args[n-1] = NULL;
3671     for (i = 0; i < inst->ngtkargs; i++)
3672         args[i+1] = inst->gtkargvstart[i];
3673
3674     i++;
3675     va_start(ap, fd_to_close);
3676     while ((args[i++] = va_arg(ap, char *)) != NULL);
3677     va_end(ap);
3678
3679     assert(i == n);
3680
3681     /*
3682      * Do the double fork.
3683      */
3684     pid = fork();
3685     if (pid < 0) {
3686         perror("fork");
3687         sfree(args);
3688         return;
3689     }
3690
3691     if (pid == 0) {
3692         int pid2 = fork();
3693         if (pid2 < 0) {
3694             perror("fork");
3695             _exit(1);
3696         } else if (pid2 > 0) {
3697             /*
3698              * First child has successfully forked second child. My
3699              * Work Here Is Done. Note the use of _exit rather than
3700              * exit: the latter appears to cause destroy messages
3701              * to be sent to the X server. I suspect gtk uses
3702              * atexit.
3703              */
3704             _exit(0);
3705         }
3706
3707         /*
3708          * If we reach here, we are the second child, so we now
3709          * actually perform the exec.
3710          */
3711         if (fd_to_close >= 0)
3712             close(fd_to_close);
3713
3714         execv("/proc/self/exe", args);
3715         execvp(inst->progname, args);
3716         perror("exec");
3717         _exit(127);
3718
3719     } else {
3720         int status;
3721         sfree(args);
3722         waitpid(pid, &status, 0);
3723     }
3724
3725 }
3726
3727 void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
3728 {
3729     struct gui_data *inst = (struct gui_data *)gdata;
3730     /*
3731      * For this feature we must marshal conf and (possibly) pty_argv
3732      * into a byte stream, create a pipe, and send this byte stream
3733      * to the child through the pipe.
3734      */
3735     int i, ret, sersize, size;
3736     char *data;
3737     char option[80];
3738     int pipefd[2];
3739
3740     if (pipe(pipefd) < 0) {
3741         perror("pipe");
3742         return;
3743     }
3744
3745     size = sersize = conf_serialised_size(inst->conf);
3746     if (use_pty_argv && pty_argv) {
3747         for (i = 0; pty_argv[i]; i++)
3748             size += strlen(pty_argv[i]) + 1;
3749     }
3750
3751     data = snewn(size, char);
3752     conf_serialise(inst->conf, data);
3753     if (use_pty_argv && pty_argv) {
3754         int p = sersize;
3755         for (i = 0; pty_argv[i]; i++) {
3756             strcpy(data + p, pty_argv[i]);
3757             p += strlen(pty_argv[i]) + 1;
3758         }
3759         assert(p == size);
3760     }
3761
3762     sprintf(option, "---[%d,%d]", pipefd[0], size);
3763     noncloexec(pipefd[0]);
3764     fork_and_exec_self(inst, pipefd[1], option, NULL);
3765     close(pipefd[0]);
3766
3767     i = ret = 0;
3768     while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
3769         i += ret;
3770     if (ret < 0)
3771         perror("write to pipe");
3772     close(pipefd[1]);
3773     sfree(data);
3774 }
3775
3776 int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg)
3777 {
3778     int fd, i, ret, size, size_used;
3779     char *data;
3780
3781     if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
3782         fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
3783         exit(1);
3784     }
3785
3786     data = snewn(size, char);
3787     i = ret = 0;
3788     while (i < size && (ret = read(fd, data + i, size - i)) > 0)
3789         i += ret;
3790     if (ret < 0) {
3791         perror("read from pipe");
3792         exit(1);
3793     } else if (i < size) {
3794         fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
3795                 appname);
3796         exit(1);
3797     }
3798
3799     size_used = conf_deserialise(conf, data, size);
3800     if (use_pty_argv && size > size_used) {
3801         int n = 0;
3802         i = size_used;
3803         while (i < size) {
3804             while (i < size && data[i]) i++;
3805             if (i >= size) {
3806                 fprintf(stderr, "%s: malformed Duplicate Session data\n",
3807                         appname);
3808                 exit(1);
3809             }
3810             i++;
3811             n++;
3812         }
3813         pty_argv = snewn(n+1, char *);
3814         pty_argv[n] = NULL;
3815         n = 0;
3816         i = size_used;
3817         while (i < size) {
3818             char *p = data + i;
3819             while (i < size && data[i]) i++;
3820             assert(i < size);
3821             i++;
3822             pty_argv[n++] = dupstr(p);
3823         }
3824     }
3825
3826     sfree(data);
3827
3828     return 0;
3829 }
3830
3831 void new_session_menuitem(GtkMenuItem *item, gpointer data)
3832 {
3833     struct gui_data *inst = (struct gui_data *)data;
3834
3835     fork_and_exec_self(inst, -1, NULL);
3836 }
3837
3838 void restart_session_menuitem(GtkMenuItem *item, gpointer data)
3839 {
3840     struct gui_data *inst = (struct gui_data *)data;
3841
3842     if (!inst->back) {
3843         logevent(inst, "----- Session restarted -----");
3844         term_pwron(inst->term, FALSE);
3845         start_backend(inst);
3846         inst->exited = FALSE;
3847     }
3848 }
3849
3850 void saved_session_menuitem(GtkMenuItem *item, gpointer data)
3851 {
3852     struct gui_data *inst = (struct gui_data *)data;
3853     char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data");
3854
3855     fork_and_exec_self(inst, -1, "-load", str, NULL);
3856 }
3857
3858 void saved_session_freedata(GtkMenuItem *item, gpointer data)
3859 {
3860     char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data");
3861
3862     sfree(str);
3863 }
3864
3865 static void update_savedsess_menu(GtkMenuItem *menuitem, gpointer data)
3866 {
3867     struct gui_data *inst = (struct gui_data *)data;
3868     struct sesslist sesslist;
3869     int i;
3870
3871     gtk_container_foreach(GTK_CONTAINER(inst->sessionsmenu),
3872                           (GtkCallback)gtk_widget_destroy, NULL);
3873
3874     get_sesslist(&sesslist, TRUE);
3875     /* skip sesslist.sessions[0] == Default Settings */
3876     for (i = 1; i < sesslist.nsessions; i++) {
3877         GtkWidget *menuitem =
3878             gtk_menu_item_new_with_label(sesslist.sessions[i]);
3879         gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);
3880         gtk_widget_show(menuitem);
3881         g_object_set_data(G_OBJECT(menuitem), "user-data",
3882                           dupstr(sesslist.sessions[i]));
3883         g_signal_connect(G_OBJECT(menuitem), "activate",
3884                          G_CALLBACK(saved_session_menuitem),
3885                          inst);
3886         g_signal_connect(G_OBJECT(menuitem), "destroy",
3887                          G_CALLBACK(saved_session_freedata),
3888                          inst);
3889     }
3890     if (sesslist.nsessions <= 1) {
3891         GtkWidget *menuitem =
3892             gtk_menu_item_new_with_label("(No sessions)");
3893         gtk_widget_set_sensitive(menuitem, FALSE);
3894         gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);
3895         gtk_widget_show(menuitem);
3896     }
3897     get_sesslist(&sesslist, FALSE); /* free up */
3898 }
3899
3900 void set_window_icon(GtkWidget *window, const char *const *const *icon,
3901                      int n_icon)
3902 {
3903 #if GTK_CHECK_VERSION(2,0,0)
3904     GList *iconlist;
3905     int n;
3906 #else
3907     GdkPixmap *iconpm;
3908     GdkBitmap *iconmask;
3909 #endif
3910
3911     if (!n_icon)
3912         return;
3913
3914     gtk_widget_realize(window);
3915 #if GTK_CHECK_VERSION(2,0,0)
3916     gtk_window_set_icon(GTK_WINDOW(window),
3917                         gdk_pixbuf_new_from_xpm_data((const gchar **)icon[0]));
3918 #else
3919     iconpm = gdk_pixmap_create_from_xpm_d(gtk_widget_get_window(window),
3920                                           &iconmask, NULL, (gchar **)icon[0]);
3921     gdk_window_set_icon(gtk_widget_get_window(window), NULL, iconpm, iconmask);
3922 #endif
3923
3924 #if GTK_CHECK_VERSION(2,0,0)
3925     iconlist = NULL;
3926     for (n = 0; n < n_icon; n++) {
3927         iconlist =
3928             g_list_append(iconlist,
3929                           gdk_pixbuf_new_from_xpm_data((const gchar **)
3930                                                        icon[n]));
3931     }
3932     gtk_window_set_icon_list(GTK_WINDOW(window), iconlist);
3933 #endif
3934 }
3935
3936 void update_specials_menu(void *frontend)
3937 {
3938     struct gui_data *inst = (struct gui_data *)frontend;
3939
3940     const struct telnet_special *specials;
3941
3942     if (inst->back)
3943         specials = inst->back->get_specials(inst->backhandle);
3944     else
3945         specials = NULL;
3946
3947     /* I believe this disposes of submenus too. */
3948     gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
3949                           (GtkCallback)gtk_widget_destroy, NULL);
3950     if (specials) {
3951         int i;
3952         GtkWidget *menu = inst->specialsmenu;
3953         /* A lame "stack" for submenus that will do for now. */
3954         GtkWidget *saved_menu = NULL;
3955         int nesting = 1;
3956         for (i = 0; nesting > 0; i++) {
3957             GtkWidget *menuitem = NULL;
3958             switch (specials[i].code) {
3959               case TS_SUBMENU:
3960                 assert (nesting < 2);
3961                 saved_menu = menu; /* XXX lame stacking */
3962                 menu = gtk_menu_new();
3963                 menuitem = gtk_menu_item_new_with_label(specials[i].name);
3964                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
3965                 gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);
3966                 gtk_widget_show(menuitem);
3967                 menuitem = NULL;
3968                 nesting++;
3969                 break;
3970               case TS_EXITMENU:
3971                 nesting--;
3972                 if (nesting) {
3973                     menu = saved_menu; /* XXX lame stacking */
3974                     saved_menu = NULL;
3975                 }
3976                 break;
3977               case TS_SEP:
3978                 menuitem = gtk_menu_item_new();
3979                 break;
3980               default:
3981                 menuitem = gtk_menu_item_new_with_label(specials[i].name);
3982                 g_object_set_data(G_OBJECT(menuitem), "user-data",
3983                                   GINT_TO_POINTER(specials[i].code));
3984                 g_signal_connect(G_OBJECT(menuitem), "activate",
3985                                  G_CALLBACK(special_menuitem), inst);
3986                 break;
3987             }
3988             if (menuitem) {
3989                 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3990                 gtk_widget_show(menuitem);
3991             }
3992         }
3993         gtk_widget_show(inst->specialsitem1);
3994         gtk_widget_show(inst->specialsitem2);
3995     } else {
3996         gtk_widget_hide(inst->specialsitem1);
3997         gtk_widget_hide(inst->specialsitem2);
3998     }
3999 }
4000
4001 static void start_backend(struct gui_data *inst)
4002 {
4003     extern Backend *select_backend(Conf *conf);
4004     char *realhost;
4005     const char *error;
4006     char *s;
4007
4008     inst->back = select_backend(inst->conf);
4009
4010     error = inst->back->init((void *)inst, &inst->backhandle,
4011                              inst->conf,
4012                              conf_get_str(inst->conf, CONF_host),
4013                              conf_get_int(inst->conf, CONF_port),
4014                              &realhost,
4015                              conf_get_int(inst->conf, CONF_tcp_nodelay),
4016                              conf_get_int(inst->conf, CONF_tcp_keepalives));
4017
4018     if (error) {
4019         char *msg = dupprintf("Unable to open connection to %s:\n%s",
4020                               conf_get_str(inst->conf, CONF_host), error);
4021         inst->exited = TRUE;
4022         fatal_message_box(inst->window, msg);
4023         sfree(msg);
4024         exit(0);
4025     }
4026
4027     s = conf_get_str(inst->conf, CONF_wintitle);
4028     if (s[0]) {
4029         set_title_and_icon(inst, s, s);
4030     } else {
4031         char *title = make_default_wintitle(realhost);
4032         set_title_and_icon(inst, title, title);
4033         sfree(title);
4034     }
4035     sfree(realhost);
4036
4037     inst->back->provide_logctx(inst->backhandle, inst->logctx);
4038
4039     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
4040
4041     inst->ldisc =
4042         ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle,
4043                      inst);
4044
4045     gtk_widget_set_sensitive(inst->restartitem, FALSE);
4046 }
4047
4048 int pt_main(int argc, char **argv)
4049 {
4050     extern int cfgbox(Conf *conf);
4051     struct gui_data *inst;
4052
4053     setlocale(LC_CTYPE, "");
4054
4055     /*
4056      * Create an instance structure and initialise to zeroes
4057      */
4058     inst = snew(struct gui_data);
4059     memset(inst, 0, sizeof(*inst));
4060     inst->alt_keycode = -1;            /* this one needs _not_ to be zero */
4061     inst->busy_status = BUSY_NOT;
4062     inst->conf = conf_new();
4063     inst->wintitle = inst->icontitle = NULL;
4064     inst->quit_fn_scheduled = FALSE;
4065     inst->idle_fn_scheduled = FALSE;
4066     inst->drawtype = DRAWTYPE_DEFAULT;
4067
4068     /* defer any child exit handling until we're ready to deal with
4069      * it */
4070     block_signal(SIGCHLD, 1);
4071
4072     inst->progname = argv[0];
4073     /*
4074      * Copy the original argv before letting gtk_init fiddle with
4075      * it. It will be required later.
4076      */
4077     {
4078         int i, oldargc;
4079         inst->gtkargvstart = snewn(argc-1, char *);
4080         for (i = 1; i < argc; i++)
4081             inst->gtkargvstart[i-1] = dupstr(argv[i]);
4082         oldargc = argc;
4083         gtk_init(&argc, &argv);
4084         inst->ngtkargs = oldargc - argc;
4085     }
4086
4087     if (argc > 1 && !strncmp(argv[1], "---", 3)) {
4088         read_dupsession_data(inst, inst->conf, argv[1]);
4089         /* Splatter this argument so it doesn't clutter a ps listing */
4090         smemclr(argv[1], strlen(argv[1]));
4091     } else {
4092         /* By default, we bring up the config dialog, rather than launching
4093          * a session. This gets set to TRUE if something happens to change
4094          * that (e.g., a hostname is specified on the command-line). */
4095         int allow_launch = FALSE;
4096         if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf))
4097             exit(1);                   /* pre-defaults pass to get -class */
4098         do_defaults(NULL, inst->conf);
4099         if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf))
4100             exit(1);                   /* post-defaults, do everything */
4101
4102         cmdline_run_saved(inst->conf);
4103
4104         if (loaded_session)
4105             allow_launch = TRUE;
4106
4107         if ((!allow_launch || !conf_launchable(inst->conf)) &&
4108             !cfgbox(inst->conf))
4109             exit(0);                   /* config box hit Cancel */
4110     }
4111
4112     if (!compound_text_atom)
4113         compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
4114     if (!utf8_string_atom)
4115         utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
4116
4117     inst->area = gtk_drawing_area_new();
4118
4119 #if GTK_CHECK_VERSION(2,0,0)
4120     inst->imc = gtk_im_multicontext_new();
4121 #endif
4122
4123     {
4124         char *errmsg = setup_fonts_ucs(inst);
4125         if (errmsg) {
4126             fprintf(stderr, "%s: %s\n", appname, errmsg);
4127             exit(1);
4128         }
4129     }
4130     init_cutbuffers();
4131
4132     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4133     {
4134         const char *winclass = conf_get_str(inst->conf, CONF_winclass);
4135         if (*winclass)
4136             gtk_window_set_wmclass(GTK_WINDOW(inst->window),
4137                                    winclass, winclass);
4138     }
4139
4140     /*
4141      * Set up the colour map.
4142      */
4143     palette_reset(inst);
4144
4145     inst->width = conf_get_int(inst->conf, CONF_width);
4146     inst->height = conf_get_int(inst->conf, CONF_height);
4147     cache_conf_values(inst);
4148
4149     {
4150         int w = inst->font_width * inst->width + 2*inst->window_border;
4151         int h = inst->font_height * inst->height + 2*inst->window_border;
4152 #if GTK_CHECK_VERSION(2,0,0)
4153         gtk_widget_set_size_request(inst->area, w, h);
4154 #else
4155         gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), w, h);
4156 #endif
4157     }
4158     inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));
4159     inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);
4160     inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
4161     /*
4162      * We always create the scrollbar; it remains invisible if
4163      * unwanted, so we can pop it up quickly if it suddenly becomes
4164      * desirable.
4165      */
4166     if (conf_get_int(inst->conf, CONF_scrollbar_on_left))
4167         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
4168     gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);
4169     if (!conf_get_int(inst->conf, CONF_scrollbar_on_left))
4170         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
4171
4172     gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));
4173
4174     set_geom_hints(inst);
4175
4176     gtk_widget_show(inst->area);
4177     if (conf_get_int(inst->conf, CONF_scrollbar))
4178         gtk_widget_show(inst->sbar);
4179     else
4180         gtk_widget_hide(inst->sbar);
4181     gtk_widget_show(GTK_WIDGET(inst->hbox));
4182
4183 #if GTK_CHECK_VERSION(2,0,0)
4184     if (inst->geometry) {
4185         gtk_window_parse_geometry(GTK_WINDOW(inst->window), inst->geometry);
4186     }
4187 #else
4188     if (inst->gotpos) {
4189         int x = inst->xpos, y = inst->ypos;
4190         GtkRequisition req;
4191         gtk_widget_size_request(GTK_WIDGET(inst->window), &req);
4192         if (inst->gravity & 1) x += gdk_screen_width() - req.width;
4193         if (inst->gravity & 2) y += gdk_screen_height() - req.height;
4194         gtk_window_set_position(GTK_WINDOW(inst->window), GTK_WIN_POS_NONE);
4195         gtk_widget_set_uposition(GTK_WIDGET(inst->window), x, y);
4196     }
4197 #endif
4198
4199     g_signal_connect(G_OBJECT(inst->window), "destroy",
4200                      G_CALLBACK(destroy), inst);
4201     g_signal_connect(G_OBJECT(inst->window), "delete_event",
4202                      G_CALLBACK(delete_window), inst);
4203     g_signal_connect(G_OBJECT(inst->window), "key_press_event",
4204                      G_CALLBACK(key_event), inst);
4205     g_signal_connect(G_OBJECT(inst->window), "key_release_event",
4206                      G_CALLBACK(key_event), inst);
4207     g_signal_connect(G_OBJECT(inst->window), "focus_in_event",
4208                      G_CALLBACK(focus_event), inst);
4209     g_signal_connect(G_OBJECT(inst->window), "focus_out_event",
4210                      G_CALLBACK(focus_event), inst);
4211     g_signal_connect(G_OBJECT(inst->area), "configure_event",
4212                      G_CALLBACK(configure_area), inst);
4213 #if GTK_CHECK_VERSION(3,0,0)
4214     g_signal_connect(G_OBJECT(inst->area), "draw",
4215                      G_CALLBACK(draw_area), inst);
4216 #else
4217     g_signal_connect(G_OBJECT(inst->area), "expose_event",
4218                      G_CALLBACK(expose_area), inst);
4219 #endif
4220     g_signal_connect(G_OBJECT(inst->area), "button_press_event",
4221                      G_CALLBACK(button_event), inst);
4222     g_signal_connect(G_OBJECT(inst->area), "button_release_event",
4223                      G_CALLBACK(button_event), inst);
4224 #if GTK_CHECK_VERSION(2,0,0)
4225     g_signal_connect(G_OBJECT(inst->area), "scroll_event",
4226                      G_CALLBACK(scroll_event), inst);
4227 #endif
4228     g_signal_connect(G_OBJECT(inst->area), "motion_notify_event",
4229                      G_CALLBACK(motion_event), inst);
4230     g_signal_connect(G_OBJECT(inst->area), "selection_received",
4231                      G_CALLBACK(selection_received), inst);
4232     g_signal_connect(G_OBJECT(inst->area), "selection_get",
4233                      G_CALLBACK(selection_get), inst);
4234     g_signal_connect(G_OBJECT(inst->area), "selection_clear_event",
4235                      G_CALLBACK(selection_clear), inst);
4236 #if GTK_CHECK_VERSION(2,0,0)
4237     g_signal_connect(G_OBJECT(inst->imc), "commit",
4238                      G_CALLBACK(input_method_commit_event), inst);
4239 #endif
4240     if (conf_get_int(inst->conf, CONF_scrollbar))
4241         g_signal_connect(G_OBJECT(inst->sbar_adjust), "value_changed",
4242                          G_CALLBACK(scrollbar_moved), inst);
4243     gtk_widget_add_events(GTK_WIDGET(inst->area),
4244                           GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
4245                           GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4246                           GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK);
4247
4248     {
4249         extern const char *const *const main_icon[];
4250         extern const int n_main_icon;
4251         set_window_icon(inst->window, main_icon, n_main_icon);
4252     }
4253
4254     gtk_widget_show(inst->window);
4255
4256     set_window_background(inst);
4257
4258     /*
4259      * Set up the Ctrl+rightclick context menu.
4260      */
4261     {
4262         GtkWidget *menuitem;
4263         char *s;
4264         extern const int use_event_log, new_session, saved_sessions;
4265
4266         inst->menu = gtk_menu_new();
4267
4268 #define MKMENUITEM(title, func) do                                      \
4269         {                                                               \
4270             menuitem = gtk_menu_item_new_with_label(title);             \
4271             gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \
4272             gtk_widget_show(menuitem);                                  \
4273             g_signal_connect(G_OBJECT(menuitem), "activate",            \
4274                              G_CALLBACK(func), inst);                   \
4275         } while (0)
4276
4277 #define MKSUBMENU(title) do                                             \
4278         {                                                               \
4279             menuitem = gtk_menu_item_new_with_label(title);             \
4280             gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \
4281             gtk_widget_show(menuitem);                                  \
4282         } while (0)
4283
4284 #define MKSEP() do                                                      \
4285         {                                                               \
4286             menuitem = gtk_menu_item_new();                             \
4287             gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \
4288             gtk_widget_show(menuitem);                                  \
4289         } while (0)
4290
4291         if (new_session)
4292             MKMENUITEM("New Session...", new_session_menuitem);
4293         MKMENUITEM("Restart Session", restart_session_menuitem);
4294         inst->restartitem = menuitem;
4295         gtk_widget_set_sensitive(inst->restartitem, FALSE);
4296         MKMENUITEM("Duplicate Session", dup_session_menuitem);
4297         if (saved_sessions) {
4298             inst->sessionsmenu = gtk_menu_new();
4299             /* sessionsmenu will be updated when it's invoked */
4300             /* XXX is this the right way to do dynamic menus in Gtk? */
4301             MKMENUITEM("Saved Sessions", update_savedsess_menu);
4302             gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),
4303                                       inst->sessionsmenu);
4304         }
4305         MKSEP();
4306         MKMENUITEM("Change Settings...", change_settings_menuitem);
4307         MKSEP();
4308         if (use_event_log)
4309             MKMENUITEM("Event Log", event_log_menuitem);
4310         MKSUBMENU("Special Commands");
4311         inst->specialsmenu = gtk_menu_new();
4312         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->specialsmenu);
4313         inst->specialsitem1 = menuitem;
4314         MKSEP();
4315         inst->specialsitem2 = menuitem;
4316         gtk_widget_hide(inst->specialsitem1);
4317         gtk_widget_hide(inst->specialsitem2);
4318         MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);
4319         MKMENUITEM("Reset Terminal", reset_terminal_menuitem);
4320         MKMENUITEM("Copy All", copy_all_menuitem);
4321         MKSEP();
4322         s = dupcat("About ", appname, NULL);
4323         MKMENUITEM(s, about_menuitem);
4324         sfree(s);
4325 #undef MKMENUITEM
4326 #undef MKSUBMENU
4327 #undef MKSEP
4328     }
4329
4330     inst->textcursor = make_mouse_ptr(inst, GDK_XTERM);
4331     inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR);
4332     inst->waitcursor = make_mouse_ptr(inst, GDK_WATCH);
4333     inst->blankcursor = make_mouse_ptr(inst, -1);
4334     inst->currcursor = inst->textcursor;
4335     show_mouseptr(inst, 1);
4336
4337     inst->eventlogstuff = eventlogstuff_new();
4338
4339     request_callback_notifications(notify_toplevel_callback, inst);
4340
4341     inst->term = term_init(inst->conf, &inst->ucsdata, inst);
4342     inst->logctx = log_init(inst, inst->conf);
4343     term_provide_logctx(inst->term, inst->logctx);
4344
4345     uxsel_init();
4346
4347     term_size(inst->term, inst->height, inst->width,
4348               conf_get_int(inst->conf, CONF_savelines));
4349
4350     start_backend(inst);
4351
4352     ldisc_echoedit_update(inst->ldisc);     /* cause ldisc to notice changes */
4353
4354     /* now we're reday to deal with the child exit handler being
4355      * called */
4356     block_signal(SIGCHLD, 0);
4357
4358     /*
4359      * Block SIGPIPE: if we attempt Duplicate Session or similar
4360      * and it falls over in some way, we certainly don't want
4361      * SIGPIPE terminating the main pterm/PuTTY. Note that we do
4362      * this _after_ (at least pterm) forks off its child process,
4363      * since the child wants SIGPIPE handled in the usual way.
4364      */
4365     block_signal(SIGPIPE, 1);
4366
4367     inst->exited = FALSE;
4368
4369     gtk_main();
4370
4371     return 0;
4372 }