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/* -- macosx.c -- */
34
35#include "rfb/rfbconfig.h"
36#if (defined(__MACH__) && defined(__APPLE__) && defined(LIBVNCSERVER_HAVE_MACOSX_NATIVE_DISPLAY))
37
38#define DOMAC 1
39
40#else
41
42#define DOMAC 0
43
44#endif
45
46#include "x11vnc.h"
47#include "cleanup.h"
48#include "scan.h"
49#include "screen.h"
50#include "pointer.h"
51#include "allowed_input_t.h"
52#include "keyboard.h"
53#include "cursor.h"
54#include "connections.h"
55#include "macosxCG.h"
56#include "macosxCGP.h"
57#include "macosxCGS.h"
58
59void macosx_log(char *);
60char *macosx_console_guess(char *str, int *fd);
61void macosx_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client);
62void macosx_pointer_command(int mask, int x, int y, rfbClientPtr client);
63char *macosx_get_fb_addr(void);
64int macosx_get_cursor(void);
65int macosx_get_cursor_pos(int *, int *);
66void macosx_send_sel(char *, int);
67void macosx_set_sel(char *, int);
68int macosx_valid_window(Window, XWindowAttributes*);
69
70Status macosx_xquerytree(Window w, Window *root_return, Window *parent_return,
71    Window **children_return, unsigned int *nchildren_return);
72int macosx_get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h,
73    Window *frame, Window *win);
74
75void macosx_add_mapnotify(Window win, int level, int map);
76void macosx_add_create(Window win, int level);
77void macosx_add_destroy(Window win, int level);
78void macosx_add_visnotify(Window win, int level, int obscured);
79int macosx_checkevent(XEvent *ev);
80
81void macosx_log(char *str) {
82	rfbLog(str);
83}
84
85#if (! DOMAC)
86
87void macosx_event_loop(void) {
88	return;
89}
90char *macosx_console_guess(char *str, int *fd) {
91	if (!str || !fd) {}
92	return NULL;
93}
94void macosx_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
95	if (!down || !keysym || !client) {}
96	return;
97}
98void macosx_pointer_command(int mask, int x, int y, rfbClientPtr client) {
99	if (!mask || !x || !y || !client) {}
100	return;
101}
102char *macosx_get_fb_addr(void) {
103	return NULL;
104}
105int macosx_get_cursor(void) {
106	return 0;
107}
108int macosx_get_cursor_pos(int *x, int *y) {
109	if (!x || !y) {}
110	return 0;
111}
112void macosx_send_sel(char * str, int len) {
113	if (!str || !len) {}
114	return;
115}
116void macosx_set_sel(char * str, int len) {
117	if (!str || !len) {}
118	return;
119}
120int macosx_valid_window(Window w, XWindowAttributes* a) {
121	if (!w || !a) {}
122	return 0;
123}
124Status macosx_xquerytree(Window w, Window *root_return, Window *parent_return,
125    Window **children_return, unsigned int *nchildren_return) {
126	if (!w || !root_return || !parent_return || !children_return || !nchildren_return) {}
127	return (Status) 0;
128}
129void macosx_add_mapnotify(Window win, int level, int map) {
130	if (!win || !level || !map) {}
131	return;
132}
133void macosx_add_create(Window win, int level) {
134	if (!win || !level) {}
135	return;
136}
137void macosx_add_destroy(Window win, int level) {
138	if (!win || !level) {}
139	return;
140}
141void macosx_add_visnotify(Window win, int level, int obscured) {
142	if (!win || !level || !obscured) {}
143	return;
144}
145
146int macosx_checkevent(XEvent *ev) {
147	if (!ev) {}
148	return 0;
149}
150
151
152#else
153
154void macosx_event_loop(void) {
155	macosxCG_event_loop();
156}
157
158char *macosx_get_fb_addr(void) {
159	macosxCG_init();
160	return macosxCG_get_fb_addr();
161}
162
163int macosx_opengl_get_width(void);
164int macosx_opengl_get_height(void);
165int macosx_opengl_get_bpp(void);
166int macosx_opengl_get_bps(void);
167int macosx_opengl_get_spp(void);
168
169char *macosx_console_guess(char *str, int *fd) {
170	char *q, *in = strdup(str);
171	char *atparms = NULL, *file = NULL;
172
173	macosxCG_init();
174
175	if (strstr(in, "console") != in) {
176		rfbLog("console_guess: unrecognized console/fb format: %s\n", str);
177		free(in);
178		return NULL;
179	}
180
181	*fd = -1;
182
183	q = strrchr(in, '@');
184	if (q) {
185		atparms = strdup(q+1);
186		*q = '\0';
187	}
188	q = strrchr(in, ':');
189	if (q) {
190		file = strdup(q+1);
191		*q = '\0';
192	}
193	if (! file || file[0] == '\0')  {
194		file = strdup("/dev/null");
195	}
196	rfbLog("console_guess: file is %s\n", file);
197
198	if (! pipeinput_str) {
199		pipeinput_str = strdup("MACOSX");
200		initialize_pipeinput();
201	}
202
203	if (! atparms) {
204		int w, h, b, bps, dep;
205		unsigned long rm = 0, gm = 0, bm = 0;
206
207		if (macosx_read_opengl) {
208			w = macosx_opengl_get_width();
209			h = macosx_opengl_get_height();
210			b = macosx_opengl_get_bpp();
211
212			bps = macosx_opengl_get_bps();
213			dep = macosx_opengl_get_spp() * bps;
214
215		} else {
216			w = macosxCG_CGDisplayPixelsWide();
217			h = macosxCG_CGDisplayPixelsHigh();
218			b = macosxCG_CGDisplayBitsPerPixel();
219
220			bps = macosxCG_CGDisplayBitsPerSample();
221			dep = macosxCG_CGDisplaySamplesPerPixel() * bps;
222		}
223
224		rm = (1 << bps) - 1;
225		gm = (1 << bps) - 1;
226		bm = (1 << bps) - 1;
227		rm = rm << 2 * bps;
228		gm = gm << 1 * bps;
229		bm = bm << 0 * bps;
230
231		if (b == 8 && rm == 0xff && gm == 0xff && bm == 0xff) {
232			/* I don't believe it... */
233			rm = 0x07;
234			gm = 0x38;
235			bm = 0xc0;
236		}
237
238		/* @66666x66666x32:0xffffffff:... */
239		atparms = (char *) malloc(200);
240		sprintf(atparms, "%dx%dx%d:%lx/%lx/%lx", w, h, b, rm, gm, bm);
241	}
242	if (atparms) {
243		int gw, gh, gb;
244		if (sscanf(atparms, "%dx%dx%d", &gw, &gh, &gb) == 3)  {
245			fb_x = gw;
246			fb_y = gh;
247			fb_b = gb;
248		}
249	}
250	if (! atparms) {
251		rfbLog("console_guess: could not get @ parameters.\n");
252		return NULL;
253	}
254
255	q = (char *) malloc(strlen("map:macosx:") + strlen(file) + 1 + strlen(atparms) + 1);
256	sprintf(q, "map:macosx:%s@%s", file, atparms);
257	free(atparms);
258	return q;
259}
260
261Window macosx_click_frame = None;
262
263void macosx_pointer_command(int mask, int x, int y, rfbClientPtr client) {
264	allowed_input_t input;
265	static int last_mask = 0;
266	int rc;
267
268	if (0) fprintf(stderr, "macosx_pointer_command: %d %d - %d\n", x, y, mask);
269
270	if (mask >= 0) {
271		got_pointer_calls++;
272	}
273
274	if (view_only) {
275		return;
276	}
277
278	get_allowed_input(client, &input);
279
280	if (! input.motion || ! input.button) {
281		/* XXX fix me with last_x, last_y, etc. */
282		return;
283	}
284
285	if (mask >= 0) {
286		got_user_input++;
287		got_pointer_input++;
288		last_pointer_client = client;
289		last_pointer_time = time(NULL);
290	}
291	if (last_mask != mask) {
292		if (0) fprintf(stderr, "about to inject mask change %d -> %d: %.4f\n", last_mask, mask, dnowx());
293		if (mask) {
294			int px, py, x, y, w, h;
295			macosx_click_frame = None;
296			if (!macosx_get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &macosx_click_frame, NULL)) {
297				macosx_click_frame = None;
298			}
299		}
300	}
301
302	macosxCG_pointer_inject(mask, x, y);
303
304	if (cursor_x != x || cursor_y != y) {
305		last_pointer_motion_time = dnow();
306	}
307
308	cursor_x = x;
309	cursor_y = y;
310
311	if (last_mask != mask) {
312		last_pointer_click_time = dnow();
313		if (ncache > 0) {
314			/* XXX Y */
315			int i;
316if (0) fprintf(stderr, "about to get all windows:           %.4f\n", dnowx());
317			for (i=0; i < 2; i++) {
318				macosxCGS_get_all_windows();
319				if (0) fprintf(stderr, "!");
320				if (macosx_checkevent(NULL)) {
321					break;
322				}
323			}
324if (0) fprintf(stderr, "\ndone:                               %.4f\n", dnowx());
325		}
326	}
327	last_mask = mask;
328
329	/* record the x, y position for the rfb screen as well. */
330	cursor_position(x, y);
331
332	/* change the cursor shape if necessary */
333	rc = set_cursor(x, y, get_which_cursor());
334	cursor_changes += rc;
335
336	last_event = last_input = last_pointer_input = time(NULL);
337}
338
339void init_key_table(void) {
340	macosxCG_init_key_table();
341}
342
343void macosx_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
344	allowed_input_t input;
345	if (debug_keyboard) fprintf(stderr, "macosx_key_command: %d %s\n", (int) keysym, down ? "down" : "up");
346
347	if (view_only) {
348		return;
349	}
350	get_allowed_input(client, &input);
351	if (! input.keystroke) {
352		return;
353	}
354
355	init_key_table();
356	macosxCG_keysym_inject((int) down, (unsigned int) keysym);
357}
358
359extern void macosxGCS_poll_pb(void);
360
361int macosx_get_cursor_pos(int *x, int *y) {
362	macosxCG_get_cursor_pos(x, y);
363	if (nofb) {
364		/* good time to poll the pasteboard */
365		macosxGCS_poll_pb();
366	}
367	return 1;
368}
369
370static char *cuttext = NULL;
371static int cutlen = 0;
372
373void macosx_send_sel(char *str, int len) {
374	if (screen && all_clients_initialized()) {
375		if (cuttext) {
376			int n = cutlen;
377			if (len < n) {
378				n = len;
379			}
380			if (!memcmp(str, cuttext, (size_t) n)) {
381				/* the same text we set pasteboard to ... */
382				return;
383			}
384		}
385		if (debug_sel) {
386			rfbLog("macosx_send_sel: %d\n", len);
387		}
388		rfbSendServerCutText(screen, str, len);
389	}
390}
391
392void macosx_set_sel(char *str, int len) {
393	if (screen && all_clients_initialized()) {
394		if (cutlen <= len) {
395			if (cuttext) {
396				free(cuttext);
397			}
398			cutlen = 2*(len+1);
399			cuttext = (char *) calloc(cutlen, 1);
400		}
401		memcpy(cuttext, str, (size_t) len);
402		cuttext[len] = '\0';
403		if (debug_sel) {
404			rfbLog("macosx_set_sel: %d\n", len);
405		}
406		macosxGCS_set_pasteboard(str, len);
407	}
408}
409
410int macosx_get_cursor(void) {
411	return macosxCG_get_cursor();
412}
413
414typedef struct evdat {
415	int win;
416	int map;
417	int level;
418	int vis;
419	int type;
420} evdat_t;
421
422#define MAX_EVENTS 1024
423evdat_t mac_events[MAX_EVENTS];
424int mac_events_ptr = 0;
425int mac_events_last = 0;
426
427void macosx_add_mapnotify(Window win, int level, int map) {
428	int i = mac_events_last++;
429	mac_events[i].win = win;
430	mac_events[i].level = level;
431
432	if (map) {
433		mac_events[i].type = MapNotify;
434	} else {
435		mac_events[i].type = UnmapNotify;
436	}
437	mac_events[i].map = map;
438	mac_events[i].vis = -1;
439
440	mac_events_last = mac_events_last % MAX_EVENTS;
441
442	return;
443}
444
445void macosx_add_create(Window win, int level) {
446	int i = mac_events_last++;
447	mac_events[i].win = win;
448	mac_events[i].level = level;
449
450	mac_events[i].type = CreateNotify;
451	mac_events[i].map = -1;
452	mac_events[i].vis = -1;
453
454	mac_events_last = mac_events_last % MAX_EVENTS;
455
456	return;
457}
458
459void macosx_add_destroy(Window win, int level) {
460	int i = mac_events_last++;
461	mac_events[i].win = win;
462	mac_events[i].level = level;
463
464	mac_events[i].type = DestroyNotify;
465	mac_events[i].map = -1;
466	mac_events[i].vis = -1;
467
468	mac_events_last = mac_events_last % MAX_EVENTS;
469
470	return;
471}
472
473void macosx_add_visnotify(Window win, int level, int obscured) {
474	int i = mac_events_last++;
475	mac_events[i].win = win;
476	mac_events[i].level = level;
477
478	mac_events[i].type = VisibilityNotify;
479	mac_events[i].map = -1;
480
481	mac_events[i].vis = 1;
482	if (obscured == 0) {
483		mac_events[i].vis = VisibilityUnobscured;
484	} else if (obscured == 1) {
485		mac_events[i].vis = VisibilityPartiallyObscured;
486	} else if (obscured == 2) {
487		mac_events[i].vis = VisibilityFullyObscured; 	/* NI */
488	}
489
490	mac_events_last = mac_events_last % MAX_EVENTS;
491
492	return;
493}
494
495int macosx_checkevent(XEvent *ev) {
496	int i = mac_events_ptr;
497
498	if (mac_events_ptr == mac_events_last) {
499		return 0;
500	}
501	if (ev == NULL) {
502		return mac_events[i].type;
503	}
504
505	ev->xany.window = mac_events[i].win;
506
507	if (mac_events[i].type == CreateNotify) {
508		ev->type = CreateNotify;
509		ev->xany.window = rootwin;
510		ev->xcreatewindow.window = mac_events[i].win;
511	} else if (mac_events[i].type == DestroyNotify) {
512		ev->type = DestroyNotify;
513		ev->xdestroywindow.window = mac_events[i].win;
514	} else if (mac_events[i].type == VisibilityNotify) {
515		ev->type = VisibilityNotify;
516		ev->xvisibility.state = mac_events[i].vis;
517	} else if (mac_events[i].type == MapNotify) {
518		ev->type = MapNotify;
519	} else if (mac_events[i].type == UnmapNotify) {
520		ev->type = UnmapNotify;
521	} else {
522		fprintf(stderr, "unknown macosx_checkevent: %d\n", mac_events[i].type);
523	}
524	mac_events_ptr++;
525	mac_events_ptr = mac_events_ptr % MAX_EVENTS;
526
527	return mac_events[i].type;
528}
529
530typedef struct windat {
531	int win;
532	int x, y;
533	int width, height;
534	int level;
535	int mapped;
536	int clipped;
537	int ncache_only;
538} windat_t;
539
540extern int macwinmax;
541extern windat_t macwins[];
542
543int macosx_get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h,
544    Window *frame, Window *win) {
545	static int last_idx = -1;
546	int x1, x2, y1, y2;
547	int idx = -1, k;
548	macosxCGS_get_all_windows();
549	macosxCG_get_cursor_pos(px, py);
550
551	for (k = 0; k<macwinmax; k++) {
552		if (! macwins[k].mapped) {
553			continue;
554		}
555		x1 = macwins[k].x;
556		x2 = macwins[k].x + macwins[k].width;
557		y1 = macwins[k].y;
558		y2 = macwins[k].y + macwins[k].height;
559if (debug_wireframe) fprintf(stderr, "%d/%d:	%d %d %d  - %d %d %d\n", k, macwins[k].win, x1, *px, x2, y1, *py, y2);
560		if (x1 <= *px && *px < x2) {
561			if (y1 <= *py && *py < y2) {
562				idx = k;
563				break;
564			}
565		}
566	}
567	if (idx < 0) {
568		return 0;
569	}
570
571	*x = macwins[idx].x;
572	*y = macwins[idx].y;
573	*w = macwins[idx].width;
574	*h = macwins[idx].height;
575	*frame = (Window) macwins[idx].win;
576	if (win != NULL) {
577		*win = *frame;
578	}
579
580	last_idx = idx;
581
582	return 1;
583}
584
585int macosx_valid_window(Window w, XWindowAttributes* a) {
586	static int last_idx = -1;
587	int win = (int) w;
588	int i, k, idx = -1;
589
590	if (last_idx >= 0 && last_idx < macwinmax) {
591		if (macwins[last_idx].win == win) {
592			idx = last_idx;
593		}
594	}
595
596	if (idx < 0) {
597		idx = macosxCGS_get_qlook(w);
598		if (idx >= 0 && idx < macwinmax) {
599			if (macwins[idx].win != win) {
600				idx = -1;
601			}
602		} else {
603			idx = -1;
604		}
605	}
606
607	if (idx < 0) {
608		for (i = 0; i<macwinmax; i++) {
609			k = i;
610			if (i == -1)  {
611				if (last_idx >= 0 && last_idx < macwinmax) {
612					k = last_idx;
613				} else {
614					last_idx = -1;
615					continue;
616				}
617			}
618			if (macwins[k].win == win) {
619				idx = k;
620				break;
621			}
622		}
623	}
624	if (idx < 0) {
625		return 0;
626	}
627
628	a->x = macwins[idx].x;
629	a->y = macwins[idx].y;
630	a->width  = macwins[idx].width;
631	a->height = macwins[idx].height;
632	a->depth = depth;
633	a->border_width = 0;
634	a->backing_store = 0;
635	if (macwins[idx].mapped) {
636		a->map_state = IsViewable;
637	} else {
638		a->map_state = IsUnmapped;
639	}
640
641	last_idx = idx;
642
643	return 1;
644}
645
646#define QTMAX 2048
647static Window cret[QTMAX];
648
649extern int CGS_levelmax;
650extern int CGS_levels[];
651
652Status macosx_xquerytree(Window w, Window *root_return, Window *parent_return,
653    Window **children_return, unsigned int *nchildren_return) {
654
655	int i, n, k;
656
657	*root_return = (Window) 0;
658	*parent_return = (Window) 0;
659	if (!w) {}
660
661	macosxCGS_get_all_windows();
662
663	n = 0;
664	for (k = CGS_levelmax - 1; k >= 0; k--) {
665		for (i = macwinmax - 1; i >= 0; i--) {
666			if (n >= QTMAX) break;
667			if (macwins[i].level == CGS_levels[k]) {
668if (0) fprintf(stderr, "k=%d i=%d n=%d\n", k, i, n);
669				cret[n++] = (Window) macwins[i].win;
670			}
671		}
672	}
673	*children_return = cret;
674	*nchildren_return = (unsigned int) macwinmax;
675
676	return (Status) 1;
677}
678
679int macosx_check_offscreen(int win) {
680	sraRegionPtr r0, r1;
681	int x1, y1, x2, y2;
682	int ret;
683	int i = macosxCGS_find_index(win);
684
685	if (i < 0) {
686		return 0;
687	}
688
689	x1 = macwins[i].x;
690	y1 = macwins[i].y;
691	x2 = macwins[i].x + macwins[i].width;
692	y2 = macwins[i].y + macwins[i].height;
693
694	r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
695	r1 = sraRgnCreateRect(x1, y1, x2, y2);
696
697	if (sraRgnAnd(r1, r0)) {
698		ret = 0;
699	} else {
700		ret = 1;
701	}
702	sraRgnDestroy(r0);
703	sraRgnDestroy(r1);
704
705	return ret;
706}
707
708int macosx_check_clipped(int win, int *list, int n) {
709	sraRegionPtr r0, r1, r2;
710	int x1, y1, x2, y2;
711	int ret = 0;
712	int k, j, i = macosxCGS_find_index(win);
713
714	if (i < 0) {
715		return 0;
716	}
717
718	x1 = macwins[i].x;
719	y1 = macwins[i].y;
720	x2 = macwins[i].x + macwins[i].width;
721	y2 = macwins[i].y + macwins[i].height;
722
723	r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
724	r1 = sraRgnCreateRect(x1, y1, x2, y2);
725	sraRgnAnd(r1, r0);
726
727	for (k = 0; k < n; k++) {
728		j = macosxCGS_find_index(list[k]);	/* XXX slow? */
729		if (j < 0) {
730			continue;
731		}
732		x1 = macwins[j].x;
733		y1 = macwins[j].y;
734		x2 = macwins[j].x + macwins[j].width;
735		y2 = macwins[j].y + macwins[j].height;
736		r2 = sraRgnCreateRect(x1, y1, x2, y2);
737		if (sraRgnAnd(r2, r1)) {
738			ret = 1;
739			sraRgnDestroy(r2);
740			break;
741		}
742		sraRgnDestroy(r2);
743	}
744	sraRgnDestroy(r0);
745	sraRgnDestroy(r1);
746
747	return ret;
748}
749
750
751#endif 	/* LIBVNCSERVER_HAVE_MACOSX_NATIVE_DISPLAY */
752
753