From bc35a1e1d5dbee82391d08c4fca361c7fc77d558 Mon Sep 17 00:00:00 2001 From: Yuuki Harano Date: Thu, 1 Aug 2019 01:26:55 +0900 Subject: [PATCH] Make multipdisplay work by limiting selection while enabed MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * src/pgtkterm.c (pgtk_mouse_position): * src/pgtkselect.c (pgtk_selection_usable): new function (Fpgtk_own_selection_internal, Fpgtk_disown_selection_internal) (Fpgtk_selection_exists_p, Fpgtk_selection_owner_p) (Fpgtk_get_selection_internal): check usable selection multi-display で落ちる理由を一つ潰した。 まだまだありそう。 multi-display 時は selection を使えないようにした。 --- src/pgtkselect.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ src/pgtkterm.c | 35 ++++++++-------- 2 files changed, 121 insertions(+), 16 deletions(-) diff --git a/src/pgtkselect.c b/src/pgtkselect.c index b8854256802..4f4a43da8be 100644 --- a/src/pgtkselect.c +++ b/src/pgtkselect.c @@ -200,6 +200,93 @@ void pgtk_selection_lost(GtkWidget *widget, GdkEventSelection *event, gpointer u g_object_set_qdata(G_OBJECT(widget), quark_size, 0); } +static bool +pgtk_selection_usable (void) +{ + /* + * https://github.com/GNOME/gtk/blob/gtk-3-24/gdk/wayland/gdkselection-wayland.c#L1033 + * + * Gdk uses gdk_display_get_default() when handling selections, so + * selections don't work properly on multi-display environment. + * + * ---------------- + * #include + * + * static GtkWidget *top1, *top2; + * + * int main(int argc, char **argv) + * { + * GtkWidget *w; + * GtkTextBuffer *buf; + * + * gtk_init(&argc, &argv); + * + * static char *text = "\ + * It is fine today.\n\ + * It will be fine tomorrow too.\n\ + * It is too hot."; + * + * top1 = gtk_window_new(GTK_WINDOW_TOPLEVEL); + * gtk_window_set_title(GTK_WINDOW(top1), "default"); + * gtk_widget_show(top1); + * w = gtk_text_view_new(); + * gtk_container_add(GTK_CONTAINER(top1), w); + * gtk_widget_show(w); + * buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); + * gtk_text_buffer_insert_at_cursor(buf, text, strlen(text)); + * gtk_text_buffer_add_selection_clipboard(buf, gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY)); + * + * unsetenv("GDK_BACKEND"); + * GdkDisplay *gdpy; + * const char *dpyname2; + * if (strcmp(G_OBJECT_TYPE_NAME(gtk_widget_get_window(top1)), "GdkWaylandWindow") == 0) + * dpyname2 = ":0"; + * else + * dpyname2 = "wayland-0"; + * gdpy = gdk_display_open (dpyname2); + * top2 = gtk_window_new (GTK_WINDOW_TOPLEVEL); + * gtk_window_set_title(GTK_WINDOW(top2), dpyname2); + * gtk_window_set_screen (GTK_WINDOW (top2), gdk_display_get_default_screen(gdpy)); + * gtk_widget_show (top2); + * w = gtk_text_view_new(); + * gtk_container_add(GTK_CONTAINER(top2), w); + * gtk_widget_show(w); + * buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); + * gtk_text_buffer_insert_at_cursor(buf, text, strlen(text)); + * gtk_text_buffer_add_selection_clipboard(buf, gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY)); + * + * gtk_main(); + * + * return 0; + * } + * ---------------- + * + * This code fails if + * GDK_BACKEND=x11 ./test + * and select on both of windows. + * + * ---------------- + * (test:15345): GLib-GObject-CRITICAL **: 01:56:38.041: g_object_ref: assertion 'G_IS_OBJECT (object)' failed + * + * (test:15345): GLib-GObject-CRITICAL **: 01:56:38.042: g_object_ref: assertion 'G_IS_OBJECT (object)' failed + * + * (test:15345): GLib-GObject-CRITICAL **: 01:56:39.113: g_object_ref: assertion 'G_IS_OBJECT (object)' failed + * + * (test:15345): GLib-GObject-CRITICAL **: 01:56:39.113: g_object_ref: assertion 'G_IS_OBJECT (object)' failed + * ---------------- + * (gtk-3.24.10) + * + * This function checks whether selections work by the number of displays. + * If you use more than 2 displays, then selection is disabled. + */ + + GdkDisplayManager *dpyman = gdk_display_manager_get (); + GSList *list = gdk_display_manager_list_displays (dpyman); + int len = g_slist_length (list); + g_slist_free (list); + return len < 2; +} + /* ========================================================================== Lisp Defuns @@ -228,6 +315,9 @@ nil, it defaults to the selected frame.*/) check_window_system (NULL); + if (!pgtk_selection_usable ()) + return Qnil; + if (NILP (frame)) frame = selected_frame; if (!FRAME_LIVE_P (XFRAME (frame)) || !FRAME_PGTK_P (XFRAME (frame))) error ("pgtk selection unavailable for this frame"); @@ -308,6 +398,9 @@ On PGTK, the TIME-OBJECT is unused. */) struct frame *f = frame_for_pgtk_selection (terminal); GtkClipboard *cb; + if (!pgtk_selection_usable ()) + return Qnil; + if (!f) return Qnil; @@ -337,6 +430,9 @@ On Nextstep, TERMINAL is unused. */) struct frame *f = frame_for_pgtk_selection (terminal); GtkClipboard *cb; + if (!pgtk_selection_usable ()) + return Qnil; + if (!f) return Qnil; @@ -368,6 +464,9 @@ On Nextstep, TERMINAL is unused. */) GObject *obj; GQuark quark_data, quark_size; + if (!pgtk_selection_usable ()) + return Qnil; + cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection); selection_type_to_quarks(gtk_clipboard_get_selection(cb), &quark_data, &quark_size); @@ -406,6 +505,9 @@ On PGTK, TIME-STAMP is unused. */) if (!f) error ("PGTK selection unavailable for this frame"); + if (!pgtk_selection_usable ()) + return Qnil; + cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection_symbol); gchar *s = gtk_clipboard_wait_for_text(cb); diff --git a/src/pgtkterm.c b/src/pgtkterm.c index 50ee31e45c3..92f1409ede7 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -2975,6 +2975,8 @@ pgtk_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, int win_x, win_y; GdkSeat *seat; GdkDevice *device; + GdkModifierType mask; + GdkWindow *win; block_input (); @@ -2988,32 +2990,33 @@ pgtk_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, dpyinfo->last_mouse_scroll_bar = NULL; - seat = gdk_display_get_default_seat(dpyinfo->gdpy); - device = gdk_seat_get_pointer(seat); - if (gui_mouse_grabbed (dpyinfo)) { - GdkWindow *win; - GdkModifierType mask; - /* get x, y relative to edit window of f1. */ + /* 1.1. use last_mouse_frame as frame where the pointer is on. */ f1 = dpyinfo->last_mouse_frame; - win = gtk_widget_get_window(FRAME_GTK_WIDGET(f1)); - win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask); } else { - GdkWindow *win; - GdkModifierType mask; - /* 1. get frame where the pointer is on. */ + f1 = *fp; + /* 1.2. get frame where the pointer is on. */ win = gtk_widget_get_window(FRAME_GTK_WIDGET(*fp)); + seat = gdk_display_get_default_seat(dpyinfo->gdpy); + device = gdk_seat_get_pointer(seat); win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask); if (win != NULL) f1 = pgtk_any_window_to_frame(win); - else + else { + // crossing display server? f1 = SELECTED_FRAME(); - - /* 2. get x, y relative to edit window of the frame. */ - win = gtk_widget_get_window(FRAME_GTK_WIDGET(f1)); - win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask); + } } + /* 2. get the display and the device. */ + win = gtk_widget_get_window(FRAME_GTK_WIDGET(f1)); + GdkDisplay *gdpy = gdk_window_get_display (win); + seat = gdk_display_get_default_seat(gdpy); + device = gdk_seat_get_pointer(seat); + + /* 3. get x, y relative to edit window of the frame. */ + win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask); + if (f1 != NULL) { dpyinfo = FRAME_DISPLAY_INFO (f1); remember_mouse_glyph (f1, win_x, win_y, &dpyinfo->last_mouse_glyph); -- 2.39.5