Add a modal mode for plugins
mutter_plugin_begin_modal() and mutter_plugin_begin_modal() allow putting a plugin into a "modal" state. This means: - The plugin has the keyboard and mouse grabbed - All keyboard and mouse events go exclusively to the plugin mutter-plugin.[ch]: Add public API compositor.c compositor-private.h: Implement the API mutter-plugin-manager.c: When reloading plugins, make sure none of them are modal at that moment, and if so force-unmodal them. common.h: Add META_GRAB_OP_COMPOSITOR display: When display->grab_op is META_GRAB_OP_COMPOSITOR forward relevant events exclusively to the compositor. http://bugzilla.gnome.org/show_bug.cgi?id=590754
This commit is contained in:
parent
b1776b5ae5
commit
67682a2683
@ -23,6 +23,8 @@ struct _MetaCompositor
|
|||||||
|
|
||||||
ClutterActor *shadow_src;
|
ClutterActor *shadow_src;
|
||||||
|
|
||||||
|
MutterPlugin *modal_plugin;
|
||||||
|
|
||||||
gboolean show_redraw : 1;
|
gboolean show_redraw : 1;
|
||||||
gboolean debug : 1;
|
gboolean debug : 1;
|
||||||
gboolean no_mipmaps : 1;
|
gboolean no_mipmaps : 1;
|
||||||
@ -51,4 +53,16 @@ void mutter_set_stage_input_region (MetaScreen *screen,
|
|||||||
XserverRegion region);
|
XserverRegion region);
|
||||||
void mutter_empty_stage_input_region (MetaScreen *screen);
|
void mutter_empty_stage_input_region (MetaScreen *screen);
|
||||||
|
|
||||||
|
gboolean mutter_begin_modal_for_plugin (MetaScreen *screen,
|
||||||
|
MutterPlugin *plugin,
|
||||||
|
Window grab_window,
|
||||||
|
Cursor cursor,
|
||||||
|
MetaModalOptions options,
|
||||||
|
guint32 timestamp);
|
||||||
|
void mutter_end_modal_for_plugin (MetaScreen *screen,
|
||||||
|
MutterPlugin *plugin,
|
||||||
|
guint32 timestamp);
|
||||||
|
|
||||||
|
void mutter_check_end_modal (MetaScreen *screen);
|
||||||
|
|
||||||
#endif /* META_COMPOSITOR_PRIVATE_H */
|
#endif /* META_COMPOSITOR_PRIVATE_H */
|
||||||
|
@ -350,6 +350,117 @@ mutter_empty_stage_input_region (MetaScreen *screen)
|
|||||||
mutter_set_stage_input_region (screen, region);
|
mutter_set_stage_input_region (screen, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mutter_begin_modal_for_plugin (MetaScreen *screen,
|
||||||
|
MutterPlugin *plugin,
|
||||||
|
Window grab_window,
|
||||||
|
Cursor cursor,
|
||||||
|
MetaModalOptions options,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
|
||||||
|
* are significant differences in how we handle grabs that make it difficult to
|
||||||
|
* merge the two.
|
||||||
|
*/
|
||||||
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
|
Display *xdpy = meta_display_get_xdisplay (display);
|
||||||
|
MetaCompositor *compositor = display->compositor;
|
||||||
|
gboolean pointer_grabbed = FALSE;
|
||||||
|
gboolean keyboard_grabbed = FALSE;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
|
||||||
|
{
|
||||||
|
result = XGrabPointer (xdpy, grab_window,
|
||||||
|
False, /* owner_events */
|
||||||
|
(ButtonPressMask | ButtonReleaseMask |
|
||||||
|
EnterWindowMask | LeaveWindowMask | PointerMotionMask),
|
||||||
|
GrabModeAsync, GrabModeAsync,
|
||||||
|
None, /* confine to */
|
||||||
|
cursor,
|
||||||
|
timestamp);
|
||||||
|
if (result != Success)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
pointer_grabbed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((options & META_MODAL_KEYBOARD_ALREADY_GRABBED) == 0)
|
||||||
|
{
|
||||||
|
XGrabKeyboard (xdpy, grab_window,
|
||||||
|
False, /* owner_events */
|
||||||
|
GrabModeAsync, GrabModeAsync,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
if (result != Success)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
keyboard_grabbed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
display->grab_op = META_GRAB_OP_COMPOSITOR;
|
||||||
|
display->grab_window = NULL;
|
||||||
|
display->grab_screen = screen;
|
||||||
|
display->grab_have_pointer = TRUE;
|
||||||
|
display->grab_have_keyboard = TRUE;
|
||||||
|
|
||||||
|
compositor->modal_plugin = plugin;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (pointer_grabbed)
|
||||||
|
XUngrabPointer (xdpy, timestamp);
|
||||||
|
if (keyboard_grabbed)
|
||||||
|
XUngrabKeyboard (xdpy, timestamp);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mutter_end_modal_for_plugin (MetaScreen *screen,
|
||||||
|
MutterPlugin *plugin,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
|
Display *xdpy = meta_display_get_xdisplay (display);
|
||||||
|
MetaCompositor *compositor = display->compositor;
|
||||||
|
|
||||||
|
g_return_if_fail (compositor->modal_plugin == plugin);
|
||||||
|
|
||||||
|
XUngrabPointer (xdpy, timestamp);
|
||||||
|
XUngrabKeyboard (xdpy, timestamp);
|
||||||
|
|
||||||
|
display->grab_op = META_GRAB_OP_NONE;
|
||||||
|
display->grab_window = NULL;
|
||||||
|
display->grab_screen = NULL;
|
||||||
|
display->grab_have_pointer = FALSE;
|
||||||
|
display->grab_have_keyboard = FALSE;
|
||||||
|
|
||||||
|
compositor->modal_plugin = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is used when reloading plugins to make sure we don't have
|
||||||
|
* a left-over modal grab for this screen.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mutter_check_end_modal (MetaScreen *screen)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
|
MetaCompositor *compositor = display->compositor;
|
||||||
|
|
||||||
|
if (compositor->modal_plugin &&
|
||||||
|
mutter_plugin_get_screen (compositor->modal_plugin) == screen)
|
||||||
|
{
|
||||||
|
mutter_end_modal_for_plugin (screen,
|
||||||
|
compositor->modal_plugin,
|
||||||
|
CurrentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_compositor_manage_screen (MetaCompositor *compositor,
|
meta_compositor_manage_screen (MetaCompositor *compositor,
|
||||||
MetaScreen *screen)
|
MetaScreen *screen)
|
||||||
@ -513,11 +624,41 @@ meta_compositor_set_updates (MetaCompositor *compositor,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
is_grabbed_event (XEvent *event)
|
||||||
|
{
|
||||||
|
switch (event->xany.type)
|
||||||
|
{
|
||||||
|
case ButtonPress:
|
||||||
|
case ButtonRelease:
|
||||||
|
case EnterNotify:
|
||||||
|
case LeaveNotify:
|
||||||
|
case MotionNotify:
|
||||||
|
case KeyPressMask:
|
||||||
|
case KeyReleaseMask:
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_compositor_process_event (MetaCompositor *compositor,
|
meta_compositor_process_event (MetaCompositor *compositor,
|
||||||
XEvent *event,
|
XEvent *event,
|
||||||
MetaWindow *window)
|
MetaWindow *window)
|
||||||
{
|
{
|
||||||
|
if (compositor->modal_plugin && is_grabbed_event (event))
|
||||||
|
{
|
||||||
|
MutterPluginClass *klass = MUTTER_PLUGIN_GET_CLASS (compositor->modal_plugin);
|
||||||
|
|
||||||
|
if (klass->xevent_filter)
|
||||||
|
klass->xevent_filter (compositor->modal_plugin, event);
|
||||||
|
|
||||||
|
/* We always consume events even if the plugin says it didn't handle them;
|
||||||
|
* exclusive is exclusive */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
if (window)
|
if (window)
|
||||||
{
|
{
|
||||||
MetaCompScreen *info;
|
MetaCompScreen *info;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "compositor-private.h"
|
||||||
#include "mutter-plugin-manager.h"
|
#include "mutter-plugin-manager.h"
|
||||||
#include "prefs.h"
|
#include "prefs.h"
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
@ -340,6 +341,10 @@ mutter_plugin_manager_reload (MutterPluginManager *plugin_mgr)
|
|||||||
* plugins to unload? We are probably not going to have large numbers of
|
* plugins to unload? We are probably not going to have large numbers of
|
||||||
* plugins loaded at the same time, so it might not be worth it.
|
* plugins loaded at the same time, so it might not be worth it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Prevent stale grabs on unloaded plugins */
|
||||||
|
mutter_check_end_modal (plugin_mgr->screen);
|
||||||
|
|
||||||
mutter_plugin_manager_unload (plugin_mgr);
|
mutter_plugin_manager_unload (plugin_mgr);
|
||||||
return mutter_plugin_manager_load (plugin_mgr);
|
return mutter_plugin_manager_load (plugin_mgr);
|
||||||
}
|
}
|
||||||
|
@ -467,6 +467,64 @@ mutter_plugin_get_windows (MutterPlugin *plugin)
|
|||||||
return mutter_get_windows (priv->screen);
|
return mutter_get_windows (priv->screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mutter_plugin_begin_modal:
|
||||||
|
* @plugin: a #MutterPlugin
|
||||||
|
* @grab_window: the X window to grab the keyboard and mouse on
|
||||||
|
* @cursor: the cursor to use for the pointer grab, or None,
|
||||||
|
* to use the normal cursor for the grab window and
|
||||||
|
* its descendants.
|
||||||
|
* @options: flags that modify the behavior of the modal grab
|
||||||
|
* @timestamp: the timestamp used for establishing grabs
|
||||||
|
*
|
||||||
|
* This function is used to grab the keyboard and mouse for the exclusive
|
||||||
|
* use of the plugin. Correct operation requires that both the keyboard
|
||||||
|
* and mouse are grabbed, or thing will break. (In particular, other
|
||||||
|
* passive X grabs in Mutter can trigger but not be handled by the normal
|
||||||
|
* keybinding handling code.) However, the plugin can establish the keyboard
|
||||||
|
* and/or mouse grabs ahead of time and pass in the
|
||||||
|
* %META_MODAL_POINTER_ALREADY_GRABBED and/or %META_MODAL_KEYBOARD_ALREADY_GRABBED
|
||||||
|
* options. This facility is provided for two reasons: first to allow using
|
||||||
|
* this function to establish modality after a passive grab, and second to
|
||||||
|
* allow using obscure features of XGrabPointer() and XGrabKeyboard() without
|
||||||
|
* having to add them to this API.
|
||||||
|
*
|
||||||
|
* Return value: whether we successfully grabbed the keyboard and
|
||||||
|
* mouse and made the plugin modal.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
mutter_plugin_begin_modal (MutterPlugin *plugin,
|
||||||
|
Window grab_window,
|
||||||
|
Cursor cursor,
|
||||||
|
MetaModalOptions options,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
MutterPluginPrivate *priv = MUTTER_PLUGIN (plugin)->priv;
|
||||||
|
|
||||||
|
return mutter_begin_modal_for_plugin (priv->screen, plugin,
|
||||||
|
grab_window, cursor, options, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mutter_plugin_end_modal
|
||||||
|
* @plugin: a #MutterPlugin
|
||||||
|
* @timestamp: the time used for releasing grabs
|
||||||
|
*
|
||||||
|
* Ends the modal operation begun with meta_plugin_begin_modal(). This
|
||||||
|
* ungrabs both the mouse and keyboard even when
|
||||||
|
* %META_MODAL_POINTER_ALREADY_GRABBED or
|
||||||
|
* %META_MODAL_KEYBOARD_ALREADY_GRABBED were provided as options
|
||||||
|
* when beginnning the modal operation.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mutter_plugin_end_modal (MutterPlugin *plugin,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
MutterPluginPrivate *priv = MUTTER_PLUGIN (plugin)->priv;
|
||||||
|
|
||||||
|
mutter_end_modal_for_plugin (priv->screen, plugin, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
Display *
|
Display *
|
||||||
mutter_plugin_get_xdisplay (MutterPlugin *plugin)
|
mutter_plugin_get_xdisplay (MutterPlugin *plugin)
|
||||||
{
|
{
|
||||||
|
@ -1199,6 +1199,7 @@ grab_op_is_mouse (MetaGrabOp op)
|
|||||||
case META_GRAB_OP_KEYBOARD_RESIZING_SW:
|
case META_GRAB_OP_KEYBOARD_RESIZING_SW:
|
||||||
case META_GRAB_OP_KEYBOARD_RESIZING_NW:
|
case META_GRAB_OP_KEYBOARD_RESIZING_NW:
|
||||||
case META_GRAB_OP_KEYBOARD_MOVING:
|
case META_GRAB_OP_KEYBOARD_MOVING:
|
||||||
|
case META_GRAB_OP_COMPOSITOR:
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1228,6 +1229,7 @@ grab_op_is_keyboard (MetaGrabOp op)
|
|||||||
case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
|
case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
|
||||||
case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
|
case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
|
||||||
case META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING:
|
case META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING:
|
||||||
|
case META_GRAB_OP_COMPOSITOR:
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1670,6 +1672,9 @@ event_callback (XEvent *event,
|
|||||||
{
|
{
|
||||||
case KeyPress:
|
case KeyPress:
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
|
if (display->grab_op == META_GRAB_OP_COMPOSITOR)
|
||||||
|
break;
|
||||||
|
|
||||||
/* For key events, it's important to enforce single-handling, or
|
/* For key events, it's important to enforce single-handling, or
|
||||||
* we can get into a confused state. So if a keybinding is
|
* we can get into a confused state. So if a keybinding is
|
||||||
* handled (because it's one of our hot-keys, or because we are
|
* handled (because it's one of our hot-keys, or because we are
|
||||||
@ -1679,12 +1684,14 @@ event_callback (XEvent *event,
|
|||||||
bypass_compositor = meta_display_process_key_event (display, window, event);
|
bypass_compositor = meta_display_process_key_event (display, window, event);
|
||||||
break;
|
break;
|
||||||
case ButtonPress:
|
case ButtonPress:
|
||||||
|
if (display->grab_op == META_GRAB_OP_COMPOSITOR)
|
||||||
|
break;
|
||||||
|
|
||||||
if (event->xbutton.button == 4 || event->xbutton.button == 5)
|
if (event->xbutton.button == 4 || event->xbutton.button == 5)
|
||||||
{
|
/* Scrollwheel event, do nothing and deliver event to compositor below */
|
||||||
/* Scrollwheel event, do nothing and deliver event to compositor below
|
break;
|
||||||
*/
|
|
||||||
}
|
if ((window &&
|
||||||
else if ((window &&
|
|
||||||
grab_op_is_mouse (display->grab_op) &&
|
grab_op_is_mouse (display->grab_op) &&
|
||||||
display->grab_button != (int) event->xbutton.button &&
|
display->grab_button != (int) event->xbutton.button &&
|
||||||
display->grab_window == window) ||
|
display->grab_window == window) ||
|
||||||
@ -1874,16 +1881,25 @@ event_callback (XEvent *event,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ButtonRelease:
|
case ButtonRelease:
|
||||||
|
if (display->grab_op == META_GRAB_OP_COMPOSITOR)
|
||||||
|
break;
|
||||||
|
|
||||||
if (display->grab_window == window &&
|
if (display->grab_window == window &&
|
||||||
grab_op_is_mouse (display->grab_op))
|
grab_op_is_mouse (display->grab_op))
|
||||||
meta_window_handle_mouse_grab_op_event (window, event);
|
meta_window_handle_mouse_grab_op_event (window, event);
|
||||||
break;
|
break;
|
||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
|
if (display->grab_op == META_GRAB_OP_COMPOSITOR)
|
||||||
|
break;
|
||||||
|
|
||||||
if (display->grab_window == window &&
|
if (display->grab_window == window &&
|
||||||
grab_op_is_mouse (display->grab_op))
|
grab_op_is_mouse (display->grab_op))
|
||||||
meta_window_handle_mouse_grab_op_event (window, event);
|
meta_window_handle_mouse_grab_op_event (window, event);
|
||||||
break;
|
break;
|
||||||
case EnterNotify:
|
case EnterNotify:
|
||||||
|
if (display->grab_op == META_GRAB_OP_COMPOSITOR)
|
||||||
|
break;
|
||||||
|
|
||||||
if (display->grab_window == window &&
|
if (display->grab_window == window &&
|
||||||
grab_op_is_mouse (display->grab_op))
|
grab_op_is_mouse (display->grab_op))
|
||||||
{
|
{
|
||||||
@ -1976,6 +1992,9 @@ event_callback (XEvent *event,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LeaveNotify:
|
case LeaveNotify:
|
||||||
|
if (display->grab_op == META_GRAB_OP_COMPOSITOR)
|
||||||
|
break;
|
||||||
|
|
||||||
if (display->grab_window == window &&
|
if (display->grab_window == window &&
|
||||||
grab_op_is_mouse (display->grab_op))
|
grab_op_is_mouse (display->grab_op))
|
||||||
meta_window_handle_mouse_grab_op_event (window, event);
|
meta_window_handle_mouse_grab_op_event (window, event);
|
||||||
|
@ -138,7 +138,10 @@ typedef enum
|
|||||||
META_GRAB_OP_CLICKING_ABOVE,
|
META_GRAB_OP_CLICKING_ABOVE,
|
||||||
META_GRAB_OP_CLICKING_UNABOVE,
|
META_GRAB_OP_CLICKING_UNABOVE,
|
||||||
META_GRAB_OP_CLICKING_STICK,
|
META_GRAB_OP_CLICKING_STICK,
|
||||||
META_GRAB_OP_CLICKING_UNSTICK
|
META_GRAB_OP_CLICKING_UNSTICK,
|
||||||
|
|
||||||
|
/* Special grab op when the compositor asked for a grab */
|
||||||
|
META_GRAB_OP_COMPOSITOR
|
||||||
} MetaGrabOp;
|
} MetaGrabOp;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
|
@ -255,6 +255,31 @@ void
|
|||||||
mutter_plugin_set_stage_input_region (MutterPlugin *plugin,
|
mutter_plugin_set_stage_input_region (MutterPlugin *plugin,
|
||||||
XserverRegion region);
|
XserverRegion region);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaModalOptions:
|
||||||
|
* @META_MODAL_POINTER_ALREADY_GRABBED: if set the pointer is already
|
||||||
|
* grabbed by the plugin and should not be grabbed again.
|
||||||
|
* @META_MODAL_KEYBOARD_ALREADY_GRABBED: if set the keyboard is already
|
||||||
|
* grabbed by the plugin and should not be grabbed again.
|
||||||
|
*
|
||||||
|
* Options that can be provided when calling mutter_plugin_begin_modal().
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
META_MODAL_POINTER_ALREADY_GRABBED = 1 << 0,
|
||||||
|
META_MODAL_KEYBOARD_ALREADY_GRABBED = 1 << 1
|
||||||
|
} MetaModalOptions;
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mutter_plugin_begin_modal (MutterPlugin *plugin,
|
||||||
|
Window grab_window,
|
||||||
|
Cursor cursor,
|
||||||
|
MetaModalOptions options,
|
||||||
|
guint32 timestamp);
|
||||||
|
|
||||||
|
void
|
||||||
|
mutter_plugin_end_modal (MutterPlugin *plugin,
|
||||||
|
guint32 timestamp);
|
||||||
|
|
||||||
GList *
|
GList *
|
||||||
mutter_plugin_get_windows (MutterPlugin *plugin);
|
mutter_plugin_get_windows (MutterPlugin *plugin);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user