Added a desktop gadget implementation.
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
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.Reflection;
41 using System.Runtime.InteropServices;
42 using System.Windows.Forms;
44 namespace OpenHardwareMonitor.GUI {
45 public class GadgetWindow : NativeWindow {
47 private bool visible = false;
48 private bool lockPosition = false;
49 private bool alwaysOnTop = false;
50 private byte opacity = 255;
51 private Point location = new Point(100, 100);
52 private Size size = new Size(130, 84);
53 private ContextMenu contextMenu = null;
54 private MethodInfo commandDispatch;
56 public GadgetWindow() {
58 typeof(Form).Assembly.GetType("System.Windows.Forms.Command");
59 commandDispatch = commandType.GetMethod("DispatchID",
60 BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
61 null, new Type[]{ typeof(int) }, null);
63 this.CreateHandle(CreateParams);
65 // move window to the bottom
68 // prevent window from fading to a glass sheet when peek is invoked
71 int r = NativeMethods.DwmSetWindowAttribute(Handle,
72 WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value,
73 Marshal.SizeOf(value));
74 } catch (DllNotFoundException) { } catch (EntryPointNotFoundException) { }
77 private void ShowDesktopChanged(bool showDesktop) {
79 MoveToTopMost(Handle);
85 private void MoveToBottom(IntPtr handle) {
86 NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0,
87 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
90 private void MoveToTopMost(IntPtr handle) {
91 NativeMethods.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0,
92 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
95 private void ShowContextMenu(Point position) {
96 NativeMethods.TrackPopupMenuEx(contextMenu.Handle,
97 TPM_RIGHTBUTTON | TPM_VERTICAL, position.X,
98 position.Y, Handle, IntPtr.Zero);
101 protected virtual CreateParams CreateParams {
103 CreateParams cp = new CreateParams();
104 cp.Width = size.Width;
105 cp.Height = size.Height;
108 cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW;
113 protected override void WndProc(ref Message message) {
114 switch (message.Msg) {
116 // need to dispatch the message for the context menu
117 if (message.LParam == IntPtr.Zero)
118 commandDispatch.Invoke(null, new object[] {
119 message.WParam.ToInt32() & 0xFFFF });
122 // all pixels of the form belong to the caption
123 message.Result = HTCAPTION;
125 case WM_NCLBUTTONDBLCLK:
126 message.Result = IntPtr.Zero; break;
127 case WM_NCRBUTTONDOWN:
128 message.Result = IntPtr.Zero; break;
130 if (contextMenu != null)
131 ShowContextMenu(new Point(
132 (int)((uint)message.LParam & 0xFFFF),
133 (int)(((uint)message.LParam >>16) & 0xFFFF)));
134 message.Result = IntPtr.Zero;
136 case WM_WINDOWPOSCHANGING:
137 WindowPos wp = (WindowPos)Marshal.PtrToStructure(
138 message.LParam, typeof(WindowPos));
140 // add the nomove flag if position is locked
142 wp.flags |= SWP_NOMOVE;
144 // prevent the window from leaving the screen
145 if ((wp.flags & SWP_NOMOVE) == 0) {
146 Rectangle rect = Screen.GetWorkingArea(new Point(wp.x, wp.y));
147 const int margin = 20;
148 wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
149 wp.x = Math.Min(wp.x, rect.Right - margin);
150 wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
151 wp.y = Math.Min(wp.y, rect.Bottom - margin);
153 // raise the event if location changed
154 if (location.X != wp.x || location.Y != wp.y) {
155 location = new Point(wp.x, wp.y);
156 if (LocationChanged != null)
157 LocationChanged(this, EventArgs.Empty);
161 Marshal.StructureToPtr(wp, message.LParam, false);
162 message.Result = IntPtr.Zero;
165 base.WndProc(ref message); break;
169 private BlendFunction CreateBlendFunction() {
170 BlendFunction blend = new BlendFunction();
171 blend.BlendOp = AC_SRC_OVER;
172 blend.BlendFlags = 0;
173 blend.SourceConstantAlpha = opacity;
174 blend.AlphaFormat = AC_SRC_ALPHA;
178 public void Update(Bitmap bitmap) {
179 IntPtr screen = NativeMethods.GetDC(IntPtr.Zero);
180 IntPtr memory = NativeMethods.CreateCompatibleDC(screen);
181 IntPtr newHBitmap = IntPtr.Zero;
182 IntPtr oldHBitmap = IntPtr.Zero;
185 newHBitmap = bitmap.GetHbitmap(Color.Black);
186 oldHBitmap = NativeMethods.SelectObject(memory, newHBitmap);
188 Size size = bitmap.Size;
189 Point pointSource = Point.Empty;
190 Point topPos = Location;
192 BlendFunction blend = CreateBlendFunction();
193 NativeMethods.UpdateLayeredWindow(Handle, screen, ref topPos,
194 ref size, memory, ref pointSource, 0, ref blend, ULW_ALPHA);
196 NativeMethods.ReleaseDC(IntPtr.Zero, screen);
197 if (newHBitmap != IntPtr.Zero) {
198 NativeMethods.SelectObject(memory, oldHBitmap);
199 NativeMethods.DeleteObject(newHBitmap);
201 NativeMethods.DeleteDC(memory);
205 public byte Opacity {
210 if (opacity != value) {
212 BlendFunction blend = CreateBlendFunction();
213 NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
214 IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blend, ULW_ALPHA);
219 public bool Visible {
224 if (visible != value) {
226 NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0,
227 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER |
228 (value ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
230 ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
232 ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
237 // if locked, the window can not be moved
238 public bool LockPosition {
243 lockPosition = value;
247 public bool AlwaysOnTop {
252 if (value != alwaysOnTop) {
255 ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
256 MoveToTopMost(Handle);
258 MoveToBottom(Handle);
259 ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
272 NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, size.Width,
273 size.Height, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER |
279 public Point Location {
284 NativeMethods.SetWindowPos(Handle, IntPtr.Zero, value.X, value.Y, 0,
285 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
287 if (LocationChanged != null)
288 LocationChanged(this, EventArgs.Empty);
292 public event EventHandler LocationChanged;
294 public ContextMenu ContextMenu {
299 this.contextMenu = value;
303 [StructLayout(LayoutKind.Sequential, Pack = 1)]
304 private struct BlendFunction {
306 public byte BlendFlags;
307 public byte SourceConstantAlpha;
308 public byte AlphaFormat;
311 [StructLayout(LayoutKind.Sequential, Pack = 1)]
312 private struct WindowPos {
314 public IntPtr hwndInsertAfter;
322 public static readonly IntPtr HWND_BOTTOM = (IntPtr)1;
323 public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
325 public const int WS_EX_LAYERED = 0x00080000;
326 public const int WS_EX_TOOLWINDOW = 0x00000080;
328 public const uint SWP_NOSIZE = 0x0001;
329 public const uint SWP_NOMOVE = 0x0002;
330 public const uint SWP_NOACTIVATE = 0x0010;
331 public const uint SWP_HIDEWINDOW = 0x0080;
332 public const uint SWP_SHOWWINDOW = 0x0040;
333 public const uint SWP_NOZORDER = 0x0004;
334 public const uint SWP_NOSENDCHANGING = 0x0400;
336 public const int ULW_COLORKEY = 0x00000001;
337 public const int ULW_ALPHA = 0x00000002;
338 public const int ULW_OPAQUE = 0x00000004;
340 public const byte AC_SRC_OVER = 0x00;
341 public const byte AC_SRC_ALPHA = 0x01;
343 public const int WM_NCHITTEST = 0x0084;
344 public const int WM_NCLBUTTONDBLCLK = 0x00A3;
345 public const int WM_NCLBUTTONDOWN = 0x00A1;
346 public const int WM_NCLBUTTONUP = 0x00A2;
347 public const int WM_NCRBUTTONDOWN = 0x00A4;
348 public const int WM_NCRBUTTONUP = 0x00A5;
349 public const int WM_WINDOWPOSCHANGING = 0x0046;
350 public const int WM_COMMAND = 0x0111;
352 public const int TPM_RIGHTBUTTON = 0x0002;
353 public const int TPM_VERTICAL = 0x0040;
355 public readonly IntPtr HTCAPTION = (IntPtr)2;
357 private enum WindowAttribute : int {
358 DWMWA_NCRENDERING_ENABLED = 1,
359 DWMWA_NCRENDERING_POLICY,
360 DWMWA_TRANSITIONS_FORCEDISABLED,
362 DWMWA_CAPTION_BUTTON_BOUNDS,
363 DWMWA_NONCLIENT_RTL_LAYOUT,
364 DWMWA_FORCE_ICONIC_REPRESENTATION,
366 DWMWA_EXTENDED_FRAME_BOUNDS,
367 DWMWA_HAS_ICONIC_BITMAP,
369 DWMWA_EXCLUDED_FROM_PEEK,
373 private static class NativeMethods {
374 private const string USER = "user32.dll";
375 private const string GDI = "gdi32.dll";
376 public const string DWMAPI = "dwmapi.dll";
378 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
379 [return: MarshalAs(UnmanagedType.Bool)]
380 public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
381 ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc,
382 int crKey, ref BlendFunction pblend, int dwFlags);
384 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
385 [return: MarshalAs(UnmanagedType.Bool)]
386 public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
387 IntPtr pptDst, IntPtr psize, IntPtr hdcSrc, IntPtr pprSrc,
388 int crKey, ref BlendFunction pblend, int dwFlags);
390 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
391 public static extern IntPtr GetDC(IntPtr hWnd);
393 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
394 public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
396 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
397 public static extern bool SetWindowPos(IntPtr hWnd,
398 IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
400 [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
401 public static extern bool TrackPopupMenuEx(IntPtr hMenu, uint uFlags,
402 int x, int y, IntPtr hWnd, IntPtr tpmParams);
404 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
405 public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
407 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
408 [return: MarshalAs(UnmanagedType.Bool)]
409 public static extern bool DeleteDC(IntPtr hdc);
411 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
412 public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
414 [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
415 [return: MarshalAs(UnmanagedType.Bool)]
416 public static extern bool DeleteObject(IntPtr hObject);
418 [DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)]
419 public static extern int DwmSetWindowAttribute(IntPtr hwnd,
420 WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);