Skip to content

Commit 06b6d8b

Browse files
committed
Pause mouse updates when resizing focused application
1 parent 91ca673 commit 06b6d8b

File tree

5 files changed

+106
-36
lines changed

5 files changed

+106
-36
lines changed

mousetracks2/components/processing.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from ..utils.monitor import MonitorData
2525
from ..utils.input import get_cursor_pos
2626
from ..utils.interface import Interfaces
27-
from ..utils.system import hide_child_process
27+
from ..utils.system import hide_child_process, UserResizeAppListener
2828
from ..constants import DEFAULT_PROFILE_NAME, UPDATES_PER_SECOND, DOUBLE_CLICK_MS, DOUBLE_CLICK_TOL, RADIAL_ARRAY_SIZE, DEBUG
2929
from ..render import render, EmptyRenderError, LayerBlend
3030

@@ -69,6 +69,9 @@ def __post_init__(self) -> None:
6969
self._current_application = Application('', RectList())
7070
self.current_application = Application(DEFAULT_PROFILE_NAME, RectList())
7171

72+
self._resize_listener = UserResizeAppListener()
73+
self._resize_listener.start()
74+
7275
@property
7376
def timestamp(self) -> int:
7477
"""Get the timestamp."""
@@ -166,6 +169,13 @@ def profile_age_days(self) -> int:
166169
current_day = self.timestamp // 86400
167170
return max(0, current_day - creation_day)
168171

172+
@property
173+
def app_resizing(self) -> bool:
174+
"""Determine if the focused application is being resized."""
175+
if self.current_application.name == DEFAULT_PROFILE_NAME:
176+
return False
177+
return self._resize_listener.triggered
178+
169179
def _monitor_offset(self, pixel: tuple[int, int]) -> tuple[tuple[int, int], tuple[int, int]] | None:
170180
"""Detect which monitor the pixel is on."""
171181
monitors = self.monitor_data.physical
@@ -617,14 +627,14 @@ def _process_message(self, message: ipc.Message) -> None:
617627
print('[Processing] Render request completed')
618628

619629
case ipc.MouseMove():
620-
if not self.profile.config.track_mouse:
630+
if not self.profile.config.track_mouse or self.app_resizing:
621631
return
622632

623633
distance = self._record_move(self.profile.cursor_map, message.position)
624634
self.profile.daily_distance[self.profile_age_days] += distance
625635

626636
case ipc.MouseHeld():
627-
if not self.profile.config.track_mouse:
637+
if not self.profile.config.track_mouse or self.app_resizing:
628638
return
629639

630640
result = self._monitor_offset(message.position)

mousetracks2/gui/main_window.py

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from ..utils.math import calculate_line, calculate_distance
3939
from ..utils.monitor import MonitorData
4040
from ..utils.system import SUPPORTS_TRAY, set_autostart, remove_autostart, split_autostart
41+
from ..utils.system import UserResizeAppListener
4142
from ..utils.update import is_latest_version, background_update
4243

4344
if TYPE_CHECKING:
@@ -193,6 +194,7 @@ def __init__(self, component: GUI) -> None:
193194
self.component = component
194195
self.config = GlobalConfig()
195196

197+
# Set initial states
196198
self.pause_redraw = 0
197199
self.pause_colour_change = False
198200
self._pixel_redraw_queue: list[tuple[tuple[int, int] | None, tuple[int, int] | None, tuple[int, int] | None]] = []
@@ -220,6 +222,10 @@ def __init__(self, component: GUI) -> None:
220222
self._network_speed = NetworkSpeedStats()
221223
self.state = ipc.TrackingState.Paused
222224

