Commit 28f011eb authored by Olivier Fourdan's avatar Olivier Fourdan

wayland: prefer subsurface when possible

Quite a few applications use GTK_WINDOW_POPUP to create various
temporary windows and place then on screen. That works fine on X11 but
on Wayland there is no global coordinate system for regular surfaces.

If the application is using a gdk temp window and set a parent with
gtk_window_transient_for(), the gdk wayland backend has all it needs to
create a subsurface that can be placed at will by the application.

Bugzilla: https://bugzilla.gnome.org/show_bug.cgi?id=759738
parent d85b3fc6
......@@ -1228,20 +1228,35 @@ should_be_mapped (GdkWindow *window)
}
static gboolean
should_map_as_subsurface (GdkWindow *window)
should_map_as_popup (GdkWindow *window)
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE)
/* Ideally, popup would be temp windows with a parent and grab */
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP)
{
/* If a temp window has a parent and a grab, we can use a popup */
if (impl->transient_for)
{
if (impl->grab_input_seat)
return TRUE;
}
else
g_warning ("Window %p is a temporary window without parent, "
"application will not be able to position it on screen.",
window);
}
/* Yet we need to keep the window type hint tests for compatibility */
switch (impl->hint)
{
case GDK_WINDOW_TYPE_HINT_TOOLTIP:
case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
case GDK_WINDOW_TYPE_HINT_COMBO:
return TRUE;
case GDK_WINDOW_TYPE_HINT_UTILITY:
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP)
if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_TEMP)
return TRUE;
break;
......@@ -1253,24 +1268,32 @@ should_map_as_subsurface (GdkWindow *window)
}
static gboolean
should_map_as_popup (GdkWindow *window)
should_map_as_subsurface (GdkWindow *window)
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
switch (impl->hint)
{
case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
case GDK_WINDOW_TYPE_HINT_COMBO:
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE)
return TRUE;
case GDK_WINDOW_TYPE_HINT_UTILITY:
if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_TEMP)
return FALSE;
/* if we want a popup, we do not want a subsurface */
if (should_map_as_popup (window))
return FALSE;
if (impl->transient_for)
{
GdkWindowImplWayland *impl_parent;
impl_parent = GDK_WINDOW_IMPL_WAYLAND (impl->transient_for->impl);
/* subsurface require that the parent is mapped */
if (impl_parent->mapped)
return TRUE;
break;
else
g_warning ("Couldn't map window %p as susburface because its parent is not mapped.",
window);
default:
break;
}
return FALSE;
......@@ -1313,7 +1336,7 @@ gdk_wayland_window_map (GdkWindow *window)
if (impl->transient_for)
gdk_wayland_window_create_subsurface (window);
else
g_warning ("Couldn't map as window %p as susburface yet because it doesn't have a parent",
g_warning ("Couldn't map window %p as susburface yet because it doesn't have a parent",
window);
}
else if (should_map_as_popup (window))
......
......@@ -3229,6 +3229,12 @@ gtk_window_unset_transient_for (GtkWindow *window)
*
* Passing %NULL for @parent unsets the current transient window.
*
* On Wayland, this function can also be used to attach a new
* #GTK_WINDOW_POPUP to a #GTK_WINDOW_TOPLEVEL parent already mapped
* on screen so that the #GTK_WINDOW_POPUP will be created as a
* subsurface-based window #GDK_WINDOW_SUBSURFACE which can be
* positioned at will relatively to the #GTK_WINDOW_TOPLEVEL surface.
*
* On Windows, this function puts the child window on top of the parent,
* much as the window manager would have done on X.
*/
......
......@@ -170,6 +170,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
gdkgears \
listmodel \
foreigndrawing \
testpopup \
$(NULL)
if USE_X11
......
#include <gtk/gtk.h>
static gboolean
draw_popup (GtkWidget *widget,
cairo_t *cr,
gpointer data)
{
cairo_set_source_rgb (cr, 1, 0, 0);
cairo_paint (cr);
return FALSE;
}
static gboolean
place_popup (GtkWidget *parent,
GdkEvent *event,
GtkWidget *popup)
{
GdkEventMotion *ev_motion = (GdkEventMotion *) event;
gint width, height;
gtk_window_get_size (GTK_WINDOW (popup), &width, &height);
gtk_window_move (GTK_WINDOW (popup),
(int) ev_motion->x_root - width / 2,
(int) ev_motion->y_root - height / 2);
return FALSE;
}
static gboolean
on_map_event (GtkWidget *parent,
GdkEvent *event,
gpointer data)
{
GtkWidget *popup;
popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_widget_set_size_request (GTK_WIDGET (popup), 20, 20);
gtk_widget_set_app_paintable (GTK_WIDGET (popup), TRUE);
gtk_window_set_transient_for (GTK_WINDOW (popup), GTK_WINDOW (parent));
g_signal_connect (popup, "draw", G_CALLBACK (draw_popup), NULL);
g_signal_connect (parent, "motion-notify-event", G_CALLBACK (place_popup), popup);
gtk_widget_show (popup);
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_events (window, GDK_POINTER_MOTION_MASK);
g_signal_connect (window, "destroy", gtk_main_quit, NULL);
g_signal_connect (window, "map-event", G_CALLBACK (on_map_event), NULL);
gtk_widget_show (window);
gtk_main ();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment