GUI/SensorNotifyIcon.cs
author moel.mich
Sat, 25 Jun 2011 14:46:28 +0000
changeset 304 16a86362c2ca
parent 252 e62afa69214f
child 317 1ccf99e620a9
permissions -rw-r--r--
Changed the maximum buffer size for double buffering of controls that isn't disposed after each draw call to the size of the screen. This should reduce the memory allocation and disposing on each sensor update. Also page faults are no longer increasing with this change.
moel@202
     1
/*
moel@40
     2
  
moel@40
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@40
     4
moel@40
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@40
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@40
     7
  the License. You may obtain a copy of the License at
moel@40
     8
 
moel@40
     9
  http://www.mozilla.org/MPL/
moel@40
    10
moel@40
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@40
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@40
    13
  for the specific language governing rights and limitations under the License.
moel@40
    14
moel@40
    15
  The Original Code is the Open Hardware Monitor code.
moel@40
    16
moel@40
    17
  The Initial Developer of the Original Code is 
moel@40
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@40
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2010
moel@40
    20
  the Initial Developer. All Rights Reserved.
moel@40
    21
moel@40
    22
  Contributor(s):
moel@40
    23
moel@40
    24
  Alternatively, the contents of this file may be used under the terms of
moel@40
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@40
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@40
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@40
    28
  of those above. If you wish to allow use of your version of this file only
moel@40
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@40
    30
  use your version of this file under the terms of the MPL, indicate your
moel@40
    31
  decision by deleting the provisions above and replace them with the notice
moel@40
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@40
    33
  the provisions above, a recipient may use your version of this file under
moel@40
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@40
    35
 
moel@40
    36
*/
moel@40
    37
moel@40
    38
using System;
moel@40
    39
using System.Drawing;
moel@40
    40
using System.Drawing.Drawing2D;
moel@40
    41
using System.Drawing.Imaging;
moel@40
    42
using System.Drawing.Text;
moel@176
    43
using System.Runtime.InteropServices;
moel@40
    44
using System.Windows.Forms;
moel@40
    45
using OpenHardwareMonitor.Hardware;
moel@40
    46
using OpenHardwareMonitor.Utilities;
moel@40
    47
moel@40
    48
namespace OpenHardwareMonitor.GUI {
moel@40
    49
  public class SensorNotifyIcon : IDisposable {
moel@40
    50
moel@40
    51
    private ISensor sensor;
moel@40
    52
    private NotifyIcon notifyIcon;
moel@40
    53
    private Bitmap bitmap;
moel@40
    54
    private Graphics graphics;
moel@42
    55
    private Color color;
moel@43
    56
    private Color darkColor;
moel@43
    57
    private Brush brush;
moel@43
    58
    private Brush darkBrush;
moel@43
    59
    private Pen pen;
moel@70
    60
    private Font font;
moel@40
    61
moel@133
    62
    public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
moel@165
    63
      bool balloonTip, PersistentSettings settings) 
moel@42
    64
    {
moel@40
    65
      this.sensor = sensor;
moel@40
    66
      this.notifyIcon = new NotifyIcon();
moel@43
    67
moel@43
    68
      Color defaultColor = Color.Black;
moel@217
    69
      if (sensor.SensorType == SensorType.Load ||
moel@217
    70
          sensor.SensorType == SensorType.Control ||
moel@217
    71
          sensor.SensorType == SensorType.Level) 
moel@217
    72
      {
moel@43
    73
        defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
moel@43
    74
      }
moel@166
    75
      Color = settings.GetValue(new Identifier(sensor.Identifier, 
moel@109
    76
        "traycolor").ToString(), defaultColor);      
moel@40
    77
      
moel@43
    78
      this.pen = new Pen(Color.FromArgb(96, Color.Black));
moel@117
    79
      this.font = SystemFonts.MessageBoxFont;
moel@43
    80
moel@156
    81
      ContextMenu contextMenu = new ContextMenu();
moel@156
    82
      MenuItem hideShowItem = new MenuItem("Hide/Show");
moel@133
    83
      hideShowItem.Click += delegate(object obj, EventArgs args) {
moel@133
    84
        sensorSystemTray.SendHideShowCommand();
moel@133
    85
      };
moel@156
    86
      contextMenu.MenuItems.Add(hideShowItem);
moel@156
    87
      contextMenu.MenuItems.Add(new MenuItem("-"));
moel@156
    88
      MenuItem removeItem = new MenuItem("Remove Sensor");
moel@42
    89
      removeItem.Click += delegate(object obj, EventArgs args) {
moel@42
    90
        sensorSystemTray.Remove(this.sensor);
moel@40
    91
      };
moel@156
    92
      contextMenu.MenuItems.Add(removeItem);
moel@156
    93
      MenuItem colorItem = new MenuItem("Change Color...");
moel@42
    94
      colorItem.Click += delegate(object obj, EventArgs args) {
moel@42
    95
        ColorDialog dialog = new ColorDialog();
moel@43
    96
        dialog.Color = Color;
moel@42
    97
        if (dialog.ShowDialog() == DialogResult.OK) {
moel@43
    98
          Color = dialog.Color;
moel@166
    99
          settings.SetValue(new Identifier(sensor.Identifier,
moel@109
   100
            "traycolor").ToString(), Color);
moel@42
   101
        }
moel@42
   102
      };
moel@156
   103
      contextMenu.MenuItems.Add(colorItem);
moel@156
   104
      contextMenu.MenuItems.Add(new MenuItem("-"));
moel@156
   105
      MenuItem exitItem = new MenuItem("Exit");
moel@133
   106
      exitItem.Click += delegate(object obj, EventArgs args) {
moel@133
   107
        sensorSystemTray.SendExitCommand();
moel@133
   108
      };
moel@156
   109
      contextMenu.MenuItems.Add(exitItem);
moel@156
   110
      this.notifyIcon.ContextMenu = contextMenu;
moel@133
   111
      this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
moel@133
   112
        sensorSystemTray.SendHideShowCommand();
moel@133
   113
      };
moel@40
   114
moel@252
   115
      // get the default dpi to create an icon with the correct size
moel@252
   116
      float dpiX, dpiY;
moel@252
   117
      using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
moel@252
   118
        dpiX = b.HorizontalResolution;
moel@252
   119
        dpiY = b.VerticalResolution;
moel@252
   120
      }
