From http://www.jwz.org/xscreensaver/xscreensaver-5.15.tar.gz
[xscreensaver] / OSX / XScreenSaverView.m
index d483c8885ce70cd5de654ca2d7abd98143aea8c4..404cfc1c42bc5c163592278cf5e8c90d5b282544 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2006-2009 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 #import "xlockmoreI.h"
 #import "jwxyz-timers.h"
 
+/* Garbage collection only exists if we are being compiled against the 
+   10.6 SDK or newer, not if we are building against the 10.4 SDK.
+ */
+#ifndef  MAC_OS_X_VERSION_10_6
+# define MAC_OS_X_VERSION_10_6 1060  /* undefined in 10.4 SDK, grr */
+#endif
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6  /* 10.6 SDK */
+# import <objc/objc-auto.h>
+# define DO_GC_HACKERY
+#endif
+
 extern struct xscreensaver_function_table *xscreensaver_function_table;
 
 /* Global variables used by the screen savers
@@ -53,6 +64,7 @@ int mono_p = 0;
   
   void *addr = CFBundleGetDataPointerForName (cfb, (CFStringRef) table_name);
   NSAssert2 (addr, @"no symbol \"%@\" in bundle %@", table_name, path);
+  CFRelease (cfb);
 
 //  NSLog (@"%@ = 0x%08X", table_name, (unsigned long) addr);
   return (struct xscreensaver_function_table *) addr;
@@ -122,6 +134,7 @@ add_default_options (const XrmOptionDescRec *opts,
     { "-text-literal",           ".textLiteral",       XrmoptionSepArg, 0 },
     { "-text-file",              ".textFile",          XrmoptionSepArg, 0 },
     { "-text-url",               ".textURL",           XrmoptionSepArg, 0 },
+    { "-text-program",           ".textProgram",       XrmoptionSepArg, 0 },
     { "-grab-desktop",           ".grabDesktopImages", XrmoptionNoArg, "True" },
     { "-no-grab-desktop",        ".grabDesktopImages", XrmoptionNoArg, "False"},
     { "-choose-random-images",   ".chooseRandomImages",XrmoptionNoArg, "True" },
@@ -133,10 +146,13 @@ add_default_options (const XrmOptionDescRec *opts,
   };
   static const char *default_defaults [] = {
     ".doFPS:              False",
+    ".doubleBuffer:       True",
+    ".multiSample:        False",
     ".textMode:           date",
  // ".textLiteral:        ",
  // ".textFile:           ",
- // ".textURL:            ",
+    ".textURL:            http://twitter.com/statuses/public_timeline.atom",
+ // ".textProgram:        ",
     ".grabDesktopImages:  yes",
     ".chooseRandomImages: no",
     ".imageDirectory:     ~/Pictures",
@@ -301,7 +317,7 @@ add_default_options (const XrmOptionDescRec *opts,
 static void
 screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
 {
-  fps_compute (fpst, 0);
+  fps_compute (fpst, 0, -1);
   fps_draw (fpst);
 }
 
@@ -430,6 +446,37 @@ screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
   gettimeofday (&tv, 0);
   now = tv.tv_sec + (tv.tv_usec / 1000000.0);
   next_frame_time = now + (delay / 1000000.0);
+
+
+# ifdef DO_GC_HACKERY
+  /* Current theory is that the 10.6 garbage collector sucks in the
+     following way:
+
+     It only does a collection when a threshold of outstanding
+     collectable allocations has been surpassed.  However, CoreGraphics
+     creates lots of small collectable allocations that contain pointers
+     to very large non-collectable allocations: a small CG object that's
+     collectable referencing large malloc'd allocations (non-collectable)
+     containing bitmap data.  So the large allocation doesn't get freed
+     until GC collects the small allocation, which triggers its finalizer
+     to run which frees the large allocation.  So GC is deciding that it
+     doesn't really need to run, even though the process has gotten
+     enormous.  GC eventually runs once pageouts have happened, but by
+     then it's too late, and the machine's resident set has been
+     sodomized.
+
+     So, we force an exhaustive garbage collection in this process
+     approximately every 5 seconds whether the system thinks it needs 
+     one or not.
+  */
+  {
+    static int tick = 0;
+    if (++tick > 5*30) {
+      tick = 0;
+      objc_collect (OBJC_EXHAUSTIVE_COLLECTION);
+    }
+  }
+# endif // DO_GC_HACKERY
 }
 
 
@@ -494,6 +541,12 @@ screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
 }
 
 
+- (NSUserDefaultsController *) userDefaultsController
+{
+  return [prefsReader userDefaultsController];
+}
+
+
 /* Announce our willingness to accept keyboard input.
 */
 - (BOOL)acceptsFirstResponder