Ich versuche, meinen Monitor und meine Tastatur/Maus zwischen meinem PC (Windows 7) und Macbook Pro (1,1 - 2006, Snow Leopard) mit einem einfachen KVM-Switch (Trendnet TK207) gemeinsam zu nutzen.
Durch zweimaliges Drücken von Scroll Lock wechselt der KVM von einem Computer zum anderen. Das Problem ist, dass dies unter Windows funktioniert, aber nicht unter OS X. OS X erkennt meine Scroll Lock-Taste überhaupt nicht (selbst das Scroll Lock-Licht funktioniert nicht).
Irgendwelche Ideen, wie man dieses Problem debuggen/beheben kann?
Ich habe ein kleines Befehlszeilenprogramm erstellt, um dies für mein Trendnet TK-204UK zu beheben. Der Trick besteht darin, den Status der Scroll Lock-LED auf der Tastatur umzuschalten, da er anscheinend vom Schalter überwacht wird. Der Mac schaltet die LED normalerweise nicht um, wenn Sie die Scroll Lock-Taste drücken. Ich habe ein Apple-Beispielprogramm (HID-LED-Testtool) gefunden, das Low-Level-Zugriff auf die Tastatur ausführt, und es so modifiziert, dass die Scroll-Lock-LED blinkt. Ich nannte es kvm, legte es in mein /usr/local/bin und bingo, keine Notwendigkeit, unter den Schreibtisch zu greifen, um das zu tun, wofür ich diesen Schalter gekauft habe. Hier ist die modifizierte main-Funktion der main.c-Datei des HID_LED_test_tool-Projekts. Beachten Sie, dass Sie wahrscheinlich eine Einstellung des Xcode-Projekts ändern müssen, um es mit dem 10.7 SDK zu kompilieren, da es für die Verwendung von 10.5 konfiguriert ist.
--- aktualisieren ---
Diese Lösung funktioniert (vielen Dank), aber Ihnen fehlt ein Teil des Codes. Sie müssen zu http://developer.apple.com/library/mac/#samplecode/HID_LED_test_tool/Listings/main_c.html gehen und die oben deklarierte Funktion abrufen.
Auch für alle, die nicht wissen, was damit zu tun ist, müssen Sie eine neue OS X-Befehlszeilenanwendung in Xcode erstellen und diese erstellen. Dann können Sie es ausführen, um Ihren KVM umzuschalten. Sie müssen dem Build auch CoreFoundation.framework und IOKit.framework hinzufügen.
--- Update beenden ---
--- Update 2 ---
Ich habe gerade festgestellt, dass Sie, anstatt eine "Befehlszeilen-OS X-Anwendung" zu erstellen, eine "Cocoa-Anwendung" erstellen und dann alles außer main.m löschen und den folgenden Code darin ausgeben, stattdessen eine "normale" Anwendung erstellen einer 'Befehlszeilen'-Anwendung, die schneller startet (ohne das Terminal zu laden) und an das Dock angedockt werden kann usw. Wenn Sie sich für die "Cocoa App" entscheiden, müssen Sie einige der Build-Einstellungen aktualisieren entsprechen den Dateien, die Sie löschen.
--- Update 2 beenden ---
//
// 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 */
Die obige Lösung hat bei mir auf neueren Versionen von MacOS nicht funktioniert, also habe ich ein einfaches Tool geschrieben, um das Double-Scroll-Lock-Signal an meinen KVM zu senden – es kann sogar einer Tastenkombination zugewiesen werden: https://github.com/ benjaminstout/osx-kvm
Kyle Cronin
r1x