Compare commits

...

31 Commits

Author SHA1 Message Date
c7b8f26cad compositor: Add an API to query if the stage is focused
gnome-shell needs to know whether the stage window is focused so
it can synchronize between stage window focus and Clutter key actor
focus. Track all X windows, even those without MetaWindows, when
tracking the focus window, and add a compositor-level API to determine
when the stage is focused.
2013-05-28 15:07:13 -04:00
8747b97ba3 display: Ensure that we ignore our own focus events for focus predictions
When we set the input focus, we first set the predicted window,
and then try to process focus events. But as XI_FocusOut on the
existing window comes before XI_FocusIn on the new window, we'll
see the focus out on the old window and think the focus is going
to nothing, which makes mutter think the prediction failed.

This didn't really matter as nothing paid attention to the focus
window changing, but with gnome-shell's focus rework, we'll try
and drop keyboard focus in events like these.

Fix this by making sure that we ignore focus window changes of our
own cause when updating the focus window field, by ignoring all
focus events that have a serial the same as the focus request or
lower. Note that if mutter doens't make any requests after the
focus request, this could be racy, as another client could steal
the focus, but mutter would ignore it as the serial was the same.
Bump the serial by making a dummy ChangeProperty request to the
root window in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=701017
2013-05-28 14:59:20 -04:00
d20078574e Bump version to 3.9.2
Update NEWS.
2013-05-28 17:23:25 +02:00
26bd4fde5c Updated Norwegian bokmål translation 2013-05-28 09:48:06 +02:00
2af49e503f keybindings: Grab and emit a signal when XK_ISO_Next_Group is pressed
This will make it possible to implement input source switching in
gnome-shell using the popular modifiers-only keybinding that's
implemented on the X server through an XKB option.

https://bugzilla.gnome.org/show_bug.cgi?id=697002
2013-05-27 13:56:04 +02:00
6ea6af6eb4 prefs: Track the XKB 'grp:' option in gsettings as a keybinding pref
We'll use the value of this option to establish a passive grab on the
keycode/modifier combos generating XK_ISO_Next_Group.

https://bugzilla.gnome.org/show_bug.cgi?id=697002
2013-05-27 13:55:57 +02:00
10df80762c keybindings: Add API to freeze/unfreeze the keyboard
We'll use this in gnome-shell to freeze the keyboard right before
switching input source and unfreeze it after that's finished so that
we don't lose any key events to the wrong input source.

https://bugzilla.gnome.org/show_bug.cgi?id=697001
2013-05-27 13:55:52 +02:00
f86032d700 prefs: Fix binding remaining grabbed after clearing all strokes
If a binding is updated with a clear set of strokes (effectively
disabling it) we aren't signaling that the binding changed and thus
the previous strokes will continue to be grabbed.

This fixes that and tries to do a better effort at checking if the
binding changed or not.

https://bugzilla.gnome.org/show_bug.cgi?id=697000
2013-05-27 13:55:33 +02:00
a8eb33f6fd Updated slovak translation 2013-05-25 21:58:43 +02:00
bd19de9429 compositor: Add an API to focus the stage X window
gnome-shell has traditionally just called XSetInputFocus when wanting to
set the input focus to the stage window, but this might cause strange,
hard-to-reproduce bugs because of an interference with mutter's focus
prediction. Add API to allow gnome-shell to focus the stage window that
also updates mutter's internal focus prediction state.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-24 17:43:37 -04:00
2ca2838548 display: Consolidate code calling XSetInputFocus into a new function
At the same time, rename set_focus_window and add a comment so we're
not confused about which function does what.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-24 17:43:37 -04:00
df8234c5e3 window: Properly handle focusing override redirect windows
If an app pops up an OR window and sets input focus to it, like
Steam does, we'll think the focus window is null, causing us to
think the app is not focused.

OR windows should not be special if they get input focus, where
the input focus would be set to NULL. Instead, the window should
be marked as focused.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
d03ffd801e display: Use XI2 constants for mode/detail focus event values
This makes no functional difference, except conceptual clarity.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
7a4c808e43 display: clean up focus_window vs expected_focus_window
Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
4f1d62170b test-focus: test program for focus window management
https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
e10804727d compositor: remove the overlay_group concept
The hierarchy handling is handled in the shell by adding stuff
directly to the uiGroup, and we have a dedicated actor for
the overview there, so we don't need this anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 18:36:05 +02:00
e430e051b7 window: Clean up the set_focused_internal function
Move things out of an indentation layer, and reshuffle
things around.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 12:35:04 -04:00
696d9d2fa9 window: Merge got_focus/lost_focus to a new function
Make it a static function for now, but this will be a private
function soon, replacing meta_window_lost_focus. This should
contain no functional changes, only cosmetic indentation changes,
so best viewed with ignorews=1 or -w or -b, you know the drill.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 12:35:04 -04:00
f6dd081acd window: Refactor "got focus" code
Just move this out to a separate function.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 12:35:04 -04:00
eddd6f8e9b Updated Norwegian bokmål translation 2013-05-20 14:46:56 +02:00
dfa4c7d670 compositor: Fix regression of shaded windows
Fix issues drawing shaded window shadows.

https://bugzilla.gnome.org/show_bug.cgi?id=693714
2013-05-15 16:52:28 +02:00
a487d4dd01 window: Eliminate a potential race condition with _NET_WM_MOVERESIZE
Clients using _NET_WM_MOVERESIZE to start a drag operation may encounter
a race condition if the user presses and releases a mouse button very
fast, getting "stuck" in a grab state. While this is easily fixed with
the user pressing the button or hitting Escape as the EWMH spec suggests,
its's still a bit of annoyance for users.

After starting a grab operation, check that the button is actually pressed
by the client, and if not, cancel the grab operation. This prevents the
stuck grab in a race-free way, although it requires an extra round-trip
to the server.

With client-side decorations becoming more popular, the use of
_NET_WM_MOVERESIZE is on the rise, thus this bug is seen more frequently
than before.

https://bugzilla.gnome.org/show_bug.cgi?id=699777
2013-05-14 14:46:20 -04:00
c2ecdd0524 prefs: Add support for string-array preferences
As we only had one string-array preference so far, we didn't bother
with adding a generic way to handle string-array preferences, and
just handled the preference in question explicitly. However we are
going to parse another string-array setting, so generalize the
existing code to make it reusable for that case.

https://bugzilla.gnome.org/show_bug.cgi?id=700223
2013-05-13 22:15:13 +02:00
50b9042ac2 window: Add an accessor for whether the window can close
The shell will use this to determine whether to show a close
button in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=699269
2013-05-09 15:34:37 -04:00
f5e75de330 ui: add missing delimiter in GTK-Doc comment block
g-ir-scanner will emit more warnings regarding broken GTK-Doc
syntax in the near future, which due to --warn-error being used
would break the build:

'''
ui/theme.c:1883: Warning: Meta: missing ":" at column 20:
 * @tokens_p: (out) The resulting tokens
                   ^
g-ir-scanner: compile: gcc -Wall -Wno-deprecated-declarations ...
g-ir-scanner: link: /bin/sh ../libtool --mode=link --tag=CC gcc ...
libtool: link: gcc -o /home/dieterv/gnome.org/checkout/mutter/...
<unknown>:: Fatal: Meta: warnings configured as fatal
<unknown>:: Fatal: Meta: warnings configured as fatal

make[4]: *** [Meta-3.0.gir] Error 1
'''

https://bugzilla.gnome.org/show_bug.cgi?id=699636
2013-05-04 00:23:11 +02:00
1ffe1eae4d Bump version to 3.9.1
Update NEWS.
2013-04-30 23:28:26 +02:00
970a446bd8 window: Add missing chain-up for finalize()
https://bugzilla.gnome.org/show_bug.cgi?id=698710
2013-04-29 14:58:31 +02:00
8880dffbdb background: Fix memory leak
https://bugzilla.gnome.org/show_bug.cgi?id=698710
2013-04-29 14:58:30 +02:00
5b6621811c barrier: Fix memory leak
https://bugzilla.gnome.org/show_bug.cgi?id=698710
2013-04-29 14:58:28 +02:00
c2a9ccb7e2 Let the UI layer (via the core) construct the frame mask
This essentially just moves install_corners() from the compositor, through
the core, into the UI layer where it arguably should have been anyway,
leaving behind stub functions which call through the various layers. This
removes the compositor's special knowledge of how rounded corners work,
replacing it with "ask the UI for an alpha mask".

The computation of border widths and heights changes a bit, because the
width and height used in install_corners() are the
meta_window_get_outer_rect() (which includes the visible borders but not
the invisible ones), whereas the more readily-available rectangle is the
MetaFrame.rect (which includes both). Computing the same width and height
as meta_window_get_outer_rect() involves compensating for the invisible
borders, but the UI layer is the authority on those anyway, so it seems
clearer to have it do the calculations from scratch.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=697758
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2013-04-17 13:35:06 +01:00
4608cb6027 Fix use of uninitialized variables
If mutter is going to -Werror by default, then it can play footloose
and fancy free with this sorta stuff.

https://bugzilla.gnome.org/show_bug.cgi?id=698179
2013-04-17 11:58:43 +02:00
31 changed files with 1931 additions and 868 deletions

1
.gitignore vendored
View File

@ -62,6 +62,7 @@ mutter-message
mutter-window-demo
focus-window
test-attached
test-focus
test-gravity
test-resizing
test-size-hints

27
NEWS
View File

