summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PluginDevelopment155
1 files changed, 155 insertions, 0 deletions
diff --git a/PluginDevelopment b/PluginDevelopment
new file mode 100644
index 0000000..fef9246
--- /dev/null
+++ b/PluginDevelopment
@@ -0,0 +1,155 @@
+Plugin development for Compiz Fusion
+
+0. About this document
+1. Plugin initialization and finalization
+2. Dealing with options
+3. Hooking into core
+4. Debugging
+5. Pitfalls
+
+0. About this document
+======================
+This is meant to be a brief introduction to development. It applies to both
+new plugins, and working on existing plugins.
+
+When talking about Compiz development, we talk about plugins and core. Core
+is the part of Compiz that are running and not plugins. In other words, it's
+Compiz.
+
+1. Plugin initialization and finalization
+=========================================
+Plugin initialization starts with a dlOpen, and the only data that core
+reads is the vTable, and as such, the vTable is essential.
+
+The vTable is almost almost found at the bottom of all plugins, and the
+content of the vTable changes from time to time. One thing remains the same,
+however: The vTable contains the unique plugin-name and function pointers to
+various initialization and finalization functions.
+
+Generally you never modify the vTable in a plugin except to track core
+changes. When dealing with the vTable, bcop also comes into play. BCop is a
+code generator used in most of Compiz Fusion, but not Compiz. It allows
+generation of much of the option handling code, based on metadata. More on
+bcop later.
+
+The vTable currently only contains getMetadata, which you should let bcop
+handle, so just leave it as 0 (it simply returns a pointer to the metadata
+structure, which bcop generates for us). The initPlugin and finiPlugin
+functions are, respectively, the first and last functions to be executed by
+the plugin. They should allocate your displayPrivateIndex, which is the only
+variable that has to be stored in the plugin in file scope. It is an index
+we will use later, to store private data in CompDisplay. You may wonder why
+it's not allocated during display initialization; That's because there may
+be multiple Displays, and we only want one index. The same applies to all
+indexes, as you will see shortly.
+
+Next in the vTable we have initObject and finiObject. Previously, we had
+{init|fini}{Screen|Display|Window} instead. Now we have {init|fini}Object
+which returns a list of these functions. This was implemented in
+anticipation of a more advanced object framework.
+
+initDisplay is called first, and you want to allocate your PluginDisplay
+structure here if you need one, and wrap anything that takes place in
+display-context. This generally means anything that has to do with input.
+Also, allocate a screenPrivateIndex, and store it. Since Compiz, in theory,
+can have multiple displays (though the author has no idea why or how), it is
+considered good practice to store the screenPracticeIndex in your
+PluginDisplay variable.
+
+initScreen is called for _each screen_ after this. It should do set up
+the PluginScreen structure and wrap anything related to output. You should
+probably read up on Multihead to understand how Compiz may or may not have
+multiple CompScreen structures on mutlihead setups. The rule of thumb is
+that you always do output in CompScreen context, and that if you have to
+work on CompScreen in display-context, you have to iterate over all the
+possible screens.
+
+Lastly, we have initWindow. This is run both when you load your plugin and
+when new windows are created. You setup up window-private data here, similar
+to initScreen.
+
+On unload, finiWindow will be called for all windows, followed by finiScreen
+for all screens, then finiDisplay, then finiPlugin. On unload, you only need
+to worry about disabeling all timeouts (that's why you store their handlers,
+right?), UNWRAP anything you WRAP'ed in init*, then free () data and free
+indexes you allocated in the same init*.
+
+2. Dealing with options
+=======================
+There are two ways to deal with options. It's the Compiz way, and the Compiz
+Fusion way. In Compiz Fusion, we use bcop (Beryl/Compiz Option
+generator, or something along those lines). It's from the days of Beryl, and
+takes care of all the boring parts of option handling. To understand what it
+does, we'll first look at how Compiz handles options.
+
+Options are stored in metadata files, typically xml. I won't go into details
+on how CompizConfig and Compiz handles it, but the essential part is that
+the data needs to be available from BOTH C and the XML-data. In Compiz, that
+means you have the .xml files which contains descriptions used in gconf and
+ccsm. In the C code, you have a metadata structure which essentially
+duplicates this data.
+
+You also have to have functions set up to handle when an option change, to
+store it in your PluginFoo-variable. As you can imagine, this is mostly
+identical in all plugins. What bcop does is take your xml data and generate
+the metadata structure from it, then it generates get/set functions and
+modifies the relevant bindings to core.
+
+This means a few things:
+1. No get/set code or option code at all in your plugin.
+2. No direct access to the option-values, or "default" functions that are
+called when an option change.
+
+bcop solves the lack of direct access by generating functions that take the
+form of "pluginGetOptionName (foo)", where foo is the core structure of the
+relevant scope. Convention in metadata is to use underscore for option
+names, while Compiz uses camelCase, so bcop translates this for you. So
+some_option in Display context can be accessed by calling
+pluginGetSomeOption (d), assuming d is a CompDisplay.
+
+To watch for an option change, you can use pluginSetOptionNotify (foo, bar),
+where foo is the context-variable (CompDisplay, for instance) and bar is a
+function that will be executed.
+
+3. Hooking into core
+====================
+So now you can start your plugin and even read, set and watch for option
+changes. To make it actually do something meaningful, you need to hook into
+core. For that, you need two macros, WRAP and UNWRAP. These are essentially
+stack push/pop operators. The core structures has a number of wrappable
+functions, for instance to paint an output device. To modify how an output
+device (ie: your monitor) is drawn you tell core that whenever it's about
+to update the output, run OUR function. This means you have to make sure you
+actually run the core-version afterwards. In reality, plugins stack this on
+top of each other, so when you wrap a function, you're most likely saying
+"execute my function before this other plugins function". This is why load
+order is essential when creating effects that persist through different
+plugins, like the fade plugin which looks at the intended opacity for a
+window paint and modifies it so instead of an instant change, we get a
+smooth gradual change, regardless of wether it was an other plugin that
+modified the opacity, or core.
+
+Unfortunately, there are far too many wrappables to explain them all here.
+You will have to look at what other plugins do, or ask around. Hopefully,
+more documentation will arrive.
+
+4. Debugging
+============
+Debugging a compiz plugin is hard. You will be restarting compiz a lot.
+Using gdb on compiz means that you STOP your window manager, and input and
+output along with it. If you really want to use GDB, you need to do it
+remotely.
+
+Luckily, we have a crashhandler plugin that runs gdb for you if compiz
+crashes. If you are running compiz in a terminal (like you should), this
+should give you what you need to debug crashes. Other than that, it's mostly
+printf.
+
+Some might mention valgrind, but if you need valgrind, you're in deep water.
+
+5. Pitfalls
+===========
+ - Mixing up WRAP/UNWRAP order, specially on unload.
+ - Mixing up CompScreen and CompDisplay context. Read Documentation/Multihead.
+ - Not unloading properly.
+ - ?