Files
UpdateDisplay/Main.cpp
2025-12-13 18:19:55 +01:00

246 lines
7.4 KiB
C++

#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include <iostream>
#include <string>
#include <exception>
#include "Display.h"
void print_help()
{
std::cout
<< "UpdateDisplay" << std::endl
<< "=============" << std::endl
<< std::endl
<< "Usage: UpdateDisplay.exe <options...>" << std::endl
<< std::endl
<< "By default the program will refresh display settings." << std::endl
<< std::endl
<< "Possible options:" << std::endl
<< std::endl
<< "--rotate" << "\t - Rotates Display" << std::endl
<< "\t0" << "\t - Landscape" << std::endl
<< "\t90" << "\t - Prtrait" << std::endl
<< "\t180" << "\t - Flippd Landscape" << std::endl
<< "\t270" << "\t - Flippd Prtrait" << std::endl
<< std::endl;
}
void main(int argc, char** argv)
{
if (argc == 1)
return print_help();
Display dis;
char** arg = argv;
arg++;
for (int i = 1; i < argc; i++, arg++)
{
std::string a(*arg);
if (a == "--help")
print_help();
else if (a == "--rotate")
{
i++;
arg++;
if (i == argc)
throw std::exception("Argumment to --rotate not specified");
a = std::string(*arg);
if (a == "0")
dis.rotate(Display::Rotation::ROTATE_0);
else if (a == "90")
dis.rotate(Display::Rotation::ROTATE_90);
else if (a == "180")
dis.rotate(Display::Rotation::ROTATE_180);
else if (a == "270")
dis.rotate(Display::Rotation::ROTATE_270);
else
throw std::exception(("Unknown rotation: "+a).c_str());
}
}
dis.apply();
}
// Taken from https://stackoverflow.com/a/74999569
static void get_command_line_args(int* argc, char*** argv)
{
// Get the command line arguments as wchar_t strings
wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), argc);
if (!wargv) { *argc = 0; *argv = NULL; return; }
// Count the number of bytes necessary to store the UTF-8 versions of those strings
int n = 0;
for (int i = 0; i < *argc; i++)
n += WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL) + 1;
// Allocate the argv[] array + all the UTF-8 strings
*argv = (char**)malloc((*argc + 1) * sizeof(char*) + n);
if (!*argv) { *argc = 0; return; }
// Convert all wargv[] --> argv[]
char* arg = (char*)&((*argv)[*argc + 1]);
for (int i = 0; i < *argc; i++)
{
(*argv)[i] = arg;
arg += WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, arg, n, NULL, NULL) + 1;
}
(*argv)[*argc] = NULL;
}
// Source - https://stackoverflow.com/a
// Posted by Roger Sanders, modified by community. See post 'Timeline' for change history
// Retrieved 2025-12-13, License - CC BY-SA 3.0
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr)
{
// Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been
// observed that the file number of our standard handle file objects can be assigned internally to a value of -2
// when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our
// call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value
// before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to
// use the "nul" device, which will place them into a valid state, after which we can redirect them to our target
// using the "_dup2" function.
if (bindStdIn)
{
FILE* dummyFile;
freopen_s(&dummyFile, "nul", "r", stdin);
}
if (bindStdOut)
{
FILE* dummyFile;
freopen_s(&dummyFile, "nul", "w", stdout);
}
if (bindStdErr)
{
FILE* dummyFile;
freopen_s(&dummyFile, "nul", "w", stderr);
}
// Redirect unbuffered stdin from the current standard input handle
if (bindStdIn)
{
HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE);
if (stdHandle != INVALID_HANDLE_VALUE)
{
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
if (fileDescriptor != -1)
{
FILE* file = _fdopen(fileDescriptor, "r");
if (file != NULL)
{
int dup2Result = _dup2(_fileno(file), _fileno(stdin));
if (dup2Result == 0)
{
setvbuf(stdin, NULL, _IONBF, 0);
}
}
}
}
}
// Redirect unbuffered stdout to the current standard output handle
if (bindStdOut)
{
HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (stdHandle != INVALID_HANDLE_VALUE)
{
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
if (fileDescriptor != -1)
{
FILE* file = _fdopen(fileDescriptor, "w");
if (file != NULL)
{
int dup2Result = _dup2(_fileno(file), _fileno(stdout));
if (dup2Result == 0)
{
setvbuf(stdout, NULL, _IONBF, 0);
}
}
}
}
}
// Redirect unbuffered stderr to the current standard error handle
if (bindStdErr)
{
HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE);
if (stdHandle != INVALID_HANDLE_VALUE)
{
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
if (fileDescriptor != -1)
{
FILE* file = _fdopen(fileDescriptor, "w");
if (file != NULL)
{
int dup2Result = _dup2(_fileno(file), _fileno(stderr));
if (dup2Result == 0)
{
setvbuf(stderr, NULL, _IONBF, 0);
}
}
}
}
}
// Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the
// standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In
// versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything
// has been read from or written to the targets or not.
if (bindStdIn)
{
std::wcin.clear();
std::cin.clear();
}
if (bindStdOut)
{
std::wcout.clear();
std::cout.clear();
}
if (bindStdErr)
{
std::wcerr.clear();
std::cerr.clear();
}
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow)
{
if (AttachConsole(ATTACH_PARENT_PROCESS))
BindCrtHandlesToStdHandles(true, true, true);
int argc = 0;
char** argv = nullptr;
try
{
get_command_line_args(&argc, &argv);
main(argc, argv);
if (argv)
free(argv);
return EXIT_SUCCESS;
}
catch (std::exception& ex)
{
if (argv)
free(argv);
MessageBoxA(nullptr, ex.what(), "UpdateDisplay error", MB_ICONERROR);
return EXIT_FAILURE;
}
}