/* -- macosxCGS.c -- */ /* * We need to keep this separate from nearly everything else, e.g. rfb.h * and the other stuff, otherwise it does not work properly, mouse drags * will not work!! */ #if (defined(__MACH__) && defined(__APPLE__)) #include #include #include extern CGDirectDisplayID displayID; void macosxCGS_get_all_windows(void); int macosxCGS_get_qlook(int); void macosxGCS_set_pasteboard(char *str, int len); typedef CGError CGSError; typedef long CGSWindowCount; typedef void * CGSConnectionID; typedef int CGSWindowID; typedef CGSWindowID* CGSWindowIDList; typedef CGWindowLevel CGSWindowLevel; typedef NSRect CGSRect; extern CGSConnectionID _CGSDefaultConnection (); extern CGSError CGSGetOnScreenWindowList (CGSConnectionID cid, CGSConnectionID owner, CGSWindowCount listCapacity, CGSWindowIDList list, CGSWindowCount *listCount); extern CGSError CGSGetWindowList (CGSConnectionID cid, CGSConnectionID owner, CGSWindowCount listCapacity, CGSWindowIDList list, CGSWindowCount *listCount); extern CGSError CGSGetScreenRectForWindow (CGSConnectionID cid, CGSWindowID wid, CGSRect *rect); extern CGWindowLevel CGSGetWindowLevel (CGSConnectionID cid, CGSWindowID wid, CGSWindowLevel *level); typedef enum _CGSWindowOrderingMode { kCGSOrderAbove = 1, /* Window is ordered above target. */ kCGSOrderBelow = -1, /* Window is ordered below target. */ kCGSOrderOut = 0 /* Window is removed from the on-screen window list. */ } CGSWindowOrderingMode; extern OSStatus CGSOrderWindow(const CGSConnectionID cid, const CGSWindowID wid, CGSWindowOrderingMode place, CGSWindowID relativeToWindowID); static CGSConnectionID cid = NULL; int macwinmax = 0; typedef struct windat { int win; int x, y; int width, height; int level; int mapped; int clipped; int ncache_only; } windat_t; extern int ncache; #define MAXWINDAT 4096 windat_t macwins[MAXWINDAT]; static CGSWindowID _wins_all[MAXWINDAT]; static CGSWindowID _wins_mapped[MAXWINDAT]; static CGSWindowCount _wins_all_cnt, _wins_mapped_cnt; static int _wins_int[MAXWINDAT]; #define WINHISTNUM 32768 #define WINHISTMAX 4 char whist[WINHISTMAX][WINHISTNUM]; int whist_idx = -1; int qlook[WINHISTNUM]; char is_exist = 0x1; char is_mapped = 0x2; char is_clipped = 0x4; char is_offscreen = 0x8; extern double dnow(void); extern double dnowx(void); extern int dpy_x, dpy_y; extern int macosx_icon_anim_time; extern void macosx_add_mapnotify(int, int, int); extern void macosx_add_create(int, int); extern void macosx_add_destroy(int, int); extern void macosx_add_visnotify(int, int, int); int CGS_levelmax; int CGS_levels[16]; int macosxCGS_get_qlook(int w) { if (w >= WINHISTNUM) { return -1; } return qlook[w]; } int macosxCGS_find_index(int w) { static int last_index = -1; int idx; if (last_index >= 0) { if (macwins[last_index].win == w) { return last_index; } } idx = macosxCGS_get_qlook(w); if (idx >= 0) { if (macwins[idx].win == w) { last_index = idx; return idx; } } for (idx=0; idx < macwinmax; idx++) { if (macwins[idx].win == w) { last_index = idx; return idx; } } return -1; } extern void usleep(unsigned long usec); int macosxCGS_follow_animation_win(int win, int idx, int grow) { double t = dnow(); int diffs = 0; int x, y, w, h; int xp = -1, yp = -1, wp = -1, hp = -1; CGSRect rect; CGSError err; int reps = 0; if (cid == NULL) { cid = _CGSDefaultConnection(); if (cid == NULL) { return 0; } } if (idx < 0) { idx = macosxCGS_find_index(win); } if (idx < 0) { return 0; } while (dnow() < t + 0.001 * macosx_icon_anim_time) { err = CGSGetScreenRectForWindow(cid, win, &rect); if (err != 0) { break; } x = (int) rect.origin.x; y = (int) rect.origin.y; w = (int) rect.size.width; h = (int) rect.size.height; if (grow) { macwins[idx].x = x; macwins[idx].y = y; macwins[idx].width = w; macwins[idx].height = h; } fprintf(stderr, " chase: %03dx%03d+%03d+%03d %d\n", w, h, x, y, win); if (x == xp && y == yp && w == wp && h == hp) { reps++; if (reps >= 2) { break; } } else { diffs++; reps = 0; } xp = x; yp = y; wp = w; hp = h; usleep(50 * 1000); } if (diffs >= 2) { return 1; } else { return 0; } } extern int macosx_check_clipped(int win, int *list, int n); extern int macosx_check_offscreen(int win); static int check_clipped(int win) { int i, n = 0, win2; for (i = 0; i < (int) _wins_mapped_cnt; i++) { win2 = (int) _wins_mapped[i]; if (win2 == win) { break; } _wins_int[n++] = win2; } return macosx_check_clipped(win, _wins_int, n); } static int check_offscreen(int win) { return macosx_check_offscreen(win); } extern int macosx_ncache_macmenu; void macosxCGS_get_all_windows(void) { static double last = 0.0; static int totcnt = 0; double dt = 0.0, now = dnow(); int i, db = 0, whist_prv = 0, maxwin = 0, whist_skip = 0; CGSWindowCount cap = (CGSWindowCount) MAXWINDAT; CGSError err; CGS_levelmax = 0; CGS_levels[CGS_levelmax++] = (int) kCGDraggingWindowLevel; /* 500 ? */ if (0) CGS_levels[CGS_levelmax++] = (int) kCGHelpWindowLevel; /* 102 ? */ if (macosx_ncache_macmenu) CGS_levels[CGS_levelmax++] = (int) kCGPopUpMenuWindowLevel; /* 101 pulldown menu */ CGS_levels[CGS_levelmax++] = (int) kCGMainMenuWindowLevelKey; /* 24 ? */ CGS_levels[CGS_levelmax++] = (int) kCGModalPanelWindowLevel; /* 8 open dialog box */ CGS_levels[CGS_levelmax++] = (int) kCGFloatingWindowLevel; /* 3 ? */ CGS_levels[CGS_levelmax++] = (int) kCGNormalWindowLevel; /* 0 regular window */ if (cid == NULL) { cid = _CGSDefaultConnection(); if (cid == NULL) { return; } } if (dt > 0.0 && now < last + dt) { return; } last = now; macwinmax = 0; totcnt++; if (ncache > 0) { whist_prv = whist_idx++; if (whist_prv < 0) { whist_skip = 1; whist_prv = 0; } whist_idx = whist_idx % WINHISTMAX; for (i=0; i < WINHISTNUM; i++) { whist[whist_idx][i] = 0; qlook[i] = -1; } } err = CGSGetWindowList(cid, NULL, cap, _wins_all, &_wins_all_cnt); if (db) fprintf(stderr, "cnt: %d err: %d\n", (int) _wins_all_cnt, err); if (err != 0) { return; } for (i=0; i < (int) _wins_all_cnt; i++) { CGSRect rect; CGSWindowLevel level; int j, keepit = 0; err = CGSGetScreenRectForWindow(cid, _wins_all[i], &rect); if (err != 0) { continue; } if (rect.origin.x == 0 && rect.origin.y == 0) { if (rect.size.width == dpy_x) { if (rect.size.height == dpy_y) { continue; } } } err = CGSGetWindowLevel(cid, _wins_all[i], &level); if (err != 0) { continue; } for (j=0; j maxwin) { maxwin = macwins[macwinmax].win; } } macwinmax++; } err = CGSGetOnScreenWindowList(cid, NULL, cap, _wins_mapped, &_wins_mapped_cnt); if (db) fprintf(stderr, "cnt: %d err: %d\n", (int) _wins_mapped_cnt, err); if (err != 0) { return; } for (i=0; i < (int) _wins_mapped_cnt; i++) { int j, idx = -1; int win = (int) _wins_mapped[i]; if (0 <= win && win < WINHISTNUM) { j = qlook[win]; if (j >= 0 && macwins[j].win == win) { idx = j; } } if (idx < 0) { for (j=0; j < macwinmax; j++) { if (macwins[j].win == win) { idx = j; break; } } } if (idx >= 0) { macwins[idx].mapped = 1; } } if (ncache > 0) { int nv= 0, NBMAX = 64; int nv_win[64]; int nv_lvl[64]; int nv_vis[64]; for (i=0; i < macwinmax; i++) { int win = macwins[i].win; char prev, curr; if (win >= WINHISTNUM) { continue; } whist[whist_idx][win] |= is_exist; if (macwins[i].mapped) { whist[whist_idx][win] |= is_mapped; if (check_clipped(win)) { whist[whist_idx][win] |= is_clipped; macwins[i].clipped = 1; } if (check_offscreen(win)) { whist[whist_idx][win] |= is_offscreen; } } else { whist[whist_idx][win] |= is_offscreen; } curr = whist[whist_idx][win]; prev = whist[whist_prv][win]; if (whist_skip) { ; } else if ( !(prev & is_mapped) && (curr & is_mapped)) { /* MapNotify */ fprintf(stderr, "MapNotify: %d/%d %d %.4f tot=%d\n", prev, curr, win, dnowx(), totcnt); macosx_add_mapnotify(win, macwins[i].level, 1); if (0) macosxCGS_follow_animation_win(win, i, 1); } else if ( !(curr & is_mapped) && (prev & is_mapped)) { /* UnmapNotify */ fprintf(stderr, "UnmapNotify: %d/%d %d %.4f A tot=%d\n", prev, curr, win, dnowx(), totcnt); macosx_add_mapnotify(win, macwins[i].level, 0); } else if ( !(prev & is_exist) && (curr & is_exist)) { /* CreateNotify */ 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); macosx_add_create(win, macwins[i].level); if (curr & is_mapped) { fprintf(stderr, "MapNotify: %d/%d %d %.4f tot=%d\n", prev, curr, win, dnowx(), totcnt); macosx_add_mapnotify(win, macwins[i].level, 1); } } if (whist_skip) { ; } else if (nv >= NBMAX) { ; } else if (!(curr & is_mapped)) { ; } else if (!(prev & is_mapped)) { if (1) { ; } else if (curr & is_clipped) { fprintf(stderr, "VisibNotify: %d/%d %d OBS tot=%d\n", prev, curr, win, totcnt); nv_win[nv] = win; nv_lvl[nv] = macwins[i].level; nv_vis[nv++] = 1; } else { fprintf(stderr, "VisibNotify: %d/%d %d UNOBS tot=%d\n", prev, curr, win, totcnt); nv_win[nv] = win; nv_lvl[nv] = macwins[i].level; nv_vis[nv++] = 0; } } else { if ( !(prev & is_clipped) && (curr & is_clipped) ) { fprintf(stderr, "VisibNotify: %d/%d %d OBS tot=%d\n", prev, curr, win, totcnt); nv_win[nv] = win; nv_lvl[nv] = macwins[i].level; nv_vis[nv++] = 1; } else if ( (prev & is_clipped) && !(curr & is_clipped) ) { fprintf(stderr, "VisibNotify: %d/%d %d UNOBS tot=%d\n", prev, curr, win, totcnt); nv_win[nv] = win; nv_lvl[nv] = macwins[i].level; nv_vis[nv++] = 0; } } } for (i=0; i < maxwin; i++) { char prev, curr; int win = i; int q = qlook[i]; int lvl = 0; if (whist_skip) { break; } if (q >= 0) { lvl = macwins[q].level; } curr = whist[whist_idx][win]; prev = whist[whist_prv][win]; if (!(curr & is_exist) && (prev & is_exist)) { if (prev & is_mapped) { fprintf(stderr, "UnmapNotify: %d/%d %d %.4f B tot=%d\n", prev, curr, win, dnowx(), totcnt); macosx_add_mapnotify(win, lvl, 0); } /* DestroyNotify */ fprintf(stderr, "DestroNotify:%d/%d %d %.4f tot=%d\n", prev, curr, win, dnowx(), totcnt); macosx_add_destroy(win, lvl); } } if (nv) { int k; for (k = 0; k < nv; k++) { macosx_add_visnotify(nv_win[k], nv_lvl[k], nv_vis[k]); } } } } #if 1 NSLock *pblock = nil; NSString *pbstr = nil; NSString *cuttext = nil; int pbcnt = -1; NSStringEncoding pbenc = NSWindowsCP1252StringEncoding; void macosxGCS_initpb(void) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; pblock = [[NSLock alloc] init]; if (![NSPasteboard generalPasteboard]) { fprintf(stderr, "macosxGCS_initpb: pasteboard inaccessible.\n"); pbcnt = 0; pbstr = [[NSString alloc] initWithString:@"\e\e"]; } [pool release]; } void macosxGCS_set_pasteboard(char *str, int len) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; if (pbcnt != 0) { [pblock lock]; [cuttext release]; cuttext = [[NSString alloc] initWithData:[NSData dataWithBytes:str length:len] encoding: pbenc]; if ([[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]) { NS_DURING [[NSPasteboard generalPasteboard] setString:cuttext forType:NSStringPboardType]; NS_HANDLER fprintf(stderr, "macosxGCS_set_pasteboard: problem writing to pasteboard\n"); NS_ENDHANDLER } else { fprintf(stderr, "macosxGCS_set_pasteboard: problem writing to pasteboard\n"); } [cuttext release]; cuttext = nil; [pblock unlock]; } [pool release]; } extern void macosx_send_sel(char *, int); void macosxGCS_poll_pb(void) { static double dlast = 0.0; double now = dnow(); if (now < dlast + 0.2) { return; } dlast = now; { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [pblock lock]; if (pbcnt != [[NSPasteboard generalPasteboard] changeCount]) { pbcnt = [[NSPasteboard generalPasteboard] changeCount]; [pbstr release]; pbstr = nil; if ([[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) { pbstr = [[[NSPasteboard generalPasteboard] stringForType:NSStringPboardType] copy]; if (pbstr) { NSData *str = [pbstr dataUsingEncoding:pbenc allowLossyConversion:YES]; if ([str length]) { macosx_send_sel((char *) [str bytes], [str length]); } } } } [pblock unlock]; [pool release]; } } #endif #endif /* __APPLE__ */