/// <summary> /// Saves the current profile and switches to the new given profile /// </summary> /// <param name="nameOfProfileToSwitchTo"></param> /// <param name="outputFolder"></param> public static void ChangeProfile(string?nameOfProfileToSwitchTo, string outputFolder) { try { if (_currentProfile != null) { SaveCurrentProfile(outputFolder); } if (nameOfProfileToSwitchTo != null) { _currentProfile = new Profile(Path.Join(Program.AppDataPath, "/Profiles/"), nameOfProfileToSwitchTo); } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to switch profile"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to switch profile"); } throw exception; } }
/// <summary> /// Get all the devices that the current profile is supposed to monitor /// </summary> /// <returns>List of strings representing the devices</returns> public static List <string>?GetListOfDevices() { try { if (_currentProfile == null) { return(null); } List <string> devices = new List <string> { "Keyboard", "Mouse" }; if (_currentProfile.HasGamepad) { devices.Add("Gamepad"); } return(devices); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to get the current profiles devices"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to get the current profiles devices"); } throw exception; } }
/// <summary> /// removes the given handlers from the device /// </summary> /// <param name="newKeyPress"></param> /// <param name="oldKeyPress"></param> /// <param name="addedList"></param> /// <param name="removeList"></param> public static void RemoveHandlers(DictionaryWithEvents.DictionaryEvent newKeyPress, DictionaryWithEvents.DictionaryEvent oldKeyPress, DictionaryWithEvents.DictionaryEvent addedList, DictionaryWithEvents.DictionaryEvent removeList) { try { if (CurrentDevice != null && CurrentDevice.KeysCount.HasSubscribers) { CurrentDevice.KeysCount.OnAddStatus -= newKeyPress; CurrentDevice.KeysCount.OnUpdateStatus -= oldKeyPress; CurrentDevice.KeysCount.OnAddList -= addedList; CurrentDevice.KeysCount.OnRemoveList -= removeList; CurrentDevice.KeysCount.HasSubscribers = false; } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to remove device handlers"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to remove device handlers"); } throw exception; } }
/// <summary> /// Add to the count of each key 1 or if they are not in the dictionary initialize them with 1. /// Raises the event OnAddList /// </summary> public void AddList(List <string> keys) { try { foreach (var key in keys) { if (Keys.ContainsKey(key)) { Keys[key] += 1; } else { Keys.GetOrAdd(key, 1); } } OnAddList?.Invoke(this, new DictionaryEventArgs(keys, false)); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to add a list of keys to the count dictionary"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to add a list of keys to the count dictionary"); } throw exception; } }
/// <summary> /// JSON serializes the current options /// </summary> public void WriteOptions() { try { using (StreamWriter sw = File.CreateText(Program.AppDataPath + "\\" + "options.cfg")) { JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; sw.Write(JsonSerializer.Serialize(this, jsonSerializerOptions)); } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to write options"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to write options"); } throw exception; } }
/// <summary> /// Disposes of both the templates and images loaded in memory /// </summary> public void UnloadImages() { try { if (Images.Count != 0) { foreach (var key in Images.Keys) { Images[key].Dispose(); } } if (Templates.Count != 0) { foreach (var key in Templates.Keys) { Templates[key].Dispose(); } } Templates.Clear(); Images.Clear(); ImagesLoaded = false; } catch (Exception e) { var exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to unload images"); throw exception; } }
/// <summary> /// If the image for the specific key is already created it returns it else it creates it and returns it /// </summary> /// <param name="key">the key for which an image should be returned</param> /// <returns>An image for the key or null if the images are not loaded in memory</returns> public Image?GetOrCreateImageForKey(string key) { try { if (!ImagesLoaded) { return(null); } if (Images.ContainsKey(key) && Images[key] != null) { return(Images[key]); } CreateImageForKey(key); return(Images[key]); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to get or create image"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to get or create image"); } throw exception; } }
/// <summary> /// Remove the given list from the dictionary. /// Raises the OnRemoveList event /// </summary> /// <param name="keys"></param> public void RemoveList(List <string> keys) { try { foreach (var key in keys) { Keys.TryRemove(key, out _); } OnRemoveList?.Invoke(this, new DictionaryEventArgs(keys, true)); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to remove key from count dictionary"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to remove key from count dictionary"); } throw exception; } }
/// <summary> /// Resize and center a given image while keeping the initial image aspect ratio to the same dimensions that the key images usually use, /// surplus space from resizing the image is transparent /// </summary> /// <param name="image"></param> /// <returns></returns> public void ResizeImage(ref Image image) { try { float ratio = Math.Min((float)130 / image.Width, (float)69 / image.Height); int newWidth = (int)(image.Width * ratio); int newHeight = (int)(image.Height * ratio); var finalImage = new Bitmap(130, 69); using (var graphics = Graphics.FromImage(finalImage)) { int x = (finalImage.Width / 2) - newWidth / 2; int y = (finalImage.Height / 2) - newHeight / 2; graphics.DrawImage(image, x, y, newWidth, newHeight); } image.Dispose(); image = null; image = finalImage; } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to resize an image"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to resize an image"); } throw exception; } }
/// <summary> /// Clears the content of the dictionary. /// Raises the OnRemoveList envent. /// </summary> public void Clear() { try { List <string>?keys = Keys.Keys as List <string>; Keys.Clear(); if (keys != null) { OnRemoveList?.Invoke(this, new DictionaryEventArgs(keys, true)); } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to clear the count dictionary"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to clear the count dictionary"); } throw exception; } }
/// <summary> /// Adds one to the count of the key, if they are present in the dictionary, else initialize the key with 1. /// Raises the event OnAddStatus if the key was not present and OnUpdateStatus if the key was already in the dictionary. /// </summary> /// <param name="key"></param> public void AddOne(string key) { //Console.WriteLine(key); try { if (Keys.ContainsKey(key)) { Keys[key]++; OnUpdateStatus?.Invoke(this, new DictionaryEventArgs(key, false)); } else { this.Add(key, 1); } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to increment or create entry in count dictionary"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to increment or create entry in count dictionary"); } throw exception; } }
/// <summary> /// Adds or replaces an image in the folder of images, this removes any image on the Images folder that has the same name as newImageName /// </summary> /// <param name="newImageName"></param> /// <param name="newImage"></param> public void AddOrReplace(string newImageName, Image?newImage) { try { if (File.Exists(Path.Join(ImagesPath, "/" + newImageName + ".png"))) { File.Delete(Path.Join(ImagesPath, "/" + newImageName + ".png")); } Images.TryRemove(newImageName, out var aux); aux?.Dispose(); if (newImage == null) { return; } newImage.Save(Path.Join(ImagesPath, "/" + newImageName + ".png")); Images.GetOrAdd(newImageName, (Image)newImage.Clone()); newImage.Dispose(); } catch (Exception e) { var exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to replace image"); throw exception; } }
/// <summary> /// Reads a profile from the folder containing all profiles /// </summary> /// <param name="containingFolder"></param> /// <param name="name"></param> public Profile(string containingFolder, string name) { Keyboard = new Keyboard(); Mouse = new Mouse(); Gamepad = new Gamepad(); if (File.Exists(containingFolder + "\\" + name + ".txt")) { string fileContents = File.ReadAllText(containingFolder + "\\" + name + ".txt"); try { //Console.WriteLine("trying to read profile"); var profile = JsonSerializer.Deserialize <Profile>(fileContents); if (profile == null) { throw new ArgumentNullException("Profile is null"); } Name = profile.Name ?? throw new ArgumentNullException("Profile name is null"); HasGamepad = profile.HasGamepad; Keyboard.KeysCount = profile.Keyboard?.KeysCount ?? throw new ArgumentNullException("Keyboard dictionary is null"); Keyboard.KeysRename = profile.Keyboard.KeysRename ?? throw new ArgumentNullException("Keyboard rename dictionary is null"); Mouse.KeysCount = profile.Mouse?.KeysCount ?? throw new ArgumentNullException("Mouse dictionary is null"); Mouse.KeysRename = profile.Mouse.KeysRename ?? throw new ArgumentNullException("Mouse rename dictionary is null"); Gamepad.KeysCount = profile.Gamepad?.KeysCount ?? throw new ArgumentNullException("Gamepad dictionary is null"); Gamepad.KeysRename = profile.Gamepad.KeysRename ?? throw new ArgumentNullException("Gamepad rename dictionary is null"); TimeSpent = profile.TimeSpent; } catch (Exception e) { var exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to read profile file"); throw exception; } } }
/// <summary> /// Creates default options, if resetProfileList is false then the current list of profiles is kept, else it is reset to default /// </summary> /// <param name="resetProfileList"></param> public void MakeOptionsDefault(bool resetProfileList) { try { AutoStart = false; ProfilesLocation = Path.Join(Program.AppDataPath, "Profiles"); LastUsedProfile = null; UseLastProfile = true; StartMinimized = false; OnStartProfile = null; if (resetProfileList) { // don't also delete the files of the previous profiles in case the options were reset to default cause of a corrupted options file ProfileList = new BindingList <string>() { "Default" } } ; } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to default options"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to default options"); } throw exception; } }
/// <summary> /// JSON serializes a given profile to the given output folder /// </summary> /// <param name="outputFolder"></param> /// <param name="profile"></param> private static void SaveProfile(string outputFolder, Profile profile) { try { if (!Directory.Exists(outputFolder)) { Directory.CreateDirectory(outputFolder); } using (StreamWriter sw = File.CreateText(outputFolder + "\\" + profile.Name + ".txt")) { JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true, IncludeFields = true }; sw.Write(JsonSerializer.Serialize(profile, jsonSerializerOptions)); } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to save a given profile"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to save a given profile"); } throw exception; } }
/// <summary> /// Tries to use one of the two templates depending on the length of the input and create a new image for that key /// </summary> /// <param name="key">the key for which an image should be created</param> /// /// <returns>true if the image was created, false otherwise</returns> protected virtual bool CreateImageForKey(string key) { try { var template = key.Length > 2 ? "big_key" : "key"; if (ImagesLoaded == false) { return(false); } Images.GetOrAdd(key, WriteOnTemplate((Image)Templates[template].Clone(), key)); return(true); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to create image"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to create image"); } throw exception; } }
/// <summary> /// Check if the user wants to rename the key to its original name /// </summary> /// <param name="rename"></param> /// <param name="maybeOriginal"></param> /// <returns></returns> private bool IsRenamedTo(string maybeOriginal, string rename) { try { if (ProfileManager.CurrentDevice.RenamesContainsKey(maybeOriginal)) { return(ProfileManager.CurrentDevice.KeysRename[maybeOriginal] == rename); } return(false); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to see if a key is in keys rename"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to see if a key is in keys rename"); } throw exception; } }
/// <summary> /// Writes the keys name on a given template /// </summary> /// <param name="template">Image on which the name of the key should be written</param> /// <param name="name">The name of the key</param> /// <returns>An image with the name centered on it</returns> protected Image WriteOnTemplate(Image template, string name) { try { using (Graphics g = Graphics.FromImage(template)) { int size = 150; _font = new Font(_fontFamily, size); // measure the size of the font so that the text fits the image while (g.MeasureString(name, _font).Width > template.Width || g.MeasureString(name, _font).Height > template.Height) { _font.Dispose(); size -= 5; _font = new Font(_fontFamily, size); if (size < 6) { break; } } //actually write the text, centered, on the image g.DrawString(name, _font, Brushes.Gray, new Point((int)((template.Width - g.MeasureString(name, _font).Width) / 2), (int)((template.Height - g.MeasureString(name, _font).Height) / 2))); try { if (File.Exists(Path.Join(ImagesPath, "/" + name + ".png"))) { File.Delete(Path.Join(ImagesPath, "/" + name + ".png")); } template.Save(Path.Join(ImagesPath, "/" + name + ".png")); } catch (Exception e) { var exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to delete image"); throw exception; } _font.Dispose(); } return(template); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to write on template image"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to write on template image"); } throw exception; } }
private static IntPtr HandleKeyboardEvent(int nCode, IntPtr wParam, IntPtr lParam) { try { var keyboardStructure = (LowLevelKeyboardStructure)(Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardStructure)) ?? throw new NullReferenceException("keyboard structure was null")); // if the event is of type key down or alt key down add them to a the corresponding // dictionary of the CurrentProfile and to a dictionary containing all the pressed keys // one of the values is for a normal key down, the other is for the alt-key down since they have different codes if ((int)wParam == 256 || (int)wParam == 260) { if (!_currentlyPressed.Contains((Keys)keyboardStructure.VirtualKeyCode)) { //for some reason gamepads report the middle button as a keypress, so check for that if (((Keys)keyboardStructure.VirtualKeyCode).ToString() != "LButton, XButton2") { ProfileManager.KeyboardInputUpdate(((Keys)keyboardStructure.VirtualKeyCode).ToString()); _currentlyPressed.Add((Keys)keyboardStructure.VirtualKeyCode); } else { ProfileManager.GamepadInputUpdate(new List <string>() { "LButton, XButton2" }); } } } // if the event is "key up" remove the key from the dictionary as it was released and now can be pressed physically again else if ((int)wParam == 257) { _currentlyPressed.Remove((Keys)keyboardStructure.VirtualKeyCode); } return(CallNextHookEx(_keyboardHookId, nCode, wParam, lParam)); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying reading keyboard input"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying reading keyboard input"); } throw exception; } }
/// <summary> /// Tries to read the options, if the file does not exist then create new options with default values and save them /// </summary> public void ReadOrCreateOptions() { try { if (File.Exists(Program.AppDataPath + "\\" + "options.cfg")) { string textOptions = File.ReadAllText(Program.AppDataPath + "\\" + "options.cfg"); try { //Console.WriteLine("Trying to read options"); // am making options to create options, creates recursive behaviour and stack overflows var options = JsonSerializer.Deserialize <Options>(textOptions); AutoStart = options.AutoStart; ProfilesLocation = options.ProfilesLocation ?? throw new Exception("Null option"); LastUsedProfile = options.LastUsedProfile; UseLastProfile = options.UseLastProfile; StartMinimized = options.StartMinimized; OnStartProfile = options.OnStartProfile; ProfileList = options.ProfileList ?? throw new Exception("Null option"); } catch (Exception) { MakeOptionsDefault(true); WriteOptions(); throw new ChainingException("Options file corrupted, created new file with default settings"); } } else { //assume that it is a first time setup, probably could have created files using the installer, but meh ProfileManager.CreateNewProfile(Path.Join(Program.AppDataPath, "Profiles"), "Default", false); MakeOptionsDefault(true); WriteOptions(); } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to read options"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to read options"); } throw exception; } }
/// <summary> /// Switches the current device to the given type /// </summary> /// <param name="newDevice"></param> public static void ChangeDevice(string newDevice) { try { if (_currentProfile == null) { throw new NullReferenceException("Profile is null"); } switch (newDevice) { case "Keyboard": { CurrentDevice = _currentProfile.Keyboard ?? throw new NullReferenceException("Device keyboard is null"); break; } case "Mouse": { CurrentDevice = _currentProfile.Mouse ?? throw new NullReferenceException("Device mouse is null"); break; } case "Gamepad": { CurrentDevice = _currentProfile.Gamepad ?? throw new NullReferenceException("Device gamepad is null"); break; } default: { throw new ChainingException("Unknown device"); } } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to change the current device"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to change the current device"); } throw exception; } }
/// <summary> /// removes an entry from the dictionary /// </summary> /// <param name="originalKey"></param> public void RemoveImage(string originalKey) { try { if (Images.ContainsKey(originalKey)) { Images[originalKey].Dispose(); } Images.TryRemove(originalKey, out var aux); aux?.Dispose(); } catch (Exception e) { var exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to remove image from dictionary"); throw exception; } }
/// <summary> /// If needed tell teh main form to rename a key and change its icon /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void KeyInfo_FormClosing(object sender, FormClosingEventArgs e) { try { var owner = this.Owner as keyCounterMainFrame_frame; if (!Text.Equals(KeyName_textBox.Text) && KeyName_textBox.BackColor == SystemColors.Window) { //if the user selected a new icon and renamed the key at the same time then the key receives the new name with a zero width space at the end and the icon given by the user if (_newImage != null) { owner?.RenameAndChangeIcon(Text, KeyName_textBox.Text + " ", _newImage); } else { owner?.RenameAndChangeIcon(Text, KeyName_textBox.Text, null); } _newImage?.Dispose(); KeyImage_pictureBox.Image.Dispose(); return; } if (_newImage != null) { owner?.RenameAndChangeIcon(Text, KeyName_textBox.Text + " ", _newImage); _newImage.Dispose(); } KeyImage_pictureBox.Dispose(); } catch (Exception ex) { if (ex is ChainingException exception) { exception.AddErrorToChain("While trying to close info form"); } else { exception = new ChainingException(ex.Message); exception.AddErrorToChain("While trying to close info form"); } throw exception; } }
/// <summary> /// For each key in the list increment its count if it exists, else adds it to the dictionary of keys with a count of 1 for the gamepads /// </summary> /// <param name="listOfButtonPresses"></param> public static void GamepadInputUpdate(List <string> listOfButtonPresses) { try { _currentProfile?.Gamepad?.AddList(listOfButtonPresses); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to update the gamepad key counts"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to update the gamepad key counts"); } throw exception; } }
/// <summary> /// Increment the count of the key if it exists, else adds it to the dictionary of keys with a count of 1 for the mouse /// </summary> /// <param name="key"></param> public static void MouseInputUpdate(string key) { try { _currentProfile?.Mouse?.AddOne(key); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to update the mouse key counts"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to update the mouse key counts"); } throw exception; } }
/// <summary> /// Gets the currents profile time /// </summary> /// <returns>long representing the time spent in the current profile</returns> public static long?GetTimeSpent() { try { return(_currentProfile?.TimeSpent); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to get the time spent"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to get the time spent"); } throw exception; } }
/// <summary> /// Add to the dictionary the <paramref name="key"/> with a initialized with <paramref name="value"/>. /// Raises the OnAddStatus event /// </summary> /// <param name="key">the key</param> /// <param name="value">number of presses</param> public void Add(string key, uint value) { try { Keys.GetOrAdd(key, value); OnAddStatus?.Invoke(this, new DictionaryEventArgs(key, false)); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to add value in the count dictionary"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to add value in the count dictionary"); } throw exception; } }
/// <summary> /// Empties the dictionary of counts of the current device /// </summary> public static void ClearCurrentDeviceDictionary() { try { var list = new List <string>(CurrentDevice.KeysCount.Keys.Keys); CurrentDevice.KeysCount.RemoveList(list); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying clear the currents device keys counts"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying clear the currents device keys counts"); } throw exception; } }
/// <summary> /// Remove the key from the dictionary /// Raises the event OnUpdateStatus /// </summary> /// <param name="key"></param> /// <returns>true if the operation was successful, false otherwise</returns> public bool Remove(string key) { try { bool result = Keys.TryRemove(key, out _); OnUpdateStatus?.Invoke(this, new DictionaryEventArgs(key, true)); return(result); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to remove key from count dictionary"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to remove key from count dictionary"); } throw exception; } }
/// <summary> /// Creates and saves a new profile /// </summary> /// <param name="outputFolder"></param> /// <param name="newProfileName"></param> /// <param name="usesGamepad"></param> /// <returns></returns> public static bool CreateNewProfile(string outputFolder, string newProfileName, bool usesGamepad) { try { var newProfile = new Profile(newProfileName, usesGamepad); SaveProfile(outputFolder, newProfile); return(true); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to create new profile"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to create new profile"); } throw exception; } }