On 4 April 2011 16:18, Bryan Bennett <[email protected]> wrote:
> While I understand wanting applications to adhere to the Unix
> Philosophy, it seems to me that inputting and outputting text
> is what a terminal essentially does and copying & pasting is
> just a small extension of that role. I'd like to see a sane
> way of copying & pasting with the keyboard, rather than
> relying on the mouse for that.
How about something similar to Vimperator's edit-textbox idea?
You hit a key-combo, and the terminal writes what is visible on the
screen to a temporary file, then fires up $EDITOR on said file. You
delete what you don't want, then the rest is inserted into X's
selection.
I've knocked together a quick patch, attached, along with a whitespace
patch that removes trailing whitespace. At the moment, the "key-combo"
is middle mouse.
The patch is a bit of a bodge too - it doesn't support unicode
characters properly and for the system() call to work, SIGCHLD is reset
temporarily, which could be a problem if the shell dies mid-selection
edit. I'm open to suggestions. Avoid system() entirely?
Also, I wasn't sure how to open $EDITOR in st's terminal itself, if
people are interested I'll give it a go, but for the moment it just
fires up another st to host the editor.
Cheers,
Rob.
diff -r fe61798f04a5 st.c
--- a/st.c Sun Apr 03 21:40:33 2011 +0200
+++ b/st.c Tue Apr 05 13:04:11 2011 +0100
@@ -45,7 +45,7 @@
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define LEN(a) (sizeof(a) / sizeof(a[0]))
-#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
@@ -57,7 +57,7 @@
CURSOR_SAVE, CURSOR_LOAD };
enum { CURSOR_DEFAULT = 0, CURSOR_HIDE = 1, CURSOR_WRAPNEXT = 2 };
enum { GLYPH_SET=1, GLYPH_DIRTY=2 };
-enum { MODE_WRAP=1, MODE_INSERT=2, MODE_APPKEYPAD=4, MODE_ALTSCREEN=8,
+enum { MODE_WRAP=1, MODE_INSERT=2, MODE_APPKEYPAD=4, MODE_ALTSCREEN=8,
MODE_CRLF=16 };
enum { ESC_START=1, ESC_CSI=2, ESC_OSC=4, ESC_TITLE=8, ESC_ALTCHARSET=16 };
enum { SCREEN_UPDATE, SCREEN_REDRAW };
@@ -96,7 +96,7 @@
/* Internal representation of the screen */
typedef struct {
- int row; /* nb row */
+ int row; /* nb row */
int col; /* nb col */
Line* line; /* screen */
Line* alt; /* alternate screen */
@@ -125,7 +125,7 @@
int ch; /* char height */
int cw; /* char width */
char state; /* focus, redraw, visible */
-} XWindow;
+} XWindow;
typedef struct {
KeySym k;
@@ -340,7 +340,7 @@
else if((*c1&(B7|B6|B5)) == (B7|B6) && b == 1)
return 0;
else if((*c1&(B7|B6|B5|B4)) == (B7|B6|B5) &&
- ((b == 1) ||
+ ((b == 1) ||
((b == 2) && (*c2&(B7|B6)) == B7)))
return 0;
else if((*c1&(B7|B6|B5|B4|B3)) == (B7|B6|B5|B4) &&
@@ -362,7 +362,7 @@
return 2;
else if ((c&(B7|B6|B5|B4)) == (B7|B6|B5))
return 3;
- else
+ else
return 4;
}
@@ -376,20 +376,20 @@
sel.xtarget = XA_STRING;
}
-static inline int
+static inline int
selected(int x, int y) {
if(sel.ey == y && sel.by == y) {
int bx = MIN(sel.bx, sel.ex);
int ex = MAX(sel.bx, sel.ex);
return BETWEEN(x, bx, ex);
}
- return ((sel.b.y < y&&y < sel.e.y) || (y==sel.e.y && x<=sel.e.x))
+ return ((sel.b.y < y&&y < sel.e.y) || (y==sel.e.y && x<=sel.e.x))
|| (y==sel.b.y && x>=sel.b.x && (x<=sel.e.x || sel.b.y!=sel.e.y));
}
void
getbuttoninfo(XEvent *e, int *b, int *x, int *y) {
- if(b)
+ if(b)
*b = e->xbutton.button;
*x = (e->xbutton.x - BORDER)/xw.cw;
@@ -560,7 +560,7 @@
exit(EXIT_FAILURE);
}
-void
+void
sigchld(int a) {
int stat = 0;
if(waitpid(pid, &stat, 0) < 0)
@@ -574,7 +574,7 @@
void
ttynew(void) {
int m, s;
-
+
/* seems to work fine on linux, openbsd and freebsd */
struct winsize w = {term.row, term.col, 0, 0};
if(openpty(&m, &s, NULL, NULL, &w) < 0)
@@ -613,7 +613,7 @@
void
ttyread(void) {
static char buf[BUFSIZ];
- static int buflen = 0;
+ static int buflen = 0;
char *ptr;
char s[UTF_SIZ];
int charsize; /* size of utf8 char in bytes */
@@ -669,11 +669,11 @@
void
treset(void) {
term.c = (TCursor){{
- .mode = ATTR_NULL,
- .fg = DefaultFG,
+ .mode = ATTR_NULL,
+ .fg = DefaultFG,
.bg = DefaultBG
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
-
+
term.top = 0, term.bot = term.row - 1;
term.mode = MODE_WRAP;
tclearregion(0, 0, term.col-1, term.row-1);
@@ -705,11 +705,11 @@
tscrolldown(int orig, int n) {
int i;
Line temp;
-
+
LIMIT(n, 0, term.bot-orig+1);
tclearregion(0, term.bot-n+1, term.col-1, term.bot);
-
+
for(i = term.bot; i >= orig+n; i--) {
temp = term.line[i];
term.line[i] = term.line[i-n];
@@ -722,12 +722,12 @@
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
-
+
tclearregion(0, orig, term.col-1, orig+n-1);
-
- for(i = orig; i <= term.bot-n; i++) {
+
+ for(i = orig; i <= term.bot-n; i++) {
temp = term.line[i];
- term.line[i] = term.line[i+n];
+ term.line[i] = term.line[i+n];
term.line[i+n] = temp;
}
}
@@ -750,7 +750,7 @@
escseq.narg = 0;
if(*p == '?')
escseq.priv = 1, p++;
-
+
while(p < escseq.buf+escseq.len) {
while(isdigit(*p)) {
escseq.arg[escseq.narg] *= 10;
@@ -857,22 +857,22 @@
term.c.attr.bg = DefaultBG;
break;
case 1:
- term.c.attr.mode |= ATTR_BOLD;
+ term.c.attr.mode |= ATTR_BOLD;
break;
- case 4:
+ case 4:
term.c.attr.mode |= ATTR_UNDERLINE;
break;
- case 7:
- term.c.attr.mode |= ATTR_REVERSE;
+ case 7:
+ term.c.attr.mode |= ATTR_REVERSE;
break;
- case 22:
- term.c.attr.mode &= ~ATTR_BOLD;
+ case 22:
+ term.c.attr.mode &= ~ATTR_BOLD;
break;
- case 24:
+ case 24:
term.c.attr.mode &= ~ATTR_UNDERLINE;
break;
- case 27:
- term.c.attr.mode &= ~ATTR_REVERSE;
+ case 27:
+ term.c.attr.mode &= ~ATTR_REVERSE;
break;
case 38:
if (i + 2 < l && attr[i + 1] == 5) {
@@ -883,7 +883,7 @@
fprintf(stderr, "erresc: bad fgcolor %d\n", attr[i]);
}
else
- fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
+ fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
break;
case 39:
term.c.attr.fg = DefaultFG;
@@ -897,7 +897,7 @@
fprintf(stderr, "erresc: bad bgcolor %d\n", attr[i]);
}
else
- fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
+ fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
break;
case 49:
term.c.attr.bg = DefaultBG;
@@ -911,9 +911,9 @@
term.c.attr.fg = attr[i] - 90 + 8;
else if(BETWEEN(attr[i], 100, 107))
term.c.attr.fg = attr[i] - 100 + 8;
- else
+ else
fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]), csidump();
-
+
break;
}
}
@@ -931,7 +931,7 @@
b = temp;
}
term.top = t;
- term.bot = b;
+ term.bot = b;
}
void
@@ -1156,7 +1156,7 @@
}
void
-csidump(void) {
+csidump(void) {
int i;
printf("ESC [ %s", escseq.priv ? "? " : "");
if(escseq.narg)
@@ -1189,7 +1189,7 @@
csiparse(), csihandle();
}
/* TODO: handle other OSC */
- } else if(term.esc & ESC_OSC) {
+ } else if(term.esc & ESC_OSC) {
if(ascii == ';') {
term.titlelen = 0;
term.esc = ESC_START | ESC_TITLE;
@@ -1354,7 +1354,7 @@
term.line[i] = calloc(col, sizeof(Glyph));
term.alt [i] = calloc(col, sizeof(Glyph));
}
-
+
/* update terminal size */
term.col = col, term.row = row;
/* make use of the LIMIT in tmoveto */
@@ -1513,7 +1513,7 @@
if(!(xw.dpy = XOpenDisplay(NULL)))
die("Can't open display\n");
xw.scr = XDefaultScreen(xw.dpy);
-
+
/* font */
initfonts(FONT, BOLDFONT);
@@ -1550,16 +1550,16 @@
/* input methods */
xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL);
- xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
- | XIMStatusNothing, XNClientWindow, xw.win,
+ xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
+ | XIMStatusNothing, XNClientWindow, xw.win,
XNFocusWindow, xw.win, NULL);
/* gc */
dc.gc = XCreateGC(xw.dpy, xw.win, 0, NULL);
-
+
/* white cursor, black outline */
cursor = XCreateFontCursor(xw.dpy, XC_xterm);
XDefineCursor(xw.dpy, xw.win, cursor);
- XRecolorCursor(xw.dpy, cursor,
+ XRecolorCursor(xw.dpy, cursor,
&(XColor){.red = 0xffff, .green = 0xffff, .blue = 0xffff},
&(XColor){.red = 0x0000, .green = 0x0000, .blue = 0x0000});
@@ -1595,7 +1595,7 @@
XmbDrawImageString(xw.dpy, xw.buf, base.mode & ATTR_BOLD ? dc.bfont.set : dc.font.set,
dc.gc, winx, winy, s, bytelen);
-
+
if(base.mode & ATTR_UNDERLINE)
XDrawLine(xw.dpy, xw.buf, dc.gc, winx, winy+1, winx+width-1, winy+1);
}
@@ -1606,10 +1606,10 @@
static int oldy = 0;
int sl;
Glyph g = {{' '}, ATTR_NULL, DefaultBG, DefaultCS, 0};
-
+
LIMIT(oldx, 0, term.col-1);
LIMIT(oldy, 0, term.row-1);
-
+
if(term.line[term.c.y][term.c.x].state & GLYPH_SET)
memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ);
@@ -1619,7 +1619,7 @@
xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, oldy, 1, sl);
} else
xclear(oldx, oldy, oldx, oldy);
-
+
/* draw the new one */
if(!(term.c.state & CURSOR_HIDE) && (xw.state & WIN_FOCUSED)) {
sl = utf8size(g.c);
@@ -1768,7 +1768,7 @@
meta = e->state & Mod1Mask;
shift = e->state & ShiftMask;
len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status);
-
+
/* 1. custom keys from config.h */
if((customkey = kmap(ksym)))
ttywrite(customkey, strlen(customkey));
@@ -1806,10 +1806,10 @@
void
resize(XEvent *e) {
int col, row;
-
+
if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
return;
-
+
xw.w = e->xconfigure.width;
xw.h = e->xconfigure.height;
col = (xw.w - 2*BORDER) / xw.cw;
@@ -1839,7 +1839,7 @@
}
if(FD_ISSET(cmdfd, &rfd)) {
ttyread();
- draw(SCREEN_UPDATE);
+ draw(SCREEN_UPDATE);
}
while(XPending(xw.dpy)) {
XNextEvent(xw.dpy, &ev);
@@ -1854,7 +1854,7 @@
int
main(int argc, char *argv[]) {
int i;
-
+
for(i = 1; i < argc; i++) {
switch(argv[i][0] != '-' || argv[i][2] ? -1 : argv[i][1]) {
case 't':
diff -r fe61798f04a5 st.c
--- a/st.c Sun Apr 03 21:40:33 2011 +0200
+++ b/st.c Tue Apr 05 13:01:54 2011 +0100
@@ -220,6 +220,7 @@
static inline int selected(int, int);
static void selcopy(void);
static void selpaste(void);
+static void seledit(void);
static int utf8decode(char *, long *);
static int utf8encode(long *, char *);
@@ -402,9 +403,86 @@
void
bpress(XEvent *e) {
- sel.mode = 1;
- sel.ex = sel.bx = (e->xbutton.x - BORDER)/xw.cw;
- sel.ey = sel.by = (e->xbutton.y - BORDER)/xw.ch;
+ if(e->xbutton.button == 2){
+ seledit();
+ }else{
+ sel.mode = 1;
+ sel.ex = sel.bx = (e->xbutton.x - BORDER)/xw.cw;
+ sel.ey = sel.by = (e->xbutton.y - BORDER)/xw.ch;
+ }
+}
+
+void
+seledit(void) {
+ struct stat st;
+ char fname[64], cmd[128];
+ char *env = getenv("TMPDIR");
+ char *sel = NULL;
+ int fd;
+ int x, y;
+
+ DEFAULT(env, "/tmp");
+ snprintf(fname, sizeof fname, "%s/st_edXXXXXX", env);
+
+ fd = mkstemp(fname);
+ if(fd == -1){
+ perror("mkstemp()");
+ return;
+ }
+
+ for(y = 0; y < term.row; y++) {
+ for(x = 0; x < term.col; x++)
+ if(isprint(term.line[y][x].c[0])) /* FIXME: unicode */
+ write(fd, term.line[y][x].c, utf8size(term.line[y][x].c));
+
+ write(fd, "\n", 1);
+ }
+ close(fd);
+
+ env = getenv("VISUAL");
+ DEFAULT(env, getenv("EDITOR"));
+ DEFAULT(env, "vi");
+
+ snprintf(cmd, sizeof cmd, "%s %s %s", EDIT_TERMINAL " -e", env, fname);
+
+ signal(SIGCHLD, SIG_DFL);
+ if(system(cmd) != 0){ /* FIXME: manual fork() and wait() */
+ signal(SIGCHLD, sigchld);
+ perror(cmd);
+ goto bail;
+ }
+ signal(SIGCHLD, sigchld);
+
+ fd = open(fname, O_RDONLY);
+ if(fd == -1){
+ perror(fname);
+ goto bail;
+ }
+
+ if(fstat(fd, &st) != 0){
+ perror("fstat()");
+ goto bail;
+ }
+
+ sel = malloc(st.st_size + 1);
+ if(!sel){
+ perror("malloc()");
+ goto bail;
+ }
+
+ if(read(fd, sel, st.st_size) == -1){
+ perror("read()");
+ goto bail;
+ }
+
+ sel[st.st_size] = '\0';
+ xsetsel(sel);
+ sel = NULL; /* prevent sel.clip deallocation */
+
+bail:
+ free(sel);
+ close(fd);
+ remove(fname);
}
void
diff -r fe61798f04a5 config.def.h
--- a/config.def.h Sun Apr 03 21:40:33 2011 +0200
+++ b/config.def.h Tue Apr 05 13:08:10 2011 +0100
@@ -4,6 +4,7 @@
#define BOLDFONT "-*-*-bold-r-*-*-*-120-75-75-*-60-*-*"
#define BORDER 2
#define SHELL "/bin/sh"
+#define EDIT_TERMINAL "st"
/* Terminal colors */
static const char *colorname[] = {