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