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

Razakhel / RaZ / 19909351348

03 Dec 2025 09:04PM UTC coverage: 74.364% (+0.001%) from 74.363%
19909351348

push

github

Razakhel
[Render/Overlay] Added a way to rescale the UI

- It's automatically scaled after being initialized and whenever the window's content scale changes (can happen when moving to another monitor with a different resolution)

10 of 12 new or added lines in 2 files covered. (83.33%)

1 existing line in 1 file now uncovered.

8534 of 11476 relevant lines covered (74.36%)

1722.68 hits per line

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

62.22
/src/RaZ/Render/Window.cpp
1
#include "RaZ/Data/Image.hpp"
2
#include "RaZ/Render/Renderer.hpp"
3
#include "RaZ/Render/RenderSystem.hpp"
4
#include "RaZ/Render/Window.hpp"
5
#include "RaZ/Utils/Logger.hpp"
6

7
// GLEW is needed for V-sync management
8
#include "GL/glew.h"
9
#if defined(RAZ_PLATFORM_WINDOWS)
10
#include "GL/wglew.h"
11
#elif defined(RAZ_PLATFORM_LINUX)
12
#include "GL/glxew.h"
13
#endif
14

15
#include "GLFW/glfw3.h"
16

17
#if !defined(RAZ_NO_OVERLAY)
18
// Needed to set ImGui's callbacks
19
#include "imgui_impl_glfw.h"
20
#endif
21

22
#include "tracy/Tracy.hpp"
23
#include "tracy/TracyOpenGL.hpp"
24

25
#if defined(RAZ_PLATFORM_EMSCRIPTEN)
26
#include <emscripten/html5.h>
27
#endif
28

29
namespace Raz {
30

31
Window::Window(RenderSystem& renderSystem,
11✔
32
               unsigned int width, unsigned int height,
33
               const std::string& title,
34
               WindowSetting settings,
35
               uint8_t antiAliasingSampleCount) : m_renderSystem{ &renderSystem } {
11✔
36
  ZoneScopedN("Window::Window");
37

38
  Logger::debug("[Window] Initializing...");
11✔
39

40
  glfwSetErrorCallback([] (int errorCode, const char* description) {
11✔
41
    Logger::error("[GLFW] {} (error code {})", description, errorCode);
11✔
42
  });
11✔
43

44
  if (!glfwInit())
11✔
45
    throw std::runtime_error("Error: Failed to initialize GLFW");
×
46

47
#if !defined(USE_OPENGL_ES)
48
  glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
11✔
49
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
11✔
50
#else
51
  glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
52
#endif
53

54
#if defined(RAZ_CONFIG_DEBUG)
55
  glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
11✔
56
#endif
57

58
#if defined(RAZ_PLATFORM_MAC) // Setting the OpenGL forward compatibility is required on macOS
59
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
60
#endif
61

62
  glfwWindowHint(GLFW_FOCUSED, static_cast<int>(settings & WindowSetting::FOCUSED));
11✔
63
  glfwWindowHint(GLFW_RESIZABLE, static_cast<int>(settings & WindowSetting::RESIZABLE));
11✔
64
  glfwWindowHint(GLFW_VISIBLE, static_cast<int>(settings & WindowSetting::VISIBLE));
11✔
65
  glfwWindowHint(GLFW_DECORATED, static_cast<int>(settings & WindowSetting::DECORATED));
11✔
66
  glfwWindowHint(GLFW_AUTO_ICONIFY, static_cast<int>(settings & WindowSetting::AUTO_MINIMIZE));
11✔
67
  glfwWindowHint(GLFW_FLOATING, static_cast<int>(settings & WindowSetting::ALWAYS_ON_TOP));
11✔
68
  glfwWindowHint(GLFW_MAXIMIZED, static_cast<int>(settings & WindowSetting::MAXIMIZED));
11✔
69
#if !defined(RAZ_PLATFORM_EMSCRIPTEN)
70
  glfwWindowHint(GLFW_CENTER_CURSOR, static_cast<int>(settings & WindowSetting::CENTER_CURSOR));
11✔
71
  glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, static_cast<int>(settings & WindowSetting::TRANSPARENT_FB));
11✔
72
  glfwWindowHint(GLFW_FOCUS_ON_SHOW, static_cast<int>(settings & WindowSetting::AUTOFOCUS));
11✔
73
#endif
74

75
  glfwWindowHint(GLFW_SAMPLES, antiAliasingSampleCount);
11✔
76

77
#if !defined(RAZ_PLATFORM_EMSCRIPTEN)
78
  static constexpr std::array<std::pair<int, int>, 8> glVersions = {{
79
    { 4, 6 },
80
    { 4, 5 },
81
    { 4, 4 },
82
    { 4, 3 },
83
    { 4, 2 },
84
    { 4, 1 },
85
    { 4, 0 },
86
    { 3, 3 }
87
  }};
88

89
  for (auto [major, minor] : glVersions) {
22✔
90
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major);
22✔
91
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor);
22✔
92

