all operations pass though Pipe

This commit is contained in:
Paddy Xu
2017-08-12 18:17:04 +03:00
parent 6bae00361b
commit 7676d5867b
14 changed files with 160 additions and 174 deletions

View File

@@ -17,6 +17,7 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
@@ -36,7 +37,7 @@ namespace QuickLook.Plugin.IPreviewHandlers
public void Dispose()
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
presenter.Child = null;
presenter?.Dispose();
@@ -48,7 +49,7 @@ namespace QuickLook.Plugin.IPreviewHandlers
public void PreviewFile(string file, ContextObject context)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_control = new PreviewHandlerHost();
presenter.Child = _control;

View File

@@ -10,7 +10,7 @@
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/ScrollBarStyleDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
<FontFamily x:Key="SegoeMDL2">./Fonts/#Segoe MDL2 Assets</FontFamily>
<FontFamily x:Key="SegoeMDL2">./Fonts/#Segoe MDL2 Assets</FontFamily>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -22,7 +22,6 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using QuickLook.Helpers;
using QuickLook.Properties;
@@ -103,26 +102,23 @@ namespace QuickLook
private void RemoteCallShowPreview(StartupEventArgs e)
{
PipeServerManager.SendMessage(e.Args.First());
PipeServerManager.SendMessage(PipeMessages.Toggle, e.Args.First());
}
private void RunListener(StartupEventArgs e)
{
TrayIconManager.GetInstance();
if (!e.Args.Contains("/autorun") && !IsUWP)
TrayIconManager.GetInstance()
.ShowNotification("", TranslationHelper.GetString("APP_START"));
TrayIconManager.ShowNotification("", TranslationHelper.GetString("APP_START"));
if (e.Args.Contains("/first"))
AutoStartupHelper.CreateAutorunShortcut();
NativeMethods.QuickLook.Init();
PluginManager.GetInstance();
ViewWindowManager.GetInstance();
BackgroundListener.GetInstance();
PipeServerManager.GetInstance().MessageReceived +=
(msg, ea) => Dispatcher.BeginInvoke(
new Action(() => ViewWindowManager.GetInstance().InvokeViewer(msg as string, true)),
DispatcherPriority.ApplicationIdle);
PipeServerManager.GetInstance();
}
private void App_OnExit(object sender, ExitEventArgs e)

View File

@@ -16,8 +16,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Windows.Threading;
using QuickLook.Helpers;
namespace QuickLook
@@ -60,22 +60,50 @@ namespace QuickLook
{
_isKeyDownInDesktopOrShell = NativeMethods.QuickLook.GetFocusedWindowType() !=
NativeMethods.QuickLook.FocusedWindowType.Invalid;
_isKeyDownInDesktopOrShell |= WindowHelper.IsForegroundWindowBelongToSelf();
}
// call InvokeRoutine only when the KeyDown is valid
if (_isKeyDownInDesktopOrShell)
Dispatcher.CurrentDispatcher.BeginInvoke(
new Action<bool>(down =>
ViewWindowManager.GetInstance().InvokeRoutine(e, down)),
DispatcherPriority.ApplicationIdle,
isKeyDown);
InvokeRoutine(e.KeyCode, isKeyDown);
// reset variable only when KeyUp
if (!isKeyDown)
_isKeyDownInDesktopOrShell = false;
}
private void InvokeRoutine(Keys key, bool isKeyDown)
{
var path = NativeMethods.QuickLook.GetCurrentSelection();
Debug.WriteLine($"InvokeRoutine: key={key},down={isKeyDown}");
if (isKeyDown)
switch (key)
{
case Keys.Enter:
PipeServerManager.SendMessage(PipeMessages.RunAndClose);
break;
}
else
switch (key)
{
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
PipeServerManager.SendMessage(PipeMessages.Invoke, path);
break;
case Keys.Space:
PipeServerManager.SendMessage(PipeMessages.Toggle, path);
break;
case Keys.Escape:
PipeServerManager.SendMessage(PipeMessages.Close);
break;
}
}
private void InstallKeyHook(KeyEventHandler downHandler, KeyEventHandler upHandler)
{
_hook = GlobalKeyboardHook.GetInstance();

View File

@@ -10,7 +10,10 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<busyDecorator:SpinIcon FontFamily="{StaticResource SegoeMDL2}" FontSize="40" Spin="True">&#xE72C;</busyDecorator:SpinIcon>
<busyDecorator:SpinIcon FontFamily="{StaticResource SegoeMDL2}" FontSize="40"
Spin="True">
&#xE72C;
</busyDecorator:SpinIcon>
</ControlTemplate>
</Setter.Value>
</Setter>

View File

@@ -15,7 +15,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -23,20 +22,21 @@ namespace QuickLook
{
internal class FocusMonitor
{
public delegate void HeartbeatEventHandler(HeartbeatEventArgs e);
private static FocusMonitor _instance;
public bool IsRunning { get; private set; }
public event HeartbeatEventHandler Heartbeat;
public void Start()
{
if (IsRunning)
return;
IsRunning = true;
new Task(() =>
{
var last = string.Empty;
while (IsRunning)
{
Thread.Sleep(500);
@@ -45,8 +45,12 @@ namespace QuickLook
NativeMethods.QuickLook.FocusedWindowType.Invalid)
continue;
var file = NativeMethods.QuickLook.GetCurrentSelection();
Heartbeat?.Invoke(new HeartbeatEventArgs(DateTime.Now.Ticks, file));
var path = NativeMethods.QuickLook.GetCurrentSelection();
if (IsRunning && last != path)
{
last = path;
PipeServerManager.SendMessage(PipeMessages.Invoke, path);
}
}
}).Start();
}
@@ -61,16 +65,4 @@ namespace QuickLook
return _instance ?? (_instance = new FocusMonitor());
}
}
internal class HeartbeatEventArgs : EventArgs
{
public HeartbeatEventArgs(long tick, string files)
{
InvokeTick = tick;
FocusedFile = files;
}
public long InvokeTick { get; }
public string FocusedFile { get; }
}
}

