diff --git a/.config/kermit.conf b/.config/kermit.conf index f6b485c..7ec6c70 100644 --- a/.config/kermit.conf +++ b/.config/kermit.conf @@ -6,15 +6,27 @@ locale en_US.UTF-8 # Word char exceptions char "-./?%&#_=+@~" -# Action key binding (alt/shift) +# Action key binding (alt/shift) key alt # Cursor shape (block/ibeam/underline) cursor_shape block # Custom command key bindings -# bind/bindx [KEY]~"[COMMAND]" +# bind/bindx/bindi [KEY]~"[COMMAND]" # bindx f~"df -h" +bindi c~"copy" +bindi v~"paste" +bindi t~"new-tab" +bindi r~"reload-config" +bindi d~"default-config" +bindi k~"inc-font-size" +bindi j~"dec-font-size" +bindi equal~"default-font-size" +bindi plus~"default-font-size" +bindi l~"next-tab" +bindi h~"prev-tab" +bindi BackSpace~"close-tab" # Tab position (top/bottom) tab bottom diff --git a/README.md b/README.md index 245bdb3..1e83d8f 100644 --- a/README.md +++ b/README.md @@ -91,28 +91,27 @@ kermit [-h] [-v] [-d] [-c config] [-t title] [-w workdir] [-e command] [-e command] sets the command to execute in terminal ``` -## Key Bindings - -| Key | Action | -| --------------------------- | ------------------------------ | -| `ctrl-alt-[c]` | copy to clipboard | -| `ctrl-alt-[v]` | paste from clipboard | -| `ctrl-alt-[t]` | open a new tab | -| `ctrl-alt-[r]` | reload configuration file | -| `ctrl-alt-[d]` | load the default configuration | -| `ctrl-alt-[q]` | exit the terminal | -| `ctrl-alt-[k][up]` | increase font size | -| `ctrl-alt-[j][down]` | decrease font size | -| `ctrl-alt-[=]` | reset font size to default | -| `ctrl-alt-[return]` | open a new tab | -| `ctrl-alt-[num]` | switch to the tab number | -| `ctrl-alt-[l][right][pgup]` | switch to the next tab | -| `ctrl-alt-[h][left][pgdn]` | switch to the previous tab | -| `ctrl-alt-[w][backspace]` | close the selected tab | - -• Key bindings (`ctrl-alt`) can be set to `ctrl-shift` with using the config file. - -• Default key bindings (`ctrl-alt`) might conflict with your desktop environments window shortcuts. To solve this issue, key bindings should be changed to `ctrl-shift`. +## Default Key Bindings + +| Key | Action | +|----------------------------------------|-----------------------------------| +| `ctrl` + `alt` + `c` | Copy to clipboard | +| `ctrl` + `alt` + `v` | Paste from clipboard | +| `ctrl` + `alt` + `t`/`return` | Open a new tab | +| `ctrl` + `alt` + `r` | Reload configuration file | +| `ctrl` + `alt` + `d` | Load default configuration | +| `ctrl` + `alt` + `q` | Exit the terminal | +| `ctrl` + `alt` + `k`/`up` | Increase font size | +| `ctrl` + `alt` + `j`/`down` | Decrease font size | +| `ctrl` + `alt` + `equals`/`plus` | Reset font size to default | +| `ctrl` + `alt` + `` | Switch to tab with number `` | +| `ctrl` + `alt` + `l`/`right`/`pageup` | Switch to next tab | +| `ctrl` + `alt` + `h`/`left`/`pagedown` | Switch to previous tab | +| `ctrl` + `alt` + `backspace` | Close the current tab | + +The default modifiers (`ctrl` + `alt`) can be set to `ctrl` + `shift` using the config file. +Key bindings can be overridden by custom key bindings. +See [Configuration / Key Bindings](#key-bindings) for more information. ## Customization @@ -136,10 +135,10 @@ font [FAMILY-LIST] [STYLE-OPTIONS] [SIZE] `FAMILY-LIST` is a comma-separated list of families optionally terminated by a comma, `STYLE_OPTIONS` is a whitespace-separated list of words where each WORD describes one of style, variant, weight, or stretch, and `SIZE` is a decimal number (size in points). -• Available font families: `Normal, Sans, Serif and Monospace`. -• Available styles: `Normal, Oblique, Italic`. -• Available weights: `Ultra-Light, Light, Normal, Bold,Ultra-Bold, Heavy`. -• Available variants: `Normal, Small-Caps`. +• Available font families: `Normal, Sans, Serif and Monospace`. +• Available styles: `Normal, Oblique, Italic`. +• Available weights: `Ultra-Light, Light, Normal, Bold,Ultra-Bold, Heavy`. +• Available variants: `Normal, Small-Caps`. • Available stretch styles: `Ultra-Condensed, Extra-Condensed, Condensed, Semi-Condensed, Normal, Semi-Expanded, Expanded, Extra-Expanded, Ultra-Expanded`. Examples: @@ -155,11 +154,12 @@ font monospace bold italic condensed 12 Custom keys and associated commands can be specified with the configuration file. An example entry is available [here](https://github.com/orhun/kermit/blob/master/.config/kermit.conf#L14) and entry format is shown below. ``` -bind/bindx [KEY]~"[COMMAND]" +bind/bindx/bindi [KEY]~"[COMMAND]" ``` - bind: `Send command to the terminal.` - bindx: `Send command to the terminal and execute.` +- bindi: `Execute internal command` Examples: @@ -168,7 +168,24 @@ bindx f~"df -h" bind r~"rm -i " bind p~"ps aux | grep " bind k~"kill -9 " -``` +bindi c~"copy" +``` + +Currently available internal commands (`bindi`): +* `copy`: copy to clipboard +* `paste`: paste from clipboard +* `reload-config`: reload config +* `default-config`: load default config +* `new-tab`: open new tab +* `exit`: exit kermit +* `inc-font-size`: increase font size by 1 +* `dec-font-size`: decrease font size by 1 +* `default-font-size`: reset font size to default +* `next-tab`: go to next tab +* `prev-tab`: go to previous tab +* `close-tab`: close current tab +* `new-window`: open new window with same working directory (requires `vte.sh`). + ### Padding diff --git a/src/kermit.c b/src/kermit.c index a14ece2..a27208d 100644 --- a/src/kermit.c +++ b/src/kermit.c @@ -71,17 +71,46 @@ static gboolean defaultConfigFile = TRUE; /* Boolean value for -c argument */ static gboolean debugMessages = FALSE; /* Boolean value for -d argument */ static gboolean closeTab = FALSE; /* Close the tab on child-exited signal */ static va_list vargs; /* Hold information about variable arguments */ -typedef struct KeyBindings { /* Key bindings struct */ +static GdkRGBA termPalette[TERM_PALETTE_SIZE]; /* Terminal colors */ +typedef struct { /* Key bindings struct */ + gboolean internal; char *key; char *cmd; } Bindings; +typedef struct { /* Default key bindings struct */ + Bindings bind; + gboolean invalid; +} DefaultBindings; static Bindings keyBindings[TERM_CONFIG_LENGTH]; /* Array for custom key bindings */ -static GdkRGBA termPalette[TERM_PALETTE_SIZE]; /* Terminal colors */ +static DefaultBindings defaultKeyBindings[] = { /* Preconfigured default key bindings */ + { .bind = { .key = "c", .cmd = "copy", .internal = TRUE } }, + { .bind = { .key = "v", .cmd = "paste", .internal = TRUE } }, + { .bind = { .key = "t", .cmd = "new-tab", .internal = TRUE } }, + { .bind = { .key = "return", .cmd = "new-tab", .internal = TRUE } }, + { .bind = { .key = "r", .cmd = "reload-config", .internal = TRUE } }, + { .bind = { .key = "d", .cmd = "default-config", .internal = TRUE } }, + { .bind = { .key = "q", .cmd = "exit", .internal = TRUE } }, + { .bind = { .key = "k", .cmd = "inc-font-size", .internal = TRUE } }, + { .bind = { .key = "up", .cmd = "inc-font-size", .internal = TRUE } }, + { .bind = { .key = "j", .cmd = "dec-font-size", .internal = TRUE } }, + { .bind = { .key = "down", .cmd = "dec-font-size", .internal = TRUE } }, + { .bind = { .key = "equals", .cmd = "default-font-size", .internal = TRUE } }, + { .bind = { .key = "plus", .cmd = "default-font-size", .internal = TRUE } }, + { .bind = { .key = "l", .cmd = "next-tab", .internal = TRUE } }, + { .bind = { .key = "right", .cmd = "next-tab", .internal = TRUE } }, + { .bind = { .key = "page_down", .cmd = "next-tab", .internal = TRUE } }, + { .bind = { .key = "h", .cmd = "prev-tab", .internal = TRUE } }, + { .bind = { .key = "left", .cmd = "prev-tab", .internal = TRUE } }, + { .bind = { .key = "page_up", .cmd = "prev-tab", .internal = TRUE } }, + { .bind = { .key = "w", .cmd = "close-tab", .internal = TRUE } }, + { .bind = { .key = "backspace", .cmd = "close-tab", .internal = TRUE } }, +}; +static size_t defaultKeyCount = sizeof(defaultKeyBindings) / sizeof(DefaultBindings); /*! * Print log (debug) message with format specifiers. * - * \param format string and format specifiers for vfprintf function + * \param format string and format specifiers for vfprintf function * \return 0 on success */ static int printLog(char *format, ...) { @@ -115,6 +144,57 @@ static int connectSignals(GtkWidget *terminal) { return 0; } +/* + * Handle internal action + * + * \param terminal + * \param action + * \return FALSE if no action is found & TRUE otherwise + */ +static gboolean termAction(GtkWidget *terminal, const char *action) { + if (strcmp(action, "copy") == 0) { + vte_terminal_copy_clipboard_format(VTE_TERMINAL(terminal), + VTE_FORMAT_TEXT); + } else if (strcmp(action, "paste") == 0) { + vte_terminal_paste_clipboard(VTE_TERMINAL(terminal)); + } else if (strcmp(action, "reload-config") == 0) { + printLog("Reloading configuration file...\n"); + if (defaultConfigFile) + configFileName = NULL; + parseSettings(); + configureTerm(terminal); + } else if (strcmp(action, "default-config") == 0) { + printLog("Loading the default configuration...\n"); + colorCount = 0; + configureTerm(terminal); + } else if (strcmp(action, "new-tab") == 0) { + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), getTerm(), NULL); + gtk_widget_show_all(window); + } else if (strcmp(action, "exit") == 0) { + gtk_main_quit(); + } else if (strcmp(action, "inc-font-size") == 0) { + setTermFont(terminal, currentFontSize + 1); + } else if (strcmp(action, "dec-font-size") == 0) { + setTermFont(terminal, currentFontSize - 1); + } else if (strcmp(action, "default-font-size") == 0) { + setTermFont(terminal, defaultFontSize); + } else if (strcmp(action, "next-tab") == 0) { + gtk_notebook_next_page(GTK_NOTEBOOK(notebook)); + } else if (strcmp(action, "prev-tab") == 0) { + gtk_notebook_prev_page(GTK_NOTEBOOK(notebook)); + } else if (strcmp(action, "close-tab") == 0) { + if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) == 1) + return TRUE; + closeTab = TRUE; + gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + gtk_widget_queue_draw(GTK_WIDGET(notebook)); + } else { + return FALSE; + } + return TRUE; +} + /*! * Handle terminal exit. * @@ -148,7 +228,7 @@ static gboolean termOnChildExit(VteTerminal *terminal, gint status, /*! * Handle terminal key press events. - * + * * \param terminal * \param event (key press or release) * \param userData @@ -167,96 +247,29 @@ static gboolean termOnKeyPress(GtkWidget *terminal, GdkEventKey *event, atoi(gdk_keyval_name(event->keyval)) - 1); return TRUE; } - switch (event->keyval) { - /* Copy & Paste */ - case GDK_KEY_C: /* Fallthrough */ - case GDK_KEY_c: - vte_terminal_copy_clipboard_format(VTE_TERMINAL(terminal), - VTE_FORMAT_TEXT); - return TRUE; - case GDK_KEY_V: /* Fallthrough */ - case GDK_KEY_v: - vte_terminal_paste_clipboard(VTE_TERMINAL(terminal)); - return TRUE; - /* Reload configuration file */ - case GDK_KEY_R: /* Fallthrough */ - case GDK_KEY_r: - printLog("Reloading configuration file...\n"); - if (defaultConfigFile) - configFileName = NULL; - parseSettings(); - configureTerm(terminal); - return TRUE; - /* Load the default configuration */ - case GDK_KEY_D: - case GDK_KEY_d: - printLog("Loading the default configuration...\n"); - colorCount = 0; - configureTerm(terminal); - return TRUE; - /* Open new tab */ - case GDK_KEY_T: /* Fallthrough */ - case GDK_KEY_t: - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), getTerm(), NULL); - gtk_widget_show_all(window); - return TRUE; - /* Exit */ - case GDK_KEY_Q: /* Fallthrough */ - case GDK_KEY_q: - gtk_main_quit(); - return TRUE; - /* Change font size */ - case GDK_KEY_K: /* Fallthrough */ - case GDK_KEY_k: /* Fallthrough */ - case GDK_KEY_Up: - setTermFont(terminal, currentFontSize + 1); - return TRUE; - case GDK_KEY_J: /* Fallthrough */ - case GDK_KEY_j: /* Fallthrough */ - case GDK_KEY_Down: - setTermFont(terminal, currentFontSize - 1); - return TRUE; - case GDK_KEY_equal: - setTermFont(terminal, defaultFontSize); - return TRUE; - /* Open new tab */ - case GDK_KEY_Return: - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), getTerm(), NULL); - gtk_widget_show_all(window); - return TRUE; - /* Switch to the next tab */ - case GDK_KEY_L: /* Fallthrough */ - case GDK_KEY_l: /* Fallthrough */ - case GDK_KEY_KP_Page_Up: /* Fallthrough */ - case GDK_KEY_Right: - gtk_notebook_next_page(GTK_NOTEBOOK(notebook)); - return TRUE; - /* Switch to the previous tab */ - case GDK_KEY_H: /* Fallthrough */ - case GDK_KEY_h: /* Fallthrough */ - case GDK_KEY_KP_Page_Down: /* Fallthrough */ - case GDK_KEY_Left: - gtk_notebook_prev_page(GTK_NOTEBOOK(notebook)); - return TRUE; - /* Close the current tab */ - case GDK_KEY_W: /* Fallthrough */ - case GDK_KEY_w: /* Fallthrough */ - case GDK_KEY_BackSpace: /* Fallthrough */ - if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) == 1) - return TRUE; - closeTab = TRUE; - gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), - gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); - gtk_widget_queue_draw(GTK_WIDGET(notebook)); - return TRUE; - default: - for (int i = 0; i < keyCount; i++) { - if (!strcasecmp(gdk_keyval_name(event->keyval), keyBindings[i].key)) { + for (int i = 0; i < defaultKeyCount; i++) { + if (!strcasecmp(gdk_keyval_name(event->keyval), defaultKeyBindings[i].bind.key)) { + if (!defaultKeyBindings[i].invalid) { + if (defaultKeyBindings[i].bind.internal) { + termAction(terminal, defaultKeyBindings[i].bind.cmd); + } else { vte_terminal_feed_child(VTE_TERMINAL(terminal), - keyBindings[i].cmd, -1); - return TRUE; + defaultKeyBindings[i].bind.cmd, -1); } + return TRUE; + } + } + } + for (int i = 0; i < keyCount; i++) { + if (!strcasecmp(gdk_keyval_name(event->keyval), keyBindings[i].key)) { + if (keyBindings[i].internal) { + termAction(terminal, keyBindings[i].cmd); + } else { + vte_terminal_feed_child(VTE_TERMINAL(terminal), + keyBindings[i].cmd, -1); } + return TRUE; + } } } return FALSE; @@ -525,6 +538,20 @@ static GtkWidget *getTerm() { return terminal; } +/*! + * Invalidate the default binding with the corresponding action + * + * \param binding + */ +static void invalidateDefaultBinding(const Bindings *binding) { + for (int i = 0; i < defaultKeyCount; i++) { + if (strcmp(defaultKeyBindings[i].bind.cmd, binding->cmd) == 0 + && defaultKeyBindings[i].bind.internal == binding->internal) { + defaultKeyBindings[i].invalid = TRUE; + } + } +} + /*! * Initialize and start the terminal. * @@ -641,11 +668,19 @@ static void parseSettings() { g_strconcat(g_strdup(cmd + 1), "\r", NULL); else keyBindings[keyCount].cmd = g_strdup(cmd + 1); + /* Internal option is specified */ + if (!strcmp(option, "bindi")) + /* Set binding as internal*/ + keyBindings[keyCount].internal = TRUE; + else + keyBindings[keyCount].internal = FALSE; /* Add key binding to the keys */ keyBindings[keyCount].key = g_strdup(key); printLog("cmd %d = %s -> \"%s\"\n", keyCount + 1, keyBindings[keyCount].key, keyBindings[keyCount].cmd); + /* Invalidate associated default bindings */ + invalidateDefaultBinding(&keyBindings[keyCount]); /* Increment the keys count */ keyCount++; }