Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
c7b8f26cad | |||
8747b97ba3 | |||
d20078574e | |||
26bd4fde5c | |||
2af49e503f | |||
6ea6af6eb4 | |||
10df80762c | |||
f86032d700 | |||
a8eb33f6fd | |||
bd19de9429 | |||
2ca2838548 | |||
df8234c5e3 | |||
d03ffd801e | |||
7a4c808e43 | |||
4f1d62170b | |||
e10804727d | |||
e430e051b7 | |||
696d9d2fa9 | |||
f6dd081acd | |||
eddd6f8e9b | |||
dfa4c7d670 | |||
a487d4dd01 | |||
c2ecdd0524 | |||
50b9042ac2 | |||
f5e75de330 | |||
1ffe1eae4d | |||
970a446bd8 | |||
8880dffbdb | |||
5b6621811c | |||
c2a9ccb7e2 | |||
4608cb6027 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -62,6 +62,7 @@ mutter-message
|
||||
mutter-window-demo
|
||||
focus-window
|
||||
test-attached
|
||||
test-focus
|
||||
test-gravity
|
||||
test-resizing
|
||||
test-size-hints
|
||||
|
27
NEWS
27
NEWS
@ -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]
|
||||
|
@ -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])
|
||||
|
36
po/nb.po
36
po/nb.po
@ -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"
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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, ×tamp))
|
||||
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, ×tamp))
|
||||
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, ×tamp))
|
||||
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, ×tamp))
|
||||
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
|
||||
*/
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
279
src/core/prefs.c
279
src/core/prefs.c
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
112
src/ui/frames.c
112
src/ui/frames.c
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
23
src/ui/ui.c
23
src/ui/ui.c
@ -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,
|
||||
|
14
src/ui/ui.h
14
src/ui/ui.h
@ -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);
|
||||
|
||||
|
@ -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
362
src/wm-tester/test-focus.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user