• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DemoBytom / DemoEngine / 24940020806

25 Apr 2026 08:30PM UTC coverage: 28.251% (-1.5%) from 29.75%
24940020806

push

coveralls.net

DemoBytom
Remove old `StaThreadService` and related codebase

Superseeded by `WindowsMessagePump`

1208 of 4276 relevant lines covered (28.25%)

0.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/Demo.Engine.Core/Services/MainLoopService.cs
1
// Copyright © Michał Dembski and contributors.
2
// Distributed under MIT license. See LICENSE file in the root for more information.
3

4
using System.Diagnostics;
5
using Demo.Engine.Core.Features.StaThread;
6
using Demo.Engine.Core.Interfaces;
7
using Demo.Engine.Core.Interfaces.Rendering;
8
using Demo.Engine.Core.Requests.Keyboard;
9
using Demo.Engine.Core.ValueObjects;
10
using Mediator;
11
using Microsoft.Extensions.Logging;
12

13
namespace Demo.Engine.Core.Services;
14

15
internal sealed class MainLoopService
16
    : IMainLoopService,
17
      IAsyncDisposable
18
{
19
    private readonly ILogger<MainLoopService> _logger;
20
    private readonly IStaThreadWriter _staThreadWriter;
21
    private readonly IMediator _mediator;
22
    private readonly IFpsTimer _fpsTimer;
23
    private readonly IMainLoopLifetime _mainLoopLifetime;
24
    private readonly ILoopJob _loopJob;
25
    private bool _disposedValue;
26

27
    public Task ExecutingTask { get; }
28

29
    public MainLoopService(
1✔
30
        ILogger<MainLoopService> logger,
1✔
31
        IStaThreadWriter staThreadWriter,
1✔
32
        IMediator mediator,
1✔
33
        IFpsTimer fpsTimer,
1✔
34
        IRenderingEngine renderingEngine,
1✔
35
        IMainLoopLifetime mainLoopLifetime,
1✔
36
        ILoopJob loopJob)
1✔
37
    {
38
        _logger = logger;
1✔
39
        _staThreadWriter = staThreadWriter;
1✔
40
        _mediator = mediator;
1✔
41
        _fpsTimer = fpsTimer;
1✔
42
        _mainLoopLifetime = mainLoopLifetime;
1✔
43
        _loopJob = loopJob;
1✔
44
        ExecutingTask = Task.Run(async () =>
1✔
45
        {
1✔
46
            try
1✔
47
            {
1✔
48
                await DoAsync(renderingEngine);
1✔
49
            }
1✔
50
            catch (TaskCanceledException)
×
51
            {
1✔
52
                _mainLoopLifetime.Cancel();
×
53
            }
×
54
            catch (Exception ex)
×
55
            {
1✔
56
                _logger.LogMainLoopFailedWithError(ex);
×
57
                _mainLoopLifetime.Cancel();
×
58
                //throw;
1✔
59
            }
×
60
        });
1✔
61
    }
1✔
62

63
    private async Task DoAsync(
64
        IRenderingEngine renderingEngine)
65
    {
66
        var keyboardHandle = await _mediator.Send(new KeyboardHandleRequest(), CancellationToken.None);
1✔
67
        var keyboardCharCache = await _mediator.Send(new KeyboardCharCacheRequest(), CancellationToken.None);
1✔
68

69
        var surfaces = new List<RenderingSurfaceId>
1✔
70
        {
1✔
71
            await _staThreadWriter.CreateSurface(
1✔
72
                renderingEngine,
1✔
73
                _mainLoopLifetime.Token),
1✔
74
            //await _staThreadWriter.CreateSurface(
1✔
75
            //    _mainLoopLifetime.Token),
1✔
76
        };
1✔
77

78
        if (surfaces is [var mainFormId, ..]
1✔
79
            && renderingEngine.TryGetRenderingSurface(mainFormId, out var mainForm))
1✔
80
        {
81
            mainForm.RenderingControl.RenderingFormClosed += (_, _) => _mainLoopLifetime.Cancel();
1✔
82
        }
83

84
        var previous = Stopwatch.GetTimestamp();
1✔
85
        var lag = TimeSpan.Zero;
1✔
86

87
        var msPerUpdate = TimeSpan.FromSeconds(1) / 60;
1✔
88

89
        var cnt = 0;
1✔
90
        while (
1✔
91
            //&& IsRunning
1✔
92
            !_disposedValue
1✔
93
            && !_mainLoopLifetime.Token.IsCancellationRequested)
1✔
94
        {
95
            var current = Stopwatch.GetTimestamp();
1✔
96
            var elapsed = Stopwatch.GetElapsedTime(previous, current);
1✔
97
            previous = current;
1✔
98
            lag += elapsed;
1✔
99

100
            //process input
101
            // TODO!
102

103
            while (lag >= msPerUpdate)
1✔
104
            {
105
                //Update
106
                // TODO - fix the UPS timer.. somehow :D
107
                _fpsTimer.StopUpdateTimer();
1✔
108
                RenderingSurfaceId? sId = null;
1✔
109

110
                ++cnt;
1✔
111
                foreach (var renderingSurfaceId in surfaces)
1✔
112
                {
113
                    if (!renderingEngine.TryGetRenderingSurface(
114
                        renderingSurfaceId,
1✔
115
                        out var renderingSurface))
1✔
116
                    {
117
                        _logger.LogRenderingSurfaceNotFound(
×
118
                            renderingSurfaceId);
×
119
                        break;
×
120
                    }
121

122
                    await _loopJob.Update(
1✔
123
                          renderingSurface,
1✔
124
                          keyboardHandle,
1✔
125
                          keyboardCharCache);
1✔
126

127
                    if (cnt == 10 * 60)
128
                    {
129
                        sId = await _staThreadWriter.CreateSurface(
×
130
                            renderingEngine,
×
131
                            _mainLoopLifetime.Token);
×
132

133
                        cnt = 0;
×
134
                    }
135
                }
136
                if (sId is not null)
137
                {
138
                    surfaces.Add(sId.Value);
×
139
                    sId = null;
×
140
                }
141
                lag -= msPerUpdate;
1✔
142
                _fpsTimer.StartUpdateTimer();
1✔
143
            }
144

145
            //Render
146
            foreach (var renderingSurfaceId in surfaces)
1✔
147
            {
148
                using var scope = _fpsTimer.StartRenderingTimerScope(
1✔
149
                    renderingSurfaceId);
1✔
150

151
                _loopJob.Render(
1✔
152
                    renderingEngine,
1✔
153
                    renderingSurfaceId);
1✔
154
            }
155
        }
156
        _mainLoopLifetime.Cancel();
1✔
157
    }
1✔
158

159
    private async ValueTask Dispose(bool disposing)
160
    {
161
        if (!_disposedValue)
1✔
162
        {
163
            if (disposing)
1✔
164
            {
165
            }
166

167
            _disposedValue = true;
1✔
168
            //Make sure the loop finishes
169
            _mainLoopLifetime.Cancel();
1✔
170
            await ExecutingTask;
1✔
171
        }
172
    }
1✔
173

174
    public async ValueTask DisposeAsync()
175
    {
176
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
177
        await Dispose(disposing: true);
1✔
178
        GC.SuppressFinalize(this);
1✔
179
    }
1✔
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc