Рисование в NSView внутри обработчика событий
Я перепробовал все, что мог придумать, но не повезло.
У меня есть проект, который по сути является "интерпретатором" языка программирования. Таким образом, события передаются в цикл интерпретатора, интерпретированный код делает что угодно (в этом случае обновляет битовую карту памяти), затем цикл интерпретатора возвращается и возвращается обработчик событий. В конце концов вызывается drawRect приложения, и растровое изображение памяти обращается к NSView. Это все самое лучшее из того времени.
Но... есть несколько ситуаций, когда интерпретируемый "код" хочет вызвать короткую анимацию, и делает это, обновляя битовую карту памяти, используя usleep() в течение нескольких миллисекунд, обновляя битовую карту памяти, используя usleep() и т. д. Анимация занимает чуть меньше секунды, поэтому блокировка потоков не должна быть проблемой.
Проблема в том, что ни одна из анимаций не отображается, и экран не обновляется до тех пор, пока интерпретированный "код" не закончится и событие не вернется.
Функция сна, которая вызывается, когда интерпретированный код указывает, что она хочет спать, выглядит следующим образом:
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* для единственного представления окна. ПРИМЕЧАНИЕ. Да, часть приведенного выше кода является избыточной, я просто включил ее, чтобы показать, что я пробовал многочисленные комбинации, пытаясь указать ОС, что представление является грязным, и обновить его. Никто из них не работал.
Код drawRect(удаляющий весь код, который выполняет обычную проверку растрового изображения) выглядит следующим образом:
-(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) Интерпретатор, увидев вызов сна, вызывает Ksleep() (см. Выше).
4) Ksleep блокирует фокус, который, кажется, создает контекст, так как без этого отладчик выдает предупреждения о контексте 0x0 во время функции drawRect(), а с помощью lockFocus он не имеет и, по-видимому, имеет допустимое значение контекста,
5) Ksleep помечает представление как нуждающееся в обновлении и вызывает (по-разному) "display" и / или "drawRect" и т. Д.
6) Процедура drawRect ПОЛУЧАЕТ управление (точки останова указывают, что в этом отношении все работает нормально). 'inSleep' установлен правильно. Он выполняет все действия в drawRect, как и ожидалось. Но НИЧЕГО не показывает на дисплее, пока...
7) drawRect возвращается к Ksleep, время ожидания истекает, интерпретатор продолжает интерпретацию, интерпретированный код выполняет больше прорисовки и больше снов, примерно 10 раз (таким образом повторяя шаги 2-7 примерно 10 раз).
С момента запуска программы до тех пор, пока действие мыши не вызовет попытку "анимации", желтый жёлтый прямоугольник отображается на виде. Как только происходит щелчок мыши, вызывающий "анимацию", НИЧЕГО не обновляется в окне до тех пор, пока анимация полностью не завершится (хотя drawRect IS выполняется несколько раз в течение этой попытки анимации), ТОГДА прямоугольник становится синим. Но точки останова показывают, что выполнение проходит через drawRect(с true inSleep) каждый раз, когда вызывается подпрограмма KSleep().
Это нить вещь? (Программа явно не создает никаких потоков.)
Я не особо ищу предложения о том, как избежать структуры анимации /KSleep, я понимаю, что это не самый предпочтительный метод Macos для работы, но это попытка портировать старый проект из другого места и модифицировать "интерпретированный" код избежать этого не возможно.
Спасибо за любые идеи или предложения.
1 ответ
Решение, которое я нашел, состояло в том, чтобы поместить ВСЕ код интерпретатора во второй поток, освобождая основной поток, чтобы иметь возможность обновлять отображение. Несмотря на то, что процедура drawRect работала и рисовала, она не отображалась на дисплее до тех пор, пока текущий цикл "интерпретация команд" не завершился и не вернулся. На этой странице имеется достаточно информации по этому вопросу: https://developer.apple.com/library/content/technotes/tn2133/_index.html
Кроме того, все недействительные вызовы / операторы должны были быть сохранены / помещены в общий "наименьший прямоугольник, включающий все недействительные значения", а затем этот недействительный результат сделан ПОСЛЕ "интерпретации команд", возвращенной И / ИЛИ перед сном () было сделано.
PITA.