== Dev Week -- How to write a compiz plugin -- smspillaz -- Tue, Mar 1st, 2011 == {{{#!irc [15:59] smspillaz, also known as Sam Spilsbury graciously agreed to give a session, and he'll talk to you about bringing more bling to compiz by writing spiffy plugins [15:59] smspillaz, the stage is yours! [15:59] horray! [16:00] ok, awesome, so yes, here I am, I am the maintainer of the Compiz 0.9 series, i.e the version of compiz that will be used in Ubuntu 11.04 and Unity as well as most upcoming distros [16:00] first things' first, because I overprepare for everything, I made some documents that you all might find useful (and me too!) when following me [16:00] so the first one is the internal architecture of compiz [16:00] which can be found here: [16:00] http://smspillaz.ucc.asn.au/unity/compiz_arch/compiz_arch.pdf === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - https://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Questions in #ubuntu-classroom-chat || Event: Ubuntu Developer Week - Current Session: How to write a compiz plugin - Instructors: smspillaz [16:01] and the second is a 7 page guide on writing compiz plugins, which is half-finished, since I've been quite busy this week [16:01] Logs for this session will be available at http://irclogs.ubuntu.com/2011/03/01/%23ubuntu-classroom.html following the conclusion of the session. [16:01] http://smspillaz.ucc.asn.au/unity/compiz_arch/writing.odt [16:01] so feel free to grab those :) [16:01] alrighty, so starting from the beginning [16:02] first of all, a quick introduction to how compiz works, and what exactly its function is [16:02] and then how to get and compile a development version of compiz, or just develop for it on ubuntu [16:02] and then I'll do a quick walkthrough of the basic structure of a plugin and some commonly done things in plugins [16:03] ok, so as for how compiz works, and what exactly it does [16:04] to take a page from the compiz wiki, compiz is basically what we call a "Compositing Window Manager" in X land, which means that as well as managing windows on screen, it also is in charge of drawing those windows. This technology came about in around 2005ish when XComposite came about === m4n1sh is now known as manish [16:04] so this means that in terms of your scope of compiz plugins, you can affect how windows are actually managed, affect how they are drawn, and affect how the entire scene is drawn [16:05] along with its plugin interface, this makes it pretty much possible to do any bling that you want === manish is now known as manish__ [16:05] so, now moving on to exactly how to set up a development environment [16:05] luckly, ubuntu makes this pretty easy [16:06] just sudo apt-get install compiz-dev and compiz-plugins-main-dev and you're done [16:06] (On maverick, it is compiz-fusion-plugins-main) === manish__ is now known as manish_ [16:06] but, if you want to do development in a little more, I usually suggest having a local build of compiz handy. [16:07] Luckily, most of that is relatively straightforward these days, since there are scripts to do that [16:07] http://git.compiz.org/~soreau/scripts/ <- like these fantastic ones [16:08] just clone the git repo there and with the script you'll have a full working installation from source. [16:08] This helps if, for example, some plugin does something similar to what you want to do, and you can start hacking on that plugin first. [16:08] so, a quick question I've recieved [16:09] QUESTION: What about Ubuntu 11.04 [16:09] so, this tutorial here, as I probably should have explained will be for Ubuntu 11.04 [16:09] since compiz was re-written in between the 0.8 and 0.9 series (maverick and natty) [16:09] however, those git scripts about should also work on maverick [16:10] Now on to the nitty gritty of writing your first plugin [16:10] Compiz plugins are written in C++. We don't really use very many advanced features of the languages or external libraries that heavily, so if you have a basic knowledge of that, then you should be good [16:11] we only use boost to a small extent, glib more recently and sigc [16:11] CMake is also used for building rather than automake [16:11] to answer another quick question [16:11] QUESTION:Is it possible to do some type of local build using the ubuntu packaging and pbuilder or something like that so that you can remove it easily? [16:12] So, the answer to this is is that you can indeed clone lp:compiz , however, this is the packaging branch of compiz core, and doesn't get all the modules [16:12] in addition, I'm not sure about how to make it build and install locally [16:12] (plus, adding extra stuff to it is a pain since you need to rebuild the whole thing all the time using bzr bd) [16:12] so I'd suggest you stick to the scripts or to the master source [16:13] (A link to the build tutorial is in the documents I linked to earlier) [16:13] so now back to writing plugins [16:13] so the basic knowledge, you will need once again, is C++, boost or CMake [16:14] note that OpenGL isn't really a strict requirement, since a lot of the graphics functions that are commonly used are abstracted through the API of compiz [16:14] it's just that if you want to go down the road of bling-bling, then OpenGL is nice [16:14] ok, so now for creating your plugin project [16:14] so to start off with, say you want to create a plugin called "moveleft" which, every time a window is opened, if moves every other window to the left [16:15] so, your directory structure would be [16:15] moveleft [16:15] - [16:15] -> src [16:15] ---> moveleft.cpp [16:15] ---> moveleft.h [16:15] -> moveleft.xml.in [16:15] -> CMakeLists.txt === qwebirc46515 is now known as geco2 [16:16] now to explain each component of that [16:16] basically, the .cpp and .h files are the implementation of your plugin, eg the loadable object code [16:16] the .xml.in describes information about your plugin for CCSM and also its options [16:16] and the CMakeLists.txt is the buildsystem [16:16] now, lucky for plugin authors, the buildsystem for compiz plugins is insanely easy [16:17] all you need is three lines [16:17] find_package (Compiz REQUIRED) [16:17] include (CompizPlugin) [16:17] compiz_plugin (moveleft PLUGINDEPS foo bar PKGDEPS baz) [16:17] so now on to what each line dos [16:17] *does [16:18] the first basically finds compiz with pkgconfig [16:18] the second imports all the plugin buildsystem [16:18] the third sets up a new "compiz plugin" project in CMake with the name "moveleft" depending on other plugins "foo" and "bar" and depending on external packages "baz" [16:18] so to disect that further [16:19] now that you've called your plugin "moveleft" you must stick with that shortname everywhere [16:19] so for example, the .xml.in must be named "moveleft" [16:19] However in the src/ tree, you don't need to worry about that [16:19] another interesting thing is that you can depend on other plugins [16:20] compiz 0.9 was designed in mind with inter-plugin dependency with each plugin providing objects to other plugins [16:20] so, a common example of this is the fact htat the opengl and composite bits of compiz are in fact plugins themselves [16:20] so most plugins that need to access this functionality will set those as their PLUGINDEPS [16:21] now on to the metadata file [16:21] I've explained this in more detail in the documents I linked to earlier [16:21] but basically, here you need to define your plugin name too [16:21] so [16:21] [16:22] the useBcop is crucial if you want your life to be easier with autogenerated options code [16:22] you can also define some options there [16:22] but we will get back to that later [16:22] so close all the tags and save it [16:22] next, you need to define the classes of your plugin [16:23] so the way things work in compiz is that each plugin is responsible for one or both of two things [16:23] 1) attaching their own information and hooks to existing objects [16:23] 2) creating their own objects [16:23] the vast majority of plugins only do 1) [16:23] and they usually hook two very important existing object [16:23] CompWindow and CompScreen [16:24] so, starting with the second [16:24] compiz runs per X-screen [16:24] so, this means that CompScreen is kind of like the "master class" where everything relating to everything going on on screen happens [16:24] CompWindow is a representation of some managed X11 window [16:25] (and believe me, there are a lot of them, see xwininfo -root -tree for how much there is to manage) [16:25] now each plugin works by attaching their own structures to those core objects [16:25] so if you defined a MoveLeftScreen as attaching to CompScreen [16:25] there would be one MoveLeftScreen per CompScreen [16:26] same thing with MoveLeftWindow [16:26] this allows you to store data and functions on those core structures [16:26] so, for moveleft, you might want to track if you've moved a window already, and if it has moved since you've moved it [16:26] so you would store that information in MoveLeftWindow [16:27] the actual attaching [16:27] ok [16:27] so the actual attaching is done by a special class called PluginClassHandler [16:27] it's basically a template class that sets up some special indexes in the base object and attaches your class to it [16:28] so that, whenever you want to get your own data for the base object, you just call MoveLeftWindow::get (CompWindow *) [16:28] so in constructing your MoveLeftWindow, you'll get something like: [16:28] class MoveLeftWindow : [16:28] public PluginClassHandler , [16:28] public WindowInterface [16:28] { [16:28] } [16:28] etc [16:28] (See the handout) [16:29] the WindowInterface is for interfacable functions and will be explained in a bi [16:29] so now that you've got your MoveLeftScreen and MoveLeftWindow defined as classes attaching to the relevant core structures [16:29] there are a few final things to do [16:29] the first is to define a VTable for your plugin [16:30] this tells core how to load and what "attaching classes" to initialize when objects are created [16:30] we have templates for the most common usecases [16:30] so, you'd have [16:30] class MoveLeftPluginVTable : [16:30] public CompPlugin::VTableForScreenAndWindow [16:31] { [16:31] public [16:31] bool init (); [16:31] } [16:31] the only thing yout need to put in your init function for now is a core ABI version check [16:31] eg [16:31] if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION)) return true; else return false; [16:32] and finally, a extern C object definition for core to look up the VTable on plugin load [16:32] and like all good things, we have a macro for that [16:33] COMPIZ_PLUGIN_20090315 (MoveLeftPluginVTable, moveleft); [16:33] note the use of "moveleft" again [16:33] that is *crucial* [16:33] since the library is going to be named "moveleft" and core will be loading a plugin called "moveleft" [16:33] so its going to be looking for the right symbol [16:33] As a quick aside, I've been asked to explain what CCSM is [16:34] in fact, I should probably explain some of the other compiz modules [16:34] so "compizconfig" == our library to handle configuration and desktop integration amongst different settings backends (eg ini, dconf, gconf, kconfig4) [16:35] "ccsm" == "compizconfig-settings-manager" which is a tool to adjust all the settings in compiz (seriously, there are tons) [16:35] "core" == the core executable of compiz, where the base window management logic and API comes from [16:35] ok, so back to plugins [16:36] once you have checked off implementing your MoveLeftScreen, MoveLeftWindow, MoveLeftPluginVTable, COMPIZ_PLUGIN_20090315 etc [16:36] and #included [16:36] you now have an installing plugin! [16:36] however, it doesn't really do all that much [16:36] so lets get on to something more interesting [16:37] what about being able to "plug-in" to things that core or other plugins do [16:37] ah, QUESTION: how can we make sure there isn't another plugin called "moveleft" [16:37] well, the simple answer to that is, you don't [16:37] there aren't too many compiz plugins around, but if a plugin with the same name is already loaded then yours fails to load [16:38] ok, back to being able to plugin-in to things [16:38] so, as I was saying a bit before, we have another special thing in compiz called "wrapable interfaces" [16:39] these are basically just call chains of function objects in doubly linked lists [16:40] when you thing you are calling some core function that was implemented in an "interfacable way" you are actually just calling a function which increments the linked list counter by 1 and calls the next function in the list [16:40] the convention is that at the end of each of the plugin's function calls, they will call the same core function again, which increases the function counter, calls the next function and so forth [16:41] this allows us to, on each core function call, call lots of other functions at the same time [16:41] So the first thing you would have seen in the MoveLeftWindow definition was that we inherited a class called "WindowInterface" [16:42] this is the "Interface class" to all of these Intefacable functions [16:42] so if you have a look in core/include/window.h, you will see all the functions that plugins can hook [16:42] by inheriting this class you are said to be "implementing" this interface [16:43] by default, the functions in WindowInterface all do nothing, but that is because they are all virtual! [16:43] so to change that, you overload one of the functions [16:43] for example, void moveNotify (int, int, bool); is a function that tells us when a window has moved and by how much [16:44] so if we implement a function like that in our MoveLeftWindow class which inherits WindowInterface, we will have implemented that [16:44] next, in the MoveLeftwindow constructor, we have a call to WindowInterface::setHandler (window); [16:45] what this does, is set the handler of the interface class that we inherited to the base class that owns the function [16:45] so that now whenever we call the base class function [16:45] our function will be called first [16:46] then functions from the previously loaded plugin [16:46] etc etc [16:46] until you get to the main function in the class [16:46] ok, another question! [16:46] QUESTION: so, can i write a separate MoveLeft function, embed in Window.h, and then call n override it whenever n wherever i want? === dholbach_ is now known as dholbach [16:47] So, I'm not too sure what is meant by this, but if you mean adding a new wrapable function to core, I am afraid that is out of the scope of this tutorial [16:47] plugins should generally only overload the functions in the interface classes [16:48] anyways, interfaces are explained a bit more in the document I posted [16:48] next up: options [16:48] so say you've interfaced CompWindow::windowNotify from WindowInterface [16:48] so now whenever someone else calls window->windowNotify (), MoveLeftWindow::windowNotify is also called [16:49] and you've handled the CompWindowNotifyMap there to loop over all windows in screen->windows () (eg, loop over an std::list) and call w->move (x, y, immediate) to move them to the left [16:50] but now, you want to play with an option whereby it actually moves windows to the right instead of the left! [16:50] so you need to go back into your moveleft.xml.in and add some information about this option [16:50] so [16:51] that defines the option, with the default value of false [16:51] now, in your MoveLeftScreen make sure you inherit MoveleftOptions (note the small case l) to get access to autogenerated code from this option [16:52] ok, so now here is where it gets funky [16:52] that options code is owned by MoveLeftScreen [16:52] so you need to MoveLeftScreen *mls = MoveLeftScreen::get (screen) in your ::windowNotify [16:53] then, you want to read the option to see what it says [16:53] so for "right" that would be [16:53] mls->optionGetRight () [16:53] and it returns true or false depending on the option value [16:53] now, here is how the generation code works [16:53] first you have optionGet, which is prepended to call getters [16:54] then you have "Right" right came from "right" in the xml file [16:54] so the parser replaces underscores with CamelCase [16:54] so if you had right_foo [16:54] it would be come [16:54] optionGetRightFoo () [16:54] Ok, cool so now you've read the option [16:54] so with some simple math [16:54] and that skeleton [16:55] you should be able to make everything go to the left when a new window is mapped [16:55] or at your choosing [16:55] go to the right [16:55] I'll open the floor to questions [16:55] ok [16:55] QUESTION: since you mentioned compiz 0.9 written for Ubuntu, do you use any distro specific API implementations which may break if I write a plugin for, say, Arch? [16:55] Good question [16:55] There are 5 minutes remaining in the current session. [16:55] so, compiz 0.9 was written independently, and has only been started to be used for unity until early this year [16:56] so it is still an upstream project [16:56] and as such I keep the API consistent between all version [16:56] there used to be an API inconsistency with the glib mainloop stuff, but that has been upstreamed [16:56] any other questions? [16:57] (I might just paste again those documents I'm referring to in case you missed them) [16:57] http://smspillaz.ucc.asn.au/unity/compiz_arch/compiz_arch.pdf [16:57] and [16:57] http://smspillaz.ucc.asn.au/unity/compiz_arch/writing.odt [16:57] QUESTION: What happened to the compiz mindcontrol? [16:57] I love it [16:58] that project would be cool, except that the developer with that idea didn't cobble together the funds to buy a headset for that [16:58] plus it requires shaving your head [16:58] which I would never do since I have awesome hair [16:58] any more questions before I wrap it up? [16:59] ok, well it is 16:59, I should probably wrap it up [16:59] thanks for attending! [16:59] now go write some awesome plugins! }}}