moel@252
   121
moel@252
   122
      // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi) 
moel@252
   123
      int width = (int)Math.Round(16 * dpiX / 96);
moel@252
   124
      int height = (int)Math.Round(16 * dpiY / 96);
moel@252
   125
moel@252
   126
      // make sure it does never get smaller than 16x16
moel@252
   127
      width = width < 16 ? 16: width;
moel@252
   128
      height = height < 16 ? 16: height;
moel@252
   129
moel@252
   130
      this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);      
moel@40
   131
      this.graphics = Graphics.FromImage(this.bitmap);
moel@117
   132
moel@117
   133
      if (Environment.OSVersion.Version.Major > 5) {
moel@117
   134
        this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
moel@117
   135
        this.graphics.SmoothingMode = SmoothingMode.HighQuality;
moel@117
   136
      }
moel@40
   137
    }
moel@40
   138
moel@40
   139
    public ISensor Sensor {
moel@40
   140
      get { return sensor; }
moel@40
   141
    }
moel@40
   142
moel@42
   143
    public Color Color {
moel@42
   144
      get { return color; }
moel@43
   145
      set { 
moel@43
   146
        this.color = value;
moel@43
   147
        this.darkColor = Color.FromArgb(255,
moel@44
   148
          this.color.R / 3,
moel@44
   149
          this.color.G / 3,
moel@44
   150
          this.color.B / 3);
moel@43
   151
        Brush brush = this.brush;
moel@43
   152
        this.brush = new SolidBrush(this.color);
moel@43
   153
        if (brush != null)
moel@43
   154
          brush.Dispose();
moel@43
   155
        Brush darkBrush = this.darkBrush;
moel@43
   156
        this.darkBrush = new SolidBrush(this.darkColor);
moel@43
   157
        if (darkBrush != null)
moel@43
   158
          darkBrush.Dispose();
moel@43
   159
      }
moel@42
   160
    }
moel@42
   161
moel@40
   162
    public void Dispose() {      
moel@40
   163
      Icon icon = notifyIcon.Icon;
moel@40
   164
      notifyIcon.Icon = null;
moel@40
   165
      if (icon != null)
moel@40
   166
        icon.Dispose();      
moel@40
   167
      notifyIcon.Dispose();
moel@40
   168
moel@43
   169
      if (brush != null)
moel@43
   170
        brush.Dispose();
moel@43
   171
      if (darkBrush != null)
moel@43
   172
        darkBrush.Dispose();
moel@43
   173
      pen.Dispose();
moel@85
   174
      graphics.Dispose();      
moel@85
   175
      bitmap.Dispose();      
moel@40
   176
    }
moel@40
   177
moel@40
   178
    private string GetString() {
moel@282
   179
      if (!sensor.Value.HasValue)
moel@282
   180
        return "-";
moel@282
   181
moel@40
   182
      switch (sensor.SensorType) {
moel@40
   183
        case SensorType.Voltage:
moel@40
   184
          return string.Format("{0:F11}", sensor.Value);
moel@40
   185
        case SensorType.Clock:
moel@40
   186
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@40
   187
        case SensorType.Load: 
moel@118
   188
          return string.Format("{0:F0}", sensor.Value);
moel@40
   189
        case SensorType.Temperature: 
moel@40
   190
          return string.Format("{0:F0}", sensor.Value);
moel@40
   191
        case SensorType.Fan: 
moel@40
   192
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@57
   193
        case SensorType.Flow:
moel@57
   194
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@118
   195
        case SensorType.Control:
moel@118
   196
          return string.Format("{0:F0}", sensor.Value);
moel@217
   197
        case SensorType.Level:
moel@217
   198
          return string.Format("{0:F0}", sensor.Value);
moel@40
   199
      }
moel@40
   200
      return "-";
moel@40
   201
    }
