]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pterm.c
9189385016218978b4b9fae742ae0ccc8fa8765d
[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 struct gui_data {
38     GtkWidget *window, *area, *sbar;
39     GtkBox *hbox;
40     GtkAdjustment *sbar_adjust;
41     GdkPixmap *pixmap;
42     GdkFont *fonts[4];                 /* normal, bold, wide, widebold */
43     struct {
44         int charset;
45         int is_wide;
46     } fontinfo[4];
47     GdkCursor *rawcursor, *textcursor, *blankcursor, *currcursor;
48     GdkColor cols[NCOLOURS];
49     GdkColormap *colmap;
50     wchar_t *pastein_data;
51     int direct_to_font;
52     int pastein_data_len;
53     char *pasteout_data, *pasteout_data_utf8;
54     int pasteout_data_len, pasteout_data_utf8_len;
55     int font_width, font_height;
56     int ignore_sbar;
57     int mouseptr_visible;
58     guint term_paste_idle_id;
59     GdkAtom compound_text_atom, utf8_string_atom;
60     int alt_keycode;
61     int alt_digits;
62     char wintitle[sizeof(((Config *)0)->wintitle)];
63     char icontitle[sizeof(((Config *)0)->wintitle)];
64     int master_fd, master_func_id, exited;
65     void *ldisc;
66     Backend *back;
67     void *backhandle;
68     Terminal *term;
69     void *logctx;
70 };
71
72 struct draw_ctx {
73     GdkGC *gc;
74     struct gui_data *inst;
75 };
76
77 static int send_raw_mouse;
78
79 static char *app_name = "pterm";
80
81 char *x_get_default(char *key)
82 {
83     return XGetDefault(GDK_DISPLAY(), app_name, key);
84 }
85
86 /*
87  * Default settings that are specific to pterm.
88  */
89 char *platform_default_s(char *name)
90 {
91     if (!strcmp(name, "Font"))
92         return "fixed";        /* COE_NORMAL works badly in an xterm */
93     return NULL;
94 }
95
96 int platform_default_i(char *name, int def)
97 {
98     if (!strcmp(name, "CloseOnExit"))
99         return COE_ALWAYS;             /* COE_NORMAL works badly in an xterm */
100     return def;
101 }
102
103 void ldisc_update(void *frontend, int echo, int edit)
104 {
105     /*
106      * This is a stub in pterm. If I ever produce a Unix
107      * command-line ssh/telnet/rlogin client (i.e. a port of plink)
108      * then it will require some termios manoeuvring analogous to
109      * that in the Windows plink.c, but here it's meaningless.
110      */
111 }
112
113 int askappend(void *frontend, char *filename)
114 {
115     /*
116      * Logging in an xterm-alike is liable to be something you only
117      * do at serious diagnostic need. Hence, I'm going to take the
118      * easy option for now and assume we always want to overwrite
119      * log files. I can always make it properly configurable later.
120      */
121     return 2;
122 }
123
124 void logevent(void *frontend, char *string)
125 {
126     /*
127      * This is not a very helpful function: events are logged
128      * pretty much exclusively by the back end, and our pty back
129      * end is self-contained. So we need do nothing.
130      */
131 }
132
133 int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
134 {
135     struct gui_data *inst = (struct gui_data *)frontend;
136
137     if (which)
138         return inst->font_height;
139     else
140         return inst->font_width;
141 }
142
143 /*
144  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
145  * into a cooked one (SELECT, EXTEND, PASTE).
146  * 
147  * In Unix, this is not configurable; the X button arrangement is
148  * rock-solid across all applications, everyone has a three-button
149  * mouse or a means of faking it, and there is no need to switch
150  * buttons around at all.
151  */
152 Mouse_Button translate_button(void *frontend, Mouse_Button button)
153 {
154     /* struct gui_data *inst = (struct gui_data *)frontend; */
155
156     if (button == MBT_LEFT)
157         return MBT_SELECT;
158     if (button == MBT_MIDDLE)
159         return MBT_PASTE;
160     if (button == MBT_RIGHT)
161         return MBT_EXTEND;
162     return 0;                          /* shouldn't happen */
163 }
164
165 /*
166  * Minimise or restore the window in response to a server-side
167  * request.
168  */
169 void set_iconic(void *frontend, int iconic)
170 {
171     /*
172      * GTK 1.2 doesn't know how to do this.
173      */
174 #if GTK_CHECK_VERSION(2,0,0)
175     struct gui_data *inst = (struct gui_data *)frontend;
176     if (iconic)
177         gtk_window_iconify(GTK_WINDOW(inst->window));
178     else
179         gtk_window_deiconify(GTK_WINDOW(inst->window));
180 #endif
181 }
182
183 /*
184  * Move the window in response to a server-side request.
185  */
186 void move_window(void *frontend, int x, int y)
187 {
188     struct gui_data *inst = (struct gui_data *)frontend;
189     /*
190      * I assume that when the GTK version of this call is available
191      * we should use it. Not sure how it differs from the GDK one,
192      * though.
193      */
194 #if GTK_CHECK_VERSION(2,0,0)
195     gtk_window_move(GTK_WINDOW(inst->window), x, y);
196 #else
197     gdk_window_move(inst->window->window, x, y);
198 #endif
199 }
200
201 /*
202  * Move the window to the top or bottom of the z-order in response
203  * to a server-side request.
204  */
205 void set_zorder(void *frontend, int top)
206 {
207     struct gui_data *inst = (struct gui_data *)frontend;
208     if (top)
209         gdk_window_raise(inst->window->window);
210     else
211         gdk_window_lower(inst->window->window);
212 }
213
214 /*
215  * Refresh the window in response to a server-side request.
216  */
217 void refresh_window(void *frontend)
218 {
219     struct gui_data *inst = (struct gui_data *)frontend;
220     term_invalidate(inst->term);
221 }
222
223 /*
224  * Maximise or restore the window in response to a server-side
225  * request.
226  */
227 void set_zoomed(void *frontend, int zoomed)
228 {
229     /*
230      * GTK 1.2 doesn't know how to do this.
231      */
232 #if GTK_CHECK_VERSION(2,0,0)
233     struct gui_data *inst = (struct gui_data *)frontend;
234     if (iconic)
235         gtk_window_maximize(GTK_WINDOW(inst->window));
236     else
237         gtk_window_unmaximize(GTK_WINDOW(inst->window));
238 #endif
239 }
240
241 /*
242  * Report whether the window is iconic, for terminal reports.
243  */
244 int is_iconic(void *frontend)
245 {
246     struct gui_data *inst = (struct gui_data *)frontend;
247     return !gdk_window_is_viewable(inst->window->window);
248 }
249
250 /*
251  * Report the window's position, for terminal reports.
252  */
253 void get_window_pos(void *frontend, int *x, int *y)
254 {
255     struct gui_data *inst = (struct gui_data *)frontend;
256     /*
257      * I assume that when the GTK version of this call is available
258      * we should use it. Not sure how it differs from the GDK one,
259      * though.
260      */
261 #if GTK_CHECK_VERSION(2,0,0)
262     gtk_window_get_position(GTK_WINDOW(inst->window), x, y);
263 #else
264     gdk_window_get_position(inst->window->window, x, y);
265 #endif
266 }
267
268 /*
269  * Report the window's pixel size, for terminal reports.
270  */
271 void get_window_pixels(void *frontend, int *x, int *y)
272 {
273     struct gui_data *inst = (struct gui_data *)frontend;
274     /*
275      * I assume that when the GTK version of this call is available
276      * we should use it. Not sure how it differs from the GDK one,
277      * though.
278      */
279 #if GTK_CHECK_VERSION(2,0,0)
280     gtk_window_get_size(GTK_WINDOW(inst->window), x, y);
281 #else
282     gdk_window_get_size(inst->window->window, x, y);
283 #endif
284 }
285
286 /*
287  * Return the window or icon title.
288  */
289 char *get_window_title(void *frontend, int icon)
290 {
291     struct gui_data *inst = (struct gui_data *)frontend;
292     return icon ? inst->wintitle : inst->icontitle;
293 }
294
295 gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
296 {
297     /*
298      * We could implement warn-on-close here if we really wanted
299      * to.
300      */
301     return FALSE;
302 }
303
304 static void show_mouseptr(struct gui_data *inst, int show)
305 {
306     if (!cfg.hide_mouseptr)
307         show = 1;
308     if (show)
309         gdk_window_set_cursor(inst->area->window, inst->currcursor);
310     else
311         gdk_window_set_cursor(inst->area->window, inst->blankcursor);
312     inst->mouseptr_visible = show;
313 }
314
315 gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
316 {
317     struct gui_data *inst = (struct gui_data *)data;
318     int w, h, need_size = 0;
319
320     w = (event->width - 2*cfg.window_border) / inst->font_width;
321     h = (event->height - 2*cfg.window_border) / inst->font_height;
322
323     if (w != cfg.width || h != cfg.height) {
324         if (inst->pixmap) {
325             gdk_pixmap_unref(inst->pixmap);
326             inst->pixmap = NULL;
327         }
328         cfg.width = w;
329         cfg.height = h;
330         need_size = 1;
331     }
332     if (!inst->pixmap) {
333         GdkGC *gc;
334
335         inst->pixmap = gdk_pixmap_new(widget->window,
336                                       (cfg.width * inst->font_width +
337                                        2*cfg.window_border),
338                                       (cfg.height * inst->font_height +
339                                        2*cfg.window_border), -1);
340
341         gc = gdk_gc_new(inst->area->window);
342         gdk_gc_set_foreground(gc, &inst->cols[18]);   /* default background */
343         gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
344                            cfg.width * inst->font_width + 2*cfg.window_border,
345                            cfg.height * inst->font_height + 2*cfg.window_border);
346         gdk_gc_unref(gc);
347     }
348
349     if (need_size) {
350         term_size(inst->term, h, w, cfg.savelines);
351     }
352
353     return TRUE;
354 }
355
356 gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
357 {
358     struct gui_data *inst = (struct gui_data *)data;
359
360     /*
361      * Pass the exposed rectangle to terminal.c, which will call us
362      * back to do the actual painting.
363      */
364     if (inst->pixmap) {
365         gdk_draw_pixmap(widget->window,
366                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
367                         inst->pixmap,
368                         event->area.x, event->area.y,
369                         event->area.x, event->area.y,
370                         event->area.width, event->area.height);
371     }
372     return TRUE;
373 }
374
375 #define KEY_PRESSED(k) \
376     (inst->keystate[(k) / 32] & (1 << ((k) % 32)))
377
378 gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
379 {
380     struct gui_data *inst = (struct gui_data *)data;
381     char output[32];
382     int start, end;
383
384     /* By default, nothing is generated. */
385     end = start = 0;
386
387     /*
388      * If Alt is being released after typing an Alt+numberpad
389      * sequence, we should generate the code that was typed.
390      * 
391      * Note that we only do this if more than one key was actually
392      * pressed - I don't think Alt+NumPad4 should be ^D or that
393      * Alt+NumPad3 should be ^C, for example. There's no serious
394      * inconvenience in having to type a zero before a single-digit
395      * character code.
396      */
397     if (event->type == GDK_KEY_RELEASE &&
398         (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
399          event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) &&
400         inst->alt_keycode >= 0 && inst->alt_digits > 1) {
401 #ifdef KEY_DEBUGGING
402         printf("Alt key up, keycode = %d\n", inst->alt_keycode);
403 #endif
404         output[0] = inst->alt_keycode;
405         end = 1;
406         goto done;
407     }
408
409     if (event->type == GDK_KEY_PRESS) {
410 #ifdef KEY_DEBUGGING
411         {
412             int i;
413             printf("keypress: keyval = %04x, state = %08x; string =",
414                    event->keyval, event->state);
415             for (i = 0; event->string[i]; i++)
416                 printf(" %02x", (unsigned char) event->string[i]);
417             printf("\n");
418         }
419 #endif
420
421         /*
422          * NYI: Compose key (!!! requires Unicode faff before even trying)
423          */
424
425         /*
426          * If Alt has just been pressed, we start potentially
427          * accumulating an Alt+numberpad code. We do this by
428          * setting alt_keycode to -1 (nothing yet but plausible).
429          */
430         if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
431              event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R)) {
432             inst->alt_keycode = -1;
433             inst->alt_digits = 0;
434             goto done;                 /* this generates nothing else */
435         }
436
437         /*
438          * If we're seeing a numberpad key press with Mod1 down,
439          * consider adding it to alt_keycode if that's sensible.
440          * Anything _else_ with Mod1 down cancels any possibility
441          * of an ALT keycode: we set alt_keycode to -2.
442          */
443         if ((event->state & GDK_MOD1_MASK) && inst->alt_keycode != -2) {
444             int digit = -1;
445             switch (event->keyval) {
446               case GDK_KP_0: case GDK_KP_Insert: digit = 0; break;
447               case GDK_KP_1: case GDK_KP_End: digit = 1; break;
448               case GDK_KP_2: case GDK_KP_Down: digit = 2; break;
449               case GDK_KP_3: case GDK_KP_Page_Down: digit = 3; break;
450               case GDK_KP_4: case GDK_KP_Left: digit = 4; break;
451               case GDK_KP_5: case GDK_KP_Begin: digit = 5; break;
452               case GDK_KP_6: case GDK_KP_Right: digit = 6; break;
453               case GDK_KP_7: case GDK_KP_Home: digit = 7; break;
454               case GDK_KP_8: case GDK_KP_Up: digit = 8; break;
455               case GDK_KP_9: case GDK_KP_Page_Up: digit = 9; break;
456             }
457             if (digit < 0)
458                 inst->alt_keycode = -2;   /* it's invalid */
459             else {
460 #ifdef KEY_DEBUGGING
461                 printf("Adding digit %d to keycode %d", digit,
462                        inst->alt_keycode);
463 #endif
464                 if (inst->alt_keycode == -1)
465                     inst->alt_keycode = digit;   /* one-digit code */
466                 else
467                     inst->alt_keycode = inst->alt_keycode * 10 + digit;
468                 inst->alt_digits++;
469 #ifdef KEY_DEBUGGING
470                 printf(" gives new code %d\n", inst->alt_keycode);
471 #endif
472                 /* Having used this digit, we now do nothing more with it. */
473                 goto done;
474             }
475         }
476
477         /*
478          * Shift-PgUp and Shift-PgDn don't even generate keystrokes
479          * at all.
480          */
481         if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {
482             term_scroll(inst->term, 0, -cfg.height/2);
483             return TRUE;
484         }
485         if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {
486             term_scroll(inst->term, 0, +cfg.height/2);
487             return TRUE;
488         }
489
490         /*
491          * Neither does Shift-Ins.
492          */
493         if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) {
494             request_paste(inst);
495             return TRUE;
496         }
497
498         /* ALT+things gives leading Escape. */
499         output[0] = '\033';
500         strncpy(output+1, event->string, 31);
501         output[31] = '\0';
502         end = strlen(output);
503         if (event->state & GDK_MOD1_MASK) {
504             start = 0;
505             if (end == 1) end = 0;
506         } else
507             start = 1;
508
509         /* Control-` is the same as Control-\ (unless gtk has a better idea) */
510         if (!event->string[0] && event->keyval == '`' &&
511             (event->state & GDK_CONTROL_MASK)) {
512             output[1] = '\x1C';
513             end = 2;
514         }
515
516         /* Control-Break is the same as Control-C */
517         if (event->keyval == GDK_Break &&
518             (event->state & GDK_CONTROL_MASK)) {
519             output[1] = '\003';
520             end = 2;
521         }
522
523         /* Control-2, Control-Space and Control-@ are NUL */
524         if (!event->string[0] &&
525             (event->keyval == ' ' || event->keyval == '2' ||
526              event->keyval == '@') &&
527             (event->state & (GDK_SHIFT_MASK |
528                              GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {
529             output[1] = '\0';
530             end = 2;
531         }
532
533         /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
534         if (!event->string[0] && event->keyval == ' ' &&
535             (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==
536             (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
537             output[1] = '\240';
538             end = 2;
539         }
540
541         /* We don't let GTK tell us what Backspace is! We know better. */
542         if (event->keyval == GDK_BackSpace &&
543             !(event->state & GDK_SHIFT_MASK)) {
544             output[1] = cfg.bksp_is_delete ? '\x7F' : '\x08';
545             end = 2;
546         }
547         /* For Shift Backspace, do opposite of what is configured. */
548         if (event->keyval == GDK_BackSpace &&
549             (event->state & GDK_SHIFT_MASK)) {
550             output[1] = cfg.bksp_is_delete ? '\x08' : '\x7F';
551             end = 2;
552         }
553
554         /* Shift-Tab is ESC [ Z */
555         if (event->keyval == GDK_ISO_Left_Tab ||
556             (event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {
557             end = 1 + sprintf(output+1, "\033[Z");
558         }
559
560         /*
561          * NetHack keypad mode.
562          */
563         if (cfg.nethack_keypad) {
564             char *keys = NULL;
565             switch (event->keyval) {
566               case GDK_KP_1: case GDK_KP_End: keys = "bB"; break;
567               case GDK_KP_2: case GDK_KP_Down: keys = "jJ"; break;
568               case GDK_KP_3: case GDK_KP_Page_Down: keys = "nN"; break;
569               case GDK_KP_4: case GDK_KP_Left: keys = "hH"; break;
570               case GDK_KP_5: case GDK_KP_Begin: keys = ".."; break;
571               case GDK_KP_6: case GDK_KP_Right: keys = "lL"; break;
572               case GDK_KP_7: case GDK_KP_Home: keys = "yY"; break;
573               case GDK_KP_8: case GDK_KP_Up: keys = "kK"; break;
574               case GDK_KP_9: case GDK_KP_Page_Up: keys = "uU"; break;
575             }
576             if (keys) {
577                 end = 2;
578                 if (event->state & GDK_SHIFT_MASK)
579                     output[1] = keys[1];
580                 else
581                     output[1] = keys[0];
582                 goto done;
583             }
584         }
585
586         /*
587          * Application keypad mode.
588          */
589         if (inst->term->app_keypad_keys && !cfg.no_applic_k) {
590             int xkey = 0;
591             switch (event->keyval) {
592               case GDK_Num_Lock: xkey = 'P'; break;
593               case GDK_KP_Divide: xkey = 'Q'; break;
594               case GDK_KP_Multiply: xkey = 'R'; break;
595               case GDK_KP_Subtract: xkey = 'S'; break;
596                 /*
597                  * Keypad + is tricky. It covers a space that would
598                  * be taken up on the VT100 by _two_ keys; so we
599                  * let Shift select between the two. Worse still,
600                  * in xterm function key mode we change which two...
601                  */
602               case GDK_KP_Add:
603                 if (cfg.funky_type == 2) {
604                     if (event->state & GDK_SHIFT_MASK)
605                         xkey = 'l';
606                     else
607                         xkey = 'k';
608                 } else if (event->state & GDK_SHIFT_MASK)
609                         xkey = 'm';
610                 else
611                     xkey = 'l';
612                 break;
613               case GDK_KP_Enter: xkey = 'M'; break;
614               case GDK_KP_0: case GDK_KP_Insert: xkey = 'p'; break;
615               case GDK_KP_1: case GDK_KP_End: xkey = 'q'; break;
616               case GDK_KP_2: case GDK_KP_Down: xkey = 'r'; break;
617               case GDK_KP_3: case GDK_KP_Page_Down: xkey = 's'; break;
618               case GDK_KP_4: case GDK_KP_Left: xkey = 't'; break;
619               case GDK_KP_5: case GDK_KP_Begin: xkey = 'u'; break;
620               case GDK_KP_6: case GDK_KP_Right: xkey = 'v'; break;
621               case GDK_KP_7: case GDK_KP_Home: xkey = 'w'; break;
622               case GDK_KP_8: case GDK_KP_Up: xkey = 'x'; break;
623               case GDK_KP_9: case GDK_KP_Page_Up: xkey = 'y'; break;
624               case GDK_KP_Decimal: case GDK_KP_Delete: xkey = 'n'; break;
625             }
626             if (xkey) {
627                 if (inst->term->vt52_mode) {
628                     if (xkey >= 'P' && xkey <= 'S')
629                         end = 1 + sprintf(output+1, "\033%c", xkey);
630                     else
631                         end = 1 + sprintf(output+1, "\033?%c", xkey);
632                 } else
633                     end = 1 + sprintf(output+1, "\033O%c", xkey);
634                 goto done;
635             }
636         }
637
638         /*
639          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
640          * for integer decimal nn.)
641          *
642          * We also deal with the weird ones here. Linux VCs replace F1
643          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
644          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
645          * respectively.
646          */
647         {
648             int code = 0;
649             switch (event->keyval) {
650               case GDK_F1:
651                 code = (event->state & GDK_SHIFT_MASK ? 23 : 11);
652                 break;
653               case GDK_F2:
654                 code = (event->state & GDK_SHIFT_MASK ? 24 : 12);
655                 break;
656               case GDK_F3:
657                 code = (event->state & GDK_SHIFT_MASK ? 25 : 13);
658                 break;
659               case GDK_F4:
660                 code = (event->state & GDK_SHIFT_MASK ? 26 : 14);
661                 break;
662               case GDK_F5:
663                 code = (event->state & GDK_SHIFT_MASK ? 28 : 15);
664                 break;
665               case GDK_F6:
666                 code = (event->state & GDK_SHIFT_MASK ? 29 : 17);
667                 break;
668               case GDK_F7:
669                 code = (event->state & GDK_SHIFT_MASK ? 31 : 18);
670                 break;
671               case GDK_F8:
672                 code = (event->state & GDK_SHIFT_MASK ? 32 : 19);
673                 break;
674               case GDK_F9:
675                 code = (event->state & GDK_SHIFT_MASK ? 33 : 20);
676                 break;
677               case GDK_F10:
678                 code = (event->state & GDK_SHIFT_MASK ? 34 : 21);
679                 break;
680               case GDK_F11:
681                 code = 23;
682                 break;
683               case GDK_F12:
684                 code = 24;
685                 break;
686               case GDK_F13:
687                 code = 25;
688                 break;
689               case GDK_F14:
690                 code = 26;
691                 break;
692               case GDK_F15:
693                 code = 28;
694                 break;
695               case GDK_F16:
696                 code = 29;
697                 break;
698               case GDK_F17:
699                 code = 31;
700                 break;
701               case GDK_F18:
702                 code = 32;
703                 break;
704               case GDK_F19:
705                 code = 33;
706                 break;
707               case GDK_F20:
708                 code = 34;
709                 break;
710             }
711             if (!(event->state & GDK_CONTROL_MASK)) switch (event->keyval) {
712               case GDK_Home: case GDK_KP_Home:
713                 code = 1;
714                 break;
715               case GDK_Insert: case GDK_KP_Insert:
716                 code = 2;
717                 break;
718               case GDK_Delete: case GDK_KP_Delete:
719                 code = 3;
720                 break;
721               case GDK_End: case GDK_KP_End:
722                 code = 4;
723                 break;
724               case GDK_Page_Up: case GDK_KP_Page_Up:
725                 code = 5;
726                 break;
727               case GDK_Page_Down: case GDK_KP_Page_Down:
728                 code = 6;
729                 break;
730             }
731             /* Reorder edit keys to physical order */
732             if (cfg.funky_type == 3 && code <= 6)
733                 code = "\0\2\1\4\5\3\6"[code];
734
735             if (inst->term->vt52_mode && code > 0 && code <= 6) {
736                 end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);
737                 goto done;
738             }
739
740             if (cfg.funky_type == 5 &&     /* SCO function keys */
741                 code >= 11 && code <= 34) {
742                 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
743                 int index = 0;
744                 switch (event->keyval) {
745                   case GDK_F1: index = 0; break;
746                   case GDK_F2: index = 1; break;
747                   case GDK_F3: index = 2; break;
748                   case GDK_F4: index = 3; break;
749                   case GDK_F5: index = 4; break;
750                   case GDK_F6: index = 5; break;
751                   case GDK_F7: index = 6; break;
752                   case GDK_F8: index = 7; break;
753                   case GDK_F9: index = 8; break;
754                   case GDK_F10: index = 9; break;
755                   case GDK_F11: index = 10; break;
756                   case GDK_F12: index = 11; break;
757                 }
758                 if (event->state & GDK_SHIFT_MASK) index += 12;
759                 if (event->state & GDK_CONTROL_MASK) index += 24;
760                 end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);
761                 goto done;
762             }
763             if (cfg.funky_type == 5 &&     /* SCO small keypad */
764                 code >= 1 && code <= 6) {
765                 char codes[] = "HL.FIG";
766                 if (code == 3) {
767                     output[1] = '\x7F';
768                     end = 2;
769                 } else {
770                     end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);
771                 }
772                 goto done;
773             }
774             if ((inst->term->vt52_mode || cfg.funky_type == 4) &&
775                 code >= 11 && code <= 24) {
776                 int offt = 0;
777                 if (code > 15)
778                     offt++;
779                 if (code > 21)
780                     offt++;
781                 if (inst->term->vt52_mode)
782                     end = 1 + sprintf(output+1,
783                                       "\x1B%c", code + 'P' - 11 - offt);
784                 else
785                     end = 1 + sprintf(output+1,
786                                       "\x1BO%c", code + 'P' - 11 - offt);
787                 goto done;
788             }
789             if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
790                 end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
791                 goto done;
792             }
793             if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
794                 if (inst->term->vt52_mode)
795                     end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
796                 else
797                     end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);
798                 goto done;
799             }
800             if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
801                 end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
802                 goto done;
803             }
804             if (code) {
805                 end = 1 + sprintf(output+1, "\x1B[%d~", code);
806                 goto done;
807             }
808         }
809
810         /*
811          * Cursor keys. (This includes the numberpad cursor keys,
812          * if we haven't already done them due to app keypad mode.)
813          * 
814          * Here we also process un-numlocked un-appkeypadded KP5,
815          * which sends ESC [ G.
816          */
817         {
818             int xkey = 0;
819             switch (event->keyval) {
820               case GDK_Up: case GDK_KP_Up: xkey = 'A'; break;
821               case GDK_Down: case GDK_KP_Down: xkey = 'B'; break;
822               case GDK_Right: case GDK_KP_Right: xkey = 'C'; break;
823               case GDK_Left: case GDK_KP_Left: xkey = 'D'; break;
824               case GDK_Begin: case GDK_KP_Begin: xkey = 'G'; break;
825             }
826             if (xkey) {
827                 /*
828                  * The arrow keys normally do ESC [ A and so on. In
829                  * app cursor keys mode they do ESC O A instead.
830                  * Ctrl toggles the two modes.
831                  */
832                 if (inst->term->vt52_mode) {
833                     end = 1 + sprintf(output+1, "\033%c", xkey);
834                 } else if (!inst->term->app_cursor_keys ^
835                            !(event->state & GDK_CONTROL_MASK)) {
836                     end = 1 + sprintf(output+1, "\033O%c", xkey);
837                 } else {                    
838                     end = 1 + sprintf(output+1, "\033[%c", xkey);
839                 }
840                 goto done;
841             }
842         }
843         goto done;
844     }
845
846     done:
847
848     if (end-start > 0) {
849 #ifdef KEY_DEBUGGING
850         int i;
851         printf("generating sequence:");
852         for (i = start; i < end; i++)
853             printf(" %02x", (unsigned char) output[i]);
854         printf("\n");
855 #endif
856
857         if (!inst->direct_to_font) {
858             /*
859              * The stuff we've just generated is assumed to be
860              * ISO-8859-1! This sounds insane, but `man
861              * XLookupString' agrees: strings of this type returned
862              * from the X server are hardcoded to 8859-1. Strictly
863              * speaking we should be doing this using some sort of
864              * GtkIMContext, which (if we're lucky) would give us
865              * our data directly in Unicode; but that's not
866              * supported in GTK 1.2 as far as I can tell, and it's
867              * poorly documented even in 2.0, so it'll have to
868              * wait.
869              */
870             lpage_send(inst->ldisc, CS_ISO8859_1, output+start, end-start, 1);
871         } else {
872             /*
873              * In direct-to-font mode, we just send the string
874              * exactly as we received it.
875              */
876             ldisc_send(inst->ldisc, output+start, end-start, 1);
877         }
878
879         show_mouseptr(inst, 0);
880         term_seen_key_event(inst->term);
881         term_out(inst->term);
882     }
883
884     return TRUE;
885 }
886
887 gint button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
888 {
889     struct gui_data *inst = (struct gui_data *)data;
890     int shift, ctrl, alt, x, y, button, act;
891
892     show_mouseptr(inst, 1);
893
894     if (event->button == 4 && event->type == GDK_BUTTON_PRESS) {
895         term_scroll(inst->term, 0, -5);
896         return TRUE;
897     }
898     if (event->button == 5 && event->type == GDK_BUTTON_PRESS) {
899         term_scroll(inst->term, 0, +5);
900         return TRUE;
901     }
902
903     shift = event->state & GDK_SHIFT_MASK;
904     ctrl = event->state & GDK_CONTROL_MASK;
905     alt = event->state & GDK_MOD1_MASK;
906     if (event->button == 1)
907         button = MBT_LEFT;
908     else if (event->button == 2)
909         button = MBT_MIDDLE;
910     else if (event->button == 3)
911         button = MBT_RIGHT;
912     else
913         return FALSE;                  /* don't even know what button! */
914
915     switch (event->type) {
916       case GDK_BUTTON_PRESS: act = MA_CLICK; break;
917       case GDK_BUTTON_RELEASE: act = MA_RELEASE; break;
918       case GDK_2BUTTON_PRESS: act = MA_2CLK; break;
919       case GDK_3BUTTON_PRESS: act = MA_3CLK; break;
920       default: return FALSE;           /* don't know this event type */
921     }
922
923     if (send_raw_mouse && !(cfg.mouse_override && shift) &&
924         act != MA_CLICK && act != MA_RELEASE)
925         return TRUE;                   /* we ignore these in raw mouse mode */
926
927     x = (event->x - cfg.window_border) / inst->font_width;
928     y = (event->y - cfg.window_border) / inst->font_height;
929
930     term_mouse(inst->term, button, act, x, y, shift, ctrl, alt);
931
932     return TRUE;
933 }
934
935 gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
936 {
937     struct gui_data *inst = (struct gui_data *)data;
938     int shift, ctrl, alt, x, y, button;
939
940     show_mouseptr(inst, 1);
941
942     shift = event->state & GDK_SHIFT_MASK;
943     ctrl = event->state & GDK_CONTROL_MASK;
944     alt = event->state & GDK_MOD1_MASK;
945     if (event->state & GDK_BUTTON1_MASK)
946         button = MBT_LEFT;
947     else if (event->state & GDK_BUTTON2_MASK)
948         button = MBT_MIDDLE;
949     else if (event->state & GDK_BUTTON3_MASK)
950         button = MBT_RIGHT;
951     else
952         return FALSE;                  /* don't even know what button! */
953
954     x = (event->x - cfg.window_border) / inst->font_width;
955     y = (event->y - cfg.window_border) / inst->font_height;
956
957     term_mouse(inst->term, button, MA_DRAG, x, y, shift, ctrl, alt);
958
959     return TRUE;
960 }
961
962 void done_with_pty(struct gui_data *inst)
963 {
964     extern void pty_close(void);
965
966     if (inst->master_fd >= 0) {
967         pty_close();
968         inst->master_fd = -1;
969         gtk_input_remove(inst->master_func_id);
970     }
971
972     if (!inst->exited && inst->back->exitcode(inst->backhandle) >= 0) {
973         int exitcode = inst->back->exitcode(inst->backhandle);
974         int clean;
975
976         clean = WIFEXITED(exitcode) && (WEXITSTATUS(exitcode) == 0);
977
978         /*
979          * Terminate now, if the Close On Exit setting is
980          * appropriate.
981          */
982         if (cfg.close_on_exit == COE_ALWAYS ||
983             (cfg.close_on_exit == COE_NORMAL && clean))
984             exit(0);
985
986         /*
987          * Otherwise, output an indication that the session has
988          * closed.
989          */
990         {
991             char message[512];
992             if (WIFEXITED(exitcode))
993                 sprintf(message, "\r\n[pterm: process terminated with exit"
994                         " code %d]\r\n", WEXITSTATUS(exitcode));
995             else if (WIFSIGNALED(exitcode))
996 #ifdef HAVE_NO_STRSIGNAL
997                 sprintf(message, "\r\n[pterm: process terminated on signal"
998                         " %d]\r\n", WTERMSIG(exitcode));
999 #else
1000                 sprintf(message, "\r\n[pterm: process terminated on signal"
1001                         " %d (%.400s)]\r\n", WTERMSIG(exitcode),
1002                         strsignal(WTERMSIG(exitcode)));
1003 #endif
1004             from_backend((void *)inst->term, 0, message, strlen(message));
1005         }
1006         inst->exited = 1;
1007     }
1008 }
1009
1010 void frontend_keypress(void *handle)
1011 {
1012     struct gui_data *inst = (struct gui_data *)handle;
1013
1014     /*
1015      * If our child process has exited but not closed, terminate on
1016      * any keypress.
1017      */
1018     if (inst->exited)
1019         exit(0);
1020 }
1021
1022 gint timer_func(gpointer data)
1023 {
1024     struct gui_data *inst = (struct gui_data *)data;
1025
1026     if (inst->back->exitcode(inst->backhandle) >= 0) {
1027         /*
1028          * The primary child process died. We could keep the
1029          * terminal open for remaining subprocesses to output to,
1030          * but conventional wisdom seems to feel that that's the
1031          * Wrong Thing for an xterm-alike, so we bail out now
1032          * (though we don't necessarily _close_ the window,
1033          * depending on the state of Close On Exit). This would be
1034          * easy enough to change or make configurable if necessary.
1035          */
1036         done_with_pty(inst);
1037     }
1038
1039     term_update(inst->term);
1040     term_blink(inst->term, 0);
1041     return TRUE;
1042 }
1043
1044 void pty_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
1045 {
1046     struct gui_data *inst = (struct gui_data *)data;
1047     char buf[4096];
1048     int ret;
1049
1050     ret = read(sourcefd, buf, sizeof(buf));
1051
1052     /*
1053      * Clean termination condition is that either ret == 0, or ret
1054      * < 0 and errno == EIO. Not sure why the latter, but it seems
1055      * to happen. Boo.
1056      */
1057     if (ret == 0 || (ret < 0 && errno == EIO)) {
1058         done_with_pty(inst);
1059     } else if (ret < 0) {
1060         perror("read pty master");
1061         exit(1);
1062     } else if (ret > 0)
1063         from_backend(inst->term, 0, buf, ret);
1064     term_blink(inst->term, 1);
1065     term_out(inst->term);
1066 }
1067
1068 void destroy(GtkWidget *widget, gpointer data)
1069 {
1070     gtk_main_quit();
1071 }
1072
1073 gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1074 {
1075     struct gui_data *inst = (struct gui_data *)data;
1076     inst->term->has_focus = event->in;
1077     term_out(inst->term);
1078     term_update(inst->term);
1079     show_mouseptr(inst, 1);
1080     return FALSE;
1081 }
1082
1083 /*
1084  * set or clear the "raw mouse message" mode
1085  */
1086 void set_raw_mouse_mode(void *frontend, int activate)
1087 {
1088     struct gui_data *inst = (struct gui_data *)frontend;
1089     activate = activate && !cfg.no_mouse_rep;
1090     send_raw_mouse = activate;
1091     if (send_raw_mouse)
1092         inst->currcursor = inst->rawcursor;
1093     else
1094         inst->currcursor = inst->textcursor;
1095     show_mouseptr(inst, inst->mouseptr_visible);
1096 }
1097
1098 void request_resize(void *frontend, int w, int h)
1099 {
1100     struct gui_data *inst = (struct gui_data *)frontend;
1101     int large_x, large_y;
1102     int offset_x, offset_y;
1103     int area_x, area_y;
1104     GtkRequisition inner, outer;
1105
1106     /*
1107      * This is a heinous hack dreamed up by the gnome-terminal
1108      * people to get around a limitation in gtk. The problem is
1109      * that in order to set the size correctly we really need to be
1110      * calling gtk_window_resize - but that needs to know the size
1111      * of the _whole window_, not the drawing area. So what we do
1112      * is to set an artificially huge size request on the drawing
1113      * area, recompute the resulting size request on the window,
1114      * and look at the difference between the two. That gives us
1115      * the x and y offsets we need to translate drawing area size
1116      * into window size for real, and then we call
1117      * gtk_window_resize.
1118      */
1119
1120     /*
1121      * We start by retrieving the current size of the whole window.
1122      * Adding a bit to _that_ will give us a value we can use as a
1123      * bogus size request which guarantees to be bigger than the
1124      * current size of the drawing area.
1125      */
1126     get_window_pixels(inst, &large_x, &large_y);
1127     large_x += 32;
1128     large_y += 32;
1129
1130 #if GTK_CHECK_VERSION(2,0,0)
1131     gtk_widget_set_size_request(inst->area, large_x, large_y);
1132 #else
1133     gtk_widget_set_usize(inst->area, large_x, large_y);
1134 #endif
1135     gtk_widget_size_request(inst->area, &inner);
1136     gtk_widget_size_request(inst->window, &outer);
1137
1138     offset_x = outer.width - inner.width;
1139     offset_y = outer.height - inner.height;
1140
1141     area_x = inst->font_width * w + 2*cfg.window_border;
1142     area_y = inst->font_height * h + 2*cfg.window_border;
1143
1144     /*
1145      * Now we must set the size request on the drawing area back to
1146      * something sensible before we commit the real resize. Best
1147      * way to do this, I think, is to set it to what the size is
1148      * really going to end up being.
1149      */
1150 #if GTK_CHECK_VERSION(2,0,0)
1151     gtk_widget_set_size_request(inst->area, area_x, area_y);
1152 #else
1153     gtk_widget_set_usize(inst->area, area_x, area_y);
1154 #endif
1155
1156 #if GTK_CHECK_VERSION(2,0,0)
1157     gtk_window_resize(GTK_WINDOW(inst->window),
1158                       area_x + offset_x, area_y + offset_y);
1159 #else
1160     gdk_window_resize(inst->window->window,
1161                       area_x + offset_x, area_y + offset_y);
1162 #endif
1163 }
1164
1165 static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)
1166 {
1167     gboolean success[1];
1168
1169     inst->cols[n].red = r * 0x0101;
1170     inst->cols[n].green = g * 0x0101;
1171     inst->cols[n].blue = b * 0x0101;
1172
1173     gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,
1174                               FALSE, FALSE, success);
1175     if (!success[0])
1176         g_error("pterm: couldn't allocate colour %d (#%02x%02x%02x)\n",
1177                 n, r, g, b);
1178 }
1179
1180 void set_window_background(struct gui_data *inst)
1181 {
1182     if (inst->area && inst->area->window)
1183         gdk_window_set_background(inst->area->window, &inst->cols[18]);
1184     if (inst->window && inst->window->window)
1185         gdk_window_set_background(inst->window->window, &inst->cols[18]);
1186 }
1187
1188 void palette_set(void *frontend, int n, int r, int g, int b)
1189 {
1190     struct gui_data *inst = (struct gui_data *)frontend;
1191     static const int first[21] = {
1192         0, 2, 4, 6, 8, 10, 12, 14,
1193         1, 3, 5, 7, 9, 11, 13, 15,
1194         16, 17, 18, 20, 22
1195     };
1196     real_palette_set(inst, first[n], r, g, b);
1197     if (first[n] >= 18)
1198         real_palette_set(inst, first[n] + 1, r, g, b);
1199     if (first[n] == 18)
1200         set_window_background(inst);
1201 }
1202
1203 void palette_reset(void *frontend)
1204 {
1205     struct gui_data *inst = (struct gui_data *)frontend;
1206     /* This maps colour indices in cfg to those used in inst->cols. */
1207     static const int ww[] = {
1208         6, 7, 8, 9, 10, 11, 12, 13,
1209         14, 15, 16, 17, 18, 19, 20, 21,
1210         0, 1, 2, 3, 4, 5
1211     };
1212     gboolean success[NCOLOURS];
1213     int i;
1214
1215     assert(lenof(ww) == NCOLOURS);
1216
1217     if (!inst->colmap) {
1218         inst->colmap = gdk_colormap_get_system();
1219     } else {
1220         gdk_colormap_free_colors(inst->colmap, inst->cols, NCOLOURS);
1221     }
1222
1223     for (i = 0; i < NCOLOURS; i++) {
1224         inst->cols[i].red = cfg.colours[ww[i]][0] * 0x0101;
1225         inst->cols[i].green = cfg.colours[ww[i]][1] * 0x0101;
1226         inst->cols[i].blue = cfg.colours[ww[i]][2] * 0x0101;
1227     }
1228
1229     gdk_colormap_alloc_colors(inst->colmap, inst->cols, NCOLOURS,
1230                               FALSE, FALSE, success);
1231     for (i = 0; i < NCOLOURS; i++) {
1232         if (!success[i])
1233             g_error("pterm: couldn't allocate colour %d (#%02x%02x%02x)\n",
1234                     i, cfg.colours[i][0], cfg.colours[i][1], cfg.colours[i][2]);
1235     }
1236
1237     set_window_background(inst);
1238 }
1239
1240 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
1241 {
1242     struct gui_data *inst = (struct gui_data *)frontend;
1243     if (inst->pasteout_data)
1244         sfree(inst->pasteout_data);
1245     if (inst->pasteout_data_utf8)
1246         sfree(inst->pasteout_data_utf8);
1247
1248     /*
1249      * Set up UTF-8 paste data. This only happens if we aren't in
1250      * direct-to-font mode using the D800 hack.
1251      */
1252     if (!inst->direct_to_font) {
1253         wchar_t *tmp = data;
1254         int tmplen = len;
1255
1256         inst->pasteout_data_utf8 = smalloc(len*6);
1257         inst->pasteout_data_utf8_len = len*6;
1258         inst->pasteout_data_utf8_len =
1259             charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,
1260                                  inst->pasteout_data_utf8_len,
1261                                  CS_UTF8, NULL, NULL, 0);
1262         if (inst->pasteout_data_utf8_len == 0) {
1263             sfree(inst->pasteout_data_utf8);
1264             inst->pasteout_data_utf8 = NULL;
1265         } else {
1266             inst->pasteout_data_utf8 =
1267                 srealloc(inst->pasteout_data_utf8,
1268                          inst->pasteout_data_utf8_len);
1269         }
1270     } else {
1271         inst->pasteout_data_utf8 = NULL;
1272         inst->pasteout_data_utf8_len = 0;
1273     }
1274
1275     inst->pasteout_data = smalloc(len*6);
1276     inst->pasteout_data_len = len*6;
1277     inst->pasteout_data_len = wc_to_mb(line_codepage, 0, data, len,
1278                                        inst->pasteout_data,
1279                                        inst->pasteout_data_len, NULL, NULL);
1280     if (inst->pasteout_data_len == 0) {
1281         sfree(inst->pasteout_data);
1282         inst->pasteout_data = NULL;
1283     } else {
1284         inst->pasteout_data =
1285             srealloc(inst->pasteout_data, inst->pasteout_data_len);
1286     }
1287
1288     if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
1289                                 GDK_CURRENT_TIME)) {
1290         gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
1291                                  GDK_SELECTION_TYPE_STRING, 1);
1292         gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
1293                                  inst->compound_text_atom, 1);
1294         if (inst->pasteout_data_utf8)
1295             gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
1296                                      inst->utf8_string_atom, 1);
1297     }
1298 }
1299
1300 void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
1301                    guint info, guint time_stamp, gpointer data)
1302 {
1303     struct gui_data *inst = (struct gui_data *)data;
1304     if (seldata->target == inst->utf8_string_atom)
1305         gtk_selection_data_set(seldata, seldata->target, 8,
1306                                inst->pasteout_data_utf8,
1307                                inst->pasteout_data_utf8_len);
1308     else
1309         gtk_selection_data_set(seldata, seldata->target, 8,
1310                                inst->pasteout_data, inst->pasteout_data_len);
1311 }
1312
1313 gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
1314                      gpointer data)
1315 {
1316     struct gui_data *inst = (struct gui_data *)data;
1317     term_deselect(inst->term);
1318     if (inst->pasteout_data)
1319         sfree(inst->pasteout_data);
1320     if (inst->pasteout_data_utf8)
1321         sfree(inst->pasteout_data_utf8);
1322     inst->pasteout_data = NULL;
1323     inst->pasteout_data_len = 0;
1324     inst->pasteout_data_utf8 = NULL;
1325     inst->pasteout_data_utf8_len = 0;
1326     return TRUE;
1327 }
1328
1329 void request_paste(void *frontend)
1330 {
1331     struct gui_data *inst = (struct gui_data *)frontend;
1332     /*
1333      * In Unix, pasting is asynchronous: all we can do at the
1334      * moment is to call gtk_selection_convert(), and when the data
1335      * comes back _then_ we can call term_do_paste().
1336      */
1337
1338     if (!inst->direct_to_font) {
1339         /*
1340          * First we attempt to retrieve the selection as a UTF-8
1341          * string (which we will convert to the correct code page
1342          * before sending to the session, of course). If that
1343          * fails, selection_received() will be informed and will
1344          * fall back to an ordinary string.
1345          */
1346         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
1347                               inst->utf8_string_atom, GDK_CURRENT_TIME);
1348     } else {
1349         /*
1350          * If we're in direct-to-font mode, we disable UTF-8
1351          * pasting, and go straight to ordinary string data.
1352          */
1353         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
1354                               GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
1355     }
1356 }
1357
1358 gint idle_paste_func(gpointer data);   /* forward ref */
1359
1360 void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
1361                         guint time, gpointer data)
1362 {
1363     struct gui_data *inst = (struct gui_data *)data;
1364
1365     if (seldata->target == inst->utf8_string_atom && seldata->length <= 0) {
1366         /*
1367          * Failed to get a UTF-8 selection string. Try an ordinary
1368          * string.
1369          */
1370         gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
1371                               GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
1372         return;
1373     }
1374
1375     /*
1376      * Any other failure should just go foom.
1377      */
1378     if (seldata->length <= 0 ||
1379         (seldata->type != GDK_SELECTION_TYPE_STRING &&
1380          seldata->type != inst->utf8_string_atom))
1381         return;                        /* Nothing happens. */
1382
1383     if (inst->pastein_data)
1384         sfree(inst->pastein_data);
1385
1386     inst->pastein_data = smalloc(seldata->length * sizeof(wchar_t));
1387     inst->pastein_data_len = seldata->length;
1388     inst->pastein_data_len =
1389         mb_to_wc((seldata->type == inst->utf8_string_atom ?
1390                   CS_UTF8 : line_codepage),
1391                  0, seldata->data, seldata->length,
1392                  inst->pastein_data, inst->pastein_data_len);
1393
1394     term_do_paste(inst->term);
1395
1396     if (term_paste_pending(inst->term))
1397         inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);
1398 }
1399
1400 gint idle_paste_func(gpointer data)
1401 {
1402     struct gui_data *inst = (struct gui_data *)data;
1403
1404     if (term_paste_pending(inst->term))
1405         term_paste(inst->term);
1406     else
1407         gtk_idle_remove(inst->term_paste_idle_id);
1408
1409     return TRUE;
1410 }
1411
1412
1413 void get_clip(void *frontend, wchar_t ** p, int *len)
1414 {
1415     struct gui_data *inst = (struct gui_data *)frontend;
1416
1417     if (p) {
1418         *p = inst->pastein_data;
1419         *len = inst->pastein_data_len;
1420     }
1421 }
1422
1423 void set_title(void *frontend, char *title)
1424 {
1425     struct gui_data *inst = (struct gui_data *)frontend;
1426     strncpy(inst->wintitle, title, lenof(inst->wintitle));
1427     inst->wintitle[lenof(inst->wintitle)-1] = '\0';
1428     gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
1429 }
1430
1431 void set_icon(void *frontend, char *title)
1432 {
1433     struct gui_data *inst = (struct gui_data *)frontend;
1434     strncpy(inst->icontitle, title, lenof(inst->icontitle));
1435     inst->icontitle[lenof(inst->icontitle)-1] = '\0';
1436     gdk_window_set_icon_name(inst->window->window, inst->icontitle);
1437 }
1438
1439 void set_sbar(void *frontend, int total, int start, int page)
1440 {
1441     struct gui_data *inst = (struct gui_data *)frontend;
1442     if (!cfg.scrollbar)
1443         return;
1444     inst->sbar_adjust->lower = 0;
1445     inst->sbar_adjust->upper = total;
1446     inst->sbar_adjust->value = start;
1447     inst->sbar_adjust->page_size = page;
1448     inst->sbar_adjust->step_increment = 1;
1449     inst->sbar_adjust->page_increment = page/2;
1450     inst->ignore_sbar = TRUE;
1451     gtk_adjustment_changed(inst->sbar_adjust);
1452     inst->ignore_sbar = FALSE;
1453 }
1454
1455 void scrollbar_moved(GtkAdjustment *adj, gpointer data)
1456 {
1457     struct gui_data *inst = (struct gui_data *)data;
1458
1459     if (!cfg.scrollbar)
1460         return;
1461     if (!inst->ignore_sbar)
1462         term_scroll(inst->term, 1, (int)adj->value);
1463 }
1464
1465 void sys_cursor(void *frontend, int x, int y)
1466 {
1467     /*
1468      * This is meaningless under X.
1469      */
1470 }
1471
1472 /*
1473  * This is still called when mode==BELL_VISUAL, even though the
1474  * visual bell is handled entirely within terminal.c, because we
1475  * may want to perform additional actions on any kind of bell (for
1476  * example, taskbar flashing in Windows).
1477  */
1478 void beep(void *frontend, int mode)
1479 {
1480     if (mode != BELL_VISUAL)
1481         gdk_beep();
1482 }
1483
1484 int char_width(Context ctx, int uc)
1485 {
1486     /*
1487      * Under X, any fixed-width font really _is_ fixed-width.
1488      * Double-width characters will be dealt with using a separate
1489      * font. For the moment we can simply return 1.
1490      */
1491     return 1;
1492 }
1493
1494 Context get_ctx(void *frontend)
1495 {
1496     struct gui_data *inst = (struct gui_data *)frontend;
1497     struct draw_ctx *dctx;
1498
1499     if (!inst->area->window)
1500         return NULL;
1501
1502     dctx = smalloc(sizeof(*dctx));
1503     dctx->inst = inst;
1504     dctx->gc = gdk_gc_new(inst->area->window);
1505     return dctx;
1506 }
1507
1508 void free_ctx(Context ctx)
1509 {
1510     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1511     /* struct gui_data *inst = dctx->inst; */
1512     GdkGC *gc = dctx->gc;
1513     gdk_gc_unref(gc);
1514     sfree(dctx);
1515 }
1516
1517 /*
1518  * Draw a line of text in the window, at given character
1519  * coordinates, in given attributes.
1520  *
1521  * We are allowed to fiddle with the contents of `text'.
1522  */
1523 void do_text_internal(Context ctx, int x, int y, char *text, int len,
1524                       unsigned long attr, int lattr)
1525 {
1526     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1527     struct gui_data *inst = dctx->inst;
1528     GdkGC *gc = dctx->gc;
1529
1530     int nfg, nbg, t, fontid, shadow, rlen, widefactor;
1531
1532     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1533     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1534     if (attr & ATTR_REVERSE) {
1535         t = nfg;
1536         nfg = nbg;
1537         nbg = t;
1538     }
1539     if (cfg.bold_colour && (attr & ATTR_BOLD))
1540         nfg++;
1541     if (cfg.bold_colour && (attr & ATTR_BLINK))
1542         nbg++;
1543     if (attr & TATTR_ACTCURS) {
1544         nfg = NCOLOURS-2;
1545         nbg = NCOLOURS-1;
1546     }
1547
1548     fontid = shadow = 0;
1549
1550     if (attr & ATTR_WIDE) {
1551         widefactor = 2;
1552         fontid |= 2;
1553     } else {
1554         widefactor = 1;
1555     }
1556
1557     if ((attr & ATTR_BOLD) && !cfg.bold_colour) {
1558         if (inst->fonts[fontid | 1])
1559             fontid |= 1;
1560         else
1561             shadow = 1;
1562     }
1563
1564     if (lattr != LATTR_NORM) {
1565         x *= 2;
1566         if (x >= inst->term->cols)
1567             return;
1568         if (x + len*2*widefactor > inst->term->cols)
1569             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
1570         rlen = len * 2;
1571     } else
1572         rlen = len;
1573
1574     {
1575         GdkRectangle r;
1576
1577         r.x = x*inst->font_width+cfg.window_border;
1578         r.y = y*inst->font_height+cfg.window_border;
1579         r.width = rlen*widefactor*inst->font_width;
1580         r.height = inst->font_height;
1581         gdk_gc_set_clip_rectangle(gc, &r);
1582     }
1583
1584     gdk_gc_set_foreground(gc, &inst->cols[nbg]);
1585     gdk_draw_rectangle(inst->pixmap, gc, 1,
1586                        x*inst->font_width+cfg.window_border,
1587                        y*inst->font_height+cfg.window_border,
1588                        rlen*widefactor*inst->font_width, inst->font_height);
1589
1590     gdk_gc_set_foreground(gc, &inst->cols[nfg]);
1591     {
1592         GdkWChar *gwcs;
1593         gchar *gcs;
1594         wchar_t *wcs;
1595         int i;
1596
1597         wcs = smalloc(sizeof(wchar_t) * (len+1));
1598         for (i = 0; i < len; i++) {
1599             wcs[i] = (wchar_t) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
1600         }
1601
1602         if (inst->fonts[fontid] == NULL) {
1603             /*
1604              * The font for this contingency does not exist.
1605              * Typically this means we've been given ATTR_WIDE
1606              * character and have no wide font. So we display
1607              * nothing at all; such is life.
1608              */
1609         } else if (inst->fontinfo[fontid].is_wide) {
1610             /*
1611              * At least one version of gdk_draw_text_wc() has a
1612              * weird bug whereby it reads `len' elements of the
1613              * input string, but only draws `len/2'. Hence I'm
1614              * going to make its input array twice as long as it
1615              * theoretically needs to be, and pass in twice the
1616              * actual number of characters. If a fixed gdk actually
1617              * takes the doubled length seriously, then (a) the
1618              * array will stand scrutiny up to the full length, (b)
1619              * the spare elements of the array are full of zeroes
1620              * which will probably be an empty glyph in the font,
1621              * and (c) the clip rectangle should prevent it causing
1622              * trouble anyway.
1623              */
1624             gwcs = smalloc(sizeof(GdkWChar) * (len*2+1));
1625             memset(gwcs, 0, sizeof(GdkWChar) * (len*2+1));
1626             /*
1627              * FIXME: when we have a wide-char equivalent of
1628              * from_unicode, use it instead of this.
1629              */
1630             for (i = 0; i <= len; i++)
1631                 gwcs[i] = wcs[i];
1632             gdk_draw_text_wc(inst->pixmap, inst->fonts[fontid], gc,
1633                              x*inst->font_width+cfg.window_border,
1634                              y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
1635                              gwcs, len*2);
1636             sfree(gwcs);
1637         } else {
1638             gcs = smalloc(sizeof(GdkWChar) * (len+1));
1639             wc_to_mb(inst->fontinfo[fontid].charset, 0,
1640                      wcs, len, gcs, len, ".", NULL);
1641             gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
1642                           x*inst->font_width+cfg.window_border,
1643                           y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
1644                           gcs, len);
1645             sfree(gcs);
1646         }
1647         sfree(wcs);
1648     }
1649
1650     if (shadow) {
1651         gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
1652                       x*inst->font_width+cfg.window_border + cfg.shadowboldoffset,
1653                       y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
1654                       text, len);
1655     }
1656
1657     if (attr & ATTR_UNDER) {
1658         int uheight = inst->fonts[0]->ascent + 1;
1659         if (uheight >= inst->font_height)
1660             uheight = inst->font_height - 1;
1661         gdk_draw_line(inst->pixmap, gc, x*inst->font_width+cfg.window_border,
1662                       y*inst->font_height + uheight + cfg.window_border,
1663                       (x+len)*widefactor*inst->font_width-1+cfg.window_border,
1664                       y*inst->font_height + uheight + cfg.window_border);
1665     }
1666
1667     if (lattr != LATTR_NORM) {
1668         /*
1669          * I can't find any plausible StretchBlt equivalent in the
1670          * X server, so I'm going to do this the slow and painful
1671          * way. This will involve repeated calls to
1672          * gdk_draw_pixmap() to stretch the text horizontally. It's
1673          * O(N^2) in time and O(N) in network bandwidth, but you
1674          * try thinking of a better way. :-(
1675          */
1676         int i;
1677         for (i = 0; i < len * widefactor * inst->font_width; i++) {
1678             gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
1679                             x*inst->font_width+cfg.window_border + 2*i,
1680                             y*inst->font_height+cfg.window_border,
1681                             x*inst->font_width+cfg.window_border + 2*i+1,
1682                             y*inst->font_height+cfg.window_border,
1683                             len * inst->font_width - i, inst->font_height);
1684         }
1685         len *= 2;
1686         if (lattr != LATTR_WIDE) {
1687             int dt, db;
1688             /* Now stretch vertically, in the same way. */
1689             if (lattr == LATTR_BOT)
1690                 dt = 0, db = 1;
1691             else
1692                 dt = 1, db = 0;
1693             for (i = 0; i < inst->font_height; i+=2) {
1694                 gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
1695                                 x*inst->font_width+cfg.window_border,
1696                                 y*inst->font_height+cfg.window_border+dt*i+db,
1697                                 x*widefactor*inst->font_width+cfg.window_border,
1698                                 y*inst->font_height+cfg.window_border+dt*(i+1),
1699                                 len * inst->font_width, inst->font_height-i-1);
1700             }
1701         }
1702     }
1703 }
1704
1705 void do_text(Context ctx, int x, int y, char *text, int len,
1706              unsigned long attr, int lattr)
1707 {
1708     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1709     struct gui_data *inst = dctx->inst;
1710     GdkGC *gc = dctx->gc;
1711     int widefactor;
1712
1713     do_text_internal(ctx, x, y, text, len, attr, lattr);
1714
1715     if (attr & ATTR_WIDE) {
1716         widefactor = 2;
1717     } else {
1718         widefactor = 1;
1719     }
1720
1721     if (lattr != LATTR_NORM) {
1722         x *= 2;
1723         if (x >= inst->term->cols)
1724             return;
1725         if (x + len*2*widefactor > inst->term->cols)
1726             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
1727         len *= 2;
1728     }
1729
1730     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
1731                     x*inst->font_width+cfg.window_border,
1732                     y*inst->font_height+cfg.window_border,
1733                     x*inst->font_width+cfg.window_border,
1734                     y*inst->font_height+cfg.window_border,
1735                     len*widefactor*inst->font_width, inst->font_height);
1736 }
1737
1738 void do_cursor(Context ctx, int x, int y, char *text, int len,
1739                unsigned long attr, int lattr)
1740 {
1741     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
1742     struct gui_data *inst = dctx->inst;
1743     GdkGC *gc = dctx->gc;
1744
1745     int passive, widefactor;
1746
1747     if (attr & TATTR_PASCURS) {
1748         attr &= ~TATTR_PASCURS;
1749         passive = 1;
1750     } else
1751         passive = 0;
1752     if ((attr & TATTR_ACTCURS) && cfg.cursor_type != 0) {
1753         attr &= ~TATTR_ACTCURS;
1754     }
1755     do_text_internal(ctx, x, y, text, len, attr, lattr);
1756
1757     if (attr & ATTR_WIDE) {
1758         widefactor = 2;
1759     } else {
1760         widefactor = 1;
1761     }
1762
1763     if (lattr != LATTR_NORM) {
1764         x *= 2;
1765         if (x >= inst->term->cols)
1766             return;
1767         if (x + len*2*widefactor > inst->term->cols)
1768             len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
1769         len *= 2;
1770     }
1771
1772     if (cfg.cursor_type == 0) {
1773         /*
1774          * An active block cursor will already have been done by
1775          * the above do_text call, so we only need to do anything
1776          * if it's passive.
1777          */
1778         if (passive) {
1779             gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]);
1780             gdk_draw_rectangle(inst->pixmap, gc, 0,
1781                                x*inst->font_width+cfg.window_border,
1782                                y*inst->font_height+cfg.window_border,
1783                                len*inst->font_width-1, inst->font_height-1);
1784         }
1785     } else {
1786         int uheight;
1787         int startx, starty, dx, dy, length, i;
1788
1789         int char_width;
1790
1791         if ((attr & ATTR_WIDE) || lattr != LATTR_NORM)
1792             char_width = 2*inst->font_width;
1793         else
1794             char_width = inst->font_width;
1795
1796         if (cfg.cursor_type == 1) {
1797             uheight = inst->fonts[0]->ascent + 1;
1798             if (uheight >= inst->font_height)
1799                 uheight = inst->font_height - 1;
1800
1801             startx = x * inst->font_width + cfg.window_border;
1802             starty = y * inst->font_height + cfg.window_border + uheight;
1803             dx = 1;
1804             dy = 0;
1805             length = len * char_width;
1806         } else {
1807             int xadjust = 0;
1808             if (attr & TATTR_RIGHTCURS)
1809                 xadjust = char_width - 1;
1810             startx = x * inst->font_width + cfg.window_border + xadjust;
1811             starty = y * inst->font_height + cfg.window_border;
1812             dx = 0;
1813             dy = 1;
1814             length = inst->font_height;
1815         }
1816
1817         gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]);
1818         if (passive) {
1819             for (i = 0; i < length; i++) {
1820                 if (i % 2 == 0) {
1821                     gdk_draw_point(inst->pixmap, gc, startx, starty);
1822                 }
1823                 startx += dx;
1824                 starty += dy;
1825             }
1826         } else {
1827             gdk_draw_line(inst->pixmap, gc, startx, starty,
1828                           startx + (length-1) * dx, starty + (length-1) * dy);
1829         }
1830     }
1831
1832     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
1833                     x*inst->font_width+cfg.window_border,
1834                     y*inst->font_height+cfg.window_border,
1835                     x*inst->font_width+cfg.window_border,
1836                     y*inst->font_height+cfg.window_border,
1837                     len*widefactor*inst->font_width, inst->font_height);
1838 }
1839
1840 GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
1841 {
1842     /*
1843      * Truly hideous hack: GTK doesn't allow us to set the mouse
1844      * cursor foreground and background colours unless we've _also_
1845      * created our own cursor from bitmaps. Therefore, I need to
1846      * load the `cursor' font and draw glyphs from it on to
1847      * pixmaps, in order to construct my cursors with the fg and bg
1848      * I want. This is a gross hack, but it's more self-contained
1849      * than linking in Xlib to find the X window handle to
1850      * inst->area and calling XRecolorCursor, and it's more
1851      * futureproof than hard-coding the shapes as bitmap arrays.
1852      */
1853     static GdkFont *cursor_font = NULL;
1854     GdkPixmap *source, *mask;
1855     GdkGC *gc;
1856     GdkColor cfg = { 0, 65535, 65535, 65535 };
1857     GdkColor cbg = { 0, 0, 0, 0 };
1858     GdkColor dfg = { 1, 65535, 65535, 65535 };
1859     GdkColor dbg = { 0, 0, 0, 0 };
1860     GdkCursor *ret;
1861     gchar text[2];
1862     gint lb, rb, wid, asc, desc, w, h, x, y;
1863
1864     if (cursor_val == -2) {
1865         gdk_font_unref(cursor_font);
1866         return NULL;
1867     }
1868
1869     if (cursor_val >= 0 && !cursor_font)
1870         cursor_font = gdk_font_load("cursor");
1871
1872     /*
1873      * Get the text extent of the cursor in question. We use the
1874      * mask character for this, because it's typically slightly
1875      * bigger than the main character.
1876      */
1877     if (cursor_val >= 0) {
1878         text[1] = '\0';
1879         text[0] = (char)cursor_val + 1;
1880         gdk_string_extents(cursor_font, text, &lb, &rb, &wid, &asc, &desc);
1881         w = rb-lb; h = asc+desc; x = -lb; y = asc;
1882     } else {
1883         w = h = 1;
1884         x = y = 0;
1885     }
1886
1887     source = gdk_pixmap_new(NULL, w, h, 1);
1888     mask = gdk_pixmap_new(NULL, w, h, 1);
1889
1890     /*
1891      * Draw the mask character on the mask pixmap.
1892      */
1893     gc = gdk_gc_new(mask);
1894     gdk_gc_set_foreground(gc, &dbg);
1895     gdk_draw_rectangle(mask, gc, 1, 0, 0, w, h);
1896     if (cursor_val >= 0) {
1897         text[1] = '\0';
1898         text[0] = (char)cursor_val + 1;
1899         gdk_gc_set_foreground(gc, &dfg);
1900         gdk_draw_text(mask, cursor_font, gc, x, y, text, 1);
1901     }
1902     gdk_gc_unref(gc);
1903
1904     /*
1905      * Draw the main character on the source pixmap.
1906      */
1907     gc = gdk_gc_new(source);
1908     gdk_gc_set_foreground(gc, &dbg);
1909     gdk_draw_rectangle(source, gc, 1, 0, 0, w, h);
1910     if (cursor_val >= 0) {
1911         text[1] = '\0';
1912         text[0] = (char)cursor_val;
1913         gdk_gc_set_foreground(gc, &dfg);
1914         gdk_draw_text(source, cursor_font, gc, x, y, text, 1);
1915     }
1916     gdk_gc_unref(gc);
1917
1918     /*
1919      * Create the cursor.
1920      */
1921     ret = gdk_cursor_new_from_pixmap(source, mask, &cfg, &cbg, x, y);
1922
1923     /*
1924      * Clean up.
1925      */
1926     gdk_pixmap_unref(source);
1927     gdk_pixmap_unref(mask);
1928
1929     return ret;
1930 }
1931
1932 void modalfatalbox(char *p, ...)
1933 {
1934     va_list ap;
1935     fprintf(stderr, "FATAL ERROR: ");
1936     va_start(ap, p);
1937     vfprintf(stderr, p, ap);
1938     va_end(ap);
1939     fputc('\n', stderr);
1940     exit(1);
1941 }
1942
1943 char *get_x_display(void *frontend)
1944 {
1945     return gdk_get_display();
1946 }
1947
1948 static void help(FILE *fp) {
1949     if(fprintf(fp,
1950 "pterm option summary:\n"
1951 "\n"
1952 "  --display DISPLAY         Specify X display to use (note '--')\n"
1953 "  -name PREFIX              Prefix when looking up resources (default: pterm)\n"
1954 "  -fn FONT                  Normal text font\n"
1955 "  -fb FONT                  Bold text font\n"
1956 "  -geometry WIDTHxHEIGHT    Size of terminal in characters\n"
1957 "  -sl LINES                 Number of lines of scrollback\n"
1958 "  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"
1959 "  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"
1960 "  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"
1961 "  -T TITLE                  Window title\n"
1962 "  -ut, +ut                  Do(default) or do not update utmp\n"
1963 "  -ls, +ls                  Do(default) or do not make shell a login shell\n"
1964 "  -sb, +sb                  Do(default) or do not display a scrollbar\n"
1965 "  -log PATH                 Log all output to a file\n"
1966 "  -nethack                  Map numeric keypad to hjklyubn direction keys\n"
1967 "  -xrm RESOURCE-STRING      Set an X resource\n"
1968 "  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"
1969          ) < 0 || fflush(fp) < 0) {
1970         perror("output error");
1971         exit(1);
1972     }
1973 }
1974
1975 int do_cmdline(int argc, char **argv, int do_everything)
1976 {
1977     int err = 0;
1978     extern char **pty_argv;            /* declared in pty.c */
1979
1980     /*
1981      * Macros to make argument handling easier. Note that because
1982      * they need to call `continue', they cannot be contained in
1983      * the usual do {...} while (0) wrapper to make them
1984      * syntactically single statements; hence it is not legal to
1985      * use one of these macros as an unbraced statement between
1986      * `if' and `else'.
1987      */
1988 #define EXPECTS_ARG { \
1989     if (--argc <= 0) { \
1990         err = 1; \
1991         fprintf(stderr, "pterm: %s expects an argument\n", p); \
1992         continue; \
1993     } else \
1994         val = *++argv; \
1995 }
1996 #define SECOND_PASS_ONLY { if (!do_everything) continue; }
1997
1998     /*
1999      * TODO:
2000      * 
2001      * finish -geometry
2002      */
2003
2004     char *val;
2005     while (--argc > 0) {
2006         char *p = *++argv;
2007         if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
2008             EXPECTS_ARG;
2009             SECOND_PASS_ONLY;
2010             strncpy(cfg.font, val, sizeof(cfg.font));
2011             cfg.font[sizeof(cfg.font)-1] = '\0';
2012
2013         } else if (!strcmp(p, "-fb")) {
2014             EXPECTS_ARG;
2015             SECOND_PASS_ONLY;
2016             strncpy(cfg.boldfont, val, sizeof(cfg.boldfont));
2017             cfg.boldfont[sizeof(cfg.boldfont)-1] = '\0';
2018
2019         } else if (!strcmp(p, "-fw")) {
2020             EXPECTS_ARG;
2021             SECOND_PASS_ONLY;
2022             strncpy(cfg.widefont, val, sizeof(cfg.widefont));
2023             cfg.widefont[sizeof(cfg.widefont)-1] = '\0';
2024
2025         } else if (!strcmp(p, "-fwb")) {
2026             EXPECTS_ARG;
2027             SECOND_PASS_ONLY;
2028             strncpy(cfg.wideboldfont, val, sizeof(cfg.wideboldfont));
2029             cfg.wideboldfont[sizeof(cfg.wideboldfont)-1] = '\0';
2030
2031         } else if (!strcmp(p, "-cs")) {
2032             EXPECTS_ARG;
2033             SECOND_PASS_ONLY;
2034             strncpy(cfg.line_codepage, val, sizeof(cfg.line_codepage));
2035             cfg.line_codepage[sizeof(cfg.line_codepage)-1] = '\0';
2036
2037         } else if (!strcmp(p, "-geometry")) {
2038             int flags, x, y, w, h;
2039             EXPECTS_ARG;
2040             SECOND_PASS_ONLY;
2041
2042             flags = XParseGeometry(val, &x, &y, &w, &h);
2043             if (flags & WidthValue)
2044                 cfg.width = w;
2045             if (flags & HeightValue)
2046                 cfg.height = h;
2047
2048             /*
2049              * Apparently setting the initial window position is
2050              * difficult in GTK 1.2. Not entirely sure why this
2051              * should be. 2.0 has gtk_window_parse_geometry(),
2052              * which would help... For the moment, though, I can't
2053              * be bothered with this.
2054              */
2055
2056         } else if (!strcmp(p, "-sl")) {
2057             EXPECTS_ARG;
2058             SECOND_PASS_ONLY;
2059             cfg.savelines = atoi(val);
2060
2061         } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
2062                    !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
2063                    !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
2064             GdkColor col;
2065
2066             EXPECTS_ARG;
2067             SECOND_PASS_ONLY;
2068             if (!gdk_color_parse(val, &col)) {
2069                 err = 1;
2070                 fprintf(stderr, "pterm: unable to parse colour \"%s\"\n", val);
2071             } else {
2072                 int index;
2073                 index = (!strcmp(p, "-fg") ? 0 :
2074                          !strcmp(p, "-bg") ? 2 :
2075                          !strcmp(p, "-bfg") ? 1 :
2076                          !strcmp(p, "-bbg") ? 3 :
2077                          !strcmp(p, "-cfg") ? 4 :
2078                          !strcmp(p, "-cbg") ? 5 : -1);
2079                 assert(index != -1);
2080                 cfg.colours[index][0] = col.red / 256;
2081                 cfg.colours[index][1] = col.green / 256;
2082                 cfg.colours[index][2] = col.blue / 256;
2083             }
2084
2085         } else if (!strcmp(p, "-e")) {
2086             /* This option swallows all further arguments. */
2087             if (!do_everything)
2088                 break;
2089
2090             if (--argc > 0) {
2091                 int i;
2092                 pty_argv = smalloc((argc+1) * sizeof(char *));
2093                 ++argv;
2094                 for (i = 0; i < argc; i++)
2095                     pty_argv[i] = argv[i];
2096                 pty_argv[argc] = NULL;
2097                 break;                 /* finished command-line processing */
2098             } else
2099                 err = 1, fprintf(stderr, "pterm: -e expects an argument\n");
2100
2101         } else if (!strcmp(p, "-T")) {
2102             EXPECTS_ARG;
2103             SECOND_PASS_ONLY;
2104             strncpy(cfg.wintitle, val, sizeof(cfg.wintitle));
2105             cfg.wintitle[sizeof(cfg.wintitle)-1] = '\0';
2106
2107         } else if (!strcmp(p, "-log")) {
2108             EXPECTS_ARG;
2109             SECOND_PASS_ONLY;
2110             strncpy(cfg.logfilename, val, sizeof(cfg.logfilename));
2111             cfg.logfilename[sizeof(cfg.logfilename)-1] = '\0';
2112             cfg.logtype = LGTYP_DEBUG;
2113
2114         } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
2115             SECOND_PASS_ONLY;
2116             cfg.stamp_utmp = 0;
2117
2118         } else if (!strcmp(p, "-ut")) {
2119             SECOND_PASS_ONLY;
2120             cfg.stamp_utmp = 1;
2121
2122         } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
2123             SECOND_PASS_ONLY;
2124             cfg.login_shell = 0;
2125
2126         } else if (!strcmp(p, "-ls")) {
2127             SECOND_PASS_ONLY;
2128             cfg.login_shell = 1;
2129
2130         } else if (!strcmp(p, "-nethack")) {
2131             SECOND_PASS_ONLY;
2132             cfg.nethack_keypad = 1;
2133
2134         } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
2135             SECOND_PASS_ONLY;
2136             cfg.scrollbar = 0;
2137
2138         } else if (!strcmp(p, "-sb")) {
2139             SECOND_PASS_ONLY;
2140             cfg.scrollbar = 0;
2141
2142         } else if (!strcmp(p, "-name")) {
2143             EXPECTS_ARG;
2144             app_name = val;
2145
2146         } else if (!strcmp(p, "-xrm")) {
2147             EXPECTS_ARG;
2148             provide_xrm_string(val);
2149
2150         } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
2151             help(stdout);
2152             exit(0);
2153             
2154         } else {
2155             err = 1;
2156             fprintf(stderr, "pterm: unrecognized option '%s'\n", p);
2157         }
2158     }
2159
2160     return err;
2161 }
2162
2163 static void block_signal(int sig, int block_it) {
2164   sigset_t ss;
2165
2166   sigemptyset(&ss);
2167   sigaddset(&ss, sig);
2168   if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) {
2169     perror("sigprocmask");
2170     exit(1);
2171   }
2172 }
2173
2174 /*
2175  * This function retrieves the character set encoding of a font. It
2176  * returns the character set without the X11 hack (in case the user
2177  * asks to use the font's own encoding).
2178  */
2179 static int set_font_info(struct gui_data *inst, int fontid)
2180 {
2181     GdkFont *font = inst->fonts[fontid];
2182     XFontStruct *xfs = GDK_FONT_XFONT(font);
2183     Display *disp = GDK_FONT_XDISPLAY(font);
2184     Atom charset_registry, charset_encoding;
2185     unsigned long registry_ret, encoding_ret;
2186     int retval = CS_NONE;
2187
2188     charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);
2189     charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);
2190     inst->fontinfo[fontid].charset = CS_NONE;
2191     inst->fontinfo[fontid].is_wide = 0;
2192     if (XGetFontProperty(xfs, charset_registry, &registry_ret) &&
2193         XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
2194         char *reg, *enc;
2195         reg = XGetAtomName(disp, (Atom)registry_ret);
2196         enc = XGetAtomName(disp, (Atom)encoding_ret);
2197         if (reg && enc) {
2198             char *encoding = dupcat(reg, "-", enc, NULL);
2199             retval = inst->fontinfo[fontid].charset =
2200                 charset_from_xenc(encoding);
2201             /* FIXME: when libcharset supports wide encodings fix this. */
2202             if (!strcasecmp(encoding, "iso10646-1")) {
2203                 inst->fontinfo[fontid].is_wide = 1;
2204                 retval = CS_UTF8;
2205             }
2206
2207             /*
2208              * Hack for X line-drawing characters: if the primary
2209              * font is encoded as ISO-8859-anything, and has valid
2210              * glyphs in the first 32 char positions, it is assumed
2211              * that those glyphs are the VT100 line-drawing
2212              * character set.
2213              * 
2214              * Actually, we'll hack even harder by only checking
2215              * position 0x19 (vertical line, VT100 linedrawing
2216              * `x'). Then we can check it easily by seeing if the
2217              * ascent and descent differ.
2218              */
2219             if (inst->fontinfo[fontid].charset == CS_ISO8859_1) {
2220                 int lb, rb, wid, asc, desc;
2221                 gchar text[2];
2222
2223                 text[1] = '\0';
2224                 text[0] = '\x12';
2225                 gdk_string_extents(inst->fonts[fontid], text,
2226                                    &lb, &rb, &wid, &asc, &desc);
2227                 if (asc != desc)
2228                     inst->fontinfo[fontid].charset = CS_ISO8859_1_X11;
2229             }
2230
2231             sfree(encoding);
2232         }
2233     }
2234
2235     return retval;
2236 }
2237
2238 int main(int argc, char **argv)
2239 {
2240     extern int pty_master_fd;          /* declared in pty.c */
2241     extern void pty_pre_init(void);    /* declared in pty.c */
2242     struct gui_data *inst;
2243     int font_charset;
2244
2245     /* defer any child exit handling until we're ready to deal with
2246      * it */
2247     block_signal(SIGCHLD, 1);
2248
2249     pty_pre_init();
2250
2251     gtk_init(&argc, &argv);
2252
2253     if (do_cmdline(argc, argv, 0))     /* pre-defaults pass to get -class */
2254         exit(1);
2255     do_defaults(NULL, &cfg);
2256     if (do_cmdline(argc, argv, 1))     /* post-defaults, do everything */
2257         exit(1);
2258
2259     /*
2260      * Create an instance structure and initialise to zeroes
2261      */
2262     inst = smalloc(sizeof(*inst));
2263     memset(inst, 0, sizeof(*inst));
2264     inst->alt_keycode = -1;            /* this one needs _not_ to be zero */
2265
2266     inst->fonts[0] = gdk_font_load(cfg.font);
2267     if (!inst->fonts[0]) {
2268         fprintf(stderr, "pterm: unable to load font \"%s\"\n", cfg.font);
2269         exit(1);
2270     }
2271     font_charset = set_font_info(inst, 0);
2272     if (cfg.boldfont[0]) {
2273         inst->fonts[1] = gdk_font_load(cfg.boldfont);
2274         if (!inst->fonts[1]) {
2275             fprintf(stderr, "pterm: unable to load bold font \"%s\"\n",
2276                     cfg.boldfont);
2277             exit(1);
2278         }
2279         set_font_info(inst, 1);
2280     } else
2281         inst->fonts[1] = NULL;
2282     if (cfg.widefont[0]) {
2283         inst->fonts[2] = gdk_font_load(cfg.widefont);
2284         if (!inst->fonts[2]) {
2285             fprintf(stderr, "pterm: unable to load wide font \"%s\"\n",
2286                     cfg.boldfont);
2287             exit(1);
2288         }
2289         set_font_info(inst, 2);
2290     } else
2291         inst->fonts[2] = NULL;
2292     if (cfg.wideboldfont[0]) {
2293         inst->fonts[3] = gdk_font_load(cfg.wideboldfont);
2294         if (!inst->fonts[3]) {
2295             fprintf(stderr, "pterm: unable to load wide/bold font \"%s\"\n",
2296                     cfg.boldfont);
2297             exit(1);
2298         }
2299         set_font_info(inst, 3);
2300     } else
2301         inst->fonts[3] = NULL;
2302
2303     inst->font_width = gdk_char_width(inst->fonts[0], ' ');
2304     inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
2305
2306     inst->compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
2307     inst->utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
2308
2309     inst->direct_to_font = init_ucs(cfg.line_codepage, font_charset);
2310
2311     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2312
2313     if (cfg.wintitle[0])
2314         set_title(inst, cfg.wintitle);
2315     else
2316         set_title(inst, "pterm");
2317
2318     /*
2319      * Set up the colour map.
2320      */
2321     palette_reset(inst);
2322
2323     inst->area = gtk_drawing_area_new();
2324     gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),
2325                           inst->font_width * cfg.width + 2*cfg.window_border,
2326                           inst->font_height * cfg.height + 2*cfg.window_border);
2327     if (cfg.scrollbar) {
2328         inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));
2329         inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);
2330     }
2331     inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
2332     if (cfg.scrollbar) {
2333         if (cfg.scrollbar_on_left)
2334             gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
2335         else
2336             gtk_box_pack_end(inst->hbox, inst->sbar, FALSE, FALSE, 0);
2337     }
2338     gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);
2339
2340     gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));
2341
2342     {
2343         GdkGeometry geom;
2344         geom.min_width = inst->font_width + 2*cfg.window_border;
2345         geom.min_height = inst->font_height + 2*cfg.window_border;
2346         geom.max_width = geom.max_height = -1;
2347         geom.base_width = 2*cfg.window_border;
2348         geom.base_height = 2*cfg.window_border;
2349         geom.width_inc = inst->font_width;
2350         geom.height_inc = inst->font_height;
2351         geom.min_aspect = geom.max_aspect = 0;
2352         gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,
2353                                       GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
2354                                       GDK_HINT_RESIZE_INC);
2355     }
2356
2357     gtk_signal_connect(GTK_OBJECT(inst->window), "destroy",
2358                        GTK_SIGNAL_FUNC(destroy), inst);
2359     gtk_signal_connect(GTK_OBJECT(inst->window), "delete_event",
2360                        GTK_SIGNAL_FUNC(delete_window), inst);
2361     gtk_signal_connect(GTK_OBJECT(inst->window), "key_press_event",
2362                        GTK_SIGNAL_FUNC(key_event), inst);
2363     gtk_signal_connect(GTK_OBJECT(inst->window), "key_release_event",
2364                        GTK_SIGNAL_FUNC(key_event), inst);
2365     gtk_signal_connect(GTK_OBJECT(inst->window), "focus_in_event",
2366                        GTK_SIGNAL_FUNC(focus_event), inst);
2367     gtk_signal_connect(GTK_OBJECT(inst->window), "focus_out_event",
2368                        GTK_SIGNAL_FUNC(focus_event), inst);
2369     gtk_signal_connect(GTK_OBJECT(inst->area), "configure_event",
2370                        GTK_SIGNAL_FUNC(configure_area), inst);
2371     gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event",
2372                        GTK_SIGNAL_FUNC(expose_area), inst);
2373     gtk_signal_connect(GTK_OBJECT(inst->area), "button_press_event",
2374                        GTK_SIGNAL_FUNC(button_event), inst);
2375     gtk_signal_connect(GTK_OBJECT(inst->area), "button_release_event",
2376                        GTK_SIGNAL_FUNC(button_event), inst);
2377     gtk_signal_connect(GTK_OBJECT(inst->area), "motion_notify_event",
2378                        GTK_SIGNAL_FUNC(motion_event), inst);
2379     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received",
2380                        GTK_SIGNAL_FUNC(selection_received), inst);
2381     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get",
2382                        GTK_SIGNAL_FUNC(selection_get), inst);
2383     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",
2384                        GTK_SIGNAL_FUNC(selection_clear), inst);
2385     if (cfg.scrollbar)
2386         gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
2387                            GTK_SIGNAL_FUNC(scrollbar_moved), inst);
2388     gtk_timeout_add(20, timer_func, inst);
2389     gtk_widget_add_events(GTK_WIDGET(inst->area),
2390                           GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
2391                           GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2392                           GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK);
2393
2394     gtk_widget_show(inst->area);
2395     if (cfg.scrollbar)
2396         gtk_widget_show(inst->sbar);
2397     gtk_widget_show(GTK_WIDGET(inst->hbox));
2398     gtk_widget_show(inst->window);
2399
2400     set_window_background(inst);
2401
2402     inst->textcursor = make_mouse_ptr(inst, GDK_XTERM);
2403     inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR);
2404     inst->blankcursor = make_mouse_ptr(inst, -1);
2405     make_mouse_ptr(inst, -2);          /* clean up cursor font */
2406     inst->currcursor = inst->textcursor;
2407     show_mouseptr(inst, 1);
2408
2409     inst->term = term_init(&cfg, inst);
2410     inst->logctx = log_init(inst, &cfg);
2411     term_provide_logctx(inst->term, inst->logctx);
2412
2413     inst->back = &pty_backend;
2414     inst->back->init((void *)inst->term, &inst->backhandle, &cfg,
2415                      NULL, 0, NULL, 0);
2416     inst->back->provide_logctx(inst->backhandle, inst->logctx);
2417
2418     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
2419
2420     term_size(inst->term, cfg.height, cfg.width, cfg.savelines);
2421
2422     inst->ldisc =
2423         ldisc_create(&cfg, inst->term, inst->back, inst->backhandle, inst);
2424     ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
2425
2426     inst->master_fd = pty_master_fd;
2427     inst->exited = FALSE;
2428     inst->master_func_id = gdk_input_add(pty_master_fd, GDK_INPUT_READ,
2429                                          pty_input_func, inst);
2430
2431     /* now we're reday to deal with the child exit handler being
2432      * called */
2433     block_signal(SIGCHLD, 0);
2434     
2435     gtk_main();
2436
2437     return 0;
2438 }