Compare commits

...

14 Commits

Author SHA1 Message Date
Marco Trevisan (Treviño)
9056fb00b0 gitlab-ci: run tests too 2018-11-14 02:46:16 -06:00
Marco Trevisan (Treviño)
0503ecaf84 tests: add headless_tests support
Run tests using Xvfb so that we can run them in CI
2018-11-14 02:19:09 -06:00
Marco Trevisan (Treviño)
dec20d13e1 meson, tests: Use suites for test cases
They allows to filter tests better and so we can just launch tests with:
  meson test --suite [mutter/stacking|cogl|clutter] [single-test-name]
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
0242c1a527 meson, tests: Add single stacking tests with suite
Don't launch the stacking tests in one single shot, to allow better debugging
and being able to launch just one single test using meson test.

Those tests can now be all launched with:
  meson test --suite stacking [single-test-name]
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
8652ec9ee2 window-x11: Focus the default window waiting for take focus
It's not guaranteed that a focus window will require the focus (this doesn't
happen for no-input gtk windows in fact [to be fixed there too]).
And in such case instead of unsetting the focus, while waiting the take focus
request to arrive, we should focus the current workspace default focus window
instead.
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
4c607164fd window: Add is_focusable class method
Implement is_focusable for both x11 and wayland and just use this check
so that we can abstract things more and be less dependent on window backend.
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
08f920606e tests: Verify focused window in closed-transient tests
Ensure that we have a focused window when closing transient windows with
no-focus or no-take-focus atoms
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
ef3ea0a59c test-runner: Add assert_focused command
This allows to verify which window should have the focus, which might not
be the same as the top of the stack.
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
a5f360cb9e workspace: Focus only ancestors who can be actually focused
When destroying a window that has a parent, we initially try to focus one of
its ancestors. However if no ancestor can be focused, then we should instead
focus the fault window instead of trying to request focus for a window
that can't get focus anyways.

Fixes #308
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
26cb0a5a75 tests, stacking: Add tests with no-input and no-take-focus windows
When a window with no frame, that doesn't accept focus and that has no take-focus
atom set is destroyed, we end up in not changing the current_focus window, causing
a crash.

Added test cases that verify this situation (expected to fail).

See #308
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
7c89167644 tests: Add take_focus command to runner and client
Allow to set/unset WM_TAKE_FOCUS from client window.
This is added by default by gtk, but this might not happen in other toolkits,
so add an ability to (un)set this.

So fetch the protocols with XGetWMProtocols and unset the atom.

test-client now needs to depend on Xlib directly in meson build.
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
984d27f050 tests: Add accept_focus command to client and runner
Under the hood, calls gtk_window_set_accept_focus in the client
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
5b13372fd4 meson, tests: Add missing stacking tests
Those .metatest where added to autotools and where missing in meson port.
2018-11-13 22:14:22 -06:00
Marco Trevisan (Treviño)
4334534742 tests: Accept warnings when initializing mutter
Initializing mutter, might cause warning such as the `failed to bind to` when
another XServer is running. However these warnings are not critical, so we can
safely ignore them.
2018-11-13 22:14:22 -06:00
24 changed files with 494 additions and 56 deletions

View File

@ -2,10 +2,45 @@ image: registry.gitlab.gnome.org/gnome/mutter/master:v1
stages:
- build
- test
build-mutter:
stage: build
script:
- meson . build -Degl_device=true -Dwayland_eglstream=true
- meson . build -Degl_device=true -Dwayland_eglstream=true -Dheadless_tests=enabled
- ninja -C build
- ninja -C build install
- meson test -v -C build --suite headless
# artifacts:
# paths:
# - build
# test-cogl:
# stage: test
# dependencies:
# - build-mutter
# artifacts:
# paths:
# - build
# script:
# - meson test -v -C build --suite cogl-headless
# test-clutter:
# stage: test
# dependencies:
# - build-mutter
# artifacts:
# paths:
# - build
# script:
# - meson test -v -C build --suite clutter-headless
# test-mutter:
# stage: test
# dependencies:
# - build-mutter
# artifacts:
# paths:
# - build
# script:
# - meson test -v -C build --suite mutter-headless

View File

@ -7,5 +7,8 @@ RUN dnf -y update && dnf -y upgrade && \
# Until Fedora catches up with meson build-deps
dnf install -y meson xorg-x11-server-Xorg gnome-settings-daemon-devel egl-wayland-devel xorg-x11-server-Xwayland && \
# To enable testing headless
dnf install -y xorg-x11-server-Xvfb && \
dnf install -y intltool redhat-rpm-config make && \
dnf clean all

View File

@ -78,7 +78,18 @@ foreach test : clutter_conform_tests
install: false,
)
test('clutter/conform/@0@'.format(test), test_executable,
test(test, test_executable,
suite: ['clutter', 'clutter/conform'],
env: test_env
)
if have_headless_tests
test(test, xvfb,
args: test_executable,
suite: ['clutter-headless', 'clutter-headless/conform', 'headless'],
env: test_env,
is_parallel: false,
timeout: 60,
)
endif
endforeach

View File

@ -95,7 +95,8 @@ cogl_conform_unit_tests = custom_target('cogl-tests-conform-unit-tests',
install: false,
)
test('cogl/conform', cogl_run_tests,
test('conform', cogl_run_tests,
suite: ['cogl'],
args: [
cogl_config_env,
libmutter_cogl_test_conformance,
@ -104,3 +105,17 @@ test('cogl/conform', cogl_run_tests,
is_parallel: false,
timeout: 60,
)
if have_headless_tests
test('conform', xvfb,
suite: ['cogl-headless', 'headless'],
args: [
cogl_run_tests.path(),
cogl_config_env,
libmutter_cogl_test_conformance,
cogl_conform_unit_tests
],
is_parallel: false,
timeout: 500,
)
endif

View File

@ -32,11 +32,22 @@ cogl_unit_unit_tests = custom_target('cogl-tests-unit-unit-tests',
install: false,
)
test('cogl/unit', cogl_run_tests,
args: [
cogl_config_env,
libmutter_cogl_test_unit,
cogl_unit_unit_tests
],
cogl_unit_test_args = [
cogl_config_env,
libmutter_cogl_test_unit,
cogl_unit_unit_tests
]
test('unit', cogl_run_tests,
suite: ['cogl'],
args: cogl_unit_test_args,
is_parallel: false,
)
if have_headless_tests
test('unit', xvfb,
suite: ['cogl-headless', 'headless'],
args: [ cogl_run_tests.path() ] + cogl_unit_test_args,
is_parallel: false,
timeout: 90,
)
endif

View File

@ -245,6 +245,26 @@ if have_tests
endif
endif
headless_tests_option = get_option('headless_tests')
have_headless_tests = not headless_tests_option.disabled()
if have_headless_tests
if not have_tests and not have_cogl_tests and not have_clutter_tests
have_headless_tests = false
if headless_tests_option.enabled()
error('Headless tests are enabled, but no other test suite is')
endif
endif
if have_headless_tests
xvfb = find_program('xvfb-run', required: headless_tests_option.enabled())
have_headless_tests = xvfb.found()
endif
endif
if have_headless_tests
headless_tests_suite = ['headless']
else
headless_tests_suite = []
endif
required_functions = [
'ffs',
'clz',

View File

@ -123,6 +123,12 @@ option('tests',
description: 'Enable mutter tests'
)
option('headless_tests',
type: 'feature',
value: 'auto',
description: 'Enable mutter headless tests'
)
option('verbose',
type: 'boolean',
value: true,

View File

@ -15,6 +15,9 @@ dist_stacking_DATA = \
$(srcdir)/tests/stacking/basic-x11.metatest \
$(srcdir)/tests/stacking/basic-wayland.metatest \
$(srcdir)/tests/stacking/closed-transient.metatest \
$(srcdir)/tests/stacking/closed-transient-no-input-no-take-focus-parent.metatest \
$(srcdir)/tests/stacking/closed-transient-no-input-no-take-focus-parents.metatest \
$(srcdir)/tests/stacking/closed-transient-no-input-parent.metatest \
$(srcdir)/tests/stacking/minimized.metatest \
$(srcdir)/tests/stacking/mixed-windows.metatest \
$(srcdir)/tests/stacking/set-parent.metatest \

View File

@ -1214,10 +1214,7 @@ get_default_focus_window (MetaStack *stack,
if (window->unmaps_pending > 0)
continue;
if (window->unmanaging)
continue;
if (!(window->input || window->take_focus))
if (!meta_window_is_focusable (window))
continue;
if (!meta_window_should_be_showing (window))

View File

@ -570,6 +570,7 @@ struct _MetaWindowClass
ClutterInputDevice *source);
gboolean (*shortcuts_inhibited) (MetaWindow *window,
ClutterInputDevice *source);
gboolean (*is_focusable) (MetaWindow *window);
gboolean (*is_stackable) (MetaWindow *window);
gboolean (*are_updates_frozen) (MetaWindow *window);
};
@ -664,6 +665,8 @@ void meta_window_update_unfocused_button_grabs (MetaWindow *window);
void meta_window_set_focused_internal (MetaWindow *window,
gboolean focused);
gboolean meta_window_is_focusable (MetaWindow *window);
void meta_window_current_workspace_changed (MetaWindow *window);
void meta_window_show_menu (MetaWindow *window,

View File

@ -2205,7 +2205,7 @@ window_state_on_map (MetaWindow *window,
/* don't initially focus windows that are intended to not accept
* focus
*/
if (!(window->input || window->take_focus))
if (!meta_window_is_focusable (window))
{
*takes_focus = FALSE;
return;
@ -8512,6 +8512,15 @@ meta_window_shortcuts_inhibited (MetaWindow *window,
return META_WINDOW_GET_CLASS (window)->shortcuts_inhibited (window, source);
}
gboolean
meta_window_is_focusable (MetaWindow *window)
{
if (window->unmanaging)
return FALSE;
return META_WINDOW_GET_CLASS (window)->is_focusable (window);
}
gboolean
meta_window_is_stackable (MetaWindow *window)
{

View File

@ -87,6 +87,12 @@ typedef struct _MetaWorkspaceLogicalMonitorData
MetaRectangle logical_monitor_work_area;
} MetaWorkspaceLogicalMonitorData;
typedef struct _MetaWorkspaceFocusableAncestorData
{
MetaWorkspace *workspace;
MetaWindow **win;
} MetaWorkspaceFocusableAncestorData;
static MetaWorkspaceLogicalMonitorData *
meta_workspace_get_logical_monitor_data (MetaWorkspace *workspace,
MetaLogicalMonitor *logical_monitor)
@ -1328,13 +1334,21 @@ meta_workspace_focus_default_window (MetaWorkspace *workspace,
}
static gboolean
record_ancestor (MetaWindow *window,
void *data)
find_focusable_ancestor (MetaWindow *window,
void *data)
{
MetaWindow **result = data;
MetaWorkspaceFocusableAncestorData *mwfa = data;
MetaWindow **result = mwfa->win;
*result = window;
return FALSE; /* quit with the first ancestor we find */
if (meta_window_is_focusable (window) &&
meta_window_located_on_workspace (window, mwfa->workspace) &&
meta_window_showing_on_its_workspace (window))
{
*result = window;
return FALSE;
}
return TRUE;
}
/* Focus ancestor of not_this_one if there is one */
@ -1355,12 +1369,10 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace,
/* First, check to see if we need to focus an ancestor of a window */
if (not_this_one)
{
MetaWindow *ancestor;
ancestor = NULL;
meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor);
if (ancestor != NULL &&
meta_window_located_on_workspace (ancestor, workspace) &&
meta_window_showing_on_its_workspace (ancestor))
MetaWindow *ancestor = NULL;
MetaWorkspaceFocusableAncestorData mwfa = { workspace, &ancestor };
meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &mwfa);
if (ancestor != NULL)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing %s, ancestor of %s\n",

View File

@ -191,8 +191,7 @@ main (int argc, char *argv[])
META_TYPE_BACKEND_TEST);
meta_wayland_override_display_name ("mutter-test-display");
meta_init ();
meta_register_with_session ();
test_meta_init ();
g_idle_add (run_tests, NULL);

View File

@ -22,6 +22,7 @@ test_client = executable('mutter-test-client',
dependencies: [
gtk3_dep,
gio_unix_dep,
x11_dep,
xext_dep,
],
install: false,
@ -81,32 +82,73 @@ headless_start_test = executable('mutter-headless-start-test',
install: false,
)
stacking_tests = files([
'stacking/basic-x11.metatest',
'stacking/basic-wayland.metatest',
'stacking/minimized.metatest',
'stacking/mixed-windows.metatest',
'stacking/set-parent.metatest',
'stacking/override-redirect.metatest',
])
stacking_tests = [
'basic-x11',
'basic-wayland',
'client-side-decorated',
'closed-transient',
'closed-transient-no-input-no-take-focus-parent',
'closed-transient-no-input-no-take-focus-parents',
'closed-transient-no-input-parent',
'minimized',
'mixed-windows',
'set-parent',
'override-redirect',
'set-parent-exported',
]
test('mutter/stacking', test_runner,
env: test_env,
args: [
stacking_tests,
],
is_parallel: false,
timeout: 60,
)
foreach stacking_test: stacking_tests
test(stacking_test, test_runner,
suite: ['mutter/stacking'],
env: test_env,
args: [
files(join_paths('stacking', stacking_test + '.metatest')),
],
is_parallel: false,
timeout: 60,
)
if have_headless_tests
test(stacking_test, xvfb,
suite: ['mutter-headless', 'mutter-headless/stacking', 'headless'],
env: test_env,
args: [
test_runner,
files(join_paths('stacking', stacking_test + '.metatest')),
],
is_parallel: false,
timeout: 60,
)
endif
endforeach
test('mutter/unit', unit_tests,
test('normal', unit_tests,
suite: ['mutter/unit'],
env: test_env,
is_parallel: false,
timeout: 60,
)
test('mutter/unit/headless-start', headless_start_test,
test('headless-start', headless_start_test,
suite: ['mutter/unit'],
env: test_env,
is_parallel: false,
timeout: 60,
)
if have_headless_tests
test('normal', xvfb,
args: unit_tests,
suite: ['mutter-headless', 'mutter-headless/unit', 'headless'],
env: test_env,
is_parallel: false,
timeout: 60,
)
test('headless-start', xvfb,
args: headless_start_test,
suite: ['mutter-headless', 'mutter-headless/unit', 'headless'],
env: test_env,
is_parallel: false,
timeout: 60,
)
endif

View File

@ -0,0 +1,25 @@
new_client 1 x11
create 1/1
show 1/1
wait
create 1/2 csd
set_parent 1/2 1
take_focus 1/2 false
accept_focus 1/2 false
show 1/2
wait
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 1/1 1/2 1/3
destroy 1/3
wait
assert_focused 1/1
assert_stacking 1/1 1/2

View File

@ -0,0 +1,32 @@
new_client 2 x11
create 2/1
show 2/1
wait
new_client 1 x11
create 1/1
accept_focus 1/1 false
take_focus 1/1 false
show 1/1
wait
create 1/2 csd
set_parent 1/2 1
take_focus 1/2 false
accept_focus 1/2 false
show 1/2
wait
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
wait
assert_stacking 1/1 1/2 2/1
assert_focused 2/1

View File

@ -0,0 +1,29 @@
new_client 2 x11
create 2/1
show 2/1
wait
new_client 1 x11
create 1/1
show 1/1
wait
create 1/2 csd
set_parent 1/2 1
accept_focus 1/2 false
show 1/2
wait
create 1/3 csd
set_parent 1/3 2
show 1/3
wait
assert_focused 1/3
assert_stacking 2/1 1/1 1/2 1/3
destroy 1/3
wait
assert_focused 1/1
assert_stacking 2/1 1/1 1/2

View File

@ -196,6 +196,74 @@ process_line (const char *line)
NULL))
g_print ("Fail to export handle for window id %s", argv[2]);
}
else if (strcmp (argv[0], "accept_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0;
gtk_window_set_accept_focus (GTK_WINDOW (window), enabled);
}
else if (strcmp (argv[0], "take_focus") == 0)
{
if (argc != 3)
{
g_print ("usage: %s <window-id> [true|false]", argv[0]);
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
if (wayland)
{
g_print ("%s not supported under wayland", argv[0]);
goto out;
}
GdkDisplay *display = gdk_display_get_default ();
GdkWindow *gdkwindow = gtk_widget_get_window (window);
Display *xdisplay = gdk_x11_display_get_xdisplay (display);
Window xwindow = GDK_WINDOW_XID (gdkwindow);
Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0;
Atom *protocols = NULL;
Atom *new_protocols;
int n_protocols = 0;
int i, n = 0;
gdk_display_sync (display);
XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols);
new_protocols = g_malloc0 (sizeof (Atom) * (n_protocols + (add ? 1 : 0)));
for (i = 0; i < n_protocols; ++i)
{
if (protocols[i] != wm_take_focus)
new_protocols[n++] = protocols[i];
}
if (add)
new_protocols[n++] = wm_take_focus;
XSetWMProtocols (xdisplay, xwindow, new_protocols, n);
XFree (new_protocols);
XFree (protocols);
}
else if (strcmp (argv[0], "show") == 0)
{
if (argc != 2)

View File

@ -237,6 +237,38 @@ test_case_assert_stacking (TestCase *test,
return *error == NULL;
}
static gboolean
test_case_assert_focused (TestCase *test,
const char *expected_window,
GError **error)
{
MetaDisplay *display = meta_get_display ();
if (!display->focus_window)
{
if (g_ascii_strcasecmp (expected_window, "null") != 0 &&
g_ascii_strcasecmp (expected_window, "none") != 0 &&
g_strcmp0 (expected_window, "0") != 0)
{
g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED,
"focus: expected='%s', actual='NONE'", expected_window);
}
}
else
{
const char *focused = display->focus_window->title;
if (g_str_has_prefix (focused, "test/"))
focused += 5;
if (g_strcmp0 (focused, expected_window) != 0)
g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED,
"stacking: expected='%s', actual='%s'",
expected_window, focused);
}
return *error == NULL;
}
static gboolean
test_case_check_xserver_stacking (TestCase *test,
GError **error)
@ -398,6 +430,44 @@ test_case_do (TestCase *test,
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "accept_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "take_focus") == 0)
{
if (argc != 3 ||
(g_ascii_strcasecmp (argv[2], "true") != 0 &&
g_ascii_strcasecmp (argv[2], "false") != 0))
BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
argv[0], window_id,
argv[2],
@ -485,6 +555,11 @@ test_case_do (TestCase *test,
if (!test_case_check_xserver_stacking (test, error))
return FALSE;
}
else if (strcmp (argv[0], "assert_focused") == 0)
{
if (!test_case_assert_focused (test, argv[1], error))
return FALSE;
}
else
{
BAD_COMMAND("Unknown command %s", argv[0]);
@ -799,8 +874,7 @@ main (int argc, char **argv)
meta_plugin_manager_load (test_get_plugin_name ());
meta_wayland_override_display_name ("mutter-test-display");
meta_init ();
meta_register_with_session ();
test_meta_init ();
RunTestsInfo info;
info.tests = (char **)tests->pdata;

View File

@ -22,6 +22,7 @@
#include "tests/test-utils.h"
#include <gio/gio.h>
#include <meta/main.h>
#include <string.h>
#include "core/display-private.h"
@ -94,6 +95,21 @@ test_init (int *argc,
ensure_test_client_path (*argc, *argv);
}
void
test_meta_init ()
{
GLogLevelFlags log_flags;
/* Accept warnings in mutter initialization, as `failed to bind` one */
log_flags = g_log_set_always_fatal (G_LOG_FATAL_MASK);
g_log_set_always_fatal (log_flags & ~G_LOG_LEVEL_WARNING);
meta_init ();
meta_register_with_session ();
g_log_set_always_fatal (log_flags);
}
AsyncWaiter *
async_waiter_new (void)
{

View File

@ -43,6 +43,8 @@ typedef struct _TestClient TestClient;
void test_init (int *argc,
char ***argv);
void test_meta_init (void);
gboolean async_waiter_alarm_filter (AsyncWaiter *waiter,
MetaX11Display *x11_display,
XSyncAlarmNotifyEvent *event);

View File

@ -265,8 +265,7 @@ main (int argc, char *argv[])
META_TYPE_BACKEND_TEST);
meta_wayland_override_display_name ("mutter-test-display");
meta_init ();
meta_register_with_session ();
test_meta_init ();
g_idle_add (run_tests, NULL);

View File

@ -141,7 +141,7 @@ static void
meta_window_wayland_focus (MetaWindow *window,
guint32 timestamp)
{
if (window->input)
if (meta_window_is_focusable (window))
meta_x11_display_set_input_focus_window (window->display->x11_display,
window,
FALSE,
@ -585,6 +585,12 @@ meta_window_wayland_shortcuts_inhibited (MetaWindow *window,
return meta_wayland_compositor_is_shortcuts_inhibited (compositor, source);
}
static gboolean
meta_window_wayland_is_focusable (MetaWindow *window)
{
return window->input;
}
static gboolean
meta_window_wayland_is_stackable (MetaWindow *window)
{
@ -618,6 +624,7 @@ meta_window_wayland_class_init (MetaWindowWaylandClass *klass)
window_class->get_client_pid = meta_window_wayland_get_client_pid;
window_class->force_restore_shortcuts = meta_window_wayland_force_restore_shortcuts;
window_class->shortcuts_inhibited = meta_window_wayland_shortcuts_inhibited;
window_class->is_focusable = meta_window_wayland_is_focusable;
window_class->is_stackable = meta_window_wayland_is_stackable;
window_class->are_updates_frozen = meta_window_wayland_are_updates_frozen;
}

View File

@ -752,8 +752,7 @@ meta_window_x11_focus (MetaWindow *window,
* Still, we have to do this or keynav breaks for these windows.
*/
if (window->frame &&
(window->shaded ||
!(window->input || window->take_focus)))
(window->shaded || !meta_window_is_focusable (window)))
{
meta_topic (META_DEBUG_FOCUS,
"Focusing frame of %s\n", window->desc);
@ -790,13 +789,27 @@ meta_window_x11_focus (MetaWindow *window,
* Normally, we want to just leave the focus undisturbed until
* the window responds to WM_TAKE_FOCUS, but if we're unmanaging
* the current focus window we *need* to move the focus away, so
* we focus the no_focus_window now (and set
* display->focus_window to that) before sending WM_TAKE_FOCUS.
* we focus the default focus window excluding this one,
* before sending WM_TAKE_FOCUS.
*/
if (window->display->focus_window != NULL &&
window->display->focus_window->unmanaging)
meta_x11_display_focus_the_no_focus_window (window->display->x11_display,
timestamp);
{
MetaWindow *focus_window = window;
MetaWorkspace *workspace = window->workspace;
do
{
focus_window = meta_stack_get_default_focus_window (workspace->display->stack,
workspace,
focus_window);
}
while (!(!focus_window || focus_window->input ||
(focus_window->frame && focus_window->shaded)));
if (focus_window)
meta_window_x11_focus (focus_window, timestamp);
}
}
request_take_focus (window, timestamp);
@ -1627,6 +1640,12 @@ meta_window_x11_shortcuts_inhibited (MetaWindow *window,
return FALSE;
}
static gboolean
meta_window_x11_is_focusable (MetaWindow *window)
{
return window->input || window->take_focus;
}
static gboolean
meta_window_x11_is_stackable (MetaWindow *window)
{
@ -1669,6 +1688,7 @@ meta_window_x11_class_init (MetaWindowX11Class *klass)
window_class->get_client_pid = meta_window_x11_get_client_pid;
window_class->force_restore_shortcuts = meta_window_x11_force_restore_shortcuts;
window_class->shortcuts_inhibited = meta_window_x11_shortcuts_inhibited;
window_class->is_focusable = meta_window_x11_is_focusable;
window_class->is_stackable = meta_window_x11_is_stackable;
window_class->are_updates_frozen = meta_window_x11_are_updates_frozen;
}