Missing "Down" Events

This is an offshoot of my previous thread regarding DirectEntry and events–in short, while the problem that I was encountering there seems to be solved, the lack of certain events is still causing a problem, and I’m not convinced that the issue stems from DirectEntry.

In short, under certain circumstances–I think related to the stashing and un-stashing of certain MouseWatcher nodes–I seem to lose arrow-key-related “down”-events until I press the key in question a second time.

And I do seem to be losing the events themselves: I have some calls to “accept” tying events to a specific method, and that method doesn’t appear to be fired in the case of the “missing” events. Further, previous testing with a call to “messenger.toggleVerbose” showed that the events in question weren’t showing up.

As I’ve written this, a thought has occurred to me: Could it be that, somewhere in the business of stashing and un-stashing, a previous “down”-event isn’t being marked as “completed” (or some such)?

That is, if the code run in response to a “down”-event causes the then-current MouseWatcher to be stashed and another un-stashed, could it be that the “down”-event isn’t being concluded, and that as a result it’s only after a subsequent “up”-event that a new “down”-event of that sort may be fired?

With that thought, here below is a short test-program that displays the behaviour in question on my machine.

To perform the test, run the program, and note the two displays: the top one shows a response to the “down” arrow-key, and the bottom one shows the stashing-state of the MouseWatcher.

With that in mind, press-hold-and-release the “down” arrow-key, then the “up” arrow-key, and then the “down” arrow-key again.

You should find that the last of those, the second “down” arrow-key press, should only report an “release” event, not a “press” event.

from panda3d.core import loadPrcFile, loadPrcFileData
from direct.showbase.ShowBase import ShowBase

from panda3d.core import ButtonThrower, MouseWatcher
from direct.gui.DirectGui import *

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.label1 = DirectLabel(text = "",
                                  scale = 0.1,
                                  pos = (0, 0, 0.25),
                                  frameSize = (-10, 10, -2, 2))

        self.label2 = DirectLabel(text = "",
                                  scale = 0.1,
                                  pos = (0, 0, -0.25),
                                  frameSize = (-10, 10, -2, 2))

        self.accept("arrow_down", self.keyUpdate, [1])
        self.accept("arrow_down-up", self.keyUpdate, [0])
        self.accept("arrow_up-up", self.resetStashing)

        self.keyboardOnlyThrower = ButtonThrower("keyboardOnlyThrower")
        self.keyboardOnlyWatcher = MouseWatcher("keyboardOnlyWatcher")
        self.keyboardOnlyWatcher.addChild(self.keyboardOnlyThrower)
        base.mouseWatcher.getParent().node().addChild(self.keyboardOnlyWatcher)
        self.keyboardOnlyNodePath = self.mouseWatcher.getParent().find("keyboardOnlyWatcher")
        self.keyboardOnlyNodePath.stash()

    def resetStashing(self):
        self.setStashState(0)

        self.label1["text"] = "Key state: "
        self.label1.setText()

    def openDialogue(self):
        self.dialogue.show()

    def keyUpdate(self, state):
        self.label1["text"] = "Key state: " + str(state)
        self.label1.setText()

        if state == 1:
            self.setStashState(1)

    def setStashState(self, state):
        if state == 1:
            if not self.mouseWatcher.isStashed():
                self.mouseWatcher.stash()
                self.keyboardOnlyNodePath.unstash()
                self.label2["text"] = "Main watcher is STASHED"
        else:
            if self.mouseWatcher.isStashed():
                self.mouseWatcher.unstash()
                self.keyboardOnlyNodePath.stash()
                self.label2["text"] = "Main watcher is UN-stashed"

app = Game()
app.run()

Interestingly, the problem doesn’t seem to occur if I instead use “raw” events, which does at least provide a workaround.

So, I suppose that my question is this: If it is as I guess, how might I (perhaps manually) “conclude” the relevant “down” events so that they might be fired again?