wayland: manually activate/deactivate stage when taking/dropping grab

clutter currently never emits activated or deactivated signals on
the stage object when using the EGL backend. Since the stage never
gets activated, accessibility tools, like orca, don't work.

This commit makes mutter take on the responsibility, by tracking
when the stage gains/loses focus, and then synthesizing stage
CLUTTER_STAGE_STATE_ACTIVATED state events.

A limitation of this approach is that clutter's own notion of
the stage activeness won't reflect mutter's notion of the
stage activeness.  This isn't a problem, in practice, and can
be addressed in the medium-term after making changes to
clutter.

https://bugzilla.gnome.org/show_bug.cgi?id=746670
This commit is contained in:
Ray Strode 2015-03-24 12:40:10 -04:00
parent c3455b01af
commit 9f17c05a15
3 changed files with 73 additions and 0 deletions

View File

@ -41,6 +41,7 @@ typedef struct {
struct _MetaStagePrivate { struct _MetaStagePrivate {
MetaOverlay cursor_overlay; MetaOverlay cursor_overlay;
gboolean is_active;
}; };
typedef struct _MetaStagePrivate MetaStagePrivate; typedef struct _MetaStagePrivate MetaStagePrivate;
@ -126,15 +127,41 @@ meta_stage_paint (ClutterActor *actor)
meta_overlay_paint (&priv->cursor_overlay); meta_overlay_paint (&priv->cursor_overlay);
} }
static void
meta_stage_activate (ClutterStage *actor)
{
MetaStage *stage = META_STAGE (actor);
MetaStagePrivate *priv = meta_stage_get_instance_private (stage);
CLUTTER_STAGE_CLASS (meta_stage_parent_class)->activate (actor);
priv->is_active = TRUE;
}
static void
meta_stage_deactivate (ClutterStage *actor)
{
MetaStage *stage = META_STAGE (actor);
MetaStagePrivate *priv = meta_stage_get_instance_private (stage);
CLUTTER_STAGE_CLASS (meta_stage_parent_class)->deactivate (actor);
priv->is_active = FALSE;
}
static void static void
meta_stage_class_init (MetaStageClass *klass) meta_stage_class_init (MetaStageClass *klass)
{ {
ClutterStageClass *stage_class = (ClutterStageClass *) klass;
ClutterActorClass *actor_class = (ClutterActorClass *) klass; ClutterActorClass *actor_class = (ClutterActorClass *) klass;
GObjectClass *object_class = (GObjectClass *) klass; GObjectClass *object_class = (GObjectClass *) klass;
object_class->finalize = meta_stage_finalize; object_class->finalize = meta_stage_finalize;
actor_class->paint = meta_stage_paint; actor_class->paint = meta_stage_paint;
stage_class->activate = meta_stage_activate;
stage_class->deactivate = meta_stage_deactivate;
} }
static void static void
@ -195,3 +222,42 @@ meta_stage_set_cursor (MetaStage *stage,
meta_overlay_set (&priv->cursor_overlay, texture, rect); meta_overlay_set (&priv->cursor_overlay, texture, rect);
queue_redraw_for_overlay (stage, &priv->cursor_overlay); queue_redraw_for_overlay (stage, &priv->cursor_overlay);
} }
void
meta_stage_set_active (MetaStage *stage,
gboolean is_active)
{
MetaStagePrivate *priv = meta_stage_get_instance_private (stage);
ClutterEvent event = { 0 };
/* Used by the native backend to inform accessibility technologies
* about when the stage loses and gains input focus.
*
* For the X11 backend, clutter transparently takes care of this
* for us.
*/
if (priv->is_active == is_active)
return;
clutter_event_set_stage (&event, CLUTTER_STAGE (stage));
event.stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
if (is_active)
event.stage_state.new_state = CLUTTER_STAGE_STATE_ACTIVATED;
/* Emitting this StageState event will result in the stage getting
* activated or deactivated (with the activated or deactivated signal
* getting emitted from the stage)
*
* FIXME: This won't update ClutterStage's own notion of its
* activeness. For that we would need to somehow trigger a
* _clutter_stage_update_state call, which will probably
* require new API in clutter. In practice, nothing relies
* on the ClutterStage's own notion of activeness when using
* the EGL backend.
*
* See http://bugzilla.gnome.org/746670
*/
clutter_stage_event (CLUTTER_STAGE (stage), &event);
}

View File

@ -54,6 +54,9 @@ ClutterActor *meta_stage_new (void);
void meta_stage_set_cursor (MetaStage *stage, void meta_stage_set_cursor (MetaStage *stage,
CoglTexture *texture, CoglTexture *texture,
MetaRectangle *rect); MetaRectangle *rect);
void meta_stage_set_active (MetaStage *stage,
gboolean is_active);
G_END_DECLS G_END_DECLS
#endif /* META_STAGE_H */ #endif /* META_STAGE_H */

View File

@ -52,6 +52,7 @@
#include <meta/meta-backend.h> #include <meta/meta-backend.h>
#include "backends/native/meta-backend-native.h" #include "backends/native/meta-backend-native.h"
#include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-backend-x11.h"
#include "backends/meta-stage.h"
#include <clutter/x11/clutter-x11.h> #include <clutter/x11/clutter-x11.h>
#ifdef HAVE_RANDR #ifdef HAVE_RANDR
@ -1411,6 +1412,8 @@ meta_display_sync_wayland_input_focus (MetaDisplay *display)
#ifdef HAVE_WAYLAND #ifdef HAVE_WAYLAND
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWindow *focus_window = NULL; MetaWindow *focus_window = NULL;
MetaBackend *backend = meta_get_backend ();
MetaStage *stage = META_STAGE (meta_backend_get_stage (backend));
if (!meta_display_windows_are_interactable (display)) if (!meta_display_windows_are_interactable (display))
focus_window = NULL; focus_window = NULL;
@ -1421,6 +1424,7 @@ meta_display_sync_wayland_input_focus (MetaDisplay *display)
else else
meta_topic (META_DEBUG_FOCUS, "Focus change has no effect, because there is no matching wayland surface"); meta_topic (META_DEBUG_FOCUS, "Focus change has no effect, because there is no matching wayland surface");
meta_stage_set_active (stage, focus_window == NULL);
meta_wayland_compositor_set_input_focus (compositor, focus_window); meta_wayland_compositor_set_input_focus (compositor, focus_window);
meta_wayland_seat_repick (compositor->seat); meta_wayland_seat_repick (compositor->seat);