PDA

View Full Version : WRAP and UNWRAP


ioannis
April 8th, 2007, 11:38 PM
I'm trying to understand how the plugins work and I have to say I'm confused :oops: . What are the WRAP and UNWRAP macros for. What programming metaphor do they implement? What are these 'real' and 'private' semantics?

I can see that the 'screen' maintains an array of (private?) 'screens', one for every plugin. Looking at the 'triangle' example plugin, when it's initialised, the 'screen' is left with its 'paintScreen()' function set to the one provided by the plugin, while the plugin holds what the 'screen' originally had (?) And if that wasn't confusing enough :-P, every time the plugin's main function (which I assume is 'trianglePaintScreen()') is called, the UNWRAP and WRAP macros seem to do something that looks redundant to me. At some point I thought it might be some threading safety mechanism of some sort, but quickly dismissed that idea. Then I thought it might be a kind of object-oriented polymorphism implementation, but it doesn't look right.

As you can see I'm totally confused :cry:

thanks

roico
April 8th, 2007, 11:53 PM
it's a stack of functions, WRAP adds a function to the stack, UNWRAP removes a function from the stack...
so when you see a UNWRAP / call / WRAP series, it just calls the next function in that stack...
at plugin startup, plugins wrap some of their functions (add it to the stack), and this way their functions get executed.
when a plugin is removed, it unwraps its functions (remove it from the stack), and this way their functions aren't called any more.

ioannis
April 9th, 2007, 08:08 PM
thanks roico.

that's an odd 'stack'. I guess that's why the names wrap/unwrap have been chosen instead of push/pop. In fact, it doesn't really look like a stack.
Is it like a 'graphics pipeline'? I don't understand why it's the plugins' responsibility to call functions from the 'stack'. Are the plugin functions 'atomic'?

Actually, is there any documentation that explains all these things ?

thanks

lowfi
April 9th, 2007, 08:36 PM
Francis Woodhouse called it 'an ugly C version of polymorphism'. :)

Here's a short plugin introduction http://www.downwithnumbers.com/compiz_plugins.html

I think the idea for WRAP/UNWRAP comes from X. IIRC there are similar macros in some extensions, but i don't
know if they do the same thing.

rememo
April 9th, 2007, 10:04 PM
it's a stack of functions,


Hmm, I'm not an expert in this (actually looked the first time at WRAP/UNWRAP so please forgive any mistakes :) ),
but after looking at the defines it doesn't look like a stack. What is done with WRAP seems to be a simple swapping of function pointers between private(plugin)- and core-display/screen (d, s). This seems to provide a way to overwrite the functions of the core (base_class) like in OOP (a form of polymorphism as lowfi already mentioned). So when you write i.e. :

WRAP(plugin_private_display, d, d_func, plugin_func):
1. you store d->d_func pointer in plugin_private_display->d_func
and
2. set d->d_func pointer to plugin_func

An UNWRAP then sets the d->d_func back to it's original which was stored in the plugins private structs due to a WRAP call before.

I guess, you do a WRAP at display/screen init time because you want the display/screen to call your plugin functions whenever your plugin grabs the screen. After finished grab you need an UNWRAP when display/screen finishs to set back to original core function. This is done to get back the same initial situation for every plugin.

Code like this in plugin_func:
[some code here]
UNWRAP
d->d_func
WRAP
[some_code_there]

...then simply says: after/before doing my special plugin stuff in [some_code_here, some_code_there], call the original core function. This is mostly done to pass over special interface parameters (masks, transforms, window_attributes, etc. ), that get set before (depending on whatever), to the core function.


