Rewrite PGTK selection code from scratch

* src/frame.c (delete_frame): Clear selections and swallow
special events.

* src/keyboard.c (kbd_buffer_get_event, process_special_events):
Also handle selection events on PGTK.

* src/keyboard.h (union buffered_input_event): Include selection
events on PGTK.

* src/pgtkselect.c (symbol_to_gtk_clipboard, LOCAL_SELECTION):
New functions and macros.
(selection_type_to_quarks, get_func, clear_func): Delete
functions.
(pgtk_selection_init, pgtk_selection_lost):
(pgtk_selection_usable): New functions.
(Fpgtk_own_selection_internal, Fpgtk_disown_selection_internal)
(Fpgtk_selection_exists_p, Fpgtk_selection_owner_p)
(Fpgtk_get_selection_internal): Complete rewrite.
(syms_of_pgtkselect): Update defsyms and add more hooks.

* src/pgtkselect.h: Delete file.

* src/pgtkterm.c (evq_enqueue): Set last user time based on the
event.
(pgtk_any_window_to_frame, button_event): Fix coding style.
(pgtk_set_event_handler): Add selection events.
(pgtk_find_selection_owner, pgtk_selection_event): New
functions.
(pgtk_term_init): Remove call to `pgtk_selection_init'.

* src/pgtkterm.h (struct pgtk_display_info): New field
`display'.
(enum selection_input_event): New struct.  New macros for
accessing its fields.
This commit is contained in:
Po Lu 2022-06-21 22:03:42 +08:00
parent b1af8c2c00
commit be35c92c90
7 changed files with 1863 additions and 398 deletions

View file

