kranewm

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in C++
git clone git clone git://git.deurzen.net/kranewm.git
Log | Files | Refs | LICENSE

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:
Msrc/core/model.cc | 48++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/core/model.hh | 3+++
Msrc/core/workspace.cc | 13+++++++++++++
Msrc/core/workspace.hh | 8+++++++-
Msrc/winsys/connection.hh | 3++-
Msrc/winsys/event.hh | 2--
Msrc/winsys/xdata/xconnection.cc | 34+++++++++++++++++++++++++++++++++-
Msrc/winsys/xdata/xconnection.hh | 4+++-
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();