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/* -- 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 */ 40void 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 48extern CGDirectDisplayID displayID; 49 50void macosxCGS_get_all_windows(void); 51int macosxCGS_get_qlook(int); 52void macosxGCS_set_pasteboard(char *str, int len); 53 54typedef CGError CGSError; 55typedef long CGSWindowCount; 56typedef void * CGSConnectionID; 57typedef int CGSWindowID; 58typedef CGSWindowID* CGSWindowIDList; 59typedef CGWindowLevel CGSWindowLevel; 60typedef NSRect CGSRect; 61 62extern CGSConnectionID _CGSDefaultConnection (); 63 64extern CGSError CGSGetOnScreenWindowList (CGSConnectionID cid, 65 CGSConnectionID owner, CGSWindowCount listCapacity, 66 CGSWindowIDList list, CGSWindowCount *listCount); 67 68extern CGSError CGSGetWindowList (CGSConnectionID cid, 69 CGSConnectionID owner, CGSWindowCount listCapacity, 70 CGSWindowIDList list, CGSWindowCount *listCount); 71 72extern CGSError CGSGetScreenRectForWindow (CGSConnectionID cid, 73 CGSWindowID wid, CGSRect *rect); 74 75extern CGWindowLevel CGSGetWindowLevel (CGSConnectionID cid, 76 CGSWindowID wid, CGSWindowLevel *level); 77 78typedef 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 84extern OSStatus CGSOrderWindow(const CGSConnectionID cid, 85 const CGSWindowID wid, CGSWindowOrderingMode place, CGSWindowID relativeToWindowID); 86 87static CGSConnectionID cid = NULL; 88 89extern void macosx_log(char *); 90 91int macwinmax = 0; 92typedef 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 102extern int ncache; 103 104#define MAXWINDAT 4096 105windat_t macwins[MAXWINDAT]; 106static CGSWindowID _wins_all[MAXWINDAT]; 107static CGSWindowID _wins_mapped[MAXWINDAT]; 108static CGSWindowCount _wins_all_cnt, _wins_mapped_cnt; 109static int _wins_int[MAXWINDAT]; 110 111#define WINHISTNUM 32768 112#define WINHISTMAX 4 113char whist[WINHISTMAX][WINHISTNUM]; 114int whist_idx = -1; 115int qlook[WINHISTNUM]; 116 117char is_exist = 0x1; 118char is_mapped = 0x2; 119char is_clipped = 0x4; 120char is_offscreen = 0x8; 121 122extern double dnow(void); 123extern double dnowx(void); 124 125extern int dpy_x, dpy_y; 126extern int macosx_icon_anim_time; 127 128extern void macosx_add_mapnotify(int, int, int); 129extern void macosx_add_create(int, int); 130extern void macosx_add_destroy(int, int); 131extern void macosx_add_visnotify(int, int, int); 132 133int CGS_levelmax; 134int CGS_levels[16]; 135 136int macosxCGS_get_qlook(int w) { 137 if (w >= WINHISTNUM) { 138 return -1; 139 } 140 return qlook[w]; 141} 142 143int 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 171extern void usleep(unsigned long usec); 172#else 173extern int usleep(useconds_t usec); 174#endif 175 176int 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 240extern int macosx_check_clipped(int win, int *list, int n); 241extern int macosx_check_offscreen(int win); 242 243static 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 255static int check_offscreen(int win) { 256 return macosx_check_offscreen(win); 257} 258 259extern int macosx_ncache_macmenu; 260 261 262void 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 311if (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 359if (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 374if (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 524NSLock *pblock = nil; 525NSString *pbstr = nil; 526NSString *cuttext = nil; 527 528int pbcnt = -1; 529NSStringEncoding pbenc = NSWindowsCP1252StringEncoding; 530 531void 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 544void 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 566extern void macosx_send_sel(char *, int); 567 568void 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