1 /* 2 KeePass Password Safe - The Open-Source Password Manager 3 Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 using System; 21 using System.Collections.Generic; 22 using System.Collections.Specialized; 23 using System.Diagnostics; 24 using System.Drawing; 25 using System.Drawing.Drawing2D; 26 using System.Drawing.Imaging; 27 using System.IO; 28 using System.Reflection; 29 using System.Text; 30 using System.Windows.Forms; 31 32 using KeePass.Native; 33 using KeePass.Util; 34 35 using KeePassLib; 36 using KeePassLib.Utility; 37 38 using NativeLib = KeePassLib.Native.NativeLib; 39 40 namespace KeePass.UI 41 { 42 public static class DpiUtil 43 { 44 private const int StdDpi = 96; 45 46 private static bool m_bInitialized = false; 47 48 private static int m_nDpiX = StdDpi; 49 private static int m_nDpiY = StdDpi; 50 51 private static double m_dScaleX = 1.0; 52 public static double FactorX 53 { 54 get 55 { 56 EnsureInitialized(); 57 return m_dScaleX; 58 } 59 } 60 61 private static double m_dScaleY = 1.0; 62 public static double FactorY 63 { 64 get 65 { 66 EnsureInitialized(); 67 return m_dScaleY; 68 } 69 } 70 71 public static bool ScalingRequired 72 { 73 get 74 { 75 if(Program.DesignMode) return false; 76 77 EnsureInitialized(); 78 return ((m_nDpiX != StdDpi) || (m_nDpiY != StdDpi)); 79 } 80 } 81 EnsureInitialized()82 private static void EnsureInitialized() 83 { 84 if(m_bInitialized) return; 85 if(NativeLib.IsUnix()) { m_bInitialized = true; return; } 86 87 try 88 { 89 IntPtr hDC = NativeMethods.GetDC(IntPtr.Zero); 90 if(hDC != IntPtr.Zero) 91 { 92 m_nDpiX = NativeMethods.GetDeviceCaps(hDC, 93 NativeMethods.LOGPIXELSX); 94 m_nDpiY = NativeMethods.GetDeviceCaps(hDC, 95 NativeMethods.LOGPIXELSY); 96 if((m_nDpiX <= 0) || (m_nDpiY <= 0)) 97 { 98 Debug.Assert(false); 99 m_nDpiX = StdDpi; 100 m_nDpiY = StdDpi; 101 } 102 103 if(NativeMethods.ReleaseDC(IntPtr.Zero, hDC) != 1) 104 { 105 Debug.Assert(false); 106 } 107 } 108 else { Debug.Assert(false); } 109 } 110 catch(Exception) { Debug.Assert(false); } 111 112 m_dScaleX = (double)m_nDpiX / (double)StdDpi; 113 m_dScaleY = (double)m_nDpiY / (double)StdDpi; 114 115 m_bInitialized = true; 116 } 117 ConfigureProcess()118 public static void ConfigureProcess() 119 { 120 Debug.Assert(!m_bInitialized); // Configure process before use 121 if(NativeLib.IsUnix()) return; 122 123 // try 124 // { 125 // ConfigurationManager.AppSettings.Set( 126 // "EnableWindowsFormsHighDpiAutoResizing", "true"); 127 // } 128 // catch(Exception) { Debug.Assert(false); } 129 #if DEBUG 130 // Ensure that the .config file enables high DPI features 131 string strExeConfig = WinUtil.GetExecutable() + ".config"; 132 if(File.Exists(strExeConfig)) 133 { 134 string strCM = "System.Configuration.ConfigurationManager, "; 135 strCM += "System.Configuration, Version="; 136 strCM += Environment.Version.Major.ToString() + ".0.0.0, "; 137 strCM += "Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; 138 139 Type tCM = Type.GetType(strCM, false); 140 if(tCM != null) 141 { 142 PropertyInfo pi = tCM.GetProperty("AppSettings", 143 (BindingFlags.Public | BindingFlags.Static)); 144 if(pi != null) 145 { 146 NameValueCollection nvc = (pi.GetValue(null, null) as 147 NameValueCollection); 148 if(nvc != null) 149 { 150 Debug.Assert(string.Equals(nvc.Get( 151 "EnableWindowsFormsHighDpiAutoResizing"), 152 "true", StrUtil.CaseIgnoreCmp)); 153 } 154 else { Debug.Assert(false); } 155 } 156 else { Debug.Assert(false); } 157 } 158 else { Debug.Assert(false); } // Assembly should be loaded 159 } 160 #endif 161 162 try 163 { 164 // SetProcessDPIAware is obsolete; use 165 // SetProcessDpiAwareness on Windows 10 and higher 166 if(WinUtil.IsAtLeastWindows10) // 8.1 partially 167 { 168 if(NativeMethods.SetProcessDpiAwareness( 169 NativeMethods.ProcessDpiAwareness.SystemAware) < 0) 170 { 171 Debug.Assert(false); 172 } 173 } 174 else if(WinUtil.IsAtLeastWindowsVista) 175 { 176 if(!NativeMethods.SetProcessDPIAware()) { Debug.Assert(false); } 177 } 178 } 179 catch(Exception) { Debug.Assert(false); } 180 } 181 Configure(ToolStrip ts)182 internal static void Configure(ToolStrip ts) 183 { 184 if(ts == null) { Debug.Assert(false); return; } 185 if(!DpiUtil.ScalingRequired) return; 186 187 Size sz = ts.ImageScalingSize; 188 if((sz.Width == 16) && (sz.Height == 16)) 189 { 190 sz.Width = ScaleIntX(16); 191 sz.Height = ScaleIntY(16); 192 193 ts.ImageScalingSize = sz; 194 } 195 else 196 { 197 Debug.Assert(((sz.Width == ScaleIntX(16)) && 198 (sz.Height == ScaleIntY(16))), sz.ToString()); 199 } 200 } 201 ScaleIntX(int i)202 public static int ScaleIntX(int i) 203 { 204 EnsureInitialized(); 205 return (int)Math.Round((double)i * m_dScaleX); 206 } 207 ScaleIntY(int i)208 public static int ScaleIntY(int i) 209 { 210 EnsureInitialized(); 211 return (int)Math.Round((double)i * m_dScaleY); 212 } 213 ScaleImage(Image img, bool bForceNewObject)214 public static Image ScaleImage(Image img, bool bForceNewObject) 215 { 216 if(img == null) { Debug.Assert(false); return null; } 217 218 // EnsureInitialized(); // Done by ScaleIntX 219 220 int w = img.Width; 221 int h = img.Height; 222 int sw = ScaleIntX(w); 223 int sh = ScaleIntY(h); 224 225 if((w == sw) && (h == sh) && !bForceNewObject) 226 return img; 227 228 return GfxUtil.ScaleImage(img, sw, sh, ScaleTransformFlags.UIIcon); 229 } 230 ScaleToolStripItems(ToolStripItem[] vItems, int[] vWidths)231 internal static void ScaleToolStripItems(ToolStripItem[] vItems, 232 int[] vWidths) 233 { 234 if(vItems == null) { Debug.Assert(false); return; } 235 if(vWidths == null) { Debug.Assert(false); return; } 236 if(vItems.Length != vWidths.Length) { Debug.Assert(false); return; } 237 238 for(int i = 0; i < vItems.Length; ++i) 239 { 240 ToolStripItem tsi = vItems[i]; 241 if(tsi == null) { Debug.Assert(false); continue; } 242 243 int nWidth = vWidths[i]; 244 int nWidthScaled = ScaleIntX(nWidth); 245 Debug.Assert(nWidth >= 0); 246 247 if(nWidth == nWidthScaled) 248 { 249 Debug.Assert(tsi.Width == nWidth); 250 continue; 251 } 252 253 try 254 { 255 int w = tsi.Width; 256 257 // .NET scales some ToolStripItems, some not 258 Debug.Assert(((w == nWidth) || (w == nWidthScaled)), 259 tsi.Name + ": w = " + w.ToString()); 260 261 if(Math.Abs(w - nWidth) < Math.Abs(w - nWidthScaled)) 262 tsi.Width = nWidthScaled; 263 } 264 catch(Exception) { Debug.Assert(false); } 265 } 266 } 267 GetIcon(PwDatabase pd, PwUuid pwUuid)268 internal static Image GetIcon(PwDatabase pd, PwUuid pwUuid) 269 { 270 if(pd == null) { Debug.Assert(false); return null; } 271 if(pwUuid == null) { Debug.Assert(false); return null; } 272 273 int w = ScaleIntX(16); 274 int h = ScaleIntY(16); 275 276 return pd.GetCustomIcon(pwUuid, w, h); 277 } 278 279 [Conditional("DEBUG")] AssertUIImage(Image img)280 internal static void AssertUIImage(Image img) 281 { 282 #if DEBUG 283 if(img == null) { Debug.Assert(false); return; } 284 285 EnsureInitialized(); 286 287 try 288 { 289 // Windows XP scales images based on the DPI resolution 290 // specified in the image file; thus ensure that the 291 // image file does not specify a DPI resolution; 292 // https://sourceforge.net/p/keepass/bugs/1487/ 293 294 int d = (int)Math.Round(img.HorizontalResolution); 295 Debug.Assert((d == 0) || (d == m_nDpiX)); 296 297 d = (int)Math.Round(img.VerticalResolution); 298 Debug.Assert((d == 0) || (d == m_nDpiY)); 299 } 300 catch(Exception) { Debug.Assert(false); } 301 #endif 302 } 303 } 304 } 305