It’s been a very long time since I made a post, mostly because my college started this week which meant I lost around a week in traveling, registration and the whole process of ‘setting in’. Either ways, since my last post, a lot of changes in KWin. Actually, a very important change has been made in the entire structure of how clients are generated and currently things are looking very neat and much more systematic.
Clients and toplevels
In KWin, the Client object inherits from Toplevel. Toplevel is the class for all clients, whereas Client handles managed clients. Now, I could just directly wrap Client and not really care about Toplevel, but doing these things separately enables me to reuse the code for Toplevel for unmanaged clients as well if I plan to. I have always been doing these two things separately, but my previous method was pretty tacky.
Previously: Earlier, the SWrapper::Client object and SWrapper::Toplevel classes were just standalone classes that both used to generate QScriptValues. So, inside the SWrapper::Client::generate function:
- Firstly, it used to call the SWrapper::Toplevel::generate function and obtain a toplevel scriptobject, let’s call it so_toplevel
- Then it generates a QScriptValue for the KWin::Client in question.
- Both the above functions use a pointer to the KWin::Client* object and KWin::Toplevel* respectively, so that process is actually pretty simple.
- Then both these objects are merged using a helper function defined in scripting/meta.cpp
The merging took some time and there was no reason why I shouldn’t just generate a toplevel object first and then build the client methods on it. The problem is that, since I am generating the script object from a QObject, and I cannot give a previous object to build on. Whichever way it is, it was ugly and things had to change.
Now: The things now work like this:
- SWrapper::Client now inherits from SWrapper::Toplevel
- SWrapper::Client first generates an object and then SWrapper::Toplevel appends to it. This is the exact reverse of how inheritance works, but there is a reason to it. Read Rationale below
- The final object is then returned.
Rationale: First the QObject must be wrapped using QScriptEngine::newQObject and then more methods can be added. Hence, when using a qscriptvalue_cast, the object converted from is the object that the QScriptValue can be converted to. So, if a convert a MyObject* to QScriptValue, then I can ONLY cast it back to MyObject*. So for a QScriptValue generated from KWin::Client* can be converted only KWin::Client*. Now, I must ensure that KWin::Toplevel* is not at all aware of what object inherits it, it must work only on a toplevel. Which is why, if I generate the ScriptValue from KWin::Toplevel*, I cannot convert it to KWin::Client*. However, KWin::Client* can be converted to KWin::Toplevel* since it is an inherited object.
I’ve finished adding support for Window tabbing from within scripting and almost all methods from clientgroup.cpp have been implemented with the same parameter sequence other than for a few changes. A list of all the the available methods till now are:
# (m) clientgroup.ClientGroup (ctor) : Constructor for a new ClientGroup object. Must pass a client object, otherwise returns an invalid scriptvalue.
# (m) clientgroup.add : Add a client to a client group. Expects 1 parameter.
# (m) workspace.clientGroups : Returns an array of clientGroups currently present in the workspace.
# (m) clientgroup.remove : Removes the client specified by the parameter which is either a client object or an indexof the client in the clientlist.
# (m) clientgroup.clients : Returns an array of clients which are members of the clientgroup.
# (m) client.clientGroup : Returns the clientgroup a given client belongs to, or an undefined scriptvalue otherwise.
# (m) clientgroup.contains : Accepts a client parameter, and returns true the client is contained within the clientgroup
# (m) clientgroup.indexOf : Returns the index of the parameter 'client' within the clientgroup
# (m) clientgroup.move : Move a client within the group. Accepts move(client, client), move(index, index), move(index, client), move(client, index). All calls except move(client, client) are eventually mapped to move(index, index) using indexOf(client)
# (m) clientgroup.removeAll : Removes all clients from a group and assigns them individual groups.
# (m) clientgroup.closeAll : Closes all the clients in a group.
# (m) clientgroup.minSize : Returns the minimum size cumulative of all the clients in the clientgroup.
# (m) clientgroup.maxSize : Returns the maximum size cumulative of all the clients in the clientgroup.
# (m) clientgroup.visible : Returns the currently visible client in the clientgroup.
A ClientGroup must be initialized with an existing client as a parameter. If the client already belongs to a ClientGroup, that ClientGroup is wrapped, otherwise a new one is made. However, if the new ClientGroup is made with a parameter ‘detach’:
var x = new ClientGroup(myClient, /*detach*/ 1);
then, myClient is removed from the ClientGroup and then added to a new ClientGroup of it’s own.
Also, clientgroup.cpp supports two methods to rearrange tabs: move(client, client) and move(index, index). From scripting one can use:
move(client, index) ; move(client, client) ; move(index, client) ; move(index, index)
Events for the same are still to be added.
I wrote a small script to arrange mathematica better when it loads: BetterMathematica.kwinscripting
It is a simple script, which arranges the basic math input palette on your left and sets up the ‘Classroom Assistant’, ‘Writing Assistant’ and all in nice little tabs on the right and the rest of the window takes up the rest of the space.
Here are a few screenshots of how things look before and after the script:
After the Script:
Eventually, Mathematica looks a lot better and is much easier to work with now. Other than that, client.windowInfo now returns a lot more data including windowClassClass, windowClassName, windowRole and so on. Do refer to SCRIPTING/IMPLIST for a list of all implemented events.
Also, ClientGroup marks the first time we’re creating an instantiable object i.e. objects that can be created. Do leave your comments and opinion, as this time I’ve made something which would actually be useful as compared to KWin|PONG, which was well, far from useful