Gamepad is recognised in Comand Line app, but not in a Cocoa app

I can't get GameController class working with my joystick. However I found a simple library https://github.com/suzukiplan/gamepad-osx using IOKit written is C, and got it working in swift command line app using the following code for main.swift:


import Foundation
   
    func  callback(_  type: Int32, _ page: Int32,  _ usage: Int32, _ value: Int32) -> Void  {
        print("Type: \(type); page: \(page), usage: \(usage), value: \(value)")
    }
   
    let ctx = gamepad_init(1, 1, 0)
   
    if ctx == nil {
        print("Init  failed")
        exit(4)
    }
   
    gamepad_set_callback(ctx, callback(_:_:_:_:))
   
    CFRunLoopRun()
    exit(0)


Console messages:


    attched device: Controller
    attched device: 2.4G RX
    attched device: VirtualHIDKeyboard
    Type: 2; page: 7, usage: 227, value: 1
    Type: 2; page: 7, usage: -1, value: 6
    ....



Since I am actually targeting MacOS app, I created a single-view Cocoa app with the following (very similar) code in Main View:


   import AppKit
   
    class MainView: NSView {
   
        required init?(coder decoder: NSCoder) {
            super.init(coder: decoder)

            print("Init started")
           
            let ctx = gamepad_init(1, 1, 0)
            if ctx == nil {
               print("Init  failed")
               return
           }
   
            print("Init succeeded")
   
            gamepad_set_callback(ctx, callback(_:_:_:_:))
   
            print("Callback attached")
   
        }
    }
   
    func  callback(_  type: Int32, _ page: Int32,  _ usage: Int32, _ value: Int32) -> Void  {
        print("Type: \(type); page: \(page), usage: \(usage), value: \(value)")
    }

The window appears, but console output has only that:


    Initialising started
    Initialising succeeded
    Callback attached


No attachment messages, and quite obviously no callback from device


Also tried an running and additional thread (DispatchQueue) in Main view and in AppDelegate


    func applicationDidFinishLaunching(_ aNotification: Notification) {
       DispatchQueue(label: "Joy").async {
            print("Initialising started")

            let ctx = gamepad_init(1, 1, 0)
            if ctx == nil {
                print("Init  failed")
                return
            }
       
            print("Initialising succeeded")
            gamepad_set_callback(ctx, callback(_:_:_:_:))
   
           CFRunLoopRun()
       }
    }


Again, no attachment messages


Any ideas?

Accepted Reply

The macOS > Cocoa App template has the App Sandbox enabled by default, and that will prevent you from accessing arbitrary hardware via I/O Kit. My recommendation is that you disable the sandbox temporarily to see if that fixes the problem.

If it does then you need to consider how to proceed here. If you’re just noodling around, running with the sandbox disabled is fine. If you plan to deploy your app widely, the sandbox is a good idea in general and required for the Mac App Store.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

The macOS > Cocoa App template has the App Sandbox enabled by default, and that will prevent you from accessing arbitrary hardware via I/O Kit. My recommendation is that you disable the sandbox temporarily to see if that fixes the problem.

If it does then you need to consider how to proceed here. If you’re just noodling around, running with the sandbox disabled is fine. If you plan to deploy your app widely, the sandbox is a good idea in general and required for the Mac App Store.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

You were absolutely right: once I disabled Sandbox, it started working.


As the good news, it also works with Sandbox on, provided USB is ticked. Hopefully this is acceptable for App Store.