xref: /haiku/src/kits/screensaver/ScreenSaverRunner.cpp (revision e7c8829c5d8e5d34a2a1e111f1c06aceff256013)
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 	const uint32 kTickBase = 50000;
176 	if (fWindow->Lock()) {
177 		fView->SetViewColor(0, 0, 0);
178 		fView->SetLowColor(0, 0, 0);
179 		if (fSaver)
180 			fHasStarted = fSaver->StartSaver(fView, fPreview) == B_OK;
181 		fWindow->Unlock();
182 	}
183 
184 	int32 snoozeCount = 0;
185 	int32 frame = 0;
186 	bigtime_t lastTickTime = 0;
187 	bigtime_t tick = fSaver ? fSaver->TickSize() : kTickBase;
188 
189 	while (!fQuitting) {
190 		// break the idle time up into ticks so that we can evaluate
191 		// the quit condition with greater responsiveness
192 		// otherwise a screen saver that sets, say, a 30 second tick
193 		// will result in the screen saver not responding to deactivation
194 		// for that length of time
195 		snooze(kTickBase);
196 		if (system_time() - lastTickTime < tick)
197 			continue;
198 		else {
199 			// re-evaluate the tick time after each successful wakeup -
200 			// screensavers can adjust it on the fly and we must be
201 			// prepared to accomodate that
202 			tick = fSaver ? fSaver->TickSize() : kTickBase;
203 			lastTickTime = system_time();
204 		}
205 
206 		if (snoozeCount) {
207 			// if we are sleeping, do nothing
208 			snoozeCount--;
209 		} else if (fSaver != NULL && fHasStarted) {
210 			if (fSaver->LoopOnCount() && frame >= fSaver->LoopOnCount()) {
211 				// Time to nap
212 				frame = 0;
213 				snoozeCount = fSaver->LoopOffCount();
214 			} else if (fWindow->LockWithTimeout(5000LL) == B_OK) {
215 				if (!fQuitting) {
216 					// NOTE: R5 really calls DirectDraw()
217 					// and then Draw() for the same frame
218 					if (fDirectWindow)
219 						fSaver->DirectDraw(frame);
220 					fSaver->Draw(fView, frame);
221 					fView->Sync();
222 					frame++;
223 				}
224 				fWindow->Unlock();
225 			}
226 		} else
227 			snoozeCount = 1000;
228 	}
229 
230 	if (fSaver)
231 		fSaver->StopSaver();
232 }
233 
234 
235 status_t
236 ScreenSaverRunner::_ThreadFunc(void *data)
237 {
238 	ScreenSaverRunner* runner = (ScreenSaverRunner*)data;
239 	runner->_Run();
240 	return B_OK;
241 }
242 
243