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