+ /*
+ * Hack for X line-drawing characters: if the primary
+ * font is encoded as ISO-8859-anything, and has valid
+ * glyphs in the first 32 char positions, it is assumed
+ * that those glyphs are the VT100 line-drawing
+ * character set.
+ *
+ * Actually, we'll hack even harder by only checking
+ * position 0x19 (vertical line, VT100 linedrawing
+ * `x'). Then we can check it easily by seeing if the
+ * ascent and descent differ.
+ */
+ if (inst->fontinfo[fontid].charset == CS_ISO8859_1) {
+ int lb, rb, wid, asc, desc;
+ gchar text[2];
+
+ text[1] = '\0';
+ text[0] = '\x12';
+ gdk_string_extents(inst->fonts[fontid], text,
+ &lb, &rb, &wid, &asc, &desc);
+ if (asc != desc)
+ inst->fontinfo[fontid].charset = CS_ISO8859_1_X11;
+ }
+
+ sfree(encoding);
+ }
+ }
+
+ return retval;
+}
+
+int uxsel_input_add(int fd, int rwx) {
+ int flags = 0;
+ if (rwx & 1) flags |= GDK_INPUT_READ;
+ if (rwx & 2) flags |= GDK_INPUT_WRITE;
+ if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
+ return gdk_input_add(fd, flags, fd_input_func, NULL);
+}
+
+void uxsel_input_remove(int id) {
+ gdk_input_remove(id);
+}
+
+char *guess_derived_font_name(GdkFont *font, int bold, int wide)
+{
+ XFontStruct *xfs = GDK_FONT_XFONT(font);
+ Display *disp = GDK_FONT_XDISPLAY(font);
+ Atom fontprop = XInternAtom(disp, "FONT", False);
+ unsigned long ret;
+ if (XGetFontProperty(xfs, fontprop, &ret)) {
+ char *name = XGetAtomName(disp, (Atom)ret);
+ if (name && name[0] == '-') {
+ char *strings[13];
+ char *dupname, *extrafree = NULL, *ret;
+ char *p, *q;
+ int nstr;
+
+ p = q = dupname = dupstr(name); /* skip initial minus */
+ nstr = 0;
+
+ while (*p && nstr < lenof(strings)) {
+ if (*p == '-') {
+ *p = '\0';
+ strings[nstr++] = p+1;
+ }
+ p++;
+ }
+
+ if (nstr < lenof(strings))
+ return NULL; /* XLFD was malformed */
+
+ if (bold)
+ strings[2] = "bold";
+
+ if (wide) {
+ /* 4 is `wideness', which obviously may have changed. */
+ /* 5 is additional style, which may be e.g. `ja' or `ko'. */
+ strings[4] = strings[5] = "*";
+ strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));
+ }
+
+ ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],
+ "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],
+ "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],
+ "-", strings[ 9], "-", strings[10], "-", strings[11],
+ "-", strings[12], NULL);
+ sfree(extrafree);
+ sfree(dupname);
+
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+void setup_fonts_ucs(struct gui_data *inst)
+{
+ int font_charset;
+ char *name;
+ int guessed;
+
+ if (inst->fonts[0])
+ gdk_font_unref(inst->fonts[0]);
+ if (inst->fonts[1])
+ gdk_font_unref(inst->fonts[1]);
+ if (inst->fonts[2])
+ gdk_font_unref(inst->fonts[2]);
+ if (inst->fonts[3])
+ gdk_font_unref(inst->fonts[3]);
+
+ inst->fonts[0] = gdk_font_load(inst->cfg.font.name);
+ if (!inst->fonts[0]) {
+ fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,
+ inst->cfg.font.name);
+ exit(1);
+ }
+ font_charset = set_font_info(inst, 0);
+
+ if (inst->cfg.shadowbold) {
+ inst->fonts[1] = NULL;
+ } else {
+ if (inst->cfg.boldfont.name[0]) {
+ name = inst->cfg.boldfont.name;
+ guessed = FALSE;
+ } else {
+ name = guess_derived_font_name(inst->fonts[0], TRUE, FALSE);
+ guessed = TRUE;
+ }
+ inst->fonts[1] = name ? gdk_font_load(name) : NULL;
+ if (inst->fonts[1]) {
+ set_font_info(inst, 1);
+ } else if (!guessed) {
+ fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
+ inst->cfg.boldfont.name);
+ exit(1);
+ }
+ if (guessed)
+ sfree(name);
+ }
+
+ if (inst->cfg.widefont.name[0]) {
+ name = inst->cfg.widefont.name;
+ guessed = FALSE;
+ } else {
+ name = guess_derived_font_name(inst->fonts[0], FALSE, TRUE);
+ guessed = TRUE;
+ }
+ inst->fonts[2] = name ? gdk_font_load(name) : NULL;
+ if (inst->fonts[2]) {
+ set_font_info(inst, 2);
+ } else if (!guessed) {
+ fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
+ inst->cfg.widefont.name);
+ exit(1);
+ }
+ if (guessed)
+ sfree(name);
+
+ if (inst->cfg.shadowbold) {
+ inst->fonts[3] = NULL;
+ } else {
+ if (inst->cfg.wideboldfont.name[0]) {
+ name = inst->cfg.wideboldfont.name;
+ guessed = FALSE;
+ } else {
+ /*
+ * Here we have some choices. We can widen the bold font,
+ * bolden the wide font, or widen and bolden the standard
+ * font. Try them all, in that order!
+ */
+ if (inst->cfg.widefont.name[0])
+ name = guess_derived_font_name(inst->fonts[2], TRUE, FALSE);
+ else if (inst->cfg.boldfont.name[0])
+ name = guess_derived_font_name(inst->fonts[1], FALSE, TRUE);
+ else
+ name = guess_derived_font_name(inst->fonts[0], TRUE, TRUE);
+ guessed = TRUE;
+ }
+ inst->fonts[3] = name ? gdk_font_load(name) : NULL;
+ if (inst->fonts[3]) {
+ set_font_info(inst, 3);
+ } else if (!guessed) {
+ fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n", appname,
+ inst->cfg.wideboldfont.name);
+ exit(1);
+ }
+ if (guessed)
+ sfree(name);
+ }
+
+ inst->font_width = gdk_char_width(inst->fonts[0], ' ');
+ inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
+
+ inst->direct_to_font = init_ucs(&inst->ucsdata,
+ inst->cfg.line_codepage, font_charset,
+ inst->cfg.vtmode);
+}
+
+void set_geom_hints(struct gui_data *inst)
+{
+ GdkGeometry geom;
+ geom.min_width = inst->font_width + 2*inst->cfg.window_border;
+ geom.min_height = inst->font_height + 2*inst->cfg.window_border;
+ geom.max_width = geom.max_height = -1;
+ geom.base_width = 2*inst->cfg.window_border;
+ geom.base_height = 2*inst->cfg.window_border;
+ geom.width_inc = inst->font_width;
+ geom.height_inc = inst->font_height;
+ geom.min_aspect = geom.max_aspect = 0;
+ gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,
+ GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
+ GDK_HINT_RESIZE_INC);
+}
+
+void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ term_clrsb(inst->term);
+}
+
+void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ term_pwron(inst->term);
+ ldisc_send(inst->ldisc, NULL, 0, 0);
+}
+
+void copy_all_menuitem(GtkMenuItem *item, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ term_copyall(inst->term);
+}
+
+void special_menuitem(GtkMenuItem *item, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ int code = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),
+ "user-data"));
+
+ inst->back->special(inst->backhandle, code);
+}
+
+void about_menuitem(GtkMenuItem *item, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ about_box(inst->window);
+}
+
+void event_log_menuitem(GtkMenuItem *item, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ showeventlog(inst->eventlogstuff, inst->window);
+}
+
+void change_settings_menuitem(GtkMenuItem *item, gpointer data)
+{
+ /* This maps colour indices in inst->cfg to those used in inst->cols. */
+ static const int ww[] = {
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21,
+ 0, 1, 2, 3, 4, 5
+ };
+ struct gui_data *inst = (struct gui_data *)data;
+ char *title = dupcat(appname, " Reconfiguration", NULL);
+ Config cfg2, oldcfg;
+ int i, need_size;
+
+ cfg2 = inst->cfg; /* structure copy */
+
+ if (do_config_box(title, &cfg2, 1)) {
+
+ oldcfg = inst->cfg; /* structure copy */
+ inst->cfg = cfg2; /* structure copy */
+
+ /* Pass new config data to the logging module */
+ log_reconfig(inst->logctx, &cfg2);
+ /*
+ * Flush the line discipline's edit buffer in the case
+ * where local editing has just been disabled.
+ */
+ ldisc_send(inst->ldisc, NULL, 0, 0);
+ /* Pass new config data to the terminal */
+ term_reconfig(inst->term, &cfg2);
+ /* Pass new config data to the back end */
+ inst->back->reconfig(inst->backhandle, &cfg2);
+
+ /*
+ * Just setting inst->cfg is sufficient to cause colour
+ * setting changes to appear on the next ESC]R palette
+ * reset. But we should also check whether any colour
+ * settings have been changed, and revert the ones that
+ * have to the new default, on the assumption that the user
+ * is most likely to want an immediate update.
+ */
+ for (i = 0; i < NCOLOURS; i++) {
+ if (oldcfg.colours[ww[i]][0] != cfg2.colours[ww[i]][0] ||
+ oldcfg.colours[ww[i]][1] != cfg2.colours[ww[i]][1] ||
+ oldcfg.colours[ww[i]][2] != cfg2.colours[ww[i]][2]) {
+ real_palette_set(inst, i, cfg2.colours[ww[i]][0],
+ cfg2.colours[ww[i]][1],
+ cfg2.colours[ww[i]][2]);
+
+ /*
+ * If the default background has changed, we must
+ * repaint the space in between the window border
+ * and the text area.
+ */
+ if (i == 18) {
+ set_window_background(inst);
+ draw_backing_rect(inst);
+ }
+ }
+ }
+
+ /*
+ * If the scrollbar needs to be shown, hidden, or moved
+ * from one end to the other of the window, do so now.
+ */
+ if (oldcfg.scrollbar != cfg2.scrollbar) {
+ if (cfg2.scrollbar)
+ gtk_widget_show(inst->sbar);
+ else
+ gtk_widget_hide(inst->sbar);
+ }
+ if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) {
+ gtk_box_reorder_child(inst->hbox, inst->sbar,
+ cfg2.scrollbar_on_left ? 0 : 1);
+ }
+
+ /*
+ * Change the window title, if required.
+ */
+ if (strcmp(oldcfg.wintitle, cfg2.wintitle))
+ set_title(inst, cfg2.wintitle);
+ set_window_titles(inst);
+
+ /*
+ * Redo the whole tangled fonts and Unicode mess if
+ * necessary.
+ */
+ if (strcmp(oldcfg.font.name, cfg2.font.name) ||
+ strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) ||
+ strcmp(oldcfg.widefont.name, cfg2.widefont.name) ||
+ strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) ||
+ strcmp(oldcfg.line_codepage, cfg2.line_codepage) ||
+ oldcfg.vtmode != cfg2.vtmode ||
+ oldcfg.shadowbold != cfg2.shadowbold) {
+ setup_fonts_ucs(inst);
+ need_size = 1;
+ } else
+ need_size = 0;
+
+ /*
+ * Resize the window.
+ */
+ if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height ||
+ oldcfg.window_border != cfg2.window_border || need_size) {
+ set_geom_hints(inst);
+ request_resize(inst, cfg2.width, cfg2.height);
+ } else {
+ /*
+ * The above will have caused a call to term_size() for
+ * us if it happened. If the user has fiddled with only
+ * the scrollback size, the above will not have
+ * happened and we will need an explicit term_size()
+ * here.
+ */
+ if (oldcfg.savelines != cfg2.savelines)
+ term_size(inst->term, inst->term->rows, inst->term->cols,
+ cfg2.savelines);
+ }
+
+ term_invalidate(inst->term);
+
+ /*
+ * We do an explicit full redraw here to ensure the window
+ * border has been redrawn as well as the text area.
+ */
+ gtk_widget_queue_draw(inst->area);
+ }
+ sfree(title);
+}
+
+void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)
+{
+ /*
+ * Re-execing ourself is not an exact science under Unix. I do
+ * the best I can by using /proc/self/exe if available and by
+ * assuming argv[0] can be found on $PATH if not.
+ *
+ * Note that we also have to reconstruct the elements of the
+ * original argv which gtk swallowed, since the user wants the
+ * new session to appear on the same X display as the old one.
+ */
+ char **args;
+ va_list ap;
+ int i, n;
+ int pid;
+
+ /*
+ * Collect the arguments with which to re-exec ourself.
+ */
+ va_start(ap, fd_to_close);
+ n = 2; /* progname and terminating NULL */
+ n += inst->ngtkargs;
+ while (va_arg(ap, char *) != NULL)
+ n++;
+ va_end(ap);
+
+ args = snewn(n, char *);
+ args[0] = inst->progname;
+ args[n-1] = NULL;
+ for (i = 0; i < inst->ngtkargs; i++)
+ args[i+1] = inst->gtkargvstart[i];
+
+ i++;
+ va_start(ap, fd_to_close);
+ while ((args[i++] = va_arg(ap, char *)) != NULL);
+ va_end(ap);
+
+ assert(i == n);
+
+ /*
+ * Do the double fork.
+ */
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return;
+ }