3 votes

OSX ne reconnaît pas la touche de verrouillage du défilement sur mon clavier USB externe

J'essaie de partager mon moniteur et mon clavier/souris entre mon PC (Windows 7) et mon Macbook Pro (1,1 - 2006, Snow Leopard) en utilisant un commutateur KVM basique (Trendnet TK207).

En appuyant deux fois sur le verrouillage du défilement, le KVM passe d'un ordinateur à l'autre. Le problème est que cela fonctionne sous Windows mais pas sous OS X. OS X ne reconnaît pas du tout ma touche Scroll Lock (même le voyant Scroll Lock ne fonctionne pas).

Des idées sur la manière de déboguer/résoudre ce problème ?

5voto

rzr Points 66

J'ai créé un petit utilitaire de ligne de commande pour résoudre ce problème pour mon Trendnet TK-204UK. L'astuce consiste à basculer l'état de la LED de verrouillage de défilement sur le clavier car elle semble être surveillée par le commutateur. Le Mac ne fait normalement pas basculer la LED lorsque vous appuyez sur la touche de verrouillage du défilement. J'ai trouvé un exemple de programme Apple (HID LED test tool) qui permet un accès de bas niveau au clavier et je l'ai modifié pour faire clignoter la LED de verrouillage du défilement. Je l'ai appelé kvm, je l'ai mis dans mon /usr/local/bin et bingo, plus besoin d'aller sous le bureau pour faire ce pour quoi j'ai acheté ce switch. Voici la fonction principale modifiée du fichier main.c du projet HID_LED_test_tool. Notez que vous devrez probablement changer un paramètre du projet Xcode pour le compiler avec le SDK 10.7 car il est configuré pour utiliser la 10.5.

--- mise à jour ---

Cette solution fonctionne (merci beaucoup) mais il manque une partie du code. Vous devez aller à http://developer.apple.com/library/mac/#samplecode/HID_LED_test_tool/Listings/main_c.html et saisir la fonction déclarée en haut de la page.

Pour ceux qui ne savent pas quoi faire, il faut créer une nouvelle application OS X en ligne de commande dans Xcode et la construire. Ensuite, vous pouvez l'exécuter pour basculer votre KVM. Vous devrez également ajouter CoreFoundation.framework et IOKit.framework à la compilation.

--- fin de la mise à jour ---

--- mise à jour 2 ---

Je viens de réaliser que si au lieu de créer une "application OS X en ligne de commande", vous créez une "application Cocoa" et que vous supprimez tout sauf le fichier main.m et que vous y insérez le code ci-dessous, vous créerez une application "normale" au lieu d'une application "en ligne de commande" et qui se lancera plus rapidement (sans charger le terminal) et qui pourra être ancrée dans le dock, etc. Si vous optez pour la "Cocoa App", vous devrez mettre à jour certains paramètres de construction pour qu'ils correspondent aux fichiers que vous supprimez.

--- fin de la mise à jour 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 */

0voto

elcollie Points 11

La solution ci-dessus n'a pas fonctionné pour moi sur les nouvelles versions de MacOS, j'ai donc écrit un outil simple pour envoyer le signal de verrouillage du double défilement à mon KVM - il peut même être assigné à une combinaison de touches : https://github.com/benjaminstout/osx-kvm

LesApples.com

LesApples est une communauté de Apple où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres utilisateurs d'appareils Apple, poser vos propres questions ou résoudre celles des autres.

Powered by:

X