#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <X11/Xatom.h>
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
wchar_t *pastein_data;
int direct_to_font;
int pastein_data_len;
- char *pasteout_data, *pasteout_data_utf8;
- int pasteout_data_len, pasteout_data_utf8_len;
+ char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8;
+ int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len;
int font_width, font_height;
int width, height;
int ignore_sbar;
void *eventlogstuff;
char *progname, **gtkargvstart;
int ngtkargs;
+ guint32 input_event_time; /* Timestamp of the most recent input event. */
};
struct draw_ctx {
{
if (!strcmp(name, "CloseOnExit"))
return 2; /* maps to FORCE_ON after painful rearrangement :-( */
+ if (!strcmp(name, "WinNameAlways"))
+ return 0; /* X natively supports icon titles, so use 'em by default */
return def;
}
return term_data(inst->term, is_stderr, data, len);
}
-void logevent(void *frontend, char *string)
+void logevent(void *frontend, const char *string)
{
struct gui_data *inst = (struct gui_data *)frontend;
char *get_window_title(void *frontend, int icon)
{
struct gui_data *inst = (struct gui_data *)frontend;
- return icon ? inst->wintitle : inst->icontitle;
+ return icon ? inst->icontitle : inst->wintitle;
}
gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
inst->mouseptr_visible = show;
}
+void draw_backing_rect(struct gui_data *inst)
+{
+ GdkGC *gc = gdk_gc_new(inst->area->window);
+ gdk_gc_set_foreground(gc, &inst->cols[18]); /* default background */
+ gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
+ inst->cfg.width * inst->font_width + 2*inst->cfg.window_border,
+ inst->cfg.height * inst->font_height + 2*inst->cfg.window_border);
+ gdk_gc_unref(gc);
+}
+
gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
struct gui_data *inst = (struct gui_data *)data;
int w, h, need_size = 0;
- GdkGC *gc;
/*
* See if the terminal size has changed, in which case we must
(inst->cfg.height * inst->font_height +
2*inst->cfg.window_border), -1);
- gc = gdk_gc_new(inst->area->window);
- gdk_gc_set_foreground(gc, &inst->cols[18]); /* default background */
- gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
- inst->cfg.width * inst->font_width + 2*inst->cfg.window_border,
- inst->cfg.height * inst->font_height + 2*inst->cfg.window_border);
- gdk_gc_unref(gc);
+ draw_backing_rect(inst);
if (need_size && inst->term) {
term_size(inst->term, h, w, inst->cfg.savelines);
{
struct gui_data *inst = (struct gui_data *)data;
char output[32];
- int start, end, special;
+ wchar_t ucsoutput[2];
+ int ucsval, start, end, special, use_ucsoutput;
+
+ /* Remember the timestamp. */
+ inst->input_event_time = event->time;
/* By default, nothing is generated. */
end = start = 0;
+ special = use_ucsoutput = FALSE;
/*
* If Alt is being released after typing an Alt+numberpad
}
special = FALSE;
+ use_ucsoutput = FALSE;
/* ALT+things gives leading Escape. */
output[0] = '\033';
strncpy(output+1, event->string, 31);
- output[31] = '\0';
- end = strlen(output);
+ if (!*event->string &&
+ (ucsval = keysym_to_unicode(event->keyval)) >= 0) {
+ ucsoutput[0] = '\033';
+ ucsoutput[1] = ucsval;
+ use_ucsoutput = TRUE;
+ end = 2;
+ } else {
+ output[31] = '\0';
+ end = strlen(output);
+ }
if (event->state & GDK_MOD1_MASK) {
start = 0;
if (end == 1) end = 0;
if (!event->string[0] && event->keyval == '`' &&
(event->state & GDK_CONTROL_MASK)) {
output[1] = '\x1C';
+ use_ucsoutput = FALSE;
end = 2;
}
if (event->keyval == GDK_Break &&
(event->state & GDK_CONTROL_MASK)) {
output[1] = '\003';
+ use_ucsoutput = FALSE;
end = 2;
special = TRUE;
}
* special to ldisc. */
if (event->keyval == GDK_Return) {
output[1] = '\015';
+ use_ucsoutput = FALSE;
end = 2;
special = TRUE;
}
(event->state & (GDK_SHIFT_MASK |
GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {
output[1] = '\0';
+ use_ucsoutput = FALSE;
end = 2;
}
(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==
(GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
output[1] = '\240';
+ use_ucsoutput = FALSE;
end = 2;
}
if (event->keyval == GDK_BackSpace &&
!(event->state & GDK_SHIFT_MASK)) {
output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08';
+ use_ucsoutput = FALSE;
end = 2;
special = TRUE;
}
if (event->keyval == GDK_BackSpace &&
(event->state & GDK_SHIFT_MASK)) {
output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F';
+ use_ucsoutput = FALSE;
end = 2;
special = TRUE;
}
if (event->keyval == GDK_ISO_Left_Tab ||
(event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {
end = 1 + sprintf(output+1, "\033[Z");
+ use_ucsoutput = FALSE;
}
/*
output[1] = keys[1];
else
output[1] = keys[0];
+ use_ucsoutput = FALSE;
goto done;
}
}
end = 1 + sprintf(output+1, "\033?%c", xkey);
} else
end = 1 + sprintf(output+1, "\033O%c", xkey);
+ use_ucsoutput = FALSE;
goto done;
}
}
if (inst->term->vt52_mode && code > 0 && code <= 6) {
end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);
+ use_ucsoutput = FALSE;
goto done;
}
if (event->state & GDK_SHIFT_MASK) index += 12;
if (event->state & GDK_CONTROL_MASK) index += 24;
end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);
+ use_ucsoutput = FALSE;
goto done;
}
if (inst->cfg.funky_type == 5 && /* SCO small keypad */
} else {
end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);
}
+ use_ucsoutput = FALSE;
goto done;
}
if ((inst->term->vt52_mode || inst->cfg.funky_type == 4) &&
else
end = 1 + sprintf(output+1,
"\x1BO%c", code + 'P' - 11 - offt);
+ use_ucsoutput = FALSE;
goto done;
}
if (inst->cfg.funky_type == 1 && code >= 11 && code <= 15) {
end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
+ use_ucsoutput = FALSE;
goto done;
}
if (inst->cfg.funky_type == 2 && code >= 11 && code <= 14) {
end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
else
end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);
+ use_ucsoutput = FALSE;
goto done;
}
if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) {
end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
+ use_ucsoutput = FALSE;
goto done;
}
if (code) {
end = 1 + sprintf(output+1, "\x1B[%d~", code);
+ use_ucsoutput = FALSE;
goto done;
}
}
} else {
end = 1 + sprintf(output+1, "\033[%c", xkey);
}
+ use_ucsoutput = FALSE;
goto done;
}
}
output[end] = '\0'; /* NUL-terminate */
ldisc_send(inst->ldisc, output+start, -2, 1);
} else if (!inst->direct_to_font) {
- /*
- * The stuff we've just generated is assumed to be
- * ISO-8859-1! This sounds insane, but `man
- * XLookupString' agrees: strings of this type returned
- * from the X server are hardcoded to 8859-1. Strictly
- * speaking we should be doing this using some sort of
- * GtkIMContext, which (if we're lucky) would give us
- * our data directly in Unicode; but that's not
- * supported in GTK 1.2 as far as I can tell, and it's
- * poorly documented even in 2.0, so it'll have to
- * wait.
- */
- lpage_send(inst->ldisc, CS_ISO8859_1, output+start, end-start, 1);
+ if (!use_ucsoutput) {
+ /*
+ * The stuff we've just generated is assumed to be
+ * ISO-8859-1! This sounds insane, but `man
+ * XLookupString' agrees: strings of this type
+ * returned from the X server are hardcoded to
+ * 8859-1. Strictly speaking we should be doing
+ * this using some sort of GtkIMContext, which (if
+ * we're lucky) would give us our data directly in
+ * Unicode; but that's not supported in GTK 1.2 as
+ * far as I can tell, and it's poorly documented
+ * even in 2.0, so it'll have to wait.
+ */
+ lpage_send(inst->ldisc, CS_ISO8859_1, output+start,
+ end-start, 1);
+ } else {
+ /*
+ * We generated our own Unicode key data from the
+ * keysym, so use that instead.
+ */
+ luni_send(inst->ldisc, ucsoutput+start, end-start, 1);
+ }
} else {
/*
* In direct-to-font mode, we just send the string
struct gui_data *inst = (struct gui_data *)data;
int shift, ctrl, alt, x, y, button, act;
+ /* Remember the timestamp. */
+ inst->input_event_time = event->time;
+
show_mouseptr(inst, 1);
if (event->button == 4 && event->type == GDK_BUTTON_PRESS) {
struct gui_data *inst = (struct gui_data *)data;
int shift, ctrl, alt, x, y, button;
+ /* Remember the timestamp. */
+ inst->input_event_time = event->time;
+
show_mouseptr(inst, 1);
shift = event->state & GDK_SHIFT_MASK;
set_window_background(inst);
}
+/* Ensure that all the cut buffers exist - according to the ICCCM, we must
+ * do this before we start using cut buffers.
+ */
+void init_cutbuffers()
+{
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, "", 0);
+}
+
+/* Store the data in a cut-buffer. */
+void store_cutbuffer(char * ptr, int len)
+{
+ /* ICCCM says we must rotate the buffers before storing to buffer 0. */
+ XRotateBuffers(GDK_DISPLAY(), 1);
+ XStoreBytes(GDK_DISPLAY(), ptr, len);
+}
+
+/* Retrieve data from a cut-buffer.
+ * Returned data needs to be freed with XFree().
+ */
+char * retrieve_cutbuffer(int * nbytes)
+{
+ char * ptr;
+ ptr = XFetchBytes(GDK_DISPLAY(), nbytes);
+ if (nbytes <= 0 && ptr != 0) {
+ XFree(ptr);
+ ptr = 0;
+ }
+ return ptr;
+}
+
void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
{
struct gui_data *inst = (struct gui_data *)frontend;
if (inst->pasteout_data)
sfree(inst->pasteout_data);
+ if (inst->pasteout_data_ctext)
+ sfree(inst->pasteout_data_ctext);
if (inst->pasteout_data_utf8)
sfree(inst->pasteout_data_utf8);
/*
- * Set up UTF-8 paste data. This only happens if we aren't in
- * direct-to-font mode using the D800 hack.
+ * Set up UTF-8 and compound text paste data. This only happens
+ * if we aren't in direct-to-font mode using the D800 hack.
*/
if (!inst->direct_to_font) {
wchar_t *tmp = data;
int tmplen = len;
+ XTextProperty tp;
+ char *list[1];
inst->pasteout_data_utf8 = snewn(len*6, char);
inst->pasteout_data_utf8_len = len*6;
} else {
inst->pasteout_data_utf8 =
sresize(inst->pasteout_data_utf8,
- inst->pasteout_data_utf8_len, char);
+ inst->pasteout_data_utf8_len + 1, char);
+ inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0';
+ }
+
+ /*
+ * Now let Xlib convert our UTF-8 data into compound text.
+ */
+ list[0] = inst->pasteout_data_utf8;
+ if (Xutf8TextListToTextProperty(GDK_DISPLAY(), list, 1,
+ XCompoundTextStyle, &tp) == 0) {
+ inst->pasteout_data_ctext = snewn(tp.nitems+1, char);
+ memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);
+ inst->pasteout_data_ctext_len = tp.nitems;
+ XFree(tp.value);
}
} else {
inst->pasteout_data_utf8 = NULL;
inst->pasteout_data_utf8_len = 0;
+ inst->pasteout_data_ctext = NULL;
+ inst->pasteout_data_ctext_len = 0;
}
inst->pasteout_data = snewn(len*6, char);
sresize(inst->pasteout_data, inst->pasteout_data_len, char);
}
+ store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len);
+
if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
- GDK_CURRENT_TIME)) {
+ inst->input_event_time)) {
gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
GDK_SELECTION_TYPE_STRING, 1);
- gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
- compound_text_atom, 1);
+ if (inst->pasteout_data_ctext)
+ gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
+ compound_text_atom, 1);
if (inst->pasteout_data_utf8)
gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
utf8_string_atom, 1);
gtk_selection_data_set(seldata, seldata->target, 8,
inst->pasteout_data_utf8,
inst->pasteout_data_utf8_len);
+ else if (seldata->target == compound_text_atom)
+ gtk_selection_data_set(seldata, seldata->target, 8,
+ inst->pasteout_data_ctext,
+ inst->pasteout_data_ctext_len);
else
gtk_selection_data_set(seldata, seldata->target, 8,
inst->pasteout_data, inst->pasteout_data_len);
gpointer data)
{
struct gui_data *inst = (struct gui_data *)data;
+
term_deselect(inst->term);
if (inst->pasteout_data)
sfree(inst->pasteout_data);
+ if (inst->pasteout_data_ctext)
+ sfree(inst->pasteout_data_ctext);
if (inst->pasteout_data_utf8)
sfree(inst->pasteout_data_utf8);
inst->pasteout_data = NULL;
inst->pasteout_data_len = 0;
+ inst->pasteout_data_ctext = NULL;
+ inst->pasteout_data_ctext_len = 0;
inst->pasteout_data_utf8 = NULL;
inst->pasteout_data_utf8_len = 0;
return TRUE;
* fall back to an ordinary string.
*/
gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
- utf8_string_atom, GDK_CURRENT_TIME);
+ utf8_string_atom,
+ inst->input_event_time);
} else {
/*
* If we're in direct-to-font mode, we disable UTF-8
* pasting, and go straight to ordinary string data.
*/
gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
- GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+ GDK_SELECTION_TYPE_STRING,
+ inst->input_event_time);
}
}
guint time, gpointer data)
{
struct gui_data *inst = (struct gui_data *)data;
+ XTextProperty tp;
+ char **list;
+ char *text;
+ int length, count, ret;
+ int free_list_required = 0;
+ int free_required = 0;
+ int charset;
if (seldata->target == utf8_string_atom && seldata->length <= 0) {
/*
- * Failed to get a UTF-8 selection string. Try an ordinary
+ * Failed to get a UTF-8 selection string. Try compound
+ * text next.
+ */
+ gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+ compound_text_atom,
+ inst->input_event_time);
+ return;
+ }
+
+ if (seldata->target == compound_text_atom && seldata->length <= 0) {
+ /*
+ * Failed to get UTF-8 or compound text. Try an ordinary
* string.
*/
gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
- GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+ GDK_SELECTION_TYPE_STRING,
+ inst->input_event_time);
return;
}
/*
- * Any other failure should just go foom.
+ * If we have data, but it's not of a type we can deal with,
+ * we have to ignore the data.
+ */
+ if (seldata->length > 0 &&
+ seldata->type != GDK_SELECTION_TYPE_STRING &&
+ seldata->type != compound_text_atom &&
+ seldata->type != utf8_string_atom)
+ return;
+
+ /*
+ * If we have no data, try looking in a cut buffer.
*/
- if (seldata->length <= 0 ||
- (seldata->type != GDK_SELECTION_TYPE_STRING &&
- seldata->type != utf8_string_atom))
- return; /* Nothing happens. */
+ if (seldata->length <= 0) {
+ text = retrieve_cutbuffer(&length);
+ if (length == 0)
+ return;
+ /* Xterm is rumoured to expect Latin-1, though I havn't checked the
+ * source, so use that as a de-facto standard. */
+ charset = CS_ISO8859_1;
+ free_required = 1;
+ } else {
+ /*
+ * Convert COMPOUND_TEXT into UTF-8.
+ */
+ if (seldata->type == compound_text_atom) {
+ tp.value = seldata->data;
+ tp.encoding = (Atom) seldata->type;
+ tp.format = seldata->format;
+ tp.nitems = seldata->length;
+ ret = Xutf8TextPropertyToTextList(GDK_DISPLAY(), &tp, &list, &count);
+ if (ret != 0 || count != 1) {
+ /*
+ * Compound text failed; fall back to STRING.
+ */
+ gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING,
+ inst->input_event_time);
+ return;
+ }
+ text = list[0];
+ length = strlen(list[0]);
+ charset = CS_UTF8;
+ free_list_required = 1;
+ } else {
+ text = (char *)seldata->data;
+ length = seldata->length;
+ charset = (seldata->type == utf8_string_atom ?
+ CS_UTF8 : inst->ucsdata.line_codepage);
+ }
+ }
if (inst->pastein_data)
sfree(inst->pastein_data);
- inst->pastein_data = snewn(seldata->length, wchar_t);
- inst->pastein_data_len = seldata->length;
+ inst->pastein_data = snewn(length, wchar_t);
+ inst->pastein_data_len = length;
inst->pastein_data_len =
- mb_to_wc((seldata->type == utf8_string_atom ?
- CS_UTF8 : inst->ucsdata.line_codepage),
- 0, seldata->data, seldata->length,
+ mb_to_wc(charset, 0, text, length,
inst->pastein_data, inst->pastein_data_len);
term_do_paste(inst->term);
if (term_paste_pending(inst->term))
inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);
+
+ if (free_list_required)
+ XFreeStringList(list);
+ if (free_required)
+ XFree(text);
}
gint idle_paste_func(gpointer data)
}
}
+static void set_window_titles(struct gui_data *inst)
+{
+ /*
+ * We must always call set_icon_name after calling set_title,
+ * since set_title will write both names. Irritating, but such
+ * is life.
+ */
+ gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
+ if (!inst->cfg.win_name_always)
+ gdk_window_set_icon_name(inst->window->window, inst->icontitle);
+}
+
void set_title(void *frontend, char *title)
{
struct gui_data *inst = (struct gui_data *)frontend;
strncpy(inst->wintitle, title, lenof(inst->wintitle));
inst->wintitle[lenof(inst->wintitle)-1] = '\0';
- gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
+ set_window_titles(inst);
}
void set_icon(void *frontend, char *title)
struct gui_data *inst = (struct gui_data *)frontend;
strncpy(inst->icontitle, title, lenof(inst->icontitle));
inst->icontitle[lenof(inst->icontitle)-1] = '\0';
- gdk_window_set_icon_name(inst->window->window, inst->icontitle);
+ set_window_titles(inst);
}
void set_sbar(void *frontend, int total, int start, int page)
char *p = *++argv;
int ret;
+ /*
+ * Shameless cheating. Debian requires all X terminal
+ * emulators to support `-T title'; but
+ * cmdline_process_param will eat -T (it means no-pty) and
+ * complain that pterm doesn't support it. So, in pterm
+ * only, we convert -T into -title.
+ */
+ if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
+ !strcmp(p, "-T"))
+ p = "-title";
+
ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
do_everything ? 1 : -1, cfg);
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]);
exit(1);
}
font_charset = set_font_info(inst, 0);
- if (inst->cfg.boldfont.name[0]) {
- inst->fonts[1] = gdk_font_load(inst->cfg.boldfont.name);
- if (!inst->fonts[1]) {
+
+ 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);
}
- set_font_info(inst, 1);
- } else
- inst->fonts[1] = NULL;
+ if (guessed)
+ sfree(name);
+ }
+
if (inst->cfg.widefont.name[0]) {
- inst->fonts[2] = gdk_font_load(inst->cfg.widefont.name);
- if (!inst->fonts[2]) {
- fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
- inst->cfg.widefont.name);
- exit(1);
- }
+ 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
- inst->fonts[2] = NULL;
- if (inst->cfg.wideboldfont.name[0]) {
- inst->fonts[3] = gdk_font_load(inst->cfg.wideboldfont.name);
- if (!inst->fonts[3]) {
- fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n",
- appname, inst->cfg.wideboldfont.name);
+ } 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);
}
- set_font_info(inst, 3);
- } else
- inst->fonts[3] = NULL;
+ 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.line_codepage, font_charset,
+ inst->cfg.vtmode);
}
void set_geom_hints(struct gui_data *inst)
void special_menuitem(GtkMenuItem *item, gpointer data)
{
struct gui_data *inst = (struct gui_data *)data;
- int code = (int)gtk_object_get_data(GTK_OBJECT(item), "user-data");
+ int code = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),
+ "user-data"));
inst->back->special(inst->backhandle, code);
}
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])
+ 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 (strcmp(oldcfg.wintitle, cfg2.wintitle))
set_title(inst, cfg2.wintitle);
+ set_window_titles(inst);
/*
* Redo the whole tangled fonts and Unicode mess if
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)) {
+ strcmp(oldcfg.line_codepage, cfg2.line_codepage) ||
+ oldcfg.vtmode != cfg2.vtmode ||
+ oldcfg.shadowbold != cfg2.shadowbold) {
setup_fonts_ucs(inst);
need_size = 1;
} else
}
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);
}
fork_and_exec_self(inst, pipefd[1], option, NULL);
close(pipefd[0]);
- i = 0;
+ i = ret = 0;
while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
i += ret;
if (ret < 0)
}
data = snewn(size, char);
- i = 0;
+ i = ret = 0;
while (i < size && (ret = read(fd, data + i, size - i)) > 0)
i += ret;
if (ret < 0) {
if (*specials[i].name) {
menuitem = gtk_menu_item_new_with_label(specials[i].name);
gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
- (gpointer)specials[i].code);
+ GINT_TO_POINTER(specials[i].code));
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
GTK_SIGNAL_FUNC(special_menuitem), inst);
} else
* it */
block_signal(SIGCHLD, 1);
- /*
- * SIGPIPE is not something we want to see terminating the
- * process.
- */
- block_signal(SIGPIPE, 1);
-
inst->progname = argv[0];
/*
* Copy the original argv before letting gtk_init fiddle with
utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
setup_fonts_ucs(inst);
+ init_cutbuffers();
inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
inst->back = select_backend(&inst->cfg);
{
- char *realhost, *error;
+ char *realhost;
+ const char *error;
error = inst->back->init((void *)inst, &inst->backhandle,
&inst->cfg, inst->cfg.host, inst->cfg.port,
return 0;
}
- if (inst->cfg.wintitle[0])
+ if (inst->cfg.wintitle[0]) {
set_title(inst, inst->cfg.wintitle);
- else {
+ set_icon(inst, inst->cfg.wintitle);
+ } else {
char *title = make_default_wintitle(realhost);
set_title(inst, title);
+ set_icon(inst, title);
sfree(title);
}
}
* called */
block_signal(SIGCHLD, 0);
+ /*
+ * Block SIGPIPE: if we attempt Duplicate Session or similar
+ * and it falls over in some way, we certainly don't want
+ * SIGPIPE terminating the main pterm/PuTTY. Note that we do
+ * this _after_ (at least pterm) forks off its child process,
+ * since the child wants SIGPIPE handled in the usual way.
+ */
+ block_signal(SIGPIPE, 1);
+
inst->exited = FALSE;
gtk_main();