gpt4 book ai didi

objective-c - 如何从 macOS 命令行工具使用 Apple 的 GameController 框架?

转载 作者:搜寻专家 更新时间:2023-10-30 19:47:22 26 4
gpt4 key购买 nike

我正在尝试让以下代码作为 macOS 命令行工具运行。重要的是这不是 Cocoa 应用程序,所以这不是一个选项。

这段相同的代码在具有 Cocoa App 目标的同一项目中完美运行,并检测到兼容的 Controller ,但是当作为命令行工具目标运行时,没有任何反应,API 显示没有连接 Controller 。

显然,其中一些是人为设计的...这只是我可以将其归结为最简单的,并且在它实际工作时有一些迹象表明正在发生的事情。

#import <Cocoa/Cocoa.h>
#import <GameController/GameController.h>


int main( int argc, const char * argv[] )
{
@autoreleasepool
{
NSApplication * application = [NSApplication sharedApplication];

NSNotificationCenter * center = [NSNotificationCenter defaultCenter];

[center addObserverForName: GCControllerDidConnectNotification
object: nil
queue: nil
usingBlock: ^(NSNotification * note) {
GCController * controller = note.object;
printf( "ATTACHED: %s\n", controller.vendorName.UTF8String );
}
];

[application finishLaunching];

bool shouldKeepRunning = true;
while (shouldKeepRunning)
{
printf( "." );

while (true)
{
NSEvent * event = [application
nextEventMatchingMask: NSEventMaskAny
untilDate: nil
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (event == NULL)
{
break;
}
else
{
[application sendEvent: event];
}
}

usleep( 100 * 1000 );
}
}

return 0;
}

我猜这与 Cocoa 应用程序的设置方式或事件循环的处理方式有关。或者可能有一些内部触发器初始化 GameController 框架。 API 似乎没有任何明确的方法来初始化它。

https://developer.apple.com/documentation/gamecontroller?language=objc

任何人都可以阐明我如何使它工作吗?

最终,这段代码确实需要在 Core Foundation 包中运行,所以如果它真的可以与 Core Foundation runloop 一起运行,那将是理想的。

-- 编辑--

为了更清楚地说明问题,我做了一个测试项目。有两个构建目标。 Cocoa 应用构建目标工作并接收 Controller 连接事件。另一个构建目标,只是一个简单的 CLI 应用程序,不起作用。它们都使用相同的源文件。它还包括两个代码路径,一个是传统的[NSApp run],另一个是上面的手动事件循环。结果是一样的。

https://www.dropbox.com/s/a6fw3nuegq7bg8x/ControllerTest.zip?dl=0

最佳答案

虽然每个线程都会创建一个运行循环(NSRunLoop 对于 Cocoa 应用程序)来处理输入事件,但循环不会自动启动。下面的代码使它与 [application run] 调用一起运行。当运行循环处理了适当的事件时,将引发通知。我将观察者安装在应用程序委托(delegate)中,只是为了确保所有其他系统此时都已完成初始化。

#import <Cocoa/Cocoa.h>
#import <GameController/GameController.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> @end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
[center addObserverForName: GCControllerDidConnectNotification
object: nil
queue: nil
usingBlock: ^(NSNotification * note) {
GCController * controller = note.object;
printf( "ATTACHED: %s\n", controller.vendorName.UTF8String );
}
];
}

@end


int main(int argc, const char * argv[]) {
@autoreleasepool {
NSApplication * application = [NSApplication sharedApplication]; // You can get rid of the variable and just use the global NSApp below instead
AppDelegate *delegate = [[AppDelegate alloc] init];
[application setDelegate:delegate];
[application run];
}
return 0;
}

更新对不起,我误解了这个问题。上面的代码适用于连接和断开 Controller ,但它没有使用应用程序启动时已经连接的设备正确初始化 [GCController controllers] 数组。

正如您所指出的,连接的设备在 Cocoa 应用程序中使用相同的代码发送通知,但在命令行应用程序中则不然。不同之处在于 Cocoa 应用程序会收到 didBecomeActive 通知,这会导致私有(private) _GCControllerManager (负责 GameControllerDaemon 发布的 NSXPCConnection 的对象)接收填充 controllersCBApplicationDidBecomeActive 消息> 数组。

无论如何,我尝试激活命令行应用程序以便它路由这些消息,但这没有用;应用程序需要在启动期间尽早发送 didBecomeActive 消息。

然后我尝试创建自己的 _GCGameController 并手动发送 CBApplicationDidBecomeActive;那种工作,除了应用程序最终有 2 个这样的 Controller ,并且连接被复制。

我需要访问私有(private) _GCGameController 对象,但我不知道谁拥有它,所以我无法直接引用它。

所以最后,我采用了 method swizzling。下面的代码更改了在终端应用程序初始化时调用的最后一个方法 _GCGameController startIdleWatchTimer,因此它随后发送 CBApplicationDidBecomeActive

我知道这不是一个很好的解决方案,使用各种 Apple 的内部代码,但也许它可以帮助某人获得更好的东西。在上一个代码中添加以下代码:

#import <objc/runtime.h>


@interface _GCControllerManager : NSObject
-(void) CBApplicationDidBecomeActive;
-(void) startIdleWatchTimer;
@end

@implementation _GCControllerManager (Extras)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];

SEL originalSelector = @selector(startIdleWatchTimer);
SEL swizzledSelector = @selector(myStartIdleWatchTimer);

Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));

if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}

- (void) myStartIdleWatchTimer {
[self myStartIdleWatchTimer];
[self CBApplicationDidBecomeActive];
}

@end

关于objective-c - 如何从 macOS 命令行工具使用 Apple 的 GameController 框架?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55226373/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com