Где проджоб? Логика или накосячил с WinAPI?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.23 20:18
Оценка:
Здравствуйте!

Всем спасибо, баг пойман. Как обычно, там, где не ждали.

Вопрос закрыт
Маньяк Робокряк колесит по городу
Отредактировано 01.12.2023 23:34 Marty . Предыдущая версия .
Re: Где проджоб? Логика или накосячил с WinAPI?
От: kov_serg Россия  
Дата: 01.12.23 20:42
Оценка:
Здравствуйте, Marty, Вы писали:

M>Что я тут не так сделал, где косяк?

нету InvalidateRect
Re: Где проджоб? Логика или накосячил с WinAPI?
От: Melamed Россия  
Дата: 01.12.23 21:31
Оценка:
Здравствуйте, Marty, Вы писали:
И еще одно замечание. Если вам нужно точно прорисовывать 30 раз в секунду, то лучше использовать мультимедийный таймер и рисовать в фоновом потоке, чем использовать системный таймер и сообщения WM_TIMER по двум причинам:
1. Сообщение WM_TIMER имеет наименьший приоритет.
2. Если я правильно помню, его минимальная разрешающая способность чуть больше 18 миллисекунд, а 30 раз в секунду — это 33,3 миллисекунд. Павда эта информация, может быть, устарела так как относилась к Windows 95/98. Для более поздних Windows я такой информации не видел

M>Здравствуйте!


M>Есть следующая логика.


M>Раз в 30 в секунду файрится OnTimer, который делает Invalidate для всей клиентской части. В OnPaint рисуется сцена, с дабл буфферингом:


M>
  DoPaint
M>
M>    void DoPaint(CDCHandle dc)
M>    {
M>        CPoint clientSize = getClientSizePoint();
M>        cachedBitmapSize  = clientSize;

M>        CDC memDc = ::CreateCompatibleDC(dc.m_hDC);

M>        HBITMAP hMemBmp  = ::CreateCompatibleBitmap ( dc.m_hDC, clientSize.x, clientSize.y );
M>        HBITMAP hOldBmp = memDc.SelectBitmap(hMemBmp);

M>        RECT clRect;
M>        clRect.left   = 0;
M>        clRect.top    = 0;
M>        clRect.right  = clientSize.x;
M>        clRect.bottom = clientSize.y;
M>        //::FillRect(memDc, &clRect, (HBRUSH)COLOR_WINDOW);
M>        memDc.FillRect(&clRect, (HBRUSH)COLOR_WINDOW);
M>        //memDc.FillRect(&clRect, (HBRUSH)COLOR_HOTLIGHT);
        
M>        auto idc = makeMultiDc(memDc.m_hDC, marty_draw_context::HdcReleaseMode::doNothing, m_hWnd);

M>        IDrawContext *pDc = &idc;

M>        DoPaintImpl(pDc);

M>        ::BitBlt( dc.m_hDC       // A handle to the destination device context.
M>                , 0, 0           //dstX, dstY   // The x/y-coordinates, in logical units, of the upper-left corner of the destination rectangle.
M>                , clientSize.x, clientSize.y         // The width/height, in logical units, of the source and destination rectangles.
M>                , memDc.m_hDC    // hdcCopyFrom  // A handle to the source device context.
M>                , 0, 0           // The x/y-coordinate, in logical units, of the upper-left corner of the source rectangle.
M>                , SRCCOPY        // A raster-operation code - Copies the source rectangle directly to the destination rectangle.
M>                );

M>        ::SelectObject(memDc, hOldBmp);

M>        if (hbitmapCached)
M>        {
M>            ::DeleteObject(hbitmapCached);
M>        }

M>        hbitmapCached = hMemBmp;

M>    }
M>



M>Отрисованную тут сцену я сохраняю в hbitmapCached.


M>Далее. Есть события мыши. Я не хочу перерисовывать сцену по событиям мыши, хочу рисовать допустим только какой-то rect, от нажатия кнопки мыши до текущего положения, или, даже, просто циркуль-кругляшик на месте курсора, если ничего не нажато на мышке.


