1 /*
2    Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3    All rights reserved.
4 
5 This file is part of x11vnc.
6 
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11 
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21 
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables.  You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL".  If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so.  If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32 
33 /* -- macosxCGS.c -- */
34 
35 /*
36  * We need to keep this separate from nearly everything else, e.g. rfb.h
37  * and the other stuff, otherwise it does not work properly, mouse drags
38  * will not work!!
39  */
macosxCGS_unused(void)40 void macosxCGS_unused(void) {}
41 
42 #if (defined(__MACH__) && defined(__APPLE__))
43 
44 #include <ApplicationServices/ApplicationServices.h>
45 #include <Cocoa/Cocoa.h>
46 #include <Carbon/Carbon.h>
47 
48 extern CGDirectDisplayID displayID;
49 
50 void macosxCGS_get_all_windows(void);
51 int macosxCGS_get_qlook(int);
52 void macosxGCS_set_pasteboard(char *str, int len);
53 
54 typedef CGError       CGSError;
55 typedef long          CGSWindowCount;
56 typedef void *        CGSConnectionID;
57 typedef int           CGSWindowID;
58 typedef CGSWindowID*  CGSWindowIDList;
59 typedef CGWindowLevel CGSWindowLevel;
60 typedef NSRect        CGSRect;
61 
62 extern CGSConnectionID _CGSDefaultConnection ();
63 
64 extern CGSError CGSGetOnScreenWindowList (CGSConnectionID cid,
65     CGSConnectionID owner, CGSWindowCount listCapacity,
66     CGSWindowIDList list, CGSWindowCount *listCount);
67 
68 extern CGSError CGSGetWindowList (CGSConnectionID cid,
69     CGSConnectionID owner, CGSWindowCount listCapacity,
70     CGSWindowIDList list, CGSWindowCount *listCount);
71 
72 extern CGSError CGSGetScreenRectForWindow (CGSConnectionID cid,
73     CGSWindowID wid, CGSRect *rect);
74 
75 extern CGWindowLevel CGSGetWindowLevel (CGSConnectionID cid,
76     CGSWindowID wid, CGSWindowLevel *level);
77 
78 typedef enum _CGSWindowOrderingMode {
79     kCGSOrderAbove                =  1, /* Window is ordered above target. */
80     kCGSOrderBelow                = -1, /* Window is ordered below target. */
81     kCGSOrderOut                  =  0  /* Window is removed from the on-screen window list. */
82 } CGSWindowOrderingMode;
83 
84 extern OSStatus CGSOrderWindow(const CGSConnectionID cid,
85     const CGSWindowID wid, CGSWindowOrderingMode place, CGSWindowID relativeToWindowID);
86 
87 static CGSConnectionID cid = NULL;
88 
89 extern void macosx_log(char *);
90 
91 int macwinmax = 0;
92 typedef struct windat {
93 	int win;
94 	int x, y;
95 	int width, height;
96 	int level;
97 	int mapped;
98 	int clipped;
99 	int ncache_only;
100 } windat_t;
101 
102 extern int ncache;
103 
104 #define MAXWINDAT 4096
105 windat_t macwins[MAXWINDAT];
106 static CGSWindowID _wins_all[MAXWINDAT];
107 static CGSWindowID _wins_mapped[MAXWINDAT];
108 static CGSWindowCount _wins_all_cnt, _wins_mapped_cnt;
109 static int _wins_int[MAXWINDAT];
110 
111 #define WINHISTNUM 32768
112 #define WINHISTMAX 4
113 char whist[WINHISTMAX][WINHISTNUM];
114 int whist_idx = -1;
115 int qlook[WINHISTNUM];
116 
117 char is_exist     = 0x1;
118 char is_mapped    = 0x2;
119 char is_clipped   = 0x4;
120 char is_offscreen = 0x8;
121 
122 extern double dnow(void);
123 extern double dnowx(void);
124 
125 extern int dpy_x, dpy_y;
126 extern int macosx_icon_anim_time;
127 
128 extern void macosx_add_mapnotify(int, int, int);
129 extern void macosx_add_create(int, int);
130 extern void macosx_add_destroy(int, int);
131 extern void macosx_add_visnotify(int, int, int);
132 
133 int CGS_levelmax;
134 int CGS_levels[16];
135 
macosxCGS_get_qlook(int w)136 int macosxCGS_get_qlook(int w) {
137 	if (w >= WINHISTNUM) {
138 		return -1;
139 	}
140 	return qlook[w];
141 }
142 
macosxCGS_find_index(int w)143 int macosxCGS_find_index(int w) {
144 	static int last_index = -1;
145 	int idx;
146 
147 	if (last_index >= 0) {
148 		if (macwins[last_index].win == w) {
149 			return last_index;
150 		}
151 	}
152 
153 	idx = macosxCGS_get_qlook(w);
154 	if (idx >= 0) {
155 		if (macwins[idx].win == w) {
156 			last_index = idx;
157 			return idx;
158 		}
159 	}
160 
161 	for (idx=0; idx < macwinmax; idx++) {
162 		if (macwins[idx].win == w) {
163 			last_index = idx;
164 			return idx;
165 		}
166 	}
167 	return -1;
168 }
169 
170 #if 0
171 extern void usleep(unsigned long usec);
172 #else
173 extern int usleep(useconds_t usec);
174 #endif
175 
macosxCGS_follow_animation_win(int win,int idx,int grow)176 int macosxCGS_follow_animation_win(int win, int idx, int grow) {
177 	double t = dnow();
178 	int diffs = 0;
179 	int x, y, w, h;
180 	int xp = -1, yp = -1, wp = -1, hp = -1;
181 	CGSRect rect;
182 	CGSError err;
183 
184 	int reps = 0;
185 
186 	if (cid == NULL) {
187 		cid = _CGSDefaultConnection();
188 		if (cid == NULL) {
189 			return 0;
190 		}
191 	}
192 
193 	if (idx < 0) {
194 		idx = macosxCGS_find_index(win);
195 	}
196 	if (idx < 0) {
197 		return 0;
198 	}
199 
200 	while (dnow() < t + 0.001 * macosx_icon_anim_time)  {
201 		err = CGSGetScreenRectForWindow(cid, win, &rect);
202 		if (err != 0) {
203 			break;
204 		}
205 		x = (int) rect.origin.x;
206 		y = (int) rect.origin.y;
207 		w = (int) rect.size.width;
208 		h = (int) rect.size.height;
209 
210 		if (grow) {
211 			macwins[idx].x      = x;
212 			macwins[idx].y      = y;
213 			macwins[idx].width  = w;
214 			macwins[idx].height = h;
215 		}
216 
217 		if (0) fprintf(stderr, " chase: %03dx%03d+%03d+%03d  %d\n", w, h, x, y, win);
218 		if (x == xp && y == yp && w == wp && h == hp)  {
219 			reps++;
220 			if (reps >= 2) {
221 				break;
222 			}
223 		} else {
224 			diffs++;
225 			reps = 0;
226 		}
227 		xp = x;
228 		yp = y;
229 		wp = w;
230 		hp = h;
231 		usleep(50 * 1000);
232 	}
233 	if (diffs >= 2) {
234 		return 1;
235 	} else {
236 		return 0;
237 	}
238 }
239 
240 extern int macosx_check_clipped(int win, int *list, int n);
241 extern int macosx_check_offscreen(int win);
242 
check_clipped(int win)243 static int check_clipped(int win) {
244 	int i, n = 0, win2;
245 	for (i = 0; i < (int) _wins_mapped_cnt; i++) {
246 		win2 = (int) _wins_mapped[i];
247 		if (win2 == win) {
248 			break;
249 		}
250 		_wins_int[n++] = win2;
251 	}
252 	return macosx_check_clipped(win, _wins_int, n);
253 }
254 
check_offscreen(int win)255 static int check_offscreen(int win) {
256 	return macosx_check_offscreen(win);
257 }
258 
259 extern int macosx_ncache_macmenu;
260 
261 
macosxCGS_get_all_windows(void)262 void macosxCGS_get_all_windows(void) {
263 	static double last = 0.0;
264 	static int totcnt = 0;
265 	double dt = 0.0, now = dnow();
266 	int i, db = 0, whist_prv = 0, maxwin = 0, whist_skip = 0;
267 	CGSWindowCount cap = (CGSWindowCount) MAXWINDAT;
268 	CGSError err;
269 
270 	CGS_levelmax = 0;
271 	CGS_levels[CGS_levelmax++] = (int) kCGDraggingWindowLevel;	/* 500 ? */
272 	if (0) CGS_levels[CGS_levelmax++] = (int) kCGHelpWindowLevel;		/* 102 ? */
273 	if (macosx_ncache_macmenu) CGS_levels[CGS_levelmax++] = (int) kCGPopUpMenuWindowLevel;	/* 101 pulldown menu */
274 	CGS_levels[CGS_levelmax++] = (int) kCGMainMenuWindowLevelKey;	/*  24 ? */
275 	CGS_levels[CGS_levelmax++] = (int) kCGModalPanelWindowLevel;	/*   8 open dialog box */
276 	CGS_levels[CGS_levelmax++] = (int) kCGFloatingWindowLevel;	/*   3 ? */
277 	CGS_levels[CGS_levelmax++] = (int) kCGNormalWindowLevel;	/*   0 regular window */
278 
279 	if (cid == NULL) {
280 		cid = _CGSDefaultConnection();
281 		if (cid == NULL) {
282 			return;
283 		}
284 	}
285 
286 	if (dt > 0.0 && now < last + dt) {
287 		return;
288 	}
289 
290 	last = now;
291 
292 	macwinmax = 0;
293 
294 	totcnt++;
295 
296 	if (ncache > 0) {
297 		whist_prv = whist_idx++;
298 		if (whist_prv < 0) {
299 			whist_skip = 1;
300 			whist_prv = 0;
301 		}
302 		whist_idx = whist_idx % WINHISTMAX;
303 		for (i=0; i < WINHISTNUM; i++) {
304 			whist[whist_idx][i] = 0;
305 			qlook[i] = -1;
306 		}
307 	}
308 
309 	err = CGSGetWindowList(cid, NULL, cap, _wins_all, &_wins_all_cnt);
310 
311 if (db) fprintf(stderr, "cnt: %d err: %d\n", (int) _wins_all_cnt, err);
312 
313 	if (err != 0) {
314 		return;
315 	}
316 
317 	for (i=0; i < (int) _wins_all_cnt; i++) {
318 		CGSRect rect;
319 		CGSWindowLevel level;
320 		int j, keepit = 0;
321 		err = CGSGetScreenRectForWindow(cid, _wins_all[i], &rect);
322 		if (err != 0) {
323 			continue;
324 		}
325 		if (rect.origin.x == 0 && rect.origin.y == 0) {
326 			if (rect.size.width == dpy_x) {
327 				if (rect.size.height == dpy_y) {
328 					continue;
329 				}
330 			}
331 		}
332 		err = CGSGetWindowLevel(cid, _wins_all[i], &level);
333 		if (err != 0) {
334 			continue;
335 		}
336 		for (j=0; j<CGS_levelmax; j++) {
337 			if ((int) level == CGS_levels[j]) {
338 				keepit = 1;
339 				break;
340 			}
341 		}
342 		if (! keepit) {
343 			continue;
344 		}
345 
346 		macwins[macwinmax].level  = (int) level;
347 		macwins[macwinmax].win    = (int) _wins_all[i];
348 		macwins[macwinmax].x      = (int) rect.origin.x;
349 		macwins[macwinmax].y      = (int) rect.origin.y;
350 		macwins[macwinmax].width  = (int) rect.size.width;
351 		macwins[macwinmax].height = (int) rect.size.height;
352 		macwins[macwinmax].mapped = 0;
353 		macwins[macwinmax].clipped = 0;
354 		macwins[macwinmax].ncache_only = 0;
355 		if (level == kCGPopUpMenuWindowLevel) {
356 			macwins[macwinmax].ncache_only = 1;
357 		}
358 
359 if (0 || db) fprintf(stderr, "i=%03d ID: %06d  x: %03d  y: %03d  w: %03d h: %03d level: %d\n", i, _wins_all[i],
360     (int) rect.origin.x, (int) rect.origin.y,(int) rect.size.width, (int) rect.size.height, (int) level);
361 
362 		if (macwins[macwinmax].win < WINHISTNUM) {
363 			qlook[macwins[macwinmax].win] = macwinmax;
364 			if (macwins[macwinmax].win > maxwin) {
365 				maxwin = macwins[macwinmax].win;
366 			}
367 		}
368 
369 		macwinmax++;
370 	}
371 
372 	err = CGSGetOnScreenWindowList(cid, NULL, cap, _wins_mapped, &_wins_mapped_cnt);
373 
374 if (db) fprintf(stderr, "cnt: %d err: %d\n", (int) _wins_mapped_cnt, err);
375 
376 	if (err != 0) {
377 		return;
378 	}
379 
380 	for (i=0; i < (int) _wins_mapped_cnt; i++) {
381 		int j, idx = -1;
382 		int win = (int) _wins_mapped[i];
383 
384 		if (0 <= win && win < WINHISTNUM) {
385 			j = qlook[win];
386 			if (j >= 0 && macwins[j].win == win) {
387 				idx = j;
388 			}
389 		}
390 		if (idx < 0) {
391 			for (j=0; j < macwinmax; j++) {
392 				if (macwins[j].win == win) {
393 					idx = j;
394 					break;
395 				}
396 			}
397 		}
398 		if (idx >= 0) {
399 			macwins[idx].mapped = 1;
400 		}
401 	}
402 
403 	if (ncache > 0) {
404 		int nv= 0, NBMAX = 64;
405 		int nv_win[64];
406 		int nv_lvl[64];
407 		int nv_vis[64];
408 
409 		for (i=0; i < macwinmax; i++) {
410 			int win = macwins[i].win;
411 			char prev, curr;
412 
413 			if (win >= WINHISTNUM) {
414 				continue;
415 			}
416 
417 			whist[whist_idx][win] |= is_exist;
418 			if (macwins[i].mapped) {
419 				whist[whist_idx][win] |= is_mapped;
420 				if (check_clipped(win)) {
421 					whist[whist_idx][win] |= is_clipped;
422 					macwins[i].clipped = 1;
423 				}
424 				if (check_offscreen(win)) {
425 					whist[whist_idx][win] |= is_offscreen;
426 				}
427 			} else {
428 				whist[whist_idx][win] |= is_offscreen;
429 			}
430 
431 			curr = whist[whist_idx][win];
432 			prev = whist[whist_prv][win];
433 
434 			if (whist_skip) {
435 				;
436 			} else if ( !(prev & is_mapped) && (curr & is_mapped)) {
437 				/* MapNotify */
438 				if (0) fprintf(stderr, "MapNotify:   %d/%d  %d               %.4f tot=%d\n", prev, curr, win, dnowx(), totcnt);
439 				macosx_add_mapnotify(win, macwins[i].level, 1);
440 				if (0) macosxCGS_follow_animation_win(win, i, 1);
441 
442 			} else if ( !(curr & is_mapped) && (prev & is_mapped)) {
443 				/* UnmapNotify */
444 				if (0) fprintf(stderr, "UnmapNotify: %d/%d  %d               %.4f A tot=%d\n", prev, curr, win, dnowx(), totcnt);
445 				macosx_add_mapnotify(win, macwins[i].level, 0);
446 			} else if ( !(prev & is_exist) && (curr & is_exist)) {
447 				/* CreateNotify */
448 				if (0) fprintf(stderr, "CreateNotify:%d/%d  %d               %.4f whist: %d/%d 0x%x tot=%d\n", prev, curr, win, dnowx(), whist_prv, whist_idx, win, totcnt);
449 				macosx_add_create(win, macwins[i].level);
450 				if (curr & is_mapped) {
451 					if (0) fprintf(stderr, "MapNotify:   %d/%d  %d               %.4f tot=%d\n", prev, curr, win, dnowx(), totcnt);
452 					macosx_add_mapnotify(win, macwins[i].level, 1);
453 				}
454 			}
455 			if (whist_skip) {
456 				;
457 			} else if (nv >= NBMAX) {
458 				;
459 			} else if (!(curr & is_mapped)) {
460 				;
461 			} else if (!(prev & is_mapped)) {
462 				if (1) {
463 					;
464 				} else if (curr & is_clipped) {
465 					if (0) fprintf(stderr, "VisibNotify: %d/%d  %d               OBS tot=%d\n", prev, curr, win, totcnt);
466 					nv_win[nv] = win;
467 					nv_lvl[nv] = macwins[i].level;
468 					nv_vis[nv++] = 1;
469 				} else {
470 					if (0) fprintf(stderr, "VisibNotify: %d/%d  %d               UNOBS tot=%d\n", prev, curr, win, totcnt);
471 					nv_win[nv] = win;
472 					nv_lvl[nv] = macwins[i].level;
473 					nv_vis[nv++] = 0;
474 				}
475 			} else {
476 				if        ( !(prev & is_clipped) &&  (curr & is_clipped) ) {
477 					if (0) fprintf(stderr, "VisibNotify: %d/%d  %d               OBS tot=%d\n", prev, curr, win, totcnt);
478 					nv_win[nv] = win;
479 					nv_lvl[nv] = macwins[i].level;
480 					nv_vis[nv++] = 1;
481 				} else if (  (prev & is_clipped) && !(curr & is_clipped) ) {
482 					if (0) fprintf(stderr, "VisibNotify: %d/%d  %d               UNOBS tot=%d\n", prev, curr, win, totcnt);
483 					nv_win[nv] = win;
484 					nv_lvl[nv] = macwins[i].level;
485 					nv_vis[nv++] = 0;
486 				}
487 			}
488 		}
489 		for (i=0; i < maxwin; i++) {
490 			char prev, curr;
491 			int win = i;
492 			int q = qlook[i];
493 			int lvl = 0;
494 
495 			if (whist_skip) {
496 				break;
497 			}
498 
499 			if (q >= 0) {
500 				lvl = macwins[q].level;
501 			}
502 			curr = whist[whist_idx][win];
503 			prev = whist[whist_prv][win];
504 			if (!(curr & is_exist) && (prev & is_exist)) {
505 				if (prev & is_mapped) {
506 					if (0) fprintf(stderr, "UnmapNotify: %d/%d  %d               %.4f B tot=%d\n", prev, curr, win, dnowx(), totcnt);
507 					macosx_add_mapnotify(win, lvl, 0);
508 				}
509 				/* DestroyNotify */
510 				if (0) fprintf(stderr, "DestroNotify:%d/%d  %d               %.4f tot=%d\n", prev, curr, win, dnowx(), totcnt);
511 				macosx_add_destroy(win, lvl);
512 			}
513 		}
514 		if (nv) {
515 			int k;
516 			for (k = 0; k < nv; k++) {
517 				macosx_add_visnotify(nv_win[k], nv_lvl[k], nv_vis[k]);
518 			}
519 		}
520 	}
521 }
522 
523 #if 1
524 NSLock *pblock = nil;
525 NSString *pbstr = nil;
526 NSString *cuttext = nil;
527 
528 int pbcnt = -1;
529 NSStringEncoding pbenc = NSWindowsCP1252StringEncoding;
530 
macosxGCS_initpb(void)531 void macosxGCS_initpb(void) {
532 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
533 	pblock = [[NSLock alloc] init];
534 	if (![NSPasteboard generalPasteboard]) {
535 		macosx_log("macosxGCS_initpb: **PASTEBOARD INACCESSIBLE**.\n");
536 		macosx_log("macosxGCS_initpb: Clipboard exchange will NOT work.\n");
537 		macosx_log("macosxGCS_initpb: Start x11vnc *inside* Aqua for Clipboard.\n");
538 		pbcnt = 0;
539 		pbstr = [[NSString alloc] initWithString:@"\e<PASTEBOARD INACCESSIBLE>\e"];
540 	}
541 	[pool release];
542 }
543 
macosxGCS_set_pasteboard(char * str,int len)544 void macosxGCS_set_pasteboard(char *str, int len) {
545 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
546 	if (pbcnt != 0) {
547 		[pblock lock];
548 		[cuttext release];
549 		cuttext = [[NSString alloc] initWithData:[NSData dataWithBytes:str length:len] encoding: pbenc];
550 		if ([[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]) {
551 			NS_DURING
552 				[[NSPasteboard generalPasteboard] setString:cuttext forType:NSStringPboardType];
553 			NS_HANDLER
554 				fprintf(stderr, "macosxGCS_set_pasteboard: problem writing to pasteboard\n");
555 			NS_ENDHANDLER
556 		} else {
557 			fprintf(stderr, "macosxGCS_set_pasteboard: problem writing to pasteboard\n");
558 		}
559 		[cuttext release];
560 		cuttext = nil;
561 		[pblock unlock];
562 	}
563 	[pool release];
564 }
565 
566 extern void macosx_send_sel(char *, int);
567 
macosxGCS_poll_pb(void)568 void macosxGCS_poll_pb(void) {
569 
570 	static double dlast = 0.0;
571 	double now = dnow();
572 
573 	if (now < dlast + 0.2) {
574 		return;
575 	}
576 	dlast = now;
577 
578    {
579 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
580 	[pblock lock];
581 	if (pbcnt != [[NSPasteboard generalPasteboard] changeCount]) {
582 		pbcnt = [[NSPasteboard generalPasteboard] changeCount];
583 		[pbstr release];
584 		pbstr = nil;
585 		if ([[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) {
586 			pbstr = [[[NSPasteboard generalPasteboard] stringForType:NSStringPboardType] copy];
587 			if (pbstr) {
588 				NSData *str = [pbstr dataUsingEncoding:pbenc allowLossyConversion:YES];
589 				if ([str length]) {
590 					macosx_send_sel((char *) [str bytes], [str length]);
591 				}
592 			}
593 		}
594 	}
595 	[pblock unlock];
596 	[pool release];
597    }
598 }
599 #endif
600 
601 #endif	/* __APPLE__ */
602 
603