From 8fac0ab42e371ec8302e11f2ea1e8034aac3169d Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Mon, 9 May 2005 02:17:47 +0000 Subject: [PATCH] reordered KEY_xxx definitions added KEY_DEL=127 (formerly KEY_BACKSPACE) added KEY_BS=8 C-H to handle control-h as backspace on some ttys fixed KEY_F6 thru KEY_F12 (were off by one) added strtokeys() to convert a string with key names to a key buffer added define-kbd-macro() from key string added execute-macro-keys() to execute commands from a key string improved qe_key_process(). changed method to update shell cursor : tty_update_cursor disabled shell_display_hook() used instead greatly improved shell buffer behaviour, charset handling. much improved tty input handling : support more key combinations and modes. added missing charset declarations in qe.h added borders_invalid and complete_refresh window flags made C-L completely refresh display moved window border display to separate function changed edit_display() to allow complete redisplay even with popups fixed window delete crash bugs by improving consistency with edit_attach and edit_detach. --- display.h | 1 + qe.c | 235 +++++++++++++++++++++++++++++++++++++++-------------- qe.h | 133 ++++++++++++++++++++---------- qeconfig.h | 12 +-- shell.c | 66 +++++++++++---- tty.c | 156 ++++++++++++++++++++++++++++------- util.c | 183 +++++++++++++++++++++++++---------------- win32.c | 2 +- x11.c | 5 +- 9 files changed, 567 insertions(+), 226 deletions(-) diff --git a/display.h b/display.h index d317a06..5ca4ef7 100644 --- a/display.h +++ b/display.h @@ -102,6 +102,7 @@ typedef struct QEDisplay { int x, int y, int w, int h); void (*dpy_selection_activate)(QEditScreen *s); void (*dpy_selection_request)(QEditScreen *s); + void (*dpy_invalidate)(void); /* bitmap support */ int (*dpy_bmp_alloc)(QEditScreen *s, QEBitmap *b); void (*dpy_bmp_free)(QEditScreen *s, QEBitmap *b); diff --git a/qe.c b/qe.c index 92ba024..8fe7b2c 100644 --- a/qe.c +++ b/qe.c @@ -329,22 +329,14 @@ void command_completion(StringArray *cs, const char *input) void do_global_set_key(EditState *s, const char *keystr, const char *cmd_name) { - int key, nb_keys; + int nb_keys; unsigned int keys[MAX_KEYS]; - const char *p; CmdDef *d; - p = keystr; - nb_keys = 0; - for(;;) { - skip_spaces(&p); - if (*p == '\0') - break; - key = strtokey(&p); - keys[nb_keys++] = key; - if (nb_keys >= MAX_KEYS) - break; - } + nb_keys = strtokeys(keystr, keys, MAX_KEYS); + if (!nb_keys) + return; + d = qe_find_cmd(cmd_name); if (!d) return; @@ -1681,6 +1673,7 @@ void do_insert_file(EditState *s, const char *filename); void do_save(EditState *s, int save_as); void do_isearch(EditState *s, int dir); void do_refresh(EditState *s); +void do_refresh_complete(EditState *s); /* compute string for the first part of the mode line (flags, filename, modename) */ @@ -1767,6 +1760,45 @@ void display_mode_line(EditState *s) } } +void display_window_borders(EditState *e) +{ + QEmacsState *qs = e->qe_state; + + if (e->borders_invalid) { + if (e->flags & (WF_POPUP | WF_RSEPARATOR)) { + CSSRect rect; + QEColor color; + + rect.x1 = 0; + rect.y1 = 0; + rect.x2 = qs->width; + rect.y2 = qs->height; + set_clip_rectangle(qs->screen, &rect); + color = qe_styles[QE_STYLE_WINDOW_BORDER].bg_color; + if (e->flags & WF_POPUP) { + fill_rectangle(qs->screen, + e->x1, e->y1, + qs->border_width, e->y2 - e->y1, color); + fill_rectangle(qs->screen, + e->x2 - qs->border_width, e->y1, + qs->border_width, e->y2 - e->y1, color); + fill_rectangle(qs->screen, + e->x1, e->y1, + e->x2 - e->x1, qs->border_width, color); + fill_rectangle(qs->screen, + e->x1, e->y2 - qs->border_width, + e->x2 - e->x1, qs->border_width, color); + } + if (e->flags & WF_RSEPARATOR) { + fill_rectangle(qs->screen, + e->x2 - qs->separator_width, e->y1, + qs->separator_width, e->y2 - e->y1, color); + } + } + e->borders_invalid = 0; + } +} + /* compute style */ static void apply_style(QEStyleDef *style, int style_index) { @@ -3320,6 +3352,7 @@ void window_display(EditState *s) s->mode->display(s); display_mode_line(s); + display_window_borders(s); } /* display all windows */ @@ -3335,23 +3368,34 @@ void edit_display(QEmacsState *qs) s->mode->display_hook(s); } - /* first display popups and minibuf */ + /* count popups */ + /* CG: maybe a separate list for popups? */ has_popups = 0; for (s = qs->first_window; s != NULL; s = s->next_window) { if (s->flags & WF_POPUP) { - window_display(s); has_popups = 1; - } else if (s->minibuf) { - window_display(s); } } - if (!has_popups) { - for(s = qs->first_window; s != NULL; s = s->next_window) { - if (!s->minibuf) - window_display(s); - } + /* refresh normal windows and minibuf with popup kludge */ + for (s = qs->first_window; s != NULL; s = s->next_window) { + if (!(s->flags & WF_POPUP) && + (s->minibuf || !has_popups || qs->complete_refresh)) { + window_display(s); + } } + /* refresh popups if any */ + if (has_popups) { + for (s = qs->first_window; s != NULL; s = s->next_window) { + if (s->flags & WF_POPUP) { + if (qs->complete_refresh) + /* refresh frame */; + window_display(s); + } + } + } + + qs->complete_refresh = 0; } void do_universal_argument(EditState *s) @@ -3442,6 +3486,46 @@ void do_call_macro(EditState *s) } } +void do_execute_macro_keys(EditState *s, const char *keys) +{ + int key; + const char *p; + + p = keys; + for (;;) { + skip_spaces(&p); + if (*p == '\0') + break; + key = strtokey(&p); + qe_key_process(key); + } +} + +void do_define_kbd_macro(EditState *s, const char *name, const char *keys, + const char *keystr) +{ + CmdDef *def; + int size; + char *macro_name, *p; + + size = strlen(name) + 1; + macro_name = malloc(size + 2); + memcpy(macro_name, name, size); + p = macro_name + size; + *p++ = 'S'; + *p++ = '\0'; + + def = malloc(2 * sizeof(CmdDef)); + memset(def, 0, sizeof(CmdDef) * 2); + def->key = def->alt_key = KEY_NONE; + def->name = macro_name; + def->action.func = do_execute_macro_keys; + def->val = strdup(keystr); + + qe_register_cmd_table(def, NULL); + do_global_set_key(s, keystr, name); +} + #define MACRO_KEY_INCR 64 static void macro_add_key(int key) @@ -3518,7 +3602,7 @@ static void qe_key_process(int key) EditState *s; KeyDef *kd; CmdDef *d; - char buf1[32]; + char buf1[128]; int len; if (qs->defining_macro) { @@ -3544,14 +3628,14 @@ static void qe_key_process(int key) /* special case for escape : we transform it as meta so that unix users are happy ! */ + /* CG: should allow for other key compositions, such as + * diacritics, and compositions of more than 2 keys + */ if (key == KEY_ESC) { c->is_escape = 1; goto next; } else if (c->is_escape) { - if (c->nb_keys >= 2) { - c->nb_keys--; - c->keys[c->nb_keys - 1] = KEY_META(key); - } + compose_keys(c->keys, &c->nb_keys); c->is_escape = 0; } @@ -3595,8 +3679,15 @@ static void qe_key_process(int key) } } } - qe_key_init(); + if (!c->describe_key) + /* CG: should beep */; + + put_status(s, "No command on %s", + keys_to_str(buf1, sizeof(buf1), c->keys, c->nb_keys)); c->describe_key = 0; + qe_key_init(); + dpy_flush(&global_screen); + return; } else if (c->nb_keys == kd->nb_keys) { exec_cmd: d = kd->cmd; @@ -3615,7 +3706,6 @@ static void qe_key_process(int key) } } if (c->describe_key) { - char buf1[128]; put_status(s, "%s runs the command %s", keys_to_str(buf1, sizeof(buf1), c->keys, c->nb_keys), d->name); @@ -3632,6 +3722,7 @@ static void qe_key_process(int key) next: /* display key pressed */ if (!s->minibuf) { + /* Should print argument if any in a more readable way */ keytostr(buf1, sizeof(buf1), key); len = strlen(c->buf); if (len >= 1) @@ -3806,6 +3897,7 @@ static void compute_client_area(EditState *s) EditState *edit_new(EditBuffer *b, int x1, int y1, int width, int height, int flags) { + /* b may be NULL ??? */ EditState *s; QEmacsState *qs = &qe_state; @@ -3829,6 +3921,30 @@ EditState *edit_new(EditBuffer *b, return s; } +/* detach the window from the window tree. */ +void edit_detach(EditState *s) +{ + QEmacsState *qs = s->qe_state; + EditState **ep; + + for (ep = &qs->first_window; *ep; ep = &(*ep)->next_window) { + if (*ep == s) { + *ep = s->next_window; + s->next_window = NULL; + break; + } + } + if (qs->active_window == s) + qs->active_window = qs->first_window; +} + +/* attach the window to the window list. */ +void edit_attach(EditState *s, EditState **ep) +{ + s->next_window = *ep; + *ep = s; +} + /* close the edit window. If it is active, find another active window. If the buffer is only referenced by this window, then save in the buffer all the window state so that it can be recovered. */ @@ -4222,6 +4338,7 @@ void minibuffer_edit(const char *input, const char *prompt, s = edit_new(b, 0, qs->screen->height - qs->status_height, qs->screen->width, qs->status_height, 0); + /* Should insert at end of window list */ do_set_mode(s, &minibuffer_mode, NULL); s->prompt = strdup(prompt); s->minibuf = 1; @@ -4274,6 +4391,7 @@ void do_less_quit(EditState *s) QEmacsState *qs = s->qe_state; EditBuffer *b; + /* CG: should verify that popup_saved_active still exists */ qs->active_window = popup_saved_active; b = s->b; edit_close(s); @@ -5065,7 +5183,8 @@ static void isearch_key(void *opaque, int ch) int i, j; switch (ch) { - case KEY_BACKSPACE: + case KEY_DEL: + case KEY_BS: /* Should test actual binding of KEY_BS */ if (is->pos > 0) is->pos--; break; @@ -5306,6 +5425,7 @@ static void do_query_replace(EditState *s, void do_doctor(EditState *s) { + /* Should show keys? */ put_status(s, "Hello, how are you ?"); } @@ -5334,6 +5454,11 @@ void do_refresh(EditState *s1) int new_status_height, new_mode_line_height, content_height; int width, height; + if (qs->complete_refresh) { + if (qs->screen->dpy.dpy_invalidate) + qs->screen->dpy.dpy_invalidate(); + } + /* recompute various dimensions */ if (qs->screen->media & CSS_MEDIA_TTY) { qs->separator_width = 1; @@ -5396,41 +5521,21 @@ void do_refresh(EditState *s1) /* invalidate all the edit windows and draw borders */ for(e = qs->first_window; e != NULL; e = e->next_window) { edit_invalidate(e); - if (e->flags & (WF_POPUP | WF_RSEPARATOR)) { - CSSRect rect; - QEColor color; - rect.x1 = 0; - rect.y1 = 0; - rect.x2 = qs->width; - rect.y2 = qs->height; - set_clip_rectangle(qs->screen, &rect); - color = qe_styles[QE_STYLE_WINDOW_BORDER].bg_color; - if (e->flags & WF_POPUP) { - fill_rectangle(qs->screen, - e->x1, e->y1, - qs->border_width, e->y2 - e->y1, color); - fill_rectangle(qs->screen, - e->x2 - qs->border_width, e->y1, - qs->border_width, e->y2 - e->y1, color); - fill_rectangle(qs->screen, - e->x1, e->y1, - e->x2 - e->x1, qs->border_width, color); - fill_rectangle(qs->screen, - e->x1, e->y2 - qs->border_width, - e->x2 - e->x1, qs->border_width, color); - } - if (e->flags & WF_RSEPARATOR) { - fill_rectangle(qs->screen, - e->x2 - qs->separator_width, e->y1, - qs->separator_width, e->y2 - e->y1, color); - } - } + e->borders_invalid = 1; } /* invalidate status line */ qs->status_shadow[0] = '\0'; put_status(NULL, " "); } +void do_refresh_complete(EditState *s) +{ + QEmacsState *qs = s->qe_state; + + qs->complete_refresh = 1; + do_refresh(s); +} + void do_other_window(EditState *s) { QEmacsState *qs = s->qe_state; @@ -5510,6 +5615,8 @@ void do_delete_window(EditState *s, int force) if (e) compute_client_area(e); } + if (qs->active_window == s) + qs->active_window = e ? e : qs->first_window; edit_close(s); if (qs->first_window) do_refresh(qs->first_window); @@ -5539,6 +5646,7 @@ void do_delete_other_windows(EditState *s) /* XXX: add minimum size test and refuse to split if reached */ void do_split_window(EditState *s, int horiz) { + QEmacsState *qs = s->qe_state; EditState *e; int x, y; @@ -5564,6 +5672,13 @@ void do_split_window(EditState *s, int horiz) return; s->y2 = y; } + /* insert in the window list after current window */ + edit_detach(e); + edit_attach(e, &s->next_window); + + if (qs->flag_split_window_change_focus) + qs->active_window = e; + compute_client_area(s); do_refresh(s); } @@ -5638,7 +5753,7 @@ void do_describe_bindings(EditState *s) b->flags |= BF_READONLY; if (show) { show_popup(b); - eb_free(b); + //eb_free(b); } } diff --git a/qe.h b/qe.h index 7192517..9f053dc 100644 --- a/qe.h +++ b/qe.h @@ -92,7 +92,9 @@ int ustristart(const unsigned int *str, const char *val, const unsigned int **pt void css_strtolower(char *buf, int buf_size); void get_str(const char **pp, char *buf, int buf_size, const char *stop); +int compose_keys(int *keys, int *nb_keys); int strtokey(const char **pp); +int strtokeys(const char *keystr, unsigned int *keys, int max_keys); void keytostr(char *buf, int buf_size, int key); int css_define_color(const char *name, const char *value); int css_get_color(int *color_ptr, const char *p); @@ -209,8 +211,32 @@ typedef struct QECharset { struct QECharset *next; } QECharset; -extern QECharset charset_utf8, charset_8859_1; /* predefined charsets */ extern QECharset *first_charset; +extern QECharset charset_utf8, charset_8859_1; /* predefined charsets */ +extern QECharset charset_8859_2; +extern QECharset charset_cp1125; +extern QECharset charset_cp737; +extern QECharset charset_koi8_r; +extern QECharset charset_8859_4; +extern QECharset charset_cp1250; +extern QECharset charset_cp850; +extern QECharset charset_koi8_u; +extern QECharset charset_viscii; +extern QECharset charset_8859_13; +extern QECharset charset_8859_5; +extern QECharset charset_cp1251; +extern QECharset charset_cp852; +extern QECharset charset_mac_lat2; +extern QECharset charset_8859_15; +extern QECharset charset_8859_7; +extern QECharset charset_cp1257; +extern QECharset charset_cp866; +extern QECharset charset_macroman; +extern QECharset charset_8859_16; +extern QECharset charset_8859_9; +extern QECharset charset_cp437; +extern QECharset charset_kamen; +extern QECharset charset_tcvn5712; typedef struct CharsetDecodeState { /* 256 ushort table for hyper fast decoding */ @@ -286,50 +312,63 @@ enum QEEventType { QE_SELECTION_CLEAR_EVENT, /* request selection clear (X11 type selection) */ }; -#define KEY_META(c) ((c) | 0xe000) -#define KEY_ESC1(c) ((c) | 0xe100) -#define KEY_CTRLX(c) ((c) | 0xe200) +#define KEY_CTRL(c) ((c) & 0x001f) +#define KEY_META(c) ((c) | 0xe000) +#define KEY_ESC1(c) ((c) | 0xe100) +#define KEY_CTRLX(c) ((c) | 0xe200) #define KEY_CTRLXRET(c) ((c) | 0xe300) -#define KEY_CTRLH(c) ((c) | 0xe500) -#define KEY_CTRL(c) ((c) & 0x1f) -#define KEY_UP KEY_ESC1('A') -#define KEY_DOWN KEY_ESC1('B') -#define KEY_RIGHT KEY_ESC1('C') -#define KEY_LEFT KEY_ESC1('D') -#define KEY_CTRL_UP KEY_ESC1('a') -#define KEY_CTRL_DOWN KEY_ESC1('b') -#define KEY_CTRL_RIGHT KEY_ESC1('c') -#define KEY_CTRL_LEFT KEY_ESC1('d') -#define KEY_CTRL_HOME 0xe402 -#define KEY_CTRL_END 0xe403 -#define KEY_TAB KEY_CTRL('i') -#define KEY_SHIFT_TAB 0xe404 -#define KEY_F1 KEY_ESC1(11) -#define KEY_F2 KEY_ESC1(12) -#define KEY_F3 KEY_ESC1(13) -#define KEY_F4 KEY_ESC1(14) -#define KEY_F5 KEY_ESC1(15) -#define KEY_F6 KEY_ESC1(16) -#define KEY_F7 KEY_ESC1(17) -#define KEY_F8 KEY_ESC1(18) -#define KEY_F9 KEY_ESC1(19) -#define KEY_F10 KEY_ESC1(20) -#define KEY_F11 KEY_ESC1(21) -#define KEY_F12 KEY_ESC1(22) -#define KEY_BACKSPACE 127 -#define KEY_INSERT KEY_ESC1(2) -#define KEY_DELETE KEY_ESC1(3) -#define KEY_PAGEUP KEY_ESC1(5) -#define KEY_PAGEDOWN KEY_ESC1(6) -#define KEY_HOME KEY_ESC1(7) -#define KEY_END KEY_ESC1(8) -#define KEY_REFRESH KEY_CTRL('l') -#define KEY_RET 0x000d -#define KEY_ESC 0x001b -#define KEY_SPC 0x0020 -#define KEY_NONE 0xffff -#define KEY_DEFAULT 0xe401 /* to handle all non special keys */ -#define KEY_SPECIAL(c) (((c) >= 0xe000 && (c) < 0xf000) || ((c) >= 0 && (c) < 32)) +#define KEY_CTRLH(c) ((c) | 0xe500) +#define KEY_SPECIAL(c) (((c) >= 0xe000 && (c) < 0xf000) || ((c) >= 0 && (c) < 32)) + +#define KEY_NONE 0xffff +#define KEY_TAB KEY_CTRL('i') +#define KEY_RET KEY_CTRL('m') +#define KEY_REFRESH KEY_CTRL('l') +#define KEY_ESC KEY_CTRL('[') +#define KEY_SPC 0x0020 +#define KEY_DEL 127 // kbs +#define KEY_BS KEY_CTRL('h') // kbs + +#define KEY_DEFAULT 0xe401 /* to handle all non special keys */ +#define KEY_UP KEY_ESC1('A') // kcuu1 +#define KEY_DOWN KEY_ESC1('B') // kcud1 +#define KEY_RIGHT KEY_ESC1('C') // kcuf1 +#define KEY_LEFT KEY_ESC1('D') // kcub1 +#define KEY_CTRL_UP KEY_ESC1('a') +#define KEY_CTRL_DOWN KEY_ESC1('b') +#define KEY_CTRL_RIGHT KEY_ESC1('c') +#define KEY_CTRL_LEFT KEY_ESC1('d') +#define KEY_CTRL_END KEY_ESC1('f') +#define KEY_CTRL_HOME KEY_ESC1('h') +#define KEY_CTRL_PAGEUP KEY_ESC1('i') +#define KEY_CTRL_PAGEDOWN KEY_ESC1('j') +#define KEY_SHIFT_TAB KEY_ESC1('Z') // kcbt +#define KEY_HOME KEY_ESC1(1) // khome +#define KEY_INSERT KEY_ESC1(2) // kich1 +#define KEY_DELETE KEY_ESC1(3) // kdch1 +#define KEY_END KEY_ESC1(4) // kend +#define KEY_PAGEUP KEY_ESC1(5) // kpp +#define KEY_PAGEDOWN KEY_ESC1(6) // knp +#define KEY_F1 KEY_ESC1(11) +#define KEY_F2 KEY_ESC1(12) +#define KEY_F3 KEY_ESC1(13) +#define KEY_F4 KEY_ESC1(14) +#define KEY_F5 KEY_ESC1(15) +#define KEY_F6 KEY_ESC1(17) +#define KEY_F7 KEY_ESC1(18) +#define KEY_F8 KEY_ESC1(19) +#define KEY_F9 KEY_ESC1(20) +#define KEY_F10 KEY_ESC1(21) +#define KEY_F11 KEY_ESC1(23) +#define KEY_F12 KEY_ESC1(24) +#define KEY_F13 KEY_ESC1(25) +#define KEY_F14 KEY_ESC1(26) +#define KEY_F15 KEY_ESC1(28) +#define KEY_F16 KEY_ESC1(29) +#define KEY_F17 KEY_ESC1(31) +#define KEY_F18 KEY_ESC1(32) +#define KEY_F19 KEY_ESC1(33) +#define KEY_F20 KEY_ESC1(34) typedef struct QEKeyEvent { enum QEEventType type; @@ -698,6 +737,7 @@ typedef struct EditState { produce the display */ int display_invalid; /* true if the display was invalidated. Full redraw should be done */ + int borders_invalid; /* true if window borders should be redrawn */ int show_selection; /* if true, the selection is displayed */ /* display area info */ int width, height; @@ -838,8 +878,10 @@ typedef struct QEmacsState { int separator_width; /* full screen state */ int hide_status; /* true if status should be hidden */ + int complete_refresh; int is_full_screen; /* commands */ + int flag_split_window_change_focus; void *last_cmd_func; /* last executed command function call */ /* keyboard macros */ int defining_macro; @@ -1082,6 +1124,8 @@ void do_find_window(EditState *s, int key); /* window handling */ void edit_close(EditState *s); +void edit_detach(EditState *s); +void edit_append(EditState *s, EditState *e); EditState *edit_new(EditBuffer *b, int x1, int y1, int width, int height, int flags); void do_refresh(EditState *s); @@ -1106,6 +1150,7 @@ void do_char(EditState *s, int key); void do_switch_to_buffer(EditState *s, const char *bufname);; void do_set_mode(EditState *s, ModeDef *m, ModeSavedData *saved_data); void text_move_left_right_visual(EditState *s, int dir); +void text_move_word_left_right(EditState *s, int dir); void text_move_up_down(EditState *s, int dir); void text_scroll_up_down(EditState *s, int dir); void text_write_char(EditState *s, int key); diff --git a/qeconfig.h b/qeconfig.h index 5cbca71..e9bec2b 100644 --- a/qeconfig.h +++ b/qeconfig.h @@ -16,10 +16,10 @@ CmdDef basic_commands[] = { CMD1( KEY_CTRL('z'), KEY_NONE, "scroll-up-one", do_scroll_up_down, 1 ) CMD0( KEY_HOME, KEY_CTRL('a'), "beginning-of-line", do_bol) CMD0( KEY_END, KEY_CTRL('e'), "end-of-line", do_eol) - CMD0( KEY_ESC1(2), KEY_NONE, "overwrite-mode", do_insert) + CMD0( KEY_INSERT, KEY_NONE, "overwrite-mode", do_insert) CMD0( KEY_CTRL('d'), KEY_DELETE, "delete-char", do_delete_char) CMD0( 127, KEY_NONE, "backward-delete-char", do_backspace) - CMD1( KEY_META(KEY_BACKSPACE) , KEY_NONE, + CMD1( KEY_META(KEY_DEL) , KEY_META(KEY_BS), "backward-delete-word", do_delete_word, -1) CMD1( KEY_META('d') , KEY_NONE, "delete-word", do_delete_word, 1) CMD1( KEY_CTRL('k'), KEY_NONE, "kill-line", do_kill_region, 2 ) @@ -59,7 +59,7 @@ CmdDef basic_commands[] = { "s{Query replace: }|search|s{With: }|replace|") CMD0( KEY_CTRLX('u'), KEY_CTRL('_'), "undo", do_undo) CMD0( KEY_RET, KEY_NONE, "newline", do_return) - CMD0( KEY_CTRL('l'), KEY_NONE, "refresh", do_refresh) + CMD0( KEY_CTRL('l'), KEY_NONE, "refresh", do_refresh_complete) CMD_( KEY_META('g'), KEY_NONE, "goto-line", do_goto_line, "i{Goto line: }") CMD_( KEY_NONE, KEY_NONE, "goto-char", do_goto_char, "i{Goto char: }") CMD0( KEY_CTRLX(KEY_CTRL('q')), KEY_NONE, "vc-toggle-read-only", @@ -80,8 +80,10 @@ CmdDef basic_commands[] = { CMD0( KEY_CTRLX('('), KEY_NONE, "start-kbd-macro", do_start_macro) CMD0( KEY_CTRLX(')'), KEY_NONE, "end-kbd-macro", do_end_macro) CMD0( KEY_CTRLX('e'), KEY_NONE, "call-last-kbd-macro", do_call_macro) + CMD_( KEY_NONE, KEY_NONE, "define-kbd-macro", do_define_kbd_macro, + "s{Macro name: }[command]s{Macro keys: }s{Bind to key: }[key]") CMD_( KEY_NONE, KEY_NONE, "global-set-key", do_global_set_key, - "s{Set key globally: }s{command: }[command]|command|") + "s{Set key globally: }[key]s{command: }[command]|command|") /* window handling */ CMD0( KEY_CTRLX('o'), KEY_NONE, "other-window", do_other_window) @@ -106,7 +108,7 @@ CmdDef basic_commands[] = { /* help */ CMD0( KEY_CTRLH(KEY_CTRL('h')), KEY_F1, "help-for-help", do_help_for_help) CMD0( KEY_CTRLH('b'), KEY_NONE, "describe-bindings", do_describe_bindings) - CMD0( KEY_CTRLH('c'), KEY_NONE, "describe-key-briefly", + CMD0( KEY_CTRLH('c'), KEY_CTRLH('k'), "describe-key-briefly", do_describe_key_briefly) /* international */ diff --git a/shell.c b/shell.c index 7712a08..ded62c7 100644 --- a/shell.c +++ b/shell.c @@ -54,6 +54,7 @@ typedef struct ShellState { int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; int state; + int shifted; EditBuffer *b; EditBuffer *b_color; /* color buffer, one byte per char */ int is_shell; /* only used to display final message */ @@ -233,6 +234,7 @@ void tty_csi_m(ShellState *s, int c) /* Well, almost a hack to update cursor */ static void tty_update_cursor(ShellState *s) { +#if 0 QEmacsState *qs = s->qe_state; EditState *e; @@ -244,6 +246,17 @@ static void tty_update_cursor(ShellState *s) e->offset = s->cur_offset; } } +#endif +} + +/* CG: much cleaner way! */ +/* would need a kill hook as well ? */ +static void shell_display_hook(EditState *e) +{ + ShellState *s = e->b->priv_data; + + if (e->interactive) + e->offset = s->cur_offset; } static void tty_emulate(ShellState *s, int c) @@ -288,6 +301,14 @@ static void tty_emulate(ShellState *s, int c) s->cur_offset = offset1; } break; + case 14: + eb_set_charset(s->b, &charset_8859_1); + s->shifted = 1; + break; + case 15: + eb_set_charset(s->b, &charset_cp1125); + s->shifted = 0; + break; case 27: s->state = TTY_STATE_ESC; break; @@ -317,7 +338,7 @@ static void tty_emulate(ShellState *s, int c) break; case TTY_STATE_ESC: if (c == '[') { - for(i=0;iesc_params[i] = 0; s->nb_esc_params = 0; s->state = TTY_STATE_CSI; @@ -663,36 +684,46 @@ void shell_move_left_right(EditState *e, int dir) { if (e->interactive) { ShellState *s = e->b->priv_data; - if (dir > 0) - tty_write(s, "\033[C", -1); - else - tty_write(s, "\033[D", -1); + tty_write(s, dir > 0 ? "\033[C" : "\033[D", -1); } else { text_move_left_right_visual(e, dir); } } +void shell_move_word_left_right(EditState *e, int dir) +{ + if (e->interactive) { + ShellState *s = e->b->priv_data; + tty_write(s, dir > 0 ? "\033f" : "\033b", -1); + } else { + text_move_word_left_right(e, dir); + } +} + void shell_move_up_down(EditState *e, int dir) { if (e->interactive) { ShellState *s = e->b->priv_data; - - if (dir > 0) - tty_write(s, "\033[B", -1); - else - tty_write(s, "\033[A", -1); + tty_write(s, dir > 0 ? "\033[B" : "\033[A", -1); } else { text_move_up_down(e, dir); } } +void shell_scroll_up_down(EditState *e, int dir) +{ + ShellState *s = e->b->priv_data; + + e->interactive = 0; + text_scroll_up_down(e, dir); + e->interactive = (e->offset == s->cur_offset); +} + void shell_move_bol(EditState *e) { if (e->interactive) { ShellState *s = e->b->priv_data; - unsigned char ch; - ch = 1; - tty_write(s, &ch, 1); + tty_write(s, "\001", -1); /* Control-A */ } else { text_move_bol(e); } @@ -702,9 +733,7 @@ void shell_move_eol(EditState *e) { if (e->interactive) { ShellState *s = e->b->priv_data; - unsigned char ch; - ch = 5; - tty_write(s, &ch, 1); + tty_write(s, "\005", -1); /* Control-E */ } else { text_move_eol(e); } @@ -746,10 +775,12 @@ void shell_write_char(EditState *e, int c) void do_shell_toggle_input(EditState *e) { e->interactive = !e->interactive; +#if 0 if (e->interactive) { ShellState *s = e->b->priv_data; tty_update_cursor(s); } +#endif } static int error_offset = -1; @@ -916,8 +947,11 @@ static int shell_init(void) shell_mode.name = "shell"; shell_mode.mode_probe = NULL; shell_mode.mode_init = shell_mode_init; + shell_mode.display_hook = shell_display_hook; shell_mode.move_left_right = shell_move_left_right; + shell_mode.move_word_left_right = shell_move_word_left_right; shell_mode.move_up_down = shell_move_up_down; + shell_mode.scroll_up_down = shell_scroll_up_down; shell_mode.move_bol = shell_move_bol; shell_mode.move_eol = shell_move_eol; shell_mode.write_char = shell_write_char; diff --git a/tty.c b/tty.c index fb47c5b..b154686 100644 --- a/tty.c +++ b/tty.c @@ -41,6 +41,7 @@ enum InputState { IS_NORM, IS_ESC, IS_CSI, + IS_CSI2, IS_ESC2, }; @@ -103,6 +104,7 @@ static int term_init(QEditScreen *s, int w, int h) s->charset = &charset_8859_1; #ifndef CONFIG_CYGWIN + /* CG: Should also have a command line switch */ /* test UTF8 support by looking at the cursor position (idea from Ricardas Cepas ). Since uClibc actually tests to ensure that the format string is a valid multibyte sequence @@ -197,6 +199,11 @@ static void tty_resize(int sig) s->clip_y2 = s->height; } +static void term_invalidate(void) +{ + tty_resize(0); +} + static void term_cursor_at(QEditScreen *s, int x1, int y1, int w, int h) { TTYState *ts = s->private; @@ -219,6 +226,44 @@ static int term_is_user_input_pending(QEditScreen *s) return 0; } +static int const csi_lookup[] = { + KEY_NONE, /* 0 */ + KEY_HOME, /* 1 */ + KEY_INSERT, /* 2 */ + KEY_DELETE, /* 3 */ + KEY_END, /* 4 */ + KEY_PAGEUP, /* 5 */ + KEY_PAGEDOWN, /* 6 */ + KEY_NONE, /* 7 */ + KEY_NONE, /* 8 */ + KEY_NONE, /* 9 */ + KEY_NONE, /* 10 */ + KEY_F1, /* 11 */ + KEY_F2, /* 12 */ + KEY_F3, /* 13 */ + KEY_F4, /* 14 */ + KEY_F5, /* 15 */ + KEY_NONE, /* 16 */ + KEY_F6, /* 17 */ + KEY_F7, /* 18 */ + KEY_F8, /* 19 */ + KEY_F9, /* 20 */ + KEY_F10, /* 21 */ + KEY_NONE, /* 22 */ + KEY_F11, /* 23 */ + KEY_F12, /* 24 */ + KEY_F13, /* 25 */ + KEY_F14, /* 26 */ + KEY_NONE, /* 27 */ + KEY_F15, /* 28 */ + KEY_F16, /* 29 */ + KEY_NONE, /* 30 */ + KEY_F17, /* 31 */ + KEY_F18, /* 32 */ + KEY_F19, /* 33 */ + KEY_F20, /* 34 */ +}; + static void tty_read_handler(void *opaque) { QEditScreen *s = opaque; @@ -232,6 +277,13 @@ static void tty_read_handler(void *opaque) if (trace_buffer) { eb_write(trace_buffer, trace_buffer->total_size, ts->buf + ts->utf8_index, 1); +#if 0 + ch = ts->buf[ts->utf8_index]; + if (ch < 32 || ch == 127) + fprintf(stderr, "got %d '^%c'\n", ch, ('@' + ch) & 127); + else + fprintf(stderr, "got %d '%c'\n", ch, ch); +#endif } /* charset handling */ @@ -257,6 +309,10 @@ static void tty_read_handler(void *opaque) goto the_end; break; case IS_ESC: + if (ch == '\033') { + /* cygwin A-right transmit ESC ESC[C ... */ + goto the_end; + } if (ch == '[') { ts->input_state = IS_CSI; ts->input_param = 0; @@ -269,43 +325,82 @@ static void tty_read_handler(void *opaque) } break; case IS_CSI: - if (!isdigit(ch)) { - ts->input_state = IS_NORM; - switch(ch) { - case '~': - ch = KEY_ESC1(ts->input_param); - goto the_end; - case 'H': - ch = KEY_HOME; - goto the_end; - case 'F': - ch = KEY_END; - goto the_end; - case 'Z': - ch = KEY_SHIFT_TAB; - goto the_end; - default: - /* xterm CTRL-arrows */ - if (ts->input_param == 5 && ch >= 'A' && ch <= 'D') - ch += 'a' - 'A'; - ch = KEY_ESC1(ch); + if (isdigit(ch)) { + ts->input_param = ts->input_param * 10 + ch - '0'; + break; + } + ts->input_state = IS_NORM; + switch(ch) { + case '[': + ts->input_state = IS_CSI2; + break; + case '~': + if (ts->input_param < sizeof(csi_lookup)/sizeof(csi_lookup[0])) { + ch = csi_lookup[ts->input_param]; goto the_end; } - } else { - ts->input_param = ts->input_param * 10 + ch - '0'; + break; + /* All these for ansi|cygwin */ + case 'A': ch = KEY_UP; goto the_end; // kcuu1 + case 'B': ch = KEY_DOWN; goto the_end; // kcud1 + case 'C': ch = KEY_RIGHT; goto the_end; // kcuf1 + case 'D': ch = KEY_LEFT; goto the_end; // kcub1 + case 'F': ch = KEY_END; goto the_end; // kend + //case 'G': ch = KEY_CENTER; goto the_end; // kb2 + case 'H': ch = KEY_HOME; goto the_end; // khome + case 'L': ch = KEY_INSERT; goto the_end; // kich1 + //case 'M': ch = KEY_MOUSE; goto the_end; // kmous + case 'Z': ch = KEY_SHIFT_TAB; goto the_end; // kcbt + default: +#if 0 /* xterm CTRL-arrows */ + if (ts->input_param == 5) { + switch (ch) { + case 'A': ch = KEY_CTRL_UP; goto the_end; + case 'B': ch = KEY_CTRL_DOWN; goto the_end; + case 'C': ch = KEY_CTRL_RIGHT; goto the_end; + case 'D': ch = KEY_CTRL_LEFT; goto the_end; + } + } +#endif + break; } break; - case IS_ESC2: - /* xterm fn */ + case IS_CSI2: + /* cygwin/linux terminal */ ts->input_state = IS_NORM; - if (ch >= 'P' && ch <= 'S') { - ch = KEY_F1 + ch - 'P'; - the_end: - ev->key_event.type = QE_KEY_EVENT; - ev->key_event.key = ch; - qe_handle_event(ev); + switch(ch) { + case 'A': ch = KEY_F1; goto the_end; + case 'B': ch = KEY_F2; goto the_end; + case 'C': ch = KEY_F3; goto the_end; + case 'D': ch = KEY_F4; goto the_end; + case 'E': ch = KEY_F5; goto the_end; } break; + case IS_ESC2: // "\EO" + /* xterm/vt100 fn */ + ts->input_state = IS_NORM; + switch(ch) { + case 'A': ch = KEY_UP; goto the_end; + case 'B': ch = KEY_DOWN; goto the_end; + case 'C': ch = KEY_RIGHT; goto the_end; + case 'D': ch = KEY_LEFT; goto the_end; + case 'P': ch = KEY_F1; goto the_end; + case 'Q': ch = KEY_F2; goto the_end; + case 'R': ch = KEY_F3; goto the_end; + case 'S': ch = KEY_F4; goto the_end; + case 't': ch = KEY_F5; goto the_end; + case 'u': ch = KEY_F6; goto the_end; + case 'v': ch = KEY_F7; goto the_end; + case 'l': ch = KEY_F8; goto the_end; + case 'w': ch = KEY_F9; goto the_end; + case 'x': ch = KEY_F10; goto the_end; + } + break; + the_end: + ev->key_event.type = QE_KEY_EVENT; + ev->key_event.key = ch; + qe_handle_event(ev); + break; } } @@ -577,6 +672,7 @@ static QEDisplay tty_dpy = { term_set_clip, NULL, /* no selection handling */ NULL, /* no selection handling */ + term_invalidate, }; static int tty_init(void) diff --git a/util.c b/util.c index 67555ac..d50acdc 100644 --- a/util.c +++ b/util.c @@ -466,64 +466,95 @@ int css_get_enum(const char *str, const char *enum_str) } unsigned short keycodes[] = { - KEY_LEFT, - KEY_RIGHT, - KEY_UP, - KEY_DOWN, - KEY_CTRL_LEFT, - KEY_CTRL_RIGHT, - KEY_CTRL_UP, - KEY_CTRL_DOWN, - KEY_CTRL_HOME, - KEY_CTRL_END, - KEY_CTRL(' '), - KEY_CTRL('\\'), - KEY_CTRL(']'), - KEY_CTRL('^'), - KEY_CTRL('_'), - KEY_BACKSPACE, - KEY_INSERT, - KEY_DELETE, - KEY_PAGEUP, - KEY_PAGEDOWN, - KEY_HOME, - KEY_END, - ' ', - KEY_RET, - KEY_ESC, - KEY_TAB, - KEY_SHIFT_TAB, - KEY_DEFAULT, + KEY_SPC, KEY_DEL, KEY_RET, KEY_ESC, KEY_TAB, KEY_SHIFT_TAB, + KEY_CTRL(' '), KEY_DEL, KEY_CTRL('\\'), + KEY_CTRL(']'), KEY_CTRL('^'), KEY_CTRL('_'), + KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, + KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, + KEY_CTRL_LEFT, KEY_CTRL_RIGHT, KEY_CTRL_UP, KEY_CTRL_DOWN, + KEY_CTRL_HOME, KEY_CTRL_END, KEY_CTRL_PAGEUP, KEY_CTRL_PAGEDOWN, + KEY_PAGEUP, KEY_PAGEDOWN, KEY_CTRL_PAGEUP, KEY_CTRL_PAGEDOWN, + KEY_INSERT, KEY_DELETE, KEY_DEFAULT, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, + KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, + KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, }; const char *keystr[] = { + "SPC", "DEL", "RET", "ESC", "TAB", "S-TAB", + "C-SPC", "C-?", "C-\\", "C-]", "C-^", "C-_", "left", "right", "up", "down", - "C-left", "C-right", "C-up", "C-down", - "C-home", "C-end", - "C-space", "C-\\", "C-]", "C-^", "C-_", - "backspace", "insert", "delete", "prior", "next", - "home", "end", - "SPC", "RET", "ESC", - "TAB", "S-TAB", - "default", + "home", "end", "prior", "next", + "C-left", "C-right", "C-up", "C-down", + "C-home", "C-end", "C-prior", "C-next", + "pageup", "pagedown", "C-pageup", "C-pagedown", + "insert", "delete", "default", + "f1", "f2", "f3", "f4", "f5", + "f6", "f7", "f8", "f9", "f10", + "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", }; -static int strtokey1(const char *p) +int compose_keys(int *keys, int *nb_keys) { - int i, n; + int *keyp; - for (i = 0; i < (int)(sizeof(keycodes)/sizeof(keycodes[0])); i++) { - if (!strcmp(p, keystr[i])) - return keycodes[i]; + if (*nb_keys < 2) + return 0; + + /* compose KEY_ESC as META prefix */ + keyp = keys + *nb_keys - 2; + if (keyp[0] == KEY_ESC) { + if (keyp[1] <= 0xff) { + keyp[0] = KEY_META(keyp[1]); + --*nb_keys; + return 1; + } } + return 0; +} + +/* CG: this code is still quite inelegant */ +static int strtokey1(const char **pp) +{ + const char *p, *p1, *q; + int i, key; + + /* should return KEY_NONE at end and KEY_UNKNOWN if unrecognized */ + p = *pp; + + /* scan for separator */ + for (p1 = p; *p1 && *p1 != ' '; p1++) + continue; + + for (i = 0; i < sizeof(keycodes)/sizeof(keycodes[0]); i++) { + if (strstart(p, keystr[i], &q) && q == p1) { + key = keycodes[i]; + *pp = p1; + return key; + } + } +#if 0 if (p[0] == 'f' && p[1] >= '1' && p[1] <= '9') { - p++; - n = *p - '0'; - if (isdigit((unsigned char)p[1])) - n = n * 10 + p[1] - '0'; - return KEY_F1 + n - 1; + i = p[1] - '0'; + p += 2; + if (p1 == isdigit((unsigned char)*p)) + i = i * 10 + *p++ - '0'; + key = KEY_F1 + i - 1; + *pp = p1; + return key; } - return utf8_decode(&p); +#endif + if (p[0] == 'C' && p[1] == '-' && p1 == p + 3) { + /* control */ + key = KEY_CTRL(p[2]); + } else { + key = utf8_decode(&p); + } + *pp = p1; + + return key; } int strtokey(const char **pp) @@ -531,29 +562,42 @@ int strtokey(const char **pp) const char *p; int key; - /* XXX: handle all cases */ p = *pp; - if (p[0] == 'C' && p[1] == '-') { - /* control */ + if (p[0] == 'M' && p[1] == '-') { p += 2; - key = strtokey1(p); - if (key >= 'a' && key <= 'z') - key = KEY_CTRL(key); - } else if (p[0] == 'M' && p[1] == '-') { - p += 2; - key = strtokey1(p); - if ((key >= 'a' && key <= 'z') || - key == KEY_BACKSPACE) - key = KEY_META(key); + key = KEY_META(strtokey1(&p)); + } else + if (p[0] == 'C' && p[1] == '-' && p[0] == 'M' && p[1] == '-') { + p += 4; + key = KEY_META(KEY_CTRL(strtokey1(&p))); } else { - key = strtokey1(p); + key = strtokey1(&p); } - while (*p != ' ' && *p != '\0') - p++; *pp = p; return key; } +int strtokeys(const char *keystr, unsigned int *keys, int max_keys) +{ + int key, nb_keys; + const char *p; + + p = keystr; + nb_keys = 0; + + for (;;) { + skip_spaces(&p); + if (*p == '\0') + break; + key = strtokey(&p); + keys[nb_keys++] = key; + compose_keys(keys, &nb_keys); + if (nb_keys >= max_keys) + break; + } + return nb_keys; +} + void keytostr(char *buf, int buf_size, int key) { int i; @@ -565,18 +609,21 @@ void keytostr(char *buf, int buf_size, int key) return; } } - if (key >= KEY_META(' ') && key <= KEY_META(127)) { + if (key >= KEY_META(0) && key <= KEY_META(0xff)) { keytostr(buf1, sizeof(buf1), key & 0xff); snprintf(buf, buf_size, "M-%s", buf1); - } else if (key >= 1 && key <= 31) { + } else if (key >= KEY_CTRL('a') && key <= KEY_CTRL('z')) { snprintf(buf, buf_size, "C-%c", key + 'a' - 1); - } else if (key >= KEY_F1 && key <= KEY_F12) { - snprintf(buf, buf_size, "F%d", key - KEY_F1 + 1); + } else if (key >= KEY_F1 && key <= KEY_F20) { + snprintf(buf, buf_size, "f%d", key - KEY_F1 + 1); + } else if (key > 32 && key < 127 && buf_size >= 2) { + buf[0] = key; + buf[1] = '\0'; } else { char *q; - /* CG: assuming buf_size > 5 */ - q = utf8_encode(buf, key); + q = utf8_encode(buf1, key); *q = '\0'; + pstrcpy(buf, buf_size, buf1); } } diff --git a/win32.c b/win32.c index c1c9540..316c229 100644 --- a/win32.c +++ b/win32.c @@ -268,7 +268,7 @@ LRESULT CALLBACK qe_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) switch(scan) { case 0x00E: ignore_wchar_msg = 1; - push_key(KEY_BACKSPACE); + push_key(KEY_DEL); break; case 0x039: /* space */ ignore_wchar_msg = 1; diff --git a/x11.c b/x11.c index 13c5707..5ebc6d7 100644 --- a/x11.c +++ b/x11.c @@ -1285,7 +1285,7 @@ static void x11_handle_event(void *opaque) } else if (meta) { switch(keysym) { case XK_BackSpace: - key = KEY_META(KEY_BACKSPACE); + key = KEY_META(KEY_DEL); goto got_key; default: if (keysym >= ' ' && keysym <= '~') { @@ -1323,7 +1323,7 @@ static void x11_handle_event(void *opaque) key = KEY_LEFT; goto got_key; case XK_BackSpace: - key = KEY_BACKSPACE; + key = KEY_DEL; goto got_key; case XK_Insert: key = KEY_INSERT; @@ -1676,6 +1676,7 @@ static QEDisplay x11_dpy = { term_set_clip, term_selection_activate, term_selection_request, + NULL, x11_bmp_alloc, x11_bmp_free, x11_bmp_draw,