225+
# Setup threads
226+
self._resize_listener = UserResizeAppListener()
227+
self._resize_listener.start()
228+
223229
# Setup UI
224230
self.ui = layout.Ui_MainWindow()
225231
self.ui.setupUi(self)
@@ -853,7 +859,7 @@ def resolutions(self, resolutions: dict[tuple[int, int], tuple[int, bool]]) -> N
853859
def resolution_toggled(self, value: bool) -> None:
854860
"""Toggle rendering of a particular resolution in a profile."""
855861
checkbox = cast(QtWidgets.QCheckBox, self.sender())
856-
sanitised_profile_name, profile_name = self._get_profile_data()
862+
sanitised_profile_name, profile_name = self._selected_profile_data()
857863
if sanitised_profile_name is None or profile_name is None:
858864
return
859865
width, height = map(int, checkbox.text().split('x'))
@@ -875,7 +881,7 @@ def multi_monitor_change(self) -> None:
875881
"""Change the multiple monitor option."""
876882
if not self.ui.opts_monitor.isChecked():
877883
return
878-
sanitised_profile_name, profile_name = self._get_profile_data()
884+
sanitised_profile_name, profile_name = self._selected_profile_data()
879885
if sanitised_profile_name is None:
880886
return
881887
self.component.send_data(ipc.ToggleProfileMultiMonitor(sanitised_profile_name, self.ui.multi_monitor.isChecked()))
@@ -885,7 +891,7 @@ def multi_monitor_override_toggle(self, checked: bool) -> None:
885891
"""Enable or disable the multi monitor override."""
886892
if not self.ui.opts_monitor.isEnabled():
887893
return
888-
sanitised_profile_name, profile_name = self._get_profile_data()
894+
sanitised_profile_name, profile_name = self._selected_profile_data()
889895
if sanitised_profile_name is None:
890896
return
891897
self.component.send_data(ipc.ToggleProfileMultiMonitor(sanitised_profile_name, self.ui.multi_monitor.isChecked() if checked else None))
@@ -1030,7 +1036,7 @@ def manual_save_profile(self) -> None:
10301036
return
10311037
self.save_profile_request_sent = True
10321038

1033-
sanitised_profile_name, profile_name = self._get_profile_data()
1039+
sanitised_profile_name, profile_name = self._selected_profile_data()
10341040
if sanitised_profile_name is not None:
10351041
self.component.send_data(ipc.Save(sanitised_profile_name))
10361042

@@ -1042,7 +1048,7 @@ def toggle_autosave(self, state: QtCore.Qt.CheckState) -> None:
10421048
@QtCore.Slot(int)
10431049
def profile_changed(self, idx: int) -> None:
10441050
"""Change the profile and trigger a redraw."""
1045-
sanitised_profile_name, profile_name = self._get_profile_data(idx)
1051+
sanitised_profile_name, profile_name = self._selected_profile_data(idx)
10461052

10471053
if sanitised_profile_name is not None and not self._redrawing_profiles:
10481054
self.request_profile_data(sanitised_profile_name)
@@ -1051,7 +1057,7 @@ def profile_changed(self, idx: int) -> None:
10511057
self.set_profile_modified_text()
10521058

10531059
def request_profile_data(self, sanitised_profile_name: str) -> None:
1054-
"""Request loading profile data."""
1060+
"""Request loading profile data from the background process."""
10551061
self.component.send_data(ipc.ProfileDataRequest(sanitised_profile_name,
10561062
self._profile_names[sanitised_profile_name]))
10571063

@@ -1069,8 +1075,11 @@ def request_profile_data(self, sanitised_profile_name: str) -> None:
10691075
self._is_loading_profile += 1
10701076
self.start_rendering_timer()
10711077

1072-
def _get_profile_data(self, idx: int | None = None) -> tuple[str, str] | tuple[None, None]:
1073-
"""Get the selected profile name from the combobox."""
1078+
def _selected_profile_data(self, idx: int | None = None) -> tuple[str, str] | tuple[None, None]:
1079+
"""Get the selected profile name from the combobox.
1080+
1081+
Returns the sanitised profile name and actual name.
1082+
"""
10741083
if idx is None:
10751084
sanitised_profile_name = self.ui.current_profile.currentData()
10761085
else:
@@ -1309,7 +1318,7 @@ def _request_thumbnail(self) -> bool:
13091318

13101319
width = self.ui.thumbnail.width()
13111320
height = self.ui.thumbnail.height()
1312-
sanitised_profile_name, profile_name = self._get_profile_data()
1321+
sanitised_profile_name, profile_name = self._selected_profile_data()
13131322
if sanitised_profile_name is None:
13141323
return False
13151324

@@ -1346,7 +1355,7 @@ def _request_thumbnail(self) -> bool:
13461355
return True
13471356

