xref: /plan9/sys/src/cmd/vnc/devcons.c (revision 9b943567965ba040fd275927fbe088656eb8ce4f)
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 		t->buf = nil;	/* setsnarf took it */
283 		free(t);
284 		c->aux = nil;
285 		break;
286 	}
287 }
288 
289 static long
290 consread(Chan *c, void *buf, long n, vlong off)
291 {
292 	char *cbuf;
293 	int i, ch, eol;
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 		if(kbd.raw){
319 			cbuf = buf;
320 			if(qcanread(lineq))
321 				n = qread(lineq, buf, n);
322 			else {
323 				/* read as much as possible */
324 				do {
325 					i = qread(kbdq, cbuf, n);
326 					cbuf += i;
327 					n -= i;
328 				} while (n>0 && qcanread(kbdq));
329 				n = cbuf - (char*)buf;
330 			}
331 		} else {
332 			while(!qcanread(lineq)){
333 				qread(kbdq, &kbd.line[kbd.x], 1);
334 				ch = kbd.line[kbd.x];
335 				eol = 0;
336 				switch(ch){
337 				case '\b':
338 					if(kbd.x)
339 						kbd.x--;
340 					break;
341 				case 0x15:
342 					kbd.x = 0;
343 					break;
344 				case '\n':
345 				case 0x04:
346 					eol = 1;
347 				default:
348 					kbd.line[kbd.x++] = ch;
349 					break;
350 				}
351 				if(kbd.x == sizeof(kbd.line) || eol){
352 					if(ch == 0x04)
353 						kbd.x--;
354 					qwrite(lineq, kbd.line, kbd.x);
355 					kbd.x = 0;
356 				}
357 			}
358 			n = qread(lineq, buf, n);
359 		}
360 		qunlock(&kbd);
361 		poperror();
362 		return n;
363 
364 	default:
365 		print("consread 0x%llux\n", c->qid.path);
366 		error(Egreg);
367 	}
368 	return -1;		/* never reached */
369 }
370 
371 static long
372 conswrite(Chan *c, void *va, long n, vlong)
373 {
374 	Snarf *t;
375 	char buf[256], *a;
376 
377 	switch((ulong)c->qid.path){
378 	case Qcons:
379 		screenputs(va, n);
380 		break;
381 
382 	case Qconsctl:
383 		if(n >= sizeof(buf))
384 			n = sizeof(buf)-1;
385 		strncpy(buf, va, n);
386 		buf[n] = 0;
387 		for(a = buf; a;){
388 			if(strncmp(a, "rawon", 5) == 0){
389 				qlock(&kbd);
390 				if(kbd.x){
391 					qwrite(kbdq, kbd.line, kbd.x);
392 					kbd.x = 0;
393 				}
394 				kbd.raw = 1;
395 				qunlock(&kbd);
396 			} else if(strncmp(a, "rawoff", 6) == 0){
397 				qlock(&kbd);
398 				kbd.raw = 0;
399 				kbd.x = 0;
400 				qunlock(&kbd);
401 			}
402 			if(a = strchr(a, ' '))
403 				a++;
404 		}
405 		break;
406 
407 	case Qsnarf:
408 		t = c->aux;
409 		/* always append only */
410 		if(t->n > MAXSNARF)	/* avoid thrashing when people cut huge text */
411 			error("snarf buffer too big");
412 		a = realloc(t->buf, t->n + n + 1);
413 		if(a == nil)
414 			error("snarf buffer too big");
415 		t->buf = a;
416 		memmove(t->buf+t->n, va, n);
417 		t->n += n;
418 		t->buf[t->n] = '\0';
419 		break;
420 	default:
421 		print("conswrite: 0x%llux\n", c->qid.path);
422 		error(Egreg);
423 	}
424 	return n;
425 }
426 
427 Dev consdevtab = {
428 	'c',
429 	"cons",
430 
431 	devreset,
432 	consinit,
433 	consattach,
434 	conswalk,
435 	consstat,
436 	consopen,
437 	devcreate,
438 	consclose,
439 	consread,
440 	devbread,
441 	conswrite,
442 	devbwrite,
443 	devremove,
444 	devwstat,
445 };
446