88
99namespace 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 {
0 commit comments