[gnome-games/wip/abhinavsingh/gamepad-reassign: 27/27] Add basic reassignment
- From: Abhinav Singh <abhinavsingh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/wip/abhinavsingh/gamepad-reassign: 27/27] Add basic reassignment
- Date: Fri, 28 Jul 2017 03:49:04 +0000 (UTC)
commit 605c996f9dc275175d0df67371cb68223f5d3120
Author: theawless <theawless gmail com>
Date: Sun Jul 23 01:58:49 2017 +0530
Add basic reassignment
Add InputPopover
Add InputSet
Refactor RetroInputManager
Add incomplete InputReassigner
data/Makefile.am | 2 +
data/org.gnome.Games.gresource.xml | 2 +
data/ui/display-header-bar.ui | 25 +++++++++++
data/ui/input-popover.ui | 31 ++++++++++++++
data/ui/input-reassigner.ui | 70 +++++++++++++++++++++++++++++++
src/Makefile.am | 3 +
src/command/command-runner.vala | 4 ++
src/core/input-set.vala | 38 +++++++++++++++++
src/core/runner.vala | 1 +
src/dummy/dummy-runner.vala | 4 ++
src/retro/retro-input-manager.vala | 79 ++++++++++++------------------------
src/retro/retro-runner.vala | 4 ++
src/ui/application-window.vala | 2 +
src/ui/display-header-bar.vala | 12 +++++
src/ui/input-popover.vala | 73 +++++++++++++++++++++++++++++++++
src/ui/input-reassigner.vala | 78 +++++++++++++++++++++++++++++++++++
16 files changed, 375 insertions(+), 53 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index fff2a95..f09dc61 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -41,6 +41,8 @@ EXTRA_DIST = \
ui/gamepad-mapper.ui \
ui/gamepad-tester.ui \
ui/game-icon-view.ui \
+ ui/input-popover.ui \
+ ui/input-reassigner.ui \
ui/media-menu-button.ui \
ui/media-selector.ui \
ui/preferences-page-controllers.ui \
diff --git a/data/org.gnome.Games.gresource.xml b/data/org.gnome.Games.gresource.xml
index b8ba7b5..8bbd722 100644
--- a/data/org.gnome.Games.gresource.xml
+++ b/data/org.gnome.Games.gresource.xml
@@ -20,6 +20,8 @@
<file preprocess="xml-stripblanks">ui/gamepad-mapper.ui</file>
<file preprocess="xml-stripblanks">ui/gamepad-tester.ui</file>
<file preprocess="xml-stripblanks">ui/game-icon-view.ui</file>
+ <file preprocess="xml-stripblanks">ui/input-popover.ui</file>
+ <file preprocess="xml-stripblanks">ui/input-reassigner.ui</file>
<file preprocess="xml-stripblanks">ui/media-menu-button.ui</file>
<file preprocess="xml-stripblanks">ui/media-selector.ui</file>
<file preprocess="xml-stripblanks">ui/preferences-page-controllers.ui</file>
diff --git a/data/ui/display-header-bar.ui b/data/ui/display-header-bar.ui
index aae8f8d..7b21961 100644
--- a/data/ui/display-header-bar.ui
+++ b/data/ui/display-header-bar.ui
@@ -92,5 +92,30 @@
<property name="pack-type">end</property>
</packing>
</child>
+ <child>
+ <object class="GtkMenuButton" id="input_button">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-input">
+ <property name="accessible-name" translatable="yes">input</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="input_image">
+ <property name="visible">True</property>
+ <property name="icon-name">input-gaming-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
</template>
</interface>
diff --git a/data/ui/input-popover.ui b/data/ui/input-popover.ui
new file mode 100644
index 0000000..38ce3f2
--- /dev/null
+++ b/data/ui/input-popover.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.16"/>
+ <template class="GamesInputPopover" parent="GtkPopover">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin">6</property>
+ <property name="spacing">12</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkListBox" id="inputs_list_box">
+ <property name="visible">True</property>
+ <property name="selection_mode">none</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="reassign_button">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reassign</property>
+ <signal name="clicked" handler="on_reassign_clicked"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/input-reassigner.ui b/data/ui/input-reassigner.ui
new file mode 100644
index 0000000..b157c89
--- /dev/null
+++ b/data/ui/input-reassigner.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GamesInputReassigner" parent="GtkDialog">
+ <property name="destroy-with-parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkInfoBar" id="info_bar">
+ <property name="visible">True</property>
+ <child internal-child="content_area">
+ <object class="GtkBox">
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Some help message for the user</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkListBox" id="inputs_list_box">
+ <property name="visible">True</property>
+ <property name="selection_mode">none</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="button_apply">
+ <property name="visible">True</property>
+ <property name="can-default">True</property>
+ <property name="label" translatable="yes">Apply</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="button_cancel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Cancel</property>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="apply" default="true">button_apply</action-widget>
+ <action-widget response="cancel">button_cancel</action-widget>
+ </action-widgets>
+ </template>
+</interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 645eb45..a2d63bd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -64,6 +64,7 @@ gnome_games_SOURCES = \
core/game-collection.vala \
core/game-uri-adapter.vala \
core/icon.vala \
+ core/input-set.vala \
core/input-capabilities.vala \
core/media.vala \
core/media-info.vala \
@@ -153,6 +154,8 @@ gnome_games_SOURCES = \
ui/game-icon-view.vala \
ui/game-thumbnail.vala \
ui/gamepad-view-configuration.vala \
+ ui/input-popover.vala \
+ ui/input-reassigner.vala \
ui/media-selector.vala \
ui/media-menu-button.vala \
ui/preferences-page.vala \
diff --git a/src/command/command-runner.vala b/src/command/command-runner.vala
index e8b8612..8267c20 100644
--- a/src/command/command-runner.vala
+++ b/src/command/command-runner.vala
@@ -17,6 +17,10 @@ public class Games.CommandRunner : Object, Runner {
get { return null; }
}
+ internal InputSet? input_set {
+ get { return null; }
+ }
+
private string[] args;
private bool watch_child;
diff --git a/src/core/input-set.vala b/src/core/input-set.vala
new file mode 100644
index 0000000..3a99e32
--- /dev/null
+++ b/src/core/input-set.vala
@@ -0,0 +1,38 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.InputSet : Object {
+ public signal void changed ();
+
+ public bool has_multiple_inputs {
+ get { return gamepads.length > 0; }
+ }
+ public uint first_unplugged_port {
+ get {
+ uint i = 0;
+ while (gamepads.contains (i) || i == keyboard_port) {
+ i++;
+ }
+ return i;
+ }
+ }
+
+ public HashTable<uint, Gamepad?> gamepads { set; get; }
+ public uint keyboard_port { set; get; }
+
+ construct {
+ gamepads = new HashTable<uint, Gamepad?> (GLib.direct_hash, GLib.direct_equal);
+ keyboard_port = 0;
+ notify["gamepads"].connect (() => changed ());
+ notify["keyboard-port"].connect (() => changed ());
+ }
+
+ public void add_gamepad (uint port, Gamepad gamepad) {
+ gamepads.insert (port, gamepad);
+ changed ();
+ }
+
+ public void remove_gamepad (uint port) {
+ gamepads.remove (port);
+ changed ();
+ }
+}
diff --git a/src/core/runner.vala b/src/core/runner.vala
index 25d79b8..df1b1e2 100644
--- a/src/core/runner.vala
+++ b/src/core/runner.vala
@@ -7,6 +7,7 @@ public interface Games.Runner : Object {
public abstract bool can_quit_safely { get; }
public abstract bool can_resume { get; }
public abstract MediaSet? media_set { get; }
+ internal abstract InputSet? input_set { get; }
public abstract bool check_is_valid (out string error_message) throws Error;
public abstract Gtk.Widget get_display ();
diff --git a/src/dummy/dummy-runner.vala b/src/dummy/dummy-runner.vala
index ce18b22..e5778c9 100644
--- a/src/dummy/dummy-runner.vala
+++ b/src/dummy/dummy-runner.vala
@@ -17,6 +17,10 @@ private class Games.DummyRunner : Object, Runner {
get { return null; }
}
+ internal InputSet? input_set {
+ get { return null; }
+ }
+
public bool check_is_valid (out string error_message) throws Error {
return true;
}
diff --git a/src/retro/retro-input-manager.vala b/src/retro/retro-input-manager.vala
index 26fdc52..d8bce9b 100644
--- a/src/retro/retro-input-manager.vala
+++ b/src/retro/retro-input-manager.vala
@@ -3,9 +3,14 @@
private class Games.RetroInputManager : Retro.InputDeviceManager, Retro.Rumble {
private Retro.VirtualGamepad keyboard;
private GamepadMonitor gamepad_monitor;
- private Retro.InputDevice?[] input_devices;
- private int keyboard_port;
private bool present_analog_sticks;
+ public InputSet input_set { private set; get; }
+
+ construct {
+ input_set = new InputSet ();
+ gamepad_monitor = GamepadMonitor.get_instance ();
+ gamepad_monitor.gamepad_plugged.connect (add_gamepad);
+ }
public RetroInputManager (Gtk.Widget widget, bool present_analog_sticks) {
this.present_analog_sticks = present_analog_sticks;
@@ -13,68 +18,36 @@ private class Games.RetroInputManager : Retro.InputDeviceManager, Retro.Rumble {
keyboard = new Retro.VirtualGamepad (widget);
set_keyboard (widget);
- gamepad_monitor = GamepadMonitor.get_instance ();
- gamepad_monitor.foreach_gamepad ((gamepad) => {
- var port = input_devices.length;
- var retro_gamepad = new RetroGamepad (gamepad, present_analog_sticks);
- input_devices += retro_gamepad;
- set_controller_device (port, retro_gamepad);
- gamepad.unplugged.connect (() => handle_gamepad_unplugged (port));
- });
-
- keyboard_port = input_devices.length;
- input_devices += keyboard;
- set_controller_device (keyboard_port, keyboard);
- gamepad_monitor.gamepad_plugged.connect (handle_gamepad_plugged);
+ gamepad_monitor.foreach_gamepad (add_gamepad);
}
- private void handle_gamepad_plugged (Gamepad gamepad) {
- // Plug this gamepad to the port where the keyboard was plugged as a last resort
- var port = keyboard_port;
+ private void add_gamepad (Gamepad gamepad) {
+ // Plug this gamepad to the port where the keyboard was plugged
+ var port = input_set.keyboard_port;
+ input_set.add_gamepad (port, gamepad);
var retro_gamepad = new RetroGamepad (gamepad, present_analog_sticks);
- input_devices[port] = retro_gamepad;
set_controller_device (port, retro_gamepad);
- gamepad.unplugged.connect (() => handle_gamepad_unplugged (port));
-
- // Assign keyboard to another unplugged port if exists and return
- for (var i = keyboard_port; i < input_devices.length; i++) {
- if (input_devices[i] == null) {
- // Found an unplugged port and so assigning keyboard to it
- keyboard_port = i;
- input_devices[keyboard_port] = keyboard;
- set_controller_device (keyboard_port, keyboard);
+ gamepad.unplugged.connect (() => remove_gamepad (port));
- return;
- }
- }
-
- // Now it means that there is no unplugged port so append keyboard to ports
- keyboard_port = input_devices.length;
- input_devices += keyboard;
- set_controller_device (keyboard_port, keyboard);
+ // Assign keyboard to first unplugged port
+ input_set.keyboard_port = input_set.first_unplugged_port;
+ set_controller_device (input_set.keyboard_port, keyboard);
}
- private void handle_gamepad_unplugged (int port) {
- if (keyboard_port > port) {
- // Remove the controller and shift keyboard to "lesser" port
- input_devices[keyboard_port] = null;
- remove_controller_device (keyboard_port);
- keyboard_port = port;
- input_devices[keyboard_port] = keyboard;
- set_controller_device (keyboard_port, keyboard);
- }
- else {
- // Just remove the controller as no need to shift keyboard
- input_devices[port] = null;
- remove_controller_device (port);
+ private void remove_gamepad (uint port) {
+ input_set.remove_gamepad (port);
+ remove_controller_device (port);
+
+ if (input_set.keyboard_port > port) {
+ // Shift keyboard to lesser port
+ remove_controller_device (input_set.keyboard_port);
+ input_set.keyboard_port = port;
+ set_controller_device (input_set.keyboard_port, keyboard);
}
}
private bool set_rumble_state (uint port, Retro.RumbleEffect effect, uint16 strength) {
- if (port > input_devices.length)
- return false;
-
- if (input_devices[port] == null || input_devices[port] == keyboard)
+ if (input_set.gamepads[port] != null)
return false;
// TODO Transmit the rumble signal to the gamepad.
diff --git a/src/retro/retro-runner.vala b/src/retro/retro-runner.vala
index adeec2d..5149600 100644
--- a/src/retro/retro-runner.vala
+++ b/src/retro/retro-runner.vala
@@ -34,6 +34,10 @@ public class Games.RetroRunner : Object, Runner {
get { return _media_set; }
}
+ internal InputSet? input_set {
+ get { return input_manager.input_set; }
+ }
+
private Retro.Core core;
private Retro.CairoDisplay video;
private Retro.PaPlayer audio;
diff --git a/src/ui/application-window.vala b/src/ui/application-window.vala
index 4c157ef..f950c0e 100644
--- a/src/ui/application-window.vala
+++ b/src/ui/application-window.vala
@@ -254,6 +254,8 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
display_box.runner = runner;
display_header_bar.media_set = runner.media_set;
display_box.header_bar.media_set = runner.media_set;
+ display_header_bar.input_set = runner.input_set;
+ display_box.header_bar.input_set = runner.input_set;
is_fullscreen = settings.get_boolean ("fullscreen") && runner.can_fullscreen;
diff --git a/src/ui/display-header-bar.vala b/src/ui/display-header-bar.vala
index 5fa6749..d180f0e 100644
--- a/src/ui/display-header-bar.vala
+++ b/src/ui/display-header-bar.vala
@@ -6,6 +6,8 @@ private class Games.DisplayHeaderBar : Gtk.HeaderBar {
[GtkChild]
private MediaMenuButton media_button;
+ [GtkChild]
+ private Gtk.MenuButton input_button;
public string game_title {
set { title = value; }
@@ -21,7 +23,14 @@ private class Games.DisplayHeaderBar : Gtk.HeaderBar {
}
}
+ public InputSet? input_set {
+ set {
+ input_popover.input_set = value;
+ }
+ }
+
private MediaSelector media_selector;
+ private InputPopover input_popover;
[GtkChild]
private Gtk.Button fullscreen;
@@ -34,6 +43,9 @@ private class Games.DisplayHeaderBar : Gtk.HeaderBar {
construct {
settings = new Settings ("org.gnome.Games");
+ input_popover = new InputPopover ();
+ input_popover.set_relative_to (input_button);
+ input_button.set_popover (input_popover);
media_selector = new MediaSelector ();
media_selector.set_relative_to (media_button);
media_button.set_popover (media_selector);
diff --git a/src/ui/input-popover.vala b/src/ui/input-popover.vala
new file mode 100644
index 0000000..1a29c0f
--- /dev/null
+++ b/src/ui/input-popover.vala
@@ -0,0 +1,73 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/ui/input-popover.ui")]
+private class Games.InputPopover : Gtk.Popover {
+ private InputSet _input_set;
+ public InputSet input_set {
+ set {
+ _input_set = value;
+
+ if (value != null) {
+ reset_inputs ();
+ value.changed.connect (reset_inputs);
+ }
+ }
+ private get { return _input_set; }
+ }
+
+ [GtkChild]
+ private Gtk.ListBox inputs_list_box;
+ [GtkChild]
+ private Gtk.Button reassign_button;
+
+ private void reset_inputs () {
+ reassign_button.sensitive = input_set.has_multiple_inputs;
+
+ remove_inputs ();
+ update_inputs ();
+ }
+
+ private void update_inputs () {
+ input_set.gamepads.foreach ((port, gamepad) => {
+ var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ box.pack_start (new Gtk.Label (gamepad.name));
+ box.pack_end (new Gtk.Label (port.to_string ()));
+ box.margin = 6;
+ box.show_all ();
+ inputs_list_box.insert (box, (int) port);
+ });
+ var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ box.pack_start (new Gtk.Label (_("Keyboard")));
+ box.pack_end (new Gtk.Label (input_set.keyboard_port.to_string ()));
+ box.margin = 6;
+ box.show_all ();
+ inputs_list_box.insert (box, (int) input_set.keyboard_port);
+ }
+
+ private void remove_inputs () {
+ inputs_list_box.foreach ((child) => child.destroy ());
+ }
+
+ [GtkCallback]
+ private void on_reassign_clicked () {
+ popdown ();
+
+ var input_reassigner = new InputReassigner ();
+ input_reassigner.set_transient_for ((Gtk.Window) get_toplevel ());
+ input_reassigner.response.connect ((response) => {
+ switch (response) {
+ case Gtk.ResponseType.APPLY:
+ input_set.gamepads = input_reassigner.input_set.gamepads;
+ input_set.keyboard_port = input_reassigner.input_set.keyboard_port;
+
+ break;
+ default:
+ break;
+ }
+
+ input_reassigner.destroy ();
+ });
+ input_reassigner.show ();
+ }
+}
diff --git a/src/ui/input-reassigner.vala b/src/ui/input-reassigner.vala
new file mode 100644
index 0000000..f306507
--- /dev/null
+++ b/src/ui/input-reassigner.vala
@@ -0,0 +1,78 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/ui/input-reassigner.ui")]
+private class Games.InputReassigner : Gtk.Dialog {
+ public InputSet input_set { private set; get; }
+
+ [GtkChild]
+ private Gtk.ListBox inputs_list_box;
+
+ private uint current_port;
+ private GamepadMonitor gamepad_monitor;
+
+ private ulong key_release_event_handler_id;
+ private HashTable<Gamepad, ulong> gamepad_button_press_event_handler_ids;
+
+ construct {
+ use_header_bar = 1;
+
+ input_set = new InputSet ();
+ input_set.keyboard_port = uint.MAX;
+ input_set.changed.connect (reset_inputs);
+ current_port = 0;
+ gamepad_monitor = GamepadMonitor.get_instance ();
+ gamepad_button_press_event_handler_ids = new HashTable<Gamepad, uint> (GLib.int_hash,
GLib.int_equal);
+
+ events |= Gdk.EventMask.KEY_RELEASE_MASK;
+ key_release_event_handler_id = key_release_event.connect (keyboard_event);
+ }
+
+ public InputReassigner () {
+ gamepad_monitor.foreach_gamepad ((gamepad) => {
+ var gamepad_button_press_event_handler_id = gamepad.button_press_event.connect (() =>
gamepad_event (gamepad));
+ gamepad_button_press_event_handler_ids[gamepad] =
gamepad_button_press_event_handler_id;
+ });
+ }
+
+ private void reset_inputs () {
+ remove_inputs ();
+ update_inputs ();
+ }
+
+ private void update_inputs () {
+ input_set.gamepads.foreach ((port, gamepad) => {
+ var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ box.pack_start (new Gtk.Label (gamepad.name));
+ box.pack_end (new Gtk.Label (port.to_string ()));
+ box.margin = 6;
+ box.show_all ();
+ inputs_list_box.insert (box, (int) port);
+ });
+ var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ box.pack_start (new Gtk.Label (_("Keyboard")));
+ box.pack_end (new Gtk.Label (input_set.keyboard_port.to_string ()));
+ box.margin = 6;
+ box.show_all ();
+ inputs_list_box.insert (box, (int) input_set.keyboard_port);
+ }
+
+ private void remove_inputs () {
+ inputs_list_box.foreach ((child) => child.destroy ());
+ }
+
+ private bool keyboard_event () {
+ disconnect (key_release_event_handler_id);
+
+ input_set.keyboard_port = current_port++;
+
+ return false;
+ }
+
+ private void gamepad_event (Gamepad gamepad) {
+ gamepad.disconnect (gamepad_button_press_event_handler_ids[gamepad]);
+ gamepad_button_press_event_handler_ids.remove (gamepad);
+
+ input_set.add_gamepad (current_port++, gamepad);
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]