I made a small command line utility to fix this for my Trendnet TK-204UK. The trick is to toggle the Scroll Lock LED state on the keyboard because it seems to be monitored by the switch. Mac does not normally toggle the LED when you press the Scroll Lock key. I found an Apple sample program (HID LED test tool) that does low level access to the keyboard and modified it to flash the Scroll Lock LED. I called it kvm, put it in my /usr/local/bin and bingo no need to reach under the desk to do what I bought this switch to do. Here is the modified main function of the main.c file of the HID_LED_test_tool project. Note that you will probably have to change a setting of the Xcode project to compile it with the 10.7 SDK because it is configured to use 10.5.
--- update ---
This solution works (thank you so much) but you are missing some of the code. You need to go to http://developer.apple.com/library/mac/#samplecode/HID_LED_test_tool/Listings/main_c.html and grab the function declared at the top.
Also for anyone who doesn't know what to do with this, you need to create a new command line OS X application in Xcode and build this. Then you can run it to switch your KVM. You'll also need to add CoreFoundation.framework and IOKit.framework to the build.
--- end update ---
--- update 2 ---
Just realized that if instead of building a "Command line OS X application", you create a "Cocoa Application" and then delete everything except for main.m and dump the code below in that, you'll create a 'regular' application instead of a 'command line' application and that will launch quicker (without loading up terminal) and can be docked on the dock, etc. If you do go with the "Cocoa App" you'll have to update some of the build settings to correspond to the files you delete.
--- end update 2 ---
//
// main.m
//
// ****************************************************
#pragma mark -
#pragma mark * complation directives *
// ----------------------------------------------------
#ifndef FALSE
#define FALSE 0
#define TRUE !FALSE
#endif
// ****************************************************
#pragma mark -
#pragma mark * includes & imports *
// ----------------------------------------------------
#include <CoreFoundation/CoreFoundation.h>
#include <Carbon/Carbon.h>
#include <IOKit/hid/IOHIDLib.h>
// ****************************************************
#pragma mark -
#pragma mark * typedef's, struct's, enums, defines, etc. *
// ----------------------------------------------------
// function to create a matching dictionary for usage page & usage
static CFMutableDictionaryRef hu_CreateMatchingDictionaryUsagePageUsage(Boolean isDeviceNotElement,
UInt32 inUsagePage,
UInt32 inUsage )
{
// create a dictionary to add usage page / usages to
CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
if ( result ) {
if ( inUsagePage ) {
// Add key for device type to refine the matching dictionary.
CFNumberRef pageCFNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &inUsagePage );
if ( pageCFNumberRef ) {
if ( isDeviceNotElement ) {
CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef );
} else {
CFDictionarySetValue( result, CFSTR( kIOHIDElementUsagePageKey ), pageCFNumberRef );
}
CFRelease( pageCFNumberRef );
// note: the usage is only valid if the usage page is also defined
if ( inUsage ) {
CFNumberRef usageCFNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &inUsage );
if ( usageCFNumberRef ) {
if ( isDeviceNotElement ) {
CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), usageCFNumberRef );
} else {
CFDictionarySetValue( result, CFSTR( kIOHIDElementUsageKey ), usageCFNumberRef );
}
CFRelease( usageCFNumberRef );
} else {
fprintf( stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__ );
}
}
} else {
fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__ );
}
}
} else {
fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__ );
}
return result;
} // hu_CreateMatchingDictionaryUsagePageUsage
int main( int argc, const char * argv[] )
{
#pragma unused ( argc, argv )
// create a IO HID Manager reference
IOHIDManagerRef tIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone );
require( tIOHIDManagerRef, Oops );
// Create a device matching dictionary
CFDictionaryRef matchingCFDictRef = hu_CreateMatchingDictionaryUsagePageUsage( TRUE,
kHIDPage_GenericDesktop,
kHIDUsage_GD_Keyboard );
require( matchingCFDictRef, Oops );
// set the HID device matching dictionary
IOHIDManagerSetDeviceMatching( tIOHIDManagerRef, matchingCFDictRef );
if ( matchingCFDictRef ) {
CFRelease( matchingCFDictRef );
}
// Now open the IO HID Manager reference
IOReturn tIOReturn = IOHIDManagerOpen( tIOHIDManagerRef, kIOHIDOptionsTypeNone );
require_noerr( tIOReturn, Oops );
// and copy out its devices
CFSetRef deviceCFSetRef = IOHIDManagerCopyDevices( tIOHIDManagerRef );
require( deviceCFSetRef, Oops );
// how many devices in the set?
CFIndex deviceIndex, deviceCount = CFSetGetCount( deviceCFSetRef );
// allocate a block of memory to extact the device ref's from the set into
IOHIDDeviceRef * tIOHIDDeviceRefs = malloc( sizeof( IOHIDDeviceRef ) * deviceCount );
require( tIOHIDDeviceRefs, Oops );
// now extract the device ref's from the set
CFSetGetValues( deviceCFSetRef, (const void **) tIOHIDDeviceRefs );
// before we get into the device loop we'll setup our element matching dictionary
matchingCFDictRef = hu_CreateMatchingDictionaryUsagePageUsage( FALSE, kHIDPage_LEDs, 0 );
require( matchingCFDictRef, Oops );
int pass; // do 3 passes
for ( pass = 0; pass < 3; pass++ ) {
Boolean delayFlag = FALSE; // if we find an LED element we'll set this to TRUE
//printf( "pass = %d.\n", pass );
for ( deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++ ) {
// if this isn't a keyboard device...
if ( !IOHIDDeviceConformsTo( tIOHIDDeviceRefs[deviceIndex], kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard ) ) {
continue; // ...skip it
}
//printf( " device = %p.\n", tIOHIDDeviceRefs[deviceIndex] );
// copy all the elements
CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRefs[deviceIndex],
matchingCFDictRef,
kIOHIDOptionsTypeNone );
require( elementCFArrayRef, next_device );
// for each device on the system these values are divided by the value ranges of all LED elements found
// for example, if the first four LED element have a range of 0-1 then the four least significant bits of
// this value will be sent to these first four LED elements, etc.
int device_value = pass;
// iterate over all the elements
CFIndex elementIndex, elementCount = CFArrayGetCount( elementCFArrayRef );
for ( elementIndex = 0; elementIndex < elementCount; elementIndex++ ) {
IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elementCFArrayRef, elementIndex );
require( tIOHIDElementRef, next_element );
uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef );
uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
// if this isn't an LED element or the Scroll Lock key...
if ( kHIDPage_LEDs != usagePage || 3 != usage ) {
continue; // ...skip it
}
//IOHIDElementType tIOHIDElementType = IOHIDElementGetType( tIOHIDElementRef );
//printf( " element = %p (page: %d, usage: %d, type: %d ).\n", tIOHIDElementRef, usagePage, usage, tIOHIDElementType );
// get the logical mix/max for this LED element
CFIndex minCFIndex = IOHIDElementGetLogicalMin( tIOHIDElementRef );
CFIndex maxCFIndex = IOHIDElementGetLogicalMax( tIOHIDElementRef );
// calculate the range
CFIndex modCFIndex = maxCFIndex - minCFIndex + 1;
// compute the value for this LED element
CFIndex tCFIndex = minCFIndex + ( device_value % modCFIndex );
device_value /= modCFIndex;
//printf( " value = 0x%08lX.\n", tCFIndex );
uint64_t timestamp = 0; // create the IO HID Value to be sent to this LED element
IOHIDValueRef tIOHIDValueRef = IOHIDValueCreateWithIntegerValue( kCFAllocatorDefault, tIOHIDElementRef, timestamp, tCFIndex );
if ( tIOHIDValueRef ) {
// now set it on the device
tIOReturn = IOHIDDeviceSetValue( tIOHIDDeviceRefs[deviceIndex], tIOHIDElementRef, tIOHIDValueRef );
CFRelease( tIOHIDValueRef );
require_noerr( tIOReturn, next_element );
delayFlag = TRUE; // set this TRUE so we'll delay before changing our LED values again
}
next_element: ;
continue;
}
next_device: ;
CFRelease( elementCFArrayRef );
continue;
}
// if we found an LED we'll delay before continuing
if ( delayFlag ) {
usleep( 250000 ); // sleep 0.25 second
}
} // next pass
if ( tIOHIDManagerRef ) {
CFRelease( tIOHIDManagerRef );
}
if ( matchingCFDictRef ) {
CFRelease( matchingCFDictRef );
}
Oops: ;
return 0;
} /* main */
Best Answer
Have you checked to make sure that your CAPS LOCK key is turned on in your Keyboard Preferences?
Go to System Preferences -> Keyboard -> Modifier Keys. Make sure next to Caps Lock, it says Caps Lock and not "No Action".
Also, do you have any 3rd party Keyboard Modification Preference Panes installed, like KeyRemap4MacBook or PCKeyboard Hack? Those can be used in various ways to disable or otherwise alter the CAPS LOCK behavior.