xref: /haiku/src/kits/screensaver/ScreenSaverRunner.cpp (revision a085e81e62d7a860f809b4fb7c7bf5654c396985)
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[] = {
121 		B_USER_NONPACKAGED_ADDONS_DIRECTORY,
122 		B_USER_ADDONS_DIRECTORY,
123 		B_COMMON_NONPACKAGED_ADDONS_DIRECTORY,
124 		B_COMMON_ADDONS_DIRECTORY,
125 		B_SYSTEM_ADDONS_DIRECTORY,
126 	};
127 	BPath path;
128 
129 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
130 		if (find_directory(which[i], &path, false) != B_OK)
131 			continue;
132 
133 		path.Append("Screen Savers");
134 		path.Append(fSettings.ModuleName());
135 
136 		fAddonImage = load_add_on(path.Path());
137 		if (fAddonImage >= B_OK)
138 			break;
139 	}
140 
141 	if (fAddonImage < B_OK) {
142 		printf("Unable to open add-on: %s: %s\n", path.Path(), strerror(fAddonImage));
143 	} else {
144 		// Look for the one C function that should exist.
145 		if (get_image_symbol(fAddonImage, "instantiate_screen_saver",
146 				B_SYMBOL_TYPE_TEXT, (void **)&instantiate) != B_OK) {
147 			printf("Unable to find the instantiator\n");
148 		} else {
149 			BMessage state;
150 			fSettings.GetModuleState(fSettings.ModuleName(), &state);
151 			fSaver = instantiate(&state, fAddonImage);
152 		}
153 
154 		if (fSaver->InitCheck() != B_OK) {
155 			printf("ScreenSaver initialization failed: %s!\n", strerror(fSaver->InitCheck()));
156 			_CleanUp();
157 		}
158 	}
159 
160 	Resume();
161 }
162 
163 
164 void
165 ScreenSaverRunner::_CleanUp()
166 {
167 	delete fSaver;
168 	fSaver = NULL;
169 
170 	if (fAddonImage >= 0) {
171 		unload_add_on(fAddonImage);
172 		fAddonImage = -1;
173 	}
174 }
175 
176 
177 void
178 ScreenSaverRunner::_Run()
179 {
180 	static const uint32 kInitialTickRate = 50000;
181 
182 	if (fWindow->Lock()) {
183 		fView->SetViewColor(0, 0, 0);
184 		fView->SetLowColor(0, 0, 0);
185 		if (fSaver)
186 			fHasStarted = fSaver->StartSaver(fView, fPreview) == B_OK;
187 		fWindow->Unlock();
188 	}
189 
190 	// TODO: This code is getting awfully complicated and should
191 	// probably be refactored.
192 	uint32 tickBase = kInitialTickRate;
193 	int32 snoozeCount = 0;
194 	int32 frame = 0;
195 	bigtime_t lastTickTime = 0;
196 	bigtime_t tick = fSaver ? fSaver->TickSize() : tickBase;
197 
198 	while (!fQuitting) {
199 		// break the idle time up into ticks so that we can evaluate
200 		// the quit condition with greater responsiveness
201 		// otherwise a screen saver that sets, say, a 30 second tick
202 		// will result in the screen saver not responding to deactivation
203 		// for that length of time
204 		snooze(tickBase);
205 		if (system_time() - lastTickTime < tick) {
206 			continue;
207 		} else {
208 			// re-evaluate the tick time after each successful wakeup -
209 			// screensavers can adjust it on the fly, and we must be
210 			// prepared to accomodate that
211 			tick = fSaver ? fSaver->TickSize() : tickBase;
212 
213 			if (tick < tickBase) {
214 				if (tick < 0)
215 					tick = 0;
216 				tickBase = tick;
217 			} else if (tickBase < kInitialTickRate && tick >= kInitialTickRate) {
218 				tickBase = kInitialTickRate;
219 			}
220 
221 			lastTickTime = system_time();
222 		}
223 
224 		if (snoozeCount) {
225 			// if we are sleeping, do nothing
226 			snoozeCount--;
227 		} else if (fSaver != NULL && fHasStarted) {
228 			if (fSaver->LoopOnCount() && frame >= fSaver->LoopOnCount()) {
229 				// Time to nap
230 				frame = 0;
231 				snoozeCount = fSaver->LoopOffCount();
232 			} else if (fWindow->LockWithTimeout(5000LL) == B_OK) {
233 				if (!fQuitting) {
234 					// NOTE: R5 really calls DirectDraw()
235 					// and then Draw() for the same frame
236 					if (fDirectWindow)
237 						fSaver->DirectDraw(frame);
238 					fSaver->Draw(fView, frame);
239 					fView->Sync();
240 					frame++;
241 				}
242 				fWindow->Unlock();
243 			}
244 		} else
245 			snoozeCount = 1000;
246 	}
247 
248 	if (fSaver)
249 		fSaver->StopSaver();
250 }
251 
252 
253 status_t
254 ScreenSaverRunner::_ThreadFunc(void *data)
255 {
256 	ScreenSaverRunner* runner = (ScreenSaverRunner*)data;
257 	runner->_Run();
258 	return B_OK;
259 }
260 
261