@ -1,3 +1,30 @@
3.9.2
=====
* Add meta_window_can_close() function [Jasper; #699269]
* Add support for string-array preferences [Florian; #700223]
* Fix a potential race condition with _NET_WM_MOVERESIZE [Jasper; #699777]
* Fix shade window action [Stef; #693714]
* Remove overlay_group [Giovanni; #700735]
* Improve tracking of the focus window [Dan, Jasper; #647706]
* Add API to freeze/unfreeze the keyboard [Rui; #697001]
* Grab and emit a signal when XK_ISO_Next_Group is pressed [Rui; #697002]
* Misc bug fixes and cleanups [Dieter, Jasper, Rui; #699636, #700735, #697000]
Contributors:
Giovanni Campagna, Rui Matos, Florian Müllner, Jasper St. Pierre,
Dieter Verfaillie, Stef Walter, Dan Winship
Translations:
Kjartan Maraas [nb], Ján Kyselica [sk]
3.9.1
=====
* Fix miscellaneous memory leaks [Pavel; #698710]
* Misc fixes and cleanups [Stef, Simon; #698179, #697758]
Contributors:
Simon McVittie, Pavel Vasin, Stef Walter
3.8.1
=====
* Fix crash when getting default font [Bastien; #696814]

View File

@ -1,8 +1,8 @@
AC_PREREQ(2.50)
m4_define([mutter_major_version], [3])
m4_define([mutter_minor_version], [8])
m4_define([mutter_micro_version], [1])
m4_define([mutter_minor_version], [9])
m4_define([mutter_micro_version], [2])
m4_define([mutter_version],
[mutter_major_version.mutter_minor_version.mutter_micro_version])

View File

@ -4,10 +4,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: mutter 3.8.x\n"
"Project-Id-Version: mutter 3.9.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-03 14:10+0200\n"
"PO-Revision-Date: 2013-04-03 14:11+0200\n"
"POT-Creation-Date: 2013-05-28 09:47+0200\n"
"PO-Revision-Date: 2013-05-28 09:48+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian bokmål <i18n-no@lister.ping.uio.no>\n"
"Language: \n"
@ -205,14 +205,14 @@ msgstr "Visning delt til høyre"
#. This probably means that a non-WM compositor like xcompmgr is running;
#. * we have no way to get it to exit
#: ../src/compositor/compositor.c:568
#: ../src/compositor/compositor.c:571
#, c-format
msgid ""
"Another compositing manager is already running on screen %i on display \"%s"
"\"."
msgstr "En annen compositing manager kjører skjerm %i på display «%s»."
#: ../src/compositor/meta-background.c:1064
#: ../src/compositor/meta-background.c:1076
msgid "background texture could not be created from file"
msgstr "bakgrunnstekstur kunne ikke lages fra fil"
@ -250,17 +250,17 @@ msgstr "_Vent"
msgid "_Force Quit"
msgstr "_Tvungen nedstenging"
#: ../src/core/display.c:401
#: ../src/core/display.c:421
#, c-format
msgid "Missing %s extension required for compositing"
msgstr "Mangler utvidelsen %s som kreves for komposittfunksjon"
#: ../src/core/display.c:493
#: ../src/core/display.c:513
#, c-format
msgid "Failed to open X Window System display '%s'\n"
msgstr "Feil under åpning av X Window System skjerm «%s»\n"
#: ../src/core/keybindings.c:935
#: ../src/core/keybindings.c:1138
#, c-format
msgid ""
"Some other program is already using the key %s with modifiers %x as a "
@ -269,7 +269,7 @@ msgstr ""
"Et annet program bruker allerede nøkkelen %s med modifikatorer %x som "
"binding\n"
#: ../src/core/keybindings.c:1135
#: ../src/core/keybindings.c:1335
#, c-format
msgid "\"%s\" is not a valid accelerator\n"
msgstr "«%s» er ikke en gyldig aksellerator\n"
@ -333,7 +333,7 @@ msgstr "Skriv versjonsnummer"
msgid "Mutter plugin to use"
msgstr "Mutter-tillegg som skal brukes"
#: ../src/core/prefs.c:1095
#: ../src/core/prefs.c:1210
msgid ""
"Workarounds for broken applications disabled. Some applications may not "
"behave properly.\n"
@ -341,12 +341,12 @@ msgstr ""
"Funksjonalitet for å gå rundt ødelagte programmer er deaktivert. Noen "
"programmer vil kanskje ikke oppføre seg korrekt.\n"
#: ../src/core/prefs.c:1170
#: ../src/core/prefs.c:1285
#, c-format
msgid "Could not parse font description \"%s\" from GSettings key %s\n"
msgstr "Kunne ikke tolke skriftbeskrivelsen «%s» fra GSettings-nøkkel %s\n"
#: ../src/core/prefs.c:1236
#: ../src/core/prefs.c:1351
#, c-format
msgid ""
"\"%s\" found in configuration database is not a valid value for mouse button "
@ -355,7 +355,7 @@ msgstr ""
"«%s» funnet i konfigurasjonsdatabasen er ikke en gyldig verdi for endring av "
"musknapp\n"
#: ../src/core/prefs.c:1788
#: ../src/core/prefs.c:1928
#, c-format
msgid ""
"\"%s\" found in configuration database is not a valid value for keybinding "
@ -364,7 +364,7 @@ msgstr ""
"«%s» funnet i konfigurasjonsdatabasen er ikke en gyldig verdi for "
"tastaturbinding «%s»\n"
#: ../src/core/prefs.c:1887
#: ../src/core/prefs.c:2018
#, c-format
msgid "Workspace %d"
msgstr "Arbeidsområde %d"
@ -492,7 +492,7 @@ msgid "Window manager error: "
msgstr "Feil i vindushåndterer: "
#. first time through
#: ../src/core/window.c:7596
#: ../src/core/window.c:7505
#, c-format
msgid ""
"Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER "
@ -508,7 +508,7 @@ msgstr ""
#. * MWM but not WM_NORMAL_HINTS are basically broken. We complain
#. * about these apps but make them work.
#.
#: ../src/core/window.c:8320
#: ../src/core/window.c:8229
#, c-format
msgid ""
"Window %s sets an MWM hint indicating it isn't resizable, but sets min size "
@ -684,7 +684,9 @@ msgstr "Maksimer vinduer automatisk hvis de er nesten like store som skjermen"
msgid ""
"If enabled, new windows that are initially the size of the monitor "
"automatically get maximized."
msgstr "Nye vinduer som i utgangspunktet er samme størrelse som skjermen vil automatisk bli maksimert hvis denne slås på."
msgstr ""
"Nye vinduer som i utgangspunktet er samme størrelse som skjermen vil "
"automatisk bli maksimert hvis denne slås på."
#: ../src/org.gnome.mutter.gschema.xml.in.h:19
msgid "Select window from tab popup"

480
po/sk.po

File diff suppressed because it is too large Load Diff

View File

@ -53,17 +53,15 @@
*
* # Containers #
*
* There's three containers in the stage that can be used to place actors, here
* There's two containers in the stage that are used to place window actors, here
* are listed in the order in which they are painted:
*
* - window group, accessible with meta_get_window_group_for_screen()
* - top window group, accessible with meta_get_top_window_group_for_screen()
* - overlay group, accessible with meta_get_overlay_group_for_screen()
*
* Mutter will place actors representing windows in the window group, except for
* override-redirect windows (ie. popups and menus) which will be placed in the
* top window group. Mutter won't put any actors in the overlay group, but it's
* intended for compositors to place there panel, dashes, status bars, etc.
* top window group.
*/
#include <config.h>
@ -255,23 +253,6 @@ meta_get_stage_for_screen (MetaScreen *screen)
return info->stage;
}
/**
* meta_get_overlay_group_for_screen:
* @screen: a #MetaScreen
*
* Returns: (transfer none): The overlay group corresponding to @screen
*/
ClutterActor *
meta_get_overlay_group_for_screen (MetaScreen *screen)
{
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (!info)
return NULL;
return info->overlay_group;
}
/**
* meta_get_window_group_for_screen:
* @screen: a #MetaScreen
@ -388,6 +369,46 @@ meta_empty_stage_input_region (MetaScreen *screen)
meta_set_stage_input_region (screen, region);
}
void
meta_focus_stage_window (MetaScreen *screen,
guint32 timestamp)
{
ClutterStage *stage;
Window window;
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
if (!stage)
return;
window = clutter_x11_get_stage_window (stage);
if (window == None)
return;
meta_display_set_input_focus_xwindow (screen->display,
screen,
window,
timestamp);
}
gboolean
meta_stage_is_focused (MetaScreen *screen)
{
ClutterStage *stage;
Window window;
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
if (!stage)
return FALSE;
window = clutter_x11_get_stage_window (stage);
if (window == None)
return FALSE;
return (screen->display->focus_xwindow == window);
}
gboolean
meta_begin_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
@ -649,11 +670,9 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->window_group = meta_window_group_new (screen);
info->top_window_group = meta_window_group_new (screen);
info->overlay_group = clutter_actor_new ();
clutter_actor_add_child (info->stage, info->window_group);
clutter_actor_add_child (info->stage, info->top_window_group);
clutter_actor_add_child (info->stage, info->overlay_group);
info->plugin_mgr = meta_plugin_manager_new (screen);
@ -684,8 +703,6 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->pending_input_region = None;
}
clutter_actor_show (info->overlay_group);
/* Map overlay window before redirecting windows offscreen so we catch their
* contents until we show the stage.
*/

View File

@ -472,6 +472,17 @@ meta_background_dispose (GObject *object)
G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
}
static void
meta_background_finalize (GObject *object)
{
MetaBackground *self = META_BACKGROUND (object);
MetaBackgroundPrivate *priv = self->priv;
g_free (priv->filename);
G_OBJECT_CLASS (meta_background_parent_class)->finalize (object);
}
static void
ensure_pipeline (MetaBackground *self)
{
@ -643,6 +654,7 @@ meta_background_class_init (MetaBackgroundClass *klass)
g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate));
object_class->dispose = meta_background_dispose;
object_class->finalize = meta_background_finalize;
object_class->set_property = meta_background_set_property;
object_class->get_property = meta_background_get_property;

View File

@ -1645,7 +1645,7 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
if (priv->back_pixmap && priv->opacity == 0xff)
if (priv->back_pixmap && priv->opacity == 0xff && !priv->window->shaded)
return priv->opaque_region;
else
return NULL;
@ -1993,73 +1993,6 @@ meta_window_actor_sync_visibility (MetaWindowActor *self)
}
}
#define TAU (2*M_PI)
static void
install_corners (MetaWindow *window,
MetaFrameBorders *borders,
cairo_t *cr)
{
float top_left, top_right, bottom_left, bottom_right;
int x, y;
MetaRectangle outer;
meta_frame_get_corner_radiuses (window->frame,
&top_left,
&top_right,
&bottom_left,
&bottom_right);
meta_window_get_outer_rect (window, &outer);
/* top left */
x = borders->invisible.left;
y = borders->invisible.top;
cairo_arc (cr,
x + top_left,
y + top_left,
top_left,
2 * TAU / 4,
3 * TAU / 4);
/* top right */
x = borders->invisible.left + outer.width - top_right;
y = borders->invisible.top;
cairo_arc (cr,
x,
y + top_right,
top_right,
3 * TAU / 4,
4 * TAU / 4);
/* bottom right */
x = borders->invisible.left + outer.width - bottom_right;
y = borders->invisible.top + outer.height - bottom_right;
cairo_arc (cr,
x,
y,
bottom_right,
0 * TAU / 4,
1 * TAU / 4);
/* bottom left */
x = borders->invisible.left;
y = borders->invisible.top + outer.height - bottom_left;
cairo_arc (cr,
x + bottom_left,
y,
bottom_left,
1 * TAU / 4,
2 * TAU / 4);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
cairo_fill (cr);
}
static cairo_region_t *
scan_visible_region (guchar *mask_data,
int stride,
@ -2099,7 +2032,6 @@ scan_visible_region (guchar *mask_data,
static void
build_and_scan_frame_mask (MetaWindowActor *self,
MetaFrameBorders *borders,
cairo_rectangle_int_t *client_area,
cairo_region_t *shape_region)
{
@ -2145,7 +2077,7 @@ build_and_scan_frame_mask (MetaWindowActor *self,
gdk_cairo_region (cr, frame_paint_region);
cairo_clip (cr);
install_corners (priv->window, borders, cr);
meta_frame_get_mask (priv->window->frame, cr);
cairo_surface_flush (surface);
scanned_region = scan_visible_region (mask_data, stride, frame_paint_region);
@ -2214,7 +2146,10 @@ check_needs_reshape (MetaWindowActor *self)
client_area.x = borders.total.left;
client_area.y = borders.total.top;
client_area.width = priv->window->rect.width;
client_area.height = priv->window->rect.height;
if (priv->window->shaded)
client_area.height = 0;
else
client_area.height = priv->window->rect.height;
meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
@ -2308,7 +2243,7 @@ check_needs_reshape (MetaWindowActor *self)
* and scans the mask looking for all opaque pixels,
* adding it to region.
*/
build_and_scan_frame_mask (self, &borders, &client_area, region);
build_and_scan_frame_mask (self, &client_area, region);
}
priv->shape_region = region;

View File

@ -59,6 +59,8 @@ struct _MetaBarrierPrivate
PointerBarrier xbarrier;
};
static void meta_barrier_event_unref (MetaBarrierEvent *event);
static void
meta_barrier_get_property (GObject *object,
guint prop_id,
@ -359,6 +361,8 @@ meta_barrier_fire_event (MetaBarrier *barrier,
default:
g_assert_not_reached ();
}
meta_barrier_event_unref (event);
}
gboolean

View File

@ -103,19 +103,20 @@ struct _MetaDisplay
#include <meta/atomnames.h>
#undef item
/* This is the actual window from focus events,
* not the one we last set
/* The window and serial of the most recent FocusIn event. */
Window server_focus_window;
gulong server_focus_serial;
/* Our best guess as to the "currently" focused window (that is, the
* window that we expect will be focused at the point when the X
* server processes our next request), and the serial of the request
* or event that caused this.
*/
MetaWindow *focus_window;
/* window we are expecting a FocusIn event for or the current focus
* window if we are not expecting any FocusIn/FocusOut events; not
* perfect because applications can call XSetInputFocus directly.
* (It could also be messed up if a timestamp later than current
* time is sent to meta_display_set_input_focus_window, though that
* would be a programming error). See bug 154598 for more info.
*/
MetaWindow *expected_focus_window;
/* For windows we've focused that don't necessarily have an X window,
* like the no_focus_window or the stage X window. */
Window focus_xwindow;
gulong focus_serial;
/* last timestamp passed to XSetInputFocus */
guint32 last_focus_time;
@ -239,6 +240,8 @@ struct _MetaDisplay
unsigned int meta_mask;
MetaKeyCombo overlay_key_combo;
gboolean overlay_key_only_pressed;
MetaKeyCombo *iso_next_group_combos;
int n_iso_next_group_combos;
/* Monitor cache */
unsigned int monitor_cache_invalidated : 1;
@ -458,6 +461,7 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
void meta_display_accelerator_activate (MetaDisplay *display,
guint action,
guint deviceid);
gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display);
/* In above-tab-keycode.c */
guint meta_display_get_above_tab_keycode (MetaDisplay *display);
@ -467,4 +471,9 @@ gboolean meta_display_process_barrier_event (MetaDisplay *display,
XIBarrierEvent *event);
#endif /* HAVE_XI23 */
void meta_display_set_input_focus_xwindow (MetaDisplay *display,
MetaScreen *screen,
Window window,
guint32 timestamp);
#endif

View File

@ -139,6 +139,7 @@ enum
{
OVERLAY_KEY,
ACCELERATOR_ACTIVATED,
MODIFIERS_ACCELERATOR_ACTIVATED,
FOCUS_WINDOW,
WINDOW_CREATED,
WINDOW_DEMANDS_ATTENTION,
@ -255,6 +256,25 @@ meta_display_class_init (MetaDisplayClass *klass)
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
/**
* MetaDisplay::modifiers-accelerator-activated:
* @display: the #MetaDisplay instance
*
* The ::modifiers-accelerator-activated signal will be emitted when
* a special modifiers-only keybinding is activated.
*
* Returns: %TRUE means that the keyboard device should remain
* frozen and %FALSE for the default behavior of unfreezing the
* keyboard.
*/
display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] =
g_signal_new ("modifiers-accelerator-activated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_first_wins, NULL, NULL,
G_TYPE_BOOLEAN, 0);
display_signals[WINDOW_CREATED] =
g_signal_new ("window-created",
G_TYPE_FROM_CLASS (klass),
@ -519,7 +539,9 @@ meta_display_open (void)
the_display->autoraise_timeout_id = 0;
the_display->autoraise_window = NULL;
the_display->focus_window = NULL;
the_display->expected_focus_window = NULL;
the_display->focus_serial = 0;
the_display->server_focus_window = None;
the_display->server_focus_serial = 0;
the_display->grab_old_window_stacking = NULL;
the_display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */
@ -1632,12 +1654,12 @@ meta_display_mouse_mode_focus (MetaDisplay *display,
* alternative mechanism works great.
*/
if (meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE &&
display->expected_focus_window != NULL)
display->focus_window != NULL)
{
meta_topic (META_DEBUG_FOCUS,
"Unsetting focus from %s due to mouse entering "
"the DESKTOP window\n",
display->expected_focus_window->desc);
display->focus_window->desc);
meta_display_focus_the_no_focus_window (display,
window->screen,
timestamp);
@ -1851,6 +1873,251 @@ get_input_event (MetaDisplay *display,
return NULL;
}
static void
update_focus_window (MetaDisplay *display,
MetaWindow *window,
Window xwindow,
gulong serial)
{
display->focus_serial = serial;
if (display->focus_xwindow == xwindow)
return;
if (display->focus_window)
{
MetaWindow *previous;
meta_topic (META_DEBUG_FOCUS,
"%s is now the previous focus window due to being focused out or unmapped\n",
display->focus_window->desc);
/* Make sure that signals handlers invoked by
* meta_window_set_focused_internal() don't see
* display->focus_window->has_focus == FALSE
*/
previous = display->focus_window;
display->focus_window = NULL;
display->focus_xwindow = None;
meta_window_set_focused_internal (previous, FALSE);
}
display->focus_window = window;
display->focus_xwindow = xwindow;
if (display->focus_window)
{
meta_topic (META_DEBUG_FOCUS, "* Focus --> %s with serial %lu\n",
display->focus_window->desc, serial);
meta_window_set_focused_internal (display->focus_window, TRUE);
}
else
meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL with serial %lu\n", serial);
g_object_notify (G_OBJECT (display), "focus-window");
meta_display_update_active_window_hint (display);
}
static gboolean
timestamp_too_old (MetaDisplay *display,
guint32 *timestamp)
{
/* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
* us to sanity check the timestamp here and ensure it doesn't correspond to
* a future time (though we would want to rename to
* timestamp_too_old_or_in_future).
*/
if (*timestamp == CurrentTime)
{
*timestamp = meta_display_get_current_time_roundtrip (display);
return FALSE;
}
else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time))
{
if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time))
return TRUE;
else
{
*timestamp = display->last_focus_time;
return FALSE;
}
}
return FALSE;
}
static void
request_xserver_input_focus_change (MetaDisplay *display,
MetaScreen *screen,
Window xwindow,
guint32 timestamp)
{
MetaWindow *meta_window;
gulong serial;
if (timestamp_too_old (display, &timestamp))
return;
meta_window = meta_display_lookup_x_window (display, xwindow);
meta_error_trap_push (display);
/* In order for mutter to know that the focus request succeeded, we track
* the serial of the "focus request" we made, but if we take the serial
* of the XSetInputFocus request, then there's no way to determine the
* difference between focus events as a result of the SetInputFocus and
* focus events that other clients send around the same time. Ensure that
* we know which is which by making two requests that the server will
* process at the same time.
*/
meta_display_grab (display);
XSetInputFocus (display->xdisplay,
xwindow,
RevertToPointerRoot,
timestamp);
serial = XNextRequest (display->xdisplay);
{
unsigned long data[1] = { 0 };
XChangeProperty (display->xdisplay, display->timestamp_pinging_window,
display->atom__MUTTER_FOCUS_SET,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 1);
}
meta_display_ungrab (display);
update_focus_window (display,
meta_window,
xwindow,
serial);
meta_error_trap_pop (display);
display->last_focus_time = timestamp;
display->active_screen = screen;
if (meta_window == NULL || meta_window != display->autoraise_window)
meta_display_remove_autoraise_callback (display);
}
static void
handle_window_focus_event (MetaDisplay *display,
MetaWindow *window,
XIEnterEvent *event,
unsigned long serial)
{
MetaWindow *focus_window;
#ifdef WITH_VERBOSE_MODE
const char *window_type;
/* Note the event can be on either the window or the frame,
* we focus the frame for shaded windows
*/
if (window)
{
if (event->event == window->xwindow)
window_type = "client window";
else if (window->frame && event->event == window->frame->xwindow)
window_type = "frame window";
else
window_type = "unknown client window";
}
else if (meta_display_xwindow_is_a_no_focus_window (display, event->event))
window_type = "no_focus_window";
else if (meta_display_screen_for_root (display, event->event))
window_type = "root window";
else
window_type = "unknown window";
meta_topic (META_DEBUG_FOCUS,
"Focus %s event received on %s 0x%lx (%s) "
"mode %s detail %s serial %lu\n",
event->evtype == XI_FocusIn ? "in" :
event->evtype == XI_FocusOut ? "out" :
"???",
window ? window->desc : "",
event->event, window_type,
meta_event_mode_to_string (event->mode),
meta_event_detail_to_string (event->mode),
event->serial);
#endif
/* FIXME our pointer tracking is broken; see how
* gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
* for how to handle it the correct way. In brief you need to track
* pointer focus and regular focus, and handle EnterNotify in
* PointerRoot mode with no window manager. However as noted above,
* accurate focus tracking will break things because we want to keep
* windows "focused" when using keybindings on them, and also we
* sometimes "focus" a window by focusing its frame or
* no_focus_window; so this all needs rethinking massively.
*
* My suggestion is to change it so that we clearly separate
* actual keyboard focus tracking using the xterm algorithm,
* and mutter's "pretend" focus window, and go through all
* the code and decide which one should be used in each place;
* a hard bit is deciding on a policy for that.
*
* http://bugzilla.gnome.org/show_bug.cgi?id=90382
*/
/* We ignore grabs, though this is questionable. It may be better to
* increase the intelligence of the focus window tracking.
*
* The problem is that keybindings for windows are done with
* XGrabKey, which means focus_window disappears and the front of
* the MRU list gets confused from what the user expects once a
* keybinding is used.
*/
if (event->mode == XINotifyGrab ||
event->mode == XINotifyUngrab ||
/* From WindowMaker, ignore all funky pointer root events */
event->detail > XINotifyNonlinearVirtual)
{
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus event generated by a grab or other weirdness\n");
return;
}
if (event->evtype == XI_FocusIn)
{
display->server_focus_window = event->event;
display->server_focus_serial = serial;
focus_window = window;
}
else if (event->evtype == XI_FocusOut)
{
if (event->detail == XINotifyInferior)
{
/* This event means the client moved focus to a subwindow */
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus out with NotifyInferior\n");
return;
}
display->server_focus_window = None;
display->server_focus_serial = serial;
focus_window = NULL;
}
else
g_return_if_reached ();
if (display->server_focus_serial >= display->focus_serial)
{
update_focus_window (display,
focus_window,
focus_window ? focus_window->xwindow : None,
display->server_focus_serial);
}
}
/**
* event_callback:
* @event: The event that just happened
@ -1893,7 +2160,19 @@ event_callback (XEvent *event,
filter_out_event = FALSE;
display->current_time = event_get_time (display, event);
display->monitor_cache_invalidated = TRUE;
if (event->xany.serial > display->focus_serial &&
display->focus_window &&
display->focus_window->xwindow != display->server_focus_window)
{
meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n",
display->focus_window->desc);
update_focus_window (display,
meta_display_lookup_x_window (display, display->server_focus_window),
display->server_focus_window,
display->server_focus_serial);
}
modified = event_get_modified_window (display, event);
input_event = get_input_event (display, event);
@ -2350,40 +2629,19 @@ event_callback (XEvent *event,
break;
case XI_FocusIn:
case XI_FocusOut:
if (window)
{
meta_window_notify_focus (window, enter_event);
}
else if (meta_display_xwindow_is_a_no_focus_window (display,
enter_event->event))
{
meta_topic (META_DEBUG_FOCUS,
"Focus %s event received on no_focus_window 0x%lx "
"mode %s detail %s\n",
enter_event->evtype == XI_FocusIn ? "in" :
enter_event->evtype == XI_FocusOut ? "out" :
"???",
enter_event->event,
meta_event_mode_to_string (enter_event->mode),
meta_event_detail_to_string (enter_event->detail));
}
else
/* libXi does not properly copy the serial to the XIEnterEvent, so pull it
* from the parent XAnyEvent.
* See: https://bugs.freedesktop.org/show_bug.cgi?id=64687
*/
handle_window_focus_event (display, window, enter_event, event->xany.serial);
if (!window)
{
/* Check if the window is a root window. */
MetaScreen *screen =
meta_display_screen_for_root(display,
enter_event->event);
if (screen == NULL)
break;
meta_topic (META_DEBUG_FOCUS,
"Focus %s event received on root window 0x%lx "
"mode %s detail %s\n",
enter_event->evtype == XI_FocusIn ? "in" :
enter_event->evtype == XI_FocusOut ? "out" :
"???",
enter_event->event,
meta_event_mode_to_string (enter_event->mode),
meta_event_detail_to_string (enter_event->detail));
if (enter_event->evtype == XI_FocusIn &&
enter_event->mode == XINotifyDetailNone)
@ -2521,13 +2779,6 @@ event_callback (XEvent *event,
window->unmaps_pending);
}
}
/* Unfocus on UnmapNotify, do this after the possible
* window_free above so that window_free can see if window->has_focus
* and move focus to another window
*/
if (window)
meta_window_lost_focus (window);
}
break;
case MapNotify:
@ -5561,98 +5812,74 @@ sanity_check_timestamps (MetaDisplay *display,
}
}
static gboolean
timestamp_too_old (MetaDisplay *display,
MetaWindow *window,
guint32 *timestamp)
{
/* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
* us to sanity check the timestamp here and ensure it doesn't correspond to
* a future time (though we would want to rename to
* timestamp_too_old_or_in_future).
*/
if (*timestamp == CurrentTime)
{
meta_warning ("Got a request to focus %s with a timestamp of 0. This "
"shouldn't happen!\n",
window ? window->desc : "the no_focus_window");
*timestamp = meta_display_get_current_time_roundtrip (display);
return FALSE;
}
else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time))
{
if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time))
{
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus request for %s since %u "
"is less than %u and %u.\n",
window ? window->desc : "the no_focus_window",
*timestamp,
display->last_user_time,
display->last_focus_time);
return TRUE;
}
else
{
meta_topic (META_DEBUG_FOCUS,
"Received focus request for %s which is newer than most "
"recent user_time, but less recent than "
"last_focus_time (%u < %u < %u); adjusting "
"accordingly. (See bug 167358)\n",
window ? window->desc : "the no_focus_window",
display->last_user_time,
*timestamp,
display->last_focus_time);
*timestamp = display->last_focus_time;
return FALSE;
}
}
return FALSE;
}
void
meta_display_set_input_focus_window (MetaDisplay *display,
MetaWindow *window,
gboolean focus_frame,
guint32 timestamp)
{
if (timestamp_too_old (display, window, &timestamp))
return;
meta_error_trap_push (display);
XSetInputFocus (display->xdisplay,
focus_frame ? window->frame->xwindow : window->xwindow,
RevertToPointerRoot,
timestamp);
meta_error_trap_pop (display);
display->expected_focus_window = window;
display->last_focus_time = timestamp;
display->active_screen = window->screen;
if (window != display->autoraise_window)
meta_display_remove_autoraise_callback (window->display);
request_xserver_input_focus_change (display,
window->screen,
focus_frame ? window->frame->xwindow : window->xwindow,
timestamp);
}
void
meta_display_focus_the_no_focus_window (MetaDisplay *display,
meta_display_request_take_focus (MetaDisplay *display,
MetaWindow *window,
guint32 timestamp)
{
if (timestamp_too_old (display, &timestamp))
return;
meta_topic (META_DEBUG_FOCUS, "WM_TAKE_FOCUS(%s, %u)\n",
window->desc, timestamp);
if (window != display->focus_window)
{
/* The "Globally Active Input" window case, where the window
* doesn't want us to call XSetInputFocus on it, but does
* want us to send a WM_TAKE_FOCUS.
*
* We can't just set display->focus_window to @window, since we
* we don't know when (or even if) the window will actually take
* focus, so we could end up being wrong for arbitrarily long.
* But we also can't leave it set to the current window, or else
* bug #597352 would come back. So we focus the no_focus_window
* now (and set display->focus_window to that), send the
* WM_TAKE_FOCUS, and then just forget about @window
* until/unless we get a FocusIn.
*/
meta_display_focus_the_no_focus_window (display,
window->screen,
timestamp);
}
meta_window_send_icccm_message (window,
display->atom_WM_TAKE_FOCUS,
timestamp);
}
void
meta_display_set_input_focus_xwindow (MetaDisplay *display,
MetaScreen *screen,
Window window,
guint32 timestamp)
{
request_xserver_input_focus_change (display,
screen,
window,
timestamp);
}
void
meta_display_focus_the_no_focus_window (MetaDisplay *display,
MetaScreen *screen,
guint32 timestamp)
{
if (timestamp_too_old (display, NULL, &timestamp))
return;
XSetInputFocus (display->xdisplay,
screen->no_focus_window,
RevertToPointerRoot,
timestamp);
display->expected_focus_window = NULL;
display->last_focus_time = timestamp;
display->active_screen = screen;
meta_display_remove_autoraise_callback (display);
request_xserver_input_focus_change (display,
screen,
screen->no_focus_window,
timestamp);
}
void
@ -5681,6 +5908,16 @@ meta_display_accelerator_activate (MetaDisplay *display,
0, action, deviceid);
}
gboolean
meta_display_modifiers_accelerator_activate (MetaDisplay *display)
{
gboolean freeze;
g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze);
return freeze;
}
void
meta_display_get_compositor_version (MetaDisplay *display,
int *major,
@ -5762,11 +5999,9 @@ meta_display_has_shape (MetaDisplay *display)
* meta_display_get_focus_window:
* @display: a #MetaDisplay
*
* Get the window that, according to events received from X server,
* currently has the input focus. We may have already sent a request
* to the X server to move the focus window elsewhere. (The
* expected_focus_window records where we've last set the input
* focus.)
* Get our best guess as to the "currently" focused window (that is,
* the window that we expect will be focused at the point when the X
* server processes our next request).
*
* Return Value: (transfer none): The current focus window
*/

View File

@ -332,19 +332,6 @@ meta_frame_calc_borders (MetaFrame *frame,
borders);
}
void
meta_frame_get_corner_radiuses (MetaFrame *frame,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right)
{
meta_ui_get_corner_radiuses (frame->window->screen->ui,
frame->xwindow,
top_left, top_right,
bottom_left, bottom_right);
}
gboolean
meta_frame_sync_to_window (MetaFrame *frame,
int resize_gravity,
@ -400,6 +387,14 @@ meta_frame_get_frame_bounds (MetaFrame *frame)
frame->rect.height);
}
void
meta_frame_get_mask (MetaFrame *frame,
cairo_t *cr)
{
meta_ui_get_frame_mask (frame->window->screen->ui, frame->xwindow,
frame->rect.width, frame->rect.height, cr);
}
void
meta_frame_queue_draw (MetaFrame *frame)
{

View File

@ -63,12 +63,6 @@ Window meta_frame_get_xwindow (MetaFrame *frame);
void meta_frame_calc_borders (MetaFrame *frame,
MetaFrameBorders *borders);
void meta_frame_get_corner_radiuses (MetaFrame *frame,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right);
gboolean meta_frame_sync_to_window (MetaFrame *frame,
int gravity,
gboolean need_move,
@ -76,6 +70,9 @@ gboolean meta_frame_sync_to_window (MetaFrame *frame,
cairo_region_t *meta_frame_get_frame_bounds (MetaFrame *frame);
void meta_frame_get_mask (MetaFrame *frame,
cairo_t *cr);
void meta_frame_set_screen_cursor (MetaFrame *frame,
MetaCursor cursor);

View File

@ -302,6 +302,172 @@ reload_modmap (MetaDisplay *display)
display->meta_mask);
}
/* Original code from gdk_x11_keymap_get_entries_for_keyval() in
* gdkkeys-x11.c */
static int
get_keycodes_for_keysym (MetaDisplay *display,
int keysym,
int **keycodes)
{
GArray *retval;
int n_keycodes;
int keycode;
retval = g_array_new (FALSE, FALSE, sizeof (int));
keycode = display->min_keycode;
while (keycode <= display->max_keycode)
{
const KeySym *syms = display->keymap + (keycode - display->min_keycode) * display->keysyms_per_keycode;
int i = 0;
while (i < display->keysyms_per_keycode)
{
if (syms[i] == (unsigned int)keysym)
g_array_append_val (retval, keycode);
++i;
}
++keycode;
}
n_keycodes = retval->len;
*keycodes = (int*) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE);
return n_keycodes;
}
static void
reload_iso_next_group_combos (MetaDisplay *display)
{
const char *iso_next_group_option;
MetaKeyCombo *combos;
int *keycodes;
int n_keycodes;
int n_combos;
int i;
g_clear_pointer (&display->iso_next_group_combos, g_free);
display->n_iso_next_group_combos = 0;
iso_next_group_option = meta_prefs_get_iso_next_group_option ();
if (iso_next_group_option == NULL)
return;
n_keycodes = get_keycodes_for_keysym (display, XK_ISO_Next_Group, &keycodes);
if (g_str_equal (iso_next_group_option, "toggle") ||
g_str_equal (iso_next_group_option, "lalt_toggle") ||
g_str_equal (iso_next_group_option, "lwin_toggle") ||
g_str_equal (iso_next_group_option, "rwin_toggle") ||
g_str_equal (iso_next_group_option, "lshift_toggle") ||
g_str_equal (iso_next_group_option, "rshift_toggle") ||
g_str_equal (iso_next_group_option, "lctrl_toggle") ||
g_str_equal (iso_next_group_option, "rctrl_toggle") ||
g_str_equal (iso_next_group_option, "sclk_toggle") ||
g_str_equal (iso_next_group_option, "menu_toggle") ||
g_str_equal (iso_next_group_option, "caps_toggle"))
{
n_combos = n_keycodes;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = 0;
}
}
else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") ||
g_str_equal (iso_next_group_option, "shifts_toggle"))
{
n_combos = n_keycodes;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = ShiftMask;
}
}
else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") ||
g_str_equal (iso_next_group_option, "alt_space_toggle"))
{
n_combos = n_keycodes;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = Mod1Mask;
}
}
else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") ||
g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") ||
g_str_equal (iso_next_group_option, "rctrl_rshift_toggle"))
{
n_combos = n_keycodes * 2;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = ShiftMask;
combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
combos[i + n_keycodes].keycode = keycodes[i];
combos[i + n_keycodes].modifiers = ControlMask;
}
}
else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle"))
{
n_combos = n_keycodes * 2;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = Mod1Mask;
combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
combos[i + n_keycodes].keycode = keycodes[i];
combos[i + n_keycodes].modifiers = ControlMask;
}
}
else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") ||
g_str_equal (iso_next_group_option, "lalt_lshift_toggle"))
{
n_combos = n_keycodes * 2;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = Mod1Mask;
combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
combos[i + n_keycodes].keycode = keycodes[i];
combos[i + n_keycodes].modifiers = ShiftMask;
}
}
else
{
n_combos = 0;
combos = NULL;
}
g_free (keycodes);
display->n_iso_next_group_combos = n_combos;
display->iso_next_group_combos = combos;
}
static guint
keysym_to_keycode (MetaDisplay *display,
guint keysym)
@ -328,6 +494,8 @@ reload_keycodes (MetaDisplay *display)
display->overlay_key_combo.keycode = 0;
}
reload_iso_next_group_combos (display);
if (display->key_bindings)
{
int i;
@ -1026,6 +1194,22 @@ meta_screen_change_keygrabs (MetaScreen *screen,
display->overlay_key_combo.keycode,
display->overlay_key_combo.modifiers);
if (display->iso_next_group_combos)
{
int i = 0;
while (i < display->n_iso_next_group_combos)
{
if (display->iso_next_group_combos[i].keycode != 0)
{
meta_change_keygrab (display, screen->xroot, grab,
display->iso_next_group_combos[i].keysym,
display->iso_next_group_combos[i].keycode,
display->iso_next_group_combos[i].modifiers);
}
++i;
}
}
change_binding_keygrabs (screen->display->key_bindings,
screen->display->n_key_bindings,
screen->display, screen->xroot,
@ -1264,7 +1448,8 @@ grab_status_to_string (int status)
static gboolean
grab_keyboard (MetaDisplay *display,
Window xwindow,
guint32 timestamp)
guint32 timestamp,
int grab_mode)
{
int result;
int grab_status;
@ -1280,12 +1465,21 @@ grab_keyboard (MetaDisplay *display,
*/
meta_error_trap_push_with_return (display);
/* Strictly, we only need to set grab_mode on the keyboard device
* while the pointer should always be XIGrabModeAsync. Unfortunately
* there is a bug in the X server, only fixed (link below) in 1.15,
* which swaps these arguments for keyboard devices. As such, we set
* both the device and the paired device mode which works around
* that bug and also works on fixed X servers.
*
* http://cgit.freedesktop.org/xorg/xserver/commit/?id=9003399708936481083424b4ff8f18a16b88b7b3
*/
grab_status = XIGrabDevice (display->xdisplay,
META_VIRTUAL_CORE_KEYBOARD_ID,
xwindow,
timestamp,
None,
XIGrabModeAsync, XIGrabModeAsync,
grab_mode, grab_mode,
True, /* owner_events */
&mask);
@ -1339,7 +1533,7 @@ meta_screen_grab_all_keys (MetaScreen *screen, guint32 timestamp)
meta_topic (META_DEBUG_KEYBINDINGS,
"Grabbing all keys on RootWindow\n");
retval = grab_keyboard (screen->display, screen->xroot, timestamp);
retval = grab_keyboard (screen->display, screen->xroot, timestamp, XIGrabModeAsync);
if (retval)
{
screen->all_keys_grabbed = TRUE;
@ -1392,7 +1586,7 @@ meta_window_grab_all_keys (MetaWindow *window,
meta_topic (META_DEBUG_KEYBINDINGS,
"Grabbing all keys on window %s\n", window->desc);
retval = grab_keyboard (window->display, grabwindow, timestamp);
retval = grab_keyboard (window->display, grabwindow, timestamp, XIGrabModeAsync);
if (retval)
{
window->keys_grabbed = FALSE;
@ -1419,6 +1613,32 @@ meta_window_ungrab_all_keys (MetaWindow *window, guint32 timestamp)
}
}
void
meta_display_freeze_keyboard (MetaDisplay *display, Window window, guint32 timestamp)
{
grab_keyboard (display, window, timestamp, XIGrabModeSync);
}
void
meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp)
{
ungrab_keyboard (display, timestamp);
}
void
meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp)
{
meta_error_trap_push (display);
XIAllowEvents (display->xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID,
XIAsyncDevice, timestamp);
/* We shouldn't need to unfreeze the pointer device here, however we
* have to, due to the workaround we do in grab_keyboard().
*/
XIAllowEvents (display->xdisplay, META_VIRTUAL_CORE_POINTER_ID,
XIAsyncDevice, timestamp);
meta_error_trap_pop (display);
}
static gboolean
is_modifier (MetaDisplay *display,
unsigned int keycode)
@ -1765,6 +1985,41 @@ process_overlay_key (MetaDisplay *display,
return FALSE;
}
static gboolean
process_iso_next_group (MetaDisplay *display,
MetaScreen *screen,
XIDeviceEvent *event,
KeySym keysym)
{
gboolean activate;
unsigned int mods;
int i;
if (event->evtype != XI_KeyPress)
return FALSE;
activate = FALSE;
mods = (event->mods.effective & 0xff & ~(display->ignored_modifier_mask));
for (i = 0; i < display->n_iso_next_group_combos; ++i)
{
if (event->detail == (int)display->iso_next_group_combos[i].keycode &&
mods == display->iso_next_group_combos[i].modifiers)
{
/* If the signal handler returns TRUE the keyboard will
remain frozen. It's the signal handler's responsibility
to unfreeze it. */
if (!meta_display_modifiers_accelerator_activate (display))
XIAllowEvents (display->xdisplay, event->deviceid,
XIAsyncDevice, event->time);
activate = TRUE;
break;
}
}
return activate;
}
/* Handle a key event. May be called recursively: some key events cause
* grabs to be ended and then need to be processed again in their own
* right. This cannot cause infinite recursion because we never call
@ -1839,6 +2094,10 @@ meta_display_process_key_event (MetaDisplay *display,
handled = process_overlay_key (display, screen, event, keysym);
if (handled)
return TRUE;
handled = process_iso_next_group (display, screen, event, keysym);
if (handled)
return TRUE;
}
XIAllowEvents (display->xdisplay, event->deviceid,
@ -4532,6 +4791,12 @@ meta_display_init_keys (MetaDisplay *display)
g_hash_table_insert (key_handlers, g_strdup ("overlay-key"), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("iso-next-group");
handler->flags = META_KEY_BINDING_BUILTIN;
g_hash_table_insert (key_handlers, g_strdup ("iso-next-group"), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("external-grab");
handler->func = handle_external_grab;

View File

@ -55,6 +55,7 @@
#define KEY_GNOME_ANIMATIONS "enable-animations"
#define KEY_GNOME_CURSOR_THEME "cursor-theme"
#define KEY_GNOME_CURSOR_SIZE "cursor-size"
#define KEY_XKB_OPTIONS "xkb-options"
#define KEY_OVERLAY_KEY "overlay-key"
#define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary"
@ -65,6 +66,7 @@
#define SCHEMA_GENERAL "org.gnome.desktop.wm.preferences"
#define SCHEMA_MUTTER "org.gnome.mutter"
#define SCHEMA_INTERFACE "org.gnome.desktop.interface"
#define SCHEMA_INPUT_SOURCES "org.gnome.desktop.input-sources"
#define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s))
@ -115,6 +117,7 @@ static gboolean workspaces_only_on_primary = FALSE;
static gboolean no_tab_popup = FALSE;
static char *iso_next_group_option = NULL;
static void handle_preference_update_enum (GSettings *settings,
gchar *key);
@ -122,7 +125,6 @@ static gboolean update_binding (MetaKeyPref *binding,
gchar **strokes);
static gboolean update_key_binding (const char *key,
gchar **strokes);
static gboolean update_workspace_names (void);
static void settings_changed (GSettings *settings,
gchar *key,
@ -140,11 +142,11 @@ static gboolean theme_name_handler (GVariant*, gpointer*, gpointer);
static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer);
static gboolean button_layout_handler (GVariant*, gpointer*, gpointer);
static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer);
static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer);
static void do_override (char *key, char *schema);
static void init_bindings (void);
static void init_workspace_names (void);
typedef struct
@ -198,6 +200,13 @@ typedef struct
gchar **target;
} MetaStringPreference;
typedef struct
{
MetaBasePreference base;
GSettingsGetMapping handler;
gchar ***target;
} MetaStringArrayPreference;
typedef struct
{
MetaBasePreference base;
@ -436,6 +445,27 @@ static MetaStringPreference preferences_string[] =
{ { NULL, 0, 0 }, NULL },
};
static MetaStringArrayPreference preferences_string_array[] =
{
{
{ KEY_WORKSPACE_NAMES,
SCHEMA_GENERAL,
META_PREF_KEYBINDINGS,
},
NULL,
&workspace_names,
},
{
{ KEY_XKB_OPTIONS,
SCHEMA_INPUT_SOURCES,
META_PREF_KEYBINDINGS,
},
iso_next_group_handler,
NULL,
},
{ { NULL, 0, 0 }, NULL },
};
static MetaIntPreference preferences_int[] =
{
{
@ -555,6 +585,42 @@ handle_preference_init_string (void)
}
}
static void
handle_preference_init_string_array (void)
{
MetaStringArrayPreference *cursor = preferences_string_array;
while (cursor->base.key != NULL)
{
char **value;
/* Complex keys have a mapping function to check validity */
if (cursor->handler)
{
if (cursor->target)
meta_bug ("%s has both a target and a handler\n", cursor->base.key);
g_settings_get_mapped (SETTINGS (cursor->base.schema),
cursor->base.key, cursor->handler, NULL);
}
else
{
if (!cursor->target)
meta_bug ("%s must have handler or target\n", cursor->base.key);
if (*(cursor->target))
g_strfreev (*(cursor->target));
value = g_settings_get_strv (SETTINGS (cursor->base.schema),
cursor->base.key);
*(cursor->target) = value;
}
++cursor;
}
}
static void
handle_preference_init_int (void)
{
@ -673,6 +739,56 @@ handle_preference_update_string (GSettings *settings,
queue_changed (cursor->base.pref);
}
static void
handle_preference_update_string_array (GSettings *settings,
gchar *key)
{
MetaStringArrayPreference *cursor = preferences_string_array;
gboolean inform_listeners = FALSE;
while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
++cursor;
if (cursor->base.key==NULL)
/* Didn't recognise that key. */
return;
/* Complex keys have a mapping function to check validity */
if (cursor->handler)
{
if (cursor->target)
meta_bug ("%s has both a target and a handler\n", cursor->base.key);
g_settings_get_mapped (SETTINGS (cursor->base.schema),
cursor->base.key, cursor->handler, NULL);
}
else
{
char **values, **previous;
int n_values, n_previous, i;
if (!cursor->target)
meta_bug ("%s must have handler or target\n", cursor->base.key);
values = g_settings_get_strv (SETTINGS (cursor->base.schema),
cursor->base.key);
n_values = g_strv_length (values);
previous = *(cursor->target);
n_previous = previous ? g_strv_length (previous) : 0;
inform_listeners = n_previous != n_values;
for (i = 0; i < n_values && !inform_listeners; i++)
inform_listeners = g_strcmp0 (values[i], previous[i]) != 0;
if (*(cursor->target))
g_strfreev (*(cursor->target));
*(cursor->target) = values;
}
if (inform_listeners)
queue_changed (cursor->base.pref);
}
static void
handle_preference_update_int (GSettings *settings,
gchar *key)
@ -857,6 +973,11 @@ meta_prefs_init (void)
G_CALLBACK (settings_changed), NULL);
g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings);
settings = g_settings_new (SCHEMA_INPUT_SOURCES);
g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS,
G_CALLBACK (settings_changed), NULL);
g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings);
for (tmp = overridden_keys; tmp; tmp = tmp->next)
{
@ -869,10 +990,10 @@ meta_prefs_init (void)
handle_preference_init_enum ();
handle_preference_init_bool ();
handle_preference_init_string ();
handle_preference_init_string_array ();
handle_preference_init_int ();
init_bindings ();
init_workspace_names ();
}
static gboolean
@ -1020,14 +1141,6 @@ settings_changed (GSettings *settings,
MetaEnumPreference *cursor;
gboolean found_enum;
/* String array, handled separately */
if (strcmp (key, KEY_WORKSPACE_NAMES) == 0)
{
if (update_workspace_names ())
queue_changed (META_PREF_WORKSPACE_NAMES);
return;
}
value = g_settings_get_value (settings, key);
type = g_variant_get_type (value);
@ -1035,6 +1148,8 @@ settings_changed (GSettings *settings,
handle_preference_update_bool (settings, key);
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
handle_preference_update_int (settings, key);
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY))
handle_preference_update_string_array (settings, key);
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
{
cursor = preferences_enum;
@ -1554,6 +1669,39 @@ overlay_key_handler (GVariant *value,
return TRUE;
}
static gboolean
iso_next_group_handler (GVariant *value,
gpointer *result,
gpointer data)
{
const char **xkb_options, **p;
const char *option = NULL;
gboolean changed = FALSE;
*result = NULL; /* ignored */
xkb_options = g_variant_get_strv (value, NULL);
for (p = xkb_options; p && *p; ++p)
if (g_str_has_prefix (*p, "grp:"))
{
option = (*p + 4);
break;
}
changed = (g_strcmp0 (option, iso_next_group_option) != 0);
if (changed)
{
g_free (iso_next_group_option);
iso_next_group_option = g_strdup (option);
queue_changed (META_PREF_KEYBINDINGS);
}
g_free (xkb_options);
return TRUE;
}
const PangoFontDescription*
meta_prefs_get_titlebar_font (void)
{
@ -1708,12 +1856,13 @@ meta_prefs_set_num_workspaces (int n_workspaces)
{
MetaBasePreference *pref;
find_pref (preferences_int, sizeof(MetaIntPreference),
KEY_NUM_WORKSPACES, &pref);
g_settings_set_int (SETTINGS (pref->schema),
KEY_NUM_WORKSPACES,
n_workspaces);
if (find_pref (preferences_int, sizeof(MetaIntPreference),
KEY_NUM_WORKSPACES, &pref))
{
g_settings_set_int (SETTINGS (pref->schema),
KEY_NUM_WORKSPACES,
n_workspaces);
}
}
static GHashTable *key_bindings;
@ -1747,20 +1896,15 @@ init_bindings (void)
g_hash_table_insert (key_bindings, g_strdup ("overlay-key"), pref);
}
static void
init_workspace_names (void)
{
update_workspace_names ();
}
static gboolean
update_binding (MetaKeyPref *binding,
gchar **strokes)
{
GSList *old_bindings, *a, *b;
gboolean changed;
unsigned int keysym;
unsigned int keycode;
MetaVirtualModifier mods;
gboolean changed = FALSE;
MetaKeyCombo *combo;
int i;
@ -1768,13 +1912,9 @@ update_binding (MetaKeyPref *binding,
"Binding \"%s\" has new GSettings value\n",
binding->name);
/* Okay, so, we're about to provide a new list of key combos for this
* action. Delete any pre-existing list.
*/
g_slist_foreach (binding->bindings, (GFunc) g_free, NULL);
g_slist_free (binding->bindings);
old_bindings = binding->bindings;
binding->bindings = NULL;
for (i = 0; strokes && strokes[i]; i++)
{
keysym = 0;
@ -1809,8 +1949,6 @@ update_binding (MetaKeyPref *binding,
* Changing the key in response to a modification could lead to cyclic calls. */
continue;
}
changed = TRUE;
combo = g_malloc0 (sizeof (MetaKeyCombo));
combo->keysym = keysym;
@ -1825,6 +1963,34 @@ update_binding (MetaKeyPref *binding,
binding->bindings = g_slist_reverse (binding->bindings);
a = old_bindings;
b = binding->bindings;
while (TRUE)
{
if ((!a && b) || (a && !b))
{
changed = TRUE;
break;
}
else if (!a && !b)
{
changed = FALSE;
break;
}
else if (memcmp (a->data, b->data, sizeof (MetaKeyCombo)) != 0)
{
changed = TRUE;
break;
}
else
{
a = a->next;
b = b->next;
}
}
g_slist_free_full (old_bindings, g_free);
return changed;
}
@ -1840,41 +2006,6 @@ update_key_binding (const char *key,
return FALSE;
}
static gboolean
update_workspace_names (void)
{
int i;
char **names;
int n_workspace_names, n_names;
gboolean changed = FALSE;
names = g_settings_get_strv (SETTINGS (SCHEMA_GENERAL), KEY_WORKSPACE_NAMES);
n_names = g_strv_length (names);
n_workspace_names = workspace_names ? g_strv_length (workspace_names) : 0;
for (i = 0; i < n_names; i++)
if (n_workspace_names < i + 1 || !workspace_names[i] ||
g_strcmp0 (names[i], workspace_names[i]) != 0)
{
changed = TRUE;
break;
}
if (n_workspace_names != n_names)
changed = TRUE;
if (changed)
{
if (workspace_names)
g_strfreev (workspace_names);
workspace_names = names;
}
else
g_strfreev (names);
return changed;
}
const char*
meta_prefs_get_workspace_name (int i)
{
@ -2074,6 +2205,12 @@ meta_prefs_get_overlay_binding (MetaKeyCombo *combo)
*combo = overlay_key_combo;
}
const char *
meta_prefs_get_iso_next_group_option (void)
{
return iso_next_group_option;
}
GDesktopTitlebarAction
meta_prefs_get_action_double_click_titlebar (void)
{
@ -2216,9 +2353,11 @@ meta_prefs_set_no_tab_popup (gboolean whether)
{
MetaBasePreference *pref;
find_pref (preferences_bool, sizeof(MetaBoolPreference),
KEY_NO_TAB_POPUP, &pref);
g_settings_set_boolean (SETTINGS (pref->schema), KEY_NO_TAB_POPUP, whether);
if (find_pref (preferences_bool, sizeof(MetaBoolPreference),
KEY_NO_TAB_POPUP, &pref))
{
g_settings_set_boolean (SETTINGS (pref->schema), KEY_NO_TAB_POPUP, whether);
}
}
int

View File

@ -278,7 +278,7 @@ static gboolean
is_focused_foreach (MetaWindow *window,
void *data)
{
if (window == window->display->expected_focus_window)
if (window->has_focus)
{
*((gboolean*) data) = TRUE;
return FALSE;
@ -335,11 +335,11 @@ get_standalone_layer (MetaWindow *window)
layer = META_LAYER_BOTTOM;
else if (window->fullscreen &&
(focused_transient ||
window == window->display->expected_focus_window ||
window->display->expected_focus_window == NULL ||
(window->display->expected_focus_window != NULL &&
window == window->display->focus_window ||
window->display->focus_window == NULL ||
(window->display->focus_window != NULL &&
windows_on_different_monitor (window,
window->display->expected_focus_window))))
window->display->focus_window))))
layer = META_LAYER_FULLSCREEN;
else if (window->wm_state_above && !META_WINDOW_MAXIMIZED (window))
layer = META_LAYER_TOP;

View File

@ -277,7 +277,7 @@ struct _MetaWindow
/* EWHH demands attention flag */
guint wm_state_demands_attention : 1;
/* this flag tracks receipt of focus_in focus_out */
/* TRUE iff window == window->display->focus_window */
guint has_focus : 1;
/* Have we placed this window? */
@ -590,9 +590,8 @@ gboolean meta_window_property_notify (MetaWindow *window,
XEvent *event);
gboolean meta_window_client_message (MetaWindow *window,
XEvent *event);
gboolean meta_window_notify_focus (MetaWindow *window,
XIEnterEvent *event);
void meta_window_lost_focus (MetaWindow *window);
void meta_window_set_focused_internal (MetaWindow *window,
gboolean focused);
void meta_window_set_current_workspace_hint (MetaWindow *window);

View File

@ -253,6 +253,8 @@ meta_window_finalize (GObject *object)
g_free (window->gtk_window_object_path);
g_free (window->gtk_app_menu_object_path);
g_free (window->gtk_menubar_object_path);
G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
}
static void
@ -1752,16 +1754,6 @@ meta_window_unmanage (MetaWindow *window,
window,
timestamp);
}
else if (window->display->expected_focus_window == window)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing default window since expected focus window freed %s\n",
window->desc);
window->display->expected_focus_window = NULL;
meta_workspace_focus_default_window (window->screen->active_workspace,
window,
timestamp);
}
else
{
meta_topic (META_DEBUG_FOCUS,
@ -1769,6 +1761,8 @@ meta_window_unmanage (MetaWindow *window,
window->desc);
}
g_assert (window->display->focus_window != window);
if (window->struts)
{
meta_free_gslist_and_elements (window->struts);
@ -1791,12 +1785,6 @@ meta_window_unmanage (MetaWindow *window,
g_assert (window->display->grab_window != window);
if (window->display->focus_window == window)
{
window->display->focus_window = NULL;
g_object_notify (G_OBJECT (window->display), "focus-window");
}
if (window->maximized_horizontally || window->maximized_vertically)
unmaximize_window_before_freeing (window);
@ -3330,14 +3318,7 @@ meta_window_hide (MetaWindow *window)
invalidate_work_areas (window);
}
/* The check on expected_focus_window is a temporary workaround for
* https://bugzilla.gnome.org/show_bug.cgi?id=597352
* We may have already switched away from this window but not yet
* gotten FocusIn/FocusOut events. A more complete comprehensive
* fix for these type of issues is described in the bug.
*/
if (window->has_focus &&
window == window->display->expected_focus_window)
if (window->has_focus)
{
MetaWindow *not_this_one = NULL;
MetaWorkspace *my_workspace = meta_window_get_workspace (window);
@ -5973,10 +5954,10 @@ meta_window_focus (MetaWindow *window,
meta_topic (META_DEBUG_FOCUS,
"Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
window->desc);
meta_window_send_icccm_message (window,
window->display->atom_WM_TAKE_FOCUS,
timestamp);
window->display->expected_focus_window = window;
meta_display_request_take_focus (window->display,
window,
timestamp);
}
}
@ -6516,7 +6497,7 @@ meta_window_configure_request (MetaWindow *window,
if (event->xconfigurerequest.value_mask & CWStackMode)
{
MetaWindow *active_window;
active_window = window->display->expected_focus_window;
active_window = window->display->focus_window;
if (meta_prefs_get_disable_workarounds ())
{
meta_topic (META_DEBUG_STACK,
@ -6624,6 +6605,41 @@ meta_window_change_workspace_by_index (MetaWindow *window,
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10
#define _NET_WM_MOVERESIZE_CANCEL 11
static int
query_pressed_buttons (MetaWindow *window)
{
double x, y, query_root_x, query_root_y;
Window root, child;
XIButtonState buttons;
XIModifierState mods;
XIGroupState group;
int button = 0;
meta_error_trap_push (window->display);
XIQueryPointer (window->display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
window->xwindow,
&root, &child,
&query_root_x, &query_root_y,
&x, &y,
&buttons, &mods, &group);
if (meta_error_trap_pop_with_return (window->display) != Success)
goto out;
if (XIMaskIsSet (buttons.mask, Button1))
button |= 1 << 1;
if (XIMaskIsSet (buttons.mask, Button2))
button |= 1 << 2;
if (XIMaskIsSet (buttons.mask, Button3))
button |= 1 << 3;
free (buttons.mask);
out:
return button;
}
gboolean
meta_window_client_message (MetaWindow *window,
XEvent *event)
@ -6982,56 +6998,58 @@ meta_window_client_message (MetaWindow *window,
(op != META_GRAB_OP_MOVING &&
op != META_GRAB_OP_KEYBOARD_MOVING))))
{
/*
* the button SHOULD already be included in the message
*/
int button_mask;
meta_topic (META_DEBUG_WINDOW_OPS,
"Beginning move/resize with button = %d\n", button);
meta_display_begin_grab_op (window->display,
window->screen,
window,
op,
FALSE,
frame_action,
button, 0,
timestamp,
x_root,
y_root);
button_mask = query_pressed_buttons (window);
if (button == 0)
{
double x, y, query_root_x, query_root_y;
Window root, child;
XIButtonState buttons;
XIModifierState mods;
XIGroupState group;
/* The race conditions in this _NET_WM_MOVERESIZE thing
* are mind-boggling
/*
* the button SHOULD already be included in the message
*/
meta_error_trap_push (window->display);
XIQueryPointer (window->display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
window->xwindow,
&root, &child,
&query_root_x, &query_root_y,
&x, &y,
&buttons, &mods, &group);
meta_error_trap_pop (window->display);
if (XIMaskIsSet (buttons.mask, Button1))
if ((button_mask & (1 << 1)) != 0)
button = 1;
else if (XIMaskIsSet (buttons.mask, Button2))
else if ((button_mask & (1 << 2)) != 0)
button = 2;
else if (XIMaskIsSet (buttons.mask, Button3))
else if ((button_mask & (1 << 3)) != 0)
button = 3;
if (button != 0)
window->display->grab_button = button;
else
button = 0;
free (buttons.mask);
meta_display_end_grab_op (window->display,
timestamp);
}
if (button != 0)
else
{
meta_topic (META_DEBUG_WINDOW_OPS,
"Beginning move/resize with button = %d\n", button);
meta_display_begin_grab_op (window->display,
window->screen,
window,
op,
FALSE,
frame_action,
button, 0,
timestamp,
x_root,
y_root);
/* There is a potential race here. If the user presses and
* releases their mouse button very fast, it's possible for
* both the ButtonPress and ButtonRelease to be sent to the
* client before it can get a chance to send _NET_WM_MOVERESIZE
* to us. When that happens, we'll become stuck in a grab
* state, as we haven't received a ButtonRelease to cancel the
* grab.
*
* We can solve this by querying after we take the explicit
* pointer grab -- if the button isn't pressed, we cancel the
* drag immediately.
*/
if ((button_mask & (1 << button)) == 0)
meta_display_end_grab_op (window->display, timestamp);
}
}
@ -7158,8 +7176,7 @@ meta_window_propagate_focus_appearance (MetaWindow *window,
parent->attached_focus_window = NULL;
}
if (child_focus_state_changed && !parent->has_focus &&
parent != window->display->expected_focus_window)
if (child_focus_state_changed && !parent->has_focus)
{
meta_window_appears_focused_changed (parent);
}
@ -7170,23 +7187,85 @@ meta_window_propagate_focus_appearance (MetaWindow *window,
}
void
meta_window_lost_focus (MetaWindow *window)
meta_window_set_focused_internal (MetaWindow *window,
gboolean focused)
{
if (window == window->display->focus_window)
if (focused)
{
meta_topic (META_DEBUG_FOCUS,
"%s is now the previous focus window due to being focused out or unmapped\n",
window->desc);
window->has_focus = TRUE;
if (window->override_redirect)
return;
meta_topic (META_DEBUG_FOCUS,
"* Focus --> NULL (was %s)\n", window->desc);
/* Move to the front of the focusing workspace's MRU list.
* We should only be "removing" it from the MRU list if it's
* not already there. Note that it's possible that we might
* be processing this FocusIn after we've changed to a
* different workspace; we should therefore update the MRU
* list only if the window is actually on the active
* workspace.
*/
if (window->screen->active_workspace &&
meta_window_located_on_workspace (window,
window->screen->active_workspace))
{
GList* link;
link = g_list_find (window->screen->active_workspace->mru_list,
window);
g_assert (link);
window->screen->active_workspace->mru_list =
g_list_remove_link (window->screen->active_workspace->mru_list,
link);
g_list_free (link);
window->screen->active_workspace->mru_list =
g_list_prepend (window->screen->active_workspace->mru_list,
window);
}
if (window->frame)
meta_frame_queue_draw (window->frame);
meta_error_trap_push (window->display);
XInstallColormap (window->display->xdisplay,
window->colormap);
meta_error_trap_pop (window->display);
/* move into FOCUSED_WINDOW layer */
meta_window_update_layer (window);
/* Ungrab click to focus button since the sync grab can interfere
* with some things you might do inside the focused window, by
* causing the client to get funky enter/leave events.
*
* The reason we usually have a passive grab on the window is
* so that we can intercept clicks and raise the window in
* response. For click-to-focus we don't need that since the
* focused window is already raised. When raise_on_click is
* FALSE we also don't need that since we don't do anything
* when the window is clicked.
*
* There is dicussion in bugs 102209, 115072, and 461577
*/
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK ||
!meta_prefs_get_raise_on_click())
meta_display_ungrab_focus_window_button (window->display, window);
g_signal_emit (window, window_signals[FOCUS], 0);
if (!window->attached_focus_window)
meta_window_appears_focused_changed (window);
meta_window_propagate_focus_appearance (window, TRUE);
}
else
{
window->has_focus = FALSE;
if (window->override_redirect)
return;
meta_window_propagate_focus_appearance (window, FALSE);
window->display->focus_window = NULL;
g_object_notify (G_OBJECT (window->display), "focus-window");
window->has_focus = FALSE;
if (!window->attached_focus_window)
meta_window_appears_focused_changed (window);
@ -7205,176 +7284,6 @@ meta_window_lost_focus (MetaWindow *window)
}
}
gboolean
meta_window_notify_focus (MetaWindow *window,
XIEnterEvent *event)
{
/* note the event can be on either the window or the frame,
* we focus the frame for shaded windows
*/
/* The event can be FocusIn, FocusOut, or UnmapNotify.
* On UnmapNotify we have to pretend it's focus out,
* because we won't get a focus out if it occurs, apparently.
*/
/* We ignore grabs, though this is questionable.
* It may be better to increase the intelligence of
* the focus window tracking.
*
* The problem is that keybindings for windows are done with
* XGrabKey, which means focus_window disappears and the front of
* the MRU list gets confused from what the user expects once a
* keybinding is used.
*/
meta_topic (META_DEBUG_FOCUS,
"Focus %s event received on %s 0x%lx (%s) "
"mode %s detail %s\n",
event->evtype == XI_FocusIn ? "in" :
event->evtype == XI_FocusOut ? "out" :
"???",
window->desc, event->event,
event->event == window->xwindow ?
"client window" :
(window->frame && event->event == window->frame->xwindow) ?
"frame window" :
"unknown window",
meta_event_mode_to_string (event->mode),
meta_event_detail_to_string (event->detail));
/* FIXME our pointer tracking is broken; see how
* gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
* handle it for the correct way. In brief you need to track
* pointer focus and regular focus, and handle EnterNotify in
* PointerRoot mode with no window manager. However as noted above,
* accurate focus tracking will break things because we want to keep
* windows "focused" when using keybindings on them, and also we
* sometimes "focus" a window by focusing its frame or
* no_focus_window; so this all needs rethinking massively.
*
* My suggestion is to change it so that we clearly separate
* actual keyboard focus tracking using the xterm algorithm,
* and mutter's "pretend" focus window, and go through all
* the code and decide which one should be used in each place;
* a hard bit is deciding on a policy for that.
*
* http://bugzilla.gnome.org/show_bug.cgi?id=90382
*/
if ((event->evtype == XI_FocusIn ||
event->evtype == XI_FocusOut) &&
(event->mode == NotifyGrab ||
event->mode == NotifyUngrab ||
/* From WindowMaker, ignore all funky pointer root events */
event->detail > NotifyNonlinearVirtual))
{
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus event generated by a grab or other weirdness\n");
return TRUE;
}
if (event->evtype == XI_FocusIn)
{
if (window->override_redirect)
{
window->display->focus_window = NULL;
g_object_notify (G_OBJECT (window->display), "focus-window");
return FALSE;
}
if (window != window->display->focus_window)
{
meta_topic (META_DEBUG_FOCUS,
"* Focus --> %s\n", window->desc);
window->display->focus_window = window;
window->has_focus = TRUE;
/* Move to the front of the focusing workspace's MRU list.
* We should only be "removing" it from the MRU list if it's
* not already there. Note that it's possible that we might
* be processing this FocusIn after we've changed to a
* different workspace; we should therefore update the MRU
* list only if the window is actually on the active
* workspace.
*/
if (window->screen->active_workspace &&
meta_window_located_on_workspace (window,
window->screen->active_workspace))
{
GList* link;
link = g_list_find (window->screen->active_workspace->mru_list,
window);
g_assert (link);
window->screen->active_workspace->mru_list =
g_list_remove_link (window->screen->active_workspace->mru_list,
link);
g_list_free (link);
window->screen->active_workspace->mru_list =
g_list_prepend (window->screen->active_workspace->mru_list,
window);
}
if (window->frame)
meta_frame_queue_draw (window->frame);
meta_error_trap_push (window->display);
XInstallColormap (window->display->xdisplay,
window->colormap);
meta_error_trap_pop (window->display);
/* move into FOCUSED_WINDOW layer */
meta_window_update_layer (window);
/* Ungrab click to focus button since the sync grab can interfere
* with some things you might do inside the focused window, by
* causing the client to get funky enter/leave events.
*
* The reason we usually have a passive grab on the window is
* so that we can intercept clicks and raise the window in
* response. For click-to-focus we don't need that since the
* focused window is already raised. When raise_on_click is
* FALSE we also don't need that since we don't do anything
* when the window is clicked.
*
* There is dicussion in bugs 102209, 115072, and 461577
*/
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK ||
!meta_prefs_get_raise_on_click())
meta_display_ungrab_focus_window_button (window->display, window);
g_signal_emit (window, window_signals[FOCUS], 0);
g_object_notify (G_OBJECT (window->display), "focus-window");
if (!window->attached_focus_window)
meta_window_appears_focused_changed (window);
meta_window_propagate_focus_appearance (window, TRUE);
}
}
else if (event->evtype == XI_FocusOut)
{
if (event->detail == NotifyInferior)
{
/* This event means the client moved focus to a subwindow */
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus out on %s with NotifyInferior\n",
window->desc);
return TRUE;
}
else
{
meta_window_lost_focus (window);
}
}
/* Now set _NET_ACTIVE_WINDOW hint */
meta_display_update_active_window_hint (window->display);
return FALSE;
}
static gboolean
process_property_notify (MetaWindow *window,
XPropertyEvent *event)
@ -11216,3 +11125,9 @@ meta_window_compute_tile_match (MetaWindow *window)
window->tile_match = match;
}
}
gboolean
meta_window_can_close (MetaWindow *window)
{
return window->has_close_func;
}

