-
-
Save rbreaves/257c3edfa301786e66e964d7ac036269 to your computer and use it in GitHub Desktop.
# Single Command, runs 2 calls to gdbus to get the currently active Window from Gnome 3.x | |
# Escaped so you can copy and paste into terminal directly | |
gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval global.get_window_actors\(\)[`gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval global.get_window_actors\(\).findIndex\(a\=\>a.meta_window.has_focus\(\)===true\) | cut -d"'" -f 2`].get_meta_window\(\).get_wm_class\(\) | cut -d'"' -f 2 | |
# Unescaped version, will not run | |
# Broken down into 2 commands. | |
# Call to Gnome to get the array location of the active Application | |
gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m \ | |
org.gnome.Shell.Eval global.get_window_actors().findIndex(a=>a.meta_window.has_focus()===true) \ | |
| cut -d"'" -f 2 | |
# Replace the array number 2 with the one from the previous command and you will get the App Name of the actively focused Window | |
gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval \ | |
global.get_window_actors()[2].get_meta_window().get_wm_class() \ | |
| cut -d'"' -f 2 |
Thanks @rbreaves happy to try to make sense of our ecosystem and perhaps nudge towards stronger community and interop.
@dceee The gnome 41 solution seems to be to write a gnome shell extension that adds the needed interface. Here's an example that makes it possible to move windows (something that could also only be achieved with Eval previously):
https://gist.github.com/tuberry/dc651e69d9b7044359d25f1493ee0b39
@canadaduane Thanks, this is exactly what I did. I moved all calls against the Eval interface to an extension that acts like a proxy for my python scripts.
@dceee Is that extension something you can share? I'd be interested in seeing how you did it.
Based on the example code above, I replaced the methods with the ones I need:
const { Gio } = imports.gi;
const MR_DBUS_IFACE = `
<node>
<interface name="org.gnome.Shell.Extensions.Windows">
<method name="List">
<arg type="s" direction="out" name="win"/>
</method>
<method name="Focus">
<arg type="u" direction="in" name="winid"/>
</method>
<method name="Minimize">
<arg type="u" direction="in" name="winid"/>
</method>
</interface>
</node>`;
class Extension {
enable() {
this._dbus = Gio.DBusExportedObject.wrapJSObject(MR_DBUS_IFACE, this);
this._dbus.export(Gio.DBus.session, '/org/gnome/Shell/Extensions/Windows');
}
disable() {
this._dbus.flush();
this._dbus.unexport();
delete this._dbus;
}
List() {
let win = global.get_window_actors()
.map(a => a.meta_window)
.map(w => ({ class: w.get_wm_class(), pid: w.get_pid(), id: w.get_id(), max: w.get_maximized(), focus: w.has_focus()}));
return JSON.stringify(win);
}
Focus(winid) {
let win = global.get_window_actors().map(a=>a.meta_window).find(w=>w.get_id()==winid);
if (win) {
win.activate(0);
} else {
throw new Error('Not found');
}
}
Minimize(winid) {
let win = global.get_window_actors().map(a=>a.meta_window).find(w=>w.get_id()==winid);
if (win) {
win.minimize(0);
} else {
throw new Error('Not found');
}
}
}
To get a list of windows, you can now place the DBUS call against /org/gnome/Shell/Extensions/Windows:
gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell/Extensions/Windows --method org.gnome.Shell.Extensions.Windows.List
Thanks!
Btw, as for the extension code above, is missing the "init" function, so the code needs to have this at the end
function init() {
return new Extension();
}
Now with that out of the way, wouldn't the best way to implement this on GNOME, be:
Implement a gdbus "client" inside xkeysnail, this would have to be async I guess, then, we could have our extension, but what we could try to do (I don't know is GNOME has an API or an event for this), but we can detect if the user changed windows, after that, we can emit a dbus event (This example if from the command line, but I could make one programatically) and since we are connected to the system/user bus via our client, we could just "listen" for this signal, the moment the signal is emited, we could just grab the window that is marked as "active" and return that as the window title
Maybe I’d have to look into it more. Xremap has solved it apparently for gnome & wayland. Kinto could be redone to use xremap or we can yes patch xkeysnail & do a PR on it.
@rbreaves Xremap seem to do a similar dbus Eval call according to the README
busctl --user call org.gnome.Shell /org/gnome/Shell org.gnome.Shell Eval s 'global.get_window_actors().map(a => a.get_meta_window().get_wm_class());'
@rbreaves I like how xremap looks and I think it is a little bit more modern than xkeysnail, however, I don't know how much effort it would take to rewrite or redo Kinto to use xremap.
@DiegoMagdaleno probably not as much as you think. The syntax for it looks quite similar to a yaml config format I’ve been wanting to make part of either Kinto or xkeysnail actually. That would have been a simple translation layer though.
Just wanted to highlight some cool work that rvaiya is doing in keyd here: https://github.com/rvaiya/keyd/blob/master/scripts/keyd-application-mapper
In this latest beta, keyd
now offers its hot-swappable keyboard layouts and layers with application focus conveyed by that python script via a socket to the keyd
daemon. Works on X, Wayland+Sway, and Wayland+Gnome.
That is very interesting, I've not used sway any.. and sadly my distro of choice, Ubuntu Budgie, does not have access to gnome-extensions even though it works with gnome-shell still.
@canadaduane That solution does look nice! I hope that dceee is wrong about the latest update in Gnome 41 or that something new & useful is replacing it - although I am not sure that the Gnome team or Wayland are paying attention just yet.
I did appreciate your article and the visibility it got - I believe that will likely help us out a lot because I imagine most devs just aren't thinking of the use cases that involves key remappers & other accessibility related apps needing to know the active window name.