diff -Naur ratpoison/configure.in ratpoison.line-editor/configure.in --- ratpoison/configure.in Sun Sep 16 19:58:24 2001 +++ ratpoison.line-editor/configure.in Tue Nov 20 17:59:01 2001 @@ -74,7 +74,7 @@ dnl Checks for typedefs, structures, and compiler characteristics. dnl Checks for library functions. -AC_CHECK_FUNCS(getopt getopt_long setsid setpgid setpgrp) +AC_CHECK_FUNCS(getopt getopt_long setsid setpgid setpgrp usleep) AC_TYPE_SIGNAL AC_OUTPUT(Makefile doc/Makefile src/Makefile contrib/Makefile) diff -Naur ratpoison/src/conf.h ratpoison.line-editor/src/conf.h --- ratpoison/src/conf.h Fri Oct 19 08:47:45 2001 +++ ratpoison.line-editor/src/conf.h Tue Nov 20 17:59:01 2001 @@ -50,4 +50,7 @@ /* Maximum depth of a link. Used in the 'link' command. */ #define MAX_LINK_DEPTH 16 +/* use a visual bell in the input window */ +#define VISUAL_BELL + #endif /* !_ _RATPOISON_CONF_H */ diff -Naur ratpoison/src/data.h ratpoison.line-editor/src/data.h --- ratpoison/src/data.h Thu Oct 18 17:51:30 2001 +++ ratpoison.line-editor/src/data.h Tue Nov 20 17:59:01 2001 @@ -27,6 +27,7 @@ #include #define FONT_HEIGHT(f) ((f)->max_bounds.ascent + (f)->max_bounds.descent) +#define MAX_FONT_WIDTH(f) ((f)->max_bounds.width) #define WIN_EVENTS (StructureNotifyMask | PropertyChangeMask | ColormapChangeMask | FocusChangeMask) diff -Naur ratpoison/src/input.c ratpoison.line-editor/src/input.c --- ratpoison/src/input.c Fri Oct 19 08:47:45 2001 +++ ratpoison.line-editor/src/input.c Tue Nov 20 17:59:01 2001 @@ -19,9 +19,11 @@ * Boston, MA 02111-1307 USA */ +#include #include #include #include +#include #include #include #include @@ -241,7 +243,7 @@ | rp_modifier_info.alt_mod_mask | rp_modifier_info.hyper_mod_mask | rp_modifier_info.super_mod_mask - | ControlMask ); + | ControlMask); return nbytes; } @@ -263,21 +265,23 @@ } static void -update_input_window (screen_info *s, char *prompt, char *input, int input_len) +update_input_window (screen_info *s, input_line *line) { - int prompt_width = XTextWidth (defaults.font, prompt, strlen (prompt)); - int input_width = XTextWidth (defaults.font, input, input_len); - int width; + int prompt_width = XTextWidth (defaults.font, line->prompt, strlen (line->prompt)); + int input_width = XTextWidth (defaults.font, line->buffer, line->length); + int total_width; + GC lgc; + XGCValues gv; - width = defaults.bar_x_padding * 2 + prompt_width + input_width; + total_width = defaults.bar_x_padding * 2 + prompt_width + input_width + MAX_FONT_WIDTH (defaults.font); - if (width < defaults.input_window_size + prompt_width) + if (total_width < defaults.input_window_size + prompt_width) { - width = defaults.input_window_size + prompt_width; + total_width = defaults.input_window_size + prompt_width; } XMoveResizeWindow (dpy, s->input_window, - bar_x (s, width), bar_y (s), width, + bar_x (s, total_width), bar_y (s), total_width, (FONT_HEIGHT (defaults.font) + defaults.bar_y_padding * 2)); XClearWindow (dpy, s->input_window); @@ -285,20 +289,125 @@ XDrawString (dpy, s->input_window, s->normal_gc, defaults.bar_x_padding, - defaults.bar_y_padding + defaults.font->max_bounds.ascent, prompt, - strlen (prompt)); + defaults.bar_y_padding + defaults.font->max_bounds.ascent, + line->prompt, + strlen (line->prompt)); XDrawString (dpy, s->input_window, s->normal_gc, defaults.bar_x_padding + prompt_width, - defaults.bar_y_padding + defaults.font->max_bounds.ascent, input, - input_len); + defaults.bar_y_padding + defaults.font->max_bounds.ascent, + line->buffer, + line->length); + + gv.function = GXxor; + gv.foreground = s->fg_color ^ s->bg_color; + lgc = XCreateGC (dpy, s->input_window, GCFunction | GCForeground, &gv); + + /* Draw a cheap-o cursor - MkII */ + XFillRectangle (dpy, s->input_window, lgc, + defaults.bar_x_padding + prompt_width + XTextWidth (defaults.font, line->buffer, line->position), + defaults.bar_y_padding, + XTextWidth (defaults.font, &line->buffer[line->position], 1), + FONT_HEIGHT (defaults.font)); - /* Draw a cheap-o cursor. */ - XDrawLine (dpy, s->input_window, s->normal_gc, - defaults.bar_x_padding * 2 + prompt_width + input_width + 2, - defaults.bar_y_padding + 1, - defaults.bar_x_padding * 2 + prompt_width + input_width + 2, - defaults.bar_y_padding + FONT_HEIGHT (defaults.font) - 1); + XFlush (dpy); + XFreeGC (dpy, lgc); +} + +static int +get_edit_action (KeySym ch, unsigned int modifier) +{ + /* TODO: This is bbbb...bodgy! + Fix it when we merge it with the existing binding mechanism or something better. */ + + char *abort_keysym_name = keysym_to_string (INPUT_ABORT_KEY, x11_mask_to_rp_mask (INPUT_ABORT_MODIFIER)); + + edit_binding edit_bindings[] = + { {abort_keysym_name, INPUT_ABORT}, + {"C-d", INPUT_DELETE_CHAR}, + {"C-k", INPUT_KILL_LINE}, + {"C-u", INPUT_KILL_WHOLE_LINE}, + {"C-b", INPUT_BACKWARD_CHAR}, + {"C-w", INPUT_BACKWARD_KILL_WORD}, + {"C-f", INPUT_FORWARD_CHAR}, + {"C-a", INPUT_BEGINNING_OF_LINE}, + {"C-e", INPUT_END_OF_LINE}, + {"M-b", INPUT_BACKWARD_WORD}, + {"M-f", INPUT_FORWARD_WORD}, + {"M-d", INPUT_KILL_WORD}, + {"BackSpace", INPUT_BACKWARD_DELETE_CHAR}, + {"Delete", INPUT_DELETE_CHAR}, + {"Left", INPUT_BACKWARD_CHAR}, + {"Right", INPUT_FORWARD_CHAR}, + {"Home", INPUT_BEGINNING_OF_LINE}, + {"End", INPUT_END_OF_LINE}, + {"Return", INPUT_ENTER}, + {"KP_Enter", INPUT_ENTER}, + {"Escape", INPUT_NO_ACTION}, + {"Tab", INPUT_NO_ACTION}, + { 0, 0} }; + + struct edit_binding *binding; + char *keysym_name = keysym_to_string (ch, x11_mask_to_rp_mask (modifier)); + int found_binding = 0; + + PRINT_DEBUG ("keysym = %s\n", keysym_name); + + for (binding = edit_bindings; binding->key; binding++) + { + if (!strcmp (keysym_name, binding->key)) + { + found_binding = 1; + break; + } + } + + free (abort_keysym_name); + free (keysym_name); + + if (found_binding) + return binding->action; + else if (modifier) + return INPUT_NO_ACTION; + else + return INPUT_INSERT; +} + +static void +ring_bell (screen_info *s) +{ +#ifdef VISUAL_BELL + GC lgc; + XGCValues gv; + XWindowAttributes attr; + + XGetWindowAttributes (dpy, s->input_window, &attr); + + gv.function = GXxor; + gv.foreground = s->fg_color ^ s->bg_color; + lgc = XCreateGC (dpy, s->input_window, GCFunction | GCForeground, &gv); + + XFillRectangle (dpy, s->input_window, lgc, 0, 0, attr.width, attr.height); + XFlush (dpy); + + #ifdef HAVE_USLEEP + usleep (15000); + #else + { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 15000; + select (0, NULL, NULL, NULL, &tv); + } + #endif + + XFillRectangle (dpy, s->input_window, lgc, 0, 0, attr.width, attr.height); + XFlush (dpy); + XFreeGC (dpy, lgc); +#else + XBell (dpy, 0); +#endif } char * @@ -315,20 +424,23 @@ int keysym_bufsize = sizeof (keysym_buf); int nbytes; screen_info *s = current_screen (); - int cur_len = 0; /* Current length of the string. */ - int allocated_len=100; /* The amount of memory we allocated for str */ KeySym ch; unsigned int modifier; int revert; Window fwin; - char *str; + int edit_action; + input_line input; + input_line *line = &input; + + line->prompt = prompt; /* Allocate some memory to start with */ - str = (char *) xmalloc ( allocated_len ); + line->size = strlen (preinput) + 100; + line->buffer = (char *) xmalloc (line->size); /* load in the preinput */ - strcpy (str, preinput); - cur_len = strlen (preinput); + strcpy (line->buffer, preinput); + line->position = line->length = strlen (preinput); /* We don't want to draw overtop of the program bar. */ hide_bar (s); @@ -338,50 +450,201 @@ XClearWindow (dpy, s->input_window); XSync (dpy, False); - update_input_window (s, prompt, str, cur_len); + update_input_window (s, line); XGetInputFocus (dpy, &fwin, &revert); XSetInputFocus (dpy, s->input_window, RevertToPointerRoot, CurrentTime); /* XSync (dpy, False); */ - - nbytes = read_key (&ch, &modifier, keysym_buf, keysym_bufsize); - while (ch != XK_Return) + while (1) { + int i, diff; + + nbytes = read_key (&ch, &modifier, keysym_buf, keysym_bufsize); + edit_action = get_edit_action (ch, modifier); + PRINT_DEBUG ("key %ld\n", ch); - if (ch == XK_BackSpace) - { - if (cur_len > 0) cur_len--; - update_input_window(s, prompt, str, cur_len); - } - else if (ch == INPUT_ABORT_KEY && modifier == INPUT_ABORT_MODIFIER) + + switch (edit_action) { - /* User aborted. */ - free (str); + case INPUT_FORWARD_CHAR: + if (line->position < line->length) + { + line->position++; + update_input_window (s, line); + } + else + ring_bell (s); + break; + + case INPUT_BACKWARD_CHAR: + if (line->position > 0) + { + line->position--; + update_input_window (s, line); + } + else + ring_bell (s); + break; + + case INPUT_FORWARD_WORD: + if (line->position < line->length) + { + for (; line->position < line->length && isspace (line->buffer[line->position]); line->position++); + for (; line->position < line->length && !isspace (line->buffer[line->position]); line->position++); + + update_input_window (s, line); + } + break; + + case INPUT_BACKWARD_WORD: + if (line->position > 0) + { + for (; line->position > 0 && isspace (line->buffer[line->position - 1]); line->position--); + for (; line->position > 0 && !isspace (line->buffer[line->position - 1]); line->position--); + + update_input_window (s, line); + } + break; + + case INPUT_BEGINNING_OF_LINE: + if (line->position > 0) + { + line->position = 0; + update_input_window (s, line); + } + break; + + case INPUT_END_OF_LINE: + if (line->position < line->length) + { + line->position = line->length; + update_input_window (s, line); + } + break; + + case INPUT_DELETE_CHAR: + if (line->position < line->length) + { + for (i = line->position; i < line->length; i++) + line->buffer[i] = line->buffer[i + 1]; + + line->length--; + + update_input_window (s, line); + } + else + ring_bell (s); + break; + + case INPUT_BACKWARD_DELETE_CHAR: + if (line->position > 0) + { + for (i = line->position - 1; i < line->length; i++) + line->buffer[i] = line->buffer[i + 1]; + + line->position--; + line->length--; + + update_input_window (s, line); + } + else + ring_bell (s); + break; + + case INPUT_KILL_WORD: + if (line->position < line->length) + { + for (i = line->position; i < line->length && isspace (line->buffer[i]); i++); + for (; i < line->length && !isspace (line->buffer[i]); i++); + + diff = i - line->position; + + for (i = line->position; i <= line->length - diff; i++) + line->buffer[i] = line->buffer[i + diff]; + + line->length -= diff; + + update_input_window (s, line); + } + break; + + case INPUT_BACKWARD_KILL_WORD: + if (line->position > 0) + { + for (i = line->position; i > 0 && isspace (line->buffer[i - 1]); i--); + for (; i > 0 && !isspace (line->buffer[i - 1]); i--); + + diff = line->position - i; + + line->position = i; + + for (; i <= line->length - diff; i++) + line->buffer[i] = line->buffer[i + diff]; + + line->length -= diff; + + update_input_window (s, line); + } + break; + + case INPUT_KILL_LINE: + if (line->position < line->length) + { + line->length = line->position; + line->buffer[line->length] = 0; + update_input_window (s, line); + } + break; + + case INPUT_KILL_WHOLE_LINE: + if (line->length > 0) + { + line->length = line->position = 0; + line->buffer[line->length] = 0; + update_input_window (s, line); + } + break; + + case INPUT_ABORT: + free (line->buffer); XSetInputFocus (dpy, fwin, RevertToPointerRoot, CurrentTime); XUnmapWindow (dpy, s->input_window); return NULL; - } - else - { - if (cur_len + nbytes > allocated_len - 1) + + case INPUT_NO_ACTION: + ring_bell (s); + break; + + /* + case INPUT_OPERATOR_PENDING: + break; + */ + + case INPUT_INSERT: + if (line->length + nbytes > line->size - 1) { - allocated_len += nbytes + 100; - str = xrealloc ( str, allocated_len ); + line->size += nbytes + 100; + line->buffer = xrealloc (line->buffer, line->size); } - strncpy (&str[cur_len], keysym_buf, nbytes); -/* str[cur_len] = ch; */ - cur_len+=nbytes; + for (i = line->length + nbytes; i > line->position; i--) + line->buffer[i] = line->buffer[i - nbytes]; - update_input_window(s, prompt, str, cur_len); - } + strncpy (&line->buffer[line->position], keysym_buf, nbytes); - nbytes = read_key (&ch, &modifier, keysym_buf, keysym_bufsize); - } + line->length += nbytes; + line->position += nbytes; + + update_input_window (s, line); + break; + + case INPUT_ENTER: + line->buffer[line->length] = 0; + XSetInputFocus (dpy, fwin, RevertToPointerRoot, CurrentTime); + XUnmapWindow (dpy, s->input_window); + return line->buffer; - str[cur_len] = 0; - XSetInputFocus (dpy, fwin, RevertToPointerRoot, CurrentTime); - XUnmapWindow (dpy, s->input_window); - return str; -} + } + } +} diff -Naur ratpoison/src/input.h ratpoison.line-editor/src/input.h --- ratpoison/src/input.h Thu Oct 18 17:51:59 2001 +++ ratpoison.line-editor/src/input.h Tue Nov 20 17:59:01 2001 @@ -22,6 +22,51 @@ #ifndef _RATPOISON_INPUT_H #define _RATPOISON_INPUT_H 1 +typedef struct input_line input_line; + +struct +input_line +{ + char *buffer; + char *prompt; + int position; + int length; + int size; +}; + +typedef struct edit_binding edit_binding; + +struct +edit_binding +{ + char *key; + int action; +}; + +typedef enum edit_action edit_action; + +enum +edit_action +{ + INPUT_ABORT, + INPUT_NO_ACTION, + /*INPUT_OPERATOR_PENDING,*/ + INPUT_INSERT, + INPUT_ENTER, + INPUT_FORWARD_CHAR, + INPUT_BACKWARD_CHAR, + INPUT_FORWARD_WORD, + INPUT_BACKWARD_WORD, + INPUT_BEGINNING_OF_LINE, + INPUT_END_OF_LINE, + INPUT_DELETE_CHAR, + INPUT_BACKWARD_DELETE_CHAR, + INPUT_KILL_WORD, + INPUT_BACKWARD_KILL_WORD, + INPUT_KILL_LINE, + INPUT_KILL_WHOLE_LINE +}; + char *keysym_to_string (KeySym keysym, unsigned int modifier); int cook_keycode (XKeyEvent *ev, KeySym *keysym, unsigned int *mod, char *keysym_name, int len, int ignore_bad_mods); char *get_input (char *prompt); @@ -31,5 +76,6 @@ unsigned int rp_mask_to_x11_mask (unsigned int mask); void update_modifier_map (); void grab_key (int keycode, unsigned int modifiers, Window grab_window); +/*int get_edit_action(KeySym ch, unsigned int modifier);*/ #endif /* ! _RATPOISON_INPUT_H */