-
Notifications
You must be signed in to change notification settings - Fork 575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor streamdeck #2283
Refactor streamdeck #2283
Conversation
…t for the Stream Deck Mini
…hange the continuation bool to actually indicate the final page. Remove unconditional 180 degree rotation from the NSImage BMP extension. Fix NSImage flipping extension to actually do things the right way, instead of exactly the opposite of what was asked for. Fix up the deck properties to match the change in image behaviour
So, in theory, this should now work with all the Stream Deck models? |
Just the Original and Mini. Original V2 and XL are not yet implemented. I'm unsure if it's better to try and get them right without being able to test, or just leave them out. |
I'm not sure if I've got the Original or Original V2 - will check when I get home tonight. I've just ordered an XL & Mini, so I can test those when those arrive later in the week. Now I just need to get some GitHub Sponsors to fund all these fancy toys, haha! |
@latenitefilms Ok, since you've ordered hardware I'll add some speculative support for the XL. If we have that working, I think the differences for the Original V2 should be pretty minimal. |
…rt for the Stream Deck XL
ok @latenitefilms - awaiting your feedback on the XL :) |
I've just connected up the XL, Mini & Original. Unfortunately, the XL doesn't seem to be working at all yet. I'm using this test code: hs.console.clearConsole()
hs.streamdeck.init(function(connected, object)
if connected then
local horizontal, vertical = object:buttonLayout()
print(string.format("Stream Deck Connected: %s", object))
print(string.format(" - Serial Number: %s", object:serialNumber()))
print(string.format(" - Firmware Version: %s", object:firmwareVersion()))
print(string.format(" - Button Layout: %s %s", horizontal, vertical))
local i = 1
for _=1, vertical do
for _=1, horizontal do
local imageHolder = hs.canvas.new{x = 0, y = 0, h = 100, w = 100}
imageHolder[1] = {
frame = { h = 100, w = 100, x = 0, y = 0 },
fillColor = { alpha = 0.5, green = 1.0 },
type = "rectangle",
}
imageHolder[2] = {
frame = { h = 100, w = 100, x = 0, y = 40 },
text = i,
textAlignment = "center",
textColor = { white = 1.0 },
textSize = 20,
type = "text",
}
local textIcon = imageHolder:imageFromCanvas()
object:setButtonImage(i, textIcon)
i = i + 1
end
end
else
print(string.format("Stream Deck Disconnected: %s", object))
end
end) ...and I get: 2020-01-10 11:00:17: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck (Original v1), serial: AL29G1A09928 (0x6040009817f8)
2020-01-10 11:00:17: - Serial Number: AL29G1A09928
2020-01-10 11:00:17: - Firmware Version: 1.0.170118��
2020-01-10 11:00:17: - Button Layout: 5 3
2020-01-10 11:00:17: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck (Mini), serial: BL19H1A09914 (0x6040009f8cb8)
2020-01-10 11:00:17: - Serial Number: BL19H1A09914
2020-01-10 11:00:17: - Firmware Version: 2.02.001����
2020-01-10 11:00:17: - Button Layout: 3 2 It looks like this: Notice that the Stream Deck Mini has 1 to 6 in what I would say is the correct order, but the original Stream Deck goes the opposite direction. I think it makes more sense to go left to right, top to bottom, like you do on the Mini. I'll have a play and see if I can work out why the XL isn't being recognised. |
break; | ||
|
||
case USB_PID_STREAMDECK_ORIGINAL_V2: | ||
case USB_PID_STREAMDECK_XL: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing:
deck = [[HSStreamDeckDeviceXL alloc] initWithDevice:device manager:self];
break;
@@ -13,6 +13,8 @@ | |||
@import LuaSkin; | |||
|
|||
#import "HSStreamDeckDevice.h" | |||
#import "HSStreamDeckDeviceOriginal.h" | |||
#import "HSStreamDeckDeviceMini.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing:
#import "HSStreamDeckDeviceXL.h"
It looks like there's just a bunch of XL-related code missing, noted above. However, when I add this in, I do get a crash - a stack buffer overflow in Logs below:
|
FWIW, I tried using the below test code using the Stream Deck extension in the #2102 pull request: hs.console.clearConsole()
hs.streamdeck.init(function(connected, object)
if connected then
print(string.format("Stream Deck Connected: %s", object))
print(string.format(" - Serial Number: %s", object:serialNumber()))
print(string.format(" - Firmware Version: %s", object:firmwareVersion()))
for i=1, 32 do
local imageHolder = hs.canvas.new{x = 0, y = 0, h = 100, w = 100}
imageHolder[1] = {
frame = { h = 100, w = 100, x = 0, y = 0 },
fillColor = { alpha = 0.5, green = 1.0 },
type = "rectangle",
}
imageHolder[2] = {
frame = { h = 100, w = 100, x = 0, y = 40 },
text = i,
textAlignment = "center",
textColor = { white = 1.0 },
textSize = 20,
type = "text",
}
local textIcon = imageHolder:imageFromCanvas()
object:setButtonImage(i, textIcon)
i = i + 1
end
else
print(string.format("Stream Deck Disconnected: %s", object))
end
end) It gives me: 2020-01-10 16:48:38: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck, serial: �1.00.006��� (0x604001704178)
2020-01-10 16:48:38: - Serial Number: �1.00.006���
2020-01-10 16:48:38: - Firmware Version: 5I1A04271���
2020-01-10 16:48:38: -- Loading extension: canvas However, the screen looks like this: |
…eading code to react properly to differently sized requests
@latenitefilms apologies for missing out the XL initialisation, that was very stupid of me! I've just pushed that code and also (I hope) fixed |
As for the button directions thing, yes it's super annoying that the Original numbers right-to-left. I'm not sure how much I want to do to hide that. We probably ought to, but let's figure out the basic hardware support first and then see what we can do about the buttons. |
Awesome, thanks! This fixes the Here's my test code: hs.console.clearConsole()
hs.streamdeck.init(function(connected, object)
if connected then
local horizontal, vertical = object:buttonLayout()
print(string.format("Stream Deck Connected: %s", object))
print(string.format(" - Serial Number: %s", object:serialNumber()))
print(string.format(" - Firmware Version: %s", object:firmwareVersion()))
print(string.format(" - Button Layout: %s %s", horizontal, vertical))
local i = 1
for _=1, vertical do
for _=1, horizontal do
local imageHolder = hs.canvas.new{x = 0, y = 0, h = 100, w = 100}
imageHolder[1] = {
frame = { h = 100, w = 100, x = 0, y = 0 },
fillColor = { alpha = 0.5, green = 1.0 },
type = "rectangle",
}
imageHolder[2] = {
frame = { h = 100, w = 100, x = 0, y = 40 },
text = i,
textAlignment = "center",
textColor = { white = 1.0 },
textSize = 20,
type = "text",
}
local textIcon = imageHolder:imageFromCanvas()
print(string.format("Applying Image to Button: %s", i))
object:setButtonImage(i, textIcon)
i = i + 1
end
end
object:buttonCallback(function(obj, num, pressed)
if pressed then
log.df("Button #%s Pressed (%s)", num, obj)
else
log.df("Button #%s Released (%s)", num, obj)
end
end)
else
print(string.format("Stream Deck Disconnected: %s", object))
end
end) Here's what's in the Console: 2020-01-11 09:58:19: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck (XL), serial: 5I1A04271�������������������� �� (0x604000bcea78)
2020-01-11 09:58:19: - Serial Number: 5I1A04271�������������������� ��
2020-01-11 09:58:19: - Firmware Version: �1.00.006�������������������� ��
2020-01-11 09:58:19: - Button Layout: 8 4
2020-01-11 09:58:19: Applying Image to Button: 1
2020-01-11 09:58:19: Applying Image to Button: 2
2020-01-11 09:58:19: Applying Image to Button: 3
2020-01-11 09:58:19: Applying Image to Button: 4
2020-01-11 09:58:19: Applying Image to Button: 5
2020-01-11 09:58:19: Applying Image to Button: 6
2020-01-11 09:58:19: Applying Image to Button: 7
2020-01-11 09:58:19: Applying Image to Button: 8
2020-01-11 09:58:19: Applying Image to Button: 9
2020-01-11 09:58:19: Applying Image to Button: 10
2020-01-11 09:58:19: Applying Image to Button: 11
2020-01-11 09:58:19: Applying Image to Button: 12
2020-01-11 09:58:19: Applying Image to Button: 13
2020-01-11 09:58:19: Applying Image to Button: 14
2020-01-11 09:58:19: Applying Image to Button: 15
2020-01-11 09:58:19: Applying Image to Button: 16
2020-01-11 09:58:19: Applying Image to Button: 17
2020-01-11 09:58:19: Applying Image to Button: 18
2020-01-11 09:58:19: Applying Image to Button: 19
2020-01-11 09:58:19: Applying Image to Button: 20
2020-01-11 09:58:19: Applying Image to Button: 21
2020-01-11 09:58:19: Applying Image to Button: 22
2020-01-11 09:58:19: Applying Image to Button: 23
2020-01-11 09:58:19: Applying Image to Button: 24
2020-01-11 09:58:19: Applying Image to Button: 25
2020-01-11 09:58:19: Applying Image to Button: 26
2020-01-11 09:58:19: Applying Image to Button: 27
2020-01-11 09:58:19: Applying Image to Button: 28
2020-01-11 09:58:19: Applying Image to Button: 29
2020-01-11 09:58:19: Applying Image to Button: 30
2020-01-11 09:58:19: Applying Image to Button: 31
2020-01-11 09:58:19: Applying Image to Button: 32 May this offset is relevant? |
Hmm. It's really hard to know where the problem is - it's probably not that offset, that looks to be useful for reacting to button press events. It's either something to do with the JPEG image encoding code, or |
@latenitefilms I've ordered an XL, it's probably the only way we'll get this figured out :) |
… report length property
@latenitefilms I also forgot to actually include the JPEG rendering code the XL/v2 need 🙄 |
…ating it as if it starts at 1, so an offset is needed in the image writing
Woohoo! You're a GENIUS! It's getting close now! Using this test code: hs.console.clearConsole()
hs.streamdeck.init(function(connected, object)
if connected then
local horizontal, vertical = object:buttonLayout()
print(string.format("Stream Deck Connected: %s", object))
print(string.format(" - Serial Number: %s", object:serialNumber()))
print(string.format(" - Firmware Version: %s", object:firmwareVersion()))
print(string.format(" - Button Layout: %s %s", horizontal, vertical))
local i = 1
for _=1, vertical do
for _=1, horizontal do
local imageHolder = hs.canvas.new{x = 0, y = 0, h = 100, w = 100}
imageHolder[1] = {
frame = { h = 100, w = 100, x = 0, y = 0 },
fillColor = { alpha = 1, red = 1.0 },
type = "rectangle",
}
imageHolder[2] = {
frame = { h = 100, w = 100, x = 0, y = 40 },
text = i,
textAlignment = "center",
textColor = { white = 1.0 },
textSize = 20,
type = "text",
}
local textIcon = imageHolder:imageFromCanvas()
--print(string.format("Applying Image to Button: %s", i))
object:setButtonImage(i, textIcon)
i = i + 1
end
end
object:buttonCallback(function(obj, num, pressed)
if pressed then
print(string.format("Button #%s Pressed (%s)", num, obj))
else
--print(string.format("Button #%s Released (%s)", num, obj))
end
end)
else
print(string.format("Stream Deck Disconnected: %s", object))
end
end) I get: 2020-01-12 20:42:01: -- Loading extension: streamdeck
2020-01-12 20:42:02: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck (XL), serial: 5I1A04271����������������������� (0x604000f78438)
2020-01-12 20:42:02: - Serial Number: 5I1A04271�����������������������
2020-01-12 20:42:02: - Firmware Version: �1.00.006�����������������������
2020-01-12 20:42:02: - Button Layout: 8 4
2020-01-12 20:42:02: -- Loading extension: canvas
2020-01-12 20:42:02: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck (Original v1), serial: AL29G1A09928 (0x604001366978)
2020-01-12 20:42:02: - Serial Number: AL29G1A09928
2020-01-12 20:42:02: - Firmware Version: 1.0.170118��
2020-01-12 20:42:02: - Button Layout: 5 3
2020-01-12 20:42:02: Stream Deck Connected: hs.streamdeck: Elgato Stream Deck (Mini), serial: BL19H1A09914 (0x604001c7d478)
2020-01-12 20:42:02: - Serial Number: BL19H1A09914
2020-01-12 20:42:02: - Firmware Version: 2.02.001����
2020-01-12 20:42:02: - Button Layout: 3 2 However, when I pressed button #1 on the Stream Deck XL I get: 2020-01-12 20:43:54: Button #186 Pressed (hs.streamdeck: Elgato Stream Deck (XL), serial: 5I1A04271�������������������� �� (0x604001f33e38)) When I pressed button #32 on the Stream Deck XL I get: 2020-01-12 20:44:46: Button #217 Pressed (hs.streamdeck: Elgato Stream Deck (XL), serial: 5I1A04271�������������������� �� (0x604002145b78)) When I press button 1 on the Stream Deck Mini I get: 2020-01-12 20:45:38: Button #62 Pressed (hs.streamdeck: Elgato Stream Deck (Mini), serial: BL19H1A09914 (0x6040024208b8)) When I press button #6 on the Stream Deck Mini I get: 2020-01-12 20:45:59: Button #67 Pressed (hs.streamdeck: Elgato Stream Deck (Mini), serial: BL19H1A09914 (0x6040025614b8)) The original Stream Deck works as expected. FWIW, I vote to make button 1 on the top left on the original Stream Deck. Thanks again for all your help and support @cmsj - absolute legend! |
… of its image rendering bugs
Ok, with that last commit, I believe I've fully fixed the button drawing on the XL 😁 |
Not sure what to do about the buttons - those HID values appear to either be deck specific, or more likely, are semi-random. Right now button 1 on my XL is identifying as |
This is the line that needs to change to fix the buttons IDs: int button = IOHIDElementGetCookie(element) - 84; For example, to fix the Stream Deck XL, I used: int button = IOHIDElementGetCookie(element) - 84 - 185; |
@latenitefilms I don't believe it's possible to do that because the numbers are not universal - like I said, button 1 on my XL is currently identifying as 208, so subtracting 185 wouldn't yield 1. |
@cmsj - Sorry, posted before I saw your message. That's weird. On another note, we could do something similar to this for the original Stream Deck button order. |
@cmsj - In regards to button order, maybe have a look at this code and see if it sparks any ideas? https://github.com/Hammerspoon/hammerspoon/pull/2102/files#diff-aa78f02549f5b46cdaf02e368a6a834dR39 |
Or more specifically: https://github.com/Hammerspoon/hammerspoon/pull/2102/files#diff-9fb411bfe3bb7555b1b323372604eefeR14 There seems to be offset for the XL: https://github.com/Hammerspoon/hammerspoon/pull/2102/files#diff-aa78f02549f5b46cdaf02e368a6a834dR100 |
@latenitefilms yeah we're going to have to switch to using IOHIDReports, which is a bit of a shame, but I think it should be doable. My current plan is to cache the state of the buttons in the device object and when we get a report, see which buttons have changed, and call the user's callback accordingly, then update the cache. It ought to perform the same from the user's point of view, and means we'll have consistend 1-n button numbering across everything (and yes, I'll reverse the order on the Original decks, which will break existing configs, but it'll be better in the long run). |
Ah, so that's what you were talking about regarding |
Run out of time for tonight, but this is getting close to making the button numbers correspond to what they should be. It's still buggy though - the last button won't work, lots of the XL buttons (oddly) don't respond at all, and there are several arrays that are still zero-indexed and need to move to being one-indexed. (In short, don't bother testing this until I've had a chance to fix it up tomorrow!) |
@cmsj - Whilst I think of it, a few potential feature requests/fixes before you wrap up this branch:
|
FWIW - My latest test code: hs.console.clearConsole()
streamDecks = {}
hs.streamdeck.init(function(connected, object)
if connected then
local horizontal, vertical = object:buttonLayout()
print(string.format("Stream Deck Connected: %s", object))
print(string.format(" - Serial Number: %s", object:serialNumber()))
print(string.format(" - Firmware Version: %s", object:firmwareVersion()))
print(string.format(" - Button Layout: %s %s", horizontal, vertical))
streamDecks[object:serialNumber()] = object
local sd = streamDecks[object:serialNumber()]
local i = 1
for _=1, vertical do
for _=1, horizontal do
local imageHolder = hs.canvas.new{x = 0, y = 0, h = 100, w = 100}
imageHolder[1] = {
frame = { h = 100, w = 100, x = 0, y = 0 },
fillColor = { alpha = 1, red = 1.0 },
type = "rectangle",
}
imageHolder[2] = {
frame = { h = 100, w = 100, x = 0, y = 40 },
text = i,
textAlignment = "center",
textColor = { white = 1.0 },
textSize = 20,
type = "text",
}
local textIcon = imageHolder:imageFromCanvas()
--print(string.format("Applying Image to Button: %s", i))
sd:setButtonImage(i, textIcon)
i = i + 1
end
end
sd:buttonCallback(function(obj, num, pressed)
if pressed then
print(string.format("Button #%s Pressed (%s)", num, obj))
else
--print(string.format("Button #%s Released (%s)", num, obj))
end
end)
else
local horizontal, vertical = object:buttonLayout()
print(string.format("Stream Deck Disconnected: %s", object))
print(string.format(" - Serial Number: %s", object:serialNumber()))
print(string.format(" - Firmware Version: %s", object:firmwareVersion()))
print(string.format(" - Button Layout: %s %s", horizontal, vertical))
end
end) |
…er buffer for HID reports (1024, even though the XL deck seems to top out at 512 bytes)
I'm not going to make Buttons are now working and numbered correctly on Original v1 and Mini decks, but for some reason I don't yet understand, on the XL, most of the buttons don't report anything, only the last row. I wonder if this also affected @octplane's PR... |
Amazing work! I confess I've been running my own HS build which works for my Mini for too long. I'm also using the stock application because I'm too lazy to configure HS properly for my deck... This merge will create the occasion to work on my universal menu navigation system... |
@octplane as soon as this is finished and working, I'll merge it and cut a HS release - I want it in the release builds for me :D |
Hmm, ok, this is weird, now my XL deck is registering button presses properly, even though I don't think I've changed anything. Seems like I might be about ready to declare victory - @latenitefilms could you give it a test on your decks and see if everything is working? |
Amazing work @cmsj ! All seems to work correctly as intended with the Original, XL and Mini connected! The only remaining question I have is around garbage collection. It seems when you "reload" Hammerspoon, the callback is still being triggered? Is there some garbage collection missing from Objective-C land? Also, when you quit Hammerspoon, whatever's on the Stream Deck display "sticks". Should the Stream Deck's be "reset" during garbage collection? |
Hmm, I'm not able to reproduce the reload bug you describe - if I comment out my call to I did think about resetting the deck unconditionally, but it occurred to me that people might want to render something pretty to it and have it stay like that, but I realise that's quite a niche requirement, but it'd certainly be prettier than looking at the default Elgato logo screen. The easy workaround of course is to put a call to I'm happy to discuss those things further, but for now neither of them is a regression caused by this PR, so I'm going to go ahead and merge and then release. Thanks for reporting your testing findings! |
I'm going to merge this as-is, because I remain unconvinced about the other way of doing button inputs, and I want to have a release with support for the Stream Deck Mini :)