diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 3120ff4..649b4bf 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,15 +1,9 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
- # The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 8 * * 2'
@@ -18,54 +12,31 @@ jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
+ permissions:
+ security-events: write
strategy:
fail-fast: false
matrix:
- # Override automatic language detection by changing the below list
- # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['csharp']
- # Learn more...
- # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
- uses: actions/checkout@v2
- with:
- # We must fetch at least the immediate parents so that if this is
- # a pull request then we can checkout the head.
- fetch-depth: 2
-
- # If this run was triggered by a pull request event, then checkout
- # the head of the pull request instead of the merge commit.
- - run: git checkout HEAD^2
- if: ${{ github.event_name == 'pull_request' }}
+ uses: actions/checkout@v4
- # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v1
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 https://git.io/JvXDl
-
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
- #- run: |
- # make bootstrap
- # make release
+ - name: Build
+ run: dotnet build --configuration Release
+ working-directory: ./src/
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
new file mode 100644
index 0000000..b68c5cd
--- /dev/null
+++ b/.github/workflows/dotnet-build.yml
@@ -0,0 +1,40 @@
+name: CI
+
+on:
+ pull_request:
+ branches: [ master ]
+ push:
+ branches: [ master ]
+
+jobs:
+ build-and-test:
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Restore dependencies
+ run: dotnet restore
+ working-directory: ./src/
+
+ - name: Build
+ run: dotnet build --configuration Release --no-restore
+ working-directory: ./src/
+
+ - name: Run tests
+ run: dotnet test --configuration Release --no-build --verbosity normal --logger trx --results-directory "TestResults"
+ working-directory: ./src/
+
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: test-results
+ path: src/TestResults
+ retention-days: 5
diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml
new file mode 100644
index 0000000..26f8eb7
--- /dev/null
+++ b/.github/workflows/publish-nuget.yml
@@ -0,0 +1,56 @@
+name: Publish Nuget
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Verify tag is on master branch
+ run: |
+ if ! git branch -r --contains ${{ github.ref }} | grep -q 'origin/master'; then
+ echo "Error: Tag must be on the master branch"
+ exit 1
+ fi
+
+ - name: Get version from tag
+ id: get_version
+ run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Restore dependencies
+ run: dotnet restore
+ working-directory: ./src/
+
+ - name: Build
+ run: dotnet build --configuration Release --no-restore
+ working-directory: ./src/
+
+ - name: Pack
+ run: dotnet pack --configuration Release --no-build --output ./nupkg -p:PackageVersion=${{ steps.get_version.outputs.VERSION }}
+ working-directory: ./src/SpyderClientLibrary/
+
+ - name: Publish to NuGet
+ run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+ working-directory: ./src/SpyderClientLibrary/
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ name: Release v${{ steps.get_version.outputs.VERSION }}
+ files: ./src/SpyderClientLibrary/nupkg/*.nupkg
+ generate_release_notes: true
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index a24e676..bbb163d 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,25 @@
# SpyderClientLibrary
-
-
-[](https://sonarcloud.io/dashboard?id=dsmithson_SpyderClientLibrary)
+[](https://github.com/dsmithson/SpyderClientLibrary/actions/workflows/dotnet-build.yml)
+[](https://www.nuget.org/packages/SpyderClientLibrary)
Provides a modern, fully async compatible .Net Standard 2.0 library for creating applications and utilities targeting the
[Spyder video processor](http://www.vistasystems.net/video-processors-and-matrix-switchers/video-processors/pages/default.aspx)
-from Desktop (.Net 4.6.2 or later) or Windows UWP applications (Windows 10).
+from Desktop (.Net 4.6.2 or later) or Windows Store applications (Windows 10/11).
Key Features
-------------
* Full implementation of the published Spyder UDP control protocol
* System Config File parsing - provides full Spyder data object model to clients
* Drawing Data (live view) data stream deserialization (Supports 2.10.8 - 5.X+)
+* Partial support for Spyder-S (6.x) as of the current release
* Spyder quick file transfer procotol (QFT) implementation. List, get, and set files
* Thumbnail Manager - provides background downloading, multi-resolution scaling, and memory lifetime management of system thumbnail images
Usage
-----
-This library was built using Visual Studio 2019, and can either be downloaded and compiled from source or downloaded as a
+This library was built using Visual Studio 2026, and can either be downloaded and compiled from source or downloaded as a
[Nuget Package](https://www.nuget.org/packages/SpyderClientLibrary/) if you prefer - search for SpyderClientLibrary from
the Nuget package manager to get started.
@@ -32,3 +32,7 @@ If you run into issues
I've taken great effort to make this library as solid as possible, but if you run into any problems or want to see something added,
please feel free to log them in the issue tracker or fix them up and send me a pull request.
+
+## License
+----------------------
+This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
diff --git a/src/DrawingDataRawCaptureUtility/DrawingDataRawCaptureUtility.csproj b/src/DrawingDataRawCaptureUtility/DrawingDataRawCaptureUtility.csproj
index ee08b34..1b4504b 100644
--- a/src/DrawingDataRawCaptureUtility/DrawingDataRawCaptureUtility.csproj
+++ b/src/DrawingDataRawCaptureUtility/DrawingDataRawCaptureUtility.csproj
@@ -2,7 +2,7 @@
Exe
- net5.0
+ net10.0
AnyCPU;x86
diff --git a/src/GitVersion.yml b/src/GitVersion.yml
deleted file mode 100644
index a98b2b9..0000000
--- a/src/GitVersion.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-mode: Mainline
-next-version: 3.1.0
-branches:
- feature:
- tag: alpha
- master:
- tag: ''
-ignore:
- sha: []
diff --git a/src/LICENSE b/src/LICENSE
new file mode 100644
index 0000000..113e843
--- /dev/null
+++ b/src/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/src/SpyderClientLibrary.UWP/Assets/128x128/Black.png b/src/SpyderClientLibrary.UWP/Assets/128x128/Black.png
deleted file mode 100644
index 1af0fda..0000000
Binary files a/src/SpyderClientLibrary.UWP/Assets/128x128/Black.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.UWP/Assets/128x128/Source.png b/src/SpyderClientLibrary.UWP/Assets/128x128/Source.png
deleted file mode 100644
index 05a6355..0000000
Binary files a/src/SpyderClientLibrary.UWP/Assets/128x128/Source.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.UWP/Assets/32x32/Source.png b/src/SpyderClientLibrary.UWP/Assets/32x32/Source.png
deleted file mode 100644
index b6ca550..0000000
Binary files a/src/SpyderClientLibrary.UWP/Assets/32x32/Source.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.UWP/Assets/512x512/Source.png b/src/SpyderClientLibrary.UWP/Assets/512x512/Source.png
deleted file mode 100644
index 2220146..0000000
Binary files a/src/SpyderClientLibrary.UWP/Assets/512x512/Source.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.UWP/Images/ThumbnailIdentifier.cs b/src/SpyderClientLibrary.UWP/Images/ThumbnailIdentifier.cs
deleted file mode 100644
index e06ba51..0000000
--- a/src/SpyderClientLibrary.UWP/Images/ThumbnailIdentifier.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using Knightware.Diagnostics;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-#if NETFX_CORE
-using Windows.UI.ViewManagement;
-using Windows.UI.Xaml;
-#endif
-
-namespace Spyder.Client.Images
-{
- public class ThumbnailIdentifier : QFTThumbnailIdentifier
- {
- public int ViewID
- {
- get;
- private set;
- }
-
- public ThumbnailIdentifier(string serverIP, string fileName)
- : base(serverIP, fileName)
- {
- #if NETFX_CORE
- try
- {
-
- this.ViewID = ApplicationView.GetForCurrentView().Id;
- }
- catch(Exception ex)
- {
- TraceQueue.Trace(this, TracingLevel.Warning, "{0} occurred while getting current view ID: {1}", ex.GetType().Name, ex.Message);
- this.ViewID = -1;
- }
-#endif
- }
-
- public ThumbnailIdentifier(int viewID, string serverIP, string fileName)
- : base(serverIP, fileName)
- {
- this.ViewID = viewID;
- }
-
- public override bool Equals(QFTThumbnailIdentifier other)
- {
- var compare = other as ThumbnailIdentifier;
- if (compare == null)
- return false;
- else if (compare.ViewID != this.ViewID)
- return false;
- else
- return base.Equals(other);
- }
-
- public override bool Equals(object other)
- {
- var compare = other as ThumbnailIdentifier;
- if (compare == null)
- return false;
- else if (compare.ViewID != this.ViewID)
- return false;
- else
- return base.Equals(other);
- }
-
- public override int GetHashCode()
- {
- return this.ToString().GetHashCode();
- }
-
- public override string ToString()
- {
- return string.Format("View {0} - {1}", ViewID, base.ToString());
- }
- }
-}
diff --git a/src/SpyderClientLibrary.UWP/Images/ThumbnailManager.WinRT.cs b/src/SpyderClientLibrary.UWP/Images/ThumbnailManager.WinRT.cs
deleted file mode 100644
index 86b2864..0000000
--- a/src/SpyderClientLibrary.UWP/Images/ThumbnailManager.WinRT.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Knightware.Diagnostics;
-using Knightware.Primitives;
-using Windows.Graphics.Imaging;
-using Windows.UI.Xaml.Media.Imaging;
-
-namespace Spyder.Client.Images
-{
- public partial class ThumbnailManager : QFTThumbnailManagerBase
- {
- protected override async Task ScaleImageAsync(ThumbnailIdentifier identifier, ImageSize targetSize, Stream nativeImageStream)
- {
- try
- {
- BitmapDecoder decoder = await BitmapDecoder.CreateAsync(nativeImageStream.AsRandomAccessStream());
-
- uint scaledWidth, scaledHeight;
- GetNewsize(decoder.PixelWidth, decoder.PixelHeight, targetSize, out scaledWidth, out scaledHeight);
-
- var transform = new BitmapTransform() { ScaledWidth = scaledWidth, ScaledHeight = scaledHeight };
- var pixelData = await decoder.GetPixelDataAsync(
- BitmapPixelFormat.Rgba8,
- BitmapAlphaMode.Straight,
- transform,
- ExifOrientationMode.RespectExifOrientation,
- ColorManagementMode.DoNotColorManage);
-
- var destinationStream = new MemoryStream();
-
- //Re-encode our image at the new size
- BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream.AsRandomAccessStream());
- encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, scaledWidth, scaledHeight, 96, 96, pixelData.DetachPixelData());
- await encoder.FlushAsync();
-
- //Return our new scaled image stream
- destinationStream.Seek(0, SeekOrigin.Begin);
- return new ProcessedImageResult()
- {
- ScaledStream = destinationStream,
- NativeSize = new Size((int)decoder.PixelWidth, (int)decoder.PixelHeight),
- ScaledSize = new Size((int)scaledWidth, (int)scaledHeight)
- };
- }
- catch (Exception ex)
- {
- TraceQueue.Trace(this, TracingLevel.Warning, "{0} occurred while scaling image file '{1}': {2}",
- ex.GetType().Name, identifier, ex.Message);
-
- return null;
- }
- }
-
- protected override async Task ProcessImageAsync(ThumbnailIdentifier identifier, System.IO.Stream fileStream)
- {
- if (fileStream.Position != 0)
- fileStream.Seek(0, SeekOrigin.Begin);
-
- BitmapImage response = null;
- await dispatcher.BeginInvoke(async () =>
- {
- try
- {
- response = new BitmapImage();
- await response.SetSourceAsync(fileStream.AsRandomAccessStream());
- }
- catch (Exception ex)
- {
- TraceQueue.Trace(this, TracingLevel.Warning, "{0} occurred while trying to load a bitmap from the provided stream. Image identifier was {1}. Message: {2}",
- ex.GetType().Name, identifier.ToString(), ex.Message);
- }
- });
- return response;
- }
-
- public virtual void RemoveViewImages(int viewID)
- {
- PerformImageListOperation((items) =>
- {
- var keysToRemove = items.Keys.Where(key => key.ViewID == viewID).ToList();
- foreach (var key in keysToRemove)
- {
- items.Remove(key);
- }
- });
- }
- }
-}
diff --git a/src/SpyderClientLibrary.UWP/Properties/AssemblyInfo.cs b/src/SpyderClientLibrary.UWP/Properties/AssemblyInfo.cs
deleted file mode 100644
index 524b8d6..0000000
--- a/src/SpyderClientLibrary.UWP/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SpyderClientLibraryUWP")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SpyderClientLibraryUWP")]
-[assembly: AssemblyCopyright("Copyright © 2018")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/src/SpyderClientLibrary.UWP/Properties/SpyderClientLibraryUWP.rd.xml b/src/SpyderClientLibrary.UWP/Properties/SpyderClientLibraryUWP.rd.xml
deleted file mode 100644
index b3ec1b0..0000000
--- a/src/SpyderClientLibrary.UWP/Properties/SpyderClientLibraryUWP.rd.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/SpyderClientLibrary.UWP/SpyderClientLibrary.UWP.csproj b/src/SpyderClientLibrary.UWP/SpyderClientLibrary.UWP.csproj
deleted file mode 100644
index 7412f71..0000000
--- a/src/SpyderClientLibrary.UWP/SpyderClientLibrary.UWP.csproj
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {2C967B09-7E92-4380-B08E-8EDA186EC162}
- Library
- Properties
- SpyderClientLibraryUWP
- SpyderClientLibraryUWP
- en-US
- UAP
- 10.0.19041.0
- 10.0.19041.0
- 14
- 512
- {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- prompt
- 4
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- prompt
- 4
-
-
- x86
- true
- bin\x86\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- x86
- false
- prompt
-
-
- x86
- bin\x86\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- true
- ;2008
- pdbonly
- x86
- false
- prompt
-
-
- x64
- true
- bin\x64\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- x64
- false
- prompt
-
-
- x64
- bin\x64\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- true
- ;2008
- pdbonly
- x64
- false
- prompt
-
-
- PackageReference
-
-
-
- Images\DesignerThumbnailImage.cs
-
-
- Images\StaticThumbnailImage.cs
-
-
- Images\ThumbnailImage.cs
-
-
- Images\ThumbnailManager.cs
-
-
-
-
-
-
-
-
- 3.1.0
-
-
- 6.2.13
-
-
- 1.0.0-experimental1
-
-
-
-
- {2de05b6e-23bf-440a-921d-79043cf14599}
- SpyderClientLibrary
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 14.0
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibrary.UWP/SpyderClientLibrary.UWP.nuspec b/src/SpyderClientLibrary.UWP/SpyderClientLibrary.UWP.nuspec
deleted file mode 100644
index 06b10e8..0000000
--- a/src/SpyderClientLibrary.UWP/SpyderClientLibrary.UWP.nuspec
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- SpyderClientLibrary.UWP
- $version$
- Spyder Client Library UWP app Extensions
- Derek Smithson
- Derek Smithson
- http://www.apache.org/licenses/LICENSE-2.0.html
- https://github.com/dsmithson/SpyderClientLibrary
- false
- UWP application extensions for the SpyderClientLibrary package
-
- Copyright 2019
- spyder, x20, x80
-
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibrary.WPF/Assets/128x128/Black.png b/src/SpyderClientLibrary.WPF/Assets/128x128/Black.png
deleted file mode 100644
index 1af0fda..0000000
Binary files a/src/SpyderClientLibrary.WPF/Assets/128x128/Black.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.WPF/Assets/128x128/Source.png b/src/SpyderClientLibrary.WPF/Assets/128x128/Source.png
deleted file mode 100644
index 05a6355..0000000
Binary files a/src/SpyderClientLibrary.WPF/Assets/128x128/Source.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.WPF/Assets/32x32/Source.png b/src/SpyderClientLibrary.WPF/Assets/32x32/Source.png
deleted file mode 100644
index b6ca550..0000000
Binary files a/src/SpyderClientLibrary.WPF/Assets/32x32/Source.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.WPF/Assets/512x512/Source.png b/src/SpyderClientLibrary.WPF/Assets/512x512/Source.png
deleted file mode 100644
index 2220146..0000000
Binary files a/src/SpyderClientLibrary.WPF/Assets/512x512/Source.png and /dev/null differ
diff --git a/src/SpyderClientLibrary.WPF/IO/GZipStreamDecompressor.cs b/src/SpyderClientLibrary.WPF/IO/GZipStreamDecompressor.cs
deleted file mode 100644
index 08f26a2..0000000
--- a/src/SpyderClientLibrary.WPF/IO/GZipStreamDecompressor.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Spyder.Client.IO
-{
- public class GZipStreamDecompressor : IGZipStreamDecompressor
- {
- public byte[] Decompress(byte[] compressedData, int offset, int count, int uncompressedDataLength)
- {
- using (MemoryStream compressedStream = new MemoryStream(compressedData, offset, count, false))
- {
- using (var decompressor = new GZipStream(compressedStream, CompressionMode.Decompress))
- {
- byte[] decompressedBytes = new byte[uncompressedDataLength];
- int read = decompressor.Read(decompressedBytes, 0, uncompressedDataLength);
- return (read == uncompressedDataLength ? decompressedBytes : null);
- }
- }
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Images/DesignerThumbnailImage.cs b/src/SpyderClientLibrary.WPF/Images/DesignerThumbnailImage.cs
deleted file mode 100644
index c24a443..0000000
--- a/src/SpyderClientLibrary.WPF/Images/DesignerThumbnailImage.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-#if DESKTOP
-using System.Windows.Media.Imaging;
-#elif NETFX_CORE
-using Windows.UI.Xaml.Media.Imaging;
-#endif
-
-namespace Spyder.Client.Images
-{
- public class DesignerThumbnailImage : ThumbnailImage
- {
- public override BitmapImage SmallImage
- {
- get { return GetDefaultImage(ImageSize.Small); }
- }
-
- public override BitmapImage MediumImage
- {
- get { return GetDefaultImage(ImageSize.Medium); }
- }
-
- public override BitmapImage LargeImage
- {
- get { return GetDefaultImage(ImageSize.Large); }
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Images/StaticThumbnailImage.cs b/src/SpyderClientLibrary.WPF/Images/StaticThumbnailImage.cs
deleted file mode 100644
index 9038897..0000000
--- a/src/SpyderClientLibrary.WPF/Images/StaticThumbnailImage.cs
+++ /dev/null
@@ -1,184 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Knightware.Primitives;
-using Knightware.Diagnostics;
-
-#if DESKTOP
-using System.Windows.Media.Imaging;
-using System.Reflection;
-#elif NETFX_CORE
-using Windows.Graphics.Imaging;
-using Windows.UI.Xaml.Media.Imaging;
-using Windows.UI.ViewManagement;
-#endif
-
-namespace Spyder.Client.Images
-{
- public class StaticThumbnailImage : ThumbnailImage
- {
- private static readonly Dictionary sourceImages = new Dictionary();
- public static StaticThumbnailImage Source
- {
- get
- {
-#if DESKTOP
- return GetImage(sourceImages, () =>
- new StaticThumbnailImage(
- "Spyder.Client.Assets._32x32.Source.png",
- "Spyder.Client.Assets._128x128.Source.png",
- "Spyder.Client.Assets._512x512.Source.png",
- "Spyder.Client.Assets._512x512.Source.png"));
-#elif NETFX_CORE
- return GetImage(sourceImages, () =>
- new StaticThumbnailImage(
- "ms-appx:///Assets/32x32/Source.png",
- "ms-appx:///Assets/128x128/Source.png",
- "ms-appx:///Assets/512x512/Source.png",
- "ms-appx:///Assets/512x512/Source.png"));
-#endif
- }
- }
-
- private static readonly Dictionary blackImages = new Dictionary();
- public static StaticThumbnailImage Black
- {
- get
- {
-#if DESKTOP
- return GetImage(sourceImages, () =>
- new StaticThumbnailImage("Spyder.Client.Assets._128x128.Black.png"));
-#elif NETFX_CORE
- return GetImage(blackImages, () =>
- new StaticThumbnailImage("ms-appx:///Assets/128x128/Black.png"));
-#endif
- }
- }
-
- private static StaticThumbnailImage GetImage(Dictionary cache, Func create)
- {
- //Get view ID
- int viewID = 0;
- try
- {
-#if NETFX_CORE
- viewID = ApplicationView.GetForCurrentView().Id;
-#endif
- }
- catch (Exception ex)
- {
- TraceQueue.Trace(null, TracingLevel.Warning, "{0} occurred while getting current view ID: {1}", ex.GetType().Name, ex.Message);
- viewID = -1;
- }
-
- //Look for existing item
- if (!cache.ContainsKey(viewID))
- {
- cache.Add(viewID, create());
- }
-
- return cache[viewID];
- }
-
- private readonly string uriExtraSmall;
- private readonly string uriSmall;
- private readonly string uriMedium;
- private readonly string uriLarge;
- private BitmapImage extraSmallImage;
- private BitmapImage smallImage;
- private BitmapImage mediumImage;
- private BitmapImage largeImage;
-
- public override BitmapImage ExtraSmallImage
- {
- get
- {
- if (extraSmallImage == null)
- extraSmallImage = GetImage(uriExtraSmall);
-
- return extraSmallImage;
- }
- }
-
- public override BitmapImage SmallImage
- {
- get
- {
- if (smallImage == null)
- smallImage = GetImage(uriSmall);
-
- return smallImage;
- }
- }
- public override BitmapImage MediumImage
- {
- get
- {
- if (mediumImage == null)
- mediumImage = GetImage(uriMedium);
-
- return mediumImage;
- }
- }
- public override BitmapImage LargeImage
- {
- get
- {
- if (largeImage == null)
- largeImage = GetImage(uriLarge);
-
- return largeImage;
- }
- }
-
- public override Size NativeResolution { get; set; }
-
- public StaticThumbnailImage(string imageUri)
- : this(imageUri, imageUri, imageUri, imageUri)
- {
-
- }
-
- public StaticThumbnailImage(string extraSmallUri, string smallImageUri, string mediumImageUri, string largeImageUri)
- {
- this.uriExtraSmall = extraSmallUri;
- this.uriSmall = smallImageUri;
- this.uriMedium = mediumImageUri;
- this.uriLarge = largeImageUri;
-
- //Read in our 'native' resolution
- var image = GetImage(largeImageUri);
- NativeResolution = new Size(image.DecodePixelWidth, image.DecodePixelHeight);
- }
-
- protected override BitmapImage GetDefaultImage(ImageSize size)
- {
- if (size == ImageSize.Small)
- return GetImage(uriSmall);
- else if (size == ImageSize.Medium)
- return GetImage(uriMedium);
- else
- return GetImage(uriLarge);
- }
-
- private BitmapImage GetImage(string imageUri)
- {
-#if DESKTOP
- var assembly = Assembly.GetExecutingAssembly();
- var stream = assembly.GetManifestResourceStream(imageUri);
- var image = new BitmapImage();
- image.BeginInit();
- image.CacheOption = BitmapCacheOption.OnLoad;
- image.StreamSource = stream;
- image.EndInit();
-
- return image;
-#elif NETFX_CORE
- var image = new BitmapImage(new Uri(imageUri));
- return image;
-#endif
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Images/ThumbnailIdentifier.cs b/src/SpyderClientLibrary.WPF/Images/ThumbnailIdentifier.cs
deleted file mode 100644
index 9427f24..0000000
--- a/src/SpyderClientLibrary.WPF/Images/ThumbnailIdentifier.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Knightware.Diagnostics;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-#if NETFX_CORE
-using Windows.UI.ViewManagement;
-using Windows.UI.Xaml;
-#endif
-
-namespace Spyder.Client.Images
-{
- public class ThumbnailIdentifier : QFTThumbnailIdentifier
- {
- public ThumbnailIdentifier(string serverIP, string fileName)
- : base(serverIP, fileName)
- {
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Images/ThumbnailImage.cs b/src/SpyderClientLibrary.WPF/Images/ThumbnailImage.cs
deleted file mode 100644
index 98d6e5c..0000000
--- a/src/SpyderClientLibrary.WPF/Images/ThumbnailImage.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-#if DESKTOP
-using System.Windows.Media.Imaging;
-#elif NETFX_CORE
-using Windows.Graphics.Imaging;
-using Windows.UI.Xaml.Media.Imaging;
-#endif
-
-namespace Spyder.Client.Images
-{
- public class ThumbnailImage : ThumbnailImageBase
- {
- protected override BitmapImage GetDefaultImage(ImageSize size)
- {
- return null;
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Images/ThumbnailManager.Desktop.cs b/src/SpyderClientLibrary.WPF/Images/ThumbnailManager.Desktop.cs
deleted file mode 100644
index 1729384..0000000
--- a/src/SpyderClientLibrary.WPF/Images/ThumbnailManager.Desktop.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Media.Imaging;
-using Knightware.Diagnostics;
-
-namespace Spyder.Client.Images
-{
- public partial class ThumbnailManager
- {
- protected override Task ScaleImageAsync(ThumbnailIdentifier identifier, ImageSize targetSize, Stream nativeImageStream)
- {
- try
- {
- using(Bitmap native = (Bitmap)Bitmap.FromStream(nativeImageStream))
- {
- uint scaledWidth, scaledHeight;
- GetNewsize((uint)native.Width, (uint)native.Height, targetSize, out scaledWidth, out scaledHeight);
-
- using(Bitmap scaled = new Bitmap(native, (int)scaledWidth, (int)scaledHeight))
- {
- var scaledStream = new MemoryStream();
- scaled.Save(scaledStream, native.RawFormat);
- scaledStream.Seek(0, SeekOrigin.Begin);
-
- return Task.FromResult(new ProcessedImageResult()
- {
- NativeSize = new Knightware.Primitives.Size(native.Width, native.Height),
- ScaledSize = new Knightware.Primitives.Size(scaled.Width, scaled.Height),
- ScaledStream = scaledStream
- });
- }
- }
- }
- catch (Exception ex)
- {
- TraceQueue.Trace(this, TracingLevel.Warning, "{0} occurred while scaling image file '{1}': {2}",
- ex.GetType().Name, identifier, ex.Message);
-
- return Task.FromResult(null);
- }
- }
-
- protected override Task ProcessImageAsync(ThumbnailIdentifier identifier, System.IO.Stream fileStream)
- {
- if (fileStream.Position != 0)
- fileStream.Seek(0, SeekOrigin.Begin);
-
- try
- {
- var response = new BitmapImage();
- response.BeginInit();
- response.StreamSource = fileStream;
- response.CacheOption = BitmapCacheOption.OnLoad;
- response.EndInit();
- response.Freeze();
-
- return Task.FromResult(response);
- }
- catch (Exception ex)
- {
- TraceQueue.Trace(this, TracingLevel.Warning, "{0} occurred while trying to load a bitmap from the provided stream. Image identifier was {1}. Message: {2}",
- ex.GetType().Name, identifier.ToString(), ex.Message);
-
- return Task.FromResult(null);
- }
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Images/ThumbnailManager.cs b/src/SpyderClientLibrary.WPF/Images/ThumbnailManager.cs
deleted file mode 100644
index 60f90cb..0000000
--- a/src/SpyderClientLibrary.WPF/Images/ThumbnailManager.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Knightware.Diagnostics;
-using Knightware.Net;
-using Knightware.Primitives;
-using Knightware.Threading;
-using Spyder.Client.Net;
-
-#if DESKTOP
-using System.Windows.Media.Imaging;
-#elif NETFX_CORE
-using Windows.Graphics.Imaging;
-using Windows.UI.Xaml.Media.Imaging;
-#endif
-
-namespace Spyder.Client.Images
-{
- public delegate void ImageStreamGetRequestedHandler(object sender, ImageStreamGetRequestedEventArgs e);
- public delegate void ImageStreamSetRequestedHandler(object sender, ImageStreamSetRequestedEventArgs e);
-
- public partial class ThumbnailManager : QFTThumbnailManagerBase
- {
- private readonly Dispatcher dispatcher = Dispatcher.Current;
-
- public event ImageStreamSetRequestedHandler ImageStreamSetRequested;
- protected void OnImageStreamSetRequested(ImageStreamSetRequestedEventArgs e)
- {
- ImageStreamSetRequested?.Invoke(this, e);
- }
-
- public event ImageStreamGetRequestedHandler ImageStreamGetRequested;
- protected void OnImageStreamGetRequested(ImageStreamGetRequestedEventArgs e)
- {
- ImageStreamGetRequested?.Invoke(this, e);
- }
-
- public ThumbnailManager(string localImagesFolder, GetRemoteImagePathHandler getRemoteImagePathHandler)
- : base(localImagesFolder, getRemoteImagePathHandler)
- {
- }
-
- public virtual Task GetDemoServerImageFolder()
- {
- return Task.FromResult(GetFolder("Demo"));
- }
-
- public override async Task GetImageStreamAsync(ThumbnailIdentifier identifier, ImageSize targetSize)
- {
- //Allow the consuming class to have an opportunity to override the stream provided to the thumbnail engine. This is initially being implemented
- //to allow the application to override requests to images for it's demo server instance.
- var args = new ImageStreamGetRequestedEventArgs() { Identifier = identifier };
- OnImageStreamGetRequested(args);
- await args.WaitForAllPendingDeferralsAsync();
-
- if (args.GetNativeImageStream != null)
- {
- return await args.GetNativeImageStream(identifier);
- }
- else
- {
- return await base.GetImageStreamAsync(identifier, targetSize);
- }
- }
-
- public override Task SetImageAsync(ThumbnailIdentifier identifier, Stream stream, FileProgressDelegate progress = null)
- {
- //Allow the consuming class to have an opportunity to override the stream provided to the thumbnail engine. This is initially being implemented
- //to allow the application to override requests to images for it's demo server instance.
- var args = new ImageStreamSetRequestedEventArgs() { Identifier = identifier };
- OnImageStreamSetRequested(args);
- if (args.SetNativeImageStream != null)
- {
- return args.SetNativeImageStream(identifier, stream, progress);
- }
- else
- {
- return base.SetImageAsync(identifier, stream, progress);
- }
- }
-
- protected override async Task OnNativeResolutionAvailable(string serverIP, string fileName, Size nativeResolution)
- {
- //Find existing thumbnails that match our context
- var matches = new List();
- PerformImageListOperation((list) =>
- {
- foreach (var entry in list)
- {
- if (string.Compare(entry.Key.ServerIP, serverIP, StringComparison.OrdinalIgnoreCase) == 0 &&
- string.Compare(fileName, entry.Key.FileName, StringComparison.OrdinalIgnoreCase) == 0)
- {
- matches.Add(entry.Value);
- }
- }
- });
-
- await dispatcher.BeginInvoke((Action)(() =>
- {
- foreach (var match in matches)
- {
- match.NativeResolution = nativeResolution;
- }
- }));
-
- //Allow the base to update it's internal properties
- //Note: there is a hack here - the base will try to update the NativeResolution property on the thumbnails also,
- //which looks redundant, however it would cause an invalid operation exception since we're on a background thread.
- //Since I've updated the property above, this won't happen...
- await base.OnNativeResolutionAvailable(serverIP, fileName, nativeResolution);
- }
-
- private void GetNewsize(uint sourceWidth, uint sourceHeight, ImageSize targetSize, out uint targetWidth, out uint targetHeight)
- {
- double aspectRatio = (double)sourceWidth / (double)sourceHeight;
- uint maxWidthOrHeight = (uint)targetSize;
-
- //Apply default sizing with constraint on width
- targetWidth = Math.Min(sourceWidth, maxWidthOrHeight);
- targetHeight = (uint)Math.Round((double)targetWidth / aspectRatio);
-
- //Sanity check on new height
- if (targetHeight > maxWidthOrHeight)
- {
- //Resize by height constraint
- targetHeight = maxWidthOrHeight;
- targetWidth = (uint)Math.Round((double)maxWidthOrHeight * aspectRatio);
- }
- }
- }
-}
diff --git a/src/SpyderClientLibrary.WPF/Properties/AssemblyInfo.cs b/src/SpyderClientLibrary.WPF/Properties/AssemblyInfo.cs
deleted file mode 100644
index 82df91f..0000000
--- a/src/SpyderClientLibrary.WPF/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SpyderClientLibraryWPF")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SpyderClientLibraryWPF")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("4d752173-779a-4c80-b8e7-7d8f89e252b8")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.0.0")]
-[assembly: AssemblyFileVersion("2.0.0.0")]
diff --git a/src/SpyderClientLibrary.WPF/SpyderClientLibrary.WPF.csproj b/src/SpyderClientLibrary.WPF/SpyderClientLibrary.WPF.csproj
deleted file mode 100644
index ddc6fc2..0000000
--- a/src/SpyderClientLibrary.WPF/SpyderClientLibrary.WPF.csproj
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}
- Library
- Properties
- Spyder.Client
- SpyderClientLibraryWPF
- v4.6.1
- 512
- SAK
- SAK
- SAK
- SAK
-
-
-
-
-
- true
- full
- false
- bin\Debug\
- TRACE;DEBUG;DESKTOP
- prompt
- 4
- true
-
-
- pdbonly
- true
- bin\Release\
- TRACE;DESKTOP
- prompt
- 4
- true
-
-
- true
- bin\x64\Debug\
- TRACE;DEBUG;DESKTOP
- true
- full
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\x64\Release\
- TRACE;DESKTOP
- true
- true
- pdbonly
- x64
- prompt
- MinimumRecommendedRules.ruleset
- bin\x64\Release\SpyderClientLibraryWPF.XML
-
-
- true
- bin\x86\Debug\
- TRACE;DEBUG;DESKTOP
- true
- full
- x86
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\x86\Release\
- TRACE;DESKTOP
- true
- true
- pdbonly
- x86
- prompt
- MinimumRecommendedRules.ruleset
-
-
-
- ..\packages\KnightwareCore.3.1.0\lib\netstandard2.0\KnightwareCore.dll
-
-
-
-
-
-
-
- ..\..\..\SpyderClient\src\packages\System.Threading.Tasks.Dataflow.6.0.0-rc.2.21480.5\lib\net461\System.Threading.Tasks.Dataflow.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {2de05b6e-23bf-440a-921d-79043cf14599}
- SpyderClientLibrary
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibrary.WPF/SpyderClientLibrary.WPF.nuspec b/src/SpyderClientLibrary.WPF/SpyderClientLibrary.WPF.nuspec
deleted file mode 100644
index 53f46d6..0000000
--- a/src/SpyderClientLibrary.WPF/SpyderClientLibrary.WPF.nuspec
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- SpyderClientLibrary.WPF
- $version$
- Spyder Client Library WPF app Extensions
- Derek Smithson
- Derek Smithson
- http://www.apache.org/licenses/LICENSE-2.0.html
- https://github.com/dsmithson/SpyderClientLibrary
- false
- WPF application extensions for the SpyderClientLibrary package
-
- Copyright 2019
- spyder, x20, x80
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibrary.WPF/app.config b/src/SpyderClientLibrary.WPF/app.config
deleted file mode 100644
index c67ea86..0000000
--- a/src/SpyderClientLibrary.WPF/app.config
+++ /dev/null
@@ -1,223 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/SpyderClientLibrary.WPF/packages.config b/src/SpyderClientLibrary.WPF/packages.config
deleted file mode 100644
index 3b5e3aa..0000000
--- a/src/SpyderClientLibrary.WPF/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibrary.sln b/src/SpyderClientLibrary.sln
index 464b0c6..d7b1b5f 100644
--- a/src/SpyderClientLibrary.sln
+++ b/src/SpyderClientLibrary.sln
@@ -1,17 +1,15 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.31729.503
+# Visual Studio Version 18
+VisualStudioVersion = 18.2.11408.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpyderClientLibrary", "SpyderClientLibrary\SpyderClientLibrary.csproj", "{2DE05B6E-23BF-440A-921D-79043CF14599}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpyderClientLibrary.WPF", "SpyderClientLibrary.WPF\SpyderClientLibrary.WPF.csproj", "{FB0FC802-4FC5-4446-B2DD-48201C738DD1}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpyderClientLibraryTests", "SpyderClientLibraryTests\SpyderClientLibraryTests.csproj", "{86E8F979-1B27-433D-B443-8B881EC87E1F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpyderClientLibrary.UWP", "SpyderClientLibrary.UWP\SpyderClientLibrary.UWP.csproj", "{2C967B09-7E92-4380-B08E-8EDA186EC162}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DrawingDataRawCaptureUtility", "DrawingDataRawCaptureUtility\DrawingDataRawCaptureUtility.csproj", "{B5576525-F72D-4C9C-BBE9-9CB98EFC3846}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawingDataRawCaptureUtility", "DrawingDataRawCaptureUtility\DrawingDataRawCaptureUtility.csproj", "{B5576525-F72D-4C9C-BBE9-9CB98EFC3846}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{72FF007D-BC3E-4D06-A47B-49DB716122AD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -35,18 +33,6 @@ Global
{2DE05B6E-23BF-440A-921D-79043CF14599}.Release|x64.Build.0 = Release|x64
{2DE05B6E-23BF-440A-921D-79043CF14599}.Release|x86.ActiveCfg = Release|x86
{2DE05B6E-23BF-440A-921D-79043CF14599}.Release|x86.Build.0 = Release|x86
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Debug|x64.ActiveCfg = Debug|x64
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Debug|x64.Build.0 = Debug|x64
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Debug|x86.ActiveCfg = Debug|x86
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Debug|x86.Build.0 = Debug|x86
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Release|Any CPU.Build.0 = Release|Any CPU
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Release|x64.ActiveCfg = Release|x64
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Release|x64.Build.0 = Release|x64
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Release|x86.ActiveCfg = Release|x86
- {FB0FC802-4FC5-4446-B2DD-48201C738DD1}.Release|x86.Build.0 = Release|x86
{86E8F979-1B27-433D-B443-8B881EC87E1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86E8F979-1B27-433D-B443-8B881EC87E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86E8F979-1B27-433D-B443-8B881EC87E1F}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -59,18 +45,6 @@ Global
{86E8F979-1B27-433D-B443-8B881EC87E1F}.Release|x64.Build.0 = Release|Any CPU
{86E8F979-1B27-433D-B443-8B881EC87E1F}.Release|x86.ActiveCfg = Release|x86
{86E8F979-1B27-433D-B443-8B881EC87E1F}.Release|x86.Build.0 = Release|x86
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Debug|x64.ActiveCfg = Debug|x64
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Debug|x64.Build.0 = Debug|x64
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Debug|x86.ActiveCfg = Debug|x86
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Debug|x86.Build.0 = Debug|x86
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Release|Any CPU.Build.0 = Release|Any CPU
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Release|x64.ActiveCfg = Release|x64
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Release|x64.Build.0 = Release|x64
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Release|x86.ActiveCfg = Release|x86
- {2C967B09-7E92-4380-B08E-8EDA186EC162}.Release|x86.Build.0 = Release|x86
{B5576525-F72D-4C9C-BBE9-9CB98EFC3846}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5576525-F72D-4C9C-BBE9-9CB98EFC3846}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5576525-F72D-4C9C-BBE9-9CB98EFC3846}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -87,6 +61,9 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {B5576525-F72D-4C9C-BBE9-9CB98EFC3846} = {72FF007D-BC3E-4D06-A47B-49DB716122AD}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1F8970A9-5785-44B1-BAB3-98BF6948138B}
EndGlobalSection
diff --git a/src/SpyderClientLibrary/Common/ConnectorType.cs b/src/SpyderClientLibrary/Common/ConnectorType.cs
index c625b8e..5c160d2 100644
--- a/src/SpyderClientLibrary/Common/ConnectorType.cs
+++ b/src/SpyderClientLibrary/Common/ConnectorType.cs
@@ -1,5 +1,6 @@
using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Diagnostics;
namespace Spyder.Client.Common
{
@@ -25,26 +26,27 @@ public static class ConnectorTypeExtensions
public static List GetValidConnectorTypes(this HardwareType hardwareType, ConnectorTypeUsage usage)
{
var response = new List();
- if(hardwareType == HardwareType.SpyderX80)
+ switch (hardwareType)
{
- response.Add(ConnectorType.HDMI);
- response.Add(ConnectorType.DisplayPort);
- response.Add(ConnectorType.SDI);
-
- //Auto is valid for inputs or router types
- if(usage != ConnectorTypeUsage.Output)
- {
- response.Add(ConnectorType.Auto);
- }
- }
- else
- {
- response.Add(ConnectorType.Analog);
- response.Add(ConnectorType.DVI);
- response.Add(ConnectorType.SDI);
- response.Add(ConnectorType.Composite);
- response.Add(ConnectorType.SVideo);
+ case HardwareType.SpyderS:
+ case HardwareType.SpyderX80:
+ response.Add(ConnectorType.HDMI);
+ response.Add(ConnectorType.DisplayPort);
+ response.Add(ConnectorType.SDI);
+ break;
+ default:
+ response.Add(ConnectorType.Analog);
+ response.Add(ConnectorType.DVI);
+ response.Add(ConnectorType.SDI);
+ response.Add(ConnectorType.Composite);
+ response.Add(ConnectorType.SVideo);
+ break;
}
+
+ //Auto is valid for inputs or router types
+ if (usage != ConnectorTypeUsage.Output)
+ response.Add(ConnectorType.Auto);
+
return response;
}
diff --git a/src/SpyderClientLibrary/Common/FieldRate.cs b/src/SpyderClientLibrary/Common/FieldRate.cs
index e715c23..06ce501 100644
--- a/src/SpyderClientLibrary/Common/FieldRate.cs
+++ b/src/SpyderClientLibrary/Common/FieldRate.cs
@@ -1,4 +1,6 @@
-namespace Spyder.Client.Common
+using System.ComponentModel;
+
+namespace Spyder.Client.Common
{
public enum FieldRate
{
@@ -10,6 +12,16 @@ public enum FieldRate
FR_29_97,
FR_25,
FR_24,
- FR_23_98
- }
+ FR_23_98,
+
+ //Additional values added with Spyder-S
+ FR_47_95 = 47,
+ FR_50 = 50,
+ FR_59_94 = 59,
+ FR_96 = 96,
+ FR_100 = 100,
+ FR_119_88 = 119,
+ FR_120 = 120,
+ Unknown,
+ }
}
diff --git a/src/SpyderClientLibrary/Common/HardwareType.cs b/src/SpyderClientLibrary/Common/HardwareType.cs
index b87ff7b..6a83642 100644
--- a/src/SpyderClientLibrary/Common/HardwareType.cs
+++ b/src/SpyderClientLibrary/Common/HardwareType.cs
@@ -6,6 +6,7 @@ public enum HardwareType
Montage,
Spyder300,
SpyderX20,
- SpyderX80
+ SpyderX80,
+ SpyderS,
}
}
diff --git a/src/SpyderClientLibrary/Common/KeyFrame.cs b/src/SpyderClientLibrary/Common/KeyFrame.cs
index f4c8b53..dbd7921 100644
--- a/src/SpyderClientLibrary/Common/KeyFrame.cs
+++ b/src/SpyderClientLibrary/Common/KeyFrame.cs
@@ -1,5 +1,6 @@
using Knightware.Primitives;
using System;
+using Spyder.Client;
namespace Spyder.Client.Common
{
@@ -411,17 +412,42 @@ public CloneMode CloneMode
}
}
- private float cloneOffset;
+ ///
+ /// Spyder-S supports up to 4 clones
+ ///
+ public float[] CloneOffsets
+ {
+ get { return cloneOffsets; }
+ set
+ {
+ if(!cloneOffsets.SequenceEqualSafe(value))
+ {
+ cloneOffsets = value;
+ OnPropertyChanged(nameof(CloneOffsets));
+ OnPropertyChanged(nameof(CloneOffset));
+ }
+ }
+ }
+ private float[] cloneOffsets;
+
+ ///
+ /// Gets or sets the offset value for the first clone in the collection - all Spyder types before Spyder-S support only one clone
+ ///
public float CloneOffset
{
- get { return cloneOffset; }
+ get
+ {
+ if (cloneOffsets == null || cloneOffsets.Length < 1)
+ return 0f;
+
+ return cloneOffsets[0];
+ }
set
{
- if (cloneOffset != value)
- {
- cloneOffset = value;
- OnPropertyChanged();
- }
+ if (value == 0)
+ CloneOffsets = null;
+ else
+ CloneOffsets = new float[] { value };
}
}
diff --git a/src/SpyderClientLibrary/Common/MixEffectResolution.cs b/src/SpyderClientLibrary/Common/MixEffectResolution.cs
new file mode 100644
index 0000000..c65386e
--- /dev/null
+++ b/src/SpyderClientLibrary/Common/MixEffectResolution.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Spyder.Client.Common
+{
+ public enum MixEffectResolution
+ {
+ HD,
+ UHD
+ }
+}
diff --git a/src/SpyderClientLibrary/Common/MixEffectType.cs b/src/SpyderClientLibrary/Common/MixEffectType.cs
new file mode 100644
index 0000000..f5f35c0
--- /dev/null
+++ b/src/SpyderClientLibrary/Common/MixEffectType.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Spyder.Client.Common
+{
+ public enum MixEffectType
+ {
+ Normal,
+ Background,
+ Aux,
+ }
+}
diff --git a/src/SpyderClientLibrary/Common/OutputMode.cs b/src/SpyderClientLibrary/Common/OutputMode.cs
index 745bfa8..29cc87a 100644
--- a/src/SpyderClientLibrary/Common/OutputMode.cs
+++ b/src/SpyderClientLibrary/Common/OutputMode.cs
@@ -1,4 +1,6 @@
-namespace Spyder.Client.Common
+using System.ComponentModel;
+
+namespace Spyder.Client.Common
{
public enum OutputMode
{
@@ -16,7 +18,13 @@ public enum OutputMode
//Enum formats in correct byte order for X20 and X80 are below
Multiviewer,
Aux,
- UnscaledAux
+ UnscaledAux,
+
+ //Added in Spyder-S
+ //Note these are not in the enum order of Spyder-S, and explicit serialization/deserialization is required.
+ //Enum formats in correct byte order for Spyder-S is below
+ Tiled,
+ Unused,
};
///
@@ -50,4 +58,17 @@ public enum OutputModeX20
ActiveStereo,
SourceMon
};
+
+ public enum OutputModeSpyderS
+ {
+ Normal,
+ Multiviewer,
+ Scaled,
+ Aux,
+ UnscaledAux,
+ OpMon,
+ SourceMon,
+ Tiled,
+ Unused,
+ }
}
diff --git a/src/SpyderClientLibrary/Common/SpyderModels.cs b/src/SpyderClientLibrary/Common/SpyderModels.cs
index e652749..1dd27e9 100644
--- a/src/SpyderClientLibrary/Common/SpyderModels.cs
+++ b/src/SpyderClientLibrary/Common/SpyderModels.cs
@@ -1,4 +1,7 @@
-namespace Spyder.Client.Common
+using System;
+using System.ComponentModel;
+
+namespace Spyder.Client.Common
{
public enum SpyderModels
{
@@ -26,10 +29,17 @@ public enum SpyderModels
X20_1608,
X20_0808,
X80,
- Custom
+ Custom,
+ SpyderS_HDMI_4x4,
+ SpyderS_SDI_8x0,
+ SpyderS_DP_4x4,
+ SpyderS_SDI_16x0,
+ SpyderS_SDI_4x4,
+ SpyderS_SDI_0x8,
+ SpyderS_SFP_4x4,
}
- public enum SpyderModelsX20
+ public enum Spyder200_300Models
{
Spyder_380,
Spyder_374,
@@ -52,37 +62,52 @@ public enum SpyderModelsX20
Spyder_213,
Spyder_207,
Spyder_204,
- X20_1608,
- X20_0808,
- Custom
+ Custom,
}
- public enum SpyderModelsX80
+ public enum SpyderX20Models
{
- Spyder_380,
- Spyder_374,
- Spyder_371,
- Spyder_368,
- Spyder_365,
- Spyder_362,
- Spyder_353,
- Spyder_344,
- Spyder_335,
- Spyder_326,
- Spyder_308,
- Spyder_359,
- Spyder_3410,
- Spyder_240,
- Spyder_234,
- Spyder_231,
- Spyder_225,
- Spyder_222,
- Spyder_213,
- Spyder_207,
- Spyder_204,
X20_1608,
X20_0808,
- X80,
Custom
}
+
+ public enum SpyderSModels
+ {
+ Unknown = 0,
+ HDMI_4x4 = 1,
+ SDI_8x0 = 2,
+ DP_4x4 = 3,
+ SDI_16x0 = 4,
+ SDI_4x4 = 5,
+ SDI_0x8 = 6,
+ SFP_4x4 = 7,
+ }
+
+ public static class SpyderModelsExtensions
+ {
+ public static SpyderModels Convert(this Spyder200_300Models model)
+ {
+ if(Enum.TryParse(model.ToString(), out var result))
+ {
+ return result;
+ }
+ return SpyderModels.Custom;
+ }
+
+ public static SpyderModels Convert(this SpyderSModels model)
+ {
+ return model switch
+ {
+ SpyderSModels.HDMI_4x4 => SpyderModels.SpyderS_HDMI_4x4,
+ SpyderSModels.SDI_8x0 => SpyderModels.SpyderS_SDI_8x0,
+ SpyderSModels.DP_4x4 => SpyderModels.SpyderS_DP_4x4,
+ SpyderSModels.SDI_16x0 => SpyderModels.SpyderS_SDI_16x0,
+ SpyderSModels.SDI_4x4 => SpyderModels.SpyderS_SDI_4x4,
+ SpyderSModels.SDI_0x8 => SpyderModels.SpyderS_SDI_0x8,
+ SpyderSModels.SFP_4x4 => SpyderModels.SpyderS_SFP_4x4,
+ _ => SpyderModels.Custom,
+ };
+ }
+ }
}
diff --git a/src/SpyderClientLibrary/Common/SystemLayerMode.cs b/src/SpyderClientLibrary/Common/SystemLayerMode.cs
new file mode 100644
index 0000000..a2ae352
--- /dev/null
+++ b/src/SpyderClientLibrary/Common/SystemLayerMode.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Spyder.Client.Common
+{
+ public enum SystemLayerMode
+ {
+ Layers,
+ MixEffects,
+ }
+}
diff --git a/src/SpyderClientLibrary/Common/UnscaledAuxSourceType.cs b/src/SpyderClientLibrary/Common/UnscaledAuxSourceType.cs
new file mode 100644
index 0000000..b48532b
--- /dev/null
+++ b/src/SpyderClientLibrary/Common/UnscaledAuxSourceType.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Spyder.Client.Common
+{
+ public enum UnscaledAuxSourceType
+ {
+ MixEffect,
+ LocalInput,
+ }
+}
diff --git a/src/SpyderClientLibrary/ExtensionMethods.cs b/src/SpyderClientLibrary/ExtensionMethods.cs
new file mode 100644
index 0000000..35b7caa
--- /dev/null
+++ b/src/SpyderClientLibrary/ExtensionMethods.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Spyder.Client
+{
+ public static class ExtensionMethods
+ {
+ ///
+ /// Checks for sequence equality and compares differences in null
+ ///
+ /// True if both sides are null or not null and their contents match
+ public static bool SequenceEqualSafe(this IEnumerable a, IEnumerable b)
+ {
+ return ReferenceEquals(a, b) ||
+ (a is not null && b is not null && a.SequenceEqual(b));
+ }
+ }
+}
diff --git a/src/SpyderClientLibrary/Images/ThumbnailImageBase.cs b/src/SpyderClientLibrary/Images/ThumbnailImageBase.cs
index 5a0ab12..843bb6f 100644
--- a/src/SpyderClientLibrary/Images/ThumbnailImageBase.cs
+++ b/src/SpyderClientLibrary/Images/ThumbnailImageBase.cs
@@ -186,28 +186,28 @@ public void SetImage(ImageSize size, T image)
IsLoadingExtraSmallImage = false;
extraSmallImageLoadFailed = (image == null);
extraSmallImage = (image == null ? null : new TimedCacheWeakReference(image, TimeSpan.FromMinutes(1)));
- OnPropertyChanged("ExtraSmallImage");
+ OnPropertyChanged(nameof(ExtraSmallImage));
break;
case ImageSize.Small:
IsLoadingSmallImage = false;
smallImageLoadFailed = (image == null);
smallImage = (image == null ? null : new TimedCacheWeakReference(image, TimeSpan.FromMinutes(1)));
- OnPropertyChanged("SmallImage");
+ OnPropertyChanged(nameof(SmallImage));
break;
case ImageSize.Medium:
IsLoadingMediumImage = false;
mediumImageLoadFailed = (image == null);
mediumImage = (image == null ? null : new TimedCacheWeakReference(image, TimeSpan.FromMinutes(1)));
- OnPropertyChanged("MediumImage");
+ OnPropertyChanged(nameof(MediumImage));
break;
case ImageSize.Large:
IsLoadingLargeImage = false;
largeImageLoadFailed = (image == null);
largeImage = (image == null ? null : new TimedCacheWeakReference(image, TimeSpan.FromMinutes(1)));
- OnPropertyChanged("LargeImage");
+ OnPropertyChanged(nameof(LargeImage));
break;
default:
diff --git a/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer.cs b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer.cs
index 3510391..c50c192 100644
--- a/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer.cs
+++ b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer.cs
@@ -150,7 +150,9 @@ private IDrawingDataDeserializer GetDeserializer(int drawingDataVersion, string
case 51: return new DrawingDataDeserializer_Version51(serverVersion);
case 52: return new DrawingDataDeserializer_Version52(serverVersion);
case 53: return new DrawingDataDeserializer_Version53(serverVersion);
- case 54: return new DrawingDataDeserializer_Version54(serverVersion);
+ case 54: return new DrawingDataDeserializer_Version54(serverVersion);
+ case 64: return new DrawingDataDeserializer_Version64(serverVersion);
+ case 68: return new DrawingDataDeserializer_Version68(serverVersion);
default: return null;
}
}
diff --git a/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version54.cs b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version54.cs
index 53afb34..6760d52 100644
--- a/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version54.cs
+++ b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version54.cs
@@ -452,8 +452,6 @@ public override DrawingData Deserialize(byte[] stream)
output.HTotal = stream.GetShort(ref index);
output.VTotal = stream.GetShort(ref index);
- output.DirectAuxId = stream.GetInt(ref index);
-
var outputFlags = (OutputFlags)stream[index++];
output.Interlaced = outputFlags.HasFlag(OutputFlags.Interlaced);
output.IsFrameLocked = outputFlags.HasFlag(OutputFlags.IsFrameLocked);
diff --git a/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version64.cs b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version64.cs
new file mode 100644
index 0000000..d4f31a1
--- /dev/null
+++ b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version64.cs
@@ -0,0 +1,666 @@
+using Knightware.Diagnostics;
+using Knightware.Primitives;
+using Spyder.Client.Common;
+using Spyder.Client.IO;
+using System;
+using System.IO;
+using System.IO.Pipes;
+
+namespace Spyder.Client.Net.DrawingData.Deserializers
+{
+ ///
+ /// Deserializes DrawingData messages in the version 64 serialization format - SpyderS 6.0.1
+ ///
+ public class DrawingDataDeserializer_Version64 : IDrawingDataDeserializer
+ {
+ private readonly string serverVersion;
+ public DrawingDataDeserializer_Version64(string serverVersion)
+ {
+ this.serverVersion = serverVersion;
+ }
+
+ public virtual DrawingData Deserialize(byte[] stream)
+ {
+ if (stream == null || stream.Length == 0)
+ return null;
+
+ DrawingData response = new DrawingData();
+ int index = 0;
+
+ int newPsCount = stream[index++];
+ int newDkCount = stream[index++];
+ int newPvwDkCount = stream[index++];
+ int newMixEffectCount = stream[index++];
+ int newRtrCount = stream[index++];
+ int newOutputCount = stream[index++];
+ int previewPixelSpaceCount = stream[index++];
+ int stereoPixelSpaceCount = stream[index++];
+ int newMachineCount = stream[index++];
+ int frameCount = stream[index++];
+ int runningScriptCount = stream[index++];
+ int diagnosticWarningCount = stream[index++];
+ int configOutputCount = stream[index++];
+ int inputCount = stream[index++];
+
+ //Global values
+ response.TimeOfDay = new TimeSpan(
+ stream[index++],
+ stream[index++],
+ stream[index++]
+ );
+
+ response.PercentComplete = stream[index++];
+ response.LastFrontPanelMsg = (FrontPanelDisplayCommand)stream[index++];
+ response.ProgressString = stream.GetString(ref index);
+ response.ConfigSource = stream.GetString(ref index);
+ response.ConfigLayer = stream.GetInt(ref index);
+ response.ConfigBus = ParseSpyderSMixerBus(stream[index++]);
+
+ response.HardwareType = (HardwareType)stream[index++];
+ index++; //Ignore StereoMode
+ response.DataObjectVersion = stream.GetInt(ref index);
+ response.DataObjectLastChangeType = (DataType)stream.GetInt(ref index);
+
+ //Global Flags
+ byte flags = stream[index++];
+ response.OpMonOverlay = (flags & 0x01) > 0;
+ response.IsMachineHalEnabled = (flags & 0x02) > 0;
+ response.IsRouterHalEnabled = (flags & 0x04) > 0;
+ response.IsDataIOIdle = (flags & 0x08) > 0;
+ response.IsPreviewOnlyScriptingEnabled = (flags & 0x10) > 0;
+ response.IsLiveUpdateEnabled = (flags & 0x20) > 0;
+
+ flags = stream[index++];
+ response.LiveUpdatesTemporarilyDisabled = (flags & 0x01) > 0;
+ response.IsHdcpEnabled = (flags & 0x02) > 0;
+
+ //System Frame Rate
+ response.SystemFrameRate = ParseSpyderSFieldRate(stream[index++]);
+
+ //System Layer Mode
+ response.SystemLayerMode = (SystemLayerMode)stream[index++];
+
+ //Get the frames
+ for (int i = 0; i < frameCount; i++)
+ {
+ int frameID = stream[index++];
+ var frame = new DrawingFrame()
+ {
+ FrameID = frameID,
+ FrameAOR = stream.GetRectangle(ref index),
+ Model = SpyderModelFromByte(stream[index++]),
+ RenewalMasterFrameID = stream[index++],
+ };
+ //Note: Ignoring model capabilities flag for now
+ index++;
+
+ frame.ProgramAOR = frame.FrameAOR; //Spyder-S doesn't require PGM/PVW AORs
+ frame.PreviewAOR = frame.FrameAOR;
+ response.Frames.Add(frameID, frame);
+ }
+
+ //Get the running scripts
+ for (int i = 0; i < runningScriptCount; i++)
+ {
+ int scriptID = stream.GetInt(ref index);
+ long countDown = stream.GetLong(ref index);
+ response.RunningScripts.Add(scriptID, countDown);
+ }
+
+ //Get the preview pixelspace list
+ for (int i = 0; i < previewPixelSpaceCount; i++)
+ {
+ int pgmID = stream.GetShort(ref index);
+ int pvwID = stream.GetShort(ref index);
+ response.PreviewPixelSpaceIDs.Add(pgmID, pvwID);
+ }
+
+ //Get the stereo pixelspace list
+ for (int i = 0; i < stereoPixelSpaceCount; i++)
+ {
+ int leftEyeID = stream.GetShort(ref index);
+ int rightEyeID = stream.GetShort(ref index);
+ response.StereoPixelSpaceIDs.Add(leftEyeID, rightEyeID);
+ }
+
+ //Get all PixelSpaces
+ for (int i = 0; i < newPsCount; i++)
+ {
+ //Create the new pixelspace
+ DrawingPixelSpace newPS = new DrawingPixelSpace();
+
+ //Fill the new pixelspace
+ newPS.Rect = stream.GetRectangle(ref index);
+
+ //Versions above 5.0.x (5.1.x / 5.2.x / etc) no longer have a RenewalMasterFrameID
+ newPS.RenewMasterFrameID = -1;
+
+ newPS.ID = stream.GetShort(ref index);
+ MixerBus bus = ParseSpyderSMixerBus(stream[index++]);
+ newPS.Name = stream.GetString(ref index);
+
+ //Note: We'll come back after we get the mix effects below to try and set background content thumbnails
+
+ //PixelSpace flags
+ flags = stream[index++];
+ newPS.StereoMode = (flags & 0x01) == 0x01 ? PixelSpaceStereoMode.Stereo : PixelSpaceStereoMode.Off;
+
+ //Version 5.0.3+ no longer has PixelSpace scale factor. For backwards compatibility we'll set either 1f for PGM or .99 for PVW.
+ //In the future if this turns out to be problematic we could calculate an effective scale below after we've deserialized all pixelspaces
+ newPS.Scale = bus == MixerBus.Program ? 1f : .99f;
+
+ //Add Pixelspace to the list
+ response.PixelSpaces.Add(newPS.ID, newPS);
+ }
+
+ //Get our mix effects
+ for (int i = 0; i < newMixEffectCount; i++)
+ {
+ DrawingMixEffect mixEffect = new DrawingMixEffect();
+ mixEffect.ID = stream.GetInt(ref index);
+ mixEffect.Name = stream.GetString(ref index);
+ mixEffect.Type = (MixEffectType)stream[index++];
+ mixEffect.BottomContentName = stream.GetString(ref index);
+ mixEffect.BottomContentThumbnail = stream.GetString(ref index);
+ mixEffect.TopContentName = stream.GetString(ref index);
+ mixEffect.TopContentThumbnail = stream.GetString(ref index);
+ mixEffect.TopContentOpacity = stream.GetFloat(ref index);
+ mixEffect.BackgroundPixelSpaceId = stream.GetInt(ref index);
+
+ //Usages
+ int usageCount = stream[index++];
+ for (int j = 0; j < usageCount; j++)
+ {
+ mixEffect.Usages.Add(new DrawingMixEffectUsage()
+ {
+ SourceID = stream.GetInt(ref index),
+ UsageType = (DrawingMixEffectUsageType)stream[index++],
+ Label = stream.GetString(ref index),
+ });
+ }
+
+ //Flags
+ byte mixEffectFlags = stream[index++];
+ mixEffect.TopIsFrozen = (mixEffectFlags & 0x01) > 0;
+ mixEffect.BottomIsFrozen = (mixEffectFlags & 0x02) > 0;
+ mixEffect.TopSupportsFreeze = (mixEffectFlags & 0x04) > 0;
+ mixEffect.BottomSupportsFreeze = (mixEffectFlags & 0x08) > 0;
+
+ response.DrawingMixEffects.Add(mixEffect.ID, mixEffect);
+ }
+
+ //Now that we have MixEffects, update pixelspace background info
+ foreach (var ps in response.PixelSpaces.Values)
+ {
+ foreach (var me in response.DrawingMixEffects.Values)
+ {
+ if (me.BackgroundPixelSpaceId == ps.ID)
+ {
+ ps.NextBackgroundStillIsOnLayer1 = true;
+ ps.LastBackgroundStill = me.BottomContentThumbnail;
+ ps.NextBackgroundStill = me.TopContentThumbnail;
+ ps.Layer1Transparency = (byte)(me.TopContentOpacity * byte.MaxValue);
+ }
+ }
+ }
+
+ //Get all of our DrawingKeyFrames
+ int allLayerCount = newDkCount + newPvwDkCount;
+
+ //Add a couple 'virtual' background layers to maintain overall compatibility in library. Spyder-S removed these and starts layer IDs at zero
+ response.DrawingKeyFrames.Add(0, new DrawingKeyFrame() { LayerID = 0, IsBackground = true });
+ response.DrawingKeyFrames.Add(1, new DrawingKeyFrame() { LayerID = 1, IsBackground = true });
+
+ for (int i = 0; i < allLayerCount; i++)
+ {
+ DrawingKeyFrame l = new DrawingKeyFrame();
+ KeyFrame kf = l.KeyFrame;
+
+ l.FrameID = stream[index++];
+
+ l.MixEffectID = stream.GetInt(ref index);
+ if(response.DrawingMixEffects.ContainsKey(l.MixEffectID))
+ {
+ l.MixEffect = response.DrawingMixEffects[l.MixEffectID];
+ }
+
+ l.Priority = stream.GetShort(ref index);
+ l.HActive = stream.GetShort(ref index); //HActive of the input itself (used for calculating the scaled border size)
+ l.VActive = stream.GetShort(ref index); //VActive of the input itself (used for Pan calculations)
+ l.LayerID = stream.GetShort(ref index) + 2; //Layer ID (adding offset for Spyder-S zero-based layer ID system)
+ _ = stream.GetShort(ref index); //LayerIndex - layer index on frame (we're ignoring this for now)
+ l.PixelSpaceID = stream.GetInt(ref index); //Pixelspace ID
+ l.EffectID = stream.GetInt(ref index);
+ l.LastScript = stream.GetInt(ref index);
+ l.LastCue = stream.GetInt(ref index);
+
+ l.Thumbnail = stream.GetString(ref index); //thumbnail
+ l.WindowLabel = stream.GetString(ref index);
+ l.Source = stream.GetString(ref index);
+ l.LoadedStill = stream.GetString(ref index);
+ l.TestPattern = stream.GetString(ref index);
+ l.SourceRouterID = stream.GetInt(ref index);
+ l.SourceRouterInput = stream.GetInt(ref index);
+ l.InputConfigID = stream.GetInt(ref index);
+
+ _ = stream.GetInt(ref index); //CurrentInputID - we're ignoring this for now
+
+ l.AspectRatio = stream.GetFloat(ref index); //aspect ratio
+ l.LayerRect = stream.GetRectangle(ref index);
+ l.AOIRect = stream.GetRectangle(ref index);
+
+ //Not storing stereoMode, element type
+ index++; //StereoMode
+ index++; //ElementType
+
+ l.Transparency = stream[index++]; //Visible / Transparency
+
+ //Layer flags (block 1)
+ flags = stream[index++];
+ l.IsMixer = (flags & 0x01) > 0;
+ l.IsFrozen = (flags & 0x02) > 0;
+ l.IsSlave = (flags & 0x04) > 0;
+ l.AlwaysRelative = (flags & 0x08) > 0;
+ kf.BorderOutsideSoftBottom = (flags & 0x10) > 0;
+ kf.BorderOutsideSoftLeft = (flags & 0x20) > 0;
+ kf.BorderOutsideSoftRight = (flags & 0x40) > 0;
+ l.AutoSyncOnTimingChange = (flags & 0x80) > 0;
+
+ //Layer flags (block 2)
+ flags = stream[index++];
+ kf.BorderOutsideSoftTop = (flags & 0x01) > 0;
+ kf.UseDefaultMotionValues = (flags & 0x02) > 0;
+ l.IsBackground = (flags & 0x04) > 0;
+ l.IsHardwarePreview = (flags & 0x08) > 0;
+ l.IsMixing = (flags & 0x10) > 0;
+ l.IsWithinPixelSpace = (flags & 0x20) > 0;
+ l.HdcpAuthenticated = (flags & 0x40) > 0;
+ l.ShadowIsEnabled = (flags & 0x80) > 0;
+
+ //Layer flags (block 3)
+ flags = stream[index++];
+ l.IsLocked = (flags & 0x01) > 0;
+ //l.IsVisible = (flags & 0x02) > 0;
+ //l.IsDrawingApplicable = (flags & 0x04) > 0;
+ //l.IsDualLayerMode = (flags & 0x08) > 0;
+ //l.HasFrameBuffer = (flags & 0x10) > 0;
+ //l.InputConnectionDetected = (flags & 0x20) > 0;
+
+ kf.BorderThickness = stream.GetShort(ref index); //border thickness
+ kf.Width = (ushort)stream.GetShort(ref index); //HSize
+ kf.BorderInsideSoftness = stream.GetShort(ref index); //Border inside softness
+ kf.BorderOutsideSoftness = stream.GetShort(ref index); //Border outside softness
+
+ kf.ShadowHOffset = stream.GetShort(ref index); //Shadow H Offset
+ kf.ShadowVOffset = stream.GetShort(ref index); //Shadow V Offset
+ kf.ShadowHSize = stream.GetShort(ref index); //Shadow H Size
+ kf.ShadowVSize = kf.ShadowHSize;
+
+ kf.ShadowSoftness = stream.GetShort(ref index); //Shadow softness
+ kf.ShadowTransparency = stream.GetShort(ref index); //Shadow Transparency
+ kf.BorderLumaOffsetBottom = stream.GetShort(ref index);
+ kf.BorderLumaOffsetLeft = stream.GetShort(ref index);
+ kf.BorderLumaOffsetRight = stream.GetShort(ref index);
+ kf.BorderLumaOffsetTop = stream.GetShort(ref index);
+
+ kf.HPosition = stream.GetFloat(ref index); //HPosition
+ kf.VPosition = stream.GetFloat(ref index); //VPosition
+ kf.TopCrop = stream.GetFloat(ref index); //top crop
+ kf.LeftCrop = stream.GetFloat(ref index); //Left crop
+ kf.RightCrop = stream.GetFloat(ref index); //Right crop
+ kf.BottomCrop = stream.GetFloat(ref index); //Bottom crop
+ kf.Zoom = stream.GetFloat(ref index); //Zoom for zoom/pan
+ kf.AspectRatioOffset = stream.GetFloat(ref index); //Aspect ratio offset
+ kf.EaseIn = stream.GetFloat(ref index); //Ease in
+ kf.EaseOut = stream.GetFloat(ref index); //Ease out
+ kf.PanH = stream.GetInt(ref index); //Horizontal Pan
+ kf.PanV = stream.GetInt(ref index); //Vertical Pan
+ kf.CropAnchor = (CropAnchorTypes)stream[index++];
+ kf.Transparency = stream[index++]; //Transparency
+ kf.Duration = stream.GetShort(ref index);
+ kf.CloneMode = (CloneMode)stream[index++]; //clone mode
+
+ //Clone offsets
+ int cloneOffsetCount = stream[index++];
+ float[] cloneOffsets = new float[cloneOffsetCount];
+ for(int j=0; j 0;
+ router.SerialRouter = (flags & 0x02) > 0;
+ router.LevelControlledRouter = (flags & 0x04) > 0;
+
+ router.ID = stream[index++];
+ router.InputCount = stream.GetShort(ref index);
+ router.OutputCount = stream.GetShort(ref index);
+ router.Port = stream[index++];
+ router.ConnectorType = ParseRouterConnectorType(stream[index++]);
+ router.ControlLevel = stream.GetInt(ref index);
+ router.LevelCount = stream.GetInt(ref index);
+
+ //Write the soft patch for the router
+ int patchCount = stream[index++];
+ router.Patch.Clear();
+ for (int patchIndex = 0; patchIndex < patchCount; patchIndex++)
+ {
+ int physicalOutput = stream[index++];
+ int downstreamRouterID = stream[index++];
+ int downstreamRouterInput = stream[index++];
+
+ //downstream ID may be -1 or -2 if we are patched to a layer. This will have been clipped to 255
+ if (downstreamRouterID == 255)
+ downstreamRouterID = -1;
+ else if (downstreamRouterID == 254)
+ downstreamRouterID = -2;
+
+ router.SetRouterOutputPatch(physicalOutput, downstreamRouterID, downstreamRouterInput);
+ }
+
+ //Write the crosspoints for the router
+ for (int outputID = 0; outputID < router.OutputCount; outputID++)
+ {
+ if (router.InputCount >= 255)
+ {
+ //Desrialize two bytes of data
+ router.Crosspoints.Add(stream.GetShort(ref index));
+ }
+ else
+ {
+ //Deserialize only one byte of data
+ int input = stream[index++];
+ if (input == 255)
+ input = -1;
+
+ router.Crosspoints.Add(input);
+ }
+ }
+
+ //Add router to list
+ response.Routers.Add(router.ID, router);
+ }
+
+ //Get the Machines
+ for (int i = 0; i < newMachineCount; i++)
+ {
+ var newMachine = new DrawingMachine();
+ MachineStatus status = newMachine.Status;
+
+ //Port ID
+ newMachine.Port = stream[index++];
+
+ //Last PlayItemID
+ newMachine.LastPlayItemID = stream.GetInt(ref index);
+
+ //Machine Time
+ FieldRate frameRate = (FieldRate)stream[index++];
+ long frames = stream.GetLong(ref index);
+ newMachine.Time = new TimeCode(frameRate, frames);
+
+ //Machine Status (Block 1)
+ flags = stream[index++];
+ status.Cued = (flags & 0x01) > 0;
+ status.Ejecting = (flags & 0x02) > 0;
+ status.FastForwarding = (flags & 0x04) > 0;
+ status.Jog = (flags & 0x08) > 0;
+ status.Local = (flags & 0x10) > 0;
+ status.Playing = (flags & 0x20) > 0;
+ status.Recording = (flags & 0x40) > 0;
+ status.Rewinding = (flags & 0x80) > 0;
+
+ //Machine Status (Block 2)
+ flags = stream[index++];
+ status.ServoLock = (flags & 0x01) > 0;
+ status.ServoRefMissing = (flags & 0x02) > 0;
+ status.Shuttle = (flags & 0x04) > 0;
+ status.Standby = (flags & 0x08) > 0;
+ status.Still = (flags & 0x10) > 0;
+ status.Stopped = (flags & 0x20) > 0;
+ status.TapeDir = (flags & 0x40) > 0;
+ status.TapeOut = (flags & 0x80) > 0;
+
+ //Machine Status (Block 3)
+ flags = stream[index++];
+ status.TsoMode = (flags & 0x01) > 0;
+ status.Var = (flags & 0x02) > 0;
+
+ //Add to collection
+ response.Machines.Add(newMachine.Port, newMachine);
+ }
+
+ //Get the outputs
+ for (int i = 0; i < newOutputCount; i++)
+ {
+ DrawingOutput output = new DrawingOutput();
+
+ output.OutputType = OutputModuleType.SpyderS;
+ output.ID = stream.GetShort(ref index);
+ _ = stream.GetShort(ref index); //PortID - ignoring for now
+ output.FrameID = stream.GetShort(ref index);
+ output.RenewalMasterFrameID = stream.GetShort(ref index);
+ output.HActive = stream.GetShort(ref index);
+ output.VActive = stream.GetShort(ref index);
+ output.HTotal = stream.GetShort(ref index);
+ output.VTotal = stream.GetShort(ref index);
+
+ //Ignoring capabilities for now
+ _ = stream.GetShort(ref index);
+
+ byte outputFlags = stream[index++];
+ output.Interlaced = (outputFlags & 0x01) > 0;
+ output.IsFrameLocked = (outputFlags & 0x02) > 0;
+ //output.IsFrozen = (outputFlags & 0x04) > 0; //No IsFrozen flag actually on output yet
+
+ output.VerticalRefresh = stream.GetFloat(ref index);
+ output.Name = stream.GetString(ref index);
+ output.HdcpStatus = (HdcpLinkStatus)stream[index++];
+
+ output.OutputMode = OutputModeFromByte(stream[index++]);
+ output.Rotation = (RotationMode)stream[index++];
+ output.MST = (MSTMode)stream[index++];
+ output.AuxSource = stream.GetString(ref index);
+ output.AuxInput = stream[index++];
+
+ output.UnscaledAuxSourceType = (UnscaledAuxSourceType)stream[index++];
+ output.UnscaledAuxSourceId = stream.GetInt(ref index);
+
+ output.ScaledSource = stream.GetRectangle(ref index);
+ output.ScaledDest = stream.GetRectangle(ref index);
+
+ int rectCount = stream[index++];
+ for (int j = 0; j < rectCount; j++)
+ output.Rectangles.Add(stream.GetRectangle(ref index));
+
+ response.Outputs.Add(output.ID, output);
+ }
+
+ //Get Diagnostic status
+ for (int i = 0; i < diagnosticWarningCount; i++)
+ {
+ DiagnosticStatus status = (DiagnosticStatus)stream[index++];
+ DiagnosticType diagnosticType = (DiagnosticType)stream[index++];
+ response.DiagnosticWarnings.Add(diagnosticType, status);
+ }
+
+ //Get the configuration outputs
+ response.ConfigOutputs.Clear();
+ for (int i = 0; i < configOutputCount; i++)
+ {
+ response.ConfigOutputs.Add(stream[index++]);
+ }
+
+ //[Ignored for now] Get Inputs
+ for (int i = 0; i < inputCount; i++)
+ {
+ _ = stream.GetString(ref index);
+ }
+
+ return response;
+ }
+
+ protected SpyderModels SpyderModelFromByte(ushort value)
+ {
+ SpyderSModels model = (SpyderSModels)value;
+ return model.Convert();
+ }
+
+ protected virtual ConnectorType ParseRouterConnectorType(byte val)
+ {
+ return val switch
+ {
+ 1 => ConnectorType.HDMI,
+ 2 => ConnectorType.DisplayPort,
+ 4 => ConnectorType.SDI,
+ _ => ConnectorType.Auto, //Unknown
+ };
+ }
+
+ protected virtual OutputMode OutputModeFromByte(byte val)
+ {
+ return val switch
+ {
+ 0 => OutputMode.Normal,
+ 1 => OutputMode.Multiviewer,
+ 2 => OutputMode.Scaled,
+ 3 => OutputMode.Aux,
+ 4 => OutputMode.UnscaledAux,
+ 5 => OutputMode.OpMon,
+ 6 => OutputMode.SourceMon,
+ 7 => OutputMode.Tiled,
+ 8 => OutputMode.Unused,
+ _ => OutputMode.Normal, //Unknown
+ };
+ }
+
+ ///
+ /// Someone thought it was a good idea to swap the preview/program enum values in Spyder-S software, so we'll need to flip the values here
+ ///
+ ///
+ ///
+ protected MixerBus ParseSpyderSMixerBus(byte value)
+ {
+ return value == 0 ? MixerBus.Program : MixerBus.Preview;
+
+ }
+
+ protected FieldRate ParseSpyderSFieldRate(int value)
+ {
+ switch (value)
+ {
+ case 23:
+ return FieldRate.FR_23_98;
+ case 24:
+ return FieldRate.FR_24;
+ case 25:
+ return FieldRate.FR_25;
+ case 29:
+ return FieldRate.FR_29_97;
+ case 30:
+ return FieldRate.FR_30;
+ case 47:
+ return FieldRate.FR_47_95;
+ case 48:
+ return FieldRate.FR_48;
+ case 50:
+ return FieldRate.FR_50;
+ case 59:
+ return FieldRate.FR_59_94;
+ case 60:
+ return FieldRate.FR_60;
+ case 96:
+ return FieldRate.FR_96;
+ case 100:
+ return FieldRate.FR_100;
+ case 119:
+ return FieldRate.FR_119_88;
+ case 120:
+ return FieldRate.FR_120;
+ default:
+ return FieldRate.Unknown;
+ }
+ }
+ }
+}
diff --git a/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version68.cs b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version68.cs
new file mode 100644
index 0000000..fc5b117
--- /dev/null
+++ b/src/SpyderClientLibrary/Net/DrawingData/Deserializers/DrawingDataDeserializer_Version68.cs
@@ -0,0 +1,580 @@
+using Knightware.Diagnostics;
+using Knightware.Primitives;
+using Spyder.Client.Common;
+using Spyder.Client.IO;
+using System;
+using System.IO;
+using System.IO.Pipes;
+
+namespace Spyder.Client.Net.DrawingData.Deserializers
+{
+ ///
+ /// Deserializes DrawingData messages in the version 68 serialization format - SpyderS 6.1.0
+ ///
+ public class DrawingDataDeserializer_Version68 : DrawingDataDeserializer_Version64
+ {
+ public DrawingDataDeserializer_Version68(string serverVersion) : base(serverVersion)
+ {
+ }
+
+ public override DrawingData Deserialize(byte[] stream)
+ {
+ if (stream == null || stream.Length == 0)
+ return null;
+
+ DrawingData response = new DrawingData();
+ int index = 0;
+
+ int newPsCount = stream[index++];
+ int newDkCount = stream[index++];
+ int newPvwDkCount = stream[index++];
+ int newMixEffectCount = stream[index++];
+ int newRtrCount = stream[index++];
+ int newOutputCount = stream[index++];
+ int previewPixelSpaceCount = stream[index++];
+ int newMachineCount = stream[index++];
+ int frameCount = stream[index++];
+ int runningScriptCount = stream[index++];
+ int diagnosticWarningCount = stream[index++];
+ int configOutputCount = stream[index++];
+ int inputCount = stream[index++];
+
+ //Global values
+ response.TimeOfDay = new TimeSpan(
+ stream[index++],
+ stream[index++],
+ stream[index++]
+ );
+
+ response.PercentComplete = stream[index++];
+ response.LastFrontPanelMsg = (FrontPanelDisplayCommand)stream[index++];
+ response.ProgressString = stream.GetString(ref index);
+ response.ConfigSource = stream.GetString(ref index);
+ response.ConfigLayer = stream.GetInt(ref index);
+ response.ConfigBus = ParseSpyderSMixerBus(stream[index++]);
+
+ response.HardwareType = (HardwareType)stream[index++];
+ response.DataObjectVersion = stream.GetInt(ref index);
+ response.DataObjectLastChangeType = (DataType)stream.GetInt(ref index);
+
+ //Global Flags
+ byte flags = stream[index++];
+ response.OpMonOverlay = (flags & 0x01) > 0;
+ response.IsMachineHalEnabled = (flags & 0x02) > 0;
+ response.IsRouterHalEnabled = (flags & 0x04) > 0;
+ response.IsDataIOIdle = (flags & 0x08) > 0;
+ response.IsPreviewOnlyScriptingEnabled = (flags & 0x10) > 0;
+ response.IsLiveUpdateEnabled = (flags & 0x20) > 0;
+
+ flags = stream[index++];
+ response.LiveUpdatesTemporarilyDisabled = (flags & 0x01) > 0;
+ response.IsHdcpEnabled = (flags & 0x02) > 0;
+
+ //System Frame Rate
+ response.SystemFrameRate = ParseSpyderSFieldRate(stream[index++]);
+
+ //System Layer Mode
+ response.SystemLayerMode = (SystemLayerMode)stream[index++];
+
+ //Get the frames
+ for (int i = 0; i < frameCount; i++)
+ {
+ int frameID = stream[index++];
+ var frame = new DrawingFrame()
+ {
+ FrameID = frameID,
+ FrameAOR = stream.GetRectangle(ref index),
+ Model = SpyderModelFromByte(stream[index++]),
+ RenewalMasterFrameID = stream[index++],
+ };
+ //Note: Ignoring model capabilities flag for now
+ index++;
+
+ frame.ProgramAOR = frame.FrameAOR; //Spyder-S doesn't require PGM/PVW AORs
+ frame.PreviewAOR = frame.FrameAOR;
+ response.Frames.Add(frameID, frame);
+ }
+
+ //Get the running scripts
+ for (int i = 0; i < runningScriptCount; i++)
+ {
+ int scriptID = stream.GetInt(ref index);
+ long countDown = stream.GetLong(ref index);
+ response.RunningScripts.Add(scriptID, countDown);
+ }
+
+ //Get the preview pixelspace list
+ for (int i = 0; i < previewPixelSpaceCount; i++)
+ {
+ int pgmID = stream.GetShort(ref index);
+ int pvwID = stream.GetShort(ref index);
+ response.PreviewPixelSpaceIDs.Add(pgmID, pvwID);
+ }
+
+ //Get all PixelSpaces
+ for (int i = 0; i < newPsCount; i++)
+ {
+ //Create the new pixelspace
+ DrawingPixelSpace newPS = new DrawingPixelSpace();
+
+ //Fill the new pixelspace
+ newPS.Rect = stream.GetRectangle(ref index);
+
+ //Versions above 5.0.x (5.1.x / 5.2.x / etc) no longer have a RenewalMasterFrameID
+ newPS.RenewMasterFrameID = -1;
+
+ newPS.ID = stream.GetShort(ref index);
+ MixerBus bus = ParseSpyderSMixerBus(stream[index++]);
+ newPS.Name = stream.GetString(ref index);
+
+ //Note: We'll come back after we get the mix effects below to try and set background content thumbnails
+ //newPS.NextBackgroundStillIsOnLayer1 = true;
+ //newPS.LastBackgroundStill = stream.GetString(ref index); //Last background still
+ //newPS.NextBackgroundStill = stream.GetString(ref index); //Next Background still
+
+ //Version 5.0.3+ no longer has PixelSpace scale factor. For backwards compatibility we'll set either 1f for PGM or .99 for PVW.
+ //In the future if this turns out to be problematic we could calculate an effective scale below after we've deserialized all pixelspaces
+ newPS.Scale = bus == MixerBus.Program ? 1f : .99f;
+
+ //Add Pixelspace to the list
+ response.PixelSpaces.Add(newPS.ID, newPS);
+ }
+
+ //Get our mix effects
+ for (int i = 0; i < newMixEffectCount; i++)
+ {
+ DrawingMixEffect mixEffect = new DrawingMixEffect();
+ mixEffect.ID = stream.GetInt(ref index);
+ mixEffect.Name = stream.GetString(ref index);
+ mixEffect.Type = (MixEffectType)stream[index++];
+ mixEffect.BottomContentName = stream.GetString(ref index);
+ mixEffect.BottomContentThumbnail = stream.GetString(ref index);
+ mixEffect.TopContentName = stream.GetString(ref index);
+ mixEffect.TopContentThumbnail = stream.GetString(ref index);
+ mixEffect.TopContentOpacity = stream.GetFloat(ref index);
+ mixEffect.BackgroundPixelSpaceId = stream.GetInt(ref index);
+
+ //Usages
+ int usageCount = stream[index++];
+ for (int j = 0; j < usageCount; j++)
+ {
+ mixEffect.Usages.Add(new DrawingMixEffectUsage()
+ {
+ SourceID = stream.GetInt(ref index),
+ UsageType = (DrawingMixEffectUsageType)stream[index++],
+ Label = stream.GetString(ref index),
+ });
+ }
+
+ //Flags
+ byte mixEffectFlags = stream[index++];
+ mixEffect.TopIsFrozen = (mixEffectFlags & 0x01) > 0;
+ mixEffect.BottomIsFrozen = (mixEffectFlags & 0x02) > 0;
+ mixEffect.TopSupportsFreeze = (mixEffectFlags & 0x04) > 0;
+ mixEffect.BottomSupportsFreeze = (mixEffectFlags & 0x08) > 0;
+
+ response.DrawingMixEffects.Add(mixEffect.ID, mixEffect);
+ }
+
+ //Now that we have MixEffects, update pixelspace background info
+ foreach (var ps in response.PixelSpaces.Values)
+ {
+ foreach (var me in response.DrawingMixEffects.Values)
+ {
+ if (me.BackgroundPixelSpaceId == ps.ID)
+ {
+ ps.NextBackgroundStillIsOnLayer1 = true;
+ ps.LastBackgroundStill = me.BottomContentThumbnail;
+ ps.NextBackgroundStill = me.TopContentThumbnail;
+ ps.Layer1Transparency = (byte)(me.TopContentOpacity * byte.MaxValue);
+ }
+ }
+ }
+
+ //Get all of our DrawingKeyFrames
+ int allLayerCount = newDkCount + newPvwDkCount;
+
+ //Add a couple 'virtual' background layers to maintain overall compatibility in library. Spyder-S removed these and starts layer IDs at zero
+ response.DrawingKeyFrames.Add(0, new DrawingKeyFrame() { LayerID = 0, IsBackground = true });
+ response.DrawingKeyFrames.Add(1, new DrawingKeyFrame() { LayerID = 1, IsBackground = true });
+
+ for (int i = 0; i < allLayerCount; i++)
+ {
+ DrawingKeyFrame l = new DrawingKeyFrame();
+ KeyFrame kf = l.KeyFrame;
+
+ l.FrameID = stream[index++];
+
+ //Fill Drawing Keyframe
+ l.MixEffectID = stream.GetInt(ref index);
+ if(response.DrawingMixEffects.ContainsKey(l.MixEffectID))
+ {
+ l.MixEffect = response.DrawingMixEffects[l.MixEffectID];
+ }
+
+ l.Priority = stream.GetShort(ref index);
+ l.HActive = stream.GetShort(ref index); //HActive of the input itself (used for calculating the scaled border size)
+ l.VActive = stream.GetShort(ref index); //VActive of the input itself (used for Pan calculations)
+ l.LayerID = stream.GetShort(ref index) + 2; ; //Layer ID (offset by 2 since Spyder-S uses a zero-based layer ID system)
+ _ = stream.GetShort(ref index); //LayerIndex - layer index on frame (we're ignoring this for now)
+ l.PixelSpaceID = stream.GetInt(ref index); //Pixelspace ID
+ l.EffectID = stream.GetInt(ref index);
+ l.LastScript = stream.GetInt(ref index);
+ l.LastCue = stream.GetInt(ref index);
+
+ l.Thumbnail = stream.GetString(ref index); //thumbnail
+ l.WindowLabel = stream.GetString(ref index);
+ l.Source = stream.GetString(ref index);
+ l.LoadedStill = stream.GetString(ref index);
+ l.TestPattern = stream.GetString(ref index);
+ l.SourceRouterID = stream.GetInt(ref index);
+ l.SourceRouterInput = stream.GetInt(ref index);
+ l.InputConfigID = stream.GetInt(ref index);
+
+ _ = stream.GetInt(ref index); //CurrentInputID - we're ignoring this for now
+
+ l.AspectRatio = stream.GetFloat(ref index); //aspect ratio
+ l.LayerRect = stream.GetRectangle(ref index);
+ l.AOIRect = stream.GetRectangle(ref index);
+
+ //Not storing element type
+ //l.ElementType = (ElementType)stream[index++]; //Element Type
+ index++;
+
+ l.Transparency = stream[index++]; //Visible / Transparency
+
+ //Layer flags (block 1)
+ flags = stream[index++];
+ l.IsMixer = (flags & 0x01) > 0;
+ l.IsFrozen = (flags & 0x02) > 0;
+ l.IsSlave = (flags & 0x04) > 0;
+ l.AlwaysRelative = (flags & 0x08) > 0;
+ kf.BorderOutsideSoftBottom = (flags & 0x10) > 0;
+ kf.BorderOutsideSoftLeft = (flags & 0x20) > 0;
+ kf.BorderOutsideSoftRight = (flags & 0x40) > 0;
+ l.AutoSyncOnTimingChange = (flags & 0x80) > 0;
+
+ //Layer flags (block 2)
+ flags = stream[index++];
+ kf.BorderOutsideSoftTop = (flags & 0x01) > 0;
+ kf.UseDefaultMotionValues = (flags & 0x02) > 0;
+ l.IsBackground = (flags & 0x04) > 0;
+ l.IsHardwarePreview = (flags & 0x08) > 0;
+ l.IsMixing = (flags & 0x10) > 0;
+ l.IsWithinPixelSpace = (flags & 0x20) > 0;
+ l.HdcpAuthenticated = (flags & 0x40) > 0;
+ l.ShadowIsEnabled = (flags & 0x80) > 0;
+
+ //Layer flags (block 3)
+ flags = stream[index++];
+ l.IsLocked = (flags & 0x01) > 0;
+ //l.IsVisible = (flags & 0x02) > 0;
+ //l.IsDrawingApplicable = (flags & 0x04) > 0;
+ //l.IsDualLayerMode = (flags & 0x08) > 0;
+ //l.HasFrameBuffer = (flags & 0x10) > 0;
+ //l.InputConnectionDetected = (flags & 0x20) > 0;
+ l.IsGlobalLayer = (flags & 0x40) > 0;
+
+ //Ignoring keyer mode for now
+ //l.KeyerMode = (KeyerMode)Stream[index++]; //Keyer Mode
+ index++;
+
+ kf.BorderThickness = stream.GetShort(ref index); //border thickness
+ kf.Width = (ushort)stream.GetShort(ref index); //HSize
+ kf.BorderInsideSoftness = stream.GetShort(ref index); //Border inside softness
+ kf.BorderOutsideSoftness = stream.GetShort(ref index); //Border outside softness
+
+ kf.ShadowHOffset = stream.GetShort(ref index); //Shadow H Offset
+ kf.ShadowVOffset = stream.GetShort(ref index); //Shadow V Offset
+ kf.ShadowHSize = stream.GetShort(ref index); //Shadow H Size
+ kf.ShadowVSize = kf.ShadowHSize;
+
+ kf.ShadowSoftness = stream.GetShort(ref index); //Shadow softness
+ kf.ShadowTransparency = stream.GetShort(ref index); //Shadow Transparency
+ kf.BorderLumaOffsetBottom = stream.GetShort(ref index);
+ kf.BorderLumaOffsetLeft = stream.GetShort(ref index);
+ kf.BorderLumaOffsetRight = stream.GetShort(ref index);
+ kf.BorderLumaOffsetTop = stream.GetShort(ref index);
+
+ kf.HPosition = stream.GetFloat(ref index); //HPosition
+ kf.VPosition = stream.GetFloat(ref index); //VPosition
+ kf.TopCrop = stream.GetFloat(ref index); //top crop
+ kf.LeftCrop = stream.GetFloat(ref index); //Left crop
+ kf.RightCrop = stream.GetFloat(ref index); //Right crop
+ kf.BottomCrop = stream.GetFloat(ref index); //Bottom crop
+ kf.Zoom = stream.GetFloat(ref index); //Zoom for zoom/pan
+ kf.AspectRatioOffset = stream.GetFloat(ref index); //Aspect ratio offset
+ kf.EaseIn = stream.GetFloat(ref index); //Ease in
+ kf.EaseOut = stream.GetFloat(ref index); //Ease out
+ kf.PanH = stream.GetInt(ref index); //Horizontal Pan
+ kf.PanV = stream.GetInt(ref index); //Vertical Pan
+ kf.CropAnchor = (CropAnchorTypes)stream[index++];
+ kf.Transparency = stream[index++]; //Transparency
+ kf.Duration = stream.GetShort(ref index);
+ kf.CloneMode = (CloneMode)stream[index++]; //clone mode
+
+ //Clone offsets
+ int cloneOffsetCount = stream[index++];
+ float[] cloneOffsets = new float[cloneOffsetCount];
+ for (int j = 0; j < cloneOffsets.Length; j++)
+ {
+ cloneOffsets[j] = stream.GetFloat(ref index);
+ }
+ kf.CloneOffsets = cloneOffsets;
+
+ //Clone absolute pixel offsets
+ int absoluteCloneOffsetCount = stream[index++];
+ Rectangle[] cloneRects = new Rectangle[absoluteCloneOffsetCount];
+ for (int j = 0; j < cloneRects.Length; j++)
+ {
+ int cloneHOffset = stream.GetInt(ref index);
+ cloneRects[j] = new Rectangle()
+ {
+ X = l.LayerRect.X + cloneHOffset,
+ Y = l.LayerRect.Y,
+ Width = l.LayerRect.Width,
+ Height = l.LayerRect.Height
+ };
+ }
+ l.CloneRects = cloneRects;
+
+ kf.BorderColor = new Color(
+ stream[index++],
+ stream[index++],
+ stream[index++]);
+
+ kf.ShadowColor = new Color(
+ stream[index++],
+ stream[index++],
+ stream[index++]);
+
+ kf.BorderShapeSource = (ShapeSource)stream[index++];
+ kf.BorderShape = (ShapeType)stream[index++];
+ kf.BorderShapeFile = stream.GetString(ref index);
+ kf.BorderShapeStretch = (BorderStretchMode)stream[index++];
+ kf.BorderShapeStretchAspectRatio = stream.GetFloat(ref index);
+
+ kf.BorderFillSource = (TextureFillSource)stream[index++];
+ kf.BorderTextureType = (TextureType)stream[index++];
+ kf.BorderTileMode = (TextureTileMode)stream[index++];
+ kf.BorderTextureFile = stream.GetString(ref index);
+
+ //Scale (coerced from parent pixelspace)
+ l.Scale = (response.PixelSpaces.ContainsKey(l.PixelSpaceID) ? response.PixelSpaces[l.PixelSpaceID].Scale : 1);
+
+ //Add Keyframe to list
+ if (i < newDkCount)
+ {
+ response.DrawingKeyFrames.Add(l.LayerID, l);
+ }
+ else
+ {
+ response.PreviewDrawingKeyFrames.Add(l.LayerID, l);
+ }
+ }
+
+ //Compatibility fix - if any of the PGM layers are not visible but the preview layers are, promote the preview layers to PGM layers
+ //This is needed for Spyder X80 V5.0.3 and above, where PGM and PVW layers have become discrete operating objects
+ for (int i = 0; i < newDkCount+2; i++)
+ {
+ var pgmLayer = response.DrawingKeyFrames[i];
+ if (pgmLayer != null && !pgmLayer.IsVisible && !pgmLayer.IsBackground)
+ {
+ var pvwLayer = response.PreviewDrawingKeyFrames[i];
+ if (pvwLayer.IsVisible)
+ {
+ response.DrawingKeyFrames.Remove(i);
+ response.PreviewDrawingKeyFrames.Remove(i);
+ response.DrawingKeyFrames.Add(i, pvwLayer);
+ response.PreviewDrawingKeyFrames.Add(i, pgmLayer);
+ }
+ }
+ }
+
+ //Get the routers
+ for (int i = 0; i < newRtrCount; i++)
+ {
+ var router = new DrawingRouter();
+
+ router.Name = stream.GetString(ref index);
+ router.TransportType = stream.GetString(ref index);
+ router.RouterType = stream.GetString(ref index);
+ router.IPAddress = stream.GetString(ref index);
+
+ //Router flags
+ flags = stream[index++];
+ router.IPRouter = (flags & 0x01) > 0;
+ router.SerialRouter = (flags & 0x02) > 0;
+ router.LevelControlledRouter = (flags & 0x04) > 0;
+
+ router.ID = stream[index++];
+ router.InputCount = stream.GetShort(ref index);
+ router.OutputCount = stream.GetShort(ref index);
+ router.Port = stream[index++];
+ router.ConnectorType = ParseRouterConnectorType(stream[index++]);
+ router.ControlLevel = stream.GetInt(ref index);
+ router.LevelCount = stream.GetInt(ref index);
+
+ //Write the soft patch for the router
+ int patchCount = stream[index++];
+ router.Patch.Clear();
+ for (int patchIndex = 0; patchIndex < patchCount; patchIndex++)
+ {
+ int physicalOutput = stream[index++];
+ int downstreamRouterID = stream[index++];
+ int downstreamRouterInput = stream[index++];
+
+ //downstream ID may be -1 or -2 if we are patched to a layer. This will have been clipped to 255
+ if (downstreamRouterID == 255)
+ downstreamRouterID = -1;
+ else if (downstreamRouterID == 254)
+ downstreamRouterID = -2;
+
+ router.SetRouterOutputPatch(physicalOutput, downstreamRouterID, downstreamRouterInput);
+ }
+
+ //Write the crosspoints for the router
+ for (int outputID = 0; outputID < router.OutputCount; outputID++)
+ {
+ if (router.InputCount >= 255)
+ {
+ //Desrialize two bytes of data
+ router.Crosspoints.Add(stream.GetShort(ref index));
+ }
+ else
+ {
+ //Deserialize only one byte of data
+ int input = stream[index++];
+ if (input == 255)
+ input = -1;
+
+ router.Crosspoints.Add(input);
+ }
+ }
+
+ //Add router to list
+ response.Routers.Add(router.ID, router);
+ }
+
+ //Get the Machines
+ for (int i = 0; i < newMachineCount; i++)
+ {
+ var newMachine = new DrawingMachine();
+ MachineStatus status = newMachine.Status;
+
+ //Port ID
+ newMachine.Port = stream[index++];
+
+ //Last PlayItemID
+ newMachine.LastPlayItemID = stream.GetInt(ref index);
+
+ //Machine Time
+ FieldRate frameRate = (FieldRate)stream[index++];
+ long frames = stream.GetLong(ref index);
+ newMachine.Time = new TimeCode(frameRate, frames);
+
+ //Machine Status (Block 1)
+ flags = stream[index++];
+ status.Cued = (flags & 0x01) > 0;
+ status.Ejecting = (flags & 0x02) > 0;
+ status.FastForwarding = (flags & 0x04) > 0;
+ status.Jog = (flags & 0x08) > 0;
+ status.Local = (flags & 0x10) > 0;
+ status.Playing = (flags & 0x20) > 0;
+ status.Recording = (flags & 0x40) > 0;
+ status.Rewinding = (flags & 0x80) > 0;
+
+ //Machine Status (Block 2)
+ flags = stream[index++];
+ status.ServoLock = (flags & 0x01) > 0;
+ status.ServoRefMissing = (flags & 0x02) > 0;
+ status.Shuttle = (flags & 0x04) > 0;
+ status.Standby = (flags & 0x08) > 0;
+ status.Still = (flags & 0x10) > 0;
+ status.Stopped = (flags & 0x20) > 0;
+ status.TapeDir = (flags & 0x40) > 0;
+ status.TapeOut = (flags & 0x80) > 0;
+
+ //Machine Status (Block 3)
+ flags = stream[index++];
+ status.TsoMode = (flags & 0x01) > 0;
+ status.Var = (flags & 0x02) > 0;
+
+ //Add to collection
+ response.Machines.Add(newMachine.Port, newMachine);
+ }
+
+ //Get the outputs
+ for (int i = 0; i < newOutputCount; i++)
+ {
+ DrawingOutput output = new DrawingOutput();
+
+ output.OutputType = OutputModuleType.SpyderS;
+ output.ID = stream.GetShort(ref index);
+ _ = stream.GetShort(ref index); //PortID - ignoring for now
+ output.FrameID = stream.GetShort(ref index);
+ output.RenewalMasterFrameID = stream.GetShort(ref index);
+ output.HActive = stream.GetShort(ref index);
+ output.VActive = stream.GetShort(ref index);
+ output.HTotal = stream.GetShort(ref index);
+ output.VTotal = stream.GetShort(ref index);
+
+ //Ignoring capabilities for now
+ _ = stream.GetShort(ref index);
+
+ byte outputFlags = stream[index++];
+ output.Interlaced = (outputFlags & 0x01) > 0;
+ output.IsFrameLocked = (outputFlags & 0x02) > 0;
+ //output.IsFrozen = (outputFlags & 0x04) > 0; //No IsFrozen flag actually on output yet
+
+ output.VerticalRefresh = stream.GetFloat(ref index);
+ output.Name = stream.GetString(ref index);
+ output.HdcpStatus = (HdcpLinkStatus)stream[index++];
+
+ output.OutputMode = OutputModeFromByte(stream[index++]);
+ output.Rotation = (RotationMode)stream[index++];
+ output.MST = (MSTMode)stream[index++];
+ output.AuxSource = stream.GetString(ref index);
+ output.AuxInput = stream[index++];
+
+ output.UnscaledAuxSourceType = (UnscaledAuxSourceType)stream[index++];
+ output.UnscaledAuxSourceId = stream.GetInt(ref index);
+
+ output.ScaledSource = stream.GetRectangle(ref index);
+ output.ScaledDest = stream.GetRectangle(ref index);
+
+ //Ignoring still name loaded on output
+ _ = stream.GetString(ref index);
+
+ int rectCount = stream[index++];
+ for (int j = 0; j < rectCount; j++)
+ output.Rectangles.Add(stream.GetRectangle(ref index));
+
+ response.Outputs.Add(output.ID, output);
+ }
+
+ //Get Diagnostic status
+ for (int i = 0; i < diagnosticWarningCount; i++)
+ {
+ DiagnosticStatus status = (DiagnosticStatus)stream[index++];
+ DiagnosticType diagnosticType = (DiagnosticType)stream[index++];
+ response.DiagnosticWarnings.Add(diagnosticType, status);
+ }
+
+ //Get the configuration outputs
+ response.ConfigOutputs.Clear();
+ for (int i = 0; i < configOutputCount; i++)
+ {
+ response.ConfigOutputs.Add(stream[index++]);
+ }
+
+ //[Ignored for now] Get Inputs
+ for (int i = 0; i < inputCount; i++)
+ {
+ _ = stream.GetString(ref index);
+ }
+
+ return response;
+ }
+ }
+}
diff --git a/src/SpyderClientLibrary/Net/DrawingData/DrawingData.cs b/src/SpyderClientLibrary/Net/DrawingData/DrawingData.cs
index 3b72ff5..04092b6 100644
--- a/src/SpyderClientLibrary/Net/DrawingData/DrawingData.cs
+++ b/src/SpyderClientLibrary/Net/DrawingData/DrawingData.cs
@@ -80,6 +80,20 @@ public Dictionary Routers
}
}
+ private Dictionary drawingMixEffects = new Dictionary();
+ public Dictionary DrawingMixEffects
+ {
+ get { return drawingMixEffects; }
+ set
+ {
+ if (drawingMixEffects != value)
+ {
+ drawingMixEffects = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
private Dictionary drawingKeyFrames = new Dictionary();
public Dictionary DrawingKeyFrames
{
@@ -89,7 +103,7 @@ public Dictionary DrawingKeyFrames
if (drawingKeyFrames != value)
{
drawingKeyFrames = value;
- OnPropertyChanged("DrawingKeyFrames");
+ OnPropertyChanged();
}
}
}
@@ -406,6 +420,20 @@ public FieldRate SystemFrameRate
}
}
+ private SystemLayerMode systemLayerMode;
+ public SystemLayerMode SystemLayerMode
+ {
+ get { return systemLayerMode; }
+ set
+ {
+ if (systemLayerMode != value)
+ {
+ systemLayerMode = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
private int dataObjectVersion;
public int DataObjectVersion
{
diff --git a/src/SpyderClientLibrary/Net/DrawingData/DrawingKeyFrame.cs b/src/SpyderClientLibrary/Net/DrawingData/DrawingKeyFrame.cs
index 8e12811..b6ab536 100644
--- a/src/SpyderClientLibrary/Net/DrawingData/DrawingKeyFrame.cs
+++ b/src/SpyderClientLibrary/Net/DrawingData/DrawingKeyFrame.cs
@@ -1,5 +1,7 @@
using Knightware.Primitives;
using Spyder.Client.Common;
+using System;
+using Spyder.Client;
namespace Spyder.Client.Net.DrawingData
{
@@ -33,6 +35,54 @@ public int SourceRouterID
}
}
+ ///
+ /// Starting in Spyder-S, global layers are automatically provisioned with static slave layers for each frame group
+ ///
+ public bool IsGlobalLayer
+ {
+ get { return isGlobalLayer; }
+ set
+ {
+ if (isGlobalLayer != value)
+ {
+ isGlobalLayer = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ private bool isGlobalLayer;
+
+ ///
+ /// In Spyder-S frames, layers can use mix effects as content
+ ///
+ public int MixEffectID
+ {
+ get { return mixEffectID; }
+ set
+ {
+ if(mixEffectID != value)
+ {
+ mixEffectID = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ private int mixEffectID = -1;
+
+ public DrawingMixEffect MixEffect
+ {
+ get { return mixEffect; }
+ set
+ {
+ if(mixEffect != value)
+ {
+ mixEffect = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ private DrawingMixEffect mixEffect;
+
private int sourceRouterInput;
public int SourceRouterInput
{
@@ -273,20 +323,39 @@ public Rectangle LayerRect
}
}
- private Rectangle cloneRect;
public Rectangle CloneRect
{
- get { return cloneRect; }
+ get
+ {
+ if (cloneRects == null || cloneRects.Length < 1)
+ return Rectangle.Empty;
+
+ return cloneRects[0];
+ }
set
{
- if (cloneRect != value)
- {
- cloneRect = value;
- OnPropertyChanged();
- }
+ if (value.IsEmpty)
+ CloneRects = null;
+ else
+ CloneRects = new Rectangle[] { value };
}
}
+ public Rectangle[] CloneRects
+ {
+ get { return cloneRects; }
+ set
+ {
+ if(!cloneRects.SequenceEqualSafe(value))
+ {
+ cloneRects = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(CloneRect));
+ }
+ }
+ }
+ private Rectangle[] cloneRects;
+
private float aspectRatio;
public float AspectRatio
{
@@ -620,6 +689,15 @@ public virtual void CopyFrom(DrawingKeyFrame copyFrom)
this.TestPattern = copyFrom.TestPattern;
this.Scale = copyFrom.Scale;
this.ShadowIsEnabled = copyFrom.ShadowIsEnabled;
+ this.IsGlobalLayer = copyFrom.IsGlobalLayer;
+ this.MixEffectID = copyFrom.MixEffectID;
+
+ if (copyFrom.MixEffect == null)
+ this.MixEffect = null;
+ else if (this.MixEffect == null)
+ this.MixEffect = new DrawingMixEffect(copyFrom.MixEffect);
+ else
+ this.MixEffect.CopyFrom(copyFrom.MixEffect);
}
}
}
diff --git a/src/SpyderClientLibrary/Net/DrawingData/DrawingMixEffect.cs b/src/SpyderClientLibrary/Net/DrawingData/DrawingMixEffect.cs
new file mode 100644
index 0000000..bc354f4
--- /dev/null
+++ b/src/SpyderClientLibrary/Net/DrawingData/DrawingMixEffect.cs
@@ -0,0 +1,263 @@
+using Knightware.Primitives;
+using Spyder.Client.Common;
+using System;
+using System.Collections.Generic;
+using Spyder.Client;
+
+namespace Spyder.Client.Net.DrawingData
+{
+ public class DrawingMixEffect : PropertyChangedBase, IEquatable
+ {
+ private int id;
+ public int ID
+ {
+ get { return id; }
+ set
+ {
+ if (id != value)
+ {
+ id = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private string name;
+ public string Name
+ {
+ get { return name; }
+ set
+ {
+ if (name != value)
+ {
+ name = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private int backgroundPixelSpaceId = -1;
+ public int BackgroundPixelSpaceId
+ {
+ get { return backgroundPixelSpaceId; }
+ set
+ {
+ if (backgroundPixelSpaceId != value)
+ {
+ backgroundPixelSpaceId = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private MixEffectType type;
+ public MixEffectType Type
+ {
+ get { return type; }
+ set
+ {
+ if (type != value)
+ {
+ type = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private bool topIsFrozen;
+ public bool TopIsFrozen
+ {
+ get { return topIsFrozen; }
+ set
+ {
+ if (topIsFrozen != value)
+ {
+ topIsFrozen = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private bool bottomIsFrozen;
+ public bool BottomIsFrozen
+ {
+ get { return bottomIsFrozen; }
+ set
+ {
+ if (bottomIsFrozen != value)
+ {
+ bottomIsFrozen = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private bool topSupportsFreeze;
+ public bool TopSupportsFreeze
+ {
+ get { return topSupportsFreeze; }
+ set
+ {
+ if (topSupportsFreeze != value)
+ {
+ topSupportsFreeze = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private bool bottomSupportsFreeze;
+ public bool BottomSupportsFreeze
+ {
+ get { return bottomSupportsFreeze; }
+ set
+ {
+ if (bottomSupportsFreeze != value)
+ {
+ bottomSupportsFreeze = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private string topContentName;
+ public string TopContentName
+ {
+ get { return topContentName; }
+ set
+ {
+ if (topContentName != value)
+ {
+ topContentName = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(HasContent));
+ }
+ }
+ }
+
+ private string topContentThumbnail;
+ public string TopContentThumbnail
+ {
+ get { return topContentThumbnail; }
+ set
+ {
+ if (topContentThumbnail != value)
+ {
+ topContentThumbnail = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private string bottomContentName;
+ public string BottomContentName
+ {
+ get { return bottomContentName; }
+ set
+ {
+ if (bottomContentName != value)
+ {
+ bottomContentName = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(HasContent));
+ }
+ }
+ }
+
+ private string bottomContentThumbnail;
+ public string BottomContentThumbnail
+ {
+ get { return bottomContentThumbnail; }
+ set
+ {
+ if (bottomContentThumbnail != value)
+ {
+ bottomContentThumbnail = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private double topContentOpacity;
+ public double TopContentOpacity
+ {
+ get { return topContentOpacity; }
+ set
+ {
+ if (topContentOpacity != value)
+ {
+ topContentOpacity = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public bool HasContent => !string.IsNullOrEmpty(TopContentName) || !string.IsNullOrEmpty(BottomContentName);
+
+ private List usages = new List();
+ public List Usages
+ {
+ get { return usages; }
+ set
+ {
+ if (usages != value)
+ {
+ usages = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public DrawingMixEffect()
+ {
+
+ }
+
+ public DrawingMixEffect(DrawingMixEffect copyFrom)
+ {
+ CopyFrom(copyFrom);
+ }
+
+ public void CopyFrom(DrawingMixEffect copyFrom)
+ {
+ if (copyFrom == null)
+ return;
+
+ ID = copyFrom.ID;
+ Name = copyFrom.Name;
+ BackgroundPixelSpaceId = copyFrom.BackgroundPixelSpaceId;
+ Type = copyFrom.Type;
+ TopIsFrozen = copyFrom.TopIsFrozen;
+ BottomIsFrozen = copyFrom.BottomIsFrozen;
+ TopSupportsFreeze = copyFrom.TopSupportsFreeze;
+ BottomSupportsFreeze = copyFrom.BottomSupportsFreeze;
+ TopContentName = copyFrom.TopContentName;
+ TopContentThumbnail = copyFrom.TopContentThumbnail;
+ BottomContentName = copyFrom.BottomContentName;
+ BottomContentThumbnail = copyFrom.BottomContentThumbnail;
+ TopContentOpacity = copyFrom.TopContentOpacity;
+ Usages = new List(copyFrom.Usages);
+ }
+
+ public bool Equals(DrawingMixEffect other)
+ {
+ if (other == null)
+ return false;
+
+ return ID == other.ID &&
+ Name == other.Name &&
+ BackgroundPixelSpaceId == other.BackgroundPixelSpaceId &&
+ Type == other.Type &&
+ TopIsFrozen == other.TopIsFrozen &&
+ BottomIsFrozen == other.BottomIsFrozen &&
+ TopSupportsFreeze == other.TopSupportsFreeze &&
+ BottomSupportsFreeze == other.BottomSupportsFreeze &&
+ TopContentName == other.TopContentName &&
+ TopContentThumbnail == other.TopContentThumbnail &&
+ BottomContentName == other.BottomContentName &&
+ BottomContentThumbnail == other.BottomContentThumbnail &&
+ TopContentOpacity == other.TopContentOpacity &&
+ Usages.SequenceEqualSafe(other.Usages);
+ }
+ }
+}
diff --git a/src/SpyderClientLibrary/Net/DrawingData/DrawingMixEffectUsage.cs b/src/SpyderClientLibrary/Net/DrawingData/DrawingMixEffectUsage.cs
new file mode 100644
index 0000000..612bab0
--- /dev/null
+++ b/src/SpyderClientLibrary/Net/DrawingData/DrawingMixEffectUsage.cs
@@ -0,0 +1,92 @@
+using Knightware.Primitives;
+using Spyder.Client.Common;
+using System;
+
+namespace Spyder.Client.Net.DrawingData
+{
+ public enum DrawingMixEffectUsageType
+ {
+ Background,
+ ProgramLayer,
+ PreviewLayer,
+ Output,
+ ProgramPixelSpace,
+ PreviewPixelSpace,
+ }
+
+ public class DrawingMixEffectUsage : PropertyChangedBase, IEquatable
+ {
+ private int sourceID;
+ public int SourceID
+ {
+ get { return sourceID; }
+ set
+ {
+ if (sourceID != value)
+ {
+ sourceID = value;
+ OnPropertyChanged();
+ }
+ }
+
+ }
+
+ private string label;
+ public string Label
+ {
+ get { return label; }
+ set
+ {
+ if (label != value)
+ {
+ label = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private DrawingMixEffectUsageType usageType;
+ public DrawingMixEffectUsageType UsageType
+ {
+ get { return usageType; }
+ set
+ {
+ if (usageType != value)
+ {
+ usageType = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public DrawingMixEffectUsage()
+ {
+
+ }
+
+ public DrawingMixEffectUsage(DrawingMixEffectUsage copyFrom)
+ {
+ CopyFrom(copyFrom);
+ }
+
+ public void CopyFrom(DrawingMixEffectUsage copyFrom)
+ {
+ if (copyFrom == null)
+ return;
+
+ this.SourceID = copyFrom.sourceID;
+ this.Label = copyFrom.Label;
+ this.UsageType = copyFrom.UsageType;
+ }
+
+ public bool Equals(DrawingMixEffectUsage other)
+ {
+ if (other == null)
+ return false;
+
+ return SourceID == other.SourceID &&
+ Label == other.Label &&
+ UsageType == other.UsageType;
+ }
+ }
+}
diff --git a/src/SpyderClientLibrary/Net/DrawingData/DrawingOutput.cs b/src/SpyderClientLibrary/Net/DrawingData/DrawingOutput.cs
index 9f4086d..08def84 100644
--- a/src/SpyderClientLibrary/Net/DrawingData/DrawingOutput.cs
+++ b/src/SpyderClientLibrary/Net/DrawingData/DrawingOutput.cs
@@ -231,6 +231,34 @@ public int AuxInput
}
}
+ private UnscaledAuxSourceType unscaledAuxSourceType;
+ public UnscaledAuxSourceType UnscaledAuxSourceType
+ {
+ get { return unscaledAuxSourceType; }
+ set
+ {
+ if(unscaledAuxSourceType != value)
+ {
+ unscaledAuxSourceType = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private int unscaledAuxSourceId = -1;
+ public int UnscaledAuxSourceId
+ {
+ get { return unscaledAuxSourceId; }
+ set
+ {
+ if(unscaledAuxSourceId != value)
+ {
+ unscaledAuxSourceId = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
private OutputModuleType outputType;
public OutputModuleType OutputType
{
diff --git a/src/SpyderClientLibrary/Net/DrawingData/OutputModuleType.cs b/src/SpyderClientLibrary/Net/DrawingData/OutputModuleType.cs
index 1d1067d..55f5231 100644
--- a/src/SpyderClientLibrary/Net/DrawingData/OutputModuleType.cs
+++ b/src/SpyderClientLibrary/Net/DrawingData/OutputModuleType.cs
@@ -1,6 +1,4 @@
namespace Spyder.Client.Net.DrawingData
{
- public enum OutputModuleType { SpyderDX4, SpyderUniversal, X20, X80, URS, OutputBase };
- public enum OutputModuleTypeX20 { SpyderDX4, SpyderUniversal, X20, URS, OutputBase };
- public enum OutputModuleTypeX80 { SpyderDX4, SpyderUniversal, X20, X80, URS, OutputBase };
+ public enum OutputModuleType { SpyderDX4, SpyderUniversal, X20, X80, URS, SpyderS, OutputBase };
}
diff --git a/src/SpyderClientLibrary/Net/Notifications/SpyderServerEventListener.cs b/src/SpyderClientLibrary/Net/Notifications/SpyderServerEventListener.cs
index 204aff9..f3e2039 100644
--- a/src/SpyderClientLibrary/Net/Notifications/SpyderServerEventListener.cs
+++ b/src/SpyderClientLibrary/Net/Notifications/SpyderServerEventListener.cs
@@ -266,7 +266,8 @@ void deserializer_DrawingDataDeserialized(object sender, DrawingDataDeserialized
//Spyder studio (v5 and above) changes the spyder header to mantis, so we need to check both
if ((data[0] == (byte)'s' && data[1] == (byte)'p' && data[2] == (byte)'y' && data[3] == (byte)'d' && data[4] == (byte)'e' && data[5] == (byte)'r') ||
- (data[0] == (byte)'m' && data[1] == (byte)'a' && data[2] == (byte)'n' && data[3] == (byte)'t' && data[4] == (byte)'i' && data[5] == (byte)'s'))
+ (data[0] == (byte)'m' && data[1] == (byte)'a' && data[2] == (byte)'n' && data[3] == (byte)'t' && data[4] == (byte)'i' && data[5] == (byte)'s') ||
+ (data[0] == (byte)'e' && data[1] == (byte)'c' && data[2] == (byte)'h' && data[3] == (byte)'o' && data[4] == (byte)'p' && data[5] == (byte)'s'))
{
ServerEventType eventType = (ServerEventType)(data[10] | (data[11] << 8));
return eventType;
diff --git a/src/SpyderClientLibrary/Net/QFTClient.cs b/src/SpyderClientLibrary/Net/QFTClient.cs
index 3e81151..a638af3 100644
--- a/src/SpyderClientLibrary/Net/QFTClient.cs
+++ b/src/SpyderClientLibrary/Net/QFTClient.cs
@@ -104,7 +104,7 @@ public Task ShutdownAsync()
private async Task ShutdownAsync(bool sendRemoteDisconnectRequest, bool acquireSemaphore)
{
- AsyncLock.Releaser? releaser = null;
+ IDisposable releaser = null;
try
{
if (pingWorker != null)
@@ -139,8 +139,7 @@ private async Task ShutdownAsync(bool sendRemoteDisconnectRequest, bool acquireS
}
finally
{
- if (releaser.HasValue)
- releaser.Value.Dispose();
+ releaser?.Dispose();
}
}
@@ -296,7 +295,7 @@ public async Task EnableCompressionForCurrentSession(bool acquireSemaphore
const int size = 1;
byte[] buffer = new byte[size];
- AsyncLock.Releaser? releaser = null;
+ IDisposable releaser = null;
try
{
if (acquireSemaphore)
@@ -326,8 +325,7 @@ public async Task EnableCompressionForCurrentSession(bool acquireSemaphore
}
finally
{
- if (releaser.HasValue)
- releaser.Value.Dispose();
+ releaser?.Dispose();
}
}
@@ -350,7 +348,7 @@ protected async Task Ping(bool obtainSemaphore)
const int size = 1;
byte[] buffer = new byte[size];
- AsyncLock.Releaser? releaser = null;
+ IDisposable releaser = null;
try
{
if (obtainSemaphore)
@@ -371,8 +369,7 @@ protected async Task Ping(bool obtainSemaphore)
}
finally
{
- if (releaser.HasValue)
- releaser.Value.Dispose();
+ releaser?.Dispose();
}
}
diff --git a/src/SpyderClientLibrary/SpyderClientLibrary.csproj b/src/SpyderClientLibrary/SpyderClientLibrary.csproj
index 169fd89..d50de56 100644
--- a/src/SpyderClientLibrary/SpyderClientLibrary.csproj
+++ b/src/SpyderClientLibrary/SpyderClientLibrary.csproj
@@ -3,53 +3,28 @@
netstandard2.0
9.0
Spyder.Client
- AnyCPU;x86;x64
Derek Smithson
+ Knightware
+ 0.0.1
Library for controlling the Spyder 200/300/X20/X80 video processor families. Provides UDP control, server auto-discovery, drawing data (live display info), file transfer services, and image cache management.
- Knightware 2021
- 4.1.0
+ Copyright $([System.DateTime]::Now.Year)
https://github.com/dsmithson/SpyderClientLibrary
+ spyder knightware
+ Apache-2.0
https://github.com/dsmithson/SpyderClientLibrary
git
- true
- Apache-2.0
- Added client to interact with Spyder Still Server (X20) in Spyder.Client.Net.StillServerClient
- Knightware
-
- 4.1.0.0
- 4.1.0.0
-
-
- false
-
- bin\Debug
-
-
-
README.md
+
-
+
-
- false
-
- bin\Debug
-
-
-
- false
-
- bin\Debug
-
-
-
- bin\Release
-
+
+
-
+
\ No newline at end of file
diff --git a/src/SpyderClientLibrary/SpyderClientLibrary.nuspec b/src/SpyderClientLibrary/SpyderClientLibrary.nuspec
deleted file mode 100644
index 77e0332..0000000
--- a/src/SpyderClientLibrary/SpyderClientLibrary.nuspec
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- SpyderClientLibrary
- $version$
- Spyder Client Library
- Derek Smithson
- Derek Smithson
- http://www.apache.org/licenses/LICENSE-2.0.html
- https://github.com/dsmithson/SpyderClientLibrary
- false
- $description$
- Copyright 2020
- spyder, x20
-
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibrary/app.config b/src/SpyderClientLibrary/app.config
deleted file mode 100644
index 0f5a3c0..0000000
--- a/src/SpyderClientLibrary/app.config
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/SpyderClientLibrary/nuget.exe b/src/SpyderClientLibrary/nuget.exe
deleted file mode 100644
index 9f8781d..0000000
Binary files a/src/SpyderClientLibrary/nuget.exe and /dev/null differ
diff --git a/src/SpyderClientLibrary/packages.config b/src/SpyderClientLibrary/packages.config
deleted file mode 100644
index 35314f1..0000000
--- a/src/SpyderClientLibrary/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/SpyderClientLibraryTests/Common/SystemDataTestBase.cs b/src/SpyderClientLibraryTests/Common/SystemDataTestBase.cs
index c241606..334b759 100644
--- a/src/SpyderClientLibraryTests/Common/SystemDataTestBase.cs
+++ b/src/SpyderClientLibraryTests/Common/SystemDataTestBase.cs
@@ -104,8 +104,8 @@ public void LoadRegisterListTest()
foreach (RegisterType type in Enum.GetValues(typeof(RegisterType)))
{
var registers = ParseRegisterList(GetTestSystemConfigFile(), type);
- Assert.IsNotNull(registers, "Failed to parse {0} register list", type);
- Assert.AreNotEqual(0, registers.Count, "No registers were parsed from {0} list", type);
+ Assert.IsNotNull(registers, $"Failed to parse {type} register list");
+ Assert.AreNotEqual(0, registers.Count, $"No registers were parsed from {type} list");
}
}
}
diff --git a/src/SpyderClientLibraryTests/Drawing/BitmapHelperTests.cs b/src/SpyderClientLibraryTests/Drawing/BitmapHelperTests.cs
index 7258e69..141b682 100644
--- a/src/SpyderClientLibraryTests/Drawing/BitmapHelperTests.cs
+++ b/src/SpyderClientLibraryTests/Drawing/BitmapHelperTests.cs
@@ -35,9 +35,9 @@ public void GenerateSolidColorBitmapTest()
for (int x = 0; x < imageWidth; x++)
{
var pixel = bitmap.GetPixel(x, y);
- Assert.AreEqual(color.R, pixel.R, "R value was incorrect at location {0}, {1}", x, y);
- Assert.AreEqual(color.G, pixel.G, "G value was incorrect at location {0}, {1}", x, y);
- Assert.AreEqual(color.B, pixel.B, "B value was incorrect at location {0}, {1}", x, y);
+ Assert.AreEqual(color.R, pixel.R, $"R value was incorrect at location {x}, {y}");
+ Assert.AreEqual(color.G, pixel.G, $"G value was incorrect at location {x}, {y}");
+ Assert.AreEqual(color.B, pixel.B, $"B value was incorrect at location {x}, {y}");
}
}
}
diff --git a/src/SpyderClientLibraryTests/Images/QFTThumbnailManagerTests.cs b/src/SpyderClientLibraryTests/Images/QFTThumbnailManagerTests.cs
index 37dcc63..9faa985 100644
--- a/src/SpyderClientLibraryTests/Images/QFTThumbnailManagerTests.cs
+++ b/src/SpyderClientLibraryTests/Images/QFTThumbnailManagerTests.cs
@@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using Vista.QFT;
namespace Spyder.Client.Images
{
@@ -14,10 +16,8 @@ public class QFTThumbnailManagerTests
private static string remoteImagePath;
private static string localImagePath;
- private static Vista.QFT.QFTServer qftServer;
- private static Spyder.Client.Net.QFTClient qftClient;
+ private static QFTServer qftServer;
- private Dictionary imageProcessedEvents;
private MockQFTThumbnailManager thumbnailManager;
[ClassInitialize]
@@ -35,33 +35,19 @@ public static void ClassInitialize(TestContext context)
UnitTestHelper.CreateStillImage(imageFile);
}
+ qftServer = new QFTServer(remoteImagePath);
+ qftServer.Startup();
+
//Initialize our local folder object that will be used for our client directory
if (!Directory.Exists(localImagePath))
Directory.CreateDirectory(localImagePath);
-
- //Initialize our QFT Server
- qftServer = new Vista.QFT.QFTServer();
- qftServer.Startup();
-
- //Initialize our QFT Client
- qftClient = new Spyder.Client.Net.QFTClient("127.0.0.1");
- Assert.IsTrue(qftClient.StartupAsync().Result, "Failed to initialize QFT Client");
}
[ClassCleanup]
public static void ClassTearDown()
{
- if (qftClient != null)
- {
- qftClient.ShutdownAsync().Wait();
- qftClient = null;
- }
-
- if (qftServer != null)
- {
- qftServer.Shutdown();
- qftServer = null;
- }
+ qftServer?.Shutdown();
+ qftServer = null;
CleanDirectories(false, localImagePath, remoteImagePath);
localImagePath = null;
@@ -71,11 +57,7 @@ public static void ClassTearDown()
[TestInitialize]
public void TestSetup()
{
- //Reset image process event awaiters
- imageProcessedEvents = new Dictionary();
-
thumbnailManager = new MockQFTThumbnailManager(localImagePath, (serverIP) => Task.FromResult(remoteImagePath));
- thumbnailManager.ProcessImageStreamRequested += thumbnailManager_ProcessImageStreamRequested;
Assert.IsTrue(thumbnailManager.StartupAsync().Result, "Failed to initialize thumbnail manager");
}
@@ -84,27 +66,14 @@ public void TestCleanup()
{
if (thumbnailManager != null)
{
- thumbnailManager.ProcessImageStreamRequested -= thumbnailManager_ProcessImageStreamRequested;
thumbnailManager.ShutdownAsync().Wait();
thumbnailManager = null;
}
- imageProcessedEvents = null;
-
//Remove any previously downloaded files from the local client directory
CleanDirectories(true, localImagePath);
}
- void thumbnailManager_ProcessImageStreamRequested(object sender, ProcessImageStreamEventArgs e)
- {
- e.Result = fileProcessedResult;
-
- //Set the manual reset event if registered to let tests know that the file has been 'processed'
- string file = e.Identifier.FileName.ToLower();
- if (imageProcessedEvents.ContainsKey(file))
- imageProcessedEvents[file].Set();
- }
-
[TestMethod]
public async Task GetImageTest()
{
@@ -121,37 +90,67 @@ public async Task GetImagesTest()
private async Task GetImagesTest(params string[] fileNames)
{
- List identifiers = fileNames.Select(f => new QFTThumbnailIdentifier("127.0.0.1", f)).ToList();
- ManualResetEvent[] resetEvents = fileNames.Select(f => GetResetEventForFile(f)).ToArray();
- var thumbnails = identifiers.Select(f => thumbnailManager.GetThumbnail(f)).ToList();
-
- //Pull the small image to start the rendering process
- foreach (var thumbnail in thumbnails)
+ //Inline function for triggering our file event listeners
+ var imageProcessedEvents = new Dictionary>();
+ void thumbnailManager_ProcessImageStreamRequested(object sender, ProcessImageStreamEventArgs e)
{
- string s = thumbnail.SmallImage;
- }
+ e.Result = fileProcessedResult;
- //Wait to be signalled that the image has been downloaded and processing has happened
- //Note: WaitHandle.WaitAll threw a notsupported exception...
- foreach (ManualResetEvent resetEvent in resetEvents)
- {
- Assert.IsTrue(resetEvent.WaitOne(1000000), "Failed to be signalled for one or more files");
+ //Set the manual reset event if registered to let tests know that the file has been 'processed'
+ string file = e.Identifier.FileName.ToLower();
+ if (imageProcessedEvents.ContainsKey(file))
+ imageProcessedEvents[file].TrySetResult(true);
}
- //Ensure our file was created in the cache folder
- foreach (string fileName in fileNames)
- {
- string localFile = Path.Combine(localImagePath, "127.0.0.1", "Images", fileName);
- string remoteFile = Path.Combine(remoteImagePath, fileName);
- Assert.IsTrue(File.Exists(localFile), "Failed to create local file: " + fileName);
- UnitTestHelper.CompareFilesAssert(remoteFile, localFile);
+ //Inline function to add waiter task
+ Task GetTaskAwaiterForFile(string fileName)
+ {
+ string lower = fileName.ToLower();
+ if (!imageProcessedEvents.ContainsKey(lower))
+ imageProcessedEvents.Add(lower, new TaskCompletionSource());
+
+ return imageProcessedEvents[lower].Task;
}
- //Wait a second for the results to be set
- await Task.Delay(1000);
- foreach (var thumbnail in thumbnails)
+ try
+ {
+ thumbnailManager.ProcessImageStreamRequested += thumbnailManager_ProcessImageStreamRequested;
+
+ List identifiers = [.. fileNames.Select(f => new QFTThumbnailIdentifier("127.0.0.1", f))];
+ if (identifiers.Count == 0)
+ Assert.Inconclusive("Failed to get any files for testing");
+
+ var tasks = fileNames.Select(f => GetTaskAwaiterForFile(f)).ToArray();
+ var thumbnails = identifiers.Select(f => thumbnailManager.GetThumbnail(f)).ToList();
+
+ //Pull the small image to start the rendering process
+ foreach (var thumbnail in thumbnails)
+ {
+ string s = thumbnail.SmallImage;
+ }
+
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+ await Task.WhenAll(tasks).WaitAsync(cts.Token);
+
+ //Ensure our file was created in the cache folder
+ foreach (string fileName in fileNames)
+ {
+ string localFile = Path.Combine(localImagePath, "127.0.0.1", "Images", fileName);
+ string remoteFile = Path.Combine(remoteImagePath, fileName);
+ Assert.IsTrue(File.Exists(localFile), "Failed to create local file: " + fileName);
+ UnitTestHelper.CompareFilesAssert(remoteFile, localFile);
+ }
+
+ //Wait a second for the results to be set
+ await Task.Delay(1000);
+ foreach (var thumbnail in thumbnails)
+ {
+ Assert.AreEqual(fileProcessedResult, thumbnail.SmallImage, "Resulting image failed to be set on our thumbnail");
+ }
+ }
+ finally
{
- Assert.AreEqual(fileProcessedResult, thumbnail.SmallImage, "Resulting image failed to be set on our thumbnail");
+ thumbnailManager.ProcessImageStreamRequested -= thumbnailManager_ProcessImageStreamRequested;
}
}
@@ -166,14 +165,5 @@ private static void CleanDirectories(bool ensureCreated, params string[] directo
Directory.CreateDirectory(directory);
}
}
-
- private ManualResetEvent GetResetEventForFile(string fileName)
- {
- string lower = fileName.ToLower();
- if (!imageProcessedEvents.ContainsKey(lower))
- imageProcessedEvents.Add(lower, new ManualResetEvent(false));
-
- return imageProcessedEvents[lower];
- }
}
}
diff --git a/src/SpyderClientLibraryTests/Images/ThumbnailImageBaseTests.cs b/src/SpyderClientLibraryTests/Images/ThumbnailImageBaseTests.cs
index a6c7c0f..7e7ec9f 100644
--- a/src/SpyderClientLibraryTests/Images/ThumbnailImageBaseTests.cs
+++ b/src/SpyderClientLibraryTests/Images/ThumbnailImageBaseTests.cs
@@ -80,7 +80,6 @@ private void GetImageTest(ImageSize size, Func getPr
const string mockImageData = "Image File Here";
image.SetImage(size, mockImageData);
Assert.AreEqual(mockImageData, getPropertyValue(image), "Failed to get expected image back after setting it");
- Assert.IsNotNull(thumbnailChangeNotified, "No property change was fired when setting the image");
Assert.IsTrue(thumbnailChangeNotified, "Failed to notify property changed for thumbnail image");
Assert.IsTrue(isLoadingNotifiedTrue, "Failed to notify that the image was loading");
Assert.IsTrue(isLoadingNotifiedFalse, "Failed to notify that the image completed loading");
diff --git a/src/SpyderClientLibraryTests/Net/QFTTests.cs b/src/SpyderClientLibraryTests/Net/QFTTests.cs
index 2d83dbc..0511c7a 100644
--- a/src/SpyderClientLibraryTests/Net/QFTTests.cs
+++ b/src/SpyderClientLibraryTests/Net/QFTTests.cs
@@ -221,7 +221,7 @@ public async Task GetDirectories()
Assert.AreEqual(expected.Length, actual.Length, "Directory count was unexpected");
for (int i = 0; i < actual.Length; i++)
- Assert.AreEqual(expected[i], actual[i], "Directory at index {0} did not match", i);
+ Assert.AreEqual(expected[i], actual[i], $"Directory at index {i} did not match");
}
finally
{
@@ -249,7 +249,7 @@ public async Task GetFiles()
Assert.AreEqual(expected.Length, actual.Length, "File count was unexpected");
for (int i = 0; i < actual.Length; i++)
- Assert.AreEqual(expected[i], actual[i], "File at index {0} did not match", i);
+ Assert.AreEqual(expected[i], actual[i], $"File at index {i} did not match");
}
finally
{
@@ -321,7 +321,7 @@ public async Task GetRemoteTimeOffset()
{
const int maxSkewInMs = 250;
TimeSpan offset = await client.GetRemoteTimeOffset();
- Assert.IsTrue(Math.Abs(offset.TotalMilliseconds) < maxSkewInMs, "Time offset was too large. Offset was {0}", offset);
+ Assert.IsTrue(Math.Abs(offset.TotalMilliseconds) < maxSkewInMs, $"Time offset was too large. Offset was {offset}");
}
[TestMethod()]
diff --git a/src/SpyderClientLibraryTests/Net/SpyderClientTests.cs b/src/SpyderClientLibraryTests/Net/SpyderClientTests.cs
index e93b174..531a6c0 100644
--- a/src/SpyderClientLibraryTests/Net/SpyderClientTests.cs
+++ b/src/SpyderClientLibraryTests/Net/SpyderClientTests.cs
@@ -3,6 +3,7 @@
namespace Spyder.Client.Net
{
+ [TestClass]
public class SpyderClientTests : SpyderClientTestBase
{
public SpyderClientTests()
diff --git a/src/SpyderClientLibraryTests/Net/TestUdpServer.cs b/src/SpyderClientLibraryTests/Net/TestUdpServer.cs
index f69fc52..58e6b7a 100644
--- a/src/SpyderClientLibraryTests/Net/TestUdpServer.cs
+++ b/src/SpyderClientLibraryTests/Net/TestUdpServer.cs
@@ -6,7 +6,6 @@
using System.Net.Sockets;
using System.Reflection;
using System.Text;
-using Vista.SystemManager;
namespace Spyder.Client.Net
{
@@ -35,35 +34,22 @@ public TestUdpResponse(params object[] responseParts)
this.Result = ServerOperationResultCode.Success;
this.ResponseRaw = string.Join(" ", responseParts?.Select(part => part.ToString().Replace(" ", "%20")).ToList());
}
+
+ public TestUdpResponse(string responseRaw)
+ {
+ this.Result = ServerOperationResultCode.Success;
+ this.ResponseRaw = responseRaw;
+ }
}
public class TestUdpServer : IDisposable
{
private Socket udpServer;
- private static SystemMgr sys;
- private static StringCommandProcessor stringProcessor;
-
- public SystemMgr Sys { get { return sys; } }
public Func ProcessCommand { get; set; }
public TestUdpServer()
{
- //Load a test configuration
- LoadManifestFile("Spyder.Client.Resources.TestConfigs.Version4.SystemSettings.xml", @"c:\spyder\SystemSettings.xml");
- LoadManifestFile("Spyder.Client.Resources.TestConfigs.Version4.SystemConfiguration.xml", @"c:\spyder\SystemConfiguration.xml");
- LoadManifestFile("Spyder.Client.Resources.TestConfigs.Version4.Scripts.xml", @"c:\spyder\scripts\scripts.xml");
-
- //Load a systemMgr to process requests
- sys = new SystemMgr(@"c:\spyder\systemsettings.xml");
- sys.SystemDataFileName = @"c:\spyder\SystemConfiguration.xml";
- sys.Post();
- sys.AppInit();
-
- // udp interface
- stringProcessor = new StringCommandProcessor();
- stringProcessor.Startup(sys);
-
udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpServer.Bind(new IPEndPoint(IPAddress.Loopback, SpyderUdpClient.ServerPort));
BeginListening(new byte[1400]);
@@ -89,18 +75,6 @@ public void Dispose()
udpServer.Dispose();
udpServer = null;
}
-
- if (stringProcessor != null)
- {
- stringProcessor.Shutdown();
- stringProcessor = null;
- }
-
- if (sys != null)
- {
- sys.Shutdown();
- sys = null;
- }
}
private void BeginListening(byte[] buffer)
@@ -145,8 +119,7 @@ private void OnMessageReceived(IAsyncResult ar)
//Process command and get a response
if (ProcessCommand == null)
{
- //Use internal parser
- stringProcessor.ProcessCommand(fullCommand, out fullResponse);
+ throw new NotImplementedException("No handler was provided for processing the UDP message");
}
else
{
diff --git a/src/SpyderClientLibraryTests/Properties/AssemblyInfo.cs b/src/SpyderClientLibraryTests/Properties/AssemblyInfo.cs
deleted file mode 100644
index 6c92a32..0000000
--- a/src/SpyderClientLibraryTests/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("SpyderClientSharedLibraryDesktopTests")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SpyderClientSharedLibraryDesktopTests")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("2ea2499a-c579-4bbc-a185-dec1c0d4e130")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/SpyderClientLibraryTests/ReflectionTests.cs b/src/SpyderClientLibraryTests/ReflectionTests.cs
index d9acc04..81682bf 100644
--- a/src/SpyderClientLibraryTests/ReflectionTests.cs
+++ b/src/SpyderClientLibraryTests/ReflectionTests.cs
@@ -53,12 +53,12 @@ public void PropertyChangedTest()
propertiesChanged.Clear();
object existingValue = propertyInfo.GetValue(instance);
propertyInfo.SetValue(instance, existingValue);
- Assert.AreEqual(0, propertiesChanged.Count, "PropertyChanged should not have been fired when setting same value, but {0} was fired", propertiesChanged.FirstOrDefault());
+ Assert.IsEmpty(propertiesChanged, $"PropertyChanged should not have been fired when setting same value, but '{string.Join(", ", propertiesChanged)}' was fired");
object newValue = UnitTestHelper.GetNewValue(propertyInfo.PropertyType, existingValue);
propertyInfo.SetValue(instance, newValue);
- Assert.AreNotEqual(0, propertiesChanged.Count, "PropertyChanged was not raised for {0}", propertyInfo.Name);
- Assert.IsTrue(propertiesChanged.Contains(propertyInfo.Name), "PropertyChanged event raised was unexpected - ", propertiesChanged[0]);
+ Assert.AreNotEqual(0, propertiesChanged.Count, $"PropertyChanged was not raised for '{propertyInfo.Name}'");
+ Assert.Contains(propertyInfo.Name, propertiesChanged, $"Expected PropertyChanged event '{propertyInfo.Name}' raised but observed: {string.Join(", ", propertiesChanged)}");
}
}
}
diff --git a/src/SpyderClientLibraryTests/Resources/Vista.dll b/src/SpyderClientLibraryTests/Resources/Vista.dll
deleted file mode 100644
index a6f0535..0000000
Binary files a/src/SpyderClientLibraryTests/Resources/Vista.dll and /dev/null differ
diff --git a/src/SpyderClientLibraryTests/Resources/Xceed.Compression.dll b/src/SpyderClientLibraryTests/Resources/Xceed.Compression.dll
deleted file mode 100644
index c937c21..0000000
Binary files a/src/SpyderClientLibraryTests/Resources/Xceed.Compression.dll and /dev/null differ
diff --git a/src/SpyderClientLibraryTests/Resources/Xceed.FileSystem.dll b/src/SpyderClientLibraryTests/Resources/Xceed.FileSystem.dll
deleted file mode 100644
index a3222d8..0000000
Binary files a/src/SpyderClientLibraryTests/Resources/Xceed.FileSystem.dll and /dev/null differ
diff --git a/src/SpyderClientLibraryTests/Resources/Xceed.Zip.dll b/src/SpyderClientLibraryTests/Resources/Xceed.Zip.dll
deleted file mode 100644
index ca6006d..0000000
Binary files a/src/SpyderClientLibraryTests/Resources/Xceed.Zip.dll and /dev/null differ
diff --git a/src/SpyderClientLibraryTests/SpyderClientLibraryTests.csproj b/src/SpyderClientLibraryTests/SpyderClientLibraryTests.csproj
index 7ff55d2..7bf7948 100644
--- a/src/SpyderClientLibraryTests/SpyderClientLibraryTests.csproj
+++ b/src/SpyderClientLibraryTests/SpyderClientLibraryTests.csproj
@@ -1,225 +1,66 @@
-
-
-
- Debug
- AnyCPU
- {86E8F979-1B27-433D-B443-8B881EC87E1F}
- Library
- Properties
- Spyder.Client
- SpyderClientSharedLibraryDesktopTests
- v4.6.1
- 512
- {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 10.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
- $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
- False
- UnitTest
- SAK
- SAK
- SAK
- SAK
-
-
-
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- x86
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
- true
- bin\x86\Debug\
- DEBUG;TRACE
- full
- x86
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\x86\Release\
- TRACE
- true
- pdbonly
- x86
- prompt
- MinimumRecommendedRules.ruleset
-
-
- true
- bin\x64\Debug\
- DEBUG;TRACE
- full
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\x64\Release\
- TRACE
- true
- pdbonly
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
+
+
+
+ net10.0-windows
+ Spyder.Client
+ SpyderClientSharedLibraryDesktopTests
+ false
+ true
+ enable
+ disable
+
+
-
- ..\packages\KnightwareCore.3.1.0\lib\netstandard2.0\KnightwareCore.dll
-
-
-
-
-
-
- ..\..\..\SpyderClient\src\packages\System.Threading.Tasks.Dataflow.6.0.0-rc.2.21480.5\lib\net461\System.Threading.Tasks.Dataflow.dll
-
-
-
-
-
- False
- Resources\Vista.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {fb0fc802-4fc5-4446-b2dd-48201c738dd1}
- SpyderClientLibrary.WPF
-
-
- {2de05b6e-23bf-440a-921d-79043cf14599}
- SpyderClientLibrary
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
-
-
- False
-
-
- False
-
-
- False
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SpyderClientLibraryTests/Text/HexUtilTests.cs b/src/SpyderClientLibraryTests/Text/HexUtilTests.cs
index e610825..c8b56cf 100644
--- a/src/SpyderClientLibraryTests/Text/HexUtilTests.cs
+++ b/src/SpyderClientLibraryTests/Text/HexUtilTests.cs
@@ -32,7 +32,7 @@ public void GetBytesTest()
Assert.AreEqual(expected.Item2.Length, actual.Length, "Length of byte[] response was incorrect");
for (int i = 0; i < actual.Length; i++)
{
- Assert.AreEqual(expected.Item2[i], actual[i], "Byte value was incorrect at offset {0}", i);
+ Assert.AreEqual(expected.Item2[i], actual[i], $"Byte value was incorrect at offset {i}");
}
}
diff --git a/src/SpyderClientLibraryTests/UnitTestHelper.cs b/src/SpyderClientLibraryTests/UnitTestHelper.cs
index 0b76e27..046d922 100644
--- a/src/SpyderClientLibraryTests/UnitTestHelper.cs
+++ b/src/SpyderClientLibraryTests/UnitTestHelper.cs
@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Spyder.Client.Common;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -67,8 +68,8 @@ public static void TestForPropertyChanged(INotifyPropertyChanged testObject)
object expectedValue = GetNewValue(property.PropertyType, previousValue);
property.SetValue(testObject, expectedValue, null);
- Assert.IsTrue(propertyNamesChanged.Count > 0, "{0} did not fire a PropertyChanged event for property: {1}", testType, property.Name);
- Assert.IsTrue(propertyNamesChanged.Contains(property.Name), "Name of property changed was not correct on {0} property: {1}", testType, property.Name);
+ Assert.IsTrue(propertyNamesChanged.Count > 0, $"{{testType}} did not fire a PropertyChanged event for property: {{property.Name}}");
+ Assert.IsTrue(propertyNamesChanged.Contains(property.Name), $"Name of property changed was not correct on {{testType}} property: {{property.Name}}");
}
}
@@ -87,7 +88,7 @@ public static void TestPropertyGetSet(object testObject)
property.SetValue(testObject, expectedValue, null);
object actualValue = property.GetValue(testObject, null);
- Assert.AreEqual(expectedValue, actualValue, "Value retrieved didn't match set value for object type {0}, property: {1}", testType, property.Name);
+ Assert.AreEqual(expectedValue, actualValue, $"Value retrieved didn't match set value for object type {{testType}}, property: {{property.Name}}");
}
}
@@ -162,15 +163,15 @@ public static object GetNewValue(Type valueType, object currentValue)
(color.G == 255 ? 0 : color.G + 1),
(color.B == 255 ? 0 : color.B + 1));
}
- else if (valueType == typeof(System.Windows.Media.Color))
- {
- var color = (System.Windows.Media.Color)currentValue;
- return System.Windows.Media.Color.FromArgb(
- (byte)(color.A == 255 ? 0 : color.A + 1),
- (byte)(color.R == 255 ? 0 : color.R + 1),
- (byte)(color.G == 255 ? 0 : color.G + 1),
- (byte)(color.B == 255 ? 0 : color.B + 1));
- }
+ //else if (valueType == typeof(System.Windows.Media.Color))
+ //{
+ // var color = (System.Windows.Media.Color)currentValue;
+ // return System.Windows.Media.Color.FromArgb(
+ // (byte)(color.A == 255 ? 0 : color.A + 1),
+ // (byte)(color.R == 255 ? 0 : color.R + 1),
+ // (byte)(color.G == 255 ? 0 : color.G + 1),
+ // (byte)(color.B == 255 ? 0 : color.B + 1));
+ //}
else if (valueType == typeof(Knightware.Primitives.Color))
{
var color = (Knightware.Primitives.Color)currentValue;
@@ -189,15 +190,15 @@ public static object GetNewValue(Type valueType, object currentValue)
(rect.Width == int.MaxValue ? 1 : rect.Width + 1),
(rect.Height == int.MaxValue ? 1 : rect.Height + 1));
}
- else if (valueType == typeof(System.Windows.Rect))
- {
- var rect = (System.Windows.Rect)currentValue;
- return new System.Windows.Rect(
- (rect.X == double.MaxValue || double.IsInfinity(rect.X) ? 0 : rect.X + 1),
- (rect.Y == double.MaxValue || double.IsInfinity(rect.Y) ? 0 : rect.Y + 1),
- (rect.Width == double.MaxValue || double.IsInfinity(rect.Width) ? 0 : rect.Width + 1),
- (rect.Height == double.MaxValue || double.IsInfinity(rect.Height) ? 0 : rect.Height + 1));
- }
+ //else if (valueType == typeof(System.Windows.Rect))
+ //{
+ // var rect = (System.Windows.Rect)currentValue;
+ // return new System.Windows.Rect(
+ // (rect.X == double.MaxValue || double.IsInfinity(rect.X) ? 0 : rect.X + 1),
+ // (rect.Y == double.MaxValue || double.IsInfinity(rect.Y) ? 0 : rect.Y + 1),
+ // (rect.Width == double.MaxValue || double.IsInfinity(rect.Width) ? 0 : rect.Width + 1),
+ // (rect.Height == double.MaxValue || double.IsInfinity(rect.Height) ? 0 : rect.Height + 1));
+ //}
else if (valueType == typeof(Knightware.Primitives.Rectangle))
{
var rect = (Knightware.Primitives.Rectangle)currentValue;
@@ -207,13 +208,13 @@ public static object GetNewValue(Type valueType, object currentValue)
(rect.Width == int.MaxValue ? 1 : rect.Width + 1),
(rect.Height == int.MaxValue ? 1 : rect.Height + 1));
}
- else if (valueType == typeof(System.Windows.Size))
- {
- var size = (System.Windows.Size)currentValue;
- return new System.Windows.Size(
- (size.Width == double.MaxValue ? 1 : size.Width + 1),
- (size.Height == double.MaxValue ? 1 : size.Height + 1));
- }
+ //else if (valueType == typeof(System.Windows.Size))
+ //{
+ // var size = (System.Windows.Size)currentValue;
+ // return new System.Windows.Size(
+ // (size.Width == double.MaxValue ? 1 : size.Width + 1),
+ // (size.Height == double.MaxValue ? 1 : size.Height + 1));
+ //}
else if (valueType == typeof(Knightware.Primitives.Size))
{
var size = (Knightware.Primitives.Size)currentValue;
@@ -228,13 +229,13 @@ public static object GetNewValue(Type valueType, object currentValue)
((point.X == int.MaxValue) ? 0 : point.X + 1),
((point.Y == int.MaxValue) ? 0 : point.Y + 1));
}
- else if (valueType == typeof(System.Windows.Point))
- {
- var point = (System.Windows.Point)currentValue;
- return new System.Windows.Point(
- (point.X == double.MaxValue || double.IsInfinity(point.X) ? 0 : point.X + 1),
- (point.Y == double.MaxValue || double.IsInfinity(point.Y) ? 0 : point.Y + 1));
- }
+ //else if (valueType == typeof(System.Windows.Point))
+ //{
+ // var point = (System.Windows.Point)currentValue;
+ // return new System.Windows.Point(
+ // (point.X == double.MaxValue || double.IsInfinity(point.X) ? 0 : point.X + 1),
+ // (point.Y == double.MaxValue || double.IsInfinity(point.Y) ? 0 : point.Y + 1));
+ //}
else if (valueType == typeof(Knightware.Primitives.Point))
{
var point = (Knightware.Primitives.Point)currentValue;
@@ -306,15 +307,15 @@ public static object GetNewValue(Type valueType, object currentValue)
else
return typeof(string);
}
- else if (valueType == typeof(System.Windows.Point))
- {
- var currentPoint = (System.Windows.Point)currentValue;
- return new System.Windows.Point()
- {
- X = currentPoint.X + 1,
- Y = currentPoint.Y = 1
- };
- }
+ //else if (valueType == typeof(System.Windows.Point))
+ //{
+ // var currentPoint = (System.Windows.Point)currentValue;
+ // return new System.Windows.Point()
+ // {
+ // X = currentPoint.X + 1,
+ // Y = currentPoint.Y = 1
+ // };
+ //}
else if (valueType == typeof(IPAddress))
{
IPAddress currentAddress = currentValue as IPAddress;
@@ -335,6 +336,14 @@ public static object GetNewValue(Type valueType, object currentValue)
{
return currentValue == null ? true : (bool?)null;
}
+ else if(valueType == typeof(TimeCode))
+ {
+ TimeCode currentTimeCode = currentValue as TimeCode;
+ if (currentTimeCode == null)
+ return new TimeCode();
+ else
+ return new TimeCode(currentTimeCode.FieldRate, currentTimeCode.Frames + 1);
+ }
else if (valueType.IsInterface)
{
//Look for an implementation of this interface with a default constructor, and then randomize and return it
@@ -399,7 +408,7 @@ public static object GetNewValue(Type valueType, object currentValue)
{
//Need to provide one or more constructor parameters to instantiate this object
var constructorArgs = new List