M>Для этого я по событиям мыши делаю GetDC(), создаю компат MemDc, копирую туда закешированную картинку, и вызываю событие мыши


M>
  OnMouseMoveEvents/OnMouseButtonEvents
M>
M>    void OnMouseMoveEvents( marty_draw_context::MouseMoveEventType    moveEventType
M>                          , marty_draw_context::MouseButtonStateFlags mbStateFlags
M>                          , const CPoint &point
M>                          )
M>    {
M>        // При marty_draw_context::MouseMoveEventType::leave mbStateFlags и point не имеют валидного значения

M>        if (scriptSomethingFailed)
M>        {
M>            return;
M>        }

M>        try
M>        {
M>            #if defined(VIEW04_LOG_SQUIRREL_CALLS)
M>            using umba::lout;
M>            lout << "try squirrel Game.onMouseMoveEvents, moveEventType: " << enum_serialize(moveEventType) << ", mbStateFlags: " << enum_serialize_flags(mbStateFlags) << "\n";
M>            umba::lout.flush();
M>            #endif

M>            ssq::Function sqOnMouseMoveEvents = marty_simplesquirrel::findFunc(vm, "Game.onMouseMoveEvents");

M>            auto idc = makeDcForMouseHandler();
M>            marty_draw_context::IDrawContext *pDc = &idc;
M>            appHost.sys.info.graphicsBackendInfo.name = marty_simplesquirrel::to_sqstring(pDc->getEngineName());
M>            prepareDrawContext(pDc);

M>            marty_draw_context::simplesquirrel::DrawingContext sqDc = marty_draw_context::simplesquirrel::DrawingContext(vm.getHandle(), pDc);

M>            CPoint clientSize = getClientSizePoint();

M>            sqDc.ctxSizeX = (int)clientSize.x;
M>            sqDc.ctxSizeY = (int)clientSize.y;

M>            auto res = vm.callFunc(sqOnMouseMoveEvents, vm,  /* appHost,  */ &sqDc, (int)moveEventType, (int)mbStateFlags, marty_draw_context::simplesquirrel::DrawingCoords((float)point.x, (float)point.y));

M>            marty_draw_context::CallbackResultFlags resultFlags = (marty_draw_context::CallbackResultFlags)marty_simplesquirrel::fromObjectConvertHelper<int>(res, _SC("Game::onMouseMoveEvents returned"));
M>            processCallbackResult(resultFlags);

M>            setStatusReady();

M>        }
M>        MARTY_DC_IMPL_WIN32_CATCH_LOG_BULKA_EXCEPTIONS_EX(scriptSomethingFailed)
M>    }

M>    void OnMouseButtonEvents( marty_draw_context::MouseButton           mouseButton
M>                            , marty_draw_context::MouseButtonEvent      buttonEvent
M>                            , marty_draw_context::MouseButtonStateFlags mbStateFlags
M>                            , const CPoint &point
M>                            )
M>    {
M>        if (scriptSomethingFailed)
M>        {
M>            return;
M>        }

M>        try
M>        {
M>            #if defined(VIEW04_LOG_SQUIRREL_CALLS)
M>            using umba::lout;
M>            lout << "try squirrel Game.onMouseButtonEvents\n";
M>            umba::lout.flush();
M>            #endif

M>            ssq::Function sqOnMouseButtonEvents = marty_simplesquirrel::findFunc(vm, "Game.onMouseButtonEvents");

M>            auto idc = makeDcForMouseHandler();
M>            marty_draw_context::IDrawContext *pDc = &idc;
M>            appHost.sys.info.graphicsBackendInfo.name = marty_simplesquirrel::to_sqstring(pDc->getEngineName());
M>            prepareDrawContext(pDc);

M>            marty_draw_context::simplesquirrel::DrawingContext sqDc = marty_draw_context::simplesquirrel::DrawingContext(vm.getHandle(), pDc);

M>            CPoint clientSize = getClientSizePoint();

M>            sqDc.ctxSizeX = (int)clientSize.x;
M>            sqDc.ctxSizeY = (int)clientSize.y;

M>            auto res = vm.callFunc(sqOnMouseButtonEvents, vm,  /* appHost,  */ &sqDc, (int)mouseButton, (int)buttonEvent, (int)mbStateFlags, marty_draw_context::simplesquirrel::DrawingCoords((float)point.x, (float)point.y));

M>            marty_draw_context::CallbackResultFlags resultFlags = (marty_draw_context::CallbackResultFlags)marty_simplesquirrel::fromObjectConvertHelper<int>(res, _SC("Game::onMouseButtonEvents returned"));
M>            processCallbackResult(resultFlags);

M>            setStatusReady();

M>        }
M>        MARTY_DC_IMPL_WIN32_CATCH_LOG_BULKA_EXCEPTIONS_EX(scriptSomethingFailed)
M>    }
M>



