Replaced the more expensive calls to DateTime.Now with DateTime.UtcNow.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2010-2011
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
40 using System.Drawing.Drawing2D;
41 using System.Drawing.Text;
42 using System.Reflection;
43 using System.Runtime.InteropServices;
44 using System.Windows.Forms;
46 namespace OpenHardwareMonitor.GUI {
48 public class GadgetWindow : NativeWindow, IDisposable {
50 private bool visible = false;
51 private bool lockPositionAndSize = false;
52 private bool alwaysOnTop = false;
53 private byte opacity = 255;
54 private Point location = new Point(100, 100);
55 private Size size = new Size(130, 84);
56 private ContextMenu contextMenu = null;
57 private MethodInfo commandDispatch;
58 private IntPtr handleBitmapDC;
59 private Size bufferSize;
60 private Graphics graphics;
62 public GadgetWindow() {
64 typeof(Form).Assembly.GetType("System.Windows.Forms.Command");
65 commandDispatch = commandType.GetMethod("DispatchID",
66 BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
67 null, new Type[]{ typeof(int) }, null);
69 this.CreateHandle(CreateParams);
71 // move window to the bottom
74 // prevent window from fading to a glass sheet when peek is invoked
77 NativeMethods.DwmSetWindowAttribute(Handle,
78 WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value,
79 Marshal.SizeOf(value));
80 } catch (DllNotFoundException) { } catch (EntryPointNotFoundException) { }
85 private void ShowDesktopChanged(bool showDesktop) {
87 MoveToTopMost(Handle);
93 private void MoveToBottom(IntPtr handle) {
94 NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0,
95 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
98 private void MoveToTopMost(IntPtr handle) {
99 NativeMethods.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0,
100 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
103 private void ShowContextMenu(Point position) {
104 NativeMethods.TrackPopupMenuEx(contextMenu.Handle,
105 TPM_RIGHTBUTTON | TPM_VERTICAL, position.X,
106 position.Y, Handle, IntPtr.Zero);
109 protected virtual CreateParams CreateParams {
111 CreateParams cp = new CreateParams();
116 cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW;
121 protected override void WndProc(ref Message message) {
122 switch (message.Msg) {
124 // need to dispatch the message for the context menu
125 if (message.LParam == IntPtr.Zero)
126 commandDispatch.Invoke(null, new object[] {
127 message.WParam.ToInt32() & 0xFFFF });
130 message.Result = (IntPtr)HitResult.Caption;
131 if (HitTest != null) {
133 Macros.GET_X_LPARAM(message.LParam) - location.X,
134 Macros.GET_Y_LPARAM(message.LParam) - location.Y
136 HitTestEventArgs e = new HitTestEventArgs(p, HitResult.Caption);
138 message.Result = (IntPtr)e.HitResult;
141 case WM_NCLBUTTONDBLCLK: {
142 if (MouseDoubleClick != null) {
143 MouseDoubleClick(this, new MouseEventArgs(MouseButtons.Left, 2,
144 Macros.GET_X_LPARAM(message.LParam) - location.X,
145 Macros.GET_Y_LPARAM(message.LParam) - location.Y, 0));
147 message.Result = IntPtr.Zero;
149 case WM_NCRBUTTONDOWN: {
150 message.Result = IntPtr.Zero;
152 case WM_NCRBUTTONUP: {
153 if (contextMenu != null)
154 ShowContextMenu(new Point(
155 Macros.GET_X_LPARAM(message.LParam),
156 Macros.GET_Y_LPARAM(message.LParam)
158 message.Result = IntPtr.Zero;
160 case WM_WINDOWPOSCHANGING: {
161 WindowPos wp = (WindowPos)Marshal.PtrToStructure(
162 message.LParam, typeof(WindowPos));
164 if (!lockPositionAndSize) {
165 // prevent the window from leaving the screen
166 if ((wp.flags & SWP_NOMOVE) == 0) {
167 Rectangle rect = Screen.GetWorkingArea(
168 new Rectangle(wp.x, wp.y, wp.cx, wp.cy));
169 const int margin = 16;
170 wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
171 wp.x = Math.Min(wp.x, rect.Right - margin);
172 wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
173 wp.y = Math.Min(wp.y, rect.Bottom - margin);
176 // update location and fire event
177 if ((wp.flags & SWP_NOMOVE) == 0) {
178 if (location.X != wp.x || location.Y != wp.y) {
179 location = new Point(wp.x, wp.y);
180 if (LocationChanged != null)
181 LocationChanged(this, EventArgs.Empty);
185 // update size and fire event
186 if ((wp.flags & SWP_NOSIZE) == 0) {
187 if (size.Width != wp.cx || size.Height != wp.cy) {
188 size = new Size(wp.cx, wp.cy);
189 if (SizeChanged != null)
190 SizeChanged(this, EventArgs.Empty);
194 // update the size of the layered window
195 if ((wp.flags & SWP_NOSIZE) == 0) {
196 NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero,
197 IntPtr.Zero, ref size, IntPtr.Zero, IntPtr.Zero, 0,
201 // update the position of the layered window
202 if ((wp.flags & SWP_NOMOVE) == 0) {
203 NativeMethods.SetWindowPos(Handle, IntPtr.Zero,
204 location.X, location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE |
205 SWP_NOZORDER | SWP_NOSENDCHANGING);
209 // do not forward any move or size messages
210 wp.flags |= SWP_NOSIZE | SWP_NOMOVE;
212 // suppress any frame changed events
213 wp.flags &= ~SWP_FRAMECHANGED;
215 Marshal.StructureToPtr(wp, message.LParam, false);
216 message.Result = IntPtr.Zero;
219 base.WndProc(ref message);
224 private BlendFunction CreateBlendFunction() {
225 BlendFunction blend = new BlendFunction();
226 blend.BlendOp = AC_SRC_OVER;
227 blend.BlendFlags = 0;
228 blend.SourceConstantAlpha = opacity;
229 blend.AlphaFormat = AC_SRC_ALPHA;
233 private void CreateBuffer() {
234 IntPtr handleScreenDC = NativeMethods.GetDC(IntPtr.Zero);
235 handleBitmapDC = NativeMethods.CreateCompatibleDC(handleScreenDC);
236 NativeMethods.ReleaseDC(IntPtr.Zero, handleScreenDC);
239 BitmapInfo info = new BitmapInfo();
240 info.Size = Marshal.SizeOf(info);
241 info.Width = size.Width;
242 info.Height = -size.Height;
247 IntPtr hBmp = NativeMethods.CreateDIBSection(handleBitmapDC, ref info, 0,
248 out ptr, IntPtr.Zero, 0);
249 IntPtr hBmpOld = NativeMethods.SelectObject(handleBitmapDC, hBmp);
250 NativeMethods.DeleteObject(hBmpOld);
252 graphics = Graphics.FromHdc(handleBitmapDC);
254 if (Environment.OSVersion.Version.Major > 5) {
255 this.graphics.TextRenderingHint = TextRenderingHint.SystemDefault;
256 this.graphics.SmoothingMode = SmoothingMode.HighQuality;
260 private void DisposeBuffer() {
262 NativeMethods.DeleteDC(handleBitmapDC);
265 public virtual void Dispose() {
269 public PaintEventHandler Paint;
271 public void Redraw() {
272 if (!visible || Paint == null)
275 if (size != bufferSize) {
281 new PaintEventArgs(graphics, new Rectangle(Point.Empty, size)));
283 Point pointSource = Point.Empty;
284 BlendFunction blend = CreateBlendFunction();
286 NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
287 ref size, handleBitmapDC, ref pointSource, 0, ref blend, ULW_ALPHA);
289 // make sure the window is at the right location
290 NativeMethods.SetWindowPos(Handle, IntPtr.Zero,
291 location.X, location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE |
292 SWP_NOZORDER | SWP_NOSENDCHANGING);
295 public byte Opacity {
300 if (opacity != value) {
302 BlendFunction blend = CreateBlendFunction();
303 NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
304 IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blend, ULW_ALPHA);
309 public bool Visible {
314 if (visible != value) {
316 NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0,
317 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER |
318 (value ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
321 ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
324 ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
330 // if locked, the window can not be moved or resized
331 public bool LockPositionAndSize {
333 return lockPositionAndSize;
336 lockPositionAndSize = value;
340 public bool AlwaysOnTop {
345 if (value != alwaysOnTop) {
349 ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
350 MoveToTopMost(Handle);
352 MoveToBottom(Handle);
354 ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
367 NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
368 ref size, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, 0);
369 if (SizeChanged != null)
370 SizeChanged(this, EventArgs.Empty);
375 public event EventHandler SizeChanged;
377 public Point Location {
382 if (location != value) {
384 NativeMethods.SetWindowPos(Handle, IntPtr.Zero,
385 location.X, location.Y, 0, 0,
386 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
387 if (LocationChanged != null)
388 LocationChanged(this, EventArgs.Empty);
393 public event EventHandler LocationChanged;
395 public ContextMenu ContextMenu {
400 this.contextMenu = value;
404 public event HitTestEventHandler HitTest;
406 public event MouseEventHandler MouseDoubleClick;
408 [StructLayout(LayoutKind.Sequential, Pack = 1)]
409 private struct BlendFunction {
411 public byte BlendFlags;
412 public byte SourceConstantAlpha;
413 public byte AlphaFormat;
416 [StructLayout(LayoutKind.Sequential, Pack = 1)]
417 private struct WindowPos {
419 public IntPtr hwndInsertAfter;
427 [StructLayout(LayoutKind.Sequential)]
428 public struct BitmapInfo {
433 public Int16 BitCount;
434 public Int32 Compression;
435 public Int32 SizeImage;
436 public Int32 XPelsPerMeter;
437 public Int32 YPelsPerMeter;
438 public Int32 ClrUsed;
439 public Int32 ClrImportant;
443 public static readonly IntPtr HWND_BOTTOM = (IntPtr)1;
444 public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
446 public const int WS_EX_LAYERED = 0x00080000;
447 public const int WS_EX_TOOLWINDOW = 0x00000080;
449 public const uint SWP_NOSIZE = 0x0001;
450 public const uint SWP_NOMOVE = 0x0002;
451 public const uint SWP_NOACTIVATE = 0x0010;
452 public const uint SWP_FRAMECHANGED = 0x0020;
453 public const uint SWP_HIDEWINDOW = 0x0080;
454 public const uint SWP_SHOWWINDOW = 0x0040;
455 public const uint SWP_NOZORDER = 0x0004;
456 public const uint SWP_NOSENDCHANGING = 0x0400;
458 public const int ULW_COLORKEY = 0x00000001;
459 public const int ULW_ALPHA = 0x00000002;
460 public const int ULW_OPAQUE = 0x00000004;
462 public const byte AC_SRC_OVER = 0x00;
463 public const byte AC_SRC_ALPHA = 0x01;
465 public const int WM_NCHITTEST = 0x0084;
466 public const int WM_NCLBUTTONDBLCLK = 0x00A3;
467 public const int WM_NCLBUTTONDOWN = 0x00A1;
468 public const int WM_NCLBUTTONUP = 0x00A2;
469 public const int WM_NCRBUTTONDOWN = 0x00A4;
470 public const int WM_NCRBUTTONUP = 0x00A5;
471 public const int WM_WINDOWPOSCHANGING = 0x0046;
472 public const int WM_COMMAND = 0x0111;
474 public const int TPM_RIGHTBUTTON = 0x0002;
475 public const int TPM_VERTICAL = 0x0040;
477 private enum WindowAttribute : int {
478 DWMWA_NCRENDERING_ENABLED = 1,
479 DWMWA_NCRENDERING_POLICY,
480 DWMWA_TRANSITIONS_FORCEDISABLED,
482 DWMWA_CAPTION_BUTTON_BOUNDS,
483 DWMWA_NONCLIENT_RTL_LAYOUT,
484 DWMWA_FORCE_ICONIC_REPRESENTATION,
486 DWMWA_EXTENDED_FRAME_BOUNDS,
487 DWMWA_HAS_ICONIC_BITMAP,
489 DWMWA_EXCLUDED_FROM_PEEK,
494 /// Some macros imported and converted from the Windows SDK
496 private static class Macros {
497 public static ushort LOWORD(IntPtr l) {
498 return (ushort) ((ulong)l & 0xFFFF);
501 public static UInt16 HIWORD(IntPtr l) {
502 return (ushort) (((ulong)l >> 16) & 0xFFFF);
505 public static int GET_X_LPARAM(IntPtr lp) {
506 return (short) LOWORD(lp);
509 public static int GET_Y_LPARAM(IntPtr lp) {
510 return (short) HIWORD(lp);
515 /// Imported native methods
517 private static class NativeMethods {
518 private const string USER = "user32.dll";
519 private const string GDI = "gdi32.dll";
520 public const string DWMAPI = "dwmapi.dll";
522 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
523 [return: MarshalAs(UnmanagedType.Bool)]
524 public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
525 IntPtr pptDst, ref Size psize, IntPtr hdcSrc, IntPtr pprSrc,
526 int crKey, IntPtr pblend, int dwFlags);
528 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
529 [return: MarshalAs(UnmanagedType.Bool)]
530 public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
531 IntPtr pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc,
532 int crKey, ref BlendFunction pblend, int dwFlags);
534 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
535 [return: MarshalAs(UnmanagedType.Bool)]
536 public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
537 IntPtr pptDst, IntPtr psize, IntPtr hdcSrc, IntPtr pprSrc,
538 int crKey, ref BlendFunction pblend, int dwFlags);
540 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
541 public static extern IntPtr GetDC(IntPtr hWnd);
543 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
544 public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
546 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
547 public static extern bool SetWindowPos(IntPtr hWnd,
548 IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
550 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
551 public static extern bool TrackPopupMenuEx(IntPtr hMenu, uint uFlags,
552 int x, int y, IntPtr hWnd, IntPtr tpmParams);
554 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
555 public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
557 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
558 public static extern IntPtr CreateDIBSection(IntPtr hdc,
559 [In] ref BitmapInfo pbmi, uint pila, out IntPtr ppvBits,
560 IntPtr hSection, uint dwOffset);
562 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
563 [return: MarshalAs(UnmanagedType.Bool)]
564 public static extern bool DeleteDC(IntPtr hdc);
566 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
567 public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
569 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
570 [return: MarshalAs(UnmanagedType.Bool)]
571 public static extern bool DeleteObject(IntPtr hObject);
573 [DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)]
574 public static extern int DwmSetWindowAttribute(IntPtr hwnd,
575 WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);
579 public enum HitResult {
595 public delegate void HitTestEventHandler(object sender, HitTestEventArgs e);
597 public class HitTestEventArgs : EventArgs {
598 public HitTestEventArgs(Point location, HitResult hitResult) {
600 HitResult = hitResult;
602 public Point Location { get; private set; }
603 public HitResult HitResult { get; set; }