View File

@ -70,6 +70,7 @@ item(_GNOME_WM_KEYBINDINGS)
item(_GNOME_PANEL_ACTION)
item(_GNOME_PANEL_ACTION_MAIN_MENU)
item(_GNOME_PANEL_ACTION_RUN_DIALOG)
item(_MUTTER_FOCUS_SET)
item(_MUTTER_SENTINEL)
item(_MUTTER_VERSION)
item(WM_CLIENT_MACHINE)

View File

@ -35,7 +35,6 @@
/* Public compositor API */
ClutterActor *meta_get_stage_for_screen (MetaScreen *screen);
ClutterActor *meta_get_overlay_group_for_screen (MetaScreen *screen);
Window meta_get_overlay_window (MetaScreen *screen);
GList *meta_get_window_actors (MetaScreen *screen);
ClutterActor *meta_get_window_group_for_screen (MetaScreen *screen);
@ -47,5 +46,8 @@ void meta_enable_unredirect_for_screen (MetaScreen *screen);
void meta_set_stage_input_region (MetaScreen *screen,
XserverRegion region);
void meta_empty_stage_input_region (MetaScreen *screen);
void meta_focus_stage_window (MetaScreen *screen,
guint32 timestamp);
gboolean meta_stage_is_focused (MetaScreen *screen);
#endif