93
    m_windowHandle = glfwCreateWindow(static_cast<int>(width), static_cast<int>(height), title.c_str(), nullptr, glfwGetCurrentContext());
22✔
94

95
    if (m_windowHandle)
22✔
96
      break;
11✔
97

98
    if (glfwGetError(nullptr) == GLFW_VERSION_UNAVAILABLE) {
11✔
99
      Logger::error("[Window] OpenGL {}.{} unsupported; attempting to fallback to a lower version", major, minor);
11✔
100
      continue;
11✔
101
    }
102

103
    close();
×
104
    throw std::runtime_error("Error: Failed to create GLFW Window");
×
105
  }
106
#else
107
  m_windowHandle = glfwCreateWindow(static_cast<int>(width), static_cast<int>(height), title.c_str(), nullptr, glfwGetCurrentContext());
108
#endif
109

110
  glfwSetWindowUserPointer(m_windowHandle, this);
11✔
111
  glfwGetWindowSize(m_windowHandle, &m_width, &m_height);
11✔
112
  glfwGetWindowPos(m_windowHandle, &m_posX, &m_posY);
11✔
113

114
  if (glfwGetCurrentContext() == nullptr)
11✔
115
    glfwMakeContextCurrent(m_windowHandle);
2✔
116

117
  Renderer::initialize();
11✔
118
  setClearColor(0.15f, 0.15f, 0.15f);
11✔
119

120
  glfwSetFramebufferSizeCallback(m_windowHandle, [] (GLFWwindow* windowHandle, int newWidth, int newHeight) {
11✔
121
    const auto* window = static_cast<const Window*>(glfwGetWindowUserPointer(windowHandle));
1✔
122
    window->m_renderSystem->resizeViewport(static_cast<unsigned int>(newWidth), static_cast<unsigned int>(newHeight));
1✔
123
  });
1✔
124

125
#if !defined(RAZ_NO_OVERLAY)
126
  Overlay::initialize(m_windowHandle);
11✔
127

128
  glfwSetWindowContentScaleCallback(m_windowHandle, [] (GLFWwindow*, float horizScale, float /* vertScale */) {
11✔
NEW
129
    Overlay::rescale(horizScale);
×
NEW
130
  });
×
131

132
  float windowHorizScale {};
11✔
133
  glfwGetWindowContentScale(m_windowHandle, &windowHorizScale, nullptr);
11✔
134

135
  if (windowHorizScale != 0.f)
11✔
136
    Overlay::rescale(windowHorizScale);
11✔
137
#endif
138

139
  ++s_refCounter;
11✔
140

141
  Logger::debug("[Window] Initialized");
11✔
142
}
11✔
143

144
void Window::setClearColor(const Color& color, float alpha) const {
15✔
145
  Renderer::clearColor(color.red(), color.green(), color.blue(), alpha);
15✔
146
}
15✔
147

148
void Window::setTitle(const std::string& title) const {
1✔
149
  glfwSetWindowTitle(m_windowHandle, title.c_str());
1✔
150
}
1✔
151

152
void Window::setIcon(const Image& img) const {
1✔
153
  if (img.isEmpty()) {
1✔
154
    Logger::error("[Window] Empty image given as window icon");
×
155
    return;
×
156
  }
157

158
  if (img.getColorspace() != ImageColorspace::RGBA) {
1✔
159
    Logger::error("[Window] The window icon can only be created from an image having an RGBA colorspace");
×
160
    return;
×
161
  }
162

163
  if (img.getDataType() != ImageDataType::BYTE) {
1✔
164
    Logger::error("[Window] The window icon can only be created from an image having byte data");
×
165
    return;
×
166
  }
167

168
  const GLFWimage icon = { static_cast<int>(img.getWidth()),
1✔
169
                           static_cast<int>(img.getHeight()),
2✔
170
                           const_cast<unsigned char*>(static_cast<const uint8_t*>(img.getDataPtr())) };
1✔
171
  glfwSetWindowIcon(m_windowHandle, 1, &icon);
1✔
172
}
173

174
void Window::resize(unsigned int width, unsigned int height) {
1✔
175
  glfwSetWindowSize(m_windowHandle, static_cast<int>(width), static_cast<int>(height));
1✔
176
  glfwGetWindowSize(m_windowHandle, &m_width, &m_height);
1✔
177
}
1✔
178

