I agree, the doc really needs an example of the basic setup with one shift button. I'll add something to the buttons overview section, but in the meantime, here's a quick outline.
In my own cab, I use the Extra Ball button as the Shift button, because the Extra Ball function is something I use only rarely. The standard "Extra Ball" command is VP is the "2" key press, so that's going to be the action for the button. Since it's the Shift button, it has to be configured in the JSON as type="shift". And I want it to be a "Shift-OR" button, so that it still sends the "2" key when I press it, but only when I DON'T use it as a Shift button by pressing another button at the same time. To get Shift-OR behavior, I have to set up a pulse time with tPulse. So here's the buttons[] entry:
{
type: "shift", // this is a SHIFT button
shiftBits: 0x0001, // activate the 0x0001 shift bit when pressed
source: { type: "gpio", gp: 15 },
action: { type: "key", key: "2", }, // when NOT acting as a Shift button, send the "2" key ("Extra Ball" in VP)
tPulse: 20, // pulse the "2" key for 20 milliseconds when pressed WITHOUT another button press at the same time
},
Now for the buttons with two functions, depending on whether Extra Ball is being pressed at the same time. I use my Right MagnaSave and Right Flipper buttons as Audio Volume Up and Audio Volume Down when shifted. So normally, they have their standard VP mappings of "Right Ctrl" and "Right Shift", and when I press Extra Ball, they become F20 and F19 for Volume Up and Volume Down. (I map those obscure "F" keys as volume keys in PinVol, because there aren't any other applications that use them, so they don't trigger any unwanted side effects when used as global hotkeys for PinVol.) For each of these buttons, I have to set up TWO entries in the buttons[] array, one for the shifted function and one for the regular function.
// Right MagnaSave - regular function = Right Ctrl
{
type: "push",
source: { type: "gpio", gp: 10 },
action: { type: "key", key: "right ctrl" },
shiftMask: 0x0001, // only activate when shift bit...
shiftBits: 0x0000, // ...is NOT engaged (bits are all zero)
},
// Right MagnaSave - shifted function = F20 (Volume Up)
{
type: "push",
source: { type: "gpio", gp: 10 },
action: { type: "key", key: "f20",
shiftMask: 0x0001, // only activate when shift bit 0x0001...
shiftBits: 0x0001, // ... IS engaged (bits match 0x0001)
},
// Right Flipper - regular function = Right Shift
{
type: "push",
source: { type: "gpio", gp: 9 },
action: { type: "key", key: "right shift" },
shiftMask: 0x0001, // only activate when shift bit 0x0001...
shiftBits: 0x0000, // ... is NOT engaged (bits are all zero)
},
// Right Flipper - shifted function = F19 (Volume Down)
{
type: "push",
source: { type: "gpio", gp: 9 },
action: { type: "key", key: "f19",
shiftMask: 0x0001, // only activate when shift bit 0x0001...
shiftBits: 0x0001, // ... IS engaged (bits match 0x0001)
},
The shiftBits and shiftMask is where this gets ridiculously technical, and I don't want to make this post too long by repeating all of the documentation on that here, so here's a simple rule that will always work if you just want one Shift button: just make shiftMask 0x0001 in all cases, and make shiftBits 0x0001 for the shifted buttons and 0x0000 for the non-shifted buttons.
Edited by mjr, 10 June 2025 - 04:48 PM.