View File

@@ -50,7 +50,7 @@ namespace QuickLook.Helpers
}
catch (Exception)
{
TrayIconManager.GetInstance().ShowNotification("", "Failed to add QuickLook to Startup folder.");
TrayIconManager.ShowNotification("", "Failed to add QuickLook to Startup folder.");
}
}

View File

@@ -47,7 +47,7 @@ namespace QuickLook.Helpers
{
if (!silent)
Application.Current.Dispatcher.Invoke(
() => TrayIconManager.GetInstance().ShowNotification("",
() => TrayIconManager.ShowNotification("",
TranslationHelper.GetString("Update_NoUpdate")));
return;
}
@@ -57,7 +57,7 @@ namespace QuickLook.Helpers
Application.Current.Dispatcher.Invoke(
() =>
{
TrayIconManager.GetInstance().ShowNotification("",
TrayIconManager.ShowNotification("",
string.Format(TranslationHelper.GetString("Update_Found"), nVersion),
timeout: 20000,
clickEvent:
@@ -69,7 +69,7 @@ namespace QuickLook.Helpers
{
Debug.WriteLine(e.Message);
Application.Current.Dispatcher.Invoke(
() => TrayIconManager.GetInstance().ShowNotification("",
() => TrayIconManager.ShowNotification("",
string.Format(TranslationHelper.GetString("Update_Error"), e.Message)));
}
});
@@ -94,14 +94,13 @@ namespace QuickLook.Helpers
var changeLogPath = Path.GetTempFileName() + ".md";
File.WriteAllText(changeLogPath, notes);
Application.Current.Dispatcher.Invoke(() => ViewWindowManager.GetInstance()
.InvokeViewer(changeLogPath));
PipeServerManager.SendMessage(PipeMessages.Invoke, changeLogPath);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
Application.Current.Dispatcher.Invoke(
() => TrayIconManager.GetInstance().ShowNotification("",
() => TrayIconManager.ShowNotification("",
string.Format(TranslationHelper.GetString("Update_Error"), e.Message)));
}
});

View File

@@ -86,7 +86,8 @@
GlassVisibility="{Binding ContextObject.TitlebarBlurVisibility, ElementName=mainWindow, Converter={StaticResource BooleanToVisibilityConverter}}"
NoiseVisibility="{Binding ContextObject.TitlebarBlurVisibility, ElementName=mainWindow, Converter={StaticResource BooleanToVisibilityConverter}}" />
<DockPanel>
<Button DockPanel.Dock="Right" x:Name="buttonCloseWindow" Style="{StaticResource CaptionCloseButtonStyle}" Content="&#xE894;"/>
<Button DockPanel.Dock="Right" x:Name="buttonCloseWindow"
Style="{StaticResource CaptionCloseButtonStyle}" Content="&#xE894;" />
<Button DockPanel.Dock="Right" x:Name="buttonWindowStatus"
Visibility="{Binding ContextObject.CanResize, ElementName=mainWindow, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Style>
@@ -138,7 +139,8 @@
</Style>
</Button.Style>
</Button>
<Button DockPanel.Dock="Left" x:Name="buttonShare" Style="{StaticResource CaptionButtonStyle}" Content="&#xE72D;" />
<Button DockPanel.Dock="Left" x:Name="buttonShare" Style="{StaticResource CaptionButtonStyle}"
Content="&#xE72D;" />
<Grid x:Name="titleArea" Background="Transparent">
<TextBlock Text="{Binding ContextObject.Title, ElementName=mainWindow}" FontSize="12"
HorizontalAlignment="Left" TextTrimming="CharacterEllipsis"

View File

@@ -23,8 +23,6 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
@@ -300,7 +298,8 @@ namespace QuickLook
_path = string.Empty;
}
internal void BeginShow(IViewer matchedPlugin, string path, Action<ExceptionDispatchInfo> exceptionHandler)
internal void BeginShow(IViewer matchedPlugin, string path,
Action<string, ExceptionDispatchInfo> exceptionHandler)
{
_path = path;
Plugin = matchedPlugin;
@@ -338,7 +337,7 @@ namespace QuickLook
}
catch (Exception e)
{
exceptionHandler(ExceptionDispatchInfo.Capture(e));
exceptionHandler(path, ExceptionDispatchInfo.Capture(e));
}
}),
DispatcherPriority.Input);

