OSX не распознает клавишу Scroll Lock на моей внешней USB-клавиатуре

Я пытаюсь разделить свой монитор и клавиатуру/мышь между своим ПК (Windows 7) и Macbook Pro (1,1 - 2006, Snow Leopard) с помощью простого KVM-переключателя (Trendnet TK207).

При двойном нажатии Scroll Lock KVM переключается с одного компьютера на другой. Проблема в том, что это работает в Windows, но не в OS X. OS X вообще не распознает мою клавишу Scroll Lock (даже индикатор Scroll Lock не работает).

Любые идеи о том, как отладить/исправить эту проблему?

Итак, ваша клавиатура подключается к KVM, который подключен к вашему ПК и MBP? Если это так, проблема, похоже, связана с самим KVM. В разделе функций указано, что он также совместим с Mac.
OSX не распознает клавишу Scroll Lock. С KVM проблем нет. С линуксом тоже работает.

Ответы (2)

Я сделал небольшую утилиту командной строки, чтобы исправить это для моего Trendnet TK-204UK. Хитрость заключается в переключении состояния светодиода блокировки прокрутки на клавиатуре, потому что кажется, что оно контролируется переключателем. Mac обычно не переключает светодиод, когда вы нажимаете клавишу Scroll Lock. Я нашел образец программы Apple (инструмент для тестирования светодиодов HID), который выполняет низкоуровневый доступ к клавиатуре, и модифицировал его, чтобы он мигал светодиодом Scroll Lock. Я назвал его kvm, поместил в свой /usr/local/bin, и бинго не нужно лезть под стол, чтобы сделать то, для чего я купил этот переключатель. Вот измененная основная функция файла main.c проекта HID_LED_test_tool. Обратите внимание, что вам, вероятно, придется изменить параметр проекта Xcode, чтобы скомпилировать его с SDK 10.7, поскольку он настроен на использование версии 10.5.

--- обновлять ---

Это решение работает (большое спасибо), но вам не хватает части кода. Вам нужно перейти на http://developer.apple.com/library/mac/#samplecode/HID_LED_test_tool/Listings/main_c.html и взять функцию, объявленную вверху.

Также для тех, кто не знает, что с этим делать, вам нужно создать новое приложение OS X командной строки в Xcode и собрать его. Затем вы можете запустить его, чтобы переключить KVM. Вам также потребуется добавить в сборку CoreFoundation.framework и IOKit.framework.

--- конец обновления ---

--- обновление 2 ---

Только что понял, что если вместо создания «приложения командной строки OS X» вы создаете «приложение Cocoa», а затем удаляете все, кроме main.m, и выгружаете код ниже, вместо этого вы создадите «обычное» приложение приложения «командной строки», и оно будет запускаться быстрее (без загрузки терминала) и может быть закреплено на док-станции и т. д. Если вы используете «Cocoa App», вам придется обновить некоторые настройки сборки до соответствуют файлам, которые вы удаляете.

--- конец обновления 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 */
Вау, это отличное использование примера кода и приятное небольшое введение для тех, кто хочет попробовать короткую руку в Xcode. :-)
Этот кусок кода сработал. Я так и не смог заставить образец HID LED работать. Это абсолютно ничего не сделало. Спасибо!

Приведенное выше решение не сработало для меня в более новых версиях MacOS, поэтому я написал простой инструмент для отправки сигнала блокировки двойной прокрутки на мой KVM — его можно даже назначить на комбинацию клавиш: https://github.com/ Бенджаминстаут/osx-kvm