1/*
2   Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3   All rights reserved.
4
5This file is part of x11vnc.
6
7x11vnc is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or (at
10your option) any later version.
11
12x11vnc is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with x11vnc; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20or see <http://www.gnu.org/licenses/>.
21
22In addition, as a special exception, Karl J. Runge
23gives permission to link the code of its release of x11vnc with the
24OpenSSL project's "OpenSSL" library (or with modified versions of it
25that use the same license as the "OpenSSL" library), and distribute
26the linked executables.  You must obey the GNU General Public License
27in all respects for all of the code used other than "OpenSSL".  If you
28modify this file, you may extend this exception to your version of the
29file, but you are not obligated to do so.  If you do not wish to do
30so, delete this exception statement from your version.
31*/
32
33/* -- linuxfb.c -- */
34
35#include "x11vnc.h"
36#include "cleanup.h"
37#include "scan.h"
38#include "xinerama.h"
39#include "screen.h"
40#include "pointer.h"
41#include "allowed_input_t.h"
42#include "uinput.h"
43#include "keyboard.h"
44#include "macosx.h"
45
46#if LIBVNCSERVER_HAVE_SYS_IOCTL_H
47#include <sys/ioctl.h>
48#endif
49#if LIBVNCSERVER_HAVE_LINUX_FB_H
50#include <linux/fb.h>
51#endif
52
53char *console_guess(char *str, int *fd);
54void console_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client);
55void console_pointer_command(int mask, int x, int y, rfbClientPtr client);
56
57
58void linux_dev_fb_msg(char *);
59
60char *console_guess(char *str, int *fd) {
61	char *q, *in = strdup(str);
62	char *atparms = NULL, *file = NULL;
63	int do_input, have_uinput, tty = -1;
64
65#ifdef MACOSX
66	return macosx_console_guess(str, fd);
67#endif
68
69
70	if (strstr(in, "/dev/fb") == in) {
71		free(in);
72		in = (char *) malloc(strlen("console:") + strlen(str) + 1);
73		sprintf(in, "console:%s", str);
74	} else if (strstr(in, "fb") == in) {
75		free(in);
76		in = (char *) malloc(strlen("console:/dev/") + strlen(str) + 1);
77		sprintf(in, "console:/dev/%s", str);
78	} else if (strstr(in, "vt") == in) {
79		free(in);
80		in = (char *) malloc(strlen("console_") + strlen(str) + 1);
81		sprintf(in, "console_%s", str);
82	}
83
84	if (strstr(in, "console") != in) {
85		rfbLog("console_guess: unrecognized console/fb format: %s\n", str);
86		free(in);
87		return NULL;
88	}
89
90	q = strrchr(in, '@');
91	if (q) {
92		atparms = strdup(q+1);
93		*q = '\0';
94	}
95	q = strrchr(in, ':');
96	if (q) {
97		file = strdup(q+1);
98		*q = '\0';
99	}
100	if (! file || file[0] == '\0')  {
101		file = strdup("/dev/fb");
102	}
103	if (strstr(file, "fb") == file) {
104		q = (char *) malloc(strlen("/dev/") + strlen(file) + 1);
105		sprintf(q, "/dev/%s", file);
106		free(file);
107		file = q;
108	}
109	if (!strcmp(file, "/dev/fb")) {
110		/* sometimes no sylink fb -> fb0 */
111		struct stat sbuf;
112		if (stat(file, &sbuf) != 0) {
113			free(file);
114			file = strdup("/dev/fb0");
115		}
116	}
117
118	do_input = 1;
119	if (pipeinput_str) {
120		have_uinput = 0;
121		do_input = 0;
122	} else {
123		have_uinput = check_uinput();
124	}
125	if (strstr(in, "console_vt")) {
126		have_uinput = 0;
127	}
128
129	if (!strcmp(in, "consolex")) {
130		do_input = 0;
131	} else if (strstr(in, "console_vtx")) {
132		have_uinput = 0;
133		do_input = 0;
134	} else if (!strcmp(in, "console")) {
135		/* current active VT: */
136		if (! have_uinput) {
137			tty = 0;
138		}
139	} else {
140		int n;
141		if (sscanf(in, "console%d", &n) == 1)  {
142			tty = n;
143			have_uinput = 0;
144		} else if (sscanf(in, "console_vt%d", &n) == 1)  {
145			tty = n;
146			have_uinput = 0;
147		}
148	}
149	if (strstr(in, "console_vt") == in) {
150		char tmp[100];
151		int fd, rows = 30, cols = 80, w, h;
152		sprintf(tmp, "/dev/vcsa%d", tty);
153		file = strdup(tmp);
154		fd = open(file, O_RDWR);
155		if (fd >= 0) {
156			read(fd, tmp, 4);
157			rows = (unsigned char) tmp[0];
158			cols = (unsigned char) tmp[1];
159			close(fd);
160		}
161		w = cols * 8;
162		h = rows * 16;
163		rfbLog("%s %dx%d\n", file, cols, rows);
164		if (getenv("RAWFB_VCSA_BPP")) {
165			/* 8bpp, etc */
166			int bt = atoi(getenv("RAWFB_VCSA_BPP"));
167			if (bt > 0 && bt <=32) {
168				sprintf(tmp, "%dx%dx%d", w, h, bt);
169			} else {
170				sprintf(tmp, "%dx%dx16", w, h);
171			}
172		} else {
173			/* default 16bpp */
174			sprintf(tmp, "%dx%dx16", w, h);
175		}
176		atparms = strdup(tmp);
177	}
178	rfbLog("console_guess: file is %s\n", file);
179
180	if (! atparms) {
181#if LIBVNCSERVER_HAVE_LINUX_FB_H
182#if LIBVNCSERVER_HAVE_SYS_IOCTL_H
183		struct fb_var_screeninfo var_info;
184		int d = open(file, O_RDWR);
185		if (d >= 0) {
186			int w, h, b;
187			unsigned long rm = 0, gm = 0, bm = 0;
188			if (ioctl(d, FBIOGET_VSCREENINFO, &var_info) != -1) {
189				w = (int) var_info.xres;
190				h = (int) var_info.yres;
191				b = (int) var_info.bits_per_pixel;
192
193				rm = (1 << var_info.red.length)   - 1;
194				gm = (1 << var_info.green.length) - 1;
195				bm = (1 << var_info.blue.length)  - 1;
196				rm = rm << var_info.red.offset;
197				gm = gm << var_info.green.offset;
198				bm = bm << var_info.blue.offset;
199
200				if (b == 8 && rm == 0xff && gm == 0xff && bm == 0xff) {
201					/* I don't believe it... */
202					rm = 0x07;
203					gm = 0x38;
204					bm = 0xc0;
205				}
206				if (b <= 8 && (rm == gm && gm == bm)) {
207					if (b == 4) {
208						rm = 0x07;
209						gm = 0x38;
210						bm = 0xc0;
211					}
212				}
213
214				/* @66666x66666x32:0xffffffff:... */
215				atparms = (char *) malloc(200);
216				sprintf(atparms, "%dx%dx%d:%lx/%lx/%lx",
217				    w, h, b, rm, gm, bm);
218				*fd = d;
219			} else {
220				perror("ioctl");
221				close(d);
222			}
223		} else {
224			rfbLog("could not open: %s\n", file);
225			rfbLogPerror("open");
226			linux_dev_fb_msg(file);
227			close(d);
228		}
229#endif
230#endif
231	}
232
233	if (atparms) {
234		int gw, gh, gb;
235		if (sscanf(atparms, "%dx%dx%d", &gw, &gh, &gb) == 3)  {
236			fb_x = gw;
237			fb_y = gh;
238			fb_b = gb;
239		}
240	}
241
242	if (do_input) {
243		if (tty >=0 && tty < 64) {
244			pipeinput_str = (char *) malloc(10);
245			sprintf(pipeinput_str, "CONSOLE%d", tty);
246			rfbLog("console_guess: file pipeinput %s\n",
247			    pipeinput_str);
248			initialize_pipeinput();
249		} else if (have_uinput) {
250			pipeinput_str = strdup("UINPUT");
251			rfbLog("console_guess: file pipeinput %s\n",
252			    pipeinput_str);
253			initialize_pipeinput();
254		}
255	}
256
257	if (! atparms) {
258		rfbLog("console_guess: could not get @ parameters.\n");
259		return NULL;
260	}
261
262	q = (char *) malloc(strlen("mmap:") + strlen(file) + 1 + strlen(atparms) + 1);
263	if (strstr(in, "console_vt")) {
264		sprintf(q, "snap:%s@%s", file, atparms);
265	} else {
266		sprintf(q, "map:%s@%s", file, atparms);
267	}
268	free(atparms);
269	return q;
270}
271
272void console_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
273	static int control = 0, alt = 0;
274	allowed_input_t input;
275
276	if (debug_keyboard) fprintf(stderr, "console_key_command: %d %s\n", (int) keysym, down ? "down" : "up");
277
278	if (pipeinput_cons_fd < 0) {
279		return;
280	}
281	if (view_only) {
282		return;
283	}
284	get_allowed_input(client, &input);
285	if (! input.keystroke) {
286		return;
287	}
288
289	/* From LinuxVNC.c: */
290	if (keysym == XK_Control_L || keysym == XK_Control_R) {
291		if (! down) {
292			if (control > 0) {
293				control--;
294			}
295		} else {
296			control++;
297		}
298		return;
299	}
300	if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
301		if (! down) {
302			if (alt > 0) {
303				alt--;
304			}
305		} else {
306			alt++;
307		}
308		return;
309	}
310	if (!down) {
311		return;
312	}
313	if (keysym == XK_Escape) {
314		keysym = 27;
315	}
316	if (control) {
317		/* shift down to the "control" zone */
318		if (keysym >= 'a' && keysym <= 'z') {
319			keysym -= ('a' - 1);
320		} else if (keysym >= 'A' && keysym <= 'Z') {
321			keysym -= ('A' - 1);
322		} else {
323			keysym = 0xffff;
324		}
325	} else if (alt) {
326		/* shift up to the upper half Latin zone */
327		if (keysym >= '!' && keysym <= '~') {
328			keysym += 128;
329		}
330	}
331	if (debug_keyboard) fprintf(stderr, "keysym now: %d\n", (int) keysym);
332	if (keysym == XK_Tab) {
333		keysym = '\t';
334	} else if (keysym == XK_Return || keysym == XK_KP_Enter) {
335		keysym = '\r';
336	} else if (keysym == XK_BackSpace) {
337		keysym = 8;
338	} else if (keysym == XK_Home || keysym == XK_KP_Home) {
339		keysym = 1;
340	} else if (keysym == XK_End || keysym == XK_KP_End) {
341		keysym = 5;
342	} else if (keysym == XK_Up || keysym == XK_KP_Up) {
343		keysym = 16;
344	} else if (keysym == XK_Down || keysym == XK_KP_Down) {
345		keysym = 14;
346	} else if (keysym == XK_Right || keysym == XK_KP_Right) {
347		keysym = 6;
348	} else if (keysym == XK_Next || keysym == XK_KP_Next) {
349		keysym = 6;
350	} else if (keysym == XK_Left || keysym == XK_KP_Left) {
351		keysym = 2;
352	} else if (keysym == XK_Prior || keysym == XK_KP_Prior) {
353		keysym = 2;
354	} else {
355		if (keysym >= XK_KP_Multiply && keysym <= XK_KP_Equal) {
356			keysym -= 0xFF80;
357		}
358	}
359#if LIBVNCSERVER_HAVE_SYS_IOCTL_H && defined(TIOCSTI)
360	if (keysym < 0x100) {
361		if (ioctl(pipeinput_cons_fd, TIOCSTI, &keysym) != -1) {
362			return;
363		}
364		perror("ioctl");
365		close(pipeinput_cons_fd);
366		pipeinput_cons_fd = -1;
367		if (! pipeinput_cons_dev) {
368			return;
369		}
370		pipeinput_cons_fd = open(pipeinput_cons_dev, O_WRONLY);
371		if (pipeinput_cons_fd < 0) {
372			rfbLog("pipeinput: could not reopen %s\n",
373			    pipeinput_cons_dev);
374			perror("open");
375			return;
376		}
377		if (ioctl(pipeinput_cons_fd, TIOCSTI, &keysym) == -1) {
378			perror("ioctl");
379			close(pipeinput_cons_fd);
380			pipeinput_cons_fd = -1;
381			rfbLog("pipeinput: could not reopen %s\n",
382			    pipeinput_cons_dev);
383		}
384	}
385#endif
386
387	if (client) {}
388}
389
390void console_pointer_command(int mask, int x, int y, rfbClientPtr client) {
391	/* do not forget viewonly perms */
392	if (mask || x || y || client) {}
393}
394
395