From: simon Date: Sun, 13 Oct 2002 11:24:25 +0000 (+0000) Subject: Selection now supported in pterm. Required small modifications X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/commitdiff_plain/e634699906875560987ba8d6aa776ef7b8394301 Selection now supported in pterm. Required small modifications outside the unix subdir, owing to more things needing to become platform-dependent. git-svn-id: svn://svn.tartarus.org/sgt/putty@2033 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/putty.h b/putty.h index c4230c30..f2c10ba6 100644 --- a/putty.h +++ b/putty.h @@ -435,6 +435,7 @@ void modalfatalbox(char *, ...); void beep(int); void begin_session(void); void sys_cursor(int x, int y); +void request_paste(void); #define OPTIMISE_IS_SCROLL 1 void set_iconic(int iconic); diff --git a/terminal.c b/terminal.c index 7a6c5bce..7bb0b161 100644 --- a/terminal.c +++ b/terminal.c @@ -3221,8 +3221,10 @@ static void clipme(pos top, pos bottom, int rect) top.y++; top.x = rect ? old_top_x : 0; } +#if SELECTION_NUL_TERMINATED wblen++; *wbptr++ = 0; +#endif write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */ if (buflen > 0) /* indicates we allocated this buffer */ sfree(workbuf); @@ -3686,8 +3688,12 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, } else selstate = NO_SELECTION; } else if (b == MBT_PASTE - && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) { - term_do_paste(); + && (a == MA_CLICK +#if MULTICLICK_ONLY_EVENT + || a == MA_2CLK || a == MA_3CLK +#endif + )) { + request_paste(); } term_update(); diff --git a/unix/pterm.c b/unix/pterm.c index f3060895..befd7de7 100644 --- a/unix/pterm.c +++ b/unix/pterm.c @@ -32,6 +32,10 @@ struct gui_data { GdkCursor *rawcursor, *textcursor; GdkColor cols[NCOLOURS]; GdkColormap *colmap; + wchar_t *pastein_data; + int pastein_data_len; + char *pasteout_data; + int pasteout_data_len; int font_width, font_height; }; @@ -616,6 +620,68 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) return TRUE; } +gint button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + struct gui_data *inst = (struct gui_data *)data; + int shift, ctrl, alt, x, y, button, act; + + shift = event->state & GDK_SHIFT_MASK; + ctrl = event->state & GDK_CONTROL_MASK; + alt = event->state & GDK_MOD1_MASK; + if (event->button == 1) + button = MBT_LEFT; + else if (event->button == 2) + button = MBT_MIDDLE; + else if (event->button == 3) + button = MBT_RIGHT; + else + return FALSE; /* don't even know what button! */ + + switch (event->type) { + case GDK_BUTTON_PRESS: act = MA_CLICK; break; + case GDK_BUTTON_RELEASE: act = MA_RELEASE; break; + case GDK_2BUTTON_PRESS: act = MA_2CLK; break; + case GDK_3BUTTON_PRESS: act = MA_3CLK; break; + default: return FALSE; /* don't know this event type */ + } + + if (send_raw_mouse && !(cfg.mouse_override && shift) && + act != MA_CLICK && act != MA_RELEASE) + return TRUE; /* we ignore these in raw mouse mode */ + + x = (event->x - cfg.window_border) / inst->font_width; + y = (event->y - cfg.window_border) / inst->font_height; + + term_mouse(button, act, x, y, shift, ctrl, alt); + + return TRUE; +} + +gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) +{ + struct gui_data *inst = (struct gui_data *)data; + int shift, ctrl, alt, x, y, button; + + shift = event->state & GDK_SHIFT_MASK; + ctrl = event->state & GDK_CONTROL_MASK; + alt = event->state & GDK_MOD1_MASK; + if (event->state & GDK_BUTTON1_MASK) + button = MBT_LEFT; + else if (event->state & GDK_BUTTON2_MASK) + button = MBT_MIDDLE; + else if (event->state & GDK_BUTTON3_MASK) + button = MBT_RIGHT; + else + return FALSE; /* don't even know what button! */ + + x = (event->x - cfg.window_border) / inst->font_width; + y = (event->y - cfg.window_border) / inst->font_height; + + term_mouse(button, MA_DRAG, x, y, shift, ctrl, alt); + + return TRUE; +} + gint timer_func(gpointer data) { /* struct gui_data *inst = (struct gui_data *)data; */ @@ -692,15 +758,72 @@ void palette_reset(void) void write_clip(wchar_t * data, int len, int must_deselect) { - /* FIXME: currently ignored */ + if (inst->pasteout_data) + sfree(inst->pasteout_data); + inst->pasteout_data = smalloc(len); + inst->pasteout_data_len = len; + wc_to_mb(0, 0, data, len, inst->pasteout_data, inst->pasteout_data_len, + NULL, NULL); + + if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY, + GDK_CURRENT_TIME)) { + gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, 1); + } +} + +void selection_get(GtkWidget *widget, GtkSelectionData *seldata, + guint info, guint time_stamp, gpointer data) +{ + gtk_selection_data_set(seldata, GDK_SELECTION_TYPE_STRING, 8, + inst->pasteout_data, inst->pasteout_data_len); +} + +gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata, + gpointer data) +{ + term_deselect(); + if (inst->pasteout_data) + sfree(inst->pasteout_data); + inst->pasteout_data = NULL; + inst->pasteout_data_len = 0; + return TRUE; +} + +void request_paste(void) +{ + /* + * In Unix, pasting is asynchronous: all we can do at the + * moment is to call gtk_selection_convert(), and when the data + * comes back _then_ we can call term_do_paste(). + */ + gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME); +} + +void selection_received(GtkWidget *widget, GtkSelectionData *seldata, + gpointer data) +{ + if (seldata->length <= 0 || + seldata->type != GDK_SELECTION_TYPE_STRING) + return; /* Nothing happens. */ + + if (inst->pastein_data) + sfree(inst->pastein_data); + + inst->pastein_data = smalloc(seldata->length * sizeof(wchar_t)); + inst->pastein_data_len = seldata->length; + mb_to_wc(0, 0, seldata->data, seldata->length, + inst->pastein_data, inst->pastein_data_len); + + term_do_paste(); } void get_clip(wchar_t ** p, int *len) { if (p) { - /* FIXME: currently nonfunctional */ - *p = NULL; - *len = 0; + *p = inst->pastein_data; + *len = inst->pastein_data_len; } } @@ -907,7 +1030,6 @@ int main(int argc, char **argv) gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(inst->hbox)); -// gtk_container_add(GTK_CONTAINER(window), inst->sbar); { GdkGeometry geom; @@ -922,10 +1044,6 @@ int main(int argc, char **argv) gtk_window_set_geometry_hints(GTK_WINDOW(window), inst->area, &geom, GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC); - // FIXME: base_width and base_height don't seem to work properly. - // Wonder if this is because base of _zero_ is deprecated, and we - // need a nonzero base size? Will it help if I put in the scrollbar? - // FIXME also: the scrollbar is not working, find out why. } gtk_signal_connect(GTK_OBJECT(window), "destroy", @@ -942,12 +1060,26 @@ int main(int argc, char **argv) GTK_SIGNAL_FUNC(configure_area), inst); gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event", GTK_SIGNAL_FUNC(expose_area), inst); + gtk_signal_connect(GTK_OBJECT(inst->area), "button_press_event", + GTK_SIGNAL_FUNC(button_event), inst); + gtk_signal_connect(GTK_OBJECT(inst->area), "button_release_event", + GTK_SIGNAL_FUNC(button_event), inst); + gtk_signal_connect(GTK_OBJECT(inst->area), "motion_notify_event", + GTK_SIGNAL_FUNC(motion_event), inst); + gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received", + GTK_SIGNAL_FUNC(selection_received), inst); + gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get", + GTK_SIGNAL_FUNC(selection_get), inst); + gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event", + GTK_SIGNAL_FUNC(selection_clear), inst); gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed", GTK_SIGNAL_FUNC(scrollbar_moved), inst); gtk_timeout_add(20, timer_func, inst); gdk_input_add(pty_master_fd, GDK_INPUT_READ, pty_input_func, inst); gtk_widget_add_events(GTK_WIDGET(inst->area), - GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK); gtk_widget_show(inst->area); gtk_widget_show(inst->sbar); diff --git a/unix/unix.h b/unix/unix.h index a7f3fa59..28f7b180 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -5,6 +5,17 @@ typedef void *Context; /* FIXME: probably needs changing */ extern Backend pty_backend; +/* + * Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_ + * MA_3CLK, when a button is pressed for the second or third time. + */ +#define MULTICLICK_ONLY_EVENT 0 + +/* + * Under X, selection data must not be NUL-terminated. + */ +#define SELECTION_NUL_TERMINATED 0 + /* Simple wraparound timer function */ unsigned long getticks(void); /* based on gettimeofday(2) */ #define GETTICKCOUNT getticks @@ -17,6 +28,8 @@ unsigned long getticks(void); /* based on gettimeofday(2) */ int is_dbcs_leadbyte(int codepage, char byte); int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, wchar_t *wcstr, int wclen); +int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, + char *mbstr, int mblen, char *defchr, int *defused); void init_ucs(void); #define DEFAULT_CODEPAGE 0 /* FIXME: no idea how to do this */ diff --git a/unix/uxucs.c b/unix/uxucs.c index 580f94b9..7d56b321 100644 --- a/unix/uxucs.c +++ b/unix/uxucs.c @@ -92,7 +92,29 @@ int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, int ret = 0; while (mblen > 0 && wclen > 0) { *wcstr++ = (unsigned char) *mbstr++; - ret++; + mblen--, wclen--, ret++; + } + return ret; /* FIXME: check error codes! */ +} + +int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, + char *mbstr, int mblen, char *defchr, int *defused) +{ + int ret = 0; + if (defused) + *defused = 0; + while (mblen > 0 && wclen > 0) { + if (*wcstr >= 0x100) { + if (defchr) + *mbstr++ = *defchr; + else + *mbstr++ = '\xBF'; + if (defused) + *defused = 1; + } else + *mbstr++ = (unsigned char) *wcstr; + wcstr++; + mblen--, wclen--, ret++; } return ret; /* FIXME: check error codes! */ } diff --git a/window.c b/window.c index 59757d91..a4a376bb 100644 --- a/window.c +++ b/window.c @@ -3817,6 +3817,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, return -1; } +void request_paste(void) +{ + /* + * In Windows, pasting is synchronous: we can read the + * clipboard with no difficulty, so request_paste() can just go + * ahead and paste. + */ + term_do_paste(); +} + void set_title(char *title) { sfree(window_name); diff --git a/winstuff.h b/winstuff.h index 3bf43bcb..8c994c03 100644 --- a/winstuff.h +++ b/winstuff.h @@ -54,6 +54,17 @@ GLOBAL HINSTANCE hinst; #define WM_NETEVENT (WM_XUSER + 5) /* + * On Windows, we send MA_2CLK as the only event marking the second + * press of a mouse button. Compare unix.h. + */ +#define MULTICLICK_ONLY_EVENT 1 + +/* + * On Windows, data written to the clipboard must be NUL-terminated. + */ +#define SELECTION_NUL_TERMINATED 1 + +/* * Exports from winctrls.c. */