1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge pull request #348 from DarthAffe/development

Added Timer-Based ActiveWindow-detection
This commit is contained in:
Robert Beekman 2017-04-23 14:12:52 +02:00 committed by GitHub
commit 954f4864d8
13 changed files with 238 additions and 41 deletions

View File

@ -671,7 +671,11 @@
<Compile Include="Settings\OffsetSettings.cs" />
<Compile Include="Styles\DropTargetAdorners\DropTargetMetroHighlightAdorner.cs" />
<Compile Include="Styles\DropTargetAdorners\DropTargetMetroInsertionAdorner.cs" />
<Compile Include="Utilities\ActiveWindowHelper.cs" />
<Compile Include="Utilities\ActiveWindowDetection\ActiveWindowDetectionType.cs" />
<Compile Include="Utilities\ActiveWindowDetection\ActiveWindowHelper.cs" />
<Compile Include="Utilities\ActiveWindowDetection\EventActiveWindowDetector.cs" />
<Compile Include="Utilities\ActiveWindowDetection\IActiveWindowDetector.cs" />
<Compile Include="Utilities\ActiveWindowDetection\TimerActiveWindowDetector.cs" />
<Compile Include="Utilities\ColorHelpers.cs" />
<Compile Include="Utilities\Converters\JsonConverters.cs" />
<Compile Include="Utilities\Converters\NinjectCustomConverter.cs" />

View File

@ -10,6 +10,7 @@ using Artemis.DAL;
using Artemis.InjectionModules;
using Artemis.Settings;
using Artemis.Utilities;
using Artemis.Utilities.ActiveWindowDetection;
using Artemis.Utilities.Converters;
using Artemis.Utilities.DataReaders;
using Artemis.ViewModels;
@ -113,7 +114,7 @@ namespace Artemis
//TODO DarthAffe 17.12.2016: Is this the right location for this?
//TODO Move to Mainmanager and make disposable
ActiveWindowHelper.Initialize();
ActiveWindowHelper.SetActiveWindowDetectionType(SettingsProvider.Load<GeneralSettings>().ActiveWindowDetection);
}
protected override void OnExit(object sender, EventArgs e)

View File

@ -5,6 +5,7 @@ using Artemis.DAL;
using Artemis.Modules.Abstract;
using Artemis.Settings;
using Artemis.Utilities;
using Artemis.Utilities.ActiveWindowDetection;
using Ninject.Extensions.Logging;
namespace Artemis.Managers

View File

@ -11,6 +11,7 @@ using Artemis.Events;
using Artemis.Managers;
using Artemis.Modules.Abstract;
using Artemis.Utilities;
using Artemis.Utilities.ActiveWindowDetection;
using CSCore.CoreAudioAPI;
using Newtonsoft.Json;
using SpotifyAPI.Local;

View File

@ -5,9 +5,11 @@ using System.Windows;
using Artemis.DAL;
using Artemis.Profiles.Layers.Types.AmbientLight.ScreenCapturing;
using Artemis.Utilities;
using Artemis.Utilities.ActiveWindowDetection;
using Caliburn.Micro;
using MahApps.Metro;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Squirrel;
namespace Artemis.Settings
@ -68,6 +70,11 @@ namespace Artemis.Settings
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public string LogLevel { get; set; }
[DefaultValue(ActiveWindowDetectionType.Events)]
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public ActiveWindowDetectionType ActiveWindowDetection { get; set; }
public Version LastRanVersion { get; set; }
public void Save()
@ -75,6 +82,7 @@ namespace Artemis.Settings
SettingsProvider.Save(this);
Logging.SetupLogging(LogLevel);
ActiveWindowHelper.SetActiveWindowDetectionType(ActiveWindowDetection);
ApplyAutorun();
ApplyTheme();
ApplyGamestatePort();

View File

@ -0,0 +1,9 @@
namespace Artemis.Utilities.ActiveWindowDetection
{
public enum ActiveWindowDetectionType
{
Disabled = -1,
Events = 0,
Timer = 1,
}
}

View File

