Dev Week -- How to write a compiz plugin -- smspillaz -- Tue, Mar 1st, 2011

   1 [15:59] <dholbach> 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
   2 [15:59] <dholbach> smspillaz, the stage is yours!
   3 [15:59] <smspillaz> horray!
   4 [16:00] <smspillaz> 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
   5 [16:00] <smspillaz> first things' first, because I overprepare for everything, I made some documents that you all might find useful (and me too!) when following me
   6 [16:00] <smspillaz> so the first one is the internal architecture of compiz
   7 [16:00] <smspillaz> which can be found here:
   8 [16:00] <smspillaz>
   9 === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - || Support in #ubuntu || Upcoming Schedule: || Questions in #ubuntu-classroom-chat || Event: Ubuntu Developer Week - Current Session: How to write a compiz plugin  - Instructors: smspillaz
  10 [16:01] <smspillaz> and the second is a 7 page guide on writing compiz plugins, which is half-finished, since I've been quite busy this week
  11 [16:01] <ClassBot> Logs for this session will be available at following the conclusion of the session.
  12 [16:01] <smspillaz>
  13 [16:01] <smspillaz> so feel free to grab those :)
  14 [16:01] <smspillaz> alrighty, so starting from the beginning
  15 [16:02] <smspillaz> first of all, a quick introduction to how compiz works, and what exactly its function is
  16 [16:02] <smspillaz> and then how to get and compile a development version of compiz, or just develop for it on ubuntu
  17 [16:02] <smspillaz> and then I'll do a quick walkthrough of the basic structure of a plugin and some commonly done things in plugins
  18 [16:03] <smspillaz> ok, so as for how compiz works, and what exactly it does
  19 [16:04] <smspillaz> 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
  20 === m4n1sh is now known as manish
  21 [16:04] <smspillaz> 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
  22 [16:05] <smspillaz> along with its plugin interface, this makes it pretty much possible to do any bling that you want
  23 === manish is now known as manish__
  24 [16:05] <smspillaz> so, now moving on to exactly how to set up a development environment
  25 [16:05] <smspillaz> luckly, ubuntu makes this pretty easy
  26 [16:06] <smspillaz> just sudo apt-get install compiz-dev and compiz-plugins-main-dev and you're done
  27 [16:06] <smspillaz> (On maverick, it is compiz-fusion-plugins-main)
  28 === manish__ is now known as manish_
  29 [16:06] <smspillaz> but, if you want to do development in a little more, I usually suggest having a local build of compiz handy.
  30 [16:07] <smspillaz> Luckily, most of that is relatively straightforward these days, since there are scripts to do that
  31 [16:07] <smspillaz> <- like these fantastic ones
  32 [16:08] <smspillaz> just clone the git repo there and with the script you'll have a full working installation from source.
  33 [16:08] <smspillaz> 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.
  34 [16:08] <smspillaz> so, a quick question I've recieved
  35 [16:09] <smspillaz> QUESTION: What about Ubuntu 11.04
  36 [16:09] <smspillaz> so, this tutorial here, as I probably should have explained will be for Ubuntu 11.04
  37 [16:09] <smspillaz> since compiz was re-written in between the 0.8 and 0.9 series (maverick and natty)
  38 [16:09] <smspillaz> however, those git scripts about should also work on maverick
  39 [16:10] <smspillaz> Now on to the nitty gritty of writing your first plugin
  40 [16:10] <smspillaz> 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
  41 [16:11] <smspillaz> we only use boost to a small extent, glib more recently and sigc
  42 [16:11] <smspillaz> CMake is also used for building rather than automake
  43 [16:11] <smspillaz> to answer another quick question
  44 [16:11] <smspillaz> 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?
  45 [16:12] <smspillaz> 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
  46 [16:12] <smspillaz> in addition, I'm not sure about how to make it build and install locally
  47 [16:12] <smspillaz> (plus, adding extra stuff to it is a pain since you need to rebuild the whole thing all the time using bzr bd)
  48 [16:12] <smspillaz> so I'd suggest you stick to the scripts or to the master source
  49 [16:13] <smspillaz> (A link to the build tutorial is in the documents I linked to earlier)
  50 [16:13] <smspillaz> so now back to writing plugins
  51 [16:13] <smspillaz> so the basic knowledge, you will need once again, is C++, boost or CMake
  52 [16:14] <smspillaz> 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
  53 [16:14] <smspillaz> it's just that if you want to go down the road of bling-bling, then OpenGL is nice
  54 [16:14] <smspillaz> ok, so now for creating your plugin project
  55 [16:14] <smspillaz> 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
  56 [16:15] <smspillaz> so, your directory structure would be
  57 [16:15] <smspillaz> moveleft
  58 [16:15] <smspillaz> -
  59 [16:15] <smspillaz> -> src
  60 [16:15] <smspillaz> ---> moveleft.cpp
  61 [16:15] <smspillaz> ---> moveleft.h
  62 [16:15] <smspillaz> ->
  63 [16:15] <smspillaz> -> CMakeLists.txt
  64 === qwebirc46515 is now known as geco2
  65 [16:16] <smspillaz> now to explain each component of that
  66 [16:16] <smspillaz> basically, the .cpp and .h files are the implementation of your plugin, eg the loadable object code
  67 [16:16] <smspillaz> the describes information about your plugin for CCSM and also its options
  68 [16:16] <smspillaz> and the CMakeLists.txt is the buildsystem
  69 [16:16] <smspillaz> now, lucky for plugin authors, the buildsystem for compiz plugins is insanely easy
  70 [16:17] <smspillaz> all you need is three lines
  71 [16:17] <smspillaz> find_package (Compiz REQUIRED)
  72 [16:17] <smspillaz> include (CompizPlugin)
  73 [16:17] <smspillaz> compiz_plugin (moveleft PLUGINDEPS foo bar PKGDEPS baz)
  74 [16:17] <smspillaz> so now on to what each line dos
  75 [16:17] <smspillaz> *does
  76 [16:18] <smspillaz> the first basically finds compiz with pkgconfig
  77 [16:18] <smspillaz> the second imports all the plugin buildsystem
  78 [16:18] <smspillaz> 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"
  79 [16:18] <smspillaz> so to disect that further
  80 [16:19] <smspillaz> now that you've called your plugin "moveleft" you must stick with that shortname everywhere
  81 [16:19] <smspillaz> so for example, the must be named "moveleft"
  82 [16:19] <smspillaz>  However in the src/ tree, you don't need to worry about that
  83 [16:19] <smspillaz> another interesting thing is that you can depend on other plugins
  84 [16:20] <smspillaz> compiz 0.9 was designed in mind with inter-plugin dependency with each plugin providing objects to other plugins
  85 [16:20] <smspillaz> so, a common example of this is the fact htat the opengl and composite bits of compiz are in fact plugins themselves
  86 [16:20] <smspillaz> so most plugins that need to access this functionality will set those as their PLUGINDEPS
  87 [16:21] <smspillaz> now on to the metadata file
  88 [16:21] <smspillaz> I've explained this in more detail in the documents I linked to earlier
  89 [16:21] <smspillaz> but basically, here you need to define your plugin name too
  90 [16:21] <smspillaz> so <compiz>
  91 [16:21] <smspillaz>  <plugin name="myplugin" useBcop="true">
  92 [16:22] <smspillaz> the useBcop is crucial if you want your life to be easier with autogenerated options code
  93 [16:22] <smspillaz> you can also define some options there
  94 [16:22] <smspillaz> but we will get back to that later
  95 [16:22] <smspillaz> so close all the tags and save it
  96 [16:22] <smspillaz> next, you need to define the classes of your plugin
  97 [16:23] <smspillaz> so the way things work in compiz is that each plugin is responsible for one or both of two things
  98 [16:23] <smspillaz> 1) attaching their own information and hooks to existing objects
  99 [16:23] <smspillaz> 2) creating their own objects
 100 [16:23] <smspillaz> the vast majority of plugins only do 1)
 101 [16:23] <smspillaz> and they usually hook two very important existing object
 102 [16:23] <smspillaz> CompWindow and CompScreen
 103 [16:24] <smspillaz> so, starting with the second
 104 [16:24] <smspillaz> compiz runs per X-screen
 105 [16:24] <smspillaz> so, this means that CompScreen is kind of like the "master class" where everything relating to everything going on on screen happens
 106 [16:24] <smspillaz> CompWindow is a representation of some managed X11 window
 107 [16:25] <smspillaz> (and believe me, there are a lot of them, see xwininfo -root -tree for how much there is to manage)
 108 [16:25] <smspillaz> now each plugin works by attaching their own structures to those core objects
 109 [16:25] <smspillaz> so if you defined a MoveLeftScreen as attaching to CompScreen
 110 [16:25] <smspillaz> there would be one MoveLeftScreen per CompScreen
 111 [16:26] <smspillaz> same thing with MoveLeftWindow
 112 [16:26] <smspillaz> this allows you to store data and functions on those core structures
 113 [16:26] <smspillaz> 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
 114 [16:26] <smspillaz> so you would store that information in MoveLeftWindow
 115 [16:27] <smspillaz> the actual attaching
 116 [16:27] <smspillaz> ok
 117 [16:27] <smspillaz> so the actual attaching is done by a special class called PluginClassHandler
 118 [16:27] <smspillaz> it's basically a template class that sets up some special indexes in the base object and attaches your class to it
 119 [16:28] <smspillaz> so that, whenever you want to get your own data for the base object, you just call MoveLeftWindow::get (CompWindow *)
 120 [16:28] <smspillaz> so in constructing your MoveLeftWindow, you'll get something like:
 121 [16:28] <smspillaz> class MoveLeftWindow :
 122 [16:28] <smspillaz>     public PluginClassHandler <MoveLeftWindow, CompWindow>,
 123 [16:28] <smspillaz>     public WindowInterface
 124 [16:28] <smspillaz> {
 125 [16:28] <smspillaz> }
 126 [16:28] <smspillaz> etc
 127 [16:28] <smspillaz> (See the handout)
 128 [16:29] <smspillaz> the WindowInterface is for interfacable functions and will be explained in a bi
 129 [16:29] <smspillaz> so now that you've got your MoveLeftScreen and MoveLeftWindow defined as classes attaching to the relevant core structures
 130 [16:29] <smspillaz> there are a few final things to do
 131 [16:29] <smspillaz> the first is to define a VTable for your plugin
 132 [16:30] <smspillaz> this tells core how to load and what "attaching classes" to initialize when objects are created
 133 [16:30] <smspillaz> we have templates for the most common usecases
 134 [16:30] <smspillaz> so, you'd have
 135 [16:30] <smspillaz> class MoveLeftPluginVTable :
 136 [16:30] <smspillaz>     public CompPlugin::VTableForScreenAndWindow <MoveLeftScreen, MoveLeftWindow>
 137 [16:31] <smspillaz> {
 138 [16:31] <smspillaz>     public
 139 [16:31] <smspillaz>         bool init ();
 140 [16:31] <smspillaz> }
 141 [16:31] <smspillaz> the only thing yout need to put in your init function for now is a core ABI version check
 142 [16:31] <smspillaz> eg
 143 [16:31] <smspillaz> if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION)) return true; else return false;
 144 [16:32] <smspillaz> and finally, a extern C object definition for core to look up the VTable on plugin load
 145 [16:32] <smspillaz> and like all good things, we have a macro for that
 146 [16:33] <smspillaz> COMPIZ_PLUGIN_20090315 (MoveLeftPluginVTable, moveleft);
 147 [16:33] <smspillaz> note the use of "moveleft" again
 148 [16:33] <smspillaz> that is *crucial*
 149 [16:33] <smspillaz> since the library is going to be named "moveleft" and core will be loading a plugin called "moveleft"
 150 [16:33] <smspillaz> so its going to be looking for the right symbol
 151 [16:33] <smspillaz> As a quick aside, I've been asked to explain what CCSM is
 152 [16:34] <smspillaz> in fact, I should probably explain some of the other compiz modules
 153 [16:34] <smspillaz> so "compizconfig" == our library to handle configuration and desktop integration amongst different settings backends (eg ini, dconf, gconf, kconfig4)
 154 [16:35] <smspillaz> "ccsm" == "compizconfig-settings-manager" which is a tool to adjust all the settings in compiz (seriously, there are tons)
 155 [16:35] <smspillaz> "core" == the core executable of compiz, where the base window management logic and API comes from
 156 [16:35] <smspillaz> ok, so back to plugins
 157 [16:36] <smspillaz> once you have checked off implementing your MoveLeftScreen, MoveLeftWindow, MoveLeftPluginVTable, COMPIZ_PLUGIN_20090315 etc
 158 [16:36] <smspillaz> and #included <core/core.h>
 159 [16:36] <smspillaz> you now have an installing plugin!
 160 [16:36] <smspillaz> however, it doesn't really do all that much
 161 [16:36] <smspillaz> so lets get on to something more interesting
 162 [16:37] <smspillaz> what about being able to "plug-in" to things that core or other plugins do
 163 [16:37] <smspillaz> ah, QUESTION: how can we make sure there isn't another plugin called "moveleft"
 164 [16:37] <smspillaz> well, the simple answer to that is, you don't
 165 [16:37] <smspillaz> there aren't too many compiz plugins around, but if a plugin with the same name is already loaded then yours fails to load
 166 [16:38] <smspillaz> ok, back to being able to plugin-in to things
 167 [16:38] <smspillaz> so, as I was saying a bit before, we have another special thing in compiz called "wrapable interfaces"
 168 [16:39] <smspillaz> these are basically just call chains of function objects in doubly linked lists
 169 [16:40] <smspillaz> 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
 170 [16:40] <smspillaz> 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
 171 [16:41] <smspillaz> this allows us to, on each core function call, call lots of other functions at the same time
 172 [16:41] <smspillaz> So the first thing you would have seen in the MoveLeftWindow definition was that we inherited a class called "WindowInterface"
 173 [16:42] <smspillaz> this is the "Interface class" to all of these Intefacable functions
 174 [16:42] <smspillaz> so if you have a look in core/include/window.h, you will see all the functions that plugins can hook
 175 [16:42] <smspillaz> by inheriting this class you are said to be "implementing" this interface
 176 [16:43] <smspillaz> by default, the functions in WindowInterface all do nothing, but that is because they are all virtual!
 177 [16:43] <smspillaz> so to change that, you overload one of the functions
 178 [16:43] <smspillaz> for example, void moveNotify (int, int, bool); is a function that tells us when a window has moved and by how much
 179 [16:44] <smspillaz> so if we implement a function like that in our MoveLeftWindow class which inherits WindowInterface, we will have implemented that
 180 [16:44] <smspillaz> next, in the MoveLeftwindow constructor, we have a call to WindowInterface::setHandler (window);
 181 [16:45] <smspillaz> what this does, is set the handler of the interface class that we inherited to the base class that owns the function
 182 [16:45] <smspillaz> so that now whenever we call the base class function
 183 [16:45] <smspillaz> our function will be called first
 184 [16:46] <smspillaz> then functions from the previously loaded plugin
 185 [16:46] <smspillaz> etc etc
 186 [16:46] <smspillaz> until you get to the main function in the class
 187 [16:46] <smspillaz> ok, another question!
 188 [16:46] <smspillaz> QUESTION: so, can i write a separate MoveLeft function,  embed in Window.h, and then call n override it whenever n  wherever i want?
 189 === dholbach_ is now known as dholbach
 190 [16:47] <smspillaz> 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
 191 [16:47] <smspillaz> plugins should generally only overload the functions in the interface classes
 192 [16:48] <smspillaz> anyways, interfaces are explained a bit more in the document I posted
 193 [16:48] <smspillaz> next up: options
 194 [16:48] <smspillaz> so say you've interfaced CompWindow::windowNotify from WindowInterface
 195 [16:48] <smspillaz> so now whenever someone else calls window->windowNotify (), MoveLeftWindow::windowNotify is also called
 196 [16:49] <smspillaz> 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
 197 [16:50] <smspillaz> but now, you want to play with an option whereby it actually moves windows to the right instead of the left!
 198 [16:50] <smspillaz> so you need to go back into your and add some information about this option
 199 [16:50] <smspillaz> so <option name="right" type="bool">
 200 [16:50] <smspillaz>   <_short>Move right</_short>
 201 [16:50] <smspillaz>   <_long>Do the wrong thing </_long>
 202 [16:51] <ClassBot> There are 10 minutes remaining in the current session.
 203 [16:51] <smspillaz>  <default>false</default>
 204 [16:51] <smspillaz> </option>
 205 [16:51] <smspillaz> that defines the option, with the default value of false
 206 [16:51] <smspillaz> now, in your MoveLeftScreen make sure you inherit MoveleftOptions (note the small case l) to get access to autogenerated code from this option
 207 [16:52] <smspillaz> ok, so now here is where it gets funky
 208 [16:52] <smspillaz> that options code is owned by MoveLeftScreen
 209 [16:52] <smspillaz> so you need to MoveLeftScreen *mls = MoveLeftScreen::get (screen) in your ::windowNotify
 210 [16:53] <smspillaz> then, you want to read the option to see what it says
 211 [16:53] <smspillaz> so for "right" that would be
 212 [16:53] <smspillaz> mls->optionGetRight ()
 213 [16:53] <smspillaz> and it returns true or false depending on the option value
 214 [16:53] <smspillaz> now, here is how the generation code works
 215 [16:53] <smspillaz> first you have optionGet, which is prepended to call getters
 216 [16:54] <smspillaz> then you have "Right" right came from "right" in the xml file
 217 [16:54] <smspillaz> so the parser replaces underscores with CamelCase
 218 [16:54] <smspillaz> so if you had right_foo
 219 [16:54] <smspillaz> it would be come
 220 [16:54] <smspillaz> optionGetRightFoo ()
 221 [16:54] <smspillaz> Ok, cool so now you've read the option
 222 [16:54] <smspillaz> so with some simple math
 223 [16:54] <smspillaz> and that skeleton
 224 [16:55] <smspillaz> you should be able to make everything go to the left when a new window is mapped
 225 [16:55] <smspillaz> or at your choosing
 226 [16:55] <smspillaz> go to the right
 227 [16:55] <smspillaz> I'll open the floor to questions
 228 [16:55] <smspillaz> ok
 229 [16:55] <smspillaz> 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?
 230 [16:55] <smspillaz> Good question
 231 [16:55] <ClassBot> There are 5 minutes remaining in the current session.
 232 [16:55] <smspillaz> so, compiz 0.9 was written independently, and has only been started to be used for unity until early this year
 233 [16:56] <smspillaz> so it is still an upstream project
 234 [16:56] <smspillaz> and as such I keep the API consistent between all version
 235 [16:56] <smspillaz> there used to be an API inconsistency with the glib mainloop stuff, but that has been upstreamed
 236 [16:56] <smspillaz> any other questions?
 237 [16:57] <smspillaz> (I might just paste again those documents I'm referring to in case you missed them)
 238 [16:57] <smspillaz>
 239 [16:57] <smspillaz> and
 240 [16:57] <smspillaz>
 241 [16:57] <smspillaz> QUESTION: What happened to the compiz mindcontrol?
 242 [16:57] <smspillaz> I love it
 243 [16:58] <smspillaz> that project would be cool, except that the developer with that idea didn't cobble together the funds to buy a headset for that
 244 [16:58] <smspillaz> plus it requires shaving your head
 245 [16:58] <smspillaz> which I would never do since I have awesome hair
 246 [16:58] <smspillaz> any more questions before I wrap it up?
 247 [16:59] <smspillaz> ok, well it is 16:59, I should probably wrap it up
 248 [16:59] <smspillaz> thanks for attending!
 249 [16:59] <smspillaz> now go write some awesome plugins!

MeetingLogs/devweek1103/HowToWriteACompizPlugin (last edited 2011-03-02 05:50:18 by nigelbabu)