Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add focus stealing prevention to MinimalWindowManager #3693

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
12 changes: 11 additions & 1 deletion examples/example-server-lib/floating_window_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ FloatingWindowManagerPolicy::FloatingWindowManagerPolicy(
std::shared_ptr<SplashSession> const& spinner,
miral::InternalClientLauncher const& launcher,
std::function<void()>& shutdown_hook) :
MinimalWindowManager(tools),
FloatingWindowManagerPolicy{tools, spinner, launcher, shutdown_hook, FocusStealing::allow}
{
}

FloatingWindowManagerPolicy::FloatingWindowManagerPolicy(
WindowManagerTools const& tools,
std::shared_ptr<SplashSession> const& spinner,
miral::InternalClientLauncher const& launcher,
std::function<void()>& shutdown_hook,
FocusStealing focus_stealing) :
MinimalWindowManager(tools, focus_stealing),
spinner{spinner},
decoration_provider{std::make_unique<DecorationProvider>()}
{
Expand Down
8 changes: 8 additions & 0 deletions examples/example-server-lib/floating_window_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ class FloatingWindowManagerPolicy : public miral::MinimalWindowManager
std::shared_ptr<SplashSession> const& spinner,
miral::InternalClientLauncher const& launcher,
std::function<void()>& shutdown_hook);

FloatingWindowManagerPolicy(
miral::WindowManagerTools const& tools,
std::shared_ptr<SplashSession> const& spinner,
miral::InternalClientLauncher const& launcher,
std::function<void()>& shutdown_hook,
miral::FocusStealing focus_stealing);

~FloatingWindowManagerPolicy();

virtual miral::WindowSpecification place_new_window(
Expand Down
17 changes: 16 additions & 1 deletion examples/miral-shell/shell_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "miral/minimal_window_manager.h"
#include "tiling_window_manager.h"
#include "floating_window_manager.h"
#include "wallpaper_config.h"
Expand All @@ -33,6 +34,7 @@
#include <miral/x11_support.h>
#include <miral/wayland_extensions.h>

#include <stdexcept>
tarek-y-ismail marked this conversation as resolved.
Show resolved Hide resolved
#include <xkbcommon/xkbcommon-keysyms.h>

#include <cstring>
Expand Down Expand Up @@ -68,9 +70,18 @@ int main(int argc, char const* argv[])

SpinnerSplash spinner;
InternalClientLauncher launcher;
auto constexpr default_focus_stealing_prevention = false;
auto const to_focus_stealing = [](bool focus_stealing_prevention)
{
if (focus_stealing_prevention)
return FocusStealing::prevent;
else
return FocusStealing::allow;
};
auto focus_stealing_prevention = to_focus_stealing(default_focus_stealing_prevention);
WindowManagerOptions window_managers
{
add_window_manager_policy<FloatingWindowManagerPolicy>("floating", spinner, launcher, shutdown_hook),
add_window_manager_policy<FloatingWindowManagerPolicy>("floating", spinner, launcher, shutdown_hook, focus_stealing_prevention),
add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher),
};

Expand Down Expand Up @@ -139,6 +150,10 @@ int main(int argc, char const* argv[])
WaylandExtensions{},
X11Support{},
ConfigureDecorations{},
pre_init(ConfigurationOption{[&](bool option)
{ focus_stealing_prevention = to_focus_stealing(option); },
"focus-stealing-prevention", "allow or prevent focus stealing",
default_focus_stealing_prevention}),
window_managers,
display_configuration_options,
external_client_launcher,
Expand Down
17 changes: 17 additions & 0 deletions include/miral/miral/minimal_window_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,35 @@

namespace miral
{
enum class FocusStealing
{
prevent,
allow
};

/// Minimal implementation of a floating window management policy
/// \remark Since MirAL 2.5
class MinimalWindowManager : public WindowManagementPolicy
{
public:
explicit MinimalWindowManager(WindowManagerTools const& tools);

/// Allows shells to enable or disable focus stealing prevention.
/// \remark Since MirAL 5.2
explicit MinimalWindowManager(WindowManagerTools const& tools, FocusStealing focus_stealing);

/// Allows shells to change the modifer used to identify a window drag gesture
/// The default is mir_input_event_modifier_alt
/// \remark Since MirAL 3.7
MinimalWindowManager(WindowManagerTools const& tools, MirInputEventModifier pointer_drag_modifier);

/// Allows shells to to change the modifer used to identify a window drag
/// gesture and enable or disable focus stealing prevention.
/// The default drag modifier is mir_input_event_modifier_alt.
/// \remark Since MirAL 5.2
MinimalWindowManager(
WindowManagerTools const& tools, MirInputEventModifier pointer_drag_modifier, FocusStealing focus_stealing);

~MinimalWindowManager();

/// Honours the requested specification
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set(MIRPLATFORM_ABI 30)

set(MIRAL_VERSION_MAJOR 5)
set(MIRAL_VERSION_MINOR 1)
set(MIRAL_VERSION_MINOR 2)
set(MIRAL_VERSION_PATCH 0)
set(MIRAL_VERSION ${MIRAL_VERSION_MAJOR}.${MIRAL_VERSION_MINOR}.${MIRAL_VERSION_PATCH})

Expand Down
10 changes: 9 additions & 1 deletion src/miral/application_selector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ void ApplicationSelector::advise_new_window(WindowInfo const& window_info)
focus_list.push_back(window_info.window());
}

void ApplicationSelector::advise_new_window(WindowInfo const& window_info, bool focused)
{
if(!focused && !focus_list.empty())
focus_list.insert(focus_list.end() - 1, window_info.window());
else
advise_new_window(window_info);
}

void ApplicationSelector::select(miral::Window const& window)
{
if (selected)
Expand Down Expand Up @@ -281,4 +289,4 @@ auto ApplicationSelector::find(Window window) -> std::vector<Window>::iterator
{
return window == other;
});
}
}
2 changes: 2 additions & 0 deletions src/miral/application_selector.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class ApplicationSelector
/// Called when a window is created
void advise_new_window(WindowInfo const&);