All this could become a stack, if plugin displays/screens get initialized stackwise (i.e. init_p1_d, init_p2_d, init_p3_d,...,init_pn_d, finish_pn_d,...,finish_p1_d). This is because every plugin would "wrap" it's function around the function of it's predecessor (and saves it in it's privates). With this an UNWRAP would point to the function of the predecessor. I don't know if this is the case. Maybe it's that displays get initialized stackwise on startup and screens on screengrab, who knows... :)

ioannis
April 10th, 2007, 12:05 AM
yep! thanks guys. now it makes sense.

it's an ugly polymorphism with 'stacked' function calling :-) Isn't it unsafe in way?. What happens if I forget to WRAP (restore the original function pointer) after UNWRAP? I wonder if there is a better way to do that. Also why UNWRAP and not execute directly the stored function pointer? Isn't that redundant on the UNWRAP-call-WRAP cycle?

moving on to some further questions. What are the 'screen' and 'display' terms mean ? Is 'screen' a physical screen and the 'display' a viewport/workspace ?

RYX
April 10th, 2007, 12:15 AM
The WRAP/UNWRAP mechanism is really unsafe and needs to be used with care. But that applies to C in general. It's fast but unsafe ... :)

Concerning screen/display terminology you should definetly read one of the official Xlib-tutorials (http://tronche.com/gui/x/xlib/). A "Screen" is comparable to a physical screen (monitor) and the "Display" is comparable to the connection to the x-server. There is almost always only one display but there can be multiple screens (as far as I know).

EDIT: And I think the wrapping is used because that way compiz doesn't need to store the function pointers for each plugin's callbacks - instead the plugins wrap themselves into compiz's function-calls and compiz simply calls one callback without caring for each single plugin. (Correct me if I am wrong ...)

:)

roico
April 10th, 2007, 02:27 AM
rememo:
1. screen grabbing != screen initializing, and has nothing to do with WRAP / UNWRAP.
screen grab == disabling all input.

2. about what you said when this all thing becomes a stack: this is exactly how it works.
UNWRAPing without making it work like a stack gives unpredictable results (plugins will just "lose" their wrap).

so actually hat happens in screen / display initializes is just all plugin pushing their function to the stack, and on screen / display finish popping them out (to make a clean chain, this happen on plugin unload too for example).

ionnais:
if you forget to wrap after you unwrap, your plugin's function won't be called anymore (stack).

why not storing all function pointers in core? because WRAP / UNWRAP is a lot stronger...
if core just looped between all function pointers, there was no way to stop the call chain (returning).
also, you couldn't do things like cube (cube's paintTransformedScreen calls paintTransformedScreen for each visible viewport (like UNWRAP call call call call WRAP - again, impossible if core looped between function opinter)).
this is also the reason why each plugin is responsible for calling the next function in the stack.
EDIT: also, of course, being able to do things before UNWRAP / call / WRAP and being able to do things after it (really useful in some cases).

you should just think about it as a stack, this is the easiest way to think about it, and actually what it really is.

rememo
April 10th, 2007, 09:59 AM
Thanks for the explanation roico. I didn't knew exactly how/when display/screens get initialized and finished (allthough I could have guessed that it is done this way). Than of course all this becomes sort of a stack (or rather a bunch of recursive callbacks for each wrapped core function, I think the terms WRAP/UNWRAP describe this very well). So you always have to take in account that a previously loaded plugin might change your settings. Now I know why plugin loading order is so important and why some plugins conflict :)

ioannis
April 10th, 2007, 12:44 PM
Thanks roico. Yes you are right it is a stack, but it helps me think of it as a unidirectional link list:
http://farm1.static.flickr.com/223/453834260_5c81ce2f1b_b.jpg

what happens when a plguin is unloaded ? What happens to the rest of the functions in the 'chain' if for instance plugin1 (in my example that would provide the 'pg1Func()') is unloaded?

thank you for this discussion. It helps me clear up things in my mind.

PS: let me know if the diagram is correct

mikedee
April 10th, 2007, 01:01 PM
I think its easiest to look at the system with only one plugin loaded. Everything scales up without a problem.

Here is my explanation to add to the pot :)

Imaging one plugin loaded (zoom) and core. Zoom wants to know about xEvents and it wants to be able to modify certain parameters that are passed to paintScreen. the core functions we want to wrap are d->handleEvent and s->paintScreen. Only dealing with the handle event now.

Zoom plugin must have a private display struct which will contain a slot for a HandleEventProc, CompDisplay also has one of these slots which contains handleEvent. Zoom then creates its own HandleEventProc (called zoomHandleEvent normally) and swaps this with the core handleEvent proc.

