/* * rofi * * MIT/X11 License * Copyright © 2013-2022 Qball Cow * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include "rofi.h" #include "xrmoptions.h" #include #include #include "nkutils-bindings.h" typedef struct { guint id; guint scope; char *name; char *binding; char *comment; } ActionBindingEntry; /** * Data structure holding all the action keybinding. */ ActionBindingEntry rofi_bindings[] = { {.id = PASTE_PRIMARY, .name = "kb-primary-paste", .binding = "Control+V,Shift+Insert", .comment = "Paste primary selection"}, {.id = PASTE_SECONDARY, .name = "kb-secondary-paste", .binding = "Control+v,Insert", .comment = "Paste clipboard"}, {.id = COPY_SECONDARY, .name = "kb-secondary-copy", .binding = "Control+c", .comment = "Copy to clipboard"}, {.id = CLEAR_LINE, .name = "kb-clear-line", .binding = "Control+w", .comment = "Clear input line"}, {.id = MOVE_FRONT, .name = "kb-move-front", .binding = "Control+a", .comment = "Beginning of line"}, {.id = MOVE_END, .name = "kb-move-end", .binding = "Control+e", .comment = "End of line"}, {.id = MOVE_WORD_BACK, .name = "kb-move-word-back", .binding = "Alt+b,Control+Left", .comment = "Move back one word"}, {.id = MOVE_WORD_FORWARD, .name = "kb-move-word-forward", .binding = "Alt+f,Control+Right", .comment = "Move forward one word"}, {.id = MOVE_CHAR_BACK, .name = "kb-move-char-back", .binding = "Left,Control+b", .comment = "Move back one char"}, {.id = MOVE_CHAR_FORWARD, .name = "kb-move-char-forward", .binding = "Right,Control+f", .comment = "Move forward one char"}, {.id = REMOVE_WORD_BACK, .name = "kb-remove-word-back", .binding = "Control+Alt+h,Control+BackSpace", .comment = "Delete previous word"}, {.id = REMOVE_WORD_FORWARD, .name = "kb-remove-word-forward", .binding = "Control+Alt+d", .comment = "Delete next word"}, {.id = REMOVE_CHAR_FORWARD, .name = "kb-remove-char-forward", .binding = "Delete,Control+d", .comment = "Delete next char"}, {.id = REMOVE_CHAR_BACK, .name = "kb-remove-char-back", .binding = "BackSpace,Shift+BackSpace,Control+h", .comment = "Delete previous char"}, {.id = REMOVE_TO_EOL, .name = "kb-remove-to-eol", .binding = "Control+k", .comment = "Delete till the end of line"}, {.id = REMOVE_TO_SOL, .name = "kb-remove-to-sol", .binding = "Control+u", .comment = "Delete till the start of line"}, {.id = ACCEPT_ENTRY, .name = "kb-accept-entry", .binding = "Control+j,Control+m,Return,KP_Enter", .comment = "Accept entry"}, {.id = ACCEPT_CUSTOM, .name = "kb-accept-custom", .binding = "Control+Return", .comment = "Use entered text as command (in ssh/run modes)"}, {.id = ACCEPT_CUSTOM_ALT, .name = "kb-accept-custom-alt", .binding = "Control+Shift+Return", .comment = "Use entered text as command (in ssh/run modes)"}, {.id = ACCEPT_ALT, .name = "kb-accept-alt", .binding = "Shift+Return", .comment = "Use alternate accept command."}, {.id = DELETE_ENTRY, .name = "kb-delete-entry", .binding = "Shift+Delete", .comment = "Delete entry from history"}, {.id = MODE_NEXT, .name = "kb-mode-next", .binding = "Shift+Right,Control+Tab", .comment = "Switch to the next mode."}, {.id = MODE_PREVIOUS, .name = "kb-mode-previous", .binding = "Shift+Left,Control+ISO_Left_Tab", .comment = "Switch to the previous mode."}, {.id = MODE_COMPLETE, .name = "kb-mode-complete", .binding = "Control+l", .comment = "Start completion for mode."}, {.id = ROW_LEFT, .name = "kb-row-left", .binding = "Control+Page_Up", .comment = "Go to the previous column"}, {.id = ROW_RIGHT, .name = "kb-row-right", .binding = "Control+Page_Down", .comment = "Go to the next column"}, {.id = ROW_UP, .name = "kb-row-up", .binding = "Up,Control+p", .comment = "Select previous entry"}, {.id = ROW_DOWN, .name = "kb-row-down", .binding = "Down,Control+n", .comment = "Select next entry"}, {.id = ROW_TAB, .name = "kb-row-tab", .binding = "", .comment = "Go to next row, if one left, accept it, if no left next mode."}, {.id = ELEMENT_NEXT, .name = "kb-element-next", .binding = "Tab", .comment = "Go to next element (in logical order)."}, {.id = ELEMENT_PREV, .name = "kb-element-prev", .binding = "ISO_Left_Tab", .comment = "Go to next previous element (in logical order)."}, {.id = PAGE_PREV, .name = "kb-page-prev", .binding = "Page_Up", .comment = "Go to the previous page"}, {.id = PAGE_NEXT, .name = "kb-page-next", .binding = "Page_Down", .comment = "Go to the next page"}, {.id = ROW_FIRST, .name = "kb-row-first", .binding = "Home,KP_Home", .comment = "Go to the first entry"}, {.id = ROW_LAST, .name = "kb-row-last", .binding = "End,KP_End", .comment = "Go to the last entry"}, {.id = ROW_SELECT, .name = "kb-row-select", .binding = "Control+space", .comment = "Set selected item as input text"}, {.id = SCREENSHOT, .name = "kb-screenshot", .binding = "Alt+S", .comment = "Take a screenshot of the rofi window"}, {.id = CHANGE_ELLIPSIZE, .name = "kb-ellipsize", .binding = "Alt+period", .comment = "Toggle between ellipsize modes for displayed data"}, {.id = TOGGLE_CASE_SENSITIVITY, .name = "kb-toggle-case-sensitivity", .binding = "grave,dead_grave", .comment = "Toggle case sensitivity"}, {.id = TOGGLE_SORT, .name = "kb-toggle-sort", .binding = "Alt+grave", .comment = "Toggle sort"}, {.id = CANCEL, .name = "kb-cancel", .binding = "Escape,Control+g,Control+bracketleft", .comment = "Quit rofi"}, {.id = CUSTOM_1, .name = "kb-custom-1", .binding = "Alt+1", .comment = "Custom keybinding 1"}, {.id = CUSTOM_2, .name = "kb-custom-2", .binding = "Alt+2", .comment = "Custom keybinding 2"}, {.id = CUSTOM_3, .name = "kb-custom-3", .binding = "Alt+3", .comment = "Custom keybinding 3"}, {.id = CUSTOM_4, .name = "kb-custom-4", .binding = "Alt+4", .comment = "Custom keybinding 4"}, {.id = CUSTOM_5, .name = "kb-custom-5", .binding = "Alt+5", .comment = "Custom Keybinding 5"}, {.id = CUSTOM_6, .name = "kb-custom-6", .binding = "Alt+6", .comment = "Custom keybinding 6"}, {.id = CUSTOM_7, .name = "kb-custom-7", .binding = "Alt+7", .comment = "Custom Keybinding 7"}, {.id = CUSTOM_8, .name = "kb-custom-8", .binding = "Alt+8", .comment = "Custom keybinding 8"}, {.id = CUSTOM_9, .name = "kb-custom-9", .binding = "Alt+9", .comment = "Custom keybinding 9"}, {.id = CUSTOM_10, .name = "kb-custom-10", .binding = "Alt+0", .comment = "Custom keybinding 10"}, {.id = CUSTOM_11, .name = "kb-custom-11", .binding = "Alt+exclam", .comment = "Custom keybinding 11"}, {.id = CUSTOM_12, .name = "kb-custom-12", .binding = "Alt+at", .comment = "Custom keybinding 12"}, {.id = CUSTOM_13, .name = "kb-custom-13", .binding = "Alt+numbersign", .comment = "Custom keybinding 13"}, {.id = CUSTOM_14, .name = "kb-custom-14", .binding = "Alt+dollar", .comment = "Custom keybinding 14"}, {.id = CUSTOM_15, .name = "kb-custom-15", .binding = "Alt+percent", .comment = "Custom keybinding 15"}, {.id = CUSTOM_16, .name = "kb-custom-16", .binding = "Alt+dead_circumflex", .comment = "Custom keybinding 16"}, {.id = CUSTOM_17, .name = "kb-custom-17", .binding = "Alt+ampersand", .comment = "Custom keybinding 17"}, {.id = CUSTOM_18, .name = "kb-custom-18", .binding = "Alt+asterisk", .comment = "Custom keybinding 18"}, {.id = CUSTOM_19, .name = "kb-custom-19", .binding = "Alt+parenleft", .comment = "Custom Keybinding 19"}, {.id = SELECT_ELEMENT_1, .name = "kb-select-1", .binding = "Super+1", .comment = "Select row 1"}, {.id = SELECT_ELEMENT_2, .name = "kb-select-2", .binding = "Super+2", .comment = "Select row 2"}, {.id = SELECT_ELEMENT_3, .name = "kb-select-3", .binding = "Super+3", .comment = "Select row 3"}, {.id = SELECT_ELEMENT_4, .name = "kb-select-4", .binding = "Super+4", .comment = "Select row 4"}, {.id = SELECT_ELEMENT_5, .name = "kb-select-5", .binding = "Super+5", .comment = "Select row 5"}, {.id = SELECT_ELEMENT_6, .name = "kb-select-6", .binding = "Super+6", .comment = "Select row 6"}, {.id = SELECT_ELEMENT_7, .name = "kb-select-7", .binding = "Super+7", .comment = "Select row 7"}, {.id = SELECT_ELEMENT_8, .name = "kb-select-8", .binding = "Super+8", .comment = "Select row 8"}, {.id = SELECT_ELEMENT_9, .name = "kb-select-9", .binding = "Super+9", .comment = "Select row 9"}, {.id = SELECT_ELEMENT_10, .name = "kb-select-10", .binding = "Super+0", .comment = "Select row 10"}, /* Mouse-aware bindings */ {.id = SCROLL_LEFT, .scope = SCOPE_MOUSE_LISTVIEW, .name = "ml-row-left", .binding = "ScrollLeft", .comment = "Go to the previous column"}, {.id = SCROLL_RIGHT, .scope = SCOPE_MOUSE_LISTVIEW, .name = "ml-row-right", .binding = "ScrollRight", .comment = "Go to the next column"}, {.id = SCROLL_UP, .scope = SCOPE_MOUSE_LISTVIEW, .name = "ml-row-up", .binding = "ScrollUp", .comment = "Select previous entry"}, {.id = SCROLL_DOWN, .scope = SCOPE_MOUSE_LISTVIEW, .name = "ml-row-down", .binding = "ScrollDown", .comment = "Select next entry"}, {.id = SELECT_HOVERED_ENTRY, .scope = SCOPE_MOUSE_LISTVIEW_ELEMENT, .name = "me-select-entry", .binding = "MousePrimary", .comment = "Select hovered row"}, {.id = ACCEPT_HOVERED_ENTRY, .scope = SCOPE_MOUSE_LISTVIEW_ELEMENT, .name = "me-accept-entry", .binding = "MouseDPrimary", .comment = "Accept hovered row"}, {.id = ACCEPT_HOVERED_CUSTOM, .scope = SCOPE_MOUSE_LISTVIEW_ELEMENT, .name = "me-accept-custom", .binding = "Control+MouseDPrimary", .comment = "Accept hovered row with custom action"}, }; /** Default binding of mouse button to action. */ static const gchar *mouse_default_bindings[] = { [MOUSE_CLICK_DOWN] = "MousePrimary", [MOUSE_CLICK_UP] = "!MousePrimary", [MOUSE_DCLICK_DOWN] = "MouseDPrimary", [MOUSE_DCLICK_UP] = "!MouseDPrimary", }; void abe_list_all_bindings(gboolean is_term ) { int length = 0; for (gsize i = 0; i < G_N_ELEMENTS(rofi_bindings); ++i) { ActionBindingEntry *b = &rofi_bindings[i]; int sl = strlen(b->name); length = MAX(length, sl); } for (gsize i = 0; i < G_N_ELEMENTS(rofi_bindings); ++i) { ActionBindingEntry *b = &rofi_bindings[i]; if (is_term) { printf("%s%*s%s - %s\n", color_bold,length, b->name, color_reset,b->binding); } else { printf("%*s - %s\n", length, b->name, b->binding); } } } void setup_abe(void) { for (gsize i = 0; i < G_N_ELEMENTS(rofi_bindings); ++i) { ActionBindingEntry *b = &rofi_bindings[i]; b->binding = g_strdup(b->binding); config_parser_add_option(xrm_String, b->name, (void **)&(b->binding), b->comment); } } static gboolean binding_check_action(guint64 scope, G_GNUC_UNUSED gpointer target, gpointer user_data) { return rofi_view_check_action(rofi_view_get_active(), scope, GPOINTER_TO_UINT(user_data)) ? NK_BINDINGS_BINDING_TRIGGERED : NK_BINDINGS_BINDING_NOT_TRIGGERED; } static void binding_trigger_action(guint64 scope, G_GNUC_UNUSED gpointer target, gpointer user_data) { rofi_view_trigger_action(rofi_view_get_active(), scope, GPOINTER_TO_UINT(user_data)); } guint key_binding_get_action_from_name(const char *name) { for (gsize i = 0; i < G_N_ELEMENTS(rofi_bindings); ++i) { ActionBindingEntry *b = &rofi_bindings[i]; if (g_strcmp0(b->name, name) == 0) { return b->id; } } return UINT32_MAX; } gboolean parse_keys_abe(NkBindings *bindings) { GError *error = NULL; GString *error_msg = g_string_new(""); for (gsize i = 0; i < G_N_ELEMENTS(rofi_bindings); ++i) { ActionBindingEntry *b = &rofi_bindings[i]; char *keystr = g_strdup(b->binding); char *sp = NULL; // Iter over bindings. const char *const sep = ","; for (char *entry = strtok_r(keystr, sep, &sp); entry != NULL; entry = strtok_r(NULL, sep, &sp)) { if (!nk_bindings_add_binding(bindings, b->scope, entry, binding_check_action, binding_trigger_action, GUINT_TO_POINTER(b->id), NULL, &error)) { if ( error->code == NK_BINDINGS_ERROR_ALREADY_REGISTERED && error->domain == NK_BINDINGS_ERROR){ char *str = g_markup_printf_escaped( "Failed to set binding %s for: %s (%s):\n\tBinding `%s` is already bound.\n" "\tExecute rofi -list-keybindings to get the current list of configured bindings.\n", b->binding, b->comment, b->name, entry); g_string_append(error_msg, str); g_free(str); } else { char *str = g_markup_printf_escaped( "Failed to set binding %s for: %s (%s):\n\t%s\n", b->binding, b->comment, b->name, error->message); g_string_append(error_msg, str); g_free(str); } g_clear_error(&error); } } g_free(keystr); } if (error_msg->len > 0) { // rofi_view_error_dialog ( error_msg->str, TRUE ); rofi_add_error_message(error_msg); // g_string_free ( error_msg, TRUE ); return FALSE; } for (gsize i = SCOPE_MIN_FIXED; i <= SCOPE_MAX_FIXED; ++i) { for (gsize j = 1; j < G_N_ELEMENTS(mouse_default_bindings); ++j) { nk_bindings_add_binding(bindings, i, mouse_default_bindings[j], binding_check_action, binding_trigger_action, GSIZE_TO_POINTER(j), NULL, NULL); } } g_string_free(error_msg, TRUE); return TRUE; }