编辑:
在做出了一些重大的新发现之后,我对该问题进行了大量编辑,并且该问题还没有任何答案。
根据历史/AFAIK,只有在使用Apple's requirements(kext)或以root用户身份运行kernel extension的情况下,才能使Mac在处于关闭显示模式时保持清醒状态,并且不符合command的要求。但是最近,我发现必须有另一种方法。我真的可以使用一些帮助来弄清楚如何使它在(100%免费,没有IAP)沙盒Mac App Store(MAS)兼容应用程序中使用。
我已经确认其他一些MAS应用程序也可以执行此操作,并且看起来它们可能正在将YES
写入名为clamshellSleepDisabled
的键中。也许还有其他欺骗手段导致键值设置为YES?我在IOPMrootDomain.cpp中找到了该函数:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
我想尝试一下,看看是否能完成所有操作,但是我真的不知道如何调用此函数。它当然不是IOPMrootDomain文档的一部分,并且我似乎找不到IOPMrootDomain文档中的函数的任何有用的示例代码,例如
setAggressiveness
或setPMAssertionLevel
。根据Console,这是幕后情况的一些证据:通过为其他项目改编ControlPlane的源代码,我在使用IOMProotDomain方面有一点经验,但是我对如何开始此工作感到迷惑。任何帮助将不胜感激。谢谢!
编辑:
有了@pmdj的贡献/答案,此问题已解决!
完整的示例项目:
https://github.com/x74353/CDMManager
最终变得非常简单/简单:
1.导入 header :
#import <IOKit/pwr_mgt/IOPMLib.h>
2.在您的实现文件中添加此功能:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
3.使用以下代码从实现中的其他位置调用上述函数:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
最佳答案
我对PM根域没有任何个人经验,但是我对IOKit拥有丰富的经验,所以这里是:
IOPMrootDomain::setDisableClamShellSleep()
。 setDisableClamShellSleep()
的站点的代码搜索迅速显示了RootDomainUserClient::externalMethod()
文件iokit/Kernel/RootDomainUserClient.cpp
中的位置。这肯定是有希望的,因为externalMethod()
是响应调用IOConnectCall*()
系列函数的用户空间程序而调用的。 让我们深入探讨:
IOReturn RootDomainUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
IOReturn ret = kIOReturnBadArgument;
switch (selector)
{
…
…
…
case kPMSetClamshellSleepState:
fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);
ret = kIOReturnSuccess;
break;
…
因此,要调用
setDisableClamShellSleep()
,您需要:IOPMrootDomain
的用户客户端连接。这看起来很简单,因为:IOPMrootDomain
的IOUserClientClass
属性为RootDomainUserClient
,因此默认情况下,来自用户空间的IOServiceOpen()
将创建一个RootDomainUserClient
实例。 IOPMrootDomain
不会覆盖newUserClient
成员函数,因此那里没有访问控制。 RootDomainUserClient::initWithTask()
似乎没有对连接用户空间进程施加任何限制(例如,root用户,代码签名)。 io_connect_t connection = IO_OBJECT_NULL;
IOReturn ret = IOServiceOpen(
root_domain_service,
current_task(),
0, // user client type, ignored
&connection);
kPMSetClamshellSleepState
。 arguments->scalarInput[0]
为零将调用setDisableClamShellSleep(false)
,而非零值将调用setDisableClamShellSleep(true)
。 IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint64_t inputs[] = { disable ? 1 : 0 };
return IOConnectCallScalarMethod(
root_domain_connection, kPMSetClamshellSleepState,
&inputs, 1, // 1 = length of array 'inputs'
NULL, &num_outputs);
}
io_connect_t
句柄后,请不要忘记对其进行IOServiceClose()
。 这应该使您可以打开或关闭翻盖式 sleep 。请注意,似乎没有任何条款可以将值自动重置为其原始状态,因此,如果您的程序崩溃或退出而没有自行清除,则最后设置的任何状态都将保留。从用户体验的角度来看,这可能不太好,所以也许尝试以某种方式进行防御,例如在崩溃处理程序中。
关于macos - 启用不符合Apple要求的封闭显示模式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59594123/