首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Swift 3收听IOKit事件

使用Swift 3收听IOKit事件
EN

Code Review用户
提问于 2016-11-28 14:36:50
回答 1查看 2.7K关注 0票数 2

这是我在Swift/Xcode中的第一个程序。完整的源代码和xcode项目可以在GitHub中获得。

我想复习的部分是IOKeyEventMonitor.swift

代码语言:javascript
复制
//Copyright [2016] Jean Helou
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
import Foundation
import Carbon
import IOKit
import IOKit.usb
import IOKit.hid


struct IOKeyEventMonitorContext {
  var lastSeenSender: String
  init(lastSeenSender: String) {
    self.lastSeenSender = lastSeenSender
  }
}

class IOKeyEventMonitor {
  private
  let hidManager: IOHIDManager
  let notificationCenter: CFNotificationCenter
  let match: CFMutableDictionary

  var lastActiveKeyboard: String = ""
  var kb2is: [String: TISInputSource] = [String: TISInputSource]()


  private class func createDeviceMatchingDictionary( usagePage: Int, usage: Int) -> CFMutableDictionary {
    let dict = [
      kIOHIDDeviceUsageKey: usage,
      kIOHIDDeviceUsagePageKey: usagePage
    ] as NSDictionary

    return dict.mutableCopy() as! NSMutableDictionary;
  }

  init? ( usagePage: Int, usage: Int) {
    hidManager = IOHIDManagerCreate( kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone));
    notificationCenter = CFNotificationCenterGetDistributedCenter();
    match = IOKeyEventMonitor.createDeviceMatchingDictionary(usagePage: usagePage, usage: usage);
    IOHIDManagerSetDeviceMatching( hidManager, match);
  }

  deinit {
    // FIXME find out how to pass nil as an IOKit.IOHIDValueCallback to unregister the callback
    let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque());
    //IOHIDManagerRegisterInputValueCallback( hidManager, nil , context);
    CFNotificationCenterRemoveObserver(notificationCenter, context, CFNotificationName(kTISNotifySelectedKeyboardInputSourceChanged), nil);
  }

  func restoreInputSource(keyboard: String) -> Void {
    if let targetIs = kb2is[keyboard] {
      //print("set input source to \(targetIs) for keyboard \(keyboard)");
      TISSelectInputSource(targetIs)
    } else {
      self.storeInputSource(keyboard: keyboard);
    }
  }

  func storeInputSource(keyboard: String) -> Void {
    let currentSource: TISInputSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue();
    kb2is[keyboard] = currentSource;
  }

  func onInputSourceChanged() -> Void {
    self.storeInputSource(keyboard: self.lastActiveKeyboard);
  }

  func onKeyboardEvent(keyboard: String) -> Void {
    if(self.lastActiveKeyboard != keyboard) {
      //print("Active keyboard changed from \(self.lastActiveKeyboard) to \(keyboard)");
      self.restoreInputSource(keyboard: keyboard);
      self.lastActiveKeyboard = keyboard;
    }
  }


  func start() -> Void {
    let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque());
    let myHIDKeyboardCallback: IOHIDValueCallback = {
      (context, ioreturn, sender, value) in
      let selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(context!).takeUnretainedValue();
      let senderDevice = Unmanaged<IOHIDDevice>.fromOpaque(sender!).takeUnretainedValue();

      let vendorId = String(describing: IOHIDDeviceGetProperty(senderDevice, kIOHIDVendorIDKey as CFString));
      let productId = String(describing: IOHIDDeviceGetProperty(senderDevice, kIOHIDProductIDKey as CFString));
      let product = String(describing: IOHIDDeviceGetProperty(senderDevice, kIOHIDProductKey as CFString));
      let keyboard = "\(product)[\(vendorId)-\(productId)]";
      selfPtr.onKeyboardEvent(keyboard: keyboard);

    }
    let inputSourceChanged: CFNotificationCallback = {
      (center, observer, name, notif, userInfo) in
      let selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(observer!).takeUnretainedValue();
      selfPtr.onInputSourceChanged()
    }

    CFNotificationCenterAddObserver(notificationCenter,
                                    context, inputSourceChanged,
                                    kTISNotifySelectedKeyboardInputSourceChanged, nil,
                                    CFNotificationSuspensionBehavior.deliverImmediately);


    IOHIDManagerRegisterInputValueCallback( hidManager, myHIDKeyboardCallback, context);
    IOHIDManagerScheduleWithRunLoop( hidManager, CFRunLoopGetMain(), CFRunLoopMode.defaultMode!.rawValue);
    IOHIDManagerOpen( hidManager, IOOptionBits(kIOHIDOptionsTypeNone));
  }

}

你怎么把这个改写成惯用的斯威夫特?特别是如何改进start()函数?

EN

回答 1

Code Review用户

回答已采纳

发布于 2016-11-30 03:43:48

好吧,您编写的代码比许多已经滑动了一段时间的代码(可能包括我自己)都要好。所以,对于你的第一个节目,这是令人印象深刻的。

但是,对于我的口味来说,代码是有点麻烦的,也许还有几行空白行呢?还有,在一些地方跑得太远了.看看我是如何“包装”那些长功能婴儿车的。

另外,我发现一些挑剔的人:

括号一致性( x) (x) ( x )

功能声明间距不一致:

代码语言:javascript
复制
func x(){}

func y() {}


func z() {}

:忘在fixme

使用;

使用-> Void

//后无空格

在初始化器上不处于闭包时使用self

在自己的行中使用private,不确定是希望所有成员都是私有的,还是只希望第一个字段。(班级第一栏)

if(self.lastActiveKeyboard != keyboard)之间没有空隙吗?对于更复杂的语句,可以保存括号:if lastActiveKeyboard != keyboard {

为方便导航,没有// MARK:。我添加了一个,这样您就可以在属性+初始化器和函数之间快速跳转,因为您已经将它们分开了:)这是很好的!如果您还没有找到Show Document Items的键盘快捷方式,那么您将在列表中看到所有的FIXME:MARK:

错过了一种程序风格。在声明之前使用了storeInputSource .(在restore中)注意:这不是“必需的”,但它有助于您的逻辑流程(对您自己和读者),但有时您将不得不打破这取决于您的风格。

闭包通常内联到in,与此有不一致之处。我只会把它换行,如果你通过第90栏。

不一致的闭合大括号} (有时有空行,有时没有空格)

没有注释或文档(从现在起一个月你会理解这个代码吗?)那些可能会看到它的人呢?或者你要求帮助的人?)

所以大部分都是挑剔的。对这段代码没什么特别不懂的。没有什么特别的原因,我想确保您了解guard,因为其中确实有一个if let语句,但是它是对if let的一个很好的使用。只是想确保你也知道警卫的事。

这是我对您的代码的快速重新起草,自从您问:P之后,我在start()中添加了我的个人触觉。

代码语言:javascript
复制
// Copyright [2016] Jean Helou
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import Carbon
import IOKit
import IOKit.usb
import IOKit.hid


struct IOKeyEventMonitorContext {
  var lastSeenSender: String
  init(lastSeenSender: String) {
    self.lastSeenSender = lastSeenSender
  }
}

class IOKeyEventMonitor {
  private let hidManager: IOHIDManager

  let notificationCenter: CFNotificationCenter
  let match: CFMutableDictionary

  var lastActiveKeyboard: String = ""
  var kb2is: [String: TISInputSource] = [String: TISInputSource]()

  private class func createDeviceMatchingDictionary(usagePage: Int, usage: Int) -> CFMutableDictionary {
    let dict = [
      kIOHIDDeviceUsageKey: usage,
      kIOHIDDeviceUsagePageKey: usagePage
    ] as NSDictionary

    return dict.mutableCopy() as! NSMutableDictionary
  }

  init? (usagePage: Int, usage: Int) {
    hidManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
    notificationCenter = CFNotificationCenterGetDistributedCenter()
    match = IOKeyEventMonitor.createDeviceMatchingDictionary(usagePage: usagePage, usage: usage)
    IOHIDManagerSetDeviceMatching( hidManager, match)
  }

  deinit {
    // FIXME: Find out how to pass nil as an IOKit.IOHIDValueCallback to unregister the callback
    let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
    // IOHIDManagerRegisterInputValueCallback( hidManager, nil , context)
    CFNotificationCenterRemoveObserver(notificationCenter,
                                       context,
                                       CFNotificationName(kTISNotifySelectedKeyboardInputSourceChanged),
                                       nil)
  }

// MARK: - Funcs:

  func storeInputSource(keyboard: String) {
    let currentSource: TISInputSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue();
    kb2is[keyboard] = currentSource
  }

  func restoreInputSource(keyboard: String) {
    if let targetIs = kb2is[keyboard] {
      // print("set input source to \(targetIs) for keyboard \(keyboard)")
      TISSelectInputSource(targetIs)
    } else {
      storeInputSource(keyboard: keyboard)
    }
  }

  func onInputSourceChanged() {
    storeInputSource(keyboard: self.lastActiveKeyboard);
  }

  func onKeyboardEvent(keyboard: String) {
    if lastActiveKeyboard != keyboard {
      // print("Active keyboard changed from \(self.lastActiveKeyboard) to \(keyboard)")
      restoreInputSource(keyboard: keyboard)
      lastActiveKeyboard = keyboard
    }
  }

  func start() {
    let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

    let myHIDKeyboardCallback: IOHIDValueCallback = { (context, ioreturn, sender, value) in

      let
      selfPtr      = Unmanaged<IOKeyEventMonitor>.fromOpaque(context!).takeUnretainedValue(),

      senderDevice = Unmanaged<IOHIDDevice>.fromOpaque(sender!).takeUnretainedValue(),

      vendorId     = String(describing: IOHIDDeviceGetProperty(senderDevice,
                                                               kIOHIDVendorIDKey  as CFString)),
      productId    = String(describing: IOHIDDeviceGetProperty(senderDevice,
                                                               kIOHIDProductIDKey as CFString)),
      product      = String(describing: IOHIDDeviceGetProperty(senderDevice,
                                                               kIOHIDProductKey   as CFString)),
      keyboard     = "\(product)[\(vendorId)-\(productId)]"

      selfPtr.onKeyboardEvent(keyboard: keyboard)
    }

    let inputSourceChanged: CFNotificationCallback = { (center, observer, name, notif, userInfo) in
      let selfPtr = Unmanaged<IOKeyEventMonitor>.fromOpaque(observer!).takeUnretainedValue()
      selfPtr.onInputSourceChanged()
    }

    CFNotificationCenterAddObserver(notificationCenter,
                                    context,
                                    inputSourceChanged,
                                    kTISNotifySelectedKeyboardInputSourceChanged,
                                    nil,
                                    CFNotificationSuspensionBehavior.deliverImmediately)

    IOHIDManagerRegisterInputValueCallback(hidManager, myHIDKeyboardCallback, context)
    IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), CFRunLoopMode.defaultMode!.rawValue)
    IOHIDManagerOpen(hidManager, IOOptionBits(kIOHIDOptionsTypeNone))
  }
}

注意:由于导入,我无法编译它,所以我可能删除了一些太多的self (可能是在闭包中),但我认为我没有.

我要做的另一件事是typealias一些长得离谱的IO /碳类型,这将有助于使事情更接近左边,并使链接具有一定的可读性。例如,

代码语言:javascript
复制
typealias CFNCGDC = CFNotificationCenterGetDistributedCenter
// Or:
typealias CFNCGetDistCenter = CFNotificationCenterGetDistributedCenter
// Or if it is a function:
extension CrazyCarbonClass {
  public func CFNSShortFunc(/* all the prams */) {
    CFNotificationCenterLongAsHellFunc(/* prams */)
  }
}

但这只是我的偏好。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/148343

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档