1#import "ESScreensaverView.h"
2#import "ESScreensaver.h"
3#import <OpenGL/OpenGL.h>
4#include "dlfcn.h"
5#include "libgen.h"
6
7NSOpenGLContext *glContext = NULL;
8
9bool bStarted = false;
10
11@implementation ESScreensaverView
12
13- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
14{
15#ifdef SCREEN_SAVER
16	self = [super initWithFrame:frame isPreview:isPreview];
17#else
18	self = [super initWithFrame:frame];
19#endif
20
21	m_updater = NULL;
22
23	m_isFullScreen = !isPreview;
24
25	m_isPreview = isPreview;
26
27#ifdef SCREEN_SAVER
28	//if (isPreview)
29#endif
30	{
31		CFBundleRef bndl = CopyDLBundle_ex();
32		NSBundle *nsbndl;
33
34		if (bndl != NULL)
35		{
36			NSURL* url = (NSURL*)CFBridgingRelease(CFBundleCopyBundleURL(bndl));
37
38			nsbndl = [NSBundle bundleWithPath:[url path]];
39
40			m_updater = [SUUpdater updaterForBundle:nsbndl];
41
42			[m_updater setDelegate:self];
43
44			if (m_updater && [m_updater automaticallyChecksForUpdates])
45			{
46				[m_updater checkForUpdateInformation];
47			}
48
49            CFRelease( bndl );
50		}
51	}
52
53	if (self)
54    {
55        // So we modify the setup routines just a little bit to get our
56        // new OpenGL screensaver working.
57
58		// Create the new frame
59        NSRect newFrame = frame;
60        // Slam the origin values
61        newFrame.origin.x = 0.0;
62        newFrame.origin.y = 0.0;
63
64		theRect = newFrame;
65        // Now alloc and init the view, right from within the screen saver's initWithFrame:
66
67        // If the view is valid, we continue
68        //if(glView)
69        {
70            // Make sure we autoresize
71            [self setAutoresizesSubviews:YES];
72            // So if our view is valid...
73
74			glView = NULL;
75
76            // Do some setup on our context and view
77            //[glView prepareOpenGL];
78            // Then we set our animation loop timer
79            //[self setAnimationTimeInterval:1/60.0];
80#ifdef SCREEN_SAVER
81			[self setAnimationTimeInterval:-1];
82#endif
83            // Since our BasicOpenGLView class does it's setup in awakeFromNib, we call that here.
84            // Note that this could be any method you want to use as the setup routine for your view.
85            //[glView awakeFromNib];
86        }
87        //else // Log an error if we fail here
88           // NSLog(@"Error: Electric Sheep Screen Saver failed to initialize NSOpenGLView!");
89    }
90    // Finally return our newly-initialized self
91    return self;
92}
93
94- (void)startAnimation
95{
96	/*NSMutableArray *displays = [NSMutableArray arrayWithCapacity:5];
97
98	[displays addObject:[NSScreen mainScreen]];
99
100	UInt32 cnt = [[NSScreen screens] count];
101
102	for (int i=0; i<cnt; i++)
103	{
104		NSScreen *scr = [[NSScreen screens] objectAtIndex:i];
105
106		 if (scr !=  [NSScreen mainScreen])
107			[displays addObject:scr];
108	}
109
110	ESScreensaver_InitClientStorage();
111
112	SInt32 scr = ESScreensaver_GetIntSetting("settings.player.screen", 0);
113
114	ESScreensaver_DeinitClientStorage();
115
116	if (scr >= cnt)
117		scr = cnt - 1;
118
119	//main screen only for now?
120	if (!m_isPreview && [[self window] screen] != [displays objectAtIndex:scr])
121	{
122       return;
123	}
124	else
125	{*/
126        if (glView == NULL)
127		{
128			/*NSRect newRect;
129
130			newRect.size.height = theRect.size.height;
131
132			newRect.size.width = 800.0 * ( theRect.size.height / 592.0 );
133
134			newRect.origin.x = theRect.origin.x + ( theRect.size.width - newRect.size.width ) / 2.0;
135
136			newRect.origin.y = theRect.origin.y;
137
138			theRect = newRect;*/
139
140			glView = [[ESOpenGLView alloc] initWithFrame:theRect];
141
142			if(glView)
143			{
144				glContext = [glView openGLContext];
145
146				// We make it a subview of the screensaver view
147				[self addSubview:glView];
148			}
149		}
150	//}
151
152	if (glView != NULL && [glView openGLContext])
153	{
154		ESScreensaver_InitClientStorage();
155
156		ESScreenSaver_AddGLContext( (CGLContextObj)[[glView openGLContext] CGLContextObj] );
157
158		ESScreensaver_DeinitClientStorage();
159	}
160
161	uint32 width = (uint32)theRect.size.width;
162	uint32 height = (uint32)theRect.size.height;
163
164#ifdef SCREEN_SAVER
165	m_isHidden = NO;
166#endif
167
168	if ( !bStarted )
169	{
170
171		if (!ESScreensaver_Start( m_isPreview, width, height ))
172			return;
173
174		bStarted = true;
175
176		[self _beginThread];
177	}
178#ifdef SCREEN_SAVER
179	[super startAnimation];
180#endif
181}
182
183- (void)stopAnimation
184{
185#ifdef SCREEN_SAVER
186	[NSCursor unhide];
187#endif
188	if ( bStarted )
189	{
190		[self _endThread];
191
192		ESScreensaver_Stop();
193
194		ESScreensaver_Deinit();
195
196		bStarted = false;
197	}
198
199#ifdef SCREEN_SAVER
200	if (m_isHidden)
201		[NSCursor unhide];
202	m_isHidden = NO;
203	[super stopAnimation];
204#endif
205}
206
207- (void)animateOneFrame
208{
209	//[self setAnimationTimeInterval:-1];
210
211	//[animationLock unlock];
212
213	//ESScreensaver_DoFrame();
214
215	//[glView setNeedsDisplay:YES];
216}
217
218- (void)_animationThread
219{
220	@autoreleasepool {
221
222		if (animationLock != NULL)
223			[animationLock lock];
224
225		while (!m_isStopped && !ESScreensaver_Stopped() && ESScreensaver_DoFrame())
226		{
227#ifdef SCREEN_SAVER
228			if (!m_isPreview && CGCursorIsVisible())
229			{
230				[NSCursor hide];
231				m_isHidden = YES;
232			}
233#endif
234			//if (m_isStopped)
235				//break;
236
237			//if (glView != NULL)
238				//[glView setNeedsDisplay:YES];
239		}
240
241		if (animationLock != NULL)
242			[animationLock unlock];
243
244	}
245}
246
247- (void)_beginThread
248{
249	animationLock = [[NSLock alloc] init];
250
251	//[animationLock lock];
252
253	m_isStopped = NO;
254
255	[NSThread detachNewThreadSelector:@selector(_animationThread) toTarget:self withObject:nil];
256}
257
258- (void)_endThread
259{
260	m_isStopped = YES;
261
262	[animationLock lock];
263	[animationLock unlock];
264
265	animationLock = NULL;
266}
267
268- (void)windowDidResize
269{
270	[glView setFrame: [self frame]];
271
272	ESScreensaver_ForceWidthAndHeight( (uint32)[self frame].size.width, (uint32)[self frame].size.height );
273}
274
275
276- (BOOL)hasConfigureSheet
277{
278    return YES;
279}
280
281- (NSWindow*)configureSheet
282{
283    if (!m_config) {
284        m_config = [[ESConfiguration alloc] initWithWindowNibName:@"ElectricSheep" updater:m_updater];
285    }
286
287    return [m_config window];
288}
289
290- (void)flagsChanged:(NSEvent *)ev
291{
292	if ([ev keyCode] == 63) //FN Key
293		return;
294
295	[super flagsChanged:ev];
296}
297
298// keyDown
299// Capture Up/Down for rating animations
300// If there is no animation, or the computer cannot access the electricsheep server, UP and DOWN
301// act just like in a normal screensaver (they stop it) - initially I thought it should just ignore
302// the vote and not end playback (for consistency - UP/DOWN would never stop it) but I think it is
303// appropriate that if you can't vote, the default event behavior used
304
305//keycodes based on - http://www.filewatcher.com/p/BasiliskII-0.9.1.tgz.276457/share/BasiliskII/keycodes.html
306- (void) keyDown:(NSEvent *) ev
307{
308    BOOL handled = NO;
309
310    NSString *characters = [ev charactersIgnoringModifiers];
311    unsigned int characterIndex, characterCount = (unsigned int)[characters length];
312
313    for (characterIndex = 0; characterIndex < characterCount; characterIndex++) {
314		unichar c = [characters characterAtIndex:characterIndex];
315		switch (c) {
316			case NSRightArrowFunctionKey:
317				ESScreensaver_AppendKeyEvent( 0x7C );
318				handled = YES;
319				break;
320
321			case NSLeftArrowFunctionKey:
322				ESScreensaver_AppendKeyEvent( 0x7B );
323				handled = YES;
324				break;
325
326			case NSUpArrowFunctionKey:
327				ESScreensaver_AppendKeyEvent( 0x7E );
328				handled = YES;
329				break;
330
331			case NSDownArrowFunctionKey:
332				ESScreensaver_AppendKeyEvent( 0x7D );
333				handled = YES;
334				break;
335
336			case NSF1FunctionKey:
337				ESScreensaver_AppendKeyEvent( 0x7A );
338				handled = YES;
339				break;
340
341			case NSF2FunctionKey:
342				ESScreensaver_AppendKeyEvent( 0x78 );
343				handled = YES;
344				break;
345
346			case NSF3FunctionKey:
347				ESScreensaver_AppendKeyEvent( 0x63 );
348				handled = YES;
349				break;
350
351			case NSF4FunctionKey:
352				ESScreensaver_AppendKeyEvent( 0x76 );
353				handled = YES;
354				break;
355
356			case NSF8FunctionKey:
357				ESScreensaver_AppendKeyEvent( 0x64 );
358				handled = YES;
359				break;
360
361			default:
362				break;
363		}
364    }
365
366    // If we didn't handle the key press, send it to the parent class
367    if (handled == NO)
368		[super keyDown:ev];
369}
370
371// Called immediately before relaunching.
372- (void)updaterWillRelaunchApplication:(SUUpdater *) __unused updater
373{
374    if (m_config != NULL)
375		[NSApp endSheet:[m_config window]];
376}
377
378- (void)doUpdate:(NSTimer*)timer
379{
380	SUAppcastItem* update = [timer userInfo];
381
382	if (!m_isFullScreen)
383		[m_updater checkForUpdatesInBackground];
384	else
385		ESScreensaver_SetUpdateAvailable([[update displayVersionString] UTF8String]);
386}
387
388// Sent when a valid update is found by the update driver.
389- (void)updater:(SUUpdater *) __unused updater didFindValidUpdate:(SUAppcastItem *)update
390{
391	[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doUpdate:) userInfo:update repeats:NO];
392}
393
394- (BOOL)fullscreen
395{
396	return m_isFullScreen;
397}
398
399- (void)setFullScreen:(BOOL)fullscreen
400{
401	m_isFullScreen = fullscreen;
402}
403
404
405@end
406