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