Initial commit (I really need to remove Boost)

This commit is contained in:
wheremyfoodat 2022-09-15 04:47:14 +03:00
commit b5371dc66c
3226 changed files with 668081 additions and 0 deletions

24
include/emulator.hpp Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#define NOMINMAX // Windows why
#include "helpers.hpp"
#include "opengl.hpp"
#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"
class Emulator {
sf::RenderWindow window;
static constexpr u32 width = 400;
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
public:
Emulator() : window(sf::VideoMode(width, height), "Alber", sf::Style::Default, sf::ContextSettings(0, 0, 0, 4, 3)) {
reset();
window.setActive(true);
}
void step();
void render();
void reset();
void run();
void runFrame();
};

144
include/helpers.hpp Normal file
View file

@ -0,0 +1,144 @@
#pragma once
#include <iostream>
#include <vector>
#include <cstdarg>
#include <fstream>
#include "termcolor.hpp"
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using usize = std::size_t;
using uint = unsigned int;
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
class Helpers {
public:
[[noreturn]] static void panic(const char* fmt, ...) {
std::va_list args;
va_start(args, fmt);
std::cout << termcolor::on_red << "[FATAL] ";
std::vprintf (fmt, args);
std::cout << termcolor::reset;
std::cout << std::endl;
va_end(args);
exit(1);
}
static void warn(const char* fmt, ...) {
return;
std::va_list args;
va_start(args, fmt);
std::cout << termcolor::on_red << "[Warning] ";
std::vprintf (fmt, args);
std::cout << termcolor::reset;
std::cout << "\n";
va_end(args);
}
static std::vector <u8> loadROM(std::string directory) {
std::ifstream file (directory, std::ios::binary);
if (file.fail())
panic("Couldn't read %s", directory.c_str());
std::vector<u8> ROM;
file.unsetf(std::ios::skipws);
ROM.insert(ROM.begin(),
std::istream_iterator<uint8_t>(file),
std::istream_iterator<uint8_t>());
file.close();
printf ("%s loaded successfully\n", directory.c_str());
return ROM;
}
static constexpr bool buildingInDebugMode() {
#ifdef NDEBUG
return false;
#endif
return true;
}
static void debug_printf (const char* fmt, ...) {
if constexpr (buildingInDebugMode()) {
std::va_list args;
va_start(args, fmt);
std::vprintf (fmt, args);
va_end(args);
}
}
/// Sign extend an arbitrary-size value to 32 bits
static constexpr u32 inline signExtend32 (u32 value, u32 startingSize) {
auto temp = (s32) value;
auto bitsToShift = 32 - startingSize;
return (u32) (temp << bitsToShift >> bitsToShift);
}
/// Sign extend an arbitrary-size value to 16 bits
static constexpr u16 signExtend16 (u16 value, u32 startingSize) {
auto temp = (s16) value;
auto bitsToShift = 16 - startingSize;
return (u16) (temp << bitsToShift >> bitsToShift);
}
/// Check if a bit "bit" of value is set
static constexpr bool isBitSet (u32 value, int bit) {
return (value >> bit) & 1;
}
/// rotate number right
template <typename T>
static constexpr T rotr (T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value >> bits) | (value << (bitWidth - bits));
}
// rotate number left
template <typename T>
static constexpr T rotl (T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value << bits) | (value >> (bitWidth - bits));
}
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
template <typename T, T Begin, class Func, T ...Is>
static constexpr void static_for_impl( Func&& f, std::integer_sequence<T, Is...> ) {
( f( std::integral_constant<T, Begin + Is>{ } ),... );
}
template <typename T, T Begin, T End, class Func>
static constexpr void static_for(Func&& f) {
static_for_impl<T, Begin>( std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ } );
}
static constexpr inline u8 get8BitColor (u8 colorRGB555) {
return (colorRGB555 << 3) | (colorRGB555 >> 2);
}
// For values < 0x99
static constexpr inline u8 incBCDByte(u8 value) {
return ((value & 0xf) == 0x9) ? value + 7 : value + 1;
}
};
// useful macros
// likely/unlikely
#ifdef __GNUC__
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif

489
include/opengl.hpp Normal file
View file

