ref: b281eb93bd34cca00bfe31f0bb5fb4a7b4eb8c76
parent: e34b96039a780995c19333f56bdb7c13e72462e4
author: aap <[email protected]>
date: Sat Aug 31 05:01:14 EDT 2024
trying out notepad-style keyboard controls
--- a/inc.h
+++ b/inc.h
@@ -50,6 +50,7 @@
int nraw;
int posx;
+ int selflip;
};
void xinit(Text *x, Rectangle textr, Rectangle scrollr, Font *ft, Image *b, Image **cols);
@@ -102,3 +103,14 @@
Timer *timerstart(int dt);
void timerstop(Timer *t);
void timercancel(Timer *t);
+
+
+
+
+typedef struct RKeyboardctl RKeyboardctl;
+struct RKeyboardctl
+{
+ Keyboardctl;
+ int kbdfd;
+};
+RKeyboardctl *initkbd(char *file, char *kbdfile);
--- /dev/null
+++ b/kbd.c
@@ -1,0 +1,107 @@
+#include "inc.h"
+
+static void
+_ioproc(void *arg)
+{
+ int m, n, nerr;
+ char buf[1024], *e, *p;
+ Rune r;
+ RKeyboardctl *kc;
+
+ kc = arg;
+ threadsetname("kbdproc");
+ n = 0;
+ nerr = 0;
+ if(kc->kbdfd >= 0){
+ while(kc->kbdfd >= 0){
+ m = read(kc->kbdfd, buf, sizeof(buf)-1);
+ if(m <= 0){
+ yield(); /* if error is due to exiting, we'll exit here */
+ if(kc->kbdfd < 0)
+ break;
+ fprint(2, "keyboard: short read: %r\n");
+ if(m<0 || ++nerr>10)
+ threadexits("read error");
+ continue;
+ }
+ /* one read can return multiple messages, delimited by NUL
+ * split them up for sending on the channel */
+ e = buf+m;
+ e[-1] = 0;
+ e[0] = 0;
+ for(p = buf; p < e; p += strlen(p)+1)
+ chanprint(kc->c, "%s", p);
+ }
+ }else{
+ while(kc->consfd >= 0){
+ m = read(kc->consfd, buf+n, sizeof buf-n);
+ if(m <= 0){
+ yield(); /* if error is due to exiting, we'll exit here */
+ if(kc->consfd < 0)
+ break;
+ fprint(2, "keyboard: short read: %r\n");
+ if(m<0 || ++nerr>10)
+ threadexits("read error");
+ continue;
+ }
+ nerr = 0;
+ n += m;
+ while(n>0 && fullrune(buf, n)){
+ m = chartorune(&r, buf);
+ n -= m;
+ memmove(buf, buf+m, n);
+ if(chanprint(kc->c, "c%C", r) < 0)
+ break;
+ }
+ }
+ }
+ chanfree(kc->c);
+ free(kc->file);
+ free(kc);
+}
+
+RKeyboardctl*
+initkbd(char *file, char *kbdfile)
+{
+ RKeyboardctl *kc;
+ char *t;
+
+ if(file == nil)
+ file = "/dev/cons";
+ if(kbdfile == nil)
+ kbdfile = "/dev/kbd";
+
+ kc = mallocz(sizeof(RKeyboardctl), 1);
+ if(kc == nil)
+ return nil;
+ kc->file = strdup(file);
+// TODO: handle file == nil
+ kc->consfd = open(file, ORDWR|OCEXEC);
+ t = malloc(strlen(file)+16);
+ if(kc->consfd<0 || t==nil)
+ goto Error1;
+ sprint(t, "%sctl", file);
+ kc->ctlfd = open(t, OWRITE|OCEXEC);
+ if(kc->ctlfd < 0){
+ fprint(2, "initkeyboard: can't open %s: %r\n", t);
+ goto Error2;
+ }
+ if(ctlkeyboard(kc, "rawon") < 0){
+ fprint(2, "initkeyboard: can't turn on raw mode on %s: %r\n", t);
+ close(kc->ctlfd);
+ goto Error2;
+ }
+ free(t);
+ kc->kbdfd = open(kbdfile, OREAD|OCEXEC);
+ kc->c = chancreate(sizeof(char*), 20);
+ kc->pid = proccreate(_ioproc, kc, 4096);
+ return kc;
+
+Error2:
+ close(kc->consfd);
+Error1:
+ free(t);
+ free(kc->file);
+ free(kc);
+ return nil;
+}
--- a/main.c
+++ b/main.c
@@ -1,10 +1,11 @@
#include "inc.h"
#include <cursor.h>
-Keyboardctl *kbctl;
+RKeyboardctl *kbctl;
Keyboardctl *fwdkc, kbdctl2;
Mousectl *mctl;
int shiftdown; // TODO: needed?
+int ctldown;
Text text;
Image *colors[NCOL];
@@ -284,6 +285,95 @@
}
void
+xgetsel(Text *x, uint *q0, uint *q)
+{
+ if(x->selflip){
+ *q0 = x->q1;
+ *q = x->q0;
+ }else{
+ *q0 = x->q0;
+ *q = x->q1;
+ }
+}
+
+void
+xleftright(Text *x, int dir, int extend)
+{
+ uint q0, q;
+
+ xgetsel(x, &q0, &q);
+ if(dir < 0 && -dir > q)
+ q = 0;
+ else
+ q = min(q+dir, x->nr);
+ xsetselect(x, extend ? q0 : q, q);
+ xshow(x, q);
+}
+
+void
+xupdown(Text *x, int dir, int extend)
+{
+ Point p;
+ int py;
+ uint q0, q;
+
+ xgetsel(x, &q0, &q);
+
+ xshow(x, q);
+ p = frptofchar(x, q-x->org);
+ if(x->posx >= 0)
+ p.x = x->posx;
+ py = p.y;
+ p.y += dir*x->font->height;
+ if(p.y < x->Frame.r.min.y ||
+ p.y > x->Frame.r.max.y-x->font->height){
+ xscrolln(x, dir);
+ p.y = py;
+ }
+ q = x->org+frcharofpt(x, p);
+
+ xsetselect(x, extend ? q0 : q, q);
+ xshow(x, q);
+ x->posx = p.x;
+}
+
+void
+xline(Text *x, int dir, int extend)
+{
+ uint q0, q;
+
+ xgetsel(x, &q0, &q);
+
+ if(dir < 0){
+ while(q > 0 && x->r[q-1] != '\n' &&
+ q != x->qh)
+ q--;
+ }else{
+ while(q < x->nr && x->r[q] != '\n')
+ q++;
+ }
+
+ xsetselect(x, extend ? q0 : q, q);
+ xshow(x, q);
+}
+
+void
+xbegend(Text *x, int dir, int extend)
+{
+ uint q0, q;
+
+ xgetsel(x, &q0, &q);
+
+ if(dir < 0)
+ q = 0;
+ else
+ q = x->nr;
+
+ xsetselect(x, extend ? q0 : q, q);
+ xshow(x, q);
+}
+
+void
keyctl(Text *x, Rune r)
{
int nlines, n;
@@ -296,49 +386,54 @@
n = mousescrollsize(x->maxlines);
xscrolln(x, max(n, 1));
break;
- case Kdown:
- xscrolln(x, shiftdown ? 1 : nlines/3);
- break;
case Kpgdown:
- xscrolln(x, nlines*2/3);
+ if(ctldown)
+ xscrolln(x, nlines*2/3);
+ else
+ xscrolln(x, nlines*1/3);
break;
case Kscrolloneup:
n = mousescrollsize(x->maxlines);
xscrolln(x, -max(n, 1));
break;
- case Kup:
- xscrolln(x, -(shiftdown ? 1 : nlines/3));
- break;
case Kpgup:
- xscrolln(x, -nlines*2/3);
+ if(ctldown)
+ xscrolln(x, -nlines*2/3);
+ else
+ xscrolln(x, -nlines*1/3);
break;
- case Khome:
- xshow(x, 0);
+ /* Cursor movement */
+ case Kdown:
+ xupdown(x, 1, shiftdown);
break;
- case Kend:
- xshow(x, x->nr);
+ case Kup:
+ xupdown(x, -1, shiftdown);
break;
-
- /* Cursor movement */
case Kleft:
- if(x->q0 > 0)
- xplacetick(x, x->q0-1);
+ xleftright(x, -1, shiftdown);
break;
case Kright:
- if(x->q1 < x->nr)
- xplacetick(x, x->q1+1);
+ xleftright(x, 1, shiftdown);
break;
+ case Khome:
+ if(ctldown)
+ xbegend(x, -1, shiftdown);
+ else
+ xline(x, -1, shiftdown);
+ break;
+ case Kend:
+ if(ctldown)
+ xbegend(x, 1, shiftdown);
+ else
+ xline(x, 1, shiftdown);
+ break;
+
case CTRL('A'):
- while(x->q0 > 0 && x->r[x->q0-1] != '\n' &&
- x->q0 != x->qh)
- x->q0--;
- xplacetick(x, x->q0);
+ xline(x, -1, shiftdown);
break;
case CTRL('E'):
- while(x->q0 < x->nr && x->r[x->q0] != '\n')
- x->q0++;
- xplacetick(x, x->q0);
+ xline(x, 1, shiftdown);
break;
case CTRL('B'):
xplacetick(x, x->qh);
@@ -379,17 +474,42 @@
}
void
+dumpkbd(char *s)
+{
+ Rune *rs;
+ int i;
+
+ rs = runesmprint("%s", s);
+ for(i = 0; rs[i]; i++)
+ print("%C %X\n", rs[i], rs[i]);
+ print("\n");
+ free(rs);
+}
+
+void
kbthread(void*)
{
+ char *s;
Rune r;
for(;;){
- r = recvul(kbctl->c);
- if(fwdkc)
- send(fwdkc->c, &r);
- else
- keyctl(&text, r);
- flushimage(display, 1);
+ s = recvp(kbctl->c);
+//dumpkbd(s);
+ if(*s == 'k' || *s == 'K'){
+ shiftdown = utfrune(s+1, Kshift) != nil;
+ ctldown = utfrune(s+1, Kctl) != nil;
+ }
+ if(*s == 'c'){
+ chartorune(&r, s+1);
+ if(r){
+ if(fwdkc)
+ send(fwdkc->c, &r);
+ else
+ keyctl(&text, r);
+ flushimage(display, 1);
+ }
+ }
+ free(s);
}
}
@@ -414,9 +534,9 @@
if(initdraw(nil, nil, "jot") < 0)
sysfatal("initdraw: %r");
- kbctl = initkeyboard("/dev/cons");
+ kbctl = initkbd(nil, nil);
if(kbctl == nil)
- sysfatal("initkeyboard: %r");
+ sysfatal("inikeyboard: %r");
kbdctl2.c = chancreate(sizeof(Rune), 20);
mctl = initmouse("/dev/mouse", screen);
--- a/mkfile
+++ b/mkfile
@@ -4,6 +4,7 @@
OFILES=\
main.$O \
text.$O \
+ kbd.$O \
time.$O
HFILES=inc.h
--- a/text.c
+++ b/text.c
@@ -45,6 +45,49 @@
xscrdraw(x);
}
+static void
+xuntick(Text *x)
+{
+ if(!x->ticked)
+ return;
+ if(x->p0 == x->p1)
+ frtick(x, frptofchar(x, x->p0), 0);
+ else
+ frtick(x, frptofchar(x, x->selflip ? x->p0 : x->p1), 0);
+}
+
+static void
+xtick(Text *x)
+{
+ if(x->p0 == x->p1 || x->selflip)
+ frtick(x, frptofchar(x, x->p0), 1);
+ else
+ frtick(x, frptofchar(x, x->p1), 1);
+}
+
+static void
+xfrdrawsel(Text *f, Point pt, ulong p0, ulong p1, int issel)
+{
+ Image *back, *text;
+
+ xuntick(f);
+
+ if(p0 == p1){
+ frtick(f, pt, issel);
+ return;
+ }
+
+ if(issel){
+ back = f->cols[HIGH];
+ text = f->cols[HTEXT];
+ }else{
+ back = f->cols[BACK];
+ text = f->cols[TEXT];
+ }
+
+ frdrawsel0(f, pt, p0, p1, back, text);
+}
+
void
xfullredraw(Text *x)
{
@@ -51,10 +94,10 @@
xfill(x);
x->ticked = 0;
if(x->p0 > 0)
- frdrawsel(x, frptofchar(x, 0), 0, x->p0, 0);
+ xfrdrawsel(x, frptofchar(x, 0), 0, x->p0, 0);
if(x->p1 < x->nchars)
- frdrawsel(x, frptofchar(x, x->p1), x->p1, x->nchars, 0);
- frdrawsel(x, frptofchar(x, x->p0), x->p0, x->p1, 1);
+ xfrdrawsel(x, frptofchar(x, x->p1), x->p1, x->nchars, 0);
+ xfrdrawsel(x, frptofchar(x, x->p0), x->p0, x->p1, 1);
x->lastsr = ZR;
xscrdraw(x);
}
@@ -185,6 +228,14 @@
{
int p0, p1;
+ xuntick(w);
+ w->selflip = q1 < q0;
+ if(w->selflip){
+ p0 = q0;
+ q0 = q1;
+ q1 = p0;
+ }
+
w->posx = -1;
/* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
w->q0 = q0;
@@ -201,7 +252,7 @@
if(p1 > w->nchars)
p1 = w->nchars;
if(p0==w->p0 && p1==w->p1)
- return;
+ goto Return;
/* screen disagrees with desired selection */
if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
/* no overlap or too easy to bother trying */
@@ -228,6 +279,7 @@
Return:
w->p0 = p0;
w->p1 = p1;
+ xtick(w);
}
static void
@@ -237,6 +289,7 @@
Rune *r;
uint n;
+ xuntick(w);
if(org>0 && !exact){
/* org is an estimate of the char posn; find a newline */
/* don't try harder than 256 chars */
@@ -262,9 +315,14 @@
w->org = org;
xfill(w);
xscrdraw(w);
+if(w->selflip)
+ xsetselect(w, w->q1, w->q0);
+else
xsetselect(w, w->q0, w->q1);
- if(fixup && w->p1 > w->p0)
- frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
+ if(fixup && w->p1 > w->p0){
+ xfrdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
+ xtick(w);
+ }
}
@@ -481,7 +539,7 @@
endq = x->org+x->p1;
}
xscrolln(x, dl);
- xsetselect(x, min(selectq, endq), max(selectq, endq));
+ xsetselect(x, selectq, endq);
}
static void
@@ -499,7 +557,7 @@
int
iswordrune(Rune r)
{
- return isalpharune(r) || isdigitrune(r);
+ return r == '_' || isalpharune(r) || isdigitrune(r);
}
static int
@@ -666,8 +724,10 @@
selecttext = x;
selectmc = mc;
x->scroll = framescroll;
+ xuntick(x);
frselect(x, mc);
/* this is correct if the whole selection is visible */
+ x->selflip = x->p0 != x->p1 && x->p0 == frcharofpt(x, mc->xy);
q0 = x->org + x->p0;
q1 = x->org + x->p1;
/* otherwise replace one end with selectq */
@@ -675,7 +735,10 @@
q0 = selectq;
if(selectq > x->org+x->nchars)
q1 = selectq;
- xsetselect(x, q0, q1);
+ if(x->selflip)
+ xsetselect(x, q1, q0);
+ else
+ xsetselect(x, q0, q1);
/* figure out whether it was a click */
if(q0 == q1 && mc->buttons == 0){