mirror of
https://github.com/greenshot/greenshot
synced 2025-07-16 10:03:44 -07:00
Fixed some errors, removed obsolete code, removed some anonymous delegates and added some additional disposing code in the CaptureHelper.cs
This commit is contained in:
parent
15253ef295
commit
be36a3f42b
11 changed files with 211 additions and 1791 deletions
|
@ -121,7 +121,7 @@ namespace Greenshot {
|
|||
/// <summary>
|
||||
/// Cleanup all the allocated resources
|
||||
/// </summary>
|
||||
private void Cleanup() {
|
||||
private void Cleanup(object sender, EventArgs e) {
|
||||
if (gBitmap != null) {
|
||||
gBitmap.Dispose();
|
||||
gBitmap = null;
|
||||
|
@ -133,12 +133,8 @@ namespace Greenshot {
|
|||
/// </summary>
|
||||
public AboutForm() {
|
||||
// Make sure our resources are removed again.
|
||||
Disposed += delegate {
|
||||
Cleanup();
|
||||
};
|
||||
FormClosing += delegate {
|
||||
Cleanup();
|
||||
};
|
||||
Disposed += Cleanup;
|
||||
FormClosing += Cleanup;
|
||||
|
||||
// Enable animation for this form, when we don't set this the timer doesn't start as soon as the form is loaded.
|
||||
EnableAnimation = true;
|
||||
|
|
|
@ -67,7 +67,6 @@ namespace Greenshot.Forms {
|
|||
private bool _mouseDown;
|
||||
private Rectangle _captureRect = Rectangle.Empty;
|
||||
private readonly ICapture _capture;
|
||||
private readonly Image _capturedImage;
|
||||
private Point _previousMousePos = Point.Empty;
|
||||
private FixMode _fixMode = FixMode.None;
|
||||
private RectangleAnimator _windowAnimator;
|
||||
|
@ -112,6 +111,15 @@ namespace Greenshot.Forms {
|
|||
}
|
||||
}
|
||||
|
||||
private void ClosedHandler(object sender, EventArgs e) {
|
||||
_currentForm = null;
|
||||
LOG.Debug("Remove CaptureForm from currentForm");
|
||||
}
|
||||
|
||||
private void ClosingHandler(object sender, EventArgs e) {
|
||||
LOG.Debug("Closing captureform");
|
||||
WindowDetails.UnregisterIgnoreHandle(Handle);
|
||||
}
|
||||
/// <summary>
|
||||
/// This creates the capture form
|
||||
/// </summary>
|
||||
|
@ -129,16 +137,8 @@ namespace Greenshot.Forms {
|
|||
// Enable the AnimatingForm
|
||||
EnableAnimation = true;
|
||||
|
||||
// Using 32bppPArgb speeds up the drawing.
|
||||
//capturedImage = ImageHelper.Clone(capture.Image, PixelFormat.Format32bppPArgb);
|
||||
// comment the clone, uncomment the assignment and the original bitmap is used.
|
||||
_capturedImage = capture.Image;
|
||||
|
||||
// clean up
|
||||
FormClosed += delegate {
|
||||
_currentForm = null;
|
||||
LOG.Debug("Remove CaptureForm from currentForm");
|
||||
};
|
||||
FormClosed += ClosedHandler;
|
||||
|
||||
_capture = capture;
|
||||
_windows = windows;
|
||||
|
@ -155,14 +155,7 @@ namespace Greenshot.Forms {
|
|||
// Make sure we never capture the captureform
|
||||
WindowDetails.RegisterIgnoreHandle(Handle);
|
||||
// Unregister at close
|
||||
FormClosing += delegate {
|
||||
// remove the buffer if it was created inside this form
|
||||
if (_capturedImage != capture.Image) {
|
||||
_capturedImage.Dispose();
|
||||
}
|
||||
LOG.Debug("Closing captureform");
|
||||
WindowDetails.UnregisterIgnoreHandle(Handle);
|
||||
};
|
||||
FormClosing += ClosingHandler;
|
||||
|
||||
// set cursor location
|
||||
_cursorPos = WindowCapture.GetCursorLocationRelativeToScreenBounds();
|
||||
|
@ -586,7 +579,7 @@ namespace Greenshot.Forms {
|
|||
/// <param name="sourceRectangle"></param>
|
||||
/// <param name="destinationRectangle"></param>
|
||||
private void DrawZoom(Graphics graphics, Rectangle sourceRectangle, Rectangle destinationRectangle) {
|
||||
if (_capturedImage == null) {
|
||||
if (_capture.Image == null) {
|
||||
return;
|
||||
}
|
||||
ImageAttributes attributes;
|
||||
|
@ -611,9 +604,9 @@ namespace Greenshot.Forms {
|
|||
graphics.SetClip(path);
|
||||
if (!_isZoomerTransparent) {
|
||||
graphics.FillRectangle(BackgroundBrush, destinationRectangle);
|
||||
graphics.DrawImage(_capturedImage, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel);
|
||||
graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel);
|
||||
} else {
|
||||
graphics.DrawImage(_capturedImage, destinationRectangle, sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height, GraphicsUnit.Pixel, attributes);
|
||||
graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height, GraphicsUnit.Pixel, attributes);
|
||||
}
|
||||
}
|
||||
int alpha = (int)(255 * Conf.ZoomerOpacity);
|
||||
|
@ -680,7 +673,7 @@ namespace Greenshot.Forms {
|
|||
Graphics graphics = e.Graphics;
|
||||
Rectangle clipRectangle = e.ClipRectangle;
|
||||
//graphics.BitBlt((Bitmap)buffer, Point.Empty);
|
||||
graphics.DrawImageUnscaled(_capturedImage, Point.Empty);
|
||||
graphics.DrawImageUnscaled(_capture.Image, Point.Empty);
|
||||
// Only draw Cursor if it's (partly) visible
|
||||
if (_capture.Cursor != null && _capture.CursorVisible && clipRectangle.IntersectsWith(new Rectangle(_capture.CursorLocation, _capture.Cursor.Size))) {
|
||||
graphics.DrawIcon(_capture.Cursor, _capture.CursorLocation.X, _capture.CursorLocation.Y);
|
||||
|
|
|
@ -420,6 +420,16 @@ namespace Greenshot {
|
|||
HandleDataTransport(dataTransport);
|
||||
}
|
||||
|
||||
private void BalloonTipClicked(object sender, EventArgs e) {
|
||||
BalloonTipClosed(sender, e);
|
||||
ShowSetting();
|
||||
}
|
||||
private void BalloonTipClosed(object sender, EventArgs e) {
|
||||
notifyIcon.BalloonTipClicked -= BalloonTipClicked;
|
||||
notifyIcon.BalloonTipClosed -= BalloonTipClosed;
|
||||
}
|
||||
|
||||
|
||||
private void HandleDataTransport(CopyDataTransport dataTransport) {
|
||||
foreach(KeyValuePair<CommandEnum, string> command in dataTransport.Commands) {
|
||||
LOG.Debug("Data received, Command = " + command.Key + ", Data: " + command.Value);
|
||||
|
@ -430,22 +440,9 @@ namespace Greenshot {
|
|||
break;
|
||||
case CommandEnum.FirstLaunch:
|
||||
LOG.Info("FirstLaunch: Created new configuration, showing balloon.");
|
||||
|
||||
try {
|
||||
EventHandler balloonTipClickedHandler = null;
|
||||
EventHandler balloonTipClosedHandler = null;
|
||||
balloonTipClosedHandler = delegate {
|
||||
notifyIcon.BalloonTipClicked -= balloonTipClickedHandler;
|
||||
notifyIcon.BalloonTipClosed -= balloonTipClosedHandler;
|
||||
};
|
||||
|
||||
balloonTipClickedHandler = delegate {
|
||||
ShowSetting();
|
||||
notifyIcon.BalloonTipClicked -= balloonTipClickedHandler;
|
||||
notifyIcon.BalloonTipClosed -= balloonTipClosedHandler;
|
||||
};
|
||||
notifyIcon.BalloonTipClicked += balloonTipClickedHandler;
|
||||
notifyIcon.BalloonTipClosed += balloonTipClosedHandler;
|
||||
notifyIcon.BalloonTipClicked += BalloonTipClicked;
|
||||
notifyIcon.BalloonTipClosed += BalloonTipClosed;
|
||||
notifyIcon.ShowBalloonTip(2000, "Greenshot", Language.GetFormattedString(LangKey.tooltip_firststart, HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.RegionHotkey)), ToolTipIcon.Info);
|
||||
} catch (Exception ex) {
|
||||
LOG.Warn("Exception while showing first launch: ", ex);
|
||||
|
|
|
@ -65,16 +65,16 @@ namespace Greenshot {
|
|||
}
|
||||
|
||||
// This makes it possible to still capture the settings screen
|
||||
fullscreen_hotkeyControl.Enter += delegate { EnterHotkeyControl(); };
|
||||
fullscreen_hotkeyControl.Leave += delegate { LeaveHotkeyControl(); };
|
||||
window_hotkeyControl.Enter += delegate { EnterHotkeyControl(); };
|
||||
window_hotkeyControl.Leave += delegate { LeaveHotkeyControl(); };
|
||||
region_hotkeyControl.Enter += delegate { EnterHotkeyControl(); };
|
||||
region_hotkeyControl.Leave += delegate { LeaveHotkeyControl(); };
|
||||
ie_hotkeyControl.Enter += delegate { EnterHotkeyControl(); };
|
||||
ie_hotkeyControl.Leave += delegate { LeaveHotkeyControl(); };
|
||||
lastregion_hotkeyControl.Enter += delegate { EnterHotkeyControl(); };
|
||||
lastregion_hotkeyControl.Leave += delegate { LeaveHotkeyControl(); };
|
||||
fullscreen_hotkeyControl.Enter += EnterHotkeyControl;
|
||||
fullscreen_hotkeyControl.Leave += LeaveHotkeyControl;
|
||||
window_hotkeyControl.Enter += EnterHotkeyControl;
|
||||
window_hotkeyControl.Leave += LeaveHotkeyControl;
|
||||
region_hotkeyControl.Enter += EnterHotkeyControl;
|
||||
region_hotkeyControl.Leave += LeaveHotkeyControl;
|
||||
ie_hotkeyControl.Enter += EnterHotkeyControl;
|
||||
ie_hotkeyControl.Leave += LeaveHotkeyControl;
|
||||
lastregion_hotkeyControl.Enter += EnterHotkeyControl;
|
||||
lastregion_hotkeyControl.Leave += LeaveHotkeyControl;
|
||||
|
||||
DisplayPluginTab();
|
||||
UpdateUI();
|
||||
|
@ -83,12 +83,12 @@ namespace Greenshot {
|
|||
CheckSettings();
|
||||
}
|
||||
|
||||
private void EnterHotkeyControl() {
|
||||
private void EnterHotkeyControl(object sender, EventArgs e) {
|
||||
HotkeyControl.UnregisterHotkeys();
|
||||
_inHotkey = true;
|
||||
}
|
||||
|
||||
private void LeaveHotkeyControl() {
|
||||
private void LeaveHotkeyControl(object sender, EventArgs e) {
|
||||
MainForm.RegisterHotkeys();
|
||||
_inHotkey = false;
|
||||
}
|
||||
|
|
|
@ -202,14 +202,12 @@
|
|||
<DependentUpon>MovableShowColorForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="App.config" />
|
||||
<None Include="Helpers\AviHelper.cs" />
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="Helpers\CaptureHelper.cs" />
|
||||
<Compile Include="Helpers\Colors.cs" />
|
||||
<Compile Include="Helpers\CopyData.cs" />
|
||||
<Compile Include="Helpers\GeometryHelper.cs" />
|
||||
<Compile Include="Helpers\DestinationHelper.cs" />
|
||||
<None Include="Helpers\HookHelper.cs" />
|
||||
<Compile Include="Helpers\IECaptureHelper.cs" />
|
||||
<Compile Include="Helpers\IEInterop\IEContainer.cs" />
|
||||
<Compile Include="Helpers\ProcessorHelper.cs" />
|
||||
|
@ -223,7 +221,6 @@
|
|||
<Compile Include="Memento\IMemento.cs" />
|
||||
<Compile Include="Memento\DrawableContainerBoundsChangeMemento.cs" />
|
||||
<Compile Include="Memento\SurfaceBackgroundChangeMemento.cs" />
|
||||
<None Include="Helpers\ScreenCaptureHelper.cs" />
|
||||
<Compile Include="Helpers\UpdateHelper.cs" />
|
||||
<Compile Include="Helpers\EnvironmentInfo.cs" />
|
||||
<Compile Include="Helpers\GuiRectangle.cs" />
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,7 @@ using Greenshot.IniFile;
|
|||
using Greenshot.Plugin;
|
||||
using GreenshotPlugin.Core;
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
using log4net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
@ -34,7 +35,6 @@ using System.Drawing;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using log4net;
|
||||
|
||||
namespace Greenshot.Helpers {
|
||||
/// <summary>
|
||||
|
@ -118,7 +118,9 @@ namespace Greenshot.Helpers {
|
|||
}
|
||||
|
||||
public static void CaptureWindow(bool captureMouse) {
|
||||
new CaptureHelper(CaptureMode.ActiveWindow, captureMouse).MakeCapture();
|
||||
using (CaptureHelper captureHelper = new CaptureHelper(CaptureMode.ActiveWindow, captureMouse)) {
|
||||
captureHelper.MakeCapture();
|
||||
}
|
||||
}
|
||||
|
||||
public static void CaptureWindow(WindowDetails windowToCapture) {
|
||||
|
@ -214,13 +216,8 @@ namespace Greenshot.Helpers {
|
|||
/// Make Capture with specified destinations
|
||||
/// </summary>
|
||||
private void MakeCapture() {
|
||||
// Experimental code
|
||||
// TODO: when we get the screen capture code working correctly, this needs to be enabled
|
||||
//if (screenCapture != null) {
|
||||
// screenCapture.Stop();
|
||||
// screenCapture = null;
|
||||
// return;
|
||||
//}
|
||||
Thread retrieveWindowDetailsThread = null;
|
||||
|
||||
// This fixes a problem when a balloon is still visible and a capture needs to be taken
|
||||
// forcefully removes the balloon!
|
||||
if (!conf.HideTrayicon) {
|
||||
|
@ -236,11 +233,11 @@ namespace Greenshot.Helpers {
|
|||
case CaptureMode.Region:
|
||||
// Check if a region is pre-supplied!
|
||||
if (Rectangle.Empty.Equals(_captureRect)) {
|
||||
PrepareForCaptureWithFeedback();
|
||||
retrieveWindowDetailsThread = PrepareForCaptureWithFeedback();
|
||||
}
|
||||
break;
|
||||
case CaptureMode.Window:
|
||||
PrepareForCaptureWithFeedback();
|
||||
retrieveWindowDetailsThread = PrepareForCaptureWithFeedback();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -286,12 +283,6 @@ namespace Greenshot.Helpers {
|
|||
break;
|
||||
case CaptureMode.ActiveWindow:
|
||||
if (CaptureActiveWindow()) {
|
||||
// TODO: Reactive / check if the elements code is activated
|
||||
//if (windowDetailsThread != null) {
|
||||
// windowDetailsThread.Join();
|
||||
//}
|
||||
//capture.MoveElements(capture.ScreenBounds.Location.X-capture.Location.X, capture.ScreenBounds.Location.Y-capture.Location.Y);
|
||||
|
||||
// Capture worked, offset mouse according to screen bounds and capture location
|
||||
_capture.MoveMouseLocation(_capture.ScreenBounds.Location.X-_capture.Location.X, _capture.ScreenBounds.Location.Y-_capture.Location.Y);
|
||||
_capture.CaptureDetails.AddMetaData("source", "Window");
|
||||
|
@ -448,10 +439,10 @@ namespace Greenshot.Helpers {
|
|||
LOG.Warn("Unknown capture mode: " + _captureMode);
|
||||
break;
|
||||
}
|
||||
// TODO: Reactive / check if the elements code is activated
|
||||
//if (windowDetailsThread != null) {
|
||||
// windowDetailsThread.Join();
|
||||
//}
|
||||
// Wait for thread, otherwise we can't dipose the CaptureHelper
|
||||
if (retrieveWindowDetailsThread != null) {
|
||||
retrieveWindowDetailsThread.Join();
|
||||
}
|
||||
if (_capture != null) {
|
||||
LOG.Debug("Disposing capture");
|
||||
_capture.Dispose();
|
||||
|
@ -471,81 +462,46 @@ namespace Greenshot.Helpers {
|
|||
return null;
|
||||
}
|
||||
|
||||
Thread getWindowDetailsThread = new Thread (delegate() {
|
||||
// Start Enumeration of "active" windows
|
||||
List<WindowDetails> allWindows = WindowDetails.GetMetroApps();
|
||||
allWindows.AddRange(WindowDetails.GetAllWindows());
|
||||
foreach (WindowDetails window in allWindows) {
|
||||
// Window should be visible and not ourselves
|
||||
if (!window.Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip empty
|
||||
Rectangle windowRectangle = window.WindowRectangle;
|
||||
Size windowSize = windowRectangle.Size;
|
||||
if (windowSize.Width == 0 || windowSize.Height == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the details are retrieved once
|
||||
window.FreezeDetails();
|
||||
|
||||
// Force children retrieval, sometimes windows close on losing focus and this is solved by caching
|
||||
int goLevelDeep = 3;
|
||||
if (conf.WindowCaptureAllChildLocations) {
|
||||
goLevelDeep = 20;
|
||||
}
|
||||
window.GetChildren(goLevelDeep);
|
||||
lock (_windows) {
|
||||
_windows.Add(window);
|
||||
}
|
||||
|
||||
// TODO: Following code should be enabled & checked if the editor can support "elements"
|
||||
//// Get window rectangle as capture Element
|
||||
//CaptureElement windowCaptureElement = new CaptureElement(windowRectangle);
|
||||
//if (capture == null) {
|
||||
// break;
|
||||
//}
|
||||
//capture.Elements.Add(windowCaptureElement);
|
||||
|
||||
//if (!window.HasParent) {
|
||||
// // Get window client rectangle as capture Element, place all the other "children" in there
|
||||
// Rectangle clientRectangle = window.ClientRectangle;
|
||||
// CaptureElement windowClientCaptureElement = new CaptureElement(clientRectangle);
|
||||
// windowCaptureElement.Children.Add(windowClientCaptureElement);
|
||||
// AddCaptureElementsForWindow(windowClientCaptureElement, window, goLevelDeep);
|
||||
//} else {
|
||||
// AddCaptureElementsForWindow(windowCaptureElement, window, goLevelDeep);
|
||||
//}
|
||||
}
|
||||
// lock (windows) {
|
||||
// windows = WindowDetails.SortByZOrder(IntPtr.Zero, windows);
|
||||
// }
|
||||
});
|
||||
Thread getWindowDetailsThread = new Thread(RetrieveWindowDetails);
|
||||
getWindowDetailsThread.Name = "Retrieve window details";
|
||||
getWindowDetailsThread.IsBackground = true;
|
||||
getWindowDetailsThread.Start();
|
||||
return getWindowDetailsThread;
|
||||
}
|
||||
|
||||
// Code used to get the capture elements, which is not active yet
|
||||
//private void AddCaptureElementsForWindow(ICaptureElement parentElement, WindowDetails parentWindow, int level) {
|
||||
// foreach(WindowDetails childWindow in parentWindow.Children) {
|
||||
// // Make sure the details are retrieved once
|
||||
// childWindow.FreezeDetails();
|
||||
// Rectangle childRectangle = childWindow.WindowRectangle;
|
||||
// Size s1 = childRectangle.Size;
|
||||
// childRectangle.Intersect(parentElement.Bounds);
|
||||
// if (childRectangle.Width > 0 && childRectangle.Height > 0) {
|
||||
// CaptureElement childCaptureElement = new CaptureElement(childRectangle);
|
||||
// parentElement.Children.Add(childCaptureElement);
|
||||
// if (level > 0) {
|
||||
// AddCaptureElementsForWindow(childCaptureElement, childWindow, level -1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
private void RetrieveWindowDetails() {
|
||||
LOG.Debug("start RetrieveWindowDetails");
|
||||
// Start Enumeration of "active" windows
|
||||
List<WindowDetails> allWindows = WindowDetails.GetMetroApps();
|
||||
allWindows.AddRange(WindowDetails.GetAllWindows());
|
||||
foreach (WindowDetails window in allWindows) {
|
||||
// Window should be visible and not ourselves
|
||||
if (!window.Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip empty
|
||||
Rectangle windowRectangle = window.WindowRectangle;
|
||||
Size windowSize = windowRectangle.Size;
|
||||
if (windowSize.Width == 0 || windowSize.Height == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the details are retrieved once
|
||||
window.FreezeDetails();
|
||||
|
||||
// Force children retrieval, sometimes windows close on losing focus and this is solved by caching
|
||||
int goLevelDeep = 3;
|
||||
if (conf.WindowCaptureAllChildLocations) {
|
||||
goLevelDeep = 20;
|
||||
}
|
||||
window.GetChildren(goLevelDeep);
|
||||
lock (_windows) {
|
||||
_windows.Add(window);
|
||||
}
|
||||
}
|
||||
LOG.Debug("end RetrieveWindowDetails");
|
||||
}
|
||||
|
||||
private void AddConfiguredDestination() {
|
||||
foreach(string destinationDesignation in conf.OutputDestinations) {
|
||||
|
@ -556,6 +512,98 @@ namespace Greenshot.Helpers {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a balloon tip is show for a taken capture, this handles the click on it
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OpenCaptureOnClick(object sender, EventArgs e) {
|
||||
SurfaceMessageEventArgs eventArgs = MainForm.Instance.NotifyIcon.Tag as SurfaceMessageEventArgs;
|
||||
if (eventArgs == null) {
|
||||
LOG.Warn("OpenCaptureOnClick called without SurfaceMessageEventArgs");
|
||||
RemoveEventHandler(sender, e);
|
||||
return;
|
||||
}
|
||||
ISurface surface = eventArgs.Surface;
|
||||
if (surface != null && eventArgs.MessageType == SurfaceMessageTyp.FileSaved) {
|
||||
if (!string.IsNullOrEmpty(surface.LastSaveFullPath)) {
|
||||
string errorMessage = null;
|
||||
|
||||
try {
|
||||
ProcessStartInfo psi = new ProcessStartInfo("explorer.exe");
|
||||
psi.Arguments = Path.GetDirectoryName(surface.LastSaveFullPath);
|
||||
psi.UseShellExecute = false;
|
||||
using (Process p = new Process()) {
|
||||
p.StartInfo = psi;
|
||||
p.Start();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
errorMessage = ex.Message;
|
||||
}
|
||||
// Added fallback for when the explorer can't be found
|
||||
if (errorMessage != null) {
|
||||
try {
|
||||
string windowsPath = Environment.GetEnvironmentVariable("SYSTEMROOT");
|
||||
string explorerPath = Path.Combine(windowsPath, "explorer.exe");
|
||||
if (File.Exists(explorerPath)) {
|
||||
ProcessStartInfo psi = new ProcessStartInfo(explorerPath);
|
||||
psi.Arguments = Path.GetDirectoryName(surface.LastSaveFullPath);
|
||||
psi.UseShellExecute = false;
|
||||
using (Process p = new Process()) {
|
||||
p.StartInfo = psi;
|
||||
p.Start();
|
||||
}
|
||||
errorMessage = null;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
if (errorMessage != null) {
|
||||
MessageBox.Show(string.Format("{0}\r\nexplorer.exe {1}", errorMessage, surface.LastSaveFullPath), "explorer.exe", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
} else if (surface != null && !string.IsNullOrEmpty(surface.UploadURL)) {
|
||||
Process.Start(surface.UploadURL);
|
||||
}
|
||||
LOG.DebugFormat("Deregistering the BalloonTipClicked");
|
||||
RemoveEventHandler(sender, e);
|
||||
}
|
||||
|
||||
private void RemoveEventHandler(object sender, EventArgs e) {
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClicked -= OpenCaptureOnClick;
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClosed -= RemoveEventHandler;
|
||||
MainForm.Instance.NotifyIcon.Tag = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the SufraceMessageEvent receiver
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventArgs"></param>
|
||||
private void SurfaceMessageReceived(object sender, SurfaceMessageEventArgs eventArgs) {
|
||||
if (eventArgs == null || string.IsNullOrEmpty(eventArgs.Message)) {
|
||||
return;
|
||||
}
|
||||
switch (eventArgs.MessageType) {
|
||||
case SurfaceMessageTyp.Error:
|
||||
MainForm.Instance.NotifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Error);
|
||||
break;
|
||||
case SurfaceMessageTyp.Info:
|
||||
MainForm.Instance.NotifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info);
|
||||
break;
|
||||
case SurfaceMessageTyp.FileSaved:
|
||||
case SurfaceMessageTyp.UploadedUri:
|
||||
// Show a balloon and register an event handler to open the "capture" for if someone clicks the balloon.
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClicked += OpenCaptureOnClick;
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClosed += RemoveEventHandler;
|
||||
// Store for later usage
|
||||
MainForm.Instance.NotifyIcon.Tag = eventArgs;
|
||||
MainForm.Instance.NotifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void HandleCapture() {
|
||||
// Flag to see if the image was "exported" so the FileEditor doesn't
|
||||
// ask to save the file as long as nothing is done.
|
||||
|
@ -586,80 +634,7 @@ namespace Greenshot.Helpers {
|
|||
|
||||
// Register notify events if this is wanted
|
||||
if (conf.ShowTrayNotification && !conf.HideTrayicon) {
|
||||
surface.SurfaceMessage += delegate(object source, SurfaceMessageEventArgs eventArgs) {
|
||||
if (eventArgs == null || string.IsNullOrEmpty(eventArgs.Message)) {
|
||||
return;
|
||||
}
|
||||
switch (eventArgs.MessageType) {
|
||||
case SurfaceMessageTyp.Error:
|
||||
MainForm.Instance.NotifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Error);
|
||||
break;
|
||||
case SurfaceMessageTyp.Info:
|
||||
MainForm.Instance.NotifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info);
|
||||
break;
|
||||
case SurfaceMessageTyp.FileSaved:
|
||||
case SurfaceMessageTyp.UploadedUri:
|
||||
EventHandler balloonTipClickedHandler = null;
|
||||
EventHandler balloonTipClosedHandler = null;
|
||||
balloonTipClosedHandler = delegate(object sender, EventArgs e) {
|
||||
LOG.DebugFormat("Deregistering the BalloonTipClosed");
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClicked -= balloonTipClickedHandler;
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClosed -= balloonTipClosedHandler;
|
||||
};
|
||||
|
||||
balloonTipClickedHandler = delegate(object sender, EventArgs e) {
|
||||
if (eventArgs.MessageType == SurfaceMessageTyp.FileSaved) {
|
||||
if (!string.IsNullOrEmpty(surface.LastSaveFullPath)) {
|
||||
string errorMessage = null;
|
||||
|
||||
try {
|
||||
ProcessStartInfo psi = new ProcessStartInfo("explorer.exe");
|
||||
psi.Arguments = Path.GetDirectoryName(surface.LastSaveFullPath);
|
||||
psi.UseShellExecute = false;
|
||||
using (Process p = new Process()) {
|
||||
p.StartInfo = psi;
|
||||
p.Start();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
errorMessage = ex.Message;
|
||||
}
|
||||
// Added fallback for when the explorer can't be found
|
||||
if (errorMessage != null) {
|
||||
try {
|
||||
string windowsPath = Environment.GetEnvironmentVariable("SYSTEMROOT");
|
||||
string explorerPath = Path.Combine(windowsPath, "explorer.exe");
|
||||
if (File.Exists(explorerPath)) {
|
||||
ProcessStartInfo psi = new ProcessStartInfo(explorerPath);
|
||||
psi.Arguments = Path.GetDirectoryName(surface.LastSaveFullPath);
|
||||
psi.UseShellExecute = false;
|
||||
using (Process p = new Process()) {
|
||||
p.StartInfo = psi;
|
||||
p.Start();
|
||||
}
|
||||
errorMessage = null;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
if (errorMessage != null) {
|
||||
MessageBox.Show(string.Format("{0}\r\nexplorer.exe {1}", errorMessage, surface.LastSaveFullPath), "explorer.exe", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(surface.UploadURL)) {
|
||||
Process.Start(surface.UploadURL);
|
||||
}
|
||||
}
|
||||
LOG.DebugFormat("Deregistering the BalloonTipClicked");
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClicked -= balloonTipClickedHandler;
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClosed -= balloonTipClosedHandler;
|
||||
};
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClicked += balloonTipClickedHandler;
|
||||
MainForm.Instance.NotifyIcon.BalloonTipClosed += balloonTipClosedHandler;
|
||||
MainForm.Instance.NotifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info);
|
||||
break;
|
||||
}
|
||||
};
|
||||
surface.SurfaceMessage += SurfaceMessageReceived;
|
||||
|
||||
}
|
||||
// Let the processors do their job
|
||||
|
@ -984,30 +959,6 @@ namespace Greenshot.Helpers {
|
|||
}
|
||||
|
||||
if (_captureRect.Height > 0 && _captureRect.Width > 0) {
|
||||
// TODO: Reactive / check if the elements code is activated
|
||||
//if (windowDetailsThread != null) {
|
||||
// windowDetailsThread.Join();
|
||||
//}
|
||||
|
||||
// Experimental code for Video capture
|
||||
// TODO: when we get the screen capture code working correctly, this needs to be enabled
|
||||
//if (capture.CaptureDetails.CaptureMode == CaptureMode.Video) {
|
||||
// if (captureForm.UsedCaptureMode == CaptureMode.Window) {
|
||||
// screenCapture = new ScreenCaptureHelper(selectedCaptureWindow);
|
||||
// } else if (captureForm.UsedCaptureMode == CaptureMode.Region) {
|
||||
// screenCapture = new ScreenCaptureHelper(captureRect);
|
||||
// }
|
||||
// if (screenCapture != null) {
|
||||
// screenCapture.RecordMouse = capture.CursorVisible;
|
||||
// if (screenCapture.Start(25)) {
|
||||
// return;
|
||||
// }
|
||||
// // User clicked cancel or a problem occured
|
||||
// screenCapture.Stop();
|
||||
// screenCapture = null;
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
// Take the captureRect, this already is specified as bitmap coordinates
|
||||
_capture.Crop(_captureRect);
|
||||
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2014 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.Collections.Generic;
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
using GreenshotPlugin.Core;
|
||||
|
||||
namespace Greenshot.Helpers {
|
||||
public class HookHelper {
|
||||
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(HookHelper));
|
||||
private static List<IntPtr> hooks = new List<IntPtr>();
|
||||
private static WinEventDelegate _winEventProc;
|
||||
private static uint oleEventThread = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Remove the made hooks
|
||||
/// </summary>
|
||||
public static void Unhook() {
|
||||
LOG.Debug("Cleaning winEvent hooks");
|
||||
foreach (IntPtr hook in hooks) {
|
||||
if (hook != IntPtr.Zero) {
|
||||
User32.UnhookWinEvent(hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hook the WinEvents we are interested in
|
||||
/// </summary>
|
||||
public static void Hook() {
|
||||
LOG.Debug("Starting winEvent hooks");
|
||||
_winEventProc = new WinEventDelegate(WinEventProc);
|
||||
int processID = 0; //Process.GetCurrentProcess().Id;
|
||||
hooks.Add(User32.SetWinEventHook(WinEvent.EVENT_OBJECT_CREATE, WinEvent.EVENT_OBJECT_HIDE, IntPtr.Zero, _winEventProc, processID, 0, WinEventHookFlags.WINEVENT_SKIPOWNPROCESS));
|
||||
hooks.Add(User32.SetWinEventHook(WinEvent.EVENT_OBJECT_LOCATIONCHANGE, WinEvent.EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, _winEventProc, 0, 0, WinEventHookFlags.WINEVENT_SKIPOWNPROCESS));
|
||||
hooks.Add(User32.SetWinEventHook(WinEvent.EVENT_SYSTEM_MENUSTART, WinEvent.EVENT_SYSTEM_MENUPOPUPEND, IntPtr.Zero, _winEventProc, processID, 0, WinEventHookFlags.WINEVENT_SKIPOWNPROCESS));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the WinEvent
|
||||
/// </summary>
|
||||
/// <param name="hWinEventHook">The Hook IntPtr</param>
|
||||
/// <param name="eventType">Event Type to handle, enum WinEvent</param>
|
||||
/// <param name="hwnd">Window handle which caused the event</param>
|
||||
/// <param name="idObject">Object ID, enum EventObjects</param>
|
||||
/// <param name="idChild">Child ID of the window</param>
|
||||
/// <param name="dwEventThread">Thread which generated the ID</param>
|
||||
/// <param name="dwmsEventTime"></param>
|
||||
private static void WinEventProc(IntPtr hWinEventHook, WinEvent eventType, IntPtr hwnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime) {
|
||||
// Check if it's an event generated by a Window
|
||||
if (hwnd == IntPtr.Zero || idObject != EventObjects.OBJID_WINDOW) {
|
||||
// Other events do not interest us.
|
||||
return;
|
||||
}
|
||||
String classname = null;
|
||||
// Check if the event was generated by the OLE Event Thread, which causes a lot of create/destroy
|
||||
if (oleEventThread != 0 && dwEventThread == oleEventThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only get the classname when it's not a destroy (classname is not available)
|
||||
if (eventType == WinEvent.EVENT_OBJECT_CREATE) {
|
||||
classname = WindowDetails.GetClassName(hwnd);
|
||||
// Make sure the OleMainThreadWndClass events are ignored.
|
||||
if (oleEventThread == 0) {
|
||||
if (classname == "OleMainThreadWndClass") {
|
||||
oleEventThread = dwEventThread;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG.DebugFormat("eventType={0},hwnd={1},classname={4},idObject={2},idChild={3},dwEventThread={5}", eventType, hwnd, idObject, idChild, classname, dwEventThread);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,326 +0,0 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2014 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: http://getgreenshot.org/
|
||||
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
using GreenshotPlugin.Core;
|
||||
using Greenshot.IniFile;
|
||||
|
||||
namespace Greenshot.Helpers {
|
||||
/// <summary>
|
||||
/// Description of ScreenCaptureHelper.
|
||||
/// </summary>
|
||||
public class ScreenCaptureHelper {
|
||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ScreenCaptureHelper));
|
||||
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
|
||||
private const int ALIGNMENT = 8;
|
||||
private IntPtr hWndDesktop = IntPtr.Zero;
|
||||
private IntPtr hDCDesktop = IntPtr.Zero;
|
||||
private IntPtr hDCDest = IntPtr.Zero;
|
||||
private IntPtr hDIBSection = IntPtr.Zero;
|
||||
private IntPtr hOldObject = IntPtr.Zero;
|
||||
private int framesPerSecond;
|
||||
private Thread backgroundTask;
|
||||
private bool stop = false;
|
||||
private AVIWriter aviWriter;
|
||||
private WindowDetails recordingWindow;
|
||||
private Rectangle recordingRectangle;
|
||||
public bool RecordMouse = false;
|
||||
private Size recordingSize;
|
||||
private IntPtr bits0 = IntPtr.Zero; //pointer to the raw bits that make up the bitmap.
|
||||
private Bitmap GDIBitmap;
|
||||
private string filename = null;
|
||||
private Stopwatch stopwatch = new Stopwatch();
|
||||
private bool disabledDWM = false;
|
||||
|
||||
private ScreenCaptureHelper() {
|
||||
if (DWM.isDWMEnabled()) {
|
||||
// with DWM Composition disabled the capture goes ~2x faster
|
||||
DWM.DisableComposition();
|
||||
disabledDWM = true;
|
||||
}
|
||||
}
|
||||
public ScreenCaptureHelper(Rectangle recordingRectangle) : this() {
|
||||
this.recordingRectangle = recordingRectangle;
|
||||
}
|
||||
public ScreenCaptureHelper(WindowDetails recordingWindow) : this() {
|
||||
this.recordingWindow = recordingWindow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create an exception that might explain what is wrong while capturing
|
||||
/// </summary>
|
||||
/// <param name="method">string with current method</param>
|
||||
/// <param name="captureBounds">Rectangle of what we want to capture</param>
|
||||
/// <returns></returns>
|
||||
private static Exception CreateCaptureException(string method, Size size) {
|
||||
Exception exceptionToThrow = User32.CreateWin32Exception(method);
|
||||
if (size != Size.Empty) {
|
||||
exceptionToThrow.Data.Add("Height", size.Height);
|
||||
exceptionToThrow.Data.Add("Width", size.Width);
|
||||
}
|
||||
return exceptionToThrow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the recording
|
||||
/// </summary>
|
||||
/// <param name="framesPerSecond"></param>
|
||||
/// <returns></returns>
|
||||
public bool Start(int framesPerSecond) {
|
||||
if (recordingWindow != null) {
|
||||
string windowTitle = Regex.Replace(recordingWindow.Text, @"[^\x20\d\w]", "");
|
||||
if (string.IsNullOrEmpty(windowTitle)) {
|
||||
windowTitle = "greenshot-recording";
|
||||
}
|
||||
filename = Path.Combine(conf.OutputFilePath, windowTitle + ".avi");
|
||||
|
||||
} else {
|
||||
filename = Path.Combine(conf.OutputFilePath, "greenshot-recording.avi");
|
||||
}
|
||||
if (File.Exists(filename)) {
|
||||
try {
|
||||
File.Delete(filename);
|
||||
} catch {}
|
||||
}
|
||||
LOG.InfoFormat("Capturing to {0}", filename);
|
||||
|
||||
if (recordingWindow != null) {
|
||||
LOG.InfoFormat("Starting recording Window '{0}', {1}", recordingWindow.Text, recordingWindow.WindowRectangle);
|
||||
recordingSize = recordingWindow.WindowRectangle.Size;
|
||||
} else {
|
||||
LOG.InfoFormat("Starting recording rectangle {0}", recordingRectangle);
|
||||
recordingSize = recordingRectangle.Size;
|
||||
}
|
||||
//if (recordingSize.Width % ALIGNMENT > 0) {
|
||||
// LOG.InfoFormat("Correcting width to be factor alignment, {0} => {1}", recordingSize.Width, recordingSize.Width + (ALIGNMENT - (recordingSize.Width % ALIGNMENT)));
|
||||
// recordingSize = new Size(recordingSize.Width + (ALIGNMENT - (recordingSize.Width % ALIGNMENT)), recordingSize.Height);
|
||||
//}
|
||||
//if (recordingSize.Height % ALIGNMENT > 0) {
|
||||
// LOG.InfoFormat("Correcting Height to be factor alignment, {0} => {1}", recordingSize.Height, recordingSize.Height + (ALIGNMENT - (recordingSize.Height % ALIGNMENT)));
|
||||
// recordingSize = new Size(recordingSize.Width, recordingSize.Height + (ALIGNMENT - (recordingSize.Height % ALIGNMENT)));
|
||||
//}
|
||||
this.framesPerSecond = framesPerSecond;
|
||||
// "P/Invoke" Solution for capturing the screen
|
||||
hWndDesktop = User32.GetDesktopWindow();
|
||||
// get te hDC of the target window
|
||||
hDCDesktop = User32.GetWindowDC(hWndDesktop);
|
||||
// Make sure the last error is set to 0
|
||||
Win32.SetLastError(0);
|
||||
|
||||
// create a device context we can copy to
|
||||
hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);
|
||||
// Check if the device context is there, if not throw an error with as much info as possible!
|
||||
if (hDCDest == IntPtr.Zero) {
|
||||
// Get Exception before the error is lost
|
||||
Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", recordingSize);
|
||||
// Cleanup
|
||||
User32.ReleaseDC(hWndDesktop, hDCDesktop);
|
||||
// throw exception
|
||||
throw exceptionToThrow;
|
||||
}
|
||||
|
||||
// Create BitmapInfoHeader for CreateDIBSection
|
||||
BitmapInfoHeader bitmapInfoHeader = new BitmapInfoHeader(recordingSize.Width, recordingSize.Height, 32);
|
||||
|
||||
// Make sure the last error is set to 0
|
||||
Win32.SetLastError(0);
|
||||
|
||||
// create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
|
||||
hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bitmapInfoHeader, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
|
||||
|
||||
if (hDIBSection == IntPtr.Zero) {
|
||||
// Get Exception before the error is lost
|
||||
Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", recordingSize);
|
||||
exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
|
||||
exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());
|
||||
|
||||
// clean up
|
||||
GDI32.DeleteDC(hDCDest);
|
||||
User32.ReleaseDC(hWndDesktop, hDCDesktop);
|
||||
|
||||
// Throw so people can report the problem
|
||||
throw exceptionToThrow;
|
||||
}
|
||||
// Create a GDI Bitmap so we can use GDI and GDI+ operations on the same memory
|
||||
GDIBitmap = new Bitmap(recordingSize.Width, recordingSize.Height, 32, PixelFormat.Format32bppArgb, bits0);
|
||||
// select the bitmap object and store the old handle
|
||||
hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);
|
||||
stop = false;
|
||||
|
||||
aviWriter = new AVIWriter();
|
||||
// Comment the following 2 lines to make the user select it's own codec
|
||||
aviWriter.Codec = "msvc";
|
||||
aviWriter.Quality = 10000;
|
||||
|
||||
aviWriter.FrameRate = framesPerSecond;
|
||||
if (aviWriter.Open(filename, recordingSize.Width, recordingSize.Height)) {
|
||||
// Start update check in the background
|
||||
backgroundTask = new Thread (new ThreadStart(CaptureFrame));
|
||||
backgroundTask.IsBackground = true;
|
||||
backgroundTask.Name = "Capture video";
|
||||
backgroundTask.Start();
|
||||
return true;
|
||||
} else {
|
||||
// Cancel
|
||||
aviWriter.Dispose();
|
||||
aviWriter = null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do the actual frame capture
|
||||
/// </summary>
|
||||
private void CaptureFrame() {
|
||||
int MSBETWEENCAPTURES = 1000/framesPerSecond;
|
||||
int msToNextCapture = MSBETWEENCAPTURES;
|
||||
stopwatch.Reset();
|
||||
while (!stop) {
|
||||
stopwatch.Start();
|
||||
Point captureLocation;
|
||||
if (recordingWindow != null) {
|
||||
recordingWindow.Reset();
|
||||
captureLocation = recordingWindow.Location;
|
||||
} else {
|
||||
captureLocation = new Point(recordingRectangle.X, recordingRectangle.Y);
|
||||
}
|
||||
// "Capture"
|
||||
GDI32.BitBlt(hDCDest, 0, 0, recordingSize.Width, recordingSize.Height, hDCDesktop, captureLocation.X, captureLocation.Y, CopyPixelOperation.SourceCopy);
|
||||
//GDI32.BitBlt(hDCDest, 0, 0, recordingSize.Width, recordingSize.Height, hDCDesktop, captureLocation.X, captureLocation.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
|
||||
|
||||
// Mouse
|
||||
if (RecordMouse) {
|
||||
CursorInfo cursorInfo = new CursorInfo();
|
||||
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
|
||||
Point mouseLocation = Cursor.Position;
|
||||
mouseLocation.Offset(-captureLocation.X, -captureLocation.Y);
|
||||
if (User32.GetCursorInfo(out cursorInfo)) {
|
||||
User32.DrawIcon(hDCDest, mouseLocation.X, mouseLocation.Y, cursorInfo.hCursor);
|
||||
}
|
||||
}
|
||||
// add to avi
|
||||
try {
|
||||
aviWriter.AddLowLevelFrame(bits0);
|
||||
} catch (Exception) {
|
||||
LOG.Error("Error adding frame to avi, stopping capturing.");
|
||||
break;
|
||||
}
|
||||
|
||||
int restTime = (int)(msToNextCapture - stopwatch.ElapsedMilliseconds);
|
||||
|
||||
// Set time to next capture, we correct it if needed later.
|
||||
msToNextCapture = MSBETWEENCAPTURES;
|
||||
if (restTime > 0) {
|
||||
// We were fast enough, we wait for next capture
|
||||
Thread.Sleep(restTime);
|
||||
} else if (restTime < 0) {
|
||||
// Compensating, as we took to long
|
||||
int framesToSkip = ((-restTime) / MSBETWEENCAPTURES);
|
||||
int leftoverMillis = (-restTime) % MSBETWEENCAPTURES;
|
||||
//LOG.InfoFormat("Adding {0} empty frames to avi, leftover millis is {1}, sleeping {2} (of {3} total)", framesToSkip, leftover, sleepMillis, MSBETWEENCAPTURES);
|
||||
aviWriter.AddEmptyFrames(framesToSkip);
|
||||
|
||||
// check how bad it is, if we only missed our target by a few millis we hope the next capture corrects this
|
||||
if (leftoverMillis > 0 && leftoverMillis <= 2) {
|
||||
// subtract the leftover from the millis to next capture, do nothing else
|
||||
msToNextCapture -= leftoverMillis;
|
||||
} else if (leftoverMillis > 0) {
|
||||
// it's more, we add an empty frame
|
||||
aviWriter.AddEmptyFrames(1);
|
||||
// we sleep to the next time and
|
||||
int sleepMillis = MSBETWEENCAPTURES - leftoverMillis;
|
||||
// Sleep to next capture
|
||||
Thread.Sleep(sleepMillis);
|
||||
}
|
||||
}
|
||||
stopwatch.Reset();
|
||||
}
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the recording, after the next frame
|
||||
/// </summary>
|
||||
public void Stop() {
|
||||
stop = true;
|
||||
if (backgroundTask != null) {
|
||||
backgroundTask.Join();
|
||||
}
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free resources
|
||||
/// </summary>
|
||||
private void Cleanup() {
|
||||
if (hOldObject != IntPtr.Zero && hDCDest != IntPtr.Zero) {
|
||||
// restore selection (old handle)
|
||||
GDI32.SelectObject(hDCDest, hOldObject);
|
||||
GDI32.DeleteDC(hDCDest);
|
||||
}
|
||||
if (hDCDesktop != IntPtr.Zero) {
|
||||
User32.ReleaseDC(hWndDesktop, hDCDesktop);
|
||||
}
|
||||
if (hDIBSection != IntPtr.Zero) {
|
||||
// free up the Bitmap object
|
||||
GDI32.DeleteObject(hDIBSection);
|
||||
}
|
||||
|
||||
if (disabledDWM) {
|
||||
DWM.EnableComposition();
|
||||
}
|
||||
if (aviWriter != null) {
|
||||
aviWriter.Dispose();
|
||||
aviWriter = null;
|
||||
|
||||
string ffmpegexe = PluginUtils.GetExePath("ffmpeg.exe");
|
||||
if (ffmpegexe != null) {
|
||||
try {
|
||||
string webMFile = filename.Replace(".avi", ".webm");
|
||||
string arguments = "-i \"" + filename + "\" -codec:v libvpx -quality good -cpu-used 0 -b:v 1000k -qmin 10 -qmax 42 -maxrate 1000k -bufsize 4000k -threads 4 \"" + webMFile + "\"";
|
||||
LOG.DebugFormat("Starting {0} with arguments {1}", ffmpegexe, arguments);
|
||||
ProcessStartInfo processStartInfo = new ProcessStartInfo(ffmpegexe, arguments);
|
||||
processStartInfo.CreateNoWindow = false;
|
||||
processStartInfo.RedirectStandardOutput = false;
|
||||
processStartInfo.UseShellExecute = false;
|
||||
Process process = Process.Start(processStartInfo);
|
||||
process.WaitForExit();
|
||||
if (process.ExitCode == 0) {
|
||||
MessageBox.Show("Recording written to " + webMFile);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
MessageBox.Show("Recording written to " + filename + " couldn't convert due to an error: " + ex.Message);
|
||||
}
|
||||
} else {
|
||||
MessageBox.Show("Recording written to " + filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,11 @@
|
|||
* 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 Greenshot.IniFile;
|
||||
using Greenshot.Plugin;
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
using log4net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
@ -25,10 +30,6 @@ using System.Drawing;
|
|||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using Greenshot.IniFile;
|
||||
using Greenshot.Plugin;
|
||||
using GreenshotPlugin.UnmanagedHelpers;
|
||||
using log4net;
|
||||
|
||||
namespace GreenshotPlugin.Core {
|
||||
/// <summary>
|
||||
|
|
|
@ -76,6 +76,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
|||
/// A hbitmap SafeHandle implementation
|
||||
/// </summary>
|
||||
public class SafeHBitmapHandle : SafeObjectHandle {
|
||||
[SecurityCritical]
|
||||
private SafeHBitmapHandle() : base(true) {
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public SafeHBitmapHandle(IntPtr preexistingHandle) : base(true) {
|
||||
SetHandle(preexistingHandle);
|
||||
|
@ -86,6 +90,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
|||
/// A hRegion SafeHandle implementation
|
||||
/// </summary>
|
||||
public class SafeRegionHandle : SafeObjectHandle {
|
||||
[SecurityCritical]
|
||||
private SafeRegionHandle() : base(true) {
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public SafeRegionHandle(IntPtr preexistingHandle) : base(true) {
|
||||
SetHandle(preexistingHandle);
|
||||
|
@ -96,6 +104,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
|||
/// A dibsection SafeHandle implementation
|
||||
/// </summary>
|
||||
public class SafeDibSectionHandle : SafeObjectHandle {
|
||||
[SecurityCritical]
|
||||
private SafeDibSectionHandle() : base(true) {
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public SafeDibSectionHandle(IntPtr preexistingHandle) : base(true) {
|
||||
SetHandle(preexistingHandle);
|
||||
|
@ -112,6 +124,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
|||
|
||||
private SafeHandle hdc;
|
||||
|
||||
[SecurityCritical]
|
||||
private SafeSelectObjectHandle() : base(true) {
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public SafeSelectObjectHandle(SafeDCHandle hdc, SafeHandle newHandle) : base(true) {
|
||||
this.hdc = hdc;
|
||||
|
@ -135,6 +151,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
|||
[DllImport("gdi32", SetLastError = true)]
|
||||
private static extern bool DeleteDC(IntPtr hDC);
|
||||
|
||||
[SecurityCritical]
|
||||
private SafeCompatibleDCHandle() : base(true) {
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public SafeCompatibleDCHandle(IntPtr preexistingHandle) : base(true) {
|
||||
SetHandle(preexistingHandle);
|
||||
|
@ -155,6 +175,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
|||
public class SafeDeviceContextHandle : SafeDCHandle {
|
||||
private Graphics graphics = null;
|
||||
|
||||
[SecurityCritical]
|
||||
private SafeDeviceContextHandle() : base(true) {
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public SafeDeviceContextHandle(Graphics graphics, IntPtr preexistingHandle) : base(true) {
|
||||
this.graphics = graphics;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue