diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/actions.c | 23 | ||||
-rw-r--r-- | src/actions.h | 3 | ||||
-rw-r--r-- | src/data.h | 6 | ||||
-rw-r--r-- | src/editor.c | 146 | ||||
-rw-r--r-- | src/events.c | 81 | ||||
-rw-r--r-- | src/globals.c | 146 | ||||
-rw-r--r-- | src/globals.h | 9 | ||||
-rw-r--r-- | src/main.c | 1 | ||||
-rw-r--r-- | src/ratpoison.h | 2 | ||||
-rw-r--r-- | src/sbuf.c | 11 | ||||
-rw-r--r-- | src/sbuf.h | 1 |
11 files changed, 311 insertions, 118 deletions
diff --git a/src/actions.c b/src/actions.c index 92e16c4..acda13e 100644 --- a/src/actions.c +++ b/src/actions.c @@ -125,14 +125,15 @@ static user_command user_commands[] = {"cprev", cmd_cprev, arg_VOID}, {"dedicate", cmd_dedicate, arg_VOID}, {"describekey", cmd_describekey, arg_STRING}, - {"focusprev", cmd_prev_frame, arg_VOID}, {"inext", cmd_inext, arg_VOID}, {"iother", cmd_iother, arg_VOID}, {"iprev", cmd_iprev, arg_VOID}, - {"prompt", cmd_prompt, arg_STRING}, + {"prompt", cmd_prompt, arg_STRING}, {"sdump", cmd_sdump, arg_VOID}, {"sfdump", cmd_sfdump, arg_VOID}, {"undo", cmd_undo, arg_STRING}, + {"putsel", cmd_putsel, arg_STRING}, + {"getsel", cmd_getsel, arg_STRING}, /*@end (tag required for genrpbindings) */ /* Commands to help debug ratpoison. */ @@ -4973,3 +4974,21 @@ cmd_dedicate (int interactive, char *data) return NULL; } +char * +cmd_putsel (int interactive, char *data) +{ + if (data == NULL) + { + message ("putsel: one argument required"); + return NULL; + } + + set_selection(data); + return NULL; +} + +char * +cmd_getsel (int interactive, char *data) +{ + return get_selection(); +} diff --git a/src/actions.h b/src/actions.h index 028edf1..e046aa9 100644 --- a/src/actions.h +++ b/src/actions.h @@ -141,9 +141,10 @@ char *cmd_prompt (int interactive, char *data); char *cmd_sdump (int interactive, char *data); char *cmd_sfdump (int interactively, char *data); char *cmd_undo (int interactive, char *data); +char *cmd_putsel (int interactive, char *data); +char *cmd_getsel (int interactive, char *data); void pop_frame_undo (rp_frame_undo *u); - rp_keymap *find_keymap (char *name); void initialize_default_keybindings (void); void keymap_free (rp_keymap *map); @@ -340,5 +340,11 @@ struct rp_frame_undo struct list_head node; }; +typedef struct rp_xselection rp_xselection; +struct rp_xselection +{ + unsigned char *text; + int len; +}; #endif /* _RATPOISON_DATA_H */ diff --git a/src/editor.c b/src/editor.c index 3a36291..6f312e0 100644 --- a/src/editor.c +++ b/src/editor.c @@ -257,6 +257,7 @@ static edit_status editor_kill_word (rp_input_line *line) { int i, diff; + if (line->position < line->length) { @@ -265,6 +266,9 @@ editor_kill_word (rp_input_line *line) diff = i - line->position; + /* Add the word to the X11 selection. */ + set_nselection (&line->buffer[line->position], diff); + for (i = line->position; i <= line->length - diff; i++) line->buffer[i] = line->buffer[i + diff]; @@ -288,6 +292,9 @@ editor_backward_kill_word (rp_input_line *line) line->position = i; + /* Add the word to the X11 selection. */ + set_nselection (&line->buffer[line->position], diff); + for (; i <= line->length - diff; i++) line->buffer[i] = line->buffer[i + diff]; @@ -302,6 +309,9 @@ editor_kill_line (rp_input_line *line) { if (line->position < line->length) { + /* Add the line to the X11 selection. */ + set_selection (&line->buffer[line->position]); + line->length = line->position; line->buffer[line->length] = 0; } @@ -309,19 +319,33 @@ editor_kill_line (rp_input_line *line) return EDIT_DELETE; } -static edit_status -editor_backward_kill_line (rp_input_line *line) +/* Do the dirty work of killing a line backwards. */ +static void +backward_kill_line (rp_input_line *line) { int i; + /* If we're at the beginning, we have nothing to do. */ if (line->position <= 0) - return EDIT_NO_OP; + return; for (i = line->position; i<= line->length; i++) line->buffer[i - line->position] = line->buffer[i]; line->length -= line->position; line->position = 0; +} + +static edit_status +editor_backward_kill_line (rp_input_line *line) +{ + if (line->position <= 0) + return EDIT_NO_OP; + + /* Add the line to the X11 selection. */ + set_nselection (line->buffer, line->position); + + backward_kill_line (line); return EDIT_DELETE; } @@ -469,118 +493,18 @@ editor_enter (rp_input_line *line) } 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, (char *)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)) + char *text; + text = get_selection(); + if (text) { - if (loops == 0) - { - PRINT_ERROR (("selection request timed out\n")); - return EDIT_NO_OP; - } - usleep (10000); - loops--; + editor_insert(line, text); + free (text); + return EDIT_INSERT; } - - PRINT_DEBUG (("SelectionNotify event\n")); - - property = ev.xselection.property; - - if (property != None) - return paste_primary_selection (line); else - return paste_cut_buffer (line); + return EDIT_NO_OP; } static edit_status @@ -604,7 +528,7 @@ editor_complete (rp_input_line *line, int direction) return EDIT_NO_OP; /* Insert the completion. */ - editor_backward_kill_line (line); + backward_kill_line (line); editor_insert (line, s); return EDIT_COMPLETE; diff --git a/src/events.c b/src/events.c index 00ebb0a..ce11485 100644 --- a/src/events.c +++ b/src/events.c @@ -24,6 +24,7 @@ #include <X11/Xutil.h> #include <X11/Xatom.h> #include <X11/keysym.h> +#include <X11/Xmd.h> /* for CARD32. */ #include <stdio.h> #include <stdlib.h> @@ -674,6 +675,76 @@ mapping_notify (XMappingEvent *ev) } } +/* This is called whan an application has requested the + selection. Copied from rxvt. */ +static void +selection_request (XSelectionRequestEvent *rq) +{ + XEvent ev; + CARD32 target_list[4]; + Atom target; + static Atom xa_targets = None; + static Atom xa_compound_text = None; + static Atom xa_text = None; + XTextProperty ct; + XICCEncodingStyle style; + char *cl[4]; + + if (xa_text == None) + xa_text = XInternAtom(dpy, "TEXT", False); + if (xa_compound_text == None) + xa_compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False); + if (xa_targets == None) + xa_targets = XInternAtom(dpy, "TARGETS", False); + + ev.xselection.type = SelectionNotify; + ev.xselection.property = None; + ev.xselection.display = rq->display; + ev.xselection.requestor = rq->requestor; + ev.xselection.selection = rq->selection; + ev.xselection.target = rq->target; + ev.xselection.time = rq->time; + + if (rq->target == xa_targets) { + target_list[0] = (CARD32) xa_targets; + target_list[1] = (CARD32) XA_STRING; + target_list[2] = (CARD32) xa_text; + target_list[3] = (CARD32) xa_compound_text; + XChangeProperty(dpy, rq->requestor, rq->property, rq->target, + (8 * sizeof(target_list[0])), PropModeReplace, + (unsigned char *)target_list, + (sizeof(target_list) / sizeof(target_list[0]))); + ev.xselection.property = rq->property; + } else if (rq->target == XA_STRING + || rq->target == xa_compound_text + || rq->target == xa_text) { + if (rq->target == XA_STRING) { + style = XStringStyle; + target = XA_STRING; + } else { + target = xa_compound_text; + style = (rq->target == xa_compound_text) ? XCompoundTextStyle + : XStdICCTextStyle; + } + cl[0] = selection.text; + XmbTextListToTextProperty(dpy, cl, 1, style, &ct); + XChangeProperty(dpy, rq->requestor, rq->property, + target, 8, PropModeReplace, + ct.value, ct.nitems); + ev.xselection.property = rq->property; + } + XSendEvent(dpy, rq->requestor, False, 0, &ev); +} + +static void +selection_clear () +{ + if (selection.text) + free (selection.text); + selection.text = NULL; + selection.len = 0; +} + /* Given an event, call the correct function to handle it. */ static void delegate_event (XEvent *ev) @@ -740,6 +811,14 @@ delegate_event (XEvent *ev) mapping_notify( &ev->xmapping ); break; + case SelectionRequest: + selection_request(&ev->xselectionrequest); + break; + + case SelectionClear: + selection_clear(); + break; + case ConfigureNotify: case MapNotify: case Expose: @@ -747,9 +826,7 @@ delegate_event (XEvent *ev) case KeyRelease: case ReparentNotify: case EnterNotify: - case SelectionRequest: case SelectionNotify: - case SelectionClear: case CirculateRequest: /* Ignore these events. */ break; diff --git a/src/globals.c b/src/globals.c index bc369ae..a76f220 100644 --- a/src/globals.c +++ b/src/globals.c @@ -20,6 +20,8 @@ #include "ratpoison.h" +#include <unistd.h> /* for usleep(). */ + int alarm_signalled = 0; int kill_signalled = 0; int hup_signalled = 0; @@ -70,6 +72,143 @@ char *rp_error_msg = NULL; /* Global frame numset */ struct numset *rp_frame_numset; +/* The X11 selection globals */ +rp_xselection selection; + +static void +x_export_selection () +{ + /* Hang the selections off screen 0's key window. */ + XSetSelectionOwner(dpy, XA_PRIMARY, screens[0].key_window, CurrentTime); + if (XGetSelectionOwner(dpy, XA_PRIMARY) != screens[0].key_window) + PRINT_ERROR(("can't get primary selection")); + XChangeProperty(dpy, screens[0].root, XA_CUT_BUFFER0, XA_STRING, 8, + PropModeReplace, selection.text, selection.len); +} + +void +set_nselection (char *txt, int len) +{ + int i; + + /* Update the selection structure */ + if (selection.text != NULL) + free(selection.text); + + /* Copy the string by hand. */ + selection.text = malloc(len+1); + selection.len = len + 1; + for (i=0; i<len; i++) + selection.text[i] = txt[i]; + selection.text[len] = 0; + + x_export_selection(); +} + +void +set_selection (char *txt) +{ + /* Update the selection structure */ + if (selection.text != NULL) + free(selection.text); + selection.text = xstrdup (txt); + selection.len = strlen (txt); + + x_export_selection(); +} + +static char * +get_cut_buffer () +{ + int nbytes; + char *data; + + PRINT_DEBUG (("trying the cut buffer\n")); + + data = XFetchBytes (dpy, &nbytes); + + if (data) + { + struct sbuf *s = sbuf_new (0); + sbuf_nconcat (s, data, nbytes); + XFree (data); + return sbuf_free_struct (s); + } + else + return NULL; +} + +/* Lifted the code from rxvt. */ +static char * +get_primary_selection() +{ + long nread; + unsigned long bytes_after; + XTextProperty ct; + struct sbuf *s = sbuf_new(0); + + for (nread = 0, bytes_after = 1; bytes_after > 0; nread += ct.nitems) { + if ((XGetWindowProperty(dpy, current_screen()->input_window, rp_selection, (nread / 4), 4096, + True, AnyPropertyType, &ct.encoding, + &ct.format, &ct.nitems, &bytes_after, + &ct.value) != Success)) { + XFree(ct.value); + sbuf_free(s); + return NULL; + } + if (ct.value == NULL) + continue; + /* Accumulate the data. FIXME: ct.value may not be NULL + terminated. */ + sbuf_nconcat (s, ct.value, ct.nitems); + XFree(ct.value); + } + return sbuf_free_struct (s); +} + +char * +get_selection () +{ + Atom property; + XEvent ev; + rp_screen *s = current_screen (); + int loops = 1000; + + /* Just insert our text, if we own the selection. */ + if (selection.text) + { + return xstrdup (selection.text); + } + else + { + /* 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); + + /* This seems like a hack. */ + while (!XCheckTypedWindowEvent (dpy, s->input_window, SelectionNotify, &ev)) + { + if (loops == 0) + { + PRINT_ERROR (("selection request timed out\n")); + return NULL; + } + usleep (10000); + loops--; + } + + PRINT_DEBUG (("SelectionNotify event\n")); + + property = ev.xselection.property; + + if (property != None) + return get_primary_selection (); + else + return get_cut_buffer (); + } +} + /* The hook dictionary globals. */ LIST_HEAD (rp_key_hook); @@ -106,3 +245,10 @@ set_window_focus (Window window) LIST_HEAD (rp_frame_undos); int rp_num_frame_undos = 0; + +void +init_globals () +{ + selection.text = NULL; + selection.len = 0; +} diff --git a/src/globals.h b/src/globals.h index 45cb54c..8adb6ff 100644 --- a/src/globals.h +++ b/src/globals.h @@ -148,4 +148,13 @@ extern struct numset *rp_frame_numset; extern struct list_head rp_frame_undos; extern int rp_num_frame_undos; + +/* Selection handling globals */ +extern rp_xselection selection; +void set_selection (char *txt); +void set_nselection (char *txt, int len); +char *get_selection (); + +void init_globals (); + #endif @@ -615,6 +615,7 @@ main (int argc, char *argv[]) set_sig_handler (SIGCHLD, chld_handler); /* Setup ratpoison's internal structures */ + init_globals (); init_defaults (); init_groups (); init_window_stuff (); diff --git a/src/ratpoison.h b/src/ratpoison.h index 89a8059..1c84bb4 100644 --- a/src/ratpoison.h +++ b/src/ratpoison.h @@ -29,7 +29,9 @@ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> +#include <string.h> #include <X11/Xlib.h> +#include <X11/Xatom.h> #include <fcntl.h> /* Some systems don't define the close-on-exec flag in fcntl.h */ @@ -69,9 +69,9 @@ sbuf_free_struct (struct sbuf *b) } char * -sbuf_concat (struct sbuf *b, const char *str) +sbuf_nconcat (struct sbuf *b, const char *str, int len) { - size_t minsz = b->len + strlen (str) + 1; + size_t minsz = b->len + len + 1; if (b->maxsz < minsz) { @@ -86,6 +86,13 @@ sbuf_concat (struct sbuf *b, const char *str) return b->data; } + +char * +sbuf_concat (struct sbuf *b, const char *str) +{ + return sbuf_nconcat (b, str, strlen (str)); +} + char * sbuf_copy (struct sbuf *b, const char *str) { @@ -39,6 +39,7 @@ struct sbuf *sbuf_new (size_t initsz); void sbuf_free (struct sbuf *b); char *sbuf_free_struct (struct sbuf *b); char *sbuf_concat (struct sbuf *b, const char *str); +char *sbuf_nconcat (struct sbuf *b, const char *str, int len); char *sbuf_copy (struct sbuf *b, const char *str); char *sbuf_clear (struct sbuf *b); char *sbuf_get (struct sbuf *b); |