Last active
April 25, 2018 12:54
-
-
Save rubyu/62b54f0255d938c929e71dff235a36ea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Drawing; | |
using System.Threading; | |
using System.Windows.Forms; | |
using System.Collections.Concurrent; | |
using System.Runtime.InteropServices; | |
using Crevice.Threading; | |
using Crevice.Logging; | |
public class GestureStrokeOverlayManager : IDisposable | |
{ | |
protected class Message {} | |
protected class ResetMessage : Message {} | |
protected class DrawMessage : Message | |
{ | |
public readonly IEnumerable<Crevice.Core.Stroke.Stroke> Strokes; | |
public readonly IEnumerable<Point> BufferedPoints; | |
public DrawMessage(IEnumerable<Crevice.Core.Stroke.Stroke> strokes, IEnumerable<Point> bufferedPoints) | |
{ | |
this.Strokes = strokes; | |
this.BufferedPoints = bufferedPoints; | |
} | |
} | |
class Desktop : IDisposable | |
{ | |
private enum RDW : int | |
{ | |
INVALIDATE = 0x1, | |
INTERNALPAINT = 0x2, | |
ERASE = 0x4, | |
VALIDATE = 0x8, | |
NOINTERNALPAINT = 0x10, | |
NOERASE = 0x20, | |
NOCHILDREN = 0x40, | |
ALLCHILDREN = 0x80, | |
UPDATENOW = 0x100, | |
ERASENOW = 0x200, | |
FRAME = 0x400, | |
NOFRAME = 0x800 | |
} | |
[DllImport("user32.dll")] | |
private static extern IntPtr GetDC(IntPtr hwnd); | |
[DllImport("user32.dll")] | |
private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); | |
[DllImport("user32.dll")] | |
private static extern bool UpdateWindow(IntPtr hWnd); | |
[DllImport("user32.dll")] | |
private static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RDW flags); | |
private const int WS_EX_TOOLWINDOW = 0x00000080; | |
private const int WS_EX_TRANSPARENT = 0x00000020; | |
private readonly GestureStrokeOverlayManager manager; | |
private readonly IntPtr dc; | |
private readonly System.Timers.Timer timer; | |
private DrawMessage lastDrawMessage = null; | |
public Desktop(GestureStrokeOverlayManager manager) | |
{ | |
this.manager = manager; | |
this.dc = GetDC(IntPtr.Zero); | |
this.timer = new System.Timers.Timer(15); | |
this.timer.AutoReset = false; | |
this.timer.Elapsed += (sender, e) => { | |
Draw(); | |
}; | |
} | |
public void Clear() { | |
lastDrawMessage = null; | |
RedrawWindow(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, RDW.ALLCHILDREN | RDW.UPDATENOW | RDW.ERASE | RDW.INVALIDATE | RDW.INTERNALPAINT); | |
UpdateWindow(IntPtr.Zero); | |
} | |
public void Draw(DrawMessage dm) | |
{ | |
lastDrawMessage = dm; | |
timer.Start(); | |
Draw(); | |
} | |
private readonly object _lockObject = new object(); | |
private void Draw() | |
{ | |
lock (_lockObject) | |
{ | |
if (lastDrawMessage == null) | |
{ | |
return; | |
} | |
var strokes = lastDrawMessage.Strokes; | |
var bufferedPoints = lastDrawMessage.BufferedPoints; | |
using (var g = Graphics.FromHdc(dc)) | |
{ | |
if (manager.MessageQueue.Any()) | |
{ | |
Verbose.Print("DrawStrokeOverlay was canceled."); | |
return; | |
} | |
var points = (strokes.SelectMany(s => s.Points)).Concat(bufferedPoints).Append(Cursor.Position); | |
g.DrawLines(manager.NormalLinePen, points.ToArray()); | |
/* | |
foreach (var (v, i) in strokes.Select((v, i) => (v, i))) | |
{ | |
if (manager.MessageQueue.Count > 0) | |
{ | |
Verbose.Print("DrawStrokeOverlay was canceled."); | |
return; | |
} | |
var pen = v != strokes.Last() ? manager.NormalLinePen : manager.NewLinePen; | |
Verbose.Print($"Drawing Stroke({i + 1}/{strokes.Count()}) of {v.Points.Count} points."); | |
g.DrawLines(pen, v.Points.ToArray()); | |
} | |
if (bufferedPoints.Any()) | |
{ | |
var points = (strokes.Any() && strokes.Last().Points.Any() ? new List<Point> { strokes.Last().Points.Last() } : new List<Point>()).Concat(bufferedPoints).Append(Cursor.Position); | |
Verbose.Print($"Drawing undetermined stroke of {points.Count()} points."); | |
g.DrawLines(manager.UndeterminedLinePen, points.ToArray()); | |
} | |
*/ | |
} | |
timer.Start(); | |
} | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (disposing) | |
{ | |
} | |
ReleaseDC(IntPtr.Zero, dc); | |
} | |
~Desktop() | |
{ | |
Dispose(false); | |
} | |
} | |
public static Color DefaultNormalLineColor => Color.FromArgb(51, 153, 255); | |
public static Color DefaultNewLineColor => Color.FromArgb(255, 51, 153); | |
public static Color DefaultUndeterminedLineColor => Color.FromArgb(255, 141, 75); | |
public static float DefaultLineWidth => 4.0f; | |
public readonly Pen NormalLinePen; | |
public readonly Pen NewLinePen; | |
public readonly Pen UndeterminedLinePen; | |
public readonly float LineWidth; | |
public readonly bool Debug; | |
public void Clear() | |
{ | |
try { | |
MessageQueue.Add(new ResetMessage()); | |
} | |
catch(InvalidOperationException) {} | |
} | |
public void Draw(IEnumerable<Crevice.Core.Stroke.Stroke> strokes, IEnumerable<Point> bufferedPoints) | |
{ | |
try { | |
MessageQueue.Add(new DrawMessage(strokes, bufferedPoints)); | |
} | |
catch(InvalidOperationException) {} | |
} | |
protected readonly BlockingCollection<Message> MessageQueue = new BlockingCollection<Message>(); | |
public GestureStrokeOverlayManager( | |
Color? normalLineColor = null, | |
Color? newLineColor = null, | |
Color? undeterminedLineColor = null, | |
float? lineWidth = null, | |
bool debug = false) | |
{ | |
LineWidth = lineWidth ?? DefaultLineWidth; | |
NormalLinePen = new Pen(normalLineColor ?? DefaultNormalLineColor, LineWidth); | |
NewLinePen = new Pen(newLineColor ?? DefaultNewLineColor, LineWidth); | |
UndeterminedLinePen = new Pen(undeterminedLineColor ?? DefaultUndeterminedLineColor, LineWidth); | |
Debug = debug; | |
RunMessageProcessTask(); | |
} | |
private void RunMessageProcessTask() => | |
Task.Factory.StartNew(() => | |
{ | |
try | |
{ | |
Desktop desktop = null; | |
foreach (var message in MessageQueue.GetConsumingEnumerable()) | |
{ | |
if (_disposed) break; | |
try { | |
if (message is ResetMessage) | |
{ | |
Verbose.Print("[GestureStrokeOverlayManager.Clear]"); | |
desktop?.Clear(); | |
desktop?.Dispose(); | |
desktop = null; | |
} | |
else if (message is DrawMessage dm) | |
{ | |
Verbose.Print("[GestureStrokeOverlayManager.DrawMessage]"); | |
if (desktop == null) | |
{ | |
desktop = new Desktop(this); | |
} | |
desktop.Draw(dm); | |
} | |
} | |
catch(Exception ex) | |
{ | |
Verbose.Print($"Error on MessageProcessTask: {ex.ToString()}"); | |
} | |
} | |
desktop?.Clear(); | |
desktop?.Dispose(); | |
NormalLinePen?.Dispose(); | |
NewLinePen?.Dispose(); | |
UndeterminedLinePen?.Dispose(); | |
} | |
catch (OperationCanceledException) {} | |
}); | |
internal bool _disposed { get; private set; } = false; | |
public void Dispose() | |
{ | |
if (_disposed) return; | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
_disposed = true; | |
if (disposing) | |
{ | |
MessageQueue.CompleteAdding(); | |
} | |
} | |
~GestureStrokeOverlayManager() => Dispose(false); | |
} | |
GestureStrokeOverlayManager gestureStrokeOverlayManager = null; | |
Config.Callback.MachineStart += (sender, e) => { | |
MainForm.BeginInvoke((MethodInvoker)(() => { | |
gestureStrokeOverlayManager = new GestureStrokeOverlayManager(debug: false); | |
})); | |
}; | |
Config.Callback.MachineStop += (sender, e) => { | |
MainForm.BeginInvoke((MethodInvoker)(() => { | |
gestureStrokeOverlayManager?.Dispose(); | |
})); | |
}; | |
Config.Callback.StrokeUpdated += (sender, e) => { | |
gestureStrokeOverlayManager?.Draw(e.Strokes, (sender as Crevice.Core.Stroke.StrokeWatcher).GetBufferedPoints()); | |
}; | |
Config.Callback.StrokeReset += (sender, e) => { | |
gestureStrokeOverlayManager?.Clear(); | |
}; | |
Config.Callback.StateChanged += (sender, e) => { | |
gestureStrokeOverlayManager?.Clear(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment