在事件处理程序中绘制到 NSView

Drawing to an NSView while inside an event handler

我已经尝试了所有我能想到的方法,但没有成功。

我有一个项目,本质上是一种编程语言的 'interpreter'。所以事件被传递到解释器循环,解释器代码执行任何操作(在本例中,更新内存位图),然后是解释器循环 returns 和事件处理程序 returns。最终,应用程序的 drawRect 被调用,内存位图被绘制到 NSView。 MOST 的时间都很好。

但是...在某些情况下,解释的 'code' 想要产生一个简短的动画,并通过更新内存位图来实现,usleep()'ing 几毫秒,更新内存位图、usleep()'ing 等。动画耗时不到一秒,因此线程阻塞应该不是问题。

问题是动画的 NONE 显示,屏幕直到解释 'code' 结束后才更新,并且事件 returns.

当解释的代码表明它想要睡眠时调用的睡眠函数如下所示:

void KSleep(DWORD tm) {

    if( [pView lockFocusIfCanDraw] ) {
        inSleep = true;
        [pView setNeedsDisplay:YES];
        [pView display];
        [pView drawRect:NSMakeRect(0, 0, pView.frame.size.width, pView.frame.size.height)];
        [pView unlockFocus];
    }
    usleep(tm*1000);
}

'inSleep' 是我为测试目的设置的全局变量,'pView' 是全局 NSView* 到 window 的唯一视图。注意:是的,上面的一些代码是多余的,我只是将它包括在内以表明我已经尝试了多种尝试向 OS 指示视图已变脏并更新它的组合。 None 其中有效果。

drawRect 代码(删除所有执行位图 运行-of-the-mill blitting 的代码)如下所示:

-(void)drawRect:(CGRect)rect {

    CGContextRef context = [[NSGRaphicsContext currentContext] graphicsPort];
    CGContextSaveGState(context);
    if( inSleep ) CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
    else CGContextSetRGBFillColor(context, 1.0, 1.0, 0.0, 1.0);
    CGContextFillRect(context, CGRectMake(0,0,200,200));
    CGContextRestoreGState(context);
}

所以:
1) -mouseDown() 事件发生,它用事件调用解释器。
2) 解释代码绘制到位图(我在这里忽略它,因为它对屏幕更新的工作或不工作并不重要),并调用 'sleep.'
3) 解释器,看到 'sleep' 调用,调用 Ksleep() (上面)。
4) Ksleep 锁定焦点,这似乎创建了一个上下文,因为没有它,调试器在 drawRect() 函数期间发出 0x0 上下文的警告,而对于 lockFocus,它没有并且似乎具有有效的上下文值.
5) Ksleep 将视图标记为需要更新并调用(不同地)'display' and/or 'drawRect' 等
6) drawRect 例程确实得到了控制(断点表明在这方面一切正常)。 'inSleep' 设置正确。它按预期逐步执行 drawRect 中的所有内容。但是显示屏上没有任何显示,直到...
7) drawRect returns to Ksleep,sleep超时,解释器继续解释,解释后的代码多画多休眠,大约10次(这样重复步骤2-7大约10次)。

从程序启动到鼠标操作导致 'animation' 尝试,视图中会绘制一个黄色矩形。一旦发生 'animation' 引起的鼠标单击,window 中的任何内容都不会更新,直到动画完全完成(即使 drawRect 在整个动画尝试过程中执行了多次),然后矩形变为蓝色。但是断点显示每次调用 KSleep() 例程时,执行都会通过 drawRect('inSleep' true)。

这是线程的东西吗? (该程序未明确创建任何线程。)

我不是特别在寻找如何避免 animation/KSleep 结构的建议,我意识到这不是首选的 Macos 处理方法,但这是从其他地方移植旧项目的尝试,并修改 'interpreted' 代码来避免这种情况是不可能的。

感谢任何想法或建议。

我找到的解决方案是将所有解释器代码放入第二个线程,释放主线程以便能够更新显示。尽管 drawRect 例程是 运行 并正在绘制,但直到当前 "command interpretation" 循环完成并返回后,它才被刷新到显示器上。此页面包含有关该主题的大量信息: https://developer.apple.com/library/content/technotes/tn2133/_index.html

此外,所有 'invalidate' calls/statements 都必须 saved/buffered 变成通用的 "smallest rectangle that includes all of the invalidates",然后在 "command interpretation" 返回后完成无效化AND/OR 在 sleep() 完成之前。

皮塔饼。