179
void Window::makeFullscreen() {
×
180
  glfwGetWindowSize(m_windowHandle, &m_width, &m_height);
×
181
  glfwGetWindowPos(m_windowHandle, &m_posX, &m_posY);
×
182

183
  GLFWmonitor* monitor    = glfwGetPrimaryMonitor();
×
184
  const GLFWvidmode* mode = glfwGetVideoMode(monitor);
×
185

186
  glfwSetWindowMonitor(m_windowHandle, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
×
187
}
×
188

189
void Window::makeWindowed() {
1✔
190
  glfwSetWindowMonitor(m_windowHandle, nullptr, m_posX, m_posY, m_width, m_height, GLFW_DONT_CARE);
1✔
191
}
1✔
192

193
void Window::enableFaceCulling(bool value) const {
3✔
194
  if (value)
3✔
195
    Renderer::enable(Capability::CULL);
2✔
196
  else
197
    Renderer::disable(Capability::CULL);
1✔
198
}
3✔
199

200
bool Window::recoverVerticalSyncState() const {
×
201
  ZoneScopedN("Window::recoverVerticalSyncState");
202

203
#if defined(RAZ_PLATFORM_WINDOWS)
204
  if (wglGetExtensionsStringEXT())
205
    return static_cast<bool>(wglGetSwapIntervalEXT());
206

207
  return true;
208
#elif defined(RAZ_PLATFORM_LINUX)
209
  if (glXQueryExtensionsString(glXGetCurrentDisplay(), 0)) {
×
210
    unsigned int interval {};
×
211
    glXQueryDrawable(glXGetCurrentDisplay(), glXGetCurrentDrawable(), GLX_SWAP_INTERVAL_EXT, &interval);
×
212

213
    return static_cast<bool>(interval);
×
214
  }
215

216
  return true;
×
217
#elif defined(RAZ_PLATFORM_MAC)
218
  return true;
219
#else
220
  Logger::warn("Vertical synchronization unsupported");
221
  return false;
222
#endif
223
}
224

225
void Window::enableVerticalSync([[maybe_unused]] bool value) const {
3✔
226
  ZoneScopedN("Window::enableVerticalSync");
227

228
#if defined(RAZ_PLATFORM_WINDOWS)
229
  if (wglGetExtensionsStringEXT()) {
230
    wglSwapIntervalEXT(static_cast<int>(value));
231
    return;
232
  }
233
#elif defined(RAZ_PLATFORM_LINUX)
234
  if (glXQueryExtensionsString(glXGetCurrentDisplay(), 0)) {
3✔
235
    glXSwapIntervalEXT(glXGetCurrentDisplay(), glXGetCurrentDrawable(), value);
3✔
236
    glXSwapIntervalMESA(static_cast<unsigned int>(value));
3✔
237
    return;
3✔
238
  }
239
#elif defined(RAZ_PLATFORM_MAC)
240
  glfwSwapInterval(value);
241
#else
242
  Logger::warn("Vertical synchronization unsupported");
243
#endif
244
}
245

246
void Window::setCursorState(Cursor::State state) const {
3✔
247
  glfwSetInputMode(m_windowHandle, GLFW_CURSOR, state);
3✔
248
}
3✔
249

250
void Window::addKeyCallback(Keyboard::Key key, std::function<void(float)> actionPress,
3✔
251
                                               Input::ActionTrigger frequency,
252
                                               std::function<void()> actionRelease) {
253
  m_keyboardCallbacks.emplace_back(key, std::move(actionPress), frequency, std::move(actionRelease));
3✔
254
  updateCallbacks();
3✔
255
}
3✔
256

257
void Window::addMouseButtonCallback(Mouse::Button button, std::function<void(float)> actionPress,
3✔
258
                                                          Input::ActionTrigger frequency,
259
                                                          std::function<void()> actionRelease) {
260
  m_mouseButtonCallbacks.emplace_back(button, std::move(actionPress), frequency, std::move(actionRelease));
3✔
261
  updateCallbacks();
3✔
262
}
3✔
263

264
void Window::setMouseScrollCallback(std::function<void(double, double)> func) {
1✔
265
  m_mouseScrollCallback = std::move(func);
1✔
266
  updateCallbacks();
1✔
267
}
1✔
268

269
void Window::setMouseMoveCallback(std::function<void(double, double)> func) {
1✔
270
  m_mouseMoveCallback = { m_width * 0.5, m_height * 0.5, std::move(func) };
1✔
271
  updateCallbacks();
1✔
272
}
1✔
273

274
void Window::setCloseCallback(std::function<void()> func) {
1✔
275
  m_closeCallback = std::move(func);
1✔
276

277
  glfwSetWindowCloseCallback(m_windowHandle, [] (GLFWwindow* windowHandle) {
1✔
278
    static_cast<const Window*>(glfwGetWindowUserPointer(windowHandle))->m_closeCallback();
×
279
  });
×
280
}
1✔
281

282
void Window::updateCallbacks() const {
9✔
283
  ZoneScopedN("Window::updateCallbacks");
284

285
#if !defined(RAZ_NO_OVERLAY)
286
  // Monitor events
287
  glfwSetMonitorCallback([] (GLFWmonitor* monitorHandle, int event) {
9✔
288
    ImGui_ImplGlfw_MonitorCallback(monitorHandle, event);
×
289
  });
×
290
#endif
291

292
#if !defined(RAZ_NO_OVERLAY)
293
  // Window focus
294
  glfwSetWindowFocusCallback(m_windowHandle, [] (GLFWwindow* windowHandle, int focused) {
9✔
295
    ImGui_ImplGlfw_WindowFocusCallback(windowHandle, focused);
×
296
  });
×
297
#endif
298

299
  // Keyboard inputs
300
  glfwSetKeyCallback(m_windowHandle, [] (GLFWwindow* windowHandle, int key, int scancode, int action, int mods) {
9✔
301
#if !defined(RAZ_NO_OVERLAY)
302
    ImGui_ImplGlfw_KeyCallback(windowHandle, key, scancode, action, mods);
×
303

304
    // Key callbacks shouldn't be executed if the overlay requested keyboard focus
305
    if (ImGui::GetIO().WantCaptureKeyboard)
×
306
      return;
×
307
#endif
308

309
    Window& window = *static_cast<Window*>(glfwGetWindowUserPointer(windowHandle));
×
310

311
    for (const auto& callback : window.m_keyboardCallbacks) {
×
312
      if (key != callback.key)
×
313
        continue;
×
314

315
      auto& actions = window.m_inputActions;
×
316

317
      if (action == GLFW_PRESS) {
×
318
        actions.emplace(key, InputAction{ callback.actionPress, callback.frequency });
×
319
      } else if (action == GLFW_RELEASE) {
×
320
        actions.erase(key);
×
321

322
        if (const auto& actionRelease = callback.actionRelease)
×
323
          actionRelease();
×
324
      }
325
    }
326
  });
327

328
#if !defined(RAZ_NO_OVERLAY)
329
  // Unicode character inputs
330
  glfwSetCharCallback(m_windowHandle, [] (GLFWwindow* windowHandle, unsigned int codePoint) {
9✔
331
    ImGui_ImplGlfw_CharCallback(windowHandle, codePoint);
×
332
  });
×
333
#endif
334

335
#if !defined(RAZ_NO_OVERLAY)
336
  // Cursor enter event
337
  glfwSetCursorEnterCallback(m_windowHandle, [] (GLFWwindow* windowHandle, int entered) {
9✔
338
    ImGui_ImplGlfw_CursorEnterCallback(windowHandle, entered);
×
339
  });
×
340
#endif
341

342
  // Mouse buttons inputs
343
  glfwSetMouseButtonCallback(m_windowHandle, [] (GLFWwindow* windowHandle, int button, int action, int mods) {
9✔
344
#if !defined(RAZ_NO_OVERLAY)
345
    ImGui_ImplGlfw_MouseButtonCallback(windowHandle, button, action, mods);
×
346

347
    // Mouse buttons callbacks shouldn't be executed if the overlay requested mouse focus
348
    if (ImGui::GetIO().WantCaptureMouse)
×
349
      return;
×
350
#endif
351

352
    Window& window = *static_cast<Window*>(glfwGetWindowUserPointer(windowHandle));
×
353

354
    for (const auto& callback : window.m_mouseButtonCallbacks) {
×
355
      if (button != callback.button)
×
356
        continue;
×
357

358
      if (action == GLFW_PRESS) {
×
359
        window.m_inputActions.emplace(button, InputAction{ callback.actionPress, callback.frequency });
×
360
      } else if (action == GLFW_RELEASE) {
×
361
        window.m_inputActions.erase(button);
×
362

363
        if (const auto& actionRelease = callback.actionRelease)
×
364
          actionRelease();
×
365
      }
366
    }
367
  });
368

369
  // Mouse scroll input
370
  glfwSetScrollCallback(m_windowHandle, [] (GLFWwindow* windowHandle, double xOffset, double yOffset) {
9✔
371
#if !defined(RAZ_NO_OVERLAY)
372
    ImGui_ImplGlfw_ScrollCallback(windowHandle, xOffset, yOffset);
×
373

374
    // Scroll callback shouldn't be executed if the overlay requested mouse focus
375
    if (ImGui::GetIO().WantCaptureMouse)
×
376
      return;
×
377
#endif
378

379
    if (const auto& scrollCallback = static_cast<Window*>(glfwGetWindowUserPointer(windowHandle))->m_mouseScrollCallback)
×
380
      scrollCallback(xOffset, yOffset);
×
381
  });
382

383
  // Mouse move input
384
  glfwSetCursorPosCallback(m_windowHandle, [] (GLFWwindow* windowHandle, double xPosition, double yPosition) {
9✔
385
#if !defined(RAZ_NO_OVERLAY)
386
    ImGui_ImplGlfw_CursorPosCallback(windowHandle, xPosition, yPosition);
×
387

388
    if (ImGui::GetIO().WantCaptureMouse)
×
389
      return;
×
390
#endif
391

392
    auto& [xPrevPos, yPrevPos, action] = static_cast<Window*>(glfwGetWindowUserPointer(windowHandle))->m_mouseMoveCallback;
×
393

394
    if (action == nullptr)
×
395
      return;
×
396

397
    action(xPosition - xPrevPos, yPosition - yPrevPos);
×
398
    xPrevPos = xPosition;
×
399
    yPrevPos = yPosition;
×
400
  });
401
}
9✔
402

403
bool Window::run(float deltaTime) {
9✔
404
  ZoneScopedN("Window::run");
405

406
  if (glfwWindowShouldClose(m_windowHandle))
9✔
407
    return false;
×
408

409
  processInputs(deltaTime);
9✔
410

411
#if !defined(RAZ_NO_OVERLAY)
412
  if (m_isOverlayEnabled && !m_overlay.isEmpty())
9✔
413
    m_overlay.render();
4✔
414
#endif
415

416
  {
417
    ZoneScopedN("glfwSwapBuffers");
418
    TracyGpuZone("SwapBuffers")
419
    glfwSwapBuffers(m_windowHandle);
9✔
420
  }
421

422
#if defined(RAZ_PLATFORM_EMSCRIPTEN)
423
  emscripten_webgl_commit_frame();
424
#endif
425

426
  {
427
    TracyGpuZone("TracyGpuCollect")
428
    TracyGpuCollect
429
  }
430

431
  return true;
9✔
432
}
433

434
Vec2f Window::recoverMousePosition() const {
1✔
435
  double xPos {};
1✔
436
  double yPos {};
1✔
437
  glfwGetCursorPos(m_windowHandle, &xPos, &yPos);
1✔
438

439
  return Vec2f(static_cast<float>(xPos), static_cast<float>(yPos));
1✔
440
}
441

442
void Window::processInputs(float deltaTime) {
9✔
443
  ZoneScopedN("Window::processInputs");
444

445
  {
446
    ZoneScopedN("glfwPollEvents");
447
    glfwPollEvents();
9✔
448
  }
449

450
  auto actionIter = m_inputActions.cbegin();
9✔
451

452
  while (actionIter != m_inputActions.cend()) {
9✔
453
    const auto& [action, frequency] = actionIter->second;
×
454

455
    action(deltaTime);
×
456

457
    // Removing the current action if it should be executed only once, or simply increment the iterator
458
    if (frequency == Input::ONCE)
×
459
      actionIter = m_inputActions.erase(actionIter); // std::unordered_map::erase(iter) returns an iterator on the next element
×
460
    else
461
      ++actionIter;
×
462
  }
463
}
9✔
464

465
void Window::setShouldClose() const {
×
466
  glfwSetWindowShouldClose(m_windowHandle, true);
×
467
}
×
468

469
void Window::close() {
11✔
470
  ZoneScopedN("Window::close");
471

472
  if (!m_windowHandle.isValid())
11✔
473
    return;
×
474

475
  Logger::debug("[Window] Closing...");
11✔
476

477
  --s_refCounter;
11✔
478

479
  if (s_refCounter == 0) {
11✔
480
#if !defined(RAZ_NO_OVERLAY)
481
    Overlay::destroy();
2✔
482
#endif
483

484
    {
485
      ZoneScopedN("glfwTerminate");
486
      glfwTerminate();
2✔
487
    }
488
    m_windowHandle = nullptr;
2✔
489
  }
490

491
  Logger::debug("[Window] Closed");
22✔
492
}
493

494
} // namespace Raz
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