├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── doc ├── DeviceDetails.md ├── icon512.png ├── icon64.png └── images │ ├── banner │ ├── StreamDeckSharpBanner_150px.png │ └── StreamDeckSharpBanner_200px.png │ ├── minesweeper.jpg │ └── rainbow_example.png └── src ├── .gitignore ├── StreamDeckSharp.Tests ├── .editorconfig ├── AllHardwareInfoTestData.cs ├── ConcurrentBufferedQueueTests.cs ├── EnumerableCrossReferenceExtensions.cs ├── FakeStreamDeckHid.cs ├── GetInternalHardwareExtensions.cs ├── StreamDeckHidTestContext.cs ├── StreamDeckHidWriteTests.cs ├── StreamDeckInterfaceExtensionsTests.cs ├── StreamDeckIoBitmapKeyTests.cs ├── StreamDeckIoFirmwareTests.cs ├── StreamDeckIoHardwareTwins.cs ├── StreamDeckIoKeyTests.cs ├── StreamDeckIoSerialNumberTests.cs ├── StreamDeckSharp.Tests.csproj ├── TextWriterExtensions.cs ├── TheoryDataLinqExtensions.cs └── VerifiedSnapshots │ ├── StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput │ ├── Stream Deck MK.2.verified.txt │ ├── Stream Deck Mini Rev2.verified.txt │ ├── Stream Deck Mini.verified.txt │ ├── Stream Deck Rev2.verified.txt │ ├── Stream Deck XL Rev2.verified.txt │ ├── Stream Deck XL.verified.txt │ └── Stream Deck.verified.txt │ ├── StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput │ ├── Stream Deck MK.2_Value=0.verified.txt │ ├── Stream Deck MK.2_Value=100.verified.txt │ ├── Stream Deck MK.2_Value=33.verified.txt │ ├── Stream Deck MK.2_Value=66.verified.txt │ ├── Stream Deck Mini Rev2_Value=0.verified.txt │ ├── Stream Deck Mini Rev2_Value=100.verified.txt │ ├── Stream Deck Mini Rev2_Value=33.verified.txt │ ├── Stream Deck Mini Rev2_Value=66.verified.txt │ ├── Stream Deck Mini_Value=0.verified.txt │ ├── Stream Deck Mini_Value=100.verified.txt │ ├── Stream Deck Mini_Value=33.verified.txt │ ├── Stream Deck Mini_Value=66.verified.txt │ ├── Stream Deck Rev2_Value=0.verified.txt │ ├── Stream Deck Rev2_Value=100.verified.txt │ ├── Stream Deck Rev2_Value=33.verified.txt │ ├── Stream Deck Rev2_Value=66.verified.txt │ ├── Stream Deck XL Rev2_Value=0.verified.txt │ ├── Stream Deck XL Rev2_Value=100.verified.txt │ ├── Stream Deck XL Rev2_Value=33.verified.txt │ ├── Stream Deck XL Rev2_Value=66.verified.txt │ ├── Stream Deck XL_Value=0.verified.txt │ ├── Stream Deck XL_Value=100.verified.txt │ ├── Stream Deck XL_Value=33.verified.txt │ ├── Stream Deck XL_Value=66.verified.txt │ ├── Stream Deck_Value=0.verified.txt │ ├── Stream Deck_Value=100.verified.txt │ ├── Stream Deck_Value=33.verified.txt │ └── Stream Deck_Value=66.verified.txt │ ├── StreamDeckIoBitmapKeyTests.BasicBitmapKeyOutputAsExpected │ ├── Stream Deck MK.2.verified.txt │ ├── Stream Deck Mini Rev2.verified.txt │ ├── Stream Deck Mini.verified.txt │ ├── Stream Deck Rev2.verified.txt │ ├── Stream Deck XL Rev2.verified.txt │ ├── Stream Deck XL.verified.txt │ └── Stream Deck.verified.txt │ ├── StreamDeckIoBitmapKeyTests.SetBitmapResultsInExpectedOutput8PxTiles │ ├── Stream Deck MK.2.verified.txt │ ├── Stream Deck Mini Rev2.verified.txt │ ├── Stream Deck Mini.verified.txt │ ├── Stream Deck Rev2.verified.txt │ ├── Stream Deck XL Rev2.verified.txt │ ├── Stream Deck XL.verified.txt │ └── Stream Deck.verified.txt │ ├── StreamDeckIoHardwareTwins.HardwareTwinsHaveExpectedValues │ ├── Stream Deck MK.2.verified.txt │ ├── Stream Deck Mini Rev2.verified.txt │ ├── Stream Deck Mini.verified.txt │ ├── Stream Deck Rev2.verified.txt │ ├── Stream Deck XL Rev2.verified.txt │ ├── Stream Deck XL.verified.txt │ └── Stream Deck.verified.txt │ └── StreamDeckIoKeyTests.InputReportsBehaveAsExpected │ ├── StreamDeckMK2_EachKeyOnce.verified.txt │ ├── StreamDeckMK2_MultipleKeysDown.verified.txt │ ├── StreamDeckMini_EachKeyOnce.verified.txt │ ├── StreamDeckMini_MultipleKeysDown.verified.txt │ ├── StreamDeckXL_EachKeyOnce.verified.txt │ ├── StreamDeckXL_MultipleKeysDown.verified.txt │ ├── StreamDeck_EachKeyOnce.verified.txt │ └── StreamDeck_MultipleKeysDown.verified.txt └── StreamDeckSharp ├── AssemblyInfo.cs ├── Exceptions ├── StreamDeckException.cs └── StreamDeckNotFoundException.cs ├── Hardware.cs ├── IHardware.cs ├── IUsbHidHardware.cs ├── Internals ├── BasicHidClient.cs ├── CachedHidClient.cs ├── ConcurrentBufferedQueue.cs ├── DeviceListExtensions.cs ├── HidComDriverStreamDeck.cs ├── HidComDriverStreamDeckJpeg.cs ├── HidComDriverStreamDeckMini.cs ├── HidDeviceExtensions.cs ├── IStreamDeckHid.cs ├── IStreamDeckHidComDriver.cs ├── KeyBitmapExtensions.cs ├── OutputReportSplitter.cs ├── ReportReceivedEventArgs.cs ├── StreamDeckHidWrapper.cs ├── Throttle.cs └── UsbHardwareIdAndDriver.cs ├── StreamDeck.cs ├── StreamDeckDeviceReference.cs ├── StreamDeckListener.cs ├── StreamDeckSharp.csproj ├── StreamDeckSharp.nuspec ├── UsbConstants.cs ├── UsbVendorProductPair.cs └── icon.png /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | I'm trying to keep it up to date, but I'm a lazy bastard - when in doubt - check out the commit log ;-) 4 | 5 | ## [v5.0.0-preview] - 2023-08-01 6 | Again another preview because I'm pretty busy right now. 7 | - Restructure how the HID communication is abstracted internally. This is useful for consumers when Elgato releases new hardware that can be controlled with the same software. The user of the library can now register new devices with existing "HID drivers" (for example if a Stream Deck MK.3 is released) 8 | - `KeyBitmap.FromBgr24Array` was moved to `KeyBitmap.Create.FromBgr24Array` 9 | - More HID Verification Tests 10 | - Tested with real hardware 11 | - Stream Deck XL (FW v1.01.000) 12 | - Stream Deck Mini (FW v2.03.001) 13 | - Stream Deck (FW v1.0.191203) 14 | - Stream Deck MK.2 (FW v1.01.001) 15 | - Saw visual glitches in ImageGlitchTest.Rainbow without throttling 16 | - Added throttling to about 1,5 MB/s 17 | - Example video frame rate looked still ok with throttling 18 | 19 | ## [v4.0.0-preview] - 2022-11-18 20 | A preview version (v4.0.0-preview) is currently available on nuget. At the moment there isn't a migration guide, but Jon Skeet has a project that show a the changes he had to make: https://github.com/OpenMacroBoard/StreamDeckSharp/issues/45#issuecomment-1079933751 21 | Alternatively you can also take a look at the changes of the example projects: [Changes example projects](https://github.com/OpenMacroBoard/OpenMacroBoard.ExampleCollection/compare/1104c4f...3203bff) 22 | 23 | ## [3.2.0] - 2021-09-04 24 | - Support for Stream Deck MK.2 (by jdahlblom - https://github.com/OpenMacroBoard/StreamDeckSharp/pull/36) 25 | - Remove old cooldown mechanism and replace with a new throttle mechanism that limits the write speed 26 | - Fix a few reported gaps between buttons (again) 27 | 28 | ## [3.0.0] - 2021-03-05 29 | - Added HidSharp Source to SteamDeckSharp, because the original HidSharp does not have a strong name we sign it here and ship it with StreamDeckSharp. This is required so we can ship new versions of StreamDeckSharp that reference an already existing version of OpenStreamDeck.SDK 30 | 31 | ## [2.2.0] - 2020-08-24 32 | - Improve accuracy of the reported gaps between buttons (is used to draw "fullscreen" for example) 33 | 34 | ## [2.1.0] - 2020-08-22 35 | - Added `StreamDeck.CreateDeviceListener()` that allows to keep track of stream decks between reconnects and provides events to react to connection changes. 36 | 37 | ## [2.0.0] - 2020-06-05 38 | - Drop .NET 4 support. In theory one could still port back the code to .NET 4 but it's not worth it to still maintain that. netstandard20 is the only target from now on. 39 | 40 | ## [1.1.0] - 2020-03-14 41 | - A lot of code cleanup (+ static analyzers to enforce rules) 42 | - Added locks around hidstream access to fix some glitches 43 | - Switch to semantic(-ish) versioning 44 | - Fix alignment issue with keys that weren't 72px but 96px 45 | 46 | ## [0.3.5] - 2019-11-28 47 | ### Changed 48 | - Prepare for netstandard2 release 49 | - Switch to HidSharp (because of netstandard support) 50 | - Change the stream deck Xl jpg encoder (the old one was from WPF, which is not supported on netstandard) 51 | - Switch to new csproj format and embedded nuspec 52 | - Remove old build stuff (switched to nuke.build) 53 | - New icon for the nuget package 54 | 55 | ### Added 56 | - Support for Stream Deck Rev2 :tada: (Thanks to patrick-dmxc for reporting that) 57 | 58 | ## [0.3.4] - 2019-06-22 59 | ### Added 60 | - Stream Deck XL now correctly 61 | - reports firmware version 62 | - reports serial number 63 | - shows the logo 64 | - sets the brightness 65 | 66 | ### Changed 67 | - It's now possible to open devices without caching (not recommended) 68 | - Lower key cooldown for Mini and XL variant, because they don't suffer 69 | from images glitches like the classic stream deck. This improves the 70 | framerate for cached handles (try the video example ^^) 71 | - `IHardware` now has a `DeviceName` property 72 | - Cleaned up a lot of things 73 | 74 | ## [0.3.2] - 2019-06-19 75 | ### Added 76 | - Support for Stream Deck XL :tada: 77 | 78 | ## [0.3.1] - 2019-01-26 79 | ### Added 80 | - Implement `GridKeyPositionCollection` to make dealing with keyboard layouts simpler 81 | - New `IStreamDeckBoard` for easier access to `GridKeyPositionCollection` Keys 82 | (Use pattern matching on `IMacroBoard` to get `GridKeyPositionCollection`) 83 | - Methods to get serialnumber and firmware version from `IStreamDeckBoard` 84 | 85 | ### Changed 86 | - Change nuget package to license expression 87 | 88 | ## [0.2.0] - 2018-08-25 - *OpenMacroBoard* :tada: 89 | Elgato Systems released the *"Stream Deck Mini"* a few weeks ago so I decided to refactor 90 | `StreamDeckSharp` and use a more generic approach that makes it possible to implement 91 | many different macro boards (with LCD buttons) - even ones with buttons that are not placed in a grid layout (like keyboards). 92 | 93 | Because of this changes I decided to split `StreamDeckSharp` into a generic part called `OpenMacroBoard.SDK` 94 | and the StreamDeck specific part still called `StreamDeckSharp` and to change the organization name from `OpenStreamDeck` to `OpenMacroBoard` to reflect the generic nature. 95 | 96 | ### Added 97 | - `OpenMacroBoard.VirtualBoard` to allow developing software for macro boards (eg. Stream Deck) without real hardware. 98 | - A bunch of unit tests 99 | - `IKeyBitmapDataAccess` to retrieve raw RGB data from KeyBitmaps 100 | - `IKeyPositionCollection` to allow for complex key layouts. 101 | - `DrawFullScreenExtension` is now part of the library (instead of just an example project) 102 | - More unit tests 103 | - Support for different LCD key resolutions 104 | ### Changed 105 | - Interface name `IStreamDeck` to `IMacroBoard` 106 | - KeyId order for the stream deck changed to be more intuitive 107 | _(left-to-right and top-to-bottom)_ 108 | 109 | ## [0.1.10] - 2018-04-30 110 | ### Added 111 | - StreamDeck.EnumerateDevices to discover all connected StreamDeck devices. 112 | 113 | ## [0.1.9] - 2018-02-09 114 | ### Added 115 | - KeyBitmaps now overrides `Equals`, `GetHashCode`, `==` and `!=` and implements `IEquatable` 116 | ### Changed 117 | - Renamed many classes 118 | _(remove "StreamDeck"-prefix, thats what namespaces are for)_ 119 | - Moved examples to separate repository 120 | ### Fixed 121 | - Image glitches 122 | _Use the same buffer for hid reports and added a cooldown of 75ms for each key, 123 | this should prevent reuse of memory before async (overlapped) write is finished_ 124 | 125 | ## [0.1.8] - 2018-01-29 126 | ### Added 127 | - Example _"DrawFullScreen"_ 128 | _(Shows how to draw a single image that spans over all 15 keys)_ 129 | You should check out the video demo in the wiki ;-) 130 | ### Fixed 131 | - Button Images created with `FromRawBitmap` are now correctly flipped 132 | 133 | 134 | ## [0.1.7] - 2018-01-29 135 | ### Changed 136 | - HidLibrary optimizations (thanks to Sam Messina) 137 | - Don't copy write buffer. 138 | - Remove EventMonitor 139 | - Add DeviceChangeNotifier _(no more polling)_ 140 | - Number of write threads changed to one. 141 | 142 | ## [0.1.6] - 2018-01-27 143 | ### Changed 144 | - Target Framework now .Net 4.0 145 | _(XP should in theory be supported now ^^)_ 146 | - Remove async/await methods in HidLibrary to support 4.0 147 | 148 | ## [0.1.4] - 2018-01-27 149 | ### Changed 150 | - `IStreamDeck` no longer supports raw bitmap as argument. 151 | _(Use `StreamDeckKeyBitmap.FromRawBitmap`) instead_ 152 | - Factory methods for GDI and WPF stuff moved to `StreamDeckKeyBitmap` 153 | - Update _"Drawing"_ example to reflect that. 154 | 155 | ## [0.1.3] - 2018-01-27 156 | ### Added 157 | - New extension method for `IStreamDeck` to apply raw bitmap to buttons 158 | - HidLibrary forked and included as a submodule 159 | ### Changed 160 | - Changed IStreamDeck to support dis- and reconnects. 161 | _Backend doesn't support it now, but the interface is ready :-)_ 162 | - Refactoring HID communication to make `StreamDeckHID` simpler. 163 | ### Fixed 164 | - Exception in Dispose 165 | _ShowLogo tested if object was disposed after it was marked as such_ 166 | - Timeout for HID write operations, because they keep blocking forever after disconnecting (and reconnecting) the device. 167 | 168 | ## [0.1.2] - 2018-01-25 169 | ### Added 170 | - `IStreamDeck.ShowLogo()` to show the default stream deck logo 171 | - Extention methods for `IStreamDeck` to create button images from `System.Drawing` and `System.Windows` (WPF) elements. 172 | _This should make it easier to create button images programmatically (e.g. adding text to a button)_ 173 | - Example _"Drawing"_ 174 | 175 | ## [0.1.1] - 2017-06-29 176 | - First alpha release 177 | - HID communication based on [HidLibrary](https://github.com/mikeobrien/HidLibrary) 178 | - Examples _"Austria"_ and _"Rainbow"_ 179 | 180 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Christian Franzl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | ----------------- 6 | 7 | **StreamDeckSharp is a simple (unofficial) .NET interface for the [Elgato Stream Deck](https://www.elgato.com/en/gaming/stream-deck)** 8 | 9 | [![license](https://img.shields.io/github/license/OpenStreamDeck/StreamDeckSharp.svg)](https://github.com/OpenStreamDeck/StreamDeckSharp/blob/master/LICENSE.md) 10 | [![GitHub release](https://img.shields.io/github/release/OpenStreamDeck/StreamDeckSharp.svg)](https://github.com/OpenStreamDeck/StreamDeckSharp/releases) 11 | [![Nuget version](https://img.shields.io/nuget/v/streamdecksharp.svg)](https://www.nuget.org/packages/StreamDeckSharp/) 12 | 13 | #### [Recent Changes](CHANGELOG.md) 14 | 15 | ## Quickstart _(TL;DR)_ 16 | ***Should work on all major operating systems.*** 17 | I typically test on Windows and Linux (Debian, Ubuntu), but it should also work on macOS. See Wiki: [Supported Platforms](https://github.com/OpenMacroBoard/StreamDeckSharp/wiki/Supported-Platforms-and-Devices) 18 | 19 | 1. Add StreamDeckSharp reference (via nuget or download latest release) 20 | 2. Add a using directive for StreamDeckSharp: `using StreamDeckSharp;` 21 | 22 | I want to... | Code (C#) 23 | ------------------------- | --------------------------------------------------------- 24 | create a device reference | `var deck = StreamDeck.OpenDevice();` 25 | set the brightness | `deck.SetBrightness(50);` 26 | create bitmap for key | `var bitmap = KeyBitmap.Create.FromFile("icon.png")` 27 | set key image | `deck.SetKeyBitmap(keyId, bitmap)` 28 | clear key image | `deck.ClearKey(keyId)` 29 | process key events | `deck.KeyStateChanged += KeyHandler;` 30 | 31 | **Make sure to dispose the device reference correctly** _(use `using` whenever possible)_ 32 | 33 | ## Examples 34 | If you want to see some examples take a look at the [example projects](https://github.com/OpenMacroBoard/StreamDeckSharp.ExampleCollection). 35 | Here is a short example called "Austria". Copy the code and start hacking :wink: 36 | 37 | ```C# 38 | using System; 39 | using OpenMacroBoard.SDK; 40 | using StreamDeckSharp; 41 | 42 | namespace StreamDeckSharp.Examples.Austria 43 | { 44 | class Program 45 | { 46 | static void Main(string[] args) 47 | { 48 | //This example is designed for the 5x3 (original) Stream Deck. 49 | 50 | //Create some color we use later to draw the flag of austria 51 | var red = KeyBitmap.Create.FromRgb(237, 41, 57); 52 | var white = KeyBitmap.Create.FromRgb(255, 255, 255); 53 | var rowColors = new KeyBitmap[] { red, white, red }; 54 | 55 | //Open the Stream Deck device 56 | using (var deck = StreamDeck.OpenDevice()) 57 | { 58 | deck.SetBrightness(100); 59 | 60 | //Send the bitmap informaton to the device 61 | for (int i = 0; i < deck.Keys.Count; i++) 62 | deck.SetKeyBitmap(i, rowColors[i / 5]); 63 | 64 | Console.ReadKey(); 65 | } 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | Here is what the "Rainbow" example looks like after pressing some keys 72 | 73 | ![Rainbow example photo](doc/images/rainbow_example.png?raw=true "Rainbow demo after pressing some keys") 74 | 75 | ### Play games on a StreamDeck 76 | For example minesweeper (take a look at the [example projects](https://github.com/OpenMacroBoard/StreamDeckSharp.ExampleCollection) if you are interested in that) 77 | 78 | 79 | 80 | ### You can even play videos on a StreamDeck 81 | Here is a short demo, playing a video on a stream deck device. 82 | 83 | [![Demo video of the example](https://i.imgur.com/8tlkaIg.png)](http://www.youtube.com/watch?v=tNwUG0sPmKw) 84 | _*The glitches you can see are already fixed._ 85 | 86 | More about that in the Wiki: [Play video on StreamDeck](https://github.com/OpenStreamDeck/StreamDeckSharp/wiki/Play-video-on-StreamDeck) 87 | 88 | --- 89 | 90 | ###### This project is not related to *Elgato Systems GmbH* in any way 91 | 92 | --- 93 | -------------------------------------------------------------------------------- /doc/DeviceDetails.md: -------------------------------------------------------------------------------- 1 | # Device Details 2 | 3 | ## Communication protocol(s) 4 | Different Devices use a different communication protocol to send bitmaps and key press events. Here is a collection of links with some implementations and details about the raw HID communication. 5 | - https://github.com/OpenMacroBoard/StreamDeckSharp/tree/master/src/StreamDeckSharp/Internals 6 | - https://gist.github.com/cliffrowley/d18a9c4569537b195f2b1eb6c68469e0 7 | - https://github.com/Julusian/node-elgato-stream-deck/tree/master/packages/core/src/models 8 | - https://den.dev/blog/reverse-engineering-stream-deck/ 9 | 10 | ## Stream Deck Rev1 11 | ### Teardown 12 | 13 | - https://www.youtube.com/watch?v=_ha2LdSAvXU (German, with Hardware Details) 14 | - https://www.youtube.com/watch?v=9apO--Qpz58 (English, 30min uncut) 15 | - https://www.youtube.com/watch?v=rOQu9_t2zOY (No commentary, just music) 16 | - https://imgur.com/gallery/XyKiL 17 | 18 | ### Hardware 19 | According to description of the German tear down video: 20 | 21 | | | | 22 | | ------------------------- | ----------------------------------- | 23 | | Display | LD043H10-40NC-K3 4,3" 480x272 Pixel | 24 | | Processor | 400MHZ ATSAM9G45 | 25 | | SDRAM | 512MBit winbond W9751G6KB5I | 26 | | Flash | 1GBit winbond W29N01GVSIAA | 27 | | 3,3V LDO | TLV1117-33 | 28 | | Adjustable LDO | TJ4203GSF5-ADJ (2x) | 29 | | Stepper for LED Backlight | AME5142 | 30 | 31 | ## Stream Deck Mini 32 | No details atm. 33 | 34 | ## Stream Deck XL 35 | 36 | ### Button Auto-Press Issue 37 | My (and according to a quick google search also some other customers) Stream Deck XL started to automatically 38 | trigger random buttons and sometimes event kept some buttons pressed for quite a while. 39 | 40 | It looks like the Stream Deck XL has a design issue where some buttons are randomly pressed. 41 | For my device (and according to some other customer videos) the following buttons seem to be affected: 42 | 43 | 🔲🔲🔲❌🔲🔲🔲🔲 44 | 🔲🔲🔲❌🔲🔲❌❌ 45 | 🔲🔲🔲❌🔲🔲🔲🔲 46 | 🔲🔲🔲❌🔲🔲🔲🔲 47 | 48 | - More details about the issue: 49 | - Reddit 50 | - https://www.reddit.com/r/ElgatoGaming/comments/cb6lf2/streamdeck_xl_a_button_keeps_pressing_itself/ 51 | - https://www.reddit.com/r/ElgatoGaming/comments/a3duq0/pressing_stream_deck_buttons_will_push_random/ 52 | - https://www.reddit.com/r/ElgatoGaming/comments/a3ce30/bug_stream_deck_buttons_randomly_press_themselves/ 53 | - Amazon 54 | - https://www.amazon.com/-/de/gp/customer-reviews/R3MUHRTR603RM2/ref=cm_cr_arp_d_viewpnt?ie=UTF8&ASIN=B07RL8H55Z#R3MUHRTR603RM2 55 | - YouTube 56 | - https://www.youtube.com/watch?v=e3pW7UE9Ngw 57 | - https://www.youtube.com/watch?v=oPw5ESehcUM 58 | 59 | I wrote a quick diagnostics tool OpenMacroBoard.Examples.ButtonPressDiagnostics -------------------------------------------------------------------------------- /doc/icon512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/doc/icon512.png -------------------------------------------------------------------------------- /doc/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/doc/icon64.png -------------------------------------------------------------------------------- /doc/images/banner/StreamDeckSharpBanner_150px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/doc/images/banner/StreamDeckSharpBanner_150px.png -------------------------------------------------------------------------------- /doc/images/banner/StreamDeckSharpBanner_200px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/doc/images/banner/StreamDeckSharpBanner_200px.png -------------------------------------------------------------------------------- /doc/images/minesweeper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/doc/images/minesweeper.jpg -------------------------------------------------------------------------------- /doc/images/rainbow_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/doc/images/rainbow_example.png -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # Benchmark Results 46 | BenchmarkDotNet.Artifacts/ 47 | 48 | # .NET Core 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | **/Properties/launchSettings.json 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | *.VC.VC.opendb 92 | 93 | # Visual Studio profiler 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.sap 98 | 99 | # TFS 2012 Local Workspace 100 | $tf/ 101 | 102 | # Guidance Automation Toolkit 103 | *.gpState 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper*/ 107 | *.[Rr]e[Ss]harper 108 | *.DotSettings.user 109 | 110 | # JustCode is a .NET coding add-in 111 | .JustCode 112 | 113 | # TeamCity is a build add-in 114 | _TeamCity* 115 | 116 | # DotCover is a Code Coverage Tool 117 | *.dotCover 118 | 119 | # Visual Studio code coverage results 120 | *.coverage 121 | *.coveragexml 122 | 123 | # NCrunch 124 | _NCrunch_* 125 | .*crunch*.local.xml 126 | nCrunchTemp_* 127 | 128 | # MightyMoose 129 | *.mm.* 130 | AutoTest.Net/ 131 | 132 | # Web workbench (sass) 133 | .sass-cache/ 134 | 135 | # Installshield output folder 136 | [Ee]xpress/ 137 | 138 | # DocProject is a documentation generator add-in 139 | DocProject/buildhelp/ 140 | DocProject/Help/*.HxT 141 | DocProject/Help/*.HxC 142 | DocProject/Help/*.hhc 143 | DocProject/Help/*.hhk 144 | DocProject/Help/*.hhp 145 | DocProject/Help/Html2 146 | DocProject/Help/html 147 | 148 | # Click-Once directory 149 | publish/ 150 | 151 | # Publish Web Output 152 | *.[Pp]ublish.xml 153 | *.azurePubxml 154 | # TODO: Comment the next line if you want to checkin your web deploy settings 155 | # but database connection strings (with potential passwords) will be unencrypted 156 | *.pubxml 157 | *.publishproj 158 | 159 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 160 | # checkin your Azure Web App publish settings, but sensitive information contained 161 | # in these scripts will be unencrypted 162 | PublishScripts/ 163 | 164 | # NuGet Packages 165 | *.nupkg 166 | # The packages folder can be ignored because of Package Restore 167 | **/packages/* 168 | # except build/, which is used as an MSBuild target. 169 | !**/packages/build/ 170 | # Uncomment if necessary however generally it will be regenerated when needed 171 | #!**/packages/repositories.config 172 | # NuGet v3's project.json files produces more ignorable files 173 | *.nuget.props 174 | *.nuget.targets 175 | 176 | # Microsoft Azure Build Output 177 | csx/ 178 | *.build.csdef 179 | 180 | # Microsoft Azure Emulator 181 | ecf/ 182 | rcf/ 183 | 184 | # Windows Store app package directories and files 185 | AppPackages/ 186 | BundleArtifacts/ 187 | Package.StoreAssociation.xml 188 | _pkginfo.txt 189 | *.appx 190 | 191 | # Visual Studio cache files 192 | # files ending in .cache can be ignored 193 | *.[Cc]ache 194 | # but keep track of directories ending in .cache 195 | !*.[Cc]ache/ 196 | 197 | # Others 198 | ClientBin/ 199 | ~$* 200 | *~ 201 | *.dbmdl 202 | *.dbproj.schemaview 203 | *.jfm 204 | *.pfx 205 | *.publishsettings 206 | orleans.codegen.cs 207 | 208 | # Since there are multiple workflows, uncomment next line to ignore bower_components 209 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 210 | #bower_components/ 211 | 212 | # RIA/Silverlight projects 213 | Generated_Code/ 214 | 215 | # Backup & report files from converting an old project file 216 | # to a newer Visual Studio version. Backup files are not needed, 217 | # because we have git ;-) 218 | _UpgradeReport_Files/ 219 | Backup*/ 220 | UpgradeLog*.XML 221 | UpgradeLog*.htm 222 | 223 | # SQL Server files 224 | *.mdf 225 | *.ldf 226 | *.ndf 227 | 228 | # Business Intelligence projects 229 | *.rdl.data 230 | *.bim.layout 231 | *.bim_*.settings 232 | 233 | # Microsoft Fakes 234 | FakesAssemblies/ 235 | 236 | # GhostDoc plugin setting file 237 | *.GhostDoc.xml 238 | 239 | # Node.js Tools for Visual Studio 240 | .ntvs_analysis.dat 241 | node_modules/ 242 | 243 | # Typescript v1 declaration files 244 | typings/ 245 | 246 | # Visual Studio 6 build log 247 | *.plg 248 | 249 | # Visual Studio 6 workspace options file 250 | *.opt 251 | 252 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 253 | *.vbw 254 | 255 | # Visual Studio LightSwitch build output 256 | **/*.HTMLClient/GeneratedArtifacts 257 | **/*.DesktopClient/GeneratedArtifacts 258 | **/*.DesktopClient/ModelManifest.xml 259 | **/*.Server/GeneratedArtifacts 260 | **/*.Server/ModelManifest.xml 261 | _Pvt_Extensions 262 | 263 | # Paket dependency manager 264 | .paket/paket.exe 265 | paket-files/ 266 | 267 | # FAKE - F# Make 268 | .fake/ 269 | 270 | # JetBrains Rider 271 | .idea/ 272 | *.sln.iml 273 | 274 | # CodeRush 275 | .cr/ 276 | 277 | # Python Tools for Visual Studio (PTVS) 278 | __pycache__/ 279 | *.pyc 280 | 281 | # Cake - Uncomment if you are using it 282 | # tools/** 283 | # !tools/packages.config 284 | 285 | # Tabs Studio 286 | *.tss 287 | 288 | # Telerik's JustMock configuration file 289 | *.jmconfig 290 | 291 | # BizTalk build output 292 | *.btp.cs 293 | *.btm.cs 294 | *.odx.cs 295 | *.xsd.cs 296 | 297 | # Approval received files 298 | *.received.* -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS1591: Missing XML comment for publicly visible type or member 4 | dotnet_diagnostic.CS1591.severity = none 5 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/AllHardwareInfoTestData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace StreamDeckSharp.Tests 6 | { 7 | public class AllHardwareInfoTestData : IEnumerable 8 | { 9 | public IEnumerator GetEnumerator() 10 | { 11 | return Hardware 12 | .GetInternalStreamDeckHardwareInfos() 13 | .Select(h => new object[] { h }) 14 | .GetEnumerator(); 15 | } 16 | 17 | IEnumerator IEnumerable.GetEnumerator() 18 | { 19 | return GetEnumerator(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/ConcurrentBufferedQueueTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using StreamDeckSharp.Internals; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace StreamDeckSharp.Tests 9 | { 10 | public class ConcurrentBufferedQueueTests 11 | { 12 | [Fact(DisplayName = "IsCompleted is false after construction.")] 13 | public void IsCompletedIsFalseAfterConstruction() 14 | { 15 | using var q = new ConcurrentBufferedQueue(); 16 | q.IsCompleted.Should().BeFalse(); 17 | } 18 | 19 | [Fact(DisplayName = "IsAddingCompleted is false after construction.")] 20 | public void IsAddingCompletedIsFalseAfterConstruction() 21 | { 22 | using var q = new ConcurrentBufferedQueue(); 23 | q.IsAddingCompleted.Should().BeFalse(); 24 | } 25 | 26 | [Fact(DisplayName = "Count is 0 after construction.")] 27 | public void CountIsZeroAfterConstruction() 28 | { 29 | using var q = new ConcurrentBufferedQueue(); 30 | q.Count.Should().Be(0); 31 | } 32 | 33 | [Fact(DisplayName = "IsAddingCompleted and IsCompleted is true after CompleteAdding is called (if queue was empty).")] 34 | public void CompleteAddingAnEmptyQueue() 35 | { 36 | using var q = new ConcurrentBufferedQueue(); 37 | 38 | q.CompleteAdding(); 39 | q.IsAddingCompleted.Should().BeTrue(); 40 | q.IsCompleted.Should().BeTrue(); 41 | } 42 | 43 | [Fact(DisplayName = "Adding an element increases Count.")] 44 | public void AddingAnElementIncreasesCount() 45 | { 46 | using var q = new ConcurrentBufferedQueue(); 47 | 48 | q.Add(1, "Hallo"); 49 | q.Count.Should().Be(1); 50 | 51 | q.Add(2, "Hallo"); 52 | q.Count.Should().Be(2); 53 | } 54 | 55 | [Fact(DisplayName = "Adding and elements with same id doesn't increase the Count value.")] 56 | public void AddingAnElementWithSameIdDoesNotIncreasesCount() 57 | { 58 | using var q = new ConcurrentBufferedQueue(); 59 | 60 | q.Add(1, "Hallo"); 61 | q.Add(1, "Hallo"); 62 | 63 | q.Count.Should().Be(1); 64 | } 65 | 66 | [Fact(DisplayName = "Taking an element retrieves the first added element (FIFO).")] 67 | public void TakingAnElementRetrievesTheFirstOne() 68 | { 69 | using var q = new ConcurrentBufferedQueue(); 70 | 71 | q.Add(3, "Hallo 3"); 72 | q.Add(7, "Hallo 7"); 73 | q.Add(1, "Hallo 1"); 74 | 75 | var result = q.Take(); 76 | result.Key.Should().Be(3); 77 | result.Value.Should().Be("Hallo 3"); 78 | 79 | result = q.Take(); 80 | result.Key.Should().Be(7); 81 | result.Value.Should().Be("Hallo 7"); 82 | 83 | result = q.Take(); 84 | result.Key.Should().Be(1); 85 | result.Value.Should().Be("Hallo 1"); 86 | } 87 | 88 | [Fact(DisplayName = "Taking an element blocks until an element is available.")] 89 | public void TakingAnElementBlocksUnitAnElementIsAvailable() 90 | { 91 | using var q = new ConcurrentBufferedQueue(); 92 | 93 | Task.Factory.StartNew(() => 94 | { 95 | #pragma warning disable S2925 // "Thread.Sleep" should not be used in tests 96 | Thread.Sleep(50); // Make sure Take is called before the next line is executed 97 | #pragma warning restore S2925 98 | q.Add(3, "Hallo"); 99 | }); 100 | 101 | var (success, key, value) = q.Take(); 102 | 103 | success.Should().BeTrue(); 104 | key.Should().Be(3); 105 | value.Should().Be("Hallo"); 106 | } 107 | 108 | [Fact(DisplayName = "Complete Adding causes waiting Take methods to throw.")] 109 | public void CompletingTheBufferWhileTakeIsWaitingThrowsAnException() 110 | { 111 | using var q = new ConcurrentBufferedQueue(); 112 | 113 | Task.Factory.StartNew(() => 114 | { 115 | #pragma warning disable S2925 // "Thread.Sleep" should not be used in tests 116 | Thread.Sleep(50); // Make sure Take is called before the next line is executed 117 | #pragma warning restore S2925 118 | q.CompleteAdding(); 119 | }); 120 | 121 | var (success, _, _) = q.Take(); 122 | success.Should().BeFalse(); 123 | } 124 | 125 | [Fact] 126 | public void CallTakeAfterCompletingThrowsAnException() 127 | { 128 | using var q = new ConcurrentBufferedQueue(); 129 | 130 | bool? success = null; 131 | var takeAction = new Action(() => (success, _, _) = q.Take()); 132 | 133 | q.CompleteAdding(); 134 | takeAction(); 135 | 136 | #pragma warning disable AV2202 // Prefer language syntax over explicit calls to underlying implementations 137 | success.HasValue.Should().BeTrue(); 138 | #pragma warning restore AV2202 139 | 140 | #pragma warning disable S3655 // Empty nullable value should not be accessed 141 | success.Value.Should().BeFalse(); 142 | #pragma warning restore S3655 143 | } 144 | 145 | [Fact] 146 | public void CompleteAddingPropertiesWorkAsExpected() 147 | { 148 | using var q = new ConcurrentBufferedQueue(); 149 | 150 | q.Add(1, "Hallo 1"); 151 | q.Add(2, "Hallo 2"); 152 | 153 | q.CompleteAdding(); 154 | q.IsAddingCompleted.Should().BeTrue(); 155 | q.IsCompleted.Should().BeFalse(); 156 | 157 | _ = q.Take(); 158 | _ = q.Take(); 159 | 160 | q.IsCompleted.Should().BeTrue(); 161 | } 162 | 163 | [Fact] 164 | public void OverrideQueueElementWithSameKey() 165 | { 166 | using var q = new ConcurrentBufferedQueue(); 167 | 168 | q.Add(1, "Hallo 1"); 169 | q.Add(2, "Hallo 2"); 170 | q.Add(1, "Hallo NEW"); 171 | 172 | var (success, key, value) = q.Take(); 173 | 174 | success.Should().BeTrue(); 175 | key.Should().Be(1); 176 | value.Should().Be("Hallo NEW"); 177 | } 178 | 179 | [Fact] 180 | public void AddAfterCompletionThrowsException() 181 | { 182 | using var q = new ConcurrentBufferedQueue(); 183 | 184 | q.CompleteAdding(); 185 | 186 | var addAction = new Action(() => q.Add(1, "Hallo 1")); 187 | 188 | addAction.Should().Throw(); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/EnumerableCrossReferenceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | #pragma warning disable AV1505 // Namespace should match with assembly name 4 | 5 | namespace System.Linq 6 | { 7 | internal static class EnumerableCrossReferenceExtensions 8 | { 9 | public static IEnumerable<(T1 Item1, T2 Item2)> CrossRef(this IEnumerable self, IEnumerable other) 10 | { 11 | foreach (var x in self) 12 | { 13 | foreach (var y in other) 14 | { 15 | yield return (x, y); 16 | } 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/FakeStreamDeckHid.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using StreamDeckSharp.Internals; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | 7 | namespace StreamDeckSharp.Tests 8 | { 9 | internal sealed class FakeStreamDeckHid : IStreamDeckHid 10 | { 11 | private readonly TextWriter log; 12 | private readonly UsbHardwareIdAndDriver hardware; 13 | 14 | public FakeStreamDeckHid(TextWriter log, UsbHardwareIdAndDriver hardware) 15 | { 16 | this.log = log ?? throw new ArgumentNullException(nameof(log)); 17 | this.hardware = hardware ?? throw new ArgumentNullException(nameof(hardware)); 18 | } 19 | 20 | public event EventHandler ReportReceived; 21 | public event EventHandler ConnectionStateChanged; 22 | 23 | public bool IsConnected => throw new NotImplementedException(); 24 | public int OutputReportLength => hardware.Driver.ExpectedOutputReportLength; 25 | 26 | public int BytesPerLineOutput { get; set; } = 16; 27 | 28 | /// 29 | /// The response this fake stream deck HID uses to respond 30 | /// to calls. 31 | /// 32 | public bool WriteFeatureResponse { get; set; } = true; 33 | 34 | /// 35 | /// The response this fake stream deck HID uses to respond 36 | /// to calls. 37 | /// 38 | public bool WriteReportResponse { get; set; } = true; 39 | 40 | /// 41 | /// A queue of answers this fake stream deck HID uses to respond 42 | /// to calls. 43 | /// 44 | public Queue<(byte Id, bool ReturnValue, byte[] FeatureData)> ReadFeatureResonseQueue { get; } = new(); 45 | 46 | public void Dispose() 47 | { 48 | log.WriteLine("Dispose()"); 49 | } 50 | 51 | public bool ReadFeatureData(byte id, out byte[] data) 52 | { 53 | var (expextedId, returnValue, featureData) = ReadFeatureResonseQueue.Dequeue(); 54 | 55 | if (expextedId != id) 56 | { 57 | throw new InvalidOperationException("Given id didn't match expectation."); 58 | } 59 | 60 | data = featureData; 61 | return returnValue; 62 | } 63 | 64 | public bool WriteFeature(byte[] featureData) 65 | { 66 | return log.WriteBinaryBlock("Feature", WriteFeatureResponse, featureData, BytesPerLineOutput); 67 | } 68 | 69 | public bool WriteReport(byte[] reportData) 70 | { 71 | return log.WriteBinaryBlock("Report", WriteReportResponse, reportData, BytesPerLineOutput); 72 | } 73 | 74 | public void FakeIncommingInputReport(byte[] data) 75 | { 76 | ReportReceived?.Invoke(this, new ReportReceivedEventArgs(data)); 77 | } 78 | 79 | public void FakeConnectionStateChange(ConnectionEventArgs args) 80 | { 81 | ConnectionStateChanged?.Invoke(this, args); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/GetInternalHardwareExtensions.cs: -------------------------------------------------------------------------------- 1 | using StreamDeckSharp.Internals; 2 | using System; 3 | 4 | namespace StreamDeckSharp.Tests 5 | { 6 | internal static class GetInternalHardwareExtensions 7 | { 8 | public static UsbHardwareIdAndDriver Internal(this IUsbHidHardware hardware) 9 | { 10 | if (hardware is not UsbHardwareIdAndDriver result) 11 | { 12 | throw new InvalidOperationException("Failed to cast to internal hardware."); 13 | } 14 | 15 | return result; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckHidTestContext.cs: -------------------------------------------------------------------------------- 1 | using StreamDeckSharp.Internals; 2 | using System; 3 | using System.IO; 4 | 5 | namespace StreamDeckSharp.Tests 6 | { 7 | internal sealed class StreamDeckHidTestContext : IDisposable 8 | { 9 | public StreamDeckHidTestContext(UsbHardwareIdAndDriver hardware) 10 | { 11 | Hardware = hardware ?? throw new ArgumentNullException(nameof(hardware)); 12 | Log = new StringWriter(); 13 | Hid = new FakeStreamDeckHid(Log, Hardware); 14 | Board = new BasicHidClient(Hid, Hardware.Keys, Hardware.Driver); 15 | } 16 | 17 | public TextWriter Log { get; } 18 | public FakeStreamDeckHid Hid { get; } 19 | public BasicHidClient Board { get; } 20 | public UsbHardwareIdAndDriver Hardware { get; } 21 | 22 | public void Dispose() 23 | { 24 | Log.Dispose(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckHidWriteTests.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.Meta.TestUtils; 2 | using StreamDeckSharp.Internals; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using VerifyXunit; 6 | using Xunit; 7 | 8 | namespace StreamDeckSharp.Tests 9 | { 10 | [UsesVerify] 11 | public class StreamDeckHidWriteTests 12 | { 13 | public ExtendedVerifySettings Verifier { get; } = DefaultVerifySettings.Build(); 14 | 15 | public static TheoryData GetDataForBrightnessTest() 16 | { 17 | var brighnessValues = new byte[] { 100, 0, 33, 66 }; 18 | 19 | return Hardware 20 | .GetInternalStreamDeckHardwareInfos() 21 | .Cast() 22 | .CrossRef(brighnessValues) 23 | .ToTheoryData(); 24 | } 25 | 26 | [Theory] 27 | [MemberData(nameof(GetDataForBrightnessTest))] 28 | internal async Task SettingBrightnessCausesTheExpectedOuput( 29 | IUsbHidHardware hardware, 30 | byte brightness 31 | ) 32 | { 33 | var hardwareInternal = hardware.Internal(); 34 | 35 | // Arrange 36 | Verifier.Initialize(); 37 | 38 | Verifier 39 | .UseFileNameAsDirectory() 40 | .UseFileName(hardwareInternal.DeviceName) 41 | .UseUniqueSuffix($"Value={brightness}"); 42 | 43 | using var context = new StreamDeckHidTestContext(hardwareInternal); 44 | 45 | // Act 46 | context.Board.SetBrightness(brightness); 47 | 48 | // Assert 49 | await Verifier.VerifyAsync(context.Log.ToString()); 50 | } 51 | 52 | [Theory] 53 | [ClassData(typeof(AllHardwareInfoTestData))] 54 | internal async Task CallingShowLogoCausesTheExpectedOutput(UsbHardwareIdAndDriver hardware) 55 | { 56 | // Arrange 57 | Verifier.Initialize(); 58 | 59 | Verifier 60 | .UseFileNameAsDirectory() 61 | .UseFileName(hardware.DeviceName); 62 | 63 | using var context = new StreamDeckHidTestContext(hardware); 64 | 65 | // Act 66 | context.Board.ShowLogo(); 67 | 68 | // Assert 69 | await Verifier.VerifyAsync(context.Log.ToString()); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckInterfaceExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using OpenMacroBoard.SDK; 3 | using Xunit; 4 | 5 | namespace StreamDeckSharp.Tests 6 | { 7 | public class StreamDeckInterfaceExtensionsTests 8 | { 9 | [Theory] 10 | [InlineData(0, false)] 11 | [InlineData(1, true)] 12 | [InlineData(11, true)] 13 | [InlineData(200, false)] 14 | public void TestKeyEventArgs(int keyId, bool isDown) 15 | { 16 | var eventArg = new KeyEventArgs(keyId, isDown); 17 | eventArg.Key.Should().Be(keyId); 18 | eventArg.IsDown.Should().Be(isDown); 19 | } 20 | 21 | [Fact] 22 | public void ConnectionEventArgsStoresTheValueAsExpected() 23 | { 24 | var eventArg = new ConnectionEventArgs(false); 25 | eventArg.NewConnectionState.Should().BeFalse(); 26 | 27 | eventArg = new ConnectionEventArgs(true); 28 | eventArg.NewConnectionState.Should().BeTrue(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckIoBitmapKeyTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using OpenMacroBoard.Meta.TestUtils; 3 | using OpenMacroBoard.SDK; 4 | using StreamDeckSharp.Internals; 5 | using System; 6 | using System.Threading.Tasks; 7 | using VerifyXunit; 8 | using Xunit; 9 | 10 | namespace StreamDeckSharp.Tests 11 | { 12 | [UsesVerify] 13 | public class StreamDeckIoBitmapKeyTests 14 | { 15 | public ExtendedVerifySettings Verifier { get; } = DefaultVerifySettings.Build(); 16 | 17 | [Theory] 18 | [ClassData(typeof(AllHardwareInfoTestData))] 19 | internal async Task BasicBitmapKeyOutputAsExpected(UsbHardwareIdAndDriver hardware) 20 | { 21 | // Arrange 22 | Verifier.Initialize(); 23 | 24 | Verifier 25 | .UseFileNameAsDirectory() 26 | .UseFileName(hardware.DeviceName) 27 | ; 28 | 29 | using var context = new StreamDeckHidTestContext(hardware); 30 | context.Hid.BytesPerLineOutput = 1024; 31 | 32 | var colorEachChannelDifferent = KeyBitmap.Create.FromRgb(0x6A, 0x4A, 0x4F); 33 | var red = KeyBitmap.Create.FromRgb(0xFF, 0, 0); 34 | var blue = KeyBitmap.Create.FromRgb(0, 0, 0xFF); 35 | 36 | // Act 37 | context.Log.WriteLine("Set all keys to a defined color."); 38 | context.Board.SetKeyBitmap(colorEachChannelDifferent); 39 | 40 | context.Log.WriteLine("Clear all keys."); 41 | context.Board.ClearKeys(); 42 | 43 | context.Log.WriteLine("Set key 0 to red."); 44 | context.Board.SetKeyBitmap(0, red); 45 | 46 | context.Log.WriteLine("Clear key 0."); 47 | context.Board.ClearKey(0); 48 | 49 | context.Log.WriteLine("Set key 0 to blue"); 50 | context.Board.SetKeyBitmap(0, blue); 51 | 52 | context.Log.WriteLine("Set key 0 to KeyBitmap.Black"); 53 | context.Board.SetKeyBitmap(0, KeyBitmap.Black); 54 | 55 | // Assert 56 | await Verifier.VerifyAsync(context.Log.ToString()); 57 | } 58 | 59 | [Theory] 60 | [ClassData(typeof(AllHardwareInfoTestData))] 61 | internal async Task SetBitmapResultsInExpectedOutput8PxTiles(UsbHardwareIdAndDriver hardware) 62 | { 63 | // Arrange 64 | Verifier.Initialize(); 65 | 66 | Verifier 67 | .UseFileNameAsDirectory() 68 | .UseFileName(hardware.DeviceName) 69 | ; 70 | 71 | using var context = new StreamDeckHidTestContext(hardware); 72 | context.Hid.BytesPerLineOutput = 1024; 73 | 74 | // create an image that probably survives JPEG compression 75 | // by using 8x8 tiles that have the same color and hopefully results in similar JPEGs 76 | // even when using different JPEG encoders. 77 | // In a best case scenario I hope this test is resistant against swapping JPEG encoders. 78 | 79 | // we have to create a key bitmap for the exact key size to prevent automatic resizing. 80 | 81 | var keySize = hardware.Keys.KeySize; 82 | 83 | const int tileSize = 8; 84 | const int channelCount = 3; 85 | 86 | // make sure the key size is divisible by 8 87 | (keySize % tileSize).Should().Be(0); 88 | 89 | var tileCnt = keySize / tileSize; 90 | var rnd = new Random(42); 91 | var colorBuffer = new byte[3]; 92 | 93 | (byte R, byte G, byte B) GetNextRndColor() 94 | { 95 | rnd.NextBytes(colorBuffer); 96 | return (colorBuffer[0], colorBuffer[1], colorBuffer[2]); 97 | } 98 | 99 | var pixelData = new byte[keySize * keySize * channelCount]; 100 | 101 | for (int y = 0; y < tileCnt; y++) 102 | { 103 | for (int x = 0; x < tileCnt; x++) 104 | { 105 | var (r, g, b) = GetNextRndColor(); 106 | 107 | for (int dy = 0; dy < tileSize; dy++) 108 | { 109 | for (int dx = 0; dx < tileSize; dx++) 110 | { 111 | var yOffset = (y * tileSize + dy) * keySize; 112 | var xOffset = x * tileSize + dx; 113 | var index = (yOffset + xOffset) * channelCount; 114 | 115 | pixelData[index] = b; 116 | pixelData[index + 1] = g; 117 | pixelData[index + 2] = r; 118 | } 119 | } 120 | } 121 | } 122 | 123 | var colorFullKeyBitmap = KeyBitmap.Create.FromBgr24Array(keySize, keySize, pixelData); 124 | 125 | // Act 126 | context.Board.SetKeyBitmap(0, colorFullKeyBitmap); 127 | 128 | // Assert 129 | await Verifier.VerifyAsync(context.Log.ToString()); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckIoFirmwareTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Xunit; 4 | 5 | namespace StreamDeckSharp.Tests 6 | { 7 | public class StreamDeckIoFirmwareTests 8 | { 9 | [Theory] 10 | [MemberData(nameof(TestData))] 11 | public void GettingFirmwareVersionWorksAsExpected( 12 | IUsbHidHardware hardware, 13 | byte[] featureData, 14 | string expectedParsedFirmware 15 | ) 16 | { 17 | var internalHardware = hardware.Internal(); 18 | 19 | // Arrange 20 | using var context = new StreamDeckHidTestContext(internalHardware); 21 | 22 | context.Hid.ReadFeatureResonseQueue.Enqueue((internalHardware.Driver.FirmwareVersionFeatureId, true, featureData)); 23 | 24 | // Act 25 | context.Board.GetFirmwareVersion().Should().Be(expectedParsedFirmware); 26 | 27 | // Assert 28 | context.Hid.ReadFeatureResonseQueue.Should().BeEmpty(); 29 | context.Log.ToString().Should().BeEmpty(); 30 | } 31 | 32 | /// 33 | /// Real world examples from my devices. 34 | /// 35 | [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Test before test-data.")] 36 | public static TheoryData TestData { get; } = new() 37 | { 38 | { 39 | Hardware.StreamDeck, 40 | new byte[] 41 | { 42 | 0x04, 0x55, 0xAA, 0xD4, 0x04, 0x31, 0x2E, 0x30, 43 | 0x2E, 0x31, 0x39, 0x31, 0x32, 0x30, 0x33, 0x00, 44 | 0x00, 45 | }, 46 | "1.0.191203" 47 | }, 48 | { 49 | Hardware.StreamDeckXL, 50 | new byte[] 51 | { 52 | 0x05, 0x0C, 0xAC, 0x74, 0x1D, 0x08, 0x31, 0x2E, 53 | 0x30, 0x30, 0x2E, 0x30, 0x30, 0x36, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | }, 57 | "1.00.006" 58 | }, 59 | { 60 | Hardware.StreamDeckMK2, 61 | new byte[] 62 | { 63 | 0x05, 0x0C, 0x9F, 0x8E, 0x29, 0xE3, 0x31, 0x2E, 64 | 0x30, 0x30, 0x2E, 0x30, 0x30, 0x31, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | }, 68 | "1.00.001" 69 | }, 70 | { 71 | Hardware.StreamDeckMini, 72 | new byte[] 73 | { 74 | 0x04, 0x00, 0x00, 0x00, 0x00, 0x32, 0x2E, 0x30, 75 | 0x33, 0x2E, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 76 | 0x00, 77 | }, 78 | "2.03.001" 79 | }, 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckIoHardwareTwins.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using OpenMacroBoard.Meta.TestUtils; 3 | using StreamDeckSharp.Internals; 4 | using System.Threading.Tasks; 5 | using VerifyXunit; 6 | using Xunit; 7 | 8 | namespace StreamDeckSharp.Tests 9 | { 10 | [UsesVerify] 11 | public class StreamDeckIoHardwareTwins 12 | { 13 | public ExtendedVerifySettings Verifier { get; } = DefaultVerifySettings.Build(); 14 | 15 | [Theory] 16 | [ClassData(typeof(AllHardwareInfoTestData))] 17 | internal async Task HardwareTwinsHaveExpectedValues(UsbHardwareIdAndDriver hardware) 18 | { 19 | // This test is to make sure we don't accidentially change some important constants. 20 | Verifier.Initialize(); 21 | 22 | Verifier 23 | .UseFileNameAsDirectory() 24 | .UseFileName(hardware.DeviceName) 25 | ; 26 | 27 | var hardwareJson = JsonConvert.SerializeObject(hardware); 28 | await Verifier.VerifyJsonAsync(hardwareJson); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckIoSerialNumberTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Xunit; 4 | 5 | namespace StreamDeckSharp.Tests 6 | { 7 | public class StreamDeckIoSerialNumberTests 8 | { 9 | [Theory] 10 | [MemberData(nameof(TestData))] 11 | internal void GettingSerialNumberWorksAsExpected( 12 | IUsbHidHardware hardware, 13 | byte[] featureData, 14 | string expectedParsedSerialNumber 15 | ) 16 | { 17 | var internalHardware = hardware.Internal(); 18 | 19 | // Arrange 20 | using var context = new StreamDeckHidTestContext(internalHardware); 21 | 22 | context.Hid.ReadFeatureResonseQueue.Enqueue((internalHardware.Driver.SerialNumberFeatureId, true, featureData)); 23 | 24 | // Act 25 | context.Board.GetSerialNumber().Should().Be(expectedParsedSerialNumber); 26 | 27 | // Assert 28 | context.Hid.ReadFeatureResonseQueue.Should().BeEmpty(); 29 | context.Log.ToString().Should().BeEmpty(); 30 | } 31 | 32 | /// 33 | /// Real world examples from my devices. 34 | /// 35 | [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Test before test-data.")] 36 | public static TheoryData TestData { get; } = new() 37 | { 38 | { 39 | Hardware.StreamDeck, 40 | new byte[] 41 | { 42 | 0x03, 0x55, 0xAA, 0xD3, 0x03, 0x41, 0x4C, 0x31, 43 | 0x35, 0x47, 0x31, 0x41, 0x30, 0x30, 0x36, 0x34, 44 | 0x36, 45 | }, 46 | "AL15G1A00646" 47 | }, 48 | { 49 | Hardware.StreamDeckXL, 50 | new byte[] 51 | { 52 | 0x06, 0x0C, 0x43, 0x4C, 0x31, 0x35, 0x4B, 0x31, 53 | 0x41, 0x30, 0x30, 0x31, 0x32, 0x38, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | }, 57 | "CL15K1A00128" 58 | }, 59 | { 60 | Hardware.StreamDeckMK2, 61 | new byte[] 62 | { 63 | 0x06, 0x0C, 0x44, 0x4C, 0x33, 0x30, 0x4B, 0x31, 64 | 0x41, 0x37, 0x39, 0x37, 0x34, 0x38, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | }, 68 | "DL30K1A79748" 69 | }, 70 | { 71 | Hardware.StreamDeckMini, 72 | new byte[] 73 | { 74 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4C, 0x31, 75 | 0x39, 0x48, 0x31, 0x41, 0x30, 0x34, 0x37, 0x32, 76 | 0x34, 77 | }, 78 | "BL19H1A04724" 79 | }, 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/StreamDeckSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 9 6 | false 7 | StreamDeckSharp.Tests 8 | StreamDeckSharp.Tests 9 | true 10 | True 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | all 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/TextWriterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace StreamDeckSharp.Tests 6 | { 7 | internal static class TextWriterExtensions 8 | { 9 | private static readonly (char, char)[] Lookup = 10 | Enumerable 11 | .Range(0, 256) 12 | .Select(b => ((byte)b).ToString("X2", CultureInfo.InvariantCulture)) 13 | .Select(s => (s[0], s[1])) 14 | .ToArray(); 15 | 16 | public static T WriteBinaryBlock( 17 | this TextWriter writer, 18 | string blockName, 19 | T returnValue, 20 | byte[] data, 21 | int bytesPerLine = 16 22 | ) 23 | { 24 | writer.WriteLine(blockName); 25 | writer.WriteLine("{"); 26 | writer.WriteBinaryAsHex(data, " ", bytesPerLine); 27 | writer.WriteLine(); 28 | writer.WriteLine($"}} => return {returnValue}"); 29 | writer.WriteLine(); 30 | 31 | return returnValue; 32 | } 33 | 34 | public static void WriteBinaryAsHex(this TextWriter writer, byte[] data, string indentation = "", int bytesPerLine = 16) 35 | { 36 | if (data.Length == 0) 37 | { 38 | return; 39 | } 40 | 41 | for (int i = 0; i < data.Length; i++) 42 | { 43 | if (i != 0) 44 | { 45 | if (i % bytesPerLine == 0) 46 | { 47 | writer.WriteLine(); 48 | } 49 | else 50 | { 51 | writer.Write(' '); 52 | } 53 | } 54 | 55 | if (i % bytesPerLine == 0) 56 | { 57 | writer.Write(indentation); 58 | } 59 | 60 | if (data[i] != 0) 61 | { 62 | writer.Write('0'); 63 | writer.Write('x'); 64 | writer.Write(Lookup[data[i]].Item1); 65 | writer.Write(Lookup[data[i]].Item2); 66 | } 67 | else 68 | { 69 | writer.Write("____"); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/TheoryDataLinqExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Xunit; 3 | 4 | namespace StreamDeckSharp.Tests 5 | { 6 | public static class TheoryDataLinqExtensions 7 | { 8 | public static TheoryData ToTheoryData(this IEnumerable source) 9 | { 10 | var data = new TheoryData(); 11 | 12 | foreach (var item in source) 13 | { 14 | data.Add(item); 15 | } 16 | 17 | return data; 18 | } 19 | 20 | public static TheoryData ToTheoryData(this IEnumerable<(T1 Item1, T2 Item2)> source) 21 | { 22 | var data = new TheoryData(); 23 | 24 | foreach (var (item1, item2) in source) 25 | { 26 | data.Add(item1, item2); 27 | } 28 | 29 | return data; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck MK.2.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x02 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck Mini Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x0B 0x63 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck Mini.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x0B 0x63 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x02 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck XL Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x02 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck XL.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x02 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.CallingShowLogoCausesTheExpectedOutput/Stream Deck.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x0B 0x63 4 | } => return True 5 | 6 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck MK.2_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 ____ 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck MK.2_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x64 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck MK.2_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x21 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck MK.2_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x42 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini Rev2_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini Rev2_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x64 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini Rev2_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x21 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini Rev2_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x42 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x64 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x21 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Mini_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x42 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Rev2_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 ____ 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Rev2_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x64 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Rev2_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x21 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck Rev2_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x42 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL Rev2_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 ____ 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL Rev2_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x64 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL Rev2_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x21 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL Rev2_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x42 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 ____ 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x64 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x21 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck XL_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x03 0x08 0x42 0x23 0xB8 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ ____ ____ ____ ____ ____ ____ ____ 0xA5 0x49 0xCD 0x02 0xFE 0x7F ____ ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck_Value=0.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck_Value=100.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x64 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck_Value=33.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x21 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckHidWriteTests.SettingBrightnessCausesTheExpectedOuput/Stream Deck_Value=66.verified.txt: -------------------------------------------------------------------------------- 1 | Feature 2 | { 3 | 0x05 0x55 0xAA 0xD1 0x01 0x42 ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ 4 | ____ 5 | } => return True 6 | 7 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoBitmapKeyTests.SetBitmapResultsInExpectedOutput8PxTiles/Stream Deck MK.2.verified.txt: -------------------------------------------------------------------------------- 1 | Report 2 | { 3 | 0x02 0x07 ____ ____ 0xF8 0x03 ____ ____ 0xFF 0xD8 0xFF 0xE0 ____ 0x10 0x4A 0x46 0x49 0x46 ____ 0x01 0x01 0x01 ____ 0x60 ____ 0x60 ____ ____ 0xFF 0xDB ____ 0x84 ____ 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0xFF 0xC0 ____ 0x11 0x08 ____ 0x48 ____ 0x48 0x03 0x01 0x11 ____ 0x02 0x11 0x01 0x03 0x11 0x01 0xFF 0xC4 0x01 0xA2 ____ ____ 0x01 0x05 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x10 ____ 0x02 0x01 0x03 0x03 0x02 0x04 0x03 0x05 0x05 0x04 0x04 ____ ____ 0x01 0x7D 0x01 0x02 0x03 ____ 0x04 0x11 0x05 0x12 0x21 0x31 0x41 0x06 0x13 0x51 0x61 0x07 0x22 0x71 0x14 0x32 0x81 0x91 0xA1 0x08 0x23 0x42 0xB1 0xC1 0x15 0x52 0xD1 0xF0 0x24 0x33 0x62 0x72 0x82 0x09 0x0A 0x16 0x17 0x18 0x19 0x1A 0x25 0x26 0x27 0x28 0x29 0x2A 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF1 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0x01 ____ 0x03 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x11 ____ 0x02 0x01 0x02 0x04 0x04 0x03 0x04 0x07 0x05 0x04 0x04 ____ 0x01 0x02 0x77 ____ 0x01 0x02 0x03 0x11 0x04 0x05 0x21 0x31 0x06 0x12 0x41 0x51 0x07 0x61 0x71 0x13 0x22 0x32 0x81 0x08 0x14 0x42 0x91 0xA1 0xB1 0xC1 0x09 0x23 0x33 0x52 0xF0 0x15 0x62 0x72 0xD1 0x0A 0x16 0x24 0x34 0xE1 0x25 0xF1 0x17 0x18 0x19 0x1A 0x26 0x27 0x28 0x29 0x2A 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0xFF 0xDA ____ 0x0C 0x03 0x01 ____ 0x02 0x11 0x03 0x11 ____ 0x3F ____ 0xC9 0xAF 0xF3 0x08 0xFF ____ 0x9D 0xA3 0xF2 0xA6 0xBF 0xEC 0x88 0xFF ____ 0xA1 0x63 0xF4 0x76 0xBF 0xE2 0x0C 0xFF ____ 0xAC 0x13 0x0E 0xBF 0xEC 0xE0 0xFC 0x40 0xFA 0xC2 0xBF 0xE2 0xDC 0xFF ____ 0x02 0x0F 0xC9 0xBA 0xFD 0x60 0xF8 0xF3 0xEA 0xFA 0xFE 0xA8 0x3F 0xA3 0xCE 0x4A 0xBF 0x1A 0x3F 0xDE 0x93 0xFD 0x04 0x6B 0xD1 0x3F 0xE5 0xE4 0xFF ____ 0x2C 0xCA 0xF2 0x8F 0xF1 0xF4 0xF3 0xFA 0xFF ____ 0x68 0x0F 0xF5 0x74 0xFA 0x12 0xBF 0x86 0xCF 0x64 0xFE 0xE1 0x2B 0xFE 0x61 0x0F 0xD6 0x0F 0xF3 0x51 0xAF 0xFA 0xFF ____ 0x3F 0xBB 0xCF 0xB8 0xAB 0xE8 0xCF 0xE6 0x93 0xE9 0xCA 0xE3 0x3F 0xE5 0x80 0xFE 0xC3 0xAB 0xFE 0x60 0x0F 0xF5 0xD4 0xFC 0xDE 0xAF 0xED 0xD3 0xF9 0x94 0xFC 0x02 0xAF 0xD0 0x8F 0xE5 0xE3 0xD6 0xAB 0xF7 0x13 0xE5 0x8F 0x35 0xAF 0xF5 0xA8 0xFF ____ 0xBB 0xA3 0xF3 0x6E 0xBF 0x10 0x3E 0xEC 0xFE 0xCD 0x2B 0xFC 0x1A 0x3F 0xE7 0x4C 0xF7 0x2A 0xFF ____ 0xA4 0xE3 0xFE 0x28 0x0F 0xE5 0xEE 0xBF 0xAE 0xCF 0xFD 0x89 0x0F 0xDF 0x0A 0xFF ____ 0x36 0x8F 0xFC 0xE4 0x0F 0xE3 0x66 0xBF 0x94 0x8F 0xEC 0x63 0x76 0xBF 0xD7 0x93 0xFA 0x2C 0xFE 0x96 0x2B 0xFC 0x01 0x3F 0xE8 0xBC 0xFE 0xBD 0xAB 0xF8 0x9C 0xFF ____ 0x20 0xCF 0xF3 0xA9 0xAF 0xEE 0x03 0xFE 0xC0 0x0F 0xD1 0x6A 0xFE 0xF5 0x3F 0x80 0x0F 0xE7 0x92 0xBF 0x8D 0x8F 0xE5 0xB3 0xFB 0x83 0xAF 0x78 0xFF ____ 0x9C 0xF3 0xF9 0x6B 0xAF 0xF5 0x08 0xFF ____ 0x33 0x4F 0xEB 0x82 0xBF 0xA6 0x8F 0xF4 0x48 0xFE 0x2A 0xAB 0xFD 0x99 0x3F 0xDD 0xD3 0xEC 0x4A 0xFF ____ 0x85 0xF3 0xF1 0x53 0xF5 0x7E 0xBF 0xB6 0x8F 0xF8 0x58 0x3E 0x68 0xAF 0xF4 0xA4 0xFF ____ 0xB2 0xA3 0xF6 0x72 0xBF 0xE0 0xDC 0xFF ____ 0x94 0xA3 0xF1 0xBA 0xBF 0xB1 0xCF 0xFD 0x50 0xCF 0x09 0xAE 0xD3 0xFC 0x5D 0x3F 0x4E 0x6B 0xFE 0x9D 0xCF 0xF8 0xCE 0x24 0xAF 0xF3 0x94 0xFF ____ 0xB5 0xB3 0xF9 0xCE 0xAF 0xF6 0xD8 0xFF ____ 0x15 0x0F 0xEF 0xBE 0xBF 0x85 0x4F 0xF4 0x04 0xFE 0x53 0x6B 0xFD 0xB2 0x3F 0xE6 0xE8 0xFC 0xA3 0xAF 0xF8 0xA4 0x3F 0x48 0x3F 0xD3 0x92 0xBF 0xD7 0x93 0xFD 0x48 0x3F 0x9D 0x7A 0xFC 0xC8 0xFF ____ 0x10 0x8F 0xBD 0xEB 0xFE 0x29 0x8F 0xE9 0x03 0xE6 0x5A 0xFF ____ 0x43 0xCF 0xF5 0x74 0xE8 0x6B 0xFE 0xD6 0x8F 0xE5 4 | } => return True 5 | 6 | Report 7 | { 8 | 0x02 0x07 ____ 0x01 0xD1 ____ 0x01 ____ 0x93 0xF9 0x72 0xAF 0xC4 0x0F 0xC6 0xCF 0xD8 0xDA 0xFE 0x20 0x3C 0x03 0xCC 0xAB 0x13 0xFD 0xE8 0x3E 0x95 0xAF 0xE8 0xD3 0xF2 0xC3 0xF9 0x7A 0xAF 0xEC 0xE3 0xFB 0x40 0xFF ____ 0x47 0x4A 0xFF ____ 0x8F 0x43 0xF9 0xE4 0xC2 0xAF 0xFA 0x45 0x34 0x3F 0x87 0xFA 0xFF ____ 0x23 0x4F 0xFA 0x2F 0x3D 0x62 0xBF 0xE9 0xF0 0xFE 0x48 0x3E 0x57 0xAF 0xF1 0x48 0xFF ____ 0x41 0xCF 0xD0 0x3A 0xFF ____ 0xA4 0x53 0xFE 0x0C 0xCF 0xE3 0x6A 0xBF 0x23 0x3B 0xCF 0xF5 0x02 0xAF 0xF9 0xDC 0x3F 0xCC 0xF3 0xDF 0x6B 0xFD 0x5D 0x3F 0xE8 0x28 0xF8 0xFA 0xBF 0xE1 0x1C 0xFC 0x90 0xFE 0x5B 0x2B 0xFD 0x86 0x3F 0xEA 0x78 0xFA 0x66 0xBF 0xEC 0x78 0xFF ____ 0x2E 0x0F 0xCF 0x5A 0xFC 0x98 0xFF ____ 0x94 0x33 0xF5 0x1E 0xBF 0xE2 0xE0 0xFF ____ 0x46 0x4F 0x82 0x6B 0xFE 0xFE 0xCF 0xF2 0xF8 0xF8 0x32 0xBF 0xC6 0xF3 0xFD 0xE1 0x3F 0x62 0x2B 0xFB 0x78 0xFF ____ 0x1E 0xCF 0xEB 0x7A 0xBF 0xC8 0x53 0xFE 0x80 0xCF 0xC5 0xBA 0xFE 0x11 0x3F 0xCC 0xE3 0xE1 0x5A 0xFF ____ 0x6C 0xCF 0xFA 0x3C 0x3F 0x44 0x6B 0xFE 0x31 0x0F 0xF0 0x0C 0xFE 0x43 0x6B 0xFF ____ 0x4D 0xF3 0xE8 0xCF 0xFF 0xD9 0x01 0x03 0x03 0x02 0x04 0x03 0x05 0x05 0x04 0x04 ____ ____ 0x01 0x7D 0x01 0x02 0x03 ____ 0x04 0x11 0x05 0x12 0x21 0x31 0x41 0x06 0x13 0x51 0x61 0x07 0x22 0x71 0x14 0x32 0x81 0x91 0xA1 0x08 0x23 0x42 0xB1 0xC1 0x15 0x52 0xD1 0xF0 0x24 0x33 0x62 0x72 0x82 0x09 0x0A 0x16 0x17 0x18 0x19 0x1A 0x25 0x26 0x27 0x28 0x29 0x2A 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF1 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0x01 ____ 0x03 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x11 ____ 0x02 0x01 0x02 0x04 0x04 0x03 0x04 0x07 0x05 0x04 0x04 ____ 0x01 0x02 0x77 ____ 0x01 0x02 0x03 0x11 0x04 0x05 0x21 0x31 0x06 0x12 0x41 0x51 0x07 0x61 0x71 0x13 0x22 0x32 0x81 0x08 0x14 0x42 0x91 0xA1 0xB1 0xC1 0x09 0x23 0x33 0x52 0xF0 0x15 0x62 0x72 0xD1 0x0A 0x16 0x24 0x34 0xE1 0x25 0xF1 0x17 0x18 0x19 0x1A 0x26 0x27 0x28 0x29 0x2A 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0xFF 0xDA ____ 0x0C 0x03 0x01 ____ 0x02 0x11 0x03 0x11 ____ 0x3F ____ 0xC9 0xAF 0xF3 0x08 0xFF ____ 0x9D 0xA3 0xF2 0xA6 0xBF 0xEC 0x88 0xFF ____ 0xA1 0x63 0xF4 0x76 0xBF 0xE2 0x0C 0xFF ____ 0xAC 0x13 0x0E 0xBF 0xEC 0xE0 0xFC 0x40 0xFA 0xC2 0xBF 0xE2 0xDC 0xFF ____ 0x02 0x0F 0xC9 0xBA 0xFD 0x60 0xF8 0xF3 0xEA 0xFA 0xFE 0xA8 0x3F 0xA3 0xCE 0x4A 0xBF 0x1A 0x3F 0xDE 0x93 0xFD 0x04 0x6B 0xD1 0x3F 0xE5 0xE4 0xFF ____ 0x2C 0xCA 0xF2 0x8F 0xF1 0xF4 0xF3 0xFA 0xFF ____ 0x68 0x0F 0xF5 0x74 0xFA 0x12 0xBF 0x86 0xCF 0x64 0xFE 0xE1 0x2B 0xFE 0x61 0x0F 0xD6 0x0F 0xF3 0x51 0xAF 0xFA 0xFF ____ 0x3F 0xBB 0xCF 0xB8 0xAB 0xE8 0xCF 0xE6 0x93 0xE9 0xCA 0xE3 0x3F 0xE5 0x80 0xFE 0xC3 0xAB 0xFE 0x60 0x0F 0xF5 0xD4 0xFC 0xDE 0xAF 0xED 0xD3 0xF9 0x94 0xFC 0x02 0xAF 0xD0 0x8F 0xE5 0xE3 0xD6 0xAB 0xF7 0x13 0xE5 0x8F 0x35 0xAF 0xF5 0xA8 0xFF ____ 0xBB 0xA3 0xF3 0x6E 0xBF 0x10 0x3E 0xEC 0xFE 0xCD 0x2B 0xFC 0x1A 0x3F 0xE7 0x4C 0xF7 0x2A 0xFF ____ 0xA4 0xE3 0xFE 0x28 0x0F 0xE5 0xEE 0xBF 0xAE 0xCF 0xFD 0x89 0x0F 0xDF 0x0A 0xFF ____ 0x36 0x8F 0xFC 0xE4 0x0F 0xE3 0x66 0xBF 0x94 0x8F 0xEC 0x63 0x76 0xBF 0xD7 0x93 0xFA 0x2C 0xFE 0x96 0x2B 0xFC 0x01 0x3F 0xE8 0xBC 0xFE 0xBD 0xAB 0xF8 0x9C 0xFF ____ 0x20 0xCF 0xF3 0xA9 0xAF 0xEE 0x03 0xFE 0xC0 0x0F 0xD1 0x6A 0xFE 0xF5 0x3F 0x80 0x0F 0xE7 0x92 0xBF 0x8D 0x8F 0xE5 0xB3 0xFB 0x83 0xAF 0x78 0xFF ____ 0x9C 0xF3 0xF9 0x6B 0xAF 0xF5 0x08 0xFF ____ 0x33 0x4F 0xEB 0x82 0xBF 0xA6 0x8F 0xF4 0x48 0xFE 0x2A 0xAB 0xFD 0x99 0x3F 0xDD 0xD3 0xEC 0x4A 0xFF ____ 0x85 0xF3 0xF1 0x53 0xF5 0x7E 0xBF 0xB6 0x8F 0xF8 0x58 0x3E 0x68 0xAF 0xF4 0xA4 0xFF ____ 0xB2 0xA3 0xF6 0x72 0xBF 0xE0 0xDC 0xFF ____ 0x94 0xA3 0xF1 0xBA 0xBF 0xB1 0xCF 0xFD 0x50 0xCF 0x09 0xAE 0xD3 0xFC 0x5D 0x3F 0x4E 0x6B 0xFE 0x9D 0xCF 0xF8 0xCE 0x24 0xAF 0xF3 0x94 0xFF ____ 0xB5 0xB3 0xF9 0xCE 0xAF 0xF6 0xD8 0xFF ____ 0x15 0x0F 0xEF 0xBE 0xBF 0x85 0x4F 0xF4 0x04 0xFE 0x53 0x6B 0xFD 0xB2 0x3F 0xE6 0xE8 0xFC 0xA3 0xAF 0xF8 0xA4 0x3F 0x48 0x3F 0xD3 0x92 0xBF 0xD7 0x93 0xFD 0x48 0x3F 0x9D 0x7A 0xFC 0xC8 0xFF ____ 0x10 0x8F 0xBD 0xEB 0xFE 0x29 0x8F 0xE9 0x03 0xE6 0x5A 0xFF ____ 0x43 0xCF 0xF5 0x74 0xE8 0x6B 0xFE 0xD6 0x8F 0xE5 9 | } => return True 10 | 11 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoBitmapKeyTests.SetBitmapResultsInExpectedOutput8PxTiles/Stream Deck Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | Report 2 | { 3 | 0x02 0x07 ____ ____ 0xF8 0x03 ____ ____ 0xFF 0xD8 0xFF 0xE0 ____ 0x10 0x4A 0x46 0x49 0x46 ____ 0x01 0x01 0x01 ____ 0x60 ____ 0x60 ____ ____ 0xFF 0xDB ____ 0x84 ____ 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0xFF 0xC0 ____ 0x11 0x08 ____ 0x48 ____ 0x48 0x03 0x01 0x11 ____ 0x02 0x11 0x01 0x03 0x11 0x01 0xFF 0xC4 0x01 0xA2 ____ ____ 0x01 0x05 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x10 ____ 0x02 0x01 0x03 0x03 0x02 0x04 0x03 0x05 0x05 0x04 0x04 ____ ____ 0x01 0x7D 0x01 0x02 0x03 ____ 0x04 0x11 0x05 0x12 0x21 0x31 0x41 0x06 0x13 0x51 0x61 0x07 0x22 0x71 0x14 0x32 0x81 0x91 0xA1 0x08 0x23 0x42 0xB1 0xC1 0x15 0x52 0xD1 0xF0 0x24 0x33 0x62 0x72 0x82 0x09 0x0A 0x16 0x17 0x18 0x19 0x1A 0x25 0x26 0x27 0x28 0x29 0x2A 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF1 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0x01 ____ 0x03 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x11 ____ 0x02 0x01 0x02 0x04 0x04 0x03 0x04 0x07 0x05 0x04 0x04 ____ 0x01 0x02 0x77 ____ 0x01 0x02 0x03 0x11 0x04 0x05 0x21 0x31 0x06 0x12 0x41 0x51 0x07 0x61 0x71 0x13 0x22 0x32 0x81 0x08 0x14 0x42 0x91 0xA1 0xB1 0xC1 0x09 0x23 0x33 0x52 0xF0 0x15 0x62 0x72 0xD1 0x0A 0x16 0x24 0x34 0xE1 0x25 0xF1 0x17 0x18 0x19 0x1A 0x26 0x27 0x28 0x29 0x2A 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0xFF 0xDA ____ 0x0C 0x03 0x01 ____ 0x02 0x11 0x03 0x11 ____ 0x3F ____ 0xC9 0xAF 0xF3 0x08 0xFF ____ 0x9D 0xA3 0xF2 0xA6 0xBF 0xEC 0x88 0xFF ____ 0xA1 0x63 0xF4 0x76 0xBF 0xE2 0x0C 0xFF ____ 0xAC 0x13 0x0E 0xBF 0xEC 0xE0 0xFC 0x40 0xFA 0xC2 0xBF 0xE2 0xDC 0xFF ____ 0x02 0x0F 0xC9 0xBA 0xFD 0x60 0xF8 0xF3 0xEA 0xFA 0xFE 0xA8 0x3F 0xA3 0xCE 0x4A 0xBF 0x1A 0x3F 0xDE 0x93 0xFD 0x04 0x6B 0xD1 0x3F 0xE5 0xE4 0xFF ____ 0x2C 0xCA 0xF2 0x8F 0xF1 0xF4 0xF3 0xFA 0xFF ____ 0x68 0x0F 0xF5 0x74 0xFA 0x12 0xBF 0x86 0xCF 0x64 0xFE 0xE1 0x2B 0xFE 0x61 0x0F 0xD6 0x0F 0xF3 0x51 0xAF 0xFA 0xFF ____ 0x3F 0xBB 0xCF 0xB8 0xAB 0xE8 0xCF 0xE6 0x93 0xE9 0xCA 0xE3 0x3F 0xE5 0x80 0xFE 0xC3 0xAB 0xFE 0x60 0x0F 0xF5 0xD4 0xFC 0xDE 0xAF 0xED 0xD3 0xF9 0x94 0xFC 0x02 0xAF 0xD0 0x8F 0xE5 0xE3 0xD6 0xAB 0xF7 0x13 0xE5 0x8F 0x35 0xAF 0xF5 0xA8 0xFF ____ 0xBB 0xA3 0xF3 0x6E 0xBF 0x10 0x3E 0xEC 0xFE 0xCD 0x2B 0xFC 0x1A 0x3F 0xE7 0x4C 0xF7 0x2A 0xFF ____ 0xA4 0xE3 0xFE 0x28 0x0F 0xE5 0xEE 0xBF 0xAE 0xCF 0xFD 0x89 0x0F 0xDF 0x0A 0xFF ____ 0x36 0x8F 0xFC 0xE4 0x0F 0xE3 0x66 0xBF 0x94 0x8F 0xEC 0x63 0x76 0xBF 0xD7 0x93 0xFA 0x2C 0xFE 0x96 0x2B 0xFC 0x01 0x3F 0xE8 0xBC 0xFE 0xBD 0xAB 0xF8 0x9C 0xFF ____ 0x20 0xCF 0xF3 0xA9 0xAF 0xEE 0x03 0xFE 0xC0 0x0F 0xD1 0x6A 0xFE 0xF5 0x3F 0x80 0x0F 0xE7 0x92 0xBF 0x8D 0x8F 0xE5 0xB3 0xFB 0x83 0xAF 0x78 0xFF ____ 0x9C 0xF3 0xF9 0x6B 0xAF 0xF5 0x08 0xFF ____ 0x33 0x4F 0xEB 0x82 0xBF 0xA6 0x8F 0xF4 0x48 0xFE 0x2A 0xAB 0xFD 0x99 0x3F 0xDD 0xD3 0xEC 0x4A 0xFF ____ 0x85 0xF3 0xF1 0x53 0xF5 0x7E 0xBF 0xB6 0x8F 0xF8 0x58 0x3E 0x68 0xAF 0xF4 0xA4 0xFF ____ 0xB2 0xA3 0xF6 0x72 0xBF 0xE0 0xDC 0xFF ____ 0x94 0xA3 0xF1 0xBA 0xBF 0xB1 0xCF 0xFD 0x50 0xCF 0x09 0xAE 0xD3 0xFC 0x5D 0x3F 0x4E 0x6B 0xFE 0x9D 0xCF 0xF8 0xCE 0x24 0xAF 0xF3 0x94 0xFF ____ 0xB5 0xB3 0xF9 0xCE 0xAF 0xF6 0xD8 0xFF ____ 0x15 0x0F 0xEF 0xBE 0xBF 0x85 0x4F 0xF4 0x04 0xFE 0x53 0x6B 0xFD 0xB2 0x3F 0xE6 0xE8 0xFC 0xA3 0xAF 0xF8 0xA4 0x3F 0x48 0x3F 0xD3 0x92 0xBF 0xD7 0x93 0xFD 0x48 0x3F 0x9D 0x7A 0xFC 0xC8 0xFF ____ 0x10 0x8F 0xBD 0xEB 0xFE 0x29 0x8F 0xE9 0x03 0xE6 0x5A 0xFF ____ 0x43 0xCF 0xF5 0x74 0xE8 0x6B 0xFE 0xD6 0x8F 0xE5 4 | } => return True 5 | 6 | Report 7 | { 8 | 0x02 0x07 ____ 0x01 0xD1 ____ 0x01 ____ 0x93 0xF9 0x72 0xAF 0xC4 0x0F 0xC6 0xCF 0xD8 0xDA 0xFE 0x20 0x3C 0x03 0xCC 0xAB 0x13 0xFD 0xE8 0x3E 0x95 0xAF 0xE8 0xD3 0xF2 0xC3 0xF9 0x7A 0xAF 0xEC 0xE3 0xFB 0x40 0xFF ____ 0x47 0x4A 0xFF ____ 0x8F 0x43 0xF9 0xE4 0xC2 0xAF 0xFA 0x45 0x34 0x3F 0x87 0xFA 0xFF ____ 0x23 0x4F 0xFA 0x2F 0x3D 0x62 0xBF 0xE9 0xF0 0xFE 0x48 0x3E 0x57 0xAF 0xF1 0x48 0xFF ____ 0x41 0xCF 0xD0 0x3A 0xFF ____ 0xA4 0x53 0xFE 0x0C 0xCF 0xE3 0x6A 0xBF 0x23 0x3B 0xCF 0xF5 0x02 0xAF 0xF9 0xDC 0x3F 0xCC 0xF3 0xDF 0x6B 0xFD 0x5D 0x3F 0xE8 0x28 0xF8 0xFA 0xBF 0xE1 0x1C 0xFC 0x90 0xFE 0x5B 0x2B 0xFD 0x86 0x3F 0xEA 0x78 0xFA 0x66 0xBF 0xEC 0x78 0xFF ____ 0x2E 0x0F 0xCF 0x5A 0xFC 0x98 0xFF ____ 0x94 0x33 0xF5 0x1E 0xBF 0xE2 0xE0 0xFF ____ 0x46 0x4F 0x82 0x6B 0xFE 0xFE 0xCF 0xF2 0xF8 0xF8 0x32 0xBF 0xC6 0xF3 0xFD 0xE1 0x3F 0x62 0x2B 0xFB 0x78 0xFF ____ 0x1E 0xCF 0xEB 0x7A 0xBF 0xC8 0x53 0xFE 0x80 0xCF 0xC5 0xBA 0xFE 0x11 0x3F 0xCC 0xE3 0xE1 0x5A 0xFF ____ 0x6C 0xCF 0xFA 0x3C 0x3F 0x44 0x6B 0xFE 0x31 0x0F 0xF0 0x0C 0xFE 0x43 0x6B 0xFF ____ 0x4D 0xF3 0xE8 0xCF 0xFF 0xD9 0x01 0x03 0x03 0x02 0x04 0x03 0x05 0x05 0x04 0x04 ____ ____ 0x01 0x7D 0x01 0x02 0x03 ____ 0x04 0x11 0x05 0x12 0x21 0x31 0x41 0x06 0x13 0x51 0x61 0x07 0x22 0x71 0x14 0x32 0x81 0x91 0xA1 0x08 0x23 0x42 0xB1 0xC1 0x15 0x52 0xD1 0xF0 0x24 0x33 0x62 0x72 0x82 0x09 0x0A 0x16 0x17 0x18 0x19 0x1A 0x25 0x26 0x27 0x28 0x29 0x2A 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF1 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0x01 ____ 0x03 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x11 ____ 0x02 0x01 0x02 0x04 0x04 0x03 0x04 0x07 0x05 0x04 0x04 ____ 0x01 0x02 0x77 ____ 0x01 0x02 0x03 0x11 0x04 0x05 0x21 0x31 0x06 0x12 0x41 0x51 0x07 0x61 0x71 0x13 0x22 0x32 0x81 0x08 0x14 0x42 0x91 0xA1 0xB1 0xC1 0x09 0x23 0x33 0x52 0xF0 0x15 0x62 0x72 0xD1 0x0A 0x16 0x24 0x34 0xE1 0x25 0xF1 0x17 0x18 0x19 0x1A 0x26 0x27 0x28 0x29 0x2A 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0xFF 0xDA ____ 0x0C 0x03 0x01 ____ 0x02 0x11 0x03 0x11 ____ 0x3F ____ 0xC9 0xAF 0xF3 0x08 0xFF ____ 0x9D 0xA3 0xF2 0xA6 0xBF 0xEC 0x88 0xFF ____ 0xA1 0x63 0xF4 0x76 0xBF 0xE2 0x0C 0xFF ____ 0xAC 0x13 0x0E 0xBF 0xEC 0xE0 0xFC 0x40 0xFA 0xC2 0xBF 0xE2 0xDC 0xFF ____ 0x02 0x0F 0xC9 0xBA 0xFD 0x60 0xF8 0xF3 0xEA 0xFA 0xFE 0xA8 0x3F 0xA3 0xCE 0x4A 0xBF 0x1A 0x3F 0xDE 0x93 0xFD 0x04 0x6B 0xD1 0x3F 0xE5 0xE4 0xFF ____ 0x2C 0xCA 0xF2 0x8F 0xF1 0xF4 0xF3 0xFA 0xFF ____ 0x68 0x0F 0xF5 0x74 0xFA 0x12 0xBF 0x86 0xCF 0x64 0xFE 0xE1 0x2B 0xFE 0x61 0x0F 0xD6 0x0F 0xF3 0x51 0xAF 0xFA 0xFF ____ 0x3F 0xBB 0xCF 0xB8 0xAB 0xE8 0xCF 0xE6 0x93 0xE9 0xCA 0xE3 0x3F 0xE5 0x80 0xFE 0xC3 0xAB 0xFE 0x60 0x0F 0xF5 0xD4 0xFC 0xDE 0xAF 0xED 0xD3 0xF9 0x94 0xFC 0x02 0xAF 0xD0 0x8F 0xE5 0xE3 0xD6 0xAB 0xF7 0x13 0xE5 0x8F 0x35 0xAF 0xF5 0xA8 0xFF ____ 0xBB 0xA3 0xF3 0x6E 0xBF 0x10 0x3E 0xEC 0xFE 0xCD 0x2B 0xFC 0x1A 0x3F 0xE7 0x4C 0xF7 0x2A 0xFF ____ 0xA4 0xE3 0xFE 0x28 0x0F 0xE5 0xEE 0xBF 0xAE 0xCF 0xFD 0x89 0x0F 0xDF 0x0A 0xFF ____ 0x36 0x8F 0xFC 0xE4 0x0F 0xE3 0x66 0xBF 0x94 0x8F 0xEC 0x63 0x76 0xBF 0xD7 0x93 0xFA 0x2C 0xFE 0x96 0x2B 0xFC 0x01 0x3F 0xE8 0xBC 0xFE 0xBD 0xAB 0xF8 0x9C 0xFF ____ 0x20 0xCF 0xF3 0xA9 0xAF 0xEE 0x03 0xFE 0xC0 0x0F 0xD1 0x6A 0xFE 0xF5 0x3F 0x80 0x0F 0xE7 0x92 0xBF 0x8D 0x8F 0xE5 0xB3 0xFB 0x83 0xAF 0x78 0xFF ____ 0x9C 0xF3 0xF9 0x6B 0xAF 0xF5 0x08 0xFF ____ 0x33 0x4F 0xEB 0x82 0xBF 0xA6 0x8F 0xF4 0x48 0xFE 0x2A 0xAB 0xFD 0x99 0x3F 0xDD 0xD3 0xEC 0x4A 0xFF ____ 0x85 0xF3 0xF1 0x53 0xF5 0x7E 0xBF 0xB6 0x8F 0xF8 0x58 0x3E 0x68 0xAF 0xF4 0xA4 0xFF ____ 0xB2 0xA3 0xF6 0x72 0xBF 0xE0 0xDC 0xFF ____ 0x94 0xA3 0xF1 0xBA 0xBF 0xB1 0xCF 0xFD 0x50 0xCF 0x09 0xAE 0xD3 0xFC 0x5D 0x3F 0x4E 0x6B 0xFE 0x9D 0xCF 0xF8 0xCE 0x24 0xAF 0xF3 0x94 0xFF ____ 0xB5 0xB3 0xF9 0xCE 0xAF 0xF6 0xD8 0xFF ____ 0x15 0x0F 0xEF 0xBE 0xBF 0x85 0x4F 0xF4 0x04 0xFE 0x53 0x6B 0xFD 0xB2 0x3F 0xE6 0xE8 0xFC 0xA3 0xAF 0xF8 0xA4 0x3F 0x48 0x3F 0xD3 0x92 0xBF 0xD7 0x93 0xFD 0x48 0x3F 0x9D 0x7A 0xFC 0xC8 0xFF ____ 0x10 0x8F 0xBD 0xEB 0xFE 0x29 0x8F 0xE9 0x03 0xE6 0x5A 0xFF ____ 0x43 0xCF 0xF5 0x74 0xE8 0x6B 0xFE 0xD6 0x8F 0xE5 9 | } => return True 10 | 11 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoBitmapKeyTests.SetBitmapResultsInExpectedOutput8PxTiles/Stream Deck XL Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | Report 2 | { 3 | 0x02 0x07 ____ ____ 0xF8 0x03 ____ ____ 0xFF 0xD8 0xFF 0xE0 ____ 0x10 0x4A 0x46 0x49 0x46 ____ 0x01 0x01 0x01 ____ 0x60 ____ 0x60 ____ ____ 0xFF 0xDB ____ 0x84 ____ 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0xFF 0xC0 ____ 0x11 0x08 ____ 0x60 ____ 0x60 0x03 0x01 0x11 ____ 0x02 0x11 0x01 0x03 0x11 0x01 0xFF 0xC4 0x01 0xA2 ____ ____ 0x01 0x05 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x10 ____ 0x02 0x01 0x03 0x03 0x02 0x04 0x03 0x05 0x05 0x04 0x04 ____ ____ 0x01 0x7D 0x01 0x02 0x03 ____ 0x04 0x11 0x05 0x12 0x21 0x31 0x41 0x06 0x13 0x51 0x61 0x07 0x22 0x71 0x14 0x32 0x81 0x91 0xA1 0x08 0x23 0x42 0xB1 0xC1 0x15 0x52 0xD1 0xF0 0x24 0x33 0x62 0x72 0x82 0x09 0x0A 0x16 0x17 0x18 0x19 0x1A 0x25 0x26 0x27 0x28 0x29 0x2A 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF1 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0x01 ____ 0x03 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x11 ____ 0x02 0x01 0x02 0x04 0x04 0x03 0x04 0x07 0x05 0x04 0x04 ____ 0x01 0x02 0x77 ____ 0x01 0x02 0x03 0x11 0x04 0x05 0x21 0x31 0x06 0x12 0x41 0x51 0x07 0x61 0x71 0x13 0x22 0x32 0x81 0x08 0x14 0x42 0x91 0xA1 0xB1 0xC1 0x09 0x23 0x33 0x52 0xF0 0x15 0x62 0x72 0xD1 0x0A 0x16 0x24 0x34 0xE1 0x25 0xF1 0x17 0x18 0x19 0x1A 0x26 0x27 0x28 0x29 0x2A 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0xFF 0xDA ____ 0x0C 0x03 0x01 ____ 0x02 0x11 0x03 0x11 ____ 0x3F ____ 0xFB 0x42 0xBF 0x5F 0x3F 0xB7 0xCE 0x7E 0xBF 0xCA 0x33 0xFE 0x1F 0xCF 0xCB 0x0A 0xFE 0xCF 0x3F 0xEC 0x78 0xFD 0x60 0xAF 0xF7 0xE8 0xFF ____ 0x10 0x8E 0xA2 0xBF 0xE0 0x1C 0xFF ____ ____ 0xCF 0xC2 0x8A 0xFF ____ 0xD0 0x94 0xFF ____ 0xB7 0xA2 0x0A 0xFF ____ 0x8F 0x63 0xF8 0x94 0xFE 0x90 0xAB 0xF7 0x83 0xFA 0xD4 0xFC 0x6B 0xAF 0xF2 0x50 0xFE 0xED 0x3F 0xA2 0x2A 0xFC 0xFC 0xFF ____ 0x99 0x53 0xF8 0xFF ____ 0xAF 0xF5 0x80 0xFF ____ 0xD2 0x60 0xFE 0x8D 0xEB 0xC4 0x3F 0xF3 0x18 0x3D 0xAE 0xBF 0xDE 0xB3 0xF3 0x33 0xF8 0x94 0xAF 0xF7 0x58 0xFF ____ 0xB4 0x23 0xFB 0xF2 0xAF 0xFC 0xC7 0x0F 0xF9 0xC4 0x3F 0x11 0xEB 0xFD 0x06 0x3F 0xC3 0xC3 0xF1 0x7A 0xBF 0x29 0x3F 0xF6 0x33 0x3F 0x4D 0x2B 0xFB 0xA4 0xFF ____ 0x9E 0xA3 0xEE 0x4A 0xFE 0x63 0x3B 0x8F 0xE4 0xEE 0xBF 0xEB 0xC0 0xFF ____ 0x12 0xCF 0xE8 0x22 0xBF 0xF3 0xEC 0x3F 0xE9 0x70 0xFD 0x8B 0xAF 0xFA 0xB1 0x3F 0xF3 0x21 0x3C 0xC2 0xBF 0x20 0x3F 0x96 0x4F 0x15 0xAF 0xF9 0x83 0x3F 0xD3 0xB3 0xF3 0x7E 0xBF 0xEF 0xAC 0xFE 0xBC 0x3F 0x14 0xAB 0xEA 0xCF 0xFB ____ 0x3E 0xDA 0xAF 0xF0 0xD8 0xFA 0xC3 0xFA 0xB2 0xAF 0x0C 0xFE 0x03 0x3F 0xCF 0x6E 0xBF 0xAC 0x8F 0x50 0xFE 0xBF 0x2B 0xF3 0x53 0xFD 0x15 0x38 0xFA 0xFE 0x41 0x3F 0xF3 0x5C 0x3F 0x16 0xEB 0xF3 0x53 0xFE 0x91 0x0E 0x92 0xBE 0x2C 0xFF ____ 0x48 0x0F 0xDA 0x8A 0xFF ____ 0xAB 0x73 0xF8 0x24 0xFD 0x8A 0xAF 0xF0 0xB4 0xFF ____ 0x52 0x4F 0xC1 0xAA 0xFF ____ 0x39 0x4F 0xB2 0x3E 0x9E 0xAF 0x60 0xFF ____ 0x9C 0xC3 0xF1 0x06 0xBF 0xAA 0x8F 0xD6 0x4F 0xB7 0x2B 0xC7 0x3F 0xDB 0xB3 0xF2 0x62 0xBF 0xEE 0xB8 0xFE 0x75 0x3C 0xB6 0xBF 0xE1 0x68 0xFE 0xA6 0x3F 0xD0 0x1A 0xBF 0xA5 0x8F 0xE2 0xE3 0xF8 0x1A 0xAF 0xEC 0xA3 0xF9 0xFC 0xFD 0x07 0xAF 0x89 0x3F 0x73 0x3F 0xA4 0xBA 0xF9 0x53 0xFC 0x6E 0x3F 0xCD 0xE6 0xBF 0x82 0x8F 0xFA 0x4F 0x3F 0xAC 0x6A 0xFF ____ 0xA0 0xC3 0xF8 ____ 0xFE 0x93 0x2B 0xFE 0x48 0x8F 0xDC 0x0F 0xE2 0xA6 0xBF 0xEB 0x10 0xFF ____ 0x48 0x4F 0xD2 0x1A 0xFF ____ 0x2B 0x0F 0xE5 0xF3 0xEE 0x2A 0xFE 0xC1 0x3E 0x84 0xFD 0x1B 0xAF 0xF8 0xF4 0x3F 0x4D 0x3D 0xFE 0xBF 0xB9 0xCF 0xC6 0x4F 0x9F 0xEB 0xFD 0x45 0x3E 0xB8 0xFE 0x07 0x2B 4 | } => return True 5 | 6 | Report 7 | { 8 | 0x02 0x07 ____ 0x01 0xA9 0x02 0x01 ____ 0xF3 0x03 0xFA 0xA0 0xDE 0xAF 0xE6 0xF3 0xFE 0x46 0x8F 0xE8 0xAE 0xBF 0xDE 0x63 0xFC 0x5C 0x3D 0x8A 0xBF 0xC5 0xA3 0xFF ____ 0x49 0x13 0xBD 0xAF 0xEC 0x03 0xFF ____ 0x38 0xC3 0xAA 0xAF 0xF5 0xB0 0xFF ____ 0xB0 0x03 0xF8 0x4E 0xAF 0xAE 0x3F 0x3C 0x3F 0xD0 0xCE 0xBF 0xE4 0x2C 0xFF ____ 0x48 0x4F 0xE7 0x32 0xBF 0xDA 0xE3 0xFD 0x3B 0x3E 0x96 0xAF 0xF1 0x50 0xFF ____ 0xCD 0x4C 0xFC 0xA9 0xAF 0xFB 0x22 0x3F 0xE8 0x58 0xFD 0x1D 0xAF 0xF8 0x83 0x3F 0xEB 0x04 0xC3 0xAF 0xFB 0x38 0x3F 0x10 0x3E 0xB0 0xAF 0xF8 0xB7 0x3F 0xC0 0x83 0xF2 0x6E 0xBF 0x58 0x3E 0x3C 0xFA 0xBE 0xBF 0xAA 0x0F 0xE8 0xF3 0x92 0xAF 0xC6 0x8F 0xF7 0xA4 0xFF ____ 0x41 0x1A 0xF4 0x4F 0xF9 0x79 0x3F 0xCB 0x32 0xBC 0xA3 0xFC 0x7D 0x3C 0xFE 0xBF 0xDA 0x03 0xFD 0x5D 0x3E 0x84 0xAF 0xE1 0xB3 0xD9 0x3F 0xB8 0x4A 0xFF ____ 0x98 0x43 0xF5 0x83 0xFC 0xD4 0x6B 0xFE 0xBF 0xCF 0xEE 0xF3 0xEE 0x2A 0xFA 0x33 0xF9 0xA4 0xFA 0x72 0xB8 0xCF 0xF9 0x60 0x3F 0xB0 0xEA 0xFF ____ 0x98 0x03 0xFD 0x75 0x3F 0x37 0xAB 0xFB 0x74 0xFE 0x65 0x3F ____ 0xAB 0xF4 0x23 0xF9 0x78 0xF5 0xAA 0xFD 0xC4 0xF9 0x63 0xCD 0x6B 0xFD 0x6A 0x3F 0xEE 0xE8 0xFC 0xDB 0xAF 0xC4 0x0F 0xBB 0x3F 0xB3 0x4A 0xFF ____ 0x06 0x8F 0xF9 0xD3 0x3D 0xCA 0xBF 0xE9 0x38 0xFF ____ 0x8A 0x03 0xF9 0x7B 0xAF 0xEB 0xB3 0xFF ____ 0x62 0x43 0xF7 0xC2 0xBF 0xCD 0xA3 0xFF ____ 0x39 0x03 0xF8 0xD9 0xAF 0xE5 0x23 0xFB 0x18 0xDD 0xAF 0xF5 0xE4 0xFE 0x8B 0x3F 0xA5 0x8A 0xFF ____ ____ 0x4F 0xFA 0x2F 0x3F 0xAF 0x6A 0xFE 0x27 0x3F 0xC8 0x33 0xFC 0xEA 0x6B 0xFB 0x80 0xFF ____ 0xB0 0x03 0xF4 0x5A 0xBF 0xBD 0x4F 0xE0 0x03 0xF9 0xE4 0xAF 0xE3 0x63 0xF9 0x6C 0xFE 0xE0 0xEB 0xDE 0x3F 0xE7 0x3C 0xFE 0x5A 0xEB 0xFD 0x42 0x3F 0xCC 0xD3 0xFA 0xE0 0xAF 0xE9 0xA3 0xFD 0x12 0x3F 0x8A 0xAA 0xFF ____ 0x66 0x4F 0xF7 0x74 0xFB 0x12 0xBF 0xE1 0x7C 0xFC 0x54 0xFD 0x5F 0xAF 0xED 0xA3 0xFE 0x16 0x0F 0x9A 0x2B 0xFD 0x29 0x3F 0xEC 0xA8 0xFD 0x9C 0xAF 0xF8 0x37 0x3F 0xE5 0x28 0xFC 0x6E 0xAF 0xEC 0x73 0xFF ____ 0x54 0x33 0xC2 0x6B 0xB4 0xFF ____ 0x17 0x4F 0xD3 0x9A 0xFF ____ 0xA7 0x73 0xFE 0x33 0x89 0x2B 0xFC 0xE5 0x3F 0xED 0x6C 0xFE 0x73 0xAB 0xFD 0xB6 0x3F 0xC5 0x43 0xFB 0xEF 0xAF 0xE1 0x53 0xFD 0x01 0x3F 0x94 0xDA 0xFF ____ 0x6C 0x8F 0xF9 0xBA 0x3F 0x28 0xEB 0xFE 0x29 0x0F 0xD2 0x0F 0xF4 0xE4 0xAF 0xF5 0xE4 0xFF ____ 0x52 0x0F 0xE7 0x5E 0xBF 0x32 0x3F 0xC4 0x23 0xEF 0x7A 0xFF ____ 0x8A 0x63 0xFA 0x40 0xF9 0x96 0xBF 0xD0 0xF3 0xFD 0x5D 0x3A 0x1A 0xFF ____ 0xB5 0xA3 0xF9 0x64 0xFE 0x5C 0xAB 0xF1 0x03 0xF1 0xB3 0xF6 0x36 0xBF 0x88 0x0F ____ 0xF3 0x2A 0xC4 0xFF ____ 0x7A 0x0F 0xA5 0x6B 0xFA 0x34 0xFC 0xB0 0xFE 0x5E 0xAB 0xFB 0x38 0xFE 0xD0 0x3F 0xD1 0xD2 0xBF 0xE3 0xD0 0xFE 0x79 0x30 0xAB 0xFE 0x91 0x4D 0x0F 0xE1 0xFE 0xBF 0xC8 0xD3 0xFE 0x8B 0xCF 0x58 0xAF 0xFA 0x7C 0x3F 0x92 0x0F 0x95 0xEB 0xFC 0x52 0x3F 0xD0 0x73 0xF4 0x0E 0xBF 0xE9 0x14 0xFF ____ 0x83 0x33 0xF8 0xDA 0xAF 0xC8 0xCE 0xF3 0xFD 0x40 0xAB 0xFE 0x77 0x0F 0xF3 0x3C 0xF7 0xDA 0xFF ____ 0x57 0x4F 0xFA 0x0A 0x3E 0x3E 0xAF 0xF8 0x47 0x3F 0x24 0x3F 0x96 0xCA 0xFF ____ 0x61 0x8F 0xFA 0x9E 0x3E 0x99 0xAF 0xFB 0x1E 0x3F 0xCB 0x83 0xF3 0xD6 0xBF 0x26 0x3F 0xE5 0x0C 0xFD 0x47 0xAF 0xF8 0xB8 0x3F 0xD1 0x93 0xE0 0x9A 0xFF ____ 0xBF 0xB3 0xFC 0xBE 0x3E 0x0C 0xAF 0xF1 0xBC 0xFF ____ 0x78 0x4F 0xD8 0x8A 0xFE 0xDE 0x3F 0xC7 0xB3 0xFA 0xDE 0xAF 0xF2 0x14 0xFF ____ 0xA0 0x33 0xF1 0x6E 0xBF 0x84 0x4F 0xF3 0x38 0xF8 0x56 0xBF 0xDB 0x33 0xFE 0x8F 0x0F 0xD1 0x1A 0xFF ____ 0x8C 0x43 0xFC 0x03 0x3F 0x90 0xDA 0xFF ____ 0xD3 0x7C 0xFA 0x33 0xFF 0xD9 0xFF ____ 0x99 0x53 0xF8 0xFF ____ 0xAF 0xF5 0x80 0xFF ____ 0xD2 0x60 0xFE 0x8D 0xEB 0xC4 0x3F 0xF3 0x18 0x3D 0xAE 0xBF 0xDE 0xB3 0xF3 0x33 0xF8 0x94 0xAF 0xF7 0x58 0xFF ____ 0xB4 0x23 0xFB 0xF2 0xAF 0xFC 0xC7 0x0F 0xF9 0xC4 0x3F 0x11 0xEB 0xFD 0x06 0x3F 0xC3 0xC3 0xF1 0x7A 0xBF 0x29 0x3F 0xF6 0x33 0x3F 0x4D 0x2B 0xFB 0xA4 0xFF ____ 0x9E 0xA3 0xEE 0x4A 0xFE 0x63 0x3B 0x8F 0xE4 0xEE 0xBF 0xEB 0xC0 0xFF ____ 0x12 0xCF 0xE8 0x22 0xBF 0xF3 0xEC 0x3F 0xE9 0x70 0xFD 0x8B 0xAF 0xFA 0xB1 0x3F 0xF3 0x21 0x3C 0xC2 0xBF 0x20 0x3F 0x96 0x4F 0x15 0xAF 0xF9 0x83 0x3F 0xD3 0xB3 0xF3 0x7E 0xBF 0xEF 0xAC 0xFE 0xBC 0x3F 0x14 0xAB 0xEA 0xCF 0xFB ____ 0x3E 0xDA 0xAF 0xF0 0xD8 0xFA 0xC3 0xFA 0xB2 0xAF 0x0C 0xFE 0x03 0x3F 0xCF 0x6E 0xBF 0xAC 0x8F 0x50 0xFE 0xBF 0x2B 0xF3 0x53 0xFD 0x15 0x38 0xFA 0xFE 0x41 0x3F 0xF3 0x5C 0x3F 0x16 0xEB 0xF3 0x53 0xFE 0x91 0x0E 0x92 0xBE 0x2C 0xFF ____ 0x48 0x0F 0xDA 0x8A 0xFF ____ 0xAB 0x73 0xF8 0x24 0xFD 0x8A 0xAF 0xF0 0xB4 0xFF ____ 0x52 0x4F 0xC1 0xAA 0xFF ____ 0x39 0x4F 0xB2 0x3E 0x9E 0xAF 0x60 0xFF ____ 0x9C 0xC3 0xF1 0x06 0xBF 0xAA 0x8F 0xD6 0x4F 0xB7 0x2B 0xC7 0x3F 0xDB 0xB3 0xF2 0x62 0xBF 0xEE 0xB8 0xFE 0x75 0x3C 0xB6 0xBF 0xE1 0x68 0xFE 0xA6 0x3F 0xD0 0x1A 0xBF 0xA5 0x8F 0xE2 0xE3 0xF8 0x1A 0xAF 0xEC 0xA3 0xF9 0xFC 0xFD 0x07 0xAF 0x89 0x3F 0x73 0x3F 0xA4 0xBA 0xF9 0x53 0xFC 0x6E 0x3F 0xCD 0xE6 0xBF 0x82 0x8F 0xFA 0x4F 0x3F 0xAC 0x6A 0xFF ____ 0xA0 0xC3 0xF8 ____ 0xFE 0x93 0x2B 0xFE 0x48 0x8F 0xDC 0x0F 0xE2 0xA6 0xBF 0xEB 0x10 0xFF ____ 0x48 0x4F 0xD2 0x1A 0xFF ____ 0x2B 0x0F 0xE5 0xF3 0xEE 0x2A 0xFE 0xC1 0x3E 0x84 0xFD 0x1B 0xAF 0xF8 0xF4 0x3F 0x4D 0x3D 0xFE 0xBF 0xB9 0xCF 0xC6 0x4F 0x9F 0xEB 0xFD 0x45 0x3E 0xB8 0xFE 0x07 0x2B 9 | } => return True 10 | 11 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoBitmapKeyTests.SetBitmapResultsInExpectedOutput8PxTiles/Stream Deck XL.verified.txt: -------------------------------------------------------------------------------- 1 | Report 2 | { 3 | 0x02 0x07 ____ ____ 0xF8 0x03 ____ ____ 0xFF 0xD8 0xFF 0xE0 ____ 0x10 0x4A 0x46 0x49 0x46 ____ 0x01 0x01 0x01 ____ 0x60 ____ 0x60 ____ ____ 0xFF 0xDB ____ 0x84 ____ 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0xFF 0xC0 ____ 0x11 0x08 ____ 0x60 ____ 0x60 0x03 0x01 0x11 ____ 0x02 0x11 0x01 0x03 0x11 0x01 0xFF 0xC4 0x01 0xA2 ____ ____ 0x01 0x05 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x10 ____ 0x02 0x01 0x03 0x03 0x02 0x04 0x03 0x05 0x05 0x04 0x04 ____ ____ 0x01 0x7D 0x01 0x02 0x03 ____ 0x04 0x11 0x05 0x12 0x21 0x31 0x41 0x06 0x13 0x51 0x61 0x07 0x22 0x71 0x14 0x32 0x81 0x91 0xA1 0x08 0x23 0x42 0xB1 0xC1 0x15 0x52 0xD1 0xF0 0x24 0x33 0x62 0x72 0x82 0x09 0x0A 0x16 0x17 0x18 0x19 0x1A 0x25 0x26 0x27 0x28 0x29 0x2A 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF1 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0x01 ____ 0x03 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 ____ ____ ____ ____ ____ ____ 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x11 ____ 0x02 0x01 0x02 0x04 0x04 0x03 0x04 0x07 0x05 0x04 0x04 ____ 0x01 0x02 0x77 ____ 0x01 0x02 0x03 0x11 0x04 0x05 0x21 0x31 0x06 0x12 0x41 0x51 0x07 0x61 0x71 0x13 0x22 0x32 0x81 0x08 0x14 0x42 0x91 0xA1 0xB1 0xC1 0x09 0x23 0x33 0x52 0xF0 0x15 0x62 0x72 0xD1 0x0A 0x16 0x24 0x34 0xE1 0x25 0xF1 0x17 0x18 0x19 0x1A 0x26 0x27 0x28 0x29 0x2A 0x35 0x36 0x37 0x38 0x39 0x3A 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0xA2 0xA3 0xA4 0xA5 0xA6 0xA7 0xA8 0xA9 0xAA 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xC2 0xC3 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 0xCA 0xD2 0xD3 0xD4 0xD5 0xD6 0xD7 0xD8 0xD9 0xDA 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xE8 0xE9 0xEA 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7 0xF8 0xF9 0xFA 0xFF 0xDA ____ 0x0C 0x03 0x01 ____ 0x02 0x11 0x03 0x11 ____ 0x3F ____ 0xFB 0x42 0xBF 0x5F 0x3F 0xB7 0xCE 0x7E 0xBF 0xCA 0x33 0xFE 0x1F 0xCF 0xCB 0x0A 0xFE 0xCF 0x3F 0xEC 0x78 0xFD 0x60 0xAF 0xF7 0xE8 0xFF ____ 0x10 0x8E 0xA2 0xBF 0xE0 0x1C 0xFF ____ ____ 0xCF 0xC2 0x8A 0xFF ____ 0xD0 0x94 0xFF ____ 0xB7 0xA2 0x0A 0xFF ____ 0x8F 0x63 0xF8 0x94 0xFE 0x90 0xAB 0xF7 0x83 0xFA 0xD4 0xFC 0x6B 0xAF 0xF2 0x50 0xFE 0xED 0x3F 0xA2 0x2A 0xFC 0xFC 0xFF ____ 0x99 0x53 0xF8 0xFF ____ 0xAF 0xF5 0x80 0xFF ____ 0xD2 0x60 0xFE 0x8D 0xEB 0xC4 0x3F 0xF3 0x18 0x3D 0xAE 0xBF 0xDE 0xB3 0xF3 0x33 0xF8 0x94 0xAF 0xF7 0x58 0xFF ____ 0xB4 0x23 0xFB 0xF2 0xAF 0xFC 0xC7 0x0F 0xF9 0xC4 0x3F 0x11 0xEB 0xFD 0x06 0x3F 0xC3 0xC3 0xF1 0x7A 0xBF 0x29 0x3F 0xF6 0x33 0x3F 0x4D 0x2B 0xFB 0xA4 0xFF ____ 0x9E 0xA3 0xEE 0x4A 0xFE 0x63 0x3B 0x8F 0xE4 0xEE 0xBF 0xEB 0xC0 0xFF ____ 0x12 0xCF 0xE8 0x22 0xBF 0xF3 0xEC 0x3F 0xE9 0x70 0xFD 0x8B 0xAF 0xFA 0xB1 0x3F 0xF3 0x21 0x3C 0xC2 0xBF 0x20 0x3F 0x96 0x4F 0x15 0xAF 0xF9 0x83 0x3F 0xD3 0xB3 0xF3 0x7E 0xBF 0xEF 0xAC 0xFE 0xBC 0x3F 0x14 0xAB 0xEA 0xCF 0xFB ____ 0x3E 0xDA 0xAF 0xF0 0xD8 0xFA 0xC3 0xFA 0xB2 0xAF 0x0C 0xFE 0x03 0x3F 0xCF 0x6E 0xBF 0xAC 0x8F 0x50 0xFE 0xBF 0x2B 0xF3 0x53 0xFD 0x15 0x38 0xFA 0xFE 0x41 0x3F 0xF3 0x5C 0x3F 0x16 0xEB 0xF3 0x53 0xFE 0x91 0x0E 0x92 0xBE 0x2C 0xFF ____ 0x48 0x0F 0xDA 0x8A 0xFF ____ 0xAB 0x73 0xF8 0x24 0xFD 0x8A 0xAF 0xF0 0xB4 0xFF ____ 0x52 0x4F 0xC1 0xAA 0xFF ____ 0x39 0x4F 0xB2 0x3E 0x9E 0xAF 0x60 0xFF ____ 0x9C 0xC3 0xF1 0x06 0xBF 0xAA 0x8F 0xD6 0x4F 0xB7 0x2B 0xC7 0x3F 0xDB 0xB3 0xF2 0x62 0xBF 0xEE 0xB8 0xFE 0x75 0x3C 0xB6 0xBF 0xE1 0x68 0xFE 0xA6 0x3F 0xD0 0x1A 0xBF 0xA5 0x8F 0xE2 0xE3 0xF8 0x1A 0xAF 0xEC 0xA3 0xF9 0xFC 0xFD 0x07 0xAF 0x89 0x3F 0x73 0x3F 0xA4 0xBA 0xF9 0x53 0xFC 0x6E 0x3F 0xCD 0xE6 0xBF 0x82 0x8F 0xFA 0x4F 0x3F 0xAC 0x6A 0xFF ____ 0xA0 0xC3 0xF8 ____ 0xFE 0x93 0x2B 0xFE 0x48 0x8F 0xDC 0x0F 0xE2 0xA6 0xBF 0xEB 0x10 0xFF ____ 0x48 0x4F 0xD2 0x1A 0xFF ____ 0x2B 0x0F 0xE5 0xF3 0xEE 0x2A 0xFE 0xC1 0x3E 0x84 0xFD 0x1B 0xAF 0xF8 0xF4 0x3F 0x4D 0x3D 0xFE 0xBF 0xB9 0xCF 0xC6 0x4F 0x9F 0xEB 0xFD 0x45 0x3E 0xB8 0xFE 0x07 0x2B 4 | } => return True 5 | 6 | Report 7 | { 8 | 0x02 0x07 ____ 0x01 0xA9 0x02 0x01 ____ 0xF3 0x03 0xFA 0xA0 0xDE 0xAF 0xE6 0xF3 0xFE 0x46 0x8F 0xE8 0xAE 0xBF 0xDE 0x63 0xFC 0x5C 0x3D 0x8A 0xBF 0xC5 0xA3 0xFF ____ 0x49 0x13 0xBD 0xAF 0xEC 0x03 0xFF ____ 0x38 0xC3 0xAA 0xAF 0xF5 0xB0 0xFF ____ 0xB0 0x03 0xF8 0x4E 0xAF 0xAE 0x3F 0x3C 0x3F 0xD0 0xCE 0xBF 0xE4 0x2C 0xFF ____ 0x48 0x4F 0xE7 0x32 0xBF 0xDA 0xE3 0xFD 0x3B 0x3E 0x96 0xAF 0xF1 0x50 0xFF ____ 0xCD 0x4C 0xFC 0xA9 0xAF 0xFB 0x22 0x3F 0xE8 0x58 0xFD 0x1D 0xAF 0xF8 0x83 0x3F 0xEB 0x04 0xC3 0xAF 0xFB 0x38 0x3F 0x10 0x3E 0xB0 0xAF 0xF8 0xB7 0x3F 0xC0 0x83 0xF2 0x6E 0xBF 0x58 0x3E 0x3C 0xFA 0xBE 0xBF 0xAA 0x0F 0xE8 0xF3 0x92 0xAF 0xC6 0x8F 0xF7 0xA4 0xFF ____ 0x41 0x1A 0xF4 0x4F 0xF9 0x79 0x3F 0xCB 0x32 0xBC 0xA3 0xFC 0x7D 0x3C 0xFE 0xBF 0xDA 0x03 0xFD 0x5D 0x3E 0x84 0xAF 0xE1 0xB3 0xD9 0x3F 0xB8 0x4A 0xFF ____ 0x98 0x43 0xF5 0x83 0xFC 0xD4 0x6B 0xFE 0xBF 0xCF 0xEE 0xF3 0xEE 0x2A 0xFA 0x33 0xF9 0xA4 0xFA 0x72 0xB8 0xCF 0xF9 0x60 0x3F 0xB0 0xEA 0xFF ____ 0x98 0x03 0xFD 0x75 0x3F 0x37 0xAB 0xFB 0x74 0xFE 0x65 0x3F ____ 0xAB 0xF4 0x23 0xF9 0x78 0xF5 0xAA 0xFD 0xC4 0xF9 0x63 0xCD 0x6B 0xFD 0x6A 0x3F 0xEE 0xE8 0xFC 0xDB 0xAF 0xC4 0x0F 0xBB 0x3F 0xB3 0x4A 0xFF ____ 0x06 0x8F 0xF9 0xD3 0x3D 0xCA 0xBF 0xE9 0x38 0xFF ____ 0x8A 0x03 0xF9 0x7B 0xAF 0xEB 0xB3 0xFF ____ 0x62 0x43 0xF7 0xC2 0xBF 0xCD 0xA3 0xFF ____ 0x39 0x03 0xF8 0xD9 0xAF 0xE5 0x23 0xFB 0x18 0xDD 0xAF 0xF5 0xE4 0xFE 0x8B 0x3F 0xA5 0x8A 0xFF ____ ____ 0x4F 0xFA 0x2F 0x3F 0xAF 0x6A 0xFE 0x27 0x3F 0xC8 0x33 0xFC 0xEA 0x6B 0xFB 0x80 0xFF ____ 0xB0 0x03 0xF4 0x5A 0xBF 0xBD 0x4F 0xE0 0x03 0xF9 0xE4 0xAF 0xE3 0x63 0xF9 0x6C 0xFE 0xE0 0xEB 0xDE 0x3F 0xE7 0x3C 0xFE 0x5A 0xEB 0xFD 0x42 0x3F 0xCC 0xD3 0xFA 0xE0 0xAF 0xE9 0xA3 0xFD 0x12 0x3F 0x8A 0xAA 0xFF ____ 0x66 0x4F 0xF7 0x74 0xFB 0x12 0xBF 0xE1 0x7C 0xFC 0x54 0xFD 0x5F 0xAF 0xED 0xA3 0xFE 0x16 0x0F 0x9A 0x2B 0xFD 0x29 0x3F 0xEC 0xA8 0xFD 0x9C 0xAF 0xF8 0x37 0x3F 0xE5 0x28 0xFC 0x6E 0xAF 0xEC 0x73 0xFF ____ 0x54 0x33 0xC2 0x6B 0xB4 0xFF ____ 0x17 0x4F 0xD3 0x9A 0xFF ____ 0xA7 0x73 0xFE 0x33 0x89 0x2B 0xFC 0xE5 0x3F 0xED 0x6C 0xFE 0x73 0xAB 0xFD 0xB6 0x3F 0xC5 0x43 0xFB 0xEF 0xAF 0xE1 0x53 0xFD 0x01 0x3F 0x94 0xDA 0xFF ____ 0x6C 0x8F 0xF9 0xBA 0x3F 0x28 0xEB 0xFE 0x29 0x0F 0xD2 0x0F 0xF4 0xE4 0xAF 0xF5 0xE4 0xFF ____ 0x52 0x0F 0xE7 0x5E 0xBF 0x32 0x3F 0xC4 0x23 0xEF 0x7A 0xFF ____ 0x8A 0x63 0xFA 0x40 0xF9 0x96 0xBF 0xD0 0xF3 0xFD 0x5D 0x3A 0x1A 0xFF ____ 0xB5 0xA3 0xF9 0x64 0xFE 0x5C 0xAB 0xF1 0x03 0xF1 0xB3 0xF6 0x36 0xBF 0x88 0x0F ____ 0xF3 0x2A 0xC4 0xFF ____ 0x7A 0x0F 0xA5 0x6B 0xFA 0x34 0xFC 0xB0 0xFE 0x5E 0xAB 0xFB 0x38 0xFE 0xD0 0x3F 0xD1 0xD2 0xBF 0xE3 0xD0 0xFE 0x79 0x30 0xAB 0xFE 0x91 0x4D 0x0F 0xE1 0xFE 0xBF 0xC8 0xD3 0xFE 0x8B 0xCF 0x58 0xAF 0xFA 0x7C 0x3F 0x92 0x0F 0x95 0xEB 0xFC 0x52 0x3F 0xD0 0x73 0xF4 0x0E 0xBF 0xE9 0x14 0xFF ____ 0x83 0x33 0xF8 0xDA 0xAF 0xC8 0xCE 0xF3 0xFD 0x40 0xAB 0xFE 0x77 0x0F 0xF3 0x3C 0xF7 0xDA 0xFF ____ 0x57 0x4F 0xFA 0x0A 0x3E 0x3E 0xAF 0xF8 0x47 0x3F 0x24 0x3F 0x96 0xCA 0xFF ____ 0x61 0x8F 0xFA 0x9E 0x3E 0x99 0xAF 0xFB 0x1E 0x3F 0xCB 0x83 0xF3 0xD6 0xBF 0x26 0x3F 0xE5 0x0C 0xFD 0x47 0xAF 0xF8 0xB8 0x3F 0xD1 0x93 0xE0 0x9A 0xFF ____ 0xBF 0xB3 0xFC 0xBE 0x3E 0x0C 0xAF 0xF1 0xBC 0xFF ____ 0x78 0x4F 0xD8 0x8A 0xFE 0xDE 0x3F 0xC7 0xB3 0xFA 0xDE 0xAF 0xF2 0x14 0xFF ____ 0xA0 0x33 0xF1 0x6E 0xBF 0x84 0x4F 0xF3 0x38 0xF8 0x56 0xBF 0xDB 0x33 0xFE 0x8F 0x0F 0xD1 0x1A 0xFF ____ 0x8C 0x43 0xFC 0x03 0x3F 0x90 0xDA 0xFF ____ 0xD3 0x7C 0xFA 0x33 0xFF 0xD9 0xFF ____ 0x99 0x53 0xF8 0xFF ____ 0xAF 0xF5 0x80 0xFF ____ 0xD2 0x60 0xFE 0x8D 0xEB 0xC4 0x3F 0xF3 0x18 0x3D 0xAE 0xBF 0xDE 0xB3 0xF3 0x33 0xF8 0x94 0xAF 0xF7 0x58 0xFF ____ 0xB4 0x23 0xFB 0xF2 0xAF 0xFC 0xC7 0x0F 0xF9 0xC4 0x3F 0x11 0xEB 0xFD 0x06 0x3F 0xC3 0xC3 0xF1 0x7A 0xBF 0x29 0x3F 0xF6 0x33 0x3F 0x4D 0x2B 0xFB 0xA4 0xFF ____ 0x9E 0xA3 0xEE 0x4A 0xFE 0x63 0x3B 0x8F 0xE4 0xEE 0xBF 0xEB 0xC0 0xFF ____ 0x12 0xCF 0xE8 0x22 0xBF 0xF3 0xEC 0x3F 0xE9 0x70 0xFD 0x8B 0xAF 0xFA 0xB1 0x3F 0xF3 0x21 0x3C 0xC2 0xBF 0x20 0x3F 0x96 0x4F 0x15 0xAF 0xF9 0x83 0x3F 0xD3 0xB3 0xF3 0x7E 0xBF 0xEF 0xAC 0xFE 0xBC 0x3F 0x14 0xAB 0xEA 0xCF 0xFB ____ 0x3E 0xDA 0xAF 0xF0 0xD8 0xFA 0xC3 0xFA 0xB2 0xAF 0x0C 0xFE 0x03 0x3F 0xCF 0x6E 0xBF 0xAC 0x8F 0x50 0xFE 0xBF 0x2B 0xF3 0x53 0xFD 0x15 0x38 0xFA 0xFE 0x41 0x3F 0xF3 0x5C 0x3F 0x16 0xEB 0xF3 0x53 0xFE 0x91 0x0E 0x92 0xBE 0x2C 0xFF ____ 0x48 0x0F 0xDA 0x8A 0xFF ____ 0xAB 0x73 0xF8 0x24 0xFD 0x8A 0xAF 0xF0 0xB4 0xFF ____ 0x52 0x4F 0xC1 0xAA 0xFF ____ 0x39 0x4F 0xB2 0x3E 0x9E 0xAF 0x60 0xFF ____ 0x9C 0xC3 0xF1 0x06 0xBF 0xAA 0x8F 0xD6 0x4F 0xB7 0x2B 0xC7 0x3F 0xDB 0xB3 0xF2 0x62 0xBF 0xEE 0xB8 0xFE 0x75 0x3C 0xB6 0xBF 0xE1 0x68 0xFE 0xA6 0x3F 0xD0 0x1A 0xBF 0xA5 0x8F 0xE2 0xE3 0xF8 0x1A 0xAF 0xEC 0xA3 0xF9 0xFC 0xFD 0x07 0xAF 0x89 0x3F 0x73 0x3F 0xA4 0xBA 0xF9 0x53 0xFC 0x6E 0x3F 0xCD 0xE6 0xBF 0x82 0x8F 0xFA 0x4F 0x3F 0xAC 0x6A 0xFF ____ 0xA0 0xC3 0xF8 ____ 0xFE 0x93 0x2B 0xFE 0x48 0x8F 0xDC 0x0F 0xE2 0xA6 0xBF 0xEB 0x10 0xFF ____ 0x48 0x4F 0xD2 0x1A 0xFF ____ 0x2B 0x0F 0xE5 0xF3 0xEE 0x2A 0xFE 0xC1 0x3E 0x84 0xFD 0x1B 0xAF 0xF8 0xF4 0x3F 0x4D 0x3D 0xFE 0xBF 0xB9 0xCF 0xC6 0x4F 0x9F 0xEB 0xFD 0x45 0x3E 0xB8 0xFE 0x07 0x2B 9 | } => return True 10 | 11 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoHardwareTwins.HardwareTwinsHaveExpectedValues/Stream Deck MK.2.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | DeviceName: Stream Deck MK.2, 3 | Driver: { 4 | BytesPerSecondLimit: 1500000.0, 5 | ExpectedFeatureReportLength: 32, 6 | ExpectedInputReportLength: 512, 7 | ExpectedOutputReportLength: 1024, 8 | FirmwareVersionFeatureId: 5, 9 | FirmwareVersionReportSkip: 6, 10 | HeaderSize: 8, 11 | KeyReportOffset: 4, 12 | ReportSize: 1024, 13 | SerialNumberFeatureId: 6, 14 | SerialNumberReportSkip: 2 15 | }, 16 | Keys: [ 17 | { 18 | Bottom: 72, 19 | Height: 72, 20 | IsEmpty: false, 21 | Left: 0, 22 | Location: { 23 | IsEmpty: true, 24 | X: 0, 25 | Y: 0 26 | }, 27 | Right: 72, 28 | Size: { 29 | Height: 72, 30 | IsEmpty: false, 31 | Width: 72 32 | }, 33 | Top: 0, 34 | Width: 72, 35 | X: 0, 36 | Y: 0 37 | }, 38 | { 39 | Bottom: 72, 40 | Height: 72, 41 | IsEmpty: false, 42 | Left: 104, 43 | Location: { 44 | IsEmpty: false, 45 | X: 104, 46 | Y: 0 47 | }, 48 | Right: 176, 49 | Size: { 50 | Height: 72, 51 | IsEmpty: false, 52 | Width: 72 53 | }, 54 | Top: 0, 55 | Width: 72, 56 | X: 104, 57 | Y: 0 58 | }, 59 | { 60 | Bottom: 72, 61 | Height: 72, 62 | IsEmpty: false, 63 | Left: 208, 64 | Location: { 65 | IsEmpty: false, 66 | X: 208, 67 | Y: 0 68 | }, 69 | Right: 280, 70 | Size: { 71 | Height: 72, 72 | IsEmpty: false, 73 | Width: 72 74 | }, 75 | Top: 0, 76 | Width: 72, 77 | X: 208, 78 | Y: 0 79 | }, 80 | { 81 | Bottom: 72, 82 | Height: 72, 83 | IsEmpty: false, 84 | Left: 312, 85 | Location: { 86 | IsEmpty: false, 87 | X: 312, 88 | Y: 0 89 | }, 90 | Right: 384, 91 | Size: { 92 | Height: 72, 93 | IsEmpty: false, 94 | Width: 72 95 | }, 96 | Top: 0, 97 | Width: 72, 98 | X: 312, 99 | Y: 0 100 | }, 101 | { 102 | Bottom: 72, 103 | Height: 72, 104 | IsEmpty: false, 105 | Left: 416, 106 | Location: { 107 | IsEmpty: false, 108 | X: 416, 109 | Y: 0 110 | }, 111 | Right: 488, 112 | Size: { 113 | Height: 72, 114 | IsEmpty: false, 115 | Width: 72 116 | }, 117 | Top: 0, 118 | Width: 72, 119 | X: 416, 120 | Y: 0 121 | }, 122 | { 123 | Bottom: 176, 124 | Height: 72, 125 | IsEmpty: false, 126 | Left: 0, 127 | Location: { 128 | IsEmpty: false, 129 | X: 0, 130 | Y: 104 131 | }, 132 | Right: 72, 133 | Size: { 134 | Height: 72, 135 | IsEmpty: false, 136 | Width: 72 137 | }, 138 | Top: 104, 139 | Width: 72, 140 | X: 0, 141 | Y: 104 142 | }, 143 | { 144 | Bottom: 176, 145 | Height: 72, 146 | IsEmpty: false, 147 | Left: 104, 148 | Location: { 149 | IsEmpty: false, 150 | X: 104, 151 | Y: 104 152 | }, 153 | Right: 176, 154 | Size: { 155 | Height: 72, 156 | IsEmpty: false, 157 | Width: 72 158 | }, 159 | Top: 104, 160 | Width: 72, 161 | X: 104, 162 | Y: 104 163 | }, 164 | { 165 | Bottom: 176, 166 | Height: 72, 167 | IsEmpty: false, 168 | Left: 208, 169 | Location: { 170 | IsEmpty: false, 171 | X: 208, 172 | Y: 104 173 | }, 174 | Right: 280, 175 | Size: { 176 | Height: 72, 177 | IsEmpty: false, 178 | Width: 72 179 | }, 180 | Top: 104, 181 | Width: 72, 182 | X: 208, 183 | Y: 104 184 | }, 185 | { 186 | Bottom: 176, 187 | Height: 72, 188 | IsEmpty: false, 189 | Left: 312, 190 | Location: { 191 | IsEmpty: false, 192 | X: 312, 193 | Y: 104 194 | }, 195 | Right: 384, 196 | Size: { 197 | Height: 72, 198 | IsEmpty: false, 199 | Width: 72 200 | }, 201 | Top: 104, 202 | Width: 72, 203 | X: 312, 204 | Y: 104 205 | }, 206 | { 207 | Bottom: 176, 208 | Height: 72, 209 | IsEmpty: false, 210 | Left: 416, 211 | Location: { 212 | IsEmpty: false, 213 | X: 416, 214 | Y: 104 215 | }, 216 | Right: 488, 217 | Size: { 218 | Height: 72, 219 | IsEmpty: false, 220 | Width: 72 221 | }, 222 | Top: 104, 223 | Width: 72, 224 | X: 416, 225 | Y: 104 226 | }, 227 | { 228 | Bottom: 280, 229 | Height: 72, 230 | IsEmpty: false, 231 | Left: 0, 232 | Location: { 233 | IsEmpty: false, 234 | X: 0, 235 | Y: 208 236 | }, 237 | Right: 72, 238 | Size: { 239 | Height: 72, 240 | IsEmpty: false, 241 | Width: 72 242 | }, 243 | Top: 208, 244 | Width: 72, 245 | X: 0, 246 | Y: 208 247 | }, 248 | { 249 | Bottom: 280, 250 | Height: 72, 251 | IsEmpty: false, 252 | Left: 104, 253 | Location: { 254 | IsEmpty: false, 255 | X: 104, 256 | Y: 208 257 | }, 258 | Right: 176, 259 | Size: { 260 | Height: 72, 261 | IsEmpty: false, 262 | Width: 72 263 | }, 264 | Top: 208, 265 | Width: 72, 266 | X: 104, 267 | Y: 208 268 | }, 269 | { 270 | Bottom: 280, 271 | Height: 72, 272 | IsEmpty: false, 273 | Left: 208, 274 | Location: { 275 | IsEmpty: false, 276 | X: 208, 277 | Y: 208 278 | }, 279 | Right: 280, 280 | Size: { 281 | Height: 72, 282 | IsEmpty: false, 283 | Width: 72 284 | }, 285 | Top: 208, 286 | Width: 72, 287 | X: 208, 288 | Y: 208 289 | }, 290 | { 291 | Bottom: 280, 292 | Height: 72, 293 | IsEmpty: false, 294 | Left: 312, 295 | Location: { 296 | IsEmpty: false, 297 | X: 312, 298 | Y: 208 299 | }, 300 | Right: 384, 301 | Size: { 302 | Height: 72, 303 | IsEmpty: false, 304 | Width: 72 305 | }, 306 | Top: 208, 307 | Width: 72, 308 | X: 312, 309 | Y: 208 310 | }, 311 | { 312 | Bottom: 280, 313 | Height: 72, 314 | IsEmpty: false, 315 | Left: 416, 316 | Location: { 317 | IsEmpty: false, 318 | X: 416, 319 | Y: 208 320 | }, 321 | Right: 488, 322 | Size: { 323 | Height: 72, 324 | IsEmpty: false, 325 | Width: 72 326 | }, 327 | Top: 208, 328 | Width: 72, 329 | X: 416, 330 | Y: 208 331 | } 332 | ], 333 | UsbId: { 334 | UsbProductId: 128, 335 | UsbVendorId: 4057 336 | } 337 | } -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoHardwareTwins.HardwareTwinsHaveExpectedValues/Stream Deck Mini Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | DeviceName: Stream Deck Mini Rev2, 3 | Driver: { 4 | BytesPerSecondLimit: Infinity, 5 | ExpectedFeatureReportLength: 17, 6 | ExpectedInputReportLength: 17, 7 | ExpectedOutputReportLength: 1024, 8 | FirmwareVersionFeatureId: 4, 9 | FirmwareVersionReportSkip: 5, 10 | HeaderSize: 16, 11 | KeyReportOffset: 1, 12 | ReportSize: 1024, 13 | SerialNumberFeatureId: 3, 14 | SerialNumberReportSkip: 5 15 | }, 16 | Keys: [ 17 | { 18 | Bottom: 80, 19 | Height: 80, 20 | IsEmpty: false, 21 | Left: 0, 22 | Location: { 23 | IsEmpty: true, 24 | X: 0, 25 | Y: 0 26 | }, 27 | Right: 80, 28 | Size: { 29 | Height: 80, 30 | IsEmpty: false, 31 | Width: 80 32 | }, 33 | Top: 0, 34 | Width: 80, 35 | X: 0, 36 | Y: 0 37 | }, 38 | { 39 | Bottom: 80, 40 | Height: 80, 41 | IsEmpty: false, 42 | Left: 112, 43 | Location: { 44 | IsEmpty: false, 45 | X: 112, 46 | Y: 0 47 | }, 48 | Right: 192, 49 | Size: { 50 | Height: 80, 51 | IsEmpty: false, 52 | Width: 80 53 | }, 54 | Top: 0, 55 | Width: 80, 56 | X: 112, 57 | Y: 0 58 | }, 59 | { 60 | Bottom: 80, 61 | Height: 80, 62 | IsEmpty: false, 63 | Left: 224, 64 | Location: { 65 | IsEmpty: false, 66 | X: 224, 67 | Y: 0 68 | }, 69 | Right: 304, 70 | Size: { 71 | Height: 80, 72 | IsEmpty: false, 73 | Width: 80 74 | }, 75 | Top: 0, 76 | Width: 80, 77 | X: 224, 78 | Y: 0 79 | }, 80 | { 81 | Bottom: 192, 82 | Height: 80, 83 | IsEmpty: false, 84 | Left: 0, 85 | Location: { 86 | IsEmpty: false, 87 | X: 0, 88 | Y: 112 89 | }, 90 | Right: 80, 91 | Size: { 92 | Height: 80, 93 | IsEmpty: false, 94 | Width: 80 95 | }, 96 | Top: 112, 97 | Width: 80, 98 | X: 0, 99 | Y: 112 100 | }, 101 | { 102 | Bottom: 192, 103 | Height: 80, 104 | IsEmpty: false, 105 | Left: 112, 106 | Location: { 107 | IsEmpty: false, 108 | X: 112, 109 | Y: 112 110 | }, 111 | Right: 192, 112 | Size: { 113 | Height: 80, 114 | IsEmpty: false, 115 | Width: 80 116 | }, 117 | Top: 112, 118 | Width: 80, 119 | X: 112, 120 | Y: 112 121 | }, 122 | { 123 | Bottom: 192, 124 | Height: 80, 125 | IsEmpty: false, 126 | Left: 224, 127 | Location: { 128 | IsEmpty: false, 129 | X: 224, 130 | Y: 112 131 | }, 132 | Right: 304, 133 | Size: { 134 | Height: 80, 135 | IsEmpty: false, 136 | Width: 80 137 | }, 138 | Top: 112, 139 | Width: 80, 140 | X: 224, 141 | Y: 112 142 | } 143 | ], 144 | UsbId: { 145 | UsbProductId: 144, 146 | UsbVendorId: 4057 147 | } 148 | } -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoHardwareTwins.HardwareTwinsHaveExpectedValues/Stream Deck Mini.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | DeviceName: Stream Deck Mini, 3 | Driver: { 4 | BytesPerSecondLimit: Infinity, 5 | ExpectedFeatureReportLength: 17, 6 | ExpectedInputReportLength: 17, 7 | ExpectedOutputReportLength: 1024, 8 | FirmwareVersionFeatureId: 4, 9 | FirmwareVersionReportSkip: 5, 10 | HeaderSize: 16, 11 | KeyReportOffset: 1, 12 | ReportSize: 1024, 13 | SerialNumberFeatureId: 3, 14 | SerialNumberReportSkip: 5 15 | }, 16 | Keys: [ 17 | { 18 | Bottom: 80, 19 | Height: 80, 20 | IsEmpty: false, 21 | Left: 0, 22 | Location: { 23 | IsEmpty: true, 24 | X: 0, 25 | Y: 0 26 | }, 27 | Right: 80, 28 | Size: { 29 | Height: 80, 30 | IsEmpty: false, 31 | Width: 80 32 | }, 33 | Top: 0, 34 | Width: 80, 35 | X: 0, 36 | Y: 0 37 | }, 38 | { 39 | Bottom: 80, 40 | Height: 80, 41 | IsEmpty: false, 42 | Left: 112, 43 | Location: { 44 | IsEmpty: false, 45 | X: 112, 46 | Y: 0 47 | }, 48 | Right: 192, 49 | Size: { 50 | Height: 80, 51 | IsEmpty: false, 52 | Width: 80 53 | }, 54 | Top: 0, 55 | Width: 80, 56 | X: 112, 57 | Y: 0 58 | }, 59 | { 60 | Bottom: 80, 61 | Height: 80, 62 | IsEmpty: false, 63 | Left: 224, 64 | Location: { 65 | IsEmpty: false, 66 | X: 224, 67 | Y: 0 68 | }, 69 | Right: 304, 70 | Size: { 71 | Height: 80, 72 | IsEmpty: false, 73 | Width: 80 74 | }, 75 | Top: 0, 76 | Width: 80, 77 | X: 224, 78 | Y: 0 79 | }, 80 | { 81 | Bottom: 192, 82 | Height: 80, 83 | IsEmpty: false, 84 | Left: 0, 85 | Location: { 86 | IsEmpty: false, 87 | X: 0, 88 | Y: 112 89 | }, 90 | Right: 80, 91 | Size: { 92 | Height: 80, 93 | IsEmpty: false, 94 | Width: 80 95 | }, 96 | Top: 112, 97 | Width: 80, 98 | X: 0, 99 | Y: 112 100 | }, 101 | { 102 | Bottom: 192, 103 | Height: 80, 104 | IsEmpty: false, 105 | Left: 112, 106 | Location: { 107 | IsEmpty: false, 108 | X: 112, 109 | Y: 112 110 | }, 111 | Right: 192, 112 | Size: { 113 | Height: 80, 114 | IsEmpty: false, 115 | Width: 80 116 | }, 117 | Top: 112, 118 | Width: 80, 119 | X: 112, 120 | Y: 112 121 | }, 122 | { 123 | Bottom: 192, 124 | Height: 80, 125 | IsEmpty: false, 126 | Left: 224, 127 | Location: { 128 | IsEmpty: false, 129 | X: 224, 130 | Y: 112 131 | }, 132 | Right: 304, 133 | Size: { 134 | Height: 80, 135 | IsEmpty: false, 136 | Width: 80 137 | }, 138 | Top: 112, 139 | Width: 80, 140 | X: 224, 141 | Y: 112 142 | } 143 | ], 144 | UsbId: { 145 | UsbProductId: 99, 146 | UsbVendorId: 4057 147 | } 148 | } -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoHardwareTwins.HardwareTwinsHaveExpectedValues/Stream Deck Rev2.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | DeviceName: Stream Deck Rev2, 3 | Driver: { 4 | BytesPerSecondLimit: 3200000.0, 5 | ExpectedFeatureReportLength: 32, 6 | ExpectedInputReportLength: 512, 7 | ExpectedOutputReportLength: 1024, 8 | FirmwareVersionFeatureId: 5, 9 | FirmwareVersionReportSkip: 6, 10 | HeaderSize: 8, 11 | KeyReportOffset: 4, 12 | ReportSize: 1024, 13 | SerialNumberFeatureId: 6, 14 | SerialNumberReportSkip: 2 15 | }, 16 | Keys: [ 17 | { 18 | Bottom: 72, 19 | Height: 72, 20 | IsEmpty: false, 21 | Left: 0, 22 | Location: { 23 | IsEmpty: true, 24 | X: 0, 25 | Y: 0 26 | }, 27 | Right: 72, 28 | Size: { 29 | Height: 72, 30 | IsEmpty: false, 31 | Width: 72 32 | }, 33 | Top: 0, 34 | Width: 72, 35 | X: 0, 36 | Y: 0 37 | }, 38 | { 39 | Bottom: 72, 40 | Height: 72, 41 | IsEmpty: false, 42 | Left: 104, 43 | Location: { 44 | IsEmpty: false, 45 | X: 104, 46 | Y: 0 47 | }, 48 | Right: 176, 49 | Size: { 50 | Height: 72, 51 | IsEmpty: false, 52 | Width: 72 53 | }, 54 | Top: 0, 55 | Width: 72, 56 | X: 104, 57 | Y: 0 58 | }, 59 | { 60 | Bottom: 72, 61 | Height: 72, 62 | IsEmpty: false, 63 | Left: 208, 64 | Location: { 65 | IsEmpty: false, 66 | X: 208, 67 | Y: 0 68 | }, 69 | Right: 280, 70 | Size: { 71 | Height: 72, 72 | IsEmpty: false, 73 | Width: 72 74 | }, 75 | Top: 0, 76 | Width: 72, 77 | X: 208, 78 | Y: 0 79 | }, 80 | { 81 | Bottom: 72, 82 | Height: 72, 83 | IsEmpty: false, 84 | Left: 312, 85 | Location: { 86 | IsEmpty: false, 87 | X: 312, 88 | Y: 0 89 | }, 90 | Right: 384, 91 | Size: { 92 | Height: 72, 93 | IsEmpty: false, 94 | Width: 72 95 | }, 96 | Top: 0, 97 | Width: 72, 98 | X: 312, 99 | Y: 0 100 | }, 101 | { 102 | Bottom: 72, 103 | Height: 72, 104 | IsEmpty: false, 105 | Left: 416, 106 | Location: { 107 | IsEmpty: false, 108 | X: 416, 109 | Y: 0 110 | }, 111 | Right: 488, 112 | Size: { 113 | Height: 72, 114 | IsEmpty: false, 115 | Width: 72 116 | }, 117 | Top: 0, 118 | Width: 72, 119 | X: 416, 120 | Y: 0 121 | }, 122 | { 123 | Bottom: 176, 124 | Height: 72, 125 | IsEmpty: false, 126 | Left: 0, 127 | Location: { 128 | IsEmpty: false, 129 | X: 0, 130 | Y: 104 131 | }, 132 | Right: 72, 133 | Size: { 134 | Height: 72, 135 | IsEmpty: false, 136 | Width: 72 137 | }, 138 | Top: 104, 139 | Width: 72, 140 | X: 0, 141 | Y: 104 142 | }, 143 | { 144 | Bottom: 176, 145 | Height: 72, 146 | IsEmpty: false, 147 | Left: 104, 148 | Location: { 149 | IsEmpty: false, 150 | X: 104, 151 | Y: 104 152 | }, 153 | Right: 176, 154 | Size: { 155 | Height: 72, 156 | IsEmpty: false, 157 | Width: 72 158 | }, 159 | Top: 104, 160 | Width: 72, 161 | X: 104, 162 | Y: 104 163 | }, 164 | { 165 | Bottom: 176, 166 | Height: 72, 167 | IsEmpty: false, 168 | Left: 208, 169 | Location: { 170 | IsEmpty: false, 171 | X: 208, 172 | Y: 104 173 | }, 174 | Right: 280, 175 | Size: { 176 | Height: 72, 177 | IsEmpty: false, 178 | Width: 72 179 | }, 180 | Top: 104, 181 | Width: 72, 182 | X: 208, 183 | Y: 104 184 | }, 185 | { 186 | Bottom: 176, 187 | Height: 72, 188 | IsEmpty: false, 189 | Left: 312, 190 | Location: { 191 | IsEmpty: false, 192 | X: 312, 193 | Y: 104 194 | }, 195 | Right: 384, 196 | Size: { 197 | Height: 72, 198 | IsEmpty: false, 199 | Width: 72 200 | }, 201 | Top: 104, 202 | Width: 72, 203 | X: 312, 204 | Y: 104 205 | }, 206 | { 207 | Bottom: 176, 208 | Height: 72, 209 | IsEmpty: false, 210 | Left: 416, 211 | Location: { 212 | IsEmpty: false, 213 | X: 416, 214 | Y: 104 215 | }, 216 | Right: 488, 217 | Size: { 218 | Height: 72, 219 | IsEmpty: false, 220 | Width: 72 221 | }, 222 | Top: 104, 223 | Width: 72, 224 | X: 416, 225 | Y: 104 226 | }, 227 | { 228 | Bottom: 280, 229 | Height: 72, 230 | IsEmpty: false, 231 | Left: 0, 232 | Location: { 233 | IsEmpty: false, 234 | X: 0, 235 | Y: 208 236 | }, 237 | Right: 72, 238 | Size: { 239 | Height: 72, 240 | IsEmpty: false, 241 | Width: 72 242 | }, 243 | Top: 208, 244 | Width: 72, 245 | X: 0, 246 | Y: 208 247 | }, 248 | { 249 | Bottom: 280, 250 | Height: 72, 251 | IsEmpty: false, 252 | Left: 104, 253 | Location: { 254 | IsEmpty: false, 255 | X: 104, 256 | Y: 208 257 | }, 258 | Right: 176, 259 | Size: { 260 | Height: 72, 261 | IsEmpty: false, 262 | Width: 72 263 | }, 264 | Top: 208, 265 | Width: 72, 266 | X: 104, 267 | Y: 208 268 | }, 269 | { 270 | Bottom: 280, 271 | Height: 72, 272 | IsEmpty: false, 273 | Left: 208, 274 | Location: { 275 | IsEmpty: false, 276 | X: 208, 277 | Y: 208 278 | }, 279 | Right: 280, 280 | Size: { 281 | Height: 72, 282 | IsEmpty: false, 283 | Width: 72 284 | }, 285 | Top: 208, 286 | Width: 72, 287 | X: 208, 288 | Y: 208 289 | }, 290 | { 291 | Bottom: 280, 292 | Height: 72, 293 | IsEmpty: false, 294 | Left: 312, 295 | Location: { 296 | IsEmpty: false, 297 | X: 312, 298 | Y: 208 299 | }, 300 | Right: 384, 301 | Size: { 302 | Height: 72, 303 | IsEmpty: false, 304 | Width: 72 305 | }, 306 | Top: 208, 307 | Width: 72, 308 | X: 312, 309 | Y: 208 310 | }, 311 | { 312 | Bottom: 280, 313 | Height: 72, 314 | IsEmpty: false, 315 | Left: 416, 316 | Location: { 317 | IsEmpty: false, 318 | X: 416, 319 | Y: 208 320 | }, 321 | Right: 488, 322 | Size: { 323 | Height: 72, 324 | IsEmpty: false, 325 | Width: 72 326 | }, 327 | Top: 208, 328 | Width: 72, 329 | X: 416, 330 | Y: 208 331 | } 332 | ], 333 | UsbId: { 334 | UsbProductId: 109, 335 | UsbVendorId: 4057 336 | } 337 | } -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoHardwareTwins.HardwareTwinsHaveExpectedValues/Stream Deck.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | DeviceName: Stream Deck, 3 | Driver: { 4 | BytesPerSecondLimit: 3200000.0, 5 | ExpectedFeatureReportLength: 17, 6 | ExpectedInputReportLength: 17, 7 | ExpectedOutputReportLength: 8191, 8 | FirmwareVersionFeatureId: 4, 9 | FirmwareVersionReportSkip: 5, 10 | HeaderSize: 16, 11 | KeyReportOffset: 1, 12 | ReportSize: 7819, 13 | SerialNumberFeatureId: 3, 14 | SerialNumberReportSkip: 5 15 | }, 16 | Keys: [ 17 | { 18 | Bottom: 72, 19 | Height: 72, 20 | IsEmpty: false, 21 | Left: 0, 22 | Location: { 23 | IsEmpty: true, 24 | X: 0, 25 | Y: 0 26 | }, 27 | Right: 72, 28 | Size: { 29 | Height: 72, 30 | IsEmpty: false, 31 | Width: 72 32 | }, 33 | Top: 0, 34 | Width: 72, 35 | X: 0, 36 | Y: 0 37 | }, 38 | { 39 | Bottom: 72, 40 | Height: 72, 41 | IsEmpty: false, 42 | Left: 102, 43 | Location: { 44 | IsEmpty: false, 45 | X: 102, 46 | Y: 0 47 | }, 48 | Right: 174, 49 | Size: { 50 | Height: 72, 51 | IsEmpty: false, 52 | Width: 72 53 | }, 54 | Top: 0, 55 | Width: 72, 56 | X: 102, 57 | Y: 0 58 | }, 59 | { 60 | Bottom: 72, 61 | Height: 72, 62 | IsEmpty: false, 63 | Left: 204, 64 | Location: { 65 | IsEmpty: false, 66 | X: 204, 67 | Y: 0 68 | }, 69 | Right: 276, 70 | Size: { 71 | Height: 72, 72 | IsEmpty: false, 73 | Width: 72 74 | }, 75 | Top: 0, 76 | Width: 72, 77 | X: 204, 78 | Y: 0 79 | }, 80 | { 81 | Bottom: 72, 82 | Height: 72, 83 | IsEmpty: false, 84 | Left: 306, 85 | Location: { 86 | IsEmpty: false, 87 | X: 306, 88 | Y: 0 89 | }, 90 | Right: 378, 91 | Size: { 92 | Height: 72, 93 | IsEmpty: false, 94 | Width: 72 95 | }, 96 | Top: 0, 97 | Width: 72, 98 | X: 306, 99 | Y: 0 100 | }, 101 | { 102 | Bottom: 72, 103 | Height: 72, 104 | IsEmpty: false, 105 | Left: 408, 106 | Location: { 107 | IsEmpty: false, 108 | X: 408, 109 | Y: 0 110 | }, 111 | Right: 480, 112 | Size: { 113 | Height: 72, 114 | IsEmpty: false, 115 | Width: 72 116 | }, 117 | Top: 0, 118 | Width: 72, 119 | X: 408, 120 | Y: 0 121 | }, 122 | { 123 | Bottom: 174, 124 | Height: 72, 125 | IsEmpty: false, 126 | Left: 0, 127 | Location: { 128 | IsEmpty: false, 129 | X: 0, 130 | Y: 102 131 | }, 132 | Right: 72, 133 | Size: { 134 | Height: 72, 135 | IsEmpty: false, 136 | Width: 72 137 | }, 138 | Top: 102, 139 | Width: 72, 140 | X: 0, 141 | Y: 102 142 | }, 143 | { 144 | Bottom: 174, 145 | Height: 72, 146 | IsEmpty: false, 147 | Left: 102, 148 | Location: { 149 | IsEmpty: false, 150 | X: 102, 151 | Y: 102 152 | }, 153 | Right: 174, 154 | Size: { 155 | Height: 72, 156 | IsEmpty: false, 157 | Width: 72 158 | }, 159 | Top: 102, 160 | Width: 72, 161 | X: 102, 162 | Y: 102 163 | }, 164 | { 165 | Bottom: 174, 166 | Height: 72, 167 | IsEmpty: false, 168 | Left: 204, 169 | Location: { 170 | IsEmpty: false, 171 | X: 204, 172 | Y: 102 173 | }, 174 | Right: 276, 175 | Size: { 176 | Height: 72, 177 | IsEmpty: false, 178 | Width: 72 179 | }, 180 | Top: 102, 181 | Width: 72, 182 | X: 204, 183 | Y: 102 184 | }, 185 | { 186 | Bottom: 174, 187 | Height: 72, 188 | IsEmpty: false, 189 | Left: 306, 190 | Location: { 191 | IsEmpty: false, 192 | X: 306, 193 | Y: 102 194 | }, 195 | Right: 378, 196 | Size: { 197 | Height: 72, 198 | IsEmpty: false, 199 | Width: 72 200 | }, 201 | Top: 102, 202 | Width: 72, 203 | X: 306, 204 | Y: 102 205 | }, 206 | { 207 | Bottom: 174, 208 | Height: 72, 209 | IsEmpty: false, 210 | Left: 408, 211 | Location: { 212 | IsEmpty: false, 213 | X: 408, 214 | Y: 102 215 | }, 216 | Right: 480, 217 | Size: { 218 | Height: 72, 219 | IsEmpty: false, 220 | Width: 72 221 | }, 222 | Top: 102, 223 | Width: 72, 224 | X: 408, 225 | Y: 102 226 | }, 227 | { 228 | Bottom: 276, 229 | Height: 72, 230 | IsEmpty: false, 231 | Left: 0, 232 | Location: { 233 | IsEmpty: false, 234 | X: 0, 235 | Y: 204 236 | }, 237 | Right: 72, 238 | Size: { 239 | Height: 72, 240 | IsEmpty: false, 241 | Width: 72 242 | }, 243 | Top: 204, 244 | Width: 72, 245 | X: 0, 246 | Y: 204 247 | }, 248 | { 249 | Bottom: 276, 250 | Height: 72, 251 | IsEmpty: false, 252 | Left: 102, 253 | Location: { 254 | IsEmpty: false, 255 | X: 102, 256 | Y: 204 257 | }, 258 | Right: 174, 259 | Size: { 260 | Height: 72, 261 | IsEmpty: false, 262 | Width: 72 263 | }, 264 | Top: 204, 265 | Width: 72, 266 | X: 102, 267 | Y: 204 268 | }, 269 | { 270 | Bottom: 276, 271 | Height: 72, 272 | IsEmpty: false, 273 | Left: 204, 274 | Location: { 275 | IsEmpty: false, 276 | X: 204, 277 | Y: 204 278 | }, 279 | Right: 276, 280 | Size: { 281 | Height: 72, 282 | IsEmpty: false, 283 | Width: 72 284 | }, 285 | Top: 204, 286 | Width: 72, 287 | X: 204, 288 | Y: 204 289 | }, 290 | { 291 | Bottom: 276, 292 | Height: 72, 293 | IsEmpty: false, 294 | Left: 306, 295 | Location: { 296 | IsEmpty: false, 297 | X: 306, 298 | Y: 204 299 | }, 300 | Right: 378, 301 | Size: { 302 | Height: 72, 303 | IsEmpty: false, 304 | Width: 72 305 | }, 306 | Top: 204, 307 | Width: 72, 308 | X: 306, 309 | Y: 204 310 | }, 311 | { 312 | Bottom: 276, 313 | Height: 72, 314 | IsEmpty: false, 315 | Left: 408, 316 | Location: { 317 | IsEmpty: false, 318 | X: 408, 319 | Y: 204 320 | }, 321 | Right: 480, 322 | Size: { 323 | Height: 72, 324 | IsEmpty: false, 325 | Width: 72 326 | }, 327 | Top: 204, 328 | Width: 72, 329 | X: 408, 330 | Y: 204 331 | } 332 | ], 333 | UsbId: { 334 | UsbProductId: 96, 335 | UsbVendorId: 4057 336 | } 337 | } -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeckMK2_EachKeyOnce.verified.txt: -------------------------------------------------------------------------------- 1 | 1 - DOWN 2 | 1 - UP 3 | 2 - DOWN 4 | 2 - UP 5 | 3 - DOWN 6 | 3 - UP 7 | 4 - DOWN 8 | 4 - UP 9 | 5 - DOWN 10 | 5 - UP 11 | 6 - DOWN 12 | 6 - UP 13 | 7 - DOWN 14 | 7 - UP 15 | 8 - DOWN 16 | 8 - UP 17 | 9 - DOWN 18 | 9 - UP 19 | 10 - DOWN 20 | 10 - UP 21 | 11 - DOWN 22 | 11 - UP 23 | 12 - DOWN 24 | 12 - UP 25 | 13 - DOWN 26 | 13 - UP 27 | 14 - DOWN 28 | 14 - UP 29 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeckMK2_MultipleKeysDown.verified.txt: -------------------------------------------------------------------------------- 1 | 0 - DOWN 2 | 1 - DOWN 3 | 2 - DOWN 4 | 2 - UP 5 | 1 - UP 6 | 0 - UP 7 | 0 - DOWN 8 | 1 - DOWN 9 | 2 - DOWN 10 | 0 - UP 11 | 1 - UP 12 | 2 - UP 13 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeckMini_EachKeyOnce.verified.txt: -------------------------------------------------------------------------------- 1 | 1 - DOWN 2 | 1 - UP 3 | 2 - DOWN 4 | 2 - UP 5 | 3 - DOWN 6 | 3 - UP 7 | 4 - DOWN 8 | 4 - UP 9 | 5 - DOWN 10 | 5 - UP 11 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeckMini_MultipleKeysDown.verified.txt: -------------------------------------------------------------------------------- 1 | 0 - DOWN 2 | 1 - DOWN 3 | 2 - DOWN 4 | 2 - UP 5 | 1 - UP 6 | 0 - UP 7 | 0 - DOWN 8 | 1 - DOWN 9 | 2 - DOWN 10 | 0 - UP 11 | 1 - UP 12 | 2 - UP 13 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeckXL_EachKeyOnce.verified.txt: -------------------------------------------------------------------------------- 1 | 0 - DOWN 2 | 0 - UP 3 | 1 - DOWN 4 | 1 - UP 5 | 2 - DOWN 6 | 2 - UP 7 | 3 - DOWN 8 | 3 - UP 9 | 4 - DOWN 10 | 4 - UP 11 | 5 - DOWN 12 | 5 - UP 13 | 6 - DOWN 14 | 6 - UP 15 | 7 - DOWN 16 | 7 - UP 17 | 8 - DOWN 18 | 8 - UP 19 | 9 - DOWN 20 | 9 - UP 21 | 10 - DOWN 22 | 10 - UP 23 | 11 - DOWN 24 | 11 - UP 25 | 12 - DOWN 26 | 12 - UP 27 | 13 - DOWN 28 | 13 - UP 29 | 14 - DOWN 30 | 14 - UP 31 | 15 - DOWN 32 | 15 - UP 33 | 16 - DOWN 34 | 16 - UP 35 | 17 - DOWN 36 | 17 - UP 37 | 18 - DOWN 38 | 18 - UP 39 | 19 - DOWN 40 | 19 - UP 41 | 20 - DOWN 42 | 20 - UP 43 | 21 - DOWN 44 | 21 - UP 45 | 22 - DOWN 46 | 22 - UP 47 | 23 - DOWN 48 | 23 - UP 49 | 24 - DOWN 50 | 24 - UP 51 | 25 - DOWN 52 | 25 - UP 53 | 26 - DOWN 54 | 26 - UP 55 | 27 - DOWN 56 | 27 - UP 57 | 28 - DOWN 58 | 28 - UP 59 | 29 - DOWN 60 | 29 - UP 61 | 30 - DOWN 62 | 30 - UP 63 | 31 - DOWN 64 | 31 - UP 65 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeckXL_MultipleKeysDown.verified.txt: -------------------------------------------------------------------------------- 1 | 0 - DOWN 2 | 1 - DOWN 3 | 2 - DOWN 4 | 2 - UP 5 | 1 - UP 6 | 0 - UP 7 | 0 - DOWN 8 | 1 - DOWN 9 | 2 - DOWN 10 | 0 - UP 11 | 1 - UP 12 | 2 - UP 13 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeck_EachKeyOnce.verified.txt: -------------------------------------------------------------------------------- 1 | 0 - DOWN 2 | 0 - UP 3 | 1 - DOWN 4 | 1 - UP 5 | 2 - DOWN 6 | 2 - UP 7 | 3 - DOWN 8 | 3 - UP 9 | 4 - DOWN 10 | 4 - UP 11 | 5 - DOWN 12 | 5 - UP 13 | 6 - DOWN 14 | 6 - UP 15 | 7 - DOWN 16 | 7 - UP 17 | 8 - DOWN 18 | 8 - UP 19 | 9 - DOWN 20 | 9 - UP 21 | 10 - DOWN 22 | 10 - UP 23 | 11 - DOWN 24 | 11 - UP 25 | 12 - DOWN 26 | 12 - UP 27 | 13 - DOWN 28 | 13 - UP 29 | 14 - DOWN 30 | 14 - UP 31 | -------------------------------------------------------------------------------- /src/StreamDeckSharp.Tests/VerifiedSnapshots/StreamDeckIoKeyTests.InputReportsBehaveAsExpected/StreamDeck_MultipleKeysDown.verified.txt: -------------------------------------------------------------------------------- 1 | 1 - DOWN 2 | 0 - DOWN 3 | 2 - DOWN 4 | 2 - UP 5 | 1 - UP 6 | 0 - UP 7 | 0 - DOWN 8 | 1 - DOWN 9 | 2 - DOWN 10 | 0 - UP 11 | 1 - UP 12 | 2 - UP 13 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("StreamDeckSharp.Tests")] 4 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Exceptions/StreamDeckException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Runtime.Serialization; 4 | 5 | namespace StreamDeckSharp.Exceptions 6 | { 7 | /// 8 | /// Base class for all StreamDeckSharp Exceptions 9 | /// 10 | [Serializable] 11 | [ExcludeFromCodeCoverage] 12 | public abstract class StreamDeckException : Exception 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | protected StreamDeckException() 18 | { 19 | } 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The message that describes the error. 25 | protected StreamDeckException(string message) 26 | : base(message) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// The message that describes the error. 34 | /// 35 | /// The exception that is the cause of the current exception, or a null reference 36 | /// if no inner exception is specified. 37 | /// 38 | protected StreamDeckException(string message, Exception innerException) 39 | : base(message, innerException) 40 | { 41 | } 42 | 43 | /// 44 | /// Initializes a new instance of the class. 45 | /// 46 | /// 47 | /// The System.Runtime.Serialization.SerializationInfo that holds the serialized 48 | /// object data about the exception being thrown. 49 | /// 50 | /// 51 | /// The System.Runtime.Serialization.StreamingContext that contains contextual information 52 | /// about the source or destination. 53 | /// 54 | protected StreamDeckException(SerializationInfo info, StreamingContext context) 55 | : base(info, context) 56 | { 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Exceptions/StreamDeckNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Runtime.Serialization; 4 | 5 | namespace StreamDeckSharp.Exceptions 6 | { 7 | /// 8 | /// Is thrown if no device could be found 9 | /// 10 | [Serializable] 11 | [ExcludeFromCodeCoverage] 12 | public class StreamDeckNotFoundException 13 | : StreamDeckException 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | internal StreamDeckNotFoundException() 19 | : base("Stream Deck not found.") 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The message that describes the error. 27 | internal StreamDeckNotFoundException(string message) 28 | : base(message) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// The message that describes the error. 36 | /// 37 | /// The exception that is the cause of the current exception, or a null reference 38 | /// if no inner exception is specified. 39 | /// 40 | internal StreamDeckNotFoundException(string message, Exception innerException) 41 | : base(message, innerException) 42 | { 43 | } 44 | 45 | /// 46 | /// Initializes a new instance of the class. 47 | /// 48 | /// 49 | /// The System.Runtime.Serialization.SerializationInfo that holds the serialized 50 | /// object data about the exception being thrown. 51 | /// 52 | /// 53 | /// The System.Runtime.Serialization.StreamingContext that contains contextual information 54 | /// about the source or destination. 55 | /// 56 | protected StreamDeckNotFoundException(SerializationInfo info, StreamingContext context) 57 | : base(info, context) 58 | { 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Hardware.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using StreamDeckSharp.Internals; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | using static StreamDeckSharp.UsbConstants; 8 | 9 | #pragma warning disable AV1710 // Member name includes the name of its containing type 10 | 11 | namespace StreamDeckSharp 12 | { 13 | /// 14 | /// Details about different StreamDeck Hardware 15 | /// 16 | public static class Hardware 17 | { 18 | private static readonly ConcurrentDictionary RegisteredHardware = new(); 19 | 20 | static Hardware() 21 | { 22 | // .-------------. 23 | // | Stream Deck | 24 | // '-------------' 25 | 26 | var streamDeckKeys = new GridKeyLayout(5, 3, 72, 30); 27 | 28 | StreamDeck = 29 | RegisterNewHardwareInternal( 30 | new 31 | ( 32 | VendorIds.ElgatoSystemsGmbH, 33 | ProductIds.StreamDeck 34 | ), 35 | "Stream Deck", 36 | streamDeckKeys, 37 | new HidComDriverStreamDeck() 38 | { 39 | // Limit of 3'200'000 bytes/s (~3.0 MiB/s) 40 | // because without that limit glitches will happen on fast writes. 41 | BytesPerSecondLimit = 3_200_000, 42 | } 43 | ); 44 | 45 | var streamDeckKeysNew = new GridKeyLayout(5, 3, 72, 32); 46 | 47 | StreamDeckMK2 = 48 | RegisterNewHardwareInternal( 49 | new 50 | ( 51 | VendorIds.ElgatoSystemsGmbH, 52 | ProductIds.StreamDeckMK2 53 | ), 54 | "Stream Deck MK.2", 55 | streamDeckKeysNew, 56 | new HidComDriverStreamDeckJpeg(72) 57 | { 58 | // Limit of 1'500'000 bytes/s (~1.5 MB/s), 59 | // because ImageGlitchTest.Rainbow has glitches with higher speeds 60 | BytesPerSecondLimit = 1_500_000, 61 | } 62 | ); 63 | 64 | StreamDeckRev2 = 65 | RegisterNewHardwareInternal( 66 | new 67 | ( 68 | VendorIds.ElgatoSystemsGmbH, 69 | ProductIds.StreamDeckRev2 70 | ), 71 | "Stream Deck Rev2", 72 | streamDeckKeysNew, 73 | new HidComDriverStreamDeckJpeg(72) 74 | { 75 | // Limit of 3'200'000 bytes/s (~3.0 MiB/s) just to be safe, 76 | // because I don't own a StreamDeck Rev2 to test it. 77 | BytesPerSecondLimit = 3_200_000, 78 | } 79 | ); 80 | 81 | // .----------------. 82 | // | Stream Deck XL | 83 | // '----------------' 84 | 85 | var streamDeckXlkeys = new GridKeyLayout(8, 4, 96, 38); 86 | var streamDeckXlDriver = new HidComDriverStreamDeckJpeg(96); 87 | 88 | StreamDeckXL = 89 | RegisterNewHardwareInternal( 90 | new 91 | ( 92 | VendorIds.ElgatoSystemsGmbH, 93 | ProductIds.StreamDeckXL 94 | ), 95 | "Stream Deck XL", 96 | streamDeckXlkeys, 97 | streamDeckXlDriver 98 | ); 99 | 100 | StreamDeckXlRev2 = 101 | RegisterNewHardwareInternal( 102 | new 103 | ( 104 | VendorIds.ElgatoSystemsGmbH, 105 | ProductIds.StreamDeckXLRev2 106 | ), 107 | "Stream Deck XL Rev2", 108 | streamDeckXlkeys, 109 | streamDeckXlDriver 110 | ); 111 | 112 | // .------------------. 113 | // | Stream Deck Mini | 114 | // '------------------' 115 | 116 | var streamDeckMiniKeys = new GridKeyLayout(3, 2, 80, 32); 117 | var streamDeckMiniDriver = new HidComDriverStreamDeckMini(80); 118 | 119 | StreamDeckMini = 120 | RegisterNewHardwareInternal( 121 | new 122 | ( 123 | VendorIds.ElgatoSystemsGmbH, 124 | ProductIds.StreamDeckMini 125 | ), 126 | "Stream Deck Mini", 127 | streamDeckMiniKeys, 128 | streamDeckMiniDriver 129 | ); 130 | 131 | SteamDeckMiniRev2 = 132 | RegisterNewHardwareInternal( 133 | new 134 | ( 135 | VendorIds.ElgatoSystemsGmbH, 136 | ProductIds.StreamDeckMiniRev2 137 | ), 138 | "Stream Deck Mini Rev2", 139 | streamDeckMiniKeys, 140 | streamDeckMiniDriver 141 | ); 142 | } 143 | 144 | /// 145 | /// Details about the classic Stream Deck 146 | /// 147 | public static IUsbHidHardware StreamDeck { get; } 148 | 149 | /// 150 | /// Details about the updated Stream Deck MK.2 151 | /// 152 | public static IUsbHidHardware StreamDeckMK2 { get; } 153 | 154 | /// 155 | /// Details about the classic Stream Deck Rev 2 156 | /// 157 | public static IUsbHidHardware StreamDeckRev2 { get; } 158 | 159 | /// 160 | /// Details about the Stream Deck XL 161 | /// 162 | public static IUsbHidHardware StreamDeckXL { get; } 163 | 164 | /// 165 | /// Details about the Stream Deck XL Rev2 166 | /// 167 | public static IUsbHidHardware StreamDeckXlRev2 { get; } 168 | 169 | /// 170 | /// Details about the Stream Deck Mini 171 | /// 172 | public static IUsbHidHardware StreamDeckMini { get; } 173 | 174 | /// 175 | /// Details about the Stream Deck Mini Rev2 176 | /// 177 | public static IUsbHidHardware SteamDeckMiniRev2 { get; } 178 | 179 | /// 180 | /// This method registers a new (currently unknown to this library) hardware driver. 181 | /// 182 | /// 183 | /// 184 | /// This method can be used if a new stream deck hardware is released to the market and 185 | /// the library currently doesn't have support for that new device. In the past a new device 186 | /// was often pretty similar to an existing device so with this method a tech-savvy person 187 | /// can register that new device. 188 | /// 189 | /// 190 | /// This feature is a bit "low-level", just take a look at the source code 191 | /// if you are not sure what to do. 192 | /// 193 | /// 194 | /// The USB vendor and product ID. 195 | /// A human readable name of the device. 196 | /// The key layout of the device. 197 | /// The code that is used to communicate to the device. 198 | /// 199 | /// Returns a description of the device that can be used to open that device with 200 | /// or 201 | /// . 202 | /// 203 | public static IUsbHidHardware RegisterNewHardware( 204 | UsbVendorProductPair usbId, 205 | string deviceName, 206 | GridKeyLayout keyLayout, 207 | IStreamDeckHidComDriver driver 208 | ) 209 | { 210 | return RegisterNewHardwareInternal(usbId, deviceName, keyLayout, driver); 211 | } 212 | 213 | internal static UsbHardwareIdAndDriver RegisterNewHardwareInternal( 214 | UsbVendorProductPair usbId, 215 | string deviceName, 216 | GridKeyLayout keyLayout, 217 | IStreamDeckHidComDriver driver 218 | ) 219 | { 220 | var internalReference = new UsbHardwareIdAndDriver(usbId, deviceName, keyLayout, driver); 221 | 222 | RegisteredHardware.AddOrUpdate(usbId, internalReference, (_, _) => internalReference); 223 | 224 | return internalReference; 225 | } 226 | 227 | internal static IEnumerable GetInternalStreamDeckHardwareInfos() 228 | { 229 | return RegisteredHardware.Values.ToList(); 230 | } 231 | 232 | internal static UsbHardwareIdAndDriver GetInternalHardwareInfos(UsbVendorProductPair usbId) 233 | { 234 | if (RegisteredHardware.TryGetValue(usbId, out var hardwareInfo)) 235 | { 236 | return hardwareInfo; 237 | } 238 | 239 | return null; 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/IHardware.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | 3 | namespace StreamDeckSharp 4 | { 5 | /// 6 | /// A compact collection of hardware specific information about a device. 7 | /// 8 | public interface IHardware 9 | { 10 | /// 11 | /// Key layout information 12 | /// 13 | GridKeyLayout Keys { get; } 14 | 15 | /// 16 | /// Name of the device 17 | /// 18 | string DeviceName { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/IUsbHidHardware.cs: -------------------------------------------------------------------------------- 1 | namespace StreamDeckSharp 2 | { 3 | /// 4 | /// USB HID specific hardware information 5 | /// 6 | public interface IUsbHidHardware : IHardware 7 | { 8 | /// 9 | /// Unique identifier for USB device. Vendor and product ID pair. 10 | /// 11 | UsbVendorProductPair UsbId { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/BasicHidClient.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using System; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace StreamDeckSharp.Internals 7 | { 8 | internal class BasicHidClient : IMacroBoard 9 | { 10 | private readonly byte[] keyStates; 11 | private readonly object disposeLock = new(); 12 | 13 | public BasicHidClient( 14 | IStreamDeckHid deckHid, 15 | IKeyLayout keys, 16 | IStreamDeckHidComDriver hidComDriver 17 | ) 18 | { 19 | DeckHid = deckHid; 20 | Keys = keys; 21 | 22 | deckHid.ConnectionStateChanged += (_, e) => ConnectionStateChanged?.Invoke(this, e); 23 | deckHid.ReportReceived += DeckHid_ReportReceived; 24 | 25 | HidComDriver = hidComDriver; 26 | Buffer = new byte[deckHid.OutputReportLength]; 27 | keyStates = new byte[Keys.Count]; 28 | } 29 | 30 | public event EventHandler KeyStateChanged; 31 | public event EventHandler ConnectionStateChanged; 32 | 33 | public IKeyLayout Keys { get; } 34 | public bool IsDisposed { get; private set; } 35 | public bool IsConnected => DeckHid.IsConnected; 36 | 37 | protected IStreamDeckHid DeckHid { get; } 38 | protected IStreamDeckHidComDriver HidComDriver { get; } 39 | protected byte[] Buffer { get; } 40 | 41 | public void Dispose() 42 | { 43 | Dispose(true); 44 | GC.SuppressFinalize(this); 45 | } 46 | 47 | public string GetFirmwareVersion() 48 | { 49 | return ReadFeatureString(HidComDriver.FirmwareVersionFeatureId, HidComDriver.FirmwareVersionReportSkip); 50 | } 51 | 52 | public string GetSerialNumber() 53 | { 54 | return ReadFeatureString(HidComDriver.SerialNumberFeatureId, HidComDriver.SerialNumberReportSkip); 55 | } 56 | 57 | public void SetBrightness(byte percent) 58 | { 59 | ThrowIfAlreadyDisposed(); 60 | DeckHid.WriteFeature(HidComDriver.GetBrightnessMessage(percent)); 61 | } 62 | 63 | public virtual void SetKeyBitmap(int keyId, KeyBitmap bitmapData) 64 | { 65 | keyId = HidComDriver.ExtKeyIdToHardwareKeyId(keyId); 66 | 67 | var payload = HidComDriver.GeneratePayload(bitmapData); 68 | 69 | var reports = OutputReportSplitter.Split( 70 | payload, 71 | Buffer, 72 | HidComDriver.ReportSize, 73 | HidComDriver.HeaderSize, 74 | keyId, 75 | HidComDriver.PrepareDataForTransmission 76 | ); 77 | 78 | foreach (var report in reports) 79 | { 80 | DeckHid.WriteReport(report); 81 | } 82 | } 83 | 84 | public void ShowLogo() 85 | { 86 | ThrowIfAlreadyDisposed(); 87 | ShowLogoWithoutDisposeVerification(); 88 | } 89 | 90 | protected virtual void Shutdown() 91 | { 92 | } 93 | 94 | protected virtual void Dispose(bool disposing) 95 | { 96 | if (disposing) 97 | { 98 | lock (disposeLock) 99 | { 100 | if (IsDisposed) 101 | { 102 | return; 103 | } 104 | 105 | IsDisposed = true; 106 | } 107 | 108 | Shutdown(); 109 | 110 | // Sleep to let the stream deck catch up. 111 | // Without this Sleep() the stream deck might set a key image after the logo was shown. 112 | // I've no idea why it's sometimes executed out of order even though the write is synchronized. 113 | Thread.Sleep(50); 114 | 115 | ShowLogoWithoutDisposeVerification(); 116 | 117 | DeckHid.Dispose(); 118 | } 119 | } 120 | 121 | protected void ThrowIfAlreadyDisposed() 122 | { 123 | if (IsDisposed) 124 | { 125 | throw new ObjectDisposedException(nameof(BasicHidClient)); 126 | } 127 | } 128 | 129 | private string ReadFeatureString(byte featureId, int skipBytes) 130 | { 131 | if (!DeckHid.ReadFeatureData(featureId, out var featureData)) 132 | { 133 | #pragma warning disable AV1135 // Do not return null for strings, collections or tasks 134 | return null; 135 | #pragma warning restore AV1135 136 | } 137 | 138 | return Encoding.UTF8.GetString(featureData, skipBytes, featureData.Length - skipBytes).Trim('\0'); 139 | } 140 | 141 | private void DeckHid_ReportReceived(object sender, ReportReceivedEventArgs e) 142 | { 143 | ProcessKeys(e.ReportData); 144 | } 145 | 146 | private void ProcessKeys(byte[] newStates) 147 | { 148 | for (var i = 0; i < keyStates.Length; i++) 149 | { 150 | var newStatePos = i + HidComDriver.KeyReportOffset; 151 | 152 | if (keyStates[i] != newStates[newStatePos]) 153 | { 154 | var externalKeyId = HidComDriver.HardwareKeyIdToExtKeyId(i); 155 | KeyStateChanged?.Invoke(this, new KeyEventArgs(externalKeyId, newStates[newStatePos] != 0)); 156 | keyStates[i] = newStates[newStatePos]; 157 | } 158 | } 159 | } 160 | 161 | private void ShowLogoWithoutDisposeVerification() 162 | { 163 | DeckHid.WriteFeature(HidComDriver.GetLogoMessage()); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/CachedHidClient.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace StreamDeckSharp.Internals 7 | { 8 | internal class CachedHidClient : BasicHidClient 9 | { 10 | private readonly Task writerTask; 11 | private readonly ConcurrentBufferedQueue imageQueue; 12 | private readonly ConditionalWeakTable cacheKeyBitmaps = new(); 13 | 14 | public CachedHidClient( 15 | IStreamDeckHid deckHid, 16 | IKeyLayout keys, 17 | IStreamDeckHidComDriver hidComDriver 18 | ) 19 | : base(deckHid, keys, hidComDriver) 20 | { 21 | imageQueue = new ConcurrentBufferedQueue(); 22 | writerTask = StartBitmapWriterTask(); 23 | } 24 | 25 | public override void SetKeyBitmap(int keyId, KeyBitmap bitmapData) 26 | { 27 | ThrowIfAlreadyDisposed(); 28 | keyId = HidComDriver.ExtKeyIdToHardwareKeyId(keyId); 29 | 30 | var payload = cacheKeyBitmaps.GetValue(bitmapData, HidComDriver.GeneratePayload); 31 | imageQueue.Add(keyId, payload); 32 | } 33 | 34 | protected override void Shutdown() 35 | { 36 | imageQueue.CompleteAdding(); 37 | writerTask.Wait(); 38 | } 39 | 40 | protected override void Dispose(bool disposing) 41 | { 42 | base.Dispose(disposing); 43 | imageQueue.Dispose(); 44 | } 45 | 46 | private Task StartBitmapWriterTask() 47 | { 48 | void BackgroundAction() 49 | { 50 | while (true) 51 | { 52 | var (success, keyId, payload) = imageQueue.Take(); 53 | 54 | if (!success) 55 | { 56 | // image queue completed 57 | break; 58 | } 59 | 60 | var reports = OutputReportSplitter.Split( 61 | payload, 62 | Buffer, 63 | HidComDriver.ReportSize, 64 | HidComDriver.HeaderSize, 65 | keyId, 66 | HidComDriver.PrepareDataForTransmission 67 | ); 68 | 69 | foreach (var report in reports) 70 | { 71 | DeckHid.WriteReport(report); 72 | } 73 | } 74 | } 75 | 76 | return Task.Factory.StartNew( 77 | BackgroundAction, 78 | CancellationToken.None, 79 | TaskCreationOptions.LongRunning, 80 | TaskScheduler.Default 81 | ); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/ConcurrentBufferedQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace StreamDeckSharp.Internals 6 | { 7 | internal sealed class ConcurrentBufferedQueue : IDisposable 8 | { 9 | private readonly object sync = new(); 10 | 11 | private readonly Dictionary valueBuffer = new(); 12 | private readonly Queue queue = new(); 13 | 14 | private volatile bool isAddingCompleted; 15 | private volatile bool disposed; 16 | 17 | public int Count => queue.Count; 18 | 19 | public bool IsAddingCompleted 20 | { 21 | get 22 | { 23 | ThrowIfDisposed(); 24 | return isAddingCompleted; 25 | } 26 | } 27 | 28 | public bool IsCompleted 29 | { 30 | get 31 | { 32 | lock (sync) 33 | { 34 | ThrowIfDisposed(); 35 | return isAddingCompleted && Count == 0; 36 | } 37 | } 38 | } 39 | 40 | public void Add(TKey key, TValue value) 41 | { 42 | lock (sync) 43 | { 44 | ThrowIfDisposed(); 45 | 46 | if (isAddingCompleted) 47 | { 48 | throw new InvalidOperationException("Adding was already marked as completed."); 49 | } 50 | 51 | try 52 | { 53 | valueBuffer[key] = value; 54 | 55 | if (!queue.Contains(key)) 56 | { 57 | queue.Enqueue(key); 58 | } 59 | } 60 | finally 61 | { 62 | Monitor.PulseAll(sync); 63 | } 64 | } 65 | } 66 | 67 | public (bool Success, TKey Key, TValue Value) Take() 68 | { 69 | lock (sync) 70 | { 71 | while (queue.Count < 1) 72 | { 73 | ThrowIfDisposed(); 74 | 75 | if (isAddingCompleted) 76 | { 77 | return (false, default, default); 78 | } 79 | 80 | Monitor.Wait(sync); 81 | } 82 | 83 | ThrowIfDisposed(); 84 | 85 | var key = queue.Dequeue(); 86 | var value = valueBuffer[key]; 87 | valueBuffer.Remove(key); 88 | 89 | return (true, key, value); 90 | } 91 | } 92 | 93 | public void CompleteAdding() 94 | { 95 | lock (sync) 96 | { 97 | if (isAddingCompleted) 98 | { 99 | return; 100 | } 101 | 102 | isAddingCompleted = true; 103 | Monitor.PulseAll(sync); 104 | } 105 | } 106 | 107 | public void Dispose() 108 | { 109 | lock (sync) 110 | { 111 | if (disposed) 112 | { 113 | return; 114 | } 115 | 116 | disposed = true; 117 | 118 | if (!isAddingCompleted) 119 | { 120 | CompleteAdding(); 121 | } 122 | } 123 | } 124 | 125 | private void ThrowIfDisposed() 126 | { 127 | if (disposed) 128 | { 129 | throw new ObjectDisposedException(nameof(ConcurrentBufferedQueue)); 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/DeviceListExtensions.cs: -------------------------------------------------------------------------------- 1 | using HidSharp; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace StreamDeckSharp.Internals 7 | { 8 | internal static class DeviceListExtensions 9 | { 10 | public static IEnumerable GetStreamDecks( 11 | this DeviceList deviceList, 12 | params IUsbHidHardware[] hardware 13 | ) 14 | { 15 | if (deviceList is null) 16 | { 17 | throw new ArgumentNullException(nameof(deviceList)); 18 | } 19 | 20 | var matchAllKnowDevices = hardware is null || hardware.Length < 1; 21 | 22 | UsbHardwareIdAndDriver MatchingHardware(HidDevice d) 23 | { 24 | var hwDetails = d.GetHardwareInformation(); 25 | 26 | if (hwDetails is null) 27 | { 28 | return null; 29 | } 30 | 31 | if (matchAllKnowDevices) 32 | { 33 | return hwDetails; 34 | } 35 | 36 | foreach (var h in hardware) 37 | { 38 | var deviceUsbKey = new UsbVendorProductPair(d.VendorID, d.ProductID); 39 | 40 | if (deviceUsbKey == h.UsbId) 41 | { 42 | return hwDetails; 43 | } 44 | } 45 | 46 | return null; 47 | } 48 | 49 | return deviceList 50 | .GetHidDevices() 51 | .Select(device => new { HardwareInfo = MatchingHardware(device), Device = device }) 52 | .Where(i => i.HardwareInfo != null) 53 | .Select(i => new StreamDeckDeviceReference( 54 | i.Device.DevicePath, 55 | i.HardwareInfo.DeviceName, 56 | i.HardwareInfo.Keys 57 | )); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/HidComDriverStreamDeck.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using System; 3 | 4 | namespace StreamDeckSharp.Internals 5 | { 6 | /// 7 | /// HID Stream Deck communication driver for the classical Stream Deck. 8 | /// 9 | public sealed class HidComDriverStreamDeck 10 | : IStreamDeckHidComDriver 11 | { 12 | private const int ImgWidth = 72; 13 | private const int ColorChannels = 3; 14 | 15 | private static readonly byte[] BmpHeader = new byte[] 16 | { 17 | 0x42, 0x4d, 0xf6, 0x3c, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 19 | 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 20 | 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc4, 0x0e, 22 | 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | }; 25 | 26 | /// 27 | public int HeaderSize => 16; 28 | 29 | /// 30 | public int ReportSize => 7819; 31 | 32 | /// 33 | public int ExpectedFeatureReportLength => 17; 34 | 35 | /// 36 | public int ExpectedOutputReportLength => 8191; 37 | 38 | /// 39 | public int ExpectedInputReportLength => 17; 40 | 41 | /// 42 | public int KeyReportOffset => 1; 43 | 44 | /// 45 | public byte FirmwareVersionFeatureId => 4; 46 | 47 | /// 48 | public byte SerialNumberFeatureId => 3; 49 | 50 | /// 51 | public int FirmwareVersionReportSkip => 5; 52 | 53 | /// 54 | public int SerialNumberReportSkip => 5; 55 | 56 | /// 57 | public double BytesPerSecondLimit { get; set; } = double.PositiveInfinity; 58 | 59 | /// 60 | public byte[] GeneratePayload(KeyBitmap keyBitmap) 61 | { 62 | var rawData = keyBitmap.GetScaledVersion(ImgWidth, ImgWidth); 63 | 64 | var bmp = new byte[ImgWidth * ImgWidth * 3 + BmpHeader.Length]; 65 | Array.Copy(BmpHeader, 0, bmp, 0, BmpHeader.Length); 66 | 67 | if (rawData.Length != 0) 68 | { 69 | for (var y = 0; y < ImgWidth; y++) 70 | { 71 | for (var x = 0; x < ImgWidth; x++) 72 | { 73 | var src = (y * ImgWidth + x) * ColorChannels; 74 | var tar = (y * ImgWidth + (ImgWidth - 1 - x)) * ColorChannels + BmpHeader.Length; 75 | 76 | bmp[tar + 0] = rawData[src + 0]; 77 | bmp[tar + 1] = rawData[src + 1]; 78 | bmp[tar + 2] = rawData[src + 2]; 79 | } 80 | } 81 | } 82 | 83 | return bmp; 84 | } 85 | 86 | /// 87 | public int ExtKeyIdToHardwareKeyId(int extKeyId) 88 | { 89 | return FlipIdsHorizontal(extKeyId); 90 | } 91 | 92 | /// 93 | public int HardwareKeyIdToExtKeyId(int hardwareKeyId) 94 | { 95 | return FlipIdsHorizontal(hardwareKeyId); 96 | } 97 | 98 | /// 99 | public void PrepareDataForTransmission( 100 | byte[] data, 101 | int pageNumber, 102 | int payloadLength, 103 | int keyId, 104 | bool isLast 105 | ) 106 | { 107 | data[0] = 2; // Report ID ? 108 | data[1] = 1; // ? 109 | data[2] = (byte)(pageNumber + 1); 110 | data[4] = (byte)(isLast ? 1 : 0); 111 | data[5] = (byte)(keyId + 1); 112 | } 113 | 114 | /// 115 | public byte[] GetBrightnessMessage(byte percent) 116 | { 117 | if (percent > 100) 118 | { 119 | throw new ArgumentOutOfRangeException(nameof(percent)); 120 | } 121 | 122 | var buffer = new byte[] 123 | { 124 | 0x05, 0x55, 0xaa, 0xd1, 0x01, 0x64, 0x00, 0x00, 125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 126 | 0x00, 127 | }; 128 | 129 | buffer[5] = percent; 130 | return buffer; 131 | } 132 | 133 | /// 134 | public byte[] GetLogoMessage() 135 | { 136 | return new byte[] { 0x0B, 0x63 }; 137 | } 138 | 139 | /// 140 | private static int FlipIdsHorizontal(int keyId) 141 | { 142 | var diff = ((keyId % 5) - 2) * -2; 143 | return keyId + diff; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/HidComDriverStreamDeckJpeg.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using SixLabors.ImageSharp; 3 | using SixLabors.ImageSharp.Formats.Jpeg; 4 | using SixLabors.ImageSharp.PixelFormats; 5 | using System; 6 | using System.IO; 7 | 8 | namespace StreamDeckSharp.Internals 9 | { 10 | /// 11 | /// HID Stream Deck communication driver for JPEG based devices. 12 | /// 13 | public sealed class HidComDriverStreamDeckJpeg 14 | : IStreamDeckHidComDriver 15 | { 16 | private readonly int imgSize; 17 | private readonly JpegEncoder jpgEncoder; 18 | 19 | private byte[] cachedNullImage = null; 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The size of the button images in pixels. 25 | /// /// Thrown if the is smaller than one. 26 | public HidComDriverStreamDeckJpeg(int imgSize) 27 | { 28 | if (imgSize < 1) 29 | { 30 | throw new ArgumentOutOfRangeException(nameof(imgSize)); 31 | } 32 | 33 | jpgEncoder = new JpegEncoder() 34 | { 35 | Quality = 100, 36 | }; 37 | 38 | this.imgSize = imgSize; 39 | } 40 | 41 | /// 42 | public int HeaderSize => 8; 43 | 44 | /// 45 | public int ReportSize => 1024; 46 | 47 | /// 48 | public int ExpectedFeatureReportLength => 32; 49 | 50 | /// 51 | public int ExpectedOutputReportLength => 1024; 52 | 53 | /// 54 | public int ExpectedInputReportLength => 512; 55 | 56 | /// 57 | public int KeyReportOffset => 4; 58 | 59 | /// 60 | public byte FirmwareVersionFeatureId => 5; 61 | 62 | /// 63 | public byte SerialNumberFeatureId => 6; 64 | 65 | /// 66 | public int FirmwareVersionReportSkip => 6; 67 | 68 | /// 69 | public int SerialNumberReportSkip => 2; 70 | 71 | /// 72 | public double BytesPerSecondLimit { get; set; } = double.PositiveInfinity; 73 | 74 | /// 75 | public int ExtKeyIdToHardwareKeyId(int extKeyId) 76 | { 77 | return extKeyId; 78 | } 79 | 80 | /// 81 | public byte[] GeneratePayload(KeyBitmap keyBitmap) 82 | { 83 | var rawData = keyBitmap.GetScaledVersion(imgSize, imgSize); 84 | 85 | if (rawData.Length == 0) 86 | { 87 | return GetNullImage(); 88 | } 89 | 90 | return EncodeImageToJpg(rawData); 91 | } 92 | 93 | /// 94 | public int HardwareKeyIdToExtKeyId(int hardwareKeyId) 95 | { 96 | return hardwareKeyId; 97 | } 98 | 99 | /// 100 | public void PrepareDataForTransmission( 101 | byte[] data, 102 | int pageNumber, 103 | int payloadLength, 104 | int keyId, 105 | bool isLast 106 | ) 107 | { 108 | data[0] = 2; 109 | data[1] = 7; 110 | data[2] = (byte)keyId; 111 | data[3] = (byte)(isLast ? 1 : 0); 112 | data[4] = (byte)(payloadLength & 255); 113 | data[5] = (byte)(payloadLength >> 8); 114 | data[6] = (byte)pageNumber; 115 | } 116 | 117 | /// 118 | public byte[] GetBrightnessMessage(byte percent) 119 | { 120 | if (percent > 100) 121 | { 122 | throw new ArgumentOutOfRangeException(nameof(percent)); 123 | } 124 | 125 | var buffer = new byte[] 126 | { 127 | 0x03, 0x08, 0x64, 0x23, 0xB8, 0x01, 0x00, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 | 0xA5, 0x49, 0xCD, 0x02, 0xFE, 0x7F, 0x00, 0x00, 131 | }; 132 | 133 | buffer[2] = percent; 134 | buffer[3] = 0x23; // 0x23, sometimes 0x27 135 | 136 | return buffer; 137 | } 138 | 139 | /// 140 | public byte[] GetLogoMessage() 141 | { 142 | return new byte[] { 0x03, 0x02 }; 143 | } 144 | 145 | private byte[] GetNullImage() 146 | { 147 | if (cachedNullImage is null) 148 | { 149 | var rawNullImg = KeyBitmap.Create.FromBgr24Array(1, 1, new byte[] { 0, 0, 0 }).GetScaledVersion(imgSize, imgSize); 150 | cachedNullImage = EncodeImageToJpg(rawNullImg); 151 | } 152 | 153 | return cachedNullImage; 154 | } 155 | 156 | private byte[] EncodeImageToJpg(ReadOnlySpan bgr24) 157 | { 158 | // Flip XY ... for some reason the JPEG devices have flipped x and y coordinates. 159 | var flippedData = new byte[imgSize * imgSize * 3]; 160 | 161 | for (var y = 0; y < imgSize; y++) 162 | { 163 | for (var x = 0; x < imgSize; x++) 164 | { 165 | var x1 = imgSize - 1 - x; 166 | var y1 = imgSize - 1 - y; 167 | 168 | var pTarget = (y * imgSize + x) * 3; 169 | var pSource = (y1 * imgSize + x1) * 3; 170 | 171 | flippedData[pTarget + 0] = bgr24[pSource + 0]; 172 | flippedData[pTarget + 1] = bgr24[pSource + 1]; 173 | flippedData[pTarget + 2] = bgr24[pSource + 2]; 174 | } 175 | } 176 | 177 | using var image = Image.LoadPixelData(flippedData, imgSize, imgSize); 178 | 179 | using var memStream = new MemoryStream(); 180 | image.SaveAsJpeg(memStream, jpgEncoder); 181 | 182 | return memStream.ToArray(); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/HidComDriverStreamDeckMini.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using System; 3 | 4 | namespace StreamDeckSharp.Internals 5 | { 6 | /// 7 | /// HID Stream Deck communication driver for the Stream Deck Mini. 8 | /// 9 | public sealed class HidComDriverStreamDeckMini 10 | : IStreamDeckHidComDriver 11 | { 12 | private const int ColorChannels = 3; 13 | 14 | private static readonly byte[] BmpHeader = new byte[] 15 | { 16 | 0x42, 0x4d, 0x36, 0x4b, 0x00, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 18 | 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x00, 19 | 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0xc4, 0x0e, 21 | 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 23 | }; 24 | 25 | private readonly int imgSize; 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// The size of the button images in pixels. 31 | /// Thrown if the is smaller than one. 32 | public HidComDriverStreamDeckMini(int imgSize) 33 | { 34 | if (imgSize < 1) 35 | { 36 | throw new ArgumentOutOfRangeException(nameof(imgSize)); 37 | } 38 | 39 | this.imgSize = imgSize; 40 | } 41 | 42 | /// 43 | public int HeaderSize => 16; 44 | 45 | /// 46 | public int ReportSize => 1024; 47 | 48 | /// 49 | public int ExpectedFeatureReportLength => 17; 50 | 51 | /// 52 | public int ExpectedOutputReportLength => 1024; 53 | 54 | /// 55 | public int ExpectedInputReportLength => 17; 56 | 57 | /// 58 | public int KeyReportOffset => 1; 59 | 60 | /// 61 | public byte FirmwareVersionFeatureId => 4; 62 | 63 | /// 64 | public byte SerialNumberFeatureId => 3; 65 | 66 | /// 67 | public int FirmwareVersionReportSkip => 5; 68 | 69 | /// 70 | public int SerialNumberReportSkip => 5; 71 | 72 | /// 73 | public double BytesPerSecondLimit => double.PositiveInfinity; 74 | 75 | /// 76 | public byte[] GeneratePayload(KeyBitmap keyBitmap) 77 | { 78 | var rawData = keyBitmap.GetScaledVersion(imgSize, imgSize); 79 | var bmp = new byte[imgSize * imgSize * ColorChannels + BmpHeader.Length]; 80 | 81 | Array.Copy(BmpHeader, 0, bmp, 0, BmpHeader.Length); 82 | 83 | if (rawData.Length != 0) 84 | { 85 | for (var y = 0; y < imgSize; y++) 86 | { 87 | for (var x = 0; x < imgSize; x++) 88 | { 89 | var src = (y * imgSize + x) * ColorChannels; 90 | var tar = ((imgSize - x - 1) * imgSize + y) * ColorChannels + BmpHeader.Length; 91 | 92 | bmp[tar + 0] = rawData[src + 0]; 93 | bmp[tar + 1] = rawData[src + 1]; 94 | bmp[tar + 2] = rawData[src + 2]; 95 | } 96 | } 97 | } 98 | 99 | return bmp; 100 | } 101 | 102 | /// 103 | public int ExtKeyIdToHardwareKeyId(int extKeyId) 104 | { 105 | return extKeyId; 106 | } 107 | 108 | /// 109 | public int HardwareKeyIdToExtKeyId(int hardwareKeyId) 110 | { 111 | return hardwareKeyId; 112 | } 113 | 114 | /// 115 | public void PrepareDataForTransmission( 116 | byte[] data, 117 | int pageNumber, 118 | int payloadLength, 119 | int keyId, 120 | bool isLast 121 | ) 122 | { 123 | data[0] = 2; // Report ID ? 124 | data[1] = 1; // ? 125 | data[2] = (byte)pageNumber; 126 | data[4] = (byte)(isLast ? 1 : 0); 127 | data[5] = (byte)(keyId + 1); 128 | } 129 | 130 | /// 131 | public byte[] GetBrightnessMessage(byte percent) 132 | { 133 | if (percent > 100) 134 | { 135 | throw new ArgumentOutOfRangeException(nameof(percent)); 136 | } 137 | 138 | var buffer = new byte[] 139 | { 140 | 0x05, 0x55, 0xaa, 0xd1, 0x01, 0x64, 0x00, 0x00, 141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 142 | 0x00, 143 | }; 144 | 145 | buffer[5] = percent; 146 | return buffer; 147 | } 148 | 149 | /// 150 | public byte[] GetLogoMessage() 151 | { 152 | return new byte[] { 0x0B, 0x63 }; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/HidDeviceExtensions.cs: -------------------------------------------------------------------------------- 1 | using HidSharp; 2 | 3 | namespace StreamDeckSharp.Internals 4 | { 5 | internal static class HidDeviceExtensions 6 | { 7 | public static UsbHardwareIdAndDriver GetHardwareInformation(this HidDevice hid) 8 | { 9 | return Hardware.GetInternalHardwareInfos(new(hid.VendorID, hid.ProductID)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/IStreamDeckHid.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using System; 3 | 4 | namespace StreamDeckSharp.Internals 5 | { 6 | internal interface IStreamDeckHid 7 | : IDisposable 8 | { 9 | event EventHandler ReportReceived; 10 | event EventHandler ConnectionStateChanged; 11 | 12 | bool IsConnected { get; } 13 | int OutputReportLength { get; } 14 | 15 | bool WriteFeature(byte[] featureData); 16 | bool WriteReport(byte[] reportData); 17 | bool ReadFeatureData(byte id, out byte[] data); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/IStreamDeckHidComDriver.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | 3 | namespace StreamDeckSharp.Internals 4 | { 5 | /// 6 | /// Interface that describes the StreamDeck HID communication. 7 | /// 8 | /// 9 | /// 10 | /// Unless there are new stream deck versions you likely don't have to deal with this interface 11 | /// or implementations of it directly as a consumer of the library. Because this interface is 12 | /// low level, some members may not have a good documentation or any at all. 13 | /// 14 | /// Implementations must be thread-safe. 15 | /// 16 | public interface IStreamDeckHidComDriver 17 | { 18 | /// 19 | /// Gets the header size. 20 | /// 21 | int HeaderSize { get; } 22 | 23 | /// 24 | /// Gets the report size. 25 | /// 26 | int ReportSize { get; } 27 | 28 | /// 29 | /// Gets the feature report length for the device. 30 | /// 31 | /// 32 | /// This is asserted (in debug mode). 33 | /// 34 | int ExpectedFeatureReportLength { get; } 35 | 36 | /// 37 | /// Gets the output report length for the device. 38 | /// 39 | /// 40 | /// This is asserted (in debug mode). 41 | /// 42 | int ExpectedOutputReportLength { get; } 43 | 44 | /// 45 | /// Gets the input report length for the device. 46 | /// 47 | /// 48 | /// This is asserted (in debug mode). 49 | /// 50 | int ExpectedInputReportLength { get; } 51 | 52 | /// 53 | /// Gets the offset of the key information inside the key state report. 54 | /// 55 | int KeyReportOffset { get; } 56 | 57 | /// 58 | /// The ID of the feature that identifies the firmware version. 59 | /// 60 | byte FirmwareVersionFeatureId { get; } 61 | 62 | /// 63 | /// Number of bytes to skip before the firmware version string starts. 64 | /// 65 | /// 66 | /// For details see property documentation of . 67 | /// 68 | int FirmwareVersionReportSkip { get; } 69 | 70 | /// 71 | /// The ID of the feature that identifies the serial number. 72 | /// 73 | byte SerialNumberFeatureId { get; } 74 | 75 | /// 76 | /// Number of bytes to skip before the serial number string starts. 77 | /// 78 | /// 79 | /// For some reason some string reports have some "weird" data prefixed. 80 | /// I guess they are some binary encoded details or headers - no idea. 81 | /// This property can be tweaked so the resulting string doesn't contain 82 | /// strange Unicode characters. 83 | /// 84 | int SerialNumberReportSkip { get; } 85 | 86 | /// 87 | /// Limits the USB transfer speed. 88 | /// 89 | /// 90 | /// Some stream decks produce artifacts and glitches when data comes in to fast. 91 | /// I'm not sure if this happens because of this library or because of a bug in 92 | /// the stream deck's firmware but currently the work-around is to limit the transfer rate. 93 | /// This value has to be determined experimentally. 94 | /// 95 | double BytesPerSecondLimit { get; } 96 | 97 | /// 98 | /// Generate they payload for a given . 99 | /// 100 | byte[] GeneratePayload(KeyBitmap keyBitmap); 101 | 102 | /// 103 | /// This is used to convert between keyId conventions 104 | /// 105 | /// 106 | /// The original stream deck has a pretty weird way of enumerating keys. 107 | /// Index 0 starts right top and they are enumerated right to left, 108 | /// and top to bottom. Most developers would expect it to be left-to-right 109 | /// instead of right-to-left, so we change that ;-) 110 | /// 111 | int ExtKeyIdToHardwareKeyId(int extKeyId); 112 | 113 | /// 114 | /// This is used to convert between keyId conventions 115 | /// 116 | int HardwareKeyIdToExtKeyId(int hardwareKeyId); 117 | 118 | /// 119 | /// Before the report is sent to the stream deck (human interface device) this is called to 120 | /// prepare meta information and details in the report header. This depends on the target device 121 | /// and has to be reverse engineered with a USB traffic analyzer. 122 | /// 123 | void PrepareDataForTransmission( 124 | byte[] data, 125 | int pageNumber, 126 | int payloadLength, 127 | int keyId, 128 | bool isLast 129 | ); 130 | 131 | /// 132 | /// Generates a message to set a given brightness. 133 | /// 134 | byte[] GetBrightnessMessage(byte percent); 135 | 136 | /// 137 | /// Generates a message to show the vendor logo. 138 | /// 139 | byte[] GetLogoMessage(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/KeyBitmapExtensions.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | using SixLabors.ImageSharp.Processing; 3 | using System; 4 | 5 | namespace StreamDeckSharp.Internals 6 | { 7 | internal static class KeyBitmapExtensions 8 | { 9 | public static ReadOnlySpan GetScaledVersion(this KeyBitmap keyBitmap, int width, int height) 10 | { 11 | IKeyBitmapDataAccess keyDataAccess = keyBitmap; 12 | 13 | if (keyDataAccess.IsEmpty) 14 | { 15 | // default span is of length 0 (the caller has to check this special case) 16 | return default; 17 | } 18 | 19 | if (keyBitmap.Width == width && keyBitmap.Height == height) 20 | { 21 | // if it is already the size we need just return the underlying data 22 | return keyDataAccess.GetData(); 23 | } 24 | 25 | using var image = keyDataAccess.ToImage(); 26 | 27 | image.Mutate(x => x.Resize(width, height)); 28 | 29 | var scaledPixelData = image.ToBgr24PixelArray(); 30 | 31 | return new ReadOnlySpan(scaledPixelData); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/OutputReportSplitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace StreamDeckSharp.Internals 5 | { 6 | internal static class OutputReportSplitter 7 | { 8 | public delegate void PrepareDataForTransmission( 9 | byte[] data, 10 | int pageNumber, 11 | int payloadLength, 12 | int keyId, 13 | bool isLast 14 | ); 15 | 16 | public static IEnumerable Split( 17 | byte[] data, 18 | byte[] buffer, 19 | int bufferLength, 20 | int headerSize, 21 | int keyId, 22 | PrepareDataForTransmission prepareData 23 | ) 24 | { 25 | var maxPayloadLength = bufferLength - headerSize; 26 | 27 | var remainingBytes = data.Length; 28 | var bytesSent = 0; 29 | 30 | for (var splitNumber = 0; remainingBytes > 0; splitNumber++) 31 | { 32 | var isLast = remainingBytes <= maxPayloadLength; 33 | var bytesToSend = Math.Min(remainingBytes, maxPayloadLength); 34 | 35 | Array.Copy(data, bytesSent, buffer, headerSize, bytesToSend); 36 | prepareData(buffer, splitNumber, bytesToSend, keyId, isLast); 37 | yield return buffer; 38 | 39 | bytesSent += bytesToSend; 40 | remainingBytes -= bytesToSend; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/ReportReceivedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StreamDeckSharp.Internals 4 | { 5 | internal class ReportReceivedEventArgs : EventArgs 6 | { 7 | public ReportReceivedEventArgs(byte[] reportData) 8 | { 9 | ReportData = reportData; 10 | } 11 | 12 | public byte[] ReportData { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/Throttle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | 5 | #pragma warning disable AV1710 // Member name includes the name of its containing type 6 | 7 | namespace StreamDeckSharp.Internals 8 | { 9 | internal class Throttle 10 | { 11 | private readonly Stopwatch stopwatch = Stopwatch.StartNew(); 12 | private long sumBytesInWindow = 0; 13 | private int sleepCount = 0; 14 | 15 | public double BytesPerSecondLimit { get; set; } = double.PositiveInfinity; 16 | public int ByteCountBeforeThrottle { get; set; } = 16_000; 17 | 18 | public void MeasureAndBlock(int bytes) 19 | { 20 | sumBytesInWindow += bytes; 21 | 22 | var elapsedSeconds = stopwatch.Elapsed.TotalSeconds; 23 | var estimatedSeconds = sumBytesInWindow / BytesPerSecondLimit; 24 | 25 | if (sumBytesInWindow > ByteCountBeforeThrottle && elapsedSeconds < estimatedSeconds) 26 | { 27 | var delta = Math.Max(1, (int)((estimatedSeconds - elapsedSeconds) * 1000)); 28 | Thread.Sleep(delta); 29 | sleepCount++; 30 | } 31 | 32 | if (elapsedSeconds >= 1) 33 | { 34 | if (sleepCount > 1) 35 | { 36 | Debug.WriteLine($"[Throttle] {sumBytesInWindow / elapsedSeconds}"); 37 | } 38 | 39 | stopwatch.Restart(); 40 | sumBytesInWindow = 0; 41 | sleepCount = 0; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/Internals/UsbHardwareIdAndDriver.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | 3 | #pragma warning disable AV1000 // Type name contains the word 'and', which suggests it has multiple purposes 4 | 5 | namespace StreamDeckSharp.Internals 6 | { 7 | internal sealed class UsbHardwareIdAndDriver : IUsbHidHardware 8 | { 9 | public UsbHardwareIdAndDriver( 10 | UsbVendorProductPair usbId, 11 | string deviceName, 12 | GridKeyLayout keys, 13 | IStreamDeckHidComDriver driver 14 | ) 15 | { 16 | UsbId = usbId; 17 | DeviceName = deviceName; 18 | Keys = keys; 19 | Driver = driver; 20 | } 21 | 22 | public UsbVendorProductPair UsbId { get; } 23 | public string DeviceName { get; } 24 | public GridKeyLayout Keys { get; } 25 | public IStreamDeckHidComDriver Driver { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/StreamDeck.cs: -------------------------------------------------------------------------------- 1 | using HidSharp; 2 | using OpenMacroBoard.SDK; 3 | using StreamDeckSharp.Exceptions; 4 | using StreamDeckSharp.Internals; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace StreamDeckSharp 9 | { 10 | /// 11 | /// This is a factory class to create IStreamDeck References 12 | /// 13 | public static class StreamDeck 14 | { 15 | /// 16 | /// Enumerates connected Stream Decks and returns the first one. 17 | /// 18 | /// Thrown if no Stream Deck is found 19 | public static IMacroBoard OpenDevice(params IUsbHidHardware[] hardware) 20 | { 21 | return OpenDevice(true, hardware); 22 | } 23 | 24 | /// 25 | /// Enumerates connected Stream Decks and returns the first one. 26 | /// 27 | /// Thrown if no Stream Deck is found 28 | public static IMacroBoard OpenDevice(bool useWriteCache, params IUsbHidHardware[] hardware) 29 | { 30 | var dev = EnumerateDevices(hardware).FirstOrDefault() ?? throw new StreamDeckNotFoundException(); 31 | return dev.Open(); 32 | } 33 | 34 | /// 35 | /// Enumerates connected Stream Decks and returns the first one. 36 | /// 37 | /// Thrown if no Stream Deck is found 38 | public static IMacroBoard OpenDevice(string devicePath) 39 | { 40 | return OpenDevice(devicePath, true); 41 | } 42 | 43 | /// 44 | /// Get the Stream Deck with a given . 45 | /// 46 | /// Thrown if no Stream Deck is found 47 | public static IMacroBoard OpenDevice(string devicePath, bool useWriteCache) 48 | { 49 | var dev = DeviceList.Local.GetHidDevices().First(d => d.DevicePath == devicePath); 50 | return FromHid(dev ?? throw new StreamDeckNotFoundException(), useWriteCache); 51 | } 52 | 53 | /// 54 | /// Enumerate Elgato Stream Deck Devices that match a given type. 55 | /// 56 | /// If no types or null is passed as argument, all known types are found 57 | public static IEnumerable EnumerateDevices(params IUsbHidHardware[] hardware) 58 | { 59 | return DeviceList.Local.GetStreamDecks(hardware); 60 | } 61 | 62 | internal static IMacroBoard FromHid(HidDevice device, bool cached) 63 | { 64 | var hwInfo = device.GetHardwareInformation(); 65 | var hidWrapper = new StreamDeckHidWrapper(device, hwInfo.Driver); 66 | 67 | if (cached) 68 | { 69 | return new CachedHidClient(hidWrapper, hwInfo.Keys, hwInfo.Driver); 70 | } 71 | 72 | return new BasicHidClient(hidWrapper, hwInfo.Keys, hwInfo.Driver); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/StreamDeckDeviceReference.cs: -------------------------------------------------------------------------------- 1 | using OpenMacroBoard.SDK; 2 | 3 | namespace StreamDeckSharp 4 | { 5 | /// 6 | /// A device reference pointing to a stream deck. 7 | /// 8 | public sealed class StreamDeckDeviceReference : IDeviceReference 9 | { 10 | internal StreamDeckDeviceReference( 11 | string devicePath, 12 | string deviceName, 13 | GridKeyLayout keyLayout 14 | ) 15 | { 16 | DevicePath = devicePath; 17 | DeviceName = deviceName; 18 | Keys = keyLayout; 19 | } 20 | 21 | /// 22 | /// Gets the OSes unique identifier for human interface device. 23 | /// 24 | public string DevicePath { get; } 25 | 26 | /// 27 | public string DeviceName { get; set; } 28 | 29 | /// 30 | public IKeyLayout Keys { get; } 31 | 32 | /// 33 | public override string ToString() 34 | { 35 | return DeviceName; 36 | } 37 | 38 | /// 39 | public IMacroBoard Open(bool useWriteCache) 40 | { 41 | return StreamDeck.OpenDevice(DevicePath, useWriteCache); 42 | } 43 | 44 | /// 45 | public IMacroBoard Open() 46 | { 47 | return StreamDeck.OpenDevice(DevicePath); 48 | } 49 | 50 | /// 51 | public override int GetHashCode() 52 | { 53 | return DevicePath.GetHashCode(); 54 | } 55 | 56 | /// 57 | public override bool Equals(object obj) 58 | { 59 | if (obj is null) 60 | { 61 | return false; 62 | } 63 | 64 | if (ReferenceEquals(this, obj)) 65 | { 66 | return true; 67 | } 68 | 69 | if (obj is not StreamDeckDeviceReference other) 70 | { 71 | return false; 72 | } 73 | 74 | if (other.DevicePath != DevicePath) 75 | { 76 | return false; 77 | } 78 | 79 | return true; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/StreamDeckListener.cs: -------------------------------------------------------------------------------- 1 | using HidSharp; 2 | using OpenMacroBoard.SDK; 3 | using StreamDeckSharp.Internals; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace StreamDeckSharp 9 | { 10 | /// 11 | /// A listener for stream deck devices. 12 | /// 13 | public sealed class StreamDeckListener : 14 | IDisposable, 15 | IObservable 16 | { 17 | private readonly object sync = new(); 18 | private readonly List knownDevices = new(); 19 | private readonly List subscriptions = new(); 20 | private readonly Dictionary knownDeviceLookup = new(); 21 | 22 | private bool disposed = true; 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | public StreamDeckListener() 28 | { 29 | // register event handler before we load the entire list 30 | // so we don't miss stream decks connecting between the calls. 31 | DeviceList.Local.Changed += DeviceListChanged; 32 | 33 | // initial force load 34 | ProcessDelta(); 35 | } 36 | 37 | /// 38 | public IDisposable Subscribe(IObserver observer) 39 | { 40 | var subscription = new Subscription(this, observer); 41 | subscriptions.Add(subscription); 42 | subscription.SendUpdates(); 43 | return subscription; 44 | } 45 | 46 | /// 47 | public void Dispose() 48 | { 49 | if (disposed) 50 | { 51 | return; 52 | } 53 | 54 | disposed = true; 55 | DeviceList.Local.Changed -= DeviceListChanged; 56 | } 57 | 58 | private void ProcessDelta() 59 | { 60 | lock (sync) 61 | { 62 | // because the HidDevice event doesn't tell us what changed 63 | // we calculate the difference ourselves. 64 | var currentDevices = DeviceList.Local 65 | .GetStreamDecks() 66 | .ToDictionary(s => s.DevicePath, s => s); 67 | 68 | // update connection states of known devices 69 | foreach (var knownDevice in knownDevices) 70 | { 71 | knownDevice.Connected = currentDevices.ContainsKey(knownDevice.DeviceReference.DevicePath); 72 | } 73 | 74 | // add new devices 75 | foreach (var currentDevice in currentDevices) 76 | { 77 | if (knownDeviceLookup.ContainsKey(currentDevice.Key)) 78 | { 79 | // skip because this one is already known 80 | continue; 81 | } 82 | 83 | knownDeviceLookup.Add(currentDevice.Key, currentDevice.Value); 84 | knownDevices.Add(new DeviceState(currentDevice.Value, true)); 85 | } 86 | 87 | // send updates to all subscribers 88 | foreach (var subscription in subscriptions) 89 | { 90 | subscription.SendUpdates(); 91 | } 92 | } 93 | } 94 | 95 | private void DeviceListChanged(object sender, DeviceListChangedEventArgs e) 96 | { 97 | ProcessDelta(); 98 | } 99 | 100 | private sealed class DeviceState 101 | { 102 | public DeviceState(StreamDeckDeviceReference deviceReference, bool connected) 103 | { 104 | DeviceReference = deviceReference ?? throw new ArgumentNullException(nameof(deviceReference)); 105 | Connected = connected; 106 | } 107 | 108 | public StreamDeckDeviceReference DeviceReference { get; } 109 | public bool Connected { get; set; } 110 | } 111 | 112 | private sealed class Subscription : IDisposable 113 | { 114 | private readonly StreamDeckListener parent; 115 | private readonly IObserver observer; 116 | 117 | /// 118 | /// Contains the state the subscriber knows about. 119 | /// This is used to calculate new updates. 120 | /// 121 | private readonly List subscriberState = new(); 122 | 123 | public Subscription(StreamDeckListener parent, IObserver observer) 124 | { 125 | this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); 126 | this.observer = observer ?? throw new ArgumentNullException(nameof(observer)); 127 | } 128 | 129 | public void SendUpdates() 130 | { 131 | // send updates for existing devices 132 | for (int i = 0; i < subscriberState.Count; i++) 133 | { 134 | var device = parent.knownDevices[i]; 135 | 136 | if (device.Connected != subscriberState[i]) 137 | { 138 | // report new connection state 139 | observer.OnNext(new DeviceStateReport(device.DeviceReference, device.Connected, false)); 140 | subscriberState[i] = device.Connected; 141 | } 142 | } 143 | 144 | // add and send updates for new (to this subscriber) devices. 145 | for (int i = subscriberState.Count; i < parent.knownDevices.Count; i++) 146 | { 147 | var device = parent.knownDevices[i]; 148 | subscriberState.Add(device.Connected); 149 | observer.OnNext(new DeviceStateReport(device.DeviceReference, device.Connected, true)); 150 | } 151 | } 152 | 153 | public void Dispose() 154 | { 155 | lock (parent.sync) 156 | { 157 | parent.subscriptions.Remove(this); 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/StreamDeckSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net462;net6.0 5 | 9.0 6 | True 7 | 8 | 9 | 10 | 11 | StreamDeckSharp 12 | 6.0.0 13 | A simple .NET wrapper for StreamDeck (using OpenMacroBoard.SDK) 14 | https://github.com/OpenMacroBoard/StreamDeckSharp 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/StreamDeckSharp.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | StreamDeckSharp 5 | $version$ 6 | StreamDeckSharp 7 | Christian Franzl 8 | MIT 9 | 10 | https://github.com/OpenMacroBoard/StreamDeckSharp 11 | https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/master/doc/icon64.png 12 | icon.png 13 | false 14 | A simple .NET interface for the StreamDeck HID 15 | streamdeck elgato stream deck open macro board openmacroboard streamdeckmini 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/UsbConstants.cs: -------------------------------------------------------------------------------- 1 | namespace StreamDeckSharp 2 | { 3 | /// 4 | /// A collection of Stream Deck USB related constants. 5 | /// 6 | public static class UsbConstants 7 | { 8 | /// 9 | /// Known (Stream Deck related) USB Vendor IDs. 10 | /// 11 | public static class VendorIds 12 | { 13 | /// 14 | /// The USB Vendor ID for Elgato Systems GmbH. 15 | /// 16 | public const int ElgatoSystemsGmbH = 0x0fd9; 17 | } 18 | 19 | /// 20 | /// Known (Stream Deck related) USB Product IDs. 21 | /// 22 | public static class ProductIds 23 | { 24 | #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member 25 | public const int StreamDeck = 0x0060; 26 | public const int StreamDeckRev2 = 0x006d; 27 | public const int StreamDeckMK2 = 0x0080; 28 | public const int StreamDeckXL = 0x006c; 29 | public const int StreamDeckXLRev2 = 0x008f; 30 | public const int StreamDeckMini = 0x0063; 31 | public const int StreamDeckMiniRev2 = 0x0090; 32 | #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/UsbVendorProductPair.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StreamDeckSharp 4 | { 5 | /// 6 | /// Fully quallified USB product identifier. Includes the USB Vendor ID and the USB Product ID. 7 | /// 8 | public readonly struct UsbVendorProductPair : IEquatable 9 | { 10 | /// 11 | /// Initializes a new instance of the struct. 12 | /// 13 | public UsbVendorProductPair(int vendorId, int productId) 14 | { 15 | UsbVendorId = vendorId; 16 | UsbProductId = productId; 17 | } 18 | 19 | /// 20 | /// USB vendor id 21 | /// 22 | public int UsbVendorId { get; } 23 | 24 | /// 25 | /// USB product id 26 | /// 27 | public int UsbProductId { get; } 28 | 29 | /// 30 | /// The == operator. Calls internally. 31 | /// 32 | public static bool operator ==(UsbVendorProductPair a, UsbVendorProductPair b) 33 | { 34 | return Equals(a, b); 35 | } 36 | 37 | /// 38 | /// The == operator. Calls internally 39 | /// and inverts the result. 40 | /// 41 | public static bool operator !=(UsbVendorProductPair a, UsbVendorProductPair b) 42 | { 43 | return !Equals(a, b); 44 | } 45 | 46 | /// 47 | /// Indicates whether the two givel objects is equal. 48 | /// 49 | /// First object. 50 | /// Second object. 51 | /// true if the two objects are equal; otherwise, false. 52 | public static bool Equals(UsbVendorProductPair a, UsbVendorProductPair b) 53 | { 54 | return a.UsbVendorId == b.UsbVendorId && a.UsbProductId == b.UsbProductId; 55 | } 56 | 57 | /// 58 | public bool Equals(UsbVendorProductPair other) 59 | { 60 | return Equals(this, other); 61 | } 62 | 63 | /// 64 | public override bool Equals(object obj) 65 | { 66 | if (obj is not UsbVendorProductPair other) 67 | { 68 | return false; 69 | } 70 | 71 | return Equals(this, other); 72 | } 73 | 74 | /// 75 | public override int GetHashCode() 76 | { 77 | return UsbVendorId.GetHashCode() ^ UsbProductId.GetHashCode(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/StreamDeckSharp/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMacroBoard/StreamDeckSharp/d52e0a711211d1b02dbc12800dc50bfe6e648ee4/src/StreamDeckSharp/icon.png --------------------------------------------------------------------------------