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
ScreenSaverRunner(BWindow * window,BView * view,ScreenSaverSettings & settings)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
~ScreenSaverRunner()39 ScreenSaverRunner::~ScreenSaverRunner()
40 {
41 if (!fQuitting)
42 Quit();
43
44 _CleanUp();
45 }
46
47
48 status_t
Run()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
Quit()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
Suspend()73 ScreenSaverRunner::Suspend()
74 {
75 return suspend_thread(fThread);
76 }
77
78
79 status_t
Resume()80 ScreenSaverRunner::Resume()
81 {
82 return resume_thread(fThread);
83 }
84
85
86 void
_LoadAddOn()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
_CleanUp()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
_Run()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
_ThreadFunc(void * data)245 ScreenSaverRunner::_ThreadFunc(void* data)
246 {
247 ScreenSaverRunner* runner = (ScreenSaverRunner*)data;
248 return runner->_Run();
249 }
250