Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Samples/Genetec Web Player/GwpDesktopPlayerSample/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Application x:Class="Genetec.Dap.CodeSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>

</Application.Resources>
</Application>
11 changes: 11 additions & 0 deletions Samples/Genetec Web Player/GwpDesktopPlayerSample/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2025 Genetec Inc.
// Licensed under the Apache License, Version 2.0

using System.Windows;

namespace Genetec.Dap.CodeSamples;

public partial class App : Application
{
}

13 changes: 13 additions & 0 deletions Samples/Genetec Web Player/GwpDesktopPlayerSample/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2025 Genetec Inc.
// Licensed under the Apache License, Version 2.0

using System.Windows;

[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<AssemblyName>GwpDesktopPlayerSample</AssemblyName>
<RootNamespace>Genetec.Dap.CodeSamples</RootNamespace>
<Description>Sample project</Description>
<Company>Genetec Inc.</Company>
<Copyright>Copyright © Genetec Inc. 2025</Copyright>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3240.44" />
</ItemGroup>

<ItemGroup>
<Content Include="wwwroot\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
91 changes: 91 additions & 0 deletions Samples/Genetec Web Player/GwpDesktopPlayerSample/MainWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<Window x:Class="Genetec.Dap.CodeSamples.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
mc:Ignorable="d"
Title="GWP Desktop Player Sample"
Height="900"
Width="1440"
MinHeight="720"
MinWidth="1100">
<DockPanel>
<Border DockPanel.Dock="Top"
Padding="12"
Background="#0B172A"
BorderBrush="#17324F"
BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<TextBlock x:Name="StatusTextBlock"
VerticalAlignment="Center"
Text="Initializing WebView2..."
Foreground="#B7C6D8" />

<StackPanel Grid.Column="1"
Margin="20,0,16,0"
VerticalAlignment="Center">
<TextBlock Text="Security Center Authentication"
FontSize="12"
FontWeight="SemiBold"
Foreground="#F5F7FA" />
<StackPanel Margin="0,6,0,0"
Orientation="Horizontal">
<StackPanel>
<TextBlock Text="Username"
FontSize="11"
Foreground="#B7C6D8" />
<TextBox x:Name="UsernameTextBox"
Width="170"
Margin="0,4,8,0"
Padding="8,4"
TextChanged="UsernameTextBox_TextChanged" />
</StackPanel>
<StackPanel>
<TextBlock Text="Password"
FontSize="11"
Foreground="#B7C6D8" />
<PasswordBox x:Name="PasswordBox"
Width="170"
Margin="0,4,0,0"
Padding="8,4"
PasswordChanged="PasswordBox_PasswordChanged" />
</StackPanel>
</StackPanel>
<StackPanel Margin="0,8,0,0">
<TextBlock Text="SDK Certificate"
FontSize="11"
Foreground="#B7C6D8" />
<TextBox x:Name="SdkCertificateTextBox"
Width="348"
Margin="0,4,0,0"
Padding="8,4"
TextChanged="SdkCertificateTextBox_TextChanged" />
</StackPanel>
</StackPanel>

<StackPanel Grid.Column="2"
Orientation="Horizontal"
VerticalAlignment="Center">
<Button x:Name="RefreshButton"
Margin="0,0,8,0"
Padding="14,8"
Click="RefreshButton_Click"
Content="Refresh" />
<Button x:Name="DevToolsButton"
Padding="14,8"
Click="DevToolsButton_Click"
Content="DevTools" />
</StackPanel>
</Grid>
</Border>

<wv2:WebView2 x:Name="Browser" />
</DockPanel>
</Window>
157 changes: 157 additions & 0 deletions Samples/Genetec Web Player/GwpDesktopPlayerSample/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright 2025 Genetec Inc.
// Licensed under the Apache License, Version 2.0

using System;
using System.IO;
using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Web.WebView2.Core;

namespace Genetec.Dap.CodeSamples;

public partial class MainWindow : Window
{
private static readonly Uri s_appUri = new("https://app.local/index.html");

private NativePlaybackConfiguration? m_playbackConfiguration;
private TokenProvider? m_tokenProvider;

public MainWindow()
{
InitializeComponent();
#if !DEBUG
DevToolsButton.Visibility = Visibility.Collapsed;
#endif
Loaded += OnLoaded;
Closed += OnClosed;
PreviewKeyDown += OnPreviewKeyDown;
}

private async void OnLoaded(object sender, RoutedEventArgs e)
{
try
{
var webRoot = Path.Combine(AppContext.BaseDirectory, "wwwroot");
m_playbackConfiguration = NativePlaybackConfiguration.LoadFromEnvironment();
UsernameTextBox.Text = m_playbackConfiguration.Username;
PasswordBox.Password = m_playbackConfiguration.Password;
SdkCertificateTextBox.Text = m_playbackConfiguration.SdkCertificate;
m_tokenProvider = new TokenProvider(m_playbackConfiguration);

var userDataFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"GwpDesktopPlayerSample",
"WebView2Data");
var environment = await CoreWebView2Environment.CreateAsync(userDataFolder: userDataFolder);

await Browser.EnsureCoreWebView2Async(environment);
#if DEBUG
Browser.CoreWebView2.ServerCertificateErrorDetected += OnServerCertificateErrorDetected;
#endif
Browser.CoreWebView2.SetVirtualHostNameToFolderMapping(
"app.local",
webRoot,
CoreWebView2HostResourceAccessKind.Allow);
Comment thread
alafleur-genetec marked this conversation as resolved.
Browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = true;
#if DEBUG
Browser.CoreWebView2.Settings.AreDevToolsEnabled = true;
#else
Browser.CoreWebView2.Settings.AreDevToolsEnabled = false;
#endif
Browser.CoreWebView2.Settings.IsStatusBarEnabled = true;
Comment thread
alafleur-genetec marked this conversation as resolved.

var bootstrapScript = $"window.__GWP_HOST_CONFIG__ = {JsonSerializer.Serialize(new
{
mediaGatewayEndpoint = m_playbackConfiguration.MediaGatewayEndpoint,
serverVersion = m_playbackConfiguration.ServerVersion,
})};";
await Browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(bootstrapScript);

Browser.CoreWebView2.AddHostObjectToScript("tokenProvider", m_tokenProvider);
Browser.CoreWebView2.NavigationStarting += OnNavigationStarting;
Browser.CoreWebView2.NavigationCompleted += OnNavigationCompleted;
Browser.CoreWebView2.Navigate(s_appUri.ToString());
StatusTextBlock.Text =
$"WebView2 ready. Native playback configuration loaded for {m_playbackConfiguration.MediaGatewayEndpoint}.";
}
catch (Exception exception)
{
StatusTextBlock.Text = "Failed to initialize WebView2.";
MessageBox.Show(this, exception.ToString(), "WebView2 initialization failed", MessageBoxButton.OK, MessageBoxImage.Error);
}
}