View File

@@ -20,13 +20,23 @@ using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace QuickLook
{
internal static class PipeMessages
{
public const string RunAndClose = "QuickLook.App.PipeMessages.RunAndClose";
public const string Invoke = "QuickLook.App.PipeMessages.Invoke";
public const string Toggle = "QuickLook.App.PipeMessages.Toggle";
public const string Close = "QuickLook.App.PipeMessages.Close";
public const string Quit = "QuickLook.App.PipeMessages.Quit";
}
internal class PipeServerManager : IDisposable
{
private const string PipeName = "QuickLook.App.Pipe";
private const string PipeCloseMessage = "QuickLook.App.Pipe.QuitSingal";
private static PipeServerManager _instance;
private NamedPipeServerStream _server;
@@ -39,20 +49,18 @@ namespace QuickLook
{
using (var reader = new StreamReader(_server))
{
Debug.WriteLine("PipeManager: Ready");
while (true)
{
Debug.WriteLine("PipeManager: WaitForConnection");
_server.WaitForConnection();
var msg = reader.ReadLine();
Debug.WriteLine($"PipeManager: {msg}");
if (msg == PipeCloseMessage)
return;
// dispatch message
MessageReceived?.Invoke(msg, new EventArgs());
if (MessageReceived(msg))
return;
_server.Disconnect();
}
@@ -65,15 +73,16 @@ namespace QuickLook
GC.SuppressFinalize(this);
if (_server != null)
SendMessage(PipeCloseMessage);
SendMessage(PipeMessages.Quit);
_server?.Dispose();
_server = null;
}
public event EventHandler MessageReceived;
public static void SendMessage(string msg)
public static void SendMessage(string pipeMessage, string path = null)
{
if (path == null)
path = "";
try
{
using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
@@ -82,7 +91,7 @@ namespace QuickLook
using (var writer = new StreamWriter(client))
{
writer.WriteLine(msg);
writer.WriteLine($"{pipeMessage}|{path}");
writer.Flush();
}
}
@@ -93,6 +102,44 @@ namespace QuickLook
}
}
private bool MessageReceived(string msg)
{
var split = msg.IndexOf('|');
if (split == -1)
return false;
var wParam = msg.Substring(0, split);
var lParam = msg.Substring(split + 1, msg.Length - split - 1);
switch (wParam)
{
case PipeMessages.RunAndClose:
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ViewWindowManager.GetInstance().RunAndClosePreview()),
DispatcherPriority.ApplicationIdle);
return false;
case PipeMessages.Invoke:
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ViewWindowManager.GetInstance().InvokePreview(lParam)),
DispatcherPriority.ApplicationIdle);
return false;
case PipeMessages.Toggle:
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ViewWindowManager.GetInstance().TogglePreview(lParam)),
DispatcherPriority.ApplicationIdle);
return false;
case PipeMessages.Close:
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ViewWindowManager.GetInstance().ClosePreview()),
DispatcherPriority.ApplicationIdle);
return false;
case PipeMessages.Quit:
return true;
default:
return false;
}
}
~PipeServerManager()
{
Dispose();

View File

@@ -209,7 +209,7 @@ namespace QuickLook.Plugin
/// <param name="isError">Is this indicates a error?</param>
public void ShowNotification(string title, string content, bool isError = false)
{
TrayIconManager.GetInstance().ShowNotification(title, content, isError);
TrayIconManager.ShowNotification(title, content, isError);
}
/// <summary>

View File

@@ -66,25 +66,26 @@ namespace QuickLook
_icon.Visible = false;
}
public void ShowNotification(string title, string content, bool isError = false, int timeout = 5000,
public static void ShowNotification(string title, string content, bool isError = false, int timeout = 5000,
Action clickEvent = null,
Action closeEvent = null)
{
_icon.ShowBalloonTip(timeout, title, content, isError ? ToolTipIcon.Error : ToolTipIcon.Info);
_icon.BalloonTipClicked += OnIconOnBalloonTipClicked;
_icon.BalloonTipClosed += OnIconOnBalloonTipClosed;
var icon = GetInstance()._icon;
icon.ShowBalloonTip(timeout, title, content, isError ? ToolTipIcon.Error : ToolTipIcon.Info);
icon.BalloonTipClicked += OnIconOnBalloonTipClicked;
icon.BalloonTipClosed += OnIconOnBalloonTipClosed;
void OnIconOnBalloonTipClicked(object sender, EventArgs e)
{
clickEvent?.Invoke();
_icon.BalloonTipClicked -= OnIconOnBalloonTipClicked;
icon.BalloonTipClicked -= OnIconOnBalloonTipClicked;
}
void OnIconOnBalloonTipClosed(object sender, EventArgs e)
{
closeEvent?.Invoke();
_icon.BalloonTipClosed -= OnIconOnBalloonTipClosed;
icon.BalloonTipClosed -= OnIconOnBalloonTipClosed;
}
}

View File

@@ -20,8 +20,6 @@ using System.Diagnostics;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;
using QuickLook.Helpers;
using QuickLook.Plugin;
@@ -32,7 +30,7 @@ namespace QuickLook
private static ViewWindowManager _instance;
private MainWindowTransparent _currentMainWindow;
private string _path = string.Empty;
private string _invokedPath = string.Empty;
private MainWindowNoTransparent _viewWindowNoTransparent;
private MainWindowTransparent _viewWindowTransparent;
@@ -51,37 +49,7 @@ namespace QuickLook
ClosePreview();
}
internal void InvokeRoutine(KeyEventArgs kea, bool isKeyDown)
{
Debug.WriteLine($"InvokeRoutine: key={kea.KeyCode},down={isKeyDown}");
if (isKeyDown)
switch (kea.KeyCode)
{
case Keys.Enter:
RunAndClosePreview();
break;
}
else
switch (kea.KeyCode)
{
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
SwitchPreview();
break;
case Keys.Space:
TogglePreview();
break;
case Keys.Escape:
ClosePreview();
break;
}
}
internal void RunAndClosePreview()
public void RunAndClosePreview()
{
if (_currentMainWindow.Visibility != Visibility.Visible)
return;
@@ -103,7 +71,7 @@ namespace QuickLook
_currentMainWindow.RunAndHide();
}
internal void ClosePreview()
public void ClosePreview()
{
if (_currentMainWindow.Visibility != Visibility.Visible)
return;
@@ -112,66 +80,22 @@ namespace QuickLook
_currentMainWindow.BeginHide();
}
private void TogglePreview()
public void TogglePreview(string path)
{
if (_currentMainWindow.Visibility == Visibility.Visible)
{
ClosePreview();
}
else
{
_path = NativeMethods.QuickLook.GetCurrentSelection();
InvokeViewer();
}
}
private void SwitchPreview()
{
if (_currentMainWindow.Visibility != Visibility.Visible)
return;
// if the switch has been done by SwitchPreviewRemoteInvoke, we'll not do anything
var select = NativeMethods.QuickLook.GetCurrentSelection();
if (_path == select)
return;
_path = select;
Debug.WriteLine($"SwitchPreview: {_path}");
InvokeViewer();
}
private void SwitchPreviewRemoteInvoke(HeartbeatEventArgs e)
{
// if the switch has been done by SwitchPreview, we'll not do anything
if (e.FocusedFile == _path)
return;
if (string.IsNullOrEmpty(e.FocusedFile))
return;
Debug.WriteLine($"SwitchPreviewRemoteInvoke: {e.FocusedFile}");
_currentMainWindow?.Dispatcher.BeginInvoke(new Action(SwitchPreview), DispatcherPriority.ApplicationIdle);
InvokePreview(path);
}
private void RunFocusMonitor()
{
if (!FocusMonitor.GetInstance().IsRunning)
{
FocusMonitor.GetInstance().Start();
FocusMonitor.GetInstance().Heartbeat += SwitchPreviewRemoteInvoke;
}
FocusMonitor.GetInstance().Start();
}
private void StopFocusMonitor()
{
if (FocusMonitor.GetInstance().IsRunning)
{
FocusMonitor.GetInstance().Stop();
FocusMonitor.GetInstance().Heartbeat -= SwitchPreviewRemoteInvoke;
}
FocusMonitor.GetInstance().Stop();
}
internal void ForgetCurrentWindow()
@@ -186,33 +110,27 @@ namespace QuickLook
_currentMainWindow = _viewWindowTransparent;
}
internal bool InvokeViewer(string path = null, bool closeIfSame = false)
public void InvokePreview(string path)
{
if (closeIfSame)
if (_currentMainWindow.Visibility == Visibility.Visible && path == _path)
{
ClosePreview();
return false;
}
if (string.IsNullOrEmpty(path))
return;
if (path != null)
_path = path;
if (_currentMainWindow.Visibility == Visibility.Visible && path == _invokedPath)
return;
if (string.IsNullOrEmpty(_path))
return false;
if (!Directory.Exists(_path) && !File.Exists(_path))
return false;
if (!Directory.Exists(path) && !File.Exists(path))
return;
_invokedPath = path;
RunFocusMonitor();
var matchedPlugin = PluginManager.GetInstance().FindMatch(_path);
var matchedPlugin = PluginManager.GetInstance().FindMatch(path);
BeginShowNewWindow(matchedPlugin);
return true;
BeginShowNewWindow(path, matchedPlugin);
}
private void BeginShowNewWindow(IViewer matchedPlugin)
private void BeginShowNewWindow(string path, IViewer matchedPlugin)
{
_currentMainWindow.UnloadPlugin();
@@ -224,22 +142,22 @@ namespace QuickLook
if (!ReferenceEquals(oldWindow, _currentMainWindow))
oldWindow.BeginHide();
_currentMainWindow.BeginShow(matchedPlugin, _path, CurrentPluginFailed);
_currentMainWindow.BeginShow(matchedPlugin, path, CurrentPluginFailed);
}
private void CurrentPluginFailed(ExceptionDispatchInfo e)
private void CurrentPluginFailed(string path, ExceptionDispatchInfo e)
{
var plugin = _currentMainWindow.Plugin.GetType();
_currentMainWindow.BeginHide();
TrayIconManager.GetInstance().ShowNotification("", $"Failed to preview {Path.GetFileName(_path)}", true);
TrayIconManager.ShowNotification("", $"Failed to preview {Path.GetFileName(path)}", true);
Debug.WriteLine(e.SourceException.ToString());
Debug.WriteLine(e.SourceException.StackTrace);
if (plugin != PluginManager.GetInstance().DefaultPlugin.GetType())
BeginShowNewWindow(PluginManager.GetInstance().DefaultPlugin);
BeginShowNewWindow(path, PluginManager.GetInstance().DefaultPlugin);
else
e.Throw();
}