Plugins/Displays/Animation & Transitions/Window Transitions/AWRippler.m
author boredzo
Sun Jul 06 16:50:35 2008 +0000 (2008-07-06)
changeset 4138 942b66d1feef
parent 3954 2dfcd1414225
child 4246 4f52d1d98978
permissions -rw-r--r--
Fixed a leak found by the clang static analyzer. I have to hope this works, because I can't see a way to switch to this transition in the Growl prefpane.

Note: This is an original fix by me, not cribbed from Adium (GPL) source code.
     1 /*
     2  * Project:     RippleEffect
     3  * File:        AWRippler.m
     4  * Author:      Andrew Wellington
     5  *
     6  * License:
     7  * Copyright (C) 2005 Andrew Wellington.
     8  * All rights reserved.
     9  * 
    10  * Redistribution and use in source and binary forms, with or without
    11  * modification, are permitted provided that the following conditions are met:
    12  * 
    13  * 1. Redistributions of source code must retain the above copyright notice,
    14  * this list of conditions and the following disclaimer.
    15  * 2. Redistributions in binary form must reproduce the above copyright notice,
    16  * this list of conditions and the following disclaimer in the documentation
    17  * and/or other materials provided with the distribution.
    18  * 
    19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
    20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    22  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
    25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
    27  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    28  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    29  */
    30 
    31 #import "AWRippler.h"
    32 
    33 #import <math.h>
    34 
    35 /* NSWindow Category for our bonus features */
    36 @interface NSWindow(AWRipplePrivate)
    37 - (int)windowNum;
    38 - (void)scaleX:(double)x Y:(double)y;
    39 @end
    40 
    41 @implementation NSWindow(AWRipplePrivate)
    42 - (int)windowNum
    43 {
    44     return _windowNum;
    45 }
    46 - (void)scaleX:(double)x Y:(double)y {
    47 	CGAffineTransform original;
    48 	NSPoint scalePoint;
    49 	NSRect screenFrame;
    50 	//NSLog(@"%@", NSStringFromRect(_frame));
    51 	NSPoint point = NSMakePoint(_frame.size.width / 2.0, _frame.size.height / 2.0);
    52         
    53 	if ([[NSScreen screens] count]) {
    54         screenFrame = [[[NSScreen screens] objectAtIndex:0] frame];
    55         
    56         scalePoint.x = _frame.origin.x + point.x;
    57         scalePoint.y = - ((_frame.origin.y + point.y) - screenFrame.size.height);
    58         
    59         original.a = 1.0 ; original.b = 0.0 ;
    60         original.c = 0.0 ; original.d = 1.0 ;
    61         original.tx = - _frame.origin.x ;
    62         original.ty = + _frame.origin.y + _frame.size.height - NSMaxY(screenFrame);
    63         
    64         original = CGAffineTransformTranslate(original, scalePoint.x, scalePoint.y);
    65         original = CGAffineTransformScale(original, x, y);
    66         original = CGAffineTransformTranslate(original, -scalePoint.x, -scalePoint.y);
    67         
    68         CGSSetWindowTransform(_CGSDefaultConnection(), _windowNum, original);
    69 	}
    70 }
    71 
    72 @end
    73 
    74 /* NSWindow Category implementation for easy rippling */
    75 @implementation NSWindow(AWRipple)
    76 - (void)ripple
    77 {
    78     AWRippler *rippler;
    79     
    80     rippler = [[AWRippler alloc] init];
    81     [rippler rippleWindow:self];
    82     [rippler release];
    83 }
    84 @end
    85 
    86 /* Interface for Core Image Core Graphics Server filter */
    87 @interface CICGSFilter : NSObject
    88 {
    89     void *_cid;
    90     unsigned int _filter_id;
    91 }
    92 
    93 + (id)filterWithFilter:(CIFilter *)filter connectionID:(CGSConnection)cid;
    94 - (id)initWithFilter:(CIFilter *)filter connectionID:(CGSConnection)cid;
    95 - (void)dealloc;
    96 - (void)setValue:(id)value forKey:(NSString *)key;
    97 - (void)setValuesForKeysWithDictionary:(NSDictionary *)dict;
    98 - (int)addToWindow:(CGSWindow)windowID flags:(unsigned int)flags;
    99 - (int)removeFromWindow:(CGSWindow)windowID;
   100 - (id)description;
   101 @end
   102 
   103 /* NSWindow subclass for our covering window */
   104 @interface AWRippleWindow : NSWindow {
   105 }
   106 @end
   107 
   108 @implementation AWRippleWindow
   109 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
   110 #pragma unused (aStyle, bufferingType, flag)
   111     NSWindow* result = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
   112     [result setBackgroundColor: [NSColor clearColor]];
   113     [result setAlphaValue:1.0];
   114     [result setOpaque:NO];
   115     return result;
   116 }
   117 @end
   118 
   119 /* NSView subclass for our covering window */
   120 @interface AWRippleView : NSView {
   121 }
   122 @end
   123 
   124 @implementation AWRippleView
   125 -(void)drawRect:(NSRect)rect
   126 {
   127 #pragma unused (rect)
   128 	NSLog(@"%s", __FUNCTION__);	
   129 	
   130 	[[NSColor clearColor] set];
   131     NSRectFill([self frame]);
   132 }
   133 @end
   134 
   135 /* CoreGraphics private stuff */
   136 extern CGSConnection _CGSDefaultConnection(void);
   137 
   138 /* The magic rippler class */
   139 @implementation AWRippler
   140 - (id)init
   141 {
   142     self = [super init];
   143     if (!self)
   144         return nil;
   145     
   146     win = nil;
   147 	startTime = 0;
   148 	aWindowID = 0;
   149 	rippleFilter = nil;
   150 	windowFilter = nil;
   151     
   152     return self;
   153 }
   154 
   155 - (void) rippleWindow:(NSWindow *)rippleWindow
   156 {
   157     CGSConnection cid = _CGSDefaultConnection();
   158     NSRect rect;
   159     NSRect rippleRect;
   160     NSRect screenRect;
   161     NSArray *screens;
   162     NSEnumerator *screenEnum;
   163     NSScreen *screen;
   164     
   165     if (!FLOAT_EQ(startTime, 0.0))
   166         return;
   167     
   168     rippleRect = [rippleWindow frame];
   169     ripplingWindow = [rippleWindow retain];
   170     
   171     /* create covering window */
   172     rect = NSMakeRect(0.0,0.0,0.0,0.0);
   173     screens = [NSScreen screens];
   174     screenEnum = [screens objectEnumerator];
   175     
   176     while ((screen = [screenEnum nextObject]))
   177     {
   178         rect = NSUnionRect(rect, [screen frame]);
   179     }
   180         
   181     win = [[AWRippleWindow alloc] initWithContentRect:rect
   182                                             styleMask:NSBorderlessWindowMask
   183                                               backing:NSBackingStoreNonretained
   184                                                 defer:NO];
   185 	[win setBackgroundColor:[NSColor clearColor]];
   186 	[win setOpaque:NO];
   187 	[win setHasShadow:NO];
   188 	[win setContentView:[[[AWRippleView alloc] initWithFrame:[win frame]] autorelease]];
   189 	
   190 	[win orderFrontRegardless];
   191     [rippleWindow orderWindow:NSWindowAbove relativeTo:[win windowNum]];
   192     
   193     
   194     /* calculate the rectangle in the covering window */
   195     screenRect = [[[NSScreen screens] objectAtIndex:0] frame];
   196     rippleRect.origin.y = - (NSMaxY(rippleRect) - screenRect.size.height);
   197    
   198     /* create filter */
   199     rippleFilter = [[CIFilter filterWithName:@"CIShapedWaterRipple"] retain];
   200     [rippleFilter setDefaults];
   201     [rippleFilter setValue:[NSNumber numberWithFloat:40.0] forKey:@"inputCornerRadius"];
   202     [rippleFilter setValue:[CIVector vectorWithX:rippleRect.origin.x-40.0 Y:(rippleRect.origin.y - 40.0)] forKey:@"inputPoint0"];
   203     [rippleFilter setValue:[CIVector vectorWithX:(rippleRect.origin.x + rippleRect.size.width + 40.0) Y:(rippleRect.origin.y + rippleRect.size.height + 40.0)] forKey:@"inputPoint1"];
   204 
   205     
   206     [rippleFilter setValue:[NSNumber numberWithFloat:0.0] forKey:@"inputPhase"];
   207     
   208     windowFilter = [[CICGSFilter filterWithFilter:rippleFilter connectionID:cid] retain];
   209 	[windowFilter addToWindow:aWindowID flags:0x3001];    
   210     aWindowID = [win windowNum];
   211     [self retain];
   212     
   213     [NSThread detachNewThreadSelector:@selector(animationLoop:) toTarget:self withObject:self];
   214 }
   215 
   216 - (void)animationLoop:(id)sender
   217 {
   218 #pragma unused (sender)
   219     NSAutoreleasePool *pool;
   220 	CGSConnection cid = _CGSDefaultConnection();
   221     CICGSFilter *oldFilter = windowFilter;
   222     double scale;
   223     CFAbsoluteTime now;
   224     CGAffineTransform originalTransform;
   225     
   226     CGSGetWindowTransform(cid, [ripplingWindow windowNum], &originalTransform);
   227     
   228     pool = [[NSAutoreleasePool alloc] init];
   229     
   230     startTime = CFAbsoluteTimeGetCurrent();
   231     now = CFAbsoluteTimeGetCurrent();
   232     
   233     while (now < (startTime + 2.5) && (now >= startTime))
   234     {
   235         if (now - startTime < 1.5) {
   236             scale = 1.0 - exp(-2.4 * (now - startTime)) * sin(40.0/M_PI * (now - startTime)) * 0.15;
   237             //[ripplingWindow scaleX:scale Y:scale];
   238         }
   239         else
   240         {
   241             CGSSetWindowTransform(cid, [ripplingWindow windowNum], originalTransform);
   242         }
   243         
   244         [rippleFilter setValue:[NSNumber numberWithFloat:160*(now - startTime)] forKey:@"inputPhase"];
   245         windowFilter = [[CICGSFilter filterWithFilter:rippleFilter connectionID:cid] retain];
   246 		[windowFilter addToWindow:aWindowID flags:0x3001];
   247         [oldFilter removeFromWindow:aWindowID];
   248         
   249         now = CFAbsoluteTimeGetCurrent();
   250     }
   251     CGSSetWindowTransform(cid, [ripplingWindow windowNum], originalTransform);
   252 
   253     [windowFilter removeFromWindow:aWindowID];
   254     [rippleFilter release];
   255     [windowFilter release];
   256     [ripplingWindow release];
   257     [win orderOut: self];
   258     [win release];
   259     [pool release];
   260     startTime = 0.0;
   261 }
   262 
   263 @end