View File

@ -165,6 +165,10 @@ void meta_display_set_input_focus_window (MetaDisplay *display,
gboolean focus_frame,
guint32 timestamp);
void meta_display_request_take_focus (MetaDisplay *display,
MetaWindow *window,
guint32 timestamp);
/* meta_display_focus_the_no_focus_window is called when the
* designated no_focus_window should be focused, but is otherwise the
* same as meta_display_set_input_focus_window
@ -187,4 +191,11 @@ void meta_display_unmanage_screen (MetaDisplay *display,
void meta_display_clear_mouse_mode (MetaDisplay *display);
void meta_display_freeze_keyboard (MetaDisplay *display,
Window window,
guint32 timestamp);
void meta_display_ungrab_keyboard (MetaDisplay *display,
guint32 timestamp);
void meta_display_unfreeze_keyboard (MetaDisplay *display,
guint32 timestamp);
#endif

View File

@ -353,6 +353,7 @@ typedef enum _MetaKeyBindingAction
META_KEYBINDING_ACTION_MOVE_TO_SIDE_W,
META_KEYBINDING_ACTION_MOVE_TO_CENTER,
META_KEYBINDING_ACTION_OVERLAY_KEY,
META_KEYBINDING_ACTION_ISO_NEXT_GROUP,
META_KEYBINDING_ACTION_LAST
} MetaKeyBindingAction;
@ -442,6 +443,7 @@ void meta_prefs_get_window_binding (const char *name,
MetaVirtualModifier *modifiers);
void meta_prefs_get_overlay_binding (MetaKeyCombo *combo);
const char *meta_prefs_get_iso_next_group_option (void);
gboolean meta_prefs_get_visual_bell (void);
gboolean meta_prefs_bell_is_audible (void);

View File

@ -237,4 +237,6 @@ void meta_window_begin_grab_op (MetaWindow *window,
gboolean frame_action,
guint32 timestamp);
gboolean meta_window_can_close (MetaWindow *window);
#endif

View File

@ -739,22 +739,6 @@ meta_ui_frame_get_corner_radiuses (MetaFrames *frames,
*bottom_right = fgeom.bottom_right_corner_rounded_radius + sqrt(fgeom.bottom_right_corner_rounded_radius);
}
void
meta_frames_get_corner_radiuses (MetaFrames *frames,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right)
{
MetaUIFrame *frame;
frame = meta_frames_lookup_window (frames, xwindow);
meta_ui_frame_get_corner_radiuses (frames, frame, top_left, top_right,
bottom_left, bottom_right);
}
void
meta_frames_reset_bg (MetaFrames *frames,
Window xwindow)
@ -1851,6 +1835,102 @@ clip_region_to_visible_frame_border (cairo_region_t *region,
cairo_region_destroy (frame_border);
}
#define TAU (2*M_PI)
/*
* Draw the opaque and semi-opaque pixels of this frame into a mask.
*
* (0,0) in Cairo coordinates is assumed to be the top left corner of the
* invisible border.
*
* The parts of @cr's surface in the clip region are assumed to be
* initialized to fully-transparent, and the clip region is assumed to
* contain the invisible border and the visible parts of the frame, but
* not the client area.
*
* This function uses @cr to draw pixels of arbitrary color (it will
* typically be drawing in a %CAIRO_FORMAT_A8 surface, so the color is
* discarded anyway) with appropriate alpha values to reproduce this
* frame's alpha channel, as a mask to be applied to an opaque pixmap.
*
* @frame: This frame
* @xwindow: The X window for the frame, which has the client window as a child
* @width: The width of the framed window including any invisible borders
* @height: The height of the framed window including any invisible borders
* @cr: Used to draw the resulting mask
*/
void
meta_frames_get_mask (MetaFrames *frames,
Window xwindow,
guint width,
guint height,
cairo_t *cr)
{
MetaUIFrame *frame = meta_frames_lookup_window (frames, xwindow);
float top_left, top_right, bottom_left, bottom_right;
int x, y;
MetaFrameBorders borders;
if (frame == NULL)
meta_bug ("No such frame 0x%lx\n", xwindow);
cairo_save (cr);
meta_ui_frame_get_borders (frames, frame, &borders);
meta_ui_frame_get_corner_radiuses (frames, frame,
&top_left, &top_right,
&bottom_left, &bottom_right);
/* top left */
x = borders.invisible.left;
y = borders.invisible.top;
cairo_arc (cr,
x + top_left,
y + top_left,
top_left,
2 * TAU / 4,
3 * TAU / 4);
/* top right */
x = width - borders.invisible.right - top_right;
y = borders.invisible.top;
cairo_arc (cr,
x,
y + top_right,
top_right,
3 * TAU / 4,
4 * TAU / 4);
/* bottom right */
x = width - borders.invisible.right - bottom_right;
y = height - borders.invisible.bottom - bottom_right;
cairo_arc (cr,
x,
y,
bottom_right,
0 * TAU / 4,
1 * TAU / 4);
/* bottom left */
x = borders.invisible.left;
y = height - borders.invisible.bottom - bottom_left;
cairo_arc (cr,
x + bottom_left,
y,
bottom_left,
1 * TAU / 4,
2 * TAU / 4);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
cairo_fill (cr);
cairo_restore (cr);
}
static gboolean
meta_frames_draw (GtkWidget *widget,
cairo_t *cr)

