summaryrefslogtreecommitdiff
path: root/src/gtkutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gtkutil.c')
-rw-r--r--src/gtkutil.c1506
1 files changed, 1402 insertions, 104 deletions
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 45a78fc382f..718da171f49 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -17,13 +17,6 @@ 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/>. */
-/* FIXME: This code is problematic; it misuses GTK, so the GTK
- developers don't think they should fix the resulting problems in GTK
- itself. The right way to fix this is by rewriting the code in Emacs
- to use GTK3 properly. As of 2020, there is a project to do this.
- Talk with Yuuki Harano <masm+emacs@masm11.me> if you are interested
- in doing substantial work on this. */
-
#include <config.h>
#ifdef USE_GTK
@@ -37,7 +30,14 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "dispextern.h"
#include "frame.h"
#include "systime.h"
+#ifndef HAVE_PGTK
#include "xterm.h"
+#define xp x
+typedef struct x_output xp_output;
+#else
+#define xp pgtk
+typedef struct pgtk_output xp_output;
+#endif
#include "blockinput.h"
#include "window.h"
#include "gtkutil.h"
@@ -47,12 +47,18 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <gdk/gdkkeysyms.h>
+#ifdef HAVE_XINPUT2
+#include <X11/extensions/XInput2.h>
+#endif
+
#ifdef HAVE_XFT
#include <X11/Xft/Xft.h>
#endif
#ifdef HAVE_GTK3
+#ifndef HAVE_PGTK
#include <gtk/gtkx.h>
+#endif
#include "emacsgtkfixed.h"
#endif
@@ -70,6 +76,34 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#define XG_TEXT_OPEN GTK_STOCK_OPEN
#endif
+#ifdef HAVE_GTK3
+static void emacs_menu_bar_get_preferred_width (GtkWidget *, gint *, gint *);
+static GType emacs_menu_bar_get_type (void);
+
+typedef struct _EmacsMenuBar
+{
+ GtkMenuBar parent;
+} EmacsMenuBar;
+
+typedef struct _EmacsMenuBarClass
+{
+ GtkMenuBarClass parent;
+} EmacsMenuBarClass;
+
+G_DEFINE_TYPE (EmacsMenuBar, emacs_menu_bar, GTK_TYPE_MENU_BAR)
+#endif
+
+#ifndef HAVE_PGTK
+static void xg_im_context_commit (GtkIMContext *, gchar *, gpointer);
+static void xg_im_context_preedit_changed (GtkIMContext *, gpointer);
+static void xg_im_context_preedit_end (GtkIMContext *, gpointer);
+static bool xg_widget_key_press_event_cb (GtkWidget *, GdkEvent *, gpointer);
+#endif
+
+#if GTK_CHECK_VERSION (3, 10, 0)
+static void xg_widget_style_updated (GtkWidget *, gpointer);
+#endif
+
#ifndef HAVE_GTK3
#ifdef HAVE_FREETYPE
@@ -108,7 +142,46 @@ struct xg_frame_tb_info
bool xg_gtk_initialized; /* Used to make sure xwidget calls are possible */
#endif
-static GtkWidget * xg_get_widget_from_map (ptrdiff_t idx);
+static GtkWidget *xg_get_widget_from_map (ptrdiff_t idx, Display *dpy);
+
+
+
+#ifdef HAVE_GTK3
+static void
+emacs_menu_bar_init (EmacsMenuBar *menu_bar)
+{
+ return;
+}
+
+static void
+emacs_menu_bar_class_init (EmacsMenuBarClass *klass)
+{
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (klass);
+ widget_class->get_preferred_width = emacs_menu_bar_get_preferred_width;
+}
+
+static void
+emacs_menu_bar_get_preferred_width (GtkWidget *widget,
+ gint *minimum, gint *natural)
+{
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (emacs_menu_bar_parent_class);
+ widget_class->get_preferred_width (widget, minimum, natural);
+
+ if (minimum)
+ *minimum = 0;
+}
+
+static GtkWidget *
+emacs_menu_bar_new (void)
+{
+ return GTK_WIDGET (g_object_new (emacs_menu_bar_get_type (), NULL));
+}
+
+#endif
/***********************************************************************
@@ -127,6 +200,7 @@ static GdkDisplay *gdpy_def;
static void
xg_set_screen (GtkWidget *w, struct frame *f)
{
+#ifndef HAVE_PGTK
if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
{
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
@@ -137,6 +211,17 @@ xg_set_screen (GtkWidget *w, struct frame *f)
else
gtk_window_set_screen (GTK_WINDOW (w), gscreen);
}
+#else
+ if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
+ {
+ GdkScreen *gscreen = gdk_display_get_default_screen (FRAME_X_DISPLAY (f));
+
+ if (GTK_IS_MENU (w))
+ gtk_menu_set_screen (GTK_MENU (w), gscreen);
+ else
+ gtk_window_set_screen (GTK_WINDOW (w), gscreen);
+ }
+#endif
}
@@ -148,12 +233,20 @@ xg_set_screen (GtkWidget *w, struct frame *f)
multiple displays. */
void
+#ifndef HAVE_PGTK
xg_display_open (char *display_name, Display **dpy)
+#else
+xg_display_open (char *display_name, GdkDisplay **dpy)
+#endif
{
GdkDisplay *gdpy;
unrequest_sigio (); /* See comment in x_display_ok, xterm.c. */
+#ifndef HAVE_PGTK
gdpy = gdk_display_open (display_name);
+#else
+ gdpy = gdk_display_open (strlen (display_name) == 0 ? NULL : display_name);
+#endif
request_sigio ();
if (!gdpy_def && gdpy)
{
@@ -162,13 +255,18 @@ xg_display_open (char *display_name, Display **dpy)
gdpy);
}
+#ifndef HAVE_PGTK
*dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
+#else
+ *dpy = gdpy;
+#endif
}
/* Scaling/HiDPI functions. */
static int
xg_get_gdk_scale (void)
{
+#ifdef HAVE_GTK3
const char *sscale = getenv ("GDK_SCALE");
if (sscale)
@@ -177,6 +275,7 @@ xg_get_gdk_scale (void)
if (0 < scale)
return min (scale, INT_MAX);
}
+#endif
return 1;
}
@@ -184,6 +283,9 @@ xg_get_gdk_scale (void)
int
xg_get_scale (struct frame *f)
{
+#ifdef HAVE_PGTK
+ return 1;
+#endif
#ifdef HAVE_GTK3
if (FRAME_GTK_WIDGET (f))
return gtk_widget_get_scale_factor (FRAME_GTK_WIDGET (f));
@@ -194,8 +296,13 @@ xg_get_scale (struct frame *f)
/* Close display DPY. */
void
+#ifndef HAVE_PGTK
xg_display_close (Display *dpy)
+#else
+xg_display_close (GdkDisplay *gdpy)
+#endif
{
+#ifndef HAVE_PGTK
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
/* If this is the default display, try to change it before closing.
@@ -219,6 +326,31 @@ xg_display_close (Display *dpy)
}
gdk_display_close (gdpy);
+
+#else
+
+ /* If this is the default display, try to change it before closing.
+ If there is no other display to use, gdpy_def is set to NULL, and
+ the next call to xg_display_open resets the default display. */
+ if (gdk_display_get_default () == gdpy)
+ {
+ struct pgtk_display_info *dpyinfo;
+ GdkDisplay *gdpy_new = NULL;
+
+ /* Find another display. */
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ if (dpyinfo->gdpy != gdpy)
+ {
+ gdpy_new = dpyinfo->gdpy;
+ gdk_display_manager_set_default_display (gdk_display_manager_get (),
+ gdpy_new);
+ break;
+ }
+ gdpy_def = gdpy_new;
+ }
+
+ gdk_display_close (gdpy);
+#endif
}
@@ -230,12 +362,19 @@ xg_display_close (Display *dpy)
scroll bars on display DPY. */
GdkCursor *
+#ifndef HAVE_PGTK
xg_create_default_cursor (Display *dpy)
+#else
+xg_create_default_cursor (GdkDisplay *gdpy)
+#endif
{
+#ifndef HAVE_PGTK
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
+#endif
return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
}
+#ifndef HAVE_PGTK
/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
static GdkPixbuf *
@@ -335,6 +474,8 @@ xg_get_pixbuf_from_surface (cairo_surface_t *surface)
}
#endif /* USE_CAIRO && !HAVE_GTK3 */
+#endif /* !HAVE_PGTK */
+
static Lisp_Object
file_for_image (Lisp_Object image)
{
@@ -605,8 +746,13 @@ xg_check_special_colors (struct frame *f,
block_input ();
{
#ifdef HAVE_GTK3
+#ifndef HAVE_PGTK
GtkStyleContext *gsty
= gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
+#else
+ GtkStyleContext *gsty
+ = gtk_widget_get_style_context (FRAME_WIDGET (f));
+#endif
GdkRGBA col;
char buf[sizeof "rgb://rrrr/gggg/bbbb"];
int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
@@ -630,9 +776,14 @@ xg_check_special_colors (struct frame *f,
r = col.red * 65535,
g = col.green * 65535,
b = col.blue * 65535;
+#ifndef HAVE_PGTK
sprintf (buf, "rgb:%04x/%04x/%04x", r, g, b);
success_p = x_parse_color (f, buf, color) != 0;
#else
+ sprintf (buf, "#%04x%04x%04x", r, g, b);
+ success_p = pgtk_parse_color (f, buf, color) != 0;
+#endif
+#else
GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
GdkColor *grgb = get_bg
? &gsty->bg[GTK_STATE_SELECTED]
@@ -655,6 +806,9 @@ xg_check_special_colors (struct frame *f,
/***********************************************************************
Tooltips
***********************************************************************/
+
+#ifndef HAVE_PGTK
+
/* Gtk+ calls this callback when the parent of our tooltip dummy changes.
We use that to pop down the tooltip. This happens if Gtk+ for some
reason wants to change or hide the tooltip. */
@@ -665,7 +819,7 @@ hierarchy_ch_cb (GtkWidget *widget,
gpointer user_data)
{
struct frame *f = user_data;
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
if (! top || ! GTK_IS_WINDOW (top))
@@ -687,7 +841,7 @@ qttip_cb (GtkWidget *widget,
gpointer user_data)
{
struct frame *f = user_data;
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->ttip_widget == NULL)
{
GtkWidget *p;
@@ -734,7 +888,7 @@ xg_prepare_tooltip (struct frame *f,
int *width,
int *height)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkWidget *widget;
GdkWindow *gwin;
GdkScreen *screen;
@@ -785,13 +939,19 @@ xg_prepare_tooltip (struct frame *f,
void
xg_show_tooltip (struct frame *f, int root_x, int root_y)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->ttip_window)
{
block_input ();
+#ifndef HAVE_PGTK
gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
root_y / xg_get_scale (f));
gtk_widget_show (GTK_WIDGET (x->ttip_window));
+#else
+ gtk_widget_show (GTK_WIDGET (x->ttip_window));
+ gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
+ root_y / xg_get_scale (f));
+#endif
unblock_input ();
}
}
@@ -803,10 +963,9 @@ xg_show_tooltip (struct frame *f, int root_x, int root_y)
bool
xg_hide_tooltip (struct frame *f)
{
- if (f->output_data.x->ttip_window)
+ if (f->output_data.xp->ttip_window)
{
- GtkWindow *win = f->output_data.x->ttip_window;
-
+ GtkWindow *win = f->output_data.xp->ttip_window;
block_input ();
gtk_widget_hide (GTK_WIDGET (win));
@@ -824,6 +983,30 @@ xg_hide_tooltip (struct frame *f)
return FALSE;
}
+#else /* HAVE_PGTK */
+
+void
+xg_show_tooltip (struct frame *f,
+ Lisp_Object string)
+{
+ Lisp_Object encoded_string = ENCODE_UTF_8 (string);
+ gtk_widget_set_tooltip_text (FRAME_GTK_OUTER_WIDGET (f)
+ ? FRAME_GTK_OUTER_WIDGET (f)
+ : FRAME_GTK_WIDGET (f),
+ SSDATA (encoded_string));
+}
+
+bool
+xg_hide_tooltip (struct frame *f)
+{
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ gtk_widget_set_tooltip_text (FRAME_GTK_OUTER_WIDGET (f), NULL);
+ gtk_widget_set_tooltip_text (FRAME_GTK_WIDGET (f), NULL);
+ return TRUE;
+}
+
+#endif /* HAVE_PGTK */
+
/***********************************************************************
General functions for creating widgets, resizing, events, e.t.c.
@@ -839,6 +1022,27 @@ my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
}
#endif
+#if defined HAVE_GTK3 && defined HAVE_XINPUT2
+bool
+xg_is_menu_window (Display *dpy, Window wdesc)
+{
+ GtkWidget *gwdesc = xg_win_to_widget (dpy, wdesc);
+
+ if (GTK_IS_WINDOW (gwdesc))
+ {
+ GtkWidget *fw = gtk_bin_get_child (GTK_BIN (gwdesc));
+ if (GTK_IS_MENU (fw))
+ {
+ GtkWidget *parent
+ = gtk_menu_shell_get_parent_shell (GTK_MENU_SHELL (fw));
+ return GTK_IS_MENU_BAR (parent);
+ }
+ }
+
+ return false;
+}
+#endif
+
/* Make a geometry string and pass that to GTK. It seems this is the
only way to get geometry position right if the user explicitly
asked for a position when starting Emacs.
@@ -954,8 +1158,23 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
bool was_visible = false;
bool hide_child_frame;
+#ifndef HAVE_PGTK
gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
&gwidth, &gheight);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ &gwidth, &gheight);
+ }
+ else
+ {
+ GtkAllocation alloc;
+ gtk_widget_get_allocation (FRAME_GTK_WIDGET (f), &alloc);
+ gwidth = alloc.width;
+ gheight = alloc.height;
+ }
+#endif
/* Do this before resize, as we don't know yet if we will be resized. */
FRAME_RIF (f)->clear_under_internal_border (f);
@@ -975,32 +1194,83 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
remain unchanged but giving the frame back its normal size will
be broken ... */
if (EQ (fullscreen, Qfullwidth) && width == FRAME_PIXEL_WIDTH (f))
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
gwidth, outer_height);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ gwidth, outer_height);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ gwidth, outer_height);
+ }
+#endif
else if (EQ (fullscreen, Qfullheight) && height == FRAME_PIXEL_HEIGHT (f))
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
outer_width, gheight);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ outer_width, gheight);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ outer_width, gheight);
+ }
+#endif
else if (FRAME_PARENT_FRAME (f) && FRAME_VISIBLE_P (f))
{
was_visible = true;
+#ifndef HAVE_PGTK
hide_child_frame = EQ (x_gtk_resize_child_frames, Qhide);
+#else
+ hide_child_frame = false;
+#endif
if (outer_width != gwidth || outer_height != gheight)
{
if (hide_child_frame)
{
block_input ();
+#ifndef HAVE_PGTK
gtk_widget_hide (FRAME_GTK_OUTER_WIDGET (f));
+#else
+ gtk_widget_hide (FRAME_WIDGET (f));
+#endif
unblock_input ();
}
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
outer_width, outer_height);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ outer_width, outer_height);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ outer_width, outer_height);
+ }
+#endif
if (hide_child_frame)
{
block_input ();
+#ifndef HAVE_PGTK
gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
+#else
+ gtk_widget_show_all (FRAME_WIDGET (f));
+#endif
unblock_input ();
}
@@ -1009,8 +1279,21 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
}
else
{
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
outer_width, outer_height);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ outer_width, outer_height);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ outer_width, outer_height);
+ }
+#endif
fullscreen = Qnil;
}
@@ -1035,7 +1318,9 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
/* Must call this to flush out events */
(void)gtk_events_pending ();
gdk_flush ();
+#ifndef HAVE_PGTK
x_wait_for_event (f, ConfigureNotify);
+#endif
if (!NILP (fullscreen))
/* Try to restore fullscreen state. */
@@ -1068,11 +1353,12 @@ xg_height_or_width_changed (struct frame *f)
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
FRAME_TOTAL_PIXEL_WIDTH (f),
FRAME_TOTAL_PIXEL_HEIGHT (f));
- f->output_data.x->hint_flags = 0;
+ f->output_data.xp->hint_flags = 0;
x_wm_set_size_hint (f, 0, 0);
}
#endif
+#ifndef HAVE_PGTK
/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
Must be done like this, because GtkWidget:s can have "hidden"
X Window that aren't accessible.
@@ -1100,6 +1386,7 @@ xg_win_to_widget (Display *dpy, Window wdesc)
unblock_input ();
return gwdesc;
}
+#endif
/* Set the background of widget W to PIXEL. */
@@ -1107,9 +1394,18 @@ static void
xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
{
#ifdef HAVE_GTK3
- XColor xbg;
+ Emacs_Color xbg;
xbg.pixel = pixel;
+#ifndef HAVE_PGTK
if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
+#else
+ xbg.red = (pixel >> 16) & 0xff;
+ xbg.green = (pixel >> 8) & 0xff;
+ xbg.blue = (pixel >> 0) & 0xff;
+ xbg.red |= xbg.red << 8;
+ xbg.green |= xbg.green << 8;
+ xbg.blue |= xbg.blue << 8;
+#endif
{
const char format[] = "* { background-color: #%02x%02x%02x; }";
/* The format is always longer than the resulting string. */
@@ -1144,7 +1440,16 @@ style_changed_cb (GObject *go,
struct input_event event;
GdkDisplay *gdpy = user_data;
const char *display_name = gdk_display_get_name (gdpy);
+#ifndef HAVE_PGTK
Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
+#else
+ GdkDisplay *dpy = gdpy;
+#endif
+
+#ifndef HAVE_PGTK
+ if (display_name == NULL)
+ display_name = "";
+#endif
EVENT_INIT (event);
event.kind = CONFIG_CHANGED_EVENT;
@@ -1165,7 +1470,11 @@ style_changed_cb (GObject *go,
{
struct frame *f = XFRAME (frame);
if (FRAME_LIVE_P (f)
+#ifndef HAVE_PGTK
&& FRAME_X_P (f)
+#else
+ && FRAME_PGTK_P (f)
+#endif
&& FRAME_X_DISPLAY (f) == dpy)
{
FRAME_TERMINAL (f)->set_scroll_bar_default_width_hook (f);
@@ -1179,6 +1488,7 @@ style_changed_cb (GObject *go,
/* Called when a delete-event occurs on WIDGET. */
+#ifndef HAVE_PGTK
static gboolean
delete_cb (GtkWidget *widget,
GdkEvent *event,
@@ -1186,6 +1496,7 @@ delete_cb (GtkWidget *widget,
{
return TRUE;
}
+#endif
/* Create and set up the GTK widgets for frame F.
Return true if creation succeeded. */
@@ -1199,17 +1510,36 @@ xg_create_frame_widgets (struct frame *f)
#ifndef HAVE_GTK3
GtkRcStyle *style;
#endif
+#ifndef HAVE_PGTK
+ GtkIMContext *imc;
+#endif
+ GtkWindowType type = GTK_WINDOW_TOPLEVEL;
char *title = 0;
block_input ();
+#ifndef HAVE_PGTK /* gtk_plug not found. */
if (FRAME_X_EMBEDDED_P (f))
{
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
- wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
+ wtop = gtk_plug_new_for_display (gdpy, f->output_data.xp->parent_desc);
}
else
- wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ wtop = gtk_window_new (type);
+#else
+ if (f->tooltip)
+ {
+ type = GTK_WINDOW_POPUP;
+ }
+ wtop = gtk_window_new (type);
+ gtk_widget_add_events (wtop, GDK_ALL_EVENTS_MASK);
+#endif
+
+ gtk_widget_set_app_paintable (wtop, f->alpha_background != 1.0);
+#if GTK_CHECK_VERSION (3, 10, 0)
+ g_signal_connect (G_OBJECT (wtop), "style-updated",
+ G_CALLBACK (xg_widget_style_updated), f);
+#endif
/* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
has backported it to Gtk+ 2.0 and they add the resize grip for
@@ -1266,8 +1596,8 @@ xg_create_frame_widgets (struct frame *f)
FRAME_GTK_OUTER_WIDGET (f) = wtop;
FRAME_GTK_WIDGET (f) = wfixed;
- f->output_data.x->vbox_widget = wvbox;
- f->output_data.x->hbox_widget = whbox;
+ f->output_data.xp->vbox_widget = wvbox;
+ f->output_data.xp->hbox_widget = whbox;
gtk_widget_set_has_window (wfixed, TRUE);
@@ -1282,11 +1612,11 @@ xg_create_frame_widgets (struct frame *f)
with regular X drawing primitives, so from a GTK/GDK point of
view, the widget is totally blank. When an expose comes, this
will make the widget blank, and then Emacs redraws it. This flickers
- a lot, so we turn off double buffering.
- FIXME: gtk_widget_set_double_buffered is deprecated and might stop
- working in the future. We need to migrate away from combining
- X and GTK+ drawing to a pure GTK+ build. */
+ a lot, so we turn off double buffering. */
+
+#ifndef HAVE_PGTK
gtk_widget_set_double_buffered (wfixed, FALSE);
+#endif
#if ! GTK_CHECK_VERSION (3, 22, 0)
gtk_window_set_wmclass (GTK_WINDOW (wtop),
@@ -1294,10 +1624,12 @@ xg_create_frame_widgets (struct frame *f)
SSDATA (Vx_resource_class));
#endif
+#ifndef HAVE_PGTK
/* Add callback to do nothing on WM_DELETE_WINDOW. The default in
GTK is to destroy the widget. We want Emacs to do that instead. */
g_signal_connect (G_OBJECT (wtop), "delete-event",
G_CALLBACK (delete_cb), f);
+#endif
/* Convert our geometry parameters into a geometry string
and specify it.
@@ -1308,7 +1640,9 @@ xg_create_frame_widgets (struct frame *f)
gtk_widget_add_events (wfixed,
GDK_POINTER_MOTION_MASK
+#ifndef HAVE_PGTK
| GDK_EXPOSURE_MASK
+#endif
| GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_KEY_PRESS_MASK
@@ -1316,13 +1650,34 @@ xg_create_frame_widgets (struct frame *f)
| GDK_LEAVE_NOTIFY_MASK
| GDK_FOCUS_CHANGE_MASK
| GDK_STRUCTURE_MASK
+#ifdef HAVE_PGTK
+ | GDK_SCROLL_MASK
+ | GDK_SMOOTH_SCROLL_MASK
+#endif
| GDK_VISIBILITY_NOTIFY_MASK);
+ GdkScreen *screen = gtk_widget_get_screen (wtop);
+
+#if !defined HAVE_PGTK
+ GdkVisual *visual = gdk_x11_screen_lookup_visual (screen,
+ XVisualIDFromVisual (FRAME_X_VISUAL (f)));
+
+ if (!visual)
+ emacs_abort ();
+#else
+ GdkVisual *visual = gdk_screen_get_rgba_visual (screen);
+#endif
+
+ gtk_widget_set_visual (wtop, visual);
+ gtk_widget_set_visual (wfixed, visual);
+
+#ifndef HAVE_PGTK
/* Must realize the windows so the X window gets created. It is used
by callers of this function. */
gtk_widget_realize (wfixed);
FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
initial_set_up_x_back_buffer (f);
+#endif
/* Since GTK clears its window by filling with the background color,
we must keep X and GTK background in sync. */
@@ -1337,8 +1692,12 @@ xg_create_frame_widgets (struct frame *f)
/* Must use g_strdup because gtk_widget_modify_style does g_free. */
style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
gtk_widget_modify_style (wfixed, style);
+ gtk_widget_set_can_focus (wfixed, TRUE);
#else
gtk_widget_set_can_focus (wfixed, TRUE);
+#ifdef HAVE_PGTK
+ gtk_widget_grab_focus (wfixed);
+#endif
gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
#endif
@@ -1351,14 +1710,31 @@ xg_create_frame_widgets (struct frame *f)
}
/* Steal a tool tip window we can move ourselves. */
- f->output_data.x->ttip_widget = 0;
- f->output_data.x->ttip_lbl = 0;
- f->output_data.x->ttip_window = 0;
+ f->output_data.xp->ttip_widget = 0;
+ f->output_data.xp->ttip_lbl = 0;
+ f->output_data.xp->ttip_window = 0;
+#ifndef HAVE_PGTK
gtk_widget_set_tooltip_text (wtop, "Dummy text");
g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
+ imc = gtk_im_multicontext_new ();
+ g_object_ref (imc);
+ gtk_im_context_set_use_preedit (imc, TRUE);
+
+ g_signal_connect (G_OBJECT (imc), "commit",
+ G_CALLBACK (xg_im_context_commit), f);
+ g_signal_connect (G_OBJECT (imc), "preedit-changed",
+ G_CALLBACK (xg_im_context_preedit_changed), NULL);
+ g_signal_connect (G_OBJECT (imc), "preedit-end",
+ G_CALLBACK (xg_im_context_preedit_end), NULL);
+ FRAME_X_OUTPUT (f)->im_context = imc;
+
+ g_signal_connect (G_OBJECT (wfixed), "key-press-event",
+ G_CALLBACK (xg_widget_key_press_event_cb),
+ NULL);
+#endif
+
{
- GdkScreen *screen = gtk_widget_get_screen (wtop);
GtkSettings *gs = gtk_settings_get_for_screen (screen);
/* Only connect this signal once per screen. */
if (! g_signal_handler_find (G_OBJECT (gs),
@@ -1378,12 +1754,114 @@ xg_create_frame_widgets (struct frame *f)
return 1;
}
+#ifdef HAVE_PGTK
+void
+xg_create_frame_outer_widgets (struct frame *f)
+{
+ GtkWidget *wtop;
+ GtkWidget *wvbox, *whbox;
+ GtkWindowType type = GTK_WINDOW_TOPLEVEL;
+ char *title = 0;
+
+ block_input ();
+
+ wtop = gtk_window_new (type);
+ gtk_widget_add_events (wtop, GDK_ALL_EVENTS_MASK);
+
+ xg_set_screen (wtop, f);
+
+ wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
+ gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
+
+ /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
+ gtk_widget_set_name (wtop, EMACS_CLASS);
+ gtk_widget_set_name (wvbox, "pane");
+
+ /* If this frame has a title or name, set it in the title bar. */
+ if (! NILP (f->title))
+ title = SSDATA (ENCODE_UTF_8 (f->title));
+ else if (! NILP (f->name))
+ title = SSDATA (ENCODE_UTF_8 (f->name));
+
+ if (title)
+ gtk_window_set_title (GTK_WINDOW (wtop), title);
+
+ if (FRAME_UNDECORATED (f))
+ {
+ gtk_window_set_decorated (GTK_WINDOW (wtop), FALSE);
+ store_frame_param (f, Qundecorated, Qt);
+ }
+
+ FRAME_GTK_OUTER_WIDGET (f) = wtop;
+ f->output_data.xp->vbox_widget = wvbox;
+ f->output_data.xp->hbox_widget = whbox;
+
+ gtk_container_add (GTK_CONTAINER (wtop), wvbox);
+ gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
+
+ if (FRAME_EXTERNAL_TOOL_BAR (f))
+ update_frame_tool_bar (f);
+
+#if ! GTK_CHECK_VERSION (3, 22, 0)
+ gtk_window_set_wmclass (GTK_WINDOW (wtop),
+ SSDATA (Vx_resource_name),
+ SSDATA (Vx_resource_class));
+#endif
+
+ /* Convert our geometry parameters into a geometry string
+ and specify it.
+ GTK will itself handle calculating the real position this way. */
+ xg_set_geometry (f);
+ f->win_gravity
+ = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+
+ gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
+
+ if (FRAME_OVERRIDE_REDIRECT (f))
+ {
+ GdkWindow *gwin = gtk_widget_get_window (wtop);
+
+ if (gwin)
+ gdk_window_set_override_redirect (gwin, TRUE);
+ }
+
+ /* Steal a tool tip window we can move ourselves. */
+ f->output_data.xp->ttip_widget = 0;
+ f->output_data.xp->ttip_lbl = 0;
+ f->output_data.xp->ttip_window = 0;
+#ifndef HAVE_PGTK
+ gtk_widget_set_tooltip_text (wtop, "Dummy text");
+ g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
+#endif
+
+ {
+ GdkScreen *screen = gtk_widget_get_screen (wtop);
+ GtkSettings *gs = gtk_settings_get_for_screen (screen);
+ /* Only connect this signal once per screen. */
+ if (! g_signal_handler_find (G_OBJECT (gs),
+ G_SIGNAL_MATCH_FUNC,
+ 0, 0, 0,
+ (gpointer) G_CALLBACK (style_changed_cb),
+ 0))
+ {
+ g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
+ G_CALLBACK (style_changed_cb),
+ gdk_screen_get_display (screen));
+ }
+ }
+
+ unblock_input ();
+}
+#endif
+
void
xg_free_frame_widgets (struct frame *f)
{
if (FRAME_GTK_OUTER_WIDGET (f))
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
struct xg_frame_tb_info *tbinfo
= g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
TB_INFO_KEY);
@@ -1391,10 +1869,15 @@ xg_free_frame_widgets (struct frame *f)
xfree (tbinfo);
/* x_free_frame_resources should have taken care of it */
+#ifndef HAVE_PGTK
eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
+ g_object_unref (FRAME_X_OUTPUT (f)->im_context);
+#endif
gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
+#ifndef HAVE_PGTK
FRAME_X_RAW_DRAWABLE (f) = 0;
+#endif
FRAME_GTK_OUTER_WIDGET (f) = 0;
if (x->ttip_widget)
{
@@ -1436,9 +1919,12 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
XSETFRAME (frame, f);
fs_state = Fframe_parameter (frame, Qfullscreen);
- if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
- (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
- x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
+ if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth))
+#ifndef HAVE_PGTK
+ && (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
+ x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen))
+#endif
+ )
{
/* Don't set hints when maximized or fullscreen. Apparently KWin and
Gtk3 don't get along and the frame shrinks (!).
@@ -1449,14 +1935,14 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
if (flags)
{
memset (&size_hints, 0, sizeof (size_hints));
- f->output_data.x->size_hints = size_hints;
- f->output_data.x->hint_flags = hint_flags;
+ f->output_data.xp->size_hints = size_hints;
+ f->output_data.xp->hint_flags = hint_flags;
}
else
flags = f->size_hint_flags;
- size_hints = f->output_data.x->size_hints;
- hint_flags = f->output_data.x->hint_flags;
+ size_hints = f->output_data.xp->size_hints;
+ hint_flags = f->output_data.xp->hint_flags;
hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
@@ -1500,12 +1986,12 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
else if (win_gravity == StaticGravity)
size_hints.win_gravity = GDK_GRAVITY_STATIC;
- if (x_gtk_use_window_move)
- {
- if (flags & PPosition) hint_flags |= GDK_HINT_POS;
- if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
- if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
- }
+ if (flags & PPosition)
+ hint_flags |= GDK_HINT_POS;
+ if (flags & USPosition)
+ hint_flags |= GDK_HINT_USER_POS;
+ if (flags & USSize)
+ hint_flags |= GDK_HINT_USER_SIZE;
if (user_position)
{
@@ -1518,16 +2004,16 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
size_hints.width_inc /= scale;
size_hints.height_inc /= scale;
- if (hint_flags != f->output_data.x->hint_flags
+ if (hint_flags != f->output_data.xp->hint_flags
|| memcmp (&size_hints,
- &f->output_data.x->size_hints,
+ &f->output_data.xp->size_hints,
sizeof (size_hints)) != 0)
{
block_input ();
gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
NULL, &size_hints, hint_flags);
- f->output_data.x->size_hints = size_hints;
- f->output_data.x->hint_flags = hint_flags;
+ f->output_data.xp->size_hints = size_hints;
+ f->output_data.xp->hint_flags = hint_flags;
unblock_input ();
}
}
@@ -1552,8 +2038,8 @@ xg_set_background_color (struct frame *f, unsigned long bg)
!NILP (bar);
bar = XSCROLL_BAR (bar)->next)
{
- GtkWidget *scrollbar =
- xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window);
+ GtkWidget *scrollbar = xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window,
+ FRAME_X_DISPLAY (f));
GtkWidget *webox = gtk_widget_get_parent (scrollbar);
xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
}
@@ -1567,6 +2053,10 @@ xg_set_background_color (struct frame *f, unsigned long bg)
void
xg_set_undecorated (struct frame *f, Lisp_Object undecorated)
{
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
if (FRAME_GTK_WIDGET (f))
{
block_input ();
@@ -1593,7 +2083,11 @@ xg_frame_restack (struct frame *f1, struct frame *f2, bool above_flag)
XSETFRAME (frame2, f2);
gdk_window_restack (gwin1, gwin2, above_flag);
+#ifndef HAVE_PGTK
x_sync (f1);
+#else
+ gdk_flush ();
+#endif
}
unblock_input ();
}
@@ -1604,10 +2098,17 @@ void
xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar)
{
block_input ();
+#ifndef HAVE_PGTK
if (FRAME_GTK_WIDGET (f))
gdk_window_set_skip_taskbar_hint
(gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
NILP (skip_taskbar) ? FALSE : TRUE);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ gdk_window_set_skip_taskbar_hint
+ (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
+ NILP (skip_taskbar) ? FALSE : TRUE);
+#endif
unblock_input ();
}
@@ -1616,6 +2117,10 @@ xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar)
void
xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map)
{
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
block_input ();
if (FRAME_GTK_WIDGET (f))
{
@@ -1631,12 +2136,19 @@ xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map)
void
xg_set_no_accept_focus (struct frame *f, Lisp_Object no_accept_focus)
{
+ gboolean g_no_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE;
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ {
+ if (FRAME_WIDGET (f))
+ gtk_widget_set_can_focus (FRAME_WIDGET (f), g_no_accept_focus);
+ return;
+ }
+#endif
block_input ();
if (FRAME_GTK_WIDGET (f))
{
GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
- gboolean g_no_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE;
-
gtk_window_set_accept_focus (gwin, g_no_accept_focus);
}
unblock_input ();
@@ -1657,18 +2169,24 @@ xg_set_override_redirect (struct frame *f, Lisp_Object override_redirect)
unblock_input ();
}
+#ifndef HAVE_PGTK
/* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
functions so GTK does not overwrite the icon. */
void
xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
{
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
icon_pixmap,
icon_mask);
if (gp)
gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
}
+#endif
@@ -1919,7 +2437,7 @@ xg_maybe_add_timer (gpointer data)
static int
xg_dialog_run (struct frame *f, GtkWidget *w)
{
- ptrdiff_t count = SPECPDL_INDEX ();
+ specpdl_ref count = SPECPDL_INDEX ();
struct xg_dialog_data dd;
xg_set_screen (w, f);
@@ -2205,6 +2723,11 @@ xg_get_file_name (struct frame *f,
int filesel_done = 0;
xg_get_file_func func;
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ error ("Can't open dialog from child frames");
+#endif
+
#ifdef HAVE_GTK_FILE_SELECTION_NEW
if (xg_uses_old_file_dialog ())
@@ -2237,20 +2760,34 @@ xg_get_file_name (struct frame *f,
#ifdef HAVE_GTK3
-#define XG_WEIGHT_TO_SYMBOL(w) \
- (w <= PANGO_WEIGHT_THIN ? Qextra_light \
- : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
- : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
- : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
- : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
- : w <= PANGO_WEIGHT_BOLD ? Qbold \
- : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
- : Qultra_bold)
-
-#define XG_STYLE_TO_SYMBOL(s) \
- (s == PANGO_STYLE_OBLIQUE ? Qoblique \
- : s == PANGO_STYLE_ITALIC ? Qitalic \
- : Qnormal)
+static
+Lisp_Object xg_weight_to_symbol (PangoWeight w)
+{
+ return
+ (w <= PANGO_WEIGHT_THIN ? Qthin /* 100 */
+ : w <= PANGO_WEIGHT_ULTRALIGHT ? Qultra_light /* 200 */
+ : w <= PANGO_WEIGHT_LIGHT ? Qlight /* 300 */
+#if PANGO_VERSION_CHECK(1, 36, 7)
+ : w <= PANGO_WEIGHT_SEMILIGHT ? Qsemi_light /* 350 */
+#endif
+ : w <= PANGO_WEIGHT_BOOK ? Qbook /* 380 */
+ : w <= PANGO_WEIGHT_NORMAL ? Qnormal /* 400 */
+ : w <= PANGO_WEIGHT_MEDIUM ? Qmedium /* 500 */
+ : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold /* 600 */
+ : w <= PANGO_WEIGHT_BOLD ? Qbold /* 700 */
+ : w <= PANGO_WEIGHT_ULTRABOLD ? Qultra_bold /* 800 */
+ : w <= PANGO_WEIGHT_HEAVY ? Qblack /* 900 */
+ : Qultra_heavy); /* 1000 */
+}
+
+static
+Lisp_Object xg_style_to_symbol (PangoStyle s)
+{
+ return
+ (s == PANGO_STYLE_OBLIQUE ? Qoblique
+ : s == PANGO_STYLE_ITALIC ? Qitalic
+ : Qnormal);
+}
#endif /* HAVE_GTK3 */
@@ -2288,6 +2825,11 @@ xg_get_font (struct frame *f, const char *default_name)
int done = 0;
Lisp_Object font = Qnil;
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ error ("Can't open dialog from child frames");
+#endif
+
w = gtk_font_chooser_dialog_new
("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
@@ -2341,8 +2883,8 @@ xg_get_font (struct frame *f, const char *default_name)
font = CALLN (Ffont_spec,
QCfamily, build_string (family),
QCsize, make_float (pango_units_to_double (size)),
- QCweight, XG_WEIGHT_TO_SYMBOL (weight),
- QCslant, XG_STYLE_TO_SYMBOL (style));
+ QCweight, xg_weight_to_symbol (weight),
+ QCslant, xg_style_to_symbol (style));
char *font_desc_str = pango_font_description_to_string (desc);
dupstring (&x_last_font_name, font_desc_str);
@@ -2485,7 +3027,7 @@ xg_mark_data (void)
{
struct frame *f = XFRAME (frame);
- if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
+ if ((FRAME_X_P (f) || FRAME_PGTK_P (f)) && FRAME_GTK_OUTER_WIDGET (f))
{
struct xg_frame_tb_info *tbinfo
= g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
@@ -2497,6 +3039,16 @@ xg_mark_data (void)
}
}
}
+
+#ifndef HAVE_PGTK
+ if (xg_pending_quit_event.kind != NO_EVENT)
+ {
+ eassert (xg_pending_quit_event.kind == ASCII_KEYSTROKE_EVENT);
+
+ mark_object (xg_pending_quit_event.frame_or_window);
+ mark_object (xg_pending_quit_event.arg);
+ }
+#endif
}
/* Callback called when a menu item is destroyed. Used to free data.
@@ -2649,6 +3201,11 @@ make_menu_item (const char *utf8_label,
if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
+#ifdef HAVE_PGTK
+ if (!NILP (item->help))
+ gtk_widget_set_tooltip_text (w, SSDATA (item->help));
+#endif
+
return w;
}
@@ -2715,6 +3272,25 @@ xg_create_one_menuitem (widget_value *item,
return w;
}
+#ifdef HAVE_PGTK
+static gboolean
+menu_bar_button_pressed_cb (GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
+{
+ struct frame *f = user_data;
+
+ if (event->button.button < 4
+ && event->button.window != gtk_widget_get_window (widget)
+ && !popup_activated ())
+ {
+ pgtk_menu_set_in_use (true);
+ set_frame_menubar (f, true);
+ }
+
+ return false;
+}
+#endif
+
/* Create a full menu tree specified by DATA.
F is the frame the created menu belongs to.
SELECT_CB is the callback to use when a menu item is selected.
@@ -2771,7 +3347,16 @@ create_menus (widget_value *data,
}
else
{
+#ifndef HAVE_GTK3
wmenu = gtk_menu_bar_new ();
+#else
+ wmenu = emacs_menu_bar_new ();
+#endif
+
+#ifdef HAVE_PGTK
+ g_signal_connect (G_OBJECT (wmenu), "button-press-event",
+ G_CALLBACK (menu_bar_button_pressed_cb), f);
+#endif
/* Set width of menu bar to a small value so it doesn't enlarge
a small initial frame size. The width will be set to the
width of the frame later on when it is added to a container.
@@ -2788,9 +3373,15 @@ create_menus (widget_value *data,
if (name)
gtk_widget_set_name (wmenu, name);
+#ifndef HAVE_PGTK
if (deactivate_cb)
g_signal_connect (G_OBJECT (wmenu),
"selection-done", deactivate_cb, 0);
+#else
+ if (deactivate_cb)
+ g_signal_connect (G_OBJECT (wmenu),
+ "deactivate", deactivate_cb, 0);
+#endif
}
for (item = data; item; item = item->next)
@@ -3512,8 +4103,9 @@ menubar_map_cb (GtkWidget *w, gpointer user_data)
void
xg_update_frame_menubar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkRequisition req;
+ int scale = xg_get_scale (f);
if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
return;
@@ -3531,9 +4123,21 @@ xg_update_frame_menubar (struct frame *f)
gtk_widget_show_all (x->menubar_widget);
gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
req.height *= xg_get_scale (f);
- if (FRAME_MENUBAR_HEIGHT (f) != req.height)
+
+#if !defined HAVE_PGTK && defined HAVE_GTK3
+ if (FRAME_DISPLAY_INFO (f)->n_planes == 32)
{
- FRAME_MENUBAR_HEIGHT (f) = req.height;
+ GdkScreen *screen = gtk_widget_get_screen (x->menubar_widget);
+ GdkVisual *visual = gdk_screen_get_system_visual (screen);
+
+ gtk_widget_realize (x->menubar_widget);
+ gtk_widget_set_visual (x->menubar_widget, visual);
+ }
+#endif
+
+ if (FRAME_MENUBAR_HEIGHT (f) != (req.height * scale))
+ {
+ FRAME_MENUBAR_HEIGHT (f) = req.height * scale;
adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
}
unblock_input ();
@@ -3545,7 +4149,7 @@ xg_update_frame_menubar (struct frame *f)
void
free_frame_menubar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->menubar_widget)
{
@@ -3561,6 +4165,7 @@ free_frame_menubar (struct frame *f)
}
}
+#ifndef HAVE_PGTK
bool
xg_event_is_for_menubar (struct frame *f, const XEvent *event)
{
@@ -3575,6 +4180,18 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
if (! x->menubar_widget) return 0;
+#ifdef HAVE_XINPUT2
+ XIDeviceEvent *xev = (XIDeviceEvent *) event->xcookie.data;
+ if (event->type == GenericEvent) /* XI_ButtonPress or XI_ButtonRelease or a touch event. */
+ {
+ if (! (xev->event_x >= 0
+ && xev->event_x < FRAME_PIXEL_WIDTH (f)
+ && xev->event_y >= 0
+ && xev->event_y < FRAME_MENUBAR_HEIGHT (f)))
+ return 0;
+ }
+ else
+#endif
if (! (event->xbutton.x >= 0
&& event->xbutton.x < FRAME_PIXEL_WIDTH (f)
&& event->xbutton.y >= 0
@@ -3583,7 +4200,12 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
return 0;
gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
- gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
+#ifdef HAVE_XINPUT2
+ if (event->type == GenericEvent)
+ gw = gdk_x11_window_lookup_for_display (gdpy, xev->event);
+ else
+#endif
+ gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
if (! gw) return 0;
gevent.any.window = gw;
gevent.any.type = GDK_NOTHING;
@@ -3597,8 +4219,21 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
if (! list) return 0;
int scale = xg_get_scale (f);
- rec.x = event->xbutton.x / scale;
- rec.y = event->xbutton.y / scale;
+#ifdef HAVE_XINPUT2
+ if (event->type == GenericEvent)
+ {
+ rec.x = xev->event_x / scale;
+ rec.y = xev->event_y / scale;
+ }
+ else
+ {
+#endif
+ rec.x = event->xbutton.x / scale;
+ rec.y = event->xbutton.y / scale;
+#ifdef HAVE_XINPUT2
+ }
+#endif
+
rec.width = 1;
rec.height = 1;
@@ -3611,6 +4246,7 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
g_list_free (list);
return iter != 0;
}
+#endif
@@ -3628,6 +4264,8 @@ bool xg_ignore_gtk_scrollbar;
static int scroll_bar_width_for_theme;
static int scroll_bar_height_for_theme;
+#if defined HAVE_PGTK || !defined HAVE_GTK3
+
/* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
may be larger than 32 bits. Keep a mapping from integer index to widget
pointers to get around the 32 bit limitation. */
@@ -3699,7 +4337,7 @@ xg_remove_widget_from_map (ptrdiff_t idx)
/* Get the widget pointer at IDX from id_to_widget. */
static GtkWidget *
-xg_get_widget_from_map (ptrdiff_t idx)
+xg_get_widget_from_map (ptrdiff_t idx, Display *dpy)
{
if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
return id_to_widget.widgets[idx];
@@ -3707,6 +4345,42 @@ xg_get_widget_from_map (ptrdiff_t idx)
return 0;
}
+#else
+static void
+find_scrollbar_cb (GtkWidget *widget, gpointer user_data)
+{
+ GtkWidget **scroll_bar = user_data;
+
+ if (GTK_IS_SCROLLBAR (widget))
+ *scroll_bar = widget;
+}
+
+static GtkWidget *
+xg_get_widget_from_map (ptrdiff_t window, Display *dpy)
+{
+ GtkWidget *gwdesc, *scroll_bar = NULL;
+ GdkWindow *gdkwin;
+
+ gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
+ (Window) window);
+ if (gdkwin)
+ {
+ GdkEvent event;
+ event.any.window = gdkwin;
+ event.any.type = GDK_NOTHING;
+ gwdesc = gtk_get_event_widget (&event);
+
+ if (gwdesc && GTK_IS_EVENT_BOX (gwdesc))
+ gtk_container_forall (GTK_CONTAINER (gwdesc),
+ find_scrollbar_cb, &scroll_bar);
+ }
+ else
+ return NULL;
+
+ return scroll_bar;
+}
+#endif
+
static void
update_theme_scrollbar_width (void)
{
@@ -3766,6 +4440,7 @@ xg_get_default_scrollbar_height (struct frame *f)
return scroll_bar_width_for_theme * xg_get_scale (f);
}
+#ifndef HAVE_GTK3
/* Return the scrollbar id for X Window WID on display DPY.
Return -1 if WID not in id_to_widget. */
@@ -3786,17 +4461,46 @@ xg_get_scroll_id_for_window (Display *dpy, Window wid)
return -1;
}
+#endif
/* Callback invoked when scroll bar WIDGET is destroyed.
DATA is the index into id_to_widget for WIDGET.
We free pointer to last scroll bar values here and remove the index. */
+#if !defined HAVE_GTK3 || defined HAVE_PGTK
static void
xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
{
intptr_t id = (intptr_t) data;
xg_remove_widget_from_map (id);
}
+#endif
+
+#if defined HAVE_GTK3 && !defined HAVE_PGTK
+static void
+xg_scroll_bar_size_allocate_cb (GtkWidget *widget,
+ GdkRectangle *allocation,
+ gpointer user_data)
+{
+ GdkEvent *event = gtk_get_current_event ();
+ GdkEvent dummy;
+
+ if (event && event->any.type == GDK_CONFIGURE)
+ x_scroll_bar_configure (event);
+ else
+ {
+ /* These are the only fields used by x_scroll_bar_configure. */
+ dummy.configure.send_event = FALSE;
+ dummy.configure.x = allocation->x;
+ dummy.configure.y = allocation->y;
+ dummy.configure.width = allocation->width;
+ dummy.configure.height = allocation->height;
+ dummy.configure.window = gtk_widget_get_window (widget);
+
+ x_scroll_bar_configure (&dummy);
+ }
+}
+#endif
static void
xg_finish_scroll_bar_creation (struct frame *f,
@@ -3807,19 +4511,32 @@ xg_finish_scroll_bar_creation (struct frame *f,
const char *scroll_bar_name)
{
GtkWidget *webox = gtk_event_box_new ();
+#ifdef HAVE_GTK3
+ GtkCssProvider *foreground_provider;
+ GtkCssProvider *background_provider;
+#endif
gtk_widget_set_name (wscroll, scroll_bar_name);
#ifndef HAVE_GTK3
gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
#endif
- g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
+ g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer) f);
+#if defined HAVE_GTK3 && !defined HAVE_PGTK
+ g_signal_connect (G_OBJECT (webox), "size-allocate",
+ G_CALLBACK (xg_scroll_bar_size_allocate_cb),
+ NULL);
+#endif
+
+#if defined HAVE_PGTK || !defined HAVE_GTK3
ptrdiff_t scroll_id = xg_store_widget_in_map (wscroll);
g_signal_connect (G_OBJECT (wscroll),
"destroy",
G_CALLBACK (xg_gtk_scroll_destroy),
(gpointer) scroll_id);
+#endif
+
g_signal_connect (G_OBJECT (wscroll),
"change-value",
scroll_callback,
@@ -3835,7 +4552,7 @@ xg_finish_scroll_bar_creation (struct frame *f,
also, which causes flicker. Put an event box between the edit widget
and the scroll bar, so the scroll bar instead draws itself on the
event box window. */
- gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
+ gtk_fixed_put (GTK_FIXED (f->output_data.xp->edit_widget), webox, -1, -1);
gtk_container_add (GTK_CONTAINER (webox), wscroll);
xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
@@ -3845,12 +4562,37 @@ xg_finish_scroll_bar_creation (struct frame *f,
real X window, it and its scroll-bar child try to draw on the
Emacs main window, which we draw over using Xlib. */
gtk_widget_realize (webox);
+#ifdef HAVE_PGTK
+ gtk_widget_show_all (webox);
+#elif defined HAVE_GTK3
+ bar->x_window = GTK_WIDGET_TO_X_WIN (webox);
+ gtk_widget_show_all (webox);
+#else
GTK_WIDGET_TO_X_WIN (webox);
+#endif
/* Set the cursor to an arrow. */
xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
+#ifdef HAVE_GTK3
+ GtkStyleContext *ctxt = gtk_widget_get_style_context (wscroll);
+ foreground_provider = FRAME_OUTPUT_DATA (f)->scrollbar_foreground_css_provider;
+ background_provider = FRAME_OUTPUT_DATA (f)->scrollbar_background_css_provider;
+
+ gtk_style_context_add_provider (ctxt, GTK_STYLE_PROVIDER (foreground_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ gtk_style_context_add_provider (ctxt, GTK_STYLE_PROVIDER (background_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+
+#ifndef HAVE_PGTK
+ gtk_widget_add_events (webox, GDK_STRUCTURE_MASK);
+ gtk_widget_set_double_buffered (wscroll, FALSE);
+#endif
+#endif
+
+#if defined HAVE_PGTK || !defined HAVE_GTK3
bar->x_window = scroll_id;
+#endif
}
/* Create a scroll bar widget for frame F. Store the scroll bar
@@ -3924,7 +4666,8 @@ xg_create_horizontal_scroll_bar (struct frame *f,
void
xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
{
- GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
+ GtkWidget *w = xg_get_widget_from_map (scrollbar_id,
+ FRAME_X_DISPLAY (f));
if (w)
{
GtkWidget *wparent = gtk_widget_get_parent (w);
@@ -3947,11 +4690,15 @@ xg_update_scrollbar_pos (struct frame *f,
int width,
int height)
{
- GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
+ GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id,
+ FRAME_X_DISPLAY (f));
if (wscroll)
{
- GtkWidget *wfixed = f->output_data.x->edit_widget;
+ GtkWidget *wfixed = f->output_data.xp->edit_widget;
GtkWidget *wparent = gtk_widget_get_parent (wscroll);
+#if !defined HAVE_PGTK && defined HAVE_GTK3
+ GdkWindow *wdesc = gtk_widget_get_window (wparent);
+#endif
gint msl;
int scale = xg_get_scale (f);
@@ -3984,29 +4731,53 @@ xg_update_scrollbar_pos (struct frame *f,
{
gtk_widget_show_all (wparent);
gtk_widget_set_size_request (wscroll, width, height);
+
+#if !defined HAVE_PGTK && defined HAVE_GTK3
+ if (wdesc)
+ {
+ gdk_window_move_resize (wdesc, left, top, width, height);
+#if GTK_CHECK_VERSION (3, 20, 0)
+ gtk_widget_queue_allocate (wparent);
+#endif
+ }
+#endif
}
+
if (oldx != -1 && oldw > 0 && oldh > 0)
{
/* Clear under old scroll bar position. */
oldw += (scale - 1) * oldw;
oldx -= (scale - 1) * oldw;
+#ifndef HAVE_PGTK
x_clear_area (f, oldx, oldy, oldw, oldh);
+#else
+ pgtk_clear_area (f, oldx, oldy, oldw, oldh);
+#endif
}
if (!hidden)
{
- GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id);
+ GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id,
+ FRAME_X_DISPLAY (f));
GtkWidget *webox = gtk_widget_get_parent (scrollbar);
+#ifndef HAVE_PGTK
/* Don't obscure any child frames. */
XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
+#else
+ gdk_window_lower (gtk_widget_get_window (webox));
+#endif
}
/* GTK does not redraw until the main loop is entered again, but
if there are no X events pending we will not enter it. So we sync
here to get some events. */
+#ifndef HAVE_PGTK
x_sync (f);
+#else
+ gdk_flush ();
+#endif
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
@@ -4026,13 +4797,16 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
int width,
int height)
{
-
- GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
+ GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id,
+ FRAME_X_DISPLAY (f));
if (wscroll)
{
- GtkWidget *wfixed = f->output_data.x->edit_widget;
+ GtkWidget *wfixed = f->output_data.xp->edit_widget;
GtkWidget *wparent = gtk_widget_get_parent (wscroll);
+#if !defined HAVE_PGTK && defined HAVE_GTK3
+ GdkWindow *wdesc = gtk_widget_get_window (wparent);
+#endif
gint msl;
int scale = xg_get_scale (f);
@@ -4064,10 +4838,24 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
{
gtk_widget_show_all (wparent);
gtk_widget_set_size_request (wscroll, width, height);
+
+#if !defined HAVE_PGTK && defined HAVE_GTK3
+ if (wdesc)
+ {
+ gdk_window_move_resize (wdesc, left, top, width, height);
+#if GTK_CHECK_VERSION (3, 20, 0)
+ gtk_widget_queue_allocate (wparent);
+#endif
+ }
+#endif
}
if (oldx != -1 && oldw > 0 && oldh > 0)
/* Clear under old scroll bar position. */
+#ifndef HAVE_PGTK
x_clear_area (f, oldx, oldy, oldw, oldh);
+#else
+ pgtk_clear_area (f, oldx, oldy, oldw, oldh);
+#endif
/* GTK does not redraw until the main loop is entered again, but
if there are no X events pending we will not enter it. So we sync
@@ -4075,14 +4863,22 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
{
GtkWidget *scrollbar =
- xg_get_widget_from_map (scrollbar_id);
+ xg_get_widget_from_map (scrollbar_id, FRAME_X_DISPLAY (f));
GtkWidget *webox = gtk_widget_get_parent (scrollbar);
+#ifndef HAVE_PGTK
/* Don't obscure any child frames. */
XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
+#else
+ gdk_window_lower (gtk_widget_get_window (webox));
+#endif
}
+#ifndef HAVE_PGTK
x_sync (f);
+#else
+ gdk_flush ();
+#endif
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
@@ -4107,9 +4903,10 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
int position,
int whole)
{
- GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
-
struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+ GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window,
+ FRAME_X_DISPLAY (f));
+
if (wscroll && bar->dragging == -1)
{
@@ -4194,7 +4991,9 @@ xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
int position,
int whole)
{
- GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
+ struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+ GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window,
+ FRAME_X_DISPLAY (f));
if (wscroll && bar->dragging == -1)
{
@@ -4226,14 +5025,38 @@ xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
frame. This function does additional checks. */
bool
-xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
+xg_event_is_for_scrollbar (struct frame *f, const EVENT *event,
+ bool for_valuator)
{
bool retval = 0;
- if (f && event->type == ButtonPress && event->xbutton.button < 4)
+#ifdef HAVE_XINPUT2
+ XIDeviceEvent *xev = (XIDeviceEvent *) event->xcookie.data;
+ if (f && ((FRAME_DISPLAY_INFO (f)->supports_xi2
+ && event->type == GenericEvent
+ && (event->xgeneric.extension
+ == FRAME_DISPLAY_INFO (f)->xi2_opcode)
+ && (event->xgeneric.evtype == XI_ButtonPress
+ && xev->detail < 4))
+ || (event->type == ButtonPress
+ && event->xbutton.button < 4)
+ || for_valuator))
+#else
+ if (f
+#ifndef HAVE_PGTK
+ && event->type == ButtonPress && event->xbutton.button < 4
+#else
+ && event->type == GDK_BUTTON_PRESS && event->button.button < 4
+#endif
+ )
+#endif /* HAVE_XINPUT2 */
{
/* Check if press occurred outside the edit widget. */
+#ifndef HAVE_PGTK
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+#else
+ GdkDisplay *gdpy = FRAME_X_DISPLAY (f);
+#endif
GdkWindow *gwin;
#ifdef HAVE_GTK3
#if GTK_CHECK_VERSION (3, 20, 0)
@@ -4247,11 +5070,30 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
#else
gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
#endif
- retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
+ retval = gwin != gtk_widget_get_window (f->output_data.xp->edit_widget);
}
+#ifdef HAVE_XINPUT2
+ else if (f && ((FRAME_DISPLAY_INFO (f)->supports_xi2
+ && event->type == GenericEvent
+ && (event->xgeneric.extension
+ == FRAME_DISPLAY_INFO (f)->xi2_opcode)
+ && ((event->xgeneric.evtype == XI_ButtonRelease
+ && xev->detail < 4)
+ || (event->xgeneric.evtype == XI_Motion)))
+ || ((event->type == ButtonRelease
+ && event->xbutton.button < 4)
+ || event->type == MotionNotify)))
+#else
else if (f
+#ifndef HAVE_PGTK
&& ((event->type == ButtonRelease && event->xbutton.button < 4)
- || event->type == MotionNotify))
+ || event->type == MotionNotify)
+#else
+ && ((event->type == GDK_BUTTON_RELEASE && event->button.button < 4)
+ || event->type == GDK_MOTION_NOTIFY)
+#endif
+ )
+#endif /* HAVE_XINPUT2 */
{
/* If we are releasing or moving the scroll bar, it has the grab. */
GtkWidget *w = gtk_grab_get_current ();
@@ -4329,7 +5171,11 @@ draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
struct frame *f = XFRAME (Fnth (make_fixnum (page_nr), frames));
cairo_t *cr = gtk_print_context_get_cairo_context (context);
+#ifndef HAVE_PGTK
x_cr_draw_frame (cr, f);
+#else
+ pgtk_cr_draw_frame (cr, f);
+#endif
}
void
@@ -4430,7 +5276,11 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
/* Convert between the modifier bits GDK uses and the modifier bits
Emacs uses. This assumes GDK and X masks are the same, which they are when
this is written. */
+#ifndef HAVE_PGTK
event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
+#else
+ event.modifiers = pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
+#endif
kbd_buffer_store_event (&event);
/* Return focus to the frame after we have clicked on a detached
@@ -4527,7 +5377,7 @@ xg_tool_bar_item_expose_callback (GtkWidget *w,
static void
xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
GtkWidget *top_widget = x->toolbar_widget;
@@ -4583,7 +5433,7 @@ tb_size_cb (GtkWidget *widget,
static void
xg_create_tool_bar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
#ifdef HAVE_GTK3
GtkStyleContext *gsty;
#endif
@@ -4822,10 +5672,11 @@ xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
static bool
xg_update_tool_bar_sizes (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkRequisition req;
int nl = 0, nr = 0, nt = 0, nb = 0;
GtkWidget *top_widget = x->toolbar_widget;
+ int scale = xg_get_scale (f);
gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
if (x->toolbar_in_hbox)
@@ -4834,8 +5685,10 @@ xg_update_tool_bar_sizes (struct frame *f)
gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
top_widget,
"position", &pos, NULL);
- if (pos == 0) nl = req.width;
- else nr = req.width;
+ if (pos == 0)
+ nl = req.width * scale;
+ else
+ nr = req.width * scale;
}
else
{
@@ -4843,8 +5696,10 @@ xg_update_tool_bar_sizes (struct frame *f)
gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
top_widget,
"position", &pos, NULL);
- if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
- else nb = req.height;
+ if (pos == 0 || (pos == 1 && x->menubar_widget))
+ nt = req.height * scale;
+ else
+ nb = req.height * scale;
}
if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
@@ -4908,7 +5763,7 @@ void
update_frame_tool_bar (struct frame *f)
{
int i, j;
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
int hmargin = 0, vmargin = 0;
GtkToolbar *wtoolbar;
GtkToolItem *ti;
@@ -4923,6 +5778,11 @@ update_frame_tool_bar (struct frame *f)
if (! FRAME_GTK_WIDGET (f))
return;
+#ifdef HAVE_PGTK
+ if (! FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
+
block_input ();
if (RANGED_FIXNUMP (1, Vtool_bar_button_margin, INT_MAX))
@@ -5218,7 +6078,7 @@ update_frame_tool_bar (struct frame *f)
void
free_frame_tool_bar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->toolbar_widget)
{
@@ -5263,7 +6123,7 @@ free_frame_tool_bar (struct frame *f)
void
xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkWidget *top_widget = x->toolbar_widget;
if (! x->toolbar_widget || ! top_widget)
@@ -5312,8 +6172,10 @@ xg_initialize (void)
xg_menu_cb_list.prev = xg_menu_cb_list.next =
xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
+#if defined HAVE_PGTK || !defined HAVE_GTK3
id_to_widget.max_size = id_to_widget.used = 0;
id_to_widget.widgets = 0;
+#endif
settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
(gdk_display_get_default ()));
@@ -5361,4 +6223,440 @@ xg_initialize (void)
#endif
}
+#ifndef HAVE_PGTK
+static void
+xg_add_virtual_mods (struct x_display_info *dpyinfo, GdkEventKey *key)
+{
+ guint modifiers = key->state;
+
+ if (modifiers & dpyinfo->meta_mod_mask)
+ {
+ /* GDK always assumes Mod1 is alt, but that's no reason for
+ us to make that mistake as well. */
+ if (!dpyinfo->alt_mod_mask)
+ key->state |= GDK_MOD1_MASK;
+ else
+ key->state |= GDK_META_MASK;
+ }
+
+ if (modifiers & dpyinfo->alt_mod_mask)
+ key->state |= GDK_MOD1_MASK;
+ if (modifiers & dpyinfo->super_mod_mask)
+ key->state |= GDK_SUPER_MASK;
+ if (modifiers & dpyinfo->hyper_mod_mask)
+ key->state |= GDK_HYPER_MASK;
+}
+
+static unsigned int
+xg_virtual_mods_to_x (struct x_display_info *dpyinfo, guint virtual)
+{
+ unsigned int modifiers = virtual & ~(GDK_SUPER_MASK
+ | GDK_META_MASK
+ | GDK_HYPER_MASK
+ | GDK_MOD2_MASK
+ | GDK_MOD3_MASK
+ | GDK_MOD4_MASK
+ | GDK_MOD5_MASK);
+
+ if (virtual & GDK_META_MASK)
+ modifiers |= dpyinfo->meta_mod_mask;
+ if (virtual & GDK_SUPER_MASK)
+ modifiers |= dpyinfo->super_mod_mask;
+ if (virtual & GDK_HYPER_MASK)
+ modifiers |= dpyinfo->hyper_mod_mask;
+
+ return modifiers;
+}
+
+static void
+xg_im_context_commit (GtkIMContext *imc, gchar *str,
+ gpointer user_data)
+{
+ struct frame *f = user_data;
+ struct input_event ie;
+#ifdef HAVE_XINPUT2
+ struct xi_device_t *source;
+ struct x_display_info *dpyinfo;
+#endif
+
+ EVENT_INIT (ie);
+ /* This used to use g_utf8_to_ucs4_fast, which led to bad results
+ when STR wasn't actually a UTF-8 string, which some input method
+ modules commit. */
+
+ ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+ ie.arg = decode_string_utf_8 (Qnil, str, strlen (str),
+ Qnil, false, Qnil, Qnil);
+
+ /* STR is invalid and not really encoded in UTF-8. */
+ if (NILP (ie.arg))
+ ie.arg = build_unibyte_string (str);
+
+ Fput_text_property (make_fixnum (0),
+ make_fixnum (SCHARS (ie.arg)),
+ Qcoding, Qt, ie.arg);
+
+#ifdef HAVE_XINPUT2
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+
+ /* There is no timestamp associated with commit events, so use the
+ device that sent the last event to be filtered. */
+ if (dpyinfo->pending_keystroke_time)
+ {
+ dpyinfo->pending_keystroke_time = 0;
+ source = xi_device_from_id (dpyinfo,
+ dpyinfo->pending_keystroke_source);
+
+ if (source)
+ ie.device = source->name;
+ }
+#endif
+
+ XSETFRAME (ie.frame_or_window, f);
+ ie.modifiers = 0;
+ ie.timestamp = 0;
+
+ kbd_buffer_store_event (&ie);
+}
+
+static void
+xg_im_context_preedit_changed (GtkIMContext *imc, gpointer user_data)
+{
+ PangoAttrList *list;
+ gchar *str;
+ gint cursor;
+ struct input_event inev;
+
+ gtk_im_context_get_preedit_string (imc, &str, &list, &cursor);
+
+ EVENT_INIT (inev);
+ inev.kind = PREEDIT_TEXT_EVENT;
+ inev.arg = build_string_from_utf8 (str);
+
+ if (SCHARS (inev.arg))
+ Fput_text_property (make_fixnum (min (SCHARS (inev.arg) - 1,
+ max (0, cursor))),
+ make_fixnum (min (SCHARS (inev.arg),
+ max (0, cursor) + 1)),
+ Qcursor, Qt, inev.arg);
+
+ kbd_buffer_store_event (&inev);
+
+ g_free (str);
+ pango_attr_list_unref (list);
+}
+
+static void
+xg_im_context_preedit_end (GtkIMContext *imc, gpointer user_data)
+{
+ struct input_event inev;
+
+ EVENT_INIT (inev);
+ inev.kind = PREEDIT_TEXT_EVENT;
+ inev.arg = Qnil;
+ kbd_buffer_store_event (&inev);
+}
+
+static bool
+xg_widget_key_press_event_cb (GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
+{
+ Lisp_Object tail, tem;
+ struct frame *f = NULL;
+ union buffered_input_event inev;
+ guint keysym = event->key.keyval;
+ unsigned int xstate;
+ gunichar uc;
+#ifdef HAVE_XINPUT2
+ Time pending_keystroke_time;
+ struct xi_device_t *source;
+#endif
+
+ FOR_EACH_FRAME (tail, tem)
+ {
+ if (FRAME_X_P (XFRAME (tem))
+ && (FRAME_GTK_WIDGET (XFRAME (tem)) == widget))
+ {
+ f = XFRAME (tem);
+ break;
+ }
+ }
+
+ if (!f)
+ return true;
+
+#ifdef HAVE_XINPUT2
+ pending_keystroke_time
+ = FRAME_DISPLAY_INFO (f)->pending_keystroke_time;
+
+ if (event->key.time >= pending_keystroke_time)
+ FRAME_DISPLAY_INFO (f)->pending_keystroke_time = 0;
+#endif
+
+ if (!x_gtk_use_native_input
+ && !FRAME_DISPLAY_INFO (f)->prefer_native_input)
+ return true;
+
+ EVENT_INIT (inev.ie);
+ XSETFRAME (inev.ie.frame_or_window, f);
+
+ xstate = xg_virtual_mods_to_x (FRAME_DISPLAY_INFO (f),
+ event->key.state);
+
+ inev.ie.modifiers
+ |= x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), xstate);
+ inev.ie.timestamp = event->key.time;
+
+#ifdef HAVE_XINPUT2
+ if (event->key.time == pending_keystroke_time)
+ {
+ source = xi_device_from_id (FRAME_DISPLAY_INFO (f),
+ FRAME_DISPLAY_INFO (f)->pending_keystroke_source);
+
+ if (source)
+ inev.ie.device = source->name;
+ }
+#endif
+
+ if (event->key.is_modifier)
+ goto done;
+
+#ifndef HAVE_GTK3
+ /* FIXME: event->key.is_modifier is not accurate on GTK 2. */
+
+ if (keysym >= GDK_KEY_Shift_L && keysym <= GDK_KEY_Hyper_R)
+ goto done;
+#endif
+
+ /* First deal with keysyms which have defined
+ translations to characters. */
+ if (keysym >= 32 && keysym < 128)
+ /* Avoid explicitly decoding each ASCII character. */
+ {
+ inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = keysym;
+ goto done;
+ }
+
+ /* Keysyms directly mapped to Unicode characters. */
+ if (keysym >= 0x01000000 && keysym <= 0x0110FFFF)
+ {
+ if (keysym < 0x01000080)
+ inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+ else
+ inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+ inev.ie.code = keysym & 0xFFFFFF;
+ goto done;
+ }
+
+ /* Random non-modifier sorts of keysyms. */
+ if (((keysym >= GDK_KEY_BackSpace && keysym <= GDK_KEY_Escape)
+ || keysym == GDK_KEY_Delete
+#ifdef GDK_KEY_ISO_Left_Tab
+ || (keysym >= GDK_KEY_ISO_Left_Tab && keysym <= GDK_KEY_ISO_Enter)
+#endif
+ || IsCursorKey (keysym) /* 0xff50 <= x < 0xff60 */
+ || IsMiscFunctionKey (keysym) /* 0xff60 <= x < VARIES */
+#ifdef GDK_KEY_dead_circumflex
+ || keysym == GDK_KEY_dead_circumflex
+#endif
+#ifdef GDK_KEY_dead_grave
+ || keysym == GDK_KEY_dead_grave
+#endif
+#ifdef GDK_KEY_dead_tilde
+ || keysym == GDK_KEY_dead_tilde
+#endif
+#ifdef GDK_KEY_dead_diaeresis
+ || keysym == GDK_KEY_dead_diaeresis
+#endif
+#ifdef GDK_KEY_dead_macron
+ || keysym == GDK_KEY_dead_macron
+#endif
+#ifdef GDK_KEY_dead_degree
+ || keysym == GDK_KEY_dead_degree
+#endif
+#ifdef GDK_KEY_dead_acute
+ || keysym == GDK_KEY_dead_acute
+#endif
+#ifdef GDK_KEY_dead_cedilla
+ || keysym == GDK_KEY_dead_cedilla
+#endif
+#ifdef GDK_KEY_dead_breve
+ || keysym == GDK_KEY_dead_breve
+#endif
+#ifdef GDK_KEY_dead_ogonek
+ || keysym == GDK_KEY_dead_ogonek
+#endif
+#ifdef GDK_KEY_dead_caron
+ || keysym == GDK_KEY_dead_caron
+#endif
+#ifdef GDK_KEY_dead_doubleacute
+ || keysym == GDK_KEY_dead_doubleacute
+#endif
+#ifdef GDK_KEY_dead_abovedot
+ || keysym == GDK_KEY_dead_abovedot
+#endif
+ || IsKeypadKey (keysym) /* 0xff80 <= x < 0xffbe */
+ || IsFunctionKey (keysym) /* 0xffbe <= x < 0xffe1 */
+ /* Any "vendor-specific" key is ok. */
+ || (keysym & (1 << 28))))
+ {
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = keysym;
+ goto done;
+ }
+
+ uc = gdk_keyval_to_unicode (keysym);
+
+ if (uc)
+ {
+ inev.ie.kind = (SINGLE_BYTE_CHAR_P (uc)
+ ? ASCII_KEYSTROKE_EVENT
+ : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
+ inev.ie.code = uc;
+ }
+ else
+ {
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = keysym;
+ }
+
+ done:
+ if (inev.ie.kind != NO_EVENT)
+ {
+ xg_pending_quit_event.kind = NO_EVENT;
+ kbd_buffer_store_buffered_event (&inev, &xg_pending_quit_event);
+ }
+
+ XNoOp (FRAME_X_DISPLAY (f));
+#ifdef USABLE_SIGIO
+ raise (SIGIO);
+#endif
+ return true;
+}
+
+bool
+xg_filter_key (struct frame *frame, XEvent *xkey)
+{
+ GdkEvent *xg_event = gdk_event_new ((xkey->type == KeyPress
+#ifdef HAVE_XINPUT2
+ || (xkey->type == GenericEvent
+ && xkey->xgeneric.evtype == XI_KeyPress)
+#endif
+ ) ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
+ GdkDisplay *dpy = gtk_widget_get_display (FRAME_GTK_WIDGET (frame));
+ GdkKeymap *keymap = gdk_keymap_get_for_display (dpy);
+ GdkModifierType consumed;
+ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
+ bool result;
+
+ xg_event->any.window = gtk_widget_get_window (FRAME_GTK_WIDGET (frame));
+ g_object_ref (xg_event->any.window);
+
+#if GTK_CHECK_VERSION (3, 20, 0)
+ GdkSeat *seat = gdk_display_get_default_seat (dpy);
+
+ gdk_event_set_device (xg_event,
+ gdk_seat_get_keyboard (seat));
+#elif GTK_CHECK_VERSION (3, 16, 0)
+ GdkDeviceManager *manager = gdk_display_get_device_manager (dpy);
+ GList *devices = gdk_device_manager_list_devices (manager,
+ GDK_DEVICE_TYPE_MASTER);
+ GdkDevice *device;
+ GList *tem;
+ for (tem = devices; tem; tem = tem->next)
+ {
+ device = GDK_DEVICE (tem->data);
+
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ {
+ gdk_event_set_device (xg_event, device);
+ break;
+ }
+ }
+
+ g_list_free (devices);
+#endif
+
+#ifdef HAVE_XINPUT2
+ if (xkey->type != GenericEvent)
+ {
+#endif
+ xg_event->key.hardware_keycode = xkey->xkey.keycode;
+
+#ifdef HAVE_XKB
+ if (dpyinfo->supports_xkb)
+ xg_event->key.group = XkbGroupForCoreState (xkey->xkey.state);
+#endif
+ xg_event->key.state = xkey->xkey.state;
+ gdk_keymap_translate_keyboard_state (keymap,
+ xkey->xkey.keycode,
+ xkey->xkey.state,
+ xg_event->key.group,
+ &xg_event->key.keyval,
+ NULL, NULL, &consumed);
+ xg_add_virtual_mods (dpyinfo, &xg_event->key);
+ xg_event->key.state &= ~consumed;
+ xg_event->key.time = xkey->xkey.time;
+#if GTK_CHECK_VERSION (3, 6, 0)
+ xg_event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap,
+ xg_event->key.hardware_keycode);
+#endif
+#ifdef HAVE_XINPUT2
+ }
+ else
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xkey->xcookie.data;
+
+ xg_event->key.hardware_keycode = xev->detail;
+ xg_event->key.group = xev->group.effective;
+ xg_event->key.state = xev->mods.effective;
+ xg_event->key.time = xev->time;
+ gdk_keymap_translate_keyboard_state (keymap,
+ xev->detail,
+ xev->mods.effective,
+ xg_event->key.group,
+ &xg_event->key.keyval,
+ NULL, NULL, &consumed);
+ xg_add_virtual_mods (dpyinfo, &xg_event->key);
+ xg_event->key.state &= ~consumed;
+#if GTK_CHECK_VERSION (3, 6, 0)
+ xg_event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap,
+ xg_event->key.hardware_keycode);
+#endif
+ }
+#endif
+
+ result = gtk_im_context_filter_keypress (FRAME_X_OUTPUT (frame)->im_context,
+ &xg_event->key);
+
+ gdk_event_free (xg_event);
+
+ return result;
+}
+#endif
+
+#if GTK_CHECK_VERSION (3, 10, 0)
+static void
+xg_widget_style_updated (GtkWidget *widget, gpointer user_data)
+{
+ struct frame *f = user_data;
+
+ if (f->alpha_background < 1.0)
+ {
+#ifndef HAVE_PGTK
+ XChangeProperty (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ FRAME_DISPLAY_INFO (f)->Xatom_net_wm_opaque_region,
+ XA_CARDINAL, 32, PropModeReplace,
+ NULL, 0);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f)
+ && gtk_widget_get_realized (FRAME_GTK_OUTER_WIDGET (f)))
+ gdk_window_set_opaque_region (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
+ NULL);
+#endif
+ }
+}
+#endif
#endif /* USE_GTK */