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