moel@40
   202
moel@40
   203
    private Icon CreateTransparentIcon() {
moel@40
   204
moel@40
   205
      graphics.Clear(Color.Black);
moel@70
   206
      TextRenderer.DrawText(graphics, GetString(), font,
moel@117
   207
        new Point(-2, 0), Color.White, Color.Black);        
moel@40
   208
moel@40
   209
      BitmapData data = bitmap.LockBits(
moel@40
   210
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@40
   211
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@40
   212
moel@40
   213
      IntPtr Scan0 = data.Scan0;
moel@40
   214
moel@40
   215
      int numBytes = bitmap.Width * bitmap.Height * 4;
moel@40
   216
      byte[] bytes = new byte[numBytes];
moel@40
   217
      Marshal.Copy(Scan0, bytes, 0, numBytes);
moel@40
   218
      bitmap.UnlockBits(data);
moel@40
   219
moel@40
   220
      byte red, green, blue;
moel@40
   221
      for (int i = 0; i < bytes.Length; i += 4) {
moel@40
   222
        blue = bytes[i];
moel@40
   223
        green = bytes[i + 1];
moel@40
   224
        red = bytes[i + 2];
moel@40
   225
moel@42
   226
        bytes[i] = color.B;
moel@42
   227
        bytes[i + 1] = color.G;
moel@42
   228
        bytes[i + 2] = color.R;
moel@40
   229
        bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
moel@40
   230
      }
moel@40
   231
moel@252
   232
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   233
        PixelFormat.Format32bppArgb);
moel@40
   234
    }
moel@40
   235
moel@217
   236
    private Icon CreatePercentageIcon() {      
moel@86
   237
      try {
moel@86
   238
        graphics.Clear(Color.Transparent);
moel@86
   239
      } catch (ArgumentException) {
moel@86
   240
        graphics.Clear(Color.Black);
moel@86
   241
      }
moel@252
   242
      graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
moel@282
   243
      float value = sensor.Value.GetValueOrDefault();
moel@282
   244
      float y = 0.16f * (100 - value);
moel@252
   245
      graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
moel@252
   246
      graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
moel@43
   247
moel@43
   248
      BitmapData data = bitmap.LockBits(
moel@43
   249
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@43
   250
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@43
   251
      byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
moel@43
   252
      Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
moel@43
   253
      bitmap.UnlockBits(data);
moel@43
   254
moel@252
   255
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   256
        PixelFormat.Format32bppArgb);
moel@43
   257
    }
moel@43
   258
moel@40
   259
    public void Update() {
moel@40
   260
      Icon icon = notifyIcon.Icon;
moel@40
   261
moel@217
   262
      switch (sensor.SensorType) {
moel@217
   263
        case SensorType.Load:
moel@217
   264
        case SensorType.Control:
moel@217
   265
        case SensorType.Level:
moel@217
   266
          notifyIcon.Icon = CreatePercentageIcon();
moel@217
   267
          break;
moel@217
   268
        default:
moel@217
   269
          notifyIcon.Icon = CreateTransparentIcon();
moel@217
   270
          break;
moel@40
   271
      }
moel@217
   272
moel@40
   273
      if (icon != null) 
moel@40
   274
        icon.Dispose();
moel@40
   275
moel@40
   276
      string format = "";
moel@40
   277
      switch (sensor.SensorType) {
moel@116
   278
        case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
moel@116
   279
        case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
moel@116
   280
        case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
moel@116
   281
        case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
moel@116
   282
        case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
moel@116
   283
        case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
moel@118
   284
        case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
moel@217
   285
        case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
moel@40
   286
      }
moel@116
   287
      string formattedValue = string.Format(format, sensor.Name, sensor.Value);
moel@116
   288
      string hardwareName = sensor.Hardware.Name;
moel@116
   289
      hardwareName = hardwareName.Substring(0, 
moel@116
   290
        Math.Min(63 - formattedValue.Length, hardwareName.Length));
moel@116
   291
      string text = hardwareName + formattedValue;
moel@116
   292
      if (text.Length > 63)
moel@116
   293
        text = null;
moel@40
   294
moel@116
   295
      notifyIcon.Text = text;
moel@42
   296
      notifyIcon.Visible = true;         
moel@40
   297
    }
moel@40
   298
  }
moel@40
   299
}