Skip to content

Commit 776f903

Browse files
committed
updated for v1.4
1 parent fba5317 commit 776f903

File tree

3 files changed

+14
-45
lines changed

3 files changed

+14
-45
lines changed

FinalShot/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Runtime.CompilerServices;
33

44
[assembly: AssemblyCopyright("© 2024 - NS Tech Bytes 🇵🇰")]
5-
[assembly: AssemblyVersion("1.3.0.0")]
5+
[assembly: AssemblyVersion("1.4.0.0")]
66

77
// Do not change the entries below!
88
#if X64

FinalShot/FinalShot.cs

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
namespace PluginScreenshot
1010
{
11-
// Logger class for writing debug messages to a file.
1211
public static class Logger
1312
{
1413
public static bool DebugEnabled = false;
@@ -23,36 +22,32 @@ public static void Log(string message)
2322
string logMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + message + Environment.NewLine;
2423
File.AppendAllText(LogFilePath, logMessage);
2524
}
26-
catch { /* Ignore logging errors */ }
25+
catch {}
2726
}
2827
}
2928
}
3029

3130
internal class Measure
3231
{
33-
private string savePath; // Save path from the .ini file
34-
private string finishAction = ""; // Action to execute after screenshot is taken
35-
private Rainmeter.API api; // Rainmeter API reference
36-
public static bool showCursor; // Include cursor in the screenshot
37-
38-
// Predefined coordinates for -ps command.
32+
private string savePath;
33+
private string finishAction = "";
34+
private Rainmeter.API api;
35+
public static bool showCursor;
3936
private int predefX;
4037
private int predefY;
4138
private int predefWidth;
4239
private int predefHeight;
4340

44-
// DllImport for setting thread DPI awareness context.
41+
4542
[DllImport("user32.dll")]
4643
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
4744
private static readonly IntPtr DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = new IntPtr(-4);
4845

49-
// DllImport for getting cursor information and drawing the cursor.
5046
[DllImport("user32.dll")]
5147
private static extern bool GetCursorInfo(out CURSORINFO pci);
5248
[DllImport("user32.dll")]
5349
private static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
5450

55-
// Structs for cursor information.
5651
[StructLayout(LayoutKind.Sequential)]
5752
private struct POINT { public int x, y; }
5853
[StructLayout(LayoutKind.Sequential)]
@@ -63,7 +58,6 @@ private struct CURSORINFO
6358
public POINT ptScreenPos;
6459
}
6560

66-
// Constant for cursor information.
6761
private const int CURSOR_SHOWING = 0x00000001;
6862

6963
public Measure(Rainmeter.API api)
@@ -73,17 +67,13 @@ public Measure(Rainmeter.API api)
7367

7468
public void Reload(Rainmeter.API api, ref double maxValue)
7569
{
76-
// Read configuration from the .ini file.
7770
savePath = api.ReadString("SavePath", "");
7871
finishAction = api.ReadString("ScreenshotFinishAction", "");
7972
showCursor = api.ReadInt("ShowCursor", 0) > 0;
80-
8173
predefX = api.ReadInt("PredefX", 0);
8274
predefY = api.ReadInt("PredefY", 0);
8375
predefWidth = api.ReadInt("PredefWidth", 0);
8476
predefHeight = api.ReadInt("PredefHeight", 0);
85-
86-
// Read debugging options.
8777
bool debugEnabled = api.ReadInt("DebugLog", 0) == 1;
8878
Logger.DebugEnabled = debugEnabled;
8979
string debugPath = api.ReadString("DebugLogPath", "");
@@ -93,7 +83,6 @@ public void Reload(Rainmeter.API api, ref double maxValue)
9383
Logger.Log("Reload complete. SavePath: " + savePath);
9484
}
9585

96-
// Helper: Draw the cursor on the screenshot.
9786
public static void DrawCursor(Graphics g, Rectangle bounds)
9887
{
9988
var ci = new CURSORINFO { cbSize = Marshal.SizeOf(typeof(CURSORINFO)) };
@@ -108,7 +97,6 @@ public static void DrawCursor(Graphics g, Rectangle bounds)
10897
}
10998
}
11099

