summaryrefslogtreecommitdiff
path: root/PluginDevelopment
blob: fef924621947597f52fed5cb108014da4f73ba33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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.
 - ?