@ -0,0 +1,48 @@
namespace Artemis.Utilities.ActiveWindowDetection
{
public static class ActiveWindowHelper
{
#region Properties & Fields
private static ActiveWindowDetectionType _currentDetectionType = ActiveWindowDetectionType.Disabled;
private static IActiveWindowDetector _activeWindowDetector;
public static string ActiveWindowProcessName => _activeWindowDetector?.ActiveWindowProcessName ?? string.Empty;
public static string ActiveWindowWindowTitle => _activeWindowDetector?.ActiveWindowWindowTitle ?? string.Empty;
public static bool MainWindowActive => ActiveWindowProcessName.Contains("Artemis");
#endregion
#region Methods
public static void Dispose()
{
_activeWindowDetector?.Dispose();
}
public static void SetActiveWindowDetectionType(ActiveWindowDetectionType detectionType)
{
if (detectionType == _currentDetectionType) return;
_activeWindowDetector?.Dispose();
switch (detectionType)
{
case ActiveWindowDetectionType.Events:
_activeWindowDetector = new EventActiveWindowDetector();
break;
case ActiveWindowDetectionType.Timer:
_activeWindowDetector = new TimerActiveWindowDetector();
break;
case ActiveWindowDetectionType.Disabled:
_activeWindowDetector = null;
break;
}
_activeWindowDetector?.Initialize();
_currentDetectionType = detectionType;
}
#endregion
}
}

View File

