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

Razakhel / RaZ / 19970761955

05 Dec 2025 05:26PM UTC coverage: 74.364% (+0.001%) from 74.363%
19970761955

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)

- Updated license

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
#if !defined(RAZ_PLATFORM_EMSCRIPTEN) // glfwGetWindowContentScale() isn't available with Emscripten
133
  float windowHorizScale {};
11✔
134
  glfwGetWindowContentScale(m_windowHandle, &windowHorizScale, nullptr);
11✔
135

136
  if (windowHorizScale != 0.f)
11✔
137
    Overlay::rescale(windowHorizScale);
11✔
138
#endif // RAZ_PLATFORM_EMSCRIPTEN
139
#endif // RAZ_NO_OVERLAY
140

141
  ++s_refCounter;
11✔
142

143
  Logger::debug("[Window] Initialized");
11✔
144
}
11✔
145

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

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

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

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

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

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

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

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

185
  GLFWmonitor* monitor    = glfwGetPrimaryMonitor();
×
186
  const GLFWvidmode* mode = glfwGetVideoMode(monitor);
×
187

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

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

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

202
bool Window::recoverVerticalSyncState() const {
×
203
  ZoneScopedN("Window::recoverVerticalSyncState");
204

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

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

215
    return static_cast<bool>(interval);
×
216
  }
217

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

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

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

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

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

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

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

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

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

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

284
void Window::updateCallbacks() const {
9✔
285
  ZoneScopedN("Window::updateCallbacks");
286

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

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

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

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

311
    Window& window = *static_cast<Window*>(glfwGetWindowUserPointer(windowHandle));
×
312

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

317
      auto& actions = window.m_inputActions;
×
318

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

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

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

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

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

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

354
    Window& window = *static_cast<Window*>(glfwGetWindowUserPointer(windowHandle));
×
355

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

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

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

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

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

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

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

390
    if (ImGui::GetIO().WantCaptureMouse)
×
391
      return;
×
392
#endif
393

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

396
    if (action == nullptr)
×
397
      return;
×
398

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

405
bool Window::run(float deltaTime) {
9✔
406
  ZoneScopedN("Window::run");
407

408
  if (glfwWindowShouldClose(m_windowHandle))
9✔
409
    return false;
×
410

411
  processInputs(deltaTime);
9✔
412

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

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

424
#if defined(RAZ_PLATFORM_EMSCRIPTEN)
425
  emscripten_webgl_commit_frame();
426
#endif
427

428
  {
429
    TracyGpuZone("TracyGpuCollect")
430
    TracyGpuCollect
431
  }
432

433
  return true;
9✔
434
}
435

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

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

444
void Window::processInputs(float deltaTime) {
9✔
445
  ZoneScopedN("Window::processInputs");
446

447
  {
448
    ZoneScopedN("glfwPollEvents");
449
    glfwPollEvents();
9✔
450
  }
451

452
  auto actionIter = m_inputActions.cbegin();
9✔
453

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

457
    action(deltaTime);
×
458

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

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

471
void Window::close() {
11✔
472
  ZoneScopedN("Window::close");
473

474
  if (!m_windowHandle.isValid())
11✔
475
    return;
×
476

477
  Logger::debug("[Window] Closing...");
11✔
478

479
  --s_refCounter;
11✔
480

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

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

493
  Logger::debug("[Window] Closed");
22✔
494
}
495

496
} // 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