@NVAccess @ppatel I can't get at the shift at a low enough level to capture what I want, though.
Tapping shift when speech is going pauses and resumes. That's *exactly* what i want my airpod to do. Sure, having a fancy say all just for the headset would be nifty, but just being able to pause and resume would be a major step up from the nothing we have now.
That shift must be intercepted at a fairly low level, though. even an unmapped headset key interrupts say all, I presume because NVDA treats it as a keypress.
@cachondo @NVAccess @ppatel Put this in scratchpad\globalPlugins. If it breaks, you get to keep both pieces. I've tested it with the play key on my keyboard and NVDA 2026.1 beta 8. It should work going back to 2025.
from globalPluginHandler import GlobalPlugin
import inputCore
from scriptHandler import script
import queueHandler
import speech
from speech import sayAll
import tones
VK_MEDIA_PLAY_PAUSE = 0xB3
class GlobalPlugin(GlobalPlugin):
def __init__(self):
super().__init__()
self.patched = False
# We're dealing with low-level keyboard processing, so patch on the first press of play/pause.
# In case this breaks, I still need a keyboard.
@script(gesture="kb:mediaPlayPause", description="Patch")
def script_patch(self, gesture):
if not self.patched:
self.patch()
tones.beep(2000, 50)
def patch(self):
inputCore.decide_handleRawKey.register(self.handle)
self.patched = True
def terminate(self):
if self.patched:
inputCore.decide_handleRawKey.unregister(self.handle)
self.patched = False
def handle(self, vkCode=None, pressed=None, **kwargs):
if vkCode != VK_MEDIA_PLAY_PAUSE or not pressed:
return True
if not sayAll.SayAllHandler.isRunning():
sayAll.SayAllHandler.readText(sayAll.CURSOR.CARET, startedFromScript=True)
return False
if speech.getState().isPaused:
queueHandler.queueFunction(queueHandler.eventQueue, speech.pauseSpeech, False)
return False
else:
queueHandler.queueFunction(queueHandler.eventQueue, speech.pauseSpeech, True)
return False