Hi Adafruit team,
I had a question related to the Macropad RP2040 Hotkeys demo.
On a typical keyboard, with a browser application in focus, pressing the SPACEBAR = Page Down.
And when SHIFT is held in combination = Page Up.
You can continue to hold the SHIFT key and tap the SPACEBAR to Page Up again and again.
This works on the Macropad, however only for the first time SHIFT is held in combination with the SPACEBAR.
Continuing to hold down SHIFT and tapping the space bar results in first a Page Up as expected but the it continues as Page Down. The SHIFT key not being registered.
Is there a way to persist the continually held SHIFT key across multiple key input commands?
I hope this is enough information. Please let me know if you'd like more detail.
Thank you,
Andrew
https://www.adafruit.com/product/5128
Macropad RP2040 - Hotkeys demo and continually holding SHIFT
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- somenice
- Posts: 3
- Joined: Tue Nov 23, 2010 11:48 pm
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Macropad RP2040 - Hotkeys demo and continually holding S
Andrew,
Are you using kbd.press and Keycode.SHIFT right now. eg:
Are you using kbd.press and Keycode.SHIFT right now. eg:
Code: Select all
# Press and hold the shifted '1' key to get '!' (exclamation mark).
kbd.press(Keycode.SHIFT, Keycode.ONE)
- somenice
- Posts: 3
- Joined: Tue Nov 23, 2010 11:48 pm
Re: Macropad RP2040 - Hotkeys demo and continually holding S
Hey thank you for the reply,
I'm running a version of the hotkey example.
https://learn.adafruit.com/macropad-hotkeys/
The combination of keys is as follows:
HOLD = (0x101010, 'Shift', [Keycode.SHIFT]),
then
TAP = (0x101010, 'Space', ' '),
As a note, I get expected behaviour when I continually
HOLD = (0x101010, 'Cmd', [Keycode.COMMAND]),
then
TAP = (0x101010, 'Tab', [Keycode.TAB]),
Kind of hard to describe so please let me know if you need more information.
Thanks again,
Andrew
I'm running a version of the hotkey example.
https://learn.adafruit.com/macropad-hotkeys/
The combination of keys is as follows:
HOLD = (0x101010, 'Shift', [Keycode.SHIFT]),
then
TAP = (0x101010, 'Space', ' '),
As a note, I get expected behaviour when I continually
HOLD = (0x101010, 'Cmd', [Keycode.COMMAND]),
then
TAP = (0x101010, 'Tab', [Keycode.TAB]),
Kind of hard to describe so please let me know if you need more information.
Thanks again,
Andrew
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Macropad RP2040 - Hotkeys demo and continually holding S
Looking at the MACROPAD HotKeys guide it looks like there are multiple place the example script is releasing all the keys. It might be possible to be more strategic about which keys are being released or when to use these:
Code: Select all
macropad.keyboard.release_all()
macropad.keyboard.release(item)
Code: Select all
"""
A macro/hotkey program for Adafruit MACROPAD. Macro setups are stored in the
/macros folder (configurable below), load up just the ones you're likely to
use. Plug into computer's USB port, use dial to select an application macro
set, press MACROPAD keys to send key sequences and other USB protocols.
"""
# pylint: disable=import-error, unused-import, too-few-public-methods
import os
import time
import displayio
import terminalio
from adafruit_display_shapes.rect import Rect
from adafruit_display_text import label
from adafruit_macropad import MacroPad
# CONFIGURABLES ------------------------
MACRO_FOLDER = '/macros'
# CLASSES AND FUNCTIONS ----------------
class App:
""" Class representing a host-side application, for which we have a set
of macro sequences. Project code was originally more complex and
this was helpful, but maybe it's excessive now?"""
def __init__(self, appdata):
self.name = appdata['name']
self.macros = appdata['macros']
def switch(self):
""" Activate application settings; update OLED labels and LED
colors. """
group[13].text = self.name # Application name
for i in range(12):
if i < len(self.macros): # Key in use, set label + LED color
macropad.pixels[i] = self.macros[i][0]
group[i].text = self.macros[i][1]
else: # Key not in use, no label or LED
macropad.pixels[i] = 0
group[i].text = ''
macropad.keyboard.release_all()
macropad.consumer_control.release()
macropad.mouse.release_all()
macropad.stop_tone()
macropad.pixels.show()
macropad.display.refresh()
# INITIALIZATION -----------------------
macropad = MacroPad()
macropad.display.auto_refresh = False
macropad.pixels.auto_write = False
# Set up displayio group with all the labels
group = displayio.Group()
for key_index in range(12):
x = key_index % 3
y = key_index // 3
group.append(label.Label(terminalio.FONT, text='', color=0xFFFFFF,
anchored_position=((macropad.display.width - 1) * x / 2,
macropad.display.height - 1 -
(3 - y) * 12),
anchor_point=(x / 2, 1.0)))
group.append(Rect(0, 0, macropad.display.width, 12, fill=0xFFFFFF))
group.append(label.Label(terminalio.FONT, text='', color=0x000000,
anchored_position=(macropad.display.width//2, -2),
anchor_point=(0.5, 0.0)))
macropad.display.show(group)
# Load all the macro key setups from .py files in MACRO_FOLDER
apps = []
files = os.listdir(MACRO_FOLDER)
files.sort()
for filename in files:
if filename.endswith('.py'):
try:
module = __import__(MACRO_FOLDER + '/' + filename[:-3])
apps.append(App(module.app))
except (SyntaxError, ImportError, AttributeError, KeyError, NameError,
IndexError, TypeError) as err:
pass
if not apps:
group[13].text = 'NO MACRO FILES FOUND'
macropad.display.refresh()
while True:
pass
last_position = None
last_encoder_switch = macropad.encoder_switch_debounced.pressed
app_index = 0
apps[app_index].switch()
# MAIN LOOP ----------------------------
while True:
# Read encoder position. If it's changed, switch apps.
position = macropad.encoder
if position != last_position:
app_index = position % len(apps)
apps[app_index].switch()
last_position = position
# Handle encoder button. If state has changed, and if there's a
# corresponding macro, set up variables to act on this just like
# the keypad keys, as if it were a 13th key/macro.
macropad.encoder_switch_debounced.update()
encoder_switch = macropad.encoder_switch_debounced.pressed
if encoder_switch != last_encoder_switch:
last_encoder_switch = encoder_switch
if len(apps[app_index].macros) < 13:
continue # No 13th macro, just resume main loop
key_number = 12 # else process below as 13th macro
pressed = encoder_switch
else:
event = macropad.keys.events.get()
if not event or event.key_number >= len(apps[app_index].macros):
continue # No key events, or no corresponding macro, resume loop
key_number = event.key_number
pressed = event.pressed
# If code reaches here, a key or the encoder button WAS pressed/released
# and there IS a corresponding macro available for it...other situations
# are avoided by 'continue' statements above which resume the loop.
sequence = apps[app_index].macros[key_number][2]
if pressed:
# 'sequence' is an arbitrary-length list, each item is one of:
# Positive integer (e.g. Keycode.KEYPAD_MINUS): key pressed
# Negative integer: (absolute value) key released
# Float (e.g. 0.25): delay in seconds
# String (e.g. "Foo"): corresponding keys pressed & released
# List []: one or more Consumer Control codes (can also do float delay)
# Dict {}: mouse buttons/motion (might extend in future)
if key_number < 12: # No pixel for encoder button
macropad.pixels[key_number] = 0xFFFFFF
macropad.pixels.show()
for item in sequence:
if isinstance(item, int):
if item >= 0:
macropad.keyboard.press(item)
else:
macropad.keyboard.release(-item)
elif isinstance(item, float):
time.sleep(item)
elif isinstance(item, str):
macropad.keyboard_layout.write(item)
elif isinstance(item, list):
for code in item:
if isinstance(code, int):
macropad.consumer_control.release()
macropad.consumer_control.press(code)
if isinstance(code, float):
time.sleep(code)
elif isinstance(item, dict):
if 'buttons' in item:
if item['buttons'] >= 0:
macropad.mouse.press(item['buttons'])
else:
macropad.mouse.release(-item['buttons'])
macropad.mouse.move(item['x'] if 'x' in item else 0,
item['y'] if 'y' in item else 0,
item['wheel'] if 'wheel' in item else 0)
if 'tone' in item:
if item['tone'] > 0:
macropad.stop_tone()
macropad.start_tone(item['tone'])
else:
macropad.stop_tone()
elif 'play' in item:
macropad.play_file(item['play'])
else:
# Release any still-pressed keys, consumer codes, mouse buttons
# Keys and mouse buttons are individually released this way (rather
# than release_all()) because pad supports multi-key, e.g.
# could have a meta key or right-mouse held down by one macro and
# press/release keys/buttons with others. Navigate popups, etc.
for item in sequence:
if isinstance(item, int):
if item >= 0:
macropad.keyboard.release(item)
elif isinstance(item, dict):
if 'buttons' in item:
if item['buttons'] >= 0:
macropad.mouse.release(item['buttons'])
elif 'tone' in item:
macropad.stop_tone()
macropad.consumer_control.release()
if key_number < 12: # No pixel for encoder button
macropad.pixels[key_number] = apps[app_index].macros[key_number][0]
macropad.pixels.show()
Please be positive and constructive with your questions and comments.