Skip to content

Linux: mouse click events other than primary, secondary and tertiary are not recognized #152128

Open

Description

@Feichtmeier

Steps to reproduce

  1. create a Linux Flutter app with
flutter create --platforms=linux --org com.test my_app
  1. Create an implementation of BaseTapGestureRecognizer
class NthTap extends BaseTapGestureRecognizer {
  GestureTapDownCallback? onTapDown;

  NthTap({
    super.debugOwner,
    super.supportedDevices,
    super.allowedButtonsFilter,
  });

  @override
  void handleTapCancel({
    required PointerDownEvent down,
    PointerCancelEvent? cancel,
    required String reason,
  }) {}

  @override
  void handleTapDown({required PointerDownEvent down}) {
    final TapDownDetails details = TapDownDetails(
      globalPosition: down.position,
      localPosition: down.localPosition,
      kind: getKindForPointer(down.pointer),
    );

    if (onTapDown != null) {
      invokeCallback<void>('onTapDown', () => onTapDown!(details));
    }
  }

  @override
  void handleTapUp({
    required PointerDownEvent down,
    required PointerUpEvent up,
  }) {}
}
  1. Use it inside the widget tree
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: <Type, GestureRecognizerFactory>{
        NthTap: GestureRecognizerFactoryWithHandlers<NthTap>(
          () => NthTap(),
          (instance) =>
              instance.onTapDown = (details) => ScaffoldMessenger.of(context)
                ..clearSnackBars()
                ..showSnackBar(SnackBar(
                  content: Center(
                    child: Text(
                      details.globalPosition.toString(),
                    ),
                  ),
                  behavior: SnackBarBehavior.floating,
                )),
        ),
      },
      child: const Scaffold(),
    );
  }
}

Expected results

All mouse buttons should be recognized

Actual results

Only 1.,2. and 3. buttons are recognized. 4th and 5th arent

Code sample

Code sample
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: <Type, GestureRecognizerFactory>{
        NthTap: GestureRecognizerFactoryWithHandlers<NthTap>(
          () => NthTap(),
          (instance) =>
              instance.onTapDown = (details) => ScaffoldMessenger.of(context)
                ..clearSnackBars()
                ..showSnackBar(SnackBar(
                  content: Center(
                    child: Text(
                      details.globalPosition.toString(),
                    ),
                  ),
                  behavior: SnackBarBehavior.floating,
                )),
        ),
      },
      child: const Scaffold(),
    );
  }
}

class NthTap extends BaseTapGestureRecognizer {
  GestureTapDownCallback? onTapDown;

  NthTap({
    super.debugOwner,
    super.supportedDevices,
    super.allowedButtonsFilter,
  });

  @override
  void handleTapCancel({
    required PointerDownEvent down,
    PointerCancelEvent? cancel,
    required String reason,
  }) {}

  @override
  void handleTapDown({required PointerDownEvent down}) {
    final TapDownDetails details = TapDownDetails(
      globalPosition: down.position,
      localPosition: down.localPosition,
      kind: getKindForPointer(down.pointer),
    );

    if (onTapDown != null) {
      invokeCallback<void>('onTapDown', () => onTapDown!(details));
    }
  }

  @override
  void handleTapUp({
    required PointerDownEvent down,
    required PointerUpEvent up,
  }) {}
}

Screenshots or Video

Screenshots / Video demonstration
Bildschirmaufzeichnung.vom.2024-07-22.20-18-47.mp4

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
frederik@xps:~/my_app$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.19.5, on Ubuntu 24.04 LTS 6.8.0-31-generic, locale de_DE.UTF-8)
[!] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc2)
    ✗ No Java Development Kit (JDK) found; You must have the environment variable JAVA_HOME set and the java binary in your PATH. You can download the JDK from
      https://www.oracle.com/technetwork/java/javase/downloads/.
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[!] Android Studio (not installed)
[✓] VS Code (version 1.91.1)
[✓] Connected device (2 available)
[✓] Network resources

! Doctor found issues in 2 categories.

this also happens in 3.22.2 stable

