diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 99144557..01bf6f29 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -54,6 +54,19 @@ GdkAtom compound_text_atom, utf8_string_atom; struct clipboard_data_instance; +struct clipboard_state { + struct gui_data *inst; + int clipboard; + GdkAtom atom; +#ifdef JUST_USE_GTK_CLIPBOARD_UTF8 + GtkClipboard *gtkclipboard; + struct clipboard_data_instance *current_cdi; +#else + char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8; + int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len; +#endif +}; + struct gui_data { GtkWidget *window, *area, *sbar; gboolean sbar_visible; @@ -101,13 +114,7 @@ struct gui_data { GdkColormap *colmap; #endif int direct_to_font; -#ifdef JUST_USE_GTK_CLIPBOARD_UTF8 - GtkClipboard *clipboard; - struct clipboard_data_instance *current_cdi; -#else - char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8; - int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len; -#endif + struct clipboard_state clipstates[N_CLIPBOARDS]; int font_width, font_height; int width, height; int ignore_sbar; @@ -1022,7 +1029,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) #ifdef KEY_EVENT_DIAGNOSTICS debug((" - Shift-Insert paste\n")); #endif - term_request_paste(inst->term, CLIP_SYSTEM); + term_request_paste(inst->term, SHIFT_INS_CLIPBOARD); return TRUE; } @@ -2490,10 +2497,26 @@ void palette_reset(void *frontend) * formats it feels like. */ -void init_clipboard(struct gui_data *inst) +static void init_one_clipboard(struct gui_data *inst, int clipboard, + GdkAtom atom) { - inst->clipboard = gtk_clipboard_get_for_display(gdk_display_get_default(), - DEFAULT_CLIPBOARD); + struct clipboard_state *state = &inst->clipstates[clipboard]; + + state->inst = inst; + state->atom = atom; + state->clipboard = clipboard; + + state->gtkclipboard = gtk_clipboard_get_for_display( + gdk_display_get_default(), atom); + + g_object_set_data(G_OBJECT(state->gtkclipboard), "user-data", state); +} + +int init_clipboard(struct gui_data *inst) +{ + init_one_clipboard(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY); + init_one_clipboard(inst, CLIP_CLIPBOARD, GDK_SELECTION_CLIPBOARD); + return TRUE; } /* @@ -2506,7 +2529,6 @@ void init_clipboard(struct gui_data *inst) * to 'inst', to one of these. */ struct clipboard_data_instance { - struct gui_data *inst; char *pasteout_data_utf8; int pasteout_data_utf8_len; }; @@ -2517,9 +2539,10 @@ static void clipboard_provide_data(GtkClipboard *clipboard, { struct clipboard_data_instance *cdi = (struct clipboard_data_instance *)data; - struct gui_data *inst = cdi->inst; + struct clipboard_state *state = (struct clipboard_state *) + g_object_get_data(G_OBJECT(clipboard), "user-data"); - if (inst->current_cdi == cdi) { + if (state->current_cdi == cdi) { gtk_selection_data_set_text(selection_data, cdi->pasteout_data_utf8, cdi->pasteout_data_utf8_len); } @@ -2529,21 +2552,23 @@ static void clipboard_clear(GtkClipboard *clipboard, gpointer data) { struct clipboard_data_instance *cdi = (struct clipboard_data_instance *)data; - struct gui_data *inst = cdi->inst; + struct clipboard_state *state = (struct clipboard_state *) + g_object_get_data(G_OBJECT(clipboard), "user-data"); - if (inst->current_cdi == cdi) { - term_lost_clipboard_ownership(inst->term, CLIP_SYSTEM); - inst->current_cdi = NULL; + if (state->current_cdi == cdi) { + term_lost_clipboard_ownership(state->inst->term, state->clipboard); + state->current_cdi = NULL; } sfree(cdi->pasteout_data_utf8); sfree(cdi); } void write_clip(void *frontend, int clipboard, - char_t *data, int *attr, truecolour *truecolour, int len, + wchar_t *data, int *attr, truecolour *truecolour, int len, int must_deselect) { struct gui_data *inst = (struct gui_data *)frontend; + struct clipboard_state *state = &inst->clipstates[clipboard]; struct clipboard_data_instance *cdi; if (inst->direct_to_font) { @@ -2556,8 +2581,7 @@ void write_clip(void *frontend, int clipboard, } cdi = snew(struct clipboard_data_instance); - cdi->inst = inst; - inst->current_cdi = cdi; + state->current_cdi = cdi; cdi->pasteout_data_utf8 = snewn(len*6, char); { const wchar_t *tmp = data; @@ -2581,9 +2605,9 @@ void write_clip(void *frontend, int clipboard, targetlist = gtk_target_list_new(NULL, 0); gtk_target_list_add_text_targets(targetlist, 0); targettable = gtk_target_table_new_from_list(targetlist, &n_targets); - gtk_clipboard_set_with_data(inst->clipboard, targettable, n_targets, - clipboard_provide_data, clipboard_clear, - cdi); + gtk_clipboard_set_with_data(state->gtkclipboard, targettable, + n_targets, clipboard_provide_data, + clipboard_clear, cdi); gtk_target_table_free(targettable, n_targets); gtk_target_list_unref(targetlist); } @@ -2613,8 +2637,9 @@ static void clipboard_text_received(GtkClipboard *clipboard, void frontend_request_paste(void *frontend, int clipboard) { struct gui_data *inst = (struct gui_data *)frontend; - assert(clipboard == CLIP_SYSTEM); - gtk_clipboard_request_text(inst->clipboard, clipboard_text_received, inst); + struct clipboard_state *state = &inst->clipstates[clipboard]; + gtk_clipboard_request_text(state->gtkclipboard, + clipboard_text_received, inst); } #else /* JUST_USE_GTK_CLIPBOARD_UTF8 */ @@ -2674,12 +2699,14 @@ void write_clip(void *frontend, int clipboard, 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); + struct clipboard_state *state = &inst->clipstates[clipboard]; + + if (state->pasteout_data) + sfree(state->pasteout_data); + if (state->pasteout_data_ctext) + sfree(state->pasteout_data_ctext); + if (state->pasteout_data_utf8) + sfree(state->pasteout_data_utf8); /* * Set up UTF-8 and compound text paste data. This only happens @@ -2694,74 +2721,76 @@ void write_clip(void *frontend, int clipboard, Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); #endif - inst->pasteout_data_utf8 = snewn(len*6, char); - inst->pasteout_data_utf8_len = len*6; - inst->pasteout_data_utf8_len = - charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8, - inst->pasteout_data_utf8_len, + state->pasteout_data_utf8 = snewn(len*6, char); + state->pasteout_data_utf8_len = len*6; + state->pasteout_data_utf8_len = + charset_from_unicode(&tmp, &tmplen, state->pasteout_data_utf8, + state->pasteout_data_utf8_len, CS_UTF8, NULL, NULL, 0); - if (inst->pasteout_data_utf8_len == 0) { - sfree(inst->pasteout_data_utf8); - inst->pasteout_data_utf8 = NULL; + if (state->pasteout_data_utf8_len == 0) { + sfree(state->pasteout_data_utf8); + state->pasteout_data_utf8 = NULL; } else { - inst->pasteout_data_utf8 = - sresize(inst->pasteout_data_utf8, - inst->pasteout_data_utf8_len + 1, char); - inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0'; + state->pasteout_data_utf8 = + sresize(state->pasteout_data_utf8, + state->pasteout_data_utf8_len + 1, char); + state->pasteout_data_utf8[state->pasteout_data_utf8_len] = '\0'; } /* * Now let Xlib convert our UTF-8 data into compound text. */ #ifndef NOT_X_WINDOWS - list[0] = inst->pasteout_data_utf8; + list[0] = state->pasteout_data_utf8; if (Xutf8TextListToTextProperty(disp, 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; + state->pasteout_data_ctext = snewn(tp.nitems+1, char); + memcpy(state->pasteout_data_ctext, tp.value, tp.nitems); + state->pasteout_data_ctext_len = tp.nitems; XFree(tp.value); } else #endif { - inst->pasteout_data_ctext = NULL; - inst->pasteout_data_ctext_len = 0; + state->pasteout_data_ctext = NULL; + state->pasteout_data_ctext_len = 0; } } else { - inst->pasteout_data_utf8 = NULL; - inst->pasteout_data_utf8_len = 0; - inst->pasteout_data_ctext = NULL; - inst->pasteout_data_ctext_len = 0; + state->pasteout_data_utf8 = NULL; + state->pasteout_data_utf8_len = 0; + state->pasteout_data_ctext = NULL; + state->pasteout_data_ctext_len = 0; } - inst->pasteout_data = snewn(len*6, char); - inst->pasteout_data_len = len*6; - inst->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0, - data, len, inst->pasteout_data, - inst->pasteout_data_len, + state->pasteout_data = snewn(len*6, char); + state->pasteout_data_len = len*6; + state->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0, + data, len, state->pasteout_data, + state->pasteout_data_len, NULL, NULL, NULL); - if (inst->pasteout_data_len == 0) { - sfree(inst->pasteout_data); - inst->pasteout_data = NULL; + if (state->pasteout_data_len == 0) { + sfree(state->pasteout_data); + state->pasteout_data = NULL; } else { - inst->pasteout_data = - sresize(inst->pasteout_data, inst->pasteout_data_len, char); + state->pasteout_data = + sresize(state->pasteout_data, state->pasteout_data_len, char); } - store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len); + /* The legacy X cut buffers go with PRIMARY, not any other clipboard */ + if (state->atom == GDK_SELECTION_PRIMARY) + store_cutbuffer(state->pasteout_data, state->pasteout_data_len); - if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY, + if (gtk_selection_owner_set(inst->area, state->atom, inst->input_event_time)) { #if GTK_CHECK_VERSION(2,0,0) - gtk_selection_clear_targets(inst->area, GDK_SELECTION_PRIMARY); + gtk_selection_clear_targets(inst->area, state->atom); #endif - gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY, + gtk_selection_add_target(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, 1); - if (inst->pasteout_data_ctext) - gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY, + if (state->pasteout_data_ctext) + gtk_selection_add_target(inst->area, state->atom, compound_text_atom, 1); - if (inst->pasteout_data_utf8) - gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY, + if (state->pasteout_data_utf8) + gtk_selection_add_target(inst->area, state->atom, utf8_string_atom, 1); } @@ -2769,51 +2798,75 @@ void write_clip(void *frontend, int clipboard, term_lost_clipboard_ownership(inst->term, clipboard); } +static struct clipboard_state *clipboard_from_atom( + struct gui_data *inst, GdkAtom atom) +{ + int i; + + for (i = 0; i < N_CLIPBOARDS; i++) { + struct clipboard_state *state = &inst->clipstates[i]; + if (state->inst == inst && state->atom == atom) + return state; + } + + return NULL; +} + static void selection_get(GtkWidget *widget, GtkSelectionData *seldata, guint info, guint time_stamp, gpointer data) { struct gui_data *inst = (struct gui_data *)data; GdkAtom target = gtk_selection_data_get_target(seldata); + struct clipboard_state *state = clipboard_from_atom( + inst, gtk_selection_data_get_selection(seldata)); + + if (!state) + return; + if (target == utf8_string_atom) gtk_selection_data_set(seldata, target, 8, - (unsigned char *)inst->pasteout_data_utf8, - inst->pasteout_data_utf8_len); + (unsigned char *)state->pasteout_data_utf8, + state->pasteout_data_utf8_len); else if (target == compound_text_atom) gtk_selection_data_set(seldata, target, 8, - (unsigned char *)inst->pasteout_data_ctext, - inst->pasteout_data_ctext_len); + (unsigned char *)state->pasteout_data_ctext, + state->pasteout_data_ctext_len); else gtk_selection_data_set(seldata, target, 8, - (unsigned char *)inst->pasteout_data, - inst->pasteout_data_len); + (unsigned char *)state->pasteout_data, + state->pasteout_data_len); } static gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata, gpointer data) { struct gui_data *inst = (struct gui_data *)data; + struct clipboard_state *state = clipboard_from_atom( + inst, seldata->selection); - term_lost_clipboard_ownership(inst->term, CLIP_SYSTEM); - 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; + if (!state) + return TRUE; + + term_lost_clipboard_ownership(inst->term, state->clipboard); + if (state->pasteout_data) + sfree(state->pasteout_data); + if (state->pasteout_data_ctext) + sfree(state->pasteout_data_ctext); + if (state->pasteout_data_utf8) + sfree(state->pasteout_data_utf8); + state->pasteout_data = NULL; + state->pasteout_data_len = 0; + state->pasteout_data_ctext = NULL; + state->pasteout_data_ctext_len = 0; + state->pasteout_data_utf8 = NULL; + state->pasteout_data_utf8_len = 0; return TRUE; } void frontend_request_paste(void *frontend, int clipboard) { struct gui_data *inst = (struct gui_data *)frontend; - - assert(clipboard == CLIP_SYSTEM); + struct clipboard_state *state = &inst->clipstates[clipboard]; /* * In Unix, pasting is asynchronous: all we can do at the @@ -2829,17 +2882,16 @@ void frontend_request_paste(void *frontend, int clipboard) * fails, selection_received() will be informed and will * fall back to an ordinary string. */ - gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY, - utf8_string_atom, + gtk_selection_convert(inst->area, state->atom, 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, - inst->input_event_time); + gtk_selection_convert(inst->area, state->atom, + GDK_SELECTION_TYPE_STRING, + inst->input_event_time); } } @@ -2861,13 +2913,18 @@ static void selection_received(GtkWidget *widget, GtkSelectionData *seldata, gint seldata_length = gtk_selection_data_get_length(seldata); wchar_t *paste; int paste_len; + struct clipboard_state *state = clipboard_from_atom( + inst, gtk_selection_data_get_selection(seldata)); + + if (!state) + return; if (seldata_target == utf8_string_atom && seldata_length <= 0) { /* * Failed to get a UTF-8 selection string. Try compound * text next. */ - gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY, + gtk_selection_convert(inst->area, state->atom, compound_text_atom, inst->input_event_time); return; @@ -2878,7 +2935,7 @@ static void selection_received(GtkWidget *widget, GtkSelectionData *seldata, * Failed to get UTF-8 or compound text. Try an ordinary * string. */ - gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY, + gtk_selection_convert(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, inst->input_event_time); return; @@ -2935,7 +2992,7 @@ static void selection_received(GtkWidget *widget, GtkSelectionData *seldata, /* * Compound text failed; fall back to STRING. */ - gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY, + gtk_selection_convert(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, inst->input_event_time); return; @@ -2963,6 +3020,16 @@ static void selection_received(GtkWidget *widget, GtkSelectionData *seldata, #endif } +static void init_one_clipboard(struct gui_data *inst, int clipboard, + GdkAtom atom) +{ + struct clipboard_state *state = &inst->clipstates[clipboard]; + + state->inst = inst; + state->atom = atom; + state->clipboard = clipboard; +} + void init_clipboard(struct gui_data *inst) { #ifndef NOT_X_WINDOWS @@ -2998,6 +3065,9 @@ void init_clipboard(struct gui_data *inst) XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0); #endif + init_one_clipboard(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY); + init_one_clipboard(inst, CLIP_CLIPBOARD, GDK_SELECTION_CLIPBOARD); + g_signal_connect(G_OBJECT(inst->area), "selection_received", G_CALLBACK(selection_received), inst); g_signal_connect(G_OBJECT(inst->area), "selection_get", @@ -3016,7 +3086,7 @@ void init_clipboard(struct gui_data *inst) void paste_menu_action(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; - term_request_paste(inst->term, CLIP_SYSTEM); + term_request_paste(inst->term, MENU_PASTE_CLIPBOARD); } static void set_window_titles(struct gui_data *inst) @@ -4934,8 +5004,8 @@ void new_session_window(Conf *conf, const char *geometry_string) inst->eventlogstuff = eventlogstuff_new(); inst->term = term_init(inst->conf, &inst->ucsdata, inst); - inst->term->mouse_select_clipboard = CLIP_SYSTEM; - inst->term->mouse_paste_clipboard = CLIP_SYSTEM; + inst->term->mouse_select_clipboard = MOUSE_SELECT_CLIPBOARD; + inst->term->mouse_paste_clipboard = MOUSE_PASTE_CLIPBOARD; inst->logctx = log_init(inst, inst->conf); term_provide_logctx(inst->term, inst->logctx); diff --git a/unix/unix.h b/unix/unix.h index 6c9d11c7..d9e8f7a3 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -29,7 +29,6 @@ /* this potential one of the Meta keys needs manual handling */ #define META_MANUAL_MASK (GDK_MOD1_MASK) #define JUST_USE_GTK_CLIPBOARD_UTF8 /* low-level gdk_selection_* fails */ -#define DEFAULT_CLIPBOARD GDK_SELECTION_CLIPBOARD /* OS X has no PRIMARY */ #define BUILDINFO_PLATFORM_GTK "OS X (GTK)" #define BUILDINFO_GTK @@ -119,10 +118,23 @@ unsigned long getticks(void); */ #define FLAG_STDERR_TTY 0x1000 -#define PLATFORM_CLIPBOARDS(X) \ - X(CLIP_SYSTEM, "system clipboard") \ +#define PLATFORM_CLIPBOARDS(X) \ + X(CLIP_PRIMARY, "X11 primary selection") \ + X(CLIP_CLIPBOARD, "XDG clipboard") \ /* end of list */ +#ifdef OSX_GTK +/* OS X has no PRIMARY selection */ +#define SHIFT_INS_CLIPBOARD CLIP_CLIPBOARD +#define MOUSE_SELECT_CLIPBOARD CLIP_CLIPBOARD +#define MOUSE_PASTE_CLIPBOARD CLIP_CLIPBOARD +#else +#define SHIFT_INS_CLIPBOARD CLIP_PRIMARY +#define MOUSE_SELECT_CLIPBOARD CLIP_PRIMARY +#define MOUSE_PASTE_CLIPBOARD CLIP_PRIMARY +#endif +#define MENU_PASTE_CLIPBOARD CLIP_CLIPBOARD + /* The per-session frontend structure managed by gtkwin.c */ struct gui_data;