MODE_APPCURSOR = 2048,
MODE_MOUSESGR = 4096,
MODE_8BIT = 8192,
+ MODE_BLINK = 16384,
+ MODE_FBLINK = 32768,
};
enum escape_state {
char state; /* focus, redraw, visible */
} XWindow;
+typedef struct {
+ int b;
+ uint mask;
+ char s[ESC_BUF_SIZ];
+} Mousekey;
+
typedef struct {
KeySym k;
uint mask;
static void strparse(void);
static void strreset(void);
+static int tattrset(int);
static void tclearregion(int, int, int, int);
static void tcursor(int);
static void tdeletechar(int);
static void tsetscroll(int, int);
static void tswapscreen(void);
static void tsetdirt(int, int);
+static void tsetdirtattr(int);
static void tsetmode(bool, bool, int *, int);
static void tfulldirt(void);
static void techo(char *, int);
void
selsnap(int mode, int *x, int *y, int direction) {
+ int i;
+
switch(mode) {
case SNAP_WORD:
- while(*x > 0 && *x < term.col-1
- && term.line[*y][*x + direction].c[0] != ' ') {
+ /*
+ * Snap around if the word wraps around at the end or
+ * beginning of a line.
+ */
+ for(;;) {
+ if(direction < 0 && *x <= 0) {
+ if(*y > 0 && term.line[*y - 1][term.col-1].mode
+ & ATTR_WRAP) {
+ *y -= 1;
+ *x = term.col-1;
+ } else {
+ break;
+ }
+ }
+ if(direction > 0 && *x >= term.col-1) {
+ if(*y < term.row-1 && term.line[*y][*x].mode
+ & ATTR_WRAP) {
+ *y += 1;
+ *x = 0;
+ } else {
+ break;
+ }
+ }
+
+ if(term.line[*y][*x + direction].c[0] == ' ')
+ break;
+
*x += direction;
}
break;
case SNAP_LINE:
+ /*
+ * Snap around if the the previous line or the current one
+ * has set ATTR_WRAP at its end. Then the whole next or
+ * previous line will be selected.
+ */
*x = (direction < 0) ? 0 : term.col - 1;
+ if(direction < 0 && *y > 0) {
+ for(; *y > 0; *y += direction) {
+ if(!(term.line[*y-1][term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+ }
+ } else if(direction > 0 && *y < term.row-1) {
+ for(; *y < term.row; *y += direction) {
+ if(!(term.line[*y][term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+ }
+ }
break;
default:
+ /*
+ * Select the whole line when the end of line is reached.
+ */
+ if(direction > 0) {
+ i = term.col;
+ while(--i > 0 && term.line[*y][i].c[0] == ' ')
+ /* nothing */;
+ if(i > 0 && i < *x)
+ *x = term.col - 1;
+ }
break;
}
}
void
bpress(XEvent *e) {
struct timeval now;
+ Mousekey *mk;
if(IS_SET(MODE_MOUSE)) {
mousereport(e);
- } else if(e->xbutton.button == Button1) {
+ return;
+ }
+
+ for(mk = mshortcuts; mk < mshortcuts + LEN(mshortcuts); mk++) {
+ if(e->xbutton.button == mk->b
+ && match(mk->mask, e->xbutton.state)) {
+ ttywrite(mk->s, strlen(mk->s));
+ if(IS_SET(MODE_ECHO))
+ techo(mk->s, strlen(mk->s));
+ return;
+ }
+ }
+
+ if(e->xbutton.button == Button1) {
gettimeofday(&now, NULL);
/* Clear previous selection, logically and visually. */
sel.ey = sel.by = y2row(e->xbutton.y);
/*
- * Snap handling.
- * If user clicks are fasst enough (e.g. below timeouts),
- * we ignore if his hand slipped left or down and accidentally
- * selected more; we are just snapping to whatever we're
- * snapping.
+ * If the user clicks below predefined timeouts specific
+ * snapping behaviour is exposed.
*/
if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
sel.snap = SNAP_LINE;
sel.snap = 0;
}
selsnap(sel.snap, &sel.bx, &sel.by, -1);
- selsnap(sel.snap, &sel.ex, &sel.ey, 1);
+ selsnap(sel.snap, &sel.ex, &sel.ey, +1);
sel.b.x = sel.bx;
sel.b.y = sel.by;
sel.e.x = sel.ex;
* Draw selection, unless it's regular and we don't want to
* make clicks visible
*/
- if (sel.snap != 0) {
+ if(sel.snap != 0) {
+ sel.mode++;
tsetdirt(sel.b.y, sel.e.y);
draw();
}
sel.tclick2 = sel.tclick1;
sel.tclick1 = now;
- } else if(e->xbutton.button == Button4) {
- ttywrite("\031", 1);
- } else if(e->xbutton.button == Button5) {
- ttywrite("\005", 1);
}
}
if(e->xbutton.button == Button2) {
selpaste(NULL);
} else if(e->xbutton.button == Button1) {
- sel.mode = 0;
- getbuttoninfo(e);
- term.dirty[sel.ey] = 1;
- if(sel.bx == sel.ex && sel.by == sel.ey) {
+ if(sel.mode < 2) {
sel.bx = -1;
} else {
+ getbuttoninfo(e);
selcopy();
}
+ sel.mode = 0;
+ term.dirty[sel.ey] = 1;
}
}
if(!sel.mode)
return;
+ sel.mode++;
oldey = sel.ey;
oldex = sel.ex;
oldsby = sel.b.y;
fprintf(stderr, "Couldn't set window size: %s\n", SERRNO);
}
+int
+tattrset(int attr) {
+ int i, j;
+
+ for(i = 0; i < term.row-1; i++) {
+ for(j = 0; j < term.col-1; j++) {
+ if(term.line[i][j].mode & attr)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
void
tsetdirt(int top, int bot) {
int i;
term.dirty[i] = 1;
}
+void
+tsetdirtattr(int attr) {
+ int i, j;
+
+ for(i = 0; i < term.row-1; i++) {
+ for(j = 0; j < term.col-1; j++) {
+ if(term.line[i][j].mode & attr) {
+ tsetdirt(i, i);
+ break;
+ }
+ }
+ }
+}
+
void
tfulldirt(void) {
tsetdirt(0, term.row-1);
void
tclearregion(int x1, int y1, int x2, int y2) {
- int x, y, temp, mask;
+ int x, y, temp;
if(x1 > x2)
temp = x1, x1 = x2, x2 = temp;
for(y = y1; y <= y2; y++) {
term.dirty[y] = 1;
for(x = x1; x <= x2; x++) {
- mask = selected(x, y) ? ATTR_REVERSE : 0;
+ if(selected(x, y))
+ selclear(NULL);
term.line[y][x] = term.c.attr;
- term.line[y][x].mode ^= mask;
memcpy(term.line[y][x].c, " ", 2);
}
}
bg = temp;
}
+ if(base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
+ fg = bg;
+
/* Intelligent cleaning up of the borders. */
if(x == 0) {
xclear(0, (y == 0)? 0 : winy, borderpx,
run(void) {
XEvent ev;
fd_set rfd;
- int xfd = XConnectionNumber(xw.dpy), xev;
- struct timeval drawtimeout, *tv = NULL, now, last;
+ int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
+ struct timeval drawtimeout, *tv = NULL, now, last, lastblink;
+ gettimeofday(&lastblink, NULL);
gettimeofday(&last, NULL);
for(xev = actionfps;;) {
FD_ZERO(&rfd);
FD_SET(cmdfd, &rfd);
FD_SET(xfd, &rfd);
- if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) {
+
+ switch(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) {
+ case -1:
if(errno == EINTR)
continue;
die("select failed: %s\n", SERRNO);
- }
+ default:
+ if(FD_ISSET(cmdfd, &rfd)) {
+ ttyread();
+ if(blinktimeout) {
+ blinkset = tattrset(ATTR_BLINK);
+ if(!blinkset && term.mode & ATTR_BLINK)
+ term.mode &= ~(MODE_BLINK);
+ }
+ }
+ if(FD_ISSET(xfd, &rfd))
+ xev = actionfps;
+ break;
+ }
gettimeofday(&now, NULL);
drawtimeout.tv_sec = 0;
drawtimeout.tv_usec = (1000/xfps) * 1000;
tv = &drawtimeout;
- if(FD_ISSET(cmdfd, &rfd))
- ttyread();
-
- if(FD_ISSET(xfd, &rfd))
- xev = actionfps;
+ dodraw = 0;
+ if(blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
+ tsetdirtattr(ATTR_BLINK);
+ term.mode ^= MODE_BLINK;
+ gettimeofday(&lastblink, NULL);
+ dodraw = 1;
+ }
+ if(TIMEDIFF(now, last) \
+ > (xev? (1000/xfps) : (1000/actionfps))) {
+ dodraw = 1;
+ last = now;
+ }
- if(TIMEDIFF(now, last) > \
- (xev ? (1000/xfps) : (1000/actionfps))) {
+ if(dodraw) {
while(XPending(xw.dpy)) {
XNextEvent(xw.dpy, &ev);
if(XFilterEvent(&ev, None))
draw();
XFlush(xw.dpy);
- last = now;
if(xev && !FD_ISSET(xfd, &rfd))
xev--;
- if(!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd))
- tv = NULL;
+ if(!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
+ if(blinkset) {
+ if(TIMEDIFF(now, lastblink) \
+ > blinktimeout) {
+ drawtimeout.tv_usec = 1;
+ } else {
+ drawtimeout.tv_usec = (1000 * \
+ (blinktimeout - \
+ TIMEDIFF(now,
+ lastblink)));
+ }
+ } else {
+ tv = NULL;
+ }
+ }
}
}
}