-
Notifications
You must be signed in to change notification settings - Fork 4
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
Add gamepad support #300
Add gamepad support #300
Changes from 29 commits
6089add
fa20fe0
44ba663
b4e2c04
9629ca4
b9175cb
4d7c61a
404b29c
a2274fe
a0271c3
8c2ffc9
9d25f5d
91598b0
ffc826e
812c7e6
d04d597
1cd464d
535f8f6
d247ce3
66e9506
79c68ac
8d09814
f0f7d05
31f810c
6e5cd9f
959c3c9
eec060d
943d0c6
f19fc75
f056bc7
38c36db
4bcfb9e
244226b
3c2f1f9
bab074b
c44e5bf
b1b31fb
157ee1e
ecb69c4
9c9c98d
acd9adc
d99b062
a4c0b92
3f33a80
7af68b1
64ae04f
83a39a3
ae0bfee
a51622e
2ad0443
dba5011
69a1b5f
a022b77
6a0b033
f11f593
c4a6ca0
3939534
4dd6394
727b99a
56dde71
6b1e3bd
0415532
1013c03
b5341df
b4a633b
9b79570
0025669
07cd128
5d29dfa
32a7518
4edce35
c35028e
5a12b7e
6187d6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// This file is part of GNOME Games. License: GPLv3 | ||
|
||
/** | ||
* This class provides a way to the client to monitor gamepads | ||
* | ||
* The client interfaces with this class primarily | ||
*/ | ||
public class LibGamepad.GamepadMonitor : Object { | ||
/** | ||
* Emitted when a gamepad is plugged in | ||
* @param gamepad The gamepad | ||
*/ | ||
public signal void gamepad_plugged (Gamepad gamepad); | ||
|
||
public delegate void GamepadCallback (Gamepad gamepad); | ||
|
||
private static GamepadMonitor instance; | ||
|
||
private SList <Gamepad> gamepads; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need order so it's better to use a GenericSet, also we don't put spaces between the generic type and <. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GenericSet needs a hash function, which I dont know how to use. would be helpful if you tell me that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just pass |
||
private RawGamepadMonitor raw_gamepad_monitor; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you made it a singleton you don't need to store it anymore: the instance will never be destroyed. |
||
|
||
private GamepadMonitor () { | ||
if (gamepads == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to check for |
||
gamepads = new SList <Gamepad> (); | ||
|
||
raw_gamepad_monitor = LinuxRawGamepadMonitor.get_instance (); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this line. |
||
raw_gamepad_monitor.gamepad_plugged.connect (on_raw_gamepad_plugged); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. |
||
raw_gamepad_monitor.foreach_gamepad ((raw_gamepad) => add_gamepad (raw_gamepad)); | ||
} | ||
|
||
public static GamepadMonitor get_instance () { | ||
if (instance == null) | ||
instance = new GamepadMonitor (); | ||
|
||
return instance; | ||
} | ||
|
||
/** | ||
* This function allows to iterate over all gamepads | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably obvious enough. From now on I'll only comment the plugins that I think should be removed. |
||
* @param callback The callback | ||
*/ | ||
public void foreach_gamepad (GamepadCallback callback) { | ||
gamepads.foreach ((gamepad) => callback (gamepad)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. foreach (var gamepad in gamepads) |
||
} | ||
|
||
private Gamepad add_gamepad (RawGamepad raw_gamepad) { | ||
var gamepad = new Gamepad (raw_gamepad); | ||
gamepads.append (gamepad); | ||
gamepad.unplugged.connect (() => gamepads.remove (gamepad)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't use lambdas as callbacks to signals. Create a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seriously! making a function for a one liner! Wow! I am amazed by your style guide |
||
|
||
return gamepad; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a new line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Newline. |
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we add the |
||
|
||
private void on_raw_gamepad_plugged (RawGamepad raw_gamepad) { | ||
gamepad_plugged (add_gamepad (raw_gamepad)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't chain calls, first add the gamepad then pass it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. damn looks like this feature doesnot exist in vala. lets code as if it were assembly |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// This file is part of GNOME Games. License: GPLv3 | ||
|
||
/** | ||
* This class represents a gamepad | ||
* | ||
* The client interfaces with this class primarily | ||
*/ | ||
public class LibGamepad.Gamepad : Object { | ||
|
||
/** | ||
* Emitted when a button is pressed/released | ||
* @param button The button pressed | ||
* @param value True if pressed, False if released | ||
*/ | ||
public signal void button_event (StandardGamepadButton button, bool value); | ||
|
||
/** | ||
* Emitted when an axis's value changes | ||
* @param axis The axis number from 0 to axes_number | ||
* @param value The value of the axis ranging from -1 to 1 | ||
*/ | ||
public signal void axis_event (StandardGamepadAxis axis, double value); | ||
|
||
/** | ||
* Emitted when the gamepad is unplugged | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably not hyper useful but it's not that annoying either. |
||
*/ | ||
public signal void unplugged (); | ||
|
||
|
||
/** | ||
* The guid | ||
*/ | ||
public string? guid { get; private set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is useless (and a technical detail of raw gamepads we want to hide). |
||
|
||
/** | ||
* The name present in our database | ||
*/ | ||
public string? name { get; private set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, we don't use it yet, let's keep it aside for now and add it when we will have a UI using it. :) |
||
|
||
/** | ||
* The raw gamepad behind this gamepad | ||
*/ | ||
public RawGamepad raw_gamepad { get; private set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably better to make it a private field, especially as we don't use it outside (which is good) and we shouldn't expose it. |
||
|
||
/** | ||
* Whether this gamepad is mapped | ||
*/ | ||
public bool mapped { get; private set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't use that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used in retro-input-manager and only the mapped gamepads are selected. This is particularly useful when we are remapping controls GUI There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As in the case we want to list all the gamepads but want to know if they are mapped or unmapped There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If they are not mapped then they shouldn't be Gamepad object, we already have an object type representing the unmapped gamepads: RawGamepad. And you clearly don't want unmapped gamepads: you are filtering them out in RetroInputManager! If later we need them we can have the GamepadManager maintaining two lists: one of Gamepad and one of the remaining RawGamepad it wasn't able to map. Hence, let's remove this useless |
||
|
||
/** | ||
* The mapping object | ||
*/ | ||
public Mapping? mapping { get; private set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto, it would be better as a private field. |
||
|
||
public Gamepad (RawGamepad raw_gamepad) throws FileError { | ||
this.raw_gamepad = raw_gamepad; | ||
guid = raw_gamepad.guid; | ||
name = MappingsManager.get_name (guid) ?? raw_gamepad.name; | ||
try { | ||
mapping = new Mapping.from_sdl_string (MappingsManager.get_mapping (guid)); | ||
mapped = true; | ||
raw_gamepad.button_event.connect (on_raw_button_event); | ||
raw_gamepad.axis_event.connect (on_raw_axis_event); | ||
raw_gamepad.dpad_event.connect (on_raw_dpad_event); | ||
} catch (MappingError e) { | ||
debug ("%s - for %s/%s", e.message, guid, name); | ||
mapping = null; | ||
mapped = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's not mapped it's not a valid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a valid gamepad object imo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we can't know where the A button is, it's not a Gamepad, it's a RawGamepad in disguise. |
||
} | ||
raw_gamepad.unplugged.connect (() => unplugged ()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If possible try There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nah.. doesnt work.
|
||
} | ||
|
||
private void on_raw_button_event (int button, bool value) { | ||
var event = mapping.get_button_mapping (button); | ||
emit_event (event, value ? 1 : 0); | ||
} | ||
|
||
private void on_raw_axis_event (int axis, double value) { | ||
var event = mapping.get_axis_mapping (axis); | ||
emit_event (event, value); | ||
} | ||
|
||
private void on_raw_dpad_event (int dpad_index, int axis, int value) { | ||
var event = mapping.get_dpad_mapping (dpad_index, axis, value); | ||
emit_event (event, value.abs ()); | ||
} | ||
|
||
private void emit_event (MappedEvent event, double value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Forcing a boolean and an integer into a double may not be the best solution, let's have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually don't create new methods, add this to the body of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well the purpose is to have this in emit_event to prevent code duplication. Otherwise I need a switch case in every function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoiding code duplication is good except that there wouldn't be code duplication as you would handle the values differently and properly in each method. |
||
switch (event.type) { | ||
case InputType.AXIS: | ||
axis_event (event.axis, value); | ||
break; | ||
case InputType.BUTTON: | ||
button_event (event.button, (bool) value); | ||
break; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// This file is part of GNOME Games. License: GPLv3 | ||
|
||
namespace LibGamepad { | ||
private const int GUID_LENGTH = 8; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this as to LinuxRawGamepad. |
||
|
||
private string uint16s_to_hex_string (uint16[] data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this as a static method to LinuxRawGamepad. |
||
requires (data.length == GUID_LENGTH) | ||
{ | ||
const string hex_to_ascii_map = "0123456789abcdef"; | ||
|
||
var builder = new StringBuilder (); | ||
foreach (uint16 el in data) { | ||
uint8 c = (uint8) el; | ||
builder.append_unichar (hex_to_ascii_map[c >> 4]); | ||
builder.append_unichar (hex_to_ascii_map[c & 0x0F]); | ||
|
||
c = (uint8) (el >> 8); | ||
builder.append_unichar (hex_to_ascii_map[c >> 4]); | ||
builder.append_unichar (hex_to_ascii_map[c & 0x0F]); | ||
} | ||
|
||
return builder.str; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be a newline before. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// This file is part of GNOME Games. License: GPLv3 | ||
|
||
public enum LibGamepad.InputType { | ||
AXIS, | ||
BUTTON, | ||
INVALID; | ||
|
||
public static uint length () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I doesn't seem to be used to me, if it's not we better remove it, otherwise tell me so I don't annoy you with it later. |
||
return InputType.INVALID + 1; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// This file is part of GNOME Games. License: GPLv3 | ||
|
||
[CCode (cheader_filename = "libevdev/libevdev.h")] | ||
namespace Libevdev { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line looks extra (yes I also annoy you for extra empty lines :p ). |
||
[CCode (cname = "libevdev_read_flag", cprefix = "LIBEVDEV_READ_FLAG_", has_type_id = false)] | ||
[Flags] | ||
public enum ReadFlag { | ||
SYNC, | ||
NORMAL, | ||
FORCE_SYNC, | ||
BLOCKING | ||
} | ||
|
||
[CCode (cname = "struct libevdev", cprefix = "libevdev_", free_function = "libevdev_free")] | ||
[Compact] | ||
public class Evdev { | ||
[CCode (cname = "libevdev_new")] | ||
public Evdev (); | ||
|
||
public int get_fd (); | ||
public int set_fd (int fd); | ||
|
||
public string name { get; set; } | ||
|
||
public int id_bustype { get; set; } | ||
public int id_vendor { get; set; } | ||
public int id_product { get; set; } | ||
public int id_version { get; set; } | ||
|
||
public unowned Linux.Input.AbsInfo? get_abs_info (uint code); | ||
public bool has_event_code (uint type, uint code); | ||
public int has_event_pending (); | ||
public int next_event (uint flags, out Linux.Input.Event ev); | ||
|
||
public static unowned string event_code_get_name(uint type, uint code); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// This file is part of GNOME Games. License: GPLv3 | ||
|
||
private class LibGamepad.LinuxGuidHelpers : Object { | ||
public static string from_dev (Libevdev.Evdev device) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should go with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make it simple instead, just move it to LinuxRawGamepad. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Earlier this was also being used in raw-gamepad-monitor, so it is here. but moved |
||
uint16 guid[8]; | ||
guid[0] = (uint16) device.id_bustype.to_little_endian (); | ||
guid[1] = 0; | ||
guid[2] = (uint16) device.id_vendor.to_little_endian (); | ||
guid[3] = 0; | ||
guid[4] = (uint16) device.id_product.to_little_endian (); | ||
guid[5] = 0; | ||
guid[6] = (uint16) device.id_version.to_little_endian (); | ||
guid[7] = 0; | ||
return uint16s_to_hex_string (guid); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a newline before. |
||
} | ||
|
||
public static string from_file (string file_name) throws FileError { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not used and should be removed. |
||
var fd = Posix.open (file_name, Posix.O_RDONLY | Posix.O_NONBLOCK); | ||
|
||
if (fd < 0) | ||
throw new FileError.FAILED (@"Unable to open file $file_name: $(Posix.strerror (Posix.errno))"); | ||
|
||
var device = new Libevdev.Evdev (); | ||
if (device.set_fd (fd) < 0) | ||
throw new FileError.FAILED (@"Evdev error on opening file $file_name: $(Posix.strerror (Posix.errno))"); | ||
|
||
var guid = from_dev (device); | ||
Posix.close (fd); | ||
return guid; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting comment, we can keep it.