After wrap the 2 structs contain this

CompDisplay->handleEvent = zoomHandleEvent
ZoomDisplay->handleEvent = handleEvent

Now when an external source calls d->handleEvent (ie in response to an XEvent) zoomHandleEvent is actually called instead.

Thats very nice but handleEvent is actually vital to the running of your machine, if you do nothing in zoomHandleEvent then events will stop processing and things get ugly.

The solution to this is to call the core function, you could just call ZoomDisplay->handleEvent but that would not propagate the chain properly. So what you do is unwrap - call - wrap. This makes sure it all works with > 1 plugin.

UNWRAP - CompDisplay->handleEvent = handleEvent
CALL - calls handleEvent so things work
WRAP - Puts it back for next time CompDisplay->handleEvent = zoomHandleEvent

When your plugin is unloaded you MUST unwrap anything you have wrapped, if you do not do that then CompDisplay will contain an invalid reference to zoomHandleEvent and things will crash.

To think of it with multiple plugins I imagine it as a line of people and a 'core' person walks down the line swapping functions with each person in the line. Try it with your collegues!

maniac
April 10th, 2007, 01:37 PM
Thanks roico. Yes you are right it is a stack, but it helps me think of it as a unidirectional link list:
http://farm1.static.flickr.com/223/453834260_5c81ce2f1b_b.jpg

what happens when a plguin is unloaded ? What happens to the rest of the functions in the 'chain' if for instance plugin1 (in my example that would provide the 'pg1Func()') is unloaded?

If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)


PS: let me know if the diagram is correct
It is.

ioannis
April 10th, 2007, 02:03 PM
If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)

oh! I see. So the core handles 'unloading' of plugins explicitly. It doesn't simply call the *Finit() functions of the plugin it unloads, but rather rebuilds all function-call 'stacks' up-to and including the functions of the plugin it's unloaded (and in the process omitting those functions), right?

ok, it's getting clearer now. thanks :)

...though, how does the core know which core-functions the plugin has overloaded (wrapped) ? I guess it rebuilds all of them even if the plugin has wrapped only one.

maniac
April 10th, 2007, 02:38 PM
If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)

oh! I see. So the core handles 'unloading' of plugins explicitly. It doesn't simply call the *Finit() functions of the plugin it unloads, but rather rebuilds all function-call 'stacks' up-to and including the functions of the plugin it's unloaded (and in the process omitting those functions), right?

ok, it's getting clearer now. thanks :)

...though, how does the core know which core-functions the plugin has overloaded (wrapped) ? I guess it rebuilds all of them even if the plugin has wrapped only one.
Core fully unloads all those plugins - you may want to have a look at the functions updatePlugins() (in display.c) and pushPlugin / popPlugin (plugin.c) ;)

mikedee
April 10th, 2007, 02:42 PM
If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)

oh! I see. So the core handles 'unloading' of plugins explicitly. It doesn't simply call the *Finit() functions of the plugin it unloads, but rather rebuilds all function-call 'stacks' up-to and including the functions of the plugin it's unloaded (and in the process omitting those functions), right?

As far as I understand this is not right, unloading one plugin will not cause any other plugins to unload (except in the case of cube/rotate), the fini functions are called and the plugin is responsible for putting the core state back to normal (ie how it was before it was loaded). This means swapping back whatever you took off when your plugin loaded. It may well be another plugins function, but as far as your plugin is concerned, it does not matter. The key is putting things back.

...though, how does the core know which core-functions the plugin has overloaded (wrapped) ? I guess it rebuilds all of them even if the plugin has wrapped only one.

It doesn't - as far as core is concerned, wrapping doesn't even exist. The plugins just swap out functions the core has no knowledge of how many plugins have wrapped or what they have wrapped. Thats why things go seriously wrong if you mess up WRAP/UNWRAP.

If the macros were called SWAP and UNSWAP I think it would make more sense, they only become WRAP and UNWRAP because the swapped function is responsible for calling the core one too.

mikedee
April 10th, 2007, 02:46 PM
If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)

