xref: /plan9-contrib/sys/src/cmd/vnc/devcons.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
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
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
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
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 *
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
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
158 kbdputc(int ch)
159 {
160 	int n;
161 	char buf[3];
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
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
198 consinit(void)
199 {
200 	kbdputcinit();
201 }
202 
203 static Chan*
204 consattach(char *spec)
205 {
206 	return devattach('c', spec);
207 }
208 
209 static Walkqid*
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
216 consstat(Chan *c, uchar *dp, int n)
217 {
218 	return devstat(c, dp, n, consdir, nelem(consdir), devgen);
219 }
220 
221 static Chan*
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 == OWRITE || c->mode == ORDWR)
234 			c->aux = smalloc(sizeof(Snarf));
235 		break;
236 	}
237 	return c;
238 }
239 
240 void
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
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 		break;
283 	}
284 }
285 
286 static long
287 consread(Chan *c, void *buf, long n, vlong off)
288 {
289 	char *cbuf;
290 	int i, ch, eol;
291 
292 	if(n <= 0)
293 		return n;
294 	switch((ulong)c->qid.path){
295 	case Qsnarf:
296 		qlock(&snarf);
297 		if(off < snarf.n){
298 			if(off + n > snarf.n)
299 				n = snarf.n - off;
300 			memmove(buf, snarf.buf+off, n);
301 		}else
302 			n = 0;
303 		qunlock(&snarf);
304 		return n;
305 
306 	case Qdir:
307 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
308 
309 	case Qcons:
310 		qlock(&kbd);
311 		if(waserror()) {
312 			qunlock(&kbd);
313 			nexterror();
314 		}
315 		if(kbd.raw) {
316 			cbuf = buf;
317 			if(qcanread(lineq))
318 				n = qread(lineq, buf, n);
319 			else {
320 				/* read as much as possible */
321 				do {
322 					i = qread(kbdq, cbuf, n);
323 					cbuf += i;
324 					n -= i;
325 				} while (n>0 && qcanread(kbdq));
326 				n = cbuf - (char*)buf;
327 			}
328 		} else {
329 			while(!qcanread(lineq)) {
330 				qread(kbdq, &kbd.line[kbd.x], 1);
331 				ch = kbd.line[kbd.x];
332 				eol = 0;
333 				switch(ch){
334 				case '\b':
335 					if(kbd.x)
336 						kbd.x--;
337 					break;
338 				case 0x15:
339 					kbd.x = 0;
340 					break;
341 				case '\n':
342 				case 0x04:
343 					eol = 1;
344 				default:
345 					kbd.line[kbd.x++] = ch;
346 					break;
347 				}
348 				if(kbd.x == sizeof(kbd.line) || eol){
349 					if(ch == 0x04)
350 						kbd.x--;
351 					qwrite(lineq, kbd.line, kbd.x);
352 					kbd.x = 0;
353 				}
354 			}
355 			n = qread(lineq, buf, n);
356 		}
357 		qunlock(&kbd);
358 		poperror();
359 		return n;
360 
361 	default:
362 		print("consread 0x%llux\n", c->qid.path);
363 		error(Egreg);
364 	}
365 	return -1;		/* never reached */
366 }
367 
368 static long
369 conswrite(Chan *c, void *va, long n, vlong)
370 {
371 	Snarf *t;
372 	char buf[256], *a;
373 
374 	switch((ulong)c->qid.path){
375 	case Qcons:
376 		screenputs(va, n);
377 		break;
378 
379 	case Qconsctl:
380 		if(n >= sizeof(buf))
381 			n = sizeof(buf)-1;
382 		strncpy(buf, va, n);
383 		buf[n] = 0;
384 		for(a = buf; a;){
385 			if(strncmp(a, "rawon", 5) == 0){
386 				qlock(&kbd);
387 				if(kbd.x){
388 					qwrite(kbdq, kbd.line, kbd.x);
389 					kbd.x = 0;
390 				}
391 				kbd.raw = 1;
392 				qunlock(&kbd);
393 			} else if(strncmp(a, "rawoff", 6) == 0){
394 				qlock(&kbd);
395 				kbd.raw = 0;
396 				kbd.x = 0;
397 				qunlock(&kbd);
398 			}
399 			if(a = strchr(a, ' '))
400 				a++;
401 		}
402 		break;
403 
404 	case Qsnarf:
405 		t = c->aux;
406 		/* always append only */
407 		if(t->n > MAXSNARF)	/* avoid thrashing when people cut huge text */
408 			error("snarf buffer too big");
409 		a = realloc(t->buf, t->n + n + 1);
410 		if(a == nil)
411 			error("snarf buffer too big");
412 		t->buf = a;
413 		memmove(t->buf+t->n, va, n);
414 		t->n += n;
415 		t->buf[t->n] = '\0';
416 		break;
417 	default:
418 		print("conswrite: 0x%llux\n", c->qid.path);
419 		error(Egreg);
420 	}
421 	return n;
422 }
423 
424 Dev consdevtab = {
425 	'c',
426 	"cons",
427 
428 	devreset,
429 	consinit,
430 	consattach,
431 	conswalk,
432 	consstat,
433 	consopen,
434 	devcreate,
435 	consclose,
436 	consread,
437 	devbread,
438 	conswrite,
439 	devbwrite,
440 	devremove,
441 	devwstat,
442 };
443