diff options
author | Armin Weatherwax | 2010-06-14 16:27:25 +0200 |
---|---|---|
committer | Armin Weatherwax | 2010-09-23 15:42:38 +0200 |
commit | 3069d8886241b2d9deaadfbf8823d463b469ec6b (patch) | |
tree | e70aeae85d110d66a7c048a0132085e0301ab068 /linden/indra/media_plugins/webkit | |
parent | make it compile and run. (diff) | |
download | meta-impy-3069d8886241b2d9deaadfbf8823d463b469ec6b.zip meta-impy-3069d8886241b2d9deaadfbf8823d463b469ec6b.tar.gz meta-impy-3069d8886241b2d9deaadfbf8823d463b469ec6b.tar.bz2 meta-impy-3069d8886241b2d9deaadfbf8823d463b469ec6b.tar.xz |
update to latest SG2 media_plugins
Diffstat (limited to 'linden/indra/media_plugins/webkit')
6 files changed, 821 insertions, 114 deletions
diff --git a/linden/indra/media_plugins/webkit/CMakeLists.txt b/linden/indra/media_plugins/webkit/CMakeLists.txt index 1c999ba..be02781 100644 --- a/linden/indra/media_plugins/webkit/CMakeLists.txt +++ b/linden/indra/media_plugins/webkit/CMakeLists.txt | |||
@@ -14,10 +14,12 @@ include(Linking) | |||
14 | include(PluginAPI) | 14 | include(PluginAPI) |
15 | include(MediaPluginBase) | 15 | include(MediaPluginBase) |
16 | include(FindOpenGL) | 16 | include(FindOpenGL) |
17 | #include(PulseAudio) | ||
17 | 18 | ||
18 | include(WebKitLibPlugin) | 19 | include(WebKitLibPlugin) |
19 | 20 | ||
20 | include_directories( | 21 | include_directories( |
22 | ${PULSEAUDIO_INCLUDE_DIRS} | ||
21 | ${LLPLUGIN_INCLUDE_DIRS} | 23 | ${LLPLUGIN_INCLUDE_DIRS} |
22 | ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} | 24 | ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} |
23 | ${LLCOMMON_INCLUDE_DIRS} | 25 | ${LLCOMMON_INCLUDE_DIRS} |
@@ -35,13 +37,13 @@ set(media_plugin_webkit_SOURCE_FILES | |||
35 | media_plugin_webkit.cpp | 37 | media_plugin_webkit.cpp |
36 | ) | 38 | ) |
37 | 39 | ||
38 | if(WORD_SIZE EQUAL 64) | 40 | if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) |
39 | if(WINDOWS) | 41 | if(WINDOWS) |
40 | add_definitions(/FIXED:NO) | 42 | add_definitions(/FIXED:NO) |
41 | else(WINDOWS) # not windows therefore gcc LINUX and DARWIN | 43 | else(WINDOWS) # not windows therefore gcc LINUX and DARWIN |
42 | add_definitions(-fPIC) | 44 | add_definitions(-fPIC) |
43 | endif(WINDOWS) | 45 | endif(WINDOWS) |
44 | endif (WORD_SIZE EQUAL 64) | 46 | endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) |
45 | 47 | ||
46 | set(media_plugin_webkit_LINK_LIBRARIES | 48 | set(media_plugin_webkit_LINK_LIBRARIES |
47 | ${LLPLUGIN_LIBRARIES} | 49 | ${LLPLUGIN_LIBRARIES} |
@@ -49,9 +51,11 @@ set(media_plugin_webkit_LINK_LIBRARIES | |||
49 | ${LLCOMMON_LIBRARIES} | 51 | ${LLCOMMON_LIBRARIES} |
50 | ${WEBKIT_PLUGIN_LIBRARIES} | 52 | ${WEBKIT_PLUGIN_LIBRARIES} |
51 | ${PLUGIN_API_WINDOWS_LIBRARIES} | 53 | ${PLUGIN_API_WINDOWS_LIBRARIES} |
54 | ${PULSEAUDIO_LIBRARIES} | ||
52 | ) | 55 | ) |
53 | 56 | ||
54 | if(LINUX) | 57 | if (LINUX) |
58 | list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp) | ||
55 | list(APPEND media_plugin_webkit_LINK_LIBRARIES | 59 | list(APPEND media_plugin_webkit_LINK_LIBRARIES |
56 | ${UI_LIBRARIES} # for glib/GTK | 60 | ${UI_LIBRARIES} # for glib/GTK |
57 | ) | 61 | ) |
diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp b/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp new file mode 100755 index 0000000..15a2dfb --- /dev/null +++ b/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp | |||
@@ -0,0 +1,490 @@ | |||
1 | /** | ||
2 | * @file linux_volume_catcher.cpp | ||
3 | * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources | ||
4 | * | ||
5 | * @cond | ||
6 | * $LicenseInfo:firstyear=2010&license=viewergpl$ | ||
7 | * | ||
8 | * Copyright (c) 2010, Linden Research, Inc. | ||
9 | * | ||
10 | * Second Life Viewer Source Code | ||
11 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
12 | * to you under the terms of the GNU General Public License, version 2.0 | ||
13 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
14 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
15 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
16 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
17 | * | ||
18 | * There are special exceptions to the terms and conditions of the GPL as | ||
19 | * it is applied to this Source Code. View the full text of the exception | ||
20 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
21 | * online at | ||
22 | * http://secondlife.com/developers/opensource/flossexception | ||
23 | * | ||
24 | * By copying, modifying or distributing this software, you acknowledge | ||
25 | * that you have read and understood your obligations described above, | ||
26 | * and agree to abide by those obligations. | ||
27 | * | ||
28 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
29 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
30 | * COMPLETENESS OR PERFORMANCE. | ||
31 | * $/LicenseInfo$ | ||
32 | * | ||
33 | * @endcond | ||
34 | */ | ||
35 | |||
36 | /* | ||
37 | The high-level design is as follows: | ||
38 | 1) Connect to the PulseAudio daemon | ||
39 | 2) Watch for the creation of new audio players connecting to the daemon (this includes ALSA clients running on the PulseAudio emulation layer, such as Flash plugins) | ||
40 | 3) Examine any new audio player's PID to see if it belongs to our own process | ||
41 | 4) If so, tell PA to adjust the volume of that audio player ('sink input' in PA parlance) | ||
42 | 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call | ||
43 | */ | ||
44 | |||
45 | #include "linden_common.h" | ||
46 | |||
47 | #include "linux_volume_catcher.h" | ||
48 | |||
49 | |||
50 | #if LL_PULSEAUDIO_ENABLED | ||
51 | |||
52 | extern "C" { | ||
53 | #include <glib.h> | ||
54 | |||
55 | #include <pulse/introspect.h> | ||
56 | #include <pulse/context.h> | ||
57 | #include <pulse/subscribe.h> | ||
58 | #include <pulse/glib-mainloop.h> // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken. | ||
59 | |||
60 | #include "apr_pools.h" | ||
61 | #include "apr_dso.h" | ||
62 | } | ||
63 | |||
64 | //////////////////////////////////////////////////// | ||
65 | |||
66 | #define DEBUGMSG(...) do {} while(0) | ||
67 | #define INFOMSG(...) do {} while(0) | ||
68 | #define WARNMSG(...) do {} while(0) | ||
69 | |||
70 | #define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) RTN (*ll##PASYM)(__VA_ARGS__) = NULL | ||
71 | #include "linux_volume_catcher_pa_syms.inc" | ||
72 | #include "linux_volume_catcher_paglib_syms.inc" | ||
73 | #undef LL_PA_SYM | ||
74 | |||
75 | static bool sSymsGrabbed = false; | ||
76 | static apr_pool_t *sSymPADSOMemoryPool = NULL; | ||
77 | static apr_dso_handle_t *sSymPADSOHandleG = NULL; | ||
78 | |||
79 | bool grab_pa_syms(std::string pulse_dso_name) | ||
80 | { | ||
81 | if (sSymsGrabbed) | ||
82 | { | ||
83 | // already have grabbed good syms | ||
84 | return true; | ||
85 | } | ||
86 | |||
87 | bool sym_error = false; | ||
88 | bool rtn = false; | ||
89 | apr_status_t rv; | ||
90 | apr_dso_handle_t *sSymPADSOHandle = NULL; | ||
91 | |||
92 | #define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0) | ||
93 | |||
94 | //attempt to load the shared library | ||
95 | apr_pool_create(&sSymPADSOMemoryPool, NULL); | ||
96 | |||
97 | if ( APR_SUCCESS == (rv = apr_dso_load(&sSymPADSOHandle, | ||
98 | pulse_dso_name.c_str(), | ||
99 | sSymPADSOMemoryPool) )) | ||
100 | { | ||
101 | INFOMSG("Found DSO: %s", pulse_dso_name.c_str()); | ||
102 | |||
103 | #include "linux_volume_catcher_pa_syms.inc" | ||
104 | #include "linux_volume_catcher_paglib_syms.inc" | ||
105 | |||
106 | if ( sSymPADSOHandle ) | ||
107 | { | ||
108 | sSymPADSOHandleG = sSymPADSOHandle; | ||
109 | sSymPADSOHandle = NULL; | ||
110 | } | ||
111 | |||
112 | rtn = !sym_error; | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | INFOMSG("Couldn't load DSO: %s", pulse_dso_name.c_str()); | ||
117 | rtn = false; // failure | ||
118 | } | ||
119 | |||
120 | if (sym_error) | ||
121 | { | ||
122 | WARNMSG("Failed to find necessary symbols in PulseAudio libraries."); | ||
123 | } | ||
124 | #undef LL_PA_SYM | ||
125 | |||
126 | sSymsGrabbed = rtn; | ||
127 | return rtn; | ||
128 | } | ||
129 | |||
130 | |||
131 | void ungrab_pa_syms() | ||
132 | { | ||
133 | // should be safe to call regardless of whether we've | ||
134 | // actually grabbed syms. | ||
135 | |||
136 | if ( sSymPADSOHandleG ) | ||
137 | { | ||
138 | apr_dso_unload(sSymPADSOHandleG); | ||
139 | sSymPADSOHandleG = NULL; | ||
140 | } | ||
141 | |||
142 | if ( sSymPADSOMemoryPool ) | ||
143 | { | ||
144 | apr_pool_destroy(sSymPADSOMemoryPool); | ||
145 | sSymPADSOMemoryPool = NULL; | ||
146 | } | ||
147 | |||
148 | // NULL-out all of the symbols we'd grabbed | ||
149 | #define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{ll##PASYM = NULL;}while(0) | ||
150 | #include "linux_volume_catcher_pa_syms.inc" | ||
151 | #include "linux_volume_catcher_paglib_syms.inc" | ||
152 | #undef LL_PA_SYM | ||
153 | |||
154 | sSymsGrabbed = false; | ||
155 | } | ||
156 | //////////////////////////////////////////////////// | ||
157 | |||
158 | // PulseAudio requires a chain of callbacks with C linkage | ||
159 | extern "C" { | ||
160 | void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *i, int eol, void *userdata); | ||
161 | void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata); | ||
162 | void callback_context_state(pa_context *context, void *userdata); | ||
163 | } | ||
164 | |||
165 | |||
166 | class LinuxVolumeCatcherImpl | ||
167 | { | ||
168 | public: | ||
169 | LinuxVolumeCatcherImpl(); | ||
170 | ~LinuxVolumeCatcherImpl(); | ||
171 | |||
172 | void setVolume(F32 volume); | ||
173 | void pump(void); | ||
174 | |||
175 | // for internal use - can't be private because used from our C callbacks | ||
176 | |||
177 | bool loadsyms(std::string pulse_dso_name); | ||
178 | void init(); | ||
179 | void cleanup(); | ||
180 | |||
181 | void update_all_volumes(F32 volume); | ||
182 | void update_index_volume(U32 index, F32 volume); | ||
183 | void connected_okay(); | ||
184 | |||
185 | std::set<U32> mSinkInputIndices; | ||
186 | std::map<U32,U32> mSinkInputNumChannels; | ||
187 | F32 mDesiredVolume; | ||
188 | pa_glib_mainloop *mMainloop; | ||
189 | pa_context *mPAContext; | ||
190 | bool mConnected; | ||
191 | bool mGotSyms; | ||
192 | }; | ||
193 | |||
194 | LinuxVolumeCatcherImpl::LinuxVolumeCatcherImpl() | ||
195 | : mDesiredVolume(0.0f), | ||
196 | mMainloop(NULL), | ||
197 | mPAContext(NULL), | ||
198 | mConnected(false), | ||
199 | mGotSyms(false) | ||
200 | { | ||
201 | init(); | ||
202 | } | ||
203 | |||
204 | LinuxVolumeCatcherImpl::~LinuxVolumeCatcherImpl() | ||
205 | { | ||
206 | cleanup(); | ||
207 | } | ||
208 | |||
209 | bool LinuxVolumeCatcherImpl::loadsyms(std::string pulse_dso_name) | ||
210 | { | ||
211 | return grab_pa_syms(pulse_dso_name); | ||
212 | } | ||
213 | |||
214 | void LinuxVolumeCatcherImpl::init() | ||
215 | { | ||
216 | // try to be as defensive as possible because PA's interface is a | ||
217 | // bit fragile and (for our purposes) we'd rather simply not function | ||
218 | // than crash | ||
219 | |||
220 | // we cheat and rely upon libpulse-mainloop-glib.so.0 to pull-in | ||
221 | // libpulse.so.0 - this isn't a great assumption, and the two DSOs should | ||
222 | // probably be loaded separately. Our Linux DSO framework needs refactoring, | ||
223 | // we do this sort of thing a lot with practically identical logic... | ||
224 | mGotSyms = loadsyms("libpulse-mainloop-glib.so.0"); | ||
225 | if (!mGotSyms) return; | ||
226 | |||
227 | mMainloop = llpa_glib_mainloop_new(g_main_context_default()); | ||
228 | if (mMainloop) | ||
229 | { | ||
230 | pa_mainloop_api *api = llpa_glib_mainloop_get_api(mMainloop); | ||
231 | if (api) | ||
232 | { | ||
233 | pa_proplist *proplist = llpa_proplist_new(); | ||
234 | if (proplist) | ||
235 | { | ||
236 | llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-player"); | ||
237 | llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.secondlife.viewer.mediaplugvoladjust"); | ||
238 | llpa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "SL Plugin Volume Adjuster"); | ||
239 | llpa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "1"); | ||
240 | |||
241 | // plain old pa_context_new() is broken! | ||
242 | mPAContext = llpa_context_new_with_proplist(api, NULL, proplist); | ||
243 | llpa_proplist_free(proplist); | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | |||
248 | // Now we've set up a PA context and mainloop, try connecting the | ||
249 | // PA context to a PA daemon. | ||
250 | if (mPAContext) | ||
251 | { | ||
252 | llpa_context_set_state_callback(mPAContext, callback_context_state, this); | ||
253 | pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN? | ||
254 | if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0) | ||
255 | { | ||
256 | // Okay! We haven't definitely connected, but we | ||
257 | // haven't definitely failed yet. | ||
258 | } | ||
259 | else | ||
260 | { | ||
261 | // Failed to connect to PA manager... we'll leave | ||
262 | // things like that. Perhaps we should try again later. | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | void LinuxVolumeCatcherImpl::cleanup() | ||
268 | { | ||
269 | mConnected = false; | ||
270 | |||
271 | if (mGotSyms && mPAContext) | ||
272 | { | ||
273 | llpa_context_disconnect(mPAContext); | ||
274 | llpa_context_unref(mPAContext); | ||
275 | } | ||
276 | mPAContext = NULL; | ||
277 | |||
278 | if (mGotSyms && mMainloop) | ||
279 | { | ||
280 | llpa_glib_mainloop_free(mMainloop); | ||
281 | } | ||
282 | mMainloop = NULL; | ||
283 | } | ||
284 | |||
285 | void LinuxVolumeCatcherImpl::setVolume(F32 volume) | ||
286 | { | ||
287 | mDesiredVolume = volume; | ||
288 | |||
289 | if (!mGotSyms) return; | ||
290 | |||
291 | if (mConnected && mPAContext) | ||
292 | { | ||
293 | update_all_volumes(mDesiredVolume); | ||
294 | } | ||
295 | |||
296 | pump(); | ||
297 | } | ||
298 | |||
299 | void LinuxVolumeCatcherImpl::pump() | ||
300 | { | ||
301 | gboolean may_block = FALSE; | ||
302 | g_main_context_iteration(g_main_context_default(), may_block); | ||
303 | } | ||
304 | |||
305 | void LinuxVolumeCatcherImpl::connected_okay() | ||
306 | { | ||
307 | pa_operation *op; | ||
308 | |||
309 | // fetch global list of existing sinkinputs | ||
310 | if ((op = llpa_context_get_sink_input_info_list(mPAContext, | ||
311 | callback_discovered_sinkinput, | ||
312 | this))) | ||
313 | { | ||
314 | llpa_operation_unref(op); | ||
315 | } | ||
316 | |||
317 | // subscribe to future global sinkinput changes | ||
318 | llpa_context_set_subscribe_callback(mPAContext, | ||
319 | callback_subscription_alert, | ||
320 | this); | ||
321 | if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t) | ||
322 | (PA_SUBSCRIPTION_MASK_SINK_INPUT), | ||
323 | NULL, NULL))) | ||
324 | { | ||
325 | llpa_operation_unref(op); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | void LinuxVolumeCatcherImpl::update_all_volumes(F32 volume) | ||
330 | { | ||
331 | for (std::set<U32>::iterator it = mSinkInputIndices.begin(); | ||
332 | it != mSinkInputIndices.end(); ++it) | ||
333 | { | ||
334 | update_index_volume(*it, volume); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | void LinuxVolumeCatcherImpl::update_index_volume(U32 index, F32 volume) | ||
339 | { | ||
340 | static pa_cvolume cvol; | ||
341 | llpa_cvolume_set(&cvol, mSinkInputNumChannels[index], | ||
342 | llpa_sw_volume_from_linear(volume)); | ||
343 | |||
344 | pa_context *c = mPAContext; | ||
345 | uint32_t idx = index; | ||
346 | const pa_cvolume *cvolumep = &cvol; | ||
347 | pa_context_success_cb_t cb = NULL; // okay as null | ||
348 | void *userdata = NULL; // okay as null | ||
349 | |||
350 | pa_operation *op; | ||
351 | if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata))) | ||
352 | { | ||
353 | llpa_operation_unref(op); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | |||
358 | void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata) | ||
359 | { | ||
360 | LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata); | ||
361 | llassert(impl); | ||
362 | |||
363 | if (0 == eol) | ||
364 | { | ||
365 | pa_proplist *proplist = sii->proplist; | ||
366 | pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID)); | ||
367 | |||
368 | if (sinkpid == getpid()) // does the discovered sinkinput belong to this process? | ||
369 | { | ||
370 | bool is_new = (impl->mSinkInputIndices.find(sii->index) == | ||
371 | impl->mSinkInputIndices.end()); | ||
372 | |||
373 | impl->mSinkInputIndices.insert(sii->index); | ||
374 | impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels; | ||
375 | |||
376 | if (is_new) | ||
377 | { | ||
378 | // new! | ||
379 | impl->update_index_volume(sii->index, impl->mDesiredVolume); | ||
380 | } | ||
381 | else | ||
382 | { | ||
383 | // seen it already, do nothing. | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | |||
389 | void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) | ||
390 | { | ||
391 | LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata); | ||
392 | llassert(impl); | ||
393 | |||
394 | switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { | ||
395 | case PA_SUBSCRIPTION_EVENT_SINK_INPUT: | ||
396 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == | ||
397 | PA_SUBSCRIPTION_EVENT_REMOVE) | ||
398 | { | ||
399 | // forget this sinkinput, if we were caring about it | ||
400 | impl->mSinkInputIndices.erase(index); | ||
401 | impl->mSinkInputNumChannels.erase(index); | ||
402 | } | ||
403 | else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == | ||
404 | PA_SUBSCRIPTION_EVENT_NEW) | ||
405 | { | ||
406 | // ask for more info about this new sinkinput | ||
407 | pa_operation *op; | ||
408 | if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl))) | ||
409 | { | ||
410 | llpa_operation_unref(op); | ||
411 | } | ||
412 | } | ||
413 | else | ||
414 | { | ||
415 | // property change on this sinkinput - we don't care. | ||
416 | } | ||
417 | break; | ||
418 | |||
419 | default:; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | void callback_context_state(pa_context *context, void *userdata) | ||
424 | { | ||
425 | LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata); | ||
426 | llassert(impl); | ||
427 | |||
428 | switch (llpa_context_get_state(context)) | ||
429 | { | ||
430 | case PA_CONTEXT_READY: | ||
431 | impl->mConnected = true; | ||
432 | impl->connected_okay(); | ||
433 | break; | ||
434 | case PA_CONTEXT_TERMINATED: | ||
435 | impl->mConnected = false; | ||
436 | break; | ||
437 | case PA_CONTEXT_FAILED: | ||
438 | impl->mConnected = false; | ||
439 | break; | ||
440 | default:; | ||
441 | } | ||
442 | } | ||
443 | |||
444 | ///////////////////////////////////////////////////// | ||
445 | |||
446 | LinuxVolumeCatcher::LinuxVolumeCatcher() | ||
447 | { | ||
448 | pimpl = new LinuxVolumeCatcherImpl(); | ||
449 | } | ||
450 | |||
451 | LinuxVolumeCatcher::~LinuxVolumeCatcher() | ||
452 | { | ||
453 | delete pimpl; | ||
454 | pimpl = NULL; | ||
455 | } | ||
456 | |||
457 | void LinuxVolumeCatcher::setVolume(F32 volume) | ||
458 | { | ||
459 | llassert(pimpl); | ||
460 | pimpl->setVolume(volume); | ||
461 | } | ||
462 | |||
463 | void LinuxVolumeCatcher::pump() | ||
464 | { | ||
465 | llassert(pimpl); | ||
466 | pimpl->pump(); | ||
467 | } | ||
468 | |||
469 | #else // !LL_PULSEAUDIO_ENABLED | ||
470 | |||
471 | // stub. | ||
472 | |||
473 | LinuxVolumeCatcher::LinuxVolumeCatcher() | ||
474 | { | ||
475 | pimpl = NULL; | ||
476 | } | ||
477 | |||
478 | LinuxVolumeCatcher::~LinuxVolumeCatcher() | ||
479 | { | ||
480 | } | ||
481 | |||
482 | void LinuxVolumeCatcher::setVolume(F32 volume) | ||
483 | { | ||
484 | } | ||
485 | |||
486 | void LinuxVolumeCatcher::pump() | ||
487 | { | ||
488 | } | ||
489 | |||
490 | #endif // LL_PULSEAUDIO_ENABLED | ||
diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher.h b/linden/indra/media_plugins/webkit/linux_volume_catcher.h new file mode 100755 index 0000000..d4a1b38 --- /dev/null +++ b/linden/indra/media_plugins/webkit/linux_volume_catcher.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /** | ||
2 | * @file linux_volume_catcher.h | ||
3 | * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources | ||
4 | * | ||
5 | * @cond | ||
6 | * $LicenseInfo:firstyear=2010&license=viewergpl$ | ||
7 | * | ||
8 | * Copyright (c) 2010, Linden Research, Inc. | ||
9 | * | ||
10 | * Second Life Viewer Source Code | ||
11 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
12 | * to you under the terms of the GNU General Public License, version 2.0 | ||
13 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
14 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
15 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
16 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
17 | * | ||
18 | * There are special exceptions to the terms and conditions of the GPL as | ||
19 | * it is applied to this Source Code. View the full text of the exception | ||
20 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
21 | * online at | ||
22 | * http://secondlife.com/developers/opensource/flossexception | ||
23 | * | ||
24 | * By copying, modifying or distributing this software, you acknowledge | ||
25 | * that you have read and understood your obligations described above, | ||
26 | * and agree to abide by those obligations. | ||
27 | * | ||
28 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
29 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
30 | * COMPLETENESS OR PERFORMANCE. | ||
31 | * $/LicenseInfo$ | ||
32 | * | ||
33 | * @endcond | ||
34 | */ | ||
35 | |||
36 | #ifndef LINUX_VOLUME_CATCHER_H | ||
37 | #define LINUX_VOLUME_CATCHER_H | ||
38 | |||
39 | #include "linden_common.h" | ||
40 | |||
41 | class LinuxVolumeCatcherImpl; | ||
42 | |||
43 | class LinuxVolumeCatcher | ||
44 | { | ||
45 | public: | ||
46 | LinuxVolumeCatcher(); | ||
47 | ~LinuxVolumeCatcher(); | ||
48 | |||
49 | void setVolume(F32 volume); // 0.0 - 1.0 | ||
50 | void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume | ||
51 | |||
52 | private: | ||
53 | LinuxVolumeCatcherImpl *pimpl; | ||
54 | }; | ||
55 | |||
56 | #endif // LINUX_VOLUME_CATCHER_H | ||
diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc b/linden/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc new file mode 100755 index 0000000..d806b48 --- /dev/null +++ b/linden/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc | |||
@@ -0,0 +1,21 @@ | |||
1 | // required symbols to grab | ||
2 | LL_PA_SYM(true, pa_context_connect, int, pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api); | ||
3 | LL_PA_SYM(true, pa_context_disconnect, void, pa_context *c); | ||
4 | LL_PA_SYM(true, pa_context_get_sink_input_info, pa_operation*, pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata); | ||
5 | LL_PA_SYM(true, pa_context_get_sink_input_info_list, pa_operation*, pa_context *c, pa_sink_input_info_cb_t cb, void *userdata); | ||
6 | LL_PA_SYM(true, pa_context_get_state, pa_context_state_t, pa_context *c); | ||
7 | LL_PA_SYM(true, pa_context_new_with_proplist, pa_context*, pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist); | ||
8 | LL_PA_SYM(true, pa_context_set_sink_input_volume, pa_operation*, pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); | ||
9 | LL_PA_SYM(true, pa_context_set_state_callback, void, pa_context *c, pa_context_notify_cb_t cb, void *userdata); | ||
10 | LL_PA_SYM(true, pa_context_set_subscribe_callback, void, pa_context *c, pa_context_subscribe_cb_t cb, void *userdata); | ||
11 | LL_PA_SYM(true, pa_context_subscribe, pa_operation*, pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata); | ||
12 | LL_PA_SYM(true, pa_context_unref, void, pa_context *c); | ||
13 | LL_PA_SYM(true, pa_cvolume_set, pa_cvolume*, pa_cvolume *a, unsigned channels, pa_volume_t v); | ||
14 | LL_PA_SYM(true, pa_operation_unref, void, pa_operation *o); | ||
15 | LL_PA_SYM(true, pa_proplist_free, void, pa_proplist* p); | ||
16 | LL_PA_SYM(true, pa_proplist_gets, const char*, pa_proplist *p, const char *key); | ||
17 | LL_PA_SYM(true, pa_proplist_new, pa_proplist*, void); | ||
18 | LL_PA_SYM(true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value); | ||
19 | LL_PA_SYM(true, pa_sw_volume_from_linear, pa_volume_t, double v); | ||
20 | |||
21 | // optional symbols to grab | ||
diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc b/linden/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc new file mode 100755 index 0000000..abf628c --- /dev/null +++ b/linden/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc | |||
@@ -0,0 +1,6 @@ | |||
1 | // required symbols to grab | ||
2 | LL_PA_SYM(true, pa_glib_mainloop_free, void, pa_glib_mainloop* g); | ||
3 | LL_PA_SYM(true, pa_glib_mainloop_get_api, pa_mainloop_api*, pa_glib_mainloop* g); | ||
4 | LL_PA_SYM(true, pa_glib_mainloop_new, pa_glib_mainloop *, GMainContext *c); | ||
5 | |||
6 | // optional symbols to grab | ||
diff --git a/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp b/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp index 2e3f06d..28c6e98 100755 --- a/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp | |||
@@ -13,13 +13,13 @@ | |||
13 | * ("GPL"), unless you have obtained a separate licensing agreement | 13 | * ("GPL"), unless you have obtained a separate licensing agreement |
14 | * ("Other License"), formally executed by you and Linden Lab. Terms of | 14 | * ("Other License"), formally executed by you and Linden Lab. Terms of |
15 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | 15 | * the GPL can be found in doc/GPL-license.txt in this distribution, or |
16 | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 | 16 | * online at http://secondlife.com/developers/opensource/gplv2 |
17 | * | 17 | * |
18 | * There are special exceptions to the terms and conditions of the GPL as | 18 | * There are special exceptions to the terms and conditions of the GPL as |
19 | * it is applied to this Source Code. View the full text of the exception | 19 | * it is applied to this Source Code. View the full text of the exception |
20 | * in the file doc/FLOSS-exception.txt in this software distribution, or | 20 | * in the file doc/FLOSS-exception.txt in this software distribution, or |
21 | * online at | 21 | * online at |
22 | * http://secondlifegrid.net/programs/open_source/licensing/flossexception | 22 | * http://secondlife.com/developers/opensource/flossexception |
23 | * | 23 | * |
24 | * By copying, modifying or distributing this software, you acknowledge | 24 | * By copying, modifying or distributing this software, you acknowledge |
25 | * that you have read and understood your obligations described above, | 25 | * that you have read and understood your obligations described above, |
@@ -29,9 +29,10 @@ | |||
29 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | 29 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, |
30 | * COMPLETENESS OR PERFORMANCE. | 30 | * COMPLETENESS OR PERFORMANCE. |
31 | * $/LicenseInfo$ | 31 | * $/LicenseInfo$ |
32 | * | ||
32 | * @endcond | 33 | * @endcond |
33 | */ | 34 | */ |
34 | #include <iomanip>//FIXME: This is included from elsewhere in SG2.0 | 35 | |
35 | #include "llqtwebkit.h" | 36 | #include "llqtwebkit.h" |
36 | 37 | ||
37 | #include "linden_common.h" | 38 | #include "linden_common.h" |
@@ -44,11 +45,22 @@ | |||
44 | #include "llpluginmessageclasses.h" | 45 | #include "llpluginmessageclasses.h" |
45 | #include "media_plugin_base.h" | 46 | #include "media_plugin_base.h" |
46 | 47 | ||
48 | // set to 1 if you're using the version of llqtwebkit that's QPixmap-ified | ||
49 | #if LL_LINUX | ||
50 | # define LL_QTWEBKIT_USES_PIXMAPS 0 | ||
51 | #else | ||
52 | # define LL_QTWEBKIT_USES_PIXMAPS 0 | ||
53 | #endif // LL_LINUX | ||
54 | |||
55 | #if LL_LINUX | ||
56 | # include "linux_volume_catcher.h" | ||
57 | #endif // LL_LINUX | ||
58 | |||
47 | #if LL_WINDOWS | 59 | #if LL_WINDOWS |
48 | #include <direct.h> | 60 | # include <direct.h> |
49 | #else | 61 | #else |
50 | #include <unistd.h> | 62 | # include <unistd.h> |
51 | #include <stdlib.h> | 63 | # include <stdlib.h> |
52 | #endif | 64 | #endif |
53 | 65 | ||
54 | #if LL_WINDOWS | 66 | #if LL_WINDOWS |
@@ -78,10 +90,16 @@ public: | |||
78 | private: | 90 | private: |
79 | 91 | ||
80 | std::string mProfileDir; | 92 | std::string mProfileDir; |
93 | std::string mHostLanguage; | ||
94 | std::string mUserAgent; | ||
95 | bool mCookiesEnabled; | ||
96 | bool mJavascriptEnabled; | ||
97 | bool mPluginsEnabled; | ||
81 | 98 | ||
82 | enum | 99 | enum |
83 | { | 100 | { |
84 | INIT_STATE_UNINITIALIZED, // Browser instance hasn't been set up yet | 101 | INIT_STATE_UNINITIALIZED, // LLQtWebkit hasn't been set up yet |
102 | INIT_STATE_INITIALIZED, // LLQtWebkit has been set up, but no browser window has been created yet. | ||
85 | INIT_STATE_NAVIGATING, // Browser instance has been set up and initial navigate to about:blank has been issued | 103 | INIT_STATE_NAVIGATING, // Browser instance has been set up and initial navigate to about:blank has been issued |
86 | INIT_STATE_NAVIGATE_COMPLETE, // initial navigate to about:blank has completed | 104 | INIT_STATE_NAVIGATE_COMPLETE, // initial navigate to about:blank has completed |
87 | INIT_STATE_WAIT_REDRAW, // First real navigate begin has been received, waiting for page changed event to start handling redraws | 105 | INIT_STATE_WAIT_REDRAW, // First real navigate begin has been received, waiting for page changed event to start handling redraws |
@@ -103,6 +121,10 @@ private: | |||
103 | F32 mBackgroundG; | 121 | F32 mBackgroundG; |
104 | F32 mBackgroundB; | 122 | F32 mBackgroundB; |
105 | 123 | ||
124 | #if LL_LINUX | ||
125 | LinuxVolumeCatcher mLinuxVolumeCatcher; | ||
126 | #endif // LL_LINUX | ||
127 | |||
106 | void setInitState(int state) | 128 | void setInitState(int state) |
107 | { | 129 | { |
108 | // std::cerr << "changing init state to " << state << std::endl; | 130 | // std::cerr << "changing init state to " << state << std::endl; |
@@ -115,6 +137,10 @@ private: | |||
115 | { | 137 | { |
116 | LLQtWebKit::getInstance()->pump( milliseconds ); | 138 | LLQtWebKit::getInstance()->pump( milliseconds ); |
117 | 139 | ||
140 | #if LL_LINUX | ||
141 | mLinuxVolumeCatcher.pump(); | ||
142 | #endif // LL_LINUX | ||
143 | |||
118 | checkEditState(); | 144 | checkEditState(); |
119 | 145 | ||
120 | if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) | 146 | if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) |
@@ -131,7 +157,11 @@ private: | |||
131 | { | 157 | { |
132 | const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); | 158 | const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); |
133 | 159 | ||
134 | unsigned int buffer_size = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ) * LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId ); | 160 | unsigned int rowspan = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ); |
161 | unsigned int height = LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId ); | ||
162 | #if !LL_QTWEBKIT_USES_PIXMAPS | ||
163 | unsigned int buffer_size = rowspan * height; | ||
164 | #endif // !LL_QTWEBKIT_USES_PIXMAPS | ||
135 | 165 | ||
136 | // std::cerr << "webkit plugin: updating" << std::endl; | 166 | // std::cerr << "webkit plugin: updating" << std::endl; |
137 | 167 | ||
@@ -139,7 +169,16 @@ private: | |||
139 | if ( mPixels && browser_pixels ) | 169 | if ( mPixels && browser_pixels ) |
140 | { | 170 | { |
141 | // std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl; | 171 | // std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl; |
172 | |||
173 | #if LL_QTWEBKIT_USES_PIXMAPS | ||
174 | // copy the pixel data upside-down because of the co-ord system | ||
175 | for (int y=0; y<height; ++y) | ||
176 | { | ||
177 | memcpy( &mPixels[(height-y-1)*rowspan], &browser_pixels[y*rowspan], rowspan ); | ||
178 | } | ||
179 | #else | ||
142 | memcpy( mPixels, browser_pixels, buffer_size ); | 180 | memcpy( mPixels, browser_pixels, buffer_size ); |
181 | #endif // LL_QTWEBKIT_USES_PIXMAPS | ||
143 | } | 182 | } |
144 | 183 | ||
145 | if ( mWidth > 0 && mHeight > 0 ) | 184 | if ( mWidth > 0 && mHeight > 0 ) |
@@ -160,13 +199,6 @@ private: | |||
160 | if ( mInitState > INIT_STATE_UNINITIALIZED ) | 199 | if ( mInitState > INIT_STATE_UNINITIALIZED ) |
161 | return true; | 200 | return true; |
162 | 201 | ||
163 | // not enough information to initialize the browser yet. | ||
164 | if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || | ||
165 | mTextureWidth < 0 || mTextureHeight < 0 ) | ||
166 | { | ||
167 | return false; | ||
168 | }; | ||
169 | |||
170 | // set up directories | 202 | // set up directories |
171 | char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use | 203 | char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use |
172 | if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) | 204 | if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) |
@@ -177,12 +209,12 @@ private: | |||
177 | std::string application_dir = std::string( cwd ); | 209 | std::string application_dir = std::string( cwd ); |
178 | 210 | ||
179 | #if LL_DARWIN | 211 | #if LL_DARWIN |
180 | // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on. | 212 | // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on. |
181 | // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger. | 213 | // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger. |
182 | // This wouldn't cause any problems except for the fact that the current release version of the Flash plugin has a call to Debugger() in it | 214 | // This wouldn't cause any problems except for the fact that the current release version of the Flash plugin has a call to Debugger() in it |
183 | // which gets hit when the plugin is probed by webkit. | 215 | // which gets hit when the plugin is probed by webkit. |
184 | // Unsetting the environment variable here works around this issue. | 216 | // Unsetting the environment variable here works around this issue. |
185 | unsetenv("USERBREAK"); | 217 | unsetenv("USERBREAK"); |
186 | #endif | 218 | #endif |
187 | 219 | ||
188 | #if LL_WINDOWS | 220 | #if LL_WINDOWS |
@@ -223,65 +255,91 @@ private: | |||
223 | bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); | 255 | bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); |
224 | if ( result ) | 256 | if ( result ) |
225 | { | 257 | { |
226 | // create single browser window | 258 | mInitState = INIT_STATE_INITIALIZED; |
227 | mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); | ||
228 | #if LL_WINDOWS | ||
229 | // Enable plugins | ||
230 | LLQtWebKit::getInstance()->enablePlugins(true); | ||
231 | #elif LL_DARWIN | ||
232 | // Enable plugins | ||
233 | LLQtWebKit::getInstance()->enablePlugins(true); | ||
234 | #elif LL_LINUX | ||
235 | // Enable plugins | ||
236 | LLQtWebKit::getInstance()->enablePlugins(true); | ||
237 | #endif | ||
238 | // Enable cookies | ||
239 | LLQtWebKit::getInstance()->enableCookies( true ); | ||
240 | |||
241 | // tell LLQtWebKit about the size of the browser window | ||
242 | LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); | ||
243 | |||
244 | // observer events that LLQtWebKit emits | ||
245 | LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); | ||
246 | |||
247 | // append details to agent string | ||
248 | LLQtWebKit::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" ); | ||
249 | |||
250 | // don't flip bitmap | ||
251 | LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); | ||
252 | |||
253 | // set background color | ||
254 | // convert background color channels from [0.0, 1.0] to [0, 255]; | ||
255 | LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) ); | ||
256 | |||
257 | // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns. | ||
258 | setInitState(INIT_STATE_NAVIGATING); | ||
259 | |||
260 | // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. | ||
261 | // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially | ||
262 | // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date. | ||
263 | // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E" | ||
264 | // where RRGGBB is the background color in HTML style | ||
265 | std::stringstream url; | ||
266 | 259 | ||
267 | url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#"; | ||
268 | // convert background color channels from [0.0, 1.0] to [0, 255]; | ||
269 | url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f); | ||
270 | url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f); | ||
271 | url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f); | ||
272 | url << "%22%3E%3C/body%3E%3C/html%3E"; | ||
273 | |||
274 | lldebugs << "data url is: " << url.str() << llendl; | ||
275 | |||
276 | LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() ); | ||
277 | // LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); | ||
278 | |||
279 | return true; | 260 | return true; |
280 | }; | 261 | }; |
281 | 262 | ||
282 | return false; | 263 | return false; |
283 | }; | 264 | }; |
284 | 265 | ||
266 | //////////////////////////////////////////////////////////////////////////////// | ||
267 | // | ||
268 | bool initBrowserWindow() | ||
269 | { | ||
270 | // already initialized | ||
271 | if ( mInitState > INIT_STATE_INITIALIZED ) | ||
272 | return true; | ||
273 | |||
274 | // not enough information to initialize the browser yet. | ||
275 | if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || | ||
276 | mTextureWidth < 0 || mTextureHeight < 0 ) | ||
277 | { | ||
278 | return false; | ||
279 | }; | ||
280 | |||
281 | // Set up host language before creating browser window | ||
282 | if(!mHostLanguage.empty()) | ||
283 | { | ||
284 | LLQtWebKit::getInstance()->setHostLanguage(mHostLanguage); | ||
285 | } | ||
286 | |||
287 | // turn on/off cookies based on what host app tells us | ||
288 | LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled ); | ||
289 | |||
290 | // turn on/off plugins based on what host app tells us | ||
291 | LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled ); | ||
292 | |||
293 | // turn on/off Javascript based on what host app tells us | ||
294 | LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); | ||
295 | |||
296 | // create single browser window | ||
297 | mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); | ||
298 | |||
299 | // tell LLQtWebKit about the size of the browser window | ||
300 | LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); | ||
301 | |||
302 | // observer events that LLQtWebKit emits | ||
303 | LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); | ||
304 | |||
305 | // append details to agent string | ||
306 | LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent ); | ||
307 | |||
308 | #if !LL_QTWEBKIT_USES_PIXMAPS | ||
309 | // don't flip bitmap | ||
310 | LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); | ||
311 | #endif // !LL_QTWEBKIT_USES_PIXMAPS | ||
312 | |||
313 | // set background color | ||
314 | // convert background color channels from [0.0, 1.0] to [0, 255]; | ||
315 | LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) ); | ||
316 | |||
317 | // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns. | ||
318 | setInitState(INIT_STATE_NAVIGATING); | ||
319 | |||
320 | // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. | ||
321 | // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially | ||
322 | // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date. | ||
323 | // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E" | ||
324 | // where RRGGBB is the background color in HTML style | ||
325 | std::stringstream url; | ||
326 | |||
327 | url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#"; | ||
328 | // convert background color channels from [0.0, 1.0] to [0, 255]; | ||
329 | url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f); | ||
330 | url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f); | ||
331 | url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f); | ||
332 | url << "%22%3E%3C/body%3E%3C/html%3E"; | ||
333 | |||
334 | lldebugs << "data url is: " << url.str() << llendl; | ||
335 | |||
336 | LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() ); | ||
337 | // LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); | ||
338 | |||
339 | return true; | ||
340 | } | ||
341 | |||
342 | void setVolume(F32 vol); | ||
285 | 343 | ||
286 | //////////////////////////////////////////////////////////////////////////////// | 344 | //////////////////////////////////////////////////////////////////////////////// |
287 | // virtual | 345 | // virtual |
@@ -616,6 +674,11 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_ | |||
616 | mBackgroundR = 0.0f; | 674 | mBackgroundR = 0.0f; |
617 | mBackgroundG = 0.0f; | 675 | mBackgroundG = 0.0f; |
618 | mBackgroundB = 0.0f; | 676 | mBackgroundB = 0.0f; |
677 | |||
678 | mHostLanguage = "en"; // default to english | ||
679 | mJavascriptEnabled = true; // default to on | ||
680 | mPluginsEnabled = true; // default to on | ||
681 | mUserAgent = "LLPluginMedia Web Browser"; | ||
619 | } | 682 | } |
620 | 683 | ||
621 | MediaPluginWebKit::~MediaPluginWebKit() | 684 | MediaPluginWebKit::~MediaPluginWebKit() |
@@ -642,9 +705,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
642 | { | 705 | { |
643 | if(message_name == "init") | 706 | if(message_name == "init") |
644 | { | 707 | { |
645 | std::string user_data_path = message_in.getValue("user_data_path"); // n.b. always has trailing platform-specific dir-delimiter | ||
646 | mProfileDir = user_data_path + "browser_profile"; | ||
647 | |||
648 | LLPluginMessage message("base", "init_response"); | 708 | LLPluginMessage message("base", "init_response"); |
649 | LLSD versions = LLSD::emptyMap(); | 709 | LLSD versions = LLSD::emptyMap(); |
650 | versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; | 710 | versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; |
@@ -656,19 +716,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
656 | plugin_version += LLQtWebKit::getInstance()->getVersion(); | 716 | plugin_version += LLQtWebKit::getInstance()->getVersion(); |
657 | message.setValue("plugin_version", plugin_version); | 717 | message.setValue("plugin_version", plugin_version); |
658 | sendMessage(message); | 718 | sendMessage(message); |
659 | |||
660 | // Plugin gets to decide the texture parameters to use. | ||
661 | mDepth = 4; | ||
662 | |||
663 | message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); | ||
664 | message.setValueS32("default_width", 1024); | ||
665 | message.setValueS32("default_height", 1024); | ||
666 | message.setValueS32("depth", mDepth); | ||
667 | message.setValueU32("internalformat", GL_RGBA); | ||
668 | message.setValueU32("format", GL_RGBA); | ||
669 | message.setValueU32("type", GL_UNSIGNED_BYTE); | ||
670 | message.setValueBoolean("coords_opengl", true); | ||
671 | sendMessage(message); | ||
672 | } | 719 | } |
673 | else if(message_name == "idle") | 720 | else if(message_name == "idle") |
674 | { | 721 | { |
@@ -733,9 +780,68 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
733 | // std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; | 780 | // std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; |
734 | } | 781 | } |
735 | } | 782 | } |
783 | else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) | ||
784 | { | ||
785 | if(message_name == "set_volume") | ||
786 | { | ||
787 | F32 volume = message_in.getValueReal("volume"); | ||
788 | setVolume(volume); | ||
789 | } | ||
790 | } | ||
736 | else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) | 791 | else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) |
737 | { | 792 | { |
738 | if(message_name == "size_change") | 793 | if(message_name == "init") |
794 | { | ||
795 | // This is the media init message -- all necessary data for initialization should have been received. | ||
796 | if(initBrowser()) | ||
797 | { | ||
798 | |||
799 | // Plugin gets to decide the texture parameters to use. | ||
800 | mDepth = 4; | ||
801 | |||
802 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); | ||
803 | message.setValueS32("default_width", 1024); | ||
804 | message.setValueS32("default_height", 1024); | ||
805 | message.setValueS32("depth", mDepth); | ||
806 | message.setValueU32("internalformat", GL_RGBA); | ||
807 | #if LL_QTWEBKIT_USES_PIXMAPS | ||
808 | message.setValueU32("format", GL_BGRA_EXT); // I hope this isn't system-dependant... is it? If so, we'll have to check the root window's pixel layout or something... yuck. | ||
809 | #else | ||
810 | message.setValueU32("format", GL_RGBA); | ||
811 | #endif // LL_QTWEBKIT_USES_PIXMAPS | ||
812 | message.setValueU32("type", GL_UNSIGNED_BYTE); | ||
813 | message.setValueBoolean("coords_opengl", true); | ||
814 | sendMessage(message); | ||
815 | } | ||
816 | else | ||
817 | { | ||
818 | // if initialization failed, we're done. | ||
819 | mDeleteMe = true; | ||
820 | } | ||
821 | |||
822 | } | ||
823 | else if(message_name == "set_user_data_path") | ||
824 | { | ||
825 | std::string user_data_path = message_in.getValue("path"); // n.b. always has trailing platform-specific dir-delimiter | ||
826 | mProfileDir = user_data_path + "browser_profile"; | ||
827 | |||
828 | // FIXME: Should we do anything with this if it comes in after the browser has been initialized? | ||
829 | } | ||
830 | else if(message_name == "set_language_code") | ||
831 | { | ||
832 | mHostLanguage = message_in.getValue("language"); | ||
833 | |||
834 | // FIXME: Should we do anything with this if it comes in after the browser has been initialized? | ||
835 | } | ||
836 | else if(message_name == "plugins_enabled") | ||
837 | { | ||
838 | mPluginsEnabled = message_in.getValueBoolean("enable"); | ||
839 | } | ||
840 | else if(message_name == "javascript_enabled") | ||
841 | { | ||
842 | mJavascriptEnabled = message_in.getValueBoolean("enable"); | ||
843 | } | ||
844 | else if(message_name == "size_change") | ||
739 | { | 845 | { |
740 | std::string name = message_in.getValue("name"); | 846 | std::string name = message_in.getValue("name"); |
741 | S32 width = message_in.getValueS32("width"); | 847 | S32 width = message_in.getValueS32("width"); |
@@ -757,29 +863,36 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
757 | mWidth = width; | 863 | mWidth = width; |
758 | mHeight = height; | 864 | mHeight = height; |
759 | 865 | ||
760 | // initialize (only gets called once) | 866 | if(initBrowserWindow()) |
761 | initBrowser(); | ||
762 | |||
763 | // size changed so tell the browser | ||
764 | LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); | ||
765 | |||
766 | // std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight | ||
767 | // << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; | ||
768 | |||
769 | S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); | ||
770 | |||
771 | // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. | ||
772 | if(real_width <= texture_width) | ||
773 | { | 867 | { |
774 | texture_width = real_width; | 868 | |
869 | // size changed so tell the browser | ||
870 | LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); | ||
871 | |||
872 | // std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight | ||
873 | // << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; | ||
874 | |||
875 | S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); | ||
876 | |||
877 | // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. | ||
878 | if(real_width <= texture_width) | ||
879 | { | ||
880 | texture_width = real_width; | ||
881 | } | ||
882 | else | ||
883 | { | ||
884 | // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. | ||
885 | // std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; | ||
886 | mDeleteMe = true; | ||
887 | return; | ||
888 | } | ||
775 | } | 889 | } |
776 | else | 890 | else |
777 | { | 891 | { |
778 | // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. | 892 | // Setting up the browser window failed. This is a fatal error. |
779 | // std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; | ||
780 | mDeleteMe = true; | 893 | mDeleteMe = true; |
781 | return; | ||
782 | } | 894 | } |
895 | |||
783 | 896 | ||
784 | mTextureWidth = texture_width; | 897 | mTextureWidth = texture_width; |
785 | mTextureHeight = texture_height; | 898 | mTextureHeight = texture_height; |
@@ -929,8 +1042,18 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
929 | } | 1042 | } |
930 | else if(message_name == "enable_cookies") | 1043 | else if(message_name == "enable_cookies") |
931 | { | 1044 | { |
932 | bool val = message_in.getValueBoolean("enable"); | 1045 | mCookiesEnabled = message_in.getValueBoolean("enable"); |
933 | LLQtWebKit::getInstance()->enableCookies( val ); | 1046 | LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled ); |
1047 | } | ||
1048 | else if(message_name == "enable_plugins") | ||
1049 | { | ||
1050 | mPluginsEnabled = message_in.getValueBoolean("enable"); | ||
1051 | LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled ); | ||
1052 | } | ||
1053 | else if(message_name == "enable_javascript") | ||
1054 | { | ||
1055 | mJavascriptEnabled = message_in.getValueBoolean("enable"); | ||
1056 | //LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); | ||
934 | } | 1057 | } |
935 | else if(message_name == "proxy_setup") | 1058 | else if(message_name == "proxy_setup") |
936 | { | 1059 | { |
@@ -967,8 +1090,8 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
967 | } | 1090 | } |
968 | else if(message_name == "set_user_agent") | 1091 | else if(message_name == "set_user_agent") |
969 | { | 1092 | { |
970 | std::string user_agent = message_in.getValue("user_agent"); | 1093 | mUserAgent = message_in.getValue("user_agent"); |
971 | LLQtWebKit::getInstance()->setBrowserAgentId( user_agent ); | 1094 | LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent ); |
972 | } | 1095 | } |
973 | else if(message_name == "init_history") | 1096 | else if(message_name == "init_history") |
974 | { | 1097 | { |
@@ -999,6 +1122,13 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) | |||
999 | } | 1122 | } |
1000 | } | 1123 | } |
1001 | 1124 | ||
1125 | void MediaPluginWebKit::setVolume(F32 volume) | ||
1126 | { | ||
1127 | #if LL_LINUX | ||
1128 | mLinuxVolumeCatcher.setVolume(volume); | ||
1129 | #endif // LL_LINUX | ||
1130 | } | ||
1131 | |||
1002 | int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) | 1132 | int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) |
1003 | { | 1133 | { |
1004 | MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); | 1134 | MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); |