top of page

Creating a console window for debug output.

Writer's picture: riskrisk

I've been working on my Vulkan rendering code and today I took a small detour to enhance the logging within the engine. I'm currently using spdlog in async mode which has been working great. I can enable console logging via configuration and, in debug mode, this is always enabled by default. This works great when running within the IDE as the output is redirected to the IDEs output window but when running the application outside of the IDE it's still useful to be able to see what's being logged in real-time without needing to tail the log manually.


The solution to this was to create a console window when the application/game starts. In order to accomplish this I used the code below. When running my test applications now I get a nice window where I can keep track of what's in the log without having to manually inspect or tail the file!



/*
 * Copyright (c) 2022.  PlayTheory LLC
 * Author: Kristian Carazo
 */
#include "platform_utils.h"
#include <windows.h>
#include <cstdio>
#include <corecrt_io.h>
#include <fcntl.h>

namespace theory_engine::platform_utils
{
  void redirect_standard_handle(const char* file, DWORD std_handle_id, int std_fd)
  {
    FILE* file_stream;
    freopen_s(&file_stream, file, std_fd == _fileno(stdin) ? "r" : "w", _fdopen(std_fd, std_fd == _fileno(stdin) ? "r" : "w"));

    auto handle = (intptr_t) GetStdHandle(std_handle_id);
    int crt_handle = _open_osfhandle(handle, _O_TEXT);
    if(crt_handle != -1)
    {
      _dup2(crt_handle, std_fd);
    }
  }

  // There is likely a better way to do what's shown below.
  void initialize_standard_handles()
  {
    FILE* file_stream = nullptr;
    const char* null_file = "//./NUL";

    // Clear stdin
    if(_get_osfhandle(0) < 0)
    {
      _close(0);
    }
    freopen_s(&file_stream, null_file, "r", stdin);
    setvbuf(stdin, nullptr, _IONBF, 0);

    // Clear stdout
    if(_get_osfhandle(1) < 0)
    {
      _close(1);
    }
    freopen_s(&file_stream, null_file, "w", stdout);
    setvbuf(stdout, nullptr, _IONBF, 0);

    // Clear stderr
    if(_get_osfhandle(2) < 0)
    {
      _close(2);
    }
    freopen_s(&file_stream, null_file, "w", stderr);
    setvbuf(stderr, nullptr, _IONBF, 0);

    FreeConsole();
  }
};

void theory_engine::platform_utils::create_console_window()
{
  const uint32_t console_dimensions = 8192;

  initialize_standard_handles();

  AllocConsole();

  SetConsoleTitle("TheoryEngine Output Console");

  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info = {};
  GetConsoleScreenBufferInfo(hConsole, &console_screen_buffer_info);

  console_screen_buffer_info.dwSize = {.X = console_dimensions, .Y = console_dimensions};
  SetConsoleScreenBufferSize(hConsole, console_screen_buffer_info.dwSize);

  // Redirect stdin, stdout, and stderr
  redirect_standard_handle("CONIN$", STD_INPUT_HANDLE, _fileno(stdin));
  redirect_standard_handle("CONOUT$", STD_OUTPUT_HANDLE, _fileno(stdout));
  redirect_standard_handle("CONOUT$", STD_ERROR_HANDLE, _fileno(stderr));

  // Sync C++ standard streams with C standard streams
  std::ios::sync_with_stdio(true);
}

11 views0 comments

Recent Posts

See All

Comments


PlayTheory®

bottom of page