i3-cycle-focus
Version:
Simulates an alt-tab operation for the i3 window manager. Shifts the mouse cursor position to the center of focus. Cycles visible windows only.
125 lines (113 loc) • 3.1 kB
JavaScript
/**
* @author Justin Collier <jpcxme@gmail.com>
* @license MIT
* @see {@link http://github.com/jpcx/i3-cycle-focus|GitHub}
*/
const i3 = require('i3').createClient()
const dp = require('deep-props')
const { spawn } = require('child_process')
/**
* Array of workspace information.
*
* @private
* @typedef {Array} I3Data-workspaces
*/
/**
* Object containing i3 tree structure.
*
* @private
* @typedef {Object} I3Data-tree
*/
/**
* Contains information about the i3 state.
*
* @private
* @typedef {Object} I3Data
* @property {I3Data-workspaces} workspaces - Array of workspace information.
* @property {I3Data-tree} tree - Object containing i3 tree structure.
*/
/**
* Loads i3 workspaces and tree using the i3 module.
*
* @private
* @function loadI3Data
* @return {I3Data} I3 data.
*/
const loadI3Data = () =>
new Promise((resolve, reject) => {
i3.workspaces((err, workspaces) => {
if (err) reject(err)
i3.tree((err, tree) => {
if (err) reject(err)
resolve({ workspaces, tree })
})
})
})
/**
* Gets the window ID of the current focused window.
*
* @private
* @function getFocusedWindow
* @param {I3Data-tree} tree - Object containing i3 tree structure.
* @return {number} ID of focused window.
*/
const getFocusedWindow = tree =>
dp.extract(tree).reduce((a, v) => {
if (v.path.slice(-1)[0] === 'window' && v.value) {
if (dp.get(tree, v.path.slice(0, -1)).focused === true) {
return v.value
}
}
return a
})
const forwardHist = []
const reverseHist = []
test(`Shifts focus`, async () => {
try {
let activity = false
i3.on('window', data => {
if (data.change === 'focus' && data.container) {
activity = true
forwardHist.push(data.container.window)
expect(forwardHist.length).toBe(2)
expect(forwardHist[0] === forwardHist[1]).toBe(false)
i3.removeAllListeners()
}
})
const i3Data = await loadI3Data()
forwardHist.push(getFocusedWindow(i3Data.tree))
spawn('i3-cycle-focus')
await new Promise(resolve => setTimeout(resolve, 2000))
expect(activity).toBe(true)
} catch (err) {
throw err
}
})
test(
`Shifts focus reversely`,
async () => {
try {
let activity = false
i3.on('window', data => {
if (data.change === 'focus' && data.container) {
activity = true
reverseHist.push(data.container.window)
expect(reverseHist.length).toBe(2)
expect(reverseHist[0] === reverseHist[1]).toBe(false)
expect(forwardHist[0]).toBe(reverseHist[1])
expect(reverseHist[0]).toBe(forwardHist[1])
i3.removeAllListeners()
}
})
const i3Data = await loadI3Data()
reverseHist.push(getFocusedWindow(i3Data.tree))
spawn('i3-cycle-focus', ['--reverse'])
await new Promise(resolve => setTimeout(resolve, 2000))
expect(activity).toBe(true)
} catch (err) {
throw err
}
}
)
afterAll(() => setTimeout(() => process.exit(), 1000))