@ -2176,6 +2176,17 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
x_clear_frame_selections (f);
#endif
#ifdef HAVE_PGTK
if (FRAME_PGTK_P (f))
{
/* Do special selection events now, in case the window gets
destroyed by this deletion. Does this run Lisp code? */
swallow_events (false);
pgtk_clear_frame_selections (f);
}
#endif
/* Free glyphs.
This function must be called before the window tree of the
frame is deleted because windows contain dynamically allocated

View file

@ -4003,14 +4003,19 @@ kbd_buffer_get_event (KBOARD **kbp,
case SELECTION_REQUEST_EVENT:
case SELECTION_CLEAR_EVENT:
{
#ifdef HAVE_X11
#if defined HAVE_X11 || HAVE_PGTK
/* Remove it from the buffer before processing it,
since otherwise swallow_events will see it
and process it again. */
struct selection_input_event copy = event->sie;
kbd_fetch_ptr = next_kbd_event (event);
input_pending = readable_events (0);
#ifdef HAVE_X11
x_handle_selection_event (&copy);
#else
pgtk_handle_selection_event (&copy);
#endif
#else
/* We're getting selection request events, but we don't have
a window system. */
@ -4381,7 +4386,7 @@ process_special_events (void)
if (event->kind == SELECTION_REQUEST_EVENT
|| event->kind == SELECTION_CLEAR_EVENT)
{
#ifdef HAVE_X11
#if defined HAVE_X11 || defined HAVE_PGTK
/* Remove the event from the fifo buffer before processing;
otherwise swallow_events called recursively could see it
@ -4406,7 +4411,12 @@ process_special_events (void)
moved_events * sizeof *kbd_fetch_ptr);
kbd_fetch_ptr = next_kbd_event (kbd_fetch_ptr);
input_pending = readable_events (0);
#ifdef HAVE_X11
x_handle_selection_event (&copy);
#else
pgtk_handle_selection_event (&copy);
#endif
#else
/* We're getting selection request events, but we don't have
a window system. */

View file

@ -27,6 +27,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
# include "xterm.h" /* for struct selection_input_event */
#endif
#ifdef HAVE_PGTK
#include "pgtkterm.h" /* for struct selection_input_event */
#endif
INLINE_HEADER_BEGIN
/* Most code should use this macro to access Lisp fields in struct kboard. */
@ -226,7 +230,7 @@ union buffered_input_event
{
ENUM_BF (event_kind) kind : EVENT_KIND_WIDTH;
struct input_event ie;
#ifdef HAVE_X11
#if defined HAVE_X11 || defined HAVE_PGTK
struct selection_input_event sie;
#endif
};

File diff suppressed because it is too large Load diff

View file

@ -1,31 +0,0 @@
/* Definitions and headers for selection of pure Gtk+3.
Copyright (C) 1989, 1993, 2005, 2008-2022 Free Software Foundation,
Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "dispextern.h"
#include "frame.h"
#ifdef HAVE_PGTK
#include <gtk/gtk.h>
extern void pgtk_selection_init (void);
extern void pgtk_selection_lost (GtkWidget *, GdkEventSelection *, gpointer);
#endif /* HAVE_PGTK */

View file

@ -61,7 +61,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "buffer.h"
#include "font.h"
#include "xsettings.h"
#include "pgtkselect.h"
#include "emacsgtkfixed.h"
#ifdef GDK_WINDOWING_WAYLAND
@ -290,6 +289,9 @@ static void
evq_enqueue (union buffered_input_event *ev)
{
struct event_queue_t *evq = &event_q;
struct frame *frame;
struct pgtk_display_info *dpyinfo;
if (evq->cap == 0)
{
evq->cap = 4;
@ -303,6 +305,27 @@ evq_enqueue (union buffered_input_event *ev)
}
evq->q[evq->nr++] = *ev;
if (ev->ie.kind != SELECTION_REQUEST_EVENT
&& ev->ie.kind != SELECTION_CLEAR_EVENT)
{
frame = NULL;
if (WINDOWP (ev->ie.frame_or_window))
frame = WINDOW_XFRAME (XWINDOW (ev->ie.frame_or_window));
if (FRAMEP (ev->ie.frame_or_window))
frame = XFRAME (ev->ie.frame_or_window);
if (frame)
{
dpyinfo = FRAME_DISPLAY_INFO (frame);
if (dpyinfo->last_user_time < ev->ie.timestamp)
dpyinfo->last_user_time = ev->ie.timestamp;
}
}
raise (SIGIO);
}
@ -4809,16 +4832,16 @@ pgtk_any_window_to_frame (GdkWindow *window)
return NULL;
FOR_EACH_FRAME (tail, frame)
{
if (found)
break;
f = XFRAME (frame);
if (FRAME_PGTK_P (f))
{
if (pgtk_window_is_of_frame (f, window))
found = f;
}
}
{
if (found)
break;
f = XFRAME (frame);
if (FRAME_PGTK_P (f))
{
if (pgtk_window_is_of_frame (f, window))
found = f;
}
}
return found;
}
@ -5868,8 +5891,7 @@ construct_mouse_click (struct input_event *result,
}
static gboolean
button_event (GtkWidget *widget,
GdkEvent *event,
button_event (GtkWidget *widget, GdkEvent *event,
gpointer *user_data)
{
union buffered_input_event inev;
@ -6174,6 +6196,8 @@ pgtk_monitors_changed_cb (GdkScreen *screen, gpointer user_data)
evq_enqueue (&inev);
}
static gboolean pgtk_selection_event (GtkWidget *, GdkEvent *, gpointer);
void
pgtk_set_event_handler (struct frame *f)
{
@ -6225,14 +6249,20 @@ pgtk_set_event_handler (struct frame *f)
G_CALLBACK (button_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "scroll-event",
G_CALLBACK (scroll_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-clear-event",
G_CALLBACK (pgtk_selection_lost), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "configure-event",
G_CALLBACK (configure_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "drag-data-received",
G_CALLBACK (drag_data_received), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "draw",
G_CALLBACK (pgtk_handle_draw), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "property-notify-event",
G_CALLBACK (pgtk_selection_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-clear-event",
G_CALLBACK (pgtk_selection_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-request-event",
G_CALLBACK (pgtk_selection_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-notify-event",
G_CALLBACK (pgtk_selection_event), NULL);
g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "event",
G_CALLBACK (pgtk_handle_event), NULL);
}
@ -6292,6 +6322,73 @@ same_x_server (const char *name1, const char *name2)
&& (*name2 == '.' || *name2 == '\0'));
}
static struct frame *
pgtk_find_selection_owner (GdkWindow *window)
{
Lisp_Object tail, tem;
struct frame *f;
FOR_EACH_FRAME (tail, tem)
{
f = XFRAME (tem);
if (FRAME_PGTK_P (f)
&& (FRAME_GDK_WINDOW (f) == window))
return f;
}
return NULL;
}
static gboolean
pgtk_selection_event (GtkWidget *widget, GdkEvent *event,
gpointer user_data)
{
struct frame *f;
union buffered_input_event inev;
if (event->type == GDK_PROPERTY_NOTIFY)
pgtk_handle_property_notify (&event->property);
else if (event->type == GDK_SELECTION_CLEAR
|| event->type == GDK_SELECTION_REQUEST)
{
f = pgtk_find_selection_owner (event->selection.window);
if (f)
{
EVENT_INIT (inev.ie);
inev.sie.kind = (event->type == GDK_SELECTION_CLEAR
? SELECTION_CLEAR_EVENT
: SELECTION_REQUEST_EVENT);
SELECTION_EVENT_DPYINFO (&inev.sie) = FRAME_DISPLAY_INFO (f);
SELECTION_EVENT_SELECTION (&inev.sie) = event->selection.selection;
SELECTION_EVENT_TIME (&inev.sie) = event->selection.time;
if (event->type == GDK_SELECTION_REQUEST)
{
/* FIXME: when does GDK destroy the requestor GdkWindow
object?
It would make sense to wait for the transfer to
complete. But I don't know if GDK actually does
that. */
SELECTION_EVENT_REQUESTOR (&inev.sie) = event->selection.requestor;
SELECTION_EVENT_TARGET (&inev.sie) = event->selection.target;
SELECTION_EVENT_PROPERTY (&inev.sie) = event->selection.property;
}
evq_enqueue (&inev);
return TRUE;
}
}
else if (event->type == GDK_SELECTION_NOTIFY)
pgtk_handle_selection_notify (&event->selection);
return FALSE;
}
/* Open a connection to X display DISPLAY_NAME, and return
the structure that describes the open display.
If we cannot contact the display, return null. */
@ -6525,8 +6622,6 @@ pgtk_term_init (Lisp_Object display_name, char *resource_name)
xsettings_initialize (dpyinfo);
pgtk_selection_init ();
pgtk_im_init (dpyinfo);
g_signal_connect (G_OBJECT (dpyinfo->gdpy), "seat-added",

View file

@ -127,8 +127,14 @@ struct pgtk_display_info
/* The generic display parameters corresponding to this PGTK display. */
struct terminal *terminal;
/* This says how to access this display in Gdk. */
GdkDisplay *gdpy;
union
{
/* This says how to access this display through GDK. */
GdkDisplay *gdpy;
/* An alias defined to make porting X code easier. */
GdkDisplay *display;
};
/* This is a cons cell of the form (NAME . FONT-LIST-CACHE). */
Lisp_Object name_list_element;
@ -210,6 +216,9 @@ struct pgtk_display_info
/* Time of last mouse movement. */
Time last_mouse_movement_time;
/* Time of last user interaction. */
guint32 last_user_time;
/* The scroll bar in which the last motion event occurred. */
void *last_mouse_scroll_bar;
@ -443,10 +452,11 @@ enum
FRAME_GTK_OUTER_WIDGET (f) : \
FRAME_GTK_WIDGET (f))
/* aliases */
#define FRAME_PGTK_VIEW(f) FRAME_GTK_WIDGET (f)
#define FRAME_X_WINDOW(f) FRAME_GTK_OUTER_WIDGET (f)
#define FRAME_NATIVE_WINDOW(f) GTK_WINDOW (FRAME_X_WINDOW (f))
#define FRAME_GDK_WINDOW(f) \
(gtk_widget_get_window (FRAME_GTK_WIDGET (f)))
#define FRAME_X_DISPLAY(f) (FRAME_DISPLAY_INFO (f)->gdpy)
@ -484,6 +494,49 @@ enum
#define FRAME_CR_SURFACE_DESIRED_HEIGHT(f) \
((f)->output_data.pgtk->cr_surface_desired_height)
/* If a struct input_event has a kind which is SELECTION_REQUEST_EVENT
or SELECTION_CLEAR_EVENT, then its contents are really described
by this structure. */
/* For an event of kind SELECTION_REQUEST_EVENT,
this structure really describes the contents. */
struct selection_input_event
{
ENUM_BF (event_kind) kind : EVENT_KIND_WIDTH;
struct pgtk_display_info *dpyinfo;
/* We spell it with an "o" here because X does. */
GdkWindow *requestor;
GdkAtom selection, target, property;
guint32 time;
};
/* Unlike macros below, this can't be used as an lvalue. */
INLINE GdkDisplay *
SELECTION_EVENT_DISPLAY (struct selection_input_event *ev)
{
return ev->dpyinfo->display;
}
#define SELECTION_EVENT_DPYINFO(eventp) \
((eventp)->dpyinfo)
/* We spell it with an "o" here because X does. */
#define SELECTION_EVENT_REQUESTOR(eventp) \
((eventp)->requestor)
#define SELECTION_EVENT_SELECTION(eventp) \
((eventp)->selection)
#define SELECTION_EVENT_TARGET(eventp) \
((eventp)->target)
#define SELECTION_EVENT_PROPERTY(eventp) \
((eventp)->property)
#define SELECTION_EVENT_TIME(eventp) \
((eventp)->time)
extern void pgtk_handle_selection_event (struct selection_input_event *);
extern void pgtk_clear_frame_selections (struct frame *);
extern void pgtk_handle_property_notify (GdkEventProperty *);
extern void pgtk_handle_selection_notify (GdkEventSelection *);
/* Display init/shutdown functions implemented in pgtkterm.c */
extern struct pgtk_display_info *pgtk_term_init (Lisp_Object display_name,
char *resource_name);
@ -493,7 +546,7 @@ extern void pgtk_term_shutdown (int sig);
extern void pgtk_clear_frame (struct frame *f);
extern char *pgtk_xlfd_to_fontname (const char *xlfd);
/* Implemented in pgtkfns. */
/* Implemented in pgtkfns.c. */
extern void pgtk_set_doc_edited (void);
extern const char *pgtk_get_defaults_value (const char *key);
extern const char *pgtk_get_string_resource (XrmDatabase rdb,