GUI/GadgetWindow.cs
author moel.mich
Mon, 06 Sep 2010 19:53:13 +0000
changeset 176 c16fd81b520a
child 180 d40f49d45614
permissions -rw-r--r--
Added a desktop gadget implementation.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     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
     8  
     9   http://www.mozilla.org/MPL/
    10 
    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.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    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.
    21 
    22   Contributor(s):
    23 
    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.
    35  
    36 */
    37 
    38 using System;
    39 using System.Drawing;
    40 using System.Reflection;
    41 using System.Runtime.InteropServices;
    42 using System.Windows.Forms;
    43 
    44 namespace OpenHardwareMonitor.GUI {
    45   public class GadgetWindow : NativeWindow {
    46 
    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;
    55 
    56     public GadgetWindow() {
    57       Type commandType = 
    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);
    62 
    63       this.CreateHandle(CreateParams);
    64 
    65       // move window to the bottom
    66       MoveToBottom(Handle);
    67 
    68       // prevent window from fading to a glass sheet when peek is invoked
    69       try {
    70         bool value = true;
    71         int r = NativeMethods.DwmSetWindowAttribute(Handle,
    72           WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value,
    73           Marshal.SizeOf(value));
    74       } catch (DllNotFoundException) { } catch (EntryPointNotFoundException) { }
    75     }
    76 
    77     private void ShowDesktopChanged(bool showDesktop) {
    78       if (showDesktop) {
    79         MoveToTopMost(Handle);
    80       } else {
    81         MoveToBottom(Handle);
    82       }
    83     }
    84 
    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);
    88     }
    89 
    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);
    93     }
    94 
    95     private void ShowContextMenu(Point position) {
    96       NativeMethods.TrackPopupMenuEx(contextMenu.Handle, 
    97         TPM_RIGHTBUTTON | TPM_VERTICAL, position.X,
    98         position.Y, Handle, IntPtr.Zero);
    99     }
   100 
   101     protected virtual CreateParams CreateParams {
   102       get {
   103         CreateParams cp = new CreateParams();
   104         cp.Width = size.Width;
   105         cp.Height = size.Height;
   106         cp.X = location.X;
   107         cp.Y = location.Y;
   108         cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW;
   109         return cp;
   110       }
   111     }
   112 
   113     protected override void WndProc(ref Message message) {
   114       switch (message.Msg) {
   115         case WM_COMMAND:
   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 });          
   120           break;
   121         case WM_NCHITTEST:           
   122           // all pixels of the form belong to the caption
   123           message.Result = HTCAPTION; 
   124           break;
   125         case WM_NCLBUTTONDBLCLK:  
   126           message.Result = IntPtr.Zero; break;
   127         case WM_NCRBUTTONDOWN:
   128           message.Result = IntPtr.Zero; break;
   129         case WM_NCRBUTTONUP:
   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; 
   135           break;
   136         case WM_WINDOWPOSCHANGING:         
   137           WindowPos wp = (WindowPos)Marshal.PtrToStructure(
   138             message.LParam, typeof(WindowPos));
   139 
   140           // add the nomove flag if position is locked
   141           if (lockPosition)
   142             wp.flags |= SWP_NOMOVE;
   143 
   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);
   152 
   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);
   158             }
   159           }          
   160 
   161           Marshal.StructureToPtr(wp, message.LParam, false);
   162           message.Result = IntPtr.Zero;
   163           break;           
   164         default:
   165           base.WndProc(ref message); break;
   166       }      
   167     }
   168 
   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;
   175       return blend;
   176     }
   177 
   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;
   183 
   184       try {
   185         newHBitmap = bitmap.GetHbitmap(Color.Black);
   186         oldHBitmap = NativeMethods.SelectObject(memory, newHBitmap);
   187 
   188         Size size = bitmap.Size;
   189         Point pointSource = Point.Empty;
   190         Point topPos = Location;
   191 
   192         BlendFunction blend = CreateBlendFunction();
   193         NativeMethods.UpdateLayeredWindow(Handle, screen, ref topPos,
   194           ref size, memory, ref pointSource, 0, ref blend, ULW_ALPHA);
   195       } finally {
   196         NativeMethods.ReleaseDC(IntPtr.Zero, screen);
   197         if (newHBitmap != IntPtr.Zero) {
   198           NativeMethods.SelectObject(memory, oldHBitmap);
   199           NativeMethods.DeleteObject(newHBitmap);
   200         }
   201         NativeMethods.DeleteDC(memory);
   202       }
   203     }
   204 
   205     public byte Opacity {
   206       get {
   207         return opacity;
   208       }
   209       set {
   210         if (opacity != value) {
   211           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);
   215         }
   216       }
   217     }
   218 
   219     public bool Visible {
   220       get {
   221         return visible;
   222       }
   223       set {
   224         if (visible != value) {
   225           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));
   229           if (value)
   230             ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
   231           else
   232             ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;         
   233         }
   234       }
   235     }
   236 
   237     // if locked, the window can not be moved
   238     public bool LockPosition {
   239       get {
   240         return lockPosition;
   241       }
   242       set {
   243         lockPosition = value;
   244       }
   245     }
   246 
   247     public bool AlwaysOnTop {
   248       get {
   249         return alwaysOnTop;
   250       }
   251       set {
   252         if (value != alwaysOnTop) {
   253           alwaysOnTop = value;
   254           if (alwaysOnTop) {
   255             ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
   256             MoveToTopMost(Handle);            
   257           } else {
   258             MoveToBottom(Handle);
   259             ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
   260           }
   261         }
   262       }
   263     }
   264 
   265     public Size Size {
   266       get {
   267         return size; 
   268       }
   269       set {
   270         if (size != value) {
   271           size = value;
   272           NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, size.Width,
   273             size.Height, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | 
   274             SWP_NOSENDCHANGING);
   275         }
   276       }
   277     }
   278 
   279     public Point Location {
   280       get {
   281         return location;
   282       }
   283       set {
   284         NativeMethods.SetWindowPos(Handle, IntPtr.Zero, value.X, value.Y, 0,
   285           0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
   286         location = value;
   287         if (LocationChanged != null)
   288           LocationChanged(this, EventArgs.Empty);
   289       }
   290     }
   291 
   292     public event EventHandler LocationChanged;
   293 
   294     public ContextMenu ContextMenu {
   295       get {
   296         return contextMenu;
   297       }
   298       set {
   299         this.contextMenu = value;
   300       }
   301     }
   302 
   303     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   304     private struct BlendFunction {
   305       public byte BlendOp;
   306       public byte BlendFlags;
   307       public byte SourceConstantAlpha;
   308       public byte AlphaFormat;
   309     }
   310 
   311     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   312     private struct WindowPos {
   313       public IntPtr hwnd;
   314       public IntPtr hwndInsertAfter;
   315       public int x;
   316       public int y;
   317       public int cx;
   318       public int cy;
   319       public uint flags;
   320     }
   321 
   322     public static readonly IntPtr HWND_BOTTOM = (IntPtr)1;
   323     public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
   324 
   325     public const int WS_EX_LAYERED = 0x00080000;
   326     public const int WS_EX_TOOLWINDOW = 0x00000080;
   327 
   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;
   335 
   336     public const int ULW_COLORKEY = 0x00000001;
   337     public const int ULW_ALPHA = 0x00000002;
   338     public const int ULW_OPAQUE = 0x00000004;
   339 
   340     public const byte AC_SRC_OVER = 0x00;
   341     public const byte AC_SRC_ALPHA = 0x01;
   342 
   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;
   351 
   352     public const int TPM_RIGHTBUTTON = 0x0002;
   353     public const int TPM_VERTICAL = 0x0040;
   354 
   355     public readonly IntPtr HTCAPTION = (IntPtr)2;
   356 
   357     private enum WindowAttribute : int {
   358       DWMWA_NCRENDERING_ENABLED = 1,
   359       DWMWA_NCRENDERING_POLICY,
   360       DWMWA_TRANSITIONS_FORCEDISABLED,
   361       DWMWA_ALLOW_NCPAINT,
   362       DWMWA_CAPTION_BUTTON_BOUNDS,
   363       DWMWA_NONCLIENT_RTL_LAYOUT,
   364       DWMWA_FORCE_ICONIC_REPRESENTATION,
   365       DWMWA_FLIP3D_POLICY,
   366       DWMWA_EXTENDED_FRAME_BOUNDS,
   367       DWMWA_HAS_ICONIC_BITMAP,
   368       DWMWA_DISALLOW_PEEK,
   369       DWMWA_EXCLUDED_FROM_PEEK,
   370       DWMWA_LAST
   371     }
   372 
   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";
   377 
   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);
   383 
   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);  
   389 
   390       [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
   391       public static extern IntPtr GetDC(IntPtr hWnd);
   392 
   393       [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
   394       public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
   395 
   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);
   399 
   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);
   403 
   404       [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
   405       public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
   406 
   407       [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
   408       [return: MarshalAs(UnmanagedType.Bool)]
   409       public static extern bool DeleteDC(IntPtr hdc);
   410       
   411       [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
   412       public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
   413 
   414       [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
   415       [return: MarshalAs(UnmanagedType.Bool)]
   416       public static extern bool DeleteObject(IntPtr hObject);
   417 
   418       [DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)]
   419       public static extern int DwmSetWindowAttribute(IntPtr hwnd,
   420         WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);
   421     }    
   422   }
   423 }