PowerToys/doc/devdocs/shared-hooks.md
yuyoyuppe f385e46927
Devdocs reorganisation (#913)
* docs: split usage and dev docs

* # This is a combination of 2 commits.
# This is the 1st commit message:

docs: split usage and dev docs

# The commit message #2 will be skipped:

# fixup add docs

* docs: add runner documentation and move hooks documentation to devdocs

* docs: add stubs for modules technical description

* docs: add paragraph about event thread-safety

* docs: add 'Current modules' section header
2019-12-12 12:25:19 +03:00

3.9 KiB

Shared hooks

To minimize the performance impact on the machine only runner installs global hooks, passing the events to registered callbacks in each PowerToy module.

When a PowerToy module is loaded, the runner calls the get_events() method to get a NULL-terminated array of NULL-terminated strings with the names of the events that the PowerToy wants to subscribe to. A const wchar_t* string is provided for each of the event names.

Events are signalled by the runner calling the signal_event(name, data) method of the PowerToy module. The name parameter contains the NULL-terminated name of the event. The data parameter and the method return value are specific for each event.

Currently supported hooks:

Low Level Keyboard Hook

This event is signaled whenever the user presses or releases a key on the keyboard. To subscribe to this event, add "ll_keyboard" to the table returned by the get_events() method.

The PowerToys runner installs low-level keyboard hook using SetWindowsHookEx(WH_KEYBOARD_LL, ...). See this MSDN page for details.

When a keyboard event is signaled and ncCode equals HC_ACTION, the wParam and lParam event parameters are passed to all subscribed clients in the LowlevelKeyboardEvent struct.

The intptr_t data event argument is a pointer to the LowlevelKeyboardEvent struct.

A non-zero return value from any of the subscribed PowerToys will cause the runner hook proc to return 1, thus swallowing the keyboard event.

Example usage, that makes Windows ignore the L key:

virtual const wchar_t** get_events() override {
  static const wchar_t* events[2] = { ll_keyboard,
                                      nullptr };
  return events;
}

virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
  if (wcscmp(name, ll_keyboard) == 0) {
    auto& event = *(reinterpret_cast<LowlevelKeyboardEvent*>(data));
    // The L key has vkCode of 0x4C, see:
    // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
    if (event.wParam == WM_KEYDOWN && event.lParam->vkCode == 0x4C) {
      return 1;
    } else {
      return 0;
    }
  } else {
    return 0;
  }
}

Windows Event Hook

This event is signaled for a range of events. To subscribe to this event, add "win_hook_event" to the table returned by the get_events() method. See this MSDN doc for details.

The intptr_t data event argument is a pointer to the WinHookEvent struct.

The return value of the event handler is ignored.

Example usage, that detects a window being resized:

virtual const wchar_t** get_events() override {
  static const wchar_t* events[2] = { win_hook_event,
                                      nullptr };
  return events;
}

virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
  if (wcscmp(name, win_hook_event) == 0) {
    auto& event = *(reinterpret_cast<WinHookEvent*>(data));
    switch (event.event) {
    case EVENT_SYSTEM_MOVESIZESTART:
      size_start(event.hwnd);
      break;
    case EVENT_SYSTEM_MOVESIZEEND:
      size_end(event.hwnd);
      break;
    default:
      break;
    }
  }
  return 0;
}

Taking too long to process the events has negative impact on the whole system performance. To address this, the events are signaled from a different thread, not from the event hook callback itself.