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.
moel@176
     1
/*
moel@176
     2
  
moel@176
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@176
     4
moel@176
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@176
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@176
     7
  the License. You may obtain a copy of the License at
moel@176
     8
 
moel@176
     9
  http://www.mozilla.org/MPL/
moel@176
    10
moel@176
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@176
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@176
    13
  for the specific language governing rights and limitations under the License.
moel@176
    14
moel@176
    15
  The Original Code is the Open Hardware Monitor code.
moel@176
    16
moel@176
    17
  The Initial Developer of the Original Code is 
moel@176
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@176
    19
  Portions created by the Initial Developer are Copyright (C) 2010
moel@176
    20
  the Initial Developer. All Rights Reserved.
moel@176
    21
moel@176
    22
  Contributor(s):
moel@176
    23
moel@176
    24
  Alternatively, the contents of this file may be used under the terms of
moel@176
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@176
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@176
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@176
    28
  of those above. If you wish to allow use of your version of this file only
moel@176
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@176
    30
  use your version of this file under the terms of the MPL, indicate your
moel@176
    31
  decision by deleting the provisions above and replace them with the notice
moel@176
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@176
    33
  the provisions above, a recipient may use your version of this file under
moel@176
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@176
    35
 
moel@176
    36
*/
moel@176
    37
moel@176
    38
using System;
moel@176
    39
using System.Drawing;
moel@176
    40
using System.Reflection;
moel@176
    41
using System.Runtime.InteropServices;
moel@176
    42
using System.Windows.Forms;
moel@176
    43
moel@176
    44
namespace OpenHardwareMonitor.GUI {
moel@176
    45
  public class GadgetWindow : NativeWindow {
moel@176
    46
moel@176
    47
    private bool visible = false;
moel@176
    48
    private bool lockPosition = false;
moel@176
    49
    private bool alwaysOnTop = false;
moel@176
    50
    private byte opacity = 255;
moel@176
    51
    private Point location = new Point(100, 100);
moel@176
    52
    private Size size = new Size(130, 84);
moel@176
    53
    private ContextMenu contextMenu = null;
moel@176
    54
    private MethodInfo commandDispatch;
moel@176
    55
moel@176
    56
    public GadgetWindow() {
moel@176
    57
      Type commandType = 
moel@176
    58
        typeof(Form).Assembly.GetType("System.Windows.Forms.Command");
moel@176
    59
      commandDispatch = commandType.GetMethod("DispatchID", 
moel@176
    60
        BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, 
moel@176
    61
        null, new Type[]{ typeof(int) }, null);
moel@176
    62
moel@176
    63
      this.CreateHandle(CreateParams);
moel@176
    64
moel@176
    65
      // move window to the bottom
moel@176
    66
      MoveToBottom(Handle);
moel@176
    67
moel@176
    68
      // prevent window from fading to a glass sheet when peek is invoked
moel@176
    69
      try {
moel@176
    70
        bool value = true;
moel@176
    71
        int r = NativeMethods.DwmSetWindowAttribute(Handle,
moel@176
    72
          WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value,
moel@176
    73
          Marshal.SizeOf(value));
moel@176
    74
      } catch (DllNotFoundException) { } catch (EntryPointNotFoundException) { }
moel@176
    75
    }
moel@176
    76
moel@176
    77
    private void ShowDesktopChanged(bool showDesktop) {
moel@176
    78
      if (showDesktop) {
moel@176
    79
        MoveToTopMost(Handle);
moel@176
    80
      } else {
moel@176
    81
        MoveToBottom(Handle);
moel@176
    82
      }
moel@176
    83
    }
moel@176
    84
moel@176
    85
    private void MoveToBottom(IntPtr handle) {
moel@176
    86
      NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0,
moel@176
    87
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
moel@176
    88
    }
moel@176
    89
moel@176
    90
    private void MoveToTopMost(IntPtr handle) {
moel@176
    91
      NativeMethods.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0,
moel@176
    92
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
moel@176
    93
    }
moel@176
    94
moel@176
    95
    private void ShowContextMenu(Point position) {
moel@176
    96
      NativeMethods.TrackPopupMenuEx(contextMenu.Handle, 
moel@176
    97
        TPM_RIGHTBUTTON | TPM_VERTICAL, position.X,
moel@176
    98
        position.Y, Handle, IntPtr.Zero);
moel@176
    99
    }
moel@176
   100
moel@176
   101
    protected virtual CreateParams CreateParams {
moel@176
   102
      get {
moel@176
   103
        CreateParams cp = new CreateParams();
moel@176
   104
        cp.Width = size.Width;
moel@176
   105
        cp.Height = size.Height;
moel@176
   106
        cp.X = location.X;
moel@176
   107
        cp.Y = location.Y;
moel@176
   108
        cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW;
moel@176
   109
        return cp;
moel@176
   110
      }
moel@176
   111
    }
moel@176
   112
moel@176
   113
    protected override void WndProc(ref Message message) {
moel@176
   114
      switch (message.Msg) {
moel@176
   115
        case WM_COMMAND:
moel@176
   116
          // need to dispatch the message for the context menu
moel@176
   117
          if (message.LParam == IntPtr.Zero) 
moel@176
   118
            commandDispatch.Invoke(null, new object[] { 
moel@176
   119
              message.WParam.ToInt32() & 0xFFFF });          
moel@176
   120
          break;
moel@176
   121
        case WM_NCHITTEST:           
moel@176
   122
          // all pixels of the form belong to the caption
moel@176
   123
          message.Result = HTCAPTION; 
moel@176
   124
          break;
moel@176
   125
        case WM_NCLBUTTONDBLCLK:  
moel@176
   126
          message.Result = IntPtr.Zero; break;
moel@176
   127
        case WM_NCRBUTTONDOWN:
moel@176
   128
          message.Result = IntPtr.Zero; break;
moel@176
   129
        case WM_NCRBUTTONUP:
moel@176
   130
          if (contextMenu != null)
moel@176
   131
            ShowContextMenu(new Point(
moel@176
   132
              (int)((uint)message.LParam & 0xFFFF), 
moel@176
   133
              (int)(((uint)message.LParam >>16) & 0xFFFF)));          
moel@176
   134
          message.Result = IntPtr.Zero; 
moel@176
   135
          break;
moel@176
   136
        case WM_WINDOWPOSCHANGING:         
moel@176
   137
          WindowPos wp = (WindowPos)Marshal.PtrToStructure(
moel@176
   138
            message.LParam, typeof(WindowPos));
moel@176
   139
moel@176
   140
          // add the nomove flag if position is locked
moel@176
   141
          if (lockPosition)
moel@176
   142
            wp.flags |= SWP_NOMOVE;
moel@176
   143
moel@176
   144
          // prevent the window from leaving the screen
moel@176
   145
          if ((wp.flags & SWP_NOMOVE) == 0) {            
moel@176
   146
            Rectangle rect = Screen.GetWorkingArea(new Point(wp.x, wp.y));
moel@176
   147
            const int margin = 20;
moel@176
   148
            wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
moel@176
   149
            wp.x = Math.Min(wp.x, rect.Right - margin);
moel@176
   150
            wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
moel@176
   151
            wp.y = Math.Min(wp.y, rect.Bottom - margin);
moel@176
   152
moel@176
   153
            // raise the event if location changed
moel@176
   154
            if (location.X != wp.x || location.Y != wp.y) {
moel@176
   155
              location = new Point(wp.x, wp.y);
moel@176
   156
              if (LocationChanged != null)
moel@176
   157
                LocationChanged(this, EventArgs.Empty);
moel@176
   158
            }
moel@176
   159
          }          
moel@176
   160
moel@176
   161
          Marshal.StructureToPtr(wp, message.LParam, false);
moel@176
   162
          message.Result = IntPtr.Zero;
moel@176
   163
          break;           
moel@176
   164
        default:
moel@176
   165
          base.WndProc(ref message); break;
moel@176
   166
      }      
moel@176
   167
    }
moel@176
   168
moel@176
   169
    private BlendFunction CreateBlendFunction() {
moel@176
   170
      BlendFunction blend = new BlendFunction();
moel@176
   171
      blend.BlendOp = AC_SRC_OVER;
moel@176
   172
      blend.BlendFlags = 0;
moel@176
   173
      blend.SourceConstantAlpha = opacity;
moel@176
   174
      blend.AlphaFormat = AC_SRC_ALPHA;
moel@176
   175
      return blend;
moel@176
   176
    }
moel@176
   177
moel@176
   178
    public void Update(Bitmap bitmap) {
moel@176
   179
      IntPtr screen = NativeMethods.GetDC(IntPtr.Zero);
moel@176
   180
      IntPtr memory = NativeMethods.CreateCompatibleDC(screen);
moel@176
   181
      IntPtr newHBitmap = IntPtr.Zero;
moel@176
   182
      IntPtr oldHBitmap = IntPtr.Zero;
moel@176
   183
moel@176
   184
      try {
moel@176
   185
        newHBitmap = bitmap.GetHbitmap(Color.Black);
moel@176
   186
        oldHBitmap = NativeMethods.SelectObject(memory, newHBitmap);
moel@176
   187
moel@176
   188
        Size size = bitmap.Size;
moel@176
   189
        Point pointSource = Point.Empty;
moel@176
   190
        Point topPos = Location;
moel@176
   191
moel@176
   192
        BlendFunction blend = CreateBlendFunction();
moel@176
   193
        NativeMethods.UpdateLayeredWindow(Handle, screen, ref topPos,
moel@176
   194
          ref size, memory, ref pointSource, 0, ref blend, ULW_ALPHA);
moel@176
   195
      } finally {
moel@176
   196
        NativeMethods.ReleaseDC(IntPtr.Zero, screen);
moel@176
   197
        if (newHBitmap != IntPtr.Zero) {
moel@176
   198
          NativeMethods.SelectObject(memory, oldHBitmap);
moel@176
   199
          NativeMethods.DeleteObject(newHBitmap);
moel@176
   200
        }
moel@176
   201
        NativeMethods.DeleteDC(memory);
moel@176
   202
      }
moel@176
   203
    }
moel@176
   204
moel@176
   205
    public byte Opacity {
moel@176
   206
      get {
moel@176
   207
        return opacity;
moel@176
   208
      }
moel@176
   209
      set {
moel@176
   210
        if (opacity != value) {
moel@176
   211
          opacity = value;
moel@176
   212
          BlendFunction blend = CreateBlendFunction();
moel@176
   213
          NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
moel@176
   214
            IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blend, ULW_ALPHA);
moel@176
   215
        }
moel@176
   216
      }
moel@176
   217
    }
moel@176
   218
moel@176
   219
    public bool Visible {
moel@176
   220
      get {
moel@176
   221
        return visible;
moel@176
   222
      }
moel@176
   223
      set {
moel@176
   224
        if (visible != value) {
moel@176
   225
          visible = value;
moel@176
   226
          NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0,
moel@176
   227
            SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER |
moel@176
   228
            (value ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
moel@176
   229
          if (value)
moel@176
   230
            ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
moel@176
   231
          else
moel@176
   232
            ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;         
moel@176
   233
        }
moel@176
   234
      }
moel@176
   235
    }
moel@176
   236
moel@176
   237
    // if locked, the window can not be moved
moel@176
   238
    public bool LockPosition {
moel@176
   239
      get {
moel@176
   240
        return lockPosition;
moel@176
   241
      }
moel@176
   242
      set {
moel@176
   243
        lockPosition = value;
moel@176
   244
      }
moel@176
   245
    }
moel@176
   246
moel@176
   247
    public bool AlwaysOnTop {
moel@176
   248
      get {
moel@176
   249
        return alwaysOnTop;
moel@176
   250
      }
moel@176
   251
      set {
moel@176
   252
        if (value != alwaysOnTop) {
moel@176
   253
          alwaysOnTop = value;
moel@176
   254
          if (alwaysOnTop) {
moel@176
   255
            ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
moel@176
   256
            MoveToTopMost(Handle);            
moel@176
   257
          } else {
moel@176
   258
            MoveToBottom(Handle);
moel@176
   259
            ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
moel@176
   260
          }
moel@176
   261
        }
moel@176
   262
      }
moel@176
   263
    }
moel@176
   264
moel@176
   265
    public Size Size {
moel@176
   266
      get {
moel@176
   267
        return size; 
moel@176
   268
      }
moel@176
   269
      set {
moel@176
   270
        if (size != value) {
moel@176
   271
          size = value;
moel@176
   272
          NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, size.Width,
moel@176
   273
            size.Height, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | 
moel@176
   274
            SWP_NOSENDCHANGING);
moel@176
   275
        }
moel@176
   276
      }
moel@176
   277
    }
moel@176
   278
moel@176
   279
    public Point Location {
moel@176
   280
      get {
moel@176
   281
        return location;
moel@176
   282
      }
moel@176
   283
      set {
moel@176
   284
        NativeMethods.SetWindowPos(Handle, IntPtr.Zero, value.X, value.Y, 0,
moel@176
   285
          0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
moel@176
   286
        location = value;
moel@176
   287
        if (LocationChanged != null)
moel@176
   288
          LocationChanged(this, EventArgs.Empty);
moel@176
   289
      }
moel@176
   290
    }
moel@176
   291
moel@176
   292
    public event EventHandler LocationChanged;
moel@176
   293
moel@176
   294
    public ContextMenu ContextMenu {
moel@176
   295
      get {
moel@176
   296
        return contextMenu;
moel@176
   297
      }
moel@176
   298
      set {
moel@176
   299
        this.contextMenu = value;
moel@176
   300
      }
moel@176
   301
    }
moel@176
   302
moel@176
   303
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@176
   304
    private struct BlendFunction {
moel@176
   305
      public byte BlendOp;
moel@176
   306
      public byte BlendFlags;
moel@176
   307
      public byte SourceConstantAlpha;
moel@176
   308
      public byte AlphaFormat;
moel@176
   309
    }
moel@176
   310
moel@176
   311
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@176
   312
    private struct WindowPos {
moel@176
   313
      public IntPtr hwnd;
moel@176
   314
      public IntPtr hwndInsertAfter;
moel@176
   315
      public int x;
moel@176
   316
      public int y;
moel@176
   317
      public int cx;
moel@176
   318
      public int cy;
moel@176
   319
      public uint flags;
moel@176
   320
    }
moel@176
   321
moel@176
   322
    public static readonly IntPtr HWND_BOTTOM = (IntPtr)1;
moel@176
   323
    public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
moel@176
   324
moel@176
   325
    public const int WS_EX_LAYERED = 0x00080000;
moel@176
   326
    public const int WS_EX_TOOLWINDOW = 0x00000080;
moel@176
   327
moel@176
   328
    public const uint SWP_NOSIZE = 0x0001;
moel@176
   329
    public const uint SWP_NOMOVE = 0x0002;
moel@176
   330
    public const uint SWP_NOACTIVATE = 0x0010;
moel@176
   331
    public const uint SWP_HIDEWINDOW = 0x0080;
moel@176
   332
    public const uint SWP_SHOWWINDOW = 0x0040;
moel@176
   333
    public const uint SWP_NOZORDER = 0x0004;
moel@176
   334
    public const uint SWP_NOSENDCHANGING = 0x0400;
moel@176
   335
moel@176
   336
    public const int ULW_COLORKEY = 0x00000001;
moel@176
   337
    public const int ULW_ALPHA = 0x00000002;
moel@176
   338
    public const int ULW_OPAQUE = 0x00000004;
moel@176
   339
moel@176
   340
    public const byte AC_SRC_OVER = 0x00;
moel@176
   341
    public const byte AC_SRC_ALPHA = 0x01;
moel@176
   342
moel@176
   343
    public const int WM_NCHITTEST = 0x0084;
moel@176
   344
    public const int WM_NCLBUTTONDBLCLK = 0x00A3;
moel@176
   345
    public const int WM_NCLBUTTONDOWN = 0x00A1;
moel@176
   346
    public const int WM_NCLBUTTONUP = 0x00A2;
moel@176
   347
    public const int WM_NCRBUTTONDOWN = 0x00A4;
moel@176
   348
    public const int WM_NCRBUTTONUP = 0x00A5;
moel@176
   349
    public const int WM_WINDOWPOSCHANGING = 0x0046;
moel@176
   350
    public const int WM_COMMAND = 0x0111;
moel@176
   351
moel@176
   352
    public const int TPM_RIGHTBUTTON = 0x0002;
moel@176
   353
    public const int TPM_VERTICAL = 0x0040;
moel@176
   354
moel@176
   355
    public readonly IntPtr HTCAPTION = (IntPtr)2;
moel@176
   356
moel@176
   357
    private enum WindowAttribute : int {
moel@176
   358
      DWMWA_NCRENDERING_ENABLED = 1,
moel@176
   359
      DWMWA_NCRENDERING_POLICY,
moel@176
   360
      DWMWA_TRANSITIONS_FORCEDISABLED,
moel@176
   361
      DWMWA_ALLOW_NCPAINT,
moel@176
   362
      DWMWA_CAPTION_BUTTON_BOUNDS,
moel@176
   363
      DWMWA_NONCLIENT_RTL_LAYOUT,
moel@176
   364
      DWMWA_FORCE_ICONIC_REPRESENTATION,
moel@176
   365
      DWMWA_FLIP3D_POLICY,
moel@176
   366
      DWMWA_EXTENDED_FRAME_BOUNDS,
moel@176
   367
      DWMWA_HAS_ICONIC_BITMAP,
moel@176
   368
      DWMWA_DISALLOW_PEEK,
moel@176
   369
      DWMWA_EXCLUDED_FROM_PEEK,
moel@176
   370
      DWMWA_LAST
moel@176
   371
    }
moel@176
   372
moel@176
   373
    private static class NativeMethods {
moel@176
   374
      private const string USER = "user32.dll";
moel@176
   375
      private const string GDI = "gdi32.dll";
moel@176
   376
      public const string DWMAPI = "dwmapi.dll";
moel@176
   377
moel@176
   378
      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176
   379
      [return: MarshalAs(UnmanagedType.Bool)]
moel@176
   380
      public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, 
moel@176
   381
        ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, 
moel@176
   382
        int crKey, ref BlendFunction pblend, int dwFlags);
moel@176
   383
moel@176
   384
      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176
   385
      [return: MarshalAs(UnmanagedType.Bool)]
moel@176
   386
      public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
moel@176
   387
        IntPtr pptDst, IntPtr psize, IntPtr hdcSrc, IntPtr pprSrc,
moel@176
   388
        int crKey, ref BlendFunction pblend, int dwFlags);  
moel@176
   389
moel@176
   390
      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176
   391
      public static extern IntPtr GetDC(IntPtr hWnd);
moel@176
   392
moel@176
   393
      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176
   394
      public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
moel@176
   395
moel@176
   396
      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176
   397
      public static extern bool SetWindowPos(IntPtr hWnd,
moel@176
   398
        IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
moel@176
   399
moel@176
   400
      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176
   401
      public static extern bool TrackPopupMenuEx(IntPtr hMenu, uint uFlags, 
moel@176
   402
        int x, int y, IntPtr hWnd, IntPtr tpmParams);
moel@176
   403
moel@176
   404
      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
moel@176
   405
      public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
moel@176
   406
moel@176
   407
      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
moel@176
   408
      [return: MarshalAs(UnmanagedType.Bool)]
moel@176
   409
      public static extern bool DeleteDC(IntPtr hdc);
moel@176
   410
      
moel@176
   411
      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
moel@176
   412
      public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
moel@176
   413
moel@176
   414
      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
moel@176
   415
      [return: MarshalAs(UnmanagedType.Bool)]
moel@176
   416
      public static extern bool DeleteObject(IntPtr hObject);
moel@176
   417
moel@176
   418
      [DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)]
moel@176
   419
      public static extern int DwmSetWindowAttribute(IntPtr hwnd,
moel@176
   420
        WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);
moel@176
   421
    }    
moel@176
   422
  }
moel@176
   423
}