[Command] Command=" /* This adds support for KDE, Gnome, Sway and Hyprland Wayland sessions. For Sway and Hyprland, this requires: - `ydotool` utility to send copy/paste shortcuts to applications - `grim` for taking screenshot - `slurp` for selecting screenshot area - `gnome-screenshot` for screenshots in Gnome - `spectacle` for screenshots in non-Gnome environments For KDE, this requires Spectacle for taking screenshots. Getting current window title is not supported in KDE. Global shortcut commands can be triggered with: copyq triggerGlobalShortcut {COMMAND_NAME} On Gnome, clipboard monitor is executed as X11 app using XWayland. Links: - https://github.com/ReimuNotMoe/ydotool - https://github.com/emersion/grim - https://github.com/emersion/slurp */ function isSway() { return env('SWAYSOCK').length != 0 } function isHyprland() { return env('HYPRLAND_CMD').length != 0 } function isKde() { return env('XDG_CURRENT_DESKTOP') == 'KDE' } function isGnome() { return str(env('XAUTHORITY')).includes('mutter-Xwayland') } function run() { const p = execute.apply(this, arguments) if (!p) { throw 'Failed to start ' + arguments[0] } if (p.exit_code !== 0) { throw 'Failed command ' + arguments[0] + ': ' + str(p.stderr) } return p.stdout } function swayGetTree() { const tree = run('swaymsg', '--raw', '--type', 'get_tree') return JSON.parse(str(tree)) } function swayFindFocused(tree) { const nodes = tree['nodes'].concat(tree['floating_nodes']) for (const node of nodes) { if (node['focused']) return node const focusedNode = swayFindFocused(node) if (focusedNode) return focusedNode } return undefined } function hyprlandFindFocused() { const window = run('hyprctl', '-j', 'activewindow') return JSON.parse(str(window)) } function kdeFocused() { return str(run('kdotool', 'getactivewindow', 'getwindowname')) } function sendShortcut(...shortcut) { sleep(100) run('ydotool', 'key', ...shortcut) } global.currentWindowTitle = function() { if (isSway()) { const tree = swayGetTree() const focusedNode = swayFindFocused(tree) return focusedNode ? focusedNode['name'] : '' } if (isHyprland()) { const focusedWindow = hyprlandFindFocused() return focusedWindow ? focusedWindow['title'] : '' } if (isKde()) { return kdeFocused() } return '' } global.paste = function() { sendShortcut('42:1', '110:1', '110:0', '42:0') } const copy_ = global.copy global.copy = function() { if (arguments.length == 0) { sendShortcut('29:1', '46:1', '46:0', '29:0') } else { copy_.apply(this, arguments) } } global.focusPrevious = function() { hide() } function overrideToRunInXWayland(fn) { const oldFn = global[fn] global[fn] = function() { if (isGnome() && env('QT_QPA_PLATFORM') != 'xcb') { serverLog(`Starting XWayland ${fn}`) setEnv('QT_QPA_PLATFORM', 'xcb') execute('copyq', '--clipboard-access', fn) serverLog(`Stopping XWayland ${fn}`) return } return oldFn() } } overrideToRunInXWayland('monitorClipboard') overrideToRunInXWayland('synchronizeFromSelection') overrideToRunInXWayland('synchronizeToSelection') const onClipboardChanged_ = onClipboardChanged onClipboardChanged = function() { const title = currentWindowTitle() if (title) setData(mimeWindowTitle, title) onClipboardChanged_() } function gnomeScreenshot(arg) { const tmpFile = TemporaryFile() tmpFile.setFileTemplate(Dir().temp().absoluteFilePath('copyq-XXXXXX.png')) tmpFile.openWriteOnly() tmpFile.close() run( 'gnome-screenshot', arg || '--delay=0', '--include-pointer', '--file', tmpFile.fileName(), ) const file = File(tmpFile.fileName()) file.openReadOnly() const stdout = File('/dev/stdout') stdout.openWriteOnly() stdout.write(file.readAll()) } screenshot = function(format, screenName) { if (isSway() || isHyprland()) return run('grim', '-t', format || 'png', '-') if (isGnome()) return gnomeScreenshot() return run( 'spectacle', '--background', '--nonotify', '--pointer', '--output', '/dev/stdout', ) } screenshotSelect = function(format, screenName) { if (isSway() || isHyprland()) { let geometry = run('slurp') geometry = str(geometry).trim() return run('grim', '-c', '-g', geometry, '-t', format || 'png', '-') } if (isGnome()) return gnomeScreenshot('--area') return run( 'spectacle', '--background', '--nonotify', '--pointer', '--region', '--output', '/dev/stdout', ) } global.triggerGlobalShortcut = function(commandName) { const cmds = commands() for (const cmd of cmds) { if (cmd.isGlobalShortcut && cmd.enable && cmd.name == commandName) return action(cmd.cmd) } throw 'Failed to find enabled global command with given name' }" Icon= IsScript=true Name=Wayland Support