Xdotool replacement on wayland

Hi there! I’m a bit worried about the upcoming switch of KDE Neon to wayland, especially in respect of xdotool which will not work anymore.

I have a (heavly used) bash script bound to keyboard shortcuts with which I can resize and move the currently active window to a default position (left, right center) and/or size. To achieve this, I’m using these commands:

xdotool getactivewindow
xdotool getwindowname
xdotool getwindowgeometry
xdotool windowmove

I know about wtype and ydotool that provide some features of xdotool. But unfortunately, those I’m searching for are missing.

Is there a KWin API I could use for my tasks? Appreciate your help!

Yep, major pain. All my desktops are looooaded with xdotool stuff in one form or another. And ydotool doesn’t even come close to xdotool in terms of functionality.
I’ve managed to change a few ( very few) that use a kwin shortcut.
In that case you can find out which ones you might need by:
qdbus org.kde.kglobalaccel /component/kwin org.kde.kglobalaccel.Component.shortcutNames

You can then set the command as:
qdbus org.kde.kglobalaccel /component/kwin org.kde.kglobalaccel.Component.invokeShortcut “shortcutName”

It’s not much but hey… I do hope to find tons more of those qdbus commands cause they’ll be needed. A lot of them probably in some script.

Depending on what you want to do, there may be shortcuts already available under Shortcuts > KWin in System Settings, though you may have already checked for those.

What you could potentially do, is create a KWin script (or multiple scripts if appropriate) and as part of the script(s), register shortcuts that would show up under Shortcuts > KWin. An example of a script that registers a shortcut is in another post I made for a script to clear the “Demands Attention” status of all windows.

There is documentation for the KWin Scripting API, though keep in mind that it’s for Plasma 5 and I believe the API will have changes with Plasma 6, however I’m not aware of there being documentation available for that yet.

1 Like

I posted the invokes for kwin. A handy tool to have is the qdbusviewer. But all in all, aslong as there’s no valuable wayland alternative to xdotool it’ll be a killer.
Qdbusviewer should be in the repos ( some as the qttools5-dev) and it’s also available as flatpak https://flathub.org/apps/io.qt.qdbusviewer. Here’s some info on how to use it: https://freeaptitude.altervista.org/articles/playing-with-dbus-and-kde-applications-part-1.html

Actually kwin has a dbus interface that let you upload and run a kwin script js file then fetch its result:

Check how plasma-interactiveconsole --kwin uses it:

I believe you can replicate quite a lot of xdotool’s function by constructing a kwin script on-the-fly, then use this dbus interface to load it, run, get results, then unload. A kdotool for KDE.

Myeah, for the life of me…
I fiddled with a script and…went to the pub. I’ve no idea how to convert it to some reasonable “kdotool” script. Drove me bonkers.

active_window_id=$(xdotool getactivewindow)
for window_id in $(xdotool search --onlyvisible “.*”)
do
if [ $window_id != $active_window_id ]
then
xdotool windowminimize $window_id
xdotool getactivewindow windowmove 25% 25%

fi

done

Simply put, the script minimizes all windows except the active one and places it dead center on the screen.

Now, I know there’s a different way to ( more or less) do it. But to say that you can simply switch a script…
Anyway, I launched that script from a custom cornerbinding and it was pretty cool. I hope to find a reasonable solution in the future.

Ps: the other solution was to use the Only One Active widget and make a desktop app/script, a switch in the kwinrc.

Thank you so much for your input!

I managed to do it with KWin Scripting. It’s fairly easy, but I had a hell of time getting the shortcuts registered (bug report). It’s no replacement for xdotool, but I could do everything I wanted, and the execution is even less bug prone than xdotool.

In case you’re interested. The following script does the trick for me. Just put it in a main.js as described in the tutorial and install it with kpackagetool5 --type=KWin/Script -i ~/path/to/script/dir/.