M>По событиям мыши я запрещаю update-таймер, если надо

M>
  processCallbackResult
M>
M>    void processCallbackResult(marty_draw_context::CallbackResultFlags resultFlags)
M>    {
M>        if ((resultFlags&marty_draw_context::CallbackResultFlags::repaint)!=0)
M>        {
M>            invalidateClientArea();
M>        }

M>        if ((resultFlags&marty_draw_context::CallbackResultFlags::captureMouse)!=0)
M>        {
M>            ::SetCapture(m_hWnd);
M>        }
M>        else if ((resultFlags&marty_draw_context::CallbackResultFlags::releaseCapture)!=0)
M>        {
M>            ::ReleaseCapture();
M>        }

M>        if ((resultFlags&marty_draw_context::CallbackResultFlags::disableTimerUpdate)!=0)
M>        {
M>            m_timerUpdateDisabled = true;
M>        }
M>        else if ((resultFlags&marty_draw_context::CallbackResultFlags::enableTimerUpdate)!=0)
M>        {
M>            m_timerUpdateDisabled = false;
M>        }
M>    }
M>




M>Я проверял, таймерное событие перестаёт обрабатываться. Никакой onUpdate не вызывается — он бы мог перетирать то, что рисуется по мышахиным событиям. По идее, остаются только события мыши. Они происходят, тоже проверил. Все координаты и все события отдаются в обработики. Всё нормас. Только отрисовка поверх кешированного битмапа в обработчике мыши не работает. DC и копирование кеша картинки делаю так:

M>
  makeDcForMouseHandler/prepareDrawContext/copyCachedBitmapToHdc/prepareDrawContext
M>
M>    marty_draw_context::MultiDrawContext makeMultiDc(HDC hdc, marty_draw_context::HdcReleaseMode hdcReleaseMode, HWND hwnd)
M>    {
M>        #ifdef TEST_DC_USE_GDIPLUS
M>            return marty_draw_context::makeMultiDrawContext(hdc, true  /* prefferGdiPlus */, hdcReleaseMode, hwnd);
M>        #else
M>            return marty_draw_context::makeMultiDrawContext(hdc, false /* prefferGdiPlus */, hdcReleaseMode, hwnd);
M>        #endif
M>    }

M>    void copyCachedBitmapToHdc(HDC hdc)
M>    {
M>        if (!hbitmapCached)
M>        {
M>            #if defined(VIEW04_LOG_SQUIRREL_CALLS)
M>            using umba::lout;
M>            lout << "copyCachedBitmapToHdc, !hbitmapCached\n";
M>            umba::lout.flush();
M>            #endif

M>            return;
M>        }

M>        CDC memDc       = ::CreateCompatibleDC(hdc);
M>        HBITMAP hOldBmp = memDc.SelectBitmap(hbitmapCached);

M>        ::BitBlt( hdc            // A handle to the destination device context.
M>                , 0, 0           //dstX, dstY   // The x/y-coordinates, in logical units, of the upper-left corner of the destination rectangle.
M>                , cachedBitmapSize.x, cachedBitmapSize.y         // The width/height, in logical units, of the source and destination rectangles.
M>                , memDc.m_hDC    // hdcCopyFrom  // A handle to the source device context.
M>                , 0, 0           // The x/y-coordinate, in logical units, of the upper-left corner of the source rectangle.
M>                , SRCCOPY        // A raster-operation code - Copies the source rectangle directly to the destination rectangle.
M>                );

M>        ::SelectObject(memDc, hOldBmp);
M>    }

