2010年6月14日星期一

Mac下菜单栏没有响应的问题

测试P02的Mac下版本的时候,无意中发现菜单栏点击没有响应,虽说这不是什么大问题,毕竟玩游戏时基本不会点击上面的菜单栏,但是我感觉很不舒服。

我记得以前曾经遇到过这个问题并且解决了 (后来项目组的人把代码框架重构以后又引发出来了),但是我却不记得原因了。仔细查了一下,应该是没有使用系统提供的RunApplicationEventLoop(),而是用了自己编写的消息循环的缘故,代码如下:


        // 从队列中取消息,不要等待
        while (noErr != ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &theEvent))
        {
            // 获取到了消息,传送给分发器目标
            SendEventToEventTarget(theEvent, target);
            ReleaseEvent(theEvent);
        }

因为游戏有自己的主循环,每次循环时会PumpMessage,而RAEL是阻塞的,不适合使用。如果用Timer来控制每帧渲染的话,一是定时器本身不准确,另外则是会影响应用程序框架。倘若是一开始设计的时候倒也无妨,现在去修改消息框架显然不是什么好主意。

因为RAEL会自动给应用程序安装一个事件处理器,用于处理menu bar,而我无法模仿这个过程(没有RAEL的源代码),而且网络上也没有人给出RAEL的实现,所以只好迂回一下。

每次PumpMessage的时候,我不用自己写的消息循环,而是给自己发送一个自定义消息,然后启动RAEL,RAEL可以自行处理所有的消息,而我的自定义消息时会使用QuitApplicationEventLoop()通知退出消息循环。

代码如下:

    // 检测队列中是否有消息
    if (ReceiveNextEvent(0, NULL, kEventDurationNoWait, false, NULL) != noErr)
        // 没有消息,不需要处理
        return;

    // 给自己发送一个自定义消息,当收到此消息的时候则中止消息循环
    if (CreateEvent(NULL,
                    kEventClassCustomSignal,    // Class
                    0,                          // Kind
                    GetCurrentEventTime(),
                    0, &event) != noErr)
        // 无法创建事件 - 异常
        return;

    // 将消息发送到主队列中
    PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);
    ReleaseEvent(event);

    // 进入消息主循环
    RunApplicationEventLoop();

当然,事先需要注册对kEventClassCustomSignal的处理函数:

    switch (eventClass)
    {
    ///////////////////////////////////////////////////////////////////////////
    // kEventClassCustomSignal
    ///////////////////////////////////////////////////////////////////////////
    case kEventClassCustomSignal:
        // 这个消息是进入RunApplicationEventLoop之前应用程序自己发送的一条
        // 消息,用于通知消息循环结束了
        QuitApplicationEventLoop();
        break;

    ...

注册这个消息的代码很简单,略去。

这样做可以解决菜单栏没有响应的问题,但是仍然有一个缺点,就是RAEL在处理菜单消息时会阻塞,这样帧渲染就会停止,还是有一些遗憾。我想了一下,在不能深入了解Carbon实现过程的情况下,只能通过多线程解决这个问题。由一个线程专门处理RAEL,而消息回调的函数必须Take全局的Mutex以免出现回调的问题,当然这样仍然可能出现重入的问题,要想解决这个问题就要保证主循环在运行时RAEL没有访问临界资源,这样必须让主循环每次希望渲染时和RAEL进行同步,就目前来说,我暂时还不打算把事情弄得如此麻烦。

在做完以后,检索资料的时候发现原来有一个人已经给出了类似的解决方案了,而且似乎比我的方案更加完善,当然,他也没有解决会卡住主循环这个问题。具体资料见:developer.apple.com/legacy/mac/library/qa/qa2001/qa1061.html

Carbon毕竟是已经被淘汰的技术,也许使用Cocoa可以根治这个问题。但是目前应用程序框架有一些不太合理的地方,部分操作系统界面、消息相关的代码分散在几处,没有抽象成单独的中间键,这导致想切换到Cocoa的工作不是那么容易。

如果框架不合理,就会束缚代码的更新啊!

ps:Eclipse用起来感觉还可以,折腾了半天总算把符号功能弄好了(一开始检索总是会CPU 100%然后崩溃),搜索速度也算是马马虎虎。但是和VC比起来,感觉还是有一定的差距的。

没有评论:

发表评论