111-
// Helper: Run code in a DPI-aware thread context.
112100
private void RunWithHighDpiContext(Action action)
113101
{
114102
IntPtr oldContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
@@ -138,7 +126,6 @@ private void TakeScreenshot()
138126

139127
RunWithHighDpiContext(() =>
140128
{
141-
// Capture the entire virtual screen.
142129
Rectangle bounds = SystemInformation.VirtualScreen;
143130
Logger.Log("Full screenshot bounds: " + bounds);
144131
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
@@ -164,7 +151,6 @@ private void TakeCustomScreenshot()
164151
return;
165152
}
166153
Logger.Log("Launching custom screenshot form.");
167-
// Launch the custom screenshot form.
168154
Application.Run(new CustomScreenshotForm(savePath, ExecuteFinishAction));
169155
}
170156

@@ -224,38 +210,32 @@ private void ExecuteFinishAction()
224210
}
225211
}
226212

227-
// Custom screenshot form – now DPI aware so that mouse events report physical coordinates.
228-
// This version attempts to handle multi-monitor selections with different DPI by compositing portions.
229213
public class CustomScreenshotForm : Form
230214
{
231-
private Point startPoint; // Physical coordinates where mouse is pressed.
232-
private Rectangle selection; // Rectangle defined by the drag (in client coordinates).
215+
private Point startPoint;
216+
private Rectangle selection;
233217
private string savePath;
234218
private bool isSelecting;
235219
private Action finishAction;
236220

237-
// DllImport for setting thread DPI awareness.
238221
[DllImport("user32.dll")]
239222
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
240223
private static readonly IntPtr DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = new IntPtr(-4);
241224

242225
public CustomScreenshotForm(string savePath, Action finishAction)
243226
{
244-
// Set thread DPI awareness so that mouse events are in physical pixels.
245227
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
246228

247229
this.savePath = savePath;
248230
this.finishAction = finishAction;
249231
this.DoubleBuffered = true;
250232
this.FormBorderStyle = FormBorderStyle.None;
251-
// Cover the entire virtual screen.
252233
this.Bounds = SystemInformation.VirtualScreen;
253234
this.BackColor = Color.Black;
254235
this.Opacity = 0.25;
255236
this.TopMost = true;
256237
this.Cursor = Cursors.Cross;
257238
this.StartPosition = FormStartPosition.Manual;
258-
// Position the form at the virtual screen's top-left.
259239
this.Location = SystemInformation.VirtualScreen.Location;
260240

261241
this.MouseDown += OnMouseDown;
@@ -268,7 +248,6 @@ public CustomScreenshotForm(string savePath, Action finishAction)
268248

269249
private void OnMouseDown(object sender, MouseEventArgs e)
270250
{
271-
// With DPI awareness enabled, e.Location is in physical pixels.
272251
startPoint = e.Location;
273252
isSelecting = true;
274253
Logger.Log("Custom screenshot started at: " + startPoint);
@@ -278,7 +257,6 @@ private void OnMouseMove(object sender, MouseEventArgs e)
278257
{
279258
if (isSelecting)
280259
{
281-
// Update selection rectangle based on current mouse position.
282260
int x = Math.Min(startPoint.X, e.X);
283261
int y = Math.Min(startPoint.Y, e.Y);
284262
int width = Math.Abs(startPoint.X - e.X);
@@ -299,7 +277,6 @@ private void OnMouseUp(object sender, MouseEventArgs e)
299277
return;
300278
}
301279
this.Hide();
302-
// Calculate the capture rectangle in absolute physical coordinates.
303280
Rectangle captureRect = new Rectangle(
304281
this.Bounds.Left + selection.X,
305282
this.Bounds.Top + selection.Y,
@@ -314,8 +291,7 @@ private void OnPaint(object sender, PaintEventArgs e)
314291
{
315292
if (isSelecting)
316293
{
317-
// Draw only a bold dashed rectangle (no fill).
318-
using (Pen borderPen = new Pen(Color.Red, 3))
294+
using (Pen borderPen = new Pen(Color.Blue, 3))
319295
{
320296
borderPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
321297
e.Graphics.DrawRectangle(borderPen, selection);
@@ -326,11 +302,9 @@ private void OnPaint(object sender, PaintEventArgs e)
326302
private void TakeScreenshot(Rectangle captureRect)
327303
{
328304
Logger.Log("Taking screenshot from rectangle: " + captureRect);
329-
// Check if the captureRect is completely contained in one monitor.
330305
bool isContained = false;
331306
foreach (Screen screen in Screen.AllScreens)
332307
{
333-
// Use screen.Bounds which should be in physical pixels for a DPI-aware process.
334308
if (screen.Bounds.Contains(captureRect))
335309
{
336310
isContained = true;
@@ -340,13 +314,11 @@ private void TakeScreenshot(Rectangle captureRect)
340314

341315
if (isContained)
342316
{
343-
// Single-capture mode.
344317
Logger.Log("Capture rectangle contained in one monitor; using single capture.");
345318
TakeSingleCapture(captureRect);
346319
}
347320
else
348321
{
349-
// Composite capture mode: the selection spans multiple monitors.
350322
Logger.Log("Capture rectangle spans multiple monitors; composing capture from parts.");
351323
TakeCompositeCapture(captureRect);
352324
}
@@ -355,7 +327,6 @@ private void TakeScreenshot(Rectangle captureRect)
355327

356328
private void TakeSingleCapture(Rectangle rect)
357329
{
358-
// Use DPI-aware context for capturing.
359330
IntPtr oldContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
360331
try
361332
{
@@ -378,18 +349,15 @@ private void TakeSingleCapture(Rectangle rect)
378349

379350
private void TakeCompositeCapture(Rectangle rect)
380351
{
381-
// Create a final composite bitmap of the entire selection.
382352
Bitmap finalBitmap = new Bitmap(rect.Width, rect.Height);
383353
using (Graphics finalGraphics = Graphics.FromImage(finalBitmap))
384354
{
385-
// Iterate over all screens and capture the intersections.
386355
foreach (Screen screen in Screen.AllScreens)
387356
{
388357
Rectangle intersect = Rectangle.Intersect(rect, screen.Bounds);
389358
if (intersect.Width > 0 && intersect.Height > 0)
390359
{
391360
Logger.Log("Capturing intersection with screen (" + screen.DeviceName + "): " + intersect);
392-
// Capture this sub-region.
393361
using (Bitmap part = new Bitmap(intersect.Width, intersect.Height))
394362
{
395363
using (Graphics g = Graphics.FromImage(part))
@@ -398,7 +366,6 @@ private void TakeCompositeCapture(Rectangle rect)
398366
if (Measure.showCursor)
399367
Measure.DrawCursor(finalGraphics, rect);
400368
}
401-
// Draw this part into the final composite image.
402369
finalGraphics.DrawImage(part, new Rectangle(intersect.Left - rect.Left, intersect.Top - rect.Top, intersect.Width, intersect.Height));
403370
}
404371
}
@@ -436,14 +403,13 @@ private ImageFormat GetImageFormat(string path)
436403
case ".tiff":
437404
return ImageFormat.Tiff;
438405
default:
439-
return ImageFormat.Png; // fallback
406+
return ImageFormat.Png;
440407
}
441408
}
442409
}
443410

444411
public static class Plugin
445412
{
446-
// Do not mark the entire process as DPI aware to avoid affecting Rainmeter's UI.
447413
[DllExport]
448414
public static void Initialize(ref IntPtr data, IntPtr rm)
449415
{

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,16 @@ Define a measure that loads the plugin and configures the screenshot options:
5757
[MeasureScreenshot]
5858
Measure=Plugin
5959
Plugin=FinalShot
60+
;!Note supported Image extension are .png(default),jpg,.jpeg,.tiff,.bmp.
6061
SavePath=#@#Screenshots\screenshot.png
6162
PredefX=100
6263
PredefY=100
6364
PredefWidth=400
6465
PredefHeight=300
6566
DebugLog=1
6667
DebugLogPath=#@#FinalShotDebug.log
68+
;Capture Mouse Cursor in Screenshot.
69+
ShowCursor=1
6770
```
6871

6972
### 📸 **Screenshot Modes:**

0 commit comments

Comments
 (0)