1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00

Make gtkwin.c able to support multiple selections.

All the data fields referring to the selection in 'struct gui_data'
have been pulled out into a separate structure of which there are now
multiple instances, and I've plumbed through what should be the right
pointers and integer ids to everywhere they should go. So now the GTK
front end defines CLIP_PRIMARY and CLIP_CLIPBOARD in place of the
temporary cop-out CLIP_SYSTEM from the previous commit, and copying
and pasting can be done via either one.

The defaults should be the same as before, except that now the non-Mac
versions of the GtkApplication front ends will access CLIP_PRIMARY in
response to most actions but the 'Paste' menu item will paste from
CLIP_CLIPBOARD. (That's mostly just as a demonstration that accessing
multiple clipboards even works.)
This commit is contained in:
Simon Tatham 2017-12-10 10:23:39 +00:00
parent 1829719639
commit 41aa675a5b
2 changed files with 189 additions and 107 deletions

View File

@ -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);

View File

@ -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;