oh! I see. So the core handles 'unloading' of plugins explicitly. It doesn't simply call the *Finit() functions of the plugin it unloads, but rather rebuilds all function-call 'stacks' up-to and including the functions of the plugin it's unloaded (and in the process omitting those functions), right?

ok, it's getting clearer now. thanks :)

...though, how does the core know which core-functions the plugin has overloaded (wrapped) ? I guess it rebuilds all of them even if the plugin has wrapped only one.
Core fully unloads all those plugins - you may want to have a look at the functions updatePlugins() (in display.c) and pushPlugin / popPlugin (plugin.c) ;)

I think this is slightly misleading, the subject is about wrapping and core has nothing to do with that. the core plugin functions just call the fini functions from the vTable, it is not related to wrapping at all.

maniac
April 10th, 2007, 03:12 PM
If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)

oh! I see. So the core handles 'unloading' of plugins explicitly. It doesn't simply call the *Finit() functions of the plugin it unloads, but rather rebuilds all function-call 'stacks' up-to and including the functions of the plugin it's unloaded (and in the process omitting those functions), right?

ok, it's getting clearer now. thanks :)

...though, how does the core know which core-functions the plugin has overloaded (wrapped) ? I guess it rebuilds all of them even if the plugin has wrapped only one.
Core fully unloads all those plugins - you may want to have a look at the functions updatePlugins() (in display.c) and pushPlugin / popPlugin (plugin.c) ;)

I think this is slightly misleading, the subject is about wrapping and core has nothing to do with that. the core plugin functions just call the fini functions from the vTable, it is not related to wrapping at all.
Well, yes and no. Yes, it only calls the vTable fini functions. But no, it is related to wrapping because that fini calls are needed for proper unwrapping of the unloaded plugin because the plugin located one higher in the stack contains (at least might contain) references to functions of the unloaded plugin, which may cause problems after the plugin is unloaded ;)

mikedee
April 10th, 2007, 03:36 PM
If plugin1 in your example is unloaded, core would also unload plugin3 and plugin2 (in that order), unload plugin1 and after that load plugin2 and plugin3 again - just like in a stack of things ;)

oh! I see. So the core handles 'unloading' of plugins explicitly. It doesn't simply call the *Finit() functions of the plugin it unloads, but rather rebuilds all function-call 'stacks' up-to and including the functions of the plugin it's unloaded (and in the process omitting those functions), right?

ok, it's getting clearer now. thanks :)

...though, how does the core know which core-functions the plugin has overloaded (wrapped) ? I guess it rebuilds all of them even if the plugin has wrapped only one.
Core fully unloads all those plugins - you may want to have a look at the functions updatePlugins() (in display.c) and pushPlugin / popPlugin (plugin.c) ;)

I think this is slightly misleading, the subject is about wrapping and core has nothing to do with that. the core plugin functions just call the fini functions from the vTable, it is not related to wrapping at all.
Well, yes and no. Yes, it only calls the vTable fini functions. But no, it is related to wrapping because that fini calls are needed for proper unwrapping of the unloaded plugin because the plugin located one higher in the stack contains (at least might contain) references to functions of the unloaded plugin, which may cause problems after the plugin is unloaded ;)

One calls the other but they are not related in any way. You were implying that there is some sort of stack and the core functions deal with popping wrapped functions off this stack.

I think the whole stack mataphore is wrong, it appears to work superficially, but what we have is actually a series of swaps. I know this because the python plugin DOES use a stack for the python wrapped functions, the core certainly does not have a stack like this. Like I said, as far as the core is concerned there is no such thing as wrapping, it is having function pointers swapped out without its knowledge.

rememo
April 10th, 2007, 03:37 PM
The fini functions that get called from core definitely have something to do with wrap/unwrap :wink:
If you finish the plugin, UNWRAP calls are made which "peel" the plugins functionality-"layer" off, by setting all from this plugin wrapped core functions to the functions of its predecessor-plugin.

