diff --git a/man/man1/0intro.1 b/man/man1/0intro.1 index 8c520d9d00..ad851a5f41 100644 --- a/man/man1/0intro.1 +++ b/man/man1/0intro.1 @@ -29,7 +29,7 @@ conventionally When programs need to access files in the tree, they expect the .B $PLAN9 -environment variable +environment variable to contain the name of the root of the tree. See .MR install (1) @@ -57,7 +57,7 @@ expect Plan 9 regular expressions (see .MR regexp (7) ), which are closest to what Unix calls extended regular expressions. -Because of these differences, it is not recommended to put +Because of these differences, it is not recommended to put .B $PLAN9/bin before the usual system .B bin @@ -99,6 +99,11 @@ The argument is one of \fL'\fIxmin ymin xmax ymax\fL'\fR, \fRor \fIxmin\fL,\fIymin\fL,\fIxmax\fL,\fIymax\fR. +See +.MR devdraw (1) +and +.MR keyboard (7) +for details about typing and clicking in graphical applications. .PP The .MR plumber (4) @@ -135,7 +140,7 @@ is an experimental client for acme. Some programs rely on large databases that would be cumbersome to include in every release. Scripts are provided that download these databases separately. -These databases can be downloaded separately. +These databases can be downloaded separately. See .B $PLAN9/dict/README and @@ -148,7 +153,7 @@ and (see .MR 9c (1) ) provide a simple interface to the underlying system compiler and linker, -similar to the +similar to the .I 2c and .I 2l @@ -201,7 +206,7 @@ cannot) and dump data structures, but that it is the extent to which they have been developed and exercised. .SS Porting programs -The vast majority of the familiar Plan 9 programs +The vast majority of the familiar Plan 9 programs have been ported, including the Unicode-aware .MR troff (1) . .PP @@ -222,7 +227,7 @@ is in progress; see .SS Porting to new systems Porting the tree to new operating systems or architectures should be straightforward, as system-specific code has been -kept to a minimum. +kept to a minimum. The largest pieces of system-specific code are .BR , which must include the right system files and @@ -231,7 +236,7 @@ and .IR libthread , which must implement spin locks, operating system thread creation, and context switching routines. -Portable implementations of these using +Portable implementations of these using .B and .B @@ -259,7 +264,7 @@ so that the Unix utility can handle it. Some systems, for example Debian Linux, deduce the man page locations from the search path, so that -adding +adding .B $PLAN9/bin to your path is sufficient to cause .B $PLAN9/man diff --git a/man/man1/acme.1 b/man/man1/acme.1 index 852a8f7f1e..bb2c89d67e 100644 --- a/man/man1/acme.1 +++ b/man/man1/acme.1 @@ -588,6 +588,9 @@ not just or .BR 127 . (There is an easier way to locate literal text; see below.) +If shift is held down during the selection or click, +any leading regular expression search defaults to +searching backward in the text instead of forward. .PP If the text is a file name followed by a colon and an address, .I acme @@ -608,6 +611,8 @@ moved there. Thus, to search for occurrences of a word in a file, just click button 3 on the word. Because of the rule of using the selection as the button 3 action, subsequent clicks will find subsequent occurrences without moving the mouse. +If shift is held down during the selection or click, +the search looks backward in the file. .PP In all these actions, the mouse motion is not done if the text is a null string within a non-null selected string in the tag, so that (for example) complex regular expressions diff --git a/man/man1/devdraw.1 b/man/man1/devdraw.1 index da36b8e2ae..e606b3e296 100644 --- a/man/man1/devdraw.1 +++ b/man/man1/devdraw.1 @@ -2,7 +2,7 @@ .SH NAME devdraw \- draw device simulator .SH SYNOPSIS -invoked via +invoked via .I initdraw (see .MR graphics (3) ) @@ -10,19 +10,71 @@ invoked via .I Devdraw serves a custom graphics protocol and is the only program that talks directly to X window servers. -On Macintosh, setting -.BI devdrawretina -to -.BI 1 -will cause -.I devdraw -to use all available physical pixels on a retina display. +.PP +.SS "Apple macOS +.PP +On macOS, because a laptop trackpad click only has one button (the trackpad itself) +Option-click is button 2, and Command-click is button 3. +While the main mouse button is held down, +Control, Option, and Command serve as simulated buttons 1, 2, 3 for chording in +.MR acme (4) . +For example, the 1-3 pasting chord in acme can be executed by +highlighting text while holding down the trackpad button +and then, while still holding down the button, pressing the Command key. +.PP +As usual, buttons 4 and 5 represent a scroll wheel. +Two-finger scrolling on the trackpad sends those button events. +.PP +Holding down shift while clicking adds 5 to the button number. +For example, Command-Click is button 3, so Command-Shift-Click is button 8. +Most programs do not respond to those buttons; one notable exception is +.MR acme (1) , +which interprets button 8 (shifted button 3) as a reverse search. +.PP +Typing Command-F toggles full screen mode. +.PP +.I Devdraw +automatically detects high-resolution (retina) displays. +For debugging, typing Command-R toggles retina mode. +.PP +Other than the special cases mentioned above, +holding down Command while typing a character +.B Kcmd +(0xF100) +plus that character. +Some programs (notably +.IR acme (1)) +recognize standard keyboard shortcuts such as +Command-Z (undo), Command-Shift-Z (redo), +Command-X (cut), and Command-V (paste). +.SS "X Windows +.PP +On Unix systems, Control-click is mouse button 2, +and Alt-click is mouse button 3. +While the main mouse button is held down, +Control and Alt serve as simulated buttons 2, 3 for chording in +.MR acme (4) . +For example, the 1-3 pasting chord in acme can be executed by +highlighting text while holding down the trackpad button +and then, while still holding down the button, pressing the Alt key. +.PP +Because the Control and Alt keys have other meanings +(see +.MR keyboard (7) +for the Alt key's meaning) +and there is no third modifier key like on the Mac, +there is no way to type +.B Kcmd +variants, +so there is no access to keyboard shortcuts for +undo, redo, cut, and paste. .SH SOURCE .B \*9/src/cmd/devdraw .SH "SEE ALSO .MR draw (3) , .MR drawfcall (3) , -.MR graphics (3) +.MR graphics (3) , +.MR keyboard (7) .SH BUGS .I Devdraw should probably present a standard 9P server diff --git a/man/man4/acme.4 b/man/man4/acme.4 index f55a85b75b..9916f470d3 100644 --- a/man/man4/acme.4 +++ b/man/man4/acme.4 @@ -361,6 +361,10 @@ for text inserted to the tag, for a button 3 action in the body, .B l for a button 3 action in the tag, +.B R +for a shifted button 3 action in the body, +.B r +for a shifted button 3 action in the tag, .B X for a button 2 action in the body, and .B x diff --git a/man/man7/keyboard.7 b/man/man7/keyboard.7 index 69094a221d..933cdde299 100644 --- a/man/man7/keyboard.7 +++ b/man/man7/keyboard.7 @@ -62,16 +62,11 @@ Any rune can be typed using a compose key followed by several other keys. The compose key is also generally near the lower right of the main key area: the -.B NUM PAD -key on the Gnot, the -.B Alternate -key on the Next, the -.B Compose -key on the SLC, the .B Option -key on the Magnum, and either +key on the Mac +and the .B Alt -key on the PC. +key on Unix systems. To type a single rune with the value specified by a given four-digit hexadecimal number, type the compose key, diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c index d7391cf52e..2fd42c54e0 100644 --- a/src/cmd/9term/9term.c +++ b/src/cmd/9term/9term.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -56,7 +57,7 @@ threadmaybackground(void) void threadmain(int argc, char *argv[]) { - char *p; + char *p, *env; rfork(RFNOTEG); font = nil; @@ -64,6 +65,25 @@ threadmain(int argc, char *argv[]) mainpid = getpid(); messagesize = 8192; + threadmaybackground(); + + env = getenv("__CFBundleIdentifier"); + if(env != nil && strcmp(env, "com.swtch.9term") == 0) { + // Being invoked as $PLAN9/mac/9term.app. + // Set $SHELL and daemonize to let parent exit. + // This makes sure that each click on 9term + // brings up a new window. + extern void _threaddaemonize(void); + struct passwd *pw; + + unsetenv("__CFBundleIdentifier"); + pw = getpwuid(getuid()); + if(pw != nil && pw->pw_shell != nil) + setenv("SHELL", pw->pw_shell, 1); + loginshell = TRUE; + //_threaddaemonize(); + } + ARGBEGIN{ default: usage(); diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c index 0e6bc0fd7f..bb77415369 100644 --- a/src/cmd/acme/acme.c +++ b/src/cmd/acme/acme.c @@ -518,6 +518,7 @@ mousethread(void *v) Mouse m; char *act; enum { MResize, MMouse, MPlumb, MWarnings, NMALT }; + enum { Shift = 5 }; static Alt alts[NMALT+1]; USED(v); @@ -661,9 +662,9 @@ mousethread(void *v) }else if(m.buttons & 2){ if(textselect2(t, &q0, &q1, &argt)) execute(t, q0, q1, FALSE, argt); - }else if(m.buttons & 4){ + }else if(m.buttons & (4|(4<name, c->nname)){ + if(search(t, c->name, c->nname, FALSE)){ textdelete(t, t->q0, t->q1, TRUE); textsetselect(t, 0, 0); } diff --git a/src/cmd/acme/addr.c b/src/cmd/acme/addr.c index 6aee8993c2..a91425e14e 100644 --- a/src/cmd/acme/addr.c +++ b/src/cmd/acme/addr.c @@ -172,7 +172,7 @@ regexp(uint showerr, Text *t, Range lim, Range r, Rune *pat, int dir, int *found } Range -address(uint showerr, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint), int *evalp, uint *qp) +address(uint showerr, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint), int *evalp, uint *qp, int reverse) { int dir, size, npat; int prevc, c, nc, n; @@ -183,6 +183,8 @@ address(uint showerr, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, i r = ar; q = q0; dir = None; + if(reverse) + dir = Back; size = Line; c = 0; while(q < q1){ @@ -201,7 +203,7 @@ address(uint showerr, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, i if(q>=q1 && t!=nil && t->file!=nil) /* rhs defaults to $ */ r.q1 = t->file->b.nc; else{ - nr = address(showerr, t, lim, ar, a, q, q1, getc, evalp, &q); + nr = address(showerr, t, lim, ar, a, q, q1, getc, evalp, &q, FALSE); r.q1 = nr.q1; } *qp = q; diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h index e540605a31..03150aa609 100644 --- a/src/cmd/acme/dat.h +++ b/src/cmd/acme/dat.h @@ -454,6 +454,7 @@ struct Expand int nname; char *bname; int jump; + int reverse; union{ Text *at; Rune *ar; diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c index 766669c20a..247b1eaa80 100644 --- a/src/cmd/acme/exec.c +++ b/src/cmd/acme/exec.c @@ -280,13 +280,14 @@ getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp) Expand e; char *a; + memset(&e, 0, sizeof e); *rp = nil; *nrp = 0; if(argt == nil) return nil; a = nil; textcommit(argt, TRUE); - if(expand(argt, argt->q0, argt->q1, &e)){ + if(expand(argt, argt->q0, argt->q1, &e, FALSE)){ free(e.bname); if(e.nname && dofile){ e.name = runerealloc(e.name, e.nname+1); @@ -1083,7 +1084,7 @@ look(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg) if(et && et->w){ t = &et->w->body; if(narg > 0){ - search(t, arg, narg); + search(t, arg, narg, FALSE); return; } getarg(argt, FALSE, FALSE, &r, &n); @@ -1092,7 +1093,7 @@ look(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg) r = runemalloc(n); bufread(&t->file->b, t->q0, r, n); } - search(t, r, n); + search(t, r, n, FALSE); free(r); } } diff --git a/src/cmd/acme/fns.h b/src/cmd/acme/fns.h index 969db417f2..cf9a7f1176 100644 --- a/src/cmd/acme/fns.h +++ b/src/cmd/acme/fns.h @@ -63,8 +63,8 @@ void fontx(Text*, Text*, Text*, int, int, Rune*, int); #define isalnum acmeisalnum int isalnum(Rune); void execute(Text*, uint, uint, int, Text*); -int search(Text*, Rune*, uint); -void look3(Text*, uint, uint, int); +int search(Text*, Rune*, uint, int); +void look3(Text*, uint, uint, int, int); void editcmd(Text*, Rune*, uint); uint min(uint, uint); uint max(uint, uint); @@ -85,11 +85,11 @@ int isregexc(int); void *emalloc(uint); void *erealloc(void*, uint); char *estrdup(char*); -Range address(uint, Text*, Range, Range, void*, uint, uint, int (*)(void*, uint), int*, uint*); +Range address(uint, Text*, Range, Range, void*, uint, uint, int (*)(void*, uint), int*, uint*, int); int rxexecute(Text*, Rune*, uint, uint, Rangeset*); int rxbexecute(Text*, uint, Rangeset*); Window* makenewwindow(Text *t); -int expand(Text*, uint, uint, Expand*); +int expand(Text*, uint, uint, Expand*, int); Rune* skipbl(Rune*, int, int*); Rune* findbl(Rune*, int, int*); char* edittext(Window*, int, Rune*, int); diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c index a7172b505f..87bfbcd6fa 100644 --- a/src/cmd/acme/look.c +++ b/src/cmd/acme/look.c @@ -80,7 +80,7 @@ startplumbing(void) void -look3(Text *t, uint q0, uint q1, int external) +look3(Text *t, uint q0, uint q1, int external, int reverse) { int n, c, f, expanded; Text *ct; @@ -94,7 +94,7 @@ look3(Text *t, uint q0, uint q1, int external) ct = seltext; if(ct == nil) seltext = t; - expanded = expand(t, q0, q1, &e); + expanded = expand(t, q0, q1, &e, reverse); if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ /* send alphanumeric expansion to external client */ if(expanded == FALSE) @@ -109,6 +109,8 @@ look3(Text *t, uint q0, uint q1, int external) c = 'l'; if(t->what == Body) c = 'L'; + if(reverse) + c += 'R' - 'L'; n = q1-q0; if(n <= EVENTSIZE){ r = runemalloc(n); @@ -203,12 +205,17 @@ look3(Text *t, uint q0, uint q1, int external) ct = &t->w->body; if(t->w != ct->w) winlock(ct->w, 'M'); - if(t == ct) - textsetselect(ct, e.q1, e.q1); + if(t == ct) { + uint q; + q = e.q1; + if(reverse) + q = e.q0; + textsetselect(ct, q, q); + } n = e.q1 - e.q0; r = runemalloc(n); bufread(&t->file->b, e.q0, r, n); - if(search(ct, r, n) && e.jump) + if(search(ct, r, n, reverse) && e.jump) moveto(mousectl, addpt(frptofchar(&ct->fr, ct->fr.p0), Pt(4, ct->fr.font->height-4))); if(t->w != ct->w) winunlock(ct->w); @@ -241,6 +248,7 @@ plumblook(Plumbmsg *m) warning(nil, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data); return; } + memset(&e, 0, sizeof e); e.q0 = 0; e.q1 = 0; if(m->data[0] == '\0') @@ -303,11 +311,11 @@ plumbshow(Plumbmsg *m) } int -search(Text *ct, Rune *r, uint n) +search(Text *ct, Rune *r, uint n, int reverse) { - uint q, nb, maxn; + uint nb, maxn; int around; - Rune *s, *b, *c; + Rune *s, *b; if(n==0 || n>ct->file->b.nc) return FALSE; @@ -321,55 +329,111 @@ search(Text *ct, Rune *r, uint n) nb = 0; b[nb] = 0; around = 0; - q = ct->q1; - for(;;){ - if(q >= ct->file->b.nc){ - q = 0; - around = 1; - nb = 0; - b[nb] = 0; - } - if(nb > 0){ - c = runestrchr(b, r[0]); - if(c == nil){ - q += nb; + if(reverse){ + uint q1; + q1 = ct->q0; // q1 is (past) end of text being searched. + for(;;){ + if(q1 <= 0){ + q1 = ct->file->b.nc; + around = 1; nb = 0; b[nb] = 0; - if(around && q>=ct->q1) - break; - continue; } - q += (c-b); - nb -= (c-b); - b = c; - } - /* reload if buffer covers neither string nor rest of file */ - if(nbfile->b.nc-q){ - nb = ct->file->b.nc-q; - if(nb >= maxn) - nb = maxn-1; - bufread(&ct->file->b, q, s, nb); - b = s; - b[nb] = '\0'; + if(nb > 0){ + Rune *c; + for(c=b+nb; c>b; c--) + if(c[-1] == r[n-1]) + break; + if(c == b) { + q1 -= nb; + nb = 0; + b[nb] = 0; + if(around && q1 <= 0) + break; + continue; + } + q1 -= nb - (c - b); + nb = c - b; + } + /* reload if buffer covers neither string nor beginning of file */ + if(nb= maxn) + nb = maxn-1; + bufread(&ct->file->b, q1-nb, s, nb); + b = s; + b[nb] = '\0'; + } + if(runeeq(b+nb-n, n, r, n)==TRUE){ + if(ct->w){ + textshow(ct, q1-n, q1, 1); + winsettag(ct->w); + }else{ + ct->q0 = q1-n; + ct->q1 = q1; + } + seltext = ct; + fbuffree(s); + return TRUE; + } + q1--; + nb--; + if(around && q1 <= 0) + break; } - /* this runeeq is fishy but the null at b[nb] makes it safe */ - if(runeeq(b, n, r, n)==TRUE){ - if(ct->w){ - textshow(ct, q, q+n, 1); - winsettag(ct->w); - }else{ - ct->q0 = q; - ct->q1 = q+n; + }else{ + uint q; + q = ct->q1; + for(;;){ + if(q >= ct->file->b.nc){ + q = 0; + around = 1; + nb = 0; + b[nb] = 0; } - seltext = ct; - fbuffree(s); - return TRUE; + if(nb > 0){ + Rune *c; + c = runestrchr(b, r[0]); + if(c == nil){ + q += nb; + nb = 0; + b[nb] = 0; + if(around && q>=ct->q1) + break; + continue; + } + q += (c-b); + nb -= (c-b); + b = c; + } + /* reload if buffer covers neither string nor rest of file */ + if(nbfile->b.nc-q){ + nb = ct->file->b.nc-q; + if(nb >= maxn) + nb = maxn-1; + bufread(&ct->file->b, q, s, nb); + b = s; + b[nb] = '\0'; + } + /* this runeeq is fishy but the null at b[nb] makes it safe */ + if(runeeq(b, n, r, n)==TRUE){ + if(ct->w){ + textshow(ct, q, q+n, 1); + winsettag(ct->w); + }else{ + ct->q0 = q; + ct->q1 = q+n; + } + seltext = ct; + fbuffree(s); + return TRUE; + } + --nb; + b++; + q++; + if(around && q>=ct->q1) + break; } - --nb; - b++; - q++; - if(around && q>=ct->q1) - break; } fbuffree(s); return FALSE; @@ -526,7 +590,7 @@ texthas(Text *t, uint q0, Rune *r) } int -expandfile(Text *t, uint q0, uint q1, Expand *e) +expandfile(Text *t, uint q0, uint q1, Expand *e, int reverse) { int i, n, nname, colon, eval; uint amin, amax; @@ -570,6 +634,11 @@ expandfile(Text *t, uint q0, uint q1, Expand *e) break; }else amax = t->file->b.nc; + if(colon != q0) + reverse = FALSE; + }else if(reverse){ + if(textreadc(t, q0) != ':') + reverse = FALSE; } amin = amax; e->q0 = q0; @@ -643,12 +712,16 @@ expandfile(Text *t, uint q0, uint q1, Expand *e) } Isfile: + print("isfile reverse=%d colon=%d q0=%d\n", reverse, colon, q0); e->name = r; e->nname = nname; e->u.at = t; e->a0 = amin+1; + e->reverse = reverse; eval = FALSE; - address(TRUE, nil, range(-1,-1), range(0,0), t, e->a0, amax, tgetc, &eval, (uint*)&e->a1); + // Note: address is repeated in openfile when + // expandfile returns to expand returns to look3. + address(TRUE, nil, range(-1,-1), range(0,0), t, e->a0, amax, tgetc, &eval, (uint*)&e->a1, e->reverse); return TRUE; Isntfile: @@ -657,7 +730,7 @@ expandfile(Text *t, uint q0, uint q1, Expand *e) } int -expand(Text *t, uint q0, uint q1, Expand *e) +expand(Text *t, uint q0, uint q1, Expand *e, int reverse) { memset(e, 0, sizeof *e); e->agetc = tgetc; @@ -670,7 +743,7 @@ expand(Text *t, uint q0, uint q1, Expand *e) e->jump = FALSE; } - if(expandfile(t, q0, q1, e)) + if(expandfile(t, q0, q1, e, reverse)) return TRUE; if(q0 == q1){ @@ -806,7 +879,7 @@ openfile(Text *t, Expand *e) eval = FALSE; else{ eval = TRUE; - r = address(TRUE, t, range(-1,-1), range(t->q0, t->q1), e->u.at, e->a0, e->a1, e->agetc, &eval, &dummy); + r = address(TRUE, t, range(-1,-1), range(t->q0, t->q1), e->u.at, e->a0, e->a1, e->agetc, &eval, &dummy, e->reverse); if(r.q0 > r.q1) { eval = FALSE; warning(nil, "addresses out of order\n"); diff --git a/src/cmd/acme/xfid.c b/src/cmd/acme/xfid.c index dc7bf75b5c..43f6b1ce30 100644 --- a/src/cmd/acme/xfid.c +++ b/src/cmd/acme/xfid.c @@ -486,7 +486,7 @@ xfidwrite(Xfid *x) t = &w->body; wincommit(w, t); eval = TRUE; - a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb); + a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb, FALSE); free(r); if(nb < nr){ respond(x, &fc, Ebadaddr); @@ -900,7 +900,11 @@ xfideventwrite(Xfid *x, Window *w) break; case 'l': case 'L': - look3(t, q0, q1, TRUE); + look3(t, q0, q1, TRUE, FALSE); + break; + case 'r': + case 'R': + look3(t, q0, q1, TRUE, TRUE); break; default: qunlock(&row.lk); diff --git a/src/cmd/devdraw/mac-screen.m b/src/cmd/devdraw/mac-screen.m index f07054cf9a..04c807b7af 100644 --- a/src/cmd/devdraw/mac-screen.m +++ b/src/cmd/devdraw/mac-screen.m @@ -631,12 +631,17 @@ - (void)flagsChanged:(NSEvent*)e b = [NSEvent pressedMouseButtons]; b = (b&~6) | (b&4)>>1 | (b&2)<<1; if(b){ + int x; + x = 0; if(m & ~omod & NSEventModifierFlagControl) - b |= 1; + x = 1; if(m & ~omod & NSEventModifierFlagOption) - b |= 2; + x = 2; if(m & ~omod & NSEventModifierFlagCommand) - b |= 4; + x = 4; + if(m & NSEventModifierFlagShift) + x <<= 5; + b |= x; [self sendmouse:b]; }else if(m & ~omod & NSEventModifierFlagOption) gfx_keystroke(self.client, Kalt); @@ -701,6 +706,8 @@ - (void)getmouse:(NSEvent *)e }else if(m & NSEventModifierFlagCommand) b = 4; + if(m & NSEventModifierFlagShift) + b <<= 5; } [self sendmouse:b]; }