## File: README.md

<h1 align="center">Twinkle Tray</h1>



Twinkle Tray enables brightness control on external displays in Windows 10 & 11. Even though Windows is capable of adjusting the backlight on most monitors, it doesn't support external monitors natively. Windows also lacks any options to manage the brightness of multiple displays. This app inserts a new icon into your system tray, where you can click to have instant access to the brightness levels of all compatible displays. 



## Features
- Adds brightness sliders to the system tray, similar to the built-in Windows volume flyout.
- Seamlessly blends in with Windows 10 and Windows 11. Uses your Personalization settings to match your taskbar.
- Can automatically change monitor brightness depending on the time of day or when idle.
- Bind hotkeys to adjust the brightness of specific or all displays.
- Normalize backlight across different monitors.
- Control DDC/CI features such as contrast.
- Starts up with Windows.

### Design & Personalization

Twinkle Tray will automatically adjust the look and feel to match your Windows version and preferences. Additional options are available to select the Windows version and theme of your choice.



## Download

**Download the lastest version from [twinkletray.com](https://twinkletray.com/) or the [Releases page](https://github.com/xanderfrangos/twinkle-tray/releases).**

[](https://www.microsoft.com/store/productId/9PLJWWSV01LK)

## Install via Package Manager

### Windows Package Manager

```powershell
winget install xanderfrangos.twinkletray
```

### Chocolatey (unofficial)

[Chocolatey](https://chocolatey.org/) users can download and install Twinkle Tray from Chocolatey's Community Repository by installing the `twinkle-tray` package:

```powershell
choco install twinkle-tray
```

To upgrade to the latest approved package version, run the following command:

```powershell
choco upgrade twinkle-tray
```

**This package is not maintained at this repository**. Please do not create issues relating to the package here. Instead, go to the [package page](https://community.chocolatey.org/packages/twinkle-tray) and follow the [Package Triage Process](https://docs.chocolatey.org/en-us/community-repository/users/package-triage-process).

### Scoop (unofficial)

[Scoop](https://scoop.sh/) users can download and install Twinkle Tray from Scoop's Extras bucket by installing the `twinkle-tray` package:

```sh
scoop bucket add extras
scoop install extras/twinkle-tray
```

To upgrade to the latest approved package version, run the following command:

```sh
scoop update twinkle-tray
```

**This package is not maintained at this repository**. Please do not create issues relating to the package here. Instead, go to [ScoopInstallers/Extras](https://github.com/ScoopInstaller/Extras) and search for an existing [issue](https://github.com/ScoopInstaller/Extras/issues?q=is%3Aissue+twinkle-tray) or [discussion](https://github.com/ScoopInstaller/Extras/discussions?discussions_q=twinkle-tray) and create a new [issue](https://github.com/ScoopInstaller/Extras/issues/new/choose) or [discussion](https://github.com/ScoopInstaller/Extras/discussions/new/choose) if one does not already exist.

## Usage

- Download from the [Releases page](https://github.com/xanderfrangos/twinkle-tray/releases) and run the installer EXE.
- Once installation has finished, you should see the Twinkle Tray icon in your system tray. 
- Click the icon to bring up the Adjust Brightness flyout. 
- Click away to hide the flyout.
- Right-click the system tray icon to quit.

## Compatibility
Twinkle Tray uses DDC/CI and WMI to communicate with your monitors. Most monitors offer DDC/CI compatibility, but it may be off by default. Make sure you have the appropriate option(s) enabled on your monitor so that it can work with Twinkle Tray. Refer to your monitor's user manual for more information.

**Known issues:**
- The AMD Radeon Control Panel can interfere with Twinkle Tray. Ensure "Custom Colors" is not enabled.
- VGA/DVI may not be compatible.
- USB/Thunderbolt/Surface docks with HDMI or DisplayPort may not be compatible. 
- DDC/CI features such as brightness control and power state may cause certain models of monitors to behave poorly. This applies to any DDC/CI software, not just Twinkle Tray.

If some of your monitors are not being detected, please see [this page](https://github.com/xanderfrangos/twinkle-tray/wiki/Display-Detection-&-Support-Issues) for troubleshooting steps.

## Command Line Arguments

Twinkle Tray (v1.13.0+) supports requesting brightness changes from the command line. Twinkle Tray must already be running. One monitor argument and one brightness argument are required. Multiple arguments will override each other.

For example: `"%LocalAppData%\Programs\twinkle-tray\Twinkle Tray.exe" --MonitorNum=1 --Offset=-30` will adjust monitor number 1 by -30 brightness.

### Supported args:

- `--List` List all displays. *(available in v1.14.0+)*
- `--MonitorNum` Select monitor by number. Starts at 1. *Example: `--MonitorNum=2`*
- `--MonitorID` Select monitor by internal ID. Partial or whole matches accepted. *Example: `--MonitorID="UID2353"`*
- `--All` Flag to select all monitors.
- `--Set` Set brightness percentage. *Example: `--Set=95`*
- `--Offset` Adjust brightness percentage. *Example: `--Offset=-20`*
- `--VCP` Send a specific DDC/CI VCP code and value instead of brightness. The first part is the VCP code (decimal or hexadecimal), and the second is the value. *Example: `--VCP="0xD6:5"`* *(available in v1.14.4+)*
- `--Overlay` Flag to show new brightness levels in the overlay *Example: `--Overlay`*
- `--Panel` Flag to show new brightness levels in the panel *Example: `--Panel`*

*If you are using the Microsoft Store version of Twinkle Tray, you can access Twinkle Tray using the alias `Twinkle-Tray.exe` (v1.17.1+).*

## Localization
Thanks to [several contributors](https://github.com/xanderfrangos/twinkle-tray/graphs/contributors), Twinkle Tray is localized for multiple languages. If you'd like to create or update a localization, see [this page](https://github.com/xanderfrangos/twinkle-tray/wiki/Localization-files) for details. Special thanks to [Weblate](https://weblate.org/) for allowing free use of their service.

#### Localization progress
<a href="https://hosted.weblate.org/engage/twinkle-tray/?utm_source=widget">

</a>

## Build Instructions
If you wish to run a development build of Twinkly Tray:

- Download or clone.
- Install the build tools for [`node-gyp`](https://github.com/nodejs/node-gyp#installation), if not already installed. You may already have these from installing NodeJS.
- Run `npm install`.
- Run `npm run build` to build an executable or `npm start` to run a development build.

*Note: Twinkle Tray must be built on Windows.*

## Special Thanks

Twinkle Tray was built using frameworks & libraries such as [Electron](https://electronjs.org/), [Node.js](https://nodejs.org/), [node-ddcci](https://github.com/hensm/node-ddcci), and [win32-displayconfig](<https://github.com/djsweet/win32-displayconfig>). Thanks to Weblate for allowing free use of their service, along with the many contributors to the localizations of Twinkle Tray. The app would not be nearly as popular without all of your help. And thank you for the many donations, small and large, over the years. 

## License

Copyright © 2020 Xander Frangos

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

---

## File: src/modules/win32-displayconfig/README.md

# win32-displayconfig

Simplified Node bindings to the Win32 [Display Device Reference API](https://docs.microsoft.com/en-us/windows/win32/api/_display/).

## Supported Windows Versions

This module has currently only received testing on Windows 10 version 2004.
Additional testing on other versions of Windows 10 and Windows 8.1 would be appreciated.

We are not planning on supporting Windows 7 or Windows 8, as they are generally unsupported
by Microsoft. However, patches to support Windows 7 or Windows 8 are welcome.

## Supported Node Versions

This module requires N-API Version 4,
[bounding the minimum supported Node versions](https://nodejs.org/api/n-api.html#n_api_n_api_version_matrix)
to 8.16.0 (without any support for Node 9), Node 10.16.0, and Node 11.8.0. We are only actively testing
on Node 10.21 and above. We'll note incompatibilities and accept patches for Node 8 if the need arises.

## Functionality

- Querying display devices, at a low and higher
- Observing display device layout and output changes
- Querying the vertical refresh rate at a given display point
- Saving and restoring layouts via the Display Device Reference API

## Examples

### Querying Low-Level Display Device Information

The `queryDisplayConfig` function returns data somewhat similar to the outputs of the
[`QueryDisplayConfig` function](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig)
via the `pathArray` and `modeArray` properties, and naming and manufacturer info via its
`nameArray` properties.

From [`scripts/dumpquery.js`](scripts/dumpquery.js):

```javascript
const w32disp = require("win32-displayconfig");
const util = require("util");

w32disp.queryDisplayConfig().then((config) => {
  // The outputs of `pathArray` and `modeArray` refer to
  // both parsed values and raw buffer values. The raw buffers
  // are the exact structs returned by the Win32 API, and are
  // used to save and restore display positioning configuration.
  const pathArray = config.pathArray.map((pa) => pa.value);
  const modeArray = config.modeArray.map((ma) => ma.value);
  console.log(
    util.inspect(
      // However, the `nameArray` only refers to parsed values.
      { pathArray, modeArray, nameArray: config.nameArray },
      { depth: 10 }
    )
  );
});
```

Which outputs:

```
{ pathArray:
   [ { flags: 1,
       sourceInfo:
        { adapterId: { LowPart: 945385802, HighPart: 0 },
          id: 0,
          statusFlags: 1,
          modeInfoIdx: 1 },
       targetInfo:
        { adapterId: { LowPart: 945385802, HighPart: 0 },
          id: 114948,
          statusFlags: 1,
          outputTechnology: 'dvi',
          rotation: 0,
          scaling: 'identity',
          refreshRate: { Numerator: 60000, Denominator: 1000 },
          scanLineOrdering: 'progressive',
          targetAvailable: 1,
          modeInfoIdx: 0 } },
     ...
   ],
  modeArray:
   [ { infoType: 'target',
       id: 114948,
       adapterId: { LowPart: 945385802, HighPart: 0 },
       targetMode:
        { targetVideoSignalInfo:
           { pixelRate: { lowPart: 148500000, highPart: 0 },
             hSyncFreq: { Numerator: 148500000, Denominator: 2200 },
             vSyncFreq: { Numerator: 60000, Denominator: 1000 },
             activeSize: { cx: 1920, cy: 1080 },
             totalSize: { cx: 2200, cy: 1125 },
             videoStandard: 255,
             scanlineOrdering: 'progressive' } } },
     ...
   ],
  nameArray:
   [ { adapterId: { LowPart: 945385802, HighPart: 0 },
       id: 114948,
       deviceFlags: 5,
       outputTechnology: 'dvi',
       edidManufactureId: 29188,
       edidProductCodeId: 147,
       connectorInstance: 2,
       monitorFriendlyDeviceName: 'Acer X233H',
       monitorDevicePath:
        '\\\\?\\DISPLAY#ACR0093#5&326e05e0&0&UID114948#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}' },
     ...
   ] }
```

This is generally lower-level output than you want. Consider using `extractDisplayConfig` instead.

### Querying Higher-Level Display Device Information

The `extractDisplayConfig` function reshapes the output of `queryDisplayConfig` to be more useful
to application programmers. Information from the `pathArray`, `modeArray`, and `nameArray` properties
of the output of `queryDisplayConfig` are all joined according to their IDs.

From [`scripts/dumpextract.js`](scripts/dumpextract.js):

```javascript
const w32disp = require("win32-displayconfig");
const util = require("util");

w32disp.extractDisplayConfig().then((output) => {
  console.log(util.inspect(output, { depth: 10 }));
});
```

Which outputs:

```
[ { displayName: 'Acer X233H',
    devicePath:
     '\\\\?\\DISPLAY#ACR0093#5&326e05e0&0&UID114948#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}',
    sourceConfigId: { adapterId: { LowPart: 945385802, HighPart: 0 }, id: 1 },
    targetConfigId:
     { adapterId: { LowPart: 945385802, HighPart: 0 }, id: 114948 },
    inUse: false,
    outputTechnology: 'dvi',
    rotation: 0,
    scaling: 'preferred',
    sourceMode:
     { width: 1920,
       height: 1080,
       pixelFormat: 32,
       position: { x: 0, y: 0 } },
    pathBuffer:
     ,
    sourceModeBuffer:
     ,
    targetVideoSignalInfo:
     { pixelRate: { lowPart: 148500000, highPart: 0 },
       hSyncFreq: { Numerator: 148500000, Denominator: 2200 },
       vSyncFreq: { Numerator: 60000, Denominator: 1000 },
       activeSize: { cx: 1920, cy: 1080 },
       totalSize: { cx: 2200, cy: 1125 },
       videoStandard: 255,
       scanlineOrdering: 'progressive' },
    targetModeBuffer:
      },
  ...
]
```

Note that the low-level buffers are still present, but `sourceMode`, `targetVideoSignalInfo`, `devicePath`,
and `displayName` have all been combined from multiple sources. Also note the `inUse` boolean, which is
derived from comparing against low-level flags in the Display Device Reference API.

You will likely see multiple outputs for a single display device in this output, with only one of them
having `inUse: true`. The `inUse: false` entries correspond to alternative output modes available on the device.

### Observing Display Device Layout and Output Changes

The display geometry can change at any time during the execution of your program, but sometimes you
depend on knowledge of this geometry for the correct behavior. You can observe changes as they
occur using the `addDisplayChangeListener` function:

```javascript
const w32disp = require("win32-displayconfig");
const listener1 = (err, conf) => {
  if (err !== null) {
    // Report error
    console.error(err);
  } else {
    // conf is the same as the output of extractDisplayConfig
    // ...
  }
};
w32disp.addDisplayChangeListener(listener1);

// Or equivalently
const listener2 = w32disp.addDisplayChangeListener((err, conf) => {
  if (err !== null) {
    console.error(err);
  } else {
    // ...
  }
});
```

Adding these change listeners keeps the event loop active, even when all other
activities have been stopped, so for situations where graceful shutdown is required,
remove all of the listeners using the `removeDisplayChangeListener` function:

```javascript
w32disp.removeDisplayChangeListener(listener1);
w32disp.removeDisplayChangeListener(listener2);
// No more display config activities are present on the event loop,
// so Node can now exit cleanly.
```

### Querying the Vertical Refresh Rate at a Display Point

Some operations need to only occur as quickly as the vertical refresh rate
for smooth performance. For example, many Windows positioning APIs cannot
perform well if they are invoked any faster than the frame rate of the GPU.

This module provides a `VerticalRefreshRateContext` class to determine the
refresh rate of the displays servicing a given display point. Note that not
all displays will necessarily have the same refresh rate, and many displays
can be servicing the same display point. When multiple displays are servicing
the same display point, the result is the minimum vertical refresh rate.

```javascript
const { VerticalRefreshRateContext } = require("win32-displayconfig");
const ctx = new VerticalRefreshRateContext();

async function refreshRateForPoint(x, y) {
  // Here, x is the vertical offset and y is the horizontal offset.
  // These offsets are relative to the upper left hand corner of the
  // primary display, so they are allowed and expected to become negative.
  //
  // The result is either a number indicating the vertical refresh rate in Hz,
  // or undefined if the given point is not present in the display bounds of
  // the current display configuration.
  return await ctx.findVerticalRefreshRateForDisplayPoint(x, y);
}

// ...

// This context reacts to underlying display changes, as a trade-off between
// performance and accuracy. It does so using the `addDisplayChangeListener`
// capabilities, so it needs to be disposed of properly to clear the event
// loop for graceful shutdown.
//
// Call this when you are finished with the context. A good place to do so
// is in the code that handles gracefully exiting your program.
ctx.close();
```

See [`scripts/watchmouse.js`](scripts/watchmouse.js) for a working example, polling
for the refresh rate based on the mouse cursor position.

### Saving and Restoring Device Layouts

This module can save and restore display device layouts (although it cannot directly modify
them yet; patches welcome!). You can use this to implement a display configuration profile
system.

Saving a profile is achieved with the `displayConfigForRestoration` function:

```javascript
const w32disp = require("win32-displayconfig");

async function saveDisplayConfig() {
  const conf = await w32disp.displayConfigForRestoration();
  // The type of conf is JSON-safe: you can store this on the disk,
  // transmit it over a network, etc.
  return JSON.stringify(conf);
}
```

And restoring a profile is achieved with the `restoreDisplayConfig` function:

```javascript
const w32disp = require("win32-displayconfig");

async function restoreDisplayConfig(serialized, persistent) {
  // If "persistent", then this display configuration will be saved in Windows
  // as the default display configuration. This means that restoring to defaults
  // or rebooting will result in this exact configuration.
  persistent = !!persistent;
  const conf = JSON.parse(serialized);

  // If portions of the configuration refer to displays that are not actually
  // present, we make a best effort to enable all of the displays that are both
  // present and enabled in this configuration, and disable all other displays.
  //
  // Otherwise, the display configuration is set exactly.
  await w32disp.restoreDisplayConfig(conf);
}
```

## Copyright

This module is available under the terms of the MIT license. See the [`COPYRIGHT`](COPYRIGHT) file
for more information.

---

## File: src/modules/node-ddcci/README.md

# node-ddcci

## Installation

node-ddcci is currently only supported on Windows.

````bash
npm install @hensm/ddcci
````

## Usage

````js
const ddcci = require("@hensm/ddcci");

for (const monitor of ddcci.getMonitorList()) {
    console.log(`${monitor} current brightness: ${ddcci.getBrightness(monitor)}`);
    ddcci.setBrightness(monitor, 25);
}
````

## Docs

* ### `getMonitorList()`
  Gets a list of the current connected monitors.
  * #### Return value
    An array of `String` containing the monitor IDs.

* ### `getBrightness(monitorId)`
  Queries a monitor's brightness level.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to query the brightness.
  * #### Return value
    An `integer`, typically between 0-100, representing the current brightness.
    
* ### `getMaxBrightness(monitorId)`
  Queries a monitor's maximum brightness level.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to query the brightness.
  * #### Return value
    An `integer`, typically between 0-100, representing the maximum brightness.

* ### `setBrightness(monitorId, level)`
  Sets a monitor's brightness level.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to set the brightness.
    * **`level`**  
      `integer`. Between 0-100 representing the new brightness level.

* ### `getContrast(monitorId)`
  Queries a monitor's contrast level.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to query the contrast.
  * #### Return value
    An `integer`, typically between 0-100, representing the current contrast.
    
* ### `getMaxContrast(monitorId)`
  Queries a monitor's maximum contrast level.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to query the contrast.
  * #### Return value
    An `integer`, typically between 0-100, representing the maximum contrast.

* ### `setContrast(monitorId, level)`
  Sets a monitor's contrast level.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to set the contrast.
    * **`level`**  
      `integer`. Between 0-100 representing the new contrast level.

* ### `_getVCP(monitorId, vcpCode)`
  Queries a monitor for a VCP code value.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to query the VCP feature.
    * **`vcpCode`**  
      `integer`. VCP code to query
  * #### Return value
    An `array` of two `integer` values in the format of `[currentValue, maxValue]`.

* ### `_setVCP(monitorId, vcpCode, value)`
  Sets the value of a VCP code for a monitor.
  * #### Parameters
    * **`monitorId`**  
      `String`. ID of monitor for which to set the VCP feature.
    * **`vcpCode`**  
      `integer`. VCP code to set.
    * **`value`**  
      `integer`. Value of the VCP code.

* ### `_refresh()`
  Refreshes the monitor list.

---

## File: src/modules/node-active-window/README.md

# Node Active Window

[](https://www.npmjs.com/package/@paymoapp/active-window)
[](https://www.npmjs.com/package/@paymoapp/active-window)
[](https://github.com/nodejs/node-addon-api)
[](https://www.gnu.org/licenses/gpl-3.0.txt)

NodeJS library using native modules to get the active window and some metadata (including the application icon) on Windows, MacOS and Linux.

### Table of Contents



- [Getting started](#getting-started)
    - [Installation](#installation)
    - [Native addon](#native-addon)
    - [Example](#example)
- [API](#api)
    - [Data structures](#data-structures)
    - [Functions](#functions)
- [Native libraries](#native-libraries)
  - [Linux (`module/linux`)](#linux-modulelinux)
    - [Data structures](#data-structures-1)
    - [Public functions](#public-functions)
    - [Example](#example-1)
    - [Building](#building)
  - [Windows (`module/windows`)](#windows-modulewindows)
    - [Data structures](#data-structures-2)
    - [Public functions](#public-functions-1)
    - [Example](#example-2)
    - [Building](#building-1)
  - [MacOS (`module/macos`)](#macos-modulemacos)
    - [Data structures](#data-structures-3)
    - [Public functions](#public-functions-2)
    - [Example](#example-3)
    - [Building](#building-2)



## Getting started

#### Installation

```bash
npm install --save @paymoapp/active-window
```

#### Native addon

This project uses NodeJS Native Addons to function, so you can use this library in any NodeJS or Electron project, there won't be any problem with bundling and code signing.

The project uses [prebuild](https://github.com/prebuild/prebuild) to supply prebuilt libraries.

The project uses Node-API version 6, you can check [this table](https://nodejs.org/api/n-api.html#node-api-version-matrix) to see which node versions are supported.

If there's a compliant prebuilt binary, it will be downloaded during installation, or it will be built. You can also rebuild it anytime by running `npm run build:gyp`.

The library has native addons for all the three major operating systems: Windows, MacOS and Linux. For Linux, only the X11 windowing system is supported.

#### Example

You can run a demo application by calling `npm run demo`. You can browse it's source code for a detailed example using the watch API in `demo/index.ts`.

```ts
import ActiveWindow from '@paymoapp/active-window';

ActiveWindow.initialize();

if (!ActiveWindow.requestPermissions()) {
	console.log('Error: You need to grant screen recording permission in System Preferences > Security & Privacy > Privacy > Screen Recording');
	process.exit(0);
}

const activeWin = ActiveWindow.getActiveWindow();

console.log('Window title:', activeWin.title);
console.log('Application:', activeWin.application);
console.log('Application path:', activeWin.path);
console.log('Application PID:', activeWin.pid);
console.log('Application icon:', activeWin.icon);
```

## API

#### Data structures

###### 🗃 &nbsp;&nbsp; WindowInfo

```ts
interface WindowInfo {
	title: string;
	application: string;
	path: string;
	pid: number;
	icon: string;
	windows?: {
		isUWPApp: boolean;
		uwpPackage: string;
	};
}
```

This is the only object you will receive when interacting with this library. It contains information about the currently active window:

- `title` - The title of the current window
- `application` - The name of the application. On Windows you should use the `uwpPackage` parameter instead if `isUWPApp` is set to true
- `path` - The path to the application's executable
- `pid` - Process identifier of the application
- `icon` - Base64 encoded string representing the application icon
- `windows` - Object containing Windows platform specific information, undefined on other platforms
- `windows.isUWPApp` - Set to `true` if the active window is owned by an [Universal Windows Platform](https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide) application
- `windows.uwpPackage` - Contains the package family name of the UWP application

None of the parameters are nullable, even if their value couldn't be fetched, they will be either be set to an empty string (for the string values), -1 (for the numeric values) or false (for the boolean values).

#### Functions

###### 𝑓 &nbsp;&nbsp; getActiveWindow

```ts
interface IActiveWindow {
	getActiveWindow(): WindowInfo
	// ...
}
```

Requests the current foreground window in a synchronous way. It will throw an error if the current window couldn't be fetched (for example there're no focused windows at the moment).

###### 𝑓 &nbsp;&nbsp; subscribe

```ts
interface IActiveWindow {
	subscribe(callback: (windowInfo: WindowInfo | null) => void): number;
	// ...
}
```

Subscribe to changes of the active window. The supplied callback will be called with `null` if there're no focused windows at the moment.

The function returns a number representing the ID of the watch. You should store this value to remove the event listener later on.

###### 𝑓 &nbsp;&nbsp; unsubscribe

```ts
interface IActiveWindow {
	unsubscribe(watchId: number): void;
	// ...
}
```

Remove the event listener associated with the supplied watch ID. Use this to unsubscribe from the active window changed events.

###### 𝑓 &nbsp;&nbsp; initialize

```ts
interface IActiveWindow {
	initialize(opts?: { osxRunLoop: boolean }): void;
	// ...
}
```

On some platforms (Linux) the library needs some initialization to be done. You must call this function before doing anything with the library regardless of the current platform.

If you're not using this library in a GUI application there might be no runloop running for the main thread. In this case you should set the `osxRunLoop` property to true if you want to use subscriptions.

###### 𝑓 &nbsp;&nbsp; requestPermissions

```ts
interface IActiveWindow {
	requestPermissions(): boolean;
	// ...
}
```

On the MacOS platform you need to request screen recording permission to fetch the title of the current window.

The function will return `true` if the permission is granted and `false` if the permission is denied. This is a non-blocking function, so you will only get the momentary status.

You can call this function regardless of the current platform. On unsupported platforms it will simply return `true`.

When the function is called, the user will be presented with a system modal with instructions to grant the permission. You should include these instructions in your application as well, since this is a one-time modal. After the user grants the permission, it is required to relaunch the application for the changes to take effect.

If the user fails to grant the required permissions, the `title` property of the returned `WindowInfo` will be an empty string.

## Native libraries

You can import the each platform dependent library as a standalone C++ / Objective-C++ library. You can find the library itself in the module's `src` directory. The `napi` directory contains the Node-API bindings and the `demo` directory contains a small demo program that can be built using a Makefile.

You can build the demo by navigating to the `module//demo` folder and executing `make`. You can run the demo using `make run` and clean the build artifacts using `make clean`.

The demo has 4 running modes:
- _default_: `make run` - in this mode the library is used to fetch the current window details, then there's a 3 second delay after which the current window is fetched again
- _loop_: `make run MODE=loop` - in this mode the library is used the poll the current window in every 3 seconds until SIGINT (Ctrl+C) is received
- _watch_: `make run MODE=watch` - in this mode the library is used to watch the current window and it's title. There's no polling involved in this mode
- _benchmark_: `make run MODE=benchmark` - in this mode the library will fetch the current window details 100.000 times (or 10.000 times on windows) and it will print the total of the consumed CPU seconds while doing so

### Linux (`module/linux`)

#### Data structures

###### 🗃 &nbsp;&nbsp; WindowInfo

```c++
struct WindowInfo {
	std::string title; // UTF8 encoded string of window title. Empty string if couldn't fetch
	std::string application; // UTF8 encoded string of application name. Empty string if couldn't fetch
	std::string path; // UTF8 encoded string of application path. Empty string if couldn't fetch
	int pid; // PID of process. -1 if couldn't fetch
	std::string icon; // base64 encoded PNG icon. Empty string if couldn't fetch
}
```

###### 🗃 &nbsp;&nbsp; watch_t

```c++
typedef unsigned int watch_t;
```

###### 🗃 &nbsp;&nbsp; watch_callback

```c++
typedef std::function<void(WindowInfo*)> watch_callback;
```

#### Public functions

###### 𝑓 &nbsp;&nbsp; Constructor

```c++
ActiveWindow(unsigned int iconCacheSize = 0);
```

If you pass iconCacheSize > 0, then an LRU (least recently used) cache will be instantiated which will cache the fetched icons. This results in about 90% less CPU seconds consumed. You can use the benchmark mode of the demo to test it yourself.

You should pass a cache size suitable for your application. A bigger cache results in a greater memory consumption, but it also reduces the CPU utilization if your user switches across a large set of applications.

###### 𝑓 &nbsp;&nbsp; getActiveWindow

```c++
WindowInfo* getActiveWindow();
```

Returns pointer to WindowInfo containing the gathered information about the current window. The pointer can be `NULL` in the case of an error or if there is no active window (ex: all the windows are minified). You should free up the allocated WindowInfo object using `delete`.

###### 𝑓 &nbsp;&nbsp; buildAppCache

```c++
void buildAppCache();
```

Gathers all the `.desktop` entries available on the system (starting from `~/.local/share/applications` through each `$XDG_DATA_DIRS/applications`) and resolves the icon path for them. This cache is used to get the icon for a given window. If this function is not called, then no icons will be resolved.

###### 𝑓 &nbsp;&nbsp; watchActiveWindow

```c++
watch_t watchActiveWindow(watch_callback cb);
```

Sets up a watch for the active window. If there's a change in the current active window, or the title of the active window, the callback will be fired with the current active window. You don't need to call `getActiveWindow()` in the callback, you can use the supplied parameter.

This method will also start a background watch thread if it's not already running. Please note that the callbacks will be executed on this thread, so you should assure thread safety.

You __MUST NOT__ free up the WindowInfo object received in the parameter. If you need to store the active window you __SHOULD__ make a copy of it, since the WindowInfo object will be freed after calling all the callbacks.

You should save the returned watch ID to unsubscribe later.

###### 𝑓 &nbsp;&nbsp; unwatchActiveWindow

```c++
void unwatchActiveWindow(watch_t watch);
```

Removes the watch associated with the supplied watch ID.

The background watch thread will not be closed, even if there're no more watches left. It will only be closed when the class's destructor is called.

#### Example

See `module/linux/demo/main.cpp` for an example.

```c++
#include 
#include "ActiveWindow.h"

using namespace std;
using namespace PaymoActiveWindow;

int main() {
	ActiveWindow* activeWindow = new ActiveWindow(10);

	WindowInfo* windowInfo = activeWindow->getActiveWindow();

	if (windowInfo == NULL) {
		cout<<"Could not get active window\n";
	}
	else {
		cout<<"Title: "<<windowInfo->title<<"\n";
		cout<<"Application: "<<windowInfo->application<<"\n";
		cout<<"Executable path: "<<windowInfo->path<<"\n";
		cout<<"PID: "<<windowInfo->pid<<"\n";
		cout<<"Icon: "<<windowInfo->icon<<"\n";
	}

	delete windowInfo;
	delete activeWindow;

	return 0;
}
```

#### Building

See `module/linux/demo/Makefile` for a sample makefile. You need to check the `lib` target.

You should use C++17 for building and you need to link the following libraries:
- X11 (`-lX11`) - libx11-dev
- PThread (`-lpthread`) - libpthread-stubs0-dev

### Windows (`module/windows`)

#### Data structures

###### 🗃 &nbsp;&nbsp; WindowInfo

```c++
struct WindowInfo {
	std::wstring title = L""; // UTF16 encoded string of window title. Empty string if couldn't fetch
	std::wstring application = L""; // UTF16 encoded string of application. Empty string if couldn't fetch
	std::wstring path = L""; // UTF16 encoded string of executable path. Empty string if couldn't fetch
	unsigned int pid = 0; // Process PID
	bool isUWPApp = false; // if application is detected to be an Universal Windows Platform application
	std::wstring uwpPackage = L""; // UTF16 encoded string of the UWP package name. Empty string if couldn't fetch
	std::string icon = ""; // base64 encoded icon. Empty string if couldn't fetch
};
```

###### 🗃 &nbsp;&nbsp; watch_t

```c++
typedef unsigned int watch_t;
```

###### 🗃 &nbsp;&nbsp; watch_callback

```c++
typedef std::function<void(WindowInfo*)> watch_callback;
```

#### Public functions

###### 𝑓 &nbsp;&nbsp; Constructor

```c++
ActiveWindow(unsigned int iconCacheSize = 0);
```

If you pass iconCacheSize > 0, then an LRU (least recently used) cache will be instantiated which will cache the fetched icons. This results in about 90% less CPU seconds consumed. You can use the benchmark mode of the demo to test it yourself.

You should pass a cache size suitable for your application. A bigger cache results in a greater memory consumption, but it also reduces the CPU utilization if your user switches across a large set of applications.

###### 𝑓 &nbsp;&nbsp; getActiveWindow

```c++
WindowInfo* getActiveWindow();
```

Returns pointer to WindowInfo containing the gathered information about the current window. The pointer can be `NULL` in the case of an error or if there is no active window (ex: all the windows are minified or the desktop is selected). You should free up the allocated WindowInfo object using `delete`.

###### 𝑓 &nbsp;&nbsp; watchActiveWindow

```c++
watch_t watchActiveWindow(watch_callback cb);
```

Sets up a watch for the active window. If there's a change in the current active window, or the title of the active window, the callback will be fired with the current active window. You don't need to call `getActiveWindow()` in the callback, you can use the supplied parameter.

This method will also start a background watch thread if it's not already running. Please note that the callbacks will be executed on this thread, so you should assure thread safety.

You __MUST NOT__ free up the WindowInfo object received in the parameter. If you need to store the active window you __SHOULD__ make a copy of it, since the WindowInfo object will be freed after calling all the callbacks.

You should save the returned watch ID to unsubscribe later.

###### 𝑓 &nbsp;&nbsp; unwatchActiveWindow

```c++
void unwatchActiveWindow(watch_t watch);
```

Removes the watch associated with the supplied watch ID.

The background watch thread will not be closed, even if there're no more watches left. It will only be closed when the class's destructor is called.

#### Example

See `module/windows/demo/main.cpp` for an example.

```c++
#include 
#include "ActiveWindow.h"

using namespace std;
using namespace PaymoActiveWindow;

int main() {
	ActiveWindow* activeWindow = new ActiveWindow(10);

	WindowInfo* windowInfo = activeWindow->getActiveWindow();

	if (windowInfo == NULL) {
		cout<<"Could not get active window\n";
	}
	else {
		wcout<<L"Title: "<<windowInfo->title<<"\n";
		wcout<<L"Application: "<<windowInfo->application<<"\n";
		wcout<<L"Executable path: "<<windowInfo->path<<"\n";
		cout<<"PID: "<<windowInfo->pid<<"\n";
		cout<<"Is UWP application: "<<(windowInfo->isUWPApp ? "Yes" : "No")<<"\n";
		if (windowInfo->isUWPApp) {
			wcout<<"UWP package name: "<<windowInfo->uwpPackage<<"\n";
		}
		cout<<"Icon: "<<windowInfo->icon<<"\n";
	}

	delete windowInfo;
	delete activeWindow;

	return 0;
}
```

#### Building

See `module/windows/demo/Makefile` for a sample makefile. You need to check the `lib` target. This is not a GNU makefile, it should be used with Microsoft's NMAKE.

To prepare your environment, you need run the `vcvarsall.bat` batch script. If you checked the _install windows build tools_ box during the installation of NodeJS, you should find this file in the following location: `C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat`. So to gain access to the `nmake`, `cl` and `link` commands, execute this:

```batch
"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64
```

You should use C++17 for building and you need to link the following libraries:
- User32.lib
- Shell32.lib
- Version.lib
- Shlwapi.lib
- Gdi32.lib
- Gdiplus.lib
- Windowsapp.lib

### MacOS (`module/macos`)

#### Data structures

###### 🗃 &nbsp;&nbsp; WindowInfo

```c++
struct WindowInfo {
	std::string title; // UTF8 encoded string of window title. Empty string if couldn't fetch
	std::string application; // UTF8 encoded string of application name
	std::string path; // UTF8 encoded string of application path
	int pid; // PID of process
	std::string icon; // base64 encoded PNG icon
}
```

###### 🗃 &nbsp;&nbsp; watch_t

```c++
typedef unsigned int watch_t;
```

###### 🗃 &nbsp;&nbsp; watch_callback

```c++
typedef std::function<void(WindowInfo*)> watch_callback;
```

#### Public functions

###### 𝑓 &nbsp;&nbsp; Constructor

```c++
ActiveWindow(unsigned int iconCacheSize = 0);
```

If you pass iconCacheSize > 0, then an LRU (least recently used) cache will be instantiated which will cache the fetched icons. This results in about 40% less CPU seconds consumed. You can use the benchmark mode of the demo to test it yourself.

You should pass a cache size suitable for your application. A bigger cache results in a greater memory consumption, but it also reduces the CPU utilization if your user switches across a large set of applications.

###### 𝑓 &nbsp;&nbsp; getActiveWindow

```c++
WindowInfo* getActiveWindow();
```

Returns pointer to WindowInfo containing the gathered information about the current window. The pointer can be `NULL` in the case of an error or if there is no active window (ex: all the windows are minified). You should free up the allocated WindowInfo object using `delete`.

###### 𝑓 &nbsp;&nbsp; requestScreenCaptureAccess

```c++
bool requestScreenCaptureAccess();
```

To access the title of the window the process requires the screen capture permission. To check it and request it you need to call this function. This function is non-blocking and will immediately return with the current status (false - permission denied, true - permission granted).

The first time this function is called there will be a system popup instructing the user how he can grant this permission, but you should also include this information in your application, since it's a one-time popup that closes when you click outside of it.

The application needs to be relaunched after granting the permission.

###### 𝑓 &nbsp;&nbsp; watchActiveWindow

```c++
watch_t watchActiveWindow(watch_callback cb);
```

Sets up a watch for the active window. If there's a change in the current active window, the callback will be fired with the current active window. You don't need to call `getActiveWindow()` in the callback, you can use the supplied parameter.

This method will use the observer set up on the main thread to listen to the events, so the main thread has to have a running NSRunLoop. If you integrate this library into a desktop application, then this should already be resolved. Otherwise (for example when using a console application), you have to manually run the RunLoop or call the `runLoop()` helper function which will block for 0.1ms.

If you want to use the watch function, you __MUST__ use this library on the main thread, since the NSWorkspace notifications are only serviced on that thread.

The callbacks are also executed on the main thread, so you shouldn't do anything blocking in them.

You should save the returned watch ID to unsubscribe later.

###### 𝑓 &nbsp;&nbsp; unwatchActiveWindow

```c++
void unwatchActiveWindow(watch_t watch);
```

Removes the watch associated with the supplied watch ID.

###### 𝑓 &nbsp;&nbsp; runLoop

```c++
void runLoop();
```

A helper function which will run the thread's RunLoop for 0.1ms or until the first event is handled. This function should be called on the main thread.

You should only use this function only if you don't have a running RunLoop on your main thread.

#### Example

See `module/macos/demo/main.mm` for an example.

```c++
#include 
#include "ActiveWindow.h"

using namespace std;
using namespace PaymoActiveWindow;

int main() {
	ActiveWindow* activeWindow = new ActiveWindow();

	WindowInfo* windowInfo = activeWindow->getActiveWindow();

	if (windowInfo == NULL) {
		cout<<"Could not get active window\n";
	}
	else {
		cout<<"Title: "<<windowInfo->title<<"\n";
		cout<<"Application: "<<windowInfo->application<<"\n";
		cout<<"Executable path: "<<windowInfo->path<<"\n";
		cout<<"PID: "<<windowInfo->pid<<"\n";
		cout<<"Icon: "<<windowInfo->icon<<"\n";
	}

	delete windowInfo;
	delete activeWindow;

	return 0;
}
```

#### Building

See `module/macos/demo/Makefile` for a sample makefile. You need to check the `lib` target.

You should use C++17 for building and you need to link the following libraries:
- `-lc++`
- `-framework Foundation`
- `-framework AppKit`
- `-framework ApplicationServices`