The easiest way to see this is maybe to think of WRAPS as layers of an onion (for each core function) :D .
The core-function is the most inner layer. Due to a stackwise plugin-loading every plugin now WRAPS it's functionality-"layer" around the "layer" of the predecessor.
The UNWRAP/call/WRAP code in plugin-functions is just to recursively allow to travel through the layers of the onion, starting from the most outer layer down to the core function. If you forget them in one plugin you will be stuck in the layer of this plugin and never reach the core function.
If you don't want to mess up this travelling path, you will first have to "peel" off all of the plugin-layers (by finishing the plugin through fini plugin_display/screen -> triggers UNWRAP calls) that WRAP around the plugin you want to unload. After this you can unload this plugin. Then you build up the layers of the onion again :), by loading the other plugins stackwise again. I don't see any other way to unload the plugins, of course you could remap the privates function pointers, but you would need to access the privates of the plugin to unload from the plugin thats layered above this plugin. That's not possible I think.

mikedee
April 10th, 2007, 03:44 PM
Like I said, it is not a stack, its nothing like an onion. It is a chain of swaps. Plugins are responsible for restoring the swapped functions, thats all. This is why there priv and real in the WRAP macros - there are only 2 functions involved in each operation there is no stack. It might seem like one, but it isn't, like I said, the python plugin has a stack, compiz does not.

Once you understand that principle from the point of view of 1 plugin and core, you can work out what happens when there are more than 1 plugin.

roico
April 10th, 2007, 04:09 PM
remomo, you got it exactly right... :)
onion layers is a nice metaphor for stack (it really is :D), but still, WRAP / UNWRAP is a stack...

mikedee, if plugin1 plugin2 plugin3 are loaded in that order, and you want to unload plugin1, you have to first first unload plugin 3 and plugin 2 (in that order), or else the stack will get wrong (yes, it is a stack, and yes, core will actually unload plugin 3 and plugin 2 if it wants to unload plugin 1). after that, you of course load plugin 2 and plugin 3 again, so they aren't really "unloaded", but reloaded.
this was actually kind of a problem with group, when the user unlaoded any other plugin (not just group), all the groups informations was lost. we fixed that by storing the groups in xprops.

TBH, i don't like the way WRAP / UNWRAP works.
the main limitations are not being able to UWNRAP a plugin from the middle of the list (because reloading plugin 3 and plugin 2 are indeed quite redundant if you want to unload plugin 1), and not being able to recall the whole stack again.
the second point was again kind of a problem for us in group. when we move a grouped window, we want other windows to be moved, but we can't call moveWindow normally because not all plugins (or more specifically, plugins that are loaded after group) won't get the move notifies.
we fixed that by queuing the moves, and doing all th actual moves in preparePaintScreen - not the nicest solution if you ask me.
the original reason drawWindow was made and called by paintWindow was the second point (switcher wasn't able to draw decorations because it couldn't call the whole stack chain, so david decided to put decorations code in a different stack). TBH, sometimes separating between drawWindow and paintWindow is nice, but i'm not sure this was the best fix for the switcher + decorations problem.

in beryl we started to reimplement UNWRAP / WRAP by using linked lists to fix those 2 points, but unfortunately we never finished that patch and it was dropped... :\

mikedee
April 10th, 2007, 04:15 PM
mikedee, if plugin1 plugin2 plugin3 are loaded in that order, and you want to unload plugin1, you have to first first unload plugin 3 and plugin 2 (in that order), or else the stack will get wrong (yes, it is a stack, and yes, core will actually unload plugin 3 and plugin 2 if it wants to unload plugin 1). after that, you of course load plugin 2 and plugin 3 again, so they aren't really "unloaded", but reloaded.

No this is not true, stack implies a que but the WRAP system can remove and add without any other plugins being involved.

Thats why I said all these ideas are good on a superficial level but they do not properly describe what is happening.

mikedee
April 10th, 2007, 04:17 PM
in beryl we started to reimplement UNWRAP / WRAP by using linked lists to fix those 2 points, but unfortunately we never finished that patch and it was dropped... :\

There is probably a very good reason for that. I think its because you cannot replace something that is not a stack with a stack and expect everything to work properly. I suspect a major advantage of the WRAP / UNWRAP macros is speed.

maniac
April 10th, 2007, 04:23 PM
mikedee, if plugin1 plugin2 plugin3 are loaded in that order, and you want to unload plugin1, you have to first first unload plugin 3 and plugin 2 (in that order), or else the stack will get wrong (yes, it is a stack, and yes, core will actually unload plugin 3 and plugin 2 if it wants to unload plugin 1). after that, you of course load plugin 2 and plugin 3 again, so they aren't really "unloaded", but reloaded.

No this is not true, stack implies a que but the WRAP system can remove and add without any other plugins being involved.

Ok, then I ask you: how?
As soon as more than one plugin is involved in the chain, one plugin contains references to functions of other plugins. In that case, while plugins can set the pointers to anything they like, the only thing that makes sense is to either set it to the core function or to UNWRAP. Everything else might be possible in theory, but makes just no sense in practical use, so in that practical use you will always use WRAP and UNWRAP stack-like.

mikedee
April 10th, 2007, 04:34 PM
mikedee, if plugin1 plugin2 plugin3 are loaded in that order, and you want to unload plugin1, you have to first first unload plugin 3 and plugin 2 (in that order), or else the stack will get wrong (yes, it is a stack, and yes, core will actually unload plugin 3 and plugin 2 if it wants to unload plugin 1). after that, you of course load plugin 2 and plugin 3 again, so they aren't really "unloaded", but reloaded.

No this is not true, stack implies a que but the WRAP system can remove and add without any other plugins being involved.

Ok, then I ask you: how?
As soon as more than one plugin is involved in the chain, one plugin contains references to functions of other plugins. In that case, while plugins can set the pointers to anything they like, the only thing that makes sense is to either set it to the core function or to UNWRAP. Everything else might be possible in theory, but makes just no sense in practical use, so in that practical use you will always use WRAP and UNWRAP stack-like.

What you say is true, my point was that the explanations you gave were misleading (irregardless if you understand or not). If people do not think of it as a stack then its much clearer (IMHO - after 3 weeks of reimplementing WRAP/UNWRAP). You can easily look on it as a stack, but it will boggle your brain eventually.

Each plugin developer should only be concerned about the core and itself, any other unintended side effects are a problem with the plugin or a problem with core which should be fixed. From what I gather David would like to remove all the requires and loads after stuff, but unfortunately it is unavoidable some times.

There are possibilities to do really weird things like wrapping 2 functions, conditionally wrapping as well as the usual blocking tactics. Not much of this power is used yet, but I find that David would rather provide as many options as possible to provide flexibility. You can see that with the options handling stuff. In theory its possible to override other plugins settings and block your settings on non rest based criteria, but its unlikely anyone will want that. When the day comes it will be possible though.

roico
April 10th, 2007, 04:43 PM
I suspect a major advantage of the WRAP / UNWRAP macros is speed.
linked lists can be very fast too, just something like:
WRAP: s->functionList = s->functionList->next; s->function = s->functionList->function;
UNWRAP: s->functionList = s->functionList->prev; s->function = s->functionList->function;

probably as fast as the normal WRAP / UNWRAP

EDIT: this is for the UNWRAP / call / WRAP thing, wrapping at function initialize will probably need another macro, like:
WRAP: s->functionList->next = new functionList(function-pointer); NORMAL-WRAP
UNWRAP: NORMAL-UNWRAP; delete s->functionList->next;

mikedee
April 10th, 2007, 04:48 PM
I suspect a major advantage of the WRAP / UNWRAP macros is speed.
linked lists can be very fast too, just something like:
WRAP: s->functionList = s->functionList->next; s->function = s->functionList->function;
UNWRAP: s->functionList = s->functionList->prev; s->function = s->functionList->function;

probably as fast as the normal WRAP / UNWRAP.

Why don't you finish the code and we can benchmark it ;)

ioannis
April 10th, 2007, 06:31 PM
No this is not true, stack implies a que but the WRAP system can remove and add without any other plugins being involved.

that's what I don't get. UNWRAP replaces the core's function pointer

I'll use your example and add one more plugin to it (let's say 'scale'), so I can saw you what I don't understand.

after the 'zoom' plugin has been intialised, we'll have this:
CompDisplay->handleEvent = zoomHandleEvent
ZoomDisplay->handleEvent = handleEvent

after the 'scale' plugin initialisation, we'll have this:
CompDisplay->handleEvent = scaleHandleEvent
ScaleDisplay->handleEvent = zoomHandleEvent
ZoomDisplay->handleEvent = handleEvent

now let's assume that 'zoom' plugin get's unloaded. If the only thing that is called is the *Fini() functions of 'zoom', we should end up with this situation:
CompDisplay->handleEvent = handleEvent
ScaleDisplay->handleEvent = zoomHandleEvent

which is clearly broken.

when looking at only one plugin and the core, then it looks like a conventional polymorphic paradigm (the core function is overloaded by the 'derived' one). But because the 'derived' function calls the 'base' one, when a second plugin comes into play, it introduces a stack/chain/queue of call-backs as well.

in beryl we started to reimplement UNWRAP / WRAP by using linked lists to fix those 2 points, but unfortunately we never finished that patch and it was dropped... :\
but the current UNWRAP/WRAP is already a link list (from an implementation point of view). The problem is that it's a unidirectional one and not a bidirectional, which would have simplified the problem of removing one function from the list (no need to rebuild).

PS: This is what I get so far. But I see there are some conflicting interpretation in this topic, so I can't be sure. Our discussion should clear things up :)

roico
April 10th, 2007, 06:57 PM
ioannis: you are completely right about the chain getting broken if we unload zoom like this.
when core unloads zoom, it will first unload scale too, and reload it after that (as described in this thread many times). you can take a look at display.c updatePlugins if you want to see how it works. (lines 1465-1561).
i think what mikedee wanted to say, is that plugin developers shouldn't be aware of it, they should only be concerned about their wrapped function and core's function (which, as far as the developer is concerned, is called by UNWRAP / call / WRAP).
in mikedee's point of view, you shouldn't look at WRAP / UNWRAP as a stack (although it actually is), but just as a way to call the core's function. (mikedee, please correct me if i misunderstood you).
personally, i both agree and disagree with mikedee... i think he might be right about it being easier to look at WRAP / UNWRAP not as a stack but as a way to call core's function, but i also think a developer should at least be aware of it being a stack.

mikedee
April 10th, 2007, 06:57 PM
now let's assume that 'zoom' plugin get's unloaded. If the only thing that is called is the *Fini() functions of 'zoom', we should end up with this situation:
CompDisplay->handleEvent = handleEvent
ScaleDisplay->handleEvent = zoomHandleEvent

This is correct, but the plugins are not loaded and unloaded individually. When active_plugins is changed they are all shutdown and restarted. This is not really related to WRAP/UNWRAP though, its a list thing, all lists are totally reinitialized when any item is changed, I think the logic to calculate what to do would take longer than reloading everything.

DivineGod
April 10th, 2007, 07:08 PM
I think the logic to calculate what to do would take longer than reloading everything.

hmm.. This sounds like a good place to use a double linked list.

ioannis
April 10th, 2007, 08:13 PM
i think what mikedee wanted to say, is that plugin developers shouldn't be aware of it, they should only be concerned about their wrapped function and core's function (which, as far as the developer is concerned, is called by UNWRAP / call / WRAP).
This is correct, but the plugins are not loaded and unloaded individually. When active_plugins is changed they are all shutdown and restarted. This is not really related to WRAP/UNWRAP though, its a list thing, all lists are totally reinitialized when any item is changed, I think the logic to calculate what to do would take longer than reloading everything.
ok cool. yep I saw how things are actually done, I was just trying to make a (pointless :-P) point. Also I'm not really questioning the efficiency and/or flexibility of the WRAP/UNWRAP system (though it's my nature to question everything. I learn by debating :-D). I'm just putting all the peaces together in my mind. And I agree with roico, even thought as a plugin developer you only need to use the provided UNWRAP/WRAP interface, without really worrying about all the internals of the core, you should still understand how it all works. It will save you from trouble when you'll try something out of the ordinary.

thanks guys, this has been really helpful.