summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog132
-rw-r--r--configure.in13
-rw-r--r--src/Makefile.am8
-rw-r--r--src/actions.c176
-rw-r--r--src/completions.c104
-rw-r--r--src/completions.h13
-rw-r--r--src/conf.h23
-rw-r--r--src/data.h40
-rw-r--r--src/editor.c576
-rw-r--r--src/editor.h24
-rw-r--r--src/globals.c1
-rw-r--r--src/globals.h2
-rw-r--r--src/input.c259
-rw-r--r--src/input.h7
-rw-r--r--src/linkedlist.h9
-rw-r--r--src/main.c9
-rw-r--r--src/ratpoison.h3
-rw-r--r--src/sbuf.h3
18 files changed, 1240 insertions, 162 deletions
diff --git a/ChangeLog b/ChangeLog
index 4ef9e7e..583b31b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,137 @@
+2003-05-27 Shawn Betts <sabetts@sfu.ca>
+
+ * src/editor.c (saved_command): new local global
+ (edit_binding): new typedef
+ (edit_binding): new struct
+ (edit_bindings): new local global
+ (input_line_new): new function
+ (input_line_free): likewise
+ (execute_edit_action): likewise
+ (editor_forward_char): likewise
+ (editor_backward_char): likewise
+ (editor_forward_word): likewise
+ (editor_backward_word): likewise
+ (editor_beginning_of_line): likewise
+ (editor_end_of_line): likewise
+ (editor_delete_char): likewise
+ (editor_backward_delete_char): likewise
+ (editor_kill_word): likewise
+ (editor_backward_kill_word): likewise
+ (editor_kill_line): likewise
+ (editor_backward_kill_line): likewise
+ (editor_history_previous): likewise
+ (editor_history_next): likewise
+ (editor_abort): likewise
+ (editor_no_action): likewise
+ (editor_insert): likewise
+ (editor_enter): likewise
+ (paste_cut_buffer): likewise
+ (paste_primary_selection): likewise
+ (editor_paste_selection): likewise
+ (editor_complete): likewise
+ (editor_forward_char): new prototype
+ (editor_backward_char): likewise
+ (editor_forward_word): likewise
+ (editor_backward_word): likewise
+ (editor_beginning_of_line): likewise
+ (editor_end_of_line): likewise
+ (editor_delete_char): likewise
+ (editor_backward_delete_char): likewise
+ (editor_kill_word): likewise
+ (editor_backward_kill_word): likewise
+ (editor_kill_line): likewise
+ (editor_paste_selection): likewise
+ (editor_abort): likewise
+ (editor_no_action): likewise
+ (editor_enter): likewise
+ (editor_history_previous): likewise
+ (editor_history_next): likewise
+ (editor_complete): likewise
+ (editor_backward_kill_line): likewise
+
+ * src/sbuf.h (sbuf): add node field.
+
+ * src/main.c (xrealloc): don't print debugger output
+ (init_defaults): init history_size
+ (main): initialize rp_selection
+ (main): load history
+ (clean_up): save history
+
+ * src/linkedlist.h (list_first): new macro
+
+ * src/input.h (free_history): remove prototype
+ (ring_bell): new function
+
+ * src/input.c: include unistd.h
+ (input_history): remove
+ (input_num_history_entries): likewise
+ (update_input_window): remove prompt, input, and input_len
+ arguments. add line argument.
+ (update_input_window): use line argument.
+ (ring_bell): new function
+ (get_input): take completion_fn argument. prototype and callers
+ updated.
+ (free_history): remove function
+ (get_more_input): take completion_fn argument. prototype and
+ callers updated. use line structure and its functionality.
+
+ * src/globals.h (MAX_FONT_WIDTH): new define
+ (rp_selection): new extern
+
+ * src/globals.c (rp_selection): new global
+
+ * src/completions.h (completions_new): new prototype
+ (completions_free): likewise
+ (completions_assign): likewise
+ (completions_update): likewise
+ (completions_next_completion): likewise
+
+
+ * src/completions.c (completions_new): new function
+ (completions_free): likewise
+ (completions_assign): likewise
+ (completions_update): likewise
+ (completions_next_completion): likewise
+
+ * src/Makefile.am (ratpoison_SOURCES): add editor.c editor.h
+ history.h and history.c
+
+ * src/data.h (rp_completions): new typedef
+ (rp_input_line): likewise
+ (completion_fn): likewise
+ (rp_defaults): new field, history_size
+ (rp_completions): new struct
+ (rp_input_line): new struct
+
+ * src/conf.h (MAX_HISTORY_SIZE): new define
+ (HISTORY_FILE): likewise
+ (VISUAL_BELL): likewise
+ (MODIFIER_PREFIX): set to RP_CONTROL_MASK
+ (INPUT_ABORT_MODIFIER): likewise
+ (INPUT_PREV_HISTORY_MODIFIER): likewise
+ (INPUT_NEXT_HISTORY_MODIFIER): likewise
+ (RESIZE_VGROW_MODIFIER): likewise
+ (RESIZE_VSHRINK_MODIFIER): likewise
+ (RESIZE_HGROW_MODIFIER): likewise
+ (RESIZE_HSHRINK_MODIFIER): likewise
+
+ * src/actions.c (trivial_completions): new function
+ (window_completions): likewise
+ (colon_completions): likewise
+ (exec_completions): likewise
+ (cmd_select): pass window_completions to get_input
+ (cmd_rename): pass trivial_completions to get_input
+ (cmd_colon): pass colon_completions to get_input and
+ get_more_input
+ (cmd_exec): pass exec_completions to get_input
+ (cmd_newwm): pass trivial_completions to get_input
+ (cmd_resize): convert the keysym modifier to something ratpoison
+ understands.
+
2003-05-25 Shawn Betts <sabetts@sfu.ca>
+ * src/Makefile.am (ratpoison_SOURCES): add completions.c and completions.h
+
* src/actions.c (cmd_gmerge): show a message if the group
specified by the user could not be found.
(cmd_gselect): if the user didn't type anything then return.
diff --git a/configure.in b/configure.in
index 9b83c6d..c822424 100644
--- a/configure.in
+++ b/configure.in
@@ -17,7 +17,7 @@ dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
dnl
-dnl $Id: configure.in,v 1.36 2003/05/20 07:20:14 sabetts Exp $
+dnl $Id: configure.in,v 1.37 2003/05/27 07:51:03 sabetts Exp $
AC_INIT(src/main.c)
AM_INIT_AUTOMAKE(ratpoison, 1.3.0-cvs)
@@ -63,6 +63,17 @@ if test "x$no_x" = "xyes"; then
AC_MSG_ERROR([*** Can't find X11 headers and libs])
fi
+dnl Just a hack ....
+AC_CHECK_LIB(history, add_history, [ AC_CHECK_HEADER(readline/history.h, HISTORY="yes")])
+
+
+if test "x$HISTORY" = "xyes"; then
+ LIBS="$LIBS -lhistory"
+else
+ AC_MSG_ERROR([*** Can't find History headers and libs])
+fi
+
+
LDFLAGS="$LDFLAGS $X_LDFLAGS $X_LIBS $X_EXTRA_LIBS"
CFLAGS="$CFLAGS $X_CFLAGS"
diff --git a/src/Makefile.am b/src/Makefile.am
index 457acfb..953e256 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,7 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
-## $Id: Makefile.am,v 1.17 2003/05/20 07:20:15 sabetts Exp $
+## $Id: Makefile.am,v 1.18 2003/05/27 07:51:06 sabetts Exp $
bin_PROGRAMS = ratpoison
@@ -25,10 +25,14 @@ ratpoison_SOURCES = actions.c \
actions.h \
bar.c \
bar.h \
+ completions.c \
+ completions.h \
communications.c \
communications.h \
conf.h \
data.h \
+ editor.c \
+ editor.h \
events.c \
events.h \
frame.c \
@@ -40,6 +44,8 @@ ratpoison_SOURCES = actions.c \
globals.c \
group.h \
group.c \
+ history.h \
+ history.c \
input.c \
input.h \
linkedlist.h \
diff --git a/src/actions.c b/src/actions.c
index 0b5a323..8b2a04b 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -755,6 +755,42 @@ string_to_window_number (char *str)
return *s ? -1 : i;
}
+
+struct list_head *
+trivial_completions (char* str)
+{
+ struct list_head *list;
+
+ /* Initialize our list. */
+ list = xmalloc (sizeof (struct list_head));
+ INIT_LIST_HEAD (list);
+
+ return list;
+}
+
+struct list_head *
+window_completions (char* str)
+{
+ rp_window_elem *cur;
+ struct list_head *list;
+
+ /* Initialize our list. */
+ list = xmalloc (sizeof (struct list_head));
+ INIT_LIST_HEAD (list);
+
+ /* Gather the names of all the windows. */
+ list_for_each_entry (cur, &rp_current_group->mapped_windows, node)
+ {
+ struct sbuf *name;
+
+ name = sbuf_new (0);
+ sbuf_copy (name, window_name (cur->win));
+ list_add_tail (&name->node, list);
+ }
+
+ return list;
+}
+
/* switch to window number or name */
char *
cmd_select (int interactive, char *data)
@@ -763,7 +799,7 @@ cmd_select (int interactive, char *data)
int n;
if (data == NULL)
- str = get_input (MESSAGE_PROMPT_SWITCH_TO_WINDOW);
+ str = get_input (MESSAGE_PROMPT_SWITCH_TO_WINDOW, window_completions);
else
str = xstrdup (data);
@@ -814,7 +850,7 @@ cmd_rename (int interactive, char *data)
if (current_window() == NULL) return NULL;
if (data == NULL)
- winname = get_input (MESSAGE_PROMPT_NEW_WINDOW_NAME);
+ winname = get_input (MESSAGE_PROMPT_NEW_WINDOW_NAME, trivial_completions);
else
winname = xstrdup (data);
@@ -959,6 +995,42 @@ command (int interactive, char *data)
return result;
}
+struct list_head *
+colon_completions (char* str)
+{
+ int i;
+ struct sbuf *s;
+ struct list_head *list;
+
+ /* Initialize our list. */
+ list = xmalloc (sizeof (struct list_head));
+ INIT_LIST_HEAD (list);
+
+ /* Put all the aliases in our list. */
+ for(i=0; i<alias_list_last; ++i)
+ {
+ s = sbuf_new (0);
+ sbuf_copy (s, alias_list[i].name);
+ /* The space is so when the user completes a space is
+ conveniently inserted after the command. */
+ sbuf_concat (s, " ");
+ list_add_tail (&s->node, list);
+ }
+
+ /* Put all the commands in our list. */
+ for(i=0; user_commands[i].name; ++i)
+ {
+ s = sbuf_new (0);
+ sbuf_copy (s, user_commands[i].name);
+ /* The space is so when the user completes a space is
+ conveniently inserted after the command. */
+ sbuf_concat (s, " ");
+ list_add_tail (&s->node, list);
+ }
+
+ return list;
+}
+
char *
cmd_colon (int interactive, char *data)
{
@@ -966,9 +1038,9 @@ cmd_colon (int interactive, char *data)
char *input;
if (data == NULL)
- input = get_input (MESSAGE_PROMPT_COMMAND);
+ input = get_input (MESSAGE_PROMPT_COMMAND, colon_completions);
else
- input = get_more_input (MESSAGE_PROMPT_COMMAND, data);
+ input = get_more_input (MESSAGE_PROMPT_COMMAND, data, colon_completions);
/* User aborted. */
if (input == NULL)
@@ -985,13 +1057,76 @@ cmd_colon (int interactive, char *data)
return NULL;
}
+struct list_head *
+exec_completions (char *str)
+{
+ size_t n = 256;
+ char *partial;
+ struct sbuf *line;
+ FILE *file;
+ struct list_head *head;
+ char *completion_string;
+
+ /* Initialize our list. */
+ head = xmalloc (sizeof (struct list_head));
+ INIT_LIST_HEAD (head);
+
+ /* FIXME: A Bash dependancy?? */
+ completion_string = xsprintf("bash -c \"compgen -ac %s|sort\"", str);
+ file = popen (completion_string, "r");
+ free (completion_string);
+ if (!file)
+ {
+ PRINT_ERROR (("popen failed\n"));
+ return head;
+ }
+
+ partial = (char*)xmalloc (n);
+
+ /* Read data from the file, split it into lines and store it in a
+ list. */
+ line = sbuf_new (0);
+ while (fgets (partial, n, file) != NULL)
+ {
+ /* Read a chunk from the file into our line accumulator. */
+ sbuf_concat (line, partial);
+
+ if (feof(file) || (*(sbuf_get (line) + strlen(sbuf_get (line)) - 1) == '\n'))
+ {
+ char *s;
+ struct sbuf *elem;
+
+ s = sbuf_get (line);
+
+ /* Frob the newline into */
+ if (*(s + strlen(s) - 1) == '\n')
+ *(s + strlen(s) - 1) = '\0';
+
+ /* Add our line to the list. */
+ elem = sbuf_new (0);
+ sbuf_copy (elem, s);
+ /* The space is so when the user completes a space is
+ conveniently inserted after the command. */
+ sbuf_concat (elem, " ");
+ list_add_tail (&elem->node, head);
+
+ sbuf_clear (line);
+ }
+ }
+
+ free (partial);
+ pclose (file);
+
+ return head;
+}
+
char *
cmd_exec (int interactive, char *data)
{
char *cmd;
if (data == NULL)
- cmd = get_input (MESSAGE_PROMPT_SHELL_COMMAND);
+ cmd = get_input (MESSAGE_PROMPT_SHELL_COMMAND, exec_completions);
else
cmd = xstrdup (data);
@@ -1053,7 +1188,7 @@ cmd_newwm(int interactive, char *data)
char *prog;
if (data == NULL)
- prog = get_input (MESSAGE_PROMPT_SWITCH_WM);
+ prog = get_input (MESSAGE_PROMPT_SWITCH_WM, trivial_completions);
else
prog = xstrdup (data);
@@ -1442,6 +1577,9 @@ cmd_resize (int interactive, char *data)
show_frame_message (" Resize frame ");
nbytes = read_key (&c, &mod, buffer, sizeof (buffer));
+ /* Convert the mask to be compatible with ratpoison. */
+ mod = x11_mask_to_rp_mask (mod);
+
if (c == RESIZE_VGROW_KEY && mod == RESIZE_VGROW_MODIFIER)
resize_frame_vertically (current_frame(), defaults.frame_resize_unit);
else if (c == RESIZE_VSHRINK_KEY && mod == RESIZE_VSHRINK_MODIFIER)
@@ -3193,6 +3331,30 @@ find_group (char *str)
return group;
}
+struct list_head *
+group_completions (char *str)
+{
+ struct list_head *list;
+ rp_group *cur;
+
+ /* Initialize our list. */
+ list = xmalloc (sizeof (struct list_head));
+ INIT_LIST_HEAD (list);
+
+ /* Grab all the group names. */
+ list_for_each_entry (cur, &rp_groups, node)
+ {
+ struct sbuf *s;
+
+ s = sbuf_new (0);
+ sbuf_copy (s, cur->name);
+
+ list_add_tail (&s->node, list);
+ }
+
+ return list;
+}
+
char *
cmd_gselect (int interactive, char *data)
{
@@ -3200,7 +3362,7 @@ cmd_gselect (int interactive, char *data)
rp_group *g;
if (data == NULL)
- str = get_input (MESSAGE_PROMPT_SWITCH_TO_GROUP);
+ str = get_input (MESSAGE_PROMPT_SWITCH_TO_GROUP, group_completions);
else
str = xstrdup (data);
diff --git a/src/completions.c b/src/completions.c
new file mode 100644
index 0000000..970897d
--- /dev/null
+++ b/src/completions.c
@@ -0,0 +1,104 @@
+#include <string.h>
+
+#include "ratpoison.h"
+#include "completions.h"
+
+rp_completions *
+completions_new (completion_fn list_fn)
+{
+ rp_completions *c;
+
+ c = (rp_completions *) xmalloc (sizeof(rp_completions));
+
+ INIT_LIST_HEAD (&c->completion_list);
+ c->complete_fn = list_fn;
+ c->last_match = NULL;
+ c->partial = NULL;
+ c->virgin = 1;
+
+ return c;
+}
+
+void
+completions_free (rp_completions *c)
+{
+ struct sbuf *cur;
+ struct list_head *tmp, *iter;
+
+ /* Clear our list */
+ list_for_each_safe_entry (cur, iter, tmp, &c->completion_list, node)
+ {
+ list_del (&cur->node);
+ sbuf_free (cur);
+ }
+
+ /* Free the partial string. */
+ if (c->partial)
+ free (c->partial);
+}
+
+void
+completions_assign (rp_completions *c, struct list_head *new_list)
+{
+ struct sbuf *cur;
+ struct list_head *tmp, *iter;
+
+ /* Clear our list */
+ list_for_each_safe_entry (cur, iter, tmp, &c->completion_list, node)
+ {
+ list_del (&cur->node);
+ sbuf_free (cur);
+ }
+
+ /* splice the list into completion_list. Note that we SHOULDN'T free
+ new_list, because they share the same memory. */
+ INIT_LIST_HEAD (&c->completion_list);
+ list_splice (new_list, &c->completion_list);
+
+ list_first (c->last_match, &c->completion_list, node);
+}
+
+void
+completions_update (rp_completions *c, char *partial)
+{
+ struct list_head *new_list;
+
+ new_list = c->complete_fn (partial);
+
+ c->virgin = 0;
+ if (c->partial)
+ free (c->partial);
+ c->partial = xstrdup (partial);
+
+ completions_assign (c, new_list);
+}
+
+/* Return a completed string that starts with partial. */
+char *
+completions_next_completion (rp_completions *c, char *partial)
+{
+ struct sbuf *cur;
+
+ if (c->virgin)
+ completions_update (c, partial);
+
+ if (c->last_match == NULL)
+ return NULL;
+
+ /* search forward from our last match through the list looking for
+ another match. */
+ for (cur = list_next_entry (c->last_match, &c->completion_list, node);
+ cur != c->last_match;
+ cur = list_next_entry (cur, &c->completion_list, node))
+ {
+ if (str_comp (sbuf_get (cur), c->partial, strlen (c->partial)))
+ {
+ /* We found a match so update our last_match pointer and
+ return the string. */
+ c->last_match = cur;
+ return sbuf_get (cur);
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/completions.h b/src/completions.h
new file mode 100644
index 0000000..a0a262c
--- /dev/null
+++ b/src/completions.h
@@ -0,0 +1,13 @@
+/* Function prototypes.
+ */
+
+#ifndef _RATPOISON_COMPLETIONS_H
+#define _RATPOISON_COMPLETIONS_H 1
+
+char *completions_next_completion (rp_completions *c, char *partial);
+void completions_update (rp_completions *c, char *partial);
+void completions_assign (rp_completions *c, struct list_head *new_list);
+rp_completions *completions_new (completion_fn list_fn);
+void completions_free (rp_completions *c);
+
+#endif /* ! _RATPOISON_COMPLETIONS_H */
diff --git a/src/conf.h b/src/conf.h
index 2752548..6978e8f 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -30,31 +30,31 @@
/* This is the abort key when typing input. */
#define INPUT_ABORT_KEY XK_g
-#define INPUT_ABORT_MODIFIER ControlMask
+#define INPUT_ABORT_MODIFIER RP_CONTROL_MASK
/* This is the previous history entry key when typing input. */
#define INPUT_PREV_HISTORY_KEY XK_p
-#define INPUT_PREV_HISTORY_MODIFIER ControlMask
+#define INPUT_PREV_HISTORY_MODIFIER RP_CONTROL_MASK
/* This is the next history entry key when typing input. */
#define INPUT_NEXT_HISTORY_KEY XK_n
-#define INPUT_NEXT_HISTORY_MODIFIER ControlMask
+#define INPUT_NEXT_HISTORY_MODIFIER RP_CONTROL_MASK
/* Key used to enlarge frame vertically when in resize mode. */
#define RESIZE_VGROW_KEY XK_n
-#define RESIZE_VGROW_MODIFIER ControlMask
+#define RESIZE_VGROW_MODIFIER RP_CONTROL_MASK
/* Key used to shrink frame vertically when in resize mode. */
#define RESIZE_VSHRINK_KEY XK_p
-#define RESIZE_VSHRINK_MODIFIER ControlMask
+#define RESIZE_VSHRINK_MODIFIER RP_CONTROL_MASK
/* Key used to enlarge frame horizontally when in resize mode. */
#define RESIZE_HGROW_KEY XK_f
-#define RESIZE_HGROW_MODIFIER ControlMask
+#define RESIZE_HGROW_MODIFIER RP_CONTROL_MASK
/* Key used to shrink frame horizontally when in resize mode. */
#define RESIZE_HSHRINK_KEY XK_b
-#define RESIZE_HSHRINK_MODIFIER ControlMask
+#define RESIZE_HSHRINK_MODIFIER RP_CONTROL_MASK
/* Key used to shrink frame to fit it's current window. */
#define RESIZE_SHRINK_TO_WINDOW_KEY XK_s
@@ -100,4 +100,13 @@
/* This is the name of the first group that is created. */
#define DEFAULT_GROUP_NAME "default"
+/* Maximum allowed history size */
+#define MAX_HISTORY_SIZE 100
+
+/* The default filename in which to store the history */
+#define HISTORY_FILE ".ratpoison_history"
+
+/* Use a visual bell in the input window */
+#define VISUAL_BELL 1
+
#endif /* !_ _RATPOISON_CONF_H */
diff --git a/src/data.h b/src/data.h
index ec255ab..09f9eba 100644
--- a/src/data.h
+++ b/src/data.h
@@ -36,6 +36,8 @@ typedef struct rp_frame rp_frame;
typedef struct rp_child_info rp_child_info;
typedef struct rp_group rp_group;
typedef struct rp_window_elem rp_window_elem;
+typedef struct rp_completions rp_completions;
+typedef struct rp_input_line rp_input_line;
struct rp_frame
{
@@ -212,6 +214,8 @@ struct rp_defaults
/* Pointer warping toggle. */
int warp;
+
+ int history_size;
};
/* Information about a child process. */
@@ -256,4 +260,40 @@ struct modifier_info
unsigned int scroll_lock_mask;
};
+typedef struct list_head *(*completion_fn)(char *string);
+
+struct rp_completions
+{
+ /* A pointer to the partial string that is being completed. We need
+ to store this so that the user can cycle through all possible
+ completions. */
+ char *partial;
+
+ /* A pointer to the string that was last matched string. Used to
+ keep track of where we are in the completion list. */
+ struct sbuf *last_match;
+
+ /* A list of sbuf's which are possible completions. */
+ struct list_head completion_list;
+
+ /* The function that generates the completions. */
+ completion_fn complete_fn;
+
+ /* virgin = 1 means no completions have been attempted on the input
+ string. */
+ unsigned short int virgin;
+};
+
+struct rp_input_line
+{
+ char *buffer;
+ char *prompt;
+ char *saved;
+ int position;
+ int length;
+ int size;
+ rp_completions *compl;
+ Atom selection;
+};
+
#endif /* _RATPOISON_DATA_H */
diff --git a/src/editor.c b/src/editor.c
new file mode 100644
index 0000000..f300bb3
--- /dev/null
+++ b/src/editor.c
@@ -0,0 +1,576 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include "ratpoison.h"
+
+/* bind functions */
+static edit_status editor_forward_char (rp_input_line *line);
+static edit_status editor_backward_char (rp_input_line *line);
+static edit_status editor_forward_word (rp_input_line *line);
+static edit_status editor_backward_word (rp_input_line *line);
+static edit_status editor_beginning_of_line (rp_input_line *line);
+static edit_status editor_end_of_line (rp_input_line *line);
+static edit_status editor_delete_char (rp_input_line *line);
+static edit_status editor_backward_delete_char (rp_input_line *line);
+static edit_status editor_kill_word (rp_input_line *line);
+static edit_status editor_backward_kill_word (rp_input_line *line);
+static edit_status editor_kill_line (rp_input_line *line);
+static edit_status editor_paste_selection (rp_input_line *line);
+static edit_status editor_abort (rp_input_line *line);
+static edit_status editor_no_action (rp_input_line *line);
+static edit_status editor_enter (rp_input_line *line);
+static edit_status editor_history_previous (rp_input_line *line);
+static edit_status editor_history_next (rp_input_line *line);
+static edit_status editor_complete (rp_input_line *line);
+static edit_status editor_backward_kill_line (rp_input_line *line);
+
+/* default edit action */
+edit_status editor_insert (rp_input_line *line, char *keysym_buf);
+
+
+static char *saved_command = NULL;
+
+typedef struct edit_binding edit_binding;
+
+struct edit_binding
+{
+ struct rp_key key;
+ edit_status (*func)(rp_input_line *);
+};
+
+static edit_binding edit_bindings[] =
+ { {{XK_g, RP_CONTROL_MASK}, editor_abort},
+ {{XK_Escape, 0}, editor_abort},
+ {{XK_f, RP_CONTROL_MASK}, editor_forward_char},
+ {{XK_Right, 0}, editor_forward_char},
+ {{XK_b, RP_CONTROL_MASK}, editor_backward_char},
+ {{XK_Left, 0}, editor_backward_char},
+ {{XK_f, RP_META_MASK}, editor_forward_word},
+ {{XK_b, RP_META_MASK}, editor_backward_word},
+ {{XK_a, RP_CONTROL_MASK}, editor_beginning_of_line},
+ {{XK_Home, 0}, editor_beginning_of_line},
+ {{XK_e, RP_CONTROL_MASK}, editor_end_of_line},
+ {{XK_End, 0}, editor_end_of_line},
+ {{XK_d, RP_CONTROL_MASK}, editor_delete_char},
+ {{XK_Delete, 0}, editor_delete_char},
+ {{XK_BackSpace, 0}, editor_backward_delete_char},
+ {{XK_BackSpace, RP_META_MASK}, editor_backward_kill_word},
+ {{XK_d, RP_META_MASK}, editor_kill_word},
+ {{XK_k, RP_CONTROL_MASK}, editor_kill_line},
+ {{XK_u, RP_CONTROL_MASK}, editor_backward_kill_line},
+ {{XK_y, RP_CONTROL_MASK}, editor_paste_selection},
+ {{XK_Insert, 0}, editor_paste_selection},
+ {{XK_p, RP_CONTROL_MASK}, editor_history_previous},
+ {{XK_Up, 0}, editor_history_previous},
+ {{XK_n, RP_CONTROL_MASK}, editor_history_next},
+ {{XK_Down, 0}, editor_history_next},
+ {{XK_Return, 0}, editor_enter},
+ {{XK_KP_Enter, 0}, editor_enter},
+ {{XK_Tab, 0}, editor_complete},
+ { {0, 0}, 0} };
+
+rp_input_line *
+input_line_new (char *prompt, char *preinput, completion_fn fn)
+{
+ rp_input_line *line;
+
+ line = xmalloc (sizeof (rp_input_line));
+ line->prompt = prompt;
+ line->compl = completions_new (fn);
+
+ /* Allocate some memory to start with */
+ line->size = strlen (preinput) + 100;
+ line->buffer = (char *) xmalloc (line->size);
+
+ /* load in the preinput */
+ strcpy (line->buffer, preinput);
+ line->position = line->length = strlen (preinput);
+
+ return line;
+}
+
+void
+input_line_free (rp_input_line *line)
+{
+/* completions_free (line->compl); */
+ free (line->buffer);
+ free (line);
+}
+
+edit_status
+execute_edit_action (rp_input_line *line, KeySym ch, unsigned int modifier, char *keysym_buf)
+{
+ struct edit_binding *binding = NULL;
+ int found_binding = 0;
+ edit_status status;
+
+ for (binding = edit_bindings; binding->func; binding++)
+ {
+ if (ch == binding->key.sym && modifier == binding->key.state)
+ {
+ found_binding = 1;
+ break;
+ }
+ }
+
+ if (found_binding)
+ status = binding->func (line);
+ else if (modifier)
+ status = editor_no_action (line);
+ else
+ status = editor_insert (line, keysym_buf);
+
+ return status;
+}
+
+static edit_status
+editor_forward_char (rp_input_line *line)
+{
+ if (line->position < line->length)
+ {
+ line->position++;
+ return EDIT_MOVE;
+ }
+ else
+ return EDIT_NO_OP;
+
+}
+
+static edit_status
+editor_backward_char (rp_input_line *line)
+{
+ if (line->position > 0)
+ {
+ line->position--;
+ return EDIT_MOVE;
+ }
+ else
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_forward_word (rp_input_line *line)
+{
+ if (line->position < line->length)
+ {
+ for (; line->position < line->length && !isalnum (line->buffer[line->position]); line->position++);
+ for (; line->position < line->length && isalnum (line->buffer[line->position]); line->position++);
+ }
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_backward_word (rp_input_line *line)
+{
+ if (line->position > 0)
+ {
+ for (; line->position > 0 && !isalnum (line->buffer[line->position - 1]); line->position--);
+ for (; line->position > 0 && isalnum (line->buffer[line->position - 1]); line->position--);
+ }
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_beginning_of_line (rp_input_line *line)
+{
+ if (line->position > 0)
+ line->position = 0;
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_end_of_line (rp_input_line *line)
+{
+ if (line->position < line->length)
+ line->position = line->length;
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_delete_char (rp_input_line *line)
+{
+ int i;
+
+ if (line->position < line->length)
+ {
+ for (i = line->position; i < line->length; i++)
+ line->buffer[i] = line->buffer[i + 1];
+
+ line->length--;
+ return EDIT_DELETE;
+ }
+ else
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_backward_delete_char (rp_input_line *line)
+{
+ int i;
+
+ if (line->position > 0)
+ {
+ for (i = line->position - 1; i < line->length; i++)
+ line->buffer[i] = line->buffer[i + 1];
+
+ line->position--;
+ line->length--;
+ return EDIT_DELETE;
+ }
+ else
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_kill_word (rp_input_line *line)
+{
+ int i, diff;
+
+ if (line->position < line->length)
+ {
+ for (i = line->position; i < line->length && !isalnum (line->buffer[i]); i++);
+ for (; i < line->length && isalnum (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;
+ }
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_backward_kill_word (rp_input_line *line)
+{
+ int i, diff;
+
+ if (line->position > 0)
+ {
+ for (i = line->position; i > 0 && !isalnum (line->buffer[i - 1]); i--);
+ for (; i > 0 && isalnum (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;
+ }
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_kill_line (rp_input_line *line)
+{
+ if (line->position < line->length)
+ {
+ line->length = line->position;
+ line->buffer[line->length] = 0;
+ }
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_backward_kill_line (rp_input_line *line)
+{
+ int i;
+
+ if (line->position <= 0)
+ return EDIT_NO_OP;
+
+ for (i = line->position; i<= line->length; i++)
+ line->buffer[i - line->position] = line->buffer[i];
+
+ line->length -= line->position;
+ line->position = 0;
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_history_previous (rp_input_line *line)
+{
+ char *entry = history_previous ();
+
+ if (entry)
+ {
+ if (!saved_command)
+ {
+ line->buffer[line->length] = '\0';
+ saved_command = xstrdup (line->buffer);
+ PRINT_DEBUG (("saved current command line: \'%s\'\n", saved_command));
+ }
+
+ free (line->buffer);
+ line->buffer = xstrdup (entry);
+ line->length = strlen (line->buffer);
+ line->size = line->length + 1;
+ line->position = line->length;
+ PRINT_DEBUG (("entry: \'%s\'\n", line->buffer));
+ }
+ else
+ {
+ PRINT_DEBUG (("- do nothing -"));
+ return EDIT_NO_OP;
+ }
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_history_next (rp_input_line *line)
+{
+ char *entry = history_next ();
+
+ if (entry)
+ {
+ free (line->buffer);
+ line->buffer = xstrdup (entry);
+ PRINT_DEBUG (("entry: \'%s\'\n", line->buffer));
+ }
+ else if (saved_command)
+ {
+ free (line->buffer);
+ line->buffer = saved_command;
+ saved_command = NULL;
+ PRINT_DEBUG (("restored command line: \'%s\'\n", line->buffer));
+ }
+ else
+ {
+ PRINT_DEBUG (("- do nothing -"));
+ return EDIT_NO_OP;
+ }
+
+ line->length = strlen (line->buffer);
+ line->size = line->length + 1;
+ line->position = line->length;
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_abort (rp_input_line *line)
+{
+ return EDIT_ABORT;
+}
+
+static edit_status
+editor_no_action (rp_input_line *line)
+{
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_insert (rp_input_line *line, char *keysym_buf)
+{
+ int nbytes;
+ int i;
+
+ PRINT_DEBUG (("keysym_buf: '%s'\n", keysym_buf));
+
+ nbytes = strlen (keysym_buf);
+ if (line->length + nbytes > line->size - 1)
+ {
+ line->size += nbytes + 100;
+ line->buffer = xrealloc (line->buffer, line->size);
+ }
+
+ for (i = line->length + nbytes; i > line->position; i--)
+ line->buffer[i] = line->buffer[i - nbytes];
+
+ strncpy (&line->buffer[line->position], keysym_buf, nbytes);
+
+ line->length += nbytes;
+ line->position += nbytes;
+
+ PRINT_DEBUG (("line->buffer: '%s'\n", line->buffer));
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_enter (rp_input_line *line)
+{
+ int result;
+ char *expansion;
+
+ line->buffer[line->length] = '\0';
+ result = history_expand_line (line->buffer, &expansion);
+
+ PRINT_DEBUG (("History Expansion - result: %d\n", result));
+ PRINT_DEBUG (("History Expansion - expansion: \'%s\'\n", expansion));
+
+ if (result == -1 || result == 2)
+ {
+ marked_message_printf (0, 0, " %s ", expansion);
+ free (expansion);
+ line->buffer = NULL;
+ }
+ else /* result == 0 || result == 1 */
+ {
+ history_add (expansion);
+ line->buffer = expansion;
+ }
+
+ return EDIT_DONE;
+}
+
+static edit_status
+paste_cut_buffer (rp_input_line *line)
+{
+ int nbytes;
+ char *data;
+ edit_status status;
+
+ PRINT_DEBUG (("trying the cut buffer\n"));
+
+ data = XFetchBytes (dpy, &nbytes);
+
+ if (data)
+ {
+/* status = editor_insert (line, data, nbytes); */
+ status = editor_insert (line, data);
+ XFree (data);
+ }
+ else
+ {
+ status = EDIT_NO_OP;
+ }
+
+ return status;
+}
+
+static edit_status
+paste_primary_selection (rp_input_line *line)
+{
+ Atom actual_type;
+ rp_screen *s = current_screen ();
+ int actual_format;
+ unsigned long nitems;
+ unsigned long offset;
+ unsigned long bytes_after;
+ unsigned char *data;
+
+ if (XGetWindowProperty (dpy, s->input_window, rp_selection, 0, 0, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data) == Success)
+ {
+ if (data)
+ {
+ XFree (data);
+ data = NULL;
+ }
+
+ PRINT_DEBUG (("actual_type = %ld, actual_format = %d, bytes_after = %ld\n", actual_type, actual_format, bytes_after));
+
+ if (actual_type != XA_STRING || actual_format != 8)
+ {
+ PRINT_DEBUG (("selection data is invalid\n"));
+ if (data)
+ XFree (data);
+ return EDIT_NO_OP;
+ }
+
+ offset = 0;
+
+ while (bytes_after > 0)
+ {
+ if (XGetWindowProperty (dpy, s->input_window, rp_selection, offset / 4, bytes_after / 4 + 1, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data) != Success)
+ break;
+
+ PRINT_DEBUG (("bytes_after = %ld, nitems = %ld, data = '%s'\n", bytes_after, nitems, data));
+
+ nitems *= actual_format / 8;
+ offset += nitems;
+
+/* editor_insert (line, data, nitems); */
+ editor_insert (line, data);
+
+ PRINT_DEBUG (("bytes_after = %ld, nitems = %ld, data = '%s'\n", bytes_after, nitems, data));
+
+ XFree (data);
+ }
+ }
+
+ /* notify the owner that the data has been transferred */
+ XDeleteProperty(dpy, s->input_window, rp_selection);
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_paste_selection (rp_input_line *line)
+{
+ Atom property;
+ XEvent ev;
+ rp_screen *s = current_screen ();
+ int loops = 1000;
+
+ /* be a good icccm citizen */
+ XDeleteProperty (dpy, s->input_window, rp_selection);
+ /* TODO: we shouldn't use CurrentTime here, use the time of the XKeyEvent, should we fake it? */
+ XConvertSelection (dpy, XA_PRIMARY, XA_STRING, rp_selection, s->input_window, CurrentTime);
+
+ while (!XCheckTypedWindowEvent (dpy, s->input_window, SelectionNotify, &ev))
+ {
+ if (loops == 0)
+ {
+ PRINT_ERROR (("selection request timed out\n"));
+ return EDIT_NO_OP;
+ }
+ usleep (10000);
+ loops--;
+ }
+
+ PRINT_DEBUG (("SelectionNotify event\n"));
+
+ property = ev.xselection.property;
+
+ if (property != None)
+ return paste_primary_selection (line);
+ else
+ return paste_cut_buffer (line);
+}
+
+static edit_status
+editor_complete (rp_input_line *line)
+{
+ char *tmp;
+ char *s;
+
+ /* Create our partial string that will be used for completion. It is
+ the characters up to the position of the cursor. */
+ tmp = xmalloc ((line->position + 1) * sizeof (char));
+ strncpy (tmp, line->buffer, line->position);
+ tmp[line->position] = '\0';
+
+ /* We don't need to free s because it's a string from the completion
+ list. */
+ s = completions_next_completion (line->compl, tmp);
+ free (tmp);
+
+ if (s == NULL)
+ return EDIT_NO_OP;
+
+ /* Insert the completion. */
+ editor_backward_kill_line (line);
+ editor_insert (line, s);
+
+ return EDIT_COMPLETE;
+}
diff --git a/src/editor.h b/src/editor.h
new file mode 100644
index 0000000..fdcc08b
--- /dev/null
+++ b/src/editor.h
@@ -0,0 +1,24 @@
+#ifndef _RATPOISON_EDITOR_H
+#define _RATPOISON_EDITOR_H 1
+
+typedef enum edit_status edit_status;
+
+enum
+edit_status
+{
+ EDIT_INSERT,
+ EDIT_DELETE,
+ EDIT_MOVE,
+ EDIT_COMPLETE,
+ EDIT_ABORT,
+ EDIT_DONE,
+ EDIT_NO_OP
+};
+
+/* Input line functions */
+rp_input_line *input_line_new (char *prompt, char *preinput, completion_fn fn);
+void input_line_free (rp_input_line *line);
+
+edit_status execute_edit_action (rp_input_line *line, KeySym ch, unsigned int modifier, char *keysym_buf);
+
+#endif /* ! _RATPOISON_EDITOR_H */
diff --git a/src/globals.c b/src/globals.c
index 1aef12e..319ac91 100644
--- a/src/globals.c
+++ b/src/globals.c
@@ -39,6 +39,7 @@ Atom wm_colormaps;
Atom rp_command;
Atom rp_command_request;
Atom rp_command_result;
+Atom rp_selection;
int rp_current_screen;
rp_screen *screens;
diff --git a/src/globals.h b/src/globals.h
index 4343b80..57b6df3 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -24,6 +24,7 @@
#include "data.h"
#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)
/* EMPTY is used when a frame doesn't contain a window, or a window
@@ -75,6 +76,7 @@ extern Display *dpy;
extern Atom rp_command;
extern Atom rp_command_request;
extern Atom rp_command_result;
+extern Atom rp_selection;
extern Atom wm_name;
extern Atom wm_state;
diff --git a/src/input.c b/src/input.c
index 80d17dc..31c901d 100644
--- a/src/input.c
+++ b/src/input.c
@@ -22,16 +22,13 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include "ratpoison.h"
-/* Variables to keep track of input history. */
-static char *input_history[INPUT_MAX_HISTORY];
-static int input_num_history_entries = 0;
-
/* Convert an X11 modifier mask to the rp modifier mask equivalent, as
best it can (the X server may not have a hyper key defined, for
instance). */
@@ -275,73 +272,119 @@ read_key (KeySym *keysym, unsigned int *modifiers, char *keysym_name, int len)
}
static void
-update_input_window (rp_screen *s, char *prompt, char *input, int input_len)
+update_input_window (rp_screen *s, rp_input_line *line)
{
- int prompt_width = XTextWidth (defaults.font, prompt, strlen (prompt));
- int input_width = XTextWidth (defaults.font, input, input_len);
- int width, height;
-
- width = defaults.bar_x_padding * 2 + prompt_width + input_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;
+ int height;
+
+ total_width = defaults.bar_x_padding * 2 + prompt_width + input_width + MAX_FONT_WIDTH (defaults.font);
height = (FONT_HEIGHT (defaults.font) + defaults.bar_y_padding * 2);
- 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, height), width, height);
+ bar_x (s, total_width), bar_y (s, height), total_width,
+ (FONT_HEIGHT (defaults.font) + defaults.bar_y_padding * 2));
XClearWindow (dpy, s->input_window);
XSync (dpy, False);
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_x_padding,
+ 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);
-
- /* Draw a cheap-o cursor. */
- XDrawLine (dpy, s->input_window, s->normal_gc,
- defaults.bar_x_padding + prompt_width + input_width + 2,
- defaults.bar_y_padding + 1,
- defaults.bar_x_padding + prompt_width + input_width + 2,
- defaults.bar_y_padding + FONT_HEIGHT (defaults.font) - 1);
+ defaults.bar_x_padding + prompt_width,
+ 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));
+
+ XFlush (dpy);
+ XFreeGC (dpy, lgc);
+}
+
+void
+ring_bell ()
+{
+#ifdef VISUAL_BELL
+ GC lgc;
+ XGCValues gv;
+ XWindowAttributes attr;
+ rp_screen *s = current_screen ();
+
+ 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 *
-get_input (char *prompt)
+get_input (char *prompt, completion_fn fn)
{
- return get_more_input (prompt, "");
+ return get_more_input (prompt, "", fn);
}
char *
-get_more_input (char *prompt, char *preinput)
+get_more_input (char *prompt, char *preinput,
+ completion_fn compl_fn)
{
/* Emacs 21 uses a 513 byte string to store the keysym name. */
char keysym_buf[513];
int keysym_bufsize = sizeof (keysym_buf);
int nbytes;
rp_screen *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 history_index = input_num_history_entries;
-
- /* Allocate some memory to start with. */
- str = (char *) xmalloc ( allocated_len );
+ rp_input_line *line;
+ char *final_input;
+ edit_status status;
- /* load in the preinput */
- strcpy (str, preinput);
- cur_len = strlen (preinput);
+ /* Create our line structure */
+ line = input_line_new (prompt, preinput, compl_fn);
/* We don't want to draw overtop of the program bar. */
hide_bar (s);
@@ -351,117 +394,51 @@ get_more_input (char *prompt, char *preinput)
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)
+ for (;;)
{
- 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_PREV_HISTORY_KEY
- && modifier == INPUT_PREV_HISTORY_MODIFIER)
- {
- /* Cycle through the history. */
- if (input_num_history_entries > 0)
- {
- history_index--;
- if (history_index < 0)
- {
- history_index = input_num_history_entries - 1;
- }
-
- free (str);
- str = xstrdup (input_history[history_index]);
- allocated_len = strlen (str) + 1;
- cur_len = allocated_len - 1;
-
- update_input_window (s, prompt, str, cur_len);
- }
- }
- else if (ch == INPUT_NEXT_HISTORY_KEY
- && modifier == INPUT_NEXT_HISTORY_MODIFIER)
- {
- /* Cycle through the history. */
- if (input_num_history_entries > 0)
- {
- history_index++;
- if (history_index >= input_num_history_entries)
- {
- history_index = 0;
- }
-
- free (str);
- str = xstrdup (input_history[history_index]);
- allocated_len = strlen (str) + 1;
- cur_len = allocated_len - 1;
-
- update_input_window (s, prompt, str, cur_len);
- }
- }
- else if (ch == INPUT_ABORT_KEY && modifier == INPUT_ABORT_MODIFIER)
- {
- /* User aborted. */
- free (str);
- XSetInputFocus (dpy, fwin, RevertToPointerRoot, CurrentTime);
- XUnmapWindow (dpy, s->input_window);
- return NULL;
- }
- else
- {
- if (cur_len + nbytes > allocated_len - 1)
- {
- allocated_len += nbytes + 100;
- str = xrealloc ( str, allocated_len );
- }
-
- strncpy (&str[cur_len], keysym_buf, nbytes);
-/* str[cur_len] = ch; */
- cur_len+=nbytes;
-
- update_input_window(s, prompt, str, cur_len);
- }
-
nbytes = read_key (&ch, &modifier, keysym_buf, keysym_bufsize);
+ modifier = x11_mask_to_rp_mask (modifier);
+ PRINT_DEBUG (("ch = %ld, modifier = %d, keysym_buf = %s, keysym_bufsize = %d\n",
+ ch, modifier, keysym_buf, keysym_bufsize));
+ status = execute_edit_action (line, ch, modifier, keysym_buf);
+
+ if (status == EDIT_DELETE || status == EDIT_INSERT || status == EDIT_MOVE
+ || status == EDIT_COMPLETE)
+ {
+ /* If the text changed (and we didn't just complete
+ something) then set the virgin bit. */
+ if (status != EDIT_COMPLETE)
+ line->compl->virgin = 1;
+ /* In all cases, we need to redisplay the input string. */
+ update_input_window (s, line);
+ }
+ else if (status == EDIT_NO_OP)
+ {
+ ring_bell ();
+ }
+ else if (status == EDIT_ABORT)
+ {
+ final_input = NULL;
+ break;
+ }
+ else if (status == EDIT_DONE)
+ {
+ final_input = xstrdup (line->buffer);
+ break;
+ }
}
- str[cur_len] = 0;
-
- /* Push the history entries down. */
- if (input_num_history_entries >= INPUT_MAX_HISTORY)
- {
- int i;
- free (input_history[0]);
- for (i=0; i<INPUT_MAX_HISTORY-1; i++)
- {
- input_history[i] = input_history[i+1];
- }
-
- input_num_history_entries--;
- }
-
- /* Store the string in the history. */
- input_history[input_num_history_entries] = xstrdup (str);
- input_num_history_entries++;
+ /* Clean up our line structure */
+ input_line_free (line);
XSetInputFocus (dpy, fwin, RevertToPointerRoot, CurrentTime);
XUnmapWindow (dpy, s->input_window);
- return str;
-}
-void
-free_history ()
-{
- int i;
-
- for (i=0; i<input_num_history_entries; i++)
- free (input_history[i]);
-}
+ return final_input;
+}
diff --git a/src/input.h b/src/input.h
index bc074a9..f8703e3 100644
--- a/src/input.h
+++ b/src/input.h
@@ -24,13 +24,14 @@
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);
-char *get_more_input (char *prompt, char *preinput);
+char *get_input (char *prompt, completion_fn fn);
+char *get_more_input (char *prompt, char *preinput, completion_fn fn);
int read_key (KeySym *keysym, unsigned int *modifiers, char *keysym_name, int len);
unsigned int x11_mask_to_rp_mask (unsigned int mask);
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);
-void free_history ();
+
+void ring_bell ();
#endif /* ! _RATPOISON_INPUT_H */
diff --git a/src/linkedlist.h b/src/linkedlist.h
index 1314f69..fc1b8db 100644
--- a/src/linkedlist.h
+++ b/src/linkedlist.h
@@ -171,3 +171,12 @@ void prefetch(const void *x);
prefetch(pos->member.prev))
#endif
+
+
+/* Return the first element in the list. */
+#define list_first(first, head, member) \
+{ \
+ first = list_entry((head)->next, typeof(*first), member); \
+ if (&first->member == (head)) \
+ first = NULL; \
+}
diff --git a/src/main.c b/src/main.c
index e241fa4..411fcac 100644
--- a/src/main.c
+++ b/src/main.c
@@ -79,7 +79,6 @@ xrealloc (void *ptr, size_t size)
register void *value = realloc (ptr, size);
if (value == 0)
fatal ("Virtual memory exhausted");
- PRINT_DEBUG (("realloc: %d\n", size));
return value;
}
@@ -468,6 +467,8 @@ init_defaults ()
defaults.startup_message = 1;
defaults.warp = 1;
defaults.window_list_style = STYLE_ROW;
+
+ defaults.history_size = 20;
}
int
@@ -536,6 +537,7 @@ main (int argc, char *argv[])
rp_command = XInternAtom (dpy, "RP_COMMAND", False);
rp_command_request = XInternAtom (dpy, "RP_COMMAND_REQUEST", False);
rp_command_result = XInternAtom (dpy, "RP_COMMAND_RESULT", False);
+ rp_selection = XInternAtom (dpy, "RP_SELECTION", False);
if (cmd_count > 0)
{
@@ -613,6 +615,7 @@ main (int argc, char *argv[])
init_frame_lists ();
update_modifier_map ();
initialize_default_keybindings ();
+ history_load ();
/* Scan for windows */
if (screen_arg)
@@ -766,10 +769,12 @@ clean_up ()
{
int i;
+ history_save ();
+
free_keybindings ();
free_aliases ();
free_bar ();
- free_history ();
+/* free_history (); */
free_window_stuff ();
diff --git a/src/ratpoison.h b/src/ratpoison.h
index 5646d1f..b643aab 100644
--- a/src/ratpoison.h
+++ b/src/ratpoison.h
@@ -72,6 +72,9 @@ extern XGCValues gv;
#include "frame.h"
#include "screen.h"
#include "group.h"
+#include "editor.h"
+#include "history.h"
+#include "completions.h"
void clean_up ();
rp_screen *find_screen (Window w);
diff --git a/src/sbuf.h b/src/sbuf.h
index cac77f2..cd5300f 100644
--- a/src/sbuf.h
+++ b/src/sbuf.h
@@ -30,6 +30,9 @@ sbuf
char *data;
size_t len;
size_t maxsz;
+
+ /* sbuf can exist in a list. */
+ struct list_head node;
};
struct sbuf *sbuf_new (size_t initsz);