commit 06392903e53f8b846f9cd438db0fa6927f8d05b8
parent a8292ce30c7923ee22bc886efd115e7f7209c7eb
Author: deurzen <m.deurzen@tum.de>
Date: Wed, 15 Dec 2021 09:41:15 +0100
implements per-workspace focus-follows-mouse (FFM)
Diffstat:
8 files changed, 107 insertions(+), 8 deletions(-)
diff --git a/src/core/model.cc b/src/core/model.cc
@@ -245,6 +245,11 @@ Model::Model(Connection& conn)
}))
},
+ // workspace behavior modifiers
+ { { Key::M, { Main, Shift } },
+ CALL(set_focus_follows_mouse(Toggle::Reverse, model.active_workspace()))
+ },
+
// workspace layout modifiers
{ { Key::F, { Main, Shift } },
CALL(set_layout(LayoutHandler::LayoutKind::Float))
@@ -1532,6 +1537,10 @@ Model::activate_workspace(Workspace_ptr next_workspace)
for (Client_ptr client : *mp_workspace)
if (!client->sticky)
unmap_client(client);
+ else
+ m_conn.set_window_notify_enter(client->frame,
+ next_workspace->focus_follows_mouse());
+
for (Client_ptr client : m_sticky_clients)
client->workspace = next_workspace;
@@ -1859,8 +1868,8 @@ Model::manage(const Window window, const bool ignore, const bool may_map)
m_client_map[frame] = client;
m_conn.insert_window_in_save_set(window);
- m_conn.init_window(window, false);
- m_conn.init_frame(frame, false);
+ m_conn.init_window(window);
+ m_conn.init_frame(frame, client->workspace->focus_follows_mouse());
m_conn.set_window_border_width(window, 0);
m_conn.set_window_desktop(window, client->workspace->index());
m_conn.set_icccm_window_state(window, IcccmWindowState::Normal);
@@ -2161,6 +2170,38 @@ Model::perform_resize(Pos& pos)
void
+Model::set_focus_follows_mouse(Toggle toggle, Index index)
+{
+ if (index < m_workspaces.size())
+ set_focus_follows_mouse(toggle, m_workspaces[index]);
+}
+
+void
+Model::set_focus_follows_mouse(Toggle toggle, Workspace_ptr workspace)
+{
+ bool focus_follows_mouse;
+
+ switch (toggle) {
+ case Toggle::On: focus_follows_mouse = true; break;
+ case Toggle::Off: focus_follows_mouse = false; break;
+ case Toggle::Reverse: focus_follows_mouse = !workspace->focus_follows_mouse(); break;
+ default: return;
+ }
+
+ if (workspace->focus_follows_mouse() != focus_follows_mouse) {
+ workspace->set_focus_follows_mouse(focus_follows_mouse);
+
+ for (Client_ptr client : *mp_workspace)
+ if (!client->sticky)
+ m_conn.set_window_notify_enter(client->frame, focus_follows_mouse);
+
+ for (Client_ptr client : m_sticky_clients)
+ m_conn.set_window_notify_enter(client->frame, focus_follows_mouse);
+ }
+}
+
+
+void
Model::apply_layout(Index index)
{
if (index < m_workspaces.size())
@@ -2526,6 +2567,9 @@ Model::move_client_to_workspace(Index index, Client_ptr client)
Workspace_ptr from = client->workspace;
Workspace_ptr to = get_workspace(index);
+ if (from->focus_follows_mouse() != to->focus_follows_mouse())
+ m_conn.set_window_notify_enter(client->frame, to->focus_follows_mouse());
+
client->workspace = to;
if (from == mp_workspace)
diff --git a/src/core/model.hh b/src/core/model.hh
@@ -131,6 +131,9 @@ private:
void perform_move(winsys::Pos&);
void perform_resize(winsys::Pos&);
+ void set_focus_follows_mouse(winsys::Toggle, Index);
+ void set_focus_follows_mouse(winsys::Toggle, Workspace_ptr);
+
void apply_layout(Index);
void apply_layout(Workspace_ptr);
diff --git a/src/core/workspace.cc b/src/core/workspace.cc
@@ -83,6 +83,19 @@ Workspace::contains(Client_ptr client) const
bool
+Workspace::focus_follows_mouse() const
+{
+ return m_focus_follows_mouse;
+}
+
+void
+Workspace::set_focus_follows_mouse(bool focus_follows_mouse)
+{
+ m_focus_follows_mouse = focus_follows_mouse;
+}
+
+
+bool
Workspace::layout_is_free() const
{
return m_layout_handler.layout_is_free();
diff --git a/src/core/workspace.hh b/src/core/workspace.hh
@@ -134,12 +134,16 @@ public:
mp_active(nullptr),
m_clients({}, true),
m_icons({}, true),
- m_disowned({}, true)
+ m_disowned({}, true),
+ m_focus_follows_mouse(false)
{}
bool empty() const;
bool contains(Client_ptr) const;
+ bool focus_follows_mouse() const;
+ void set_focus_follows_mouse(bool);
+
bool layout_is_free() const;
bool layout_has_margin() const;
bool layout_has_gap() const;
@@ -271,6 +275,8 @@ private:
Cycle<Client_ptr> m_icons;
Cycle<Client_ptr> m_disowned;
+ bool m_focus_follows_mouse;
+
}* Workspace_ptr;
#endif//__WORKSPACE_H_GUARD__
diff --git a/src/winsys/connection.hh b/src/winsys/connection.hh
@@ -42,7 +42,7 @@ namespace winsys
// window manipulation
virtual Window create_frame(Region) = 0;
- virtual void init_window(Window, bool) = 0;
+ virtual void init_window(Window) = 0;
virtual void init_frame(Window, bool) = 0;
virtual void init_unmanaged(Window) = 0;
virtual void init_move(Window) = 0;
@@ -69,6 +69,7 @@ namespace winsys
virtual void set_window_border_width(Window, unsigned) = 0;
virtual void set_window_border_color(Window, unsigned) = 0;
virtual void set_window_background_color(Window, unsigned) = 0;
+ virtual void set_window_notify_enter(Window, bool) = 0;
virtual void update_window_offset(Window, Window) = 0;
virtual Window get_focused_window() = 0;
virtual std::optional<Region> get_window_geometry(Window) = 0;
diff --git a/src/winsys/event.hh b/src/winsys/event.hh
@@ -53,8 +53,6 @@ namespace winsys
struct EnterEvent final
{
Window window;
- Pos root_rpos;
- Pos window_rpos;
};
struct LeaveEvent final
diff --git a/src/winsys/xdata/xconnection.cc b/src/winsys/xdata/xconnection.cc
@@ -114,6 +114,7 @@ XConnection::XConnection(const std::string_view wm_name)
m_event_dispatcher[DestroyNotify] = &XConnection::on_destroy_notify;
m_event_dispatcher[Expose] = &XConnection::on_expose;
m_event_dispatcher[FocusIn] = &XConnection::on_focus_in;
+ m_event_dispatcher[EnterNotify] = &XConnection::on_enter_notify;
m_event_dispatcher[KeyPress] = &XConnection::on_key_press;
m_event_dispatcher[MapNotify] = &XConnection::on_map_notify;
m_event_dispatcher[MapRequest] = &XConnection::on_map_request;
@@ -578,7 +579,7 @@ XConnection::create_frame(winsys::Region region)
}
void
-XConnection::init_window(winsys::Window window, bool)
+XConnection::init_window(winsys::Window window)
{
static const long window_event_mask
= PropertyChangeMask | StructureNotifyMask | FocusChangeMask;
@@ -913,6 +914,22 @@ XConnection::set_window_background_color(winsys::Window window, unsigned color)
}
void
+XConnection::set_window_notify_enter(winsys::Window window, bool notify_enter)
+{
+ static const long frame_event_mask
+ = StructureNotifyMask | SubstructureNotifyMask | SubstructureRedirectMask
+ | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
+
+ XSetWindowAttributes wa;
+ wa.event_mask = frame_event_mask;
+
+ if (notify_enter)
+ wa.event_mask |= EnterWindowMask;
+
+ XChangeWindowAttributes(mp_dpy, window, CWEventMask, &wa);
+}
+
+void
XConnection::update_window_offset(winsys::Window window, winsys::Window frame)
{
XWindowAttributes fa;
@@ -3339,6 +3356,21 @@ XConnection::on_focus_in()
}
winsys::Event
+XConnection::on_enter_notify()
+{
+ XKeyEvent event = m_current_event.xkey;
+ winsys::Window window = event.window;
+ winsys::Window subwindow = event.subwindow;
+
+ if (window == m_root || window == None)
+ window = subwindow;
+
+ return winsys::EnterEvent {
+ window
+ };
+}
+
+winsys::Event
XConnection::on_key_press()
{
XKeyEvent event = m_current_event.xkey;
diff --git a/src/winsys/xdata/xconnection.hh b/src/winsys/xdata/xconnection.hh
@@ -45,7 +45,7 @@ public:
// window manipulation
virtual winsys::Window create_frame(winsys::Region) override;
- virtual void init_window(winsys::Window, bool) override;
+ virtual void init_window(winsys::Window) override;
virtual void init_frame(winsys::Window, bool) override;
virtual void init_unmanaged(winsys::Window) override;
virtual void init_move(winsys::Window) override;
@@ -72,6 +72,7 @@ public:
virtual void set_window_border_width(winsys::Window, unsigned) override;
virtual void set_window_border_color(winsys::Window, unsigned) override;
virtual void set_window_background_color(winsys::Window, unsigned) override;
+ virtual void set_window_notify_enter(Window, bool) override;
virtual void update_window_offset(winsys::Window, winsys::Window) override;
virtual winsys::Window get_focused_window() override;
virtual std::optional<winsys::Region> get_window_geometry(winsys::Window) override;
@@ -321,6 +322,7 @@ private:
winsys::Event on_destroy_notify();
winsys::Event on_expose();
winsys::Event on_focus_in();
+ winsys::Event on_enter_notify();
winsys::Event on_key_press();
winsys::Event on_map_notify();
winsys::Event on_map_request();