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