...
 
Commits (29)
  • Jonas Ådahl's avatar
    renderer/native: Use g_set_error() instead of _cogl_set_error() · cded69da
    Jonas Ådahl authored
    It's even a GError, so lets use the proper API.
    
    !622
    (cherry picked from commit 1efb32d3)
    cded69da
  • Jonas Ådahl's avatar
    renderer/native: Make sure we're not destroying an active EGLSurface · 8307c0f7
    Jonas Ådahl authored
    When making a new surface/context pair current, mesa may want to flush
    the old context. Make sure we don't try to flush any freed memory by
    unmaking a surface/context pair current before freeing it.
    
    Not doing this results in the following valgrind warnings:
    
    ==15986== Invalid read of size 8
    ==15986==    at 0x69A6D80: dri_flush_front_buffer (gbm_dri.c:92)
    ==15986==    by 0x1750D458: intel_flush_front (brw_context.c:251)
    ==15986==    by 0x1750D4BB: intel_glFlush (brw_context.c:296)
    ==15986==    by 0x1739D8DD: dri2_make_current (egl_dri2.c:1461)
    ==15986==    by 0x17393A3A: eglMakeCurrent (eglapi.c:869)
    ==15986==    by 0x54381FB: InternalMakeCurrentVendor (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0)
    ==15986==    by 0x5438515: eglMakeCurrent (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0)
    ==15986==    by 0x522A782: _cogl_winsys_egl_make_current (cogl-winsys-egl.c:303)
    ==15986==    by 0x49B64C8: meta_renderer_native_create_view (meta-renderer-native.c:3076)
    ==15986==    by 0x48D26E7: meta_renderer_create_view (meta-renderer.c:78)
    ==15986==    by 0x48D277A: meta_renderer_rebuild_views (meta-renderer.c:111)
    ==15986==    by 0x49BF46E: meta_stage_native_rebuild_views (meta-stage-native.c:142)
    ==15986==  Address 0x1b076600 is 0 bytes inside a block of size 48 free'd
    ==15986==    at 0x4839A0C: free (vg_replace_malloc.c:540)
    ==15986==    by 0x49B59F3: meta_renderer_native_release_onscreen (meta-renderer-native.c:2651)
    ==15986==    by 0x5211441: _cogl_onscreen_free (cogl-onscreen.c:167)
    ==15986==    by 0x5210D81: _cogl_object_onscreen_indirect_free (cogl-onscreen.c:51)
    ==15986==    by 0x51D0066: _cogl_object_default_unref (cogl-object.c:103)
    ==15986==    by 0x520F989: _cogl_framebuffer_unref (cogl-framebuffer.c:1814)
    ==15986==    by 0x51D00B1: cogl_object_unref (cogl-object.c:115)
    ==15986==    by 0x536F3C7: clutter_stage_view_dispose (clutter-stage-view.c:304)
    ==15986==    by 0x4B7DAF2: g_object_unref (gobject.c:3309)
    ==15986==    by 0x4A9596C: g_list_foreach (glist.c:1013)
    ==15986==    by 0x4A9599A: g_list_free_full (glist.c:223)
    ==15986==    by 0x48D2737: meta_renderer_rebuild_views (meta-renderer.c:100)
    ==15986==  Block was alloc'd at
    ==15986==    at 0x483AB1A: calloc (vg_replace_malloc.c:762)
    ==15986==    by 0x69A76B2: gbm_dri_surface_create (gbm_dri.c:1252)
    ==15986==    by 0x69A6BFE: gbm_surface_create (gbm.c:600)
    ==15986==    by 0x49B4E29: meta_renderer_native_create_surface_gbm (meta-renderer-native.c:2221)
    ==15986==    by 0x49B57DB: meta_onscreen_native_allocate (meta-renderer-native.c:2569)
    ==15986==    by 0x49B6423: meta_renderer_native_create_view (meta-renderer-native.c:3062)
    ==15986==    by 0x48D26E7: meta_renderer_create_view (meta-renderer.c:78)
    ==15986==    by 0x48D277A: meta_renderer_rebuild_views (meta-renderer.c:111)
    ==15986==    by 0x49BF46E: meta_stage_native_rebuild_views (meta-stage-native.c:142)
    ==15986==    by 0x49A75B5: meta_backend_native_update_screen_size (meta-backend-native.c:520)
    ==15986==    by 0x48B01BB: meta_backend_sync_screen_size (meta-backend.c:224)
    ==15986==    by 0x48B09B7: meta_backend_real_post_init (meta-backend.c:501)
    
    !622
    (cherry picked from commit 56ddaaa3)
    8307c0f7
  • Jonas Ådahl's avatar
    renderer/native: Fix EGLSurface destruction order · e0922bff
    Jonas Ådahl authored
    Make sure to destroy the EGL surface after releasing held buffers,
    otherwise we'll get the following valgrind warnings:
    
    ==24016== Invalid read of size 8
    ==24016==    at 0x1739943F: release_buffer (platform_drm.c:73)
    ==24016==    by 0x49AC355: meta_drm_buffer_gbm_finalize (meta-drm-buffer-gbm.c:213)
    ==24016==    by 0x4B75B61: g_object_unref (gobject.c:3346)
    ==24016==    by 0x49B4B41: free_current_bo (meta-renderer-native.c:991)
    ==24016==    by 0x49B816F: meta_renderer_native_release_onscreen (meta-renderer-native.c:2971)
    ==24016==    by 0x5209441: _cogl_onscreen_free (cogl-onscreen.c:167)
    ==24016==    by 0x5208D81: _cogl_object_onscreen_indirect_free (cogl-onscreen.c:51)
    ==24016==    by 0x51C8066: _cogl_object_default_unref (cogl-object.c:103)
    ==24016==    by 0x5207989: _cogl_framebuffer_unref (cogl-framebuffer.c:1814)
    ==24016==    by 0x51C80B1: cogl_object_unref (cogl-object.c:115)
    ==24016==    by 0x53673C7: clutter_stage_view_dispose (clutter-stage-view.c:304)
    ==24016==    by 0x4B75AF2: g_object_unref (gobject.c:3309)
    ==24016==  Address 0x18e742a8 is 536 bytes inside a block of size 784 free'd
    ==24016==    at 0x4839A0C: free (vg_replace_malloc.c:540)
    ==24016==    by 0x17399764: dri2_drm_destroy_surface (platform_drm.c:231)
    ==24016==    by 0x1738550A: eglDestroySurface (eglapi.c:1145)
    ==24016==    by 0x5440286: eglDestroySurface (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0)
    ==24016==    by 0x49613A5: meta_egl_destroy_surface (meta-egl.c:432)
    ==24016==    by 0x49B80F9: meta_renderer_native_release_onscreen (meta-renderer-native.c:2954)
    ==24016==    by 0x5209441: _cogl_onscreen_free (cogl-onscreen.c:167)
    ==24016==    by 0x5208D81: _cogl_object_onscreen_indirect_free (cogl-onscreen.c:51)
    ==24016==    by 0x51C8066: _cogl_object_default_unref (cogl-object.c:103)
    ==24016==    by 0x5207989: _cogl_framebuffer_unref (cogl-framebuffer.c:1814)
    ==24016==    by 0x51C80B1: cogl_object_unref (cogl-object.c:115)
    ==24016==    by 0x53673C7: clutter_stage_view_dispose (clutter-stage-view.c:304)
    ==24016==  Block was alloc'd at
    ==24016==    at 0x483AB1A: calloc (vg_replace_malloc.c:762)
    ==24016==    by 0x173997AE: dri2_drm_create_window_surface (platform_drm.c:145)
    ==24016==    by 0x17388906: _eglCreateWindowSurfaceCommon (eglapi.c:929)
    ==24016==    by 0x5440197: eglCreateWindowSurface (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0)
    ==24016==    by 0x49612FF: meta_egl_create_window_surface (meta-egl.c:396)
    ==24016==    by 0x49B752E: meta_renderer_native_create_surface_gbm (meta-renderer-native.c:2538)
    ==24016==    by 0x49B7E6C: meta_onscreen_native_allocate (meta-renderer-native.c:2870)
    ==24016==    by 0x49B8BCF: meta_renderer_native_create_view (meta-renderer-native.c:3387)
    ==24016==    by 0x48D274B: meta_renderer_create_view (meta-renderer.c:78)
    ==24016==    by 0x48D27DE: meta_renderer_rebuild_views (meta-renderer.c:111)
    ==24016==    by 0x49BB4FB: meta_stage_native_rebuild_views (meta-stage-native.c:142)
    ==24016==    by 0x49A733C: meta_backend_native_update_screen_size (meta-backend-native.c:517)
    
    !622
    (cherry picked from commit d9fb11b0)
    e0922bff
  • Marco Trevisan's avatar
    workspace: Focus only ancestors that are focusable · 225d1876
    Marco Trevisan authored
    When destroying a window that has a parent, we initially try to focus one of
    its ancestors. However if no ancestor can be focused, then we should instead
    focus the default focus window instead of trying to request focus for a window
    that can't get focus anyways.
    
    Fixes #308
    (cherry picked from commit eccc791f)
    225d1876
  • Marco Trevisan's avatar
    tests: Add 'accept_focus' command to runner and client · d56bd28f
    Marco Trevisan authored
    Under the hood, calls gtk_window_set_accept_focus in the client
    
    !307
    (cherry picked from commit e1f839f4)
    d56bd28f
  • Marco Trevisan's avatar
    tests: Add 'can_take_focus' command to runner and client · 9c079a52
    Marco Trevisan authored
    Allow to set/unset WM_TAKE_FOCUS from client window.
    This is added by default by gtk, but this might not happen in other toolkits,
    so add an ability to (un)set this.
    
    So fetch the protocols with XGetWMProtocols and unset the atom.
    
    test-client now needs to depend on Xlib directly in meson build.
    
    !307
    (cherry picked from commit f2d2d473)
    9c079a52
  • Marco Trevisan's avatar
    tests, stacking: Add tests with no-input and no-take-focus windows · afcea966
    Marco Trevisan authored
    When a window with no frame, that doesn't accept focus and that has no
    take-focus atom set is destroyed, we ended up in not changing the current_focus
    window, causing a crash.
    
    Added test cases that verify this situation.
    
    Related to #308
    !307
    (cherry picked from commit 2fc7760c)
    afcea966
  • Marco Trevisan's avatar
    test-runner: Add 'assert_focused' command · 1be5a57a
    Marco Trevisan authored
    This allows to verify which window should have the focus, which might not
    be the same as the top of the stack.
    
    It's possible to assert the case where there's no focused window using
    "NONE" as parameter.
    
    !307
    (cherry picked from commit 51f9e04e)
    1be5a57a
  • Marco Trevisan's avatar
    tests: Verify focused window in closed-transient tests · 3d9771b2
    Marco Trevisan authored
    Ensure that we have a focused window when closing transient windows with
    no-focus or no-take-focus atoms
    
    !307
    (cherry picked from commit fcb408ad)
    3d9771b2
  • Marco Trevisan's avatar
    test-runner: Add 'sleep' command · 9009f8d4
    Marco Trevisan authored
    This allows to sleep for a given timeout in milliseconds.
    
    Rename test_case_before_redraw to test_case_loop_quit since it's a generic
    function and use it for the timeout too.
    
    !307
    (cherry picked from commit d08763c1)
    9009f8d4
  • Marco Trevisan's avatar
    test-runner: Add 'dispatch' command · 866d6780
    Marco Trevisan authored
    This will only wait for events to be dispatched and processed by the server
    without waiting for client processing.
    
    Reuse the code for the wait command too.
    
    !307
    (cherry picked from commit 6022b239)
    866d6780
  • Marco Trevisan's avatar
    window-x11: Focus the default window with delay while waiting for take-focus · 2db94e2e
    Marco Trevisan authored
    When requesting to a take-focus window to acquire the input, the client may or
    may not respond with a SetInputFocus (this doesn't happen for no-input gtk
    windows in fact [to be fixed there too]), in such case we were unsetting the
    focus while waiting the reply.
    
    In case the client won't respond, we wait for a small delay (set to 250 ms) for
    the take-focus window to grab the input focus before setting it to the default
    window.
    
    Added a test for this behavior and for the case in which a window takes the
    focus meanwhile we're waiting to focus the default window.
    
    !307
    (cherry picked from commit f71151a5)
    2db94e2e
  • Marco Trevisan's avatar
    window: Warn if try to focus unmanaging windows · 5efb11ac
    Marco Trevisan authored
    !307
    (cherry picked from commit e14613e7)
    5efb11ac
  • Marco Trevisan's avatar
    window: Emit an error and return when trying to activate an unmanaged · 8b986cd0
    Marco Trevisan authored
    If something (i.e. gnome-shell or an extension) tries to activate an unmanaged
    window, we should warn about this and avoid to perform further actions as this
    could lead to a crash of mutter, since the window has not valid flags (like
    workspace) set anymore at this stage.
    
    Fixes #580
    
    !564
    
    
    (cherry picked from commit a6fc656e)
    8b986cd0
  • Jonas Ådahl's avatar
    renderer/native: Remove left-over function declarations · da319528
    Jonas Ådahl authored
    There are no callers and no definitions of these.
    
    !655
    da319528
  • Jonas Ådahl's avatar
    renderer/native: Queue mode reset from new rebuild_views vfunc · 83ce89ef
    Jonas Ådahl authored
    Simplify the call site a bit and make the native renderer know it should
    queue mode reset itself when views have been rebuilt. This is done
    partly due to more things needing to be dealt with after views have been
    rebuilt.
    
    !655
    83ce89ef
  • Jonas Ådahl's avatar
    renderer/native: Discard page flip retries when rebuilding views · c5fb1d19
    Jonas Ådahl authored
    Rebuilding views means we don't care to retry page flip attempts for
    previous views, especially since connectors may have been disconnected,
    making a page flip retry hit an assert a flipped CRTC has connectors
    associated with it.
    
    !655
    c5fb1d19
  • Marco Trevisan's avatar
    window-actor: Use vfunc to set the surface actor · 0d570f2d
    Marco Trevisan authored
    As per commit 80e3c1de set_surface_actor has been added, meant to do different
    things depending on the backend, like connecting to signals under X11.
    
    However, the vfunc isn't ever used, making the X11 surfaces not to react to
    repaint-scheduled signal.
    
    !660
    
    
    (cherry picked from commit 4061c838)
    0d570f2d
  • Marco Trevisan's avatar
    window-actor: Set actor as compositor private in window before the surface · 00107633
    Marco Trevisan authored
    In MetaWindowActor creation we're setting the compositor private (i.e. the
    window actor itself) of a window before creating the surface actor, and so
    passing to the it a window without its compositor side set.
    
    Since the surface actor might use the parent actor, set this before updating
    the surface.
    
    !660
    
    
    (cherry picked from commit 7776941b)
    00107633
  • Marco Trevisan's avatar
    surface-actor-x11: Assign X11 Display only if we have resources · 0a8fbbe5
    Marco Trevisan authored
    free_damage and detach_pixmap functions are called inside dispose and an object
    can be disposed multiple times, even when the display is already closed.
    
    So, don't try to deference a possibly null-pointer, assigning the xdisplay too
    early, as if the X11 related resources have been unset, the server might not be
    open anymore. In fact, we assume that if we have a damage or a pixmap set,
    the display is still open.
    
    !660
    
    
    (cherry picked from commit d7d97f24)
    0a8fbbe5
  • Marco Trevisan's avatar
    surface-actor-x11: Bind the surface actor resources to window actor life · 947da2c5
    Marco Trevisan authored
    X11 actors need to release the server data (pixmap and damage) before the
    display is closed.
    During the close phase all the windows are unmanaged and this causes the window
    actors to be removed from the compositor, unsetting their actor surface.
    
    However, in case a window is animating the surface might not be destroyed until
    the animation is completed and a reference to it kept around by gjs in the shell
    case. By the way, per commit 7718e67f all window actors (even the animating
    ones) are destroyed before the display is closed, but this is not true for the
    child surface, because the parent window will just unref it, leaving it around
    if reffed somewhere else. This is fine for wayland surfaces, but not for X11
    ones which are bound to server-side pixmaps.
    
    So, connect to the parent MetaWindowActor "destroy" signal, releasing the x11
    resources that implies detaching the pixmap (unsetting the texture) and removing
    the damages.
    
    Closes: #629
    !660
    
    
    (cherry picked from commit de97b545)
    947da2c5
  • Jonas Ådahl's avatar
    window-actor: Remove left-over parent field · 194d93ef
    Jonas Ådahl authored
    The commit
    
    commit 60f7ff3a
    Author: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
    Date:   Fri Dec 21 18:12:49 2018 -0200
    
        window-actor: Turn into a derivable class
    
    made the previous instance struct a instance private struct, but didn't
    remove the parent field. Since it's unused, there is no point in keeping
    it around, so lets drop it.
    
    !658
    
    
    (cherry picked from commit 7229a07b)
    194d93ef
  • Marco Trevisan's avatar
    window-x11: Don't double-check for unmanaging windows · c50df143
    Marco Trevisan authored
    When looking for the best fallback focus window, we ignore it if it is in the
    unmanaging state, but meta_stack_get_default_focus_window() does this is check
    for us already.
    
    So, ignore the redundant test.
    
    !669
    
    
    (cherry picked from commit 9aee47da)
    c50df143
  • Marco Trevisan's avatar
    window-x11: Accept any focusable window as fallback focus · e50cdda4
    Marco Trevisan authored
    As per commit f71151a5 we were ignoring WM_TAKE_FOCUS-only windows as focus
    targets, however this might end-up in an infinite loop if there are multiple
    non-input windows stacked.
    
    So, accept any focusable window as fallback focus target even if it's a
    take-focus one (that might not reply to the request).
    
    Added a stacking test to verify this.
    
    Closes: #660
    !669
    
    (cherry picked from commit c327b2df)
    e50cdda4
  • Marco Trevisan's avatar
    stack: Add a function to get a sorted list of focus candidates · 763092a4
    Marco Trevisan authored
    Use a static function if a window can be the default focus window, and use such
    function to return a filtered list of the stack.
    
    !669
    
    
    (cherry picked from commit 2439255f)
    763092a4
  • Marco Trevisan's avatar
    test-client: Add x11 events GSource handler · 80869e07
    Marco Trevisan authored
    When using gtk under X11 some WM related events are always filtered and not
    delivered when using the gdk Window filters.
    
    So, add a new one with higher priority than the GTK events one so that we can
    pick those events before than Gtk itself.
    
    !669
    
    
    (cherry picked from commit bd0f1bd3)
    80869e07
  • Marco Trevisan's avatar
    tests: Add "accept_take_focus" command · 63f3f5d5
    Marco Trevisan authored
    When used it setups an X11 event monitor that replies to WM_TAKE_FOCUS
    ClientMessage's with a XSetInputFocus request.
    
    It can only be used by x11 clients on windows that have WM_TAKE_FOCUS atom set
    and that does not accept input.
    
    !669
    
    
    (cherry picked from commit b80250e4)
    63f3f5d5
  • Marco Trevisan's avatar
    window-x11: Use any focusable window as fallback delayed focus window · 61691bac
    Marco Trevisan authored
    As per commit f71151a5 we focus an input window if no take-focus-window accepts
    it. This might lead to an infinite loop if there are various focusable but
    non-input windows in the stack.
    
    When the current focus window is unmanaging and we're trying to focus a
    WM_TAKE_FOCUS window, we intent to give the focus to the first focusable input
    window in the stack.
    
    However, if an application (such as the Java ones) only uses non-input
    WM_TAKE_FOCUS windows, are not requesting these ones to get the focus. This
    might lead to a state where no window is focused, or a wrong one is.
    
    So, instead of only focus the first eventually input window available, try to
    request to all the take-focus windows that are in the stack between the
    destroyed one and the first input one to acquire the input focus.
    Use a queue to keep track of those windows, that is passed around stealing
    ownership, while we protect for unmanaged queued windows.
    
    Also, reduce the default timeout value, as the previous one might lead to an
    excessive long wait.
    
    Added metatests verifying these situations.
    
    Closes: #660
    !669
    
    (cherry picked from commit 6d8293a4)
    61691bac
  • Marco Trevisan's avatar
    meson: Bump meson requirement to 0.50.0 · ccab0f47
    Marco Trevisan authored
    We've been using configure_file's `install` property for some time now, but this
    has been officially supported and works as expected only since meson 0.50, so,
    bump version to avoid warnings and ensure the behavior is the one we want.
    
    #668
    ccab0f47