13481357
def get_render_layer_data(self, file_path: str | None = None) -> Iterator[ipc.RenderLayer]:
1349-
sanitised_profile_name, profile_name = self._get_profile_data()
1358+
sanitised_profile_name, profile_name = self._selected_profile_data()
13501359
if sanitised_profile_name is None:
13511360
return
13521361

@@ -1473,7 +1482,7 @@ def request_render(self) -> None:
14731482
case _:
14741483
name = 'Data'
14751484

1476-
sanitised_profile_name, profile_name = self._get_profile_data()
1485+
sanitised_profile_name, profile_name = self._selected_profile_data()
14771486
if sanitised_profile_name is None or profile_name is None:
14781487
return
14791488
profile_safe = re.sub(r'[^\w_.)( -]', '', profile_name)
@@ -1686,11 +1695,11 @@ def _process_message(self, message: ipc.Message) -> None:
16861695
im.save(message.request.file_path)
16871696
os.startfile(message.request.file_path)
16881697

1689-
case ipc.MouseHeld() if self.is_live and self.mouse_tracking_enabled:
1698+
case ipc.MouseHeld() if self.is_live and self.mouse_tracking_enabled and not self.app_resizing:
16901699
self.mouse_held_count += 1
16911700

16921701
case ipc.MouseMove() if self.is_live and self.mouse_tracking_enabled:
1693-
if self.render_type == ipc.RenderType.MouseMovement:
1702+
if self.render_type == ipc.RenderType.MouseMovement and not self.app_resizing:
16941703
self.draw_pixmap_line(message.position, self.cursor_data.position)
16951704
self.update_track_data(self.cursor_data, message.position)
16961705
self.ui.stat_distance.setText(format_distance(self.cursor_data.distance))
@@ -2273,11 +2282,18 @@ def update_track_data(self, data: MapData, position: tuple[int, int]) -> None:
22732282
@property
22742283
def is_live(self) -> bool:
22752284
"""Determine if the visible data is live."""
2276-
sanitised_profile_name, profile_name = self._get_profile_data()
2285+
sanitised_profile_name, profile_name = self._selected_profile_data()
22772286
if sanitised_profile_name is None:
22782287
return False
22792288
return sanitised_profile_name == sanitise_profile_name(self.current_profile.name)
22802289

2290+
@property
2291+
def app_resizing(self) -> bool:
2292+
"""Determine if the focused application is being resized."""
2293+
if self.current_profile.name == DEFAULT_PROFILE_NAME:
2294+
return False
2295+
return self._resize_listener.triggered
2296+
22812297
@property
22822298
def mouse_tracking_enabled(self) -> bool:
22832299
"""Determine if mouse tracking is enabled."""
@@ -2361,7 +2377,7 @@ def update_thumbnail_size(self) -> None:
23612377

23622378
def delete_mouse(self) -> None:
23632379
"""Request deletion of mouse data for the current profile."""
2364-
sanitised_profile_name, profile_name = self._get_profile_data()
2380+
sanitised_profile_name, profile_name = self._selected_profile_data()
23652381
if sanitised_profile_name is None or profile_name is None:
23662382
return None
23672383

@@ -2385,7 +2401,7 @@ def delete_mouse(self) -> None:
23852401

23862402
def delete_keyboard(self) -> None:
23872403
"""Request deletion of keyboard data for the current profile."""
2388-
sanitised_profile_name, profile_name = self._get_profile_data()
2404+
sanitised_profile_name, profile_name = self._selected_profile_data()
23892405
if sanitised_profile_name is None or profile_name is None:
23902406
return None
23912407

@@ -2408,7 +2424,7 @@ def delete_keyboard(self) -> None:
24082424

24092425
def delete_gamepad(self) -> None:
24102426
"""Request deletion of gamepad data for the current profile."""
2411-
sanitised_profile_name, profile_name = self._get_profile_data()
2427+
sanitised_profile_name, profile_name = self._selected_profile_data()
24122428
if sanitised_profile_name is None or profile_name is None:
24132429
return None
24142430

@@ -2432,7 +2448,7 @@ def delete_gamepad(self) -> None:
24322448

24332449
def delete_network(self) -> None:
24342450
"""Request deletion of network data for the current profile."""
2435-
sanitised_profile_name, profile_name = self._get_profile_data()
2451+
sanitised_profile_name, profile_name = self._selected_profile_data()
24362452
if sanitised_profile_name is None or profile_name is None:
24372453
return None
24382454

