|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- using System;
- using System.Drawing;
- using System.Threading;
- using RenderHookAPI.Hook.Common;
-
- namespace RenderHookAPI.Interface
- {
- [Serializable]
- public delegate void RecordingStartedEvent(CaptureConfig config);
- [Serializable]
- public delegate void RecordingStoppedEvent();
- [Serializable]
- public delegate void MessageReceivedEvent(MessageReceivedEventArgs message);
- [Serializable]
- public delegate void ScreenshotReceivedEvent(ScreenshotReceivedEventArgs response);
- [Serializable]
- public delegate void DisconnectedEvent();
- [Serializable]
- public delegate void ScreenshotRequestedEvent(ScreenshotRequest request);
- [Serializable]
- public delegate void DisplayTextEvent(DisplayTextEventArgs args);
- [Serializable]
- public delegate void DrawOverlayEvent(DrawOverlayEventArgs args);
-
- [Serializable]
- public class CaptureInterface : MarshalByRefObject
- {
- /// <summary>
- /// The client process Id
- /// </summary>
- public int ProcessId { get; set; }
-
- #region Events
-
- #region Server-side Events
-
- /// <summary>
- /// Server event for sending debug and error information from the client to server
- /// </summary>
- public event MessageReceivedEvent RemoteMessage;
-
- /// <summary>
- /// Server event for receiving screenshot image data
- /// </summary>
- public event ScreenshotReceivedEvent ScreenshotReceived;
-
- #endregion
-
- #region Client-side Events
-
- /// <summary>
- /// Client event used to communicate to the client that it is time to start recording
- /// </summary>
- public event RecordingStartedEvent RecordingStarted;
-
- /// <summary>
- /// Client event used to communicate to the client that it is time to stop recording
- /// </summary>
- public event RecordingStoppedEvent RecordingStopped;
-
- /// <summary>
- /// Client event used to communicate to the client that it is time to create a screenshot
- /// </summary>
- public event ScreenshotRequestedEvent ScreenshotRequested;
-
- /// <summary>
- /// Client event used to notify the hook to exit
- /// </summary>
- public event DisconnectedEvent Disconnected;
-
- /// <summary>
- /// Client event used to display a piece of text in-game
- /// </summary>
- public event DisplayTextEvent DisplayText;
-
- /// <summary>
- /// Client event used to (re-)draw an overlay in-game.
- /// </summary>
- public event DrawOverlayEvent DrawOverlay;
-
- #endregion
-
- #endregion
-
- public bool IsRecording { get; set; }
-
- #region Public Methods
-
- #region Video Capture
-
- /// <summary>
- /// If not <see cref="IsRecording"/> will invoke the <see cref="RecordingStarted"/> event, starting a new recording.
- /// </summary>
- /// <param name="config">The configuration for the recording</param>
- /// <remarks>Handlers in the server and remote process will be be invoked.</remarks>
- public void StartRecording(CaptureConfig config)
- {
- if (IsRecording)
- return;
- SafeInvokeRecordingStarted(config);
- IsRecording = true;
- }
-
- /// <summary>
- /// If <see cref="IsRecording"/>, will invoke the <see cref="RecordingStopped"/> event, finalising any existing recording.
- /// </summary>
- /// <remarks>Handlers in the server and remote process will be be invoked.</remarks>
- public void StopRecording()
- {
- if (!IsRecording)
- return;
- SafeInvokeRecordingStopped();
- IsRecording = false;
- }
-
- #endregion
-
- #region Still image Capture
-
- object _lock = new object();
- Guid? _requestId = null;
- Action<Screenshot> _completeScreenshot = null;
- ManualResetEvent _wait = new ManualResetEvent(false);
-
- /// <summary>
- /// Get a fullscreen screenshot with the default timeout of 2 seconds
- /// </summary>
- public Screenshot GetScreenshot()
- {
- return GetScreenshot(Rectangle.Empty, new TimeSpan(0, 0, 2), null, ImageFormat.Bitmap);
- }
-
- /// <summary>
- /// Get a screenshot of the specified region
- /// </summary>
- /// <param name="region">the region to capture (x=0,y=0 is top left corner)</param>
- /// <param name="timeout">maximum time to wait for the screenshot</param>
- public Screenshot GetScreenshot(Rectangle region, TimeSpan timeout, Size? resize, ImageFormat format)
- {
- lock (_lock)
- {
- Screenshot result = null;
- _requestId = Guid.NewGuid();
- _wait.Reset();
-
- SafeInvokeScreenshotRequested(new ScreenshotRequest(_requestId.Value, region)
- {
- Format = format,
- Resize = resize,
- });
-
- _completeScreenshot = (sc) =>
- {
- try
- {
- Interlocked.Exchange(ref result, sc);
- }
- catch
- {
- }
- _wait.Set();
-
- };
-
- _wait.WaitOne(timeout);
- _completeScreenshot = null;
- return result;
- }
- }
-
- public IAsyncResult BeginGetScreenshot(Rectangle region, TimeSpan timeout, AsyncCallback callback = null, Size? resize = null, ImageFormat format = ImageFormat.Bitmap)
- {
- Func<Rectangle, TimeSpan, Size?, ImageFormat, Screenshot> getScreenshot = GetScreenshot;
-
- return getScreenshot.BeginInvoke(region, timeout, resize, format, callback, getScreenshot);
- }
-
- public Screenshot EndGetScreenshot(IAsyncResult result)
- {
- Func<Rectangle, TimeSpan, Size?, ImageFormat, Screenshot> getScreenshot = result.AsyncState as Func<Rectangle, TimeSpan, Size?, ImageFormat, Screenshot>;
- if (getScreenshot != null)
- {
- return getScreenshot.EndInvoke(result);
- }
- else
- return null;
- }
-
- public void SendScreenshotResponse(Screenshot screenshot)
- {
- if (_requestId != null && screenshot != null && screenshot.RequestId == _requestId.Value)
- {
- if (_completeScreenshot != null)
- {
- _completeScreenshot(screenshot);
- }
- }
- }
-
- #endregion
-
- /// <summary>
- /// Tell the client process to disconnect
- /// </summary>
- public void Disconnect()
- {
- SafeInvokeDisconnected();
- }
-
- /// <summary>
- /// Send a message to all handlers of <see cref="CaptureInterface.RemoteMessage"/>.
- /// </summary>
- /// <param name="messageType"></param>
- /// <param name="format"></param>
- /// <param name="args"></param>
- public void Message(MessageType messageType, string format, params object[] args)
- {
- Message(messageType, String.Format(format, args));
- }
-
- public void Message(MessageType messageType, string message)
- {
- SafeInvokeMessageRecevied(new MessageReceivedEventArgs(messageType, message));
- }
-
- /// <summary>
- /// Display text in-game for the default duration of 5 seconds
- /// </summary>
- /// <param name="text"></param>
- public void DisplayInGameText(string text)
- {
- DisplayInGameText(text, new TimeSpan(0, 0, 5));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="text"></param>
- /// <param name="duration"></param>
- public void DisplayInGameText(string text, TimeSpan duration)
- {
- if (duration.TotalMilliseconds <= 0)
- throw new ArgumentException("Duration must be larger than 0", "duration");
- SafeInvokeDisplayText(new DisplayTextEventArgs(text, duration));
- }
-
- /// <summary>
- /// Replace the in-game overlay with the one provided.
- ///
- /// Note: this is not designed for fast updates (i.e. only a couple of times per second)
- /// </summary>
- /// <param name="overlay"></param>
- public void DrawOverlayInGame(IOverlay overlay)
- {
- SafeInvokeDrawOverlay(new DrawOverlayEventArgs()
- {
- Overlay = overlay
- });
- }
-
- #endregion
-
- #region Private: Invoke message handlers
-
- private void SafeInvokeRecordingStarted(CaptureConfig config)
- {
- if (RecordingStarted == null)
- return; //No Listeners
-
- RecordingStartedEvent listener = null;
- Delegate[] dels = RecordingStarted.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (RecordingStartedEvent)del;
- listener.Invoke(config);
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- RecordingStarted -= listener;
- }
- }
- }
-
- private void SafeInvokeRecordingStopped()
- {
- if (RecordingStopped == null)
- return; //No Listeners
-
- RecordingStoppedEvent listener = null;
- Delegate[] dels = RecordingStopped.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (RecordingStoppedEvent)del;
- listener.Invoke();
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- RecordingStopped -= listener;
- }
- }
- }
-
- private void SafeInvokeMessageRecevied(MessageReceivedEventArgs eventArgs)
- {
- if (RemoteMessage == null)
- return; //No Listeners
-
- MessageReceivedEvent listener = null;
- Delegate[] dels = RemoteMessage.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (MessageReceivedEvent)del;
- listener.Invoke(eventArgs);
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- RemoteMessage -= listener;
- }
- }
- }
-
- private void SafeInvokeScreenshotRequested(ScreenshotRequest eventArgs)
- {
- if (ScreenshotRequested == null)
- return; //No Listeners
-
- ScreenshotRequestedEvent listener = null;
- Delegate[] dels = ScreenshotRequested.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (ScreenshotRequestedEvent)del;
- listener.Invoke(eventArgs);
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- ScreenshotRequested -= listener;
- }
- }
- }
-
- private void SafeInvokeScreenshotReceived(ScreenshotReceivedEventArgs eventArgs)
- {
- if (ScreenshotReceived == null)
- return; //No Listeners
-
- ScreenshotReceivedEvent listener = null;
- Delegate[] dels = ScreenshotReceived.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (ScreenshotReceivedEvent)del;
- listener.Invoke(eventArgs);
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- ScreenshotReceived -= listener;
- }
- }
- }
-
- private void SafeInvokeDisconnected()
- {
- if (Disconnected == null)
- return; //No Listeners
-
- DisconnectedEvent listener = null;
- Delegate[] dels = Disconnected.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (DisconnectedEvent)del;
- listener.Invoke();
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- Disconnected -= listener;
- }
- }
- }
-
- private void SafeInvokeDisplayText(DisplayTextEventArgs displayTextEventArgs)
- {
- if (DisplayText == null)
- return; //No Listeners
-
- DisplayTextEvent listener = null;
- Delegate[] dels = DisplayText.GetInvocationList();
-
- foreach (Delegate del in dels)
- {
- try
- {
- listener = (DisplayTextEvent)del;
- listener.Invoke(displayTextEventArgs);
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- DisplayText -= listener;
- }
- }
- }
-
- private void SafeInvokeDrawOverlay(DrawOverlayEventArgs drawOverlayEventArgs)
- {
- if (DrawOverlay == null)
- return; //No Listeners
-
- DrawOverlayEvent listener = null;
- var dels = DrawOverlay.GetInvocationList();
-
- foreach (var del in dels)
- {
- try
- {
- listener = (DrawOverlayEvent)del;
- listener.Invoke(drawOverlayEventArgs);
- }
- catch (Exception)
- {
- //Could not reach the destination, so remove it
- //from the list
- DrawOverlay -= listener;
- }
- }
- }
-
- #endregion
-
- /// <summary>
- /// Used to confirm connection to IPC server channel
- /// </summary>
- public DateTime Ping()
- {
- return DateTime.Now;
- }
- }
-
-
- /// <summary>
- /// Client event proxy for marshalling event handlers
- /// </summary>
- public class ClientCaptureInterfaceEventProxy : MarshalByRefObject
- {
- #region Event Declarations
-
- /// <summary>
- /// Client event used to communicate to the client that it is time to start recording
- /// </summary>
- public event RecordingStartedEvent RecordingStarted;
-
- /// <summary>
- /// Client event used to communicate to the client that it is time to stop recording
- /// </summary>
- public event RecordingStoppedEvent RecordingStopped;
-
- /// <summary>
- /// Client event used to communicate to the client that it is time to create a screenshot
- /// </summary>
- public event ScreenshotRequestedEvent ScreenshotRequested;
-
- /// <summary>
- /// Client event used to notify the hook to exit
- /// </summary>
- public event DisconnectedEvent Disconnected;
-
- /// <summary>
- /// Client event used to display in-game text
- /// </summary>
- public event DisplayTextEvent DisplayText;
-
- /// <summary>
- /// Client event used to (re-)draw an overlay in-game.
- /// </summary>
- public event DrawOverlayEvent DrawOverlay;
-
- #endregion
-
- #region Lifetime Services
-
- public override object InitializeLifetimeService()
- {
- //Returning null holds the object alive
- //until it is explicitly destroyed
- return null;
- }
-
- #endregion
-
- public void RecordingStartedProxyHandler(CaptureConfig config)
- {
- if (RecordingStarted != null)
- RecordingStarted(config);
- }
-
- public void RecordingStoppedProxyHandler()
- {
- if (RecordingStopped != null)
- RecordingStopped();
- }
-
-
- public void DisconnectedProxyHandler()
- {
- if (Disconnected != null)
- Disconnected();
- }
-
- public void ScreenshotRequestedProxyHandler(ScreenshotRequest request)
- {
- if (ScreenshotRequested != null)
- ScreenshotRequested(request);
- }
-
- public void DisplayTextProxyHandler(DisplayTextEventArgs args)
- {
- if (DisplayText != null)
- DisplayText(args);
- }
-
- public void DrawOverlayProxyHandler(DrawOverlayEventArgs args)
- {
- if (DrawOverlay != null)
- DrawOverlay(args);
- }
- }
- }
|