@@ -52,9 +52,11 @@ abstract class BaseDevice {
5252
5353 static const Duration _longPressTriggerDelay = Duration (milliseconds: 550 );
5454 static const Duration _doubleClickDelay = Duration (milliseconds: 320 );
55+ static const Duration _repeatInterval = Duration (milliseconds: 150 );
5556
5657 Timer ? _longPressTimer;
5758 Timer ? _singleClickTimer;
59+ Timer ? _repeatTimer;
5860 Set <ControllerButton > _previouslyPressedButtons = < ControllerButton > {};
5961 Set <ControllerButton > _activeLongPressButtons = < ControllerButton > {};
6062 ControllerButton ? _pendingSingleClickButton;
@@ -149,6 +151,7 @@ abstract class BaseDevice {
149151 actionStreamInternal.add (LogNotification ('Buttons released' ));
150152
151153 _longPressTimer? .cancel ();
154+ _stopRepeatingSingleClick ();
152155 final releasedButtons = _previouslyPressedButtons.toList ();
153156 _previouslyPressedButtons.clear ();
154157
@@ -264,7 +267,35 @@ abstract class BaseDevice {
264267 if (keyPair == null && core.actionHandler.supportedApp == null ) {
265268 return trigger == ButtonTrigger .singleClick;
266269 }
267- return keyPair != null && ! keyPair.hasNoAction;
270+ if (keyPair != null && ! keyPair.hasNoAction) {
271+ return true ;
272+ }
273+ // Implicit repeat: long press repeats single click for Pro users by default.
274+ if (trigger == ButtonTrigger .longPress) {
275+ return _shouldRepeatSingleClick (button);
276+ }
277+ return false ;
278+ }
279+
280+ /// Whether a long press should implicitly repeat the single click action.
281+ /// Active by default for Pro users when no explicit long press action is set.
282+ bool _shouldRepeatSingleClick (ControllerButton button) {
283+ if (! supportsLongPress) return false ;
284+ try {
285+ if (! IAPManager .instance.isProEnabledForCurrentDevice) return false ;
286+ } catch (_) {
287+ return false ;
288+ }
289+ final longPressPair = core.actionHandler.supportedApp? .keymap.getKeyPair (
290+ button,
291+ trigger: ButtonTrigger .longPress,
292+ );
293+ if (longPressPair != null && ! longPressPair.hasNoAction) return false ;
294+ final singleClickPair = core.actionHandler.supportedApp? .keymap.getKeyPair (
295+ button,
296+ trigger: ButtonTrigger .singleClick,
297+ );
298+ return singleClickPair != null && ! singleClickPair.hasNoAction;
268299 }
269300
270301 void _cancelPendingClickTimers () {
@@ -309,6 +340,12 @@ abstract class BaseDevice {
309340 continue ;
310341 }
311342
343+ // Check for implicit repeat single click mode
344+ if (_shouldRepeatSingleClick (action)) {
345+ _startRepeatingSingleClick (action);
346+ continue ;
347+ }
348+
312349 // For repeated actions, don't trigger key down/up events (useful for long press)
313350 final result = await core.actionHandler.performAction (
314351 action,
@@ -323,6 +360,20 @@ abstract class BaseDevice {
323360 }
324361 }
325362
363+ void _startRepeatingSingleClick (ControllerButton button) {
364+ _repeatTimer? .cancel ();
365+ // Fire immediately, then repeat periodically
366+ unawaited (performClick ([button], trigger: ButtonTrigger .singleClick));
367+ _repeatTimer = Timer .periodic (_repeatInterval, (_) {
368+ unawaited (performClick ([button], trigger: ButtonTrigger .singleClick));
369+ });
370+ }
371+
372+ void _stopRepeatingSingleClick () {
373+ _repeatTimer? .cancel ();
374+ _repeatTimer = null ;
375+ }
376+
326377 Future <void > performClick (
327378 List <ControllerButton > buttonsClicked, {
328379 ButtonTrigger trigger = ButtonTrigger .singleClick,
@@ -350,7 +401,13 @@ abstract class BaseDevice {
350401 List <ControllerButton > buttonsReleased, {
351402 ButtonTrigger trigger = ButtonTrigger .longPress,
352403 }) async {
404+ _stopRepeatingSingleClick ();
353405 for (final action in buttonsReleased) {
406+ // Skip normal release handling for repeat-on-long-press buttons
407+ if (_shouldRepeatSingleClick (action)) {
408+ continue ;
409+ }
410+
354411 // Check IAP status before executing command
355412 if (! _canExecuteCommand ()) {
356413 _showCommandLimitAlert ();
@@ -371,6 +428,8 @@ abstract class BaseDevice {
371428 _longPressTimer? .cancel ();
372429 _singleClickTimer? .cancel ();
373430 _singleClickTimer = null ;
431+ _repeatTimer? .cancel ();
432+ _repeatTimer = null ;
374433 _pendingSingleClickButton = null ;
375434 // Release any held keys in long press mode
376435 if (core.actionHandler is DesktopActions ) {
0 commit comments