@@ -2455,7 +2471,7 @@ def delete_network(self) -> None:
24552471

24562472
def delete_profile(self) -> None:
24572473
"""Delete the selected profile."""
2458-
sanitised_profile_name, profile_name = self._get_profile_data()
2474+
sanitised_profile_name, profile_name = self._selected_profile_data()
24592475
if sanitised_profile_name is None or profile_name is None:
24602476
return None
24612477

@@ -2484,7 +2500,7 @@ def delete_profile(self) -> None:
24842500
def toggle_profile_mouse_tracking(self, state: QtCore.Qt.CheckState) -> None:
24852501
if not self.ui.track_mouse.isEnabled():
24862502
return
2487-
sanitised_profile_name, profile_name = self._get_profile_data()
2503+
sanitised_profile_name, profile_name = self._selected_profile_data()
24882504
if sanitised_profile_name is None:
24892505
return
24902506

@@ -2495,7 +2511,7 @@ def toggle_profile_mouse_tracking(self, state: QtCore.Qt.CheckState) -> None:
24952511
def toggle_profile_keyboard_tracking(self, state: QtCore.Qt.CheckState) -> None:
24962512
if not self.ui.track_keyboard.isEnabled():
24972513
return
2498-
sanitised_profile_name, profile_name = self._get_profile_data()
2514+
sanitised_profile_name, profile_name = self._selected_profile_data()
24992515
if sanitised_profile_name is None:
25002516
return
25012517

@@ -2506,7 +2522,7 @@ def toggle_profile_keyboard_tracking(self, state: QtCore.Qt.CheckState) -> None:
25062522
def toggle_profile_gamepad_tracking(self, state: QtCore.Qt.CheckState) -> None:
25072523
if not self.ui.track_gamepad.isEnabled():
25082524
return
2509-
sanitised_profile_name, profile_name = self._get_profile_data()
2525+
sanitised_profile_name, profile_name = self._selected_profile_data()
25102526
if sanitised_profile_name is None:
25112527
return
25122528

@@ -2517,7 +2533,7 @@ def toggle_profile_gamepad_tracking(self, state: QtCore.Qt.CheckState) -> None:
25172533
def toggle_profile_network_tracking(self, state: QtCore.Qt.CheckState) -> None:
25182534
if not self.ui.track_network.isEnabled():
25192535
return
2520-
sanitised_profile_name, profile_name = self._get_profile_data()
2536+
sanitised_profile_name, profile_name = self._selected_profile_data()
25212537
if sanitised_profile_name is None:
25222538
return
25232539

@@ -2572,7 +2588,7 @@ def mark_profiles_unsaved(self, *profile_names: str) -> None:
25722588

25732589
def set_profile_modified_text(self) -> None:
25742590
"""Set the text if the profile has been modified."""
2575-
sanitised_profile_name, profile_name = self._get_profile_data()
2591+
sanitised_profile_name, profile_name = self._selected_profile_data()
25762592
if sanitised_profile_name is not None and sanitised_profile_name in self._unsaved_profiles:
25772593
self.ui.profile_modified.setText('Yes')
25782594
self.ui.profile_save.setEnabled(self.state != ipc.TrackingState.Stopped)
@@ -2788,7 +2804,7 @@ def export_mouse_stats(self) -> None:
27882804
dialog.setNameFilters(['CSV Files (*.csv)"'])
27892805
dialog.setDefaultSuffix('csv')
27902806

2791-
sanitised_profile_name, profile_name = self._get_profile_data()
2807+
sanitised_profile_name, profile_name = self._selected_profile_data()
27922808
if sanitised_profile_name is None or profile_name is None:
27932809
return
27942810
profile_safe = re.sub(r'[^\w_.)( -]', '', profile_name)
@@ -2806,7 +2822,7 @@ def export_keyboard_stats(self) -> None:
28062822
dialog.setNameFilters(['CSV Files (*.csv)"'])
28072823
dialog.setDefaultSuffix('csv')
28082824

