summaryrefslogtreecommitdiffstats
path: root/src/gui_gtk_x11.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 07:39:57 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 07:40:16 +0000
commit6af24b2457752c0d36aaf9f29f03d39afd09937f (patch)
tree2671b594908d1f971de6b2a2d473f97dfb7291d2 /src/gui_gtk_x11.c
parentReleasing progress-linux version 2:9.1.0016-1~progress7.99u1. (diff)
downloadvim-6af24b2457752c0d36aaf9f29f03d39afd09937f.tar.xz
vim-6af24b2457752c0d36aaf9f29f03d39afd09937f.zip
Merging upstream version 2:9.1.0199.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/gui_gtk_x11.c')
-rw-r--r--src/gui_gtk_x11.c336
1 files changed, 245 insertions, 91 deletions
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 87838b9..d4a8b93 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -116,6 +116,7 @@ enum
TARGET_TEXT,
TARGET_TEXT_URI_LIST,
TARGET_TEXT_PLAIN,
+ TARGET_TEXT_PLAIN_UTF8,
TARGET_VIM,
TARGET_VIMENC
};
@@ -132,7 +133,9 @@ static const GtkTargetEntry selection_targets[] =
{"UTF8_STRING", 0, TARGET_UTF8_STRING},
{"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT},
{"TEXT", 0, TARGET_TEXT},
- {"STRING", 0, TARGET_STRING}
+ {"STRING", 0, TARGET_STRING},
+ {"text/plain;charset=utf-8", 0, TARGET_TEXT_PLAIN_UTF8},
+ {"text/plain", 0, TARGET_TEXT_PLAIN}
};
#define N_SELECTION_TARGETS ARRAY_LENGTH(selection_targets)
@@ -790,6 +793,36 @@ draw_event(GtkWidget *widget UNUSED,
return FALSE;
}
+
+# if GTK_CHECK_VERSION(3,10,0)
+ static gboolean
+scale_factor_event(GtkWidget *widget,
+ GParamSpec* pspec UNUSED,
+ gpointer user_data UNUSED)
+{
+ if (gui.surface != NULL)
+ cairo_surface_destroy(gui.surface);
+
+ int w, h;
+ gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h);
+ gui.surface = gdk_window_create_similar_surface(
+ gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR_ALPHA,
+ w, h);
+
+ int usable_height = h;
+ if (gtk_socket_id != 0)
+ usable_height -= (gui.char_height - (gui.char_height/2)); // sic.
+
+ gui_gtk_form_freeze(GTK_FORM(gui.formwin));
+ gui.force_redraw = 1;
+ gui_resize_shell(w, usable_height);
+ gui_gtk_form_thaw(GTK_FORM(gui.formwin));
+
+ return TRUE;
+}
+# endif // GTK_CHECK_VERSION(3,10,0)
+
#else // !GTK_CHECK_VERSION(3,0,0)
static gint
expose_event(GtkWidget *widget UNUSED,
@@ -1119,11 +1152,14 @@ modifiers_gdk2vim(guint state)
if (state & GDK_MOD1_MASK)
modifiers |= MOD_MASK_ALT;
#if GTK_CHECK_VERSION(2,10,0)
- if (state & GDK_SUPER_MASK)
+ if (state & GDK_META_MASK)
modifiers |= MOD_MASK_META;
-#endif
+ if (state & GDK_SUPER_MASK)
+ modifiers |= MOD_MASK_CMD;
+#else
if (state & GDK_MOD4_MASK)
- modifiers |= MOD_MASK_META;
+ modifiers |= MOD_MASK_CMD;
+#endif
return modifiers;
}
@@ -1528,6 +1564,8 @@ selection_get_cb(GtkWidget *widget UNUSED,
&& info != (guint)TARGET_VIMENC
&& info != (guint)TARGET_VIM
&& info != (guint)TARGET_COMPOUND_TEXT
+ && info != (guint)TARGET_TEXT_PLAIN
+ && info != (guint)TARGET_TEXT_PLAIN_UTF8
&& info != (guint)TARGET_TEXT)
return;
@@ -1659,11 +1697,12 @@ selection_get_cb(GtkWidget *widget UNUSED,
int
gui_mch_early_init_check(int give_message)
{
- char_u *p;
+ char_u *p, *q;
// Guess that when $DISPLAY isn't set the GUI can't start.
p = mch_getenv((char_u *)"DISPLAY");
- if (p == NULL || *p == NUL)
+ q = mch_getenv((char_u *)"WAYLAND_DISPLAY");
+ if ((p == NULL || *p == NUL) && (q == NULL || *q == NUL))
{
gui.dying = TRUE;
if (give_message)
@@ -1696,7 +1735,10 @@ gui_mch_init_check(void)
#if GTK_CHECK_VERSION(3,10,0)
// Vim currently assumes that Gtk means X11, so it cannot use native Gtk
// support for other backends such as Wayland.
- gdk_set_allowed_backends ("x11");
+ //
+ // Use an environment variable to enable unfinished Wayland support.
+ if (getenv("GVIM_ENABLE_WAYLAND") == NULL)
+ gdk_set_allowed_backends ("x11");
#endif
#ifdef FEAT_GUI_GNOME
@@ -2007,15 +2049,26 @@ button_press_event(GtkWidget *widget,
}
/*
- * GTK+ 2 abstracts scrolling via the GdkEventScroll.
+ * GTK+ abstracts scrolling via the GdkEventScroll.
*/
static gboolean
scroll_event(GtkWidget *widget,
GdkEventScroll *event,
gpointer data UNUSED)
{
- int button;
+ int button = 0; // silence gcc
int_u vim_modifiers;
+#if GTK_CHECK_VERSION(3,4,0)
+ static double acc_x, acc_y;
+#if !GTK_CHECK_VERSION(3,22,0)
+ static guint32 last_smooth_event_time;
+#endif
+#define DT_X11 1
+#define DT_WAYLAND 2
+ static int display_type;
+ if (!display_type)
+ display_type = gui_mch_get_display() ? DT_X11 : DT_WAYLAND;
+#endif
if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
gtk_widget_grab_focus(widget);
@@ -2034,6 +2087,29 @@ scroll_event(GtkWidget *widget,
case GDK_SCROLL_RIGHT:
button = MOUSE_6;
break;
+#if GTK_CHECK_VERSION(3,4,0)
+ case GDK_SCROLL_SMOOTH:
+ if (event->is_stop)
+ {
+ acc_x = acc_y = 0;
+ // this event tells us to stop, without an actual moving
+ return FALSE;
+ }
+#if GTK_CHECK_VERSION(3,22,0)
+ if (gdk_device_get_axes(event->device) & GDK_AXIS_FLAG_WHEEL)
+ // this is from a wheel (as oppose to a touchpad / trackpoint)
+#else
+ if (event->time - last_smooth_event_time > 50)
+ // reset our accumulations after 50ms of silence
+#endif
+ acc_x = acc_y = 0;
+ acc_x += event->delta_x;
+ acc_y += event->delta_y;
+#if !GTK_CHECK_VERSION(3,22,0)
+ last_smooth_event_time = event->time;
+#endif
+ break;
+#endif
default: // This shouldn't happen
return FALSE;
}
@@ -2046,8 +2122,42 @@ scroll_event(GtkWidget *widget,
vim_modifiers = modifiers_gdk2mouse(event->state);
- gui_send_mouse_event(button, (int)event->x, (int)event->y,
- FALSE, vim_modifiers);
+#if GTK_CHECK_VERSION(3,4,0)
+ // on x11, despite not requested, when we copy into primary clipboard,
+ // we'll get smooth events. Unsmooth ones will also come along.
+ if (event->direction == GDK_SCROLL_SMOOTH && display_type == DT_WAYLAND)
+ {
+ while (acc_x >= 1.0)
+ { // right
+ acc_x = MAX(0.0, acc_x - 1.0);
+ gui_send_mouse_event(MOUSE_6, (int)event->x, (int)event->y,
+ FALSE, vim_modifiers);
+ }
+ while (acc_x <= -1.0)
+ { // left
+ acc_x = MIN(0.0, acc_x + 1.0);
+ gui_send_mouse_event(MOUSE_7, (int)event->x, (int)event->y,
+ FALSE, vim_modifiers);
+ }
+ while (acc_y >= 1.0)
+ { // down
+ acc_y = MAX(0.0, acc_y - 1.0);
+ gui_send_mouse_event(MOUSE_5, (int)event->x, (int)event->y,
+ FALSE, vim_modifiers);
+ }
+ while (acc_y <= -1.0)
+ { // up
+ acc_y = MIN(0.0, acc_y + 1.0);
+ gui_send_mouse_event(MOUSE_4, (int)event->x, (int)event->y,
+ FALSE, vim_modifiers);
+ }
+ }
+ else
+#undef DT_X11
+#undef DT_WAYLAND
+#endif
+ gui_send_mouse_event(button, (int)event->x, (int)event->y,
+ FALSE, vim_modifiers);
return TRUE;
}
@@ -2501,10 +2611,12 @@ setup_save_yourself(void)
// Fall back to old method
// first get the existing value
- GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
+ Display * dpy = gui_mch_get_display();
+ if (!dpy)
+ return;
- if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win),
- GDK_WINDOW_XID(mainwin_win),
+ GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
+ if (XGetWMProtocols(dpy, GDK_WINDOW_XID(mainwin_win),
&existing_atoms, &count))
{
Atom *new_atoms;
@@ -2612,7 +2724,10 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
// When started with "--echo-wid" argument, write window ID on stdout.
if (echo_wid_arg)
{
- printf("WID: %ld\n", (long)GDK_WINDOW_XID(mainwin_win));
+ if (gui_mch_get_display())
+ printf("WID: %ld\n", (long)GDK_WINDOW_XID(mainwin_win));
+ else
+ printf("WID: 0\n");
fflush(stdout);
}
@@ -2647,27 +2762,30 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
setup_save_yourself();
#ifdef FEAT_CLIENTSERVER
- if (serverName == NULL && serverDelayedStartName != NULL)
+ if (gui_mch_get_display())
{
- // This is a :gui command in a plain vim with no previous server
- commWindow = GDK_WINDOW_XID(mainwin_win);
+ if (serverName == NULL && serverDelayedStartName != NULL)
+ {
+ // This is a :gui command in a plain vim with no previous server
+ commWindow = GDK_WINDOW_XID(mainwin_win);
- (void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win),
- serverDelayedStartName);
- }
- else
- {
- /*
- * Cannot handle "XLib-only" windows with gtk event routines, we'll
- * have to change the "server" registration to that of the main window
- * If we have not registered a name yet, remember the window.
- */
- serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(mainwin_win),
- GDK_WINDOW_XID(mainwin_win));
+ (void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win),
+ serverDelayedStartName);
+ }
+ else
+ {
+ /*
+ * Cannot handle "XLib-only" windows with gtk event routines, we'll
+ * have to change the "server" registration to that of the main window
+ * If we have not registered a name yet, remember the window.
+ */
+ serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(mainwin_win),
+ GDK_WINDOW_XID(mainwin_win));
+ }
+ gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK);
+ g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event",
+ G_CALLBACK(property_event), NULL);
}
- gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK);
- g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event",
- G_CALLBACK(property_event), NULL);
#endif
}
@@ -3533,30 +3651,28 @@ gui_mch_set_curtab(int nr)
* Add selection targets for PRIMARY and CLIPBOARD selections.
*/
void
-gui_gtk_set_selection_targets(void)
+gui_gtk_set_selection_targets(GdkAtom selection)
{
int i, j = 0;
- int n_targets = N_SELECTION_TARGETS;
- GtkTargetEntry targets[N_SELECTION_TARGETS];
+ static int n_targets = N_SELECTION_TARGETS;
+ static GtkTargetEntry targets[N_SELECTION_TARGETS];
- for (i = 0; i < (int)N_SELECTION_TARGETS; ++i)
+ if (targets[0].target == NULL)
{
- // OpenOffice tries to use TARGET_HTML and fails when we don't
- // return something, instead of trying another target. Therefore only
- // offer TARGET_HTML when it works.
- if (!clip_html && selection_targets[i].info == TARGET_HTML)
- n_targets--;
- else
- targets[j++] = selection_targets[i];
+ for (i = 0; i < (int)N_SELECTION_TARGETS; ++i)
+ {
+ // OpenOffice tries to use TARGET_HTML and fails when we don't
+ // return something, instead of trying another target. Therefore only
+ // offer TARGET_HTML when it works.
+ if (!clip_html && selection_targets[i].info == TARGET_HTML)
+ n_targets--;
+ else
+ targets[j++] = selection_targets[i];
+ }
}
- gtk_selection_clear_targets(gui.drawarea, (GdkAtom)GDK_SELECTION_PRIMARY);
- gtk_selection_clear_targets(gui.drawarea, (GdkAtom)clip_plus.gtk_sel_atom);
- gtk_selection_add_targets(gui.drawarea,
- (GdkAtom)GDK_SELECTION_PRIMARY,
- targets, n_targets);
- gtk_selection_add_targets(gui.drawarea,
- (GdkAtom)clip_plus.gtk_sel_atom,
+ gtk_selection_clear_targets(gui.drawarea, selection);
+ gtk_selection_add_targets(gui.drawarea, selection,
targets, n_targets);
}
@@ -3906,17 +4022,37 @@ gui_mch_init(void)
#endif
// Determine which events we will filter.
- gtk_widget_set_events(gui.drawarea,
- GDK_EXPOSURE_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_SCROLL_MASK |
- GDK_KEY_PRESS_MASK |
- GDK_KEY_RELEASE_MASK |
- GDK_POINTER_MOTION_MASK |
- GDK_POINTER_MOTION_HINT_MASK);
+ gint event_mask =
+ GDK_EXPOSURE_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+#if GTK_CHECK_VERSION(3,4,0)
+ if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
+ {
+ // for X11, if we were using smooth scroll events, we
+ // would get an scroll without deltas on the very first user scroll* and
+ // get both "unsmooth" scroll and smooth scroll events after
+ // copying into the primary selection
+ //
+ // *: https://bugzilla.gnome.org/show_bug.cgi?id=675959
+ event_mask |= GDK_SCROLL_MASK;
+ }
+ else
+ {
+ // for Wayland, touchpads don't generate "unsmooth" scroll events. Both
+ // touchpads and wheels generate smooth scroll events expectedly.
+ event_mask |= GDK_SMOOTH_SCROLL_MASK;
+ }
+#else
+ event_mask |= GDK_SCROLL_MASK;
+#endif
+ gtk_widget_set_events(gui.drawarea, event_mask);
gtk_widget_show(gui.drawarea);
gui_gtk_form_put(GTK_FORM(gui.formwin), gui.drawarea, 0, 0);
@@ -4032,20 +4168,7 @@ gui_mch_init(void)
g_signal_connect(G_OBJECT(gui.drawarea), "button-release-event",
G_CALLBACK(button_release_event), NULL);
g_signal_connect(G_OBJECT(gui.drawarea), "scroll-event",
- G_CALLBACK(&scroll_event), NULL);
-
- /*
- * Add selection handler functions.
- */
- g_signal_connect(G_OBJECT(gui.drawarea), "selection-clear-event",
- G_CALLBACK(selection_clear_event), NULL);
- g_signal_connect(G_OBJECT(gui.drawarea), "selection-received",
- G_CALLBACK(selection_received_cb), NULL);
-
- gui_gtk_set_selection_targets();
-
- g_signal_connect(G_OBJECT(gui.drawarea), "selection-get",
- G_CALLBACK(selection_get_cb), NULL);
+ G_CALLBACK(scroll_event), NULL);
// Pretend we don't have input focus, we will get an event if we do.
gui.in_focus = FALSE;
@@ -4527,6 +4650,10 @@ gui_mch_open(void)
#endif
g_signal_connect(G_OBJECT(gui.formwin), "configure-event",
G_CALLBACK(form_configure_event), NULL);
+#if GTK_CHECK_VERSION(3,10,0)
+ g_signal_connect(G_OBJECT(gui.formwin), "notify::scale-factor",
+ G_CALLBACK(scale_factor_event), NULL);
+#endif
#ifdef FEAT_DND
// Set up for receiving DND items.
@@ -4579,6 +4706,17 @@ gui_mch_open(void)
#endif
}
+ /*
+ * Add selection handler functions.
+ */
+ g_signal_connect(G_OBJECT(gui.drawarea), "selection-clear-event",
+ G_CALLBACK(selection_clear_event), NULL);
+ g_signal_connect(G_OBJECT(gui.drawarea), "selection-received",
+ G_CALLBACK(selection_received_cb), NULL);
+
+ g_signal_connect(G_OBJECT(gui.drawarea), "selection-get",
+ G_CALLBACK(selection_get_cb), NULL);
+
return OK;
}
@@ -4599,8 +4737,12 @@ gui_mch_exit(int rc UNUSED)
int
gui_mch_get_winpos(int *x, int *y)
{
- gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y);
- return OK;
+ if (gui_mch_get_display())
+ {
+ gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y);
+ return OK;
+ }
+ return FAIL;
}
/*
@@ -6225,9 +6367,10 @@ gui_mch_haskey(char_u *name)
int
gui_get_x11_windis(Window *win, Display **dis)
{
- if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
+ Display * dpy = gui_mch_get_display();
+ if (dpy)
{
- *dis = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
+ *dis = dpy;
*win = GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin));
return OK;
}
@@ -6238,18 +6381,18 @@ gui_get_x11_windis(Window *win, Display **dis)
}
#endif
-#if defined(FEAT_CLIENTSERVER) \
- || (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO)
-
Display *
gui_mch_get_display(void)
{
- if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
+ if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL
+#if GTK_CHECK_VERSION(3,0,0)
+ && GDK_IS_X11_DISPLAY(gtk_widget_get_display(gui.mainwin))
+#endif
+ )
return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
else
return NULL;
}
-#endif
void
gui_mch_beep(void)
@@ -6911,9 +7054,10 @@ clip_mch_request_selection(Clipboard_T *cbd)
return;
}
- // Final fallback position - use the X CUT_BUFFER0 store
- yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
- cbd);
+ if (gui_mch_get_display())
+ // Final fallback position - use the X CUT_BUFFER0 store
+ yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+ cbd);
}
/*
@@ -6935,10 +7079,18 @@ clip_mch_lose_selection(Clipboard_T *cbd UNUSED)
int
clip_mch_own_selection(Clipboard_T *cbd)
{
+ // If we're blocking autocmds, we are filling the register to offer the
+ // selection (inside selection-get)
+ if (is_autocmd_blocked())
+ return OK;
+
int success;
success = gtk_selection_owner_set(gui.drawarea, cbd->gtk_sel_atom,
gui.event_time);
+ // don't update on every visual selection change
+ if (!(cbd->owned && VIsual_active))
+ gui_gtk_set_selection_targets(cbd->gtk_sel_atom);
gui_mch_update();
return (success) ? OK : FAIL;
}
@@ -7071,9 +7223,11 @@ gui_mch_setmouse(int x, int y)
// Sorry for the Xlib call, but we can't avoid it, since there is no
// internal GDK mechanism present to accomplish this. (and for good
// reason...)
- XWarpPointer(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.drawarea)),
- (Window)0, GDK_WINDOW_XID(gtk_widget_get_window(gui.drawarea)),
- 0, 0, 0U, 0U, x, y);
+ Display * dpy = gui_mch_get_display();
+ if (dpy)
+ XWarpPointer(dpy, (Window)0,
+ GDK_WINDOW_XID(gtk_widget_get_window(gui.drawarea)),
+ 0, 0, 0U, 0U, x, y);
}