From 333db2b17e898e42dcaf704676dd2056d4fd7117 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Apr 2019 22:28:59 +0200 Subject: [PATCH 01/12] movies: fix compilation issue in MSVC 2015 --- panda/src/movies/vorbisAudioCursor.cxx | 2 +- panda/src/movies/wavAudioCursor.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/panda/src/movies/vorbisAudioCursor.cxx b/panda/src/movies/vorbisAudioCursor.cxx index 80c5613b0c..c84dd37aa7 100644 --- a/panda/src/movies/vorbisAudioCursor.cxx +++ b/panda/src/movies/vorbisAudioCursor.cxx @@ -104,7 +104,7 @@ seek(double t) { if (result == OV_ENOSEEK && t == 0.0) { std::istream *stream = (std::istream *)_ov.datasource; - if (stream->rdbuf()->pubseekpos(0, std::ios::in) == 0) { + if (stream->rdbuf()->pubseekpos(0, std::ios::in) == (std::streampos)0) { // Back up the callbacks, then destroy the stream, making sure to first // unset the datasource so that it won't close the file. ov_callbacks callbacks = _ov.callbacks; diff --git a/panda/src/movies/wavAudioCursor.cxx b/panda/src/movies/wavAudioCursor.cxx index 4b58794d46..92fa9dc4a9 100644 --- a/panda/src/movies/wavAudioCursor.cxx +++ b/panda/src/movies/wavAudioCursor.cxx @@ -315,7 +315,7 @@ seek(double t) { } else if (pos < current) { // Can we seek to the beginning? Some streams, such as ZStream, let us // rewind the stream. - if (buf->pubseekpos(0, std::ios::in) == 0) { + if (buf->pubseekpos(0, std::ios::in) == (std::streampos)0) { if (pos > _data_start && movies_cat.is_info()) { Filename fn = get_source()->get_filename(); movies_cat.info() From d9d30cdfd2b7d72e8933d7c0776660ee7803b8b8 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Apr 2019 22:29:36 +0200 Subject: [PATCH 02/12] collide: compat for typo'ed respect_prev_transform property in 1.10 --- panda/src/collide/collisionTraverser.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h index 8a6c912aaf..8ccd102bec 100644 --- a/panda/src/collide/collisionTraverser.h +++ b/panda/src/collide/collisionTraverser.h @@ -51,6 +51,8 @@ PUBLISHED: INLINE bool get_respect_prev_transform() const; MAKE_PROPERTY(respect_preV_transform, get_respect_prev_transform, set_respect_prev_transform); + MAKE_PROPERTY(respect_prev_transform, get_respect_prev_transform, + set_respect_prev_transform); void add_collider(const NodePath &collider, CollisionHandler *handler); bool remove_collider(const NodePath &collider); From 951218715626a10e260a936ab828fd44ea23e084 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 30 Apr 2019 10:32:01 +0200 Subject: [PATCH 03/12] test_wheel: upgrade pip inside virtualenv, don't write bytecode --- makepanda/test_wheel.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/makepanda/test_wheel.py b/makepanda/test_wheel.py index 09b0727fe7..1b6ac14175 100755 --- a/makepanda/test_wheel.py +++ b/makepanda/test_wheel.py @@ -17,31 +17,34 @@ from optparse import OptionParser def test_wheel(wheel, verbose=False): envdir = tempfile.mkdtemp(prefix="venv-") print("Setting up virtual environment in {0}".format(envdir)) - - if sys.version_info >= (3, 0): - subprocess.call([sys.executable, "-m", "venv", "--clear", envdir]) - else: - subprocess.call([sys.executable, "-m", "virtualenv", "--clear", envdir]) + sys.stdout.flush() # Make sure pip is up-to-date first. - if subprocess.call([sys.executable, "-m", "pip", "install", "-U", "pip"]) != 0: - shutil.rmtree(envdir) - sys.exit(1) + subprocess.call([sys.executable, "-B", "-m", "pip", "install", "-U", "pip"]) - # Install pytest into the environment, as well as our wheel. - if sys.platform == "win32": - pip = os.path.join(envdir, "Scripts", "pip.exe") + # Create a virtualenv. + if sys.version_info >= (3, 0): + subprocess.call([sys.executable, "-B", "-m", "venv", "--clear", envdir]) else: - pip = os.path.join(envdir, "bin", "pip") - if subprocess.call([pip, "install", "pytest", wheel]) != 0: - shutil.rmtree(envdir) - sys.exit(1) + subprocess.call([sys.executable, "-B", "-m", "virtualenv", "--clear", envdir]) - # Run the test suite. + # Determine the path to the Python interpreter. if sys.platform == "win32": python = os.path.join(envdir, "Scripts", "python.exe") else: python = os.path.join(envdir, "bin", "python") + + # Upgrade pip inside the environment too. + if subprocess.call([python, "-m", "pip", "install", "-U", "pip"]) != 0: + shutil.rmtree(envdir) + sys.exit(1) + + # Install pytest into the environment, as well as our wheel. + if subprocess.call([python, "-m", "pip", "install", "pytest", wheel]) != 0: + shutil.rmtree(envdir) + sys.exit(1) + + # Run the test suite. test_cmd = [python, "-m", "pytest", "tests"] if verbose: test_cmd.append("--verbose") From 204cbe4464d694c299edb0d584bd7611557f234f Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 1 May 2019 17:05:12 +0200 Subject: [PATCH 04/12] interrogate: support unicode characters in Python 3 for 'char' arg Fixes #626 --- dtool/src/interrogate/interfaceMakerPythonNative.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index dadd094ca4..c135ce3df4 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -4937,13 +4937,14 @@ write_function_instance(ostream &out, FunctionRemap *remap, expected_params += "NoneType"; } else if (TypeManager::is_char(type)) { - indent(out, indent_level) << "char " << param_name << default_expr << ";\n"; + indent(out, indent_level) << "char *" << param_name << "_str;\n"; + indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; - format_specifiers += "c"; - parameter_list += ", &" + param_name; + format_specifiers += "s#"; + parameter_list += ", &" + param_name + "_str, &" + param_name + "_len"; + extra_param_check << " && " << param_name << "_len == 1"; - // extra_param_check << " && isascii(" << param_name << ")"; - pexpr_string = "(char) " + param_name; + pexpr_string = param_name + "_str[0]"; expected_params += "char"; only_pyobjects = false; From d786709a49e5bd61ac020de9584901972ede8143 Mon Sep 17 00:00:00 2001 From: nate97 Date: Tue, 30 Apr 2019 21:46:16 -0500 Subject: [PATCH 05/12] sfxplayer: fixes bug when using the "node argument" in SoundInterval, also fixes Py3 TypeError exception Closes #640 --- direct/src/showbase/SfxPlayer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/direct/src/showbase/SfxPlayer.py b/direct/src/showbase/SfxPlayer.py index 4f139a89c9..c50c498236 100644 --- a/direct/src/showbase/SfxPlayer.py +++ b/direct/src/showbase/SfxPlayer.py @@ -53,6 +53,8 @@ class SfxPlayer: d = node.getDistance(listenerNode) else: d = node.getDistance(base.cam) + if not cutoff: + cutoff = self.cutoffDistance if d == None or d > cutoff: volume = 0 else: @@ -70,9 +72,6 @@ class SfxPlayer: self, sfx, looping = 0, interrupt = 1, volume = None, time = 0.0, node=None, listenerNode = None, cutoff = None): if sfx: - if not cutoff: - cutoff = self.cutoffDistance - self.setFinalVolume(sfx, node, volume, listenerNode, cutoff) # don't start over if it's already playing, unless From 16c3ca5c875706e66af8d85c2bfd3886afce3f9c Mon Sep 17 00:00:00 2001 From: Sebastian Hoffmann Date: Wed, 1 May 2019 00:27:37 +0200 Subject: [PATCH 06/12] device: Fix swapped axes on right stick for Jess Colour Rumble Pad Closes #639 --- panda/src/device/evdevInputDevice.cxx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index 4ebbc55498..fc89b5a5a4 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -62,6 +62,9 @@ enum QuirkBits { // We only connect it if it is reporting any events, because when Steam is // running, the Steam controller is muted in favour of a dummy Xbox device. QB_steam_controller = 32, + + // Axes on the right stick are swapped, using x for y and vice versa. + QB_right_axes_swapped = 64, }; static const struct DeviceMapping { @@ -81,7 +84,7 @@ static const struct DeviceMapping { // Steam Controller (wireless) {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller}, // Jess Tech Colour Rumble Pad - {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0}, + {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_right_axes_swapped}, // SPEED Link SL-6535-SBK-01 {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0}, // 8bitdo N30 Pro Controller @@ -488,7 +491,11 @@ init_device() { break; case ABS_Z: if (quirks & QB_rstick_from_z) { - axis = InputDevice::Axis::right_x; + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_y; + } else { + axis = InputDevice::Axis::right_x; + } } else if (_device_class == DeviceClass::gamepad) { axis = InputDevice::Axis::left_trigger; have_analog_triggers = true; @@ -514,7 +521,11 @@ init_device() { break; case ABS_RZ: if (quirks & QB_rstick_from_z) { - axis = InputDevice::Axis::right_y; + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_x; + } else { + axis = InputDevice::Axis::right_y; + } } else if (_device_class == DeviceClass::gamepad) { axis = InputDevice::Axis::right_trigger; have_analog_triggers = true; From 4d33db20280a726de2747538ef3bbea9a27c947c Mon Sep 17 00:00:00 2001 From: Maverick Liberty Date: Sun, 28 Apr 2019 16:00:17 -0400 Subject: [PATCH 07/12] DirectOptionMenu: Fix popup menu reset of the popup marker's pos Also defines popupMarker_pos and raises an assertion error if #showPopupMenu() is called when no items have been specified. Fixes #636 Closes #637 --- direct/src/gui/DirectOptionMenu.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/direct/src/gui/DirectOptionMenu.py b/direct/src/gui/DirectOptionMenu.py index 40d48f7672..74d33b5e9e 100644 --- a/direct/src/gui/DirectOptionMenu.py +++ b/direct/src/gui/DirectOptionMenu.py @@ -22,10 +22,12 @@ class DirectOptionMenu(DirectButton): # List of items to display on the popup menu ('items', [], self.setItems), # Initial item to display on menu button - # Can be an interger index or the same string as the button + # Can be an integer index or the same string as the button ('initialitem', None, DGG.INITOPT), # Amount of padding to place around popup button indicator ('popupMarkerBorder', (.1, .1), None), + # The initial position of the popup marker + ('popupMarker_pos', (0, 0, 0), None), # Background color to use to highlight popup menu items ('highlightColor', (.5, .5, .5, 1), None), # Extra scale to use on highlight popup menu items @@ -42,6 +44,8 @@ class DirectOptionMenu(DirectButton): DirectButton.__init__(self, parent) # Record any user specified frame size self.initFrameSize = self['frameSize'] + # Record any user specified popup marker position + self.initPopupMarkerPos = self['popupMarker_pos'] # Create a small rectangular marker to distinguish this button # as a popup menu button self.popupMarker = self.createcomponent( @@ -168,8 +172,13 @@ class DirectOptionMenu(DirectButton): else: # Or base it upon largest item bounds = [self.minX, self.maxX, self.minZ, self.maxZ] - pm.setPos(bounds[1] + pmw/2.0, 0, - bounds[2] + (bounds[3] - bounds[2])/2.0) + if self.initPopupMarkerPos: + # Use specified position + pmPos = list(self.initPopupMarkerPos) + else: + # Or base the position on the frame size. + pmPos = [bounds[1] + pmw/2.0, 0, bounds[2] + (bounds[3] - bounds[2])/2.0] + pm.setPos(pmPos[0], pmPos[1], pmPos[2]) # Adjust popup menu button to fit all items (or use user specified # frame size bounds[1] += pmw @@ -184,6 +193,12 @@ class DirectOptionMenu(DirectButton): Adjust popup position if default position puts it outside of visible screen region """ + + # Needed attributes (such as minZ) won't be set unless the user has specified + # items to display. Let's assert that we've given items to work with. + items = self['items'] + assert items and len(items) > 0, 'Cannot show an empty popup menu! You must add items!' + # Show the menu self.popupMenu.show() # Make sure its at the right scale From f32dc3cf2b02ccb02bc6e66c09af9cfee50df3cd Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 2 May 2019 16:17:17 +0200 Subject: [PATCH 08/12] tkpanels: add missing AnimPanel imports --- direct/src/tkpanels/AnimPanel.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/direct/src/tkpanels/AnimPanel.py b/direct/src/tkpanels/AnimPanel.py index 8cee51e1a6..ee61d9404e 100644 --- a/direct/src/tkpanels/AnimPanel.py +++ b/direct/src/tkpanels/AnimPanel.py @@ -9,13 +9,16 @@ __all__ = ['AnimPanel', 'ActorControl'] # Import Tkinter, Pmw, and the floater code from this directory tree. from direct.tkwidgets.AppShell import * from direct.showbase.TkGlobal import * -import Pmw, sys +import Pmw, sys, os from direct.task import Task +from panda3d.core import Filename, getModelPath if sys.version_info >= (3, 0): from tkinter.simpledialog import askfloat + from tkinter.filedialog import askopenfilename else: from tkSimpleDialog import askfloat + from tkFileDialog import askopenfilename FRAMES = 0 From a3b4486ef3b5059b6623fae64c336e8f9f088253 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 2 May 2019 16:17:43 +0200 Subject: [PATCH 09/12] tkpanels: fix a few exceptions in AnimPanel --- direct/src/tkpanels/AnimPanel.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/direct/src/tkpanels/AnimPanel.py b/direct/src/tkpanels/AnimPanel.py index ee61d9404e..a8348c299d 100644 --- a/direct/src/tkpanels/AnimPanel.py +++ b/direct/src/tkpanels/AnimPanel.py @@ -276,7 +276,7 @@ class AnimPanel(AppShell): title = 'Load Animation', parent = self.component('hull') ) - if (animFilename == ''): + if not animFilename: # no file selected, canceled return @@ -372,8 +372,9 @@ class AnimPanel(AppShell): def destroy(self): # First clean up taskMgr.remove(self.id + '_UpdateTask') - self.destroyCallBack() - self.destroyCallBack = None + if self.destroyCallBack is not None: + self.destroyCallBack() + self.destroyCallBack = None AppShell.destroy(self) class ActorControl(Pmw.MegaWidget): From 6d9b217c2c0782265fb2cf5822171d092c9ccb13 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 2 May 2019 16:18:22 +0200 Subject: [PATCH 10/12] tkwidgets: fix exceptions hovering over rgbPanel menu items --- direct/src/tkwidgets/Valuator.py | 49 +++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/direct/src/tkwidgets/Valuator.py b/direct/src/tkwidgets/Valuator.py index 39e0c8d73f..1704948571 100644 --- a/direct/src/tkwidgets/Valuator.py +++ b/direct/src/tkwidgets/Valuator.py @@ -656,25 +656,35 @@ def rgbPanel(nodePath, callback = None, style = 'mini'): pButton.pack(expand = 1, fill = BOTH) # Update menu - menu = vgp.component('menubar').component('Valuator Group-menu') + menubar = vgp.component('menubar') + menubar.deletemenuitems('Valuator Group', 1, 1) + # Some helper functions # Clear color - menu.insert_command(index = 1, label = 'Clear Color', - command = lambda: nodePath.clearColor()) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Clear Color', command=lambda: nodePath.clearColor()) # Set Clear Transparency - menu.insert_command(index = 2, label = 'Set Transparency', - command = lambda: nodePath.setTransparency(1)) - menu.insert_command( - index = 3, label = 'Clear Transparency', - command = lambda: nodePath.clearTransparency()) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Set Transparency', command=lambda: nodePath.setTransparency(1)) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Clear Transparency', command=lambda: nodePath.clearTransparency()) # System color picker - menu.insert_command(index = 4, label = 'Popup Color Picker', - command = popupColorPicker) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Popup Color Picker', command=popupColorPicker) - menu.insert_command(index = 5, label = 'Print to log', - command = printToLog) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Print to log', command=printToLog) + + menubar.addmenuitem( + 'Valuator Group', 'command', 'Dismiss Valuator Group panel', + label='Dismiss', command=vgp.destroy) def setNodePathColor(color): nodePath.setColor(color[0]/255.0, color[1]/255.0, @@ -724,18 +734,23 @@ def lightRGBPanel(light, style = 'mini'): # Update menu button vgp.component('menubar').component('Valuator Group-button')['text'] = ( 'Light Control Panel') + # Add a print button which will also serve as a color tile pButton = Button(vgp.interior(), text = 'Print to Log', bg = getTkColorString(initColor), command = printToLog) pButton.pack(expand = 1, fill = BOTH) + # Update menu - menu = vgp.component('menubar').component('Valuator Group-menu') + menubar = vgp.component('menubar') # System color picker - menu.insert_command(index = 4, label = 'Popup Color Picker', - command = popupColorPicker) - menu.insert_command(index = 5, label = 'Print to log', - command = printToLog) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Popup Color Picker', command=popupColorPicker) + menubar.addmenuitem( + 'Valuator Group', 'command', + label='Print to log', command=printToLog) + def setLightColor(color): light.setColor(Vec4(color[0]/255.0, color[1]/255.0, color[2]/255.0, color[3]/255.0)) From cc4d5259ccc6f5fd97dee2947adf605fe7999704 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 2 May 2019 19:40:23 +0200 Subject: [PATCH 11/12] linmath: fix mat4.get_col3() and mat4.get_row3() when using Eigen --- panda/src/linmath/lmatrix4_src.I | 8 ------- tests/linmath/test_lmatrix4.py | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/panda/src/linmath/lmatrix4_src.I b/panda/src/linmath/lmatrix4_src.I index a887d393f1..fe8ba22bb7 100644 --- a/panda/src/linmath/lmatrix4_src.I +++ b/panda/src/linmath/lmatrix4_src.I @@ -484,13 +484,9 @@ get_col(int col) const { */ INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LMatrix4):: get_row3(int row) const { -#ifdef HAVE_EIGEN - return FLOATNAME(LVecBase3)(_m.block<1, 3>(row, 0)); -#else return FLOATNAME(LVecBase3)((*this)(row, 0), (*this)(row, 1), (*this)(row, 2)); -#endif // HAVE_EIGEN } /** @@ -514,13 +510,9 @@ get_row3(FLOATNAME(LVecBase3) &result_vec,int row) const { */ INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LMatrix4):: get_col3(int col) const { -#ifdef HAVE_EIGEN - return FLOATNAME(LVecBase3)(_m.block<1, 3>(0, col)); -#else return FLOATNAME(LVecBase3)((*this)(0, col), (*this)(1, col), (*this)(2, col)); -#endif // HAVE_EIGEN } /** diff --git a/tests/linmath/test_lmatrix4.py b/tests/linmath/test_lmatrix4.py index 23c1bcc5e9..05c487f807 100644 --- a/tests/linmath/test_lmatrix4.py +++ b/tests/linmath/test_lmatrix4.py @@ -87,3 +87,39 @@ def test_mat4_invert_correct(type): assert (mat * inv).is_identity() assert (inv * mat).is_identity() + + +@pytest.mark.parametrize("type", (core.LMatrix4d, core.LMatrix4f)) +def test_mat4_rows(type): + mat = type((1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16)) + + assert mat.rows[0] == (1, 2, 3, 4) + assert mat.rows[1] == (5, 6, 7, 8) + assert mat.rows[2] == (9, 10, 11, 12) + assert mat.rows[3] == (13, 14, 15, 16) + + assert mat.get_row3(0) == (1, 2, 3) + assert mat.get_row3(1) == (5, 6, 7) + assert mat.get_row3(2) == (9, 10, 11) + assert mat.get_row3(3) == (13, 14, 15) + + +@pytest.mark.parametrize("type", (core.LMatrix4d, core.LMatrix4f)) +def test_mat4_cols(type): + mat = type((1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15, + 4, 8, 12, 16)) + + assert mat.cols[0] == (1, 2, 3, 4) + assert mat.cols[1] == (5, 6, 7, 8) + assert mat.cols[2] == (9, 10, 11, 12) + assert mat.cols[3] == (13, 14, 15, 16) + + assert mat.get_col3(0) == (1, 2, 3) + assert mat.get_col3(1) == (5, 6, 7) + assert mat.get_col3(2) == (9, 10, 11) + assert mat.get_col3(3) == (13, 14, 15) From 7f0ac22ca5949b64603546f26aa5c20457fa0693 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 2 May 2019 21:29:03 +0200 Subject: [PATCH 12/12] device: fix mappings for various generic gamepads on Windows See also #576 --- panda/src/device/winRawInputDevice.cxx | 49 ++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/panda/src/device/winRawInputDevice.cxx b/panda/src/device/winRawInputDevice.cxx index e2e27761bc..10262d124c 100644 --- a/panda/src/device/winRawInputDevice.cxx +++ b/panda/src/device/winRawInputDevice.cxx @@ -32,6 +32,12 @@ enum QuirkBits : int { // Throttle is reversed. QB_reversed_throttle = 4, + + // Right stick uses Z and Rz inputs. + QB_rstick_from_z = 8, + + // Axes on the right stick are swapped, using x for y and vice versa. + QB_right_axes_swapped = 64, }; // Some nonstandard gamepads have different button mappings. @@ -42,12 +48,17 @@ static const struct DeviceMapping { int quirks; const char *buttons[16]; } mapping_presets[] = { - // SNES-style USB gamepad + // SNES-style USB gamepad, or cheap unbranded USB gamepad with no sticks + // ABXY are mapped based on their position, not based on their label. {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers, - {"face_x", "face_a", "face_b", "face_y", "lshoulder", "rshoulder", "none", "none", "back", "start"} + {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start"} }, - // SPEED Link SL-6535-SBK-01 - {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers, + // Unbranded generic cheap USB gamepad + {0x0810, 0x0001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers | QB_right_axes_swapped, + {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"} + }, + // Trust GXT 24 / SPEED Link SL-6535-SBK-01 + {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers, {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"} }, // T.Flight Hotas X @@ -56,7 +67,7 @@ static const struct DeviceMapping { }, // NVIDIA Shield Controller {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, 0, - {"face_a", "face_b", "n", "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", "e", "f", "g", "start", "h", "lstick", "rstick", "i"} + {"face_a", "face_b", 0, "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", 0, 0, 0, "start", 0, "lstick", "rstick", 0} }, {0}, }; @@ -422,7 +433,14 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { break; case HID_USAGE_GENERIC_Z: if (_device_class == DeviceClass::gamepad) { - if ((quirks & QB_no_analog_triggers) == 0) { + if (quirks & QB_rstick_from_z) { + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_y; + swap(cap.LogicalMin, cap.LogicalMax); + } else { + axis = InputDevice::Axis::right_x; + } + } else if ((quirks & QB_no_analog_triggers) == 0) { axis = Axis::left_trigger; } } else if (_device_class == DeviceClass::flight_stick) { @@ -455,7 +473,14 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { break; case HID_USAGE_GENERIC_RZ: if (_device_class == DeviceClass::gamepad) { - if ((quirks & QB_no_analog_triggers) == 0) { + if (quirks & QB_rstick_from_z) { + if (quirks & QB_right_axes_swapped) { + axis = InputDevice::Axis::right_x; + } else { + axis = InputDevice::Axis::right_y; + swap(cap.LogicalMin, cap.LogicalMax); + } + } else if ((quirks & QB_no_analog_triggers) == 0) { axis = Axis::right_trigger; } } else { @@ -481,6 +506,16 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) { break; } + // If this axis already exists, don't double-map it, but take the first + // one. This is important for the Trust GXT 24 / SL-6535-SBK-01 which + // have a weird extra Z axis with DataIndex 2 that should be ignored. + for (size_t i = 0; i < _axes.size(); ++i) { + if (_axes[i].axis == axis) { + axis = Axis::none; + break; + } + } + int axis_index; if (!is_signed) { // All axes on the weird XInput-style mappings go from -1 to 1