1 /* $Id: key-bindings.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <ctype.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "tmux.h" 26 27 SPLAY_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); 28 29 struct key_bindings key_bindings; 30 struct key_bindings dead_key_bindings; 31 32 int 33 key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) 34 { 35 int key1, key2; 36 37 key1 = bd1->key & ~KEYC_PREFIX; 38 key2 = bd2->key & ~KEYC_PREFIX; 39 if (key1 != key2) 40 return (key1 - key2); 41 42 if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) 43 return (-1); 44 if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) 45 return (1); 46 return (0); 47 } 48 49 struct key_binding * 50 key_bindings_lookup(int key) 51 { 52 struct key_binding bd; 53 54 bd.key = key; 55 return (SPLAY_FIND(key_bindings, &key_bindings, &bd)); 56 } 57 58 void 59 key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) 60 { 61 struct key_binding *bd; 62 63 key_bindings_remove(key); 64 65 bd = xmalloc(sizeof *bd); 66 bd->key = key; 67 SPLAY_INSERT(key_bindings, &key_bindings, bd); 68 69 bd->can_repeat = can_repeat; 70 bd->cmdlist = cmdlist; 71 } 72 73 void 74 key_bindings_remove(int key) 75 { 76 struct key_binding *bd; 77 78 if ((bd = key_bindings_lookup(key)) == NULL) 79 return; 80 SPLAY_REMOVE(key_bindings, &key_bindings, bd); 81 SPLAY_INSERT(key_bindings, &dead_key_bindings, bd); 82 } 83 84 void 85 key_bindings_clean(void) 86 { 87 struct key_binding *bd; 88 89 while (!SPLAY_EMPTY(&dead_key_bindings)) { 90 bd = SPLAY_ROOT(&dead_key_bindings); 91 SPLAY_REMOVE(key_bindings, &dead_key_bindings, bd); 92 cmd_list_free(bd->cmdlist); 93 xfree(bd); 94 } 95 } 96 97 void 98 key_bindings_init(void) 99 { 100 static const struct { 101 int key; 102 int can_repeat; 103 const struct cmd_entry *entry; 104 } table[] = { 105 { ' ', 0, &cmd_next_layout_entry }, 106 { '!', 0, &cmd_break_pane_entry }, 107 { '"', 0, &cmd_split_window_entry }, 108 { '#', 0, &cmd_list_buffers_entry }, 109 { '$', 0, &cmd_command_prompt_entry }, 110 { '%', 0, &cmd_split_window_entry }, 111 { '&', 0, &cmd_confirm_before_entry }, 112 { '(', 0, &cmd_switch_client_entry }, 113 { ')', 0, &cmd_switch_client_entry }, 114 { ',', 0, &cmd_command_prompt_entry }, 115 { '-', 0, &cmd_delete_buffer_entry }, 116 { '.', 0, &cmd_command_prompt_entry }, 117 { '0', 0, &cmd_select_window_entry }, 118 { '1', 0, &cmd_select_window_entry }, 119 { '2', 0, &cmd_select_window_entry }, 120 { '3', 0, &cmd_select_window_entry }, 121 { '4', 0, &cmd_select_window_entry }, 122 { '5', 0, &cmd_select_window_entry }, 123 { '6', 0, &cmd_select_window_entry }, 124 { '7', 0, &cmd_select_window_entry }, 125 { '8', 0, &cmd_select_window_entry }, 126 { '9', 0, &cmd_select_window_entry }, 127 { ':', 0, &cmd_command_prompt_entry }, 128 { ';', 0, &cmd_last_pane_entry }, 129 { '=', 0, &cmd_choose_buffer_entry }, 130 { '?', 0, &cmd_list_keys_entry }, 131 { 'D', 0, &cmd_choose_client_entry }, 132 { 'L', 0, &cmd_switch_client_entry }, 133 { '[', 0, &cmd_copy_mode_entry }, 134 { '\'', 0, &cmd_command_prompt_entry }, 135 { '\002', /* C-b */ 0, &cmd_send_prefix_entry }, 136 { '\017', /* C-o */ 0, &cmd_rotate_window_entry }, 137 { '\032', /* C-z */ 0, &cmd_suspend_client_entry }, 138 { ']', 0, &cmd_paste_buffer_entry }, 139 { 'c', 0, &cmd_new_window_entry }, 140 { 'd', 0, &cmd_detach_client_entry }, 141 { 'f', 0, &cmd_command_prompt_entry }, 142 { 'i', 0, &cmd_display_message_entry }, 143 { 'l', 0, &cmd_last_window_entry }, 144 { 'n', 0, &cmd_next_window_entry }, 145 { 'o', 0, &cmd_select_pane_entry }, 146 { 'p', 0, &cmd_previous_window_entry }, 147 { 'q', 0, &cmd_display_panes_entry }, 148 { 'r', 0, &cmd_refresh_client_entry }, 149 { 's', 0, &cmd_choose_session_entry }, 150 { 't', 0, &cmd_clock_mode_entry }, 151 { 'w', 0, &cmd_choose_window_entry }, 152 { 'x', 0, &cmd_confirm_before_entry }, 153 { '{', 0, &cmd_swap_pane_entry }, 154 { '}', 0, &cmd_swap_pane_entry }, 155 { '~', 0, &cmd_show_messages_entry }, 156 { '1' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, 157 { '2' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, 158 { '3' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, 159 { '4' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, 160 { '5' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, 161 { KEYC_PPAGE, 0, &cmd_copy_mode_entry }, 162 { 'n' | KEYC_ESCAPE, 0, &cmd_next_window_entry }, 163 { 'o' | KEYC_ESCAPE, 0, &cmd_rotate_window_entry }, 164 { 'p' | KEYC_ESCAPE, 0, &cmd_previous_window_entry }, 165 { KEYC_UP, 1, &cmd_select_pane_entry }, 166 { KEYC_DOWN, 1, &cmd_select_pane_entry }, 167 { KEYC_LEFT, 1, &cmd_select_pane_entry }, 168 { KEYC_RIGHT, 1, &cmd_select_pane_entry }, 169 { KEYC_UP | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, 170 { KEYC_DOWN | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, 171 { KEYC_LEFT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, 172 { KEYC_RIGHT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, 173 { KEYC_UP | KEYC_CTRL, 1, &cmd_resize_pane_entry }, 174 { KEYC_DOWN | KEYC_CTRL, 1, &cmd_resize_pane_entry }, 175 { KEYC_LEFT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, 176 { KEYC_RIGHT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, 177 }; 178 u_int i; 179 struct cmd *cmd; 180 struct cmd_list *cmdlist; 181 182 SPLAY_INIT(&key_bindings); 183 184 for (i = 0; i < nitems(table); i++) { 185 cmdlist = xmalloc(sizeof *cmdlist); 186 TAILQ_INIT(&cmdlist->list); 187 cmdlist->references = 1; 188 189 cmd = xmalloc(sizeof *cmd); 190 cmd->entry = table[i].entry; 191 if (cmd->entry->key_binding != NULL) 192 cmd->entry->key_binding(cmd, table[i].key); 193 else 194 cmd->args = args_create(0); 195 TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry); 196 197 key_bindings_add( 198 table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist); 199 } 200 } 201 202 void printflike2 203 key_bindings_error(struct cmd_ctx *ctx, const char *fmt, ...) 204 { 205 va_list ap; 206 char *msg; 207 208 va_start(ap, fmt); 209 xvasprintf(&msg, fmt, ap); 210 va_end(ap); 211 212 *msg = toupper((u_char) *msg); 213 status_message_set(ctx->curclient, "%s", msg); 214 xfree(msg); 215 } 216 217 void printflike2 218 key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...) 219 { 220 struct winlink *wl = ctx->curclient->session->curw; 221 va_list ap; 222 223 if (wl->window->active->mode != &window_copy_mode) { 224 window_pane_reset_mode(wl->window->active); 225 window_pane_set_mode(wl->window->active, &window_copy_mode); 226 window_copy_init_for_output(wl->window->active); 227 } 228 229 va_start(ap, fmt); 230 window_copy_vadd(wl->window->active, fmt, ap); 231 va_end(ap); 232 } 233 234 void printflike2 235 key_bindings_info(struct cmd_ctx *ctx, const char *fmt, ...) 236 { 237 va_list ap; 238 char *msg; 239 240 if (options_get_number(&global_options, "quiet")) 241 return; 242 243 va_start(ap, fmt); 244 xvasprintf(&msg, fmt, ap); 245 va_end(ap); 246 247 *msg = toupper((u_char) *msg); 248 status_message_set(ctx->curclient, "%s", msg); 249 xfree(msg); 250 } 251 252 void 253 key_bindings_dispatch(struct key_binding *bd, struct client *c) 254 { 255 struct cmd_ctx ctx; 256 struct cmd *cmd; 257 int readonly; 258 259 ctx.msgdata = NULL; 260 ctx.curclient = c; 261 262 ctx.error = key_bindings_error; 263 ctx.print = key_bindings_print; 264 ctx.info = key_bindings_info; 265 266 ctx.cmdclient = NULL; 267 268 readonly = 1; 269 TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { 270 if (!(cmd->entry->flags & CMD_READONLY)) 271 readonly = 0; 272 } 273 if (!readonly && c->flags & CLIENT_READONLY) { 274 key_bindings_info(&ctx, "Client is read-only"); 275 return; 276 } 277 278 cmd_list_exec(bd->cmdlist, &ctx); 279 } 280