diff --git a/DemoPlugin/.gitignore b/DemoPlugin/.gitignore index 783bc57..74ae05a 100644 --- a/DemoPlugin/.gitignore +++ b/DemoPlugin/.gitignore @@ -1,14 +1,21 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files +*.rsuser *.suo +*.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -16,42 +23,62 @@ [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c -# DNX +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core project.lock.json project.fragment.lock.json artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -61,7 +88,9 @@ artifacts/ *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -89,6 +118,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -100,15 +132,25 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + # NCrunch _NCrunch_* .*crunch*.local.xml @@ -140,9 +182,9 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -#*.pubxml +*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to @@ -152,13 +194,15 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore -**/packages/* +**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/packages/build/ +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -175,12 +219,15 @@ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt +*.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -191,9 +238,12 @@ ClientBin/ *.jfm *.pfx *.publishsettings -node_modules/ orleans.codegen.cs +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -208,15 +258,22 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf *.ldf +*.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -226,6 +283,7 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat +node_modules/ # Visual Studio 6 build log *.plg @@ -233,6 +291,20 @@ FakesAssemblies/ # Visual Studio 6 workspace options file *.opt +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -248,63 +320,81 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ +# CodeRush personal settings +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs -# -# ANGULAR gitignore begin section -# +# OpenCover UI analysis results +OpenCover/ -# See http://help.github.com/ignore-files/ for more about ignoring files. +# Azure Stream Analytics local run output +ASALocalRun/ -# compiled output -/dist -/tmp -/out-tsc +# MSBuild Binary and Structured Log +*.binlog -# dependencies -/node_modules +# NVidia Nsight GPU debugger configuration file +*.nvuser -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace +# MFractors (Xamarin productivity tool) working folder +.mfractor/ -# IDE - VSCode +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json +*.code-workspace -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -testem.log -/typings +# Local History for Visual Studio Code +.history/ -# e2e -/e2e/*.js -/e2e/*.map +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp -# System Files -*.xcuserstate -*.xcactivitylog +# JetBrains Rider +*.sln.iml -*.DotSettings +.DS_Store \ No newline at end of file diff --git a/DemoPlugin/.vscode/launch.json b/DemoPlugin/.vscode/launch.json new file mode 100644 index 0000000..f67e0ab --- /dev/null +++ b/DemoPlugin/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Logi Plugin Service", + "type": "coreclr", + "request": "attach", + "windows": { + "processName": "LogiPluginService.exe" + }, + "osx": { + "processName": "LogiPluginService" + }, + "justMyCode": true + } + ] +} diff --git a/DemoPlugin/.vscode/tasks.json b/DemoPlugin/.vscode/tasks.json new file mode 100644 index 0000000..9d9ae1b --- /dev/null +++ b/DemoPlugin/.vscode/tasks.json @@ -0,0 +1,40 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Debug)", + "type": "shell", + "command": "dotnet build -c Debug", + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release)", + "type": "shell", + "command": "dotnet build -c Release", + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Package Plugin", + "type": "shell", + "command": "logiplugintool pack ./bin/Release ./Demo.lplug4", + "group": "none", + "problemMatcher": [] + }, + { + "label": "Install Plugin", + "type": "shell", + "command": "logiplugintool install ./Demo.lplug4", + "group": "none", + "problemMatcher": [] + }, + { + "label": "Uninstall Plugin", + "type": "shell", + "command": "logiplugintool uninstall Demo", + "group": "none", + "problemMatcher": [] + } + ] +} diff --git a/DemoPlugin/DemoPlugin.sln b/DemoPlugin/DemoPlugin.sln index 6bd8cd4..c782f2e 100644 --- a/DemoPlugin/DemoPlugin.sln +++ b/DemoPlugin/DemoPlugin.sln @@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34408.163 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoPlugin", "DemoPlugin\DemoPlugin.csproj", "{58C04AC4-DFCD-49F9-9BF3-E8240592F3BF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoPlugin", "src\DemoPlugin.csproj", "{A4AA1C9B-F4DA-4B58-A5ED-A95E06D70199}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD08438B-1C4A-4FFA-A856-D5285D570BB8}" + ProjectSection(SolutionItems) = preProject + .vscode\launch.json = .vscode\launch.json + .vscode\tasks.json = .vscode\tasks.json + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +17,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {58C04AC4-DFCD-49F9-9BF3-E8240592F3BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {58C04AC4-DFCD-49F9-9BF3-E8240592F3BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {58C04AC4-DFCD-49F9-9BF3-E8240592F3BF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {58C04AC4-DFCD-49F9-9BF3-E8240592F3BF}.Release|Any CPU.Build.0 = Release|Any CPU + {A4AA1C9B-F4DA-4B58-A5ED-A95E06D70199}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4AA1C9B-F4DA-4B58-A5ED-A95E06D70199}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4AA1C9B-F4DA-4B58-A5ED-A95E06D70199}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4AA1C9B-F4DA-4B58-A5ED-A95E06D70199}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {F627919D-59F2-470B-BFAB-8D932B3514F8} + SolutionGuid = {1C04A10A-A4D3-4205-94BE-47CB0006A47F} EndGlobalSection EndGlobal diff --git a/DemoPlugin/DemoPlugin/ButtonSwitchesCommand.cs b/DemoPlugin/DemoPlugin/ButtonSwitchesCommand.cs deleted file mode 100644 index cfc1f7b..0000000 --- a/DemoPlugin/DemoPlugin/ButtonSwitchesCommand.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Loupedeck.DemoPlugin -{ - using System; - - public class ButtonSwitchesCommand : PluginDynamicCommand - { - private const int NumberOfSwitches = 4; - private readonly Boolean[] _switches = new Boolean[NumberOfSwitches]; - - public ButtonSwitchesCommand() : base() - { - for (var i = 0; i < NumberOfSwitches; i++) - { - // Parameter is the switch index - var actionParameter = i.ToString(); - - // Add parameter - this.AddParameter(actionParameter, $"Switch {i}", "Switches"); - } - } - - protected override void RunCommand(String actionParameter) - { - if (Int32.TryParse(actionParameter, out var i)) - { - // Turn the switch - this._switches[i] = !this._switches[i]; - - // Inform Loupedeck that command display name and/or image has changed - this.ActionImageChanged(actionParameter); - } - } - - protected override String GetCommandDisplayName(String actionParameter, PluginImageSize imageSize) - { - if (Int32.TryParse(actionParameter, out var i)) - { - return $"Switch {i}: {this._switches[i]}"; - } - else - { - return null; - } - } - } -} diff --git a/DemoPlugin/DemoPlugin/CounterAdjustment.cs b/DemoPlugin/DemoPlugin/CounterAdjustment.cs deleted file mode 100644 index 20fbd93..0000000 --- a/DemoPlugin/DemoPlugin/CounterAdjustment.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Loupedeck.DemoPlugin -{ - using System; - - // This class implements an example adjustment that counts the rotation ticks of a dial. - - public class CounterAdjustment : PluginDynamicAdjustment - { - // This variable holds the current value of the counter. - private Int32 _counter = 0; - - // Initializes the adjustment class. - // When `hasReset` is set to true, a reset command is automatically created for this adjustment. - public CounterAdjustment() - : base(displayName: "Counter", description: "Counts rotation ticks", groupName: "Adjustments", hasReset: true) - { - } - - // This method is called when the dial associated to the plugin is rotated. - protected override void ApplyAdjustment(String actionParameter, Int32 diff) - { - this._counter += diff; // Increase or decrease the counter by the number of ticks. - this.AdjustmentValueChanged(); // Notify the Loupedeck service that the adjustment value has changed. - PluginLog.Info($"Counter value was changed by {diff} ticks"); - } - - // This method is called when the reset command related to the adjustment is executed. - protected override void RunCommand(String actionParameter) - { - this._counter = 0; // Reset the counter. - this.AdjustmentValueChanged(); // Notify the Loupedeck service that the adjustment value has changed. - PluginLog.Info("Counter was reset"); - } - - // Returns the adjustment value that is shown next to the dial. - protected override String GetAdjustmentValue(String actionParameter) => this._counter.ToString(); - } -} diff --git a/DemoPlugin/DemoPlugin/DemoPlugin.csproj.user b/DemoPlugin/DemoPlugin/DemoPlugin.csproj.user deleted file mode 100644 index c997f50..0000000 --- a/DemoPlugin/DemoPlugin/DemoPlugin.csproj.user +++ /dev/null @@ -1,11 +0,0 @@ - - - - Program - C:\Program Files\Logi\LogiPluginService\LogiPluginService.exe - - - Program - /Applications/Utilities/LogiPluginService.app - - \ No newline at end of file diff --git a/DemoPlugin/DemoPlugin/ThumbUpDownCommand.cs b/DemoPlugin/DemoPlugin/ThumbUpDownCommand.cs deleted file mode 100644 index 2aea404..0000000 --- a/DemoPlugin/DemoPlugin/ThumbUpDownCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Loupedeck.DemoPlugin -{ - using System; - - public class ThumbUpDownCommand : PluginDynamicCommand - { - private Boolean _isThumbDown = false; - - private readonly String _imageResourcePathThumbUp; - private readonly String _imageResourcePathThumbDown; - - public ThumbUpDownCommand() : base(displayName: "Thumb up/down", description: null, groupName: "Switches") - { - this._imageResourcePathThumbUp = PluginResources.FindFile("ThumbUp.png"); - this._imageResourcePathThumbDown = PluginResources.FindFile("ThumbDown.png"); - } - - protected override void RunCommand(String actionParameter) - { - this._isThumbDown = !this._isThumbDown; - this.ActionImageChanged(); - } - - protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) - { - var resourcePath = this._isThumbDown ? this._imageResourcePathThumbDown : this._imageResourcePathThumbUp; - return PluginResources.ReadImage(resourcePath); - } - } -} diff --git a/DemoPlugin/DemoPlugin/ToggleMuteCommand.cs b/DemoPlugin/DemoPlugin/ToggleMuteCommand.cs deleted file mode 100644 index a96e858..0000000 --- a/DemoPlugin/DemoPlugin/ToggleMuteCommand.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Loupedeck.DemoPlugin -{ - using System; - - public class ToggleMuteCommand : PluginDynamicCommand - { - public ToggleMuteCommand() - : base(displayName: "Toggle Mute", description: "Mutes and unmutes system volume", groupName: "Audio") - { - } - - protected override void RunCommand(String actionParameter) - { - this.Plugin.ClientApplication.SendKeyboardShortcut(VirtualKeyCode.VolumeMute); - } - } -} diff --git a/DemoPlugin/DemoPlugin/package/icontemplates/Loupedeck.DemoPlugin.ToggleMuteCommand.ict b/DemoPlugin/DemoPlugin/package/icontemplates/Loupedeck.DemoPlugin.ToggleMuteCommand.ict deleted file mode 100644 index 0a43af6..0000000 --- a/DemoPlugin/DemoPlugin/package/icontemplates/Loupedeck.DemoPlugin.ToggleMuteCommand.ict +++ /dev/null @@ -1,37 +0,0 @@ -{ - "backgroundColor": 4278190080, - "items": [ - { - "$type": "Loupedeck.Service.ActionIconImageItem, LoupedeckShared", - "image": "iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAABwwAAAcMBOnufsAAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAe5JREFUeF7t2rFNA0EQhWEHFEIhBJRCCZRARoioAHLojgKOHYmVVqvf5glm70bWG+lL/l07mMA+LE7bttk/YDQdRtNhNB1G02E0HUbTYTQdRtNhNB1G02E0HUbTYTQdRtNhNB1G02E0HUbTYTQdRtNhNB1G02E0HUbTYTQdxkLeoZWCsYinJobOysBYwEPTh87LwHiwcXkxdKcMjAealxdD98rAeBBaXgzdLQPjAc4tL4bul4FxZ5eWF0OvKQNjstvm/ozH5reh97xrnqc2irO4Q2epMCbrz3N/nfn9YjFfTcxLM59Hi4k7y5eIMVn2At+accYl9uX1ibvja9NhTJa9wJvmoxknFjcvL+7E3fn1qTAmy15goCWOs8vyAsZkKxYYzi1xt+UFjMlWLTC8NvNEo7tLYEy2aoHzZ9444xfLUhiTrVjgvLzPH+PsskSMybIXGA/J4/TPPPpMvPSwnQJjsuwFjg/S8xfGuMSreZBe9adcPCTTt220OFu+vIBxZ/4xIYF/zkrgH1QT0BLpXhkYDzYvke6UgbGAcYl0XgbGIvrzI52VgbEQ/2vHtcNoOoymw2g6jKbDaDqMpsNoOoymw2g6jKbDaDqMpsNoOoymw2g6jKbDaDqMpsNoOoymw2g6jKbDaDqMpsNoqu30DbZ472o00s59AAAAAElFTkSuQmCC", - "imageFileName": "ToggleMute.png", - "imageColor": 4294967295, - "imageRotation": "None", - "isVisible": true, - "itemType": "Image", - "area": { - "x": 0, - "y": -10, - "width": 100, - "height": 100, - "isFullScreen": true - } - }, - { - "$type": "Loupedeck.Service.ActionIconTextItem, LoupedeckShared", - "text": "Toggle Mute", - "textColor": 4294967295, - "fontSize": 5, - "fontName": "Brown Logitech Pan Light", - "isVisible": true, - "itemType": "Text", - "area": { - "x": 0, - "y": 70, - "width": 100, - "height": 16, - "isFullScreen": false - } - } - ] -} \ No newline at end of file diff --git a/DemoPlugin/DemoPlugin/package/metadata/DefaultIconTemplate.ict b/DemoPlugin/DemoPlugin/package/metadata/DefaultIconTemplate.ict deleted file mode 100644 index cb606e1..0000000 --- a/DemoPlugin/DemoPlugin/package/metadata/DefaultIconTemplate.ict +++ /dev/null @@ -1,33 +0,0 @@ -{ - "backgroundColor": 4278869247, - "items": [ - { - "image": "", - "imageFileName": null, - "imageColor": 4294967295, - "imageRotation": "None", - "isVisible": true, - "itemType": "Image", - "area": { - "x": 15, - "y": 0, - "width": 70, - "height": 70 - } - }, - { - "text": "", - "textColor": 4294967295, - "fontSize": 5, - "fontName": "Brown Logitech Pan Light", - "isVisible": true, - "itemType": "Text", - "area": { - "x": 0, - "y": 70, - "width": 100, - "height": 30 - } - } - ] -} \ No newline at end of file diff --git a/DemoPlugin/DemoPlugin/package/metadata/LoupedeckPackage.yaml b/DemoPlugin/DemoPlugin/package/metadata/LoupedeckPackage.yaml deleted file mode 100644 index 36bfc9a..0000000 --- a/DemoPlugin/DemoPlugin/package/metadata/LoupedeckPackage.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# ================================================================================================== -# GENERAL SETTINGS -# ================================================================================================== - -# Package type. Must be plugin4 for plugins. -type: plugin4 - -# Name that identifies the plugin. -name: Demo - -# Plugin name that is shown to the user. -displayName: Demo - -# Short description of the plugin. -description: This is a demo plugin - -# Name of the plugin library file. -pluginFileName: DemoPlugin.dll - -# Plugin version. -version: 1.0 - -# Author of the plugin. The author can be a company or an individual developer. -author: Logitech Europe SA - -# Copyright information. -copyright: Copyright © 2025 Logitech Europe SA. All rights reserved. - -# Minimum SDK version supported by the plugin. -minimumLoupedeckVersion: 6.0 - - -# ================================================================================================== -# PLUGIN PROPERTIES -# ================================================================================================== - -# Location of plugin files on Windows (relative to the plugin base directory). -# This parameter is required to support Windows. -pluginFolderWin: bin - -# Location of plugin files on macOS (relative to the plugin base directory). -# This parameter is required to support Mac. -pluginFolderMac: bin - -# List of devices the plugin is optimized for. -supportedDevices: - # LoupedeckCtFamily covers the following devices: Loupedeck CT, Live and Live S, - # Razer Stream Controller, and Razer Stream Controller X. - - LoupedeckCtFamily - - # Logitech MX Creative Console family devices. - - LoupedeckExtendedFamily - diff --git a/DemoPlugin/README.md b/DemoPlugin/README.md new file mode 100644 index 0000000..1181174 --- /dev/null +++ b/DemoPlugin/README.md @@ -0,0 +1,73 @@ +# Welcome to Logi Actions SDK + +Here you can find an introduction to Logi Actions SDK, example plugin source code and the SDK documentation. + +## Documentation + +- [Logi Actions SDK Developer Docs](https://logitech.github.io/actions-sdk-docs/) +- [Logi Developer Discord](https://discord.gg/ptV2BfHCmm) + +--- + +# DemoPlugin + +A Logitech Actions SDK plugin built with C# (.NET 8) that works on both macOS and Windows. This project is intended as a learning resource for new developers joining the team — each file is structured and commented to help you understand how the SDK works. + +## Folder Structure + +``` +DemoPlugin/ +├── .vscode/ # VS Code setup — debugger config to attach to the Logi Plugin Service +│ # and build tasks for quick builds, packaging and installing +├── src/ +│ ├── Actions/ # Where all commands and adjustments live. +│ # Each file is one feature assigned to a button or dial +│ ├── Helpers/ # Shared utility classes used across all actions. +│ # PluginLog for logging, PluginResources for loading images +│ ├── images/ # PNG icons embedded into the plugin DLL. +│ # These are displayed on device buttons at runtime +│ └── package/ # Plugin identity files read by the Logi Plugin Service +│ # on startup — name, version, icon and supported devices +└── bin/ # Auto-generated build output. Never edit this folder manually. +``` + +## Actions + +| Action | Type | Description | +|---|---|---| +| Toggle Mute | Button | Mutes and unmutes system volume on macOS and Windows | +| Button Switches | Button | 4 independent on/off toggle switches | +| Thumb Up/Down | Button | Toggles between thumb up and thumb down image | +| Counter | Dial | Counts rotation ticks, press dial to reset | + +## Build + +```bash +# Debug — used during development and debugging +dotnet build -c Debug + +# Release — used for packaging and distribution +dotnet build -c Release +``` + +## Debug + +1. Open the project in VS Code +2. Run `dotnet build -c Debug` +3. Go to **Run > Start Debugging** (or press `F5`) +4. Select **Attach to Logi Plugin Service** + +You can now set breakpoints in any action file and step through your code live. + +## Package & Install + +```bash +# Package the plugin into a distributable .lplug4 file +logiplugintool pack ./bin/Release ./Demo.lplug4 + +# Install the plugin on this machine +logiplugintool install ./Demo.lplug4 + +# Uninstall the plugin +logiplugintool uninstall Demo +``` diff --git a/DemoPlugin/.editorconfig b/DemoPlugin/src/.editorconfig similarity index 86% rename from DemoPlugin/.editorconfig rename to DemoPlugin/src/.editorconfig index cc8b97a..8ec3771 100644 --- a/DemoPlugin/.editorconfig +++ b/DemoPlugin/src/.editorconfig @@ -199,8 +199,44 @@ dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_diagnostic.CA1028.severity = none dotnet_diagnostic.CA1031.severity = none -dotnet_diagnostic.CA1303.severity = none +dotnet_diagnostic.CA1054.severity = none +dotnet_diagnostic.CA1056.severity = none dotnet_diagnostic.CA1060.severity = none +dotnet_diagnostic.CA1303.severity = none +dotnet_diagnostic.CA1308.severity = none +dotnet_diagnostic.CA1716.severity = none +dotnet_diagnostic.CA1720.severity = none dotnet_diagnostic.CA2101.severity = none +dotnet_diagnostic.CA2234.severity = none +dotnet_diagnostic.CA5350.severity = none +dotnet_diagnostic.CA9998.severity = none + +dotnet_diagnostic.CS1591.severity = none +dotnet_diagnostic.IDE0002.severity = none +dotnet_diagnostic.IDE0021.severity = warning +dotnet_diagnostic.IDE0022.severity = warning dotnet_diagnostic.IDE0058.severity = none +dotnet_diagnostic.IDE0059.severity = none + +dotnet_diagnostic.SA1108.severity = none +dotnet_diagnostic.SA1117.severity = none +dotnet_diagnostic.SA1121.severity = none +dotnet_diagnostic.SA1122.severity = none +dotnet_diagnostic.SA1131.severity = none +dotnet_diagnostic.SA1201.severity = none +dotnet_diagnostic.SA1202.severity = none +dotnet_diagnostic.SA1203.severity = none +dotnet_diagnostic.SA1204.severity = none +dotnet_diagnostic.SA1214.severity = none +dotnet_diagnostic.SA1309.severity = none +dotnet_diagnostic.SA1512.severity = none +dotnet_diagnostic.SA1513.severity = none +dotnet_diagnostic.SA1600.severity = none +dotnet_diagnostic.SA1601.severity = none +dotnet_diagnostic.SA1602.severity = none +dotnet_diagnostic.SA1629.severity = none +dotnet_diagnostic.SA1633.severity = none + +dotnet_diagnostic.SX1309.severity = warning +dotnet_diagnostic.SX1309S.severity = warning diff --git a/DemoPlugin/src/Actions/ButtonSwitchesCommand.cs b/DemoPlugin/src/Actions/ButtonSwitchesCommand.cs new file mode 100644 index 0000000..e715a8c --- /dev/null +++ b/DemoPlugin/src/Actions/ButtonSwitchesCommand.cs @@ -0,0 +1,54 @@ +namespace Loupedeck.DemoPlugin +{ + using System; + + // This command creates multiple toggle switches assigned to buttons. + // Each switch independently tracks its own on/off state. + + public class ButtonSwitchesCommand : PluginDynamicCommand + { + // Total number of switches available. + private const Int32 NumberOfSwitches = 4; + + // Array to store the on/off state of each switch. + private readonly Boolean[] _switches = new Boolean[NumberOfSwitches]; + + // Initializes the command and adds a parameter for each switch. + public ButtonSwitchesCommand() : base() + { + for (var i = 0; i < NumberOfSwitches; i++) + { + // Each switch uses its index as the action parameter. + var actionParameter = i.ToString(); + + // Register each switch as a separate button in the "Switches" group. + this.AddParameter(actionParameter, $"Switch {i}", "Switches"); + } + } + + // This method is called when the user presses a switch button. + protected override void RunCommand(String actionParameter) + { + if (Int32.TryParse(actionParameter, out var i)) + { + // Toggle the switch state. + this._switches[i] = !this._switches[i]; + + // Notify the Plugin Service to refresh the button label. + this.ActionImageChanged(actionParameter); + } + } + + // Returns the button label showing the switch index and its current state. + protected override String GetCommandDisplayName(String actionParameter, PluginImageSize imageSize) + { + if (Int32.TryParse(actionParameter, out var i)) + { + // Show "Switch 0: On" or "Switch 0: Off" based on state. + return $"Switch {i}: {(this._switches[i] ? "On" : "Off")}"; + } + + return null; + } + } +} diff --git a/DemoPlugin/src/Actions/CounterAdjustment.cs b/DemoPlugin/src/Actions/CounterAdjustment.cs new file mode 100644 index 0000000..f36b9c5 --- /dev/null +++ b/DemoPlugin/src/Actions/CounterAdjustment.cs @@ -0,0 +1,49 @@ +namespace Loupedeck.DemoPlugin +{ + using System; + + // This adjustment counts the rotation ticks of a dial/encoder. + // Rotating right increases the counter, rotating left decreases it. + // Pressing the dial resets the counter to zero. + + public class CounterAdjustment : PluginDynamicAdjustment + { + // Holds the current value of the counter. + private Int32 _counter = 0; + + // Initializes the adjustment. + // hasReset: true automatically creates a reset button for this adjustment. + public CounterAdjustment() + : base(displayName: "Counter", description: "Counts rotation ticks of the dial", groupName: "Adjustments", hasReset: true) + { + } + + // This method is called when the dial is rotated. + // diff is positive when rotating right, negative when rotating left. + protected override void ApplyAdjustment(String actionParameter, Int32 diff) + { + // Increase or decrease the counter by the number of ticks. + this._counter += diff; + + // Notify the Plugin Service that the value has changed. + this.AdjustmentValueChanged(); + + PluginLog.Info($"Counter changed by {diff}, new value: {this._counter}"); + } + + // This method is called when the reset button is pressed. + protected override void RunCommand(String actionParameter) + { + // Reset the counter back to zero. + this._counter = 0; + + // Notify the Plugin Service that the value has changed. + this.AdjustmentValueChanged(); + + PluginLog.Info("Counter reset to 0"); + } + + // Returns the current counter value shown next to the dial. + protected override String GetAdjustmentValue(String actionParameter) => this._counter.ToString(); + } +} diff --git a/DemoPlugin/src/Actions/ThumbUpDownCommand.cs b/DemoPlugin/src/Actions/ThumbUpDownCommand.cs new file mode 100644 index 0000000..51c8129 --- /dev/null +++ b/DemoPlugin/src/Actions/ThumbUpDownCommand.cs @@ -0,0 +1,44 @@ +namespace Loupedeck.DemoPlugin +{ + using System; + + // This command toggles between a Thumb Up and Thumb Down image on the button. + // It demonstrates how to use embedded image resources for button icons. + + public class ThumbUpDownCommand : PluginDynamicCommand + { + // Tracks whether the current state is thumb down or thumb up. + private Boolean _isThumbDown = false; + + // Resource paths for the thumb up and thumb down images. + private readonly String _imageResourcePathThumbUp; + private readonly String _imageResourcePathThumbDown; + + // Initializes the command and loads the embedded image resource paths. + public ThumbUpDownCommand() + : base(displayName: "Thumb Up/Down", description: "Toggles between thumb up and thumb down", groupName: "Switches") + { + // Find the embedded image resources by filename. + this._imageResourcePathThumbUp = PluginResources.FindFile("ThumbUp.png"); + this._imageResourcePathThumbDown = PluginResources.FindFile("ThumbDown.png"); + } + + // This method is called when the user presses the button. + protected override void RunCommand(String actionParameter) + { + // Toggle the thumb state. + this._isThumbDown = !this._isThumbDown; + + // Notify the Plugin Service to refresh the button image. + this.ActionImageChanged(); + } + + // Returns the correct image based on the current thumb state. + protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) + { + // Show ThumbDown image when down, ThumbUp image when up. + var resourcePath = this._isThumbDown ? this._imageResourcePathThumbDown : this._imageResourcePathThumbUp; + return PluginResources.ReadImage(resourcePath); + } + } +} diff --git a/DemoPlugin/src/Actions/ToggleMuteCommand.cs b/DemoPlugin/src/Actions/ToggleMuteCommand.cs new file mode 100644 index 0000000..704fcc7 --- /dev/null +++ b/DemoPlugin/src/Actions/ToggleMuteCommand.cs @@ -0,0 +1,47 @@ +namespace Loupedeck.DemoPlugin +{ + using System; + using System.Runtime.InteropServices; + + // This command toggles the system mute on both macOS and Windows. + // It is a universal command (no application linked) so it works globally. + + public class ToggleMuteCommand : PluginDynamicCommand + { + // Tracks the current mute state to update the button text accordingly. + private Boolean _isMuted = false; + + // Initializes the command with display name, description, and group. + public ToggleMuteCommand() + : base(displayName: "Toggle Mute", description: "Mutes and unmutes system volume", groupName: "Audio") + { + } + + // This method is called when the user presses the button assigned to this command. + protected override void RunCommand(String actionParameter) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // On macOS, use osascript to toggle the mute state. + // Reads the current mute state and flips it. + System.Diagnostics.Process.Start("osascript", "-e \"set volume output muted not (output muted of (get volume settings))\""); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // On Windows, use PowerShell to send the VolumeMute key (keycode 173). + System.Diagnostics.Process.Start("powershell", "-c \"(New-Object -ComObject WScript.Shell).SendKeys([char]173)\""); + } + + // Flip the mute state and refresh the button label. + this._isMuted = !this._isMuted; + this.ActionImageChanged(); + } + + // Returns the button label based on the current mute state. + protected override String GetCommandDisplayName(String actionParameter, PluginImageSize imageSize) + { + // Show "MUTED" when muted, "UNMUTED" when sound is on. + return this._isMuted ? "Mute" : "Unmute"; + } + } +} diff --git a/DemoPlugin/DemoPlugin/DemoApplication.cs b/DemoPlugin/src/DemoApplication.cs similarity index 53% rename from DemoPlugin/DemoPlugin/DemoApplication.cs rename to DemoPlugin/src/DemoApplication.cs index b73d462..377cd8b 100644 --- a/DemoPlugin/DemoPlugin/DemoApplication.cs +++ b/DemoPlugin/src/DemoApplication.cs @@ -2,6 +2,8 @@ namespace Loupedeck.DemoPlugin { using System; + // This class can be used to connect the Loupedeck plugin to an application. + public class DemoApplication : ClientApplication { public DemoApplication() @@ -9,9 +11,12 @@ public DemoApplication() } // This method can be used to link the plugin to a Windows application. - protected override String GetProcessName() => "DemoApplication"; + protected override String GetProcessName() => ""; // This method can be used to link the plugin to a macOS application. protected override String GetBundleName() => ""; + + // This method can be used to check whether the application is installed or not. + public override ClientApplicationStatus GetApplicationStatus() => ClientApplicationStatus.Unknown; } } diff --git a/DemoPlugin/DemoPlugin/DemoPlugin.cs b/DemoPlugin/src/DemoPlugin.cs similarity index 92% rename from DemoPlugin/DemoPlugin/DemoPlugin.cs rename to DemoPlugin/src/DemoPlugin.cs index 3582542..4b9e14b 100644 --- a/DemoPlugin/DemoPlugin/DemoPlugin.cs +++ b/DemoPlugin/src/DemoPlugin.cs @@ -2,6 +2,8 @@ namespace Loupedeck.DemoPlugin { using System; + // This class contains the plugin-level logic of the Loupedeck plugin. + public class DemoPlugin : Plugin { // Gets a value indicating whether this is an API-only plugin. diff --git a/DemoPlugin/DemoPlugin/DemoPlugin.csproj b/DemoPlugin/src/DemoPlugin.csproj similarity index 64% rename from DemoPlugin/DemoPlugin/DemoPlugin.csproj rename to DemoPlugin/src/DemoPlugin.csproj index 4cdaddb..3bd0672 100644 --- a/DemoPlugin/DemoPlugin/DemoPlugin.csproj +++ b/DemoPlugin/src/DemoPlugin.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -18,17 +18,20 @@ $(LocalAppData)\Logi\LogiPluginService\Plugins\ ~/Library/Application\ Support/Logi/LogiPluginService/Plugins/ + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\bin\')) $(BaseOutputPath)$(Configuration)\bin\ + + Demo - + - + @@ -38,22 +41,34 @@ + + - + - + + + + + + + + + + + - \ No newline at end of file + diff --git a/DemoPlugin/Directory.Build.props b/DemoPlugin/src/Directory.Build.props similarity index 74% rename from DemoPlugin/Directory.Build.props rename to DemoPlugin/src/Directory.Build.props index 287f0b1..fccb8a7 100644 --- a/DemoPlugin/Directory.Build.props +++ b/DemoPlugin/src/Directory.Build.props @@ -2,6 +2,6 @@ latest $(SolutionDir)obj\ - $(SolutionDir)bin\ + $(SolutionDir)..\bin\ diff --git a/DemoPlugin/DemoPlugin/PluginLog.cs b/DemoPlugin/src/Helpers/PluginLog.cs similarity index 94% rename from DemoPlugin/DemoPlugin/PluginLog.cs rename to DemoPlugin/src/Helpers/PluginLog.cs index b10542d..760eb8a 100644 --- a/DemoPlugin/DemoPlugin/PluginLog.cs +++ b/DemoPlugin/src/Helpers/PluginLog.cs @@ -2,7 +2,7 @@ namespace Loupedeck.DemoPlugin { using System; - // A helper class to write to the plugin log. + // A helper class that enables logging from the plugin code. internal static class PluginLog { diff --git a/DemoPlugin/DemoPlugin/PluginResources.cs b/DemoPlugin/src/Helpers/PluginResources.cs similarity index 100% rename from DemoPlugin/DemoPlugin/PluginResources.cs rename to DemoPlugin/src/Helpers/PluginResources.cs diff --git a/DemoPlugin/DemoPlugin/images/ThumbDown.png b/DemoPlugin/src/images/ThumbDown.png similarity index 100% rename from DemoPlugin/DemoPlugin/images/ThumbDown.png rename to DemoPlugin/src/images/ThumbDown.png diff --git a/DemoPlugin/DemoPlugin/images/ThumbUp.png b/DemoPlugin/src/images/ThumbUp.png similarity index 100% rename from DemoPlugin/DemoPlugin/images/ThumbUp.png rename to DemoPlugin/src/images/ThumbUp.png diff --git a/DemoPlugin/src/images/VolumeOff.png b/DemoPlugin/src/images/VolumeOff.png new file mode 100644 index 0000000..5a6f0b3 Binary files /dev/null and b/DemoPlugin/src/images/VolumeOff.png differ diff --git a/DemoPlugin/src/images/VolumeOn.png b/DemoPlugin/src/images/VolumeOn.png new file mode 100644 index 0000000..11766a6 Binary files /dev/null and b/DemoPlugin/src/images/VolumeOn.png differ diff --git a/DemoPlugin/DemoPlugin/package/metadata/Icon256x256.png b/DemoPlugin/src/package/metadata/Icon256x256.png similarity index 100% rename from DemoPlugin/DemoPlugin/package/metadata/Icon256x256.png rename to DemoPlugin/src/package/metadata/Icon256x256.png diff --git a/DemoPlugin/src/package/metadata/LoupedeckPackage.yaml b/DemoPlugin/src/package/metadata/LoupedeckPackage.yaml new file mode 100644 index 0000000..d171daa --- /dev/null +++ b/DemoPlugin/src/package/metadata/LoupedeckPackage.yaml @@ -0,0 +1,80 @@ +# ================================================================================================== +# GENERAL SETTINGS +# ================================================================================================== + +# Package type. Must be plugin4 for plugins. +type: plugin4 + +# Name that identifies the plugin. +name: Demo + +# Plugin name that is shown to the user. +displayName: Demo + +# Short description of the plugin. +description: Add a short description of the plugin here. + +# Name of the plugin library file. +pluginFileName: DemoPlugin.dll + +# Plugin version. +version: 1.0 + +# Author of the plugin. The author can be a company or an individual developer. +author: nirmal + +# Copyright information. +copyright: Copyright © 2026 nirmal. All rights reserved. + + +# ================================================================================================== +# PLUGIN PROPERTIES +# ================================================================================================== + +# Location of plugin files on Windows (relative to the plugin base directory). +# This parameter is required to support Windows. +pluginFolderWin: bin + +# Location of plugin files on macOS (relative to the plugin base directory). +# This parameter is required to support Mac. +pluginFolderMac: bin + +# List of devices the plugin is optimized for. +supportedDevices: + # LoupedeckCtFamily covers the following devices: Loupedeck CT, Live and Live S, + # Razer Stream Controller, and Razer Stream Controller X. + - LoupedeckCtFamily + + # LoupedeckPlusFamily covers Loupedeck+ device. Uncomment the following line to support Loupedeck+. + #- LoupedeckPlusFamily + +# List of plugin capabilities. +pluginCapabilities: + # Uncomment the following line if this plugin is an application plugin. + #- HasApplication + + # Uncomment the following line if the plugin sends keyboard shortcuts to the target application. + #- ActivatesApplication + +# Minimum Loupedeck version supported by the plugin. +minimumLoupedeckVersion: 6.0 + + +# ================================================================================================== +# LOUPEDECK MARKETPLACE SETTINGS +# ================================================================================================== + +# Name of the license that the plugin is licensed under. Select the one that you prefer. +# NOTE: GPL license is not compatible with Loupedeck Marketplace. +license: MIT + +# URL of the plugin license. +licenseUrl: https://opensource.org/licenses/MIT + +# URL of the support page where the users can send improvement suggestions and report bugs. +# The URL is shown in Loupedeck Marketplace. The page can be for example a GitHub issues page. +# NOTE: This setting is recommended when publishing the plugin in Loupedeck Marketplace. +#supportPageUrl: https://github.com/myusername/myplugin/issues + +# URL of the plugin homepage. The URL is shown in Loupedeck Marketplace. +#homePageUrl: https://github.com/myusername/myplugin diff --git a/README.md b/README.md index f35178d..1181174 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,73 @@ # Welcome to Logi Actions SDK -Here you can find an introduction to Logi Actions SDK, example plugin source code and the SDK documentation. +Here you can find an introduction to Logi Actions SDK, example plugin source code and the SDK documentation. ## Documentation -* [Logi Actions SDK Developer Docs](https://logitech.github.io/actions-sdk-docs) +- [Logi Actions SDK Developer Docs](https://logitech.github.io/actions-sdk-docs/) +- [Logi Developer Discord](https://discord.gg/ptV2BfHCmm) -## Developer Discord +--- -* [Logi Developer Discord](https://discord.gg/etJCPZytHg) +# DemoPlugin +A Logitech Actions SDK plugin built with C# (.NET 8) that works on both macOS and Windows. This project is intended as a learning resource for new developers joining the team — each file is structured and commented to help you understand how the SDK works. + +## Folder Structure + +``` +DemoPlugin/ +├── .vscode/ # VS Code setup — debugger config to attach to the Logi Plugin Service +│ # and build tasks for quick builds, packaging and installing +├── src/ +│ ├── Actions/ # Where all commands and adjustments live. +│ # Each file is one feature assigned to a button or dial +│ ├── Helpers/ # Shared utility classes used across all actions. +│ # PluginLog for logging, PluginResources for loading images +│ ├── images/ # PNG icons embedded into the plugin DLL. +│ # These are displayed on device buttons at runtime +│ └── package/ # Plugin identity files read by the Logi Plugin Service +│ # on startup — name, version, icon and supported devices +└── bin/ # Auto-generated build output. Never edit this folder manually. +``` + +## Actions + +| Action | Type | Description | +|---|---|---| +| Toggle Mute | Button | Mutes and unmutes system volume on macOS and Windows | +| Button Switches | Button | 4 independent on/off toggle switches | +| Thumb Up/Down | Button | Toggles between thumb up and thumb down image | +| Counter | Dial | Counts rotation ticks, press dial to reset | + +## Build + +```bash +# Debug — used during development and debugging +dotnet build -c Debug + +# Release — used for packaging and distribution +dotnet build -c Release +``` + +## Debug + +1. Open the project in VS Code +2. Run `dotnet build -c Debug` +3. Go to **Run > Start Debugging** (or press `F5`) +4. Select **Attach to Logi Plugin Service** + +You can now set breakpoints in any action file and step through your code live. + +## Package & Install + +```bash +# Package the plugin into a distributable .lplug4 file +logiplugintool pack ./bin/Release ./Demo.lplug4 + +# Install the plugin on this machine +logiplugintool install ./Demo.lplug4 + +# Uninstall the plugin +logiplugintool uninstall Demo +```