2010-06-03 9 views
6

Tôi đang sử dụng CGEventTapCreate để "lấy cắp" các khóa phương tiện từ iTunes khi ứng dụng của tôi đang chạy. Mã bên trong của cuộc gọi lại mà tôi chuyển đến CGEventTapCreate kiểm tra sự kiện và nếu phát hiện thấy đó là một trong các khóa phương tiện, hãy đăng thông báo thích hợp lên trung tâm thông báo mặc định.CGEventTapTạo vỡ xuống một cách bí ẩn với sự kiện "từ khóa"

Bây giờ, tính năng này hoạt động tốt nếu tôi đăng thông báo cho sự kiện "khóa". Nếu tôi làm điều đó cho các sự kiện "từ khóa", cuối cùng ứng dụng của tôi sẽ ngừng nhận các sự kiện quan trọng của phương tiện truyền thông và iTunes sẽ tiếp quản. Bất kỳ ý tưởng về những gì có thể gây ra điều này? Phần có liên quan của mã bên dưới là

enum { 
... 
    PlayPauseKeyDown = 0x100A00, 
    PlayPauseKeyUp = 0x100B00, 
... 
}; 

static CGEventRef event_tap_callback(CGEventTapProxy proxy, 
            CGEventType type, 
            CGEventRef event, 
            void *refcon) 
{ 
    if (!(type == NX_SYSDEFINED) || (type == NX_KEYUP) || (type == NX_KEYDOWN)) 
     return event; 

    NSEvent* keyEvent = [NSEvent eventWithCGEvent: event]; 
    if (keyEvent.type != NSSystemDefined) return event; 

    switch(keyEvent.data1) 
    { 
    case PlayPauseKeyUp: // <--- this works reliably 
    //case PlayPauseKeyDown: // <--- this will break eventually 
     post_notification(@"PlayPauseMediaKeyPressed", nil, nil); 
     return NULL; 

    ... and so on ... 
+0

Dường như là vấn đề về thời gian. Thay thế post_notification bởi giấc ngủ (1) và có nó, sau khi một vài phím nhấn iTunes đánh cắp các phím phương tiện truyền thông trở lại nếu tôi đang sử dụng PlayPauseKeyDown. Vẫn hoạt động nếu tôi đang sử dụng PlayPauseKeyUp. – svintus

Trả lời

9

Có điều gì đó giết sự kiện của tôi nếu cuộc gọi lại mất quá nhiều thời gian không?

Một số người nghi ngờ rằng Snow Leopard có lỗi đôi khi vô hiệu hóa các sự kiện nhấn ngay cả khi chúng không mất quá nhiều thời gian. Để xử lý điều đó, bạn có thể xem loại sự kiện kCGEventTapDisabledByTimeout và phản hồi bằng cách bật lại vòi nước của mình với CGEventTapEnable.

+1

Yep, đây là nó. kCGEventTapDisabledByTimeout thực hiện thủ thuật – svintus

+0

Cảm ơn bạn! Điều đó khiến tôi phát điên! –

3

Trước hết, tại sao "nếu" cho phép các sự kiện từ khóa và khóa đầu tiên của bạn vượt qua? Tuy nhiên, "if" thứ hai của bạn chỉ cho phép các sự kiện hệ thống truyền qua. Vì vậy, đối với tất cả các sự kiện key-down/-up bạn tạo một NSEvent, chỉ để thả sự kiện một dòng xuống dưới. Điều đó có ý nghĩa rất ít. Một sự kiện Tap nên luôn luôn càng nhanh càng tốt, nếu không nó sẽ làm chậm tất cả sự kiện xử lý của toàn bộ hệ thống. Gọi lại của bạn thậm chí không nên được gọi cho các sự kiện key-down/-up, vì các sự kiện hệ thống không phải là các sự kiện key-down/-up, chúng là các sự kiện hệ thống. Nếu chúng là các sự kiện quan trọng, bạn chắc chắn sẽ không bao giờ truy cập dữ liệu1, mà thay vào đó hãy sử dụng các phương thức "type" và "keyCode" để lấy thông tin liên quan từ chúng.

static CGEventRef event_tap_callback(CGEventTapProxy proxy, 
            CGEventType type, 
            CGEventRef event, 
            void *refcon) 
{ 
    NSEvent * sysEvent; 

    // No event we care for? return ASAP 
    if (type != NX_SYSDEFINED) return event; 

    sysEvent = [NSEvent eventWithCGEvent:event]; 
    // No need to test event type, we know it is NSSystemDefined, 
    // becuase that is the same as NX_SYSDEFINED 

Ngoài ra, bạn không thể xác định nếu đó là đúng loại sự kiện bằng cách chỉ nhìn vào dữ liệu, bạn cũng phải xác minh subtype, mà phải 8 cho các loại hình sự kiện:

if ([sysEvent subtype] != 8) return event; 

bước logic tiếp theo là để phân chia các dữ liệu lên thành các thành phần của nó:

int data = [sysEvent data1]; 
    int keyCode = (data & 0xFFFF0000) >> 16; 
    int keyFlags = (data & 0xFFFF); 
    int keyState = (keyFlags & 0xFF00) >> 8; 
    BOOL keyIsRepeat = (keyFlags & 0x1) > 0; 

và có thể bạn không quan tâm đến sự kiện lặp lại chìa khóa (có nghĩa là khi tôi giữ chìa khóa ép và nó giữ gửi cùng một sự kiện lặp đi lặp lại).

// You probably won't care for repeating events 
    if (keyIsRepeat) return event; 

Cuối cùng, bạn không nên xác định bất kỳ liên tục riêng, hệ thống có sẵn sàng để sử dụng hằng số đối với những phím:

// Analyze the key 
    switch (keyCode) { 
    case NX_KEYTYPE_PLAY: 
     // Play/Pause key 
     if (keyState == 0x0A) { 
     // Key down 
     // ...do your stuff here... 
     return NULL; 
     } else if (keyState == 0x0B) { 
     // Key Up 
     // ...do your stuff here... 
     return NULL; 
     } 
     // If neither down nor up, we don't know 
     // what it is and better ignore it 
     break; 


    case NX_KEYTYPE_FAST: 
     // (Fast) Forward 
     break; 

    case NX_KEYTYPE_REWIND: 
     // Rewind key 
     break; 
    } 

    // If we get here, we have not handled 
    // the event and want system to handle it 
    return event; 
} 

Và nếu điều này vẫn không hoạt động, câu hỏi tiếp theo của tôi sẽ là những gì chức năng post_notification của bạn trông giống như và bạn cũng thấy vấn đề được mô tả nếu bạn không gọi post_notification ở đó, nhưng chỉ cần thực hiện một cuộc gọi NSLog về sự kiện bạn vừa thấy?

+0

Cảm ơn, Mecki! Tôi đã sửa mã của tôi, nhưng vấn đề vẫn tồn tại. Nếu tôi đặt post_notification theo "if (keyState == 0x0A) ...", iTunes sẽ" tiếp quản "các phím media cuối cùng, nếu tôi đặt nó dưới" if (keyState == 0x0B) ", mọi thứ có vẻ hoạt động tốt. nếu tôi thay thế post_notification với giấc ngủ (1), vấn đề xảy ra nhanh hơn nhiều.Điều gì đó giết sự kiện của tôi tap nếu gọi lại mất quá lâu? Đang gửi một thông báo quá chậm để được gọi từ callback? – svintus

0

Trong trình xử lý của bạn, hãy kiểm tra loại sau và chỉ bật lại trình nghe.

if (type == kCGEventTapDisabledByTimeout) { 
    NSLog(@"Event Taps Disabled! Re-enabling"); 
      CGEventTapEnable(eventTap, true); 
    return event; 
}