project('mutter', 'c',
version: '3.32.2',
meson_version: '>= 0.48.0',
meson_version: '>= 0.50.0',
license: 'GPLv2+'
)
......
......@@ -90,6 +90,12 @@ meta_renderer_create_view (MetaRenderer *renderer,
*/
void
meta_renderer_rebuild_views (MetaRenderer *renderer)
{
return META_RENDERER_GET_CLASS (renderer)->rebuild_views (renderer);
}
static void
meta_renderer_real_rebuild_views (MetaRenderer *renderer)
{
MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
MetaBackend *backend = meta_get_backend ();
......@@ -181,4 +187,6 @@ meta_renderer_class_init (MetaRendererClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_renderer_finalize;
klass->rebuild_views = meta_renderer_real_rebuild_views;
}
......@@ -43,6 +43,7 @@ struct _MetaRendererClass
CoglRenderer * (* create_cogl_renderer) (MetaRenderer *renderer);
MetaRendererView * (* create_view) (MetaRenderer *renderer,
MetaLogicalMonitor *logical_monitor);
void (* rebuild_views) (MetaRenderer *renderer);
};
CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer);
......
......@@ -258,6 +258,9 @@ cogl_pixel_format_from_drm_format (uint32_t drm_format,
CoglPixelFormat *out_format,
CoglTextureComponents *out_components);
static void
meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
static MetaBackend *
backend_from_renderer_native (MetaRendererNative *renderer_native)
{
......@@ -1277,7 +1280,7 @@ meta_renderer_native_egl_context_created (CoglDisplay *cogl_display,
cogl_display_egl->dummy_surface,
cogl_display_egl->egl_context))
{
_cogl_set_error (error, COGL_WINSYS_ERROR,
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Failed to make context current");
return FALSE;
......@@ -3036,24 +3039,34 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen,
}
static void
meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
destroy_egl_surface (CoglOnscreen *onscreen)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native;
MetaRendererNativeGpuData *renderer_gpu_data;
/* If we never successfully allocated then there's nothing to do */
if (onscreen_egl == NULL)
return;
if (onscreen_egl->egl_surface != EGL_NO_SURFACE)
{
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
onscreen_native = onscreen_egl->platform;
meta_egl_destroy_surface (egl,
cogl_renderer_egl->edpy,
onscreen_egl->egl_surface,
NULL);
onscreen_egl->egl_surface = EGL_NO_SURFACE;
}
}
static void
discard_onscreen_page_flip_retries (MetaOnscreenNative *onscreen_native)
{
g_list_free_full (onscreen_native->pending_page_flip_retries,
(GDestroyNotify) retry_page_flip_data_free);
onscreen_native->pending_page_flip_retries = NULL;
if (onscreen_native->retry_page_flips_source)
{
MetaBackend *backend =
......@@ -3063,18 +3076,40 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
g_clear_pointer (&onscreen_native->retry_page_flips_source,
g_source_destroy);
}
}
if (onscreen_egl->egl_surface != EGL_NO_SURFACE)
{
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
static void
meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = framebuffer->context;
CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
CoglRenderer *cogl_renderer = cogl_context->display->renderer;
CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native;
MetaRendererNativeGpuData *renderer_gpu_data;
meta_egl_destroy_surface (egl,
cogl_renderer_egl->edpy,
onscreen_egl->egl_surface,
NULL);
onscreen_egl->egl_surface = EGL_NO_SURFACE;
/* If we never successfully allocated then there's nothing to do */
if (onscreen_egl == NULL)
return;
onscreen_native = onscreen_egl->platform;
if (onscreen_egl->egl_surface != EGL_NO_SURFACE &&
(cogl_display_egl->current_draw_surface == onscreen_egl->egl_surface ||
cogl_display_egl->current_read_surface == onscreen_egl->egl_surface))
{
if (!_cogl_winsys_egl_make_current (cogl_display,
cogl_display_egl->dummy_surface,
cogl_display_egl->dummy_surface,
cogl_display_egl->egl_context))
g_warning ("Failed to clear current context");
}
discard_onscreen_page_flip_retries (onscreen_native);
renderer_gpu_data =
meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
onscreen_native->render_gpu);
......@@ -3087,6 +3122,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
free_current_bo (onscreen);
destroy_egl_surface (onscreen);
if (onscreen_native->gbm.surface)
{
gbm_surface_destroy (onscreen_native->gbm.surface);
......@@ -3097,6 +3134,9 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
release_dumb_fb (&onscreen_native->egl.dumb_fb,
onscreen_native->render_gpu);
destroy_egl_surface (onscreen);
if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR)
{
MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
......@@ -3157,7 +3197,7 @@ meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native)
return TRUE;
}
void
static void
meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native)
{
MetaRenderer *renderer = META_RENDERER (renderer_native);
......@@ -3523,6 +3563,37 @@ meta_renderer_native_create_view (MetaRenderer *renderer,
return view;
}
static void
discard_page_flip_retries (MetaRenderer *renderer)
{
GList *l;
for (l = meta_renderer_get_views (renderer); l; l = l->next)
{
ClutterStageView *stage_view = l->data;
CoglFramebuffer *framebuffer =
clutter_stage_view_get_onscreen (stage_view);
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
discard_onscreen_page_flip_retries (onscreen_native);
}
}
static void
meta_renderer_native_rebuild_views (MetaRenderer *renderer)
{
MetaRendererClass *parent_renderer_class =
META_RENDERER_CLASS (meta_renderer_native_parent_class);
discard_page_flip_retries (renderer);
parent_renderer_class->rebuild_views (renderer);
meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer));
}
void
meta_renderer_native_finish_frame (MetaRendererNative *renderer_native)
{
......@@ -4321,6 +4392,7 @@ meta_renderer_native_class_init (MetaRendererNativeClass *klass)
renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer;
renderer_class->create_view = meta_renderer_native_create_view;
renderer_class->rebuild_views = meta_renderer_native_rebuild_views;
obj_props[PROP_MONITOR_MANAGER] =
g_param_spec_object ("monitor-manager",
......
......@@ -53,20 +53,6 @@ struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms);
gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native);
void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native,
MetaRendererView *view,
int width,
int height,
GError **error);
void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,
uint32_t id,
gboolean ignore);
MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native);
void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native);
int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native);
......
......@@ -140,7 +140,6 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native)
ClutterActor *stage = meta_backend_get_stage (backend);
meta_renderer_rebuild_views (renderer);
meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer));
clutter_stage_update_resource_scales (CLUTTER_STAGE (stage));
ensure_frame_callbacks (stage_native);
}
......
......@@ -32,6 +32,7 @@
#include "cogl/winsys/cogl-texture-pixmap-x11.h"
#include "compositor/meta-cullable.h"
#include "compositor/meta-shaped-texture-private.h"
#include "compositor/meta-window-actor-private.h"
#include "core/window-private.h"
#include "meta/meta-x11-errors.h"
#include "x11/meta-x11-display-private.h"
......@@ -71,11 +72,13 @@ static void
free_damage (MetaSurfaceActorX11 *self)
{
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
Display *xdisplay;
if (self->damage == None)
return;
xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
meta_x11_error_trap_push (display->x11_display);
XDamageDestroy (xdisplay, self->damage);
self->damage = None;
......@@ -86,12 +89,14 @@ static void
detach_pixmap (MetaSurfaceActorX11 *self)
{
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
Display *xdisplay;
if (self->pixmap == None)
return;
xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
/* Get rid of all references to the pixmap before freeing it; it's unclear whether
* you are supposed to be able to free a GLXPixmap after freeing the underlying
* pixmap, but it certainly doesn't work with current DRI/Mesa
......@@ -343,13 +348,19 @@ meta_surface_actor_x11_is_unredirected (MetaSurfaceActor *actor)
return self->unredirected;
}
static void
release_x11_resources (MetaSurfaceActorX11 *self)
{
detach_pixmap (self);
free_damage (self);
}
static void
meta_surface_actor_x11_dispose (GObject *object)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (object);
detach_pixmap (self);
free_damage (self);
release_x11_resources (self);
G_OBJECT_CLASS (meta_surface_actor_x11_parent_class)->dispose (object);
}
......@@ -403,8 +414,7 @@ window_decorated_notify (MetaWindow *window,
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (user_data);
detach_pixmap (self);
free_damage (self);
release_x11_resources (self);
create_damage (self);
}
......@@ -441,6 +451,10 @@ meta_surface_actor_x11_new (MetaWindow *window)
g_signal_connect_object (self->window, "notify::decorated",
G_CALLBACK (window_decorated_notify), self, 0);
g_signal_connect_object (meta_window_actor_from_window (window), "destroy",
G_CALLBACK (release_x11_resources), self,
G_CONNECT_SWAPPED);
self->unredirected = FALSE;
sync_unredirected (self);
......
......@@ -52,8 +52,6 @@ typedef enum
typedef struct _MetaWindowActorPrivate
{
ClutterActor parent;
MetaWindow *window;
MetaCompositor *compositor;
......@@ -417,7 +415,7 @@ meta_window_actor_update_surface (MetaWindowActor *self)
else
surface_actor = NULL;
set_surface (self, surface_actor);
META_WINDOW_ACTOR_GET_CLASS (self)->set_surface_actor (self, surface_actor);
}
static void
......@@ -430,6 +428,9 @@ meta_window_actor_constructed (GObject *object)
priv->compositor = window->display->compositor;
/* Hang our compositor window state off the MetaWindow for fast retrieval */
meta_window_set_compositor_private (window, object);
meta_window_actor_update_surface (self);
meta_window_actor_update_opacity (self);
......@@ -446,9 +447,6 @@ meta_window_actor_constructed (GObject *object)
priv->first_frame_state = DRAWING_FIRST_FRAME;
meta_window_actor_sync_actor_geometry (self, priv->window->placed);
/* Hang our compositor window state off the MetaWindow for fast retrieval */
meta_window_set_compositor_private (window, object);
}
static void
......@@ -476,7 +474,7 @@ meta_window_actor_dispose (GObject *object)
g_clear_object (&priv->window);
set_surface (self, NULL);
META_WINDOW_ACTOR_GET_CLASS (self)->set_surface_actor (self, NULL);
G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object);
}
......
......@@ -1194,6 +1194,27 @@ window_contains_point (MetaWindow *window,
return POINT_IN_RECT (root_x, root_y, rect);
}
static gboolean
window_can_get_default_focus (MetaWindow *window)
{
if (window->unmaps_pending > 0)
return FALSE;
if (window->unmanaging)
return FALSE;
if (!meta_window_is_focusable (window))
return FALSE;
if (!meta_window_should_be_showing (window))
return FALSE;
if (window->type == META_WINDOW_DOCK)
return FALSE;
return TRUE;
}
static MetaWindow*
get_default_focus_window (MetaStack *stack,
MetaWorkspace *workspace,
......@@ -1221,24 +1242,12 @@ get_default_focus_window (MetaStack *stack,
if (window == not_this_one)
continue;
if (window->unmaps_pending > 0)
continue;
if (window->unmanaging)
continue;
if (!meta_window_is_focusable (window))
continue;
if (!meta_window_should_be_showing (window))
if (!window_can_get_default_focus (window))
continue;
if (must_be_at_point && !window_contains_point (window, root_x, root_y))
continue;
if (window->type == META_WINDOW_DOCK)
continue;
return window;
}
......@@ -1293,6 +1302,26 @@ meta_stack_list_windows (MetaStack *stack,
return workspace_windows;
}
GList *
meta_stack_get_default_focus_candidates (MetaStack *stack,
MetaWorkspace *workspace)
{
GList *windows = meta_stack_list_windows (stack, workspace);
GList *l;
for (l = windows; l;)
{
GList *next = l->next;
if (!window_can_get_default_focus (l->data))
windows = g_list_delete_link (windows, l);
l = next;
}
return windows;
}
int
meta_stack_windows_cmp (MetaStack *stack,
MetaWindow *window_a,
......
......@@ -337,6 +337,21 @@ MetaWindow* meta_stack_get_default_focus_window_at_point (MetaStack *stack,
int root_x,
int root_y);
/**
* meta_stack_get_default_focus_candidates:
* @stack: The stack to examine.
* @workspace: If not %NULL, only windows on this workspace will be
* returned; otherwise all windows in the stack will be
* returned.
*
* Returns all the focus candidate windows in the stack, in order.
*
* Returns: (transfer container) (element-type Meta.Window):
* A #GList of #MetaWindow, in stacking order, honouring layers.
*/
GList * meta_stack_get_default_focus_candidates (MetaStack *stack,
MetaWorkspace *workspace);
/**
* meta_stack_list_windows:
* @stack: The stack to examine.
......
......@@ -3683,6 +3683,13 @@ meta_window_activate_full (MetaWindow *window,
{
MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
gboolean allow_workspace_switch;
if (window->unmanaging)
{
g_warning ("Trying to activate unmanaged window '%s'", window->desc);
return;
}
meta_topic (META_DEBUG_FOCUS,
"_NET_ACTIVE_WINDOW message sent for %s at time %u "
"by client type %u.\n",
......@@ -8562,6 +8569,8 @@ meta_window_shortcuts_inhibited (MetaWindow *window,
gboolean
meta_window_is_focusable (MetaWindow *window)
{
g_return_val_if_fail (!window->unmanaging, FALSE);
return META_WINDOW_GET_CLASS (window)->is_focusable (window);
}
......
......@@ -85,6 +85,12 @@ typedef struct _MetaWorkspaceLogicalMonitorData
MetaRectangle logical_monitor_work_area;
} MetaWorkspaceLogicalMonitorData;
typedef struct _MetaWorkspaceFocusableAncestorData
{
MetaWorkspace *workspace;
MetaWindow *out_window;
} MetaWorkspaceFocusableAncestorData;
static MetaWorkspaceLogicalMonitorData *
meta_workspace_get_logical_monitor_data (MetaWorkspace *workspace,
MetaLogicalMonitor *logical_monitor)
......@@ -1322,13 +1328,20 @@ meta_workspace_focus_default_window (MetaWorkspace *workspace,
}
static gboolean
record_ancestor (MetaWindow *window,
void *data)
find_focusable_ancestor (MetaWindow *window,
gpointer user_data)
{
MetaWindow **result = data;
MetaWorkspaceFocusableAncestorData *data = user_data;
if (!window->unmanaging && meta_window_is_focusable (window) &&
meta_window_located_on_workspace (window, data->workspace) &&
meta_window_showing_on_its_workspace (window))
{
data->out_window = window;
return FALSE;
}
*result = window;
return FALSE; /* quit with the first ancestor we find */
return TRUE;
}
/* Focus ancestor of not_this_one if there is one */
......@@ -1350,11 +1363,15 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace,
if (not_this_one)
{
MetaWindow *ancestor;
ancestor = NULL;
meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor);
if (ancestor != NULL &&
meta_window_located_on_workspace (ancestor, workspace) &&
meta_window_showing_on_its_workspace (ancestor))
MetaWorkspaceFocusableAncestorData data;
data = (MetaWorkspaceFocusableAncestorData) {
.workspace = workspace,
};
meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &data);
ancestor = data.out_window;
if (ancestor)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing %s, ancestor of %s\n",
......
......@@ -38,6 +38,7 @@ test_client = executable('mutter-test-client',
dependencies: [
gtk3_dep,
gio_unix_dep,
x11_dep,
xext_dep,
],
install: have_installed_tests,
......@@ -104,6 +105,13 @@ headless_start_test = executable('mutter-headless-start-test',
stacking_tests = files([
'stacking/basic-x11.metatest',
'stacking/basic-wayland.metatest',
'stacking/closed-transient-no-input-no-take-focus-parent.metatest',
'stacking/closed-transient-no-input-no-take-focus-parents.metatest',
'stacking/closed-transient-no-input-parent.metatest',
'stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest',
'stacking/closed-transient-no-input-parents.metatest',
'stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest',
'stacking/closed-transient-only-take-focus-parents.metatest',
'stacking/minimized.metatest',
'stacking/mixed-windows.metatest',
'stacking/set-parent.metatest',
......
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
can_take_focus 1/2 false
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 1/1 1/2 1/3
destroy 1/3
wait
assert_focused 1/1
assert_stacking 1/1 1/2
new_client 2 x11
create 2/1
show 2/1
wait
new_client 1 x11
create 1/1
accept_focus 1/1 false
can_take_focus 1/1 false
show 1/1
create 1/2 csd
set_parent 1/2 1
can_take_focus 1/2 false
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
wait
assert_stacking 1/1 1/2 2/1
assert_focused 2/1
new_client 2 x11
create 2/1
show 2/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
sleep 10
assert_focused none
assert_stacking 2/1 1/1 1/2
activate 2/1
wait
assert_focused 2/1
assert_stacking 1/1 1/2 2/1
sleep 250
assert_focused 2/1
assert_stacking 1/1 1/2 2/1
new_client 2 x11
create 2/1
show 2/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
dispatch
assert_focused none
assert_stacking 2/1 1/1 1/2
sleep 150
assert_focused 1/1
assert_stacking 2/1 1/1 1/2
new_client 0 x11
create 0/1
show 0/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
accept_focus 1/3 false
show 1/3
create 1/4 csd
set_parent 1/4 3
accept_focus 1/4 false
show 1/4
create 1/5 csd
set_parent 1/5 3
show 1/5
wait
assert_focused 1/5
assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5
destroy 1/5
dispatch
assert_focused none
assert_stacking 0/1 1/1 1/2 1/3 1/4
destroy 1/2
dispatch
sleep 450
assert_focused 1/1
assert_stacking 0/1 1/1 1/3 1/4
new_client 0 x11
create 0/1
show 0/1
new_client 1 x11
create 1/1
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
create 1/3 csd
set_parent 1/3 2
accept_focus 1/3 false
show 1/3
create 1/4 csd
set_parent 1/4 3
accept_focus 1/4 false
show 1/4
create 1/5 csd
set_parent 1/5 3
show 1/5
wait
assert_focused 1/5
assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5
destroy 1/5
dispatch
assert_focused none
assert_stacking 0/1 1/1 1/2 1/3 1/4
sleep 600
assert_focused 1/1
assert_stacking 0/1 1/1 1/2 1/3 1/4
destroy 1/3
wait
assert_focused 1/1
assert_stacking 0/1 1/1 1/2 1/4
new_client 0 x11
create 0/1
show 0/1
new_client 1 x11
create 1/1
accept_focus 1/1 false
can_take_focus 1/1 true
accept_take_focus 1/1 true
show 1/1
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
can_take_focus 1/2 true
accept_take_focus 1/2 true
show 1/2
create 1/3
set_parent 1/3 2
show 1/3
assert_focused 1/3
assert_stacking 0/1 1/1 1/2 1/3
destroy 1/3
wait
assert_focused 1/2
assert_stacking 0/1 1/1 1/2
sleep 150
assert_focused 1/2
assert_stacking 0/1 1/1 1/2
......@@ -31,6 +31,11 @@
const char *client_id = "0";
static gboolean wayland;
GHashTable *windows;
GQuark event_source_quark;
GQuark event_handlers_quark;
GQuark can_take_focus_quark;
typedef void (*XEventHandler) (GtkWidget *window, XEvent *event);
static void read_next_line (GDataInputStream *in);
......@@ -57,6 +62,186 @@ lookup_window (const char *window_id)
return window;
}
typedef struct {
GSource base;
GSource **self_ref;
GPollFD event_poll_fd;
Display *xdisplay;
} XClientEventSource;
static gboolean
x_event_source_prepare (GSource *source,
int *timeout)
{
XClientEventSource *x_source = (XClientEventSource *) source;
*timeout = -1;
return XPending (x_source->xdisplay);
}
static gboolean
x_event_source_check (GSource *source)
{
XClientEventSource *x_source = (XClientEventSource *) source;
return XPending (x_source->xdisplay);
}
static gboolean
x_event_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
XClientEventSource *x_source = (XClientEventSource *) source;
while (XPending (x_source->xdisplay))
{
GHashTableIter iter;
XEvent event;
gpointer value;
XNextEvent (x_source->xdisplay, &event);
g_hash_table_iter_init (&iter, windows);
while (g_hash_table_iter_next (&iter, NULL, &value))
{
GList *l;
GtkWidget *window = value;
GList *handlers =
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
for (l = handlers; l; l = l->next)
{
XEventHandler handler = l->data;
handler (window, &event);
}
}
}
return TRUE;
}
static void
x_event_source_finalize (GSource *source)
{
XClientEventSource *x_source = (XClientEventSource *) source;
*x_source->self_ref = NULL;
}
static GSourceFuncs x_event_funcs = {
x_event_source_prepare,
x_event_source_check,
x_event_source_dispatch,
x_event_source_finalize,
};
static GSource*
ensure_xsource_handler (GdkDisplay *gdkdisplay)
{
static GSource *source = NULL;
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
XClientEventSource *x_source;
if (source)
return g_source_ref (source);
source = g_source_new (&x_event_funcs, sizeof (XClientEventSource));
x_source = (XClientEventSource *) source;
x_source->self_ref = &source;
x_source->xdisplay = xdisplay;
x_source->event_poll_fd.fd = ConnectionNumber (xdisplay);
x_source->event_poll_fd.events = G_IO_IN;
g_source_add_poll (source, &x_source->event_poll_fd);
g_source_set_priority (source, GDK_PRIORITY_EVENTS - 1);
g_source_set_can_recurse (source, TRUE);
g_source_attach (source, NULL);
return source;
}
static gboolean
window_has_x11_event_handler (GtkWidget *window,
XEventHandler handler)
{
GList *handlers =
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
g_return_val_if_fail (handler, FALSE);
g_return_val_if_fail (!wayland, FALSE);
return g_list_find (handlers, handler) != NULL;
}
static void
unref_and_maybe_destroy_gsource (GSource *source)
{
g_source_unref (source);
if (source->ref_count == 1)
g_source_destroy (source);
}
static void
window_add_x11_event_handler (GtkWidget *window,
XEventHandler handler)
{
GSource *source;
GList *handlers =
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
g_return_if_fail (!window_has_x11_event_handler (window, handler));
source = ensure_xsource_handler (gtk_widget_get_display (window));
g_object_set_qdata_full (G_OBJECT (window), event_source_quark, source,
(GDestroyNotify) unref_and_maybe_destroy_gsource);
handlers = g_list_append (handlers, handler);
g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
}
static void
window_remove_x11_event_handler (GtkWidget *window,
XEventHandler handler)
{
GList *handlers =
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
g_return_if_fail (window_has_x11_event_handler (window, handler));
g_object_set_qdata (G_OBJECT (window), event_source_quark, NULL);
handlers = g_list_remove (handlers, handler);
g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
}
static void
handle_take_focus (GtkWidget *window,
XEvent *xevent)
{
GdkWindow *gdkwindow = gtk_widget_get_window (window);
GdkDisplay *display = gtk_widget_get_display (window);
Atom wm_protocols =
gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS");
Atom wm_take_focus =
gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
if (xevent->xany.type != ClientMessage ||
xevent->xany.window != GDK_WINDOW_XID (gdkwindow))
return;
if (xevent->xclient.message_type == wm_protocols &&
xevent->xclient.data.l[0] == wm_take_focus)
{
XSetInputFocus (xevent->xany.display,
GDK_WINDOW_XID (gdkwindow),
RevertToParent,
xevent->xclient.data.l[1]);
}
}
static void
process_line (const char *line)
{
......@@ -125,6 +310,9 @@ process_line (const char *line)
gtk_window_set_title (GTK_WINDOW (window), title);
g_free (title);
g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
GUINT_TO_POINTER (TRUE));
gtk_widget_realize (window);
if (!wayland)
......@@ -196,6 +384,130 @@ process_line (const char *line)
NULL))
g_print ("Fail to export handle for window id %s", argv[2]);
}
else if (strcmp (argv[0], "accept_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
if (!wayland &&
window_has_x11_event_handler (window, handle_take_focus))
{
g_print ("Impossible to use %s for windows accepting take focus",
argv[1]);
goto out;
}
gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0;
gtk_window_set_accept_focus (GTK_WINDOW (window), enabled);
}
else if (strcmp (argv[0], "can_take_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
if (wayland)
{
g_print ("%s not supported under wayland", argv[0]);
goto out;
}
if (window_has_x11_event_handler (window, handle_take_focus))
{
g_print ("Impossible to change %s for windows accepting take focus",
argv[1]);
goto out;
}
GdkDisplay *display = gdk_display_get_default ();
GdkWindow *gdkwindow = gtk_widget_get_window (window);
Display *xdisplay = gdk_x11_display_get_xdisplay (display);
Window xwindow = GDK_WINDOW_XID (gdkwindow);
Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0;
Atom *protocols = NULL;
Atom *new_protocols;
int n_protocols = 0;
int i, n = 0;
gdk_display_sync (display);
XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols);
new_protocols = g_new0 (Atom, n_protocols + (add ? 1 : 0));
for (i = 0; i < n_protocols; ++i)
{
if (protocols[i] != wm_take_focus)
new_protocols[n++] = protocols[i];
}
if (add)
new_protocols[n++] = wm_take_focus;
XSetWMProtocols (xdisplay, xwindow, new_protocols, n);
g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
GUINT_TO_POINTER (add));
XFree (new_protocols);
XFree (protocols);
}
else if (strcmp (argv[0], "accept_take_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
if (wayland)
{
g_print ("%s not supported under wayland", argv[0]);
goto out;
}
if (gtk_window_get_accept_focus (GTK_WINDOW (window)))
{
g_print ("%s not supported for input windows", argv[0]);
goto out;
}
if (!g_object_get_qdata (G_OBJECT (window), can_take_focus_quark))
{
g_print ("%s not supported for windows with no WM_TAKE_FOCUS set",
argv[0]);
goto out;
}
if (g_ascii_strcasecmp (argv[2], "true") == 0)
window_add_x11_event_handler (window, handle_take_focus);
else
window_remove_x11_event_handler (window, handle_take_focus);
}
else if (strcmp (argv[0], "show") == 0)
{
if (argc != 2)
......@@ -460,6 +772,9 @@ main(int argc, char **argv)
windows = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
event_source_quark = g_quark_from_static_string ("event-source");
event_handlers_quark = g_quark_from_static_string ("event-handlers");
can_take_focus_quark = g_quark_from_static_string ("can-take-focus");
GInputStream *raw_in = g_unix_input_stream_new (0, FALSE);
GDataInputStream *in = g_data_input_stream_new (raw_in);
......
......@@ -77,7 +77,7 @@ test_case_new (void)
}
static gboolean
test_case_before_redraw (gpointer data)
test_case_loop_quit (gpointer data)
{
TestCase *test = data;
......@@ -86,6 +86,24 @@ test_case_before_redraw (gpointer data)
return FALSE;
}
static gboolean
test_case_dispatch (TestCase *test,
GError **error)
{
/* Wait until we've done any outstanding queued up work.
* Though we add this as BEFORE_REDRAW, the iteration that runs the
* BEFORE_REDRAW idles will proceed on and do the redraw, so we're
* waiting until after *all* frame processing.
*/
meta_later_add (META_LATER_BEFORE_REDRAW,
test_case_loop_quit,
test,
NULL);
g_main_loop_run (test->loop);
return TRUE;
}
static gboolean
test_case_wait (TestCase *test,
GError **error)
......@@ -102,16 +120,8 @@ test_case_wait (TestCase *test,
if (!test_client_wait (value, error))
return FALSE;
/* Then wait until we've done any outstanding queued up work.
* Though we add this as BEFORE_REDRAW, the iteration that runs the
* BEFORE_REDRAW idles will proceed on and do the redraw, so we're
* waiting until after *all* frame processing.
*/
meta_later_add (META_LATER_BEFORE_REDRAW,
test_case_before_redraw,
test,
NULL);
g_main_loop_run (test->loop);
/* Then wait until we've done any outstanding queued up work. */
test_case_dispatch (test, error);
/* Then set an XSync counter ourselves and and wait until
* we receive the resulting event - this makes sure that we've
......@@ -121,6 +131,17 @@ test_case_wait (TestCase *test,
return TRUE;
}
static gboolean
test_case_sleep (TestCase *test,
guint32 interval,
GError **error)
{
g_timeout_add_full (G_PRIORITY_LOW, interval, test_case_loop_quit, test, NULL);
g_main_loop_run (test->loop);
return TRUE;
}
#define BAD_COMMAND(...) \
G_STMT_START { \
g_set_error (error, \
......@@ -237,6 +258,37 @@ test_case_assert_stacking (TestCase *test,
return *error == NULL;
}
static gboolean
test_case_assert_focused (TestCase *test,
const char *expected_window,
GError **error)
{
MetaDisplay *display = meta_get_display ();
if (!display->focus_window)
{
if (g_strcmp0 (expected_window, "none") != 0)
{
g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED,
"focus: expected='%s', actual='none'", expected_window);
}
}
else
{
const char *focused = display->focus_window->title;
if (g_str_has_prefix (focused, "test/"))
focused += 5;
if (g_strcmp0 (focused, expected_window) != 0)
g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED,
"focus: expected='%s', actual='%s'",
expected_window, focused);
}
return *error == NULL;
}
static gboolean
test_case_check_xserver_stacking (TestCase *test,
GError **error)
......@@ -401,6 +453,63 @@ test_case_do (TestCase *test,
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "accept_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "can_take_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "accept_take_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
......@@ -480,6 +589,28 @@ test_case_do (TestCase *test,
if (!test_case_wait (test, error))
return FALSE;
}
else if (strcmp (argv[0], "dispatch") == 0)
{
if (argc != 1)
BAD_COMMAND("usage: %s", argv[0]);
if (!test_case_dispatch (test, error))
return FALSE;
}
else if (strcmp (argv[0], "sleep") == 0)
{
guint64 interval;
if (argc != 2)
BAD_COMMAND("usage: %s <milliseconds>", argv[0]);
if (!g_ascii_string_to_unsigned (argv[1], 10, 0, G_MAXUINT32,
&interval, error))
return FALSE;
if (!test_case_sleep (test, (guint32) interval, error))
return FALSE;
}
else if (strcmp (argv[0], "assert_stacking") == 0)
{
if (!test_case_assert_stacking (test, argv + 1, argc - 1, error))
......@@ -488,6 +619,11 @@ test_case_do (TestCase *test,
if (!test_case_check_xserver_stacking (test, error))
return FALSE;
}
else if (strcmp (argv[0], "assert_focused") == 0)
{
if (!test_case_assert_focused (test, argv[1], error))
return FALSE;
}
else
{
BAD_COMMAND("Unknown command %s", argv[0]);
......
......@@ -50,6 +50,8 @@
#include "x11/window-props.h"
#include "x11/xprops.h"
#define TAKE_FOCUS_FALLBACK_DELAY_MS 150
enum _MetaGtkEdgeConstraints
{
META_GTK_EDGE_CONSTRAINT_TOP_TILED = 1 << 0,
......@@ -64,6 +66,11 @@ enum _MetaGtkEdgeConstraints
G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW)
static void
meta_window_x11_maybe_focus_delayed (MetaWindow *window,
GQueue *other_focus_candidates,
guint32 timestamp);
static void
meta_window_x11_init (MetaWindowX11 *window_x11)
{
......@@ -776,6 +783,152 @@ request_take_focus (MetaWindow *window,
send_icccm_message (window, display->x11_display->atom_WM_TAKE_FOCUS, timestamp);
}
typedef struct
{
MetaWindow *window;
GQueue *pending_focus_candidates;
guint32 timestamp;