=== User-readable backstory ===
Mac OS X provides a facility called NSConnection that programs can
use to communicate with each other.
Applications that support Growl use the Growl framework, which uses
NSConnection to communicate with the Growl Helper App. GrowlHelperApp
(GHA) is the program that actually shows notifications on the screen.
Every application has one or more threads, and every thread gets
(among other things) one NSConnection called the default connection.
(It can create more NSConnections if it wants them.) Growl Helper
App, through the current version, uses its default connection to
listen for notifications from applications.
Every NSConnection has a “root object”, which is the application's
emissary to other applications. It's like when a country sends an
ambassador to another country: the root object comes from the
application, is the ambassador for the application, and represents it
to other applications. Other apps talk to that object when they want
the application it came from to do something.
Every NSConnection also has a name, which is like a telephone number:
you access another application's connection by asking for it by name,
like dialing the number.
In Growl's case, our root object is the Growl Application Bridge
pathway. When an application wants to post a Growl notification, the
framework looks up the connection named
“GrowlApplicationBridgePathway”, then gets its root object (which
is supposed to be the pathway), then sends that object some messages
to tell GHA the notification.
--
Logitech Control Center consists of several parts. One of them is an
input manager. These are supposed to be used to extend Mac OS X to
extend new methods of entering text (input). Instead, they're mostly
abused to add new features to every application or specific
applications (many Safari “plug-ins” or “extensions” are
actually input managers).
The reason that this works is that an input manager gets loaded into
each process (running program), just like a plug-in. Once there, it
can affect the process any way it wants—usually to add some kind of
feature. However, this is a high-wire act: an input manager can
easily break things if it steps on the application's toes in some way.
--
Finally, it's important to remember that GrowlHelperApp is an
application just like any other. It's a “faceless background
application” (i.e., it has neither a Dock tile nor a menu-bar), but
it's still just an application, subject to all the same rules. In
particular, input managers get loaded into GHA, just as they get
loaded into any other application.
=== End of user-readable backstory ===
The problem is that, when the application launches, Logitech Control
Center's input manager gets the default connection and sets its root
object to its Scroll Enhancer object.
This is a problem for Growl because GHA also uses the default
connection, and sets its root object at about the same time. But LCC
does this slightly later, so the root object it sets is the one that
holds.
So this is what happens in the Growl Helper App process:
1. GHA gets the default connection, sets its root object to the Growl
Application Bridge pathway object, and registers it under the name
“GrowlApplicationBridgePathway”.
2. The LCC input manager gets the default connection (same one), sets
its root object to the LCC Scroll Enhancer object, and registers the
connection under the name “com.Logitech.Control Center.Scroll
Enhancer/” followed by the process ID number.
The problem comes from the fact that *one connection* (GHA's default
connection) is now registered under two names:
“GrowlApplicationBridgePathway” and “com.Logitech.Control
Center.Scroll Enhancer/$PID”.
Then, when an application tries to send Growl a notification, it
looks up the connection for “GrowlApplicationBridgePathway”, and
gets GHA's default connection. Then it gets its root object to send
it the necessary messages, but the root object is Logitech's, not
Growl's, so this fails.
Result: You don't get any notifications.
Important distinction: What we're doing is not wrong. It's perfectly
OK for an application to get its own default connection, set its root
object to an object of its own creation, and expect this to work.
There is no good reason why this should ever break.
Logitech is the party in the wrong here. Input managers should never
attempt to share any resource with the application. That's borderline
acceptable when the input manager is actually intending to affect the
application in some way (e.g., it's borderline acceptable to get the
app's Edit menu to add a menu item to it), but this was an accident
(they didn't intend to affect Growl), and it's their fault for using
the app's default connection rather than creating one of their own.
They assumed that no application would use the default connection (or
didn't think of the possibility). This assumption is incorrect.
--
So, what can we do about it?
While it's not our bug to fix, and I'm still in favor of telling
people that LCC breaks things, we *can* work around the problem. I
see at least two ways:
1. We could move the registration of our default connection to happen
later (in applicationDidFinishLaunching:, rather than initSingleton:).
I see a couple problems with this. First, we'll need to move a *lot*
of code that has to happen after it; basically, about half of
initSingleton: will happen at the start of
applicationDidFinishLaunching: instead. Second, it's potentially not
a permanent fix: if LCC changes to also use
applicationDidFinishLaunching:, or if some other input manager starts
doing the same thing in that method, we're right back in the same boat.
2. We could create our own connection instead of using
defaultConnection.
This is basically the “give up defaultConnection to the broken input
manager” solution. There's nothing wrong with this that I know of,
and it definitely does work. It also is robust against any new
breakage that LCC may come up with in the future, unless they decide
to start actively breaking Growl.
I have this solution ready to commit. It's also a lot smaller than
#1, which makes it much less likely to regress anything. We could
even put this into 1.1.3 if we want.
Of course, there's one more thing we can do:
3. Tell users not to use Logitech Control Center.
This is not mutually exclusive with either of the other two. LCC 2.4
is broken, whether we work around it or not. It's still their bug,
and it's still possible (maybe even likely) that LCC breaks other apps.
Of course, if we can work around it in Growl, we don't need to be so
aggressive about warning users against LCC. It'd be possible again to
use Growl and LCC at the same time, so we wouldn't need to say
anything like “LCC broke Growl” on login.
Devs: Thoughts?
>
> First, how Logitech Control Center breaks Growl. I'll first present a
> user-readable backstory; developers can skip ahead. Please let me
> know if any part of this is unclear.
>
> === User-readable backstory ===
>
> <snip>
>
> They assumed that no application would use the default connection (or
> didn't think of the possibility). This assumption is incorrect.
How painfully true.
> So, what can we do about it?
>
> While it's not our bug to fix, and I'm still in favor of telling
> people that LCC breaks things, we *can* work around the problem. I
> see at least two ways:
>
> 1. We could move the registration of our default connection to happen
> later (in applicationDidFinishLaunching:, rather than initSingleton:).
>
> I see a couple problems with this. First, we'll need to move a *lot*
> of code that has to happen after it; basically, about half of
> initSingleton: will happen at the start of
> applicationDidFinishLaunching: instead. Second, it's potentially not
> a permanent fix: if LCC changes to also use
> applicationDidFinishLaunching:, or if some other input manager starts
> doing the same thing in that method, we're right back in the same
> boat.
This seems like it would introduce a greater potential for instability.
> 2. We could create our own connection instead of using
> defaultConnection.
>
> This is basically the "give up defaultConnection to the broken input
> manager" solution. There's nothing wrong with this that I know of,
> and it definitely does work. It also is robust against any new
> breakage that LCC may come up with in the future, unless they decide
> to start actively breaking Growl.
>
> I have this solution ready to commit. It's also a lot smaller than
> #1, which makes it much less likely to regress anything. We could
> even put this into 1.1.3 if we want.
This seems most reasonable to me and can be reviewed by all interested
before shipping.
> Of course, there's one more thing we can do:
>
> 3. Tell users not to use Logitech Control Center.
>
> This is not mutually exclusive with either of the other two. LCC 2.4
> is broken, whether we work around it or not. It's still their bug,
> and it's still possible (maybe even likely) that LCC breaks other
> apps.
Textmate was broken as well by LCC (http://blog.macromates.com/2007/logitech-control-center/
)
> Of course, if we can work around it in Growl, we don't need to be so
> aggressive about warning users against LCC. It'd be possible again to
> use Growl and LCC at the same time, so we wouldn't need to say
> anything like "LCC broke Growl" on login.
This is certainly a better option. Thank you for the informative post
regarding the situation.
- brian 'bgannin' ganninger
>
> This is certainly a better option. Thank you for the informative post
> regarding the situation.
And by "this" I actually meant #2. Horrible grammar there.
>
> - brian 'bgannin' ganninger
To that end, here's the patch for developer review:
Check your Console (in /Applications/Utilities). Do you have any
messages like this?
> 2008-04-15 15:37:31.914 GrowlHelperApp[1720] LCC Scroll Enhancer
> loaded
If not, then that's why. LCC Scroll Enhancer is the input manager
that breaks things.
The most likely reason why you wouldn't see this message is that you
haven't logged out and back in (or restarted) yet.
Yaaay big-company tech support.
Note to the public: If anyone has a contact inside Logitech who can
get this issue *really* fixed, I think we and CodeWeavers (CrossOver
Games) and MacroMates (TextMate) would all appreciate it.
For the record, I emailed Logitech support on Monday with the basics
of the problem and a request to be put in touch with an engineer on
the LCC team. (This was before you'd posted your thorough
investigation, Peter, or I would have linked to the archive on that).
Fortunately, I'm a big fan of their MX series mice, so I have one
lying around to serve as source of part number and serial number...
since you can't get through their support form without valid entries
for both fields :)
I haven't received a response (and will be pleasantly surprised to get
one). Request for Logitech contact stands!
Cheers,
Evan
> I would suggest that you do both 2 and 3 of your
> solutions. 2 seems like a good idea as it will lessen the likelyhood
> that you will have incompatibilities further down the track, and 3 is
> a better idea because people should not stand for crap software.
I agree.
> Printer software is a good example of how hardware makers can get away
> with building shitty software to go with their products. It does not
> make sense.
Sad but true.
> Another suggestion, is there perhaps a way to error check the
> NSConnection later on during the code? Perhaps a periodic check or one
> on the final init method (after input managers, internal app
> initialisation and the init's of everything outside of the universe
> that could ever possibly exist?) that queries the root object as to
> whether it is infact the correct object and hasn't been overridden by
> some rogue force of electronics?
This is an interesting idea, but the problem is that one could
literally spend weeks adding checks and still be foiled by malignant
stupidity or oversight. For example, what if the input manager wanted
to "ensure nothing conflicts" and so installed a timer to make the
NSConnection change 60 seconds after the application finishes loading?
Using our own NSConnection is the way to go to prevent others from
messing with it.
-Evan
> To that end, here's the patch for developer review:
>
> <LCCWorkaround.diff.bz2>
Peter, this looks good to me, though I would make the comment a bit
less directed at LCC and more along the lines of:
//We create our own connection, rather than use defaultConnection,
because an input manager such as the one in Logitech Control Center
2.4.0 may also use defaultConnection and therefore steals it away from
us.
The change saves allocation of an NSPort, too, since the default one
has a send port and a receive port! Awesome! ;)
-Evan
> Note to the public: If anyone has a contact inside Logitech who can
> get this issue *really* fixed [...]
I do, and will inquire.
R.
--
Rich Siegel Bare Bones Software, Inc.
<sie...@barebones.com> <http://www.barebones.com/>
Someday I'll look back on all this and laugh... until they sedate me.
Sounds good. I'll make a grammar tweak (s/therefore steals/thereby
steal/), then commit that.
> The change saves allocation of an NSPort, too, since the default
> one has a send port and a receive port!
Actually a send and receive port. From the NSConnection docs:
> The default NSConnection object uses a single NSPort object for
> both receiving and sending …
>
> Look at our forums, it's quite prevalent.
>
> You may also be using the updated version of LCC that doesn't have
> this problem.
Which updated version of LCC is that?
-Evan
Perhaps you're using an old version of LCC. According to reports here
on the list, the current problem started with 2.4.
The updated version of LCC *does* have this problem. It's the old
versions (2.3.x) that don't.
Correct.
Done. This is [4815] on trunk.
The patch has already been committed, and will be in the next version
of Growl.
Furthermore, there is no need to apply the patch to current Growl
source code, because it already has it. Make sure your source is up-
to-date.