Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support for Web #48

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/gamepads/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ flutter:
default_package: gamepads_darwin
windows:
default_package: gamepads_windows
web:
default_package: gamepads_web

environment:
sdk: '>=2.19.0 <3.0.0'
Expand All @@ -30,6 +32,7 @@ dependencies:
gamepads_ios: ^0.1.2+2
gamepads_linux: ^0.1.1+3
gamepads_platform_interface: ^0.1.2+1
gamepads_web: ^0.1.0
gamepads_windows: ^0.1.1+3

dev_dependencies:
Expand Down
29 changes: 29 additions & 0 deletions packages/gamepads_web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/
1 change: 1 addition & 0 deletions packages/gamepads_web/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../../LICENSE
1 change: 1 addition & 0 deletions packages/gamepads_web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../../README.md
1 change: 1 addition & 0 deletions packages/gamepads_web/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:flame_lint/analysis_options.yaml
27 changes: 27 additions & 0 deletions packages/gamepads_web/lib/gamepad_detector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'dart:js_interop';

import 'package:gamepads_platform_interface/api/gamepad_controller.dart';
import 'package:gamepads_platform_interface/gamepads_platform_interface.dart';
import 'package:web/web.dart';

List<GamepadController> getGamepads(GamepadsPlatformInterface plugin) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should live in src right? And not be exposed directly to the user, since it's not part of the interface.

final controllers = <GamepadController>[];
final gamepads = getGamepadList();
for (var i = 0; i < gamepads.length; i++) {
final gamepad = gamepads[i];
controllers.add(
GamepadController(
id: gamepad!.index.toString(),
name: gamepad.id,
plugin: plugin,
),
);
}
return controllers;
}

List<Gamepad?> getGamepadList() {
final gamepads = window.navigator.getGamepads().toDart;
gamepads.removeWhere((item) => item == null);
return gamepads;
}
132 changes: 132 additions & 0 deletions packages/gamepads_web/lib/gamepads_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import 'dart:async';
import 'dart:js_interop';

import 'package:flutter/foundation.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:gamepads_platform_interface/api/gamepad_controller.dart';
import 'package:gamepads_platform_interface/api/gamepad_event.dart';
import 'package:gamepads_platform_interface/gamepads_platform_interface.dart';
import 'package:gamepads_web/gamepad_detector.dart';
import 'package:web/web.dart' as web;

class _GamePadState {
_GamePadState(int length) {
keyStates = List<dynamic>.filled(length, null);
axesStates = List<double>.filled(4, 0);
}

List<dynamic>? keyStates;
List<double>? axesStates;
}

/// A web implementation of the GamepadsWebPlatform of the GamepadsWeb plugin.
class GamepadsWeb extends GamepadsPlatformInterface {
int _gamepadCount = 0;
Timer? _gamepadPollingTimer;

final Map<String, _GamePadState> _lastGamePadstates = {};

void updateGamepadsStatus() {
final gamepads = getGamepadList();
for (final gamepad in gamepads) {
final buttonlist = gamepad!.buttons.toDart;
final axeslist = gamepad.axes.toDart;
final gamepadId = gamepad.index.toString();
_GamePadState lastState;
if (_lastGamePadstates.containsKey(gamepadId) &&
_lastGamePadstates[gamepadId]?.keyStates?.length ==
buttonlist.length) {
lastState = _lastGamePadstates[gamepadId]!;
} else {
_lastGamePadstates[gamepadId] = _GamePadState(buttonlist.length);
lastState = _lastGamePadstates[gamepadId]!;
}
for (var i = 0; i < buttonlist.length; i++) {
if (lastState.keyStates?[i] != buttonlist[i].value) {
lastState.keyStates?[i] = buttonlist[i].value;
emitGamepadEvent(
GamepadEvent(
gamepadId: gamepadId,
timestamp: DateTime.now().millisecondsSinceEpoch,
type: KeyType.button,
key: 'button $i',
value: buttonlist[i].value,
),
);
}
}
for (var i = 0; i < 4; i++) {
if ((lastState.axesStates![i] - axeslist[i].toDartDouble).abs() >
0.03) {
lastState.axesStates?[i] = axeslist[i].toDartDouble;
emitGamepadEvent(
GamepadEvent(
gamepadId: gamepadId,
timestamp: DateTime.now().millisecondsSinceEpoch,
type: KeyType.analog,
key: 'analog $i',
value: axeslist[i].toDartDouble,
),
);
}
}
}
}

/// Constructs a GamepadsWeb
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Constructs a GamepadsWeb

The dart docs should talk about what it does, docs like "constructor constructs" doesn't give anything, so I think you can just remove this.

GamepadsWeb() {
web.window.addEventListener(
'gamepadconnected',
(web.Event event) {
_gamepadCount++;
if (_gamepadCount == 1) {
// The game pad state for web is not event driven. We need to
// query the game pad state by ourself.
// By default we set the query interval is 1ms.
_gamepadPollingTimer =
Timer.periodic(const Duration(milliseconds: 1), (timer) {
updateGamepadsStatus();
});
}
}.toJS,
);

web.window.addEventListener(
'gamepaddisconnected',
(web.Event event) {
_gamepadCount--;
if (_gamepadCount == 0) {
_gamepadPollingTimer?.cancel();
}
}.toJS,
);
}

static void registerWith(Registrar registrar) {
GamepadsPlatformInterface.instance = GamepadsWeb();
}

List<GamepadController>? controllers;

@override
Future<List<GamepadController>> listGamepads() async {

Check notice on line 112 in packages/gamepads_web/lib/gamepads_web.dart

View workflow job for this annotation

GitHub Actions / analyze

The method doesn't override an inherited method.

Try updating this class to match the superclass, or removing the override annotation. See https://dart.dev/diagnostics/override_on_non_overriding_member to learn more about this problem.
controllers = getGamepads(this);
return controllers!;
}

void emitGamepadEvent(GamepadEvent event) {
_gamepadEventsStreamController.add(event);
}

final StreamController<GamepadEvent> _gamepadEventsStreamController =
StreamController<GamepadEvent>.broadcast();

@override
Stream<GamepadEvent> get gamepadEventsStream =>

Check notice on line 125 in packages/gamepads_web/lib/gamepads_web.dart

View workflow job for this annotation

GitHub Actions / analyze

The getter doesn't override an inherited getter.

Try updating this class to match the superclass, or removing the override annotation. See https://dart.dev/diagnostics/override_on_non_overriding_member to learn more about this problem.
_gamepadEventsStreamController.stream;

@mustCallSuper
Future<void> dispose() async {
_gamepadEventsStreamController.close();
}
}
32 changes: 32 additions & 0 deletions packages/gamepads_web/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: gamepads_web
description: Web implementation of gamepads, a Flutter plugin to handle gamepad input across multiple platforms.
version: 0.1.0
homepage: https://github.com/flame-engine/gamepads
repository: https://github.com/flame-engine/gamepads/tree/main/packages/gamepads_web

flutter:
plugin:
implements: gamepads
platforms:
web:
pluginClass: GamepadsWeb
fileName: gamepads_web.dart

environment:
sdk: '>=2.19.0 <3.0.0'
flutter: ">=3.3.0"

dependencies:
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
gamepads_platform_interface: ^0.1.2+1
js_interop: ^0.0.1
plugin_platform_interface: ^2.1.8
web: ^1.1.0

dev_dependencies:
flame_lint: ^0.2.0
flutter_test:
sdk: flutter
Loading