xref: /haiku/src/kits/screensaver/ScreenSaverRunner.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 /*
2  * Copyright 2003-2013 Haiku, Inc. All rights reserved
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Jérôme Duval, jerome.duval@free.fr
8  *		Michael Phipps
9  *		John Scipione, jscipione@gmail.com
10  */
11 
12 
13 #include "ScreenSaverRunner.h"
14 
15 #include <stdio.h>
16 
17 #include <DirectWindow.h>
18 #include <FindDirectory.h>
19 #include <Message.h>
20 #include <Window.h>
21 
22 
23 ScreenSaverRunner::ScreenSaverRunner(BWindow* window, BView* view,
24 	ScreenSaverSettings& settings)
25 	:
26 	fWindow(window),
27 	fView(view),
28 	fIsDirectDraw(dynamic_cast<BDirectWindow*>(window) != NULL),
29 	fSettings(settings),
30 	fSaver(NULL),
31 	fAddonImage(-1),
32 	fThread(-1),
33 	fQuitting(false)
34 {
35 	_LoadAddOn();
36 }
37 
38 
39 ScreenSaverRunner::~ScreenSaverRunner()
40 {
41 	if (!fQuitting)
42 		Quit();
43 
44 	_CleanUp();
45 }
46 
47 
48 status_t
49 ScreenSaverRunner::Run()
50 {
51 	fThread = spawn_thread(&_ThreadFunc, "ScreenSaverRenderer", B_LOW_PRIORITY,
52 		this);
53 	Resume();
54 
55 	return fThread >= B_OK ? B_OK : fThread;
56 }
57 
58 
59 void
60 ScreenSaverRunner::Quit()
61 {
62 	fQuitting = true;
63 	Resume();
64 
65 	if (fThread >= 0) {
66 		status_t returnValue;
67 		wait_for_thread(fThread, &returnValue);
68 	}
69 }
70 
71 
72 status_t
73 ScreenSaverRunner::Suspend()
74 {
75 	return suspend_thread(fThread);
76 }
77 
78 
79 status_t
80 ScreenSaverRunner::Resume()
81 {
82 	return resume_thread(fThread);
83 }
84 
85 
86 void
87 ScreenSaverRunner::_LoadAddOn()
88 {
89 	// This is a new set of preferences. Free up what we did have
90 	// TODO: this is currently not meant to be used after creation
91 	if (fThread >= B_OK) {
92 		Suspend();
93 		if (fSaver != NULL)
94 			fSaver->StopSaver();
95 	}
96 	_CleanUp();
97 
98 	const char* moduleName = fSettings.ModuleName();
99 	if (moduleName == NULL || *moduleName == '\0') {
100 		Resume();
101 		return;
102 	}
103 
104 	BScreenSaver* (*instantiate)(BMessage*, image_id);
105 
106 	// try each directory until one succeeds
107 
108 	directory_which which[] = {
109 		B_USER_NONPACKAGED_ADDONS_DIRECTORY,
110 		B_USER_ADDONS_DIRECTORY,
111 		B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
112 		B_SYSTEM_ADDONS_DIRECTORY,
113 	};
114 	BPath path;
115 
116 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
117 		if (find_directory(which[i], &path, false) != B_OK)
118 			continue;
119 		else if (path.Append("Screen Savers") != B_OK)
120 			continue;
121 		else if (path.Append(fSettings.ModuleName()) != B_OK)
122 			continue;
123 
124 		fAddonImage = load_add_on(path.Path());
125 		if (fAddonImage > 0)
126 			break;
127 	}
128 
129 	if (fAddonImage > 0) {
130 		// look for the one C function that should exist,
131 		// instantiate_screen_saver()
132 		if (get_image_symbol(fAddonImage, "instantiate_screen_saver",
133 				B_SYMBOL_TYPE_TEXT, (void **)&instantiate) != B_OK) {
134 			fprintf(stderr, "Unable to find the instantiation function.\n");
135 		} else {
136 			BMessage state;
137 			fSettings.GetModuleState(moduleName, &state);
138 			fSaver = instantiate(&state, fAddonImage);
139 		}
140 
141 		if (fSaver == NULL) {
142 			fprintf(stderr, "Screen saver initialization failed.\n");
143 			_CleanUp();
144 		} else if (fSaver->InitCheck() != B_OK) {
145 			fprintf(stderr, "Screen saver initialization failed: %s.\n",
146 				strerror(fSaver->InitCheck()));
147 			_CleanUp();
148 		}
149 	} else
150 		fprintf(stderr, "Unable to open add-on %s.\n", path.Path());
151 
152 	Resume();
153 }
154 
155 
156 void
157 ScreenSaverRunner::_CleanUp()
158 {
159 	delete fSaver;
160 	fSaver = NULL;
161 
162 	if (fAddonImage >= 0) {
163 		status_t result = unload_add_on(fAddonImage);
164 		if (result != B_OK) {
165 			fprintf(stderr, "Unable to unload screen saver add-on: %s.\n",
166 				strerror(result));
167 		}
168 		fAddonImage = -1;
169 	}
170 }
171 
172 
173 status_t
174 ScreenSaverRunner::_Run()
175 {
176 	static const uint32 kInitialTickRate = 50000;
177 
178 	// TODO: This code is getting awfully complicated and should
179 	// probably be refactored.
180 	uint32 tickBase = kInitialTickRate;
181 	int32 snoozeCount = 0;
182 	int32 frame = 0;
183 	bigtime_t lastTickTime = 0;
184 	bigtime_t tick = fSaver != NULL ? fSaver->TickSize() : tickBase;
185 
186 	while (!fQuitting) {
187 		// break the idle time up into ticks so that we can evaluate
188 		// the quit condition with greater responsiveness
189 		// otherwise a screen saver that sets, say, a 30 second tick
190 		// will result in the screen saver not responding to deactivation
191 		// for that length of time
192 		snooze(tickBase);
193 		if (system_time() - lastTickTime < tick)
194 			continue;
195 		else {
196 			// re-evaluate the tick time after each successful wakeup
197 			// screensavers can adjust it on the fly, and we must be
198 			// prepared to accomodate that
199 			tick = fSaver != NULL ? fSaver->TickSize() : tickBase;
200 
201 			if (tick < tickBase) {
202 				if (tick < 0)
203 					tick = 0;
204 				tickBase = tick;
205 			} else if (tickBase < kInitialTickRate
206 				&& tick >= kInitialTickRate) {
207 				tickBase = kInitialTickRate;
208 			}
209 
210 			lastTickTime = system_time();
211 		}
212 
213 		if (snoozeCount) {
214 			// if we are sleeping, do nothing
215 			snoozeCount--;
216 		} else if (fSaver != NULL) {
217 			if (fSaver->LoopOnCount() && frame >= fSaver->LoopOnCount()) {
218 				// Time to nap
219 				frame = 0;
220 				snoozeCount = fSaver->LoopOffCount();
221 			} else if (fWindow->LockWithTimeout(5000LL) == B_OK) {
222 				if (!fQuitting) {
223 					// NOTE: BeOS R5 really calls DirectDraw()
224 					// and then Draw() for the same frame
225 					if (fIsDirectDraw)
226 						fSaver->DirectDraw(frame);
227 					fSaver->Draw(fView, frame);
228 					fView->Sync();
229 					frame++;
230 				}
231 				fWindow->Unlock();
232 			}
233 		} else
234 			snoozeCount = 1000;
235 	}
236 
237 	if (fSaver != NULL)
238 		fSaver->StopSaver();
239 
240 	return B_OK;
241 }
242 
243 
244 status_t
245 ScreenSaverRunner::_ThreadFunc(void* data)
246 {
247 	ScreenSaverRunner* runner = (ScreenSaverRunner*)data;
248 	return runner->_Run();
249 }
250