private void OnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs e)
{
if (Uri.TryCreate(e.Uri, UriKind.Absolute, out var uri) && uri.Host != s_appUri.Host)
{
e.Cancel = true;
}
Comment thread
alafleur-genetec marked this conversation as resolved.
}

private void OnNavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e)
{
StatusTextBlock.Text = e.IsSuccess
? "Local GWP host page loaded. Enter a camera GUID to start playback."
: $"Navigation failed with error {e.WebErrorStatus}.";
}

private void OnClosed(object? sender, EventArgs e)
{
(m_tokenProvider as IDisposable)?.Dispose();
m_tokenProvider = null;
}

private void OnServerCertificateErrorDetected(object? sender, CoreWebView2ServerCertificateErrorDetectedEventArgs e)
{
e.Action = CoreWebView2ServerCertificateErrorAction.AlwaysAllow;

if (Uri.TryCreate(e.RequestUri, UriKind.Absolute, out var uri))
{
StatusTextBlock.Text = $"Allowed certificate warning for development endpoint {uri.Host}.";
}
}

private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
if (Browser.CoreWebView2 is null)
{
return;
}

Browser.Reload();
}

private void UsernameTextBox_TextChanged(object sender, TextChangedEventArgs e) => SyncNativeCredentials();

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) => SyncNativeCredentials();

private void SdkCertificateTextBox_TextChanged(object sender, TextChangedEventArgs e) => SyncNativeCredentials();

private void DevToolsButton_Click(object sender, RoutedEventArgs e)
{
#if DEBUG
Browser.CoreWebView2?.OpenDevToolsWindow();
#endif
}

private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
#if DEBUG
if (e.Key != Key.F12)
{
return;
}

Browser.CoreWebView2?.OpenDevToolsWindow();
e.Handled = true;
#endif
}

private void SyncNativeCredentials()
{
m_tokenProvider?.UpdateAuthentication(UsernameTextBox.Text, PasswordBox.Password, SdkCertificateTextBox.Text);
}
}
Loading