View File

@ -140,12 +140,11 @@ cairo_region_t *meta_frames_get_frame_bounds (MetaFrames *frames,
int window_width,
int window_height);
void meta_frames_get_corner_radiuses (MetaFrames *frames,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right);
void meta_frames_get_mask (MetaFrames *frames,
Window xwindow,
guint width,
guint height,
cairo_t *cr);
void meta_frames_move_resize_frame (MetaFrames *frames,
Window xwindow,

View File

@ -857,10 +857,7 @@ meta_convert_meta_to_wnck (MetaWindow *window, MetaScreen *screen)
WnckWindowDisplayInfo wnck_window;
wnck_window.icon = window->icon;
wnck_window.mini_icon = window->mini_icon;
wnck_window.is_active = FALSE;
if (window == window->display->expected_focus_window)
wnck_window.is_active = TRUE;
wnck_window.is_active = window->has_focus;
if (window->frame)
{

View File

@ -1880,7 +1880,7 @@ debug_print_tokens (PosToken *tokens,
/**
* pos_tokenize:
* @expr: The expression
* @tokens_p: (out) The resulting tokens
* @tokens_p: (out): The resulting tokens
* @n_tokens_p: (out): The number of resulting tokens
* @err: (out): set to the problem if there was a problem

View File

@ -324,6 +324,16 @@ meta_ui_free (MetaUI *ui)
g_free (ui);
}
void
meta_ui_get_frame_mask (MetaUI *ui,
Window frame_xwindow,
guint width,
guint height,
cairo_t *cr)
{
meta_frames_get_mask (ui->frames, frame_xwindow, width, height, cr);
}
void
meta_ui_get_frame_borders (MetaUI *ui,
Window frame_xwindow,
@ -333,19 +343,6 @@ meta_ui_get_frame_borders (MetaUI *ui,
borders);
}
void
meta_ui_get_corner_radiuses (MetaUI *ui,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right)
{
meta_frames_get_corner_radiuses (ui->frames, xwindow,
top_left, top_right,
bottom_left, bottom_right);
}
Window
meta_ui_create_frame_window (MetaUI *ui,
Display *xdisplay,

View File

@ -66,6 +66,13 @@ void meta_ui_theme_get_frame_borders (MetaUI *ui,
void meta_ui_get_frame_borders (MetaUI *ui,
Window frame_xwindow,
MetaFrameBorders *borders);
void meta_ui_get_frame_mask (MetaUI *ui,
Window frame_xwindow,
guint width,
guint height,
cairo_t *cr);
Window meta_ui_create_frame_window (MetaUI *ui,
Display *xdisplay,
Visual *xvisual,
@ -102,13 +109,6 @@ cairo_region_t *meta_ui_get_frame_bounds (MetaUI *ui,
int window_width,
int window_height);
void meta_ui_get_corner_radiuses (MetaUI *ui,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right);
void meta_ui_queue_frame_draw (MetaUI *ui,
Window xwindow);

View File

@ -19,7 +19,10 @@ test_size_hints_SOURCES= \
test_attached_SOURCES= \
test-attached.c
noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window test-size-hints test-attached
test_focus_SOURCES= \
test-focus.c
noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window test-size-hints test-attached test-focus
wm_tester_LDADD= @MUTTER_LIBS@
test_gravity_LDADD= @MUTTER_LIBS@
@ -27,3 +30,4 @@ test_resizing_LDADD= @MUTTER_LIBS@
test_size_hints_LDADD= @MUTTER_LIBS@
focus_window_LDADD= @MUTTER_LIBS@
test_attached_LDADD= @MUTTER_LIBS@
test_focus_LDADD= @MUTTER_LIBS@

362
src/wm-tester/test-focus.c Normal file
View File

@ -0,0 +1,362 @@
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
GtkWidget *main_window;
GtkWidget *noinput_window, *passive_window, *local_window;
GtkWidget *global_window, *lame_window, *grabby_window, *dying_window;
static void
clear_on_destroy (GtkWidget *widget, gpointer user_data)
{
GtkWidget **widget_pointer = user_data;
*widget_pointer = NULL;
}
static void
disable_take_focus (GtkWidget *window)
{
GdkDisplay *display;
GdkWindow *gdkwindow;
Atom *protocols, wm_take_focus;
int n_protocols, i;
gtk_widget_realize (window);
gdkwindow = gtk_widget_get_window (window);
display = gdk_window_get_display (gdkwindow);
wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
XGetWMProtocols (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (gdkwindow),
&protocols, &n_protocols);
for (i = 0; i < n_protocols; i++)
{
if (protocols[i] == wm_take_focus)
{
protocols[i] = protocols[n_protocols - 1];
n_protocols--;
break;
}
}
XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (gdkwindow),
protocols, n_protocols);
XFree (protocols);
}
static void
clear_input_hint (GtkWidget *window)
{
/* This needs to be called after gtk_widget_show, otherwise
* GTK+ will overwrite it. */
GdkWindow *gdkwindow = gtk_widget_get_window (window);
XWMHints *wm_hints;
wm_hints = XGetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
GDK_WINDOW_XID (gdkwindow));
wm_hints->flags |= InputHint;
wm_hints->input = False;
XSetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
GDK_WINDOW_XID (gdkwindow),
wm_hints);
XFree (wm_hints);
}
static void
active_notify (GObject *obj,
GParamSpec *pspec,
gpointer user_data)
{
GtkLabel *label = user_data;
if (gtk_window_is_active (GTK_WINDOW (obj)))
gtk_label_set_text (label, "Focused");
else
gtk_label_set_text (label, "Not focused");
}
static void
make_focused_label (GtkWidget *toplevel,
GtkWidget *parent)
{
GtkWidget *label;
label = gtk_label_new ("");
gtk_widget_show (label);
gtk_container_add (GTK_CONTAINER (parent), label);
g_signal_connect (toplevel, "notify::is-active",
G_CALLBACK (active_notify), label);
active_notify (G_OBJECT (toplevel), NULL, label);
}
static void
setup_test_dialog (GtkWidget *toplevel)
{
make_focused_label (toplevel, toplevel);
gtk_widget_set_size_request (toplevel, 200, 200);
}
static void
noinput_clicked (GtkButton *button, gpointer user_data)
{
if (noinput_window)
gtk_window_present_with_time (GTK_WINDOW (noinput_window), gtk_get_current_event_time ());
else
{
noinput_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "No Input",
"accept-focus", FALSE,
NULL);
setup_test_dialog (noinput_window);
g_signal_connect (noinput_window, "destroy",
G_CALLBACK (clear_on_destroy), &noinput_window);
disable_take_focus (noinput_window);
gtk_widget_show (noinput_window);
}
}
static void
passive_clicked (GtkButton *button, gpointer user_data)
{
if (passive_window)
gtk_window_present_with_time (GTK_WINDOW (passive_window), gtk_get_current_event_time ());
else
{
passive_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "Passive Input",
"accept-focus", TRUE,
NULL);
setup_test_dialog (passive_window);
g_signal_connect (passive_window, "destroy",
G_CALLBACK (clear_on_destroy), &passive_window);
disable_take_focus (passive_window);
gtk_widget_show (passive_window);
}
}
static void
local_clicked (GtkButton *button, gpointer user_data)
{
if (local_window)
gtk_window_present_with_time (GTK_WINDOW (local_window), gtk_get_current_event_time ());
else
{
local_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "Locally Active Input",
"accept-focus", TRUE,
NULL);
setup_test_dialog (local_window);
g_signal_connect (local_window, "destroy",
G_CALLBACK (clear_on_destroy), &local_window);
gtk_widget_show (local_window);
}
}
static void
global_clicked (GtkButton *button, gpointer user_data)
{
if (global_window)
gtk_window_present_with_time (GTK_WINDOW (global_window), gtk_get_current_event_time ());
else
{
/* gtk will only process WM_TAKE_FOCUS messages if accept-focus
* is TRUE. So we set that property and then manually clear the
* Input WMHint.
*/
global_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "Globally Active Input",
"accept-focus", TRUE,
NULL);
setup_test_dialog (global_window);
g_signal_connect (global_window, "destroy",
G_CALLBACK (clear_on_destroy), &global_window);
gtk_widget_show (global_window);
clear_input_hint (global_window);
}
}
static void
lame_clicked (GtkButton *button, gpointer user_data)
{
if (lame_window)
gtk_window_present_with_time (GTK_WINDOW (lame_window), gtk_get_current_event_time ());
else
{
lame_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "Lame Globally Active Input",
"accept-focus", FALSE,
NULL);
setup_test_dialog (lame_window);
g_signal_connect (lame_window, "destroy",
G_CALLBACK (clear_on_destroy), &lame_window);
gtk_widget_show (lame_window);
}
}
static void
grabby_active_changed (GObject *object, GParamSpec *param, gpointer user_data)
{
if (gtk_window_is_active (GTK_WINDOW (grabby_window)))
{
GdkWindow *gdkwindow = gtk_widget_get_window (grabby_window);
guint32 now = gdk_x11_get_server_time (gdkwindow);
gtk_window_present_with_time (GTK_WINDOW (main_window), now - 1);
XSetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
GDK_WINDOW_XID (gdkwindow),
RevertToParent,
now);
}
}
static void
grabby_clicked (GtkButton *button, gpointer user_data)
{
if (grabby_window)
gtk_window_present_with_time (GTK_WINDOW (grabby_window), gtk_get_current_event_time ());
else
{
grabby_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "Focus-grabbing Window",
"accept-focus", TRUE,
/* Because mutter maps windows
* asynchronously, our trick won't
* work if we try to do it when the
* window is first mapped.
*/
"focus-on-map", FALSE,
NULL);
setup_test_dialog (grabby_window);
g_signal_connect (grabby_window, "destroy",
G_CALLBACK (clear_on_destroy), &grabby_window);
g_signal_connect (grabby_window, "notify::is-active",
G_CALLBACK (grabby_active_changed), NULL);
gtk_widget_show (grabby_window);
}
}
static void
dying_clicked (GtkButton *button, gpointer user_data)
{
if (dying_window)
{
gtk_window_present_with_time (GTK_WINDOW (dying_window), gtk_get_current_event_time ());
gtk_widget_destroy (dying_window);
}
else
{
GtkWidget *label;
dying_window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_TOPLEVEL,
"title", "Dying Window",
"accept-focus", TRUE,
/* As with grabby window */
"focus-on-map", FALSE,
NULL);
setup_test_dialog (dying_window);
g_signal_connect (dying_window, "destroy",
G_CALLBACK (clear_on_destroy), &dying_window);
label = gtk_label_new ("Click button again to test");
gtk_container_add (GTK_CONTAINER (dying_window), label);
gtk_widget_set_size_request (dying_window, 200, 200);
gtk_widget_show_all (dying_window);
}
}
static void
main_window_destroyed (GtkWidget *widget, gpointer user_data)
{
gtk_main_quit ();
}
int
main (int argc, char **argv)
{
GtkWidget *vbox, *button;
gtk_init (&argc, &argv);
main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (main_window), "Focus Tester");
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
gtk_box_set_homogeneous (GTK_BOX (vbox), 8);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
gtk_container_add (GTK_CONTAINER (main_window), vbox);
make_focused_label (main_window, vbox);
/* ICCCM "No Input" mode; Input hint False, WM_TAKE_FOCUS absent */
button = gtk_button_new_with_label ("No Input Window");
g_signal_connect (button, "clicked", G_CALLBACK (noinput_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
/* ICCCM "Passive" mode; Input hint True, WM_TAKE_FOCUS absent */
button = gtk_button_new_with_label ("Passive Input Window");
g_signal_connect (button, "clicked", G_CALLBACK (passive_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
/* ICCCM "Locally Active" mode; Input hint True, WM_TAKE_FOCUS present.
* This is the behavior of GtkWindows with accept_focus==TRUE.
*/
button = gtk_button_new_with_label ("Locally Active Window");
g_signal_connect (button, "clicked", G_CALLBACK (local_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
/* ICCCM "Globally Active" mode; Input hint False, WM_TAKE_FOCUS present,
* and the window responds to WM_TAKE_FOCUS by calling XSetInputFocus.
*/
button = gtk_button_new_with_label ("Globally Active Window");
g_signal_connect (button, "clicked", G_CALLBACK (global_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
/* "Lame" Globally Active mode; like "Globally Active", except that
* the window does not respond to WM_TAKE_FOCUS. This is the
* behavior of GtkWindows with accept_focus==FALSE.
*/
button = gtk_button_new_with_label ("Globally Lame Window");
g_signal_connect (button, "clicked", G_CALLBACK (lame_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
/* "Grabby" window; when you activate the window, it asks the wm to
* focus the main window, but then forcibly grabs focus back with
* a newer timestamp, causing the wm to be temporarily confused
* about what window is focused and triggering the "Earlier attempt
* to focus ... failed" codepath.
*/
button = gtk_button_new_with_label ("Grabby Window");
g_signal_connect (button, "clicked", G_CALLBACK (grabby_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
/* "Dying" window; we create the window on the first click, then
* activate it and destroy it on the second click, causing mutter to
* do an XSetInputFocus but not receive the corresponding FocusIn.
*/
button = gtk_button_new_with_label ("Dying Window");
g_signal_connect (button, "clicked", G_CALLBACK (dying_clicked), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
gtk_widget_show_all (main_window);
g_signal_connect (main_window, "destroy",
G_CALLBACK (main_window_destroyed), NULL);
gtk_main ();
return 0;
}