From 6e455738b8631ad67ec94080aa0f8700f6a602c1 Mon Sep 17 00:00:00 2001 From: Leroy Hopson Date: Sun, 6 Jun 2021 19:58:50 +0700 Subject: [PATCH] Format c++ files using clang-format Add git pre-commit hooks to help with automatic formatting. --- .../native/src/libgodotxtermnative.cpp | 20 +- .../godot_xterm/native/src/pseudoterminal.cpp | 253 ++-- .../godot_xterm/native/src/pseudoterminal.h | 58 +- addons/godot_xterm/native/src/terminal.cpp | 1027 ++++++++--------- addons/godot_xterm/native/src/terminal.h | 93 +- misc/hooks/README.md | 21 + misc/hooks/canonicalize_filename.sh | 48 + misc/hooks/pre-commit | 50 + misc/hooks/pre-commit-clang-format | 148 +++ misc/hooks/pre-commit-gdformat | 23 + 10 files changed, 981 insertions(+), 760 deletions(-) create mode 100644 misc/hooks/README.md create mode 100755 misc/hooks/canonicalize_filename.sh create mode 100755 misc/hooks/pre-commit create mode 100755 misc/hooks/pre-commit-clang-format create mode 100755 misc/hooks/pre-commit-gdformat diff --git a/addons/godot_xterm/native/src/libgodotxtermnative.cpp b/addons/godot_xterm/native/src/libgodotxtermnative.cpp index 9d62953..0664156 100644 --- a/addons/godot_xterm/native/src/libgodotxtermnative.cpp +++ b/addons/godot_xterm/native/src/libgodotxtermnative.cpp @@ -3,22 +3,20 @@ #include "pseudoterminal.h" #endif -extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) -{ - godot::Godot::gdnative_init(o); +extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); } -extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) -{ - godot::Godot::gdnative_terminate(o); +extern "C" void GDN_EXPORT +godot_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); } -extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) -{ - godot::Godot::nativescript_init(handle); +extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); - godot::register_tool_class(); + godot::register_tool_class(); #if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX) - godot::register_class(); + godot::register_class(); #endif } diff --git a/addons/godot_xterm/native/src/pseudoterminal.cpp b/addons/godot_xterm/native/src/pseudoterminal.cpp index a13594f..0708b96 100644 --- a/addons/godot_xterm/native/src/pseudoterminal.cpp +++ b/addons/godot_xterm/native/src/pseudoterminal.cpp @@ -9,168 +9,147 @@ #include #endif #if defined(PLATFORM_OSX) -#include #include +#include #endif using namespace godot; -void Pseudoterminal::_register_methods() -{ +void Pseudoterminal::_register_methods() { - register_method("_init", &Pseudoterminal::_init); - register_method("_ready", &Pseudoterminal::_ready); + register_method("_init", &Pseudoterminal::_init); + register_method("_ready", &Pseudoterminal::_ready); - register_method("write", &Pseudoterminal::write); - register_method("resize", &Pseudoterminal::resize); + register_method("write", &Pseudoterminal::write); + register_method("resize", &Pseudoterminal::resize); - register_signal((char *)"data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); - register_signal((char *)"exited", "status", GODOT_VARIANT_TYPE_INT); + register_signal((char *)"data_sent", "data", + GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + register_signal((char *)"exited", "status", + GODOT_VARIANT_TYPE_INT); } -Pseudoterminal::Pseudoterminal() -{ +Pseudoterminal::Pseudoterminal() {} + +Pseudoterminal::~Pseudoterminal() { pty_thread.join(); } + +void Pseudoterminal::_init() { + bytes_to_write = 0; + pty_thread = std::thread(&Pseudoterminal::process_pty, this); } -Pseudoterminal::~Pseudoterminal() -{ - pty_thread.join(); -} +void Pseudoterminal::process_pty() { + int fd; + char *name; + int status; -void Pseudoterminal::_init() -{ - bytes_to_write = 0; - pty_thread = std::thread(&Pseudoterminal::process_pty, this); -} + should_process_pty = true; -void Pseudoterminal::process_pty() -{ - int fd; - char *name; - int status; + struct termios termios = {}; + termios.c_iflag = IGNPAR | ICRNL; + termios.c_oflag = 0; + termios.c_cflag = B38400 | CRTSCTS | CS8 | CLOCAL | CREAD; + termios.c_lflag = ICANON; + termios.c_cc[VMIN] = 1; + termios.c_cc[VTIME] = 0; - should_process_pty = true; + pid_t pty_pid = forkpty(&fd, NULL, NULL, NULL); - struct termios termios = {}; - termios.c_iflag = IGNPAR | ICRNL; - termios.c_oflag = 0; - termios.c_cflag = B38400 | CRTSCTS | CS8 | CLOCAL | CREAD; - termios.c_lflag = ICANON; - termios.c_cc[VMIN] = 1; - termios.c_cc[VTIME] = 0; + if (pty_pid == -1) { + ERR_PRINT( + String("Error forking pty: {0}").format(Array::make(strerror(errno)))); + should_process_pty = false; + return; + } else if (pty_pid == 0) { + /* Child */ - pid_t pty_pid = forkpty(&fd, NULL, NULL, NULL); + char termenv[11] = {"TERM=xterm"}; + putenv(termenv); - if (pty_pid == -1) - { - ERR_PRINT(String("Error forking pty: {0}").format(Array::make(strerror(errno)))); - should_process_pty = false; - return; - } - else if (pty_pid == 0) - { - /* Child */ + char colortermenv[20] = {"COLORTERM=truecolor"}; + putenv(colortermenv); - char termenv[11] = {"TERM=xterm"}; - putenv(termenv); + char *shell = getenv("SHELL"); + char *argv[] = {basename(shell), NULL}; + execvp(shell, argv); + } else { + Vector2 zero = Vector2(0, 0); - char colortermenv[20] = {"COLORTERM=truecolor"}; - putenv(colortermenv); + /* Parent */ + while (1) { + { + std::lock_guard guard(size_mutex); + if (size != zero) { + struct winsize ws; + memset(&ws, 0, sizeof(ws)); + ws.ws_col = size.x; + ws.ws_row = size.y; - char *shell = getenv("SHELL"); - char *argv[] = { basename(shell), NULL }; - execvp(shell, argv); - } - else - { - Vector2 zero = Vector2(0, 0); - - /* Parent */ - while (1) - { - { - std::lock_guard guard(size_mutex); - if (size != zero) - { - struct winsize ws; - memset(&ws, 0, sizeof(ws)); - ws.ws_col = size.x; - ws.ws_row = size.y; - - ioctl(fd, TIOCSWINSZ, &ws); - } - } - - if (waitpid(pty_pid, &status, WNOHANG)) - { - emit_signal("exited", status); - return; - } - - int ready = -1; - fd_set read_fds; - fd_set write_fds; - - FD_ZERO(&read_fds); - FD_SET(fd, &read_fds); - FD_SET(fd, &write_fds); - - struct timeval timeout; - timeout.tv_sec = 10; - timeout.tv_usec = 0; - - ready = select(fd + 1, &read_fds, &write_fds, NULL, &timeout); - - if (ready > 0) - { - if (FD_ISSET(fd, &write_fds)) - { - std::lock_guard guard(write_buffer_mutex); - - if (bytes_to_write > 0) - { - ::write(fd, write_buffer, bytes_to_write); - bytes_to_write = 0; - } - } - - if (FD_ISSET(fd, &read_fds)) - { - std::lock_guard guard(read_buffer_mutex); - - int ret; - int bytes_read = 0; - - bytes_read = read(fd, read_buffer, MAX_READ_BUFFER_LENGTH); - - // TODO: handle error (-1) - if (bytes_read <= 0) - continue; - - PoolByteArray data = PoolByteArray(); - data.resize(bytes_read); - memcpy(data.write().ptr(), read_buffer, bytes_read); - - emit_signal("data_sent", PoolByteArray(data)); - } - } + ioctl(fd, TIOCSWINSZ, &ws); } + } + + if (waitpid(pty_pid, &status, WNOHANG)) { + emit_signal("exited", status); + return; + } + + int ready = -1; + fd_set read_fds; + fd_set write_fds; + + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + FD_SET(fd, &write_fds); + + struct timeval timeout; + timeout.tv_sec = 10; + timeout.tv_usec = 0; + + ready = select(fd + 1, &read_fds, &write_fds, NULL, &timeout); + + if (ready > 0) { + if (FD_ISSET(fd, &write_fds)) { + std::lock_guard guard(write_buffer_mutex); + + if (bytes_to_write > 0) { + ::write(fd, write_buffer, bytes_to_write); + bytes_to_write = 0; + } + } + + if (FD_ISSET(fd, &read_fds)) { + std::lock_guard guard(read_buffer_mutex); + + int ret; + int bytes_read = 0; + + bytes_read = read(fd, read_buffer, MAX_READ_BUFFER_LENGTH); + + // TODO: handle error (-1) + if (bytes_read <= 0) + continue; + + PoolByteArray data = PoolByteArray(); + data.resize(bytes_read); + memcpy(data.write().ptr(), read_buffer, bytes_read); + + emit_signal("data_sent", PoolByteArray(data)); + } + } } + } } -void Pseudoterminal::_ready() -{ +void Pseudoterminal::_ready() {} + +void Pseudoterminal::write(PoolByteArray data) { + std::lock_guard guard(write_buffer_mutex); + bytes_to_write = data.size(); + memcpy(write_buffer, data.read().ptr(), bytes_to_write); } -void Pseudoterminal::write(PoolByteArray data) -{ - std::lock_guard guard(write_buffer_mutex); - bytes_to_write = data.size(); - memcpy(write_buffer, data.read().ptr(), bytes_to_write); -} - -void Pseudoterminal::resize(Vector2 new_size) -{ - std::lock_guard guard(size_mutex); - size = new_size; +void Pseudoterminal::resize(Vector2 new_size) { + std::lock_guard guard(size_mutex); + size = new_size; } diff --git a/addons/godot_xterm/native/src/pseudoterminal.h b/addons/godot_xterm/native/src/pseudoterminal.h index dd48cc3..8526c33 100644 --- a/addons/godot_xterm/native/src/pseudoterminal.h +++ b/addons/godot_xterm/native/src/pseudoterminal.h @@ -3,49 +3,47 @@ #include #include -#include #include +#include -namespace godot -{ +namespace godot { - class Pseudoterminal : public Node - { - GODOT_CLASS(Pseudoterminal, Node) +class Pseudoterminal : public Node { + GODOT_CLASS(Pseudoterminal, Node) - public: - static const int MAX_READ_BUFFER_LENGTH = 1024; - static const int MAX_WRITE_BUFFER_LENGTH = 1024; +public: + static const int MAX_READ_BUFFER_LENGTH = 1024; + static const int MAX_WRITE_BUFFER_LENGTH = 1024; - private: - std::thread pty_thread; - bool should_process_pty; +private: + std::thread pty_thread; + bool should_process_pty; - char write_buffer[MAX_WRITE_BUFFER_LENGTH]; - int bytes_to_write; - std::mutex write_buffer_mutex; + char write_buffer[MAX_WRITE_BUFFER_LENGTH]; + int bytes_to_write; + std::mutex write_buffer_mutex; - char read_buffer[MAX_READ_BUFFER_LENGTH]; - int bytes_to_read; - std::mutex read_buffer_mutex; + char read_buffer[MAX_READ_BUFFER_LENGTH]; + int bytes_to_read; + std::mutex read_buffer_mutex; - Vector2 size; - std::mutex size_mutex; + Vector2 size; + std::mutex size_mutex; - void process_pty(); + void process_pty(); - public: - static void _register_methods(); +public: + static void _register_methods(); - Pseudoterminal(); - ~Pseudoterminal(); + Pseudoterminal(); + ~Pseudoterminal(); - void _init(); - void _ready(); + void _init(); + void _ready(); - void write(PoolByteArray data); - void resize(Vector2 size); - }; + void write(PoolByteArray data); + void resize(Vector2 size); +}; } // namespace godot #endif // PSEUDOTERMINAL_H \ No newline at end of file diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 0c0c998..a7d2138 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -1,10 +1,10 @@ #include "terminal.h" -#include #include #include #include #include #include +#include #include using namespace godot; @@ -13,603 +13,562 @@ const struct Terminal::cell Terminal::empty_cell = {{0, 0, 0, 0, 0}, {}}; const std::map, uint32_t> Terminal::keymap = { - // Godot does not have seperate scancodes for keypad keys when NumLock is off. - // We can check the unicode value to determine whether it is off and set the - // appropriate scancode. - // Based on the patch which adds support for this to TextEdit/LineEdit: - // https://github.com/godotengine/godot/pull/3269/files - {{'0', GlobalConstants::KEY_KP_0}, XKB_KEY_KP_0}, - {{0b0, GlobalConstants::KEY_KP_0}, XKB_KEY_KP_Insert}, - {{'1', GlobalConstants::KEY_KP_1}, XKB_KEY_KP_1}, - {{0b0, GlobalConstants::KEY_KP_1}, XKB_KEY_KP_End}, - {{'2', GlobalConstants::KEY_KP_2}, XKB_KEY_KP_2}, - {{0b0, GlobalConstants::KEY_KP_2}, XKB_KEY_KP_Down}, - {{'3', GlobalConstants::KEY_KP_3}, XKB_KEY_KP_3}, - {{0b0, GlobalConstants::KEY_KP_3}, XKB_KEY_KP_Page_Down}, - {{'4', GlobalConstants::KEY_KP_4}, XKB_KEY_KP_4}, - {{0b0, GlobalConstants::KEY_KP_4}, XKB_KEY_KP_Left}, - {{'5', GlobalConstants::KEY_KP_5}, XKB_KEY_KP_5}, - {{0b0, GlobalConstants::KEY_KP_5}, XKB_KEY_KP_Begin}, - {{'6', GlobalConstants::KEY_KP_6}, XKB_KEY_KP_6}, - {{0b0, GlobalConstants::KEY_KP_6}, XKB_KEY_KP_Right}, - {{'7', GlobalConstants::KEY_KP_7}, XKB_KEY_KP_7}, - {{0b0, GlobalConstants::KEY_KP_7}, XKB_KEY_KP_Home}, - {{'8', GlobalConstants::KEY_KP_8}, XKB_KEY_KP_8}, - {{0b0, GlobalConstants::KEY_KP_8}, XKB_KEY_KP_Up}, - {{'9', GlobalConstants::KEY_KP_9}, XKB_KEY_KP_9}, - {{0b0, GlobalConstants::KEY_KP_9}, XKB_KEY_KP_Page_Up}, - {{'.', GlobalConstants::KEY_KP_PERIOD}, XKB_KEY_KP_Decimal}, - {{0b0, GlobalConstants::KEY_KP_PERIOD}, XKB_KEY_KP_Delete}, - {{'/', GlobalConstants::KEY_KP_DIVIDE}, XKB_KEY_KP_Divide}, - {{'*', GlobalConstants::KEY_KP_MULTIPLY}, XKB_KEY_KP_Multiply}, - {{'-', GlobalConstants::KEY_KP_SUBTRACT}, XKB_KEY_KP_Subtract}, - {{'+', GlobalConstants::KEY_KP_ADD}, XKB_KEY_KP_Add}, - {{0b0, GlobalConstants::KEY_KP_ENTER}, XKB_KEY_KP_Enter}, - //{{ , }, XKB_KEY_KP_Equal}, - //{{ , }, XKB_KEY_KP_Separator}, - //{{ , }, XKB_KEY_KP_Tab}, - //{{ , }, XKB_KEY_KP_F1}, - //{{ , }, XKB_KEY_KP_F2}, - //{{ , }, XKB_KEY_KP_F3}, - //{{ , }, XKB_KEY_KP_F4}, + // Godot does not have seperate scancodes for keypad keys when NumLock is + // off. + // We can check the unicode value to determine whether it is off and set the + // appropriate scancode. + // Based on the patch which adds support for this to TextEdit/LineEdit: + // https://github.com/godotengine/godot/pull/3269/files + {{'0', GlobalConstants::KEY_KP_0}, XKB_KEY_KP_0}, + {{0b0, GlobalConstants::KEY_KP_0}, XKB_KEY_KP_Insert}, + {{'1', GlobalConstants::KEY_KP_1}, XKB_KEY_KP_1}, + {{0b0, GlobalConstants::KEY_KP_1}, XKB_KEY_KP_End}, + {{'2', GlobalConstants::KEY_KP_2}, XKB_KEY_KP_2}, + {{0b0, GlobalConstants::KEY_KP_2}, XKB_KEY_KP_Down}, + {{'3', GlobalConstants::KEY_KP_3}, XKB_KEY_KP_3}, + {{0b0, GlobalConstants::KEY_KP_3}, XKB_KEY_KP_Page_Down}, + {{'4', GlobalConstants::KEY_KP_4}, XKB_KEY_KP_4}, + {{0b0, GlobalConstants::KEY_KP_4}, XKB_KEY_KP_Left}, + {{'5', GlobalConstants::KEY_KP_5}, XKB_KEY_KP_5}, + {{0b0, GlobalConstants::KEY_KP_5}, XKB_KEY_KP_Begin}, + {{'6', GlobalConstants::KEY_KP_6}, XKB_KEY_KP_6}, + {{0b0, GlobalConstants::KEY_KP_6}, XKB_KEY_KP_Right}, + {{'7', GlobalConstants::KEY_KP_7}, XKB_KEY_KP_7}, + {{0b0, GlobalConstants::KEY_KP_7}, XKB_KEY_KP_Home}, + {{'8', GlobalConstants::KEY_KP_8}, XKB_KEY_KP_8}, + {{0b0, GlobalConstants::KEY_KP_8}, XKB_KEY_KP_Up}, + {{'9', GlobalConstants::KEY_KP_9}, XKB_KEY_KP_9}, + {{0b0, GlobalConstants::KEY_KP_9}, XKB_KEY_KP_Page_Up}, + {{'.', GlobalConstants::KEY_KP_PERIOD}, XKB_KEY_KP_Decimal}, + {{0b0, GlobalConstants::KEY_KP_PERIOD}, XKB_KEY_KP_Delete}, + {{'/', GlobalConstants::KEY_KP_DIVIDE}, XKB_KEY_KP_Divide}, + {{'*', GlobalConstants::KEY_KP_MULTIPLY}, XKB_KEY_KP_Multiply}, + {{'-', GlobalConstants::KEY_KP_SUBTRACT}, XKB_KEY_KP_Subtract}, + {{'+', GlobalConstants::KEY_KP_ADD}, XKB_KEY_KP_Add}, + {{0b0, GlobalConstants::KEY_KP_ENTER}, XKB_KEY_KP_Enter}, + //{{ , }, XKB_KEY_KP_Equal}, + //{{ , }, XKB_KEY_KP_Separator}, + //{{ , }, XKB_KEY_KP_Tab}, + //{{ , }, XKB_KEY_KP_F1}, + //{{ , }, XKB_KEY_KP_F2}, + //{{ , }, XKB_KEY_KP_F3}, + //{{ , }, XKB_KEY_KP_F4}, - // Godot scancodes do not distinguish between uppercase and lowercase - // letters, so we can check the unicode value to determine this. - {{'a', GlobalConstants::KEY_A}, XKB_KEY_a}, - {{'A', GlobalConstants::KEY_A}, XKB_KEY_A}, - {{'b', GlobalConstants::KEY_B}, XKB_KEY_b}, - {{'B', GlobalConstants::KEY_B}, XKB_KEY_B}, - {{'c', GlobalConstants::KEY_C}, XKB_KEY_c}, - {{'C', GlobalConstants::KEY_C}, XKB_KEY_C}, - {{'d', GlobalConstants::KEY_D}, XKB_KEY_d}, - {{'D', GlobalConstants::KEY_D}, XKB_KEY_D}, - {{'e', GlobalConstants::KEY_E}, XKB_KEY_e}, - {{'E', GlobalConstants::KEY_E}, XKB_KEY_E}, - {{'f', GlobalConstants::KEY_F}, XKB_KEY_f}, - {{'F', GlobalConstants::KEY_F}, XKB_KEY_F}, - {{'g', GlobalConstants::KEY_G}, XKB_KEY_g}, - {{'G', GlobalConstants::KEY_G}, XKB_KEY_G}, - {{'h', GlobalConstants::KEY_H}, XKB_KEY_h}, - {{'H', GlobalConstants::KEY_H}, XKB_KEY_H}, - {{'i', GlobalConstants::KEY_I}, XKB_KEY_i}, - {{'I', GlobalConstants::KEY_I}, XKB_KEY_I}, - {{'j', GlobalConstants::KEY_J}, XKB_KEY_j}, - {{'J', GlobalConstants::KEY_J}, XKB_KEY_J}, - {{'k', GlobalConstants::KEY_K}, XKB_KEY_k}, - {{'K', GlobalConstants::KEY_K}, XKB_KEY_K}, - {{'l', GlobalConstants::KEY_L}, XKB_KEY_l}, - {{'L', GlobalConstants::KEY_L}, XKB_KEY_L}, - {{'m', GlobalConstants::KEY_M}, XKB_KEY_m}, - {{'M', GlobalConstants::KEY_M}, XKB_KEY_M}, - {{'n', GlobalConstants::KEY_N}, XKB_KEY_n}, - {{'N', GlobalConstants::KEY_N}, XKB_KEY_N}, - {{'o', GlobalConstants::KEY_O}, XKB_KEY_o}, - {{'O', GlobalConstants::KEY_O}, XKB_KEY_O}, - {{'p', GlobalConstants::KEY_P}, XKB_KEY_p}, - {{'P', GlobalConstants::KEY_P}, XKB_KEY_P}, - {{'q', GlobalConstants::KEY_Q}, XKB_KEY_q}, - {{'Q', GlobalConstants::KEY_Q}, XKB_KEY_Q}, - {{'r', GlobalConstants::KEY_R}, XKB_KEY_r}, - {{'R', GlobalConstants::KEY_R}, XKB_KEY_R}, - {{'s', GlobalConstants::KEY_S}, XKB_KEY_s}, - {{'S', GlobalConstants::KEY_S}, XKB_KEY_S}, - {{'t', GlobalConstants::KEY_T}, XKB_KEY_t}, - {{'T', GlobalConstants::KEY_T}, XKB_KEY_T}, - {{'u', GlobalConstants::KEY_U}, XKB_KEY_u}, - {{'U', GlobalConstants::KEY_U}, XKB_KEY_U}, - {{'v', GlobalConstants::KEY_V}, XKB_KEY_v}, - {{'V', GlobalConstants::KEY_V}, XKB_KEY_V}, - {{'w', GlobalConstants::KEY_W}, XKB_KEY_w}, - {{'W', GlobalConstants::KEY_W}, XKB_KEY_W}, - {{'x', GlobalConstants::KEY_X}, XKB_KEY_x}, - {{'X', GlobalConstants::KEY_X}, XKB_KEY_X}, - {{'y', GlobalConstants::KEY_Y}, XKB_KEY_y}, - {{'Y', GlobalConstants::KEY_Y}, XKB_KEY_Y}, - {{'z', GlobalConstants::KEY_Z}, XKB_KEY_z}, - {{'Z', GlobalConstants::KEY_Z}, XKB_KEY_Z}, + // Godot scancodes do not distinguish between uppercase and lowercase + // letters, so we can check the unicode value to determine this. + {{'a', GlobalConstants::KEY_A}, XKB_KEY_a}, + {{'A', GlobalConstants::KEY_A}, XKB_KEY_A}, + {{'b', GlobalConstants::KEY_B}, XKB_KEY_b}, + {{'B', GlobalConstants::KEY_B}, XKB_KEY_B}, + {{'c', GlobalConstants::KEY_C}, XKB_KEY_c}, + {{'C', GlobalConstants::KEY_C}, XKB_KEY_C}, + {{'d', GlobalConstants::KEY_D}, XKB_KEY_d}, + {{'D', GlobalConstants::KEY_D}, XKB_KEY_D}, + {{'e', GlobalConstants::KEY_E}, XKB_KEY_e}, + {{'E', GlobalConstants::KEY_E}, XKB_KEY_E}, + {{'f', GlobalConstants::KEY_F}, XKB_KEY_f}, + {{'F', GlobalConstants::KEY_F}, XKB_KEY_F}, + {{'g', GlobalConstants::KEY_G}, XKB_KEY_g}, + {{'G', GlobalConstants::KEY_G}, XKB_KEY_G}, + {{'h', GlobalConstants::KEY_H}, XKB_KEY_h}, + {{'H', GlobalConstants::KEY_H}, XKB_KEY_H}, + {{'i', GlobalConstants::KEY_I}, XKB_KEY_i}, + {{'I', GlobalConstants::KEY_I}, XKB_KEY_I}, + {{'j', GlobalConstants::KEY_J}, XKB_KEY_j}, + {{'J', GlobalConstants::KEY_J}, XKB_KEY_J}, + {{'k', GlobalConstants::KEY_K}, XKB_KEY_k}, + {{'K', GlobalConstants::KEY_K}, XKB_KEY_K}, + {{'l', GlobalConstants::KEY_L}, XKB_KEY_l}, + {{'L', GlobalConstants::KEY_L}, XKB_KEY_L}, + {{'m', GlobalConstants::KEY_M}, XKB_KEY_m}, + {{'M', GlobalConstants::KEY_M}, XKB_KEY_M}, + {{'n', GlobalConstants::KEY_N}, XKB_KEY_n}, + {{'N', GlobalConstants::KEY_N}, XKB_KEY_N}, + {{'o', GlobalConstants::KEY_O}, XKB_KEY_o}, + {{'O', GlobalConstants::KEY_O}, XKB_KEY_O}, + {{'p', GlobalConstants::KEY_P}, XKB_KEY_p}, + {{'P', GlobalConstants::KEY_P}, XKB_KEY_P}, + {{'q', GlobalConstants::KEY_Q}, XKB_KEY_q}, + {{'Q', GlobalConstants::KEY_Q}, XKB_KEY_Q}, + {{'r', GlobalConstants::KEY_R}, XKB_KEY_r}, + {{'R', GlobalConstants::KEY_R}, XKB_KEY_R}, + {{'s', GlobalConstants::KEY_S}, XKB_KEY_s}, + {{'S', GlobalConstants::KEY_S}, XKB_KEY_S}, + {{'t', GlobalConstants::KEY_T}, XKB_KEY_t}, + {{'T', GlobalConstants::KEY_T}, XKB_KEY_T}, + {{'u', GlobalConstants::KEY_U}, XKB_KEY_u}, + {{'U', GlobalConstants::KEY_U}, XKB_KEY_U}, + {{'v', GlobalConstants::KEY_V}, XKB_KEY_v}, + {{'V', GlobalConstants::KEY_V}, XKB_KEY_V}, + {{'w', GlobalConstants::KEY_W}, XKB_KEY_w}, + {{'W', GlobalConstants::KEY_W}, XKB_KEY_W}, + {{'x', GlobalConstants::KEY_X}, XKB_KEY_x}, + {{'X', GlobalConstants::KEY_X}, XKB_KEY_X}, + {{'y', GlobalConstants::KEY_Y}, XKB_KEY_y}, + {{'Y', GlobalConstants::KEY_Y}, XKB_KEY_Y}, + {{'z', GlobalConstants::KEY_Z}, XKB_KEY_z}, + {{'Z', GlobalConstants::KEY_Z}, XKB_KEY_Z}, - {{'0', GlobalConstants::KEY_0}, XKB_KEY_0}, - {{'1', GlobalConstants::KEY_1}, XKB_KEY_1}, - {{'2', GlobalConstants::KEY_2}, XKB_KEY_2}, - {{'3', GlobalConstants::KEY_3}, XKB_KEY_3}, - {{'4', GlobalConstants::KEY_4}, XKB_KEY_4}, - {{'5', GlobalConstants::KEY_5}, XKB_KEY_5}, - {{'6', GlobalConstants::KEY_6}, XKB_KEY_6}, - {{'7', GlobalConstants::KEY_7}, XKB_KEY_7}, - {{'8', GlobalConstants::KEY_8}, XKB_KEY_8}, - {{'9', GlobalConstants::KEY_9}, XKB_KEY_9}, + {{'0', GlobalConstants::KEY_0}, XKB_KEY_0}, + {{'1', GlobalConstants::KEY_1}, XKB_KEY_1}, + {{'2', GlobalConstants::KEY_2}, XKB_KEY_2}, + {{'3', GlobalConstants::KEY_3}, XKB_KEY_3}, + {{'4', GlobalConstants::KEY_4}, XKB_KEY_4}, + {{'5', GlobalConstants::KEY_5}, XKB_KEY_5}, + {{'6', GlobalConstants::KEY_6}, XKB_KEY_6}, + {{'7', GlobalConstants::KEY_7}, XKB_KEY_7}, + {{'8', GlobalConstants::KEY_8}, XKB_KEY_8}, + {{'9', GlobalConstants::KEY_9}, XKB_KEY_9}, - {{'[', GlobalConstants::KEY_BRACKETLEFT}, XKB_KEY_bracketleft}, - {{'[', GlobalConstants::KEY_BRACKETLEFT}, XKB_KEY_bracketright}, - {{'{', GlobalConstants::KEY_BRACELEFT}, XKB_KEY_braceleft}, - {{'}', GlobalConstants::KEY_BRACERIGHT}, XKB_KEY_braceright}, + {{'[', GlobalConstants::KEY_BRACKETLEFT}, XKB_KEY_bracketleft}, + {{'[', GlobalConstants::KEY_BRACKETLEFT}, XKB_KEY_bracketright}, + {{'{', GlobalConstants::KEY_BRACELEFT}, XKB_KEY_braceleft}, + {{'}', GlobalConstants::KEY_BRACERIGHT}, XKB_KEY_braceright}, - {{'\\', GlobalConstants::KEY_BACKSLASH}, XKB_KEY_backslash}, - {{'|', GlobalConstants::KEY_BAR}, XKB_KEY_bar}, - {{'`', GlobalConstants::KEY_QUOTELEFT}, XKB_KEY_grave}, - {{'~', GlobalConstants::KEY_ASCIITILDE}, XKB_KEY_asciitilde}, - {{'/', GlobalConstants::KEY_SLASH}, XKB_KEY_slash}, - {{'?', GlobalConstants::KEY_QUESTION}, XKB_KEY_question}, + {{'\\', GlobalConstants::KEY_BACKSLASH}, XKB_KEY_backslash}, + {{'|', GlobalConstants::KEY_BAR}, XKB_KEY_bar}, + {{'`', GlobalConstants::KEY_QUOTELEFT}, XKB_KEY_grave}, + {{'~', GlobalConstants::KEY_ASCIITILDE}, XKB_KEY_asciitilde}, + {{'/', GlobalConstants::KEY_SLASH}, XKB_KEY_slash}, + {{'?', GlobalConstants::KEY_QUESTION}, XKB_KEY_question}, - {{0, GlobalConstants::KEY_HOME}, XKB_KEY_Home}, - {{0, GlobalConstants::KEY_BACKSPACE}, XKB_KEY_BackSpace}, - {{0, GlobalConstants::KEY_BACKTAB}, XKB_KEY_ISO_Left_Tab}, - {{0, GlobalConstants::KEY_CLEAR}, XKB_KEY_Clear}, - {{0, GlobalConstants::KEY_PAUSE}, XKB_KEY_Pause}, - {{0, GlobalConstants::KEY_SCROLLLOCK}, XKB_KEY_Scroll_Lock}, - {{0, GlobalConstants::KEY_SYSREQ}, XKB_KEY_Sys_Req}, - {{0, GlobalConstants::KEY_ESCAPE}, XKB_KEY_Escape}, - {{0, GlobalConstants::KEY_ENTER}, XKB_KEY_Return}, - {{0, GlobalConstants::KEY_INSERT}, XKB_KEY_Insert}, - {{0, GlobalConstants::KEY_DELETE}, XKB_KEY_Delete}, - {{0, GlobalConstants::KEY_PAGEUP}, XKB_KEY_Page_Up}, - {{0, GlobalConstants::KEY_PAGEDOWN}, XKB_KEY_Page_Down}, - {{0, GlobalConstants::KEY_UP}, XKB_KEY_Up}, - {{0, GlobalConstants::KEY_DOWN}, XKB_KEY_Down}, - {{0, GlobalConstants::KEY_RIGHT}, XKB_KEY_Right}, - {{0, GlobalConstants::KEY_LEFT}, XKB_KEY_Left}, - {{0, GlobalConstants::KEY_TAB}, XKB_KEY_Tab}, - //{{ , }, XKB_KEY_Linefeed}, - //{{ , }, XKB_KEY_Find}, - //{{ , }, XKB_KEY_Select}, + {{0, GlobalConstants::KEY_HOME}, XKB_KEY_Home}, + {{0, GlobalConstants::KEY_BACKSPACE}, XKB_KEY_BackSpace}, + {{0, GlobalConstants::KEY_BACKTAB}, XKB_KEY_ISO_Left_Tab}, + {{0, GlobalConstants::KEY_CLEAR}, XKB_KEY_Clear}, + {{0, GlobalConstants::KEY_PAUSE}, XKB_KEY_Pause}, + {{0, GlobalConstants::KEY_SCROLLLOCK}, XKB_KEY_Scroll_Lock}, + {{0, GlobalConstants::KEY_SYSREQ}, XKB_KEY_Sys_Req}, + {{0, GlobalConstants::KEY_ESCAPE}, XKB_KEY_Escape}, + {{0, GlobalConstants::KEY_ENTER}, XKB_KEY_Return}, + {{0, GlobalConstants::KEY_INSERT}, XKB_KEY_Insert}, + {{0, GlobalConstants::KEY_DELETE}, XKB_KEY_Delete}, + {{0, GlobalConstants::KEY_PAGEUP}, XKB_KEY_Page_Up}, + {{0, GlobalConstants::KEY_PAGEDOWN}, XKB_KEY_Page_Down}, + {{0, GlobalConstants::KEY_UP}, XKB_KEY_Up}, + {{0, GlobalConstants::KEY_DOWN}, XKB_KEY_Down}, + {{0, GlobalConstants::KEY_RIGHT}, XKB_KEY_Right}, + {{0, GlobalConstants::KEY_LEFT}, XKB_KEY_Left}, + {{0, GlobalConstants::KEY_TAB}, XKB_KEY_Tab}, + //{{ , }, XKB_KEY_Linefeed}, + //{{ , }, XKB_KEY_Find}, + //{{ , }, XKB_KEY_Select}, - {{0, GlobalConstants::KEY_F1}, XKB_KEY_F1}, - {{0, GlobalConstants::KEY_F2}, XKB_KEY_F2}, - {{0, GlobalConstants::KEY_F3}, XKB_KEY_F3}, - {{0, GlobalConstants::KEY_F4}, XKB_KEY_F4}, - {{0, GlobalConstants::KEY_F5}, XKB_KEY_F5}, - {{0, GlobalConstants::KEY_F6}, XKB_KEY_F6}, - {{0, GlobalConstants::KEY_F7}, XKB_KEY_F7}, - {{0, GlobalConstants::KEY_F8}, XKB_KEY_F8}, - {{0, GlobalConstants::KEY_F9}, XKB_KEY_F9}, - {{0, GlobalConstants::KEY_F10}, XKB_KEY_F10}, - {{0, GlobalConstants::KEY_F11}, XKB_KEY_F11}, - {{0, GlobalConstants::KEY_F12}, XKB_KEY_F12}, - {{0, GlobalConstants::KEY_F13}, XKB_KEY_F13}, - {{0, GlobalConstants::KEY_F14}, XKB_KEY_F14}, - {{0, GlobalConstants::KEY_F15}, XKB_KEY_F15}, - {{0, GlobalConstants::KEY_F16}, XKB_KEY_F16}, - //{{0, GlobalConstants::KEY_F17}, XKB_KEY_F17}, - //{{0, GlobalConstants::KEY_F18}, XKB_KEY_F18}, - //{{0, GlobalConstants::KEY_F19}, XKB_KEY_F19}, - //{{0, GlobalConstants::KEY_F20}, XKB_KEY_F20}, + {{0, GlobalConstants::KEY_F1}, XKB_KEY_F1}, + {{0, GlobalConstants::KEY_F2}, XKB_KEY_F2}, + {{0, GlobalConstants::KEY_F3}, XKB_KEY_F3}, + {{0, GlobalConstants::KEY_F4}, XKB_KEY_F4}, + {{0, GlobalConstants::KEY_F5}, XKB_KEY_F5}, + {{0, GlobalConstants::KEY_F6}, XKB_KEY_F6}, + {{0, GlobalConstants::KEY_F7}, XKB_KEY_F7}, + {{0, GlobalConstants::KEY_F8}, XKB_KEY_F8}, + {{0, GlobalConstants::KEY_F9}, XKB_KEY_F9}, + {{0, GlobalConstants::KEY_F10}, XKB_KEY_F10}, + {{0, GlobalConstants::KEY_F11}, XKB_KEY_F11}, + {{0, GlobalConstants::KEY_F12}, XKB_KEY_F12}, + {{0, GlobalConstants::KEY_F13}, XKB_KEY_F13}, + {{0, GlobalConstants::KEY_F14}, XKB_KEY_F14}, + {{0, GlobalConstants::KEY_F15}, XKB_KEY_F15}, + {{0, GlobalConstants::KEY_F16}, XKB_KEY_F16}, + //{{0, GlobalConstants::KEY_F17}, XKB_KEY_F17}, + //{{0, GlobalConstants::KEY_F18}, XKB_KEY_F18}, + //{{0, GlobalConstants::KEY_F19}, XKB_KEY_F19}, + //{{0, GlobalConstants::KEY_F20}, XKB_KEY_F20}, }; -static struct -{ - Color col; - bool is_set; +static struct { + Color col; + bool is_set; } colours[16]; -static void term_output(const char *s, size_t len, void *user) -{ +static void term_output(const char *s, size_t len, void *user) {} + +static void write_cb(struct tsm_vte *vte, const char *u8, size_t len, + void *data) { + + Terminal *term = static_cast(data); + + PoolByteArray bytes = PoolByteArray(); + + for (int i = 0; i < len; i++) + bytes.append(u8[i]); + + if (len > 0) { + if (term->input_event_key.is_valid()) { + // The callback was fired from a key press event so emit the "key_pressed" + // signal. + term->emit_signal("key_pressed", String(u8), term->input_event_key); + term->input_event_key.unref(); + } + + term->emit_signal("data_sent", bytes); + } } -static void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) -{ +static int text_draw_cb(struct tsm_screen *con, uint64_t id, const uint32_t *ch, + size_t len, unsigned int width, unsigned int posx, + unsigned int posy, const struct tsm_screen_attr *attr, + tsm_age_t age, void *data) { - Terminal *term = static_cast(data); + Terminal *terminal = static_cast(data); - PoolByteArray bytes = PoolByteArray(); + if (age <= terminal->framebuffer_age) + return 0; - for (int i = 0; i < len; i++) - bytes.append(u8[i]); + size_t ulen; + char buf[5] = {0}; - if (len > 0) - { - if (term->input_event_key.is_valid()) - { - // The callback was fired from a key press event so emit the "key_pressed" signal. - term->emit_signal("key_pressed", String(u8), term->input_event_key); - term->input_event_key.unref(); - } + if (len > 0) { + char *utf8 = tsm_ucs4_to_utf8_alloc(ch, len, &ulen); + memcpy(terminal->cells[posy][posx].ch, utf8, ulen); + } else { + terminal->cells[posy][posx] = {}; + } - term->emit_signal("data_sent", bytes); - } + memcpy(&terminal->cells[posy][posx].attr, attr, sizeof(tsm_screen_attr)); + + if (!terminal->sleep) + terminal->update(); + + return 0; } -static int text_draw_cb(struct tsm_screen *con, - uint64_t id, - const uint32_t *ch, - size_t len, - unsigned int width, - unsigned int posx, - unsigned int posy, - const struct tsm_screen_attr *attr, - tsm_age_t age, - void *data) -{ +void Terminal::_register_methods() { + register_method("_init", &Terminal::_init); + register_method("_ready", &Terminal::_ready); + register_method("_gui_input", &Terminal::_gui_input); + register_method("_draw", &Terminal::_draw); + register_method("_notification", &Terminal::_notification); - Terminal *terminal = static_cast(data); + register_method("write", &Terminal::write); + register_method("update_size", &Terminal::update_size); - if (age <= terminal->framebuffer_age) - return 0; + register_property("rows", &Terminal::rows, 24); + register_property("cols", &Terminal::cols, 80); - size_t ulen; - char buf[5] = {0}; - - if (len > 0) - { - char *utf8 = tsm_ucs4_to_utf8_alloc(ch, len, &ulen); - memcpy(terminal->cells[posy][posx].ch, utf8, ulen); - } - else - { - terminal->cells[posy][posx] = {}; - } - - memcpy(&terminal->cells[posy][posx].attr, attr, sizeof(tsm_screen_attr)); - - if (!terminal->sleep) - terminal->update(); - - return 0; + register_signal("data_sent", "data", + GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + register_signal("key_pressed", "data", GODOT_VARIANT_TYPE_STRING, + "event", GODOT_VARIANT_TYPE_OBJECT); + register_signal("size_changed", "new_size", + GODOT_VARIANT_TYPE_VECTOR2); } -void Terminal::_register_methods() -{ - register_method("_init", &Terminal::_init); - register_method("_ready", &Terminal::_ready); - register_method("_gui_input", &Terminal::_gui_input); - register_method("_draw", &Terminal::_draw); - register_method("_notification", &Terminal::_notification); +Terminal::Terminal() {} - register_method("write", &Terminal::write); - register_method("update_size", &Terminal::update_size); +Terminal::~Terminal() {} - register_property("rows", &Terminal::rows, 24); - register_property("cols", &Terminal::cols, 80); +void Terminal::_init() { + sleep = true; - register_signal("data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); - register_signal("key_pressed", "data", GODOT_VARIANT_TYPE_STRING, "event", GODOT_VARIANT_TYPE_OBJECT); - register_signal("size_changed", "new_size", GODOT_VARIANT_TYPE_VECTOR2); + if (tsm_screen_new(&screen, NULL, NULL)) { + ERR_PRINT("Error creating new tsm screen"); + } + tsm_screen_set_max_sb(screen, + 1000); // TODO: Use config var for scrollback size. + + if (tsm_vte_new(&vte, screen, write_cb, this, NULL, NULL)) { + ERR_PRINT("Error creating new tsm vte"); + } + + update_theme(); } -Terminal::Terminal() -{ +void Terminal::_ready() { + update_size(); + connect("resized", this, "update_size"); } -Terminal::~Terminal() -{ +void Terminal::_notification(int what) { + switch (what) { + case NOTIFICATION_RESIZED: + update_size(); + break; + case NOTIFICATION_THEME_CHANGED: + update_theme(); + break; + } } -void Terminal::_init() -{ - sleep = true; +void Terminal::_gui_input(Variant event) { + Ref k = event; - if (tsm_screen_new(&screen, NULL, NULL)) - { - ERR_PRINT("Error creating new tsm screen"); - } - tsm_screen_set_max_sb(screen, 1000); // TODO: Use config var for scrollback size. + if (k.is_valid()) { + if (!k->is_pressed()) { + return; + } - if (tsm_vte_new(&vte, screen, write_cb, this, NULL, NULL)) - { - ERR_PRINT("Error creating new tsm vte"); - } + int64_t scancode = k->get_scancode(); + int64_t unicode = k->get_unicode(); + uint32_t ascii = unicode <= 127 ? unicode : 0; - update_theme(); + unsigned int mods = 0; + if (k->get_alt()) + mods |= TSM_ALT_MASK; + if (k->get_control()) + mods |= TSM_CONTROL_MASK; + if (k->get_shift()) + mods |= TSM_SHIFT_MASK; + + auto iter = keymap.find({unicode, scancode}); + uint32_t keysym = (iter != keymap.end() ? iter->second : XKB_KEY_NoSymbol); + + input_event_key = k; + tsm_vte_handle_keyboard(vte, keysym, ascii, mods, + unicode ? unicode : TSM_VTE_INVALID); + } } -void Terminal::_ready() -{ - update_size(); - connect("resized", this, "update_size"); +void Terminal::_draw() { + if (sleep) + return; + + /* Draw the full terminal rect background */ + Color background_color = palette[TSM_COLOR_BACKGROUND]; + + draw_rect(Rect2(Vector2(0, 0), get_rect().size), background_color); + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + /* Draw cell background and foreground */ + std::pair color_pair = get_cell_colors(row, col); + draw_background(row, col, color_pair.first); + draw_foreground(row, col, color_pair.second); + } + } } -void Terminal::_notification(int what) -{ - switch (what) - { - case NOTIFICATION_RESIZED: - update_size(); - break; - case NOTIFICATION_THEME_CHANGED: - update_theme(); - break; - } +void Terminal::update_theme() { + /* Generate color palette based on theme */ + + // Converts a color from the Control's theme to one that can + // be used in a tsm color palette. + auto set_pallete_color = [this](tsm_vte_color color, String theme_color, + int default_r, int default_g, + int default_b) -> void { + Color c; + + if (has_color(theme_color, "Terminal")) { + c = get_color(theme_color, "Terminal"); + } else { + int r = default_r; + int g = default_g; + int b = default_b; + c = Color((float)r / 255.0, (float)g / 255.0, (float)b / 255.0); + } + + color_palette[color][0] = c.get_r8(); + color_palette[color][1] = c.get_g8(); + color_palette[color][2] = c.get_b8(); + + palette[color] = c; + }; + + set_pallete_color(TSM_COLOR_BLACK, "Black", 0, 0, 0); + set_pallete_color(TSM_COLOR_RED, "Red", 205, 0, 0); + set_pallete_color(TSM_COLOR_GREEN, "Green", 0, 205, 0); + set_pallete_color(TSM_COLOR_YELLOW, "Yellow", 205, 205, 0); + set_pallete_color(TSM_COLOR_BLUE, "Blue", 0, 0, 238); + set_pallete_color(TSM_COLOR_MAGENTA, "Magenta", 205, 0, 205); + set_pallete_color(TSM_COLOR_CYAN, "Cyan", 0, 205, 205); + set_pallete_color(TSM_COLOR_LIGHT_GREY, "Light Grey", 229, 229, 229); + set_pallete_color(TSM_COLOR_DARK_GREY, "Dark Grey", 127, 127, 127); + set_pallete_color(TSM_COLOR_LIGHT_RED, "Light Red", 255, 0, 0); + set_pallete_color(TSM_COLOR_LIGHT_GREEN, "Light Green", 0, 255, 0); + set_pallete_color(TSM_COLOR_LIGHT_YELLOW, "Light Yellow", 255, 255, 0); + set_pallete_color(TSM_COLOR_LIGHT_BLUE, "Light Blue", 0, 0, 255); + set_pallete_color(TSM_COLOR_LIGHT_MAGENTA, "Light Magenta", 255, 0, 255); + set_pallete_color(TSM_COLOR_LIGHT_CYAN, "Light Cyan", 0, 255, 255); + set_pallete_color(TSM_COLOR_WHITE, "White", 255, 255, 255); + + set_pallete_color(TSM_COLOR_BACKGROUND, "Background", 255, 255, 255); + set_pallete_color(TSM_COLOR_FOREGROUND, "Foreground", 0, 0, 0); + + if (tsm_vte_set_custom_palette(vte, color_palette)) { + ERR_PRINT("Error setting custom palette"); + } + if (tsm_vte_set_palette(vte, "custom")) { + ERR_PRINT("Error setting palette"); + } + + /* Load fonts into the fontmap from theme */ + + auto set_font = [this](String font_style, String default_font_path) -> void { + Ref fontref; + ResourceLoader *rl = ResourceLoader::get_singleton(); + + if (has_font(font_style, "Terminal")) { + fontref = get_font(font_style, "Terminal"); + } else { + fontref = rl->load(default_font_path); + } + + fontmap.insert(std::pair>(font_style, fontref)); + }; + + set_font( + "Bold Italic", + "res://addons/godot_xterm/themes/fonts/cousine/cousine_bold_italic.tres"); + set_font("Bold", + "res://addons/godot_xterm/themes/fonts/cousine/cousine_bold.tres"); + set_font("Italic", + "res://addons/godot_xterm/themes/fonts/cousine/cousine_italic.tres"); + set_font( + "Regular", + "res://addons/godot_xterm/themes/fonts/cousine/cousine_regular.tres"); } -void Terminal::_gui_input(Variant event) -{ - Ref k = event; +void Terminal::draw_background(int row, int col, Color bgcolor) { - if (k.is_valid()) - { - if (!k->is_pressed()) - { - return; - } - - int64_t scancode = k->get_scancode(); - int64_t unicode = k->get_unicode(); - uint32_t ascii = unicode <= 127 ? unicode : 0; - - unsigned int mods = 0; - if (k->get_alt()) - mods |= TSM_ALT_MASK; - if (k->get_control()) - mods |= TSM_CONTROL_MASK; - if (k->get_shift()) - mods |= TSM_SHIFT_MASK; - - auto iter = keymap.find({unicode, scancode}); - uint32_t keysym = (iter != keymap.end() ? iter->second : XKB_KEY_NoSymbol); - - input_event_key = k; - tsm_vte_handle_keyboard(vte, keysym, ascii, mods, unicode ? unicode : TSM_VTE_INVALID); - } + /* Draw the background */ + Vector2 background_pos = Vector2(col * cell_size.x, row * cell_size.y); + Rect2 background_rect = Rect2(background_pos, cell_size); + draw_rect(background_rect, bgcolor); } -void Terminal::_draw() -{ - if (sleep) - return; +void Terminal::draw_foreground(int row, int col, Color fgcolor) { - /* Draw the full terminal rect background */ - Color background_color = palette[TSM_COLOR_BACKGROUND]; + struct cell cell = cells[row][col]; - draw_rect(Rect2(Vector2(0, 0), get_rect().size), background_color); + if (cell.ch == nullptr) + return; // No foreground to draw - for (int row = 0; row < rows; row++) - { - for (int col = 0; col < cols; col++) - { - /* Draw cell background and foreground */ - std::pair color_pair = get_cell_colors(row, col); - draw_background(row, col, color_pair.first); - draw_foreground(row, col, color_pair.second); - } - } + /* Set the font */ + + Ref fontref = get_font(""); + + if (cell.attr.bold && cell.attr.italic) { + fontref = fontmap["Bold Italic"]; + } else if (cell.attr.bold) { + fontref = fontmap["Bold"]; + } else if (cell.attr.italic) { + fontref = fontmap["Italic"]; + } else { + fontref = fontmap["Regular"]; + } + + /* Draw the foreground */ + + if (cell.attr.blink) + ; // TODO: Handle blink + + int font_height = fontref.ptr()->get_height(); + Vector2 foreground_pos = + Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25); + draw_string(fontref, foreground_pos, cell.ch, fgcolor); + + if (cell.attr.underline) + draw_string(fontref, foreground_pos, "_", fgcolor); } -void Terminal::update_theme() -{ - /* Generate color palette based on theme */ +std::pair Terminal::get_cell_colors(int row, int col) { + struct cell cell = cells[row][col]; + Color fgcol, bgcol; + float fr = 0, fg = 0, fb = 0, br = 1, bg = 1, bb = 1; - // Converts a color from the Control's theme to one that can - // be used in a tsm color palette. - auto set_pallete_color = [this](tsm_vte_color color, String theme_color, int default_r, int default_g, int default_b) -> void { - Color c; + /* Get foreground color */ - if (has_color(theme_color, "Terminal")) { - c = get_color(theme_color, "Terminal"); - } else { - int r = default_r; - int g = default_g; - int b = default_b; - c = Color((float)r / 255.0, (float)g / 255.0, (float)b / 255.0); - } + if (cell.attr.fccode && palette.count(cell.attr.fccode)) { + fgcol = palette[cell.attr.fccode]; + } else { + fr = (float)cell.attr.fr / 255.0; + fg = (float)cell.attr.fg / 255.0; + fb = (float)cell.attr.fb / 255.0; + fgcol = Color(fr, fg, fb); - color_palette[color][0] = c.get_r8(); - color_palette[color][1] = c.get_g8(); - color_palette[color][2] = c.get_b8(); + if (cell.attr.fccode != -1) { + palette.insert( + std::pair(cell.attr.fccode, Color(fr, fg, fb))); + } + } - palette[color] = c; - }; + /* Get background color */ - set_pallete_color(TSM_COLOR_BLACK, "Black", 0, 0, 0); - set_pallete_color(TSM_COLOR_RED, "Red", 205, 0, 0); - set_pallete_color(TSM_COLOR_GREEN, "Green", 0, 205, 0); - set_pallete_color(TSM_COLOR_YELLOW, "Yellow", 205, 205, 0); - set_pallete_color(TSM_COLOR_BLUE, "Blue", 0, 0, 238); - set_pallete_color(TSM_COLOR_MAGENTA, "Magenta", 205, 0, 205); - set_pallete_color(TSM_COLOR_CYAN, "Cyan", 0, 205, 205); - set_pallete_color(TSM_COLOR_LIGHT_GREY, "Light Grey", 229, 229, 229); - set_pallete_color(TSM_COLOR_DARK_GREY, "Dark Grey", 127, 127, 127); - set_pallete_color(TSM_COLOR_LIGHT_RED, "Light Red", 255, 0, 0); - set_pallete_color(TSM_COLOR_LIGHT_GREEN, "Light Green", 0, 255, 0); - set_pallete_color(TSM_COLOR_LIGHT_YELLOW, "Light Yellow", 255, 255, 0); - set_pallete_color(TSM_COLOR_LIGHT_BLUE, "Light Blue", 0, 0, 255); - set_pallete_color(TSM_COLOR_LIGHT_MAGENTA, "Light Magenta", 255, 0, 255); - set_pallete_color(TSM_COLOR_LIGHT_CYAN, "Light Cyan", 0, 255, 255); - set_pallete_color(TSM_COLOR_WHITE, "White", 255, 255, 255); + if (cell.attr.bccode && palette.count(cell.attr.bccode)) { + bgcol = palette[cell.attr.bccode]; + } else { + br = (float)cell.attr.br / 255.0; + bg = (float)cell.attr.bg / 255.0; + bb = (float)cell.attr.bb / 255.0; + bgcol = Color(br, bg, bb); - set_pallete_color(TSM_COLOR_BACKGROUND, "Background", 255, 255, 255); - set_pallete_color(TSM_COLOR_FOREGROUND, "Foreground", 0, 0, 0); + if (cell.attr.bccode != -1) { + palette.insert( + std::pair(cell.attr.bccode, Color(br, bg, bb))); + } + } - if (tsm_vte_set_custom_palette(vte, color_palette)) - { - ERR_PRINT("Error setting custom palette"); - } - if (tsm_vte_set_palette(vte, "custom")) - { - ERR_PRINT("Error setting palette"); - } + if (cell.attr.inverse) + std::swap(bgcol, fgcol); - - /* Load fonts into the fontmap from theme */ - - auto set_font = [this](String font_style, String default_font_path) -> void { - Ref fontref; - ResourceLoader* rl = ResourceLoader::get_singleton(); - - if (has_font(font_style, "Terminal")) { - fontref = get_font(font_style, "Terminal"); - } else { - fontref = rl->load(default_font_path); - } - - fontmap.insert(std::pair>(font_style, fontref)); - }; - - set_font("Bold Italic", "res://addons/godot_xterm/themes/fonts/cousine/cousine_bold_italic.tres"); - set_font("Bold", "res://addons/godot_xterm/themes/fonts/cousine/cousine_bold.tres"); - set_font("Italic", "res://addons/godot_xterm/themes/fonts/cousine/cousine_italic.tres"); - set_font("Regular", "res://addons/godot_xterm/themes/fonts/cousine/cousine_regular.tres"); + return std::make_pair(bgcol, fgcol); } -void Terminal::draw_background(int row, int col, Color bgcolor) -{ +// Recalculates the cell_size and number of cols/rows based on font size and the +// Control's rect_size +void Terminal::update_size() { + sleep = true; - /* Draw the background */ - Vector2 background_pos = Vector2(col * cell_size.x, row * cell_size.y); - Rect2 background_rect = Rect2(background_pos, cell_size); - draw_rect(background_rect, bgcolor); + Ref fontref = fontmap.count("Regular") + ? fontmap["Regular"] + : has_font("Regular", "Terminal") + ? get_font("Regular", "Terminal") + : get_font(""); + cell_size = fontref->get_string_size("W"); + + rows = std::max(2, (int)floor(get_rect().size.y / cell_size.y)); + cols = std::max(1, (int)floor(get_rect().size.x / cell_size.x)); + + emit_signal("size_changed", Vector2(cols, rows)); + + Cells new_cells = {}; + + for (int x = 0; x < rows; x++) { + Row row(cols); + + for (int y = 0; y < cols; y++) { + if (x < cells.size() && y < cells[x].size()) { + row[y] = cells[x][y]; + } else { + row[y] = empty_cell; + } + } + + new_cells.push_back(row); + } + + cells = new_cells; + + tsm_screen_resize(screen, cols, rows); + + sleep = false; + framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this); + update(); } -void Terminal::draw_foreground(int row, int col, Color fgcolor) -{ +void Terminal::write(Variant data) { - struct cell cell = cells[row][col]; + const char *u8; + size_t len; - if (cell.ch == nullptr) - return; // No foreground to draw + switch (data.get_type()) { + case Variant::Type::POOL_BYTE_ARRAY: { + PoolByteArray bytes = data; + u8 = (char *)bytes.read().ptr(); + len = bytes.size(); + break; + } + case Variant::Type::STRING: { + String string = data; + u8 = string.alloc_c_string(); + len = strlen(u8); + break; + } + default: + WARN_PRINT("Method expected a String or PoolByteArray"); + return; + } - /* Set the font */ - - Ref fontref = get_font(""); - - if (cell.attr.bold && cell.attr.italic) - { - fontref = fontmap["Bold Italic"]; - } - else if (cell.attr.bold) - { - fontref = fontmap["Bold"]; - } - else if (cell.attr.italic) - { - fontref = fontmap["Italic"]; - } - else - { - fontref = fontmap["Regular"]; - } - - /* Draw the foreground */ - - if (cell.attr.blink) - ; // TODO: Handle blink - - int font_height = fontref.ptr()->get_height(); - Vector2 foreground_pos = Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25); - draw_string(fontref, foreground_pos, cell.ch, fgcolor); - - if (cell.attr.underline) - draw_string(fontref, foreground_pos, "_", fgcolor); -} - -std::pair Terminal::get_cell_colors(int row, int col) -{ - struct cell cell = cells[row][col]; - Color fgcol, bgcol; - float fr = 0, fg = 0, fb = 0, br = 1, bg = 1, bb = 1; - - /* Get foreground color */ - - if (cell.attr.fccode && palette.count(cell.attr.fccode)) - { - fgcol = palette[cell.attr.fccode]; - } - else - { - fr = (float)cell.attr.fr / 255.0; - fg = (float)cell.attr.fg / 255.0; - fb = (float)cell.attr.fb / 255.0; - fgcol = Color(fr, fg, fb); - - if (cell.attr.fccode != -1) - { - palette.insert(std::pair(cell.attr.fccode, Color(fr, fg, fb))); - } - } - - /* Get background color */ - - if (cell.attr.bccode && palette.count(cell.attr.bccode)) - { - bgcol = palette[cell.attr.bccode]; - } - else - { - br = (float)cell.attr.br / 255.0; - bg = (float)cell.attr.bg / 255.0; - bb = (float)cell.attr.bb / 255.0; - bgcol = Color(br, bg, bb); - - if (cell.attr.bccode != -1) - { - palette.insert(std::pair(cell.attr.bccode, Color(br, bg, bb))); - } - } - - if (cell.attr.inverse) - std::swap(bgcol, fgcol); - - return std::make_pair(bgcol, fgcol); -} - -// Recalculates the cell_size and number of cols/rows based on font size and the Control's rect_size -void Terminal::update_size() -{ - sleep = true; - - Ref fontref = fontmap.count("Regular") ? fontmap["Regular"] : has_font("Regular", "Terminal") ? get_font("Regular", "Terminal") : get_font(""); - cell_size = fontref->get_string_size("W"); - - rows = std::max(2, (int)floor(get_rect().size.y / cell_size.y)); - cols = std::max(1, (int)floor(get_rect().size.x / cell_size.x)); - - emit_signal("size_changed", Vector2(cols, rows)); - - Cells new_cells = {}; - - for (int x = 0; x < rows; x++) - { - Row row(cols); - - for (int y = 0; y < cols; y++) - { - if (x < cells.size() && y < cells[x].size()) - { - row[y] = cells[x][y]; - } - else - { - row[y] = empty_cell; - } - } - - new_cells.push_back(row); - } - - cells = new_cells; - - tsm_screen_resize(screen, cols, rows); - - sleep = false; - framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this); - update(); -} - -void Terminal::write(Variant data) -{ - - const char *u8; - size_t len; - - switch (data.get_type()) - { - case Variant::Type::POOL_BYTE_ARRAY: - { - PoolByteArray bytes = data; - u8 = (char *)bytes.read().ptr(); - len = bytes.size(); - break; - } - case Variant::Type::STRING: - { - String string = data; - u8 = string.alloc_c_string(); - len = strlen(u8); - break; - } - default: - WARN_PRINT("Method expected a String or PoolByteArray"); - return; - } - - tsm_vte_input(vte, u8, len); - framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this); + tsm_vte_input(vte, u8, len); + framebuffer_age = tsm_screen_draw(screen, text_draw_cb, this); } diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index a7764f1..e74b3a1 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -1,78 +1,75 @@ #ifndef TERMINAL_H #define TERMINAL_H -#include #include #include #include +#include #include #include -namespace godot -{ +namespace godot { - class Terminal : public Control - { - GODOT_CLASS(Terminal, Control) +class Terminal : public Control { + GODOT_CLASS(Terminal, Control) - public: - struct cell - { - char ch[5]; - struct tsm_screen_attr attr; - }; - static const struct cell empty_cell; +public: + struct cell { + char ch[5]; + struct tsm_screen_attr attr; + }; + static const struct cell empty_cell; - public: - typedef std::vector> Cells; - typedef std::vector Row; +public: + typedef std::vector> Cells; + typedef std::vector Row; - Cells cells; + Cells cells; - Ref input_event_key; + Ref input_event_key; - protected: - tsm_screen *screen; - tsm_vte *vte; +protected: + tsm_screen *screen; + tsm_vte *vte; - private: - static const uint8_t default_color_palette[TSM_COLOR_NUM][3]; - static const std::map, uint32_t> keymap; +private: + static const uint8_t default_color_palette[TSM_COLOR_NUM][3]; + static const std::map, uint32_t> keymap; - Vector2 cell_size; - std::map palette = {}; - std::map> fontmap = {}; + Vector2 cell_size; + std::map palette = {}; + std::map> fontmap = {}; - void update_size(); + void update_size(); - void update_theme(); - std::pair get_cell_colors(int row, int col); - void draw_background(int row, int col, Color bgcol); - void draw_foreground(int row, int col, Color fgcol); + void update_theme(); + std::pair get_cell_colors(int row, int col); + void draw_background(int row, int col, Color bgcol); + void draw_foreground(int row, int col, Color fgcol); - public: - static void _register_methods(); +public: + static void _register_methods(); - Terminal(); - ~Terminal(); + Terminal(); + ~Terminal(); - void _init(); - void _ready(); - void _notification(int what); - void _gui_input(Variant event); - void _draw(); + void _init(); + void _ready(); + void _notification(int what); + void _gui_input(Variant event); + void _draw(); - void write(Variant data); + void write(Variant data); - int rows; - int cols; + int rows; + int cols; - bool sleep; + bool sleep; - uint8_t color_palette[TSM_COLOR_NUM][3]; + uint8_t color_palette[TSM_COLOR_NUM][3]; - tsm_age_t framebuffer_age; - }; + tsm_age_t framebuffer_age; +}; } // namespace godot #endif // TERMINAL_H diff --git a/misc/hooks/README.md b/misc/hooks/README.md new file mode 100644 index 0000000..4e98f5c --- /dev/null +++ b/misc/hooks/README.md @@ -0,0 +1,21 @@ +# Git hooks for GodotXterm + +This folder contains git hooks meant to be installed locally by GodotXterm +contributors to make sure they comply with our requirements. + +## List of hooks + +- Pre-commit hook for gdformat: Applies formatting to staged gdscript files + using the [GDScript Toolkit](https://github.com/Scony/godot-gdscript-toolkit) by Pawel Lampe et al. + +- Pre-commit hook for clang-format: Applies clang-format to the staged files + before accepting a commit; blocks the commit and generates a patch if the + style is not respected. + Should work on Linux and macOS. You may need to edit the file if your + clang-format binary is not in the $PATH, or if you want to enable colored + output with pygmentize. + +## Installation + +Symlink (or copy) all the files from this folder (except this README) into your .git/hooks folder, and make sure +the hooks and helper scripts are executable. diff --git a/misc/hooks/canonicalize_filename.sh b/misc/hooks/canonicalize_filename.sh new file mode 100755 index 0000000..5eecabf --- /dev/null +++ b/misc/hooks/canonicalize_filename.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# Provide the canonicalize filename (physical filename with out any symlinks) +# like the GNU version readlink with the -f option regardless of the version of +# readlink (GNU or BSD). + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +########################################################### +# There should be no need to change anything below this line. + +# Canonicalize by recursively following every symlink in every component of the +# specified filename. This should reproduce the results of the GNU version of +# readlink with the -f option. +# +# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac +canonicalize_filename () { + local target_file="$1" + local physical_directory="" + local result="" + + # Need to restore the working directory after work. + local working_dir="`pwd`" + + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + + # Iterate down a (possible) chain of symlinks + while [ -L "$target_file" ] + do + target_file="$(readlink -- "$target_file")" + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + done + + # Compute the canonicalized name by finding the physical path + # for the directory we're in and appending the target file. + physical_directory="`pwd -P`" + result="$physical_directory/$target_file" + + # restore the working directory after work. + cd -- "$working_dir" + + echo "$result" +} diff --git a/misc/hooks/pre-commit b/misc/hooks/pre-commit new file mode 100755 index 0000000..32e960f --- /dev/null +++ b/misc/hooks/pre-commit @@ -0,0 +1,50 @@ +#!/bin/sh +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# CONFIGURATION: +# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +HOOKS="pre-commit-gdformat" +HOOKS="pre-commit-clang-format" +########################################################### +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# Absolute path to this script, e.g. /home/user/bin/foo.sh +SCRIPT="$(canonicalize_filename "$0")" + +# Absolute path this script is in, thus /home/user/bin +SCRIPTPATH="$(dirname -- "$SCRIPT")" + + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$SCRIPTPATH/$hook" ]; then + "$SCRIPTPATH/$hook" + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable." + echo "You can disable it by removing it from the list in $SCRIPT." + echo "You can skip all pre-commit hooks with --no-verify (not recommended)." + exit 1 + fi +done diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format new file mode 100755 index 0000000..a8cd3fb --- /dev/null +++ b/misc/hooks/pre-commit-clang-format @@ -0,0 +1,148 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i sh -p clang + +# git pre-commit hook that runs a clang-format stylecheck. +# Features: +# - abort commit when commit does not comply with the style guidelines +# - create a patch of the proposed style changes +# Modifications for clang-format by rene.milk@wwu.de + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +# Some quality of life modifications made for Godot Engine. + +################################################################## +# SETTINGS +# Set path to clang-format binary +# CLANG_FORMAT="/usr/bin/clang-format" +CLANG_FORMAT=`which clang-format` + +# Remove any older patches from previous commits. Set to true or false. +# DELETE_OLD_PATCHES=false +DELETE_OLD_PATCHES=false + +# Only parse files with the extensions in FILE_EXTS. Set to true or false. +# If false every changed file in the commit will be parsed with clang-format. +# If true only files matching one of the extensions are parsed with clang-format. +# PARSE_EXTS=true +PARSE_EXTS=true + +# File types to parse. Only effective when PARSE_EXTS is true. +# FILE_EXTS=".c .h .cpp .hpp" +FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl" + +# Use pygmentize instead of cat to parse diff with highlighting. +# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) +# READER="pygmentize -l diff" +READER=cat + +################################################################## +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# check whether the given file matches any of the set extensions +matches_extension() { + local filename=$(basename "$1") + local extension=".${filename##*.}" + local ext + + for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done + + return 1 +} + +# necessary check for initial commit +if git rev-parse --verify HEAD >/dev/null 2>&1 ; then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +if [ ! -x "$CLANG_FORMAT" ] ; then + printf "Error: clang-format executable not found.\n" + printf "Set the correct path in $(canonicalize_filename "$0").\n" + exit 1 +fi + +# create a random filename to store our generated patch +prefix="pre-commit-clang-format" +suffix="$(date +%s)" +patch="/tmp/$prefix-$suffix.patch" + +# clean up any older clang-format patches +$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch + +# create one patch containing all changes to the files +git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; +do + # ignore thirdparty files + if grep -q "thirdparty" <<< $file; then + continue; + fi + + # ignore file if we do check for file extensions and the file + # does not match any of the extensions specified in $FILE_EXTS + if $PARSE_EXTS && ! matches_extension "$file"; then + continue; + fi + + # clang-format our sourcefile, create a patch with diff and append it to our $patch + # The sed call is necessary to transform the patch from + # --- $file timestamp + # +++ - timestamp + # to both lines working on the same file and having a/ and b/ prefix. + # Else it can not be applied with 'git apply'. + "$CLANG_FORMAT" -style=file "$file" | \ + diff -u "$file" - | \ + sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch" +done + +# if no patch has been generated all is ok, clean up the file stub and exit +if [ ! -s "$patch" ] ; then + printf "Files in this commit comply with the clang-format rules.\n" + rm -f "$patch" + exit 0 +fi + +# a patch has been created, notify the user and exit +printf "\nThe following differences were found between the code to commit " +printf "and the clang-format rules:\n\n" +$READER "$patch" +printf "\n" + +# Allows us to read user input below, assigns stdin to keyboard +exec < /dev/tty + +while true; do + read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + case $yn in + [Yy] ) git apply $patch; + printf "The patch was applied. You can now stage the changes and commit again.\n\n"; + break + ;; + [Nn] ) printf "\nYou can apply these changes with:\n git apply $patch\n"; + printf "(may need to be called from the root directory of your repository)\n"; + printf "Aborting commit. Apply changes and commit again or skip checking with"; + printf " --no-verify (not recommended).\n\n"; + break + ;; + [Ss] ) git apply $patch; + git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; + do git add $file; + done + printf "The patch was applied and the changed files staged. You can now commit.\n\n"; + break + ;; + * ) echo "Please answer yes or no." + ;; + esac +done +exit 1 # we don't commit in any case diff --git a/misc/hooks/pre-commit-gdformat b/misc/hooks/pre-commit-gdformat new file mode 100755 index 0000000..6b34543 --- /dev/null +++ b/misc/hooks/pre-commit-gdformat @@ -0,0 +1,23 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i sh -p python38 + +GDTOOLKIT_VERSION=f5e2746d146200ec07ac6acb6fb378fd4c64f3f0 + +FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.gd$' | sed 's| |\\ |g') +[ -z "$FILES" ] && exit 0 + +# Setup GDScript Toolkit. +if [ ! -f .venv/bin/activate ] || ! source .venv/bin/activate; then + python -m venv .venv && source .venv/bin/activate; +fi +if ! gdformat --version; then + pip install git+git://github.com/Scony/godot-gdscript-toolkit@${gdtoolkit_version}; +fi + +# Format all selected files. +echo "$FILES" | xargs gdformat + +# Add back the formatted files to staging. +echo "$FILES" | xargs git add + +exit 0