2809-
sanitised_profile_name, profile_name = self._get_profile_data()
2825+
sanitised_profile_name, profile_name = self._selected_profile_data()
28102826
if sanitised_profile_name is None or profile_name is None:
28112827
return
28122828
profile_safe = re.sub(r'[^\w_.)( -]', '', profile_name)
@@ -2824,7 +2840,7 @@ def export_gamepad_stats(self) -> None:
28242840
dialog.setNameFilters(['CSV Files (*.csv)"'])
28252841
dialog.setDefaultSuffix('csv')
28262842

2827-
sanitised_profile_name, profile_name = self._get_profile_data()
2843+
sanitised_profile_name, profile_name = self._selected_profile_data()
28282844
if sanitised_profile_name is None or profile_name is None:
28292845
return
28302846
profile_safe = re.sub(r'[^\w_.)( -]', '', profile_name)
@@ -2842,7 +2858,7 @@ def export_network_stats(self) -> None:
28422858
dialog.setNameFilters(['CSV Files (*.csv)"'])
28432859
dialog.setDefaultSuffix('csv')
28442860

2845-
sanitised_profile_name, profile_name = self._get_profile_data()
2861+
sanitised_profile_name, profile_name = self._selected_profile_data()
28462862
if sanitised_profile_name is None or profile_name is None:
28472863
return
28482864
profile_safe = re.sub(r'[^\w_.)( -]', '', profile_name)
@@ -2860,7 +2876,7 @@ def export_daily_stats(self) -> None:
28602876
dialog.setNameFilters(['CSV Files (*.csv)"'])
28612877
dialog.setDefaultSuffix('csv')
28622878

2863-
sanitised_profile_name, profile_name = self._get_profile_data()
2879+
sanitised_profile_name, profile_name = self._selected_profile_data()
28642880
if sanitised_profile_name is None or profile_name is None:
28652881
return
28662882
profile_safe = re.sub(r'[^\w_.)( -]', '', profile_name)

mousetracks2/utils/system/__init__.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
MonitorEventListener: Type[base.EventListener]
1212
ControllerEventListener: Type[base.EventListener]
1313
ForegroundAppListener: Type[base.EventListener]
14+
UserResizeAppListener: Type[base.EventListener]
1415

1516
match sys.platform:
1617
case 'win32':
@@ -19,7 +20,8 @@
1920
from .windows import get_autostart, set_autostart, remove_autostart
2021
from .windows import is_elevated, relaunch_as_elevated
2122
from .windows import Window
22-
from .windows import MonitorEventListener, ControllerEventListener, ForegroundAppListener
23+
from .windows import MonitorEventListener, ControllerEventListener
24+
from .windows import ForegroundAppListener, UserResizeAppListener
2325
from .base import hide_child_process
2426
from .windows import prepare_application_icon
2527
from .windows import update_installer_version_number
@@ -30,7 +32,8 @@
3032
from .macos import get_autostart, set_autostart, remove_autostart
3133
from .base import is_elevated, relaunch_as_elevated
3234
from .macos import Window
33-
from .base import MonitorEventListener, ControllerEventListener, ForegroundAppListener
35+
from .base import MonitorEventListener, ControllerEventListener
36+
from .base import ForegroundAppListener, UserResizeAppListener
3437
from .macos import hide_child_process, prepare_application_icon
3538
from .base import update_installer_version_number
3639

@@ -40,7 +43,8 @@
4043
from .linux import get_autostart, set_autostart, remove_autostart
4144
from .base import is_elevated, relaunch_as_elevated
4245
from .linux import Window
43-
from .base import MonitorEventListener, ControllerEventListener, ForegroundAppListener
46+
from .base import MonitorEventListener, ControllerEventListener
47+
from .base import ForegroundAppListener, UserResizeAppListener
4448
from .base import hide_child_process, prepare_application_icon
4549
from .base import update_installer_version_number
4650

@@ -50,7 +54,8 @@
5054
'get_autostart', 'set_autostart', 'remove_autostart', 'remap_autostart',
5155
'is_elevated', 'relaunch_as_elevated',
5256
'Window',
53-
'MonitorEventListener', 'ControllerEventListener', 'ForegroundAppListener',
57+
'MonitorEventListener', 'ControllerEventListener',
58+
'ForegroundAppListener', 'UserResizeAppListener',
5459
'hide_child_process', 'prepare_application_icon',
5560
'update_installer_version_number',
5661
]

0 commit comments

Comments
 (0)