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