Activity

  1. danagbemava-nc commented on Jul 23, 2024

    @danagbemava-nc
    Member

    Reproducible using the code sample provided above.

    I tested the code sample on web and it worked as expected. As pointed out in #115641 (comment), it appears that the engine does not forward the events.

    flutter doctor -v
    [!] Flutter (Channel stable, 3.22.3, on Ubuntu 24.04 LTS 6.8.0-38-generic, locale en_US.UTF-8)
        • Flutter version 3.22.3 on channel stable at /home/daniel/fvm/versions/stable
        ! Warning: `flutter` on your path resolves to /home/daniel/fvm/versions/master/bin/flutter, which is not inside your current Flutter SDK checkout at /home/daniel/fvm/versions/stable. Consider adding /home/daniel/fvm/versions/stable/bin to the front of your path.
        ! Warning: `dart` on your path resolves to /home/linuxbrew/.linuxbrew/Cellar/dart/3.2.2/libexec/bin/dart, which is not inside your current Flutter SDK checkout at /home/daniel/fvm/versions/stable. Consider adding /home/daniel/fvm/versions/stable/bin to the front of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision b0850beeb2 (6 days ago), 2024-07-16 21:43:41 -0700
        • Engine revision 235db911ba
        • Dart version 3.4.4
        • DevTools version 2.34.3
    
    [!] Flutter (Channel master, 3.24.0-1.0.pre.230, on Ubuntu 24.04 LTS 6.8.0-38-generic, locale en_US.UTF-8)
        • Flutter version 3.24.0-1.0.pre.230 on channel master at /home/daniel/fvm/versions/master
        ! Warning: `dart` on your path resolves to /home/linuxbrew/.linuxbrew/Cellar/dart/3.2.2/libexec/bin/dart, which is not inside your current Flutter SDK checkout at /home/daniel/fvm/versions/master. Consider adding /home/daniel/fvm/versions/master/bin to the front of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision ed470fd101 (25 minutes ago), 2024-07-23 00:37:30 -0700
        • Engine revision 0d32c2d195
        • Dart version 3.6.0 (build 3.6.0-71.0.dev)
        • DevTools version 2.37.1
    
  2. added
    engineflutter/engine related. See also e: labels.
    a: mouseIssues related to using a mouse or mouse support
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    team-linuxOwned by the Linux platform team
    platform-linuxBuilding on or for Linux specifically
    and removed
    in triagePresently being triaged by the triage team
    on Jul 23, 2024
  3. added
    P2Important issues not at the top of the work list
    triaged-linuxTriaged by the Linux platform team
    on Jul 23, 2024
  4. robert-ancell commented on Feb 18, 2025

    @robert-ancell
    Contributor

    Some research on this:

    • Flutter defines 5 mouse buttons, kPointerButtonMousePrimary, kPointerButtonMouseSecondary, kPointerButtonMouseMiddle, kPointerButtonMouseBack and kPointerButtonMouseForward in a bitfield.
    • For other mouse buttons, you append to the bitfield, i.e. button 6 is 1<<5.
    • GTK mouse button events only provide an integer for the button that is pressed. There are three definitions to map these values - GDK_BUTTON_PRIMARY, GDK_BUTTON_MIDDLE and GDK_BUTTON_SECONDARY.
    • In the GTK X11 device code buttons 4-7 are interpreted as scroll events, and are filtered out. Thus X11 mice will send events with buttons 1-3, and 8+.
    • In the GTK Wayland device code the button numbers are shifted so 4-7 is never sent, like in for X11 mice. Wayland devices use the Linux event codes, which has BTN_FORWARD mapped to 9 and BTN_BACK mapped to 10.
    • According to @hbatagelo the Windows mice events have both a physical and logical code for each button.

    Open questions:

    • Can we interpret the mouse buttons at all on X11, i.e. can we determine what back and forward is? Surely the button numbers are derived from the Linux event codes (needs investigation).
    • Do we send the GTK button codes as is, which given buttons 4-7 don't exist (hidden for scrolling) will never set kPointerButtonMouseBack and kPointerButtonMouseForward.
    • On Wayland, do we reverse the button shift to work out the Linux event code and then interpret those? Will this mean switching between X11 and Wayland changes buttons?
    • Do we only support buttons 1-3 on X11 and have enhanced functionality on Wayland only?
  5. robert-ancell commented on Feb 18, 2025

    @robert-ancell
    Contributor

    On my mouse (MX Anywhere 2) tested on Ubuntu 24.10

    • On Wayland the forward button is event 9, and back event 8.
    • Running xev inside the Wayland session (i.e. Xwayland) also uses the same button codes.
    • Running through Xephyr (X11 inside a window) back has 8, but forward doesn't generate an event (bug?)
    • On a full X11 session, xev shows the same 9 and 8 button codes.

    Confusingly if we reverse the Linux mapping for the Wayland codes we get button 9 mapping to BTN_EXTRA and button 8 mapping to BTN_SIDE.

  6. robert-ancell commented on Feb 18, 2025

    @robert-ancell
    Contributor

    I've found some prior art - Epiphany (a web browser) hard codes button 8 and 9 as back and forward.

  7. robert-ancell commented on Feb 18, 2025

    @robert-ancell
    Contributor

    I was originally looking in the Chromium source to see how it did forward/back. I was unable to find the Linux input code for this - if anyone knows please link it here!

  8. robert-ancell commented on Feb 28, 2025

    @robert-ancell
    Contributor

    Good confirmation - they map BTN_EXTRA to forward, which matches what I calculated. They also map BTN_FORWARD as well, so perhaps there is a case where some devices use different codes? Epiphany doesn't do this, so I expect it doesn't happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: mouseIssues related to using a mouse or mouse supportengineflutter/engine related. See also e: labels.found in release: 3.22Found to occur in 3.22found in release: 3.24Found to occur in 3.24has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-linuxBuilding on or for Linux specificallyteam-linuxOwned by the Linux platform teamtriaged-linuxTriaged by the Linux platform team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions