From 76365f3ed19318400e172e7e5c4ea7c2e62e0ac8 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 16 Sep 2018 20:22:40 +0200 Subject: [PATCH] showbase: make input device binding explicit, not automatic --- direct/src/showbase/ShowBase.py | 109 ++++++++++++-------------------- 1 file changed, 41 insertions(+), 68 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 5a7a647c0a..8ed91bd8af 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -307,12 +307,7 @@ class ShowBase(DirectObject.DirectObject): ## This is the global input device manager, which keeps track of ## connected input devices. self.devices = InputDeviceManager.getGlobalPtr() - # add existing devices to the data graph - for device in self.devices.devices: - self.connectDevice(device) - # Checks for device connection and disconnection - self.accept('connect-device', self.connectDevice) - self.accept('disconnect-device', self.disconnectDevice) + self.__inputDeviceNodes = {} self.createStats() @@ -1681,76 +1676,56 @@ class ShowBase(DirectObject.DirectObject): return self.mouseWatcherNode.getModifierButtons().isDown( KeyboardButton.meta()) - def connectDevice(self, device): + def attachInputDevice(self, device, prefix=None): """ - This function will get called each time a new device got - connected and will add that new device to the data graph. + This function attaches an input device to the data graph, which will + cause the device to be polled and generate events. If a prefix is + given and not None, it is used to prefix events generated by this + device, separated by a hyphen. - Each device class will get a specific prefix for thrown events. Those - are currently as follow - - gamepad - flight_stick - steering_wheel - dance_pad - mouse - keyboard - unclassified_device - - In addition, the index of that device will appended to the prefix, - so for example if you hit the A button of the first connected gamepad - you will get an event like "gamepad0-action_a" the second gamepad will - then be catchable via "gamepad1-button_event" and so on. - Note, each device class will have a separate 0 based index, this way - you can have a gamepad0 as well as a steering_wheel0 and flight_stick0. - - All newly created button throwers will be stored in - the deviceButtonThrowers lsit + If you call this, you should consider calling detachInputDevice when + you are done with the device or when it is disconnected. """ - idn = self.dataRoot.attachNewNode(InputDeviceNode(device, device.getName())) - prefix = "unclassified_device" - if device.getDeviceClass() == InputDevice.DC_gamepad: - prefix = "gamepad" - elif device.getDeviceClass() == InputDevice.DC_flight_stick: - prefix = "flight_stick" - elif device.getDeviceClass() == InputDevice.DC_steering_wheel: - prefix = "steering_wheel" - elif device.getDeviceClass() == InputDevice.DC_dance_pad: - prefix = "dance_pad" - elif device.getDeviceClass() == InputDevice.DC_mouse: - prefix = "mouse" - elif device.getDeviceClass() == InputDevice.DC_keyboard: - prefix = "keyboard" - currentPrefixes = [] - for np in self.dataRoot.findAllMatches("**/{}".format(prefix)): - bt = np.node() - currentPrefixes.append(bt.getPrefix()) + # Protect against the same device being attached multiple times. + assert device not in self.__inputDeviceNodes - id = 0 - # Find the next free ID for the newly connected device - while "{}{}-".format(prefix, id) in currentPrefixes: - id+=1 - # Setup the button thrower for that device and register it's event prefix - bt = idn.attachNewNode(ButtonThrower(prefix)) - assert self.notify.debug("Registered event prefix {}{}-".format(prefix, id)) - bt.node().setPrefix("{}{}-".format(prefix, id)) - # append the new button thrower to the list of device button throwers + idn = self.dataRoot.attachNewNode(InputDeviceNode(device, device.name)) + + # Setup the button thrower to generate events for the device. + bt = idn.attachNewNode(ButtonThrower(device.name)) + if prefix is not None: + bt.node().setPrefix(prefix + '-') + + assert self.notify.debug("Attached input device {0} with prefix {1}".format(device, prefix)) + self.__inputDeviceNodes[device] = idn self.deviceButtonThrowers.append(bt) - def disconnectDevice(self, device): + def detachInputDevice(self, device): """ - This function will get called each time a new device got - connected. It is then used to clean up the given device from the - data graph. + This should be called after attaching an input device using + attachInputDevice and the device is disconnected or you no longer wish + to keep polling this device for events. + + You do not strictly need to call this if you expect the device to be + reconnected (but be careful that you don't reattach it). """ - self.notify.debug("Disconnect device {}".format(device.getName())) - idn = self.dataRoot.find("**/{}".format(device.getName())) - for bt in list(self.deviceButtonThrowers): - if bt.getName() == idn.getName(): + + if device not in self.__inputDeviceNodes: + assert device in self.__inputDeviceNodes + return + + assert self.notify.debug("Detached device {0}".format(device.name)) + + # Remove the ButtonThrower from the deviceButtonThrowers list. + idn = self.__inputDeviceNodes[device] + for bt in self.deviceButtonThrowers: + if idn.isAncestorOf(bt): self.deviceButtonThrowers.remove(bt) break + idn.removeNode() + del self.__inputDeviceNodes[device] def addAngularIntegrator(self): if not self.physicsMgrAngular: @@ -1944,10 +1919,6 @@ class ShowBase(DirectObject.DirectObject): # Check if there were newly connected devices. self.devices.update() - # Poll all connected devices. - for device in self.devices.devices: - device.poll() - # traverse the data graph. This reads all the control # inputs (from the mouse and keyboard, for instance) and also # directly acts upon them (for instance, to move the avatar). @@ -3163,6 +3134,8 @@ class ShowBase(DirectObject.DirectObject): setup_mouse = setupMouse setup_mouse_cb = setupMouseCB enable_software_mouse_pointer = enableSoftwareMousePointer + detach_input_device = detachInputDevice + attach_input_device = attachInputDevice add_angular_integrator = addAngularIntegrator enable_particles = enableParticles disable_particles = disableParticles