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