Annotation of src/usr.bin/tmux/mode-key.c, Revision 1.12
1.12 ! nicm 1: /* $OpenBSD: mode-key.c,v 1.11 2009/07/28 17:05:10 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2008 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:
1.11 nicm 21: #include <string.h>
22:
1.1 nicm 23: #include "tmux.h"
24:
1.10 nicm 25: /*
26: * Mode keys. These are the key bindings used when editing (status prompt), and
27: * in the modes. They are split into two sets of three tables, one set of three
28: * for vi and the other for emacs key bindings. The three tables are for
29: * editing, for menu-like modes (choice, more), and for copy modes (copy,
30: * scroll).
31: *
32: * The fixed tables of struct mode_key_entry below are the defaults: they are
33: * built into a tree of struct mode_key_binding by mode_key_init_trees, which
34: * can then be modified.
35: *
36: * vi command mode is handled by having a mode flag in the struct which allows
37: * two sets of bindings to be swapped between. A couple of editing commands
38: * (MODEKEYEDIT_SWITCHMODE and MODEKEYEDIT_SWITCHMODEAPPEND) are special-cased
39: * to do this.
40: */
41:
42: /* Edit keys command strings. */
43: struct mode_key_cmdstr mode_key_cmdstr_edit[] = {
44: { MODEKEYEDIT_BACKSPACE, "backspace" },
45: { MODEKEYEDIT_CANCEL, "cancel" },
46: { MODEKEYEDIT_COMPLETE, "complete" },
47: { MODEKEYEDIT_CURSORLEFT, "cursor-left" },
48: { MODEKEYEDIT_CURSORRIGHT, "cursor-right" },
49: { MODEKEYEDIT_DELETE, "delete" },
50: { MODEKEYEDIT_DELETETOENDOFLINE, "delete-end-of-line" },
51: { MODEKEYEDIT_ENDOFLINE, "end-of-line" },
52: { MODEKEYEDIT_ENTER, "enter" },
53: { MODEKEYEDIT_HISTORYDOWN, "history-down" },
54: { MODEKEYEDIT_HISTORYUP, "history-up" },
55: { MODEKEYEDIT_PASTE, "paste" },
56: { MODEKEYEDIT_STARTOFLINE, "start-of-line" },
57: { MODEKEYEDIT_SWITCHMODE, "switch-mode" },
58: { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" },
1.11 nicm 59:
60: { 0, NULL }
1.10 nicm 61: };
62:
63: /* Choice keys command strings. */
64: struct mode_key_cmdstr mode_key_cmdstr_choice[] = {
65: { MODEKEYCHOICE_CANCEL, "cancel" },
66: { MODEKEYCHOICE_CHOOSE, "choose" },
67: { MODEKEYCHOICE_DOWN, "down" },
68: { MODEKEYCHOICE_PAGEDOWN, "page-down" },
69: { MODEKEYCHOICE_PAGEUP, "page-up" },
70: { MODEKEYCHOICE_UP, "up" },
1.11 nicm 71:
72: { 0, NULL }
1.10 nicm 73: };
74:
75: /* Copy keys command strings. */
76: struct mode_key_cmdstr mode_key_cmdstr_copy[] = {
77: { MODEKEYCOPY_CANCEL, "cancel" },
78: { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" },
79: { MODEKEYCOPY_CLEARSELECTION, "clear-selection" },
80: { MODEKEYCOPY_COPYSELECTION, "copy-selection" },
81: { MODEKEYCOPY_DOWN, "cursor-down" },
82: { MODEKEYCOPY_ENDOFLINE, "end-of-line" },
83: { MODEKEYCOPY_LEFT, "cursor-left" },
84: { MODEKEYCOPY_NEXTPAGE, "page-down" },
85: { MODEKEYCOPY_NEXTWORD, "next-word" },
86: { MODEKEYCOPY_PREVIOUSPAGE, "page-up" },
87: { MODEKEYCOPY_PREVIOUSWORD, "previous-word" },
88: { MODEKEYCOPY_RIGHT, "cursor-right" },
89: { MODEKEYCOPY_STARTOFLINE, "start-of-line" },
90: { MODEKEYCOPY_STARTSELECTION, "begin-selection" },
91: { MODEKEYCOPY_UP, "cursor-up" },
1.11 nicm 92:
93: { 0, NULL }
1.10 nicm 94: };
95:
1.8 nicm 96: /* vi editing keys. */
97: const struct mode_key_entry mode_key_vi_edit[] = {
98: { '\003' /* C-c */, 0, MODEKEYEDIT_CANCEL },
99: { '\010' /* C-h */, 0, MODEKEYEDIT_BACKSPACE },
100: { '\011' /* Tab */, 0, MODEKEYEDIT_COMPLETE },
101: { '\033' /* Escape */, 0, MODEKEYEDIT_SWITCHMODE },
102: { '\r', 0, MODEKEYEDIT_ENTER },
103: { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE },
104: { KEYC_DC, 0, MODEKEYEDIT_DELETE },
105:
106: { '$', 1, MODEKEYEDIT_ENDOFLINE },
107: { '0', 1, MODEKEYEDIT_STARTOFLINE },
108: { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE },
109: { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL },
110: { '\010' /* C-h */, 1, MODEKEYEDIT_BACKSPACE },
111: { '\r', 1, MODEKEYEDIT_ENTER },
112: { '^', 1, MODEKEYEDIT_STARTOFLINE },
113: { 'a', 1, MODEKEYEDIT_SWITCHMODEAPPEND },
114: { 'h', 1, MODEKEYEDIT_CURSORLEFT },
115: { 'i', 1, MODEKEYEDIT_SWITCHMODE },
116: { 'j', 1, MODEKEYEDIT_HISTORYDOWN },
117: { 'k', 1, MODEKEYEDIT_HISTORYUP },
118: { 'l', 1, MODEKEYEDIT_CURSORRIGHT },
119: { 'p', 1, MODEKEYEDIT_PASTE },
120: { KEYC_BSPACE, 1, MODEKEYEDIT_BACKSPACE },
121: { KEYC_DC, 1, MODEKEYEDIT_DELETE },
122: { KEYC_DOWN, 1, MODEKEYEDIT_HISTORYDOWN },
123: { KEYC_LEFT, 1, MODEKEYEDIT_CURSORLEFT },
124: { KEYC_RIGHT, 1, MODEKEYEDIT_CURSORRIGHT },
125: { KEYC_UP, 1, MODEKEYEDIT_HISTORYUP },
126:
127: { 0, -1, 0 }
128: };
1.10 nicm 129: struct mode_key_tree mode_key_tree_vi_edit;
1.8 nicm 130:
131: /* vi choice selection keys. */
132: const struct mode_key_entry mode_key_vi_choice[] = {
133: { '\003' /* C-c */, 0, MODEKEYCHOICE_CANCEL },
134: { '\r', 0, MODEKEYCHOICE_CHOOSE },
135: { 'j', 0, MODEKEYCHOICE_DOWN },
136: { 'k', 0, MODEKEYCHOICE_UP },
137: { 'q', 0, MODEKEYCHOICE_CANCEL },
138: { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN },
139: { KEYC_NPAGE, 0, MODEKEYCHOICE_PAGEDOWN },
140: { KEYC_PPAGE, 0, MODEKEYCHOICE_PAGEUP },
141: { KEYC_UP, 0, MODEKEYCHOICE_UP },
142:
143: { 0, -1, 0 }
144: };
1.10 nicm 145: struct mode_key_tree mode_key_tree_vi_choice;
1.8 nicm 146:
147: /* vi copy mode keys. */
148: const struct mode_key_entry mode_key_vi_copy[] = {
149: { ' ', 0, MODEKEYCOPY_STARTSELECTION },
150: { '$', 0, MODEKEYCOPY_ENDOFLINE },
151: { '0', 0, MODEKEYCOPY_STARTOFLINE },
1.9 nicm 152: { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL },
1.8 nicm 153: { '\006' /* C-f */, 0, MODEKEYCOPY_NEXTPAGE },
154: { '\010' /* C-h */, 0, MODEKEYCOPY_LEFT },
155: { '\025' /* C-u */, 0, MODEKEYCOPY_PREVIOUSPAGE },
156: { '\033' /* Escape */, 0, MODEKEYCOPY_CLEARSELECTION },
157: { '\r', 0, MODEKEYCOPY_COPYSELECTION },
158: { '^', 0, MODEKEYCOPY_BACKTOINDENTATION },
159: { 'b', 0, MODEKEYCOPY_PREVIOUSWORD },
160: { 'h', 0, MODEKEYCOPY_LEFT },
161: { 'j', 0, MODEKEYCOPY_DOWN },
162: { 'k', 0, MODEKEYCOPY_UP },
163: { 'l', 0, MODEKEYCOPY_RIGHT },
1.9 nicm 164: { 'q', 0, MODEKEYCOPY_CANCEL },
1.8 nicm 165: { 'w', 0, MODEKEYCOPY_NEXTWORD },
166: { KEYC_BSPACE, 0, MODEKEYCOPY_LEFT },
167: { KEYC_DOWN, 0, MODEKEYCOPY_DOWN },
168: { KEYC_LEFT, 0, MODEKEYCOPY_LEFT },
169: { KEYC_NPAGE, 0, MODEKEYCOPY_NEXTPAGE },
170: { KEYC_PPAGE, 0, MODEKEYCOPY_PREVIOUSPAGE },
171: { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT },
172: { KEYC_UP, 0, MODEKEYCOPY_UP },
173:
174: { 0, -1, 0 }
175: };
1.10 nicm 176: struct mode_key_tree mode_key_tree_vi_copy;
1.8 nicm 177:
178: /* emacs editing keys. */
179: const struct mode_key_entry mode_key_emacs_edit[] = {
180: { '\001' /* C-a */, 0, MODEKEYEDIT_STARTOFLINE },
181: { '\002' /* C-p */, 0, MODEKEYEDIT_CURSORLEFT },
182: { '\004' /* C-d */, 0, MODEKEYEDIT_DELETE },
183: { '\005' /* C-e */, 0, MODEKEYEDIT_ENDOFLINE },
184: { '\006' /* C-f */, 0, MODEKEYEDIT_CURSORRIGHT },
185: { '\010' /* C-H */, 0, MODEKEYEDIT_BACKSPACE },
186: { '\011' /* Tab */, 0, MODEKEYEDIT_COMPLETE },
187: { '\013' /* C-k */, 0, MODEKEYEDIT_DELETETOENDOFLINE },
188: { '\016' /* C-n */, 0, MODEKEYEDIT_HISTORYDOWN },
189: { '\020' /* C-p */, 0, MODEKEYEDIT_HISTORYUP },
190: { '\031' /* C-y */, 0, MODEKEYEDIT_PASTE },
191: { '\r', 0, MODEKEYEDIT_ENTER },
192: { 'm' | KEYC_ESCAPE, 0, MODEKEYEDIT_STARTOFLINE },
193: { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE },
194: { KEYC_DC, 0, MODEKEYEDIT_DELETE },
195: { KEYC_DOWN, 0, MODEKEYEDIT_HISTORYDOWN },
196: { KEYC_LEFT, 0, MODEKEYEDIT_CURSORLEFT },
197: { KEYC_RIGHT, 0, MODEKEYEDIT_CURSORRIGHT },
198: { KEYC_UP, 0, MODEKEYEDIT_HISTORYUP },
199:
200: { 0, -1, 0 }
201: };
1.10 nicm 202: struct mode_key_tree mode_key_tree_emacs_edit;
1.8 nicm 203:
204: /* emacs choice selection keys. */
205: const struct mode_key_entry mode_key_emacs_choice[] = {
206: { '\003' /* C-c */, 0, MODEKEYCHOICE_CANCEL },
207: { '\033' /* Escape */, 0, MODEKEYCHOICE_CANCEL },
208: { '\r', 0, MODEKEYCHOICE_CHOOSE },
209: { 'q', 0, MODEKEYCHOICE_CANCEL },
210: { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN },
211: { KEYC_NPAGE, 0, MODEKEYCHOICE_PAGEDOWN },
212: { KEYC_PPAGE, 0, MODEKEYCHOICE_PAGEUP },
213: { KEYC_UP, 0, MODEKEYCHOICE_UP },
214:
215: { 0, -1, 0 }
216: };
1.10 nicm 217: struct mode_key_tree mode_key_tree_emacs_choice;
1.8 nicm 218:
219: /* emacs copy mode keys. */
220: const struct mode_key_entry mode_key_emacs_copy[] = {
221: { ' ', 0, MODEKEYCOPY_NEXTPAGE },
222: { '\000' /* C-Space */, 0, MODEKEYCOPY_STARTSELECTION },
223: { '\001' /* C-a */, 0, MODEKEYCOPY_STARTOFLINE },
224: { '\002' /* C-b */, 0, MODEKEYCOPY_LEFT },
1.9 nicm 225: { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL },
1.8 nicm 226: { '\005' /* C-e */, 0, MODEKEYCOPY_ENDOFLINE },
227: { '\006' /* C-f */, 0, MODEKEYCOPY_RIGHT },
228: { '\007' /* C-g */, 0, MODEKEYCOPY_CLEARSELECTION },
229: { '\016' /* C-n */, 0, MODEKEYCOPY_DOWN },
230: { '\020' /* C-p */, 0, MODEKEYCOPY_UP },
231: { '\026' /* C-v */, 0, MODEKEYCOPY_NEXTPAGE },
232: { '\027' /* C-w */, 0, MODEKEYCOPY_COPYSELECTION },
1.9 nicm 233: { '\033' /* Escape */, 0, MODEKEYCOPY_CANCEL },
1.8 nicm 234: { 'b' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSWORD },
235: { 'f' | KEYC_ESCAPE, 0, MODEKEYCOPY_NEXTWORD },
236: { 'm' | KEYC_ESCAPE, 0, MODEKEYCOPY_BACKTOINDENTATION },
1.9 nicm 237: { 'q', 0, MODEKEYCOPY_CANCEL },
1.8 nicm 238: { 'v' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSPAGE },
239: { 'w' | KEYC_ESCAPE, 0, MODEKEYCOPY_COPYSELECTION },
240: { KEYC_DOWN, 0, MODEKEYCOPY_DOWN },
241: { KEYC_LEFT, 0, MODEKEYCOPY_LEFT },
242: { KEYC_NPAGE, 0, MODEKEYCOPY_NEXTPAGE },
243: { KEYC_PPAGE, 0, MODEKEYCOPY_PREVIOUSPAGE },
244: { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT },
245: { KEYC_UP, 0, MODEKEYCOPY_UP },
246:
247: { 0, -1, 0 }
248: };
1.10 nicm 249: struct mode_key_tree mode_key_tree_emacs_copy;
250:
251: /* Table mapping key table names to default settings and trees. */
252: const struct mode_key_table mode_key_tables[] = {
253: { "vi-edit", mode_key_cmdstr_edit,
254: &mode_key_tree_vi_edit, mode_key_vi_edit },
255: { "vi-choice", mode_key_cmdstr_choice,
256: &mode_key_tree_vi_choice, mode_key_vi_choice },
257: { "vi-copy", mode_key_cmdstr_copy,
258: &mode_key_tree_vi_copy, mode_key_vi_copy },
259: { "emacs-edit", mode_key_cmdstr_edit,
260: &mode_key_tree_emacs_edit, mode_key_emacs_edit },
261: { "emacs-choice", mode_key_cmdstr_choice,
262: &mode_key_tree_emacs_choice, mode_key_emacs_choice },
263: { "emacs-copy", mode_key_cmdstr_copy,
264: &mode_key_tree_emacs_copy, mode_key_emacs_copy },
265:
266: { NULL, NULL, NULL, NULL }
267: };
268:
269: SPLAY_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp);
270:
271: int
272: mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2)
273: {
274: if (mbind1->mode != mbind2->mode)
275: return (mbind1->mode - mbind2->mode);
276: return (mbind1->key - mbind2->key);
277: }
278:
279: const char *
280: mode_key_tostring(struct mode_key_cmdstr *cmdstr, enum mode_key_cmd cmd)
281: {
282: for (; cmdstr->name != NULL; cmdstr++) {
283: if (cmdstr->cmd == cmd)
284: return (cmdstr->name);
1.11 nicm 285: }
286: return (NULL);
287: }
288:
289: enum mode_key_cmd
290: mode_key_fromstring(struct mode_key_cmdstr *cmdstr, const char *name)
291: {
292: for (; cmdstr->name != NULL; cmdstr++) {
293: if (strcasecmp(cmdstr->name, name) == 0)
294: return (cmdstr->cmd);
295: }
296: return (MODEKEY_NONE);
297: }
298:
299: const struct mode_key_table *
300: mode_key_findtable(const char *name)
301: {
302: const struct mode_key_table *mtab;
303:
304: for (mtab = mode_key_tables; mtab->name != NULL; mtab++) {
305: if (strcasecmp(name, mtab->name) == 0)
306: return (mtab);
1.10 nicm 307: }
308: return (NULL);
309: }
1.1 nicm 310:
311: void
1.10 nicm 312: mode_key_init_trees(void)
1.1 nicm 313: {
1.10 nicm 314: const struct mode_key_table *mtab;
315: const struct mode_key_entry *ment;
316: struct mode_key_binding *mbind;
317:
318: for (mtab = mode_key_tables; mtab->name != NULL; mtab++) {
319: SPLAY_INIT(mtab->tree);
320: for (ment = mtab->table; ment->mode != -1; ment++) {
321: mbind = xmalloc(sizeof *mbind);
322: mbind->key = ment->key;
323: mbind->mode = ment->mode;
324: mbind->cmd = ment->cmd;
325: SPLAY_INSERT(mode_key_tree, mtab->tree, mbind);
326: }
327: }
328: }
329:
330: void
331: mode_key_free_trees(void)
332: {
333: const struct mode_key_table *mtab;
334: struct mode_key_binding *mbind;
335:
336: for (mtab = mode_key_tables; mtab->name != NULL; mtab++) {
337: while (!SPLAY_EMPTY(mtab->tree)) {
338: mbind = SPLAY_ROOT(mtab->tree);
339: SPLAY_REMOVE(mode_key_tree, mtab->tree, mbind);
1.12 ! nicm 340: xfree(mbind);
1.10 nicm 341: }
342: }
343: }
344:
345: void
346: mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree)
347: {
348: mdata->tree = mtree;
1.8 nicm 349: mdata->mode = 0;
1.1 nicm 350: }
351:
352: enum mode_key_cmd
353: mode_key_lookup(struct mode_key_data *mdata, int key)
354: {
1.10 nicm 355: struct mode_key_binding *mbind, mtmp;
356:
357: mtmp.key = key;
358: mtmp.mode = mdata->mode;
359: if ((mbind = SPLAY_FIND(mode_key_tree, mdata->tree, &mtmp)) == NULL) {
360: if (mdata->mode != 0)
361: return (MODEKEY_NONE);
362: return (MODEKEY_OTHER);
363: }
1.1 nicm 364:
1.10 nicm 365: switch (mbind->cmd) {
366: case MODEKEYEDIT_SWITCHMODE:
367: case MODEKEYEDIT_SWITCHMODEAPPEND:
368: mdata->mode = 1 - mdata->mode;
369: /* FALLTHROUGH */
370: default:
371: return (mbind->cmd);
1.1 nicm 372: }
373: }