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.Diagnostics;
23 using System.Runtime.InteropServices;
24 using System.Text;
25 using System.Windows.Forms;
26 
27 using KeePass.App;
28 using KeePass.Forms;
29 using KeePass.Native;
30 using KeePass.Resources;
31 using KeePass.UI;
32 
33 using KeePassLib;
34 using KeePassLib.Utility;
35 
36 using NativeLib = KeePassLib.Native.NativeLib;
37 
38 namespace KeePass.Util
39 {
40 	public static class HotKeyManager
41 	{
42 		private static Form m_fRecvWnd = null;
43 		private static Dictionary<int, Keys> m_vRegKeys = new Dictionary<int, Keys>();
44 
45 		// private static NativeMethods.BindKeyHandler m_hOnHotKey =
46 		//	new NativeMethods.BindKeyHandler(HotKeyManager.OnHotKey);
47 
48 		// public static Form ReceiverWindow
49 		// {
50 		//	get { return m_fRecvWnd; }
51 		//	set { m_fRecvWnd = value; }
52 		// }
53 
Initialize(Form fRecvWnd)54 		public static bool Initialize(Form fRecvWnd)
55 		{
56 			m_fRecvWnd = fRecvWnd;
57 
58 			// if(NativeLib.IsUnix())
59 			// {
60 			//	try { NativeMethods.tomboy_keybinder_init(); }
61 			//	catch(Exception) { Debug.Assert(false); return false; }
62 			// }
63 
64 			return true;
65 		}
66 
RegisterHotKey(int nId, Keys kKey)67 		public static bool RegisterHotKey(int nId, Keys kKey)
68 		{
69 			UnregisterHotKey(nId);
70 
71 			uint uMod = 0;
72 			if((kKey & Keys.Shift) != Keys.None) uMod |= NativeMethods.MOD_SHIFT;
73 			if((kKey & Keys.Alt) != Keys.None) uMod |= NativeMethods.MOD_ALT;
74 			if((kKey & Keys.Control) != Keys.None) uMod |= NativeMethods.MOD_CONTROL;
75 
76 			uint vkCode = (uint)(kKey & Keys.KeyCode);
77 			if(vkCode == (uint)Keys.None) return false; // Don't register mod keys only
78 
79 			try
80 			{
81 				if(!NativeLib.IsUnix())
82 				{
83 					if(NativeMethods.RegisterHotKey(m_fRecvWnd.Handle, nId, uMod, vkCode))
84 					{
85 						m_vRegKeys[nId] = kKey;
86 						return true;
87 					}
88 				}
89 				else // Unix
90 				{
91 					// NativeMethods.tomboy_keybinder_bind(EggAccKeysToString(kKey),
92 					//	m_hOnHotKey);
93 					// m_vRegKeys[nId] = kKey;
94 					// return true;
95 				}
96 			}
97 			catch(Exception) { Debug.Assert(false); }
98 
99 			return false;
100 		}
101 
UnregisterHotKey(int nId)102 		public static bool UnregisterHotKey(int nId)
103 		{
104 			if(m_vRegKeys.ContainsKey(nId))
105 			{
106 				// Keys k = m_vRegKeys[nId];
107 				m_vRegKeys.Remove(nId);
108 
109 				try
110 				{
111 					bool bResult;
112 					if(!NativeLib.IsUnix())
113 						bResult = NativeMethods.UnregisterHotKey(m_fRecvWnd.Handle, nId);
114 					else // Unix
115 					{
116 						// NativeMethods.tomboy_keybinder_unbind(EggAccKeysToString(k),
117 						//	m_hOnHotKey);
118 						// bResult = true;
119 						bResult = false;
120 					}
121 
122 					// Debug.Assert(bResult);
123 					return bResult;
124 				}
125 				catch(Exception) { Debug.Assert(false); }
126 			}
127 
128 			return false;
129 		}
130 
UnregisterAll()131 		public static void UnregisterAll()
132 		{
133 			List<int> vIDs = new List<int>(m_vRegKeys.Keys);
134 			foreach(int nID in vIDs) UnregisterHotKey(nID);
135 
136 			Debug.Assert(m_vRegKeys.Count == 0);
137 		}
138 
IsHotKeyRegistered(Keys kKey, bool bGlobal)139 		public static bool IsHotKeyRegistered(Keys kKey, bool bGlobal)
140 		{
141 			if(m_vRegKeys.ContainsValue(kKey)) return true;
142 			if(!bGlobal) return false;
143 
144 			int nID = AppDefs.GlobalHotKeyId.TempRegTest;
145 			if(!RegisterHotKey(nID, kKey)) return true;
146 
147 			UnregisterHotKey(nID);
148 			return false;
149 		}
150 
151 		/* private static void OnHotKey(string strKey, IntPtr lpUserData)
152 		{
153 			if(string.IsNullOrEmpty(strKey)) return;
154 			if(strKey.IndexOf(@"<Release>", StrUtil.CaseIgnoreCmp) >= 0) return;
155 
156 			if(m_fRecvWnd != null)
157 			{
158 				MainForm mf = (m_fRecvWnd as MainForm);
159 				if(mf == null) { Debug.Assert(false); return; }
160 
161 				Keys k = EggAccStringToKeys(strKey);
162 				foreach(KeyValuePair<int, Keys> kvp in m_vRegKeys)
163 				{
164 					if(kvp.Value == k) mf.HandleHotKey(kvp.Key);
165 				}
166 			}
167 			else { Debug.Assert(false); }
168 		}
169 
170 		private static Keys EggAccStringToKeys(string strKey)
171 		{
172 			if(string.IsNullOrEmpty(strKey)) return Keys.None;
173 
174 			Keys k = Keys.None;
175 
176 			if(strKey.IndexOf(@"<Alt>", StrUtil.CaseIgnoreCmp) >= 0)
177 				k |= Keys.Alt;
178 			if((strKey.IndexOf(@"<Ctl>", StrUtil.CaseIgnoreCmp) >= 0) ||
179 				(strKey.IndexOf(@"<Ctrl>", StrUtil.CaseIgnoreCmp) >= 0) ||
180 				(strKey.IndexOf(@"<Control>", StrUtil.CaseIgnoreCmp) >= 0))
181 				k |= Keys.Control;
182 			if((strKey.IndexOf(@"<Shft>", StrUtil.CaseIgnoreCmp) >= 0) ||
183 				(strKey.IndexOf(@"<Shift>", StrUtil.CaseIgnoreCmp) >= 0))
184 				k |= Keys.Shift;
185 
186 			string strKeyCode = strKey;
187 			while(strKeyCode.IndexOf('<') >= 0)
188 			{
189 				int nStart = strKeyCode.IndexOf('<');
190 				int nEnd = strKeyCode.IndexOf('>');
191 				if((nStart < 0) || (nEnd < 0) || (nEnd <= nStart)) { Debug.Assert(false); break; }
192 
193 				strKeyCode = strKeyCode.Remove(nStart, nEnd - nStart + 1);
194 			}
195 			strKeyCode = strKeyCode.Trim();
196 
197 			try { k |= (Keys)Enum.Parse(typeof(Keys), strKeyCode, true); }
198 			catch(Exception) { Debug.Assert(false); }
199 
200 			return k;
201 		}
202 
203 		private static string EggAccKeysToString(Keys k)
204 		{
205 			StringBuilder sb = new StringBuilder();
206 
207 			if((k & Keys.Shift) != Keys.None) sb.Append(@"<Shift>");
208 			if((k & Keys.Control) != Keys.None) sb.Append(@"<Control>");
209 			if((k & Keys.Alt) != Keys.None) sb.Append(@"<Alt>");
210 
211 			sb.Append((k & Keys.KeyCode).ToString());
212 			return sb.ToString();
213 		} */
214 
CheckCtrlAltA(Form fParent)215 		internal static void CheckCtrlAltA(Form fParent)
216 		{
217 			try
218 			{
219 				if(!Program.Config.Integration.CheckHotKeys) return;
220 				if(NativeLib.IsUnix()) return;
221 
222 				// Check for a conflict only in the very specific case of
223 				// Ctrl+Alt+A; in all other cases we assume that the user
224 				// is aware of a possible conflict and intentionally wants
225 				// to override any system key combination
226 				if(Program.Config.Integration.HotKeyGlobalAutoType !=
227 					(long)(Keys.Control | Keys.Alt | Keys.A)) return;
228 
229 				// Check for a conflict only on Polish systems; other
230 				// languages typically don't use Ctrl+Alt+A frequently
231 				// and a conflict warning would just be confusing for
232 				// most users
233 				IntPtr hKL = NativeMethods.GetKeyboardLayout(0);
234 				ushort uLangID = (ushort)(hKL.ToInt64() & 0xFFFFL);
235 				ushort uPriLangID = NativeMethods.GetPrimaryLangID(uLangID);
236 				if(uPriLangID != NativeMethods.LANG_POLISH) return;
237 
238 				int vk = (int)Keys.A;
239 
240 				// We actually check for RAlt (which maps to Ctrl+Alt)
241 				// instead of LCtrl+LAlt
242 				byte[] pbState = new byte[256];
243 				pbState[NativeMethods.VK_CONTROL] = 0x80;
244 				pbState[NativeMethods.VK_MENU] = 0x80;
245 				pbState[NativeMethods.VK_RMENU] = 0x80;
246 				pbState[NativeMethods.VK_NUMLOCK] = 0x01; // Toggled
247 				// pbState[vk] = 0x80;
248 
249 				string strUni = NativeMethods.ToUnicode3(vk, pbState, IntPtr.Zero);
250 				if(string.IsNullOrEmpty(strUni)) return;
251 				if(strUni.EndsWith("a") || strUni.EndsWith("A")) return;
252 
253 				if(char.IsControl(strUni, 0)) { Debug.Assert(false); strUni = "?"; }
254 
255 				string str = KPRes.CtrlAltAConflict.Replace(@"{PARAM}", strUni) +
256 					MessageService.NewParagraph + KPRes.CtrlAltAConflictHint;
257 
258 				VistaTaskDialog dlg = new VistaTaskDialog();
259 				dlg.AddButton((int)DialogResult.Cancel, KPRes.Ok, null);
260 				dlg.CommandLinks = false;
261 				dlg.Content = str;
262 				dlg.DefaultButtonID = (int)DialogResult.Cancel;
263 				dlg.MainInstruction = KPRes.KeyboardKeyCtrl + "+" +
264 					KPRes.KeyboardKeyAlt + "+A - " + KPRes.Warning;
265 				dlg.SetIcon(VtdIcon.Warning);
266 				dlg.VerificationText = KPRes.DialogNoShowAgain;
267 				dlg.WindowTitle = PwDefs.ShortProductName;
268 
269 				if(dlg.ShowDialog(fParent))
270 				{
271 					if(dlg.ResultVerificationChecked)
272 						Program.Config.Integration.CheckHotKeys = false;
273 				}
274 				else MessageService.ShowWarning(str);
275 			}
276 			catch(Exception) { Debug.Assert(false); }
277 		}
278 
HandleHotKeyIntoSelf(int wParam)279 		internal static bool HandleHotKeyIntoSelf(int wParam)
280 		{
281 			try
282 			{
283 				OptionsForm f = (GlobalWindowManager.TopWindow as OptionsForm);
284 				if(f == null) return false;
285 
286 				IntPtr h = NativeMethods.GetForegroundWindowHandle();
287 				if(h != f.Handle) return false;
288 
289 				HotKeyControlEx c = (f.ActiveControl as HotKeyControlEx);
290 				if(c == null) return false;
291 
292 				Keys k;
293 				if(!m_vRegKeys.TryGetValue(wParam, out k)) return false;
294 				if(k == Keys.None) { Debug.Assert(false); return false; }
295 
296 				c.HotKey = k;
297 				return true;
298 			}
299 			catch(Exception) { Debug.Assert(false); }
300 
301 			return false;
302 		}
303 	}
304 }
305