bool SimultaneousKeyPresses::remap(void) { if (! EventInputQueue::queue_) return false; // We consider "Shift_L+Shift_R to Space". // When we press keys by the following order. // // (1) Shift_L Down // (2) Shift_R Down // (3) Shift_L Up // (4) Shift_R Up // // First remap(): // (1) Shift_L Down -> to virtualkey_ // (2) Shift_R Down -> removed // (3) Shift_L Up -> no change // (4) Shift_R Up -> no change // // Second remap(): // (3) Shift_L Up -> removed // (4) Shift_R Up -> no change // However, we need to remove (4) at the same time. // If (4) is alive, Shift_R Up event which we don't intend is fired in EventInputQueue. // So, we retry handling KeyUp event once more when we drop KeyUp event. EventInputQueue::Item* front = static_cast<EventInputQueue::Item*>(EventInputQueue::queue_->front()); if (! front) return false; // backup device information. DeviceIdentifier deviceIdentifier(front->deviceIdentifier); // ------------------------------------------------------------ // fire KeyUp event if needed. for (size_t i = 0; i < fromInfo_.size(); ++i) { if (! fromInfo_[i].isActive()) continue; if (! fromInfo_[i].isTargetKeyUp(*front)) continue; // -------------------- EventInputQueue::queue_->pop_front(); fromInfo_[i].deactivate(); // -------------------- // if all keys are released, fire KeyUp event. bool isAllDeactived = true; for (size_t j = 0; j < fromInfo_.size(); ++j) { if (fromInfo_[j].isActive()) { isAllDeactived = false; } } if (isAllDeactived) { push_remapped(false, deviceIdentifier); } return true; } // ------------------------------------------------------------ // handle KeyDown event. if (! FlagStatus::makeFlags().isOn(fromFlags_)) return false; // -------------------- // scan items in queue_. while (downKeys_.size() < fromInfo_.size()) { downKeys_.push_back(NULL); } // Then, downKeys_.size() >= fromInfo_.size() for (size_t i = 0; i < fromInfo_.size(); ++i) { downKeys_[i].item = NULL; } for (;;) { // We consider "Shift_L+Shift_R to Space". // When we press keys by the following order. // // (1) Shift_L Down // (2) Shift_L Up // (3) Shift_R Down // (4) Shift_R Up // // If fromKey was released before all keys are pressed, // we must not handle these keys as SimultaneousKeyPresses. // for (size_t i = 0; i < fromInfo_.size(); ++i) { if (fromInfo_[i].isTargetKeyDown(*front)) { downKeys_[i].item = front; break; } else if (fromInfo_[i].isTargetKeyUp(*front)) { return false; } } // ---------------------------------------- bool isAllKeysDown = true; for (size_t i = 0; i < fromInfo_.size(); ++i) { if (! downKeys_[i].item) { isAllKeysDown = false; } else { // Checking strict key order. // // If isStrictKeyOrder_ == true, // we must not handle the following state as SimultaneousKeyPresses. // // - downKeys_[0] == NULL // - downKeys_[1] != NULL // if (! isAllKeysDown && isStrictKeyOrder_) { return false; } } } if (isAllKeysDown) { for (size_t i = 0; i < fromInfo_.size(); ++i) { fromInfo_[i].activate(); EventInputQueue::queue_->erase(downKeys_[i].item); } push_remapped(true, deviceIdentifier); return true; } // ---------------------------------------- front = static_cast<EventInputQueue::Item*>(front->getnext()); if (! front) return false; } return false; }
RemapSimultaneousKeyPressesResult::Value SimultaneousKeyPresses::remapSimultaneousKeyPresses(void) { // We consider "Shift_L+Shift_R to Space". // When we press keys by the following order. // // (1) Shift_L Down // (2) Shift_R Down // (3) Shift_L Up // (4) Shift_R Up // // First remap(): // (1) Shift_L Down -> to virtualkey_ // (2) Shift_R Down -> removed // (3) Shift_L Up -> no change // (4) Shift_R Up -> no change // // Second remap(): // (3) Shift_L Up -> removed // (4) Shift_R Up -> no change // However, we need to remove (4) at the same time. // If (4) is alive, Shift_R Up event which we don't intend is fired in EventInputQueue. // So, we retry handling KeyUp event once more when we drop KeyUp event. auto front = static_cast<EventInputQueue::Item*>(EventInputQueue::queue_.safe_front()); if (!front) { return RemapSimultaneousKeyPressesResult::NOT_CHANGED; } if (!(front->isSimultaneousKeyPressesTarget)) { return RemapSimultaneousKeyPressesResult::NOT_CHANGED; } // backup device information. DeviceIdentifier deviceIdentifier(front->deviceIdentifier); ListHookedDevice::WeakPointer_Item device(front->deviceWeakPointer); // ------------------------------------------------------------ // fire KeyUp event if needed. for (size_t i = 0; i < fromInfo_.size(); ++i) { if (!fromInfo_[i].isActive(device)) continue; if (!fromInfo_[i].fromEvent().isTargetUpEvent(front->getParamsBase())) continue; // -------------------- if (isPostFromEventsAsRaw_) { front->isSimultaneousKeyPressesTarget = false; } else { EventInputQueue::queue_.pop_front(); } fromInfo_[i].deactivate(device); // -------------------- // if all keys are released, fire KeyUp event. bool isAllDeactived = true; for (size_t j = 0; j < fromInfo_.size(); ++j) { if (fromInfo_[j].isActive(device)) { isAllDeactived = false; } } if (!isAllDeactived) { return RemapSimultaneousKeyPressesResult::QUEUE_CHANGED; } push_remapped(false, deviceIdentifier, device); return RemapSimultaneousKeyPressesResult::APPLIED; } // ------------------------------------------------------------ // handle KeyDown event. if (!FlagStatus::globalFlagStatus().isOn(fromModifierFlags_)) return RemapSimultaneousKeyPressesResult::NOT_CHANGED; // Check the first item in queue_ is target. // // We need to skip when the first item is not target. // In this case: // - shift+[a+s] to space // - [a+s] to return // When queue_ is [shift, a, s], we need to change these events to space. // If we do not check the first item is target, // [shift, a, s] will be changed to [shift, return]. // It's not intended. for (size_t i = 0; i < fromInfo_.size(); ++i) { if (fromInfo_[i].fromEvent().isTargetDownEvent(front->getParamsBase())) { goto scan; } } // skip return RemapSimultaneousKeyPressesResult::NOT_CHANGED; scan: // -------------------- // scan items in queue_. while (downKeys_.size() < fromInfo_.size()) { downKeys_.push_back(nullptr); } // Then, downKeys_.size() >= fromInfo_.size() for (size_t i = 0; i < fromInfo_.size(); ++i) { downKeys_[i].item = nullptr; } for (;;) { // We consider "Shift_L+Shift_R to Space". // When we press keys by the following order. // // (1) Shift_L Down // (2) Shift_L Up // (3) Shift_R Down // (4) Shift_R Up // // If fromKey was released before all keys are pressed, // we must not handle these keys as SimultaneousKeyPresses. // for (size_t i = 0; i < fromInfo_.size(); ++i) { if (fromInfo_[i].fromEvent().isTargetDownEvent(front->getParamsBase())) { downKeys_[i].item = front; break; } else if (fromInfo_[i].fromEvent().isTargetUpEvent(front->getParamsBase())) { return RemapSimultaneousKeyPressesResult::NOT_CHANGED; } } // ---------------------------------------- bool isAllKeysDown = true; for (size_t i = 0; i < fromInfo_.size(); ++i) { if (!downKeys_[i].item) { isAllKeysDown = false; } else { // Checking strict key order. // // If isStrictKeyOrder_ == true, // we must not handle the following state as SimultaneousKeyPresses. // // - downKeys_[0] == nullptr // - downKeys_[1] != nullptr // if (!isAllKeysDown && isStrictKeyOrder_) { return RemapSimultaneousKeyPressesResult::NOT_CHANGED; } } } if (isAllKeysDown) { // We use the reverse iterator for isPostFromEventsAsRaw_. for (int i = static_cast<int>(fromInfo_.size()) - 1; i >= 0; --i) { fromInfo_[i].activate(device); if (!downKeys_[i].item) continue; if (isPostFromEventsAsRaw_) { bool retainFlagStatusTemporaryCount = false; bool push_back = false; bool isSimultaneousKeyPressesTarget = false; EventInputQueue::enqueue_((downKeys_[i].item)->getParamsBase(), retainFlagStatusTemporaryCount, deviceIdentifier, device, push_back, isSimultaneousKeyPressesTarget); } EventInputQueue::queue_.erase_and_delete(downKeys_[i].item); } push_remapped(true, deviceIdentifier, device); return RemapSimultaneousKeyPressesResult::APPLIED; } // ---------------------------------------- // get next target item. for (;;) { front = static_cast<EventInputQueue::Item*>(front->getnext()); if (!front) { return RemapSimultaneousKeyPressesResult::NOT_CHANGED; } if (front->isSimultaneousKeyPressesTarget) { break; } } } return RemapSimultaneousKeyPressesResult::NOT_CHANGED; }