xref: /plan9/sys/src/cmd/vnc/devcons.c (revision 82726826a7b3d40fb66339b4b0e95b60314f98b9)
1 #include	<u.h>
2 #include	<libc.h>
3 #include	"compat.h"
4 #include	"kbd.h"
5 #include	"error.h"
6 
7 typedef struct Queue	Queue;
8 struct Queue
9 {
10 	QLock	qwait;
11 	Rendez	rwait;
12 
13 	Lock	lock;
14 	int	notempty;
15 	char	buf[1024];
16 	char	*w;
17 	char	*r;
18 	char	*e;
19 };
20 
21 Queue*	kbdq;			/* unprocessed console input */
22 Queue*	lineq;			/* processed console input */
23 Snarf	snarf = {
24 	.vers =	1
25 };
26 
27 static struct
28 {
29 	QLock;
30 	int	raw;		/* true if we shouldn't process input */
31 	int	ctl;		/* number of opens to the control file */
32 	int	x;		/* index into line */
33 	char	line[1024];	/* current input line */
34 } kbd;
35 
36 /*
37  * cheapo fixed-length queues
38  */
39 static void
qwrite(Queue * q,void * v,int n)40 qwrite(Queue *q, void *v, int n)
41 {
42 	char *buf, *next;
43 	int i;
44 
45 	buf = v;
46 	lock(&q->lock);
47 	for(i = 0; i < n; i++){
48 		next = q->w+1;
49 		if(next >= q->e)
50 			next = q->buf;
51 		if(next == q->r)
52 			break;
53 		*q->w = buf[i];
54 		q->w = next;
55 	}
56 	q->notempty = 1;
57 	unlock(&q->lock);
58 	rendwakeup(&q->rwait);
59 }
60 
61 static int
qcanread(void * vq)62 qcanread(void *vq)
63 {
64 	Queue *q;
65 	int ne;
66 
67 	q = vq;
68 	lock(&q->lock);
69 	ne = q->notempty;
70 	unlock(&q->lock);
71 	return ne;
72 }
73 
74 static int
qread(Queue * q,void * v,int n)75 qread(Queue *q, void *v, int n)
76 {
77 	char *a;
78 	int nn, notempty;
79 
80 	if(n == 0)
81 		return 0;
82 	a = v;
83 	nn = 0;
84 	for(;;){
85 		lock(&q->lock);
86 
87 		while(nn < n && q->r != q->w){
88 			a[nn++] = *q->r++;
89 			if(q->r >= q->e)
90 				q->r = q->buf;
91 		}
92 
93 		notempty = q->notempty;
94 		q->notempty = q->r != q->w;
95 		unlock(&q->lock);
96 		if(notempty)
97 			break;
98 
99 		/*
100 		 * wait for something to show up in the kbd buffer.
101 		 */
102 		qlock(&q->qwait);
103 		if(waserror()){
104 			qunlock(&q->qwait);
105 			nexterror();
106 		}
107 		rendsleep(&q->rwait, qcanread, q);
108 		qunlock(&q->qwait);
109 		poperror();
110 	}
111 	return nn;
112 }
113 
114 static Queue *
mkqueue(void)115 mkqueue(void)
116 {
117 	Queue *q;
118 
119 	q = smalloc(sizeof(Queue));
120 	q->r = q->buf;
121 	q->w = q->r;
122 	q->e = &q->buf[sizeof q->buf];
123 	q->notempty = 0;
124 	return q;
125 }
126 
127 static void
echoscreen(char * buf,int n)128 echoscreen(char *buf, int n)
129 {
130 	char *e, *p;
131 	char ebuf[128];
132 	int x;
133 
134 	p = ebuf;
135 	e = ebuf + sizeof(ebuf) - 4;
136 	while(n-- > 0){
137 		if(p >= e){
138 			screenputs(ebuf, p - ebuf);
139 			p = ebuf;
140 		}
141 		x = *buf++;
142 		if(x == 0x15){
143 			*p++ = '^';
144 			*p++ = 'U';
145 			*p++ = '\n';
146 		} else
147 			*p++ = x;
148 	}
149 	if(p != ebuf)
150 		screenputs(ebuf, p - ebuf);
151 }
152 
153 /*
154  *  Put character, possibly a rune, into read queue at interrupt time.
155  *  Called at interrupt time to process a character.
156  */
157 void
kbdputc(int ch)158 kbdputc(int ch)
159 {
160 	int n;
161 	char buf[UTFmax];
162 	Rune r;
163 
164 	r = ch;
165 	n = runetochar(buf, &r);
166 	qwrite(kbdq, buf, n);
167 	if(!kbd.raw)
168 		echoscreen(buf, n);
169 }
170 
171 static void
kbdputcinit(void)172 kbdputcinit(void)
173 {
174 	kbdq = mkqueue();
175 	lineq = mkqueue();
176 	kbd.raw = 0;
177 	kbd.ctl = 0;
178 	kbd.x = 0;
179 }
180 
181 enum{
182 	Qdir,
183 	Qcons,
184 	Qconsctl,
185 	Qsnarf,
186 	Qwinname,
187 };
188 
189 static Dirtab consdir[]={
190 	".",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,
191 	"cons",		{Qcons},	0,		0660,
192 	"consctl",	{Qconsctl},	0,		0220,
193 	"snarf",	{Qsnarf},	0,		0600,
194 	"winname",	{Qwinname},	0,		0000,
195 };
196 
197 static void
consinit(void)198 consinit(void)
199 {
200 	kbdputcinit();
201 }
202 
203 static Chan*
consattach(char * spec)204 consattach(char *spec)
205 {
206 	return devattach('c', spec);
207 }
208 
209 static Walkqid*
conswalk(Chan * c,Chan * nc,char ** name,int nname)210 conswalk(Chan *c, Chan *nc, char **name, int nname)
211 {
212 	return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
213 }
214 
215 static int
consstat(Chan * c,uchar * dp,int n)216 consstat(Chan *c, uchar *dp, int n)
217 {
218 	return devstat(c, dp, n, consdir, nelem(consdir), devgen);
219 }
220 
221 static Chan*
consopen(Chan * c,int omode)222 consopen(Chan *c, int omode)
223 {
224 	c->aux = nil;
225 	c = devopen(c, omode, consdir, nelem(consdir), devgen);
226 	switch((ulong)c->qid.path){
227 	case Qconsctl:
228 		qlock(&kbd);
229 		kbd.ctl++;
230 		qunlock(&kbd);
231 		break;
232 	case Qsnarf:
233 		if((c->mode&3) == OWRITE || (c->mode&3) == ORDWR)
234 			c->aux = smalloc(sizeof(Snarf));
235 		break;
236 	}
237 	return c;
238 }
239 
240 void
setsnarf(char * buf,int n,int * vers)241 setsnarf(char *buf, int n, int *vers)
242 {
243 	int i;
244 
245 	qlock(&snarf);
246 	snarf.vers++;
247 	if(vers)
248 		*vers = snarf.vers;
249 	for(i = 0; i < nelem(consdir); i++){
250 		if(consdir[i].qid.type == Qsnarf){
251 			consdir[i].qid.vers = snarf.vers;
252 			break;
253 		}
254 	}
255 	free(snarf.buf);
256 	snarf.n = n;
257 	snarf.buf = buf;
258 	qunlock(&snarf);
259 }
260 
261 static void
consclose(Chan * c)262 consclose(Chan *c)
263 {
264 	Snarf *t;
265 
266 	switch((ulong)c->qid.path){
267 	/* last close of control file turns off raw */
268 	case Qconsctl:
269 		if(c->flag&COPEN){
270 			qlock(&kbd);
271 			if(--kbd.ctl == 0)
272 				kbd.raw = 0;
273 			qunlock(&kbd);
274 		}
275 		break;
276 	/* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */
277 	case Qsnarf:
278 		t = c->aux;
279 		if(t == nil)
280 			break;
281 		setsnarf(t->buf, t->n, 0);
282 		t->buf = nil;	/* setsnarf took it */
283 		free(t);
284 		c->aux = nil;
285 		break;
286 	}
287 }
288 
289 static long
consread(Chan * c,void * buf,long n,vlong off)290 consread(Chan *c, void *buf, long n, vlong off)
291 {
292 	char ch;
293 	int	send;
294 
295 	if(n <= 0)
296 		return n;
297 	switch((ulong)c->qid.path){
298 	case Qsnarf:
299 		qlock(&snarf);
300 		if(off < snarf.n){
301 			if(off + n > snarf.n)
302 				n = snarf.n - off;
303 			memmove(buf, snarf.buf+off, n);
304 		}else
305 			n = 0;
306 		qunlock(&snarf);
307 		return n;
308 
309 	case Qdir:
310 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
311 
312 	case Qcons:
313 		qlock(&kbd);
314 		if(waserror()){
315 			qunlock(&kbd);
316 			nexterror();
317 		}
318 		while(!qcanread(lineq)){
319 			qread(kbdq, &ch, 1);
320 			send = 0;
321 			if(ch == 0){
322 				/* flush output on rawoff -> rawon */
323 				if(kbd.x > 0)
324 					send = !qcanread(kbdq);
325 			}else if(kbd.raw){
326 				kbd.line[kbd.x++] = ch;
327 				send = !qcanread(kbdq);
328 			}else{
329 				switch(ch){
330 				case '\b':
331 					if(kbd.x > 0)
332 						kbd.x--;
333 					break;
334 				case 0x15:	/* ^U */
335 					kbd.x = 0;
336 					break;
337 				case '\n':
338 				case 0x04:	/* ^D */
339 					send = 1;
340 				default:
341 					if(ch != 0x04)
342 						kbd.line[kbd.x++] = ch;
343 					break;
344 				}
345 			}
346 			if(send || kbd.x == sizeof kbd.line){
347 				qwrite(lineq, kbd.line, kbd.x);
348 				kbd.x = 0;
349 			}
350 		}
351 		n = qread(lineq, buf, n);
352 		qunlock(&kbd);
353 		poperror();
354 		return n;
355 
356 	default:
357 		print("consread 0x%llux\n", c->qid.path);
358 		error(Egreg);
359 	}
360 	return -1;		/* never reached */
361 }
362 
363 static long
conswrite(Chan * c,void * va,long n,vlong)364 conswrite(Chan *c, void *va, long n, vlong)
365 {
366 	Snarf *t;
367 	char buf[256], *a;
368 	char ch;
369 
370 	switch((ulong)c->qid.path){
371 	case Qcons:
372 		screenputs(va, n);
373 		break;
374 
375 	case Qconsctl:
376 		if(n >= sizeof(buf))
377 			n = sizeof(buf)-1;
378 		strncpy(buf, va, n);
379 		buf[n] = 0;
380 		for(a = buf; a;){
381 			if(strncmp(a, "rawon", 5) == 0){
382 				kbd.raw = 1;
383 				/* clumsy hack - wake up reader */
384 				ch = 0;
385 				qwrite(kbdq, &ch, 1);
386 			} else if(strncmp(a, "rawoff", 6) == 0){
387 				kbd.raw = 0;
388 			}
389 			if(a = strchr(a, ' '))
390 				a++;
391 		}
392 		break;
393 
394 	case Qsnarf:
395 		t = c->aux;
396 		/* always append only */
397 		if(t->n > MAXSNARF)	/* avoid thrashing when people cut huge text */
398 			error("snarf buffer too big");
399 		a = realloc(t->buf, t->n + n + 1);
400 		if(a == nil)
401 			error("snarf buffer too big");
402 		t->buf = a;
403 		memmove(t->buf+t->n, va, n);
404 		t->n += n;
405 		t->buf[t->n] = '\0';
406 		break;
407 	default:
408 		print("conswrite: 0x%llux\n", c->qid.path);
409 		error(Egreg);
410 	}
411 	return n;
412 }
413 
414 Dev consdevtab = {
415 	'c',
416 	"cons",
417 
418 	devreset,
419 	consinit,
420 	consattach,
421 	conswalk,
422 	consstat,
423 	consopen,
424 	devcreate,
425 	consclose,
426 	consread,
427 	devbread,
428 	conswrite,
429 	devbwrite,
430 	devremove,
431 	devwstat,
432 };
433