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> http://smspillaz.ucc.asn.au/unity/compiz_arch/compiz_arch.pdf
9 === 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
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 http://irclogs.ubuntu.com/2011/03/01/%23ubuntu-classroom.html following the conclusion of the session.
12 [16:01] <smspillaz> http://smspillaz.ucc.asn.au/unity/compiz_arch/writing.odt
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> http://git.compiz.org/~soreau/scripts/ <- 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> -> moveleft.xml.in
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 .xml.in 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 .xml.in 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 moveleft.xml.in 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> http://smspillaz.ucc.asn.au/unity/compiz_arch/compiz_arch.pdf
239 [16:57] <smspillaz> and
240 [16:57] <smspillaz> http://smspillaz.ucc.asn.au/unity/compiz_arch/writing.odt
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!