From 6b4a3ecba432324c548f391085b635c7124affd3 Mon Sep 17 00:00:00 2001
From: Leroy Hopson <git@leroy.geek.nz>
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::Terminal>();
+  godot::register_tool_class<godot::Terminal>();
 #if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
-	godot::register_class<godot::Pseudoterminal>();
+  godot::register_class<godot::Pseudoterminal>();
 #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 <pty.h>
 #endif
 #if defined(PLATFORM_OSX)
-#include <util.h>
 #include <sys/ioctl.h>
+#include <util.h>
 #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<Pseudoterminal>((char *)"data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
-    register_signal<Pseudoterminal>((char *)"exited", "status", GODOT_VARIANT_TYPE_INT);
+  register_signal<Pseudoterminal>((char *)"data_sent", "data",
+                                  GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
+  register_signal<Pseudoterminal>((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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> guard(size_mutex);
-    size = new_size;
+void Pseudoterminal::resize(Vector2 new_size) {
+  std::lock_guard<std::mutex> 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 <Godot.hpp>
 #include <Node.hpp>
-#include <thread>
 #include <mutex>
+#include <thread>
 
-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 <algorithm>
 #include <GlobalConstants.hpp>
 #include <InputEventKey.hpp>
 #include <OS.hpp>
 #include <ResourceLoader.hpp>
 #include <Theme.hpp>
+#include <algorithm>
 #include <xkbcommon/xkbcommon-keysyms.h>
 
 using namespace godot;
@@ -13,603 +13,562 @@ const struct Terminal::cell Terminal::empty_cell = {{0, 0, 0, 0, 0}, {}};
 
 const std::map<std::pair<int64_t, int64_t>, 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<Terminal *>(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<Terminal *>(data);
+  Terminal *terminal = static_cast<Terminal *>(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<Terminal *>(data);
+  register_method("write", &Terminal::write);
+  register_method("update_size", &Terminal::update_size);
 
-	if (age <= terminal->framebuffer_age)
-		return 0;
+  register_property<Terminal, int>("rows", &Terminal::rows, 24);
+  register_property<Terminal, int>("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<Terminal>("data_sent", "data",
+                            GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
+  register_signal<Terminal>("key_pressed", "data", GODOT_VARIANT_TYPE_STRING,
+                            "event", GODOT_VARIANT_TYPE_OBJECT);
+  register_signal<Terminal>("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<Terminal, int>("rows", &Terminal::rows, 24);
-	register_property<Terminal, int>("cols", &Terminal::cols, 80);
+void Terminal::_init() {
+  sleep = true;
 
-	register_signal<Terminal>("data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
-	register_signal<Terminal>("key_pressed", "data", GODOT_VARIANT_TYPE_STRING, "event", GODOT_VARIANT_TYPE_OBJECT);
-	register_signal<Terminal>("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<InputEventKey> 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, Color> 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<Font> 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<String, Ref<Font>>(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<InputEventKey> 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, Color> 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<Font> 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<Color, Color> 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<int, Color>(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<int, Color>(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<Font> 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<String, Ref<Font>>(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<Font> 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<Font> 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<Color, Color> 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<int, Color>(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<int, Color>(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<Font> 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 <libtsm.h>
 #include <Control.hpp>
 #include <Font.hpp>
 #include <Godot.hpp>
+#include <libtsm.h>
 #include <map>
 #include <vector>
 
-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<std::vector<struct cell>> Cells;
-		typedef std::vector<struct cell> Row;
+public:
+  typedef std::vector<std::vector<struct cell>> Cells;
+  typedef std::vector<struct cell> Row;
 
-		Cells cells;
+  Cells cells;
 
-		Ref<InputEventKey> input_event_key;
+  Ref<InputEventKey> 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<std::pair<int64_t, int64_t>, uint32_t> keymap;
+private:
+  static const uint8_t default_color_palette[TSM_COLOR_NUM][3];
+  static const std::map<std::pair<int64_t, int64_t>, uint32_t> keymap;
 
-		Vector2 cell_size;
-		std::map<int, Color> palette = {};
-		std::map<String, Ref<Font>> fontmap = {};
+  Vector2 cell_size;
+  std::map<int, Color> palette = {};
+  std::map<String, Ref<Font>> fontmap = {};
 
-		void update_size();
+  void update_size();
 
-		void update_theme();
-		std::pair<Color, Color> 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<Color, Color> 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