/// <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> /// 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> /// Increment the count of the key if it exists, else adds it to the dictionary of keys with a count of 1 for the keyboards /// </summary> /// <param name="key"></param> public static void KeyboardInputUpdate(string key) { try { _currentProfile?.Keyboard?.AddOne(key); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to update the keyboard key counts"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to update the keyboard key counts"); } 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; } }
/// <summary> /// Updates the time spent in the current profile /// </summary> /// <param name="newTime">TimeSpan to add to the count of time</param> public static void UpdateTime(TimeSpan?newTime) { try { if (newTime.HasValue && _currentProfile != null) { _currentProfile.TimeSpent += newTime.Value.Ticks; } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to update time spent"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to update time spent"); } throw exception; } }
/// <summary> /// Updates the time spent in the current profile /// </summary> /// <param name="timeToAdd">long to add to the count of time</param> public static void UpdateTime(long timeToAdd) { try { if (timeToAdd > 0 && _currentProfile != null) { _currentProfile.TimeSpent += timeToAdd; } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to update time spent"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to update time spent"); } throw exception; } }
/// <summary> /// JSON serializes the currently used profile /// </summary> /// <param name="outputFolder"></param> public static void SaveCurrentProfile(string outputFolder) { try { if (!Directory.Exists(outputFolder)) { Directory.CreateDirectory(outputFolder); } if (_currentProfile != null) { using (StreamWriter sw = File.CreateText(outputFolder + "\\" + _currentProfile.Name + ".txt")) { JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true, IncludeFields = true }; sw.Write(JsonSerializer.Serialize(_currentProfile, jsonSerializerOptions)); } } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to save the current profile"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to save the current profile"); } throw exception; } }
/// <summary> /// Sets up the handlers for the possible events of a device /// </summary> /// <param name="newKeyPress"></param> /// <param name="oldKeyPress"></param> /// <param name="addedList"></param> /// <param name="removeList"></param> public static void SetupDeviceHandlers(DictionaryWithEvents.DictionaryEvent newKeyPress, DictionaryWithEvents.DictionaryEvent oldKeyPress, DictionaryWithEvents.DictionaryEvent addedList, DictionaryWithEvents.DictionaryEvent removeList) { try { CurrentDevice.KeysCount.OnAddStatus += newKeyPress; CurrentDevice.KeysCount.OnUpdateStatus += oldKeyPress; CurrentDevice.KeysCount.OnAddList += addedList; CurrentDevice.KeysCount.OnRemoveList += removeList; CurrentDevice.KeysCount.HasSubscribers = true; } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to set up device handlers"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to set up device handlers"); } throw exception; } }
protected override bool CreateImageForKey(string key) { // Since the majority of buttons are unique and predefined and easily recognizable the images for them are in templates, // this makes is so that those "template" images are returned without writing anything on them if needed try { if (ImagesLoaded == false) { return(false); } if (Templates.Keys.Contains(key)) { Images.GetOrAdd(key, (Image)Templates[key].Clone()); } else { Images.GetOrAdd(key, WriteOnTemplate((Image)Templates["key"].Clone(), key)); } return(true); } catch (Exception ex) { if (ex is ChainingException exception) { exception.AddErrorToChain("While trying to create a gamepad image"); } else { exception = new ChainingException(ex.Message); exception.AddErrorToChain("While trying to create a gamepad image"); } throw exception; } }
/// <summary> /// Loads all images and templates in memory used in a worker thread /// </summary> /// <param name="worker">worker argument used to see if loading should be cancelled</param> /// <returns>true if the images were loaded, false if the worker was interrupted</returns> public bool LoadImages(BackgroundWorker worker) { try { if (ImagesPath == "") { throw new NullReferenceException("path to images cannot be empty"); } foreach (var file in Directory.EnumerateFiles(Path.Join(ImagesPath, "templates/"))) { if (file.EndsWith(".png")) { //frees the image after loading them so they can be freely used in other operations, otherwise the handle to the file would be // kept by the app var auxImage = Image.FromFile(file); Templates.GetOrAdd(Path.GetFileNameWithoutExtension(file), new Bitmap(auxImage)); //Images are evil, they must be both exposed and all all references removed for the GC to take'em auxImage.Dispose(); if (worker.CancellationPending) { ImagesLoaded = false; foreach (var key in Templates.Keys) { Templates[key].Dispose(); } Templates.Clear(); return(false); } } } foreach (var file in Directory.EnumerateFiles(ImagesPath)) { if (file.EndsWith(".png")) { Console.WriteLine(Path.GetFileNameWithoutExtension(file)); var auxImage = Image.FromFile(file); Images.GetOrAdd(Path.GetFileNameWithoutExtension(file), new Bitmap(auxImage)); auxImage.Dispose(); if (worker.CancellationPending) { ImagesLoaded = false; foreach (var key in Templates.Keys) { Templates[key].Dispose(); } foreach (var key in Images.Keys) { Images[key].Dispose(); } Templates.Clear(); Images.Clear(); return(false); } } } ImagesLoaded = true; return(true); } catch (Exception e) { var exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to load images"); throw exception; } }
/// <summary> /// verify if a gamepad is connected, if it is then check which buttons are pressed and the directions of the thumb sticks, /// if no gamepad is connected change the timer to 1 every 5 seconds /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> static void CheckGamepad(object?sender, EventArgs eventArgs) { try { //Console.WriteLine("checked for gamepad input"); switch (XInputGetState(0, out CurrentState)) { case ERROR_SUCCESS: { // if a controller was just connected go back to checking for input ~60 times per second if (frameTimer.Interval == 5000) { frameTimer.Interval = 15; } if (CurrentState.dwPacketNumber != LastState.dwPacketNumber) { var listOfButtonPresses = new List <string>(); //apply the bitmask for each button and take it as a press only if the result is not equal to 0 and if the previous state was equal to 0 if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) == 0) { listOfButtonPresses.Add("D_pad_up"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) == 0) { listOfButtonPresses.Add("D_pad_down"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) == 0) { listOfButtonPresses.Add("D_pad_right"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) == 0) { listOfButtonPresses.Add("D_pad_left"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_START) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_START) == 0) { listOfButtonPresses.Add("start"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) == 0) { listOfButtonPresses.Add("back"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) == 0) { listOfButtonPresses.Add("left_shoulder"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) == 0) { listOfButtonPresses.Add("right_shoulder"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) == 0) { listOfButtonPresses.Add("left_thumb"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) == 0) { listOfButtonPresses.Add("right_thumb"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_A) == 0) { listOfButtonPresses.Add("a"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_B) == 0) { listOfButtonPresses.Add("b"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_X) == 0) { listOfButtonPresses.Add("x"); } if ((CurrentState.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0 && (LastState.Gamepad.wButtons & XINPUT_GAMEPAD_Y) == 0) { listOfButtonPresses.Add("y"); } if (CurrentState.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD && LastState.Gamepad.bLeftTrigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { listOfButtonPresses.Add("left_trigger"); } if (CurrentState.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD && LastState.Gamepad.bRightTrigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { listOfButtonPresses.Add("right_trigger"); } //take care of the cases where the absolute would underflow if (CurrentState.Gamepad.sThumbLY == short.MinValue) { CurrentState.Gamepad.sThumbLY = short.MinValue + 2; } if (CurrentState.Gamepad.sThumbLX == short.MinValue) { CurrentState.Gamepad.sThumbLX = short.MinValue + 2; } var directionL = MouseHookClass.MovementDirection( new Point(CurrentState.Gamepad.sThumbLX, CurrentState.Gamepad.sThumbLY), new Point(0, 0)); // update the direction of the left thumb stick only if it is outside of the dead zone and the previous state was inside the dead zone // or if the direction is different if ((Math.Abs(CurrentState.Gamepad.sThumbLY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE || Math.Abs(CurrentState.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) && !(Math.Abs(LastState.Gamepad.sThumbLY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE || Math.Abs(LastState.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) || (directionL != null && directionL != LastDirectionL)) { if (directionL != null) { listOfButtonPresses.Add("left_thumb_move_" + directionL); LastDirectionL = directionL; } } //take care of the cases where the absolute would underflow if (CurrentState.Gamepad.sThumbRY == short.MinValue) { CurrentState.Gamepad.sThumbRY = short.MinValue + 2; } if (CurrentState.Gamepad.sThumbRX == short.MinValue) { CurrentState.Gamepad.sThumbRX = short.MinValue + 2; } var directionR = MouseHookClass.MovementDirection( new Point(CurrentState.Gamepad.sThumbRX, CurrentState.Gamepad.sThumbRY), new Point(0, 0)); // update the direction of the right thumb stick only if it is outside of the dead zone and the previous state was inside the dead zone // or if the direction is different if ((Math.Abs(CurrentState.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE || Math.Abs(CurrentState.Gamepad.sThumbRY) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) && !(Math.Abs(LastState.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE || Math.Abs(LastState.Gamepad.sThumbRY) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) || (directionR != null && directionR != LastDirectionR)) { if (directionR != null) { listOfButtonPresses.Add("right_thumb_move_" + directionR); LastDirectionR = directionR; } } if (listOfButtonPresses.Count > 0) { ProfileManager.GamepadInputUpdate(listOfButtonPresses); } LastState = CurrentState; } break; } case ERROR_DEVICE_NOT_CONNECTED: { // if no gamepad is connected then search for input only once every 5 seconds if (frameTimer.Interval != 5000) { frameTimer.Interval = 5000; } break; } } } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to read gamepad input"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to read gamepad input"); } throw exception; } }
/// <summary> /// handle the mouse event /// </summary> /// <param name="nCode"></param> /// <param name="wParam">parameter describing the type of event</param> /// <param name="lParam">pointer to a structure in memory containing the event parameters</param> /// <returns></returns> private static IntPtr HandleMouseEvent(int nCode, IntPtr wParam, IntPtr lParam) { try { //get the parameters to which the pointer point to var mouseStruct = (LowLevelMouseStructure)(Marshal.PtrToStructure(lParam, typeof(LowLevelMouseStructure)) ?? throw new NullReferenceException("Mouse structure was null")); // add to the CurrentProfile mouse dictionary the corresponding key switch ((int)wParam) { case 513: { ProfileManager.MouseInputUpdate("left_button"); break; } case 516: { ProfileManager.MouseInputUpdate("right_button"); break; } case 519: { ProfileManager.MouseInputUpdate("middle_button"); break; } case 523: { var button = 3 + short.Parse(mouseStruct.MouseData.ToString("X")) / 10000; ProfileManager.MouseInputUpdate("Button " + button); break; } case 522: { if (_lastMouseWheelMovement >= 0 && mouseStruct.MouseData < 0 || _lastMouseWheelMovement <= 0 && mouseStruct.MouseData > 0 || mouseStruct.TimeStamp - _lastMouseUpdate > 1500) { _lastMouseWheelMovement = mouseStruct.MouseData; _lastMouseUpdate = (int)mouseStruct.TimeStamp; ProfileManager.MouseInputUpdate(mouseStruct.MouseData > 0 ? "scroll_up" : "scroll_down"); } break; } case 512: { // detect a mouse movement only if the new coordinates are outside of a dead zone and if the time since the last update is big enough or if the direction // is different. // Also remember that the Y on monitor goes different than on a normal 2D space, so the true Y is height of the monitor - the given Y coordinate var direction = MovementDirection(new Point(mouseStruct.Point.X, Screen.PrimaryScreen.Bounds.Height - mouseStruct.Point.Y), _mousePos); var distance = Math.Sqrt(Math.Pow((mouseStruct.Point.X - _mousePos.X), 2) + Math.Pow((Screen.PrimaryScreen.Bounds.Height - mouseStruct.Point.Y - _mousePos.Y), 2)); if (direction != null && (mouseStruct.TimeStamp - _lastMouseUpdate > 750 || !direction.Equals(_lastMouseMovement)) && distance > 30) { _lastMouseWheelMovement = 0; _lastMouseUpdate = (int)mouseStruct.TimeStamp; _mousePos = new Point(mouseStruct.Point.X, Screen.PrimaryScreen.Bounds.Height - mouseStruct.Point.Y); ProfileManager.MouseInputUpdate(direction); _lastMouseMovement = direction; } break; } } return(CallNextHookEx(_mouseHookId, nCode, wParam, lParam)); } catch (Exception e) { if (e is ChainingException exception) { exception.AddErrorToChain("While trying to read mouse input"); } else { exception = new ChainingException(e.Message); exception.AddErrorToChain("While trying to read mouse input"); } throw exception; } }