comparison HACKING @ 16177:d88f0f320c9b

merge of '07fc4db9a3c2c12596e0354b8e7959aa847f966b' and '2e6d324c725b3e6a2c803589bca3f0ac9b9790bf'
author Richard Laager <rlaager@wiktel.com>
date Mon, 16 Apr 2007 00:44:33 +0000
parents 9f793948154a c95641c98e47
children d73ee2690376
comparison
equal deleted inserted replaced
15938:2e3eba412412 16177:d88f0f320c9b
1 Lots of this is pretty grossly out of date... 1 For information on hacking on Pidgin, Finch, or libpurple, see:
2 Some of it might still be useful. For coding style, your 2 http://developer.pidgin.im
3 best bet is to browse through some of the files in src and
4 emulate what you see there.
5 --Mark
6
7
8 The majority of the below was written by Eric Warmenhoven way back in
9 antiquity. I have taken the liberty of attempting to PARTIALLY update
10 it. I still think its helpful, but use it at your own risk.
11 --Luke
12
13
14 A lot of people have tried to hack gaim, but haven't been able to because
15 the code is just so horrid. Well, the code isn't getting better anytime
16 soon (I hate GNU indent), so to help all you would-be hackers help out
17 gaim, here's a brief tutorial on how gaim works. I'll quickly describe
18 the logical flow of things, then what you'll find in each of the source
19 files. As an added bonus, I'll try and describe as best I can how multiple
20 connections and multiple protocols work. Depending on how much I want to
21 avoid my final tomorrow I may even describe other parts of gaim that I
22 particularly want to brag about. Hopefully that's enough to get most of
23 you going.
24
25 If you don't know how event-driven programs work, stop right now. Gaim
26 uses GTK+'s main loop (actually GLib's but I won't talk about how GTK
27 works) and uses GLib functions for timeouts and socket notification. If
28 you don't know GTK+ you should go learn that first.
29
30 If you're going to hack gaim, PLEASE, PLEASE PLEASE PLEASE send patches
31 against the absolute latest CVS. I get really annoyed when I get patches
32 against the last released version, especially since I don't usually have
33 a copy of it on my computer, and gaim tends to change a lot between
34 versions. (I sometimes get annoyed when they're against CVS from 3 days
35 ago, but can't complain because it's usually my fault that I haven't
36 looked at the patch yet.) To get gaim from CVS (if you haven't already),
37 run the following commands:
38
39 $ export CVSROOT=:pserver:anonymous@cvs.sourceforge.net:/cvsroot/gaim
40 $ cvs login (hit enter as the password)
41 $ cvs co gaim (you'll see it getting all of the files)
42 $ cd gaim
43 $ ./autogen.sh
44
45 You'll now have your normal gaim tree with ./configure and all (which
46 ./autogen.sh takes the liberty of running for you). (If you want to make
47 your life really simple, learn how CVS works. CVS is your friend.) To make
48 a patch, just edit the files right there in that tree (don't bother with
49 two trees, or even two copies of the same file). Then when you're ready to
50 make your patch, simply run 'cvs diff -u >my.patch' and post it on
51 sf.net/projects/gaim in the patches section.
52
53 Some Documentation is available on the Gaim api if you run the command
54 $make docs
55 after running ./configure (or ./autogen.sh). You will need doxygen and
56 graphiz dot to generate these docs.
57
58 CODING STYLE
59 ============
60
61 Coding styles are like assholes, everyone has one and no one likes anyone
62 elses. This is mine and if you want me to accept a patch from you without
63 getting annoyed you'll follow this coding style. :)
64
65 It would probably just be easier for me to include CodingStyle from the
66 linux kernel source.
67
68 Tab indents. I *HATE* 2-space indents, and I strongly dislike 8-space
69 indents. Use a tab character. I'm likely to refuse a patch if it has
70 2-space indents.
71
72 K&R style for braces. Braces always go on the same line as the if, etc.
73 that they're associated with; the only exception is functions. Braces
74 for else statements should have both braces on the same line as the else
75 (i.e. "} else {").
76
77 No functionOrVariableNamesLikeThis. Save it for Java. Underscores are your
78 friend. "tmp" is an excellent variable name. Hungarian style will not be
79 tolerated. Go back to Microsoft.
80
81 I have a 105-char wide Eterm. Deal with it.
82
83 NO goto. I'm very likely to refuse a patch if it makes use of goto. If you
84 feel the need to use goto, you need to rethink your design and flow.
85
86
87 PROGRAM FLOW (just about every function name from here on down is wrong.
88 ============ but many of the ideas still apply under different names.)
89
90 Before gaim does anything you can see, it initializes itself, which is
91 mostly just reading ~/.gaim/*.xml (handled by the functions in prefs.[ch])
92 and parsing command-line options. It then draws the login window by
93 calling show_login, and waits for input.
94
95 At the login window, when "Accounts" is clicked, account_editor() is
96 called. This then displays all of the users and various information
97 about them. (Don't ask about what happens when "Sign On" is called. It's
98 quite hackish. The only reason the login window is there anymore is to
99 make it more palatable to people so used to WinAIM that they can't accept
100 anything else.)
101
102 When the "Sign on/off" button is clicked, serv_login is passed the
103 username and the password for the account. If the password length is
104 zero (the password field is a character array rather than pointer so it
105 will not be NULL) then the Signon callback will prompt for the password
106 before calling serv_login. serv_login then signs in the user using the
107 appropriate protocol.
108
109 After you're signed in, Gaim draws the buddy list by calling
110 show_buddy_list. Assuming the user has a buddy list (all buddy list
111 functions are controlled by list.c; when you sign on do_import is called
112 and that loads the locally saved list), the protocol calls
113 gaim_prpl_got functions, which set the information in the appropriate
114 struct buddy and then passes it off to set_buddy.
115
116 set_buddy is responsible for a lot of stuff, but most of it is done
117 implicitly. It's responsible for the sounds (which is just a call to
118 play_sound), but the biggest thing it does is call new_group_show and
119 new_buddy_show if necessary. There's only one group_show per group name,
120 even between connections, and only one buddy_show per group_show per
121 buddy name, even between connections. (If that's not confusing enough,
122 wait until I really start describing how the buddy list works.)
123
124 New connections happen the exact same way as described above. Each
125 gaim_account can have one gaim_connection associated with it. gaim_account
126 and gaim_connection both have a protocol field. This is kind of confusing:
127 gaim, except for the account editor screen and when the user signs on,
128 ignores the user's protocl field, and only uses the connection's protocol
129 field. You can change the connection's protocol field once it's created
130 and been assigned a PRPL to use to change certain behavior (Oscar does
131 this because it handles both AIM and ICQ). I'll talk about the
132 gaim_connection struct more later.
133
134 When the user opens a new conversation window, new_conversation is called.
135 That's easy enough. If there isn't a conversation with the person already
136 open (checked by calling find_conversation), show_conv is called to
137 create the new window. All sorts of neat things happen there, but it's
138 mostly drawing the window. show_conv is the best place to edit the UI.
139
140 That's pretty much it for the quick tutorial. I know it wasn't much but
141 it's enough to get you started. Make sure you know GTK+ before you get too
142 involved. Most of the back-end stuff is pretty basic; most of gaim is GTK+.
143
144
145 SOURCE FILES (this should probly be utterly removed)
146 ============
147
148 about.c:
149 Not much to say here, just a few basic functions.
150
151 account.[ch]:
152 This controls the GaimAccount struct, which stores information
153 on each account a user registers with gaim. Usernames, pass-
154 words, user info, alias, user specific options, and everything
155 else controlled from within the account editor (and then some)
156 are handled via this code.
157
158 accountopt.[ch]:
159 Api and implemenation for account options. I'm not precisely
160 sure how this meshes with account.[ch]
161
162 away.c:
163 This takes care of most of the away stuff: setting the away message
164 (do_away_message); coming back (do_im_back); drawing the away window;
165 etc. Away messages work really oddly due to multiple connections and
166 multiple protocols; I think there are really only two or three people
167 who know how it works and I don't think any of us know why it works
168 that way.
169
170 blist.[ch]:
171 This takes care of the buddy list backend, the blist.xml file,
172 importing old buddy list files, and related things like
173 finding buddies and groups. buddies, contacts, and groups
174 are controlled from these files.
175
176 buddy_chat.c:
177 This takes care of the buddy chat stuff. This used to be a lot bigger
178 until the chat and IM windows got merged in the code. Now it mostly
179 just takes care of chat-specific stuff, like ignoring people and
180 keeping track of who's in the room. This is also where the chat window
181 is created.
182
183 conversation.c:
184 This is where most of the functions dealing with the IM and chat windows
185 are hidden. It tries to abstract things as much as possible, but doesn't
186 do a very good job. This is also where things like "Enter sends" and
187 "Ctrl-{B/I/U/S}" options get carried out (look for send_callback). The
188 chat and IM toolbar (with the B/I/U/S buttons) are both built from
189 the same function, build_conv_toolbar.
190
191 core.c:
192 This is the start of what will become the main() for gaim-core.
193
194 gtkdialogs.c:
195 A massive file with a lot of little utility functions. This is where all
196 of those little dialog windows are created. Things like the warn dialog
197 and the add buddy dialog are here. Not all of the dialogs in gaim are in
198 this file, though. But most of them are. This is also where do_import
199 is housed, to import buddy lists. (The actual buddy list parsing code
200 is in util.c for winaim lists and buddy.c for gaim's own lists.)
201
202 gtkimhtml.c:
203 This is gaim's HTML widget. It replaced the old widget, GtkHtml (which
204 was different than GNOME's GtkHTML). It's self-contained (it doesn't
205 use any of gaim's code) and is actually a separate project from gaim
206 (but is maintained by Eric).
207
208 idle.c:
209 This file used to be entirely #if 0'd out of existance. However, thanks
210 to some very generous people who submitted patches, this takes care of
211 reporting idle time (imagine that). It's a pretty straight-forward file.
212 This also takes care of the auto-away stuff.
213
214 gtkmain.c:
215 This is where the main() function is. It takes care of a lot of the
216 initialization stuff, and showing the buddy list or account editor.
217
218 md5.c:
219 Oscar, Yahoo, and MSN all require md5 hashing, so better to put it in
220 the core than have the same thing in three different places.
221
222 module.c:
223 This contains all of the plugin code, except for the UI. This is what
224 actually loads the plugins, makes sure they're valid, has the code for
225 setting up plugin event handlers, and contains the plugin_event method
226 that gaim calls on events.
227
228 prefs.c:
229 Read the documentation on this file. This handles the backend
230 side of prefs.
231
232 proxy.c:
233 Adam (of libfaim glory) got bored one day and rewrote this file, so
234 now everything actually works. The main function is proxy_connect,
235 which figures out which proxy you want to use (if you want to use one
236 at all) and passes off the data to the appropriate function. This file
237 should be pretty straight-forward.
238 Except I STRONGLY suspect that time has broken this file.
239
240 prpl.c:
241 This file is what lets gaim dynamically load protocols, sort of. All
242 of the actual dlopen(), dlsym() stuff is in module.c. But this contains
243 all of the functions that the protocol plugin needs to call, and manages
244 all of the protocols. It's a pretty simple file actually.
245
246 server.c:
247 This is where all of the differentiation between the different protocols
248 is done. Nearly everything that's network related goes through here
249 at one point or another. This has good things like serv_send_im. Most of
250 it should be pretty self-explanatory.
251
252 sound.c:
253 The main function in this file is play_sound, which plays one of 8
254 (maybe 9?) sounds based on preferences. All that the rest of the code
255 should have to do is call play_sound(BUDDY_ARRIVE), for example, and
256 this file will take care of determining if a sound should be played
257 and which file should be played.
258
259 util.c:
260 There's not really a lot of cohesion to this file; it's just a lot of
261 stuff that happened to be thrown into it for no apparent reason. None
262 of it is particularly tasty; it's all just utility functions. Just
263 like the name says.
264
265 plugins/ticker/gtkticker.c:
266 Syd, our resident GTK+ God, wrote a GtkWidget, GtkTicker. This is that
267 widget. It's cool, and it's tiny. This is actually a really good example
268 widget for those of you looking to write your own.
269
270 plugins/ticker/ticker.c:
271 Syd is just so cool. I really can't get over it. He let me come
272 visit him at Netscape one day, and I got to see all of their toys
273 (don't worry, I'm under an NDA). Anyway, this file is for the buddy
274 ticker. This is also a damn cool file because it's got all of the
275 functions that you'd want right up at the top. Someday I want to be
276 as cool as Syd.
277
278 For the PRPLs, the only protocol whose "main" gaim file isn't the same as
279 the name of the protocol is ICQ; for that it's gaim_icq.c. But ICQ is
280 deprecated and you should be using Oscar for ICQ anyway.
281
282 PLUGINS (read the plugins howto, this is really out of date)
283 =======
284
285 OK, so you want to load a plugin. You go through whatever UI (you
286 can read all about the UI in plugins.c or whereever). You finally get
287 to load_plugin, the meat of the plugins stuff (plugins can actually
288 call load_plugin themselves to load other plugins). load_plugin
289 is passed the full path to the plugin you want to load
290 (e.g. /usr/local/lib/gaim/irc.so).
291
292 load_plugin does a few things with that filename. The first is to see
293 if you've already loaded that plugin. If you have, load_plugin unloads
294 the one that is currently loaded. You might wonder why; it's because
295 the same plugin can't be loaded twice. If you call g_module_open on a
296 filename twice, both times it will return the same pointer, and both times
297 increment the reference count on the GModule * that it returns. This
298 means you really do have the same plugin twice, which fucks up the
299 callback system to no end. So it's better that you can only have it
300 loaded once at any given time.
301
302 Now that we're assured that we don't have this particular plugin loaded
303 yet, we better load it. g_module_open, baby. Much more portable than
304 dlopen(). In fact, for Linux it actually is the equivalent of dlopen()
305 (you can read the gmodule source and see for yourself). There's only one
306 quirk. It always logically ORs the options you pass with RTLD_GLOBAL,
307 which means that plugins share symbols. I haven't figured out yet if
308 this means just functions or variables too; but in either case make every
309 function and variable in your plugin static except for gaim_plugin_*(),
310 name(), and description(). It's good coding practice anyway.
311
312 So, assuming we didn't get NULL back from g_module_open, we then make sure
313 it's a valid gaim plugin by looking for and calling gaim_plugin_init,
314 courtesy g_module_symbol (g_module_symbol is actually what's portable
315 about gmodule as opposed to dl*; some BSD's require '_' prepended to
316 symbol names and g_module_symbol guarantees we do The Right Thing).
317
318 Assuming we've found gaim_plugin_init and it hasn't returned non-NULL
319 to us, we then add it to our list of plugins and go merrily about our way.
320
321 So when do the callbacks happen?! plugin_event, baby, plugin_event. Any
322 time you want to trigger a plugin event simply call plugin_even with the
323 parameters to be passed to any event handlers and you're set. plugin_event
324 then makes sure that any plugins waiting for the event get passed the
325 arguments properly and passes it on to perl.
326
327 Speaking of perl. If you really want to know how this works, you're
328 better off reading X-Chat's documentation of it, because it's better
329 than what I could provide.
330
331
332 MULTIPLE CONNECTIONS AND PRPLS
333 ==============================
334
335 OK, let's start with the basics. There are users. Each user is contained
336 in an gaim_account struct, and kept track of in the gaim_accounts GSList.
337 Each gaim_account has certain features: a username, a password, and
338 user_info. It also has certain options, and the protocol it uses to sign
339 on (kept as an int which is #define'd in prpl.h).
340
341 Now then, there are protocols that gaim knows about. Each protocol is
342 in a prpl struct and kept track of in the protocols GSList. The way the
343 management of the protocols is, there will only ever be one prpl per
344 numeric protocol. Each prpl defines a basic set of functions: login,
345 logout, send_im, etc. The prpl is responsible not only for handling
346 these functions, but also for calling the appropriate prpl_got functions
347 It handles each of these on a per-account basis.
348
349 So why's it called a PRPL? It stands for PRotocol PLugin. That means
350 that it's possible to dynamically add new protocols to gaim. However,
351 all protocols must be implemented the same way: by using a prpl struct
352 and being loaded, regardless of whether they are static or dynamic.
353
354 Here's how struct gaim_connection fits into all of this. At some point
355 the User (capitalized to indicate a person and not a name) will try to
356 sign on one of Their users. serv_login is then called for that user. It
357 searches for the prpl that is assigned to that user, and calls that prpl's
358 login function, passing it the gaim_account struct that is attempting to
359 sign on. The prpl is then responsible for seeing that the gaim_connection
360 is created (by calling new_gaim_connection), and registering it as
361 being online (by calling account_online and passing it the gaim_account and
362 gaim_connection structs). At that point, the gaim_account and gaim_connection
363 structs have pointers to each other, and the gaim_connection struct has
364 a pointer to the prpl struct that it is using. The gaim_connections are
365 stored in the connections GSList. The way connection management works is,
366 there will always only be one gaim_connection per user, and the prpl that
367 the gaim_connection uses will be constant for the gaim_connection's life.
368
369 So at certain points the User is going to want to do certain things,
370 like send a message. They must send the message on a connection. So the UI
371 figures out which gaim_connection the User want to send a message on (for
372 our example), and calls serv_send_im, telling it which gaim_connection to
373 use, and the necessary information (who to send it to, etc). The serv_
374 function then calls the handler of the prpl of the connection for that
375 event (that was way too many prepositions). OK, each prpl has a send_im
376 function. Each connection has a prpl. so you call gc->prpl->send_im and
377 pass it the connection and all the necessary info. And that's how things
378 get done.
379
380 I hope some of that made sense. Looking back at it it makes absolutely no
381 sense to me. Thank god I wrote the code; otherwise I'm sure I'd be lost.
382
383
384 WRITING PRPLS
385 =============
386
387 Start off with a protocol that you want to implement; make sure it has a
388 number defined in prpl.h. If it doesn't, talk to Rob or Eric about adding
389 it. *NEVER* use an unassigned number, not even for testing or personal
390 use. It's possible that number will be used later by something else and
391 that would cause quite a few head-scratchers.
392
393 Start off with the following boiler plate:
394
395 static struct prpl *my_protocol = NULL;
396
397 void newproto_init(struct prpl *ret) {
398 ret->protocol = PROTO_NEWPROTO;
399
400 my_protocol = ret;
401 }
402
403 #ifndef STATIC
404
405 char *gaim_plugin_init(GModule *handle)
406 {
407 load_protocol(newproto_init, sizeof(struct prpl));
408 return NULL;
409 }
410
411 void gaim_plugin_remove()
412 {
413 struct prpl *p = find_prpl(PROTO_NEWPROTO);
414 if (p == my_protocol)
415 unload_protocol(p);
416 }
417
418 char *name()
419 {
420 return "New Protocol";
421 }
422
423 char *description()
424 {
425 return PRPL_DESC("New Protocol");
426 }
427
428 #endif
429
430 Replace all NEWPROTO things with your protocol name (e.g. PROTO_OSCAR
431 instead of PROTO_NEWPROTO, oscar_init instead of newproto_init). Then
432 populate your struct prpl; the most important function is actually name(),
433 because without it, Gaim will most likely segfault. The second most
434 important function is login(). Not all functions need to be implemented.
435
436 There should be absolutely *ZERO* GTK+ in the PRPLs. PRPLs should *NEVER*
437 say what the UI *looks* like, only what information needs to be there.
438 There's currently an effort to get the GTK+ that is contained in the PRPLs
439 directory out of there. If you submit a patch that adds GTK+ to those
440 directories it's very likely to be refused, unless if I'm in a good mood
441 and decide to relocate things for you. That's not likely.
442
443 You're probably wondering how you can do certain things without GTK+. Well,
444 you're just going to have to make do. Rely on the UI, that's why it's
445 there. A PRPL should have absolutely ZERO interaction with the user, it
446 should all be handled by the UI.
447
448 Don't use the _options variables at all. The core should take care of all
449 of that. There are several proto_opt fields that you can use on a per-user
450 basis. Check out existing protocols for more details.