]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkwin.c
Reimplement GTK uxsel_input_add using GIOChannel.
[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 #if GTK_CHECK_VERSION(2,0,0)
1636 gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
1637                        gpointer data)
1638 {
1639     int sourcefd = g_io_channel_unix_get_fd(source);
1640     /*
1641      * We must process exceptional notifications before ordinary
1642      * readability ones, or we may go straight past the urgent
1643      * marker.
1644      */
1645     if (condition & G_IO_PRI)
1646         select_result(sourcefd, 4);
1647     if (condition & G_IO_IN)
1648         select_result(sourcefd, 1);
1649     if (condition & G_IO_OUT)
1650         select_result(sourcefd, 2);
1651
1652     return TRUE;
1653 }
1654 #else
1655 void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
1656 {
1657     if (condition & GDK_INPUT_EXCEPTION)
1658         select_result(sourcefd, 4);
1659     if (condition & GDK_INPUT_READ)
1660         select_result(sourcefd, 1);
1661     if (condition & GDK_INPUT_WRITE)
1662         select_result(sourcefd, 2);
1663 }
1664 #endif
1665
1666 void destroy(GtkWidget *widget, gpointer data)
1667 {
1668     gtk_main_quit();
1669 }
1670
1671 gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1672 {
1673     struct gui_data *inst = (struct gui_data *)data;
1674     term_set_focus(inst->term, event->in);
1675     term_update(inst->term);
1676     show_mouseptr(inst, 1);
1677     return FALSE;
1678 }
1679
1680 void set_busy_status(void *frontend, int status)
1681 {
1682     struct gui_data *inst = (struct gui_data *)frontend;
1683     inst->busy_status = status;
1684     update_mouseptr(inst);
1685 }
1686
1687 /*
1688  * set or clear the "raw mouse message" mode
1689  */
1690 void set_raw_mouse_mode(void *frontend, int activate)
1691 {
1692     struct gui_data *inst = (struct gui_data *)frontend;
1693     activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep);
1694     send_raw_mouse = activate;
1695     update_mouseptr(inst);
1696 }
1697
1698 void request_resize(void *frontend, int w, int h)
1699 {
1700     struct gui_data *inst = (struct gui_data *)frontend;
1701     int large_x, large_y;
1702     int offset_x, offset_y;
1703     int area_x, area_y;
1704     GtkRequisition inner, outer;
1705
1706     /*
1707      * This is a heinous hack dreamed up by the gnome-terminal
1708      * people to get around a limitation in gtk. The problem is
1709      * that in order to set the size correctly we really need to be
1710      * calling gtk_window_resize - but that needs to know the size
1711      * of the _whole window_, not the drawing area. So what we do
1712      * is to set an artificially huge size request on the drawing
1713      * area, recompute the resulting size request on the window,
1714      * and look at the difference between the two. That gives us
1715      * the x and y offsets we need to translate drawing area size
1716      * into window size for real, and then we call
1717      * gtk_window_resize.
1718      */
1719
1720     /*
1721      * We start by retrieving the current size of the whole window.
1722      * Adding a bit to _that_ will give us a value we can use as a
1723      * bogus size request which guarantees to be bigger than the
1724      * current size of the drawing area.
1725      */
1726     get_window_pixels(inst, &large_x, &large_y);
1727     large_x += 32;
1728     large_y += 32;
1729
1730     gtk_widget_set_size_request(inst->area, large_x, large_y);
1731     gtk_widget_size_request(inst->area, &inner);
1732     gtk_widget_size_request(inst->window, &outer);
1733
1734     offset_x = outer.width - inner.width;
1735     offset_y = outer.height - inner.height;
1736
1737     area_x = inst->font_width * w + 2*inst->window_border;
1738     area_y = inst->font_height * h + 2*inst->window_border;
1739
1740     /*
1741      * Now we must set the size request on the drawing area back to
1742      * something sensible before we commit the real resize. Best
1743      * way to do this, I think, is to set it to what the size is
1744      * really going to end up being.
1745      */
1746     gtk_widget_set_size_request(inst->area, area_x, area_y);
1747 #if GTK_CHECK_VERSION(2,0,0)
1748     gtk_window_resize(GTK_WINDOW(inst->window),
1749                       area_x + offset_x, area_y + offset_y);
1750 #else
1751     gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), area_x, area_y);
1752     /*
1753      * I can no longer remember what this call to
1754      * gtk_container_dequeue_resize_handler is for. It was
1755      * introduced in r3092 with no comment, and the commit log
1756      * message was uninformative. I'm _guessing_ its purpose is to
1757      * prevent gratuitous resize processing on the window given
1758      * that we're about to resize it anyway, but I have no idea
1759      * why that's so incredibly vital.
1760      * 
1761      * I've tried removing the call, and nothing seems to go
1762      * wrong. I've backtracked to r3092 and tried removing the
1763      * call there, and still nothing goes wrong. So I'm going to
1764      * adopt the working hypothesis that it's superfluous; I won't
1765      * actually remove it from the GTK 1.2 code, but I won't
1766      * attempt to replicate its functionality in the GTK 2 code
1767      * above.
1768      */
1769     gtk_container_dequeue_resize_handler(GTK_CONTAINER(inst->window));
1770     gdk_window_resize(gtk_widget_get_window(inst->window),
1771                       area_x + offset_x, area_y + offset_y);
1772 #endif
1773 }
1774
1775 static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)
1776 {
1777     gboolean success[1];
1778
1779     inst->cols[n].red = r * 0x0101;
1780     inst->cols[n].green = g * 0x0101;
1781     inst->cols[n].blue = b * 0x0101;
1782
1783     gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);
1784     gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,
1785                               FALSE, TRUE, success);
1786     if (!success[0])
1787         g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname,
1788                 n, r, g, b);
1789 }
1790
1791 void set_window_background(struct gui_data *inst)
1792 {
1793     if (inst->area && gtk_widget_get_window(inst->area))
1794         gdk_window_set_background(gtk_widget_get_window(inst->area),
1795                                   &inst->cols[258]);
1796     if (inst->window && gtk_widget_get_window(inst->window))
1797         gdk_window_set_background(gtk_widget_get_window(inst->window),
1798                                   &inst->cols[258]);
1799 }
1800
1801 void palette_set(void *frontend, int n, int r, int g, int b)
1802 {
1803     struct gui_data *inst = (struct gui_data *)frontend;
1804     if (n >= 16)
1805         n += 256 - 16;
1806     if (n >= NALLCOLOURS)
1807         return;
1808     real_palette_set(inst, n, r, g, b);
1809     if (n == 258) {
1810         /* Default Background changed. Ensure space between text area and
1811          * window border is redrawn */
1812         set_window_background(inst);
1813         draw_backing_rect(inst);
1814         gtk_widget_queue_draw(inst->area);
1815     }
1816 }
1817
1818 void palette_reset(void *frontend)
1819 {
1820     struct gui_data *inst = (struct gui_data *)frontend;
1821     /* This maps colour indices in inst->conf to those used in inst->cols. */
1822     static const int ww[] = {
1823         256, 257, 258, 259, 260, 261,
1824         0, 8, 1, 9, 2, 10, 3, 11,
1825         4, 12, 5, 13, 6, 14, 7, 15
1826     };
1827     gboolean success[NALLCOLOURS];
1828     int i;
1829
1830     assert(lenof(ww) == NCFGCOLOURS);
1831
1832     if (!inst->colmap) {
1833         inst->colmap = gdk_colormap_get_system();
1834     } else {
1835         gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS);
1836     }
1837
1838     for (i = 0; i < NCFGCOLOURS; i++) {
1839         inst->cols[ww[i]].red =
1840             conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101;
1841         inst->cols[ww[i]].green =
1842             conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101;
1843         inst->cols[ww[i]].blue = 
1844             conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101;
1845     }
1846
1847     for (i = 0; i < NEXTCOLOURS; i++) {
1848         if (i < 216) {
1849             int r = i / 36, g = (i / 6) % 6, b = i % 6;
1850             inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0;
1851             inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0;
1852             inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0;
1853         } else {
1854             int shade = i - 216;
1855             shade = shade * 0x0a0a + 0x0808;
1856             inst->cols[i+16].red = inst->cols[i+16].green =
1857                 inst->cols[i+16].blue = shade;
1858         }
1859     }
1860
1861     gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS,
1862                               FALSE, TRUE, success);
1863     for (i = 0; i < NALLCOLOURS; i++) {
1864         if (!success[i])
1865             g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
1866                     appname, i,
1867                     conf_get_int_int(inst->conf, CONF_colours, i*3+0),
1868                     conf_get_int_int(inst->conf, CONF_colours, i*3+1),
1869                     conf_get_int_int(inst->conf, CONF_colours, i*3+2));
1870     }
1871
1872     /* Since Default Background may have changed, ensure that space
1873      * between text area and window border is refreshed. */
1874     set_window_background(inst);
1875     if (inst->area && gtk_widget_get_window(inst->area)) {
1876         draw_backing_rect(inst);
1877         gtk_widget_queue_draw(inst->area);
1878     }
1879 }
1880
1881 /* Ensure that all the cut buffers exist - according to the ICCCM, we must
1882  * do this before we start using cut buffers.
1883  */
1884 void init_cutbuffers()
1885 {
1886 #ifndef NOT_X_WINDOWS
1887     unsigned char empty[] = "";
1888     Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1889     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1890                     XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0);
1891     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1892                     XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0);
1893     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1894                     XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0);
1895     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1896                     XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0);
1897     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1898                     XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0);
1899     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1900                     XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0);
1901     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1902                     XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0);
1903     XChangeProperty(disp, GDK_ROOT_WINDOW(),
1904                     XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);
1905 #endif
1906 }
1907
1908 /* Store the data in a cut-buffer. */
1909 void store_cutbuffer(char * ptr, int len)
1910 {
1911 #ifndef NOT_X_WINDOWS
1912     Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1913     /* ICCCM says we must rotate the buffers before storing to buffer 0. */
1914     XRotateBuffers(disp, 1);
1915     XStoreBytes(disp, ptr, len);
1916 #endif
1917 }
1918
1919 /* Retrieve data from a cut-buffer.
1920  * Returned data needs to be freed with XFree().
1921  */
1922 char * retrieve_cutbuffer(int * nbytes)
1923 {
1924 #ifndef NOT_X_WINDOWS
1925     Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1926     char * ptr;
1927     ptr = XFetchBytes(disp, nbytes);
1928     if (*nbytes <= 0 && ptr != 0) {
1929         XFree(ptr);
1930         ptr = 0;
1931     }
1932     return ptr;
1933 #else
1934     return NULL;
1935 #endif
1936 }
1937
1938 void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)
1939 {
1940     struct gui_data *inst = (struct gui_data *)frontend;
1941     if (inst->pasteout_data)
1942         sfree(inst->pasteout_data);
1943     if (inst->pasteout_data_ctext)
1944         sfree(inst->pasteout_data_ctext);
1945     if (inst->pasteout_data_utf8)
1946         sfree(inst->pasteout_data_utf8);
1947
1948     /*
1949      * Set up UTF-8 and compound text paste data. This only happens
1950      * if we aren't in direct-to-font mode using the D800 hack.
1951      */
1952     if (!inst->direct_to_font) {
1953         const wchar_t *tmp = data;
1954         int tmplen = len;
1955 #ifndef NOT_X_WINDOWS
1956         XTextProperty tp;
1957         char *list[1];
1958         Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1959 #endif
1960
1961         inst->pasteout_data_utf8 = snewn(len*6, char);
1962         inst->pasteout_data_utf8_len = len*6;
1963         inst->pasteout_data_utf8_len =
1964             charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,
1965                                  inst->pasteout_data_utf8_len,
1966                                  CS_UTF8, NULL, NULL, 0);
1967         if (inst->pasteout_data_utf8_len == 0) {
1968             sfree(inst->pasteout_data_utf8);
1969             inst->pasteout_data_utf8 = NULL;
1970         } else {
1971             inst->pasteout_data_utf8 =
1972                 sresize(inst->pasteout_data_utf8,
1973                         inst->pasteout_data_utf8_len + 1, char);
1974             inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0';
1975         }
1976
1977         /*
1978          * Now let Xlib convert our UTF-8 data into compound text.
1979          */
1980 #ifndef NOT_X_WINDOWS
1981         list[0] = inst->pasteout_data_utf8;
1982         if (Xutf8TextListToTextProperty(disp, list, 1,
1983                                         XCompoundTextStyle, &tp) == 0) {
1984             inst->pasteout_data_ctext = snewn(tp.nitems+1, char);
1985             memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);
1986             inst->pasteout_data_ctext_len = tp.nitems;
1987             XFree(tp.value);
1988         } else
1989 #endif
1990         {
1991             inst->pasteout_data_ctext = NULL;
1992             inst->pasteout_data_ctext_len = 0;
1993         }
1994     } else {
1995         inst->pasteout_data_utf8 = NULL;
1996         inst->pasteout_data_utf8_len = 0;
1997         inst->pasteout_data_ctext = NULL;
1998         inst->pasteout_data_ctext_len = 0;
1999     }
2000
2001     inst->pasteout_data = snewn(len*6, char);
2002     inst->pasteout_data_len = len*6;
2003     inst->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0,
2004                                        data, len, inst->pasteout_data,
2005                                        inst->pasteout_data_len,
2006                                        NULL, NULL, NULL);
2007     if (inst->pasteout_data_len == 0) {
2008         sfree(inst->pasteout_data);
2009         inst->pasteout_data = NULL;
2010     } else {
2011         inst->pasteout_data =
2012             sresize(inst->pasteout_data, inst->pasteout_data_len, char);
2013     }
2014
2015     store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len);
2016
2017     if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
2018                                 inst->input_event_time)) {
2019 #if GTK_CHECK_VERSION(2,0,0)
2020         gtk_selection_clear_targets(inst->area, GDK_SELECTION_PRIMARY);
2021 #endif
2022         gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
2023                                  GDK_SELECTION_TYPE_STRING, 1);
2024         if (inst->pasteout_data_ctext)
2025             gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
2026                                      compound_text_atom, 1);
2027         if (inst->pasteout_data_utf8)
2028             gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
2029                                      utf8_string_atom, 1);
2030     }
2031
2032     if (must_deselect)
2033         term_deselect(inst->term);
2034 }
2035
2036 void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
2037                    guint info, guint time_stamp, gpointer data)
2038 {
2039     struct gui_data *inst = (struct gui_data *)data;
2040     GdkAtom target = gtk_selection_data_get_target(seldata);
2041     if (target == utf8_string_atom)
2042         gtk_selection_data_set(seldata, target, 8,
2043                                (unsigned char *)inst->pasteout_data_utf8,
2044                                inst->pasteout_data_utf8_len);
2045     else if (target == compound_text_atom)
2046         gtk_selection_data_set(seldata, target, 8,
2047                                (unsigned char *)inst->pasteout_data_ctext,
2048                                inst->pasteout_data_ctext_len);
2049     else
2050         gtk_selection_data_set(seldata, target, 8,
2051                                (unsigned char *)inst->pasteout_data,
2052                                inst->pasteout_data_len);
2053 }
2054
2055 gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
2056                      gpointer data)
2057 {
2058     struct gui_data *inst = (struct gui_data *)data;
2059
2060     term_deselect(inst->term);
2061     if (inst->pasteout_data)
2062         sfree(inst->pasteout_data);
2063     if (inst->pasteout_data_ctext)
2064         sfree(inst->pasteout_data_ctext);
2065     if (inst->pasteout_data_utf8)
2066         sfree(inst->pasteout_data_utf8);
2067     inst->pasteout_data = NULL;
2068     inst->pasteout_data_len = 0;
2069     inst->pasteout_data_ctext = NULL;
2070     inst->pasteout_data_ctext_len = 0;
2071     inst->pasteout_data_utf8 = NULL;
2072     inst->pasteout_data_utf8_len = 0;
2073     return TRUE;
2074 }
2075
2076 void request_paste(void *frontend)
2077 {
2078     struct gui_data *inst = (struct gui_data *)frontend;
2079     /*
2080      * In Unix, pasting is asynchronous: all we can do at the
2081      * moment is to call gtk_selection_convert(), and when the data
2082      * comes back _then_ we can call term_do_paste().
2083      */
2084
2085     if (!inst->direct_to_font) {
2086         /*
2087          * First we attempt to retrieve the selection as a UTF-8
2088          * string (which we will convert to the correct code page
2089          * before sending to the session, of course). If that
2090          * fails, selection_received() will be informed and will
2091          * fall back to an ordinary string.
2092          */
2093         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2094                               utf8_string_atom,
2095                               inst->input_event_time);
2096     } else {
2097         /*
2098          * If we're in direct-to-font mode, we disable UTF-8
2099          * pasting, and go straight to ordinary string data.
2100          */
2101         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2102                               GDK_SELECTION_TYPE_STRING,
2103                               inst->input_event_time);
2104     }
2105 }
2106
2107 gint idle_paste_func(gpointer data);   /* forward ref */
2108
2109 void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
2110                         guint time, gpointer data)
2111 {
2112     struct gui_data *inst = (struct gui_data *)data;
2113     char *text;
2114     int length;
2115 #ifndef NOT_X_WINDOWS
2116     char **list;
2117     int free_list_required = 0;
2118     int free_required = 0;
2119 #endif
2120     int charset;
2121     GdkAtom seldata_target = gtk_selection_data_get_target(seldata);
2122     GdkAtom seldata_type = gtk_selection_data_get_data_type(seldata);
2123     const guchar *seldata_data = gtk_selection_data_get_data(seldata);
2124     gint seldata_length = gtk_selection_data_get_length(seldata);
2125
2126     if (seldata_target == utf8_string_atom && seldata_length <= 0) {
2127         /*
2128          * Failed to get a UTF-8 selection string. Try compound
2129          * text next.
2130          */
2131         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2132                               compound_text_atom,
2133                               inst->input_event_time);
2134         return;
2135     }
2136
2137     if (seldata_target == compound_text_atom && seldata_length <= 0) {
2138         /*
2139          * Failed to get UTF-8 or compound text. Try an ordinary
2140          * string.
2141          */
2142         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2143                               GDK_SELECTION_TYPE_STRING,
2144                               inst->input_event_time);
2145         return;
2146     }
2147
2148     /*
2149      * If we have data, but it's not of a type we can deal with,
2150      * we have to ignore the data.
2151      */
2152     if (seldata_length > 0 &&
2153         seldata_type != GDK_SELECTION_TYPE_STRING &&
2154         seldata_type != compound_text_atom &&
2155         seldata_type != utf8_string_atom)
2156         return;
2157
2158     /*
2159      * If we have no data, try looking in a cut buffer.
2160      */
2161     if (seldata_length <= 0) {
2162 #ifndef NOT_X_WINDOWS
2163         text = retrieve_cutbuffer(&length);
2164         if (length == 0)
2165             return;
2166         /* Xterm is rumoured to expect Latin-1, though I havn't checked the
2167          * source, so use that as a de-facto standard. */
2168         charset = CS_ISO8859_1;
2169         free_required = 1;
2170 #else
2171         return;
2172 #endif
2173     } else {
2174         /*
2175          * Convert COMPOUND_TEXT into UTF-8.
2176          */
2177         if (seldata_type == compound_text_atom) {
2178 #ifndef NOT_X_WINDOWS
2179             XTextProperty tp;
2180             int ret, count;
2181             Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2182
2183             tp.value = (unsigned char *)seldata_data;
2184             tp.encoding = (Atom) seldata_type;
2185             tp.format = gtk_selection_data_get_format(seldata);
2186             tp.nitems = seldata_length;
2187             ret = Xutf8TextPropertyToTextList(disp, &tp, &list, &count);
2188             if (ret == 0 && count == 1) {
2189                 text = list[0];
2190                 length = strlen(list[0]);
2191                 charset = CS_UTF8;
2192                 free_list_required = 1;
2193             } else
2194 #endif
2195             {
2196                 /*
2197                  * Compound text failed; fall back to STRING.
2198                  */
2199                 gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
2200                                       GDK_SELECTION_TYPE_STRING,
2201                                       inst->input_event_time);
2202                 return;
2203             }
2204         } else {
2205             text = (char *)seldata_data;
2206             length = seldata_length;
2207             charset = (seldata_type == utf8_string_atom ?
2208                        CS_UTF8 : inst->ucsdata.line_codepage);
2209         }
2210     }
2211
2212     if (inst->pastein_data)
2213         sfree(inst->pastein_data);
2214
2215     inst->pastein_data = snewn(length, wchar_t);
2216     inst->pastein_data_len = length;
2217     inst->pastein_data_len =
2218         mb_to_wc(charset, 0, text, length,
2219                  inst->pastein_data, inst->pastein_data_len);
2220
2221     term_do_paste(inst->term);
2222
2223 #ifndef NOT_X_WINDOWS
2224     if (free_list_required)
2225         XFreeStringList(list);
2226     if (free_required)
2227         XFree(text);
2228 #endif
2229 }
2230
2231 void get_clip(void *frontend, wchar_t ** p, int *len)
2232 {
2233     struct gui_data *inst = (struct gui_data *)frontend;
2234
2235     if (p) {
2236         *p = inst->pastein_data;
2237         *len = inst->pastein_data_len;
2238     }
2239 }
2240
2241 static void set_window_titles(struct gui_data *inst)
2242 {
2243     /*
2244      * We must always call set_icon_name after calling set_title,
2245      * since set_title will write both names. Irritating, but such
2246      * is life.
2247      */
2248     gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
2249     if (!conf_get_int(inst->conf, CONF_win_name_always))
2250         gdk_window_set_icon_name(gtk_widget_get_window(inst->window),
2251                                  inst->icontitle);
2252 }
2253
2254 void set_title(void *frontend, char *title)
2255 {
2256     struct gui_data *inst = (struct gui_data *)frontend;
2257     sfree(inst->wintitle);
2258     inst->wintitle = dupstr(title);
2259     set_window_titles(inst);
2260 }
2261
2262 void set_icon(void *frontend, char *title)
2263 {
2264     struct gui_data *inst = (struct gui_data *)frontend;
2265     sfree(inst->icontitle);
2266     inst->icontitle = dupstr(title);
2267     set_window_titles(inst);
2268 }
2269
2270 void set_title_and_icon(void *frontend, char *title, char *icon)
2271 {
2272     struct gui_data *inst = (struct gui_data *)frontend;
2273     sfree(inst->wintitle);
2274     inst->wintitle = dupstr(title);
2275     sfree(inst->icontitle);
2276     inst->icontitle = dupstr(icon);
2277     set_window_titles(inst);
2278 }
2279
2280 void set_sbar(void *frontend, int total, int start, int page)
2281 {
2282     struct gui_data *inst = (struct gui_data *)frontend;
2283     if (!conf_get_int(inst->conf, CONF_scrollbar))
2284         return;
2285     gtk_adjustment_set_lower(inst->sbar_adjust, 0);
2286     gtk_adjustment_set_upper(inst->sbar_adjust, total);
2287     gtk_adjustment_set_value(inst->sbar_adjust, start);
2288     gtk_adjustment_set_page_size(inst->sbar_adjust, page);
2289     gtk_adjustment_set_step_increment(inst->sbar_adjust, 1);
2290     gtk_adjustment_set_page_increment(inst->sbar_adjust, page/2);
2291     inst->ignore_sbar = TRUE;
2292     gtk_adjustment_changed(inst->sbar_adjust);
2293     inst->ignore_sbar = FALSE;
2294 }
2295
2296 void scrollbar_moved(GtkAdjustment *adj, gpointer data)
2297 {
2298     struct gui_data *inst = (struct gui_data *)data;
2299
2300     if (!conf_get_int(inst->conf, CONF_scrollbar))
2301         return;
2302     if (!inst->ignore_sbar)
2303         term_scroll(inst->term, 1, (int)gtk_adjustment_get_value(adj));
2304 }
2305
2306 void sys_cursor(void *frontend, int x, int y)
2307 {
2308     /*
2309      * This is meaningless under X.
2310      */
2311 }
2312
2313 /*
2314  * This is still called when mode==BELL_VISUAL, even though the
2315  * visual bell is handled entirely within terminal.c, because we
2316  * may want to perform additional actions on any kind of bell (for
2317  * example, taskbar flashing in Windows).
2318  */
2319 void do_beep(void *frontend, int mode)
2320 {
2321     if (mode == BELL_DEFAULT)
2322         gdk_beep();
2323 }
2324
2325 int char_width(Context ctx, int uc)
2326 {
2327     /*
2328      * Under X, any fixed-width font really _is_ fixed-width.
2329      * Double-width characters will be dealt with using a separate
2330      * font. For the moment we can simply return 1.
2331      * 
2332      * FIXME: but is that also true of Pango?
2333      */
2334     return 1;
2335 }
2336
2337 Context get_ctx(void *frontend)
2338 {
2339     struct gui_data *inst = (struct gui_data *)frontend;
2340     struct draw_ctx *dctx;
2341     GdkWindow *target;
2342
2343     if (!gtk_widget_get_window(inst->area))
2344         return NULL;
2345
2346 #ifndef NO_BACKING_PIXMAPS
2347     target = inst->pixmap;
2348 #else
2349     target = gtk_widget_get_window(inst->area);
2350 #endif
2351
2352     dctx = snew(struct draw_ctx);
2353     dctx->inst = inst;
2354     dctx->uctx.type = inst->drawtype;
2355 #ifdef DRAW_TEXT_GDK
2356     if (dctx->uctx.type == DRAWTYPE_GDK) {
2357         dctx->uctx.u.gdk.target = target;
2358         dctx->uctx.u.gdk.gc = gdk_gc_new(gtk_widget_get_window(inst->area));
2359     }
2360 #endif
2361 #ifdef DRAW_TEXT_CAIRO
2362     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2363         dctx->uctx.u.cairo.widget = GTK_WIDGET(inst->area);
2364         dctx->uctx.u.cairo.cr = gdk_cairo_create(target);
2365         cairo_get_matrix(dctx->uctx.u.cairo.cr,
2366                          &dctx->uctx.u.cairo.origmatrix);
2367         cairo_set_line_width(dctx->uctx.u.cairo.cr, 1.0);
2368         cairo_set_line_cap(dctx->uctx.u.cairo.cr, CAIRO_LINE_CAP_SQUARE);
2369         cairo_set_line_join(dctx->uctx.u.cairo.cr, CAIRO_LINE_JOIN_MITER);
2370         /* This antialiasing setting appears to be ignored for Pango
2371          * font rendering but honoured for stroking and filling paths;
2372          * I don't quite understand the logic of that, but I won't
2373          * complain since it's exactly what I happen to want */
2374         cairo_set_antialias(dctx->uctx.u.cairo.cr, CAIRO_ANTIALIAS_NONE);
2375     }
2376 #endif
2377     return dctx;
2378 }
2379
2380 void free_ctx(Context ctx)
2381 {
2382     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2383     /* struct gui_data *inst = dctx->inst; */
2384 #ifdef DRAW_TEXT_GDK
2385     if (dctx->uctx.type == DRAWTYPE_GDK) {
2386         gdk_gc_unref(dctx->uctx.u.gdk.gc);
2387     }
2388 #endif
2389 #ifdef DRAW_TEXT_CAIRO
2390     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2391         cairo_destroy(dctx->uctx.u.cairo.cr);
2392     }
2393 #endif
2394     sfree(dctx);
2395 }
2396
2397
2398 static void draw_update(struct draw_ctx *dctx, int x, int y, int w, int h)
2399 {
2400 #ifndef NO_BACKING_PIXMAPS
2401 #ifdef DRAW_TEXT_GDK
2402     if (dctx->uctx.type == DRAWTYPE_GDK) {
2403         gdk_draw_pixmap(gtk_widget_get_window(dctx->inst->area),
2404                         dctx->uctx.u.gdk.gc, dctx->inst->pixmap,
2405                         x, y, x, y, w, h);
2406     }
2407 #endif
2408 #ifdef DRAW_TEXT_CAIRO /* FIXME: and not GTK3 where a cairo_t is all we have */
2409     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2410         GdkGC *gc = gdk_gc_new(gtk_widget_get_window(dctx->inst->area));
2411         gdk_draw_pixmap(gtk_widget_get_window(dctx->inst->area),
2412                         gc, dctx->inst->pixmap, x, y, x, y, w, h);
2413         gdk_gc_unref(gc);
2414     }
2415 #endif
2416 #endif
2417 }
2418
2419 static void draw_set_colour(struct draw_ctx *dctx, int col)
2420 {
2421 #ifdef DRAW_TEXT_GDK
2422     if (dctx->uctx.type == DRAWTYPE_GDK) {
2423         gdk_gc_set_foreground(dctx->uctx.u.gdk.gc, &dctx->inst->cols[col]);
2424     }
2425 #endif
2426 #ifdef DRAW_TEXT_CAIRO
2427     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2428         cairo_set_source_rgb(dctx->uctx.u.cairo.cr,
2429                              dctx->inst->cols[col].red / 65535.0,
2430                              dctx->inst->cols[col].green / 65535.0,
2431                              dctx->inst->cols[col].blue / 65535.0);
2432     }
2433 #endif
2434 }
2435
2436 static void draw_rectangle(struct draw_ctx *dctx, int filled,
2437                            int x, int y, int w, int h)
2438 {
2439 #ifdef DRAW_TEXT_GDK
2440     if (dctx->uctx.type == DRAWTYPE_GDK) {
2441         gdk_draw_rectangle(dctx->uctx.u.gdk.target, dctx->uctx.u.gdk.gc,
2442                            filled, x, y, w, h);
2443     }
2444 #endif
2445 #ifdef DRAW_TEXT_CAIRO
2446     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2447         cairo_new_path(dctx->uctx.u.cairo.cr);
2448         if (filled) {
2449             cairo_rectangle(dctx->uctx.u.cairo.cr, x, y, w, h);
2450             cairo_fill(dctx->uctx.u.cairo.cr);
2451         } else {
2452             cairo_rectangle(dctx->uctx.u.cairo.cr,
2453                             x + 0.5, y + 0.5, w, h);
2454             cairo_close_path(dctx->uctx.u.cairo.cr);
2455             cairo_stroke(dctx->uctx.u.cairo.cr);
2456         }
2457     }
2458 #endif
2459 }
2460
2461 static void draw_clip(struct draw_ctx *dctx, int x, int y, int w, int h)
2462 {
2463 #ifdef DRAW_TEXT_GDK
2464     if (dctx->uctx.type == DRAWTYPE_GDK) {
2465         GdkRectangle r;
2466
2467         r.x = x;
2468         r.y = y;
2469         r.width = w;
2470         r.height = h;
2471
2472         gdk_gc_set_clip_rectangle(dctx->uctx.u.gdk.gc, &r);
2473     }
2474 #endif
2475 #ifdef DRAW_TEXT_CAIRO
2476     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2477         cairo_reset_clip(dctx->uctx.u.cairo.cr);
2478         cairo_new_path(dctx->uctx.u.cairo.cr);
2479         cairo_rectangle(dctx->uctx.u.cairo.cr, x, y, w, h);
2480         cairo_clip(dctx->uctx.u.cairo.cr);
2481     }
2482 #endif
2483 }
2484
2485 static void draw_point(struct draw_ctx *dctx, int x, int y)
2486 {
2487 #ifdef DRAW_TEXT_GDK
2488     if (dctx->uctx.type == DRAWTYPE_GDK) {
2489         gdk_draw_point(dctx->uctx.u.gdk.target, dctx->uctx.u.gdk.gc, x, y);
2490     }
2491 #endif
2492 #ifdef DRAW_TEXT_CAIRO
2493     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2494         cairo_new_path(dctx->uctx.u.cairo.cr);
2495         cairo_rectangle(dctx->uctx.u.cairo.cr, x, y, 1, 1);
2496         cairo_fill(dctx->uctx.u.cairo.cr);
2497     }
2498 #endif
2499 }
2500
2501 static void draw_line(struct draw_ctx *dctx, int x0, int y0, int x1, int y1)
2502 {
2503 #ifdef DRAW_TEXT_GDK
2504     if (dctx->uctx.type == DRAWTYPE_GDK) {
2505         gdk_draw_line(dctx->uctx.u.gdk.target, dctx->uctx.u.gdk.gc,
2506                       x0, y0, x1, y1);
2507     }
2508 #endif
2509 #ifdef DRAW_TEXT_CAIRO
2510     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2511         cairo_new_path(dctx->uctx.u.cairo.cr);
2512         cairo_move_to(dctx->uctx.u.cairo.cr, x0 + 0.5, y0 + 0.5);
2513         cairo_line_to(dctx->uctx.u.cairo.cr, x1 + 0.5, y1 + 0.5);
2514         cairo_stroke(dctx->uctx.u.cairo.cr);
2515     }
2516 #endif
2517 }
2518
2519 static void draw_stretch_before(struct draw_ctx *dctx, int x, int y,
2520                                 int w, int wdouble,
2521                                 int h, int hdouble, int hbothalf)
2522 {
2523 #ifdef DRAW_TEXT_CAIRO
2524     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2525         cairo_matrix_t matrix;
2526
2527         matrix.xy = 0;
2528         matrix.yx = 0;
2529
2530         if (wdouble) {
2531             matrix.xx = 2;
2532             matrix.x0 = -x;
2533         } else {
2534             matrix.xx = 1;
2535             matrix.x0 = 0;
2536         }
2537
2538         if (hdouble) {
2539             matrix.yy = 2;
2540             if (hbothalf) {
2541                 matrix.y0 = -(y+h);
2542             } else {
2543                 matrix.y0 = -y;
2544             }
2545         } else {
2546             matrix.yy = 1;
2547             matrix.y0 = 0;
2548         }
2549         cairo_transform(dctx->uctx.u.cairo.cr, &matrix);
2550     }
2551 #endif
2552 }
2553
2554 static void draw_stretch_after(struct draw_ctx *dctx, int x, int y,
2555                                int w, int wdouble,
2556                                int h, int hdouble, int hbothalf)
2557 {
2558 #ifdef DRAW_TEXT_GDK
2559 #ifndef NO_BACKING_PIXMAPS
2560     if (dctx->uctx.type == DRAWTYPE_GDK) {
2561         /*
2562          * I can't find any plausible StretchBlt equivalent in the X
2563          * server, so I'm going to do this the slow and painful way.
2564          * This will involve repeated calls to gdk_draw_pixmap() to
2565          * stretch the text horizontally. It's O(N^2) in time and O(N)
2566          * in network bandwidth, but you try thinking of a better way.
2567          * :-(
2568          */
2569         int i;
2570         if (wdouble) {
2571             for (i = 0; i < w; i++) {
2572                 gdk_draw_pixmap(dctx->uctx.u.gdk.target,
2573                                 dctx->uctx.u.gdk.gc,
2574                                 dctx->uctx.u.gdk.target,
2575                                 x + 2*i, y,
2576                                 x + 2*i+1, y,
2577                                 w - i, h);
2578             }
2579             w *= 2;
2580         }
2581
2582         if (hdouble) {
2583             int dt, db;
2584             /* Now stretch vertically, in the same way. */
2585             if (hbothalf)
2586                 dt = 0, db = 1;
2587             else
2588                 dt = 1, db = 0;
2589             for (i = 0; i < h; i += 2) {
2590                 gdk_draw_pixmap(dctx->uctx.u.gdk.target,
2591                                 dctx->uctx.u.gdk.gc,
2592                                 dctx->uctx.u.gdk.target,
2593                                 x, y + dt*i + db,
2594                                 x, y + dt*(i+1),
2595                                 w, h-i-1);
2596             }
2597         }
2598     }
2599 #else
2600 #error No way to implement stretching in GDK without a reliable backing pixmap
2601 #endif
2602 #endif /* DRAW_TEXT_GDK */
2603 #ifdef DRAW_TEXT_CAIRO
2604     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
2605         cairo_set_matrix(dctx->uctx.u.cairo.cr,
2606                          &dctx->uctx.u.cairo.origmatrix);
2607     }
2608 #endif
2609 }
2610
2611 static void draw_backing_rect(struct gui_data *inst)
2612 {
2613     struct draw_ctx *dctx = get_ctx(inst);
2614     draw_set_colour(dctx, 258);
2615     draw_rectangle(dctx, 1, 0, 0,
2616                    inst->width * inst->font_width + 2*inst->window_border,
2617                    inst->height * inst->font_height + 2*inst->window_border);
2618     free_ctx(dctx);
2619 }
2620
2621 /*
2622  * Draw a line of text in the window, at given character
2623  * coordinates, in given attributes.
2624  *
2625  * We are allowed to fiddle with the contents of `text'.
2626  */
2627 void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
2628                       unsigned long attr, int lattr)
2629 {
2630     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2631     struct gui_data *inst = dctx->inst;
2632     int ncombining, combining;
2633     int nfg, nbg, t, fontid, shadow, rlen, widefactor, bold;
2634     int monochrome =
2635         gdk_visual_get_depth(gtk_widget_get_visual(inst->area)) == 1;
2636
2637     if (attr & TATTR_COMBINING) {
2638         ncombining = len;
2639         len = 1;
2640     } else
2641         ncombining = 1;
2642
2643     nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT);
2644     nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT);
2645     if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) {
2646         t = nfg;
2647         nfg = nbg;
2648         nbg = t;
2649     }
2650     if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) {
2651         if (nfg < 16) nfg |= 8;
2652         else if (nfg >= 256) nfg |= 1;
2653     }
2654     if ((inst->bold_style & 2) && (attr & ATTR_BLINK)) {
2655         if (nbg < 16) nbg |= 8;
2656         else if (nbg >= 256) nbg |= 1;
2657     }
2658     if ((attr & TATTR_ACTCURS) && !monochrome) {
2659         nfg = 260;
2660         nbg = 261;
2661     }
2662
2663     fontid = shadow = 0;
2664
2665     if (attr & ATTR_WIDE) {
2666         widefactor = 2;
2667         fontid |= 2;
2668     } else {
2669         widefactor = 1;
2670     }
2671
2672     if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) {
2673         bold = 1;
2674         fontid |= 1;
2675     } else {
2676         bold = 0;
2677     }
2678
2679     if (!inst->fonts[fontid]) {
2680         int i;
2681         /*
2682          * Fall back through font ids with subsets of this one's
2683          * set bits, in order.
2684          */
2685         for (i = fontid; i-- > 0 ;) {
2686             if (i & ~fontid)
2687                 continue;              /* some other bit is set */
2688             if (inst->fonts[i]) {
2689                 fontid = i;
2690                 break;
2691             }
2692         }
2693         assert(inst->fonts[fontid]);   /* we should at least have hit zero */
2694     }
2695
2696     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2697         x *= 2;
2698         if (x >= inst->term->cols)
2699             return;
2700         if (x + len*2*widefactor > inst->term->cols)
2701             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
2702         rlen = len * 2;
2703     } else
2704         rlen = len;
2705
2706     draw_clip(dctx,
2707               x*inst->font_width+inst->window_border,
2708               y*inst->font_height+inst->window_border,
2709               rlen*widefactor*inst->font_width,
2710               inst->font_height);
2711
2712     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2713         draw_stretch_before(dctx,
2714                             x*inst->font_width+inst->window_border,
2715                             y*inst->font_height+inst->window_border,
2716                             rlen*widefactor*inst->font_width, TRUE,
2717                             inst->font_height,
2718                             ((lattr & LATTR_MODE) != LATTR_WIDE),
2719                             ((lattr & LATTR_MODE) == LATTR_BOT));
2720     }
2721
2722     draw_set_colour(dctx, nbg);
2723     draw_rectangle(dctx, TRUE,
2724                    x*inst->font_width+inst->window_border,
2725                    y*inst->font_height+inst->window_border,
2726                    rlen*widefactor*inst->font_width, inst->font_height);
2727
2728     draw_set_colour(dctx, nfg);
2729     for (combining = 0; combining < ncombining; combining++) {
2730         unifont_draw_text(&dctx->uctx, inst->fonts[fontid],
2731                           x*inst->font_width+inst->window_border,
2732                           y*inst->font_height+inst->window_border+inst->fonts[0]->ascent,
2733                           text + combining, len, widefactor > 1,
2734                           bold, inst->font_width);
2735     }
2736
2737     if (attr & ATTR_UNDER) {
2738         int uheight = inst->fonts[0]->ascent + 1;
2739         if (uheight >= inst->font_height)
2740             uheight = inst->font_height - 1;
2741         draw_line(dctx, x*inst->font_width+inst->window_border,
2742                   y*inst->font_height + uheight + inst->window_border,
2743                   (x+len)*widefactor*inst->font_width-1+inst->window_border,
2744                   y*inst->font_height + uheight + inst->window_border);
2745     }
2746
2747     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2748         draw_stretch_after(dctx,
2749                            x*inst->font_width+inst->window_border,
2750                            y*inst->font_height+inst->window_border,
2751                            rlen*widefactor*inst->font_width, TRUE,
2752                            inst->font_height,
2753                            ((lattr & LATTR_MODE) != LATTR_WIDE),
2754                            ((lattr & LATTR_MODE) == LATTR_BOT));
2755     }
2756 }
2757
2758 void do_text(Context ctx, int x, int y, wchar_t *text, int len,
2759              unsigned long attr, int lattr)
2760 {
2761     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2762     struct gui_data *inst = dctx->inst;
2763     int widefactor;
2764
2765     do_text_internal(ctx, x, y, text, len, attr, lattr);
2766
2767     if (attr & ATTR_WIDE) {
2768         widefactor = 2;
2769     } else {
2770         widefactor = 1;
2771     }
2772
2773     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2774         x *= 2;
2775         if (x >= inst->term->cols)
2776             return;
2777         if (x + len*2*widefactor > inst->term->cols)
2778             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
2779         len *= 2;
2780     }
2781
2782     draw_update(dctx,
2783                 x*inst->font_width+inst->window_border,
2784                 y*inst->font_height+inst->window_border,
2785                 len*widefactor*inst->font_width, inst->font_height);
2786 }
2787
2788 void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
2789                unsigned long attr, int lattr)
2790 {
2791     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
2792     struct gui_data *inst = dctx->inst;
2793
2794     int active, passive, widefactor;
2795
2796     if (attr & TATTR_PASCURS) {
2797         attr &= ~TATTR_PASCURS;
2798         passive = 1;
2799     } else
2800         passive = 0;
2801     if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) {
2802         attr &= ~TATTR_ACTCURS;
2803         active = 1;
2804     } else
2805         active = 0;
2806     do_text_internal(ctx, x, y, text, len, attr, lattr);
2807
2808     if (attr & TATTR_COMBINING)
2809         len = 1;
2810
2811     if (attr & ATTR_WIDE) {
2812         widefactor = 2;
2813     } else {
2814         widefactor = 1;
2815     }
2816
2817     if ((lattr & LATTR_MODE) != LATTR_NORM) {
2818         x *= 2;
2819         if (x >= inst->term->cols)
2820             return;
2821         if (x + len*2*widefactor > inst->term->cols)
2822             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
2823         len *= 2;
2824     }
2825
2826     if (inst->cursor_type == 0) {
2827         /*
2828          * An active block cursor will already have been done by
2829          * the above do_text call, so we only need to do anything
2830          * if it's passive.
2831          */
2832         if (passive) {
2833             draw_set_colour(dctx, 261);
2834             draw_rectangle(dctx, FALSE,
2835                            x*inst->font_width+inst->window_border,
2836                            y*inst->font_height+inst->window_border,
2837                            len*widefactor*inst->font_width-1,
2838                            inst->font_height-1);
2839         }
2840     } else {
2841         int uheight;
2842         int startx, starty, dx, dy, length, i;
2843
2844         int char_width;
2845
2846         if ((attr & ATTR_WIDE) || (lattr & LATTR_MODE) != LATTR_NORM)
2847             char_width = 2*inst->font_width;
2848         else
2849             char_width = inst->font_width;
2850
2851         if (inst->cursor_type == 1) {
2852             uheight = inst->fonts[0]->ascent + 1;
2853             if (uheight >= inst->font_height)
2854                 uheight = inst->font_height - 1;
2855
2856             startx = x * inst->font_width + inst->window_border;
2857             starty = y * inst->font_height + inst->window_border + uheight;
2858             dx = 1;
2859             dy = 0;
2860             length = len * widefactor * char_width;
2861         } else {
2862             int xadjust = 0;
2863             if (attr & TATTR_RIGHTCURS)
2864                 xadjust = char_width - 1;
2865             startx = x * inst->font_width + inst->window_border + xadjust;
2866             starty = y * inst->font_height + inst->window_border;
2867             dx = 0;
2868             dy = 1;
2869             length = inst->font_height;
2870         }
2871
2872         draw_set_colour(dctx, 261);
2873         if (passive) {
2874             for (i = 0; i < length; i++) {
2875                 if (i % 2 == 0) {
2876                     draw_point(dctx, startx, starty);
2877                 }
2878                 startx += dx;
2879                 starty += dy;
2880             }
2881         } else if (active) {
2882             draw_line(dctx, startx, starty,
2883                       startx + (length-1) * dx, starty + (length-1) * dy);
2884         } /* else no cursor (e.g., blinked off) */
2885     }
2886
2887     draw_update(dctx,
2888                 x*inst->font_width+inst->window_border,
2889                 y*inst->font_height+inst->window_border,
2890                 len*widefactor*inst->font_width, inst->font_height);
2891
2892 #if GTK_CHECK_VERSION(2,0,0)
2893     {
2894         GdkRectangle cursorrect;
2895         cursorrect.x = x*inst->font_width+inst->window_border;
2896         cursorrect.y = y*inst->font_height+inst->window_border;
2897         cursorrect.width = len*widefactor*inst->font_width;
2898         cursorrect.height = inst->font_height;
2899         gtk_im_context_set_cursor_location(inst->imc, &cursorrect);
2900     }
2901 #endif
2902 }
2903
2904 GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
2905 {
2906     if (cursor_val == -1) {
2907 #if GTK_CHECK_VERSION(2,16,0)
2908         cursor_val = GDK_BLANK_CURSOR;
2909 #else
2910         /*
2911          * Work around absence of GDK_BLANK_CURSOR by inventing a
2912          * blank pixmap.
2913          */
2914         GdkCursor *ret;
2915         GdkColor bg = { 0, 0, 0, 0 };
2916         GdkPixmap *pm = gdk_pixmap_new(NULL, 1, 1, 1);
2917         GdkGC *gc = gdk_gc_new(pm);
2918         gdk_gc_set_foreground(gc, &bg);
2919         gdk_draw_rectangle(pm, gc, 1, 0, 0, 1, 1);
2920         gdk_gc_unref(gc);
2921         ret = gdk_cursor_new_from_pixmap(pm, pm, &bg, &bg, 1, 1);
2922         gdk_pixmap_unref(pm);
2923         return ret;
2924 #endif
2925     }
2926
2927     return gdk_cursor_new(cursor_val);
2928 }
2929
2930 void modalfatalbox(const char *p, ...)
2931 {
2932     va_list ap;
2933     fprintf(stderr, "FATAL ERROR: ");
2934     va_start(ap, p);
2935     vfprintf(stderr, p, ap);
2936     va_end(ap);
2937     fputc('\n', stderr);
2938     exit(1);
2939 }
2940
2941 void cmdline_error(const char *p, ...)
2942 {
2943     va_list ap;
2944     fprintf(stderr, "%s: ", appname);
2945     va_start(ap, p);
2946     vfprintf(stderr, p, ap);
2947     va_end(ap);
2948     fputc('\n', stderr);
2949     exit(1);
2950 }
2951
2952 char *get_x_display(void *frontend)
2953 {
2954     return gdk_get_display();
2955 }
2956
2957 #ifndef NOT_X_WINDOWS
2958 long get_windowid(void *frontend)
2959 {
2960     struct gui_data *inst = (struct gui_data *)frontend;
2961     return (long)GDK_WINDOW_XWINDOW(gtk_widget_get_window(inst->area));
2962 }
2963 #endif
2964
2965 static void help(FILE *fp) {
2966     if(fprintf(fp,
2967 "pterm option summary:\n"
2968 "\n"
2969 "  --display DISPLAY         Specify X display to use (note '--')\n"
2970 "  -name PREFIX              Prefix when looking up resources (default: pterm)\n"
2971 "  -fn FONT                  Normal text font\n"
2972 "  -fb FONT                  Bold text font\n"
2973 "  -geometry GEOMETRY        Position and size of window (size in characters)\n"
2974 "  -sl LINES                 Number of lines of scrollback\n"
2975 "  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"
2976 "  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"
2977 "  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"
2978 "  -T TITLE                  Window title\n"
2979 "  -ut, +ut                  Do(default) or do not update utmp\n"
2980 "  -ls, +ls                  Do(default) or do not make shell a login shell\n"
2981 "  -sb, +sb                  Do(default) or do not display a scrollbar\n"
2982 "  -log PATH                 Log all output to a file\n"
2983 "  -nethack                  Map numeric keypad to hjklyubn direction keys\n"
2984 "  -xrm RESOURCE-STRING      Set an X resource\n"
2985 "  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"
2986          ) < 0 || fflush(fp) < 0) {
2987         perror("output error");
2988         exit(1);
2989     }
2990 }
2991
2992 static void version(FILE *fp) {
2993     if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
2994         perror("output error");
2995         exit(1);
2996     }
2997 }
2998
2999 int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
3000                struct gui_data *inst, Conf *conf)
3001 {
3002     int err = 0;
3003     char *val;
3004
3005     /*
3006      * Macros to make argument handling easier. Note that because
3007      * they need to call `continue', they cannot be contained in
3008      * the usual do {...} while (0) wrapper to make them
3009      * syntactically single statements; hence it is not legal to
3010      * use one of these macros as an unbraced statement between
3011      * `if' and `else'.
3012      */
3013 #define EXPECTS_ARG { \
3014     if (--argc <= 0) { \
3015         err = 1; \
3016         fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
3017         continue; \
3018     } else \
3019         val = *++argv; \
3020 }
3021 #define SECOND_PASS_ONLY { if (!do_everything) continue; }
3022
3023     while (--argc > 0) {
3024         const char *p = *++argv;
3025         int ret;
3026
3027         /*
3028          * Shameless cheating. Debian requires all X terminal
3029          * emulators to support `-T title'; but
3030          * cmdline_process_param will eat -T (it means no-pty) and
3031          * complain that pterm doesn't support it. So, in pterm
3032          * only, we convert -T into -title.
3033          */
3034         if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
3035             !strcmp(p, "-T"))
3036             p = "-title";
3037
3038         ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
3039                                     do_everything ? 1 : -1, conf);
3040
3041         if (ret == -2) {
3042             cmdline_error("option \"%s\" requires an argument", p);
3043         } else if (ret == 2) {
3044             --argc, ++argv;            /* skip next argument */
3045             continue;
3046         } else if (ret == 1) {
3047             continue;
3048         }
3049
3050         if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
3051             FontSpec *fs;
3052             EXPECTS_ARG;
3053             SECOND_PASS_ONLY;
3054             fs = fontspec_new(val);
3055             conf_set_fontspec(conf, CONF_font, fs);
3056             fontspec_free(fs);
3057
3058         } else if (!strcmp(p, "-fb")) {
3059             FontSpec *fs;
3060             EXPECTS_ARG;
3061             SECOND_PASS_ONLY;
3062             fs = fontspec_new(val);
3063             conf_set_fontspec(conf, CONF_boldfont, fs);
3064             fontspec_free(fs);
3065
3066         } else if (!strcmp(p, "-fw")) {
3067             FontSpec *fs;
3068             EXPECTS_ARG;
3069             SECOND_PASS_ONLY;
3070             fs = fontspec_new(val);
3071             conf_set_fontspec(conf, CONF_widefont, fs);
3072             fontspec_free(fs);
3073
3074         } else if (!strcmp(p, "-fwb")) {
3075             FontSpec *fs;
3076             EXPECTS_ARG;
3077             SECOND_PASS_ONLY;
3078             fs = fontspec_new(val);
3079             conf_set_fontspec(conf, CONF_wideboldfont, fs);
3080             fontspec_free(fs);
3081
3082         } else if (!strcmp(p, "-cs")) {
3083             EXPECTS_ARG;
3084             SECOND_PASS_ONLY;
3085             conf_set_str(conf, CONF_line_codepage, val);
3086
3087         } else if (!strcmp(p, "-geometry")) {
3088             EXPECTS_ARG;
3089             SECOND_PASS_ONLY;
3090
3091 #if GTK_CHECK_VERSION(2,0,0)
3092             inst->geometry = val;
3093 #else
3094             /* On GTK 1, we have to do this using raw Xlib */
3095             {
3096                 int flags, x, y;
3097                 unsigned int w, h;
3098                 flags = XParseGeometry(val, &x, &y, &w, &h);
3099                 if (flags & WidthValue)
3100                     conf_set_int(conf, CONF_width, w);
3101                 if (flags & HeightValue)
3102                     conf_set_int(conf, CONF_height, h);
3103
3104                 if (flags & (XValue | YValue)) {
3105                     inst->xpos = x;
3106                     inst->ypos = y;
3107                     inst->gotpos = TRUE;
3108                     inst->gravity = ((flags & XNegative ? 1 : 0) |
3109                                      (flags & YNegative ? 2 : 0));
3110                 }
3111             }
3112 #endif
3113
3114         } else if (!strcmp(p, "-sl")) {
3115             EXPECTS_ARG;
3116             SECOND_PASS_ONLY;
3117             conf_set_int(conf, CONF_savelines, atoi(val));
3118
3119         } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
3120                    !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
3121                    !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
3122             GdkColor col;
3123
3124             EXPECTS_ARG;
3125             SECOND_PASS_ONLY;
3126             if (!gdk_color_parse(val, &col)) {
3127                 err = 1;
3128                 fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
3129                         appname, val);
3130             } else {
3131                 int index;
3132                 index = (!strcmp(p, "-fg") ? 0 :
3133                          !strcmp(p, "-bg") ? 2 :
3134                          !strcmp(p, "-bfg") ? 1 :
3135                          !strcmp(p, "-bbg") ? 3 :
3136                          !strcmp(p, "-cfg") ? 4 :
3137                          !strcmp(p, "-cbg") ? 5 : -1);
3138                 assert(index != -1);
3139                 conf_set_int_int(conf, CONF_colours, index*3+0, col.red / 256);
3140                 conf_set_int_int(conf, CONF_colours, index*3+1,col.green/ 256);
3141                 conf_set_int_int(conf, CONF_colours, index*3+2, col.blue/ 256);
3142             }
3143
3144         } else if (use_pty_argv && !strcmp(p, "-e")) {
3145             /* This option swallows all further arguments. */
3146             if (!do_everything)
3147                 break;
3148
3149             if (--argc > 0) {
3150                 int i;
3151                 pty_argv = snewn(argc+1, char *);
3152                 ++argv;
3153                 for (i = 0; i < argc; i++)
3154                     pty_argv[i] = argv[i];
3155                 pty_argv[argc] = NULL;
3156                 break;                 /* finished command-line processing */
3157             } else
3158                 err = 1, fprintf(stderr, "%s: -e expects an argument\n",
3159                                  appname);
3160
3161         } else if (!strcmp(p, "-title")) {
3162             EXPECTS_ARG;
3163             SECOND_PASS_ONLY;
3164             conf_set_str(conf, CONF_wintitle, val);
3165
3166         } else if (!strcmp(p, "-log")) {
3167             Filename *fn;
3168             EXPECTS_ARG;
3169             SECOND_PASS_ONLY;
3170             fn = filename_from_str(val);
3171             conf_set_filename(conf, CONF_logfilename, fn);
3172             conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
3173             filename_free(fn);
3174
3175         } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
3176             SECOND_PASS_ONLY;
3177             conf_set_int(conf, CONF_stamp_utmp, 0);
3178
3179         } else if (!strcmp(p, "-ut")) {
3180             SECOND_PASS_ONLY;
3181             conf_set_int(conf, CONF_stamp_utmp, 1);
3182
3183         } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
3184             SECOND_PASS_ONLY;
3185             conf_set_int(conf, CONF_login_shell, 0);
3186
3187         } else if (!strcmp(p, "-ls")) {
3188             SECOND_PASS_ONLY;
3189             conf_set_int(conf, CONF_login_shell, 1);
3190
3191         } else if (!strcmp(p, "-nethack")) {
3192             SECOND_PASS_ONLY;
3193             conf_set_int(conf, CONF_nethack_keypad, 1);
3194
3195         } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
3196             SECOND_PASS_ONLY;
3197             conf_set_int(conf, CONF_scrollbar, 0);
3198
3199         } else if (!strcmp(p, "-sb")) {
3200             SECOND_PASS_ONLY;
3201             conf_set_int(conf, CONF_scrollbar, 1);
3202
3203         } else if (!strcmp(p, "-name")) {
3204             EXPECTS_ARG;
3205             app_name = val;
3206
3207         } else if (!strcmp(p, "-xrm")) {
3208             EXPECTS_ARG;
3209             provide_xrm_string(val);
3210
3211         } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
3212             help(stdout);
3213             exit(0);
3214
3215         } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
3216             version(stdout);
3217             exit(0);
3218
3219         } else if (!strcmp(p, "-pgpfp")) {
3220             pgp_fingerprints();
3221             exit(1);
3222
3223         } else if(p[0] != '-' && (!do_everything ||
3224                                   process_nonoption_arg(p, conf,
3225                                                         allow_launch))) {
3226             /* do nothing */
3227
3228         } else {
3229             err = 1;
3230             fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
3231         }
3232     }
3233
3234     return err;
3235 }
3236
3237 struct uxsel_id {
3238 #if GTK_CHECK_VERSION(2,0,0)
3239     GIOChannel *chan;
3240     guint watch_id;
3241 #else
3242     int id;
3243 #endif
3244 };
3245
3246 uxsel_id *uxsel_input_add(int fd, int rwx) {
3247     uxsel_id *id = snew(uxsel_id);
3248
3249 #if GTK_CHECK_VERSION(2,0,0)
3250     int flags = 0;
3251     if (rwx & 1) flags |= G_IO_IN;
3252     if (rwx & 2) flags |= G_IO_OUT;
3253     if (rwx & 4) flags |= G_IO_PRI;
3254     id->chan = g_io_channel_unix_new(fd);
3255     g_io_channel_set_encoding(id->chan, NULL, NULL);
3256     id->watch_id = g_io_add_watch(id->chan, flags, fd_input_func, NULL);
3257 #else
3258     int flags = 0;
3259     if (rwx & 1) flags |= GDK_INPUT_READ;
3260     if (rwx & 2) flags |= GDK_INPUT_WRITE;
3261     if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
3262     assert(flags);
3263     id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
3264 #endif
3265
3266     return id;
3267 }
3268
3269 void uxsel_input_remove(uxsel_id *id) {
3270 #if GTK_CHECK_VERSION(2,0,0)
3271     g_source_remove(id->watch_id);
3272     g_io_channel_unref(id->chan);
3273 #else
3274     gdk_input_remove(id->id);
3275 #endif
3276     sfree(id);
3277 }
3278
3279 char *setup_fonts_ucs(struct gui_data *inst)
3280 {
3281     int shadowbold = conf_get_int(inst->conf, CONF_shadowbold);
3282     int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset);
3283     FontSpec *fs;
3284     unifont *fonts[4];
3285     int i;
3286
3287     fs = conf_get_fontspec(inst->conf, CONF_font);
3288     fonts[0] = multifont_create(inst->area, fs->name, FALSE, FALSE,
3289                                 shadowboldoffset, shadowbold);
3290     if (!fonts[0]) {
3291         return dupprintf("unable to load font \"%s\"", fs->name);
3292     }
3293
3294     fs = conf_get_fontspec(inst->conf, CONF_boldfont);
3295     if (shadowbold || !fs->name[0]) {
3296         fonts[1] = NULL;
3297     } else {
3298         fonts[1] = multifont_create(inst->area, fs->name, FALSE, TRUE,
3299                                     shadowboldoffset, shadowbold);
3300         if (!fonts[1]) {
3301             if (fonts[0])
3302                 unifont_destroy(fonts[0]);
3303             return dupprintf("unable to load bold font \"%s\"", fs->name);
3304         }
3305     }
3306
3307     fs = conf_get_fontspec(inst->conf, CONF_widefont);
3308     if (fs->name[0]) {
3309         fonts[2] = multifont_create(inst->area, fs->name, TRUE, FALSE,
3310                                     shadowboldoffset, shadowbold);
3311         if (!fonts[2]) {
3312             for (i = 0; i < 2; i++)
3313                 if (fonts[i])
3314                     unifont_destroy(fonts[i]);
3315             return dupprintf("unable to load wide font \"%s\"", fs->name);
3316         }
3317     } else {
3318         fonts[2] = NULL;
3319     }
3320
3321     fs = conf_get_fontspec(inst->conf, CONF_wideboldfont);
3322     if (shadowbold || !fs->name[0]) {
3323         fonts[3] = NULL;
3324     } else {
3325         fonts[3] = multifont_create(inst->area, fs->name, TRUE, TRUE,
3326                                     shadowboldoffset, shadowbold);
3327         if (!fonts[3]) {
3328             for (i = 0; i < 3; i++)
3329                 if (fonts[i])
3330                     unifont_destroy(fonts[i]);
3331             return dupprintf("unable to load wide bold font \"%s\"", fs->name);
3332         }
3333     }
3334
3335     /*
3336      * Now we've got past all the possible error conditions, we can
3337      * actually update our state.
3338      */
3339
3340     for (i = 0; i < 4; i++) {
3341         if (inst->fonts[i])
3342             unifont_destroy(inst->fonts[i]);
3343         inst->fonts[i] = fonts[i];
3344     }
3345
3346     inst->font_width = inst->fonts[0]->width;
3347     inst->font_height = inst->fonts[0]->height;
3348
3349     inst->direct_to_font = init_ucs(&inst->ucsdata,
3350                                     conf_get_str(inst->conf, CONF_line_codepage),
3351                                     conf_get_int(inst->conf, CONF_utf8_override),
3352                                     inst->fonts[0]->public_charset,
3353                                     conf_get_int(inst->conf, CONF_vtmode));
3354
3355     inst->drawtype = inst->fonts[0]->preferred_drawtype;
3356
3357     return NULL;
3358 }
3359
3360 void set_geom_hints(struct gui_data *inst)
3361 {
3362     GdkGeometry geom;
3363     geom.min_width = inst->font_width + 2*inst->window_border;
3364     geom.min_height = inst->font_height + 2*inst->window_border;
3365     geom.max_width = geom.max_height = -1;
3366     geom.base_width = 2*inst->window_border;
3367     geom.base_height = 2*inst->window_border;
3368     geom.width_inc = inst->font_width;
3369     geom.height_inc = inst->font_height;
3370     geom.min_aspect = geom.max_aspect = 0;
3371     gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,
3372                                   GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
3373                                   GDK_HINT_RESIZE_INC);
3374 }
3375
3376 void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)
3377 {
3378     struct gui_data *inst = (struct gui_data *)data;
3379     term_clrsb(inst->term);
3380 }
3381
3382 void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
3383 {
3384     struct gui_data *inst = (struct gui_data *)data;
3385     term_pwron(inst->term, TRUE);
3386     if (inst->ldisc)
3387         ldisc_echoedit_update(inst->ldisc);
3388 }
3389
3390 void copy_all_menuitem(GtkMenuItem *item, gpointer data)
3391 {
3392     struct gui_data *inst = (struct gui_data *)data;
3393     term_copyall(inst->term);
3394 }
3395
3396 void special_menuitem(GtkMenuItem *item, gpointer data)
3397 {
3398     struct gui_data *inst = (struct gui_data *)data;
3399     int code = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
3400                                                  "user-data"));
3401
3402     if (inst->back)
3403         inst->back->special(inst->backhandle, code);
3404 }
3405
3406 void about_menuitem(GtkMenuItem *item, gpointer data)
3407 {
3408     struct gui_data *inst = (struct gui_data *)data;
3409     about_box(inst->window);
3410 }
3411
3412 void event_log_menuitem(GtkMenuItem *item, gpointer data)
3413 {
3414     struct gui_data *inst = (struct gui_data *)data;
3415     showeventlog(inst->eventlogstuff, inst->window);
3416 }
3417
3418 void change_settings_menuitem(GtkMenuItem *item, gpointer data)
3419 {
3420     /* This maps colour indices in inst->conf to those used in inst->cols. */
3421     static const int ww[] = {
3422         256, 257, 258, 259, 260, 261,
3423         0, 8, 1, 9, 2, 10, 3, 11,
3424         4, 12, 5, 13, 6, 14, 7, 15
3425     };
3426     struct gui_data *inst = (struct gui_data *)data;
3427     char *title;
3428     Conf *oldconf, *newconf;
3429     int i, j, need_size;
3430
3431     assert(lenof(ww) == NCFGCOLOURS);
3432
3433     if (inst->reconfiguring)
3434       return;
3435     else
3436       inst->reconfiguring = TRUE;
3437
3438     title = dupcat(appname, " Reconfiguration", NULL);
3439
3440     oldconf = inst->conf;
3441     newconf = conf_copy(inst->conf);
3442
3443     if (do_config_box(title, newconf, 1,
3444                       inst->back?inst->back->cfg_info(inst->backhandle):0)) {
3445         inst->conf = newconf;
3446
3447         /* Pass new config data to the logging module */
3448         log_reconfig(inst->logctx, inst->conf);
3449         /*
3450          * Flush the line discipline's edit buffer in the case
3451          * where local editing has just been disabled.
3452          */
3453         if (inst->ldisc) {
3454             ldisc_configure(inst->ldisc, inst->conf);
3455             ldisc_echoedit_update(inst->ldisc);
3456         }
3457         /* Pass new config data to the terminal */
3458         term_reconfig(inst->term, inst->conf);
3459         /* Pass new config data to the back end */
3460         if (inst->back)
3461             inst->back->reconfig(inst->backhandle, inst->conf);
3462
3463         cache_conf_values(inst);
3464
3465         /*
3466          * Just setting inst->conf is sufficient to cause colour
3467          * setting changes to appear on the next ESC]R palette
3468          * reset. But we should also check whether any colour
3469          * settings have been changed, and revert the ones that have
3470          * to the new default, on the assumption that the user is
3471          * most likely to want an immediate update.
3472          */
3473         for (i = 0; i < NCFGCOLOURS; i++) {
3474             for (j = 0; j < 3; j++)
3475                 if (conf_get_int_int(oldconf, CONF_colours, i*3+j) !=
3476                     conf_get_int_int(newconf, CONF_colours, i*3+j))
3477                     break;
3478             if (j < 3) {
3479                 real_palette_set(inst, ww[i],
3480                                  conf_get_int_int(newconf,CONF_colours,i*3+0),
3481                                  conf_get_int_int(newconf,CONF_colours,i*3+1),
3482                                  conf_get_int_int(newconf,CONF_colours,i*3+2));
3483
3484                 /*
3485                  * If the default background has changed, we must
3486                  * repaint the space in between the window border
3487                  * and the text area.
3488                  */
3489                 if (ww[i] == 258) {
3490                     set_window_background(inst);
3491                     draw_backing_rect(inst);
3492                 }
3493             }
3494         }
3495
3496         /*
3497          * If the scrollbar needs to be shown, hidden, or moved
3498          * from one end to the other of the window, do so now.
3499          */
3500         if (conf_get_int(oldconf, CONF_scrollbar) !=
3501             conf_get_int(newconf, CONF_scrollbar)) {
3502             if (conf_get_int(newconf, CONF_scrollbar))
3503                 gtk_widget_show(inst->sbar);
3504             else
3505                 gtk_widget_hide(inst->sbar);
3506         }
3507         if (conf_get_int(oldconf, CONF_scrollbar_on_left) !=
3508             conf_get_int(newconf, CONF_scrollbar_on_left)) {
3509             gtk_box_reorder_child(inst->hbox, inst->sbar,
3510                                   conf_get_int(newconf, CONF_scrollbar_on_left)
3511                                   ? 0 : 1);
3512         }
3513
3514         /*
3515          * Change the window title, if required.
3516          */
3517         if (strcmp(conf_get_str(oldconf, CONF_wintitle),
3518                    conf_get_str(newconf, CONF_wintitle)))
3519             set_title(inst, conf_get_str(newconf, CONF_wintitle));
3520         set_window_titles(inst);
3521
3522         /*
3523          * Redo the whole tangled fonts and Unicode mess if
3524          * necessary.
3525          */
3526         need_size = FALSE;
3527         if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name,
3528                    conf_get_fontspec(newconf, CONF_font)->name) ||
3529             strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name,
3530                    conf_get_fontspec(newconf, CONF_boldfont)->name) ||
3531             strcmp(conf_get_fontspec(oldconf, CONF_widefont)->name,
3532                    conf_get_fontspec(newconf, CONF_widefont)->name) ||
3533             strcmp(conf_get_fontspec(oldconf, CONF_wideboldfont)->name,
3534                    conf_get_fontspec(newconf, CONF_wideboldfont)->name) ||
3535             strcmp(conf_get_str(oldconf, CONF_line_codepage),
3536                    conf_get_str(newconf, CONF_line_codepage)) ||
3537             conf_get_int(oldconf, CONF_utf8_override) !=
3538             conf_get_int(newconf, CONF_utf8_override) ||
3539             conf_get_int(oldconf, CONF_vtmode) !=
3540             conf_get_int(newconf, CONF_vtmode) ||
3541             conf_get_int(oldconf, CONF_shadowbold) !=
3542             conf_get_int(newconf, CONF_shadowbold) ||
3543             conf_get_int(oldconf, CONF_shadowboldoffset) !=
3544             conf_get_int(newconf, CONF_shadowboldoffset)) {
3545             char *errmsg = setup_fonts_ucs(inst);
3546             if (errmsg) {
3547                 char *msgboxtext =
3548                     dupprintf("Could not change fonts in terminal window: %s\n",
3549                               errmsg);
3550                 messagebox(inst->window, "Font setup error", msgboxtext,
3551                            string_width("Could not change fonts in terminal window:"),
3552                            "OK", 'o', +1, 1,
3553                            NULL);
3554                 sfree(msgboxtext);
3555                 sfree(errmsg);
3556             } else {
3557                 need_size = TRUE;
3558             }
3559         }
3560
3561         /*
3562          * Resize the window.
3563          */
3564         if (conf_get_int(oldconf, CONF_width) !=
3565             conf_get_int(newconf, CONF_width) ||
3566             conf_get_int(oldconf, CONF_height) !=
3567             conf_get_int(newconf, CONF_height) ||
3568             conf_get_int(oldconf, CONF_window_border) !=
3569             conf_get_int(newconf, CONF_window_border) ||
3570             need_size) {
3571             set_geom_hints(inst);
3572             request_resize(inst, conf_get_int(newconf, CONF_width),
3573                            conf_get_int(newconf, CONF_height));
3574         } else {
3575             /*
3576              * The above will have caused a call to term_size() for
3577              * us if it happened. If the user has fiddled with only
3578              * the scrollback size, the above will not have
3579              * happened and we will need an explicit term_size()
3580              * here.
3581              */
3582             if (conf_get_int(oldconf, CONF_savelines) !=
3583                 conf_get_int(newconf, CONF_savelines))
3584                 term_size(inst->term, inst->term->rows, inst->term->cols,
3585                           conf_get_int(newconf, CONF_savelines));
3586         }
3587
3588         term_invalidate(inst->term);
3589
3590         /*
3591          * We do an explicit full redraw here to ensure the window
3592          * border has been redrawn as well as the text area.
3593          */
3594         gtk_widget_queue_draw(inst->area);
3595
3596         conf_free(oldconf);
3597     } else {
3598         conf_free(newconf);
3599     }
3600     sfree(title);
3601     inst->reconfiguring = FALSE;
3602 }
3603
3604 void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)
3605 {
3606     /*
3607      * Re-execing ourself is not an exact science under Unix. I do
3608      * the best I can by using /proc/self/exe if available and by
3609      * assuming argv[0] can be found on $PATH if not.
3610      * 
3611      * Note that we also have to reconstruct the elements of the
3612      * original argv which gtk swallowed, since the user wants the
3613      * new session to appear on the same X display as the old one.
3614      */
3615     char **args;
3616     va_list ap;
3617     int i, n;
3618     int pid;
3619
3620     /*
3621      * Collect the arguments with which to re-exec ourself.
3622      */
3623     va_start(ap, fd_to_close);
3624     n = 2;                             /* progname and terminating NULL */
3625     n += inst->ngtkargs;
3626     while (va_arg(ap, char *) != NULL)
3627         n++;
3628     va_end(ap);
3629
3630     args = snewn(n, char *);
3631     args[0] = inst->progname;
3632     args[n-1] = NULL;
3633     for (i = 0; i < inst->ngtkargs; i++)
3634         args[i+1] = inst->gtkargvstart[i];
3635
3636     i++;
3637     va_start(ap, fd_to_close);
3638     while ((args[i++] = va_arg(ap, char *)) != NULL);
3639     va_end(ap);
3640
3641     assert(i == n);
3642
3643     /*
3644      * Do the double fork.
3645      */
3646     pid = fork();
3647     if (pid < 0) {
3648         perror("fork");
3649         sfree(args);
3650         return;
3651     }
3652
3653     if (pid == 0) {
3654         int pid2 = fork();
3655         if (pid2 < 0) {
3656             perror("fork");
3657             _exit(1);
3658         } else if (pid2 > 0) {
3659             /*
3660              * First child has successfully forked second child. My
3661              * Work Here Is Done. Note the use of _exit rather than
3662              * exit: the latter appears to cause destroy messages
3663              * to be sent to the X server. I suspect gtk uses
3664              * atexit.
3665              */
3666             _exit(0);
3667         }
3668
3669         /*
3670          * If we reach here, we are the second child, so we now
3671          * actually perform the exec.
3672          */
3673         if (fd_to_close >= 0)
3674             close(fd_to_close);
3675
3676         execv("/proc/self/exe", args);
3677         execvp(inst->progname, args);
3678         perror("exec");
3679         _exit(127);
3680
3681     } else {
3682         int status;
3683         sfree(args);
3684         waitpid(pid, &status, 0);
3685     }
3686
3687 }
3688
3689 void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
3690 {
3691     struct gui_data *inst = (struct gui_data *)gdata;
3692     /*
3693      * For this feature we must marshal conf and (possibly) pty_argv
3694      * into a byte stream, create a pipe, and send this byte stream
3695      * to the child through the pipe.
3696      */
3697     int i, ret, sersize, size;
3698     char *data;
3699     char option[80];
3700     int pipefd[2];
3701
3702     if (pipe(pipefd) < 0) {
3703         perror("pipe");
3704         return;
3705     }
3706
3707     size = sersize = conf_serialised_size(inst->conf);
3708     if (use_pty_argv && pty_argv) {
3709         for (i = 0; pty_argv[i]; i++)
3710             size += strlen(pty_argv[i]) + 1;
3711     }
3712
3713     data = snewn(size, char);
3714     conf_serialise(inst->conf, data);
3715     if (use_pty_argv && pty_argv) {
3716         int p = sersize;
3717         for (i = 0; pty_argv[i]; i++) {
3718             strcpy(data + p, pty_argv[i]);
3719             p += strlen(pty_argv[i]) + 1;
3720         }
3721         assert(p == size);
3722     }
3723
3724     sprintf(option, "---[%d,%d]", pipefd[0], size);
3725     noncloexec(pipefd[0]);
3726     fork_and_exec_self(inst, pipefd[1], option, NULL);
3727     close(pipefd[0]);
3728
3729     i = ret = 0;
3730     while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
3731         i += ret;
3732     if (ret < 0)
3733         perror("write to pipe");
3734     close(pipefd[1]);
3735     sfree(data);
3736 }
3737
3738 int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg)
3739 {
3740     int fd, i, ret, size, size_used;
3741     char *data;
3742
3743     if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
3744         fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
3745         exit(1);
3746     }
3747
3748     data = snewn(size, char);
3749     i = ret = 0;
3750     while (i < size && (ret = read(fd, data + i, size - i)) > 0)
3751         i += ret;
3752     if (ret < 0) {
3753         perror("read from pipe");
3754         exit(1);
3755     } else if (i < size) {
3756         fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
3757                 appname);
3758         exit(1);
3759     }
3760
3761     size_used = conf_deserialise(conf, data, size);
3762     if (use_pty_argv && size > size_used) {
3763         int n = 0;
3764         i = size_used;
3765         while (i < size) {
3766             while (i < size && data[i]) i++;
3767             if (i >= size) {
3768                 fprintf(stderr, "%s: malformed Duplicate Session data\n",
3769                         appname);
3770                 exit(1);
3771             }
3772             i++;
3773             n++;
3774         }
3775         pty_argv = snewn(n+1, char *);
3776         pty_argv[n] = NULL;
3777         n = 0;
3778         i = size_used;
3779         while (i < size) {
3780             char *p = data + i;
3781             while (i < size && data[i]) i++;
3782             assert(i < size);
3783             i++;
3784             pty_argv[n++] = dupstr(p);
3785         }
3786     }
3787
3788     sfree(data);
3789
3790     return 0;
3791 }
3792
3793 void new_session_menuitem(GtkMenuItem *item, gpointer data)
3794 {
3795     struct gui_data *inst = (struct gui_data *)data;
3796
3797     fork_and_exec_self(inst, -1, NULL);
3798 }
3799
3800 void restart_session_menuitem(GtkMenuItem *item, gpointer data)
3801 {
3802     struct gui_data *inst = (struct gui_data *)data;
3803
3804     if (!inst->back) {
3805         logevent(inst, "----- Session restarted -----");
3806         term_pwron(inst->term, FALSE);
3807         start_backend(inst);
3808         inst->exited = FALSE;
3809     }
3810 }
3811
3812 void saved_session_menuitem(GtkMenuItem *item, gpointer data)
3813 {
3814     struct gui_data *inst = (struct gui_data *)data;
3815     char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data");
3816
3817     fork_and_exec_self(inst, -1, "-load", str, NULL);
3818 }
3819
3820 void saved_session_freedata(GtkMenuItem *item, gpointer data)
3821 {
3822     char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data");
3823
3824     sfree(str);
3825 }
3826
3827 static void update_savedsess_menu(GtkMenuItem *menuitem, gpointer data)
3828 {
3829     struct gui_data *inst = (struct gui_data *)data;
3830     struct sesslist sesslist;
3831     int i;
3832
3833     gtk_container_foreach(GTK_CONTAINER(inst->sessionsmenu),
3834                           (GtkCallback)gtk_widget_destroy, NULL);
3835
3836     get_sesslist(&sesslist, TRUE);
3837     /* skip sesslist.sessions[0] == Default Settings */
3838     for (i = 1; i < sesslist.nsessions; i++) {
3839         GtkWidget *menuitem =
3840             gtk_menu_item_new_with_label(sesslist.sessions[i]);
3841         gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);
3842         gtk_widget_show(menuitem);
3843         g_object_set_data(G_OBJECT(menuitem), "user-data",
3844                           dupstr(sesslist.sessions[i]));
3845         g_signal_connect(G_OBJECT(menuitem), "activate",
3846                          G_CALLBACK(saved_session_menuitem),
3847                          inst);
3848         g_signal_connect(G_OBJECT(menuitem), "destroy",
3849                          G_CALLBACK(saved_session_freedata),
3850                          inst);
3851     }
3852     if (sesslist.nsessions <= 1) {
3853         GtkWidget *menuitem =
3854             gtk_menu_item_new_with_label("(No sessions)");
3855         gtk_widget_set_sensitive(menuitem, FALSE);
3856         gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);
3857         gtk_widget_show(menuitem);
3858     }
3859     get_sesslist(&sesslist, FALSE); /* free up */
3860 }
3861
3862 void set_window_icon(GtkWidget *window, const char *const *const *icon,
3863                      int n_icon)
3864 {
3865 #if GTK_CHECK_VERSION(2,0,0)
3866     GList *iconlist;
3867     int n;
3868 #else
3869     GdkPixmap *iconpm;
3870     GdkBitmap *iconmask;
3871 #endif
3872
3873     if (!n_icon)
3874         return;
3875
3876     gtk_widget_realize(window);
3877 #if GTK_CHECK_VERSION(2,0,0)
3878     gtk_window_set_icon(GTK_WINDOW(window),
3879                         gdk_pixbuf_new_from_xpm_data((const gchar **)icon[0]));
3880 #else
3881     iconpm = gdk_pixmap_create_from_xpm_d(gtk_widget_get_window(window),
3882                                           &iconmask, NULL, (gchar **)icon[0]);
3883     gdk_window_set_icon(gtk_widget_get_window(window), NULL, iconpm, iconmask);
3884 #endif
3885
3886 #if GTK_CHECK_VERSION(2,0,0)
3887     iconlist = NULL;
3888     for (n = 0; n < n_icon; n++) {
3889         iconlist =
3890             g_list_append(iconlist,
3891                           gdk_pixbuf_new_from_xpm_data((const gchar **)
3892                                                        icon[n]));
3893     }
3894     gtk_window_set_icon_list(GTK_WINDOW(window), iconlist);
3895 #endif
3896 }
3897
3898 void update_specials_menu(void *frontend)
3899 {
3900     struct gui_data *inst = (struct gui_data *)frontend;
3901
3902     const struct telnet_special *specials;
3903
3904     if (inst->back)
3905         specials = inst->back->get_specials(inst->backhandle);
3906     else
3907         specials = NULL;
3908
3909     /* I believe this disposes of submenus too. */
3910     gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
3911                           (GtkCallback)gtk_widget_destroy, NULL);
3912     if (specials) {
3913         int i;
3914         GtkWidget *menu = inst->specialsmenu;
3915         /* A lame "stack" for submenus that will do for now. */
3916         GtkWidget *saved_menu = NULL;
3917         int nesting = 1;
3918         for (i = 0; nesting > 0; i++) {
3919             GtkWidget *menuitem = NULL;
3920             switch (specials[i].code) {
3921               case TS_SUBMENU:
3922                 assert (nesting < 2);
3923                 saved_menu = menu; /* XXX lame stacking */
3924                 menu = gtk_menu_new();
3925                 menuitem = gtk_menu_item_new_with_label(specials[i].name);
3926                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
3927                 gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);
3928                 gtk_widget_show(menuitem);
3929                 menuitem = NULL;
3930                 nesting++;
3931                 break;
3932               case TS_EXITMENU:
3933                 nesting--;
3934                 if (nesting) {
3935                     menu = saved_menu; /* XXX lame stacking */
3936                     saved_menu = NULL;
3937                 }
3938                 break;
3939               case TS_SEP:
3940                 menuitem = gtk_menu_item_new();
3941                 break;
3942               default:
3943                 menuitem = gtk_menu_item_new_with_label(specials[i].name);
3944                 g_object_set_data(G_OBJECT(menuitem), "user-data",
3945                                   GINT_TO_POINTER(specials[i].code));
3946                 g_signal_connect(G_OBJECT(menuitem), "activate",
3947                                  G_CALLBACK(special_menuitem), inst);
3948                 break;
3949             }
3950             if (menuitem) {
3951                 gtk_container_add(GTK_CONTAINER(menu), menuitem);
3952                 gtk_widget_show(menuitem);
3953             }
3954         }
3955         gtk_widget_show(inst->specialsitem1);
3956         gtk_widget_show(inst->specialsitem2);
3957     } else {
3958         gtk_widget_hide(inst->specialsitem1);
3959         gtk_widget_hide(inst->specialsitem2);
3960     }
3961 }
3962
3963 static void start_backend(struct gui_data *inst)
3964 {
3965     extern Backend *select_backend(Conf *conf);
3966     char *realhost;
3967     const char *error;
3968     char *s;
3969
3970     inst->back = select_backend(inst->conf);
3971
3972     error = inst->back->init((void *)inst, &inst->backhandle,
3973                              inst->conf,
3974                              conf_get_str(inst->conf, CONF_host),
3975                              conf_get_int(inst->conf, CONF_port),
3976                              &realhost,
3977                              conf_get_int(inst->conf, CONF_tcp_nodelay),
3978                              conf_get_int(inst->conf, CONF_tcp_keepalives));
3979
3980     if (error) {
3981         char *msg = dupprintf("Unable to open connection to %s:\n%s",
3982                               conf_get_str(inst->conf, CONF_host), error);
3983         inst->exited = TRUE;
3984         fatal_message_box(inst->window, msg);
3985         sfree(msg);
3986         exit(0);
3987     }
3988
3989     s = conf_get_str(inst->conf, CONF_wintitle);
3990     if (s[0]) {
3991         set_title_and_icon(inst, s, s);
3992     } else {
3993         char *title = make_default_wintitle(realhost);
3994         set_title_and_icon(inst, title, title);
3995         sfree(title);
3996     }
3997     sfree(realhost);
3998
3999     inst->back->provide_logctx(inst->backhandle, inst->logctx);
4000
4001     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
4002
4003     inst->ldisc =
4004         ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle,
4005                      inst);
4006
4007     gtk_widget_set_sensitive(inst->restartitem, FALSE);
4008 }
4009
4010 int pt_main(int argc, char **argv)
4011 {
4012     extern int cfgbox(Conf *conf);
4013     struct gui_data *inst;
4014
4015     setlocale(LC_CTYPE, "");
4016
4017     /*
4018      * Create an instance structure and initialise to zeroes
4019      */
4020     inst = snew(struct gui_data);
4021     memset(inst, 0, sizeof(*inst));
4022     inst->alt_keycode = -1;            /* this one needs _not_ to be zero */
4023     inst->busy_status = BUSY_NOT;
4024     inst->conf = conf_new();
4025     inst->wintitle = inst->icontitle = NULL;
4026     inst->quit_fn_scheduled = FALSE;
4027     inst->idle_fn_scheduled = FALSE;
4028     inst->drawtype = DRAWTYPE_DEFAULT;
4029
4030     /* defer any child exit handling until we're ready to deal with
4031      * it */
4032     block_signal(SIGCHLD, 1);
4033
4034     inst->progname = argv[0];
4035     /*
4036      * Copy the original argv before letting gtk_init fiddle with
4037      * it. It will be required later.
4038      */
4039     {
4040         int i, oldargc;
4041         inst->gtkargvstart = snewn(argc-1, char *);
4042         for (i = 1; i < argc; i++)
4043             inst->gtkargvstart[i-1] = dupstr(argv[i]);
4044         oldargc = argc;
4045         gtk_init(&argc, &argv);
4046         inst->ngtkargs = oldargc - argc;
4047     }
4048
4049     if (argc > 1 && !strncmp(argv[1], "---", 3)) {
4050         read_dupsession_data(inst, inst->conf, argv[1]);
4051         /* Splatter this argument so it doesn't clutter a ps listing */
4052         smemclr(argv[1], strlen(argv[1]));
4053     } else {
4054         /* By default, we bring up the config dialog, rather than launching
4055          * a session. This gets set to TRUE if something happens to change
4056          * that (e.g., a hostname is specified on the command-line). */
4057         int allow_launch = FALSE;
4058         if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf))
4059             exit(1);                   /* pre-defaults pass to get -class */
4060         do_defaults(NULL, inst->conf);
4061         if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf))
4062             exit(1);                   /* post-defaults, do everything */
4063
4064         cmdline_run_saved(inst->conf);
4065
4066         if (loaded_session)
4067             allow_launch = TRUE;
4068
4069         if ((!allow_launch || !conf_launchable(inst->conf)) &&
4070             !cfgbox(inst->conf))
4071             exit(0);                   /* config box hit Cancel */
4072     }
4073
4074     if (!compound_text_atom)
4075         compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
4076     if (!utf8_string_atom)
4077         utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
4078
4079     inst->area = gtk_drawing_area_new();
4080
4081 #if GTK_CHECK_VERSION(2,0,0)
4082     inst->imc = gtk_im_multicontext_new();
4083 #endif
4084
4085     {
4086         char *errmsg = setup_fonts_ucs(inst);
4087         if (errmsg) {
4088             fprintf(stderr, "%s: %s\n", appname, errmsg);
4089             exit(1);
4090         }
4091     }
4092     init_cutbuffers();
4093
4094     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4095     {
4096         const char *winclass = conf_get_str(inst->conf, CONF_winclass);
4097         if (*winclass)
4098             gtk_window_set_wmclass(GTK_WINDOW(inst->window),
4099                                    winclass, winclass);
4100     }
4101
4102     /*
4103      * Set up the colour map.
4104      */
4105     palette_reset(inst);
4106
4107     inst->width = conf_get_int(inst->conf, CONF_width);
4108     inst->height = conf_get_int(inst->conf, CONF_height);
4109     cache_conf_values(inst);
4110
4111     {
4112         int w = inst->font_width * inst->width + 2*inst->window_border;
4113         int h = inst->font_height * inst->height + 2*inst->window_border;
4114 #if GTK_CHECK_VERSION(2,0,0)
4115         gtk_widget_set_size_request(inst->area, w, h);
4116 #else
4117         gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), w, h);
4118 #endif
4119     }
4120     inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));
4121     inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);
4122     inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
4123     /*
4124      * We always create the scrollbar; it remains invisible if
4125      * unwanted, so we can pop it up quickly if it suddenly becomes
4126      * desirable.
4127      */
4128     if (conf_get_int(inst->conf, CONF_scrollbar_on_left))
4129         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
4130     gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);
4131     if (!conf_get_int(inst->conf, CONF_scrollbar_on_left))
4132         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
4133
4134     gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));
4135
4136     set_geom_hints(inst);
4137
4138     gtk_widget_show(inst->area);
4139     if (conf_get_int(inst->conf, CONF_scrollbar))
4140         gtk_widget_show(inst->sbar);
4141     else
4142         gtk_widget_hide(inst->sbar);
4143     gtk_widget_show(GTK_WIDGET(inst->hbox));
4144
4145 #if GTK_CHECK_VERSION(2,0,0)
4146     if (inst->geometry) {
4147         gtk_window_parse_geometry(GTK_WINDOW(inst->window), inst->geometry);
4148     }
4149 #else
4150     if (inst->gotpos) {
4151         int x = inst->xpos, y = inst->ypos;
4152         GtkRequisition req;
4153         gtk_widget_size_request(GTK_WIDGET(inst->window), &req);
4154         if (inst->gravity & 1) x += gdk_screen_width() - req.width;
4155         if (inst->gravity & 2) y += gdk_screen_height() - req.height;
4156         gtk_window_set_position(GTK_WINDOW(inst->window), GTK_WIN_POS_NONE);
4157         gtk_widget_set_uposition(GTK_WIDGET(inst->window), x, y);
4158     }
4159 #endif
4160
4161     g_signal_connect(G_OBJECT(inst->window), "destroy",
4162                      G_CALLBACK(destroy), inst);
4163     g_signal_connect(G_OBJECT(inst->window), "delete_event",
4164                      G_CALLBACK(delete_window), inst);
4165     g_signal_connect(G_OBJECT(inst->window), "key_press_event",
4166                      G_CALLBACK(key_event), inst);
4167     g_signal_connect(G_OBJECT(inst->window), "key_release_event",
4168                      G_CALLBACK(key_event), inst);
4169     g_signal_connect(G_OBJECT(inst->window), "focus_in_event",
4170                      G_CALLBACK(focus_event), inst);
4171     g_signal_connect(G_OBJECT(inst->window), "focus_out_event",
4172                      G_CALLBACK(focus_event), inst);
4173     g_signal_connect(G_OBJECT(inst->area), "configure_event",
4174                      G_CALLBACK(configure_area), inst);
4175     g_signal_connect(G_OBJECT(inst->area), "expose_event",
4176                      G_CALLBACK(expose_area), inst);
4177     g_signal_connect(G_OBJECT(inst->area), "button_press_event",
4178                      G_CALLBACK(button_event), inst);
4179     g_signal_connect(G_OBJECT(inst->area), "button_release_event",
4180                      G_CALLBACK(button_event), inst);
4181 #if GTK_CHECK_VERSION(2,0,0)
4182     g_signal_connect(G_OBJECT(inst->area), "scroll_event",
4183                      G_CALLBACK(scroll_event), inst);
4184 #endif
4185     g_signal_connect(G_OBJECT(inst->area), "motion_notify_event",
4186                      G_CALLBACK(motion_event), inst);
4187     g_signal_connect(G_OBJECT(inst->area), "selection_received",
4188                      G_CALLBACK(selection_received), inst);
4189     g_signal_connect(G_OBJECT(inst->area), "selection_get",
4190                      G_CALLBACK(selection_get), inst);
4191     g_signal_connect(G_OBJECT(inst->area), "selection_clear_event",
4192                      G_CALLBACK(selection_clear), inst);
4193 #if GTK_CHECK_VERSION(2,0,0)
4194     g_signal_connect(G_OBJECT(inst->imc), "commit",
4195                      G_CALLBACK(input_method_commit_event), inst);
4196 #endif
4197     if (conf_get_int(inst->conf, CONF_scrollbar))
4198         g_signal_connect(G_OBJECT(inst->sbar_adjust), "value_changed",
4199                          G_CALLBACK(scrollbar_moved), inst);
4200     gtk_widget_add_events(GTK_WIDGET(inst->area),
4201                           GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
4202                           GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4203                           GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK);
4204
4205     {
4206         extern const char *const *const main_icon[];
4207         extern const int n_main_icon;
4208         set_window_icon(inst->window, main_icon, n_main_icon);
4209     }
4210
4211     gtk_widget_show(inst->window);
4212
4213     set_window_background(inst);
4214
4215     /*
4216      * Set up the Ctrl+rightclick context menu.
4217      */
4218     {
4219         GtkWidget *menuitem;
4220         char *s;
4221         extern const int use_event_log, new_session, saved_sessions;
4222
4223         inst->menu = gtk_menu_new();
4224
4225 #define MKMENUITEM(title, func) do                                      \
4226         {                                                               \
4227             menuitem = gtk_menu_item_new_with_label(title);             \
4228             gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \
4229             gtk_widget_show(menuitem);                                  \
4230             g_signal_connect(G_OBJECT(menuitem), "activate",            \
4231                              G_CALLBACK(func), inst);                   \
4232         } while (0)
4233
4234 #define MKSUBMENU(title) do                                             \
4235         {                                                               \
4236             menuitem = gtk_menu_item_new_with_label(title);             \
4237             gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \
4238             gtk_widget_show(menuitem);                                  \
4239         } while (0)
4240
4241 #define MKSEP() do                                                      \
4242         {                                                               \
4243             menuitem = gtk_menu_item_new();                             \
4244             gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \
4245             gtk_widget_show(menuitem);                                  \
4246         } while (0)
4247
4248         if (new_session)
4249             MKMENUITEM("New Session...", new_session_menuitem);
4250         MKMENUITEM("Restart Session", restart_session_menuitem);
4251         inst->restartitem = menuitem;
4252         gtk_widget_set_sensitive(inst->restartitem, FALSE);
4253         MKMENUITEM("Duplicate Session", dup_session_menuitem);
4254         if (saved_sessions) {
4255             inst->sessionsmenu = gtk_menu_new();
4256             /* sessionsmenu will be updated when it's invoked */
4257             /* XXX is this the right way to do dynamic menus in Gtk? */
4258             MKMENUITEM("Saved Sessions", update_savedsess_menu);
4259             gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),
4260                                       inst->sessionsmenu);
4261         }
4262         MKSEP();
4263         MKMENUITEM("Change Settings...", change_settings_menuitem);
4264         MKSEP();
4265         if (use_event_log)
4266             MKMENUITEM("Event Log", event_log_menuitem);
4267         MKSUBMENU("Special Commands");
4268         inst->specialsmenu = gtk_menu_new();
4269         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->specialsmenu);
4270         inst->specialsitem1 = menuitem;
4271         MKSEP();
4272         inst->specialsitem2 = menuitem;
4273         gtk_widget_hide(inst->specialsitem1);
4274         gtk_widget_hide(inst->specialsitem2);
4275         MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);
4276         MKMENUITEM("Reset Terminal", reset_terminal_menuitem);
4277         MKMENUITEM("Copy All", copy_all_menuitem);
4278         MKSEP();
4279         s = dupcat("About ", appname, NULL);
4280         MKMENUITEM(s, about_menuitem);
4281         sfree(s);
4282 #undef MKMENUITEM
4283 #undef MKSUBMENU
4284 #undef MKSEP
4285     }
4286
4287     inst->textcursor = make_mouse_ptr(inst, GDK_XTERM);
4288     inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR);
4289     inst->waitcursor = make_mouse_ptr(inst, GDK_WATCH);
4290     inst->blankcursor = make_mouse_ptr(inst, -1);
4291     inst->currcursor = inst->textcursor;
4292     show_mouseptr(inst, 1);
4293
4294     inst->eventlogstuff = eventlogstuff_new();
4295
4296     request_callback_notifications(notify_toplevel_callback, inst);
4297
4298     inst->term = term_init(inst->conf, &inst->ucsdata, inst);
4299     inst->logctx = log_init(inst, inst->conf);
4300     term_provide_logctx(inst->term, inst->logctx);
4301
4302     uxsel_init();
4303
4304     term_size(inst->term, inst->height, inst->width,
4305               conf_get_int(inst->conf, CONF_savelines));
4306
4307     start_backend(inst);
4308
4309     ldisc_echoedit_update(inst->ldisc);     /* cause ldisc to notice changes */
4310
4311     /* now we're reday to deal with the child exit handler being
4312      * called */
4313     block_signal(SIGCHLD, 0);
4314
4315     /*
4316      * Block SIGPIPE: if we attempt Duplicate Session or similar
4317      * and it falls over in some way, we certainly don't want
4318      * SIGPIPE terminating the main pterm/PuTTY. Note that we do
4319      * this _after_ (at least pterm) forks off its child process,
4320      * since the child wants SIGPIPE handled in the usual way.
4321      */
4322     block_signal(SIGPIPE, 1);
4323
4324     inst->exited = FALSE;
4325
4326     gtk_main();
4327
4328     return 0;
4329 }