M>    void prepareDrawContext( marty_draw_context::IDrawContext *pDc )
M>    {
        pDc->>setStringEncoding("UTF-8");
        pDc->>setBkMode( BkMode::transparent );
        pDc->>setSmoothingMode(SmoothingMode::antiAlias); // highSpeed highQuality antiAlias defMode none
M>    }

M>    marty_draw_context::MultiDrawContext makeDcForMouseHandler()
M>    {
M>        HDC hdc = ::GetDC(m_hWnd);
M>        copyCachedBitmapToHdc(hdc);
M>        marty_draw_context::MultiDrawContext mdc = makeMultiDc(hdc, marty_draw_context::HdcReleaseMode::releaseDc, m_hWnd);
M>        return mdc;
M>    }
M>



M>Вроде логика продумана нормас, реализация вроде тоже без косяков, но мышахины события не работают, как задумано — по нажатию ЛКМ не рисуется условный rect мышахиного выделения. Сцена есть, никуда не пропадает, координаты мыши, состояние кнопок и тп — исправно передаются в обработчики, но долбаного rect'а мышахиного выделения поверх сцены не происходит.


M>Что я тут не так сделал, где косяк?


M>Проект сам тут, кому интересно поковырять — https://github.com/al-martyn1/marty_dc_impl_win32

M>Надо зайти в tests/_libs и запустить clone_libs_http.bat
M>Проект сам — tests/tests-msvc2019.sln
M>Используется WTL, путь к WTL должен быть задан через переменную среды %WTL%
M>Версия WTL плюс-минус насрать, у меня 10ая, но ничего особого нового не используется, думаю, и седьмая сойдёт — там после седьмой версии кроме собственно номера версии ничего особо и не менялось
Re[2]: Где проджоб? Логика или накосячил с WinAPI?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.23 23:14
Оценка:
Здравствуйте, kov_serg, Вы писали:

M>>Что я тут не так сделал, где косяк?

_>нету InvalidateRect

Есть. Косяк найден, как обычно не там, где искали
Маньяк Робокряк колесит по городу
Re[2]: Где проджоб? Логика или накосячил с WinAPI?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.23 23:16
Оценка:
Здравствуйте, Melamed, Вы писали:

M>И еще одно замечание. Если вам нужно точно прорисовывать 30 раз в секунду, то лучше использовать мультимедийный таймер и рисовать в фоновом потоке, чем использовать системный таймер и сообщения WM_TIMER по двум причинам:

M>1. Сообщение WM_TIMER имеет наименьший приоритет.
M>2. Если я правильно помню, его минимальная разрешающая способность чуть больше 18 миллисекунд, а 30 раз в секунду — это 33,3 миллисекунд. Павда эта информация, может быть, устарела так как относилась к Windows 95/98. Для более поздних Windows я такой информации не видел

Спасибо, я в курсе, мне наплевать на точность таймера
Маньяк Робокряк колесит по городу
Re[2]: Где проджоб? Логика или накосячил с WinAPI?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.23 23:22
Оценка:
Здравствуйте, Melamed, Вы писали:

M>И еще одно замечание.


Ну и извини. Может, тебе стоит сосредоточится на своей курсовой?
Маньяк Робокряк колесит по городу
Re[3]: Где проджоб? Логика или накосячил с WinAPI?
От: Melamed Россия  
Дата: 02.12.23 14:01
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, Melamed, Вы писали:


M>>И еще одно замечание.


M>Ну и извини. Может, тебе стоит сосредоточится на своей курсовой?

Спасибо. Все курсовые мною написаны. А сейчас пишу большой проект.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.