// ***** collect relevant data *****
function getData () {
  const client = workspace.activeClient;
  const clientGeometry = client.frameGeometry;
  const workspaceArea = workspace.clientArea(KWin.MaximizeArea, client.screen, client.desktop);

  return {
    client,
    clientGeometry,
    workspaceArea,
  };
}

// ***** position the active window *****
//   pos = string
function position (pos) {
  const { client, clientGeometry, workspaceArea } = getData();

  let x;
  if (pos === "center") {
    x = Math.round(workspaceArea.width / 2 - clientGeometry.width / 2);
  } else if (pos === "left") {
    x = 0;
  } else if (pos === "right") {
    x = workspaceArea.width - clientGeometry.width;
  }

  client.frameGeometry = {
    x,
    y: clientGeometry.y,
    width: clientGeometry.width,
    height: clientGeometry.height,
  };
}

// ***** resize the active window ****
//   width = integer
function resize (width) {
  const { client, clientGeometry, workspaceArea } = getData();

  const x = Math.round(workspaceArea.width / 2 - width / 2);
  const y = workspaceArea.y;
  const height = workspaceArea.height;

  if (clientGeometry.height !== height ||
      clientGeometry.width !== width ||
      clientGeometry.y !== y ||
      clientGeometry.x !== x) {
    client.frameGeometry = {
      x,
      y,
      width,
      height,
    };
  }
}

// ***** shortcuts to resize the active window *****
const widths = [ 800, 1100, 1250, 1450 ];
registerShortcut(`MoRe Resize ${widths[0]}`, `MoRe Resize ${widths[0]}`, "Meta+Alt+B", () => resize(widths[0]));
registerShortcut(`MoRe Resize ${widths[1]}`, `MoRe Resize ${widths[1]}`, "Meta+Ctrl+B", () => resize(widths[1]));
registerShortcut(`MoRe Resize ${widths[2]}`, `MoRe Resize ${widths[2]}`, "Meta+B", () => resize(widths[2]));
registerShortcut(`MoRe Resize ${widths[3]}`, `MoRe Resize ${widths[3]}`, "Meta+Ctrl+Alt+B", () => resize(widths[3]));

// ***** shortcuts to position the active window *****
registerShortcut("MoRe Position Center", "MoRe Position Center", "Meta+Alt+Up", () => position("center"));
registerShortcut("MoRe Position Left", "MoRe Position Left", "Meta+Alt+Left", () => position("left"));
registerShortcut("MoRe Position Right", "MoRe Position Right", "Meta+Alt+Right", () => position("right"));

Yeah, replicating xdotool would be quite a hell of work. For a start:
(Tested on Plasma 6. Need some adjustments on Plasma 5)

kdotool-getactivewindow

#!/bin/bash
script=$(mktemp)
echo "print(\"$script\",workspace.activeWindow.internalId);" > $script
script_id=$(qdbus org.kde.KWin /Scripting loadScript $script)
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
qdbus org.kde.KWin /Scripting/Script$script_id run
journalctl --since "$timestamp" --user -u plasma-kwin_wayland.service --output cat -g "js: $script" | cut -d ' ' -f 3
qdbus org.kde.KWin /Scripting/Script$script_id stop

kdotool-windowminimize

#!/bin/bash
script=$(mktemp)
sed "s/\$WINDOWID/$1/" $(dirname $0)/windowminimize.js > $script
script_id=$(qdbus org.kde.KWin /Scripting loadScript $script)
qdbus org.kde.KWin /Scripting/Script$script_id run
qdbus org.kde.KWin /Scripting/Script$script_id stop

windowminimize.js

var windows = workspace.windowList();

for (var i = 0; i < windows.length; ++i) {
    var w = windows[i];
    if (w.internalId == "$WINDOWID") {
        w.minimized = true;
        break;
    }
}

test:

$ windowid=$(./kdotool-getactivewindow)
$ ./kdotool-windowminimize $windowid
4 Likes