@ -0,0 +1,489 @@
/***************************************************************************
* Copyright (C) 2022 PCSX-Redux authors *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
#pragma once
#include <array>
#include <cassert>
#include <functional>
#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <string_view>
#include <type_traits>
#include <utility>
#include "gl3w.h"
namespace OpenGL {
// Workaround for using static_assert inside constexpr if
// https://stackoverflow.com/questions/53945490/how-to-assert-that-a-constexpr-if-else-clause-never-happen
template <class...>
constexpr std::false_type AlwaysFalse{};
struct VertexArray {
GLuint m_handle = 0;
void create() {
if (m_handle == 0) {
glGenVertexArrays(1, &m_handle);
}
}
VertexArray(bool shouldCreate = false) {
if (shouldCreate) {
create();
}
}
~VertexArray() { glDeleteVertexArrays(1, &m_handle); }
GLuint handle() { return m_handle; }
bool exists() { return m_handle != 0; }
void bind() { glBindVertexArray(m_handle); }
template <typename T>
void setAttributeFloat(GLuint index, GLint size, GLsizei stride, const void* offset, bool normalized = false) {
if constexpr (std::is_same<T, GLfloat>()) {
glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, offset);
} else if constexpr (std::is_same<T, GLbyte>()) {
glVertexAttribPointer(index, size, GL_BYTE, normalized, stride, offset);
} else if constexpr (std::is_same<T, GLubyte>()) {
glVertexAttribPointer(index, size, GL_UNSIGNED_BYTE, normalized, stride, offset);
} else if constexpr (std::is_same<T, GLshort>()) {
glVertexAttribPointer(index, size, GL_SHORT, normalized, stride, offset);
} else if constexpr (std::is_same<T, GLushort>()) {
glVertexAttribPointer(index, size, GL_UNSIGNED_SHORT, normalized, stride, offset);
} else if constexpr (std::is_same<T, GLint>()) {
glVertexAttribPointer(index, size, GL_INT, normalized, stride, offset);
} else if constexpr (std::is_same<T, GLuint>()) {
glVertexAttribPointer(index, size, GL_UNSIGNED_INT, normalized, stride, offset);
} else {
static_assert(AlwaysFalse<T>, "Unimplemented type for OpenGL::setAttributeFloat");
}
}
template <typename T>
void setAttributeInt(GLuint index, GLint size, GLsizei stride, const void* offset) {
if constexpr (std::is_same<T, GLbyte>()) {
glVertexAttribIPointer(index, size, GL_BYTE, stride, offset);
} else if constexpr (std::is_same<T, GLubyte>()) {
glVertexAttribIPointer(index, size, GL_UNSIGNED_BYTE, stride, offset);
} else if constexpr (std::is_same<T, GLshort>()) {
glVertexAttribIPointer(index, size, GL_SHORT, stride, offset);
} else if constexpr (std::is_same<T, GLushort>()) {
glVertexAttribIPointer(index, size, GL_UNSIGNED_SHORT, stride, offset);
} else if constexpr (std::is_same<T, GLint>()) {
glVertexAttribIPointer(index, size, GL_INT, stride, offset);
} else if constexpr (std::is_same<T, GLuint>()) {
glVertexAttribIPointer(index, size, GL_UNSIGNED_INT, stride, offset);
} else {
static_assert(AlwaysFalse<T>, "Unimplemented type for OpenGL::setAttributeInt");
}
}
template <typename T>
void setAttributeFloat(GLuint index, GLint size, GLsizei stride, size_t offset, bool normalized = false) {
setAttributeFloat<T>(index, size, stride, reinterpret_cast<const void*>(offset), normalized);
}
template <typename T>
void setAttributeInt(GLuint index, GLint size, GLsizei stride, size_t offset) {
setAttributeInt<T>(index, size, stride, reinterpret_cast<const void*>(offset));
}
void enableAttribute(GLuint index) { glEnableVertexAttribArray(index); }
void disableAttribute(GLuint index) { glDisableVertexAttribArray(index); }
};
enum FramebufferTypes {
DrawFramebuffer = GL_DRAW_FRAMEBUFFER,
ReadFramebuffer = GL_READ_FRAMEBUFFER,
DrawAndReadFramebuffer = GL_FRAMEBUFFER
};
struct Texture {
GLuint m_handle = 0;
int m_width, m_height;
GLenum m_binding;
int m_samples = 1; // For MSAA
void create(int width, int height, GLint internalFormat, GLenum binding = GL_TEXTURE_2D, int samples = 1) {
m_width = width;
m_height = height;
m_binding = binding;
glGenTextures(1, &m_handle);
bind();
if (binding == GL_TEXTURE_2D_MULTISAMPLE) {
if (!glTexStorage2DMultisample) { // Assert that glTexStorage2DMultisample has been loaded
throw std::runtime_error("MSAA is not supported on this platform");
}
int maxSampleCount;
glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &maxSampleCount);
if (samples > maxSampleCount) {
samples = maxSampleCount;
}
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_TRUE);
} else {
glTexStorage2D(binding, 1, internalFormat, width, height);
}
}
void createMSAA(int width, int height, GLint internalFormat, int samples) {
create(width, height, internalFormat, GL_TEXTURE_2D_MULTISAMPLE, samples);
}
~Texture() { glDeleteTextures(1, &m_handle); }
GLuint handle() { return m_handle; }
bool exists() { return m_handle != 0; }
void bind() { glBindTexture(m_binding, m_handle); }
int width() { return m_width; }
int height() { return m_height; }
};
struct Framebuffer {
GLuint m_handle = 0;
GLenum m_textureType; // GL_TEXTURE_2D or GL_TEXTURE_2D_MULTISAMPLE
void create() {
if (m_handle == 0) {
glGenFramebuffers(1, &m_handle);
}
}
Framebuffer(bool shouldCreate = false) {
if (shouldCreate) {
create();
}
}
~Framebuffer() { glDeleteFramebuffers(1, &m_handle); }
GLuint handle() { return m_handle; }
bool exists() { return m_handle != 0; }
void bind(GLenum target) { glBindFramebuffer(target, m_handle); }
void bind(FramebufferTypes target) { bind(static_cast<GLenum>(target)); }
void createWithTexture(Texture& tex, GLenum mode = GL_FRAMEBUFFER, GLenum textureType = GL_TEXTURE_2D) {
m_textureType = textureType;
create();
bind(mode);
glFramebufferTexture2D(mode, GL_COLOR_ATTACHMENT0, textureType, tex.handle(), 0);
}
void createWithReadTexture(Texture& tex, GLenum textureType = GL_TEXTURE_2D) {
createWithTexture(tex, GL_READ_FRAMEBUFFER, textureType);
}
void createWithDrawTexture(Texture& tex, GLenum textureType = GL_TEXTURE_2D) {
createWithTexture(tex, GL_DRAW_FRAMEBUFFER, textureType);
}
void createWithTextureMSAA(Texture& tex, GLenum mode = GL_FRAMEBUFFER) {
m_textureType = GL_TEXTURE_2D_MULTISAMPLE;
create();
bind(mode);
glFramebufferTexture2D(mode, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex.handle(), 0);
}
};
enum ShaderType {
Fragment = GL_FRAGMENT_SHADER,
Vertex = GL_VERTEX_SHADER,
Geometry = GL_GEOMETRY_SHADER,
Compute = GL_COMPUTE_SHADER,
TessControl = GL_TESS_CONTROL_SHADER,
TessEvaluation = GL_TESS_EVALUATION_SHADER
};
struct Shader {
GLuint m_handle = 0;
Shader() {}
Shader(const std::string_view source, ShaderType type) { create(source, static_cast<GLenum>(type)); }
// Returns whether compilation failed or not
bool create(const std::string_view source, GLenum type) {
m_handle = glCreateShader(type);
const GLchar* const sources[1] = {source.data()};
glShaderSource(m_handle, 1, sources, nullptr);
glCompileShader(m_handle);
GLint success;
glGetShaderiv(m_handle, GL_COMPILE_STATUS, &success);
if (!success) {
char buf[4096];
glGetShaderInfoLog(m_handle, 4096, nullptr, buf);
fprintf(stderr, "Failed to compile shader\nError: %s\n", buf);
glDeleteShader(m_handle);
m_handle = 0;
}
return m_handle != 0;
}
GLuint handle() { return m_handle; }
bool exists() { return m_handle != 0; }
};
struct Program {
GLuint m_handle = 0;
bool create(std::initializer_list<std::reference_wrapper<Shader>> shaders) {
m_handle = glCreateProgram();
for (const auto& shader : shaders) {
glAttachShader(m_handle, shader.get().handle());
}
glLinkProgram(m_handle);
GLint success;
glGetProgramiv(m_handle, GL_LINK_STATUS, &success);
if (!success) {
char buf[4096];
glGetProgramInfoLog(m_handle, 4096, nullptr, buf);
fprintf(stderr, "Failed to link program\nError: %s\n", buf);
glDeleteProgram(m_handle);
m_handle = 0;
}
return m_handle != 0;
}
GLuint handle() { return m_handle; }
bool exists() { return m_handle != 0; }
void use() { glUseProgram(m_handle); }
};
struct VertexBuffer {
GLuint m_handle = 0;
void create() {
if (m_handle == 0) {
glGenBuffers(1, &m_handle);
}
}
void createFixedSize(GLsizei size, GLenum usage = GL_DYNAMIC_DRAW) {
create();
bind();
glBufferData(GL_ARRAY_BUFFER, size, nullptr, usage);
}
VertexBuffer(bool shouldCreate = false) {
if (shouldCreate) {
create();
}
}
~VertexBuffer() { glDeleteBuffers(1, &m_handle); }
GLuint handle() { return m_handle; }
bool exists() { return m_handle != 0; }
void bind() { glBindBuffer(GL_ARRAY_BUFFER, m_handle); }
// Reallocates the buffer on every call. Prefer the sub version if possible.
template <typename VertType>
void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) {
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage);
}
// Only use if you used createFixedSize
template <typename VertType>
void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) {
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices);
}
};
static void setClearColor(float val) { glClearColor(val, val, val, val); }
static void setClearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); }
static void setClearDepth(float depth) { glClearDepthf(depth); }
static void clearColor() { glClear(GL_COLOR_BUFFER_BIT); }
static void clearDepth() { glClear(GL_DEPTH_BUFFER_BIT); }
static void clearColorAndDepth() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
static void setViewport(GLsizei width, GLsizei height) { glViewport(0, 0, width, height); }
static void setViewport(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glViewport(x, y, width, height); }
static void setScissor(GLsizei width, GLsizei height) { glScissor(0, 0, width, height); }
static void setScissor(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glScissor(x, y, width, height); }
static void bindScreenFramebuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
static void enableScissor() { glEnable(GL_SCISSOR_TEST); }
static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
static void enableBlend() { glEnable(GL_BLEND); }
static void disableBlend() { glDisable(GL_BLEND); }
enum Primitives {
Triangle = GL_TRIANGLES,
Triangles = Triangle,
Tri = Triangle,
Tris = Triangle,
TriangleStrip = GL_TRIANGLE_STRIP,
TriangleFan = GL_TRIANGLE_FAN,
Line = GL_LINES,
Lines = Line,
LineStrip = GL_LINE_STRIP,
Point = GL_POINTS,
Points = Point
};
static void draw(Primitives prim, GLsizei vertexCount) { glDrawArrays(static_cast<GLenum>(prim), 0, vertexCount); }
static void draw(Primitives prim, GLint first, GLsizei vertexCount) {
glDrawArrays(static_cast<GLenum>(prim), first, vertexCount);
}
enum FillMode { DrawPoints = GL_POINT, DrawWire = GL_LINE, FillPoly = GL_FILL };
static void setFillMode(GLenum mode) { glPolygonMode(GL_FRONT_AND_BACK, mode); }
static void setFillMode(FillMode mode) { glPolygonMode(GL_FRONT_AND_BACK, static_cast<GLenum>(mode)); }
static void drawWireframe() { setFillMode(DrawWire); }
template <typename T>
T get(GLenum query) {
T ret{};
if constexpr (std::is_same<T, GLint>()) {
glGetIntegerv(query, &ret);
} else if constexpr (std::is_same<T, GLfloat>()) {
glGetFloatv(query, &ret);
} else if constexpr (std::is_same<T, GLdouble>()) {
glGetDoublev(query, &ret);
} else if constexpr (std::is_same<T, GLboolean>()) {
glGetBooleanv(query, &ret);
} else {
static_assert(AlwaysFalse<T>, "Invalid type for OpenGL::get");
}
return ret;
}
static bool isEnabled(GLenum query) { return glIsEnabled(query) != GL_FALSE; }
static GLint getDrawFramebuffer() { return get<GLint>(GL_DRAW_FRAMEBUFFER_BINDING); }
static GLint maxSamples() { return get<GLint>(GL_MAX_INTEGER_SAMPLES); }
static GLint getTex2D() { return get<GLint>(GL_TEXTURE_BINDING_2D); }
static GLint getProgram() { return get<GLint>(GL_CURRENT_PROGRAM); }
static bool scissorEnabled() { return isEnabled(GL_SCISSOR_TEST); }
static bool versionSupported(int major, int minor) { return gl3wIsSupported(major, minor); }
[[nodiscard]] static GLint uniformLocation(GLuint program, const char* name) {
return glGetUniformLocation(program, name);
}
[[nodiscard]] static GLint uniformLocation(Program& program, const char* name) {
return glGetUniformLocation(program.handle(), name);
}
enum BlendEquation {
Add = GL_FUNC_ADD,
Sub = GL_FUNC_SUBTRACT,
ReverseSub = GL_FUNC_REVERSE_SUBTRACT,
Min = GL_MIN,
Max = GL_MAX
};
static void setBlendColor(float r, float g, float b, float a = 1.0) { glBlendColor(r, g, b, a); }
static void setBlendEquation(BlendEquation eq) { glBlendEquation(eq); }
static void setBlendEquation(BlendEquation eq1, BlendEquation eq2) { glBlendEquationSeparate(eq1, eq2); }
static void setBlendFactor(GLenum fac1, GLenum fac2) { glBlendFunc(fac1, fac2); }
static void setBlendFactor(GLenum fac1, GLenum fac2, GLenum fac3, GLenum fac4) {
glBlendFuncSeparate(fac1, fac2, fac3, fac4);
}
// Abstraction for GLSL vectors
template <typename T, int size>
class Vector {
// A GLSL vector can only have 2, 3 or 4 elements
static_assert(size == 2 || size == 3 || size == 4);
T m_storage[size];
public:
T& r() { return m_storage[0]; }
T& g() { return m_storage[1]; }
T& b() {
static_assert(size >= 3, "Out of bounds OpenGL::Vector access");
return m_storage[2];
}
T& a() {
static_assert(size >= 4, "Out of bounds OpenGL::Vector access");
return m_storage[3];
}
T& x() { return r(); }
T& y() { return g(); }
T& z() { return b(); }
T& w() { return a(); }
T& operator[](int index) { return m_storage[index]; }
T& u() { return r(); }
T& v() { return g(); }
T& s() { return r(); }
T& t() { return g(); }
T& p() { return b(); }
T& q() { return a(); }
Vector(std::array<T, size> list) { std::copy(list.begin(), list.end(), &m_storage[0]); }
Vector() {}
};
using vec2 = Vector<GLfloat, 2>;
using vec3 = Vector<GLfloat, 3>;
using vec4 = Vector<GLfloat, 4>;
using dvec2 = Vector<GLdouble, 2>;
using dvec3 = Vector<GLdouble, 3>;
using dvec4 = Vector<GLdouble, 4>;
using ivec2 = Vector<GLint, 2>;
using ivec3 = Vector<GLint, 3>;
using ivec4 = Vector<GLint, 4>;
using uvec2 = Vector<GLuint, 2>;
using uvec3 = Vector<GLuint, 3>;
using uvec4 = Vector<GLuint, 4>;
// A 2D rectangle, meant to be used for stuff like scissor rects or viewport rects
// We're never supporting 3D rectangles, because rectangles were never meant to be 3D in the first place
// x, y: Coords of the top left vertex
// width, height: Dimensions of the rectangle. Initialized to 0 if not specified.
template <typename T>
struct Rectangle {
T x, y, width, height;
std::pair<T, T> topLeft() { return std::make_pair(x, y); }
std::pair<T, T> topRight() { return std::make_pair(x + width, y); }
std::pair<T, T> bottomLeft() { return std::make_pair(x, y + height); }
std::pair<T, T> bottomRight() { return std::make_pair(x + width, y + height); }
Rectangle() : x(0), y(0), width(0), height(0) {}
Rectangle(T x, T y, T width, T height) : x(x), y(y), width(width), height(height) {}
bool isEmpty() { return width == 0 && height == 0; }
bool isLine() { return (width == 0 && height != 0) || (width != 0 && height == 0); }
void setEmpty() { x = y = width = height = 0; }
};
using Rect = Rectangle<GLuint>;
} // end namespace OpenGL

636
include/termcolor.hpp Normal file
View file

@ -0,0 +1,636 @@
//!
//! termcolor
//! ~~~~~~~~~
//!
//! termcolor is a header-only c++ library for printing colored messages
//! to the terminal. Written just for fun with a help of the Force.
//!
//! :copyright: (c) 2013 by Ihor Kalnytskyi
//! :license: BSD, see LICENSE for details
//!
#ifndef TERMCOLOR_HPP_
#define TERMCOLOR_HPP_
// the following snippet of code detects the current OS and
// defines the appropriate macro that is used to wrap some
// platform specific things
#if defined(_WIN32) || defined(_WIN64)
# define TERMCOLOR_OS_WINDOWS
#elif defined(__APPLE__)
# define TERMCOLOR_OS_MACOS
#elif defined(__unix__) || defined(__unix)
# define TERMCOLOR_OS_LINUX
#else
# error unsupported platform
#endif
// This headers provides the `isatty()`/`fileno()` functions,
// which are used for testing whether a standart stream refers
// to the terminal. As for Windows, we also need WinApi funcs
// for changing colors attributes of the terminal.
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
# include <unistd.h>
#elif defined(TERMCOLOR_OS_WINDOWS)
# include <io.h>
# include <windows.h>
#endif
#include <iostream>
#include <cstdio>
namespace termcolor
{
// Forward declaration of the `_internal` namespace.
// All comments are below.
namespace _internal
{
// An index to be used to access a private storage of I/O streams. See
// colorize / nocolorize I/O manipulators for details.
static int colorize_index = std::ios_base::xalloc();
inline FILE* get_standard_stream(const std::ostream& stream);
inline bool is_colorized(std::ostream& stream);
inline bool is_atty(const std::ostream& stream);
#if defined(TERMCOLOR_OS_WINDOWS)
inline void win_change_attributes(std::ostream& stream, int foreground, int background=-1);
#endif
}
inline
std::ostream& colorize(std::ostream& stream)
{
stream.iword(_internal::colorize_index) = 1L;
return stream;
}
inline
std::ostream& nocolorize(std::ostream& stream)
{
stream.iword(_internal::colorize_index) = 0L;
return stream;
}
inline
std::ostream& reset(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[00m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, -1);
#endif
}
return stream;
}
inline
std::ostream& bold(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[1m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& dark(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[2m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& italic(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[3m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& underline(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[4m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, COMMON_LVB_UNDERSCORE);
#endif
}
return stream;
}
inline
std::ostream& blink(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[5m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& reverse(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[7m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& concealed(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[8m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& crossed(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[9m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
template <uint8_t code> inline
std::ostream& color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
char command[12];
std::snprintf(command, sizeof(command), "\033[38;5;%dm", code);
stream << command;
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
template <uint8_t code> inline
std::ostream& on_color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
char command[12];
std::snprintf(command, sizeof(command), "\033[48;5;%dm", code);
stream << command;
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
template <uint8_t r, uint8_t g, uint8_t b> inline
std::ostream& color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
char command[20];
std::snprintf(command, sizeof(command), "\033[38;2;%d;%d;%dm", r, g, b);
stream << command;
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
template <uint8_t r, uint8_t g, uint8_t b> inline
std::ostream& on_color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
char command[20];
std::snprintf(command, sizeof(command), "\033[48;2;%d;%d;%dm", r, g, b);
stream << command;
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline
std::ostream& grey(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[30m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
0 // grey (black)
);
#endif
}
return stream;
}
inline
std::ostream& red(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[31m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& green(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[32m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_GREEN
);
#endif
}
return stream;
}
inline
std::ostream& yellow(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[33m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_GREEN | FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& blue(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[34m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE
);
#endif
}
return stream;
}
inline
std::ostream& magenta(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[35m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& cyan(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[36m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_GREEN
);
#endif
}
return stream;
}
inline
std::ostream& white(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[37m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_grey(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[40m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
0 // grey (black)
);
#endif
}
return stream;
}
inline
std::ostream& on_red(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[41m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_green(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[42m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN
);
#endif
}
return stream;
}
inline
std::ostream& on_yellow(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[43m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_blue(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[44m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_BLUE
);
#endif
}
return stream;
}
inline
std::ostream& on_magenta(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[45m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_BLUE | BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_cyan(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[46m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE
);
#endif
}
return stream;
}
inline
std::ostream& on_white(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[47m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
);
#endif
}
return stream;
}
//! Since C++ hasn't a way to hide something in the header from
//! the outer access, I have to introduce this namespace which
//! is used for internal purpose and should't be access from
//! the user code.
namespace _internal
{
//! Since C++ hasn't a true way to extract stream handler
//! from the a given `std::ostream` object, I have to write
//! this kind of hack.
inline
FILE* get_standard_stream(const std::ostream& stream)
{
if (&stream == &std::cout)
return stdout;
else if ((&stream == &std::cerr) || (&stream == &std::clog))
return stderr;
return nullptr;
}
// Say whether a given stream should be colorized or not. It's always
// true for ATTY streams and may be true for streams marked with
// colorize flag.
inline
bool is_colorized(std::ostream& stream)
{
return is_atty(stream) || static_cast<bool>(stream.iword(colorize_index));
}
//! Test whether a given `std::ostream` object refers to
//! a terminal.
inline
bool is_atty(const std::ostream& stream)
{
FILE* std_stream = get_standard_stream(stream);
// Unfortunately, fileno() ends with segmentation fault
// if invalid file descriptor is passed. So we need to
// handle this case gracefully and assume it's not a tty
// if standard stream is not detected, and 0 is returned.
if (!std_stream)
return false;
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
return ::isatty(fileno(std_stream));
#elif defined(TERMCOLOR_OS_WINDOWS)
return ::_isatty(_fileno(std_stream));
#endif
}
#if defined(TERMCOLOR_OS_WINDOWS)
//! Change Windows Terminal colors attribute. If some
//! parameter is `-1` then attribute won't changed.
inline void win_change_attributes(std::ostream& stream, int foreground, int background)
{
// yeah, i know.. it's ugly, it's windows.
static WORD defaultAttributes = 0;
// Windows doesn't have ANSI escape sequences and so we use special
// API to change Terminal output color. That means we can't
// manipulate colors by means of "std::stringstream" and hence
// should do nothing in this case.
if (!_internal::is_atty(stream))
return;
// get terminal handle
HANDLE hTerminal = INVALID_HANDLE_VALUE;
if (&stream == &std::cout)
hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
else if (&stream == &std::cerr)
hTerminal = GetStdHandle(STD_ERROR_HANDLE);
// save default terminal attributes if it unsaved
if (!defaultAttributes)
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
defaultAttributes = info.wAttributes;
}
// restore all default settings
if (foreground == -1 && background == -1)
{
SetConsoleTextAttribute(hTerminal, defaultAttributes);
return;
}
// get current settings
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
if (foreground != -1)
{
info.wAttributes &= ~(info.wAttributes & 0x0F);
info.wAttributes |= static_cast<WORD>(foreground);
}
if (background != -1)
{
info.wAttributes &= ~(info.wAttributes & 0xF0);
info.wAttributes |= static_cast<WORD>(background);
}
SetConsoleTextAttribute(hTerminal, info.wAttributes);
}
#endif // TERMCOLOR_OS_WINDOWS
} // namespace _internal
} // namespace termcolor
#undef TERMCOLOR_OS_WINDOWS
#undef TERMCOLOR_OS_MACOS
#undef TERMCOLOR_OS_LINUX
#endif // TERMCOLOR_HPP_