/// <summary> /// Create an animation for the zoomer, depending on if it's active or not. /// </summary> void InitializeZoomer(bool isOn) { if (isOn) { // Initialize the zoom with a invalid position zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, EasingMode.EaseOut); VerifyZoomAnimation(cursorPos, false); } else if (zoomAnimator != null) { zoomAnimator.ChangeDestination(new Rectangle(Point.Empty, Size.Empty), FramesForMillis(1000)); } }
/// <summary> /// Create an animation for the zoomer, depending on if it's active or not. /// </summary> private void InitializeZoomer(bool isOn) { if (isOn) { // Initialize the zoom with a invalid position _zoomAnimator = new RectangleAnimator(NativeRect.Empty, new NativeRect(int.MaxValue, int.MaxValue, NativeSize.Empty), FramesForMillis(1000), EasingTypes.Quintic, EasingModes.EaseOut); VerifyZoomAnimation(_cursorPos, false); } else { _zoomAnimator?.ChangeDestination(new NativeRect(NativePoint.Empty, NativeSize.Empty), FramesForMillis(1000)); } }
/// <summary> /// Checks if the Zoom area can move there where it wants to go, change direction if not. /// </summary> /// <param name="pos">preferred destination location for the zoom area</param> /// <param name="allowZoomOverCaptureRect"> /// false to try to find a location which is neither out of screen bounds nor /// intersects with the selected rectangle /// </param> private void VerifyZoomAnimation(NativePoint pos, bool allowZoomOverCaptureRect) { var screenBounds = DisplayInfo.GetBounds(MousePosition); // convert to be relative to top left corner of all screen bounds screenBounds = screenBounds.MoveTo(WindowCapture.GetLocationRelativeToScreenBounds(screenBounds.Location)); var relativeZoomSize = Math.Min(screenBounds.Width, screenBounds.Height) / 5; // Make sure the final size is a plural of 4, this makes it look better relativeZoomSize = relativeZoomSize - relativeZoomSize % 4; var zoomSize = new NativeSize(relativeZoomSize, relativeZoomSize); var zoomOffset = new NativePoint(20, 20); var targetRectangle = _zoomAnimator.Final.Offset(pos); if (screenBounds.Contains(targetRectangle) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(targetRectangle))) { return; } var destinationLocation = NativePoint.Empty; var tl = new NativeRect(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); var tr = new NativeRect(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); var bl = new NativeRect(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); var br = new NativeRect(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) { destinationLocation = new NativePoint(zoomOffset.X, zoomOffset.Y); } else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) { destinationLocation = new NativePoint(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); } else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) { destinationLocation = new NativePoint(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); } else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) { destinationLocation = new NativePoint(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); } if (destinationLocation == NativePoint.Empty && !allowZoomOverCaptureRect) { VerifyZoomAnimation(pos, true); } else { _zoomAnimator.ChangeDestination(new NativeRect(destinationLocation, zoomSize)); } }
/// <summary> /// Checks if the Zoom area can move there where it wants to go, change direction if not. /// </summary> /// <param name="pos">preferred destination location for the zoom area</param> /// <param name="allowZoomOverCaptureRect">false to try to find a location which is neither out of screen bounds nor intersects with the selected rectangle</param> private void VerifyZoomAnimation(Point pos, bool allowZoomOverCaptureRect) { Rectangle screenBounds = Screen.GetBounds(MousePosition); // convert to be relative to top left corner of all screen bounds screenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(screenBounds.Location); int relativeZoomSize = Math.Min(screenBounds.Width, screenBounds.Height) / 5; // Make sure the final size is a plural of 4, this makes it look better relativeZoomSize = relativeZoomSize - relativeZoomSize % 4; Size zoomSize = new Size(relativeZoomSize, relativeZoomSize); Point zoomOffset = new Point(20, 20); Rectangle targetRectangle = _zoomAnimator.Final; targetRectangle.Offset(pos); if (!screenBounds.Contains(targetRectangle) || (!allowZoomOverCaptureRect && _captureRect.IntersectsWith(targetRectangle))) { Point destinationLocation = Point.Empty; Rectangle tl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); Rectangle tr = new Rectangle(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); Rectangle bl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); Rectangle br = new Rectangle(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) { destinationLocation = new Point(zoomOffset.X, zoomOffset.Y); } else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) { destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); } else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) { destinationLocation = new Point(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); } else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) { destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); } if (destinationLocation == Point.Empty && !allowZoomOverCaptureRect) { VerifyZoomAnimation(pos, true); } else { _zoomAnimator.ChangeDestination(new Rectangle(destinationLocation, zoomSize)); } } }
/// <summary> /// Create an animation for the zoomer, depending on if it's active or not. /// </summary> private void InitializeZoomer(bool isOn) { if (isOn) { var screenBounds = DisplayInfo.GetBounds(MousePosition); var zoomerSize = CalculateZoomSize(screenBounds); var initialPosition = new NativePoint(20, 20); // Initialize the zoom with an initial position _zoomAnimator = new RectangleAnimator(new NativeRect(initialPosition, NativeSize.Empty), new NativeRect(initialPosition, zoomerSize), FramesForMillis(1000), EasingTypes.Quintic, EasingModes.EaseOut); VerifyZoomAnimation(_cursorPos, false); } else { _zoomAnimator?.ChangeDestination(new NativeRect(NativePoint.Empty, NativeSize.Empty), FramesForMillis(1000)); } }
/// <summary> /// Checks if the Zoom area can move there where it wants to go, change direction if not. /// </summary> /// <param name="pos">preferred destination location for the zoom area</param> /// <param name="allowZoomOverCaptureRect"> /// false to try to find a location which is neither out of screen bounds nor /// intersects with the selected rectangle /// </param> private void VerifyZoomAnimation(NativePoint pos, bool allowZoomOverCaptureRect) { var screenBounds = DisplayInfo.GetBounds(MousePosition); var zoomSize = CalculateZoomSize(screenBounds); var zoomOffset = new NativePoint(20, 20); var targetRectangle = _zoomAnimator.Final.Offset(pos); if (screenBounds.Contains(targetRectangle) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(targetRectangle))) { return; } var destinationLocation = NativePoint.Empty; var tl = new NativeRect(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); var tr = new NativeRect(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); var bl = new NativeRect(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); var br = new NativeRect(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) { destinationLocation = new NativePoint(zoomOffset.X, zoomOffset.Y); } else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) { destinationLocation = new NativePoint(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); } else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) { destinationLocation = new NativePoint(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); } else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) { destinationLocation = new NativePoint(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); } if (destinationLocation == NativePoint.Empty && !allowZoomOverCaptureRect) { VerifyZoomAnimation(pos, true); } else { _zoomAnimator.ChangeDestination(new NativeRect(destinationLocation, zoomSize)); } }
/// <summary> /// update the frame, this only invalidates /// </summary> protected override void Animate() { var lastPos = _cursorPos; _cursorPos = _mouseMovePos; if (SelectedCaptureWindow != null && lastPos.Equals(_cursorPos) && !IsAnimating(_zoomAnimator) && !IsAnimating(_windowAnimator)) { return; } var lastWindow = SelectedCaptureWindow; var horizontalMove = false; var verticalMove = false; if (lastPos.X != _cursorPos.X) { horizontalMove = true; } if (lastPos.Y != _cursorPos.Y) { verticalMove = true; } if (UsedCaptureMode == CaptureMode.Region && _mouseDown) { _captureRect = new NativeRect(_cursorPos.X, _cursorPos.Y, _mX - _cursorPos.X, _mY - _cursorPos.Y).Normalize(); } // Iterate over the found windows and check if the current location is inside a window var cursorPosition = Cursor.Position; SelectedCaptureWindow = null; // Store the top window IInteropWindow selectedTopWindow = null; foreach (var window in _windows) { if (window.Handle == Handle) { // Ignore us continue; } if (!window.GetInfo().Bounds.Contains(cursorPosition)) { continue; } selectedTopWindow = window; SelectedCaptureWindow = window; // Only go over the children if we are in window mode if (CaptureMode.Window != UsedCaptureMode) { break; } // Find the child window which is under the mouse // Start with the parent, drill down var selectedChildWindow = window; // TODO: Limit the levels we go down? do { // Drill down, via the ZOrder var tmpChildWindow = selectedChildWindow .GetZOrderedChildren() .FirstOrDefault(interopWindow => interopWindow.GetInfo().Bounds.Contains(cursorPosition)); if (tmpChildWindow == null) { break; } selectedChildWindow = tmpChildWindow; } while (true); // Assign the found child window SelectedCaptureWindow = selectedChildWindow; break; } // Test if something changed if (SelectedCaptureWindow != null && !SelectedCaptureWindow.Equals(lastWindow)) { _capture.CaptureDetails.Title = selectedTopWindow.Text; _capture.CaptureDetails.AddMetaData("windowtitle", selectedTopWindow.Text); if (UsedCaptureMode == CaptureMode.Window) { // Recreate the WindowScroller, if this is enabled, so we can detect if we can scroll if (Conf.IsScrollingCaptureEnabled) { WindowScroller = SelectedCaptureWindow.GetWindowScroller(ScrollBarTypes.Vertical); if (WindowScroller == null) { foreach (var interopWindow in SelectedCaptureWindow.GetChildren()) { interopWindow.Dump(); } WindowScroller = SelectedCaptureWindow.GetChildren().Select(child => child.GetWindowScroller(ScrollBarTypes.Vertical)).FirstOrDefault(scroller => scroller != null); } } // We store the bound of the selected (child) window // If it's maximized we take the client-bounds, otherwise we have parts we should not copy. if (SelectedCaptureWindow.IsMaximized()) { _captureRect = SelectedCaptureWindow.GetInfo().ClientBounds; } else { _captureRect = SelectedCaptureWindow.GetInfo().Bounds; } // Make sure the bounds fit to it's parent, some windows are bigger than their parent // But only for non popups if (!SelectedCaptureWindow.GetInfo().Style.HasFlag(WindowStyleFlags.WS_POPUP)) { var parent = SelectedCaptureWindow.GetParent(); while (parent != IntPtr.Zero) { var parentWindow = InteropWindowFactory.CreateFor(parent); _captureRect = _captureRect.Intersect(parentWindow.GetInfo().Bounds); parent = parentWindow.GetParent(); } } // As the ClientRectangle is in screen coordinates and not in bitmap coordinates, we need to correct. _captureRect = _captureRect.Offset(-_capture.ScreenBounds.Location.X, -_capture.ScreenBounds.Location.Y); } } NativeRectFloat invalidateRectangle; if (_mouseDown && UsedCaptureMode != CaptureMode.Window) { var x1 = Math.Min(_mX, lastPos.X); var x2 = Math.Max(_mX, lastPos.X); var y1 = Math.Min(_mY, lastPos.Y); var y2 = Math.Max(_mY, lastPos.Y); x1 = Math.Min(x1, _cursorPos.X); x2 = Math.Max(x2, _cursorPos.X); y1 = Math.Min(y1, _cursorPos.Y); y2 = Math.Max(y2, _cursorPos.Y); // Safety correction x2 += 2; y2 += 2; // Here we correct for text-size // Calculate the size var textForWidth = Math.Max(Math.Abs(_mX - _cursorPos.X), Math.Abs(_mX - lastPos.X)); var textForHeight = Math.Max(Math.Abs(_mY - _cursorPos.Y), Math.Abs(_mY - lastPos.Y)); using (var rulerFont = new Font(FontFamily.GenericSansSerif, 8)) { var textWidth = TextRenderer.MeasureText(textForWidth.ToString(CultureInfo.InvariantCulture), rulerFont); x1 -= textWidth.Width + 15; var textHeight = TextRenderer.MeasureText(textForHeight.ToString(CultureInfo.InvariantCulture), rulerFont); y1 -= textHeight.Height + 10; } invalidateRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); Invalidate(invalidateRectangle); } else if (UsedCaptureMode != CaptureMode.Window) { var allScreenBounds = WindowCapture.GetScreenBounds(); allScreenBounds = allScreenBounds.MoveTo(WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location)); if (verticalMove) { // Before invalidateRectangle = new NativeRect(allScreenBounds.Left, lastPos.Y - 2, Width + 2, 45).Normalize(); Invalidate(invalidateRectangle); // After invalidateRectangle = new NativeRect(allScreenBounds.Left, _cursorPos.Y - 2, Width + 2, 45).Normalize(); Invalidate(invalidateRectangle); } if (horizontalMove) { // Before invalidateRectangle = new NativeRect(lastPos.X - 2, allScreenBounds.Top, 75, Height + 2).Normalize(); Invalidate(invalidateRectangle); // After invalidateRectangle = new NativeRect(_cursorPos.X - 2, allScreenBounds.Top, 75, Height + 2).Normalize(); Invalidate(invalidateRectangle); } } else if (SelectedCaptureWindow != null && !SelectedCaptureWindow.Equals(lastWindow)) { // Window changed, animate from current to newly selected window _windowAnimator.ChangeDestination(_captureRect, FramesForMillis(700)); } // always animate the Window area through to the last frame, so we see the fade-in/out untill the end // Using a safety "offset" to make sure the text is invalidated too const int safetySize = 30; // Check if the animation needs to be drawn if (IsAnimating(_windowAnimator)) { invalidateRectangle = _windowAnimator.Current.Inflate(safetySize, safetySize); Invalidate(invalidateRectangle); invalidateRectangle = _windowAnimator.Next().Inflate(safetySize, safetySize); Invalidate(invalidateRectangle); // Check if this was the last of the windows animations in the normal region capture. if (UsedCaptureMode != CaptureMode.Window && !IsAnimating(_windowAnimator)) { Invalidate(); } } if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || UsedCaptureMode != CaptureMode.Window)) { // Make sure we invalidate the old zoom area invalidateRectangle = _zoomAnimator.Current.Offset(lastPos); Invalidate(invalidateRectangle); // Only verify if we are really showing the zoom, not the outgoing animation if (Conf.ZoomerEnabled && UsedCaptureMode != CaptureMode.Window) { VerifyZoomAnimation(_cursorPos, false); } // The following logic is not needed, next always returns the current if there are no frames left // but it makes more sense if we want to change something in the logic invalidateRectangle = IsAnimating(_zoomAnimator) ? _zoomAnimator.Next() : _zoomAnimator.Current; Invalidate(invalidateRectangle.Offset(_cursorPos)); } // Force update "now" Update(); }
/// <summary> /// Handle the key down event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void CaptureFormKeyDown(object sender, KeyEventArgs e) { var step = _isCtrlPressed ? 10 : 1; switch (e.KeyCode) { case Keys.Up: Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y - step); break; case Keys.Down: Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y + step); break; case Keys.Left: Cursor.Position = new Point(Cursor.Position.X - step, Cursor.Position.Y); break; case Keys.Right: Cursor.Position = new Point(Cursor.Position.X + step, Cursor.Position.Y); break; case Keys.ShiftKey: // Fixmode if (_fixMode == FixMode.None) { _fixMode = FixMode.Initiated; } break; case Keys.ControlKey: _isCtrlPressed = true; break; case Keys.Escape: // Cancel DialogResult = DialogResult.Cancel; break; case Keys.M: // Toggle mouse cursor _capture.CursorVisible = !_capture.CursorVisible; Invalidate(); break; //// TODO: Enable when the screen capture code works reliable //case Keys.V: // // Video // if (capture.CaptureDetails.CaptureMode != CaptureMode.Video) { // capture.CaptureDetails.CaptureMode = CaptureMode.Video; // } else { // capture.CaptureDetails.CaptureMode = captureMode; // } // Invalidate(); // break; case Keys.Z: if (UsedCaptureMode == CaptureMode.Region) { // Toggle zoom Conf.ZoomerEnabled = !Conf.ZoomerEnabled; InitializeZoomer(Conf.ZoomerEnabled); Invalidate(); } break; case Keys.D: if (UsedCaptureMode == CaptureMode.Window) { // Toggle debug _showDebugInfo = !_showDebugInfo; Invalidate(); } break; case Keys.Space: // Toggle capture mode switch (UsedCaptureMode) { case CaptureMode.Region: // Set the window capture mode UsedCaptureMode = CaptureMode.Window; // "Fade out" Zoom InitializeZoomer(false); // "Fade in" window _windowAnimator = new RectangleAnimator(new NativeRect(_cursorPos, NativeSize.Empty), _captureRect, FramesForMillis(700), EasingTypes.Quintic, EasingModes.EaseOut); _captureRect = Rectangle.Empty; Invalidate(); break; case CaptureMode.Window: // Set the region capture mode UsedCaptureMode = CaptureMode.Region; // "Fade out" window _windowAnimator.ChangeDestination(new NativeRect(_cursorPos, NativeSize.Empty), FramesForMillis(700)); // Fade in zoom InitializeZoomer(Conf.ZoomerEnabled); _captureRect = Rectangle.Empty; Invalidate(); break; } SelectedCaptureWindow = null; OnMouseMove(this, new MouseEventArgs(MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0)); break; case Keys.Return: // Confirm if (UsedCaptureMode == CaptureMode.Window) { DialogResult = DialogResult.OK; } else if (!_mouseDown) { StartSelecting(); } else { FinishSelecting(); } break; case Keys.F: ToFront = !ToFront; TopMost = !TopMost; break; } }
/// <summary> /// update the frame, this only invalidates /// </summary> protected override void Animate() { Point lastPos = cursorPos.Clone(); cursorPos = mouseMovePos.Clone(); if (selectedCaptureWindow != null && lastPos.Equals(cursorPos) && !isAnimating(zoomAnimator) && !isAnimating(windowAnimator)) { return; } Rectangle lastCaptureRect = new Rectangle(captureRect.Location, captureRect.Size); WindowDetails lastWindow = selectedCaptureWindow; bool horizontalMove = false; bool verticalMove = false; if (lastPos.X != cursorPos.X) { horizontalMove = true; } if (lastPos.Y != cursorPos.Y) { verticalMove = true; } if (captureMode == CaptureMode.Region && mouseDown) { captureRect = GuiRectangle.GetGuiRectangle(cursorPos.X, cursorPos.Y, mX - cursorPos.X, mY - cursorPos.Y); } // Iterate over the found windows and check if the current location is inside a window Point cursorPosition = Cursor.Position; selectedCaptureWindow = null; lock (windows) { foreach (WindowDetails window in windows) { if (window.Contains(cursorPosition)) { // Only go over the children if we are in window mode if (CaptureMode.Window == captureMode) { selectedCaptureWindow = window.FindChildUnderPoint(cursorPosition); } else { selectedCaptureWindow = window; } break; } } } if (selectedCaptureWindow != null && !selectedCaptureWindow.Equals(lastWindow)) { capture.CaptureDetails.Title = selectedCaptureWindow.Text; capture.CaptureDetails.AddMetaData("windowtitle", selectedCaptureWindow.Text); if (captureMode == CaptureMode.Window) { // Here we want to capture the window which is under the mouse captureRect = selectedCaptureWindow.WindowRectangle; // As the ClientRectangle is not in Bitmap coordinates, we need to correct. captureRect.Offset(-capture.ScreenBounds.Location.X, -capture.ScreenBounds.Location.Y); } } Rectangle invalidateRectangle = Rectangle.Empty; if (mouseDown && (captureMode != CaptureMode.Window)) { int x1 = Math.Min(mX, lastPos.X); int x2 = Math.Max(mX, lastPos.X); int y1 = Math.Min(mY, lastPos.Y); int y2 = Math.Max(mY, lastPos.Y); x1 = Math.Min(x1, cursorPos.X); x2 = Math.Max(x2, cursorPos.X); y1 = Math.Min(y1, cursorPos.Y); y2 = Math.Max(y2, cursorPos.Y); // Safety correction x2 += 2; y2 += 2; // Here we correct for text-size // Calculate the size int textForWidth = Math.Max(Math.Abs(mX - cursorPos.X), Math.Abs(mX - lastPos.X)); int textForHeight = Math.Max(Math.Abs(mY - cursorPos.Y), Math.Abs(mY - lastPos.Y)); using (Font rulerFont = new Font(FontFamily.GenericSansSerif, 8)) { Size measureWidth = TextRenderer.MeasureText(textForWidth.ToString(), rulerFont); x1 -= measureWidth.Width + 15; Size measureHeight = TextRenderer.MeasureText(textForHeight.ToString(), rulerFont); y1 -= measureWidth.Height + 10; } invalidateRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); Invalidate(invalidateRectangle); } else if (captureMode != CaptureMode.Window) { if (!isTerminalServerSession) { Rectangle allScreenBounds = WindowCapture.GetScreenBounds(); allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location); if (verticalMove) { // Before invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, lastPos.Y - 2, this.Width + 2, 45); Invalidate(invalidateRectangle); // After invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, cursorPos.Y - 2, this.Width + 2, 45); Invalidate(invalidateRectangle); } if (horizontalMove) { // Before invalidateRectangle = GuiRectangle.GetGuiRectangle(lastPos.X - 2, allScreenBounds.Top, 75, this.Height + 2); Invalidate(invalidateRectangle); // After invalidateRectangle = GuiRectangle.GetGuiRectangle(cursorPos.X - 2, allScreenBounds.Top, 75, this.Height + 2); Invalidate(invalidateRectangle); } } } else { if (selectedCaptureWindow != null && !selectedCaptureWindow.Equals(lastWindow)) { // Window changes, make new animation from current to target windowAnimator.ChangeDestination(captureRect, FramesForMillis(700)); } } // always animate the Window area through to the last frame, so we see the fade-in/out untill the end // Using a safety "offset" to make sure the text is invalidated too const int SAFETY_SIZE = 30; // Check if the if (isAnimating(windowAnimator)) { invalidateRectangle = windowAnimator.Current; invalidateRectangle.Inflate(SAFETY_SIZE, SAFETY_SIZE); Invalidate(invalidateRectangle); invalidateRectangle = windowAnimator.Next(); invalidateRectangle.Inflate(SAFETY_SIZE, SAFETY_SIZE); Invalidate(invalidateRectangle); // Check if this was the last of the windows animations in the normal region capture. if (captureMode != CaptureMode.Window && !isAnimating(windowAnimator)) { Invalidate(); } } if (zoomAnimator != null && (isAnimating(zoomAnimator) || captureMode != CaptureMode.Window)) { // Make sure we invalidate the old zoom area invalidateRectangle = zoomAnimator.Current; invalidateRectangle.Offset(lastPos); Invalidate(invalidateRectangle); // Only verify if we are really showing the zoom, not the outgoing animation if (conf.ZoomerEnabled && captureMode != CaptureMode.Window) { VerifyZoomAnimation(cursorPos, false); } // The following logic is not needed, next always returns the current if there are no frames left // but it makes more sense if we want to change something in the logic if (isAnimating(zoomAnimator)) { invalidateRectangle = zoomAnimator.Next(); } else { invalidateRectangle = zoomAnimator.Current; } invalidateRectangle.Offset(cursorPos); Invalidate(invalidateRectangle); } // Force update "now" Update(); }
/// <summary> /// Handle the key down event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void CaptureFormKeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Up: Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y - 1); break; case Keys.Down: Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y + 1); break; case Keys.Left: Cursor.Position = new Point(Cursor.Position.X - 1, Cursor.Position.Y); break; case Keys.Right: Cursor.Position = new Point(Cursor.Position.X + 1, Cursor.Position.Y); break; case Keys.ShiftKey: // Fixmode if (fixMode == FixMode.None) { fixMode = FixMode.Initiated; return; } break; case Keys.Escape: // Cancel DialogResult = DialogResult.Cancel; break; case Keys.M: // Toggle mouse cursor capture.CursorVisible = !capture.CursorVisible; Invalidate(); break; //// TODO: Enable when the screen capture code works reliable //case Keys.V: // // Video // if (capture.CaptureDetails.CaptureMode != CaptureMode.Video) { // capture.CaptureDetails.CaptureMode = CaptureMode.Video; // } else { // capture.CaptureDetails.CaptureMode = captureMode; // } // Invalidate(); // break; case Keys.Z: if (captureMode == CaptureMode.Region) { // Toggle zoom conf.ZoomerEnabled = !conf.ZoomerEnabled; InitializeZoomer(conf.ZoomerEnabled); Invalidate(); } break; case Keys.Space: // Toggle capture mode switch (captureMode) { case CaptureMode.Region: // Set the window capture mode captureMode = CaptureMode.Window; // "Fade out" Zoom InitializeZoomer(false); // "Fade in" window windowAnimator = new RectangleAnimator(new Rectangle(cursorPos, Size.Empty), captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); captureRect = Rectangle.Empty; Invalidate(); break; case CaptureMode.Window: // Set the region capture mode captureMode = CaptureMode.Region; // "Fade out" window windowAnimator.ChangeDestination(new Rectangle(cursorPos, Size.Empty), FramesForMillis(700)); // Fade in zoom InitializeZoomer(conf.ZoomerEnabled); captureRect = Rectangle.Empty; Invalidate(); break; } selectedCaptureWindow = null; OnMouseMove(this, new MouseEventArgs(MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0)); break; case Keys.Return: // Confirm if (captureMode == CaptureMode.Window) { DialogResult = DialogResult.OK; } break; } }
/// <summary> /// Handle the key down event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void CaptureFormKeyDown(object sender, KeyEventArgs e) { int step = _isCtrlPressed ? 10 : 1; switch (e.KeyCode) { case Keys.Up: Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y - step); break; case Keys.Down: Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y + step); break; case Keys.Left: Cursor.Position = new Point(Cursor.Position.X - step, Cursor.Position.Y); break; case Keys.Right: Cursor.Position = new Point(Cursor.Position.X + step, Cursor.Position.Y); break; case Keys.ShiftKey: // Fixmode if (_fixMode == FixMode.None) { _fixMode = FixMode.Initiated; } break; case Keys.ControlKey: _isCtrlPressed = true; break; case Keys.Escape: // Cancel DialogResult = DialogResult.Cancel; break; case Keys.M: // Toggle mouse cursor _capture.CursorVisible = !_capture.CursorVisible; Invalidate(); break; //// TODO: Enable when the screen capture code works reliable //case Keys.V: // // Video // if (capture.CaptureDetails.CaptureMode != CaptureMode.Video) { // capture.CaptureDetails.CaptureMode = CaptureMode.Video; // } else { // capture.CaptureDetails.CaptureMode = captureMode; // } // Invalidate(); // break; case Keys.Z: if (_captureMode == CaptureMode.Region) { // Toggle zoom Conf.ZoomerEnabled = !Conf.ZoomerEnabled; InitializeZoomer(Conf.ZoomerEnabled); Invalidate(); } break; case Keys.Space: // Toggle capture mode switch (_captureMode) { case CaptureMode.Region: // Set the window capture mode _captureMode = CaptureMode.Window; // "Fade out" Zoom InitializeZoomer(false); // "Fade in" window _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); _captureRect = Rectangle.Empty; Invalidate(); break; case CaptureMode.Window: // Set the region capture mode _captureMode = CaptureMode.Region; // "Fade out" window _windowAnimator.ChangeDestination(new Rectangle(_cursorPos, Size.Empty), FramesForMillis(700)); // Fade in zoom InitializeZoomer(Conf.ZoomerEnabled); _captureRect = Rectangle.Empty; Invalidate(); break; } _selectedCaptureWindow = null; OnMouseMove(this, new MouseEventArgs(MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0)); break; case Keys.Return: // Confirm if (_captureMode == CaptureMode.Window) { DialogResult = DialogResult.OK; } else if (!_mouseDown) { HandleMouseDown(); } else if (_mouseDown) { HandleMouseUp(); } break; } }
/// <summary> /// update the frame, this only invalidates /// </summary> protected override void Animate() { Point lastPos = _cursorPos; _cursorPos = _mouseMovePos; if (_selectedCaptureWindow != null && lastPos.Equals(_cursorPos) && !IsAnimating(_zoomAnimator) && !IsAnimating(_windowAnimator)) { return; } WindowDetails lastWindow = _selectedCaptureWindow; bool horizontalMove = false; bool verticalMove = false; if (lastPos.X != _cursorPos.X) { horizontalMove = true; } if (lastPos.Y != _cursorPos.Y) { verticalMove = true; } if (_captureMode == CaptureMode.Region && _mouseDown) { _captureRect = GuiRectangle.GetGuiRectangle(_cursorPos.X, _cursorPos.Y, _mX - _cursorPos.X, _mY - _cursorPos.Y); } // Iterate over the found windows and check if the current location is inside a window Point cursorPosition = Cursor.Position; _selectedCaptureWindow = null; lock (_windows) { foreach (var window in _windows) { if (!window.Contains(cursorPosition)) { continue; } // Only go over the children if we are in window mode _selectedCaptureWindow = CaptureMode.Window == _captureMode?window.FindChildUnderPoint(cursorPosition) : window; break; } } if (_selectedCaptureWindow != null && !_selectedCaptureWindow.Equals(lastWindow)) { _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; _capture.CaptureDetails.AddMetaData("windowtitle", _selectedCaptureWindow.Text); if (_captureMode == CaptureMode.Window) { // Here we want to capture the window which is under the mouse _captureRect = _selectedCaptureWindow.WindowRectangle; // As the ClientRectangle is not in Bitmap coordinates, we need to correct. _captureRect.Offset(-_capture.ScreenBounds.Location.X, -_capture.ScreenBounds.Location.Y); } } Rectangle invalidateRectangle; if (_mouseDown && (_captureMode != CaptureMode.Window)) { int x1 = Math.Min(_mX, lastPos.X); int x2 = Math.Max(_mX, lastPos.X); int y1 = Math.Min(_mY, lastPos.Y); int y2 = Math.Max(_mY, lastPos.Y); x1 = Math.Min(x1, _cursorPos.X); x2 = Math.Max(x2, _cursorPos.X); y1 = Math.Min(y1, _cursorPos.Y); y2 = Math.Max(y2, _cursorPos.Y); // Safety correction x2 += 2; y2 += 2; // Here we correct for text-size // Calculate the size int textForWidth = Math.Max(Math.Abs(_mX - _cursorPos.X), Math.Abs(_mX - lastPos.X)); int textForHeight = Math.Max(Math.Abs(_mY - _cursorPos.Y), Math.Abs(_mY - lastPos.Y)); using (Font rulerFont = new Font(FontFamily.GenericSansSerif, 8)) { Size measureWidth = TextRenderer.MeasureText(textForWidth.ToString(CultureInfo.InvariantCulture), rulerFont); x1 -= measureWidth.Width + 15; Size measureHeight = TextRenderer.MeasureText(textForHeight.ToString(CultureInfo.InvariantCulture), rulerFont); y1 -= measureHeight.Height + 10; } invalidateRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); Invalidate(invalidateRectangle); } else if (_captureMode != CaptureMode.Window) { if (!IsTerminalServerSession) { Rectangle allScreenBounds = WindowCapture.GetScreenBounds(); allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location); if (verticalMove) { // Before invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, lastPos.Y - 2, Width + 2, 45); Invalidate(invalidateRectangle); // After invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, _cursorPos.Y - 2, Width + 2, 45); Invalidate(invalidateRectangle); } if (horizontalMove) { // Before invalidateRectangle = GuiRectangle.GetGuiRectangle(lastPos.X - 2, allScreenBounds.Top, 75, Height + 2); Invalidate(invalidateRectangle); // After invalidateRectangle = GuiRectangle.GetGuiRectangle(_cursorPos.X - 2, allScreenBounds.Top, 75, Height + 2); Invalidate(invalidateRectangle); } } } else { if (_selectedCaptureWindow != null && !_selectedCaptureWindow.Equals(lastWindow)) { // Window changes, make new animation from current to target _windowAnimator.ChangeDestination(_captureRect, FramesForMillis(700)); } } // always animate the Window area through to the last frame, so we see the fade-in/out untill the end // Using a safety "offset" to make sure the text is invalidated too const int safetySize = 30; // Check if the animation needs to be drawn if (IsAnimating(_windowAnimator)) { invalidateRectangle = _windowAnimator.Current; invalidateRectangle.Inflate(safetySize, safetySize); Invalidate(invalidateRectangle); invalidateRectangle = _windowAnimator.Next(); invalidateRectangle.Inflate(safetySize, safetySize); Invalidate(invalidateRectangle); // Check if this was the last of the windows animations in the normal region capture. if (_captureMode != CaptureMode.Window && !IsAnimating(_windowAnimator)) { Invalidate(); } } if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || _captureMode != CaptureMode.Window)) { // Make sure we invalidate the old zoom area invalidateRectangle = _zoomAnimator.Current; invalidateRectangle.Offset(lastPos); Invalidate(invalidateRectangle); // Only verify if we are really showing the zoom, not the outgoing animation if (Conf.ZoomerEnabled && _captureMode != CaptureMode.Window) { VerifyZoomAnimation(_cursorPos, false); } // The following logic is not needed, next always returns the current if there are no frames left // but it makes more sense if we want to change something in the logic invalidateRectangle = IsAnimating(_zoomAnimator) ? _zoomAnimator.Next() : _zoomAnimator.Current; invalidateRectangle.Offset(_cursorPos); Invalidate(invalidateRectangle); } // Force update "now" Update(); }