Wednesday, September 17, 2008

SynergyKM Mouse Scrolling Fix

SynergyKM is a very handy little Mac utility that allows you to share one keyboard and mouse across multiple computers. It's based on the utility called Synergy, which runs on many different computer platforms (Mac, Windows, Linux, etc.). So, if you are sitting at a desk with multiple monitors for various systems, you can choose one system as the master and use its sole keyboard and mouse.

Here's an example of using SynergyKM to designate the layout of the various monitors.



Once you define the layout of the various monitors and have Synergy running on all the machines, you can simply move the mouse off of one screen and it will automatically appear on the next screen. You can even share the cut-and-paste buffer across the machines.

All this is very nice, but SynergyKM hasn't been updated since 2006. One problem I've discovered is if you are on a Mac and are using a scroll wheel or trackpad to perform scrolling on a different monitor, the scrolling is super crazy fast.

Fortunately, Synergy is an open source project, so I was able to figure out the problem. SynergyKM is using a deprecated function for generating mouse scrolling events. The problem - for those interested - was the older method for generating a mouse scrolling event was based on scrolling numbers of "lines" of text. However, the values being uses for the scrolling deltas were based on pixel differences, not line differences. The newer method (CGEventCreateScrollWheelEvent) for generating scrolling events can be configured to use either line or pixel counts.

It's only a few lines of code that need changing. Specifically, edit the COSXScreen.cpp file, and search for the function COSXScreen::fakeMouseWheel(). Replace it with this:


void
COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
{
CGEventRef my_event;

if (xDelta != 0 || yDelta != 0) {
my_event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, mapScrollWheelFromSynergy(yDelta), -mapScrollWheelFromSynergy(xDelta));
CGEventPost(kCGHIDEventTap, my_event);
CFRelease(my_event);
//CGPostScrollWheelEvent(2, mapScrollWheelFromSynergy(yDelta), -mapScrollWheelFromSynergy(xDelta));
}
}


Also, look for the function COSXScreen::mapScrollWheelFromSynergy() and replace it with this:


SInt32
COSXScreen::mapScrollWheelFromSynergy(SInt32 x) const
{
// use server's acceleration with a little boost since other platforms
// take one wheel step as a larger step than the mac does.
// return static_cast(3.0 * x / 120.0);

// No boost in acceleration
return static_cast(x / 120.0);
}


Then, just recompile the project and take the new synergyc file generated and replace the existing synergyc executable on all your client machines. If you are using SynergyKM, synergyc will be located in:

/Library/PreferencePanes/SynergyKM.prefPane/Contents/Resources/Synergyd.app/Contents/Resources/synergyc

(NOTE: you can not replace the file if Synergy is currently running.)

If you'd prefer to not have to recompile the file yourself, you can get my compiled version here. It's about 370K in size (compressed).

All content copyright © 2009  Brian Stormont, unless otherwise noted.   All rights reserved.