@ -2,9 +2,9 @@
using System.Runtime.InteropServices;
using System.Text;
namespace Artemis.Utilities
namespace Artemis.Utilities.ActiveWindowDetection
{
public static class ActiveWindowHelper
public class EventActiveWindowDetector : IActiveWindowDetector
{
#region DLL-Imports
@ -14,7 +14,7 @@ namespace Artemis.Utilities
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
@ -41,47 +41,46 @@ namespace Artemis.Utilities
#region Properties & Fields
// DarthAffe 17.12.2016: We need to keep a reference to this or it might get collected by the garbage collector and cause some random crashes afterwards.
private static WinEventDelegate _activeWindowChangedDelegate;
private static IntPtr _activeWindowEventHook;
private WinEventDelegate _activeWindowChangedDelegate;
private IntPtr _activeWindowEventHook;
private static WinEventDelegate _windowTitleChangedDelegate;
private static IntPtr _windowTitleEventHook;
private WinEventDelegate _windowTitleChangedDelegate;
private IntPtr _windowTitleEventHook;
private static WinEventDelegate _windowMinimizedChangedDelegate;
private static IntPtr _windowMinimizedEventHook;
private WinEventDelegate _windowMinimizedChangedDelegate;
private IntPtr _windowMinimizedEventHook;
private static IntPtr _activeWindow;
private IntPtr _activeWindow;
public static string ActiveWindowProcessName { get; private set; } = string.Empty;
public static string ActiveWindowWindowTitle { get; private set; } = string.Empty;
public static bool MainWindowActive => ActiveWindowProcessName.Contains("Artemis");
public string ActiveWindowProcessName { get; private set; } = string.Empty;
public string ActiveWindowWindowTitle { get; private set; } = string.Empty;
#endregion
#region Methods
private static void ActiveWindowChanged(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
private void ActiveWindowChanged(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
UpdateForWindow(hwnd);
}
private static void WindowTitleChanged(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
private void WindowTitleChanged(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (_activeWindow == hwnd)
UpdateForWindow(hwnd);
}
private static void WindowMinimizedChanged(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
private void WindowMinimizedChanged(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
// DarthAffe 19.12.2016: We expect currently un-minimized windows to be active.
// DarthAffe 19.12.2016: The result of the API-function GetActiveWindow at this moment is 'idle' so we can't use this to validate this estimation.
UpdateForWindow(hwnd);
}
private static void UpdateForWindow(IntPtr hwnd)
private void UpdateForWindow(IntPtr hwnd)
{
_activeWindow = hwnd;
@ -89,13 +88,13 @@ namespace Artemis.Utilities
ActiveWindowWindowTitle = GetActiveWindowTitle(hwnd) ?? string.Empty;
}
private static string GetActiveWindowProcessName(IntPtr hwnd)
private string GetActiveWindowProcessName(IntPtr hwnd)
{
try
{
uint pid;
GetWindowThreadProcessId(hwnd, out pid);
return System.Diagnostics.Process.GetProcessById((int) pid).ProcessName;
return System.Diagnostics.Process.GetProcessById((int)pid).ProcessName;
}
catch
{
@ -103,7 +102,7 @@ namespace Artemis.Utilities
}
}
private static string GetActiveWindowTitle(IntPtr hwnd)
private string GetActiveWindowTitle(IntPtr hwnd)
{
try
{
@ -116,7 +115,7 @@ namespace Artemis.Utilities
}
}
public static void Initialize()
public void Initialize()
{
try
{
@ -124,21 +123,21 @@ namespace Artemis.Utilities
{
_activeWindowChangedDelegate = ActiveWindowChanged;
_activeWindowEventHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND,
IntPtr.Zero, _activeWindowChangedDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
IntPtr.Zero, _activeWindowChangedDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
}
if (_windowTitleEventHook == IntPtr.Zero)
{
_windowTitleChangedDelegate = WindowTitleChanged;
_windowTitleEventHook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
IntPtr.Zero, _windowTitleChangedDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
IntPtr.Zero, _windowTitleChangedDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
}
if (_windowMinimizedEventHook == IntPtr.Zero)
{
_windowMinimizedChangedDelegate = WindowMinimizedChanged;
_windowMinimizedEventHook = SetWinEventHook(EVENT_SYSTEM_MINIMIZEEND, EVENT_SYSTEM_MINIMIZEEND,
IntPtr.Zero, _windowMinimizedChangedDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
IntPtr.Zero, _windowMinimizedChangedDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
}
}
catch
@ -147,7 +146,7 @@ namespace Artemis.Utilities
}
}
public static void Dispose()
public void Dispose()
{
try
{
@ -180,4 +179,4 @@ namespace Artemis.Utilities
#endregion
}
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Artemis.Utilities.ActiveWindowDetection
{
public interface IActiveWindowDetector : IDisposable
{
string ActiveWindowProcessName { get; }
string ActiveWindowWindowTitle { get; }
void Initialize();
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Timers;
namespace Artemis.Utilities.ActiveWindowDetection
{
public class TimerActiveWindowDetector : IActiveWindowDetector
{
#region DLL-Imports
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
#endregion
#region Constants
private const int TIMER_INTERVAL = 1000;
private const int MAX_TITLE_LENGTH = 256;
#endregion
#region Properties & Fields
private Timer _timer;
public string ActiveWindowProcessName { get; private set; }
public string ActiveWindowWindowTitle { get; private set; }
#endregion
#region Methods
private void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
IntPtr activeWindow = GetForegroundWindow();
ActiveWindowProcessName = GetActiveWindowProcessName(activeWindow);
ActiveWindowWindowTitle = GetActiveWindowTitle(activeWindow);
}
private string GetActiveWindowProcessName(IntPtr hwnd)
{
try
{
uint pid;
GetWindowThreadProcessId(hwnd, out pid);
return System.Diagnostics.Process.GetProcessById((int)pid).ProcessName;
}
catch
{
return null;
}
}
private string GetActiveWindowTitle(IntPtr hwnd)
{
try
{
StringBuilder buffer = new StringBuilder(MAX_TITLE_LENGTH);
return GetWindowText(hwnd, buffer, MAX_TITLE_LENGTH) > 0 ? buffer.ToString() : null;
}
catch
{
return null;
}
}
public void Initialize()
{
_timer = new Timer(TIMER_INTERVAL) { AutoReset = true };
_timer.Elapsed += TimerOnElapsed;
_timer.Start();
}
public void Dispose()
{
_timer?.Stop();
_timer?.Dispose();
_timer = null;
}
#endregion
}
}

View File

@ -8,6 +8,7 @@ using Artemis.Events;
using Artemis.Managers;
using Artemis.Services;
using Artemis.Settings;
using Artemis.Utilities.ActiveWindowDetection;
using Caliburn.Micro;
using MahApps.Metro.Controls;
using NLog;
@ -37,6 +38,8 @@ namespace Artemis.ViewModels.Flyouts
LogLevels = new BindableCollection<string>();
LogLevels.AddRange(LogLevel.AllLoggingLevels.Select(l => l.Name));
ActiveWindowDetections = new BindableCollection<ActiveWindowDetectionType>(Enum.GetValues(typeof(ActiveWindowDetectionType)).Cast<ActiveWindowDetectionType>());
PropertyChanged += KeyboardUpdater;
mainManager.EnabledChanged += MainManagerEnabledChanged;
mainManager.ModuleManager.EffectChanged += EffectManagerEffectChanged;
@ -96,6 +99,7 @@ namespace Artemis.ViewModels.Flyouts
public string VersionText => "Artemis " + Assembly.GetExecutingAssembly().GetName().Version;
public BindableCollection<string> LogLevels { get; set; }
public BindableCollection<ActiveWindowDetectionType> ActiveWindowDetections { get; set; }
public string SelectedTheme
{
@ -130,6 +134,17 @@ namespace Artemis.ViewModels.Flyouts
}
}
public ActiveWindowDetectionType SelectedActiveWindowDetection
{
get { return GeneralSettings.ActiveWindowDetection; }
set
{
if (value == GeneralSettings.ActiveWindowDetection) return;
GeneralSettings.ActiveWindowDetection = value;
NotifyOfPropertyChange(() => SelectedActiveWindowDetection);
}
}
public string SelectedKeyboardProvider
{
get { return _selectedKeyboardProvider; }

View File

@ -23,6 +23,7 @@ using Artemis.Properties;
using Artemis.Services;
using Artemis.Styles.DropTargetAdorners;
using Artemis.Utilities;
using Artemis.Utilities.ActiveWindowDetection;
using Caliburn.Micro;
using Castle.Components.DictionaryAdapter;
using GongSolutions.Wpf.DragDrop;

View File

@ -6,7 +6,7 @@
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="300">
d:DesignHeight="600" d:DesignWidth="310">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -25,6 +25,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
@ -94,30 +95,36 @@
<controls:NumericUpDown Grid.Row="8" Grid.Column="1" Margin="10" VerticalAlignment="Center"
HorizontalAlignment="Right" Width="140" Minimum="1" Maximum="60"
Value="{Binding Path=GeneralSettings.ScreenCaptureFPS, Mode=TwoWay}" />
<!-- Logging -->
<Label Grid.Row="9" Grid.Column="0" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Left"
Content="Log level:" />
<ComboBox Grid.Row="9" Grid.Column="1" x:Name="LogLevels" Margin="10" VerticalAlignment="Center"
Content="Active window detection" />
<ComboBox Grid.Row="9" Grid.Column="1" x:Name="ActiveWindowDetections" Margin="10" VerticalAlignment="Center"
HorizontalAlignment="Right"
Width="140" />
<Button Grid.Row="10" Grid.Column="0" Margin="10" x:Name="ShowLogs" Content="Show logs"
<!-- Logging -->
<Label Grid.Row="10" Grid.Column="0" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Left"
Content="Log level:" />
<ComboBox Grid.Row="10" Grid.Column="1" x:Name="LogLevels" Margin="10" VerticalAlignment="Center"
HorizontalAlignment="Right"
Width="140" />
<Button Grid.Row="11" Grid.Column="0" Margin="10" x:Name="ShowLogs" Content="Show logs"
VerticalAlignment="Center" Width="100" HorizontalAlignment="Left"
Style="{DynamicResource SquareButtonStyle}" />
<Button Grid.Row="10" Grid.Column="1" Margin="10" x:Name="ShowDebug" Content="Show debugger"
<Button Grid.Row="11" Grid.Column="1" Margin="10" x:Name="ShowDebug" Content="Show debugger"
VerticalAlignment="Center" Width="100" HorizontalAlignment="Right"
Style="{DynamicResource SquareButtonStyle}" />
<!-- Buttons -->
<Button Grid.Row="11" Grid.Column="0" Margin="10" x:Name="ResetSettings" Content="Reset settings"
<Button Grid.Row="12" Grid.Column="0" Margin="10" x:Name="ResetSettings" Content="Reset settings"
VerticalAlignment="Center" HorizontalAlignment="Left" Width="100"
Style="{DynamicResource SquareButtonStyle}" />
<Button Grid.Row="11" Grid.Column="1" Margin="11" x:Name="SaveSettings" Content="Save changes"
<Button Grid.Row="12" Grid.Column="1" Margin="11" x:Name="SaveSettings" Content="Save changes"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="100"
Style="{DynamicResource SquareButtonStyle}" />
<!-- Version -->
<Grid Grid.Row="12" Grid.Column="0" Grid.ColumnSpan="2" Margin="10" VerticalAlignment="Bottom">
<Grid Grid.Row="13" Grid.Column="0" Grid.ColumnSpan="2" Margin="10" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />