编辑:
在做出了一些重大的新发现之后,我对该问题进行了大量编辑,并且该问题还没有任何答案。

根据历史/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文档中的函数的任何有用的示例代码,例如setAggressivenesssetPMAssertionLevel。根据Console,这是幕后情况的一些证据:

macos - 启用不符合Apple要求的封闭显示模式-LMLPHP

通过为其他项目改编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的用户客户端连接。这看起来很简单,因为:
  • 经过检查,IOPMrootDomainIOUserClientClass属性为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/

    10-09 06:39