summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <koverstreet@google.com>2013-03-18 07:19:06 -0700
committerKent Overstreet <koverstreet@google.com>2013-04-23 16:37:36 -0700
commit4056c980881d2c5aa456eed6cb7a775b1ba8a237 (patch)
tree1436edf136893912918508b2060469e569f256cb
parentff24d7e418ac11629c86b7d8f17145aab75c08ff (diff)
fix selections
-rw-r--r--st.c198
-rw-r--r--term.c201
-rw-r--r--term.h29
3 files changed, 208 insertions, 220 deletions
diff --git a/st.c b/st.c
index 6f9d279..a11cbd9 100644
--- a/st.c
+++ b/st.c
@@ -123,8 +123,13 @@ struct st_window {
struct coord winsize;
struct coord fixedsize; /* kill? */
struct coord charsize;
+
struct coord mousepos;
unsigned mousebutton;
+ struct timeval mousedown;
+ struct timeval mouseup[3];
+
+ unsigned mousemotion:1;
unsigned visible:1;
unsigned focused:1;
};
@@ -269,12 +274,7 @@ static void clippaste(struct st_window *xw, const union st_arg *dummy)
static void selclear(struct st_window *xw, XEvent *e)
{
- struct st_selection *sel = &xw->term.sel;
-
- if (sel->bx == -1)
- return;
- sel->bx = -1;
- tsetdirt(&xw->term, sel->b.y, sel->e.y);
+ term_sel_start(&xw->term, SEL_NONE, ORIGIN);
}
static void selrequest(struct st_window *xw, XEvent *e)
@@ -576,9 +576,7 @@ static struct st_glyph sel_glyph(struct st_window *xw, unsigned x, unsigned y)
{
struct st_glyph ret = xw->term.line[y][x];
- if (xw->term.sel.bx != -1 &&
- xw->term.sel.alt == xw->term.altscreen &&
- term_selected(&xw->term.sel, x, y))
+ if (term_selected(&xw->term.sel, x, y))
ret.reverse ^= 1;
return ret;
@@ -717,55 +715,21 @@ static void kpress(struct st_window *xw, XEvent *ev)
/* Mouse code */
-static int x2col(struct st_window *xw, unsigned x)
-{
- x -= borderpx;
- x /= xw->charsize.x;
-
- return min(x, xw->term.size.x - 1);
-}
-
-static int y2row(struct st_window *xw, unsigned y)
-{
- y -= borderpx;
- y /= xw->charsize.y;
-
- return min(y, xw->term.size.y - 1);
-}
-
-static void getbuttoninfo(struct st_window *xw, XEvent *ev)
+static struct coord mouse_pos(struct st_window *xw, XEvent *ev)
{
- int type;
- unsigned state = ev->xbutton.state & ~Button1Mask;
- struct st_selection *sel = &xw->term.sel;
-
- sel->alt = xw->term.altscreen;
-
- sel->ex = x2col(xw, ev->xbutton.x);
- sel->ey = y2row(xw, ev->xbutton.y);
-
- sel->b.x = sel->by < sel->ey ? sel->bx : sel->ex;
- sel->b.y = min(sel->by, sel->ey);
- sel->e.x = sel->by < sel->ey ? sel->ex : sel->bx;
- sel->e.y = max(sel->by, sel->ey);
-
- sel->type = SEL_REGULAR;
- for (type = 1; type < ARRAY_SIZE(selmasks); ++type) {
- if (match(selmasks[type], state)) {
- sel->type = type;
- break;
- }
- }
+ return (struct coord) {
+ .x = min((ev->xbutton.x - borderpx) / xw->charsize.x,
+ xw->term.size.x - 1),
+ .y = min((ev->xbutton.y - borderpx) / xw->charsize.y,
+ xw->term.size.y - 1),
+ };
}
static void mousereport(struct st_window *xw, XEvent *ev)
{
- int button = ev->xbutton.button, state = ev->xbutton.state, len;
char buf[40];
- struct coord pos = {
- x2col(xw, ev->xbutton.x),
- y2row(xw, ev->xbutton.y)
- };
+ int button = ev->xbutton.button, state = ev->xbutton.state, len;
+ struct coord pos = mouse_pos(xw, ev);
/* from urxvt */
if (ev->xbutton.type == MotionNotify) {
@@ -790,22 +754,20 @@ static void mousereport(struct st_window *xw, XEvent *ev)
}
}
- button += (state & ShiftMask ? 4 : 0)
- + (state & Mod4Mask ? 8 : 0)
- + (state & ControlMask ? 16 : 0);
+ button += (state & ShiftMask ? 4 : 0) +
+ (state & Mod4Mask ? 8 : 0) +
+ (state & ControlMask ? 16 : 0);
- len = 0;
- if (xw->term.mousesgr) {
+ if (xw->term.mousesgr)
len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
button, pos.x + 1, pos.y + 1,
ev->xbutton.type ==
ButtonRelease ? 'm' : 'M');
- } else if (pos.x < 223 && pos.y < 223) {
+ else if (pos.x < 223 && pos.y < 223)
len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
32 + button, 32 + pos.x + 1, 32 + pos.y + 1);
- } else {
+ else
return;
- }
ttywrite(&xw->term, buf, len);
}
@@ -813,20 +775,22 @@ static void mousereport(struct st_window *xw, XEvent *ev)
static void bpress(struct st_window *xw, XEvent *ev)
{
struct st_term *term = &xw->term;
- struct st_selection *sel = &xw->term.sel;
if (term->mousebtn || term->mousemotion) {
mousereport(xw, ev);
} else if (ev->xbutton.button == Button1) {
- if (sel->bx != -1) {
- sel->bx = -1;
- xw->term.dirty[sel->b.y] = 1;
- tfulldirt(&xw->term); // XXX
- }
- sel->mode = 1;
- sel->type = SEL_REGULAR;
- sel->ex = sel->bx = x2col(xw, ev->xbutton.x);
- sel->ey = sel->by = y2row(xw, ev->xbutton.y);
+ unsigned type = SEL_REGULAR;
+ unsigned state = ev->xbutton.state & ~Button1Mask;
+
+ for (unsigned i = 1; i < ARRAY_SIZE(selmasks); i++)
+ if (match(selmasks[i], state)) {
+ type = i;
+ break;
+ }
+
+ xw->mousemotion = 1;
+ term_sel_start(term, type, mouse_pos(xw, ev));
+ gettimeofday(&xw->mousedown, NULL);
} else if (ev->xbutton.button == Button4) {
ttywrite(term, "\031", 1);
} else if (ev->xbutton.button == Button5) {
@@ -834,84 +798,72 @@ static void bpress(struct st_window *xw, XEvent *ev)
}
}
-static void brelease(struct st_window *xw, XEvent *e)
+static bool isword(unsigned c)
+{
+ return c && !isspace(c);
+}
+
+static void brelease(struct st_window *xw, XEvent *ev)
{
struct st_term *term = &xw->term;
struct st_selection *sel = &term->sel;
- struct timeval now;
if (term->mousebtn || term->mousemotion) {
- mousereport(xw, e);
+ mousereport(xw, ev);
return;
}
- if (e->xbutton.button == Button2) {
+ if (ev->xbutton.button == Button2) {
selpaste(xw, NULL);
- } else if (e->xbutton.button == Button1) {
- sel->mode = 0;
- getbuttoninfo(xw, e);
- term->dirty[sel->ey] = 1;
- if (sel->bx == sel->ex && sel->by == sel->ey) {
- sel->bx = -1;
- gettimeofday(&now, NULL);
-
- if (TIMEDIFF(now, sel->tclick2) <=
+ } else if (ev->xbutton.button == Button1) {
+ struct coord end = mouse_pos(xw, ev);
+
+ memmove(xw->mouseup + 1,
+ xw->mouseup, sizeof(struct timeval) * 2);
+ gettimeofday(xw->mouseup, NULL);
+
+ if (sel->start.x == end.x &&
+ sel->start.y == end.y) {
+ if (TIMEDIFF(xw->mouseup[0], xw->mouseup[2]) <
tripleclicktimeout) {
/* triple click on the line */
- sel->b.x = sel->bx = 0;
- sel->e.x = sel->ex = term->size.x;
- sel->b.y = sel->e.y = sel->ey;
- term_selcopy(term);
- xsetsel(xw);
- } else if (TIMEDIFF(now, sel->tclick1) <=
+ sel->start.x = 0;
+ end.x = term->size.x - 1;
+ } else if (TIMEDIFF(xw->mouseup[0], xw->mouseup[1]) <
doubleclicktimeout) {
/* double click to select word */
- sel->bx = sel->ex;
- while (sel->bx > 0 &&
- isalpha(term->line[sel->ey][sel->bx - 1].c))
- sel->bx--;
- sel->b.x = sel->bx;
- while (sel->ex < term->size.x - 1 &&
- isalpha(term->line[sel->ey][sel->ex + 1].c))
- sel->ex++;
- sel->e.x = sel->ex;
- sel->b.y = sel->e.y = sel->ey;
- term_selcopy(term);
- xsetsel(xw);
+ while (sel->start.x &&
+ isword(term_pos(term, sel->start)[-1].c))
+ sel->start.x--;
+
+ while (end.x < term->size.x - 1 &&
+ isword(term_pos(term, end)[1].c))
+ end.x++;
+ } else if (TIMEDIFF(xw->mouseup[0], xw->mousedown) <
+ doubleclicktimeout) {
+ sel->type = SEL_NONE;
}
- } else {
- term_selcopy(term);
- xsetsel(xw);
}
- }
- memcpy(&sel->tclick2, &sel->tclick1, sizeof(struct timeval));
- gettimeofday(&sel->tclick1, NULL);
+ xw->mousemotion = 0;
+ term_sel_end(term, end);
+ term_sel_copy(term);
+ if (sel->clip)
+ xsetsel(xw);
+ }
}
-static void bmotion(struct st_window *xw, XEvent *e)
+static void bmotion(struct st_window *xw, XEvent *ev)
{
struct st_term *term = &xw->term;
- int oldey, oldex, oldsby, oldsey;
if (term->mousebtn || term->mousemotion) {
- mousereport(xw, e);
+ mousereport(xw, ev);
return;
}
- if (!term->sel.mode)
- return;
-
- oldey = term->sel.ey;
- oldex = term->sel.ex;
- oldsby = term->sel.b.y;
- oldsey = term->sel.e.y;
- getbuttoninfo(xw, e);
-
- if (oldey != term->sel.ey || oldex != term->sel.ex)
- tsetdirt(term,
- min(term->sel.b.y, oldsby),
- max(term->sel.e.y, oldsey));
+ if (xw->mousemotion)
+ term_sel_end(term, mouse_pos(xw, ev));
}
/* Resizing code */
diff --git a/term.c b/term.c
index b17443e..5cfe863 100644
--- a/term.c
+++ b/term.c
@@ -24,110 +24,151 @@
static void selscroll(struct st_term *term, int orig, int n)
{
- if (term->sel.bx == -1)
+ struct st_selection *sel = &term->sel;
+
+ if (sel->type == SEL_NONE)
return;
- if (BETWEEN(term->sel.by, orig, term->bot)
- || BETWEEN(term->sel.ey, orig, term->bot)) {
- if ((term->sel.by += n) > term->bot ||
- (term->sel.ey += n) < term->top) {
- term->sel.bx = -1;
+ if (BETWEEN(sel->start.y, orig, term->bot) ||
+ BETWEEN(sel->end.y, orig, term->bot)) {
+ if ((sel->start.y += n) > term->bot ||
+ (sel->end.y += n) < term->top) {
+ sel->type = SEL_NONE;
return;
}
- switch (term->sel.type) {
+ switch (sel->type) {
+ case SEL_NONE:
+ break;
case SEL_REGULAR:
- if (term->sel.by < term->top) {
- term->sel.by = term->top;
- term->sel.bx = 0;
+ if (sel->start.y < term->top) {
+ sel->start.y = term->top;
+ sel->start.x = 0;
}
- if (term->sel.ey > term->bot) {
- term->sel.ey = term->bot;
- term->sel.ex = term->size.y;
+ if (sel->end.y > term->bot) {
+ sel->end.y = term->bot;
+ sel->end.x = term->size.y;
}
break;
case SEL_RECTANGULAR:
- if (term->sel.by < term->top)
- term->sel.by = term->top;
- if (term->sel.ey > term->bot)
- term->sel.ey = term->bot;
+ if (sel->start.y < term->top)
+ sel->start.y = term->top;
+ if (sel->end.y > term->bot)
+ sel->end.y = term->bot;
break;
};
- term->sel.b.y = term->sel.by, term->sel.b.x = term->sel.bx;
- term->sel.e.y = term->sel.ey, term->sel.e.x = term->sel.ex;
+
+ sel->p1 = sel->start;
+ sel->p2 = sel->end;
}
}
bool term_selected(struct st_selection *sel, int x, int y)
{
- int bx, ex;
+ switch (sel->type) {
+ case SEL_NONE:
+ return false;
+ case SEL_REGULAR:
+ if (y < sel->p1.y || y > sel->p2.y)
+ return false;
- if (sel->ey == y && sel->by == y) {
- bx = min(sel->bx, sel->ex);
- ex = max(sel->bx, sel->ex);
- return BETWEEN(x, bx, ex);
- }
+ if (y == sel->p1.y && x < sel->p1.x)
+ return false;
- 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));
+ if (y == sel->p2.y && x > sel->p2.x)
+ return false;
- switch (sel->type) {
- case SEL_REGULAR:
- 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));
+ return true;
case SEL_RECTANGULAR:
- return ((sel->b.y <= y && y <= sel->e.y)
- && (sel->b.x <= x && x <= sel->e.x));
- };
+ return sel->p1.y <= y && y <= sel->p2.y &&
+ sel->p1.x <= x && x <= sel->p2.x;
+ }
+
+ return false;
}
-void term_selcopy(struct st_term *term)
+void term_sel_copy(struct st_term *term)
{
- unsigned char *str, *ptr;
- int x, y, bufsize, is_selected = 0;
- struct st_glyph *gp, *last;
+ struct st_selection *sel = &term->sel;
+ unsigned char *str = NULL, *ptr;
- if (term->sel.bx == -1) {
- str = NULL;
- } else {
- bufsize = (term->size.y + 1) *
- (term->sel.e.y - term->sel.b.y + 1) * UTF_SIZ;
- ptr = str = xmalloc(bufsize);
-
- /* append every set & selected glyph to the selection */
- for (y = term->sel.b.y; y < term->sel.e.y + 1; y++) {
- is_selected = 0;
- gp = &term->line[y][0];
- last = gp + term->size.y;
-
- while (--last >= gp && !last->c)
- /* nothing */ ;
-
- for (x = 0; gp <= last; x++, ++gp) {
- if (!term_selected(&term->sel, x, y)) {
- continue;
- } else {
- is_selected = 1;
- }
+ if (sel->type == SEL_NONE)
+ goto out;
- if (gp->c)
- ptr += FcUcs4ToUtf8(gp->c, ptr);
- else
- *(ptr++) = ' ';
- }
- /* \n at the end of every selected line except for the last one */
- if (is_selected && y < term->sel.e.y)
- *ptr++ = '\r';
+ ptr = str = xmalloc((sel->p2.y - sel->p1.y + 1) *
+ term->size.y * UTF_SIZ);
+
+ /* append every set & selected glyph to the selection */
+ for (unsigned y = sel->p1.y; y <= sel->p2.y; y++) {
+ struct st_glyph *gp = &term->line[y][0];
+ struct st_glyph *last = &term->line[y][term->size.x - 1];
+
+ if (sel->type == SEL_RECTANGULAR ||
+ y == sel->p1.y)
+ gp = &term->line[y][sel->p1.x];
+
+ if (sel->type == SEL_RECTANGULAR ||
+ y == sel->p2.y)
+ last = &term->line[y][sel->p2.x];
+
+ while (last > gp && !last->c)
+ last--;
+
+ for (; gp <= last; gp++) {
+ int ret = FcUcs4ToUtf8(gp->c, ptr);
+ if (ret > 0)
+ ptr += ret;
+ else
+ *ptr++ = ' ';
}
- *ptr = 0;
+
+ /* \n at the end of every selected line except for the last one */
+ if (y < sel->p2.y)
+ *ptr++ = '\r';
+ }
+ *ptr = 0;
+out:
+ free(sel->clip);
+ sel->clip = (char *) str;
+}
+
+void term_sel_start(struct st_term *term, unsigned type, struct coord start)
+{
+ struct st_selection *sel = &term->sel;
+
+ sel->type = type;
+ sel->start = sel->end = sel->p1 = sel->p2 = start;
+
+ tfulldirt(term);
+}
+
+void term_sel_end(struct st_term *term, struct coord end)
+{
+ struct st_selection *sel = &term->sel;
+
+ sel->p1 = sel->start;
+ sel->p2 = sel->end = end;
+
+ switch (sel->type) {
+ case SEL_NONE:
+ break;
+ case SEL_REGULAR:
+ if (sel->p1.y > sel->p2.y ||
+ (sel->p1.y == sel->p2.y &&
+ sel->p1.x > sel->p2.x))
+ swap(sel->p1, sel->p2);
+
+ break;
+ case SEL_RECTANGULAR:
+ if (sel->p1.x > sel->p2.x)
+ swap(sel->p1.x, sel->p2.x);
+ if (sel->p1.y > sel->p2.y)
+ swap(sel->p1.y, sel->p2.y);
+
+ break;
}
- free(term->sel.clip);
- term->sel.clip = (char *) str;
+ tfulldirt(term);
}
/* Escape handling */
@@ -580,6 +621,7 @@ static void tsetscroll(struct st_term *term, unsigned t, unsigned b)
static void tswapscreen(struct st_term *term)
{
swap(term->line, term->alt);
+ term->sel.type = SEL_NONE;
term->altscreen ^= 1;
tfulldirt(term);
}
@@ -763,7 +805,7 @@ static void csihandle(struct st_term *term)
tputtab(term, 1);
break;
case 'J': /* ED -- Clear screen */
- term->sel.bx = -1;
+ term->sel.type = SEL_NONE;
switch (csi->arg[0]) {
case 0: /* below */
tclearregion(term, term->c.pos, term->size);
@@ -1194,9 +1236,9 @@ static void tputc(struct st_term *term, unsigned c)
if (control && !term->c.attr.gfx)
return;
- if (term->sel.bx != -1 &&
- BETWEEN(term->c.pos.y, term->sel.by, term->sel.ey))
- term->sel.bx = -1;
+ if (term->sel.type != SEL_NONE &&
+ BETWEEN(term->c.pos.y, term->sel.p1.y, term->sel.p2.y))
+ term->sel.type = SEL_NONE;
if (term->wrap && term->c.wrapnext)
tnewline(term, 1); /* always go to first col */
@@ -1499,7 +1541,6 @@ void term_init(struct st_term *term, int col, int row, char *shell,
term->dirty[row] = 0;
}
- term->sel.bx = -1;
term->numlock = 1;
memset(term->tabs, 0, term->size.x * sizeof(*term->tabs));
/* setup screen */
diff --git a/term.h b/term.h
index 95f7d8d..f1f1d27 100644
--- a/term.h
+++ b/term.h
@@ -78,11 +78,6 @@ enum escape_state {
ESC_TEST = 32, /* Enter in test mode */
};
-enum selection_type {
- SEL_REGULAR = 1,
- SEL_RECTANGULAR = 2
-};
-
struct st_glyph {
unsigned c; /* character code */
union {
@@ -134,19 +129,17 @@ struct str_escape {
int narg; /* nb of args */
};
-/* TODO: use better name for vars... */
struct st_selection {
- int mode;
- int type;
- int bx, by;
- int ex, ey;
- struct {
- int x, y;
- } b, e;
+ enum {
+ SEL_NONE,
+ SEL_REGULAR,
+ SEL_RECTANGULAR,
+ } type;
+
+ struct coord start, end;
+ struct coord p1, p2;
+
char *clip;
- bool alt;
- struct timeval tclick1;
- struct timeval tclick2;
};
/* Internal representation of the screen */
@@ -202,7 +195,9 @@ struct st_term {
};
bool term_selected(struct st_selection *sel, int x, int y);
-void term_selcopy(struct st_term *term);
+void term_sel_copy(struct st_term *term);
+void term_sel_start(struct st_term *term, unsigned type, struct coord start);
+void term_sel_end(struct st_term *term, struct coord end);
void term_echo(struct st_term *term, char *buf, int len);
void term_read(struct st_term *term);