active-win
Advanced tools
Comparing version 5.0.0 to 5.1.0
'use strict'; | ||
const {promisify} = require('util'); | ||
const fs = require('fs'); | ||
const childProcess = require('child_process'); | ||
const execFile = promisify(childProcess.execFile); | ||
const readFile = promisify(fs.readFile); | ||
const readlink = promisify(fs.readlink); | ||
const xpropBin = 'xprop'; | ||
@@ -13,2 +17,3 @@ const xwininfoBin = 'xwininfo'; | ||
const result = {}; | ||
for (const row of output.trim().split('\n')) { | ||
@@ -36,3 +41,10 @@ if (row.includes('=')) { | ||
const processId = parseInt(result['_NET_WM_PID(CARDINAL)'], 10); | ||
if (Number.isNaN(processId)) { | ||
throw new Error('Failed to parse process ID'); // eslint-disable-line unicorn/prefer-type-error | ||
} | ||
return { | ||
platform: 'linux', | ||
title: JSON.parse(result['_NET_WM_NAME(UTF8_STRING)']) || null, | ||
@@ -42,3 +54,3 @@ id: windowId, | ||
name: JSON.parse(result['WM_CLASS(STRING)'].split(',').pop()), | ||
processId: parseInt(result['_NET_WM_PID(CARDINAL)'], 10) | ||
processId | ||
}, | ||
@@ -56,29 +68,74 @@ bounds: { | ||
const getMemoryUsageByPid = async pid => { | ||
const statm = await readFile(`/proc/${pid}/statm`, 'utf8'); | ||
return parseInt(statm.split(' ')[1], 10) * 4096; | ||
}; | ||
const getMemoryUsageByPidSync = pid => { | ||
const statm = require('fs').readFileSync(`/proc/${pid}/statm`, 'utf8'); | ||
return parseInt(statm.split(' ')[1], 10) * 4096; | ||
}; | ||
const getPathByPid = pid => { | ||
return readlink(`/proc/${pid}/exe`); | ||
}; | ||
const getPathByPidSync = pid => { | ||
return fs.readlinkSync(`/proc/${pid}/exe`); | ||
}; | ||
module.exports = async () => { | ||
const {stdout: activeWindowIdStdout} = await execFile(xpropBin, xpropActiveArgs); | ||
const activeWindowId = getActiveWindowId(activeWindowIdStdout); | ||
try { | ||
const {stdout: activeWindowIdStdout} = await execFile(xpropBin, xpropActiveArgs); | ||
const activeWindowId = getActiveWindowId(activeWindowIdStdout); | ||
const [{stdout}, {stdout: boundsStdout}] = await Promise.all([ | ||
execFile(xpropBin, xpropDetailsArgs.concat([activeWindowId])), | ||
execFile(xwininfoBin, xpropDetailsArgs.concat([activeWindowId])) | ||
]); | ||
if (!activeWindowId) { | ||
return; | ||
} | ||
return parseLinux({ | ||
activeWindowId, | ||
boundsStdout, | ||
stdout | ||
}); | ||
const [{stdout}, {stdout: boundsStdout}] = await Promise.all([ | ||
execFile(xpropBin, xpropDetailsArgs.concat([activeWindowId])), | ||
execFile(xwininfoBin, xpropDetailsArgs.concat([activeWindowId])) | ||
]); | ||
const data = parseLinux({ | ||
activeWindowId, | ||
boundsStdout, | ||
stdout | ||
}); | ||
const [memoryUsage, path] = await Promise.all([ | ||
getMemoryUsageByPid(data.owner.processId), | ||
getPathByPid(data.owner.processId) | ||
]); | ||
data.memoryUsage = memoryUsage; | ||
data.owner.path = path; | ||
return data; | ||
} catch (_) { | ||
return undefined; | ||
} | ||
}; | ||
module.exports.sync = () => { | ||
const activeWindowIdStdout = childProcess.execFileSync(xpropBin, xpropActiveArgs, {encoding: 'utf8'}); | ||
const activeWindowId = getActiveWindowId(activeWindowIdStdout); | ||
const stdout = childProcess.execFileSync(xpropBin, xpropDetailsArgs.concat(activeWindowId), {encoding: 'utf8'}); | ||
const boundsStdout = childProcess.execFileSync(xwininfoBin, xpropDetailsArgs.concat([activeWindowId]), {encoding: 'utf8'}); | ||
try { | ||
const activeWindowIdStdout = childProcess.execFileSync(xpropBin, xpropActiveArgs, {encoding: 'utf8'}); | ||
const activeWindowId = getActiveWindowId(activeWindowIdStdout); | ||
return parseLinux({ | ||
activeWindowId, | ||
boundsStdout, | ||
stdout | ||
}); | ||
if (!activeWindowId) { | ||
return; | ||
} | ||
const stdout = childProcess.execFileSync(xpropBin, xpropDetailsArgs.concat(activeWindowId), {encoding: 'utf8'}); | ||
const boundsStdout = childProcess.execFileSync(xwininfoBin, xpropDetailsArgs.concat([activeWindowId]), {encoding: 'utf8'}); | ||
const data = parseLinux({ | ||
activeWindowId, | ||
boundsStdout, | ||
stdout | ||
}); | ||
data.memoryUsage = getMemoryUsageByPidSync(data.owner.processId); | ||
data.owner.path = getPathByPidSync(data.owner.processId); | ||
return data; | ||
} catch (_) { | ||
return undefined; | ||
} | ||
}; |
@@ -13,2 +13,3 @@ 'use strict'; | ||
if (result !== null) { | ||
result.platform = 'macos'; | ||
return result; | ||
@@ -15,0 +16,0 @@ } |
@@ -37,2 +37,26 @@ /* eslint-disable new-cap */ | ||
const SIZE_T = 'uint64'; | ||
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-_process_memory_counters | ||
const ProcessMemoryCounters = struct({ | ||
cb: 'uint32', | ||
PageFaultCount: 'uint32', | ||
PeakWorkingSetSize: SIZE_T, | ||
WorkingSetSize: SIZE_T, | ||
QuotaPeakPagedPoolUsage: SIZE_T, | ||
QuotaPagedPoolUsage: SIZE_T, | ||
QuotaPeakNonPagedPoolUsage: SIZE_T, | ||
QuotaNonPagedPoolUsage: SIZE_T, | ||
PagefileUsage: SIZE_T, | ||
PeakPagefileUsage: SIZE_T | ||
}); | ||
const ProcessMemoryCountersPointer = ref.refType(ProcessMemoryCounters); | ||
// Create FFI declarations for the C++ library and functions needed (psapi.dll) | ||
const psapi = new ffi.Library('psapi', { | ||
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/nf-psapi-getprocessmemoryinfo | ||
GetProcessMemoryInfo: ['int', ['pointer', ProcessMemoryCountersPointer, 'uint32']] | ||
}); | ||
// Create FFI declarations for the C++ library and functions needed (Kernel32.dll), using their "Unicode" (UTF-16) version | ||
@@ -53,2 +77,7 @@ const kernel32 = new ffi.Library('kernel32', { | ||
const activeWindowHandle = user32.GetForegroundWindow(); | ||
if (ref.isNull(activeWindowHandle)) { | ||
return undefined; // Failed to get active window handle | ||
} | ||
// Get memory address of the window handle as the "window ID" | ||
@@ -77,2 +106,7 @@ const windowId = ref.address(activeWindowHandle); | ||
const processHandle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId); | ||
if (ref.isNull(processHandle)) { | ||
return undefined; // Failed to get process handle | ||
} | ||
// Set the path length to more than the Windows extended-length MAX_PATH length | ||
@@ -94,2 +128,8 @@ const pathLengthBytes = 66000; | ||
const processName = path.basename(processPath); | ||
// Get process memory counters | ||
const memoryCounters = new ProcessMemoryCounters(); | ||
memoryCounters.cb = ProcessMemoryCounters.size; | ||
const getProcessMemoryInfoResult = psapi.GetProcessMemoryInfo(processHandle, memoryCounters.ref(), ProcessMemoryCounters.size); | ||
// Close the "handle" of the process | ||
@@ -100,5 +140,14 @@ kernel32.CloseHandle(processHandle); | ||
// Get the window bounds and save it into the `bounds` variable | ||
user32.GetWindowRect(activeWindowHandle, bounds.ref()); | ||
const getWindowRectResult = user32.GetWindowRect(activeWindowHandle, bounds.ref()); | ||
if (getProcessMemoryInfoResult === 0) { | ||
return undefined; // Failed to get process memory | ||
} | ||
if (getWindowRectResult === 0) { | ||
return undefined; // Failed to get window rect | ||
} | ||
return { | ||
platform: 'windows', | ||
title: windowTitle, | ||
@@ -116,4 +165,7 @@ id: windowId, | ||
height: bounds.bottom - bounds.top | ||
} | ||
}, | ||
memoryUsage: memoryCounters.WorkingSetSize | ||
}; | ||
/* eslint-enable new-cap */ | ||
} | ||
@@ -120,0 +172,0 @@ |
{ | ||
"name": "active-win", | ||
"version": "5.0.0", | ||
"version": "5.1.0", | ||
"description": "Get metadata about the active window (title, id, bounds, owner, etc). Works on macOS, Linux, Windows.", | ||
@@ -16,3 +16,3 @@ "license": "MIT", | ||
"scripts": { | ||
"test": "xo && ava", | ||
"test": "xo && ava && tsd", | ||
"build": "swift build --configuration=release --static-swift-stdlib && mv .build/release/active-win main", | ||
@@ -23,2 +23,3 @@ "prepublishOnly": "npm run build" | ||
"index.js", | ||
"index.d.ts", | ||
"lib", | ||
@@ -51,3 +52,4 @@ "main" | ||
"devDependencies": { | ||
"ava": "^1.4.0", | ||
"ava": "^1.4.1", | ||
"tsd": "^0.7.1", | ||
"xo": "^0.24.0" | ||
@@ -54,0 +56,0 @@ }, |
@@ -49,3 +49,3 @@ # active-win [![Build Status](https://travis-ci.org/sindresorhus/active-win.svg?branch=master)](https://travis-ci.org/sindresorhus/active-win) | ||
Returns a `Promise<Object>` with the result, or `Promise<undefined>` if there is no active window. | ||
Returns a `Promise<Object>` with the result, or `Promise<undefined>` if there is no active window or if the information is not available. | ||
@@ -59,2 +59,3 @@ ### activeWin.sync() | ||
- `platform` *(string)* - `'macos'` | `'linux'` | `'windows'` | ||
- `title` *(string)* - Window title | ||
@@ -71,4 +72,4 @@ - `id` *(number)* - Window identifier | ||
- `bundleId` *(string)* - Bundle identifier *(macOS only)* | ||
- `path` *(string)* - Path to the app *(macOS and Windows only)* | ||
- `memoryUsage` *(number)* - Memory usage by the window *(macOS only)* | ||
- `path` *(string)* - Path to the app | ||
- `memoryUsage` *(number)* - Memory usage by the window owner process | ||
@@ -86,2 +87,3 @@ | ||
- [active-win-cli](https://github.com/sindresorhus/active-win-cli) - CLI for this module | ||
- [active-win-log](https://github.com/uglow/active-win-log) - Window-usage logging CLI using this module | ||
@@ -88,0 +90,0 @@ |
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
9
416
96
61385
3
3