From abd7105c8e33e03aa6ad81117ce1ea2730d225de Mon Sep 17 00:00:00 2001 From: RedBearAK <64876997+RedBearAK@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:18:55 -0800 Subject: [PATCH 1/5] Window closed event comes from foreign protocol --- .../query_cosmic_topl_mgmt_protocol.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/wlroots-dev/query_cosmic_topl_mgmt_protocol.py b/wlroots-dev/query_cosmic_topl_mgmt_protocol.py index 11a867d..356daab 100755 --- a/wlroots-dev/query_cosmic_topl_mgmt_protocol.py +++ b/wlroots-dev/query_cosmic_topl_mgmt_protocol.py @@ -86,7 +86,7 @@ def cleanup(self): def print_running_applications(self): """Print a complete list of running applications.""" - print("\nFull list of running application classes (not windows):") + print("\nFull list of open application windows:") print(f"{'App ID':<30} {'Title':<50}") print("-" * 80) for handle, info in self.wdw_handles_dct.items(): @@ -112,19 +112,24 @@ def handle_app_id_change(self, handle, app_id): def handle_window_closed(self, handle): """Remove window from local state.""" - foreign_handle: ExtForeignToplevelHandleV1 = None if self.cosmic_protocol_ver >= 2: - if handle in self.cosmic_to_foreign_map: - foreign_handle = self.cosmic_to_foreign_map.pop(handle, None) - if foreign_handle and foreign_handle in self.wdw_handles_dct: - del self.wdw_handles_dct[foreign_handle] - print(f"Window {foreign_handle} has been closed.") + # Using list() as a wrapper creates a copy of dictionary.items() (which + # produces a dynamic "view object") to iterate over without risking an + # iteration error from modifying the dictionary underlying the items() + # view object. + for cosmic_handle, foreign_handle in list(self.cosmic_to_foreign_map.items()): + if foreign_handle is handle: + del self.cosmic_to_foreign_map[cosmic_handle] + break + if handle and handle in self.wdw_handles_dct: + del self.wdw_handles_dct[handle] + print(f"Window has been closed:\n {handle}") elif self.cosmic_protocol_ver == 1: - if handle in self.wdw_handles_dct: + if handle and handle in self.wdw_handles_dct: del self.wdw_handles_dct[handle] - print(f"Window {handle} has been closed.") + print(f"Window has been closed:\n {handle}") def handle_state_change(self, handle, states_bytes): """Track active window app class and title based on state changes.""" @@ -209,16 +214,16 @@ def handle_toplevel_event_v2(self, self.cosmic_to_foreign_map[cosmic_toplvl_handle] = foreign_toplvl_handle # Event listeners for the foreign toplevel handle - # (cosmic toplevel handle will never emit these) + # (cosmic toplevel handle will never emit these events now, they are deprecated since v2) foreign_toplvl_handle.dispatcher['title'] = self.handle_title_change foreign_toplvl_handle.dispatcher['app_id'] = self.handle_app_id_change + foreign_toplvl_handle.dispatcher['closed'] = self.handle_window_closed # Event listeners for v2 cosmic toplevel handle - # (foreign toplevel handle does not have these events) - cosmic_toplvl_handle.dispatcher['closed'] = self.handle_window_closed + # (foreign toplevel handle does not have the state events) cosmic_toplvl_handle.dispatcher['state'] = self.handle_state_change - self.display.roundtrip() + # self.display.roundtrip() # Not sure if this is necessary or helpful here except KeyError as e: print(f"Error sending get_cosmic_toplevel request: {e}") From e97f7c374d91bef69f2738d0668bd8a73f71f63a Mon Sep 17 00:00:00 2001 From: RedBearAK <64876997+RedBearAK@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:04:02 -0800 Subject: [PATCH 2/5] Fix incorrect "window closed" event handling --- .../toshy_cosmic_dbus_service.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/cosmic-dbus-service/toshy_cosmic_dbus_service.py b/cosmic-dbus-service/toshy_cosmic_dbus_service.py index 6ff1c43..837d2b7 100755 --- a/cosmic-dbus-service/toshy_cosmic_dbus_service.py +++ b/cosmic-dbus-service/toshy_cosmic_dbus_service.py @@ -252,16 +252,16 @@ def handle_toplevel_event_v2(self, self.cosmic_to_foreign_map[cosmic_toplvl_handle] = foreign_toplvl_handle # Event listeners for the foreign toplevel handle - # (cosmic toplevel handle will never emit these) + # (cosmic toplevel handle will never emit these events now, they are deprecated since v2) foreign_toplvl_handle.dispatcher['title'] = self.handle_title_change foreign_toplvl_handle.dispatcher['app_id'] = self.handle_app_id_change + foreign_toplvl_handle.dispatcher['closed'] = self.handle_window_closed # Event listeners for v2 cosmic toplevel handle - # (foreign toplevel handle does not have these events) - cosmic_toplvl_handle.dispatcher['closed'] = self.handle_window_closed + # (foreign toplevel handle does not have the state events) cosmic_toplvl_handle.dispatcher['state'] = self.handle_state_change - self.display.roundtrip() + # self.display.roundtrip() # Not sure if this is necessary or helpful here except KeyError as e: print(f"Error sending get_cosmic_toplevel request: {e}") @@ -286,19 +286,24 @@ def handle_title_change(self, handle, title): def handle_window_closed(self, handle): """Remove window from local state.""" - foreign_handle: ExtForeignToplevelHandleV1 = None if self.cosmic_protocol_ver >= 2: - if handle in self.cosmic_to_foreign_map: - foreign_handle = self.cosmic_to_foreign_map.pop(handle, None) - if foreign_handle and foreign_handle in self.wdw_handles_dct: - del self.wdw_handles_dct[foreign_handle] - print(f"Window {foreign_handle} has been closed.") + # Using list() as a wrapper creates a copy of dictionary.items() (which + # produces a dynamic "view object") to iterate over without risking an + # iteration error from modifying the dictionary underlying the items() + # view object. + for cosmic_handle, foreign_handle in list(self.cosmic_to_foreign_map.items()): + if foreign_handle is handle: + del self.cosmic_to_foreign_map[cosmic_handle] + break + if handle and handle in self.wdw_handles_dct: + del self.wdw_handles_dct[handle] + print(f"Window has been closed:\n {handle}") elif self.cosmic_protocol_ver == 1: - if handle in self.wdw_handles_dct: + if handle and handle in self.wdw_handles_dct: del self.wdw_handles_dct[handle] - print(f"Window {handle} has been closed.") + print(f"Window has been closed:\n {handle}") def handle_state_change(self, handle, states_bytes): """Track active window app class and title based on state changes.""" From 73dff51b3613b05d7379813a7db7b0c9e7e0216a Mon Sep 17 00:00:00 2001 From: RedBearAK <64876997+RedBearAK@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:11:36 -0800 Subject: [PATCH 3/5] Version bump COSMIC D-Bus service --- cosmic-dbus-service/toshy_cosmic_dbus_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cosmic-dbus-service/toshy_cosmic_dbus_service.py b/cosmic-dbus-service/toshy_cosmic_dbus_service.py index 837d2b7..b981d93 100755 --- a/cosmic-dbus-service/toshy_cosmic_dbus_service.py +++ b/cosmic-dbus-service/toshy_cosmic_dbus_service.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -__version__ = '20241003' +__version__ = '20241005' # Reference for generating the protocol modules with pywayland scanner: # https://github.com/flacjacket/pywayland/issues/8#issuecomment-987040284 From 02a2fe74d8116bc64082be5464f46b48bff74879 Mon Sep 17 00:00:00 2001 From: RedBearAK <64876997+RedBearAK@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:32:00 -0800 Subject: [PATCH 4/5] Differentiate and print filtered state events --- wlroots-dev/query_cosmic_topl_mgmt_protocol.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wlroots-dev/query_cosmic_topl_mgmt_protocol.py b/wlroots-dev/query_cosmic_topl_mgmt_protocol.py index 356daab..c1dd361 100755 --- a/wlroots-dev/query_cosmic_topl_mgmt_protocol.py +++ b/wlroots-dev/query_cosmic_topl_mgmt_protocol.py @@ -134,11 +134,15 @@ def handle_window_closed(self, handle): def handle_state_change(self, handle, states_bytes): """Track active window app class and title based on state changes.""" - # Filter out empty state updates (why do these even happen?) + # Filter out empty state events (why do these even happen?) # Filter out the all-zeroes state (reset event?) before converting - if not states_bytes or states_bytes == b'\x00\x00\x00\x00': + if states_bytes == b'': print() - print("Received empty bytes value or all-zeroes 4-byte state event, ignoring.") + print(f"Received empty bytes value state event, ignoring: {states_bytes}") + return + elif states_bytes == b'\x00\x00\x00\x00': + print() + print(f"Received all-zeroes 4-byte state event, ignoring: {states_bytes}") return # Process states_bytes as an array of 4-byte (32-bit) integers From 029bfcc12ce5cb756c3fa0fb89e2e701f2977f46 Mon Sep 17 00:00:00 2001 From: RedBearAK <64876997+RedBearAK@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:45:11 -0800 Subject: [PATCH 5/5] Visually tag filtered state events in log output --- wlroots-dev/query_cosmic_topl_mgmt_protocol.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wlroots-dev/query_cosmic_topl_mgmt_protocol.py b/wlroots-dev/query_cosmic_topl_mgmt_protocol.py index c1dd361..40338d1 100755 --- a/wlroots-dev/query_cosmic_topl_mgmt_protocol.py +++ b/wlroots-dev/query_cosmic_topl_mgmt_protocol.py @@ -138,11 +138,11 @@ def handle_state_change(self, handle, states_bytes): # Filter out the all-zeroes state (reset event?) before converting if states_bytes == b'': print() - print(f"Received empty bytes value state event, ignoring: {states_bytes}") + print(f">>>> Received empty bytes value state event, ignoring: {states_bytes}") return elif states_bytes == b'\x00\x00\x00\x00': print() - print(f"Received all-zeroes 4-byte state event, ignoring: {states_bytes}") + print(f">>>> Received all-zeroes 4-byte state event, ignoring: {states_bytes}") return # Process states_bytes as an array of 4-byte (32-bit) integers