]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pterm.c
3bb9f6badd1a1ea44baea13bedaf28a963c9d59f
[PuTTY.git] / unix / pterm.c
1 /*
2  * pterm - a fusion of the PuTTY terminal emulator with a Unix pty
3  * back end, all running as a GTK application. Wish me luck.
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 <fcntl.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <gtk/gtk.h>
21 #include <gdk/gdkkeysyms.h>
22 #include <gdk/gdkx.h>
23 #include <X11/Xlib.h>
24 #include <X11/Xutil.h>
25
26 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
27
28 #include "putty.h"
29 #include "terminal.h"
30
31 #define CAT2(x,y) x ## y
32 #define CAT(x,y) CAT2(x,y)
33 #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
34
35 #define NCOLOURS (lenof(((Config *)0)->colours))
36
37 GdkAtom compound_text_atom, utf8_string_atom;
38
39 extern char **pty_argv;        /* declared in pty.c */
40 extern int use_pty_argv;
41
42 struct gui_data {
43     GtkWidget *window, *area, *sbar;
44     GtkBox *hbox;
45     GtkAdjustment *sbar_adjust;
46     GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2;
47     GtkWidget *sessionsmenu;
48     GdkPixmap *pixmap;
49     GdkFont *fonts[4];                 /* normal, bold, wide, widebold */
50     struct {
51         int charset;
52         int is_wide;
53     } fontinfo[4];
54     int xpos, ypos, gotpos, gravity;
55     GdkCursor *rawcursor, *textcursor, *blankcursor, *currcursor;
56     GdkColor cols[NCOLOURS];
57     GdkColormap *colmap;
58     wchar_t *pastein_data;
59     int direct_to_font;
60     int pastein_data_len;
61     char *pasteout_data, *pasteout_data_utf8;
62     int pasteout_data_len, pasteout_data_utf8_len;
63     int font_width, font_height;
64     int width, height;
65     int ignore_sbar;
66     int mouseptr_visible;
67     guint term_paste_idle_id;
68     int alt_keycode;
69     int alt_digits;
70     char wintitle[sizeof(((Config *)0)->wintitle)];
71     char icontitle[sizeof(((Config *)0)->wintitle)];
72     int master_fd, master_func_id;
73     void *ldisc;
74     Backend *back;
75     void *backhandle;
76     Terminal *term;
77     void *logctx;
78     int exited;
79     struct unicode_data ucsdata;
80     Config cfg;
81     void *eventlogstuff;
82     char *progname, **gtkargvstart;
83     int ngtkargs;
84 };
85
86 struct draw_ctx {
87     GdkGC *gc;
88     struct gui_data *inst;
89 };
90
91 static int send_raw_mouse;
92
93 static char *app_name = "pterm";
94
95 char *x_get_default(const char *key)
96 {
97     return XGetDefault(GDK_DISPLAY(), app_name, key);
98 }
99
100 void connection_fatal(void *frontend, char *p, ...)
101 {
102     struct gui_data *inst = (struct gui_data *)frontend;
103
104     va_list ap;
105     char *msg;
106     va_start(ap, p);
107     msg = dupvprintf(p, ap);
108     va_end(ap);
109     inst->exited = TRUE;
110     fatal_message_box(inst->window, msg);
111     sfree(msg);
112     if (inst->cfg.close_on_exit == FORCE_ON)
113         cleanup_exit(1);
114 }
115
116 /*
117  * Default settings that are specific to pterm.
118  */
119 FontSpec platform_default_fontspec(const char *name)
120 {
121     FontSpec ret;
122     if (!strcmp(name, "Font"))
123         strcpy(ret.name, "fixed");
124     else
125         *ret.name = '\0';
126     return ret;
127 }
128
129 Filename platform_default_filename(const char *name)
130 {
131     Filename ret;
132     if (!strcmp(name, "LogFileName"))
133         strcpy(ret.path, "putty.log");
134     else
135         *ret.path = '\0';
136     return ret;
137 }
138
139 char *platform_default_s(const char *name)
140 {
141     return NULL;
142 }
143
144 int platform_default_i(const char *name, int def)
145 {
146     if (!strcmp(name, "CloseOnExit"))
147         return 2;  /* maps to FORCE_ON after painful rearrangement :-( */
148     return def;
149 }
150
151 void ldisc_update(void *frontend, int echo, int edit)
152 {
153     /*
154      * This is a stub in pterm. If I ever produce a Unix
155      * command-line ssh/telnet/rlogin client (i.e. a port of plink)
156      * then it will require some termios manoeuvring analogous to
157      * that in the Windows plink.c, but here it's meaningless.
158      */
159 }
160
161 int from_backend(void *frontend, int is_stderr, const char *data, int len)
162 {
163     struct gui_data *inst = (struct gui_data *)frontend;
164     return term_data(inst->term, is_stderr, data, len);
165 }
166
167 void logevent(void *frontend, char *string)
168 {
169     struct gui_data *inst = (struct gui_data *)frontend;
170
171     log_eventlog(inst->logctx, string);
172
173     logevent_dlg(inst->eventlogstuff, string);
174 }
175
176 int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
177 {
178     struct gui_data *inst = (struct gui_data *)frontend;
179
180     if (which)
181         return inst->font_height;
182     else
183         return inst->font_width;
184 }
185
186 /*
187  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
188  * into a cooked one (SELECT, EXTEND, PASTE).
189  * 
190  * In Unix, this is not configurable; the X button arrangement is
191  * rock-solid across all applications, everyone has a three-button
192  * mouse or a means of faking it, and there is no need to switch
193  * buttons around at all.
194  */
195 static Mouse_Button translate_button(Mouse_Button button)
196 {
197     /* struct gui_data *inst = (struct gui_data *)frontend; */
198
199     if (button == MBT_LEFT)
200         return MBT_SELECT;
201     if (button == MBT_MIDDLE)
202         return MBT_PASTE;
203     if (button == MBT_RIGHT)
204         return MBT_EXTEND;
205     return 0;                          /* shouldn't happen */
206 }
207
208 /*
209  * Return the top-level GtkWindow associated with a particular
210  * front end instance.
211  */
212 void *get_window(void *frontend)
213 {
214     struct gui_data *inst = (struct gui_data *)frontend;
215     return inst->window;
216 }
217
218 /*
219  * Minimise or restore the window in response to a server-side
220  * request.
221  */
222 void set_iconic(void *frontend, int iconic)
223 {
224     /*
225      * GTK 1.2 doesn't know how to do this.
226      */
227 #if GTK_CHECK_VERSION(2,0,0)
228     struct gui_data *inst = (struct gui_data *)frontend;
229     if (iconic)
230         gtk_window_iconify(GTK_WINDOW(inst->window));
231     else
232         gtk_window_deiconify(GTK_WINDOW(inst->window));
233 #endif
234 }
235
236 /*
237  * Move the window in response to a server-side request.
238  */
239 void move_window(void *frontend, int x, int y)
240 {
241     struct gui_data *inst = (struct gui_data *)frontend;
242     /*
243      * I assume that when the GTK version of this call is available
244      * we should use it. Not sure how it differs from the GDK one,
245      * though.
246      */
247 #if GTK_CHECK_VERSION(2,0,0)
248     gtk_window_move(GTK_WINDOW(inst->window), x, y);
249 #else
250     gdk_window_move(inst->window->window, x, y);
251 #endif
252 }
253
254 /*
255  * Move the window to the top or bottom of the z-order in response
256  * to a server-side request.
257  */
258 void set_zorder(void *frontend, int top)
259 {
260     struct gui_data *inst = (struct gui_data *)frontend;
261     if (top)
262         gdk_window_raise(inst->window->window);
263     else
264         gdk_window_lower(inst->window->window);
265 }
266
267 /*
268  * Refresh the window in response to a server-side request.
269  */
270 void refresh_window(void *frontend)
271 {
272     struct gui_data *inst = (struct gui_data *)frontend;
273     term_invalidate(inst->term);
274 }
275
276 /*
277  * Maximise or restore the window in response to a server-side
278  * request.
279  */
280 void set_zoomed(void *frontend, int zoomed)
281 {
282     /*
283      * GTK 1.2 doesn't know how to do this.
284      */
285 #if GTK_CHECK_VERSION(2,0,0)
286     struct gui_data *inst = (struct gui_data *)frontend;
287     if (iconic)
288         gtk_window_maximize(GTK_WINDOW(inst->window));
289     else
290         gtk_window_unmaximize(GTK_WINDOW(inst->window));
291 #endif
292 }
293
294 /*
295  * Report whether the window is iconic, for terminal reports.
296  */
297 int is_iconic(void *frontend)
298 {
299     struct gui_data *inst = (struct gui_data *)frontend;
300     return !gdk_window_is_viewable(inst->window->window);
301 }
302
303 /*
304  * Report the window's position, for terminal reports.
305  */
306 void get_window_pos(void *frontend, int *x, int *y)
307 {
308     struct gui_data *inst = (struct gui_data *)frontend;
309     /*
310      * I assume that when the GTK version of this call is available
311      * we should use it. Not sure how it differs from the GDK one,
312      * though.
313      */
314 #if GTK_CHECK_VERSION(2,0,0)
315     gtk_window_get_position(GTK_WINDOW(inst->window), x, y);
316 #else
317     gdk_window_get_position(inst->window->window, x, y);
318 #endif
319 }
320
321 /*
322  * Report the window's pixel size, for terminal reports.
323  */
324 void get_window_pixels(void *frontend, int *x, int *y)
325 {
326     struct gui_data *inst = (struct gui_data *)frontend;
327     /*
328      * I assume that when the GTK version of this call is available
329      * we should use it. Not sure how it differs from the GDK one,
330      * though.
331      */
332 #if GTK_CHECK_VERSION(2,0,0)
333     gtk_window_get_size(GTK_WINDOW(inst->window), x, y);
334 #else
335     gdk_window_get_size(inst->window->window, x, y);
336 #endif
337 }
338
339 /*
340  * Return the window or icon title.
341  */
342 char *get_window_title(void *frontend, int icon)
343 {
344     struct gui_data *inst = (struct gui_data *)frontend;
345     return icon ? inst->wintitle : inst->icontitle;
346 }
347
348 gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
349 {
350     struct gui_data *inst = (struct gui_data *)data;
351     if (!inst->exited && inst->cfg.warn_on_close) {
352         if (!reallyclose(inst))
353             return TRUE;
354     }
355     return FALSE;
356 }
357
358 static void show_mouseptr(struct gui_data *inst, int show)
359 {
360     if (!inst->cfg.hide_mouseptr)
361         show = 1;
362     if (show)
363         gdk_window_set_cursor(inst->area->window, inst->currcursor);
364     else
365         gdk_window_set_cursor(inst->area->window, inst->blankcursor);
366     inst->mouseptr_visible = show;
367 }
368
369 gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
370 {
371     struct gui_data *inst = (struct gui_data *)data;
372     int w, h, need_size = 0;
373     GdkGC *gc;
374
375     /*
376      * See if the terminal size has changed, in which case we must
377      * let the terminal know.
378      */
379     w = (event->width - 2*inst->cfg.window_border) / inst->font_width;
380     h = (event->height - 2*inst->cfg.window_border) / inst->font_height;
381     if (w != inst->width || h != inst->height) {
382         inst->cfg.width = inst->width = w;
383         inst->cfg.height = inst->height = h;
384         need_size = 1;
385     }
386
387     if (inst->pixmap) {
388         gdk_pixmap_unref(inst->pixmap);
389         inst->pixmap = NULL;
390     }
391
392     inst->pixmap = gdk_pixmap_new(widget->window,
393                                   (inst->cfg.width * inst->font_width +
394                                    2*inst->cfg.window_border),
395                                   (inst->cfg.height * inst->font_height +
396                                    2*inst->cfg.window_border), -1);
397
398     gc = gdk_gc_new(inst->area->window);
399     gdk_gc_set_foreground(gc, &inst->cols[18]);   /* default background */
400     gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
401                        inst->cfg.width * inst->font_width + 2*inst->cfg.window_border,
402                        inst->cfg.height * inst->font_height + 2*inst->cfg.window_border);
403     gdk_gc_unref(gc);
404
405     if (need_size && inst->term) {
406         term_size(inst->term, h, w, inst->cfg.savelines);
407     }
408
409     if (inst->term)
410         term_invalidate(inst->term);
411
412     return TRUE;
413 }
414
415 gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
416 {
417     struct gui_data *inst = (struct gui_data *)data;
418
419     /*
420      * Pass the exposed rectangle to terminal.c, which will call us
421      * back to do the actual painting.
422      */
423     if (inst->pixmap) {
424         gdk_draw_pixmap(widget->window,
425                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
426                         inst->pixmap,
427                         event->area.x, event->area.y,
428                         event->area.x, event->area.y,
429                         event->area.width, event->area.height);
430     }
431     return TRUE;
432 }
433
434 #define KEY_PRESSED(k) \
435     (inst->keystate[(k) / 32] & (1 << ((k) % 32)))
436
437 gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
438 {
439     struct gui_data *inst = (struct gui_data *)data;
440     char output[32];
441     int start, end, special;
442
443     /* By default, nothing is generated. */
444     end = start = 0;
445     special = FALSE;
446
447     /*
448      * If Alt is being released after typing an Alt+numberpad
449      * sequence, we should generate the code that was typed.
450      * 
451      * Note that we only do this if more than one key was actually
452      * pressed - I don't think Alt+NumPad4 should be ^D or that
453      * Alt+NumPad3 should be ^C, for example. There's no serious
454      * inconvenience in having to type a zero before a single-digit
455      * character code.
456      */
457     if (event->type == GDK_KEY_RELEASE &&
458         (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
459          event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) &&
460         inst->alt_keycode >= 0 && inst->alt_digits > 1) {
461 #ifdef KEY_DEBUGGING
462         printf("Alt key up, keycode = %d\n", inst->alt_keycode);
463 #endif
464         output[0] = inst->alt_keycode;
465         end = 1;
466         goto done;
467     }
468
469     if (event->type == GDK_KEY_PRESS) {
470 #ifdef KEY_DEBUGGING
471         {
472             int i;
473             printf("keypress: keyval = %04x, state = %08x; string =",
474                    event->keyval, event->state);
475             for (i = 0; event->string[i]; i++)
476                 printf(" %02x", (unsigned char) event->string[i]);
477             printf("\n");
478         }
479 #endif
480
481         /*
482          * NYI: Compose key (!!! requires Unicode faff before even trying)
483          */
484
485         /*
486          * If Alt has just been pressed, we start potentially
487          * accumulating an Alt+numberpad code. We do this by
488          * setting alt_keycode to -1 (nothing yet but plausible).
489          */
490         if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
491              event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R)) {
492             inst->alt_keycode = -1;
493             inst->alt_digits = 0;
494             goto done;                 /* this generates nothing else */
495         }
496
497         /*
498          * If we're seeing a numberpad key press with Mod1 down,
499          * consider adding it to alt_keycode if that's sensible.
500          * Anything _else_ with Mod1 down cancels any possibility
501          * of an ALT keycode: we set alt_keycode to -2.
502          */
503         if ((event->state & GDK_MOD1_MASK) && inst->alt_keycode != -2) {
504             int digit = -1;
505             switch (event->keyval) {
506               case GDK_KP_0: case GDK_KP_Insert: digit = 0; break;
507               case GDK_KP_1: case GDK_KP_End: digit = 1; break;
508               case GDK_KP_2: case GDK_KP_Down: digit = 2; break;
509               case GDK_KP_3: case GDK_KP_Page_Down: digit = 3; break;
510               case GDK_KP_4: case GDK_KP_Left: digit = 4; break;
511               case GDK_KP_5: case GDK_KP_Begin: digit = 5; break;
512               case GDK_KP_6: case GDK_KP_Right: digit = 6; break;
513               case GDK_KP_7: case GDK_KP_Home: digit = 7; break;
514               case GDK_KP_8: case GDK_KP_Up: digit = 8; break;
515               case GDK_KP_9: case GDK_KP_Page_Up: digit = 9; break;
516             }
517             if (digit < 0)
518                 inst->alt_keycode = -2;   /* it's invalid */
519             else {
520 #ifdef KEY_DEBUGGING
521                 printf("Adding digit %d to keycode %d", digit,
522                        inst->alt_keycode);
523 #endif
524                 if (inst->alt_keycode == -1)
525                     inst->alt_keycode = digit;   /* one-digit code */
526                 else
527                     inst->alt_keycode = inst->alt_keycode * 10 + digit;
528                 inst->alt_digits++;
529 #ifdef KEY_DEBUGGING
530                 printf(" gives new code %d\n", inst->alt_keycode);
531 #endif
532                 /* Having used this digit, we now do nothing more with it. */
533                 goto done;
534             }
535         }
536
537         /*
538          * Shift-PgUp and Shift-PgDn don't even generate keystrokes
539          * at all.
540          */
541         if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {
542             term_scroll(inst->term, 0, -inst->cfg.height/2);
543             return TRUE;
544         }
545         if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) {
546             term_scroll(inst->term, 0, -1);
547             return TRUE;
548         }
549         if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {
550             term_scroll(inst->term, 0, +inst->cfg.height/2);
551             return TRUE;
552         }
553         if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) {
554             term_scroll(inst->term, 0, +1);
555             return TRUE;
556         }
557
558         /*
559          * Neither does Shift-Ins.
560          */
561         if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) {
562             request_paste(inst);
563             return TRUE;
564         }
565
566         special = FALSE;
567
568         /* ALT+things gives leading Escape. */
569         output[0] = '\033';
570         strncpy(output+1, event->string, 31);
571         output[31] = '\0';
572         end = strlen(output);
573         if (event->state & GDK_MOD1_MASK) {
574             start = 0;
575             if (end == 1) end = 0;
576         } else
577             start = 1;
578
579         /* Control-` is the same as Control-\ (unless gtk has a better idea) */
580         if (!event->string[0] && event->keyval == '`' &&
581             (event->state & GDK_CONTROL_MASK)) {
582             output[1] = '\x1C';
583             end = 2;
584         }
585
586         /* Control-Break is the same as Control-C */
587         if (event->keyval == GDK_Break &&
588             (event->state & GDK_CONTROL_MASK)) {
589             output[1] = '\003';
590             end = 2;
591             special = TRUE;
592         }
593
594         /* We handle Return ourselves, because it needs to be flagged as
595          * special to ldisc. */
596         if (event->keyval == GDK_Return) {
597             output[1] = '\015';
598             end = 2;
599             special = TRUE;
600         }
601
602         /* Control-2, Control-Space and Control-@ are NUL */
603         if (!event->string[0] &&
604             (event->keyval == ' ' || event->keyval == '2' ||
605              event->keyval == '@') &&
606             (event->state & (GDK_SHIFT_MASK |
607                              GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {
608             output[1] = '\0';
609             end = 2;
610         }
611
612         /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
613         if (!event->string[0] && event->keyval == ' ' &&
614             (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==
615             (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
616             output[1] = '\240';
617             end = 2;
618         }
619
620         /* We don't let GTK tell us what Backspace is! We know better. */
621         if (event->keyval == GDK_BackSpace &&
622             !(event->state & GDK_SHIFT_MASK)) {
623             output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08';
624             end = 2;
625             special = TRUE;
626         }
627         /* For Shift Backspace, do opposite of what is configured. */
628         if (event->keyval == GDK_BackSpace &&
629             (event->state & GDK_SHIFT_MASK)) {
630             output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F';
631             end = 2;
632             special = TRUE;
633         }
634
635         /* Shift-Tab is ESC [ Z */
636         if (event->keyval == GDK_ISO_Left_Tab ||
637             (event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {
638             end = 1 + sprintf(output+1, "\033[Z");
639         }
640
641         /*
642          * NetHack keypad mode.
643          */
644         if (inst->cfg.nethack_keypad) {
645             char *keys = NULL;
646             switch (event->keyval) {
647               case GDK_KP_1: case GDK_KP_End: keys = "bB"; break;
648               case GDK_KP_2: case GDK_KP_Down: keys = "jJ"; break;
649               case GDK_KP_3: case GDK_KP_Page_Down: keys = "nN"; break;
650               case GDK_KP_4: case GDK_KP_Left: keys = "hH"; break;
651               case GDK_KP_5: case GDK_KP_Begin: keys = ".."; break;
652               case GDK_KP_6: case GDK_KP_Right: keys = "lL"; break;
653               case GDK_KP_7: case GDK_KP_Home: keys = "yY"; break;
654               case GDK_KP_8: case GDK_KP_Up: keys = "kK"; break;
655               case GDK_KP_9: case GDK_KP_Page_Up: keys = "uU"; break;
656             }
657             if (keys) {
658                 end = 2;
659                 if (event->state & GDK_SHIFT_MASK)
660                     output[1] = keys[1];
661                 else
662                     output[1] = keys[0];
663                 goto done;
664             }
665         }
666
667         /*
668          * Application keypad mode.
669          */
670         if (inst->term->app_keypad_keys && !inst->cfg.no_applic_k) {
671             int xkey = 0;
672             switch (event->keyval) {
673               case GDK_Num_Lock: xkey = 'P'; break;
674               case GDK_KP_Divide: xkey = 'Q'; break;
675               case GDK_KP_Multiply: xkey = 'R'; break;
676               case GDK_KP_Subtract: xkey = 'S'; break;
677                 /*
678                  * Keypad + is tricky. It covers a space that would
679                  * be taken up on the VT100 by _two_ keys; so we
680                  * let Shift select between the two. Worse still,
681                  * in xterm function key mode we change which two...
682                  */
683               case GDK_KP_Add:
684                 if (inst->cfg.funky_type == 2) {
685                     if (event->state & GDK_SHIFT_MASK)
686                         xkey = 'l';
687                     else
688                         xkey = 'k';
689                 } else if (event->state & GDK_SHIFT_MASK)
690                         xkey = 'm';
691                 else
692                     xkey = 'l';
693                 break;
694               case GDK_KP_Enter: xkey = 'M'; break;
695               case GDK_KP_0: case GDK_KP_Insert: xkey = 'p'; break;
696               case GDK_KP_1: case GDK_KP_End: xkey = 'q'; break;
697               case GDK_KP_2: case GDK_KP_Down: xkey = 'r'; break;
698               case GDK_KP_3: case GDK_KP_Page_Down: xkey = 's'; break;
699               case GDK_KP_4: case GDK_KP_Left: xkey = 't'; break;
700               case GDK_KP_5: case GDK_KP_Begin: xkey = 'u'; break;
701               case GDK_KP_6: case GDK_KP_Right: xkey = 'v'; break;
702               case GDK_KP_7: case GDK_KP_Home: xkey = 'w'; break;
703               case GDK_KP_8: case GDK_KP_Up: xkey = 'x'; break;
704               case GDK_KP_9: case GDK_KP_Page_Up: xkey = 'y'; break;
705               case GDK_KP_Decimal: case GDK_KP_Delete: xkey = 'n'; break;
706             }
707             if (xkey) {
708                 if (inst->term->vt52_mode) {
709                     if (xkey >= 'P' && xkey <= 'S')
710                         end = 1 + sprintf(output+1, "\033%c", xkey);
711                     else
712                         end = 1 + sprintf(output+1, "\033?%c", xkey);
713                 } else
714                     end = 1 + sprintf(output+1, "\033O%c", xkey);
715                 goto done;
716             }
717         }
718
719         /*
720          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
721          * for integer decimal nn.)
722          *
723          * We also deal with the weird ones here. Linux VCs replace F1
724          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
725          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
726          * respectively.
727          */
728         {
729             int code = 0;
730             switch (event->keyval) {
731               case GDK_F1:
732                 code = (event->state & GDK_SHIFT_MASK ? 23 : 11);
733                 break;
734               case GDK_F2:
735                 code = (event->state & GDK_SHIFT_MASK ? 24 : 12);
736                 break;
737               case GDK_F3:
738                 code = (event->state & GDK_SHIFT_MASK ? 25 : 13);
739                 break;
740               case GDK_F4:
741                 code = (event->state & GDK_SHIFT_MASK ? 26 : 14);
742                 break;
743               case GDK_F5:
744                 code = (event->state & GDK_SHIFT_MASK ? 28 : 15);
745                 break;
746               case GDK_F6:
747                 code = (event->state & GDK_SHIFT_MASK ? 29 : 17);
748                 break;
749               case GDK_F7:
750                 code = (event->state & GDK_SHIFT_MASK ? 31 : 18);
751                 break;
752               case GDK_F8:
753                 code = (event->state & GDK_SHIFT_MASK ? 32 : 19);
754                 break;
755               case GDK_F9:
756                 code = (event->state & GDK_SHIFT_MASK ? 33 : 20);
757                 break;
758               case GDK_F10:
759                 code = (event->state & GDK_SHIFT_MASK ? 34 : 21);
760                 break;
761               case GDK_F11:
762                 code = 23;
763                 break;
764               case GDK_F12:
765                 code = 24;
766                 break;
767               case GDK_F13:
768                 code = 25;
769                 break;
770               case GDK_F14:
771                 code = 26;
772                 break;
773               case GDK_F15:
774                 code = 28;
775                 break;
776               case GDK_F16:
777                 code = 29;
778                 break;
779               case GDK_F17:
780                 code = 31;
781                 break;
782               case GDK_F18:
783                 code = 32;
784                 break;
785               case GDK_F19:
786                 code = 33;
787                 break;
788               case GDK_F20:
789                 code = 34;
790                 break;
791             }
792             if (!(event->state & GDK_CONTROL_MASK)) switch (event->keyval) {
793               case GDK_Home: case GDK_KP_Home:
794                 code = 1;
795                 break;
796               case GDK_Insert: case GDK_KP_Insert:
797                 code = 2;
798                 break;
799               case GDK_Delete: case GDK_KP_Delete:
800                 code = 3;
801                 break;
802               case GDK_End: case GDK_KP_End:
803                 code = 4;
804                 break;
805               case GDK_Page_Up: case GDK_KP_Page_Up:
806                 code = 5;
807                 break;
808               case GDK_Page_Down: case GDK_KP_Page_Down:
809                 code = 6;
810                 break;
811             }
812             /* Reorder edit keys to physical order */
813             if (inst->cfg.funky_type == 3 && code <= 6)
814                 code = "\0\2\1\4\5\3\6"[code];
815
816             if (inst->term->vt52_mode && code > 0 && code <= 6) {
817                 end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);
818                 goto done;
819             }
820
821             if (inst->cfg.funky_type == 5 &&     /* SCO function keys */
822                 code >= 11 && code <= 34) {
823                 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
824                 int index = 0;
825                 switch (event->keyval) {
826                   case GDK_F1: index = 0; break;
827                   case GDK_F2: index = 1; break;
828                   case GDK_F3: index = 2; break;
829                   case GDK_F4: index = 3; break;
830                   case GDK_F5: index = 4; break;
831                   case GDK_F6: index = 5; break;
832                   case GDK_F7: index = 6; break;
833                   case GDK_F8: index = 7; break;
834                   case GDK_F9: index = 8; break;
835                   case GDK_F10: index = 9; break;
836                   case GDK_F11: index = 10; break;
837                   case GDK_F12: index = 11; break;
838                 }
839                 if (event->state & GDK_SHIFT_MASK) index += 12;
840                 if (event->state & GDK_CONTROL_MASK) index += 24;
841                 end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);
842                 goto done;
843             }
844             if (inst->cfg.funky_type == 5 &&     /* SCO small keypad */
845                 code >= 1 && code <= 6) {
846                 char codes[] = "HL.FIG";
847                 if (code == 3) {
848                     output[1] = '\x7F';
849                     end = 2;
850                 } else {
851                     end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);
852                 }
853                 goto done;
854             }
855             if ((inst->term->vt52_mode || inst->cfg.funky_type == 4) &&
856                 code >= 11 && code <= 24) {
857                 int offt = 0;
858                 if (code > 15)
859                     offt++;
860                 if (code > 21)
861                     offt++;
862                 if (inst->term->vt52_mode)
863                     end = 1 + sprintf(output+1,
864                                       "\x1B%c", code + 'P' - 11 - offt);
865                 else
866                     end = 1 + sprintf(output+1,
867                                       "\x1BO%c", code + 'P' - 11 - offt);
868                 goto done;
869             }
870             if (inst->cfg.funky_type == 1 && code >= 11 && code <= 15) {
871                 end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
872                 goto done;
873             }
874             if (inst->cfg.funky_type == 2 && code >= 11 && code <= 14) {
875                 if (inst->term->vt52_mode)
876                     end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
877                 else
878                     end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);
879                 goto done;
880             }
881             if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) {
882                 end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
883                 goto done;
884             }
885             if (code) {
886                 end = 1 + sprintf(output+1, "\x1B[%d~", code);
887                 goto done;
888             }
889         }
890
891         /*
892          * Cursor keys. (This includes the numberpad cursor keys,
893          * if we haven't already done them due to app keypad mode.)
894          * 
895          * Here we also process un-numlocked un-appkeypadded KP5,
896          * which sends ESC [ G.
897          */
898         {
899             int xkey = 0;
900             switch (event->keyval) {
901               case GDK_Up: case GDK_KP_Up: xkey = 'A'; break;
902               case GDK_Down: case GDK_KP_Down: xkey = 'B'; break;
903               case GDK_Right: case GDK_KP_Right: xkey = 'C'; break;
904               case GDK_Left: case GDK_KP_Left: xkey = 'D'; break;
905               case GDK_Begin: case GDK_KP_Begin: xkey = 'G'; break;
906             }
907             if (xkey) {
908                 /*
909                  * The arrow keys normally do ESC [ A and so on. In
910                  * app cursor keys mode they do ESC O A instead.
911                  * Ctrl toggles the two modes.
912                  */
913                 if (inst->term->vt52_mode) {
914                     end = 1 + sprintf(output+1, "\033%c", xkey);
915                 } else if (!inst->term->app_cursor_keys ^
916                            !(event->state & GDK_CONTROL_MASK)) {
917                     end = 1 + sprintf(output+1, "\033O%c", xkey);
918                 } else {                    
919                     end = 1 + sprintf(output+1, "\033[%c", xkey);
920                 }
921                 goto done;
922             }
923         }
924         goto done;
925     }
926
927     done:
928
929     if (end-start > 0) {
930 #ifdef KEY_DEBUGGING
931         int i;
932         printf("generating sequence:");
933         for (i = start; i < end; i++)
934             printf(" %02x", (unsigned char) output[i]);
935         printf("\n");
936 #endif
937
938         if (special) {
939             /*
940              * For special control characters, the character set
941              * should never matter.
942              */
943             output[end] = '\0';        /* NUL-terminate */
944             ldisc_send(inst->ldisc, output+start, -2, 1);
945         } else if (!inst->direct_to_font) {
946             /*
947              * The stuff we've just generated is assumed to be
948              * ISO-8859-1! This sounds insane, but `man
949              * XLookupString' agrees: strings of this type returned
950              * from the X server are hardcoded to 8859-1. Strictly
951              * speaking we should be doing this using some sort of
952              * GtkIMContext, which (if we're lucky) would give us
953              * our data directly in Unicode; but that's not
954              * supported in GTK 1.2 as far as I can tell, and it's
955              * poorly documented even in 2.0, so it'll have to
956              * wait.
957              */
958             lpage_send(inst->ldisc, CS_ISO8859_1, output+start, end-start, 1);
959         } else {
960             /*
961              * In direct-to-font mode, we just send the string
962              * exactly as we received it.
963              */
964             ldisc_send(inst->ldisc, output+start, end-start, 1);
965         }
966
967         show_mouseptr(inst, 0);
968         term_seen_key_event(inst->term);
969         term_out(inst->term);
970     }
971
972     return TRUE;
973 }
974
975 gint button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
976 {
977     struct gui_data *inst = (struct gui_data *)data;
978     int shift, ctrl, alt, x, y, button, act;
979
980     show_mouseptr(inst, 1);
981
982     if (event->button == 4 && event->type == GDK_BUTTON_PRESS) {
983         term_scroll(inst->term, 0, -5);
984         return TRUE;
985     }
986     if (event->button == 5 && event->type == GDK_BUTTON_PRESS) {
987         term_scroll(inst->term, 0, +5);
988         return TRUE;
989     }
990
991     shift = event->state & GDK_SHIFT_MASK;
992     ctrl = event->state & GDK_CONTROL_MASK;
993     alt = event->state & GDK_MOD1_MASK;
994
995     if (event->button == 3 && ctrl) {
996         gtk_menu_popup(GTK_MENU(inst->menu), NULL, NULL, NULL, NULL,
997                        event->button, event->time);
998         return TRUE;
999     }
1000
1001     if (event->button == 1)
1002         button = MBT_LEFT;
1003     else if (event->button == 2)
1004         button = MBT_MIDDLE;
1005     else if (event->button == 3)
1006         button = MBT_RIGHT;
1007     else
1008         return FALSE;                  /* don't even know what button! */
1009
1010     switch (event->type) {
1011       case GDK_BUTTON_PRESS: act = MA_CLICK; break;
1012       case GDK_BUTTON_RELEASE: act = MA_RELEASE; break;
1013       case GDK_2BUTTON_PRESS: act = MA_2CLK; break;
1014       case GDK_3BUTTON_PRESS: act = MA_3CLK; break;
1015       default: return FALSE;           /* don't know this event type */
1016     }
1017
1018     if (send_raw_mouse && !(inst->cfg.mouse_override && shift) &&
1019         act != MA_CLICK && act != MA_RELEASE)
1020         return TRUE;                   /* we ignore these in raw mouse mode */
1021
1022     x = (event->x - inst->cfg.window_border) / inst->font_width;
1023     y = (event->y - inst->cfg.window_border) / inst->font_height;
1024
1025     term_mouse(inst->term, button, translate_button(button), act,
1026                x, y, shift, ctrl, alt);
1027
1028     return TRUE;
1029 }
1030
1031 gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
1032 {
1033     struct gui_data *inst = (struct gui_data *)data;
1034     int shift, ctrl, alt, x, y, button;
1035
1036     show_mouseptr(inst, 1);
1037
1038     shift = event->state & GDK_SHIFT_MASK;
1039     ctrl = event->state & GDK_CONTROL_MASK;
1040     alt = event->state & GDK_MOD1_MASK;
1041     if (event->state & GDK_BUTTON1_MASK)
1042         button = MBT_LEFT;
1043     else if (event->state & GDK_BUTTON2_MASK)
1044         button = MBT_MIDDLE;
1045     else if (event->state & GDK_BUTTON3_MASK)
1046         button = MBT_RIGHT;
1047     else
1048         return FALSE;                  /* don't even know what button! */
1049
1050     x = (event->x - inst->cfg.window_border) / inst->font_width;
1051     y = (event->y - inst->cfg.window_border) / inst->font_height;
1052
1053     term_mouse(inst->term, button, translate_button(button), MA_DRAG,
1054                x, y, shift, ctrl, alt);
1055
1056     return TRUE;
1057 }
1058
1059 void frontend_keypress(void *handle)
1060 {
1061     struct gui_data *inst = (struct gui_data *)handle;
1062
1063     /*
1064      * If our child process has exited but not closed, terminate on
1065      * any keypress.
1066      */
1067     if (inst->exited)
1068         exit(0);
1069 }
1070
1071 gint timer_func(gpointer data)
1072 {
1073     struct gui_data *inst = (struct gui_data *)data;
1074     int exitcode;
1075
1076     if (!inst->exited &&
1077         (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) {
1078         inst->exited = TRUE;
1079         if (inst->cfg.close_on_exit == FORCE_ON ||
1080             (inst->cfg.close_on_exit == AUTO && exitcode == 0))
1081             exit(0);                   /* just go. */
1082     }
1083
1084     term_update(inst->term);
1085     term_blink(inst->term, 0);
1086     return TRUE;
1087 }
1088
1089 void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
1090 {
1091     /*
1092      * We must process exceptional notifications before ordinary
1093      * readability ones, or we may go straight past the urgent
1094      * marker.
1095      */
1096     if (condition & GDK_INPUT_EXCEPTION)
1097         select_result(sourcefd, 4);
1098     if (condition & GDK_INPUT_READ)
1099         select_result(sourcefd, 1);
1100     if (condition & GDK_INPUT_WRITE)
1101         select_result(sourcefd, 2);
1102 }
1103
1104 void destroy(GtkWidget *widget, gpointer data)
1105 {
1106     gtk_main_quit();
1107 }
1108
1109 gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1110 {
1111     struct gui_data *inst = (struct gui_data *)data;
1112     inst->term->has_focus = event->in;
1113     term_out(inst->term);
1114     term_update(inst->term);
1115     show_mouseptr(inst, 1);
1116     return FALSE;
1117 }
1118
1119 /*
1120  * set or clear the "raw mouse message" mode
1121  */
1122 void set_raw_mouse_mode(void *frontend, int activate)
1123 {
1124     struct gui_data *inst = (struct gui_data *)frontend;
1125     activate = activate && !inst->cfg.no_mouse_rep;
1126     send_raw_mouse = activate;
1127     if (send_raw_mouse)
1128         inst->currcursor = inst->rawcursor;
1129     else
1130         inst->currcursor = inst->textcursor;
1131     show_mouseptr(inst, inst->mouseptr_visible);
1132 }
1133
1134 void request_resize(void *frontend, int w, int h)
1135 {
1136     struct gui_data *inst = (struct gui_data *)frontend;
1137     int large_x, large_y;
1138     int offset_x, offset_y;
1139     int area_x, area_y;
1140     GtkRequisition inner, outer;
1141
1142     /*
1143      * This is a heinous hack dreamed up by the gnome-terminal
1144      * people to get around a limitation in gtk. The problem is
1145      * that in order to set the size correctly we really need to be
1146      * calling gtk_window_resize - but that needs to know the size
1147      * of the _whole window_, not the drawing area. So what we do
1148      * is to set an artificially huge size request on the drawing
1149      * area, recompute the resulting size request on the window,
1150      * and look at the difference between the two. That gives us
1151      * the x and y offsets we need to translate drawing area size
1152      * into window size for real, and then we call
1153      * gtk_window_resize.
1154      */
1155
1156     /*
1157      * We start by retrieving the current size of the whole window.
1158      * Adding a bit to _that_ will give us a value we can use as a
1159      * bogus size request which guarantees to be bigger than the
1160      * current size of the drawing area.
1161      */
1162     get_window_pixels(inst, &large_x, &large_y);
1163     large_x += 32;
1164     large_y += 32;
1165
1166 #if GTK_CHECK_VERSION(2,0,0)
1167     gtk_widget_set_size_request(inst->area, large_x, large_y);
1168 #else
1169     gtk_widget_set_usize(inst->area, large_x, large_y);
1170 #endif
1171     gtk_widget_size_request(inst->area, &inner);
1172     gtk_widget_size_request(inst->window, &outer);
1173
1174     offset_x = outer.width - inner.width;
1175     offset_y = outer.height - inner.height;
1176
1177     area_x = inst->font_width * w + 2*inst->cfg.window_border;
1178     area_y = inst->font_height * h + 2*inst->cfg.window_border;
1179
1180     /*
1181      * Now we must set the size request on the drawing area back to
1182      * something sensible before we commit the real resize. Best
1183      * way to do this, I think, is to set it to what the size is
1184      * really going to end up being.
1185      */
1186 #if GTK_CHECK_VERSION(2,0,0)
1187     gtk_widget_set_size_request(inst->area, area_x, area_y);
1188 #else
1189     gtk_widget_set_usize(inst->area, area_x, area_y);
1190     gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), area_x, area_y);
1191 #endif
1192
1193     gtk_container_dequeue_resize_handler(GTK_CONTAINER(inst->window));
1194
1195 #if GTK_CHECK_VERSION(2,0,0)
1196     gtk_window_resize(GTK_WINDOW(inst->window),
1197                       area_x + offset_x, area_y + offset_y);
1198 #else
1199     gdk_window_resize(inst->window->window,
1200                       area_x + offset_x, area_y + offset_y);
1201 #endif
1202 }
1203
1204 static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)
1205 {
1206     gboolean success[1];
1207
1208     inst->cols[n].red = r * 0x0101;
1209     inst->cols[n].green = g * 0x0101;
1210     inst->cols[n].blue = b * 0x0101;
1211
1212     gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);
1213     gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,
1214                               FALSE, FALSE, success);
1215     if (!success[0])
1216         g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname,
1217                 n, r, g, b);
1218 }
1219
1220 void set_window_background(struct gui_data *inst)
1221 {
1222     if (inst->area && inst->area->window)
1223         gdk_window_set_background(inst->area->window, &inst->cols[18]);
1224     if (inst->window && inst->window->window)
1225         gdk_window_set_background(inst->window->window, &inst->cols[18]);
1226 }
1227
1228 void palette_set(void *frontend, int n, int r, int g, int b)
1229 {
1230     struct gui_data *inst = (struct gui_data *)frontend;
1231     static const int first[21] = {
1232         0, 2, 4, 6, 8, 10, 12, 14,
1233         1, 3, 5, 7, 9, 11, 13, 15,
1234         16, 17, 18, 20, 22
1235     };
1236     real_palette_set(inst, first[n], r, g, b);
1237     if (first[n] >= 18)
1238         real_palette_set(inst, first[n] + 1, r, g, b);
1239     if (first[n] == 18)
1240         set_window_background(inst);
1241 }
1242
1243 void palette_reset(void *frontend)
1244 {
1245     struct gui_data *inst = (struct gui_data *)frontend;
1246     /* This maps colour indices in inst->cfg to those used in inst->cols. */
1247     static const int ww[] = {
1248         6, 7, 8, 9, 10, 11, 12, 13,
1249         14, 15, 16, 17, 18, 19, 20, 21,
1250         0, 1, 2, 3, 4, 5
1251     };
1252     gboolean success[NCOLOURS];
1253     int i;
1254
1255     assert(lenof(ww) == NCOLOURS);
1256
1257     if (!inst->colmap) {
1258         inst->colmap = gdk_colormap_get_system();
1259     } else {
1260         gdk_colormap_free_colors(inst->colmap, inst->cols, NCOLOURS);
1261     }
1262
1263     for (i = 0; i < NCOLOURS; i++) {
1264         inst->cols[i].red = inst->cfg.colours[ww[i]][0] * 0x0101;
1265         inst->cols[i].green = inst->cfg.colours[ww[i]][1] * 0x0101;
1266         inst->cols[i].blue = inst->cfg.colours[ww[i]][2] * 0x0101;
1267     }
1268
1269     gdk_colormap_alloc_colors(inst->colmap, inst->cols, NCOLOURS,
1270                               FALSE, FALSE, success);
1271     for (i = 0; i < NCOLOURS; i++) {
1272         if (!success[i])
1273             g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
1274                     appname, i, inst->cfg.colours[i][0],
1275                     inst->cfg.colours[i][1], inst->cfg.colours[i][2]);
1276     }
1277
1278     set_window_background(inst);
1279 }
1280
1281 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
1282 {
1283     struct gui_data *inst = (struct gui_data *)frontend;
1284     if (inst->pasteout_data)
1285         sfree(inst->pasteout_data);
1286     if (inst->pasteout_data_utf8)
1287         sfree(inst->pasteout_data_utf8);
1288
1289     /*
1290      * Set up UTF-8 paste data. This only happens if we aren't in
1291      * direct-to-font mode using the D800 hack.
1292      */
1293     if (!inst->direct_to_font) {
1294         wchar_t *tmp = data;
1295         int tmplen = len;
1296
1297         inst->pasteout_data_utf8 = snewn(len*6, char);
1298         inst->pasteout_data_utf8_len = len*6;
1299         inst->pasteout_data_utf8_len =
1300             charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,
1301                                  inst->pasteout_data_utf8_len,
1302                                  CS_UTF8, NULL, NULL, 0);
1303         if (inst->pasteout_data_utf8_len == 0) {
1304             sfree(inst->pasteout_data_utf8);
1305             inst->pasteout_data_utf8 = NULL;
1306         } else {
1307             inst->pasteout_data_utf8 =
1308                 sresize(inst->pasteout_data_utf8,
1309                         inst->pasteout_data_utf8_len, char);
1310         }
1311     } else {
1312         inst->pasteout_data_utf8 = NULL;
1313         inst->pasteout_data_utf8_len = 0;
1314     }
1315
1316     inst->pasteout_data = snewn(len*6, char);
1317     inst->pasteout_data_len = len*6;
1318     inst->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0,
1319                                        data, len, inst->pasteout_data,
1320                                        inst->pasteout_data_len,
1321                                        NULL, NULL, NULL);
1322     if (inst->pasteout_data_len == 0) {
1323         sfree(inst->pasteout_data);
1324         inst->pasteout_data = NULL;
1325     } else {
1326         inst->pasteout_data =
1327             sresize(inst->pasteout_data, inst->pasteout_data_len, char);
1328     }
1329
1330     if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
1331                                 GDK_CURRENT_TIME)) {
1332         gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
1333                                  GDK_SELECTION_TYPE_STRING, 1);
1334         gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
1335                                  compound_text_atom, 1);
1336         if (inst->pasteout_data_utf8)
1337             gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
1338                                      utf8_string_atom, 1);
1339     }
1340
1341     if (must_deselect)
1342         term_deselect(inst->term);
1343 }
1344
1345 void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
1346                    guint info, guint time_stamp, gpointer data)
1347 {
1348     struct gui_data *inst = (struct gui_data *)data;
1349     if (seldata->target == utf8_string_atom)
1350         gtk_selection_data_set(seldata, seldata->target, 8,
1351                                inst->pasteout_data_utf8,
1352                                inst->pasteout_data_utf8_len);
1353     else
1354         gtk_selection_data_set(seldata, seldata->target, 8,
1355                                inst->pasteout_data, inst->pasteout_data_len);
1356 }
1357
1358 gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
1359                      gpointer data)
1360 {
1361     struct gui_data *inst = (struct gui_data *)data;
1362     term_deselect(inst->term);
1363     if (inst->pasteout_data)
1364         sfree(inst->pasteout_data);
1365     if (inst->pasteout_data_utf8)
1366         sfree(inst->pasteout_data_utf8);
1367     inst->pasteout_data = NULL;
1368     inst->pasteout_data_len = 0;
1369     inst->pasteout_data_utf8 = NULL;
1370     inst->pasteout_data_utf8_len = 0;
1371     return TRUE;
1372 }
1373
1374 void request_paste(void *frontend)
1375 {
1376     struct gui_data *inst = (struct gui_data *)frontend;
1377     /*
1378      * In Unix, pasting is asynchronous: all we can do at the
1379      * moment is to call gtk_selection_convert(), and when the data
1380      * comes back _then_ we can call term_do_paste().
1381      */
1382
1383     if (!inst->direct_to_font) {
1384         /*
1385          * First we attempt to retrieve the selection as a UTF-8
1386          * string (which we will convert to the correct code page
1387          * before sending to the session, of course). If that
1388          * fails, selection_received() will be informed and will
1389          * fall back to an ordinary string.
1390          */
1391         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
1392                               utf8_string_atom, GDK_CURRENT_TIME);
1393     } else {
1394         /*
1395          * If we're in direct-to-font mode, we disable UTF-8
1396          * pasting, and go straight to ordinary string data.
1397          */
1398         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
1399                               GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
1400     }
1401 }
1402
1403 gint idle_paste_func(gpointer data);   /* forward ref */
1404
1405 void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
1406                         guint time, gpointer data)
1407 {
1408     struct gui_data *inst = (struct gui_data *)data;
1409
1410     if (seldata->target == utf8_string_atom && seldata->length <= 0) {
1411         /*
1412          * Failed to get a UTF-8 selection string. Try an ordinary
1413          * string.
1414          */
1415         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
1416                               GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
1417         return;
1418     }
1419
1420     /*
1421      * Any other failure should just go foom.
1422      */
1423     if (seldata->length <= 0 ||
1424         (seldata->type != GDK_SELECTION_TYPE_STRING &&
1425          seldata->type != utf8_string_atom))
1426         return;                        /* Nothing happens. */
1427
1428     if (inst->pastein_data)
1429         sfree(inst->pastein_data);
1430
1431     inst->pastein_data = snewn(seldata->length, wchar_t);
1432     inst->pastein_data_len = seldata->length;
1433     inst->pastein_data_len =
1434         mb_to_wc((seldata->type == utf8_string_atom ?
1435                   CS_UTF8 : inst->ucsdata.line_codepage),
1436                  0, seldata->data, seldata->length,
1437                  inst->pastein_data, inst->pastein_data_len);
1438
1439     term_do_paste(inst->term);
1440
1441     if (term_paste_pending(inst->term))
1442         inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);
1443 }
1444
1445 gint idle_paste_func(gpointer data)
1446 {
1447     struct gui_data *inst = (struct gui_data *)data;
1448
1449     if (term_paste_pending(inst->term))
1450         term_paste(inst->term);
1451     else
1452         gtk_idle_remove(inst->term_paste_idle_id);
1453
1454     return TRUE;
1455 }
1456
1457
1458 void get_clip(void *frontend, wchar_t ** p, int *len)
1459 {
1460     struct gui_data *inst = (struct gui_data *)frontend;
1461
1462     if (p) {
1463         *p = inst->pastein_data;
1464         *len = inst->pastein_data_len;
1465     }
1466 }
1467
1468 void set_title(void *frontend, char *title)
1469 {
1470     struct gui_data *inst = (struct gui_data *)frontend;
1471     strncpy(inst->wintitle, title, lenof(inst->wintitle));
1472     inst->wintitle[lenof(inst->wintitle)-1] = '\0';
1473     gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
1474 }
1475
1476 void set_icon(void *frontend, char *title)
1477 {
1478     struct gui_data *inst = (struct gui_data *)frontend;
1479     strncpy(inst->icontitle, title, lenof(inst->icontitle));
1480     inst->icontitle[lenof(inst->icontitle)-1] = '\0';
1481     gdk_window_set_icon_name(inst->window->window, inst->icontitle);
1482 }
1483
1484 void set_sbar(void *frontend, int total, int start, int page)
1485 {
1486     struct gui_data *inst = (struct gui_data *)frontend;
1487     if (!inst->cfg.scrollbar)
1488         return;
1489     inst->sbar_adjust->lower = 0;
1490     inst->sbar_adjust->upper = total;
1491     inst->sbar_adjust->value = start;
1492     inst->sbar_adjust->page_size = page;
1493     inst->sbar_adjust->step_increment = 1;
1494     inst->sbar_adjust->page_increment = page/2;
1495     inst->ignore_sbar = TRUE;
1496     gtk_adjustment_changed(inst->sbar_adjust);
1497     inst->ignore_sbar = FALSE;
1498 }
1499
1500 void scrollbar_moved(GtkAdjustment *adj, gpointer data)
1501 {
1502     struct gui_data *inst = (struct gui_data *)data;
1503
1504     if (!inst->cfg.scrollbar)
1505         return;
1506     if (!inst->ignore_sbar)
1507         term_scroll(inst->term, 1, (int)adj->value);
1508 }
1509
1510 void sys_cursor(void *frontend, int x, int y)
1511 {
1512     /*
1513      * This is meaningless under X.
1514      */
1515 }
1516
1517 /*
1518  * This is still called when mode==BELL_VISUAL, even though the
1519  * visual bell is handled entirely within terminal.c, because we
1520  * may want to perform additional actions on any kind of bell (for
1521  * example, taskbar flashing in Windows).
1522  */
1523 void beep(void *frontend, int mode)
1524 {
1525     if (mode != BELL_VISUAL)
1526         gdk_beep();
1527 }
1528
1529 int char_width(Context ctx, int uc)
1530 {
1531     /*
1532      * Under X, any fixed-width font really _is_ fixed-width.
1533      * Double-width characters will be dealt with using a separate
1534      * font. For the moment we can simply return 1.
1535      */
1536     return 1;
1537 }
1538
1539 Context get_ctx(void *frontend)
1540 {
1541     struct gui_data *inst = (struct gui_data *)frontend;
1542     struct draw_ctx *dctx;
1543
1544     if (!inst->area->window)
1545         return NULL;
1546
1547     dctx = snew(struct draw_ctx);
1548     dctx->inst = inst;
1549     dctx->gc = gdk_gc_new(inst->area->window);
1550     return dctx;
1551 }
1552
1553 void free_ctx(Context ctx)
1554 {
1555     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1556     /* struct gui_data *inst = dctx->inst; */
1557     GdkGC *gc = dctx->gc;
1558     gdk_gc_unref(gc);
1559     sfree(dctx);
1560 }
1561
1562 /*
1563  * Draw a line of text in the window, at given character
1564  * coordinates, in given attributes.
1565  *
1566  * We are allowed to fiddle with the contents of `text'.
1567  */
1568 void do_text_internal(Context ctx, int x, int y, char *text, int len,
1569                       unsigned long attr, int lattr)
1570 {
1571     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1572     struct gui_data *inst = dctx->inst;
1573     GdkGC *gc = dctx->gc;
1574
1575     int nfg, nbg, t, fontid, shadow, rlen, widefactor;
1576
1577     nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1578     nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
1579     nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1580     nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
1581     if (attr & ATTR_REVERSE) {
1582         t = nfg;
1583         nfg = nbg;
1584         nbg = t;
1585     }
1586     if (inst->cfg.bold_colour && (attr & ATTR_BOLD))
1587         nfg |= 1;
1588     if (inst->cfg.bold_colour && (attr & ATTR_BLINK))
1589         nbg |= 1;
1590     if (attr & TATTR_ACTCURS) {
1591         nfg = NCOLOURS-2;
1592         nbg = NCOLOURS-1;
1593     }
1594
1595     fontid = shadow = 0;
1596
1597     if (attr & ATTR_WIDE) {
1598         widefactor = 2;
1599         fontid |= 2;
1600     } else {
1601         widefactor = 1;
1602     }
1603
1604     if ((attr & ATTR_BOLD) && !inst->cfg.bold_colour) {
1605         if (inst->fonts[fontid | 1])
1606             fontid |= 1;
1607         else
1608             shadow = 1;
1609     }
1610
1611     if (lattr != LATTR_NORM) {
1612         x *= 2;
1613         if (x >= inst->term->cols)
1614             return;
1615         if (x + len*2*widefactor > inst->term->cols)
1616             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
1617         rlen = len * 2;
1618     } else
1619         rlen = len;
1620
1621     {
1622         GdkRectangle r;
1623
1624         r.x = x*inst->font_width+inst->cfg.window_border;
1625         r.y = y*inst->font_height+inst->cfg.window_border;
1626         r.width = rlen*widefactor*inst->font_width;
1627         r.height = inst->font_height;
1628         gdk_gc_set_clip_rectangle(gc, &r);
1629     }
1630
1631     gdk_gc_set_foreground(gc, &inst->cols[nbg]);
1632     gdk_draw_rectangle(inst->pixmap, gc, 1,
1633                        x*inst->font_width+inst->cfg.window_border,
1634                        y*inst->font_height+inst->cfg.window_border,
1635                        rlen*widefactor*inst->font_width, inst->font_height);
1636
1637     gdk_gc_set_foreground(gc, &inst->cols[nfg]);
1638     {
1639         GdkWChar *gwcs;
1640         gchar *gcs;
1641         wchar_t *wcs;
1642         int i;
1643
1644         wcs = snewn(len+1, wchar_t);
1645         for (i = 0; i < len; i++) {
1646             wcs[i] = (wchar_t) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
1647         }
1648
1649         if (inst->fonts[fontid] == NULL) {
1650             /*
1651              * The font for this contingency does not exist.
1652              * Typically this means we've been given ATTR_WIDE
1653              * character and have no wide font. So we display
1654              * nothing at all; such is life.
1655              */
1656         } else if (inst->fontinfo[fontid].is_wide) {
1657             /*
1658              * At least one version of gdk_draw_text_wc() has a
1659              * weird bug whereby it reads `len' elements of the
1660              * input string, but only draws `len/2'. Hence I'm
1661              * going to make its input array twice as long as it
1662              * theoretically needs to be, and pass in twice the
1663              * actual number of characters. If a fixed gdk actually
1664              * takes the doubled length seriously, then (a) the
1665              * array will stand scrutiny up to the full length, (b)
1666              * the spare elements of the array are full of zeroes
1667              * which will probably be an empty glyph in the font,
1668              * and (c) the clip rectangle should prevent it causing
1669              * trouble anyway.
1670              */
1671             gwcs = snewn(len*2+1, GdkWChar);
1672             memset(gwcs, 0, sizeof(GdkWChar) * (len*2+1));
1673             /*
1674              * FIXME: when we have a wide-char equivalent of
1675              * from_unicode, use it instead of this.
1676              */
1677             for (i = 0; i <= len; i++)
1678                 gwcs[i] = wcs[i];
1679             gdk_draw_text_wc(inst->pixmap, inst->fonts[fontid], gc,
1680                              x*inst->font_width+inst->cfg.window_border,
1681                              y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
1682                              gwcs, len*2);
1683             sfree(gwcs);
1684         } else {
1685             gcs = snewn(len+1, gchar);
1686             wc_to_mb(inst->fontinfo[fontid].charset, 0,
1687                      wcs, len, gcs, len, ".", NULL, NULL);
1688             gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
1689                           x*inst->font_width+inst->cfg.window_border,
1690                           y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
1691                           gcs, len);
1692             sfree(gcs);
1693         }
1694         sfree(wcs);
1695     }
1696
1697     if (shadow) {
1698         gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
1699                       x*inst->font_width+inst->cfg.window_border + inst->cfg.shadowboldoffset,
1700                       y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
1701                       text, len);
1702     }
1703
1704     if (attr & ATTR_UNDER) {
1705         int uheight = inst->fonts[0]->ascent + 1;
1706         if (uheight >= inst->font_height)
1707             uheight = inst->font_height - 1;
1708         gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->cfg.window_border,
1709                       y*inst->font_height + uheight + inst->cfg.window_border,
1710                       (x+len)*widefactor*inst->font_width-1+inst->cfg.window_border,
1711                       y*inst->font_height + uheight + inst->cfg.window_border);
1712     }
1713
1714     if (lattr != LATTR_NORM) {
1715         /*
1716          * I can't find any plausible StretchBlt equivalent in the
1717          * X server, so I'm going to do this the slow and painful
1718          * way. This will involve repeated calls to
1719          * gdk_draw_pixmap() to stretch the text horizontally. It's
1720          * O(N^2) in time and O(N) in network bandwidth, but you
1721          * try thinking of a better way. :-(
1722          */
1723         int i;
1724         for (i = 0; i < len * widefactor * inst->font_width; i++) {
1725             gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
1726                             x*inst->font_width+inst->cfg.window_border + 2*i,
1727                             y*inst->font_height+inst->cfg.window_border,
1728                             x*inst->font_width+inst->cfg.window_border + 2*i+1,
1729                             y*inst->font_height+inst->cfg.window_border,
1730                             len * inst->font_width - i, inst->font_height);
1731         }
1732         len *= 2;
1733         if (lattr != LATTR_WIDE) {
1734             int dt, db;
1735             /* Now stretch vertically, in the same way. */
1736             if (lattr == LATTR_BOT)
1737                 dt = 0, db = 1;
1738             else
1739                 dt = 1, db = 0;
1740             for (i = 0; i < inst->font_height; i+=2) {
1741                 gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
1742                                 x*inst->font_width+inst->cfg.window_border,
1743                                 y*inst->font_height+inst->cfg.window_border+dt*i+db,
1744                                 x*widefactor*inst->font_width+inst->cfg.window_border,
1745                                 y*inst->font_height+inst->cfg.window_border+dt*(i+1),
1746                                 len * inst->font_width, inst->font_height-i-1);
1747             }
1748         }
1749     }
1750 }
1751
1752 void do_text(Context ctx, int x, int y, char *text, int len,
1753              unsigned long attr, int lattr)
1754 {
1755     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1756     struct gui_data *inst = dctx->inst;
1757     GdkGC *gc = dctx->gc;
1758     int widefactor;
1759
1760     do_text_internal(ctx, x, y, text, len, attr, lattr);
1761
1762     if (attr & ATTR_WIDE) {
1763         widefactor = 2;
1764     } else {
1765         widefactor = 1;
1766     }
1767
1768     if (lattr != LATTR_NORM) {
1769         x *= 2;
1770         if (x >= inst->term->cols)
1771             return;
1772         if (x + len*2*widefactor > inst->term->cols)
1773             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
1774         len *= 2;
1775     }
1776
1777     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
1778                     x*inst->font_width+inst->cfg.window_border,
1779                     y*inst->font_height+inst->cfg.window_border,
1780                     x*inst->font_width+inst->cfg.window_border,
1781                     y*inst->font_height+inst->cfg.window_border,
1782                     len*widefactor*inst->font_width, inst->font_height);
1783 }
1784
1785 void do_cursor(Context ctx, int x, int y, char *text, int len,
1786                unsigned long attr, int lattr)
1787 {
1788     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1789     struct gui_data *inst = dctx->inst;
1790     GdkGC *gc = dctx->gc;
1791
1792     int passive, widefactor;
1793
1794     if (attr & TATTR_PASCURS) {
1795         attr &= ~TATTR_PASCURS;
1796         passive = 1;
1797     } else
1798         passive = 0;
1799     if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) {
1800         attr &= ~TATTR_ACTCURS;
1801     }
1802     do_text_internal(ctx, x, y, text, len, attr, lattr);
1803
1804     if (attr & ATTR_WIDE) {
1805         widefactor = 2;
1806     } else {
1807         widefactor = 1;
1808     }
1809
1810     if (lattr != LATTR_NORM) {
1811         x *= 2;
1812         if (x >= inst->term->cols)
1813             return;
1814         if (x + len*2*widefactor > inst->term->cols)
1815             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
1816         len *= 2;
1817     }
1818
1819     if (inst->cfg.cursor_type == 0) {
1820         /*
1821          * An active block cursor will already have been done by
1822          * the above do_text call, so we only need to do anything
1823          * if it's passive.
1824          */
1825         if (passive) {
1826             gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]);
1827             gdk_draw_rectangle(inst->pixmap, gc, 0,
1828                                x*inst->font_width+inst->cfg.window_border,
1829                                y*inst->font_height+inst->cfg.window_border,
1830                                len*inst->font_width-1, inst->font_height-1);
1831         }
1832     } else {
1833         int uheight;
1834         int startx, starty, dx, dy, length, i;
1835
1836         int char_width;
1837
1838         if ((attr & ATTR_WIDE) || lattr != LATTR_NORM)
1839             char_width = 2*inst->font_width;
1840         else
1841             char_width = inst->font_width;
1842
1843         if (inst->cfg.cursor_type == 1) {
1844             uheight = inst->fonts[0]->ascent + 1;
1845             if (uheight >= inst->font_height)
1846                 uheight = inst->font_height - 1;
1847
1848             startx = x * inst->font_width + inst->cfg.window_border;
1849             starty = y * inst->font_height + inst->cfg.window_border + uheight;
1850             dx = 1;
1851             dy = 0;
1852             length = len * char_width;
1853         } else {
1854             int xadjust = 0;
1855             if (attr & TATTR_RIGHTCURS)
1856                 xadjust = char_width - 1;
1857             startx = x * inst->font_width + inst->cfg.window_border + xadjust;
1858             starty = y * inst->font_height + inst->cfg.window_border;
1859             dx = 0;
1860             dy = 1;
1861             length = inst->font_height;
1862         }
1863
1864         gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]);
1865         if (passive) {
1866             for (i = 0; i < length; i++) {
1867                 if (i % 2 == 0) {
1868                     gdk_draw_point(inst->pixmap, gc, startx, starty);
1869                 }
1870                 startx += dx;
1871                 starty += dy;
1872             }
1873         } else {
1874             gdk_draw_line(inst->pixmap, gc, startx, starty,
1875                           startx + (length-1) * dx, starty + (length-1) * dy);
1876         }
1877     }
1878
1879     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
1880                     x*inst->font_width+inst->cfg.window_border,
1881                     y*inst->font_height+inst->cfg.window_border,
1882                     x*inst->font_width+inst->cfg.window_border,
1883                     y*inst->font_height+inst->cfg.window_border,
1884                     len*widefactor*inst->font_width, inst->font_height);
1885 }
1886
1887 GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
1888 {
1889     /*
1890      * Truly hideous hack: GTK doesn't allow us to set the mouse
1891      * cursor foreground and background colours unless we've _also_
1892      * created our own cursor from bitmaps. Therefore, I need to
1893      * load the `cursor' font and draw glyphs from it on to
1894      * pixmaps, in order to construct my cursors with the fg and bg
1895      * I want. This is a gross hack, but it's more self-contained
1896      * than linking in Xlib to find the X window handle to
1897      * inst->area and calling XRecolorCursor, and it's more
1898      * futureproof than hard-coding the shapes as bitmap arrays.
1899      */
1900     static GdkFont *cursor_font = NULL;
1901     GdkPixmap *source, *mask;
1902     GdkGC *gc;
1903     GdkColor cfg = { 0, 65535, 65535, 65535 };
1904     GdkColor cbg = { 0, 0, 0, 0 };
1905     GdkColor dfg = { 1, 65535, 65535, 65535 };
1906     GdkColor dbg = { 0, 0, 0, 0 };
1907     GdkCursor *ret;
1908     gchar text[2];
1909     gint lb, rb, wid, asc, desc, w, h, x, y;
1910
1911     if (cursor_val == -2) {
1912         gdk_font_unref(cursor_font);
1913         return NULL;
1914     }
1915
1916     if (cursor_val >= 0 && !cursor_font)
1917         cursor_font = gdk_font_load("cursor");
1918
1919     /*
1920      * Get the text extent of the cursor in question. We use the
1921      * mask character for this, because it's typically slightly
1922      * bigger than the main character.
1923      */
1924     if (cursor_val >= 0) {
1925         text[1] = '\0';
1926         text[0] = (char)cursor_val + 1;
1927         gdk_string_extents(cursor_font, text, &lb, &rb, &wid, &asc, &desc);
1928         w = rb-lb; h = asc+desc; x = -lb; y = asc;
1929     } else {
1930         w = h = 1;
1931         x = y = 0;
1932     }
1933
1934     source = gdk_pixmap_new(NULL, w, h, 1);
1935     mask = gdk_pixmap_new(NULL, w, h, 1);
1936
1937     /*
1938      * Draw the mask character on the mask pixmap.
1939      */
1940     gc = gdk_gc_new(mask);
1941     gdk_gc_set_foreground(gc, &dbg);
1942     gdk_draw_rectangle(mask, gc, 1, 0, 0, w, h);
1943     if (cursor_val >= 0) {
1944         text[1] = '\0';
1945         text[0] = (char)cursor_val + 1;
1946         gdk_gc_set_foreground(gc, &dfg);
1947         gdk_draw_text(mask, cursor_font, gc, x, y, text, 1);
1948     }
1949     gdk_gc_unref(gc);
1950
1951     /*
1952      * Draw the main character on the source pixmap.
1953      */
1954     gc = gdk_gc_new(source);
1955     gdk_gc_set_foreground(gc, &dbg);
1956     gdk_draw_rectangle(source, gc, 1, 0, 0, w, h);
1957     if (cursor_val >= 0) {
1958         text[1] = '\0';
1959         text[0] = (char)cursor_val;
1960         gdk_gc_set_foreground(gc, &dfg);
1961         gdk_draw_text(source, cursor_font, gc, x, y, text, 1);
1962     }
1963     gdk_gc_unref(gc);
1964
1965     /*
1966      * Create the cursor.
1967      */
1968     ret = gdk_cursor_new_from_pixmap(source, mask, &cfg, &cbg, x, y);
1969
1970     /*
1971      * Clean up.
1972      */
1973     gdk_pixmap_unref(source);
1974     gdk_pixmap_unref(mask);
1975
1976     return ret;
1977 }
1978
1979 void modalfatalbox(char *p, ...)
1980 {
1981     va_list ap;
1982     fprintf(stderr, "FATAL ERROR: ");
1983     va_start(ap, p);
1984     vfprintf(stderr, p, ap);
1985     va_end(ap);
1986     fputc('\n', stderr);
1987     exit(1);
1988 }
1989
1990 void cmdline_error(char *p, ...)
1991 {
1992     va_list ap;
1993     fprintf(stderr, "%s: ", appname);
1994     va_start(ap, p);
1995     vfprintf(stderr, p, ap);
1996     va_end(ap);
1997     fputc('\n', stderr);
1998     exit(1);
1999 }
2000
2001 char *get_x_display(void *frontend)
2002 {
2003     return gdk_get_display();
2004 }
2005
2006 long get_windowid(void *frontend)
2007 {
2008     struct gui_data *inst = (struct gui_data *)frontend;
2009     return (long)GDK_WINDOW_XWINDOW(inst->area->window);
2010 }
2011
2012 static void help(FILE *fp) {
2013     if(fprintf(fp,
2014 "pterm option summary:\n"
2015 "\n"
2016 "  --display DISPLAY         Specify X display to use (note '--')\n"
2017 "  -name PREFIX              Prefix when looking up resources (default: pterm)\n"
2018 "  -fn FONT                  Normal text font\n"
2019 "  -fb FONT                  Bold text font\n"
2020 "  -geometry GEOMETRY        Position and size of window (size in characters)\n"
2021 "  -sl LINES                 Number of lines of scrollback\n"
2022 "  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"
2023 "  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"
2024 "  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"
2025 "  -T TITLE                  Window title\n"
2026 "  -ut, +ut                  Do(default) or do not update utmp\n"
2027 "  -ls, +ls                  Do(default) or do not make shell a login shell\n"
2028 "  -sb, +sb                  Do(default) or do not display a scrollbar\n"
2029 "  -log PATH                 Log all output to a file\n"
2030 "  -nethack                  Map numeric keypad to hjklyubn direction keys\n"
2031 "  -xrm RESOURCE-STRING      Set an X resource\n"
2032 "  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"
2033          ) < 0 || fflush(fp) < 0) {
2034         perror("output error");
2035         exit(1);
2036     }
2037 }
2038
2039 int do_cmdline(int argc, char **argv, int do_everything,
2040                struct gui_data *inst, Config *cfg)
2041 {
2042     int err = 0;
2043     char *val;
2044
2045     /*
2046      * Macros to make argument handling easier. Note that because
2047      * they need to call `continue', they cannot be contained in
2048      * the usual do {...} while (0) wrapper to make them
2049      * syntactically single statements; hence it is not legal to
2050      * use one of these macros as an unbraced statement between
2051      * `if' and `else'.
2052      */
2053 #define EXPECTS_ARG { \
2054     if (--argc <= 0) { \
2055         err = 1; \
2056         fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
2057         continue; \
2058     } else \
2059         val = *++argv; \
2060 }
2061 #define SECOND_PASS_ONLY { if (!do_everything) continue; }
2062
2063     while (--argc > 0) {
2064         char *p = *++argv;
2065         int ret;
2066
2067         ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
2068                                     do_everything ? 1 : -1, cfg);
2069
2070         if (ret == -2) {
2071             cmdline_error("option \"%s\" requires an argument", p);
2072         } else if (ret == 2) {
2073             --argc, ++argv;            /* skip next argument */
2074             continue;
2075         } else if (ret == 1) {
2076             continue;
2077         }
2078
2079         if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
2080             EXPECTS_ARG;
2081             SECOND_PASS_ONLY;
2082             strncpy(cfg->font.name, val, sizeof(cfg->font.name));
2083             cfg->font.name[sizeof(cfg->font.name)-1] = '\0';
2084
2085         } else if (!strcmp(p, "-fb")) {
2086             EXPECTS_ARG;
2087             SECOND_PASS_ONLY;
2088             strncpy(cfg->boldfont.name, val, sizeof(cfg->boldfont.name));
2089             cfg->boldfont.name[sizeof(cfg->boldfont.name)-1] = '\0';
2090
2091         } else if (!strcmp(p, "-fw")) {
2092             EXPECTS_ARG;
2093             SECOND_PASS_ONLY;
2094             strncpy(cfg->widefont.name, val, sizeof(cfg->widefont.name));
2095             cfg->widefont.name[sizeof(cfg->widefont.name)-1] = '\0';
2096
2097         } else if (!strcmp(p, "-fwb")) {
2098             EXPECTS_ARG;
2099             SECOND_PASS_ONLY;
2100             strncpy(cfg->wideboldfont.name, val, sizeof(cfg->wideboldfont.name));
2101             cfg->wideboldfont.name[sizeof(cfg->wideboldfont.name)-1] = '\0';
2102
2103         } else if (!strcmp(p, "-cs")) {
2104             EXPECTS_ARG;
2105             SECOND_PASS_ONLY;
2106             strncpy(cfg->line_codepage, val, sizeof(cfg->line_codepage));
2107             cfg->line_codepage[sizeof(cfg->line_codepage)-1] = '\0';
2108
2109         } else if (!strcmp(p, "-geometry")) {
2110             int flags, x, y, w, h;
2111             EXPECTS_ARG;
2112             SECOND_PASS_ONLY;
2113
2114             flags = XParseGeometry(val, &x, &y, &w, &h);
2115             if (flags & WidthValue)
2116                 cfg->width = w;
2117             if (flags & HeightValue)
2118                 cfg->height = h;
2119
2120             if (flags & (XValue | YValue)) {
2121                 inst->xpos = x;
2122                 inst->ypos = y;
2123                 inst->gotpos = TRUE;
2124                 inst->gravity = ((flags & XNegative ? 1 : 0) |
2125                                  (flags & YNegative ? 2 : 0));
2126             }
2127
2128         } else if (!strcmp(p, "-sl")) {
2129             EXPECTS_ARG;
2130             SECOND_PASS_ONLY;
2131             cfg->savelines = atoi(val);
2132
2133         } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
2134                    !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
2135                    !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
2136             GdkColor col;
2137
2138             EXPECTS_ARG;
2139             SECOND_PASS_ONLY;
2140             if (!gdk_color_parse(val, &col)) {
2141                 err = 1;
2142                 fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
2143                         appname, val);
2144             } else {
2145                 int index;
2146                 index = (!strcmp(p, "-fg") ? 0 :
2147                          !strcmp(p, "-bg") ? 2 :
2148                          !strcmp(p, "-bfg") ? 1 :
2149                          !strcmp(p, "-bbg") ? 3 :
2150                          !strcmp(p, "-cfg") ? 4 :
2151                          !strcmp(p, "-cbg") ? 5 : -1);
2152                 assert(index != -1);
2153                 cfg->colours[index][0] = col.red / 256;
2154                 cfg->colours[index][1] = col.green / 256;
2155                 cfg->colours[index][2] = col.blue / 256;
2156             }
2157
2158         } else if (use_pty_argv && !strcmp(p, "-e")) {
2159             /* This option swallows all further arguments. */
2160             if (!do_everything)
2161                 break;
2162
2163             if (--argc > 0) {
2164                 int i;
2165                 pty_argv = snewn(argc+1, char *);
2166                 ++argv;
2167                 for (i = 0; i < argc; i++)
2168                     pty_argv[i] = argv[i];
2169                 pty_argv[argc] = NULL;
2170                 break;                 /* finished command-line processing */
2171             } else
2172                 err = 1, fprintf(stderr, "%s: -e expects an argument\n",
2173                                  appname);
2174
2175         } else if (!strcmp(p, "-title")) {
2176             EXPECTS_ARG;
2177             SECOND_PASS_ONLY;
2178             strncpy(cfg->wintitle, val, sizeof(cfg->wintitle));
2179             cfg->wintitle[sizeof(cfg->wintitle)-1] = '\0';
2180
2181         } else if (!strcmp(p, "-log")) {
2182             EXPECTS_ARG;
2183             SECOND_PASS_ONLY;
2184             strncpy(cfg->logfilename.path, val, sizeof(cfg->logfilename.path));
2185             cfg->logfilename.path[sizeof(cfg->logfilename.path)-1] = '\0';
2186             cfg->logtype = LGTYP_DEBUG;
2187
2188         } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
2189             SECOND_PASS_ONLY;
2190             cfg->stamp_utmp = 0;
2191
2192         } else if (!strcmp(p, "-ut")) {
2193             SECOND_PASS_ONLY;
2194             cfg->stamp_utmp = 1;
2195
2196         } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
2197             SECOND_PASS_ONLY;
2198             cfg->login_shell = 0;
2199
2200         } else if (!strcmp(p, "-ls")) {
2201             SECOND_PASS_ONLY;
2202             cfg->login_shell = 1;
2203
2204         } else if (!strcmp(p, "-nethack")) {
2205             SECOND_PASS_ONLY;
2206             cfg->nethack_keypad = 1;
2207
2208         } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
2209             SECOND_PASS_ONLY;
2210             cfg->scrollbar = 0;
2211
2212         } else if (!strcmp(p, "-sb")) {
2213             SECOND_PASS_ONLY;
2214             cfg->scrollbar = 0;
2215
2216         } else if (!strcmp(p, "-name")) {
2217             EXPECTS_ARG;
2218             app_name = val;
2219
2220         } else if (!strcmp(p, "-xrm")) {
2221             EXPECTS_ARG;
2222             provide_xrm_string(val);
2223
2224         } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
2225             help(stdout);
2226             exit(0);
2227             
2228         } else if(p[0] != '-' && (!do_everything ||
2229                                   process_nonoption_arg(p, cfg))) {
2230             /* do nothing */
2231
2232         } else {
2233             err = 1;
2234             fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
2235         }
2236     }
2237
2238     return err;
2239 }
2240
2241 static void block_signal(int sig, int block_it) {
2242   sigset_t ss;
2243
2244   sigemptyset(&ss);
2245   sigaddset(&ss, sig);
2246   if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) {
2247     perror("sigprocmask");
2248     exit(1);
2249   }
2250 }
2251
2252 /*
2253  * This function retrieves the character set encoding of a font. It
2254  * returns the character set without the X11 hack (in case the user
2255  * asks to use the font's own encoding).
2256  */
2257 static int set_font_info(struct gui_data *inst, int fontid)
2258 {
2259     GdkFont *font = inst->fonts[fontid];
2260     XFontStruct *xfs = GDK_FONT_XFONT(font);
2261     Display *disp = GDK_FONT_XDISPLAY(font);
2262     Atom charset_registry, charset_encoding;
2263     unsigned long registry_ret, encoding_ret;
2264     int retval = CS_NONE;
2265
2266     charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);
2267     charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);
2268     inst->fontinfo[fontid].charset = CS_NONE;
2269     inst->fontinfo[fontid].is_wide = 0;
2270     if (XGetFontProperty(xfs, charset_registry, &registry_ret) &&
2271         XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
2272         char *reg, *enc;
2273         reg = XGetAtomName(disp, (Atom)registry_ret);
2274         enc = XGetAtomName(disp, (Atom)encoding_ret);
2275         if (reg && enc) {
2276             char *encoding = dupcat(reg, "-", enc, NULL);
2277             retval = inst->fontinfo[fontid].charset =
2278                 charset_from_xenc(encoding);
2279             /* FIXME: when libcharset supports wide encodings fix this. */
2280             if (!strcasecmp(encoding, "iso10646-1")) {
2281                 inst->fontinfo[fontid].is_wide = 1;
2282                 retval = CS_UTF8;
2283             }
2284
2285             /*
2286              * Hack for X line-drawing characters: if the primary
2287              * font is encoded as ISO-8859-anything, and has valid
2288              * glyphs in the first 32 char positions, it is assumed
2289              * that those glyphs are the VT100 line-drawing
2290              * character set.
2291              * 
2292              * Actually, we'll hack even harder by only checking
2293              * position 0x19 (vertical line, VT100 linedrawing
2294              * `x'). Then we can check it easily by seeing if the
2295              * ascent and descent differ.
2296              */
2297             if (inst->fontinfo[fontid].charset == CS_ISO8859_1) {
2298                 int lb, rb, wid, asc, desc;
2299                 gchar text[2];
2300
2301                 text[1] = '\0';
2302                 text[0] = '\x12';
2303                 gdk_string_extents(inst->fonts[fontid], text,
2304                                    &lb, &rb, &wid, &asc, &desc);
2305                 if (asc != desc)
2306                     inst->fontinfo[fontid].charset = CS_ISO8859_1_X11;
2307             }
2308
2309             sfree(encoding);
2310         }
2311     }
2312
2313     return retval;
2314 }
2315
2316 int uxsel_input_add(int fd, int rwx) {
2317     int flags = 0;
2318     if (rwx & 1) flags |= GDK_INPUT_READ;
2319     if (rwx & 2) flags |= GDK_INPUT_WRITE;
2320     if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
2321     return gdk_input_add(fd, flags, fd_input_func, NULL);
2322 }
2323
2324 void uxsel_input_remove(int id) {
2325     gdk_input_remove(id);
2326 }
2327
2328 void setup_fonts_ucs(struct gui_data *inst)
2329 {
2330     int font_charset;
2331
2332     if (inst->fonts[0])
2333         gdk_font_unref(inst->fonts[0]);
2334     if (inst->fonts[1])
2335         gdk_font_unref(inst->fonts[1]);
2336     if (inst->fonts[2])
2337         gdk_font_unref(inst->fonts[2]);
2338     if (inst->fonts[3])
2339         gdk_font_unref(inst->fonts[3]);
2340
2341     inst->fonts[0] = gdk_font_load(inst->cfg.font.name);
2342     if (!inst->fonts[0]) {
2343         fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,
2344                 inst->cfg.font.name);
2345         exit(1);
2346     }
2347     font_charset = set_font_info(inst, 0);
2348     if (inst->cfg.boldfont.name[0]) {
2349         inst->fonts[1] = gdk_font_load(inst->cfg.boldfont.name);
2350         if (!inst->fonts[1]) {
2351             fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
2352                     inst->cfg.boldfont.name);
2353             exit(1);
2354         }
2355         set_font_info(inst, 1);
2356     } else
2357         inst->fonts[1] = NULL;
2358     if (inst->cfg.widefont.name[0]) {
2359         inst->fonts[2] = gdk_font_load(inst->cfg.widefont.name);
2360         if (!inst->fonts[2]) {
2361             fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
2362                     inst->cfg.widefont.name);
2363             exit(1);
2364         }
2365         set_font_info(inst, 2);
2366     } else
2367         inst->fonts[2] = NULL;
2368     if (inst->cfg.wideboldfont.name[0]) {
2369         inst->fonts[3] = gdk_font_load(inst->cfg.wideboldfont.name);
2370         if (!inst->fonts[3]) {
2371             fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n",
2372                     appname, inst->cfg.wideboldfont.name);
2373             exit(1);
2374         }
2375         set_font_info(inst, 3);
2376     } else
2377         inst->fonts[3] = NULL;
2378
2379     inst->font_width = gdk_char_width(inst->fonts[0], ' ');
2380     inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
2381
2382     inst->direct_to_font = init_ucs(&inst->ucsdata,
2383                                     inst->cfg.line_codepage, font_charset);
2384 }
2385
2386 void set_geom_hints(struct gui_data *inst)
2387 {
2388     GdkGeometry geom;
2389     geom.min_width = inst->font_width + 2*inst->cfg.window_border;
2390     geom.min_height = inst->font_height + 2*inst->cfg.window_border;
2391     geom.max_width = geom.max_height = -1;
2392     geom.base_width = 2*inst->cfg.window_border;
2393     geom.base_height = 2*inst->cfg.window_border;
2394     geom.width_inc = inst->font_width;
2395     geom.height_inc = inst->font_height;
2396     geom.min_aspect = geom.max_aspect = 0;
2397     gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,
2398                                   GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
2399                                   GDK_HINT_RESIZE_INC);
2400 }
2401
2402 void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)
2403 {
2404     struct gui_data *inst = (struct gui_data *)data;
2405     term_clrsb(inst->term);
2406 }
2407
2408 void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
2409 {
2410     struct gui_data *inst = (struct gui_data *)data;
2411     term_pwron(inst->term);
2412     ldisc_send(inst->ldisc, NULL, 0, 0);
2413 }
2414
2415 void copy_all_menuitem(GtkMenuItem *item, gpointer data)
2416 {
2417     struct gui_data *inst = (struct gui_data *)data;
2418     term_copyall(inst->term);
2419 }
2420
2421 void special_menuitem(GtkMenuItem *item, gpointer data)
2422 {
2423     struct gui_data *inst = (struct gui_data *)data;
2424     int code = (int)gtk_object_get_data(GTK_OBJECT(item), "user-data");
2425
2426     inst->back->special(inst->backhandle, code);
2427 }
2428
2429 void about_menuitem(GtkMenuItem *item, gpointer data)
2430 {
2431     struct gui_data *inst = (struct gui_data *)data;
2432     about_box(inst->window);
2433 }
2434
2435 void event_log_menuitem(GtkMenuItem *item, gpointer data)
2436 {
2437     struct gui_data *inst = (struct gui_data *)data;
2438     showeventlog(inst->eventlogstuff, inst->window);
2439 }
2440
2441 void change_settings_menuitem(GtkMenuItem *item, gpointer data)
2442 {
2443     /* This maps colour indices in inst->cfg to those used in inst->cols. */
2444     static const int ww[] = {
2445         6, 7, 8, 9, 10, 11, 12, 13,
2446         14, 15, 16, 17, 18, 19, 20, 21,
2447         0, 1, 2, 3, 4, 5
2448     };
2449     struct gui_data *inst = (struct gui_data *)data;
2450     char *title = dupcat(appname, " Reconfiguration", NULL);
2451     Config cfg2, oldcfg;
2452     int i, need_size;
2453
2454     cfg2 = inst->cfg;                  /* structure copy */
2455
2456     if (do_config_box(title, &cfg2, 1)) {
2457
2458         oldcfg = inst->cfg;            /* structure copy */
2459         inst->cfg = cfg2;              /* structure copy */
2460
2461         /* Pass new config data to the logging module */
2462         log_reconfig(inst->logctx, &cfg2);
2463         /*
2464          * Flush the line discipline's edit buffer in the case
2465          * where local editing has just been disabled.
2466          */
2467         ldisc_send(inst->ldisc, NULL, 0, 0);
2468         /* Pass new config data to the terminal */
2469         term_reconfig(inst->term, &cfg2);
2470         /* Pass new config data to the back end */
2471         inst->back->reconfig(inst->backhandle, &cfg2);
2472
2473         /*
2474          * Just setting inst->cfg is sufficient to cause colour
2475          * setting changes to appear on the next ESC]R palette
2476          * reset. But we should also check whether any colour
2477          * settings have been changed, and revert the ones that
2478          * have to the new default, on the assumption that the user
2479          * is most likely to want an immediate update.
2480          */
2481         for (i = 0; i < NCOLOURS; i++) {
2482             if (oldcfg.colours[ww[i]][0] != cfg2.colours[ww[i]][0] ||
2483                 oldcfg.colours[ww[i]][1] != cfg2.colours[ww[i]][1] ||
2484                 oldcfg.colours[ww[i]][2] != cfg2.colours[ww[i]][2])
2485                 real_palette_set(inst, i, cfg2.colours[ww[i]][0],
2486                                  cfg2.colours[ww[i]][1],
2487                                  cfg2.colours[ww[i]][2]);
2488         }
2489
2490         /*
2491          * If the scrollbar needs to be shown, hidden, or moved
2492          * from one end to the other of the window, do so now.
2493          */
2494         if (oldcfg.scrollbar != cfg2.scrollbar) {
2495             if (cfg2.scrollbar)
2496                 gtk_widget_show(inst->sbar);
2497             else
2498                 gtk_widget_hide(inst->sbar);
2499         }
2500         if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) {
2501             gtk_box_reorder_child(inst->hbox, inst->sbar,
2502                                   cfg2.scrollbar_on_left ? 0 : 1);
2503         }
2504
2505         /*
2506          * Change the window title, if required.
2507          */
2508         if (strcmp(oldcfg.wintitle, cfg2.wintitle))
2509             set_title(inst, cfg2.wintitle);
2510
2511         /*
2512          * Redo the whole tangled fonts and Unicode mess if
2513          * necessary.
2514          */
2515         if (strcmp(oldcfg.font.name, cfg2.font.name) ||
2516             strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) ||
2517             strcmp(oldcfg.widefont.name, cfg2.widefont.name) ||
2518             strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) ||
2519             strcmp(oldcfg.line_codepage, cfg2.line_codepage)) {
2520             setup_fonts_ucs(inst);
2521             need_size = 1;
2522         } else
2523             need_size = 0;
2524
2525         /*
2526          * Resize the window.
2527          */
2528         if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height ||
2529             oldcfg.window_border != cfg2.window_border || need_size) {
2530             set_geom_hints(inst);
2531             request_resize(inst, cfg2.width, cfg2.height);
2532         } else {
2533             /*
2534              * The above will have caused a call to term_size() for
2535              * us if it happened. If the user has fiddled with only
2536              * the scrollback size, the above will not have
2537              * happened and we will need an explicit term_size()
2538              * here.
2539              */
2540             if (oldcfg.savelines != cfg2.savelines)
2541                 term_size(inst->term, inst->term->rows, inst->term->cols,
2542                           cfg2.savelines);
2543         }
2544
2545         term_invalidate(inst->term);
2546     }
2547     sfree(title);
2548 }
2549
2550 void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)
2551 {
2552     /*
2553      * Re-execing ourself is not an exact science under Unix. I do
2554      * the best I can by using /proc/self/exe if available and by
2555      * assuming argv[0] can be found on $PATH if not.
2556      * 
2557      * Note that we also have to reconstruct the elements of the
2558      * original argv which gtk swallowed, since the user wants the
2559      * new session to appear on the same X display as the old one.
2560      */
2561     char **args;
2562     va_list ap;
2563     int i, n;
2564     int pid;
2565
2566     /*
2567      * Collect the arguments with which to re-exec ourself.
2568      */
2569     va_start(ap, fd_to_close);
2570     n = 2;                             /* progname and terminating NULL */
2571     n += inst->ngtkargs;
2572     while (va_arg(ap, char *) != NULL)
2573         n++;
2574     va_end(ap);
2575
2576     args = snewn(n, char *);
2577     args[0] = inst->progname;
2578     args[n-1] = NULL;
2579     for (i = 0; i < inst->ngtkargs; i++)
2580         args[i+1] = inst->gtkargvstart[i];
2581
2582     i++;
2583     va_start(ap, fd_to_close);
2584     while ((args[i++] = va_arg(ap, char *)) != NULL);
2585     va_end(ap);
2586
2587     assert(i == n);
2588
2589     /*
2590      * Do the double fork.
2591      */
2592     pid = fork();
2593     if (pid < 0) {
2594         perror("fork");
2595         return;
2596     }
2597
2598     if (pid == 0) {
2599         int pid2 = fork();
2600         if (pid2 < 0) {
2601             perror("fork");
2602             _exit(1);
2603         } else if (pid2 > 0) {
2604             /*
2605              * First child has successfully forked second child. My
2606              * Work Here Is Done. Note the use of _exit rather than
2607              * exit: the latter appears to cause destroy messages
2608              * to be sent to the X server. I suspect gtk uses
2609              * atexit.
2610              */
2611             _exit(0);
2612         }
2613
2614         /*
2615          * If we reach here, we are the second child, so we now
2616          * actually perform the exec.
2617          */
2618         if (fd_to_close >= 0)
2619             close(fd_to_close);
2620
2621         execv("/proc/self/exe", args);
2622         execvp(inst->progname, args);
2623         perror("exec");
2624         _exit(127);
2625
2626     } else {
2627         int status;
2628         waitpid(pid, &status, 0);
2629     }
2630
2631 }
2632
2633 void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
2634 {
2635     struct gui_data *inst = (struct gui_data *)gdata;
2636     /*
2637      * For this feature we must marshal cfg and (possibly) pty_argv
2638      * into a byte stream, create a pipe, and send this byte stream
2639      * to the child through the pipe.
2640      */
2641     int i, ret, size;
2642     char *data;
2643     char option[80];
2644     int pipefd[2];
2645
2646     if (pipe(pipefd) < 0) {
2647         perror("pipe");
2648         return;
2649     }
2650
2651     size = sizeof(inst->cfg);
2652     if (use_pty_argv && pty_argv) {
2653         for (i = 0; pty_argv[i]; i++)
2654             size += strlen(pty_argv[i]) + 1;
2655     }
2656
2657     data = snewn(size, char);
2658     memcpy(data, &inst->cfg, sizeof(inst->cfg));
2659     if (use_pty_argv && pty_argv) {
2660         int p = sizeof(inst->cfg);
2661         for (i = 0; pty_argv[i]; i++) {
2662             strcpy(data + p, pty_argv[i]);
2663             p += strlen(pty_argv[i]) + 1;
2664         }
2665         assert(p == size);
2666     }
2667
2668     sprintf(option, "---[%d,%d]", pipefd[0], size);
2669     fcntl(pipefd[0], F_SETFD, 0);
2670     fork_and_exec_self(inst, pipefd[1], option, NULL);
2671     close(pipefd[0]);
2672
2673     i = ret = 0;
2674     while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
2675         i += ret;
2676     if (ret < 0)
2677         perror("write to pipe");
2678     close(pipefd[1]);
2679     sfree(data);
2680 }
2681
2682 int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg)
2683 {
2684     int fd, i, ret, size;
2685     char *data;
2686
2687     if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
2688         fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
2689         exit(1);
2690     }
2691
2692     data = snewn(size, char);
2693     i = ret = 0;
2694     while (i < size && (ret = read(fd, data + i, size - i)) > 0)
2695         i += ret;
2696     if (ret < 0) {
2697         perror("read from pipe");
2698         exit(1);
2699     } else if (i < size) {
2700         fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
2701                 appname);
2702         exit(1);
2703     }
2704
2705     memcpy(cfg, data, sizeof(Config));
2706     if (use_pty_argv && size > sizeof(Config)) {
2707         int n = 0;
2708         i = sizeof(Config);
2709         while (i < size) {
2710             while (i < size && data[i]) i++;
2711             if (i >= size) {
2712                 fprintf(stderr, "%s: malformed Duplicate Session data\n",
2713                         appname);
2714                 exit(1);
2715             }
2716             i++;
2717             n++;
2718         }
2719         pty_argv = snewn(n+1, char *);
2720         pty_argv[n] = NULL;
2721         n = 0;
2722         i = sizeof(Config);
2723         while (i < size) {
2724             char *p = data + i;
2725             while (i < size && data[i]) i++;
2726             assert(i < size);
2727             i++;
2728             pty_argv[n++] = dupstr(p);
2729         }
2730     }
2731
2732     return 0;
2733 }
2734
2735 void new_session_menuitem(GtkMenuItem *item, gpointer data)
2736 {
2737     struct gui_data *inst = (struct gui_data *)data;
2738
2739     fork_and_exec_self(inst, -1, NULL);
2740 }
2741
2742 void saved_session_menuitem(GtkMenuItem *item, gpointer data)
2743 {
2744     struct gui_data *inst = (struct gui_data *)data;
2745     char *str = (char *)gtk_object_get_data(GTK_OBJECT(item), "user-data");
2746
2747     fork_and_exec_self(inst, -1, "-load", str, NULL);
2748 }
2749
2750 void saved_session_freedata(GtkMenuItem *item, gpointer data)
2751 {
2752     char *str = (char *)gtk_object_get_data(GTK_OBJECT(item), "user-data");
2753
2754     sfree(str);
2755 }
2756
2757 void update_specials_menu(void *frontend)
2758 {
2759     struct gui_data *inst = (struct gui_data *)frontend;
2760
2761     const struct telnet_special *specials;
2762
2763     specials = inst->back->get_specials(inst->backhandle);
2764     gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
2765                           (GtkCallback)gtk_widget_destroy, NULL);
2766     if (specials) {
2767         int i;
2768         GtkWidget *menuitem;
2769         for (i = 0; specials[i].name; i++) {
2770             if (*specials[i].name) {
2771                 menuitem = gtk_menu_item_new_with_label(specials[i].name);
2772                 gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
2773                                     (gpointer)specials[i].code);
2774                 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
2775                                    GTK_SIGNAL_FUNC(special_menuitem), inst);
2776             } else
2777                 menuitem = gtk_menu_item_new();
2778             gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem);
2779             gtk_widget_show(menuitem);
2780         }
2781         gtk_widget_show(inst->specialsitem1);
2782         gtk_widget_show(inst->specialsitem2);
2783     } else {
2784         gtk_widget_hide(inst->specialsitem1);
2785         gtk_widget_hide(inst->specialsitem2);
2786     }
2787 }
2788
2789 int pt_main(int argc, char **argv)
2790 {
2791     extern Backend *select_backend(Config *cfg);
2792     extern int cfgbox(Config *cfg);
2793     struct gui_data *inst;
2794
2795     /*
2796      * Create an instance structure and initialise to zeroes
2797      */
2798     inst = snew(struct gui_data);
2799     memset(inst, 0, sizeof(*inst));
2800     inst->alt_keycode = -1;            /* this one needs _not_ to be zero */
2801
2802     /* defer any child exit handling until we're ready to deal with
2803      * it */
2804     block_signal(SIGCHLD, 1);
2805
2806     /*
2807      * SIGPIPE is not something we want to see terminating the
2808      * process.
2809      */
2810     block_signal(SIGPIPE, 1);
2811
2812     inst->progname = argv[0];
2813     /*
2814      * Copy the original argv before letting gtk_init fiddle with
2815      * it. It will be required later.
2816      */
2817     {
2818         int i, oldargc;
2819         inst->gtkargvstart = snewn(argc-1, char *);
2820         for (i = 1; i < argc; i++)
2821             inst->gtkargvstart[i-1] = dupstr(argv[i]);
2822         oldargc = argc;
2823         gtk_init(&argc, &argv);
2824         inst->ngtkargs = oldargc - argc;
2825     }
2826
2827     if (argc > 1 && !strncmp(argv[1], "---", 3)) {
2828         read_dupsession_data(inst, &inst->cfg, argv[1]);
2829         /* Splatter this argument so it doesn't clutter a ps listing */
2830         memset(argv[1], 0, strlen(argv[1]));
2831     } else {
2832         if (do_cmdline(argc, argv, 0, inst, &inst->cfg))
2833             exit(1);                   /* pre-defaults pass to get -class */
2834         do_defaults(NULL, &inst->cfg);
2835         if (do_cmdline(argc, argv, 1, inst, &inst->cfg))
2836             exit(1);                   /* post-defaults, do everything */
2837
2838         cmdline_run_saved(&inst->cfg);
2839
2840         if (!*inst->cfg.host && !cfgbox(&inst->cfg))
2841             exit(0);                   /* config box hit Cancel */
2842     }
2843
2844     if (!compound_text_atom)
2845         compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
2846     if (!utf8_string_atom)
2847         utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
2848
2849     setup_fonts_ucs(inst);
2850
2851     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2852
2853     /*
2854      * Set up the colour map.
2855      */
2856     palette_reset(inst);
2857
2858     inst->width = inst->cfg.width;
2859     inst->height = inst->cfg.height;
2860
2861     inst->area = gtk_drawing_area_new();
2862     gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),
2863                           inst->font_width * inst->cfg.width + 2*inst->cfg.window_border,
2864                           inst->font_height * inst->cfg.height + 2*inst->cfg.window_border);
2865     inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));
2866     inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);
2867     inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
2868     /*
2869      * We always create the scrollbar; it remains invisible if
2870      * unwanted, so we can pop it up quickly if it suddenly becomes
2871      * desirable.
2872      */
2873     if (inst->cfg.scrollbar_on_left)
2874         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
2875     gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);
2876     if (!inst->cfg.scrollbar_on_left)
2877         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
2878
2879     gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));
2880
2881     set_geom_hints(inst);
2882
2883     gtk_widget_show(inst->area);
2884     if (inst->cfg.scrollbar)
2885         gtk_widget_show(inst->sbar);
2886     else
2887         gtk_widget_hide(inst->sbar);
2888     gtk_widget_show(GTK_WIDGET(inst->hbox));
2889
2890     if (inst->gotpos) {
2891         int x = inst->xpos, y = inst->ypos;
2892         GtkRequisition req;
2893         gtk_widget_size_request(GTK_WIDGET(inst->window), &req);
2894         if (inst->gravity & 1) x += gdk_screen_width() - req.width;
2895         if (inst->gravity & 2) y += gdk_screen_height() - req.height;
2896         gtk_window_set_position(GTK_WINDOW(inst->window), GTK_WIN_POS_NONE);
2897         gtk_widget_set_uposition(GTK_WIDGET(inst->window), x, y);
2898     }
2899
2900     gtk_signal_connect(GTK_OBJECT(inst->window), "destroy",
2901                        GTK_SIGNAL_FUNC(destroy), inst);
2902     gtk_signal_connect(GTK_OBJECT(inst->window), "delete_event",
2903                        GTK_SIGNAL_FUNC(delete_window), inst);
2904     gtk_signal_connect(GTK_OBJECT(inst->window), "key_press_event",
2905                        GTK_SIGNAL_FUNC(key_event), inst);
2906     gtk_signal_connect(GTK_OBJECT(inst->window), "key_release_event",
2907                        GTK_SIGNAL_FUNC(key_event), inst);
2908     gtk_signal_connect(GTK_OBJECT(inst->window), "focus_in_event",
2909                        GTK_SIGNAL_FUNC(focus_event), inst);
2910     gtk_signal_connect(GTK_OBJECT(inst->window), "focus_out_event",
2911                        GTK_SIGNAL_FUNC(focus_event), inst);
2912     gtk_signal_connect(GTK_OBJECT(inst->area), "configure_event",
2913                        GTK_SIGNAL_FUNC(configure_area), inst);
2914     gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event",
2915                        GTK_SIGNAL_FUNC(expose_area), inst);
2916     gtk_signal_connect(GTK_OBJECT(inst->area), "button_press_event",
2917                        GTK_SIGNAL_FUNC(button_event), inst);
2918     gtk_signal_connect(GTK_OBJECT(inst->area), "button_release_event",
2919                        GTK_SIGNAL_FUNC(button_event), inst);
2920     gtk_signal_connect(GTK_OBJECT(inst->area), "motion_notify_event",
2921                        GTK_SIGNAL_FUNC(motion_event), inst);
2922     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received",
2923                        GTK_SIGNAL_FUNC(selection_received), inst);
2924     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get",
2925                        GTK_SIGNAL_FUNC(selection_get), inst);
2926     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",
2927                        GTK_SIGNAL_FUNC(selection_clear), inst);
2928     if (inst->cfg.scrollbar)
2929         gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
2930                            GTK_SIGNAL_FUNC(scrollbar_moved), inst);
2931     gtk_timeout_add(20, timer_func, inst);
2932     gtk_widget_add_events(GTK_WIDGET(inst->area),
2933                           GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
2934                           GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2935                           GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK);
2936
2937     gtk_widget_show(inst->window);
2938
2939     set_window_background(inst);
2940
2941     /*
2942      * Set up the Ctrl+rightclick context menu.
2943      */
2944     {
2945         GtkWidget *menuitem;
2946         char *s;
2947         extern const int use_event_log, new_session, saved_sessions;
2948
2949         inst->menu = gtk_menu_new();
2950
2951 #define MKMENUITEM(title, func) do { \
2952     menuitem = title ? gtk_menu_item_new_with_label(title) : \
2953     gtk_menu_item_new(); \
2954     gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \
2955     gtk_widget_show(menuitem); \
2956     if (func != NULL) \
2957         gtk_signal_connect(GTK_OBJECT(menuitem), "activate", \
2958                                GTK_SIGNAL_FUNC(func), inst); \
2959 } while (0)
2960         if (new_session)
2961             MKMENUITEM("New Session", new_session_menuitem);
2962         MKMENUITEM("Duplicate Session", dup_session_menuitem);
2963         if (saved_sessions) {
2964             struct sesslist sesslist;
2965             int i;
2966
2967             inst->sessionsmenu = gtk_menu_new();
2968
2969             get_sesslist(&sesslist, TRUE);
2970             for (i = 1; i < sesslist.nsessions; i++) {
2971                 menuitem = gtk_menu_item_new_with_label(sesslist.sessions[i]);
2972                 gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);
2973                 gtk_widget_show(menuitem);
2974                 gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
2975                                     dupstr(sesslist.sessions[i]));
2976                 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
2977                                    GTK_SIGNAL_FUNC(saved_session_menuitem),
2978                                    inst);
2979                 gtk_signal_connect(GTK_OBJECT(menuitem), "destroy",
2980                                    GTK_SIGNAL_FUNC(saved_session_freedata),
2981                                    inst);
2982             }
2983             get_sesslist(&sesslist, FALSE);
2984
2985             MKMENUITEM("Saved Sessions", NULL);
2986             gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),
2987                                       inst->sessionsmenu);
2988         }
2989         MKMENUITEM(NULL, NULL);
2990         MKMENUITEM("Change Settings", change_settings_menuitem);
2991         MKMENUITEM(NULL, NULL);
2992         if (use_event_log)
2993             MKMENUITEM("Event Log", event_log_menuitem);
2994         MKMENUITEM("Special Commands", NULL);
2995         inst->specialsmenu = gtk_menu_new();
2996         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->specialsmenu);
2997         inst->specialsitem1 = menuitem;
2998         MKMENUITEM(NULL, NULL);
2999         inst->specialsitem2 = menuitem;
3000         MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);
3001         MKMENUITEM("Reset Terminal", reset_terminal_menuitem);
3002         MKMENUITEM("Copy All", copy_all_menuitem);
3003         MKMENUITEM(NULL, NULL);
3004         s = dupcat("About ", appname, NULL);
3005         MKMENUITEM(s, about_menuitem);
3006         sfree(s);
3007 #undef MKMENUITEM
3008     }
3009
3010     inst->textcursor = make_mouse_ptr(inst, GDK_XTERM);
3011     inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR);
3012     inst->blankcursor = make_mouse_ptr(inst, -1);
3013     make_mouse_ptr(inst, -2);          /* clean up cursor font */
3014     inst->currcursor = inst->textcursor;
3015     show_mouseptr(inst, 1);
3016
3017     inst->eventlogstuff = eventlogstuff_new();
3018
3019     inst->term = term_init(&inst->cfg, &inst->ucsdata, inst);
3020     inst->logctx = log_init(inst, &inst->cfg);
3021     term_provide_logctx(inst->term, inst->logctx);
3022
3023     uxsel_init();
3024
3025     term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines);
3026
3027     inst->back = select_backend(&inst->cfg);
3028     {
3029         char *realhost, *error;
3030
3031         error = inst->back->init((void *)inst, &inst->backhandle,
3032                                  &inst->cfg, inst->cfg.host, inst->cfg.port,
3033                                  &realhost, inst->cfg.tcp_nodelay);
3034
3035         if (error) {
3036             char *msg = dupprintf("Unable to open connection to %s:\n%s",
3037                                   inst->cfg.host, error);
3038             inst->exited = TRUE;
3039             fatal_message_box(inst->window, msg);
3040             sfree(msg);
3041             return 0;
3042         }
3043
3044         if (inst->cfg.wintitle[0])
3045             set_title(inst, inst->cfg.wintitle);
3046         else {
3047             char *title = make_default_wintitle(realhost);
3048             set_title(inst, title);
3049             sfree(title);
3050         }
3051     }
3052     inst->back->provide_logctx(inst->backhandle, inst->logctx);
3053     update_specials_menu(inst);
3054
3055     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
3056
3057     inst->ldisc =
3058         ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle, inst);
3059     ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
3060
3061     /* now we're reday to deal with the child exit handler being
3062      * called */
3063     block_signal(SIGCHLD, 0);
3064
3065     inst->exited = FALSE;
3066
3067     gtk_main();
3068
3069     return 0;
3070 }