void advise_new_window(WindowInfo const&, bool focused);

/// Called when focus is given to a window.
void advise_focus_gained(WindowInfo const&);

Expand Down
46 changes: 41 additions & 5 deletions src/miral/minimal_window_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,14 @@ auto touch_center(MirTouchEvent const* event) -> mir::geometry::Point

struct miral::MinimalWindowManager::Impl
{
Impl(WindowManagerTools const& tools, MirInputEventModifier pointer_drag_modifier) :
tools{tools}, application_selector(tools), pointer_drag_modifier{pointer_drag_modifier} {}
Impl(WindowManagerTools const& tools, MirInputEventModifier pointer_drag_modifier, FocusStealing focus_stealing) :
tools{tools},
application_selector(tools),
pointer_drag_modifier{pointer_drag_modifier},
focus_stealing{focus_stealing}
{
}

WindowManagerTools tools;

Gesture gesture = Gesture::none;
Expand Down Expand Up @@ -99,16 +105,29 @@ struct miral::MinimalWindowManager::Impl
void apply_resize_by(Displacement movement);

MirInputEventModifier const pointer_drag_modifier;
FocusStealing const focus_stealing;
};

miral::MinimalWindowManager::MinimalWindowManager(WindowManagerTools const& tools) :
MinimalWindowManager{tools, mir_input_event_modifier_alt}
{
}

miral::MinimalWindowManager::MinimalWindowManager(WindowManagerTools const& tools, FocusStealing focus_stealing) :
tools{tools},
self{new Impl{tools, mir_input_event_modifier_alt, focus_stealing}}
{
}

miral::MinimalWindowManager::MinimalWindowManager(WindowManagerTools const& tools, MirInputEventModifier pointer_drag_modifier):
tools{tools},
self{new Impl{tools, pointer_drag_modifier}}
self{new Impl{tools, pointer_drag_modifier, FocusStealing::allow}}
{
}

miral::MinimalWindowManager::MinimalWindowManager(WindowManagerTools const& tools, MirInputEventModifier pointer_drag_modifier, FocusStealing focus_stealing):
tools{tools},
self{new Impl{tools, pointer_drag_modifier, focus_stealing}}
{
}

Expand All @@ -126,7 +145,9 @@ auto miral::MinimalWindowManager::place_new_window(

void miral::MinimalWindowManager::handle_window_ready(WindowInfo& window_info)
{
if (window_info.can_be_active())
// If focus stealing prevention isn't enabled, activate on window ready (if
// possible). Otherwise, only activate the first opened window.
tarek-y-ismail marked this conversation as resolved.
Show resolved Hide resolved
if (((self->focus_stealing == FocusStealing::allow) || !tools.active_window()) && window_info.can_be_active())
tarek-y-ismail marked this conversation as resolved.
Show resolved Hide resolved
{
tools.select_active_window(window_info.window());
}
Expand Down Expand Up @@ -276,7 +297,22 @@ auto miral::MinimalWindowManager::confirm_inherited_move(WindowInfo const& windo

void miral::MinimalWindowManager::advise_new_window(miral::WindowInfo const& window_info)
{
self->application_selector.advise_new_window(window_info);
// If focus stealing prevention is on, swap the old focused window (now in
// the back) with the new window in the front.
//
// If it's a legitimate window, it'll be focused and raised via
// xdg-activation.
//
// This is limited to a couple of window types, as well as windows in the
// "application" layer that don't have a parent.
tarek-y-ismail marked this conversation as resolved.
Show resolved Hide resolved
auto in_background =
(self->focus_stealing == FocusStealing::prevent) && tools.active_window() &&
window_info.depth_layer() == mir_depth_layer_application && !window_info.parent();

self->application_selector.advise_new_window(window_info, in_background);

if (in_background)
tools.swap_tree_order(tools.active_window(), window_info.window());
}

void miral::MinimalWindowManager::advise_focus_gained(WindowInfo const& window_info)
Expand Down
Loading