moel@176: /* moel@176: moel@176: Version: MPL 1.1/GPL 2.0/LGPL 2.1 moel@176: moel@176: The contents of this file are subject to the Mozilla Public License Version moel@176: 1.1 (the "License"); you may not use this file except in compliance with moel@176: the License. You may obtain a copy of the License at moel@176: moel@176: http://www.mozilla.org/MPL/ moel@176: moel@176: Software distributed under the License is distributed on an "AS IS" basis, moel@176: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License moel@176: for the specific language governing rights and limitations under the License. moel@176: moel@176: The Original Code is the Open Hardware Monitor code. moel@176: moel@176: The Initial Developer of the Original Code is moel@176: Michael Möller . moel@176: Portions created by the Initial Developer are Copyright (C) 2010 moel@176: the Initial Developer. All Rights Reserved. moel@176: moel@176: Contributor(s): moel@176: moel@176: Alternatively, the contents of this file may be used under the terms of moel@176: either the GNU General Public License Version 2 or later (the "GPL"), or moel@176: the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), moel@176: in which case the provisions of the GPL or the LGPL are applicable instead moel@176: of those above. If you wish to allow use of your version of this file only moel@176: under the terms of either the GPL or the LGPL, and not to allow others to moel@176: use your version of this file under the terms of the MPL, indicate your moel@176: decision by deleting the provisions above and replace them with the notice moel@176: and other provisions required by the GPL or the LGPL. If you do not delete moel@176: the provisions above, a recipient may use your version of this file under moel@176: the terms of any one of the MPL, the GPL or the LGPL. moel@176: moel@176: */ moel@176: moel@176: using System; moel@176: using System.Drawing; moel@176: using System.Reflection; moel@176: using System.Runtime.InteropServices; moel@176: using System.Windows.Forms; moel@176: moel@176: namespace OpenHardwareMonitor.GUI { moel@176: public class GadgetWindow : NativeWindow { moel@176: moel@176: private bool visible = false; moel@176: private bool lockPosition = false; moel@176: private bool alwaysOnTop = false; moel@176: private byte opacity = 255; moel@176: private Point location = new Point(100, 100); moel@176: private Size size = new Size(130, 84); moel@176: private ContextMenu contextMenu = null; moel@176: private MethodInfo commandDispatch; moel@176: moel@176: public GadgetWindow() { moel@176: Type commandType = moel@176: typeof(Form).Assembly.GetType("System.Windows.Forms.Command"); moel@176: commandDispatch = commandType.GetMethod("DispatchID", moel@176: BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, moel@176: null, new Type[]{ typeof(int) }, null); moel@176: moel@176: this.CreateHandle(CreateParams); moel@176: moel@176: // move window to the bottom moel@176: MoveToBottom(Handle); moel@176: moel@176: // prevent window from fading to a glass sheet when peek is invoked moel@176: try { moel@176: bool value = true; moel@176: int r = NativeMethods.DwmSetWindowAttribute(Handle, moel@176: WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value, moel@176: Marshal.SizeOf(value)); moel@176: } catch (DllNotFoundException) { } catch (EntryPointNotFoundException) { } moel@176: } moel@176: moel@176: private void ShowDesktopChanged(bool showDesktop) { moel@176: if (showDesktop) { moel@176: MoveToTopMost(Handle); moel@176: } else { moel@176: MoveToBottom(Handle); moel@176: } moel@176: } moel@176: moel@176: private void MoveToBottom(IntPtr handle) { moel@176: NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0, moel@176: SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); moel@176: } moel@176: moel@176: private void MoveToTopMost(IntPtr handle) { moel@176: NativeMethods.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, moel@176: SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); moel@176: } moel@176: moel@176: private void ShowContextMenu(Point position) { moel@176: NativeMethods.TrackPopupMenuEx(contextMenu.Handle, moel@176: TPM_RIGHTBUTTON | TPM_VERTICAL, position.X, moel@176: position.Y, Handle, IntPtr.Zero); moel@176: } moel@176: moel@176: protected virtual CreateParams CreateParams { moel@176: get { moel@176: CreateParams cp = new CreateParams(); moel@176: cp.Width = size.Width; moel@176: cp.Height = size.Height; moel@176: cp.X = location.X; moel@176: cp.Y = location.Y; moel@176: cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW; moel@176: return cp; moel@176: } moel@176: } moel@176: moel@176: protected override void WndProc(ref Message message) { moel@176: switch (message.Msg) { moel@176: case WM_COMMAND: moel@176: // need to dispatch the message for the context menu moel@176: if (message.LParam == IntPtr.Zero) moel@176: commandDispatch.Invoke(null, new object[] { moel@176: message.WParam.ToInt32() & 0xFFFF }); moel@176: break; moel@176: case WM_NCHITTEST: moel@176: // all pixels of the form belong to the caption moel@176: message.Result = HTCAPTION; moel@176: break; moel@176: case WM_NCLBUTTONDBLCLK: moel@176: message.Result = IntPtr.Zero; break; moel@176: case WM_NCRBUTTONDOWN: moel@176: message.Result = IntPtr.Zero; break; moel@176: case WM_NCRBUTTONUP: moel@176: if (contextMenu != null) moel@176: ShowContextMenu(new Point( moel@176: (int)((uint)message.LParam & 0xFFFF), moel@176: (int)(((uint)message.LParam >>16) & 0xFFFF))); moel@176: message.Result = IntPtr.Zero; moel@176: break; moel@176: case WM_WINDOWPOSCHANGING: moel@176: WindowPos wp = (WindowPos)Marshal.PtrToStructure( moel@176: message.LParam, typeof(WindowPos)); moel@176: moel@176: // add the nomove flag if position is locked moel@176: if (lockPosition) moel@176: wp.flags |= SWP_NOMOVE; moel@176: moel@176: // prevent the window from leaving the screen moel@176: if ((wp.flags & SWP_NOMOVE) == 0) { moel@176: Rectangle rect = Screen.GetWorkingArea(new Point(wp.x, wp.y)); moel@176: const int margin = 20; moel@176: wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin); moel@176: wp.x = Math.Min(wp.x, rect.Right - margin); moel@176: wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin); moel@176: wp.y = Math.Min(wp.y, rect.Bottom - margin); moel@176: moel@176: // raise the event if location changed moel@176: if (location.X != wp.x || location.Y != wp.y) { moel@176: location = new Point(wp.x, wp.y); moel@176: if (LocationChanged != null) moel@176: LocationChanged(this, EventArgs.Empty); moel@176: } moel@176: } moel@176: moel@176: Marshal.StructureToPtr(wp, message.LParam, false); moel@176: message.Result = IntPtr.Zero; moel@176: break; moel@176: default: moel@176: base.WndProc(ref message); break; moel@176: } moel@176: } moel@176: moel@176: private BlendFunction CreateBlendFunction() { moel@176: BlendFunction blend = new BlendFunction(); moel@176: blend.BlendOp = AC_SRC_OVER; moel@176: blend.BlendFlags = 0; moel@176: blend.SourceConstantAlpha = opacity; moel@176: blend.AlphaFormat = AC_SRC_ALPHA; moel@176: return blend; moel@176: } moel@176: moel@176: public void Update(Bitmap bitmap) { moel@176: IntPtr screen = NativeMethods.GetDC(IntPtr.Zero); moel@176: IntPtr memory = NativeMethods.CreateCompatibleDC(screen); moel@176: IntPtr newHBitmap = IntPtr.Zero; moel@176: IntPtr oldHBitmap = IntPtr.Zero; moel@176: moel@176: try { moel@176: newHBitmap = bitmap.GetHbitmap(Color.Black); moel@176: oldHBitmap = NativeMethods.SelectObject(memory, newHBitmap); moel@176: moel@176: Size size = bitmap.Size; moel@176: Point pointSource = Point.Empty; moel@176: Point topPos = Location; moel@176: moel@176: BlendFunction blend = CreateBlendFunction(); moel@176: NativeMethods.UpdateLayeredWindow(Handle, screen, ref topPos, moel@176: ref size, memory, ref pointSource, 0, ref blend, ULW_ALPHA); moel@176: } finally { moel@176: NativeMethods.ReleaseDC(IntPtr.Zero, screen); moel@176: if (newHBitmap != IntPtr.Zero) { moel@176: NativeMethods.SelectObject(memory, oldHBitmap); moel@176: NativeMethods.DeleteObject(newHBitmap); moel@176: } moel@176: NativeMethods.DeleteDC(memory); moel@176: } moel@176: } moel@176: moel@176: public byte Opacity { moel@176: get { moel@176: return opacity; moel@176: } moel@176: set { moel@176: if (opacity != value) { moel@176: opacity = value; moel@176: BlendFunction blend = CreateBlendFunction(); moel@176: NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero, moel@176: IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blend, ULW_ALPHA); moel@176: } moel@176: } moel@176: } moel@176: moel@176: public bool Visible { moel@176: get { moel@176: return visible; moel@176: } moel@176: set { moel@176: if (visible != value) { moel@176: visible = value; moel@176: NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, moel@176: SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | moel@176: (value ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); moel@180: if (value) { moel@180: if (!alwaysOnTop) moel@180: ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged; moel@180: } else { moel@180: if (!alwaysOnTop) moel@180: ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged; moel@180: } moel@176: } moel@176: } moel@176: } moel@176: moel@176: // if locked, the window can not be moved moel@176: public bool LockPosition { moel@176: get { moel@176: return lockPosition; moel@176: } moel@176: set { moel@176: lockPosition = value; moel@176: } moel@176: } moel@176: moel@176: public bool AlwaysOnTop { moel@176: get { moel@176: return alwaysOnTop; moel@176: } moel@176: set { moel@176: if (value != alwaysOnTop) { moel@176: alwaysOnTop = value; moel@176: if (alwaysOnTop) { moel@180: if (visible) moel@180: ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged; moel@176: MoveToTopMost(Handle); moel@176: } else { moel@176: MoveToBottom(Handle); moel@180: if (visible) moel@180: ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged; moel@176: } moel@176: } moel@176: } moel@176: } moel@176: moel@176: public Size Size { moel@176: get { moel@176: return size; moel@176: } moel@176: set { moel@176: if (size != value) { moel@176: size = value; moel@176: NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, size.Width, moel@176: size.Height, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | moel@176: SWP_NOSENDCHANGING); moel@176: } moel@176: } moel@176: } moel@176: moel@176: public Point Location { moel@176: get { moel@176: return location; moel@176: } moel@176: set { moel@176: NativeMethods.SetWindowPos(Handle, IntPtr.Zero, value.X, value.Y, 0, moel@176: 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING); moel@176: location = value; moel@176: if (LocationChanged != null) moel@176: LocationChanged(this, EventArgs.Empty); moel@176: } moel@176: } moel@176: moel@176: public event EventHandler LocationChanged; moel@176: moel@176: public ContextMenu ContextMenu { moel@176: get { moel@176: return contextMenu; moel@176: } moel@176: set { moel@176: this.contextMenu = value; moel@176: } moel@176: } moel@176: moel@176: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@176: private struct BlendFunction { moel@176: public byte BlendOp; moel@176: public byte BlendFlags; moel@176: public byte SourceConstantAlpha; moel@176: public byte AlphaFormat; moel@176: } moel@176: moel@176: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@176: private struct WindowPos { moel@176: public IntPtr hwnd; moel@176: public IntPtr hwndInsertAfter; moel@176: public int x; moel@176: public int y; moel@176: public int cx; moel@176: public int cy; moel@176: public uint flags; moel@176: } moel@176: moel@176: public static readonly IntPtr HWND_BOTTOM = (IntPtr)1; moel@176: public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1); moel@176: moel@176: public const int WS_EX_LAYERED = 0x00080000; moel@176: public const int WS_EX_TOOLWINDOW = 0x00000080; moel@176: moel@176: public const uint SWP_NOSIZE = 0x0001; moel@176: public const uint SWP_NOMOVE = 0x0002; moel@176: public const uint SWP_NOACTIVATE = 0x0010; moel@176: public const uint SWP_HIDEWINDOW = 0x0080; moel@176: public const uint SWP_SHOWWINDOW = 0x0040; moel@176: public const uint SWP_NOZORDER = 0x0004; moel@176: public const uint SWP_NOSENDCHANGING = 0x0400; moel@176: moel@176: public const int ULW_COLORKEY = 0x00000001; moel@176: public const int ULW_ALPHA = 0x00000002; moel@176: public const int ULW_OPAQUE = 0x00000004; moel@176: moel@176: public const byte AC_SRC_OVER = 0x00; moel@176: public const byte AC_SRC_ALPHA = 0x01; moel@176: moel@176: public const int WM_NCHITTEST = 0x0084; moel@176: public const int WM_NCLBUTTONDBLCLK = 0x00A3; moel@176: public const int WM_NCLBUTTONDOWN = 0x00A1; moel@176: public const int WM_NCLBUTTONUP = 0x00A2; moel@176: public const int WM_NCRBUTTONDOWN = 0x00A4; moel@176: public const int WM_NCRBUTTONUP = 0x00A5; moel@176: public const int WM_WINDOWPOSCHANGING = 0x0046; moel@176: public const int WM_COMMAND = 0x0111; moel@176: moel@176: public const int TPM_RIGHTBUTTON = 0x0002; moel@176: public const int TPM_VERTICAL = 0x0040; moel@176: moel@176: public readonly IntPtr HTCAPTION = (IntPtr)2; moel@176: moel@176: private enum WindowAttribute : int { moel@176: DWMWA_NCRENDERING_ENABLED = 1, moel@176: DWMWA_NCRENDERING_POLICY, moel@176: DWMWA_TRANSITIONS_FORCEDISABLED, moel@176: DWMWA_ALLOW_NCPAINT, moel@176: DWMWA_CAPTION_BUTTON_BOUNDS, moel@176: DWMWA_NONCLIENT_RTL_LAYOUT, moel@176: DWMWA_FORCE_ICONIC_REPRESENTATION, moel@176: DWMWA_FLIP3D_POLICY, moel@176: DWMWA_EXTENDED_FRAME_BOUNDS, moel@176: DWMWA_HAS_ICONIC_BITMAP, moel@176: DWMWA_DISALLOW_PEEK, moel@176: DWMWA_EXCLUDED_FROM_PEEK, moel@176: DWMWA_LAST moel@176: } moel@176: moel@176: private static class NativeMethods { moel@176: private const string USER = "user32.dll"; moel@176: private const string GDI = "gdi32.dll"; moel@176: public const string DWMAPI = "dwmapi.dll"; moel@176: moel@176: [DllImport(USER, CallingConvention = CallingConvention.Winapi)] moel@176: [return: MarshalAs(UnmanagedType.Bool)] moel@176: public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, moel@176: ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, moel@176: int crKey, ref BlendFunction pblend, int dwFlags); moel@176: moel@176: [DllImport(USER, CallingConvention = CallingConvention.Winapi)] moel@176: [return: MarshalAs(UnmanagedType.Bool)] moel@176: public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, moel@176: IntPtr pptDst, IntPtr psize, IntPtr hdcSrc, IntPtr pprSrc, moel@176: int crKey, ref BlendFunction pblend, int dwFlags); moel@176: moel@176: [DllImport(USER, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern IntPtr GetDC(IntPtr hWnd); moel@176: moel@176: [DllImport(USER, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); moel@176: moel@176: [DllImport(USER, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern bool SetWindowPos(IntPtr hWnd, moel@176: IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); moel@176: moel@176: [DllImport(USER, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern bool TrackPopupMenuEx(IntPtr hMenu, uint uFlags, moel@176: int x, int y, IntPtr hWnd, IntPtr tpmParams); moel@176: moel@176: [DllImport(GDI, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern IntPtr CreateCompatibleDC(IntPtr hDC); moel@176: moel@176: [DllImport(GDI, CallingConvention = CallingConvention.Winapi)] moel@176: [return: MarshalAs(UnmanagedType.Bool)] moel@176: public static extern bool DeleteDC(IntPtr hdc); moel@176: moel@176: [DllImport(GDI, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); moel@176: moel@176: [DllImport(GDI, CallingConvention = CallingConvention.Winapi)] moel@176: [return: MarshalAs(UnmanagedType.Bool)] moel@176: public static extern bool DeleteObject(IntPtr hObject); moel@176: moel@176: [DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)] moel@176: public static extern int DwmSetWindowAttribute(IntPtr hwnd, moel@176: WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute); moel@176: } moel@176: } moel@176: }