Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Csoregi Natalia 2018-01-30 18:24:05 +02:00
commit 1d557a2997
89 changed files with 1339 additions and 722 deletions

View File

@ -21,6 +21,10 @@
color: HighlightText;
}
#viewGroup > radio > .radio-label-box {
padding: 0 6px;
}
#topBar {
border-bottom: 1px solid ThreeDShadow;
padding-inline-start: 10px;

View File

@ -27,7 +27,7 @@
font: menu;
text-shadow: @loweredShadow@;
margin: 0;
padding: 0 1px;
padding: 0 6px;
height: 22px;
}

View File

@ -100,6 +100,10 @@
}
}
.sidebar-search-container {
padding: 4px;
}
.search-box {
-moz-appearance: searchfield;
padding: 1px;
@ -111,7 +115,7 @@
text-shadow: none;
}
.search-box.compact > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
.search-box > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
background-image: url(chrome://global/skin/icons/searchfield-cancel.svg);
background-repeat: no-repeat;
background-size: 11px 11px;
@ -119,12 +123,6 @@
height: 11px;
}
.search-box.compact {
padding: 0px;
/* font size is in px because the XUL it was copied from uses px */
font-size: 11px;
}
.textbox-search-clear {
margin-top: 1px;
}

View File

@ -12,9 +12,9 @@
#viewGroup > radio {
list-style-image: url("chrome://browser/skin/pageInfo.png");
-moz-appearance: none;
padding: 5px 3px 1px 3px;
margin: 0 1px;
min-width: 4.5em;
margin: 0;
padding: 3px;
}
#viewGroup > radio:hover {
@ -27,6 +27,11 @@
color: black;
}
#viewGroup > radio > .radio-label-box {
margin: 0;
padding: 0 6px;
}
#topBar {
border-bottom: 1px solid ThreeDLightShadow;
padding-inline-start: 10px;

View File

@ -289,9 +289,5 @@ https://tls1.example.com:443 privileged,tls1
# Hosts for youtube rewrite tests
https://mochitest.youtube.com:443
# Hosts for stylo blocklist tests
http://stylo-blocklist.com:80 privileged
http://test.stylo-blocklist.com:80 privileged
# Host for U2F localhost tests
https://localhost:443

View File

@ -303,6 +303,7 @@
.boxmodel-editable {
border: 1px dashed transparent;
-moz-user-select: none;
white-space: nowrap;
}
.boxmodel-editable:hover {

View File

@ -4445,24 +4445,6 @@ nsDOMWindowUtils::GetIsStyledByServo(bool* aStyledByServo)
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::AddToStyloBlocklist(const nsACString& aBlockedDomain)
{
#ifdef MOZ_STYLO
nsLayoutUtils::AddToStyloBlocklist(aBlockedDomain);
#endif
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::RemoveFromStyloBlocklist(const nsACString& aBlockedDomain)
{
#ifdef MOZ_STYLO
nsLayoutUtils::RemoveFromStyloBlocklist(aBlockedDomain);
#endif
return NS_OK;
}
NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)

View File

@ -13238,7 +13238,7 @@ nsIDocument::UpdateStyleBackendType()
#ifdef MOZ_STYLO
if (nsLayoutUtils::StyloEnabled() &&
nsLayoutUtils::ShouldUseStylo(mDocumentURI, NodePrincipal())) {
nsLayoutUtils::ShouldUseStylo(NodePrincipal())) {
mStyleBackendType = StyleBackendType::Servo;
}
#endif
@ -13902,30 +13902,11 @@ nsDocument::IsThirdParty()
return mIsThirdParty.value();
}
static bool
IsAboutReader(nsIURI* aURI)
{
if (!aURI) {
return false;
}
nsCString spec;
aURI->GetSpec(spec);
// Reader mode URLs look like about:reader?[...].
return StringBeginsWith(spec, NS_LITERAL_CSTRING("about:reader"));
}
bool
nsIDocument::IsScopedStyleEnabled()
{
if (mIsScopedStyleEnabled == eScopedStyle_Unknown) {
// We allow <style scoped> in about:reader pages since on Android
// we use it to inject some in-page UI. (We currently don't
// support styling about:reader pages in stylo anyway, so for
// now it's OK to enable it here.)
mIsScopedStyleEnabled = nsContentUtils::IsChromeDoc(this) ||
IsAboutReader(mDocumentURI) ||
nsContentUtils::IsScopedStylePrefEnabled()
? eScopedStyle_Enabled
: eScopedStyle_Disabled;

View File

@ -1980,20 +1980,6 @@ interface nsIDOMWindowUtils : nsISupports {
*/
readonly attribute boolean isStyledByServo;
/**
* Add a domain to the existing stylo blocklist.
*
* This calls nsLayoutUtils::AddToStyloBlocklist().
*/
void addToStyloBlocklist(in ACString aBlockedDomain);
/**
* Remove a domain from the existing stylo blocklist.
*
* This calls nsLayoutUtils::RemoveFromStyloBlocklist().
*/
void removeFromStyloBlocklist(in ACString aBlockedDomain);
/**
* Capture the contents of the current WebRender frame and
* save them to a folder relative to the current working directory.

View File

@ -178,10 +178,6 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = {
"layout.css.servo.enabled",
#endif
"layout.css.shape-outside.enabled",
#ifdef MOZ_STYLO
"layout.css.stylo-blocklist.blocked_domains",
"layout.css.stylo-blocklist.enabled",
#endif
"layout.css.text-align-unsafe-value.enabled",
"layout.css.text-combine-upright-digits.enabled",
"layout.css.text-combine-upright.enabled",

View File

@ -350,6 +350,7 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aChannelMap, uint32_t aRate)
params.channels = mOutChannels;
params.layout = CubebUtils::ConvertChannelMapToCubebLayout(aChannelMap);
params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
params.prefs = CUBEB_STREAM_PREF_NONE;
mAudioClock.Init(aRate);

View File

@ -627,6 +627,7 @@ AudioCallbackDriver::Init()
output.channels = mOutputChannels;
output.layout = CubebUtils::GetPreferredChannelLayoutOrSMPTE(cubebContext, mOutputChannels);
output.prefs = CUBEB_STREAM_PREF_NONE;
Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
if (latencyPref) {

View File

@ -2339,7 +2339,7 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
RefPtr<DeclarationBlock> declaration;
if (nsLayoutUtils::StyloEnabled() &&
nsLayoutUtils::ShouldUseStylo(aDocumentURI, principal)) {
nsLayoutUtils::ShouldUseStylo(principal)) {
RefPtr<URLExtraData> data =
new URLExtraData(aDocumentURI, aDocumentURI, principal);
declaration = ServoDeclarationBlock::FromCssText(

View File

@ -129,7 +129,6 @@
#include "DisplayListChecker.h"
#include "TextDrawTarget.h"
#include "nsDeckFrame.h"
#include "nsIEffectiveTLDService.h" // for IsInStyloBlocklist
#include "mozilla/StylePrefs.h"
#include "mozilla/dom/InspectorFontFace.h"
@ -196,8 +195,6 @@ typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
/* static */ bool nsLayoutUtils::sTextCombineUprightDigitsEnabled;
#ifdef MOZ_STYLO
/* static */ bool nsLayoutUtils::sStyloEnabled;
/* static */ bool nsLayoutUtils::sStyloBlocklistEnabled;
/* static */ nsTArray<nsCString>* nsLayoutUtils::sStyloBlocklist = nullptr;
#endif
/* static */ uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
/* static */ uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
@ -8231,24 +8228,6 @@ nsLayoutUtils::Initialize()
Preferences::AddBoolVarCache(&sStyloEnabled,
"layout.css.servo.enabled");
}
// We should only create the blocklist ONCE, and ignore any blocklist
// reloads happen. Because otherwise we could have a top level page that
// uses Stylo (if its load happens before the blocklist reload) and a
// child iframe that uses Gecko (if its load happens after the blocklist
// reload). If some page contains both backends, and they try to move
// element across backend boundary, it could crash (see bug 1404020).
sStyloBlocklistEnabled =
Preferences::GetBool("layout.css.stylo-blocklist.enabled");
if (sStyloBlocklistEnabled && !sStyloBlocklist) {
nsAutoCString blocklist;
Preferences::GetCString("layout.css.stylo-blocklist.blocked_domains", blocklist);
if (!blocklist.IsEmpty()) {
sStyloBlocklist = new nsTArray<nsCString>;
for (const nsACString& domainString : blocklist.Split(',')) {
sStyloBlocklist->AppendElement(domainString);
}
}
}
#endif
Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,
"layout.idle_period.time_limit",
@ -8271,13 +8250,7 @@ nsLayoutUtils::Shutdown()
delete sContentMap;
sContentMap = nullptr;
}
#ifdef MOZ_STYLO
if (sStyloBlocklist) {
sStyloBlocklist->Clear();
delete sStyloBlocklist;
sStyloBlocklist = nullptr;
}
#endif
for (auto& callback : kPrefCallbacks) {
Preferences::UnregisterCallback(callback.func, callback.name);
}
@ -8290,7 +8263,7 @@ nsLayoutUtils::Shutdown()
#ifdef MOZ_STYLO
/* static */
bool
nsLayoutUtils::ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal)
nsLayoutUtils::ShouldUseStylo(nsIPrincipal* aPrincipal)
{
// Disable stylo for system principal because XUL hasn't been fully
// supported. Other principal aren't able to use XUL by default, and
@ -8300,84 +8273,10 @@ nsLayoutUtils::ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal)
nsContentUtils::IsSystemPrincipal(aPrincipal)) {
return false;
}
// Check any internal page which we need to explicitly blacklist.
if (aDocumentURI) {
bool isAbout = false;
if (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isAbout)) && isAbout) {
nsAutoCString path;
aDocumentURI->GetFilePath(path);
// about:reader requires support of scoped style, so we have to
// use Gecko backend for now. See bug 1402094.
// This should be fixed by bug 1204818.
if (path.EqualsLiteral("reader")) {
return false;
}
}
}
// Check the stylo block list.
if (IsInStyloBlocklist(aPrincipal)) {
return false;
}
return true;
}
/* static */
bool
nsLayoutUtils::IsInStyloBlocklist(nsIPrincipal* aPrincipal)
{
if (!sStyloBlocklist) {
return false;
}
// Note that a non-codebase principal (eg the system principal) will return
// a null URI.
nsCOMPtr<nsIURI> codebaseURI;
aPrincipal->GetURI(getter_AddRefs(codebaseURI));
if (!codebaseURI) {
return false;
}
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
NS_ENSURE_TRUE(tldService, false);
// Check if a document's eTLD+1 domain belongs to one of the stylo blocklist.
nsAutoCString baseDomain;
NS_SUCCEEDED(tldService->GetBaseDomain(codebaseURI, 0, baseDomain));
for (const nsCString& domains : *sStyloBlocklist) {
if (baseDomain.Equals(domains)) {
return true;
}
}
return false;
}
/* static */
void
nsLayoutUtils::AddToStyloBlocklist(const nsACString& aBlockedDomain)
{
if (!sStyloBlocklist) {
sStyloBlocklist = new nsTArray<nsCString>;
}
sStyloBlocklist->AppendElement(aBlockedDomain);
}
/* static */
void
nsLayoutUtils::RemoveFromStyloBlocklist(const nsACString& aBlockedDomain)
{
if (!sStyloBlocklist) {
return;
}
sStyloBlocklist->RemoveElement(aBlockedDomain);
if (sStyloBlocklist->IsEmpty()) {
delete sStyloBlocklist;
sStyloBlocklist = nullptr;
}
}
/* static */
bool
nsLayoutUtils::StyloChromeEnabled()

View File

@ -2582,35 +2582,11 @@ public:
#ifdef MOZ_STYLO
/**
* Return whether stylo should be used for a given document URI and
* principal.
* Return whether stylo should be used for a given document principal.
*/
static bool ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal);
/**
* Principal-based blocklist for stylo.
* Check if aPrincipal is blocked by stylo's blocklist and should fallback to
* use Gecko's style backend. Note that using a document's principal rather
* than the document URI will let us piggy-back off the existing principal
* relationships and symmetries.
*/
static bool IsInStyloBlocklist(nsIPrincipal* aPrincipal);
/**
* Add aBlockedDomain to the existing stylo blocklist, i.e., sStyloBlocklist.
* This function is exposed to nsDOMWindowUtils and only for testing purpose.
* So, NEVER use this in any other cases.
*/
static void AddToStyloBlocklist(const nsACString& aBlockedDomain);
/**
* Remove aBlockedDomain from the existing stylo blocklist, i.e., sStyloBlocklist.
* This function is exposed to nsDOMWindowUtils and only for testing purpose.
* So, NEVER use this in any other cases.
*/
static void RemoveFromStyloBlocklist(const nsACString& aBlockedDomain);
static bool ShouldUseStylo(nsIPrincipal* aPrincipal);
#else
static bool ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal) {
static bool ShouldUseStylo(nsIPrincipal* aPrincipal) {
return false;
}
#endif
@ -3134,8 +3110,6 @@ private:
static bool sTextCombineUprightDigitsEnabled;
#ifdef MOZ_STYLO
static bool sStyloEnabled;
static bool sStyloBlocklistEnabled;
static nsTArray<nsCString>* sStyloBlocklist;
#endif
static uint32_t sIdlePeriodDeadlineLimit;
static uint32_t sQuiescentFramesBeforeIdlePeriod;

View File

@ -304,15 +304,6 @@ skip-if = toolkit == 'android' #bug 775227
[test_stylesheet_additions.html]
skip-if = !stylo
[test_stylesheet_clone_font_face.html]
[test_stylo_blocklist-01.html]
support-files = stylo_blocked_domain_page.html
skip-if = !stylo # no need to test blocklist for non-stylo platforms
[test_stylo_blocklist-02.html]
support-files = stylo_blocked_domain_page.html
skip-if = !stylo # no need to test blocklist for non-stylo platforms
[test_stylo_blocklist-03.html]
support-files = stylo_non_blocked_domain_page.html
skip-if = !stylo # no need to test blocklist for non-stylo platforms
[test_supports_rules.html]
[test_system_font_serialization.html]
[test_text_decoration_shorthands.html]

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<head>
<title>Test page with a mock domain which is listed in the stylo blocklist</title>
</head>
<script>
window.onload = function() {
if (SpecialPowers.getDOMWindowUtils(window).isStyledByServo == false) {
opener.postMessage("TEST-PASS", "*");
} else {
opener.postMessage("TEST-FAIL", "*");
}
}
</script>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<head>
<title>Test page with a mock domain which is NOT listed in the stylo blocklist</title>
</head>
<script>
window.onload = function() {
if (SpecialPowers.getDOMWindowUtils(window).isStyledByServo == true) {
opener.postMessage("TEST-PASS", "*");
} else {
opener.postMessage("TEST-FAIL", "*");
}
}
</script>

View File

@ -1,24 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test the mechanism of stylo blocklist</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
SimpleTest.waitForExplicitFinish();
SpecialPowers.getDOMWindowUtils(window).addToStyloBlocklist("stylo-blocklist.com");
let win =
window.open("http://stylo-blocklist.com/tests/layout/style/test/stylo_blocked_domain_page.html", "");
window.onmessage = function (evt) {
is(evt.data, "TEST-PASS",
"Document with blocked domain should not use servo backend");
win.close();
SpecialPowers.getDOMWindowUtils(window).removeFromStyloBlocklist("stylo-blocklist.com");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -1,24 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test the mechanism of stylo blocklist</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
SimpleTest.waitForExplicitFinish();
SpecialPowers.getDOMWindowUtils(window).addToStyloBlocklist("stylo-blocklist.com");
let win =
window.open("http://test.stylo-blocklist.com/tests/layout/style/test/stylo_blocked_domain_page.html", "");
window.onmessage = function (evt) {
is(evt.data, "TEST-PASS",
"Document with blocked sub-domain should not use servo backend");
win.close();
SpecialPowers.getDOMWindowUtils(window).removeFromStyloBlocklist("stylo-blocklist.com");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -1,25 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test the mechanism of stylo blocklist</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
SimpleTest.waitForExplicitFinish();
// "http://example.com" should not be in the blocklist by any chance. Adding this
// line is just for safety, and it also makes this test more readable.
SpecialPowers.getDOMWindowUtils(window).removeFromStyloBlocklist("example.com");
let win =
window.open("http://example.com/tests/layout/style/test/stylo_non_blocked_domain_page.html", "");
window.onmessage = function (evt) {
is(evt.data, "TEST-PASS",
"Document without blocked domain should use servo backend");
win.close();
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The audioipc-2 git repository is: https://github.com/djg/audioipc-2.git
The git commit ID used was 71cc67f44b803e0288997247c060375196f1cf9b (2018-01-19 17:16:33 +1000)
The git commit ID used was d7798606aa590ef402344b7a519a0053725a9805 (2018-01-27 09:07:03 +1000)

View File

@ -1,6 +1,6 @@
[package]
name = "audioipc"
version = "0.2.0"
version = "0.2.1"
authors = [
"Matthew Gregan <kinetik@flim.org>",
"Dan Glastonbury <dan.glastonbury@gmail.com>"

View File

@ -122,7 +122,8 @@ pub struct StreamParams {
pub format: u32,
pub rate: u16,
pub channels: u8,
pub layout: i32
pub layout: i32,
pub prefs: i32
}
impl<'a> From<&'a ffi::cubeb_stream_params> for StreamParams {
@ -133,7 +134,8 @@ impl<'a> From<&'a ffi::cubeb_stream_params> for StreamParams {
format: params.format,
rate: params.rate as u16,
channels: params.channels as u8,
layout: params.layout
layout: params.layout,
prefs: params.prefs
}
}
}
@ -144,7 +146,8 @@ impl<'a> From<&'a StreamParams> for ffi::cubeb_stream_params {
format: params.format,
rate: u32::from(params.rate),
channels: u32::from(params.channels),
layout: params.layout
layout: params.layout,
prefs: params.prefs
}
}
}

View File

@ -49,7 +49,7 @@ index 5b79b83..01463be 100644
[dependencies]
audioipc = { path = "../audioipc" }
-cubeb-core = { git = "https://github.com/djg/cubeb-rs", version="^0.1" }
-cubeb = { git = "https://github.com/djg/cubeb-rs", version="^0.3.1" }
-cubeb = { git = "https://github.com/djg/cubeb-rs", version="^0.3.2" }
+cubeb-core = { path = "../../cubeb-rs/cubeb-core" }
+cubeb = { path = "../../cubeb-rs/cubeb-api" }
bytes = "0.4"

View File

@ -1,6 +1,6 @@
[package]
name = "audioipc-server"
version = "0.2.0"
version = "0.2.1"
authors = [
"Matthew Gregan <kinetik@flim.org>",
"Dan Glastonbury <dan.glastonbury@gmail.com>"

View File

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The cubeb-rs git repository is: https://github.com/djg/cubeb-rs.git
The git commit ID used was aaba6b34368db990d3ec165ebc85053f61097bac (2017-10-19 15:43:50 +1000)
The git commit ID used was c7e5ef006dede18158582246bddafbb51f832078 (2018-01-25 11:35:14 +1000)

View File

@ -1,7 +1,7 @@
[package]
name = "cubeb"
version = "0.3.1"
version = "0.3.2"
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
license = "ISC"
readme = "README.md"

View File

@ -27,12 +27,13 @@ pub use context::Context;
// Re-export cubeb_core types
pub use cubeb_core::{ChannelLayout, Device, DeviceFormat, DeviceId, DeviceInfo,
DeviceState, DeviceType, Error, ErrorCode, LogLevel, Result,
SampleFormat, State, StreamParams};
SampleFormat, State, StreamParams, StreamPrefs};
pub use cubeb_core::{DEVICE_FMT_F32BE, DEVICE_FMT_F32LE, DEVICE_FMT_S16BE,
DEVICE_FMT_S16LE};
pub use cubeb_core::{DEVICE_PREF_ALL, DEVICE_PREF_MULTIMEDIA, DEVICE_PREF_NONE,
DEVICE_PREF_NOTIFICATION, DEVICE_PREF_VOICE};
pub use cubeb_core::{DEVICE_TYPE_INPUT, DEVICE_TYPE_OUTPUT, DEVICE_TYPE_UNKNOWN};
pub use cubeb_core::{STREAM_PREF_LOOPBACK, STREAM_PREF_NONE};
use cubeb_core::binding::Binding;
use cubeb_core::ffi;

View File

@ -40,6 +40,7 @@
//! .rate(44100)
//! .channels(1)
//! .layout(cubeb::ChannelLayout::Mono)
//! .prefs(cubeb::STREAM_PREF_NONE)
//! .take();
//!
//! let stream_init_opts = cubeb::StreamInitOptionsBuilder::new()
@ -68,9 +69,10 @@
//! ```
use {Binding, ChannelLayout, Context, Device, DeviceId, Error, Result,
SampleFormat, State, StreamParams};
SampleFormat, State, StreamParams, StreamPrefs};
use STREAM_PREF_NONE;
use ffi;
use std::{ptr, str};
use std::ptr;
use std::ffi::CString;
use std::os::raw::{c_long, c_void};
use sys;
@ -91,7 +93,7 @@ impl SampleType for i16 {
SampleFormat::S16NE
}
fn from_float(x: f32) -> i16 {
(x * i16::max_value() as f32) as i16
(x * f32::from(i16::max_value())) as i16
}
}
@ -118,7 +120,8 @@ pub struct StreamParamsBuilder {
format: SampleFormat,
rate: u32,
channels: u32,
layout: ChannelLayout
layout: ChannelLayout,
prefs: StreamPrefs
}
impl Default for StreamParamsBuilder {
@ -133,7 +136,8 @@ impl StreamParamsBuilder {
format: SampleFormat::S16NE,
rate: 0,
channels: 0,
layout: ChannelLayout::Undefined
layout: ChannelLayout::Undefined,
prefs: STREAM_PREF_NONE
}
}
@ -157,6 +161,12 @@ impl StreamParamsBuilder {
self
}
pub fn prefs(&mut self, prefs: StreamPrefs) -> &mut Self {
self.prefs = prefs;
self
}
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
pub fn take(&self) -> StreamParams {
// Convert native endian types to matching format
let raw_sample_format = match self.format {
@ -172,7 +182,8 @@ impl StreamParamsBuilder {
format: raw_sample_format,
rate: self.rate,
channels: self.channels,
layout: self.layout as ffi::cubeb_channel_layout
layout: self.layout as ffi::cubeb_channel_layout,
prefs: self.prefs.bits()
} as *const _)
}
}
@ -291,7 +302,7 @@ where
self.raw,
&mut device_ptr
));
Binding::from_raw_opt(device_ptr).ok_or(Error::from_raw(ffi::CUBEB_ERROR))
Binding::from_raw_opt(device_ptr).ok_or_else(|| Error::from_raw(ffi::CUBEB_ERROR))
}
}
@ -326,7 +337,7 @@ where
} else {
from_raw_parts(input_buffer as *const _, nframes as usize)
};
let mut output: &mut [CB::Frame] = if output_buffer.is_null() {
let output: &mut [CB::Frame] = if output_buffer.is_null() {
&mut []
} else {
from_raw_parts_mut(output_buffer as *mut _, nframes as usize)
@ -638,4 +649,21 @@ mod tests {
let raw = unsafe { &*params.raw() };
assert_eq!(raw.rate, 44100);
}
#[test]
fn stream_params_builder_prefs_default() {
use cubeb_core::STREAM_PREF_NONE;
let params = StreamParamsBuilder::new().take();
assert_eq!(params.prefs(), STREAM_PREF_NONE);
}
#[test]
fn stream_params_builder_prefs() {
use cubeb_core::STREAM_PREF_LOOPBACK;
let params = StreamParamsBuilder::new()
.prefs(STREAM_PREF_LOOPBACK)
.take();
assert_eq!(params.prefs(), STREAM_PREF_LOOPBACK);
}
}

View File

@ -174,7 +174,7 @@ pub unsafe extern "C" fn capi_stream_start<STM: Stream>(
) -> c_int {
let stm = &*(s as *const STM);
let _ = _try!(stm.start());
_try!(stm.start());
ffi::CUBEB_OK
}
@ -183,7 +183,7 @@ pub unsafe extern "C" fn capi_stream_stop<STM: Stream>(
) -> c_int {
let stm = &*(s as *const STM);
let _ = _try!(stm.stop());
_try!(stm.stop());
ffi::CUBEB_OK
}
@ -192,7 +192,7 @@ pub unsafe extern "C" fn capi_stream_reset_default_device<STM: Stream>(
) -> c_int {
let stm = &mut *(s as *mut STM);
let _ = _try!(stm.reset_default_device());
_try!(stm.reset_default_device());
ffi::CUBEB_OK
}
@ -222,7 +222,7 @@ pub unsafe extern "C" fn capi_stream_set_volume<STM: Stream>(
) -> c_int {
let stm = &mut *(s as *mut STM);
let _ = _try!(stm.set_volume(volume));
_try!(stm.set_volume(volume));
ffi::CUBEB_OK
}
@ -232,7 +232,7 @@ pub unsafe extern "C" fn capi_stream_set_panning<STM: Stream>(
) -> c_int {
let stm = &mut *(s as *mut STM);
let _ = _try!(stm.set_panning(panning));
_try!(stm.set_panning(panning));
ffi::CUBEB_OK
}
@ -263,7 +263,7 @@ pub unsafe extern "C" fn capi_register_device_collection_changed<CTX: Context>(
) -> i32 {
let ctx = &*(c as *const CTX);
let devtype = DeviceType::from_bits_truncate(devtype);
let _ = _try!(ctx.register_device_collection_changed(
_try!(ctx.register_device_collection_changed(
devtype,
collection_changed_callback,
user_ptr
@ -288,7 +288,7 @@ where
}
}
fn opt_cstr<'a, T: 'a>(_anchor: &'a T, ptr: *const c_char) -> Option<&'a CStr> {
fn opt_cstr<T>(_anchor: &T, ptr: *const c_char) -> Option<&CStr> {
if ptr.is_null() {
None
} else {

View File

@ -37,23 +37,77 @@ pub struct cubeb_layout_map {
layout: ffi::cubeb_channel_layout
}
pub type InitFn =
unsafe extern fn(context: *mut *mut ffi::cubeb, context_name: *const c_char) -> c_int;
pub type GetBackendIdFn =
unsafe extern fn(context: *mut ffi::cubeb) -> *const c_char;
pub type GetMaxChannelCountFn =
unsafe extern fn(context: *mut ffi::cubeb, max_channels: *mut u32) -> c_int;
pub type GetMinLatencyFn =
unsafe extern fn(context: *mut ffi::cubeb,
params: ffi::cubeb_stream_params,
latency_ms: *mut u32) -> c_int;
pub type GetPreferredSampleRateFn =
unsafe extern fn(context: *mut ffi::cubeb, rate: *mut u32) -> c_int;
pub type GetPreferredChannelLayoutFn =
unsafe extern fn(context: *mut ffi::cubeb, layout: *mut ffi::cubeb_channel_layout) -> c_int;
pub type EnumerateDevicesFn =
unsafe extern fn(context: *mut ffi::cubeb, devtype: ffi::cubeb_device_type,
collection: *mut ffi::cubeb_device_collection) -> c_int;
pub type DeviceCollectionDestroyFn =
unsafe extern fn(context: *mut ffi::cubeb,
collection: *mut ffi::cubeb_device_collection) -> c_int;
pub type DestroyFn =
unsafe extern "C" fn(context: *mut ffi::cubeb);
pub type StreamInitFn =
unsafe extern fn(context: *mut ffi::cubeb,
stream: *mut *mut ffi::cubeb_stream,
stream_name: *const c_char,
input_device: ffi::cubeb_devid,
input_stream_params: *const ffi::cubeb_stream_params,
output_device: ffi::cubeb_devid,
output_stream_params: *const ffi::cubeb_stream_params,
latency: u32,
data_callback: ffi::cubeb_data_callback,
state_callback: ffi::cubeb_state_callback,
user_ptr: *mut c_void) -> c_int;
pub type StreamDestroyFn =
unsafe extern "C" fn(stream: *mut ffi::cubeb_stream);
pub type StreamStartFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int;
pub type StreamStopFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int;
pub type StreamResetDefaultDeviceFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int;
pub type StreamGetPositionFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream, position: *mut u64) -> c_int;
pub type StreamGetLatencyFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream, latency: *mut u32) -> c_int;
pub type StreamSetVolumeFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream, volumes: f32) -> c_int;
pub type StreamSetPanningFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream, panning: f32) -> c_int;
pub type StreamGetCurrentDeviceFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream,
device: *mut *const ffi::cubeb_device) -> c_int;
pub type StreamDeviceDestroyFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream,
device: *const ffi::cubeb_device) -> c_int;
pub type StreamRegisterDeviceChangedCallbackFn =
unsafe extern fn(stream: *mut ffi::cubeb_stream,
device_changed_callback: ffi::cubeb_device_changed_callback) -> c_int;
pub type RegisterDeviceCollectionChangedFn =
unsafe extern fn(context: *mut ffi::cubeb,
devtype: ffi::cubeb_device_type,
callback: ffi::cubeb_device_collection_changed_callback,
user_ptr: *mut c_void) -> c_int;
#[repr(C)]
pub struct Ops {
pub init: Option<
unsafe extern fn(context: *mut *mut ffi::cubeb,
context_name: *const c_char)
-> c_int>,
pub get_backend_id: Option<
unsafe extern fn(context: *mut ffi::cubeb) -> *const c_char>,
pub get_max_channel_count: Option<
unsafe extern fn(context: *mut ffi::cubeb,
max_channels: *mut u32)
-> c_int>,
pub get_min_latency: Option<
unsafe extern fn(context: *mut ffi::cubeb,
params: ffi::cubeb_stream_params,
latency_ms: *mut u32)
-> c_int>,
pub init: Option<InitFn>,
pub get_backend_id: Option<GetBackendIdFn>,
pub get_max_channel_count: Option<GetMaxChannelCountFn>,
pub get_min_latency: Option<GetMinLatencyFn>,
pub get_preferred_sample_rate: Option<
unsafe extern fn(context: *mut ffi::cubeb, rate: *mut u32) -> c_int>,
pub get_preferred_channel_layout: Option<
@ -70,19 +124,7 @@ pub struct Ops {
collection: *mut ffi::cubeb_device_collection)
-> c_int>,
pub destroy: Option<unsafe extern "C" fn(context: *mut ffi::cubeb)>,
pub stream_init: Option<
unsafe extern fn(context: *mut ffi::cubeb,
stream: *mut *mut ffi::cubeb_stream,
stream_name: *const c_char,
input_device: ffi::cubeb_devid,
input_stream_params: *const ffi::cubeb_stream_params,
output_device: ffi::cubeb_devid,
output_stream_params: *const ffi::cubeb_stream_params,
latency: u32,
data_callback: ffi::cubeb_data_callback,
state_callback: ffi::cubeb_state_callback,
user_ptr: *mut c_void)
-> c_int>,
pub stream_init: Option<StreamInitFn>,
pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream)>,
pub stream_start: Option<
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int>,
@ -118,11 +160,7 @@ pub struct Ops {
ffi::cubeb_device_changed_callback)
-> c_int>,
pub register_device_collection_changed: Option<
unsafe extern fn(context: *mut ffi::cubeb,
devtype: ffi::cubeb_device_type,
callback: ffi::cubeb_device_collection_changed_callback,
user_ptr: *mut c_void)
-> c_int>
RegisterDeviceCollectionChangedFn>
}
#[repr(C)]
@ -154,20 +192,12 @@ cubeb_enum! {
}
#[repr(C)]
#[derive(Copy)]
#[derive(Clone, Copy)]
pub struct cubeb_channel_map {
pub channels: c_uint,
pub map: [cubeb_channel; CHANNEL_MAX as usize]
}
impl Clone for cubeb_channel_map {
/// Returns a deep copy of the value.
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl Debug for cubeb_channel_map {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("cubeb_channel_map")

View File

@ -23,6 +23,7 @@ pub trait Context {
&self,
collection: *mut ffi::cubeb_device_collection,
);
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
fn stream_init(
&self,
stream_name: Option<&CStr>,

View File

@ -18,11 +18,11 @@ impl Error {
pub unsafe fn from_raw(code: ffi::cubeb_error_code) -> Error {
let code = match code {
ffi::CUBEB_ERROR => ErrorCode::Error,
ffi::CUBEB_ERROR_INVALID_FORMAT => ErrorCode::InvalidFormat,
ffi::CUBEB_ERROR_INVALID_PARAMETER => ErrorCode::InvalidParameter,
ffi::CUBEB_ERROR_NOT_SUPPORTED => ErrorCode::NotSupported,
ffi::CUBEB_ERROR_DEVICE_UNAVAILABLE => ErrorCode::DeviceUnavailable,
// Everything else is just the generic error
_ => ErrorCode::Error,
};

View File

@ -87,13 +87,21 @@ cubeb_enum! {
}
}
cubeb_enum! {
pub enum cubeb_stream_prefs: c_int {
CUBEB_STREAM_PREF_NONE = 0x00,
CUBEB_STREAM_PREF_LOOPBACK = 0x01,
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct cubeb_stream_params {
pub format: cubeb_sample_format,
pub rate: c_uint,
pub channels: c_uint,
pub layout: cubeb_channel_layout
pub layout: cubeb_channel_layout,
pub prefs: cubeb_stream_prefs
}
#[repr(C)]
@ -222,7 +230,7 @@ mod test {
use super::cubeb_stream_params;
assert_eq!(
size_of::<cubeb_stream_params>(),
16usize,
20usize,
concat!("Size of: ", stringify!(cubeb_stream_params))
);
assert_eq!(
@ -270,6 +278,16 @@ mod test {
stringify!(layout)
)
);
assert_eq!(
unsafe { &(*(0 as *const cubeb_stream_params)).prefs as *const _ as usize },
16usize,
concat!(
"Alignment of field: ",
stringify!(cubeb_stream_params),
"::",
stringify!(prefs)
)
);
}
#[test]

View File

@ -145,7 +145,6 @@ pub enum ChannelLayout {
impl From<ffi::cubeb_channel_layout> for ChannelLayout {
fn from(x: ffi::cubeb_channel_layout) -> ChannelLayout {
match x {
ffi::CUBEB_LAYOUT_UNDEFINED => ChannelLayout::Undefined,
ffi::CUBEB_LAYOUT_DUAL_MONO => ChannelLayout::DualMono,
ffi::CUBEB_LAYOUT_DUAL_MONO_LFE => ChannelLayout::DualMonoLfe,
ffi::CUBEB_LAYOUT_MONO => ChannelLayout::Mono,
@ -165,11 +164,20 @@ impl From<ffi::cubeb_channel_layout> for ChannelLayout {
ffi::CUBEB_LAYOUT_3F3R_LFE => ChannelLayout::Layout3F3RLfe,
ffi::CUBEB_LAYOUT_3F4_LFE => ChannelLayout::Layout3F4Lfe,
// TODO: Implement TryFrom
// Everything else is just undefined.
_ => ChannelLayout::Undefined,
}
}
}
/// Miscellaneous stream preferences.
bitflags! {
pub struct StreamPrefs: ffi::cubeb_stream_prefs {
const STREAM_PREF_NONE = ffi::CUBEB_STREAM_PREF_NONE;
const STREAM_PREF_LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
}
}
/// Stream format initialization parameters.
#[derive(Clone, Copy)]
pub struct StreamParams {
@ -233,6 +241,10 @@ impl StreamParams {
CUBEB_LAYOUT_3F3R_LFE => Layout3F3RLfe,
CUBEB_LAYOUT_3F4_LFE => Layout3F4Lfe)
}
pub fn prefs(&self) -> StreamPrefs {
StreamPrefs::from_bits(self.raw.prefs).unwrap()
}
}
impl Binding for StreamParams {
@ -292,7 +304,7 @@ impl<'a> Binding for Device<'a> {
}
}
/// Stream states signaled via state_callback.
/// Stream states signaled via `state_callback`.
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum State {
/// Stream started.
@ -594,4 +606,12 @@ mod tests {
assert_eq!(params.rate(), 44100);
}
#[test]
fn stream_params_raw_prefs() {
use STREAM_PREF_LOOPBACK;
let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
raw.prefs = STREAM_PREF_LOOPBACK.bits();
let params = unsafe { super::StreamParams::from_raw(&raw as *const _) };
assert_eq!(params.prefs(), STREAM_PREF_LOOPBACK);
}
}

View File

@ -1,12 +1,8 @@
The source from this directory was copied from the cubeb
git repository using the update.sh script. The only changes
made were those applied by update.sh, the addition of
Makefile.in build files for the Mozilla build system,
and the following patches, which may be overwritten when
included upstream.
https://github.com/kinetiknz/cubeb/pull/398/commits/c8e66dee61a35e6a6d54e3630e1668bdbd6984b4
https://github.com/kinetiknz/cubeb/pull/398/commits/2ed979bc891cf1a7822799947a357d4d3b625964
made were those applied by update.sh and the addition of
Makefile.in build files for the Mozilla build system.
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
The git commit ID used was 2b98e3d3279322ce0764ae2b9f28a9ea9000418b (2018-01-19 10:17:28 +1300)
The git commit ID used was 4c18a84a4573a23a1c39ccf6c70f1a6c328cb110 (2018-01-23 08:50:28 +1300)

View File

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git
The git commit ID used was cb7141e3aae5471b3a8ac1e4524f35d04edcdf4e (2018-01-17 13:10:58 +1000)
The git commit ID used was deadde7d14e0010628471e33a538a0a1e59765ff (2018-01-25 10:00:16 +1000)

View File

@ -51,6 +51,16 @@ pub const LAYOUT_3F3R_LFE: ChannelLayout = 17;
pub const LAYOUT_3F4_LFE: ChannelLayout = 18;
pub const LAYOUT_MAX: ChannelLayout = 256;
// These need to match cubeb_device_type
bitflags! {
#[repr(C)]
pub struct StreamPrefs : u32 {
const STREAM_PREF_NONE = 0x00;
const STREAM_PREF_LOOPBACK = 0x01;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct StreamParams {
@ -58,6 +68,7 @@ pub struct StreamParams {
pub rate: u32,
pub channels: u32,
pub layout: ChannelLayout,
pub prefs: StreamPrefs,
}
#[repr(C)]
@ -308,7 +319,7 @@ extern "C" {
#[test]
fn bindgen_test_layout_stream_params() {
assert_eq!(::std::mem::size_of::<StreamParams>(),
16usize,
20usize,
concat!("Size of: ", stringify!(StreamParams)));
assert_eq!(::std::mem::align_of::<StreamParams>(),
4usize,
@ -337,6 +348,12 @@ fn bindgen_test_layout_stream_params() {
stringify!(StreamParams),
"::",
stringify!(layout)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).prefs as *const _ as usize },
16usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(layout)));
}
#[test]

View File

@ -370,7 +370,7 @@ impl SubscriptionEvent {
}
pub fn event_type(self) -> SubscriptionEventType {
unsafe { ::std::mem::transmute(((self.0 & ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK) >> 4)) }
unsafe { ::std::mem::transmute((self.0 & ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK) >> 4) }
}
}

View File

@ -37,7 +37,7 @@ fn pa_channel_to_cubeb_channel(channel: pulse::ChannelPosition) -> cubeb::Channe
fn channel_map_to_layout(cm: &pulse::ChannelMap) -> cubeb::ChannelLayout {
use pulse::ChannelPosition;
let mut cubeb_map: cubeb::ChannelMap = Default::default();
cubeb_map.channels = cm.channels as u32;
cubeb_map.channels = u32::from(cm.channels);
for i in 0usize..cm.channels as usize {
cubeb_map.map[i] = pa_channel_to_cubeb_channel(ChannelPosition::try_from(cm.map[i])
.unwrap_or(ChannelPosition::Invalid));
@ -176,6 +176,7 @@ impl Context {
}
}
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
pub fn new_stream(&mut self,
stream_name: &CStr,
input_device: cubeb::DeviceId,
@ -205,7 +206,7 @@ impl Context {
pub fn max_channel_count(&self) -> Result<u32> {
match self.default_sink_info {
Some(ref info) => Ok(info.channel_map.channels as u32),
Some(ref info) => Ok(u32::from(info.channel_map.channels)),
None => Err(cubeb::ERROR),
}
}
@ -276,7 +277,7 @@ impl Context {
preferred: preferred,
format: cubeb::DeviceFmt::all(),
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
max_channels: info.channel_map.channels as u32,
max_channels: u32::from(info.channel_map.channels),
min_rate: 1,
max_rate: PA_RATE_MAX,
default_rate: info.sample_spec.rate,
@ -332,7 +333,7 @@ impl Context {
preferred: preferred,
format: cubeb::DeviceFmt::all(),
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
max_channels: info.channel_map.channels as u32,
max_channels: u32::from(info.channel_map.channels),
min_rate: 1,
max_rate: PA_RATE_MAX,
default_rate: info.sample_spec.rate,
@ -341,7 +342,6 @@ impl Context {
};
list_data.devinfo.push(devinfo);
}
fn default_device_names(_: &pulse::Context, info: &pulse::ServerInfo, user_data: *mut c_void) {
@ -403,7 +403,7 @@ impl Context {
let mut devices = Vec::from_raw_parts(coll.device as *mut cubeb::DeviceInfo,
coll.count,
coll.count);
for dev in devices.iter_mut() {
for dev in &mut devices {
if !dev.group_id.is_null() {
let _ = CString::from_raw(dev.group_id as *mut _);
}
@ -558,18 +558,15 @@ impl Context {
}
let context_ptr: *mut c_void = self as *mut _ as *mut _;
match self.context.take() {
Some(ctx) => {
self.mainloop.lock();
if let Ok(o) = ctx.drain(drain_complete, context_ptr) {
self.operation_wait(None, &o);
}
ctx.clear_state_callback();
ctx.disconnect();
ctx.unref();
self.mainloop.unlock();
},
_ => {},
if let Some(ctx) = self.context.take() {
self.mainloop.lock();
if let Ok(o) = ctx.drain(drain_complete, context_ptr) {
self.operation_wait(None, &o);
}
ctx.clear_state_callback();
ctx.disconnect();
ctx.unref();
self.mainloop.unlock();
}
}

View File

@ -34,7 +34,7 @@ fn cubeb_channel_to_pa_channel(channel: cubeb::Channel) -> pa_channel_position_t
PA_CHANNEL_POSITION_LFE // CHANNEL_LFE
];
let idx: i32 = channel.into();
let idx: i32 = channel;
MAP[idx as usize]
}
@ -89,6 +89,7 @@ impl<'ctx> Drop for Stream<'ctx> {
}
impl<'ctx> Stream<'ctx> {
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
pub fn new(context: &'ctx Context,
stream_name: &CStr,
input_device: cubeb::DeviceId,
@ -114,10 +115,8 @@ impl<'ctx> Stream<'ctx> {
let readable_size: i32 = s.readable_size()
.and_then(|s| Ok(s as i32))
.unwrap_or(-1);
if readable_size > 0 {
if unsafe { s.peek(buffer, size).is_err() } {
return -1;
}
if readable_size > 0 && unsafe { s.peek(buffer, size).is_err() } {
return -1;
}
readable_size
}
@ -230,7 +229,6 @@ impl<'ctx> Stream<'ctx> {
return Err(e);
},
}
}
// Set up input stream
@ -258,7 +256,6 @@ impl<'ctx> Stream<'ctx> {
return Err(e);
},
}
}
let r = if stm.wait_until_ready() {
@ -309,31 +306,25 @@ impl<'ctx> Stream<'ctx> {
self.context.mainloop.lock();
{
match self.output_stream.take() {
Some(stm) => {
if !self.drain_timer.is_null() {
/* there's no pa_rttime_free, so use this instead. */
self.context
.mainloop
.get_api()
.time_free(self.drain_timer);
}
stm.clear_state_callback();
stm.clear_write_callback();
let _ = stm.disconnect();
stm.unref();
},
_ => {},
if let Some(stm) = self.output_stream.take() {
if !self.drain_timer.is_null() {
/* there's no pa_rttime_free, so use this instead. */
self.context
.mainloop
.get_api()
.time_free(self.drain_timer);
}
stm.clear_state_callback();
stm.clear_write_callback();
let _ = stm.disconnect();
stm.unref();
}
match self.input_stream.take() {
Some(stm) => {
stm.clear_state_callback();
stm.clear_read_callback();
let _ = stm.disconnect();
stm.unref();
},
_ => {},
if let Some(stm) = self.input_stream.take() {
stm.clear_state_callback();
stm.clear_read_callback();
let _ = stm.disconnect();
stm.unref();
}
}
self.context.mainloop.unlock();
@ -416,7 +407,7 @@ impl<'ctx> Stream<'ctx> {
Some(ref stm) => {
match stm.get_latency() {
Ok(StreamLatency::Positive(r_usec)) => {
let latency = (r_usec * self.output_sample_spec.rate as pa_usec_t / PA_USEC_PER_SEC) as u32;
let latency = (r_usec * pa_usec_t::from(self.output_sample_spec.rate) / PA_USEC_PER_SEC) as u32;
Ok(latency)
},
Ok(_) => {
@ -451,8 +442,8 @@ impl<'ctx> Stream<'ctx> {
self.volume = volume;
} else {
let channels = stm.get_sample_spec().channels;
let vol = pulse::sw_volume_from_linear(volume as f64);
cvol.set(channels as u32, vol);
let vol = pulse::sw_volume_from_linear(f64::from(volume));
cvol.set(u32::from(channels), vol);
let index = stm.get_index();
@ -564,6 +555,10 @@ impl<'ctx> Stream<'ctx> {
stream_name: &CStr)
-> Result<pulse::Stream> {
if stream_params.prefs == cubeb::StreamPrefs::STREAM_PREF_LOOPBACK {
return Err(cubeb::ERROR_NOT_SUPPORTED);
}
fn to_pulse_format(format: cubeb::SampleFormat) -> pulse::SampleFormat {
match format {
cubeb::SAMPLE_S16LE => pulse::SampleFormat::Signed16LE,
@ -689,6 +684,7 @@ impl<'ctx> Stream<'ctx> {
true
}
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
fn trigger_user_callback(&mut self, input_data: *const c_void, nbytes: usize) {
fn drained_cb(a: &pulse::MainloopApi, e: *mut pa_time_event, _tv: &pulse::TimeVal, u: *mut c_void) {
let stm = unsafe { &mut *(u as *mut Stream) };

View File

@ -123,6 +123,7 @@ int run_test(int num_channels, layout_info layout, int sampling_rate, int is_flo
params.rate = sampling_rate;
params.channels = num_channels;
params.layout = layout.layout;
params.prefs = CUBEB_STREAM_PREF_NONE;
synth_state synth(params.channels, params.rate);
@ -171,6 +172,7 @@ int run_panning_volume_test(int is_float)
params.rate = 44100;
params.channels = 2;
params.layout = CUBEB_LAYOUT_STEREO;
params.prefs = CUBEB_STREAM_PREF_NONE;
synth_state synth(params.channels, params.rate);

View File

@ -190,6 +190,7 @@ TEST(cubeb, enumerate_devices)
input_params.rate = output_params.rate = 48000;
input_params.channels = output_params.channels = 1;
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
NULL, &input_params, NULL, &output_params,

View File

@ -101,10 +101,12 @@ TEST(cubeb, duplex)
input_params.rate = 48000;
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
output_params.format = STREAM_FORMAT;
output_params.rate = 48000;
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_STEREO;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";

View File

@ -34,7 +34,8 @@ TEST(cubeb, latency)
CUBEB_SAMPLE_FLOAT32NE,
preferred_rate,
max_channels,
CUBEB_LAYOUT_UNDEFINED
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE
};
r = cubeb_get_min_latency(ctx, &params, &latency_frames);
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);

View File

@ -0,0 +1,575 @@
/*
* Copyright © 2017 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
/* libcubeb api/function test. Requests a loopback device and checks that
output is being looped back to input. NOTE: Usage of output devices while
performing this test will cause flakey results! */
#include "gtest/gtest.h"
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
#include <memory>
#include <mutex>
#include <string>
#include "cubeb/cubeb.h"
#include "common.h"
const uint32_t SAMPLE_FREQUENCY = 48000;
const uint32_t TONE_FREQUENCY = 440;
const double OUTPUT_AMPLITUDE = 0.25;
const uint32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
template<typename T> T ConvertSampleToOutput(double input);
template<> float ConvertSampleToOutput(double input) { return float(input); }
template<> short ConvertSampleToOutput(double input) { return short(input * 32767.0f); }
template<typename T> double ConvertSampleFromOutput(T sample);
template<> double ConvertSampleFromOutput(float sample) { return double(sample); }
template<> double ConvertSampleFromOutput(short sample) { return double(sample / 32767.0); }
/* Simple cross correlation to help find phase shift. Not a performant impl */
std::vector<double> cross_correlate(std::vector<double> & f,
std::vector<double> & g,
size_t signal_length)
{
/* the length we sweep our window through to find the cross correlation */
size_t sweep_length = f.size() - signal_length + 1;
std::vector<double> correlation;
correlation.reserve(sweep_length);
for (size_t i = 0; i < sweep_length; i++) {
double accumulator = 0.0;
for (size_t j = 0; j < signal_length; j++) {
accumulator += f.at(j) * g.at(i + j);
}
correlation.push_back(accumulator);
}
return correlation;
}
/* best effort discovery of phase shift between output and (looped) input*/
size_t find_phase(std::vector<double> & output_frames,
std::vector<double> & input_frames,
size_t signal_length)
{
std::vector<double> correlation = cross_correlate(output_frames, input_frames, signal_length);
size_t phase = 0;
double max_correlation = correlation.at(0);
for (size_t i = 1; i < correlation.size(); i++) {
if (correlation.at(i) > max_correlation) {
max_correlation = correlation.at(i);
phase = i;
}
}
return phase;
}
std::vector<double> normalize_frames(std::vector<double> & frames) {
double max = abs(*std::max_element(frames.begin(), frames.end(),
[](double a, double b) { return abs(a) < abs(b); }));
std::vector<double> normalized_frames;
normalized_frames.reserve(frames.size());
for (const double frame : frames) {
normalized_frames.push_back(frame / max);
}
return normalized_frames;
}
/* heuristic comparison of aligned output and input signals, gets flaky if TONE_FREQUENCY is too high */
void compare_signals(std::vector<double> & output_frames,
std::vector<double> & input_frames)
{
ASSERT_EQ(output_frames.size(), input_frames.size()) << "#Output frames != #input frames";
size_t num_frames = output_frames.size();
std::vector<double> normalized_output_frames = normalize_frames(output_frames);
std::vector<double> normalized_input_frames = normalize_frames(input_frames);
/* calculate mean absolute errors */
/* mean absolute errors between output and input */
double io_mas = 0.0;
/* mean absolute errors between output and silence */
double output_silence_mas = 0.0;
/* mean absolute errors between input and silence */
double input_silence_mas = 0.0;
for (size_t i = 0; i < num_frames; i++) {
io_mas += abs(normalized_output_frames.at(i) - normalized_input_frames.at(i));
output_silence_mas += abs(normalized_output_frames.at(i));
input_silence_mas += abs(normalized_input_frames.at(i));
}
io_mas /= num_frames;
output_silence_mas /= num_frames;
input_silence_mas /= num_frames;
ASSERT_LT(io_mas, output_silence_mas)
<< "Error between output and input should be less than output and silence!";
ASSERT_LT(io_mas, input_silence_mas)
<< "Error between output and input should be less than output and silence!";
/* make sure extrema are in (roughly) correct location */
/* number of maxima + minama expected in the frames*/
const long NUM_EXTREMA = 2 * TONE_FREQUENCY * NUM_FRAMES_TO_OUTPUT / SAMPLE_FREQUENCY;
/* expected index of first maxima */
const long FIRST_MAXIMUM_INDEX = SAMPLE_FREQUENCY / TONE_FREQUENCY / 4;
/* Threshold we expect all maxima and minima to be above or below. Ideally
the extrema would be 1 or -1, but particularly at the start of loopback
the values seen can be significantly lower. */
const double THRESHOLD = 0.5;
for (size_t i = 0; i < NUM_EXTREMA; i++) {
bool is_maximum = i % 2 == 0;
/* expected offset to current extreme: i * stide between extrema */
size_t offset = i * SAMPLE_FREQUENCY / TONE_FREQUENCY / 2;
if (is_maximum) {
ASSERT_GT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
<< "Output frames have unexpected missing maximum!";
ASSERT_GT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
<< "Input frames have unexpected missing maximum!";
} else {
ASSERT_LT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
<< "Output frames have unexpected missing minimum!";
ASSERT_LT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
<< "Input frames have unexpected missing minimum!";
}
}
}
struct user_state_loopback {
std::mutex user_state_mutex;
long position = 0;
/* track output */
std::vector<double> output_frames;
/* track input */
std::vector<double> input_frames;
};
template<typename T>
long data_cb_loop_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
{
struct user_state_loopback * u = (struct user_state_loopback *) user;
T * ib = (T *) inputbuffer;
T * ob = (T *) outputbuffer;
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
return CUBEB_ERROR;
}
std::lock_guard<std::mutex> lock(u->user_state_mutex);
/* generate our test tone on the fly */
for (int i = 0; i < nframes; i++) {
double tone = 0.0;
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
/* generate sine wave */
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
tone *= OUTPUT_AMPLITUDE;
}
ob[i] = ConvertSampleToOutput<T>(tone);
u->output_frames.push_back(tone);
/* store any looped back output, may be silence */
u->input_frames.push_back(ConvertSampleFromOutput(ib[i]));
}
u->position += nframes;
return nframes;
}
template<typename T>
long data_cb_loop_input_only(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
{
struct user_state_loopback * u = (struct user_state_loopback *) user;
T * ib = (T *) inputbuffer;
if (outputbuffer != NULL) {
// Can't assert as it needs to return, so expect to fail instead
EXPECT_EQ(outputbuffer, (void *) NULL) << "outputbuffer should be null in input only callback";
return CUBEB_ERROR;
}
if (stream == NULL || inputbuffer == NULL) {
return CUBEB_ERROR;
}
std::lock_guard<std::mutex> lock(u->user_state_mutex);
for (int i = 0; i < nframes; i++) {
u->input_frames.push_back(ConvertSampleFromOutput(ib[i]));
}
return nframes;
}
template<typename T>
long data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
{
struct user_state_loopback * u = (struct user_state_loopback *) user;
T * ob = (T *) outputbuffer;
if (stream == NULL || outputbuffer == NULL) {
return CUBEB_ERROR;
}
std::lock_guard<std::mutex> lock(u->user_state_mutex);
/* generate our test tone on the fly */
for (int i = 0; i < nframes; i++) {
double tone = 0.0;
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
/* generate sine wave */
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
tone *= OUTPUT_AMPLITUDE;
}
ob[i] = ConvertSampleToOutput<T>(tone);
u->output_frames.push_back(tone);
}
u->position += nframes;
return nframes;
}
void state_cb_loop(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
if (stream == NULL)
return;
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
default:
fprintf(stderr, "unknown stream state %d\n", state);
}
return;
}
void run_loopback_duplex_test(bool is_float)
{
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params input_params;
cubeb_stream_params output_params;
int r;
uint32_t latency_frames = 0;
r = common_init(&ctx, "Cubeb loopback example: duplex stream");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
input_params.rate = SAMPLE_FREQUENCY;
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
output_params.rate = SAMPLE_FREQUENCY;
output_params.channels = 1;
output_params.layout = CUBEB_LAYOUT_MONO;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
ASSERT_TRUE(!!user_data) << "Error allocating user data";
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup a duplex stream with loopback */
r = cubeb_stream_init(ctx, &stream, "Cubeb loopback",
NULL, &input_params, NULL, &output_params, latency_frames,
is_float ? data_cb_loop_duplex<float> : data_cb_loop_duplex<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(150);
cubeb_stream_stop(stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & output_frames = user_data->output_frames;
std::vector<double> & input_frames = user_data->input_frames;
ASSERT_EQ(output_frames.size(), input_frames.size())
<< "#Output frames != #input frames";
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
/* extract vectors of just the relevant signal from output and input */
auto output_frames_signal_start = output_frames.begin();
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
auto input_frames_signal_start = input_frames.begin() + phase;
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
compare_signals(trimmed_output_frames, trimmed_input_frames);
}
TEST(cubeb, loopback_duplex)
{
run_loopback_duplex_test(true);
run_loopback_duplex_test(false);
}
void run_loopback_separate_streams_test(bool is_float)
{
cubeb * ctx;
cubeb_stream * input_stream;
cubeb_stream * output_stream;
cubeb_stream_params input_params;
cubeb_stream_params output_params;
int r;
uint32_t latency_frames = 0;
r = common_init(&ctx, "Cubeb loopback example: separate streams");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
input_params.rate = SAMPLE_FREQUENCY;
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
output_params.rate = SAMPLE_FREQUENCY;
output_params.channels = 1;
output_params.layout = CUBEB_LAYOUT_MONO;
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
ASSERT_TRUE(!!user_data) << "Error allocating user data";
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup an input stream with loopback */
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
NULL, &input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
/* setup an output stream */
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
NULL, NULL, NULL, &output_params, latency_frames,
is_float ? data_cb_playback<float> : data_cb_playback<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
cubeb_stream_start(input_stream);
cubeb_stream_start(output_stream);
delay(150);
cubeb_stream_stop(output_stream);
cubeb_stream_stop(input_stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & output_frames = user_data->output_frames;
std::vector<double> & input_frames = user_data->input_frames;
ASSERT_LE(output_frames.size(), input_frames.size())
<< "#Output frames should be less or equal to #input frames";
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
/* extract vectors of just the relevant signal from output and input */
auto output_frames_signal_start = output_frames.begin();
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
auto input_frames_signal_start = input_frames.begin() + phase;
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
compare_signals(trimmed_output_frames, trimmed_input_frames);
}
TEST(cubeb, loopback_separate_streams)
{
run_loopback_separate_streams_test(true);
run_loopback_separate_streams_test(false);
}
void run_loopback_silence_test(bool is_float)
{
cubeb * ctx;
cubeb_stream * input_stream;
cubeb_stream_params input_params;
int r;
uint32_t latency_frames = 0;
r = common_init(&ctx, "Cubeb loopback example: record silence");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
input_params.rate = SAMPLE_FREQUENCY;
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
ASSERT_TRUE(!!user_data) << "Error allocating user data";
r = cubeb_get_min_latency(ctx, &input_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup an input stream with loopback */
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
NULL, &input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
cubeb_stream_start(input_stream);
delay(50);
cubeb_stream_stop(input_stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & input_frames = user_data->input_frames;
/* expect to have at least ~50ms of frames */
ASSERT_GE(input_frames.size(), SAMPLE_FREQUENCY / 20);
double EPISILON = 0.000001;
/* frames should be 0.0, but use epsilon to avoid possible issues with impls
that may use ~0.0 silence values. */
for (double frame : input_frames) {
ASSERT_LT(abs(frame), EPISILON);
}
}
TEST(cubeb, loopback_silence)
{
run_loopback_silence_test(true);
run_loopback_silence_test(false);
}
void run_loopback_device_selection_test(bool is_float)
{
cubeb * ctx;
cubeb_device_collection collection;
cubeb_stream * input_stream;
cubeb_stream * output_stream;
cubeb_stream_params input_params;
cubeb_stream_params output_params;
int r;
uint32_t latency_frames = 0;
r = common_init(&ctx, "Cubeb loopback example: device selection, separate streams");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
fprintf(stderr, "Device enumeration not supported"
" for this backend, skipping this test.\n");
return;
}
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
/* get first preferred output device id */
std::string device_id;
for (size_t i = 0; i < collection.count; i++) {
if (collection.device[i].preferred) {
device_id = collection.device[i].device_id;
break;
}
}
cubeb_device_collection_destroy(ctx, &collection);
if (device_id.empty()) {
fprintf(stderr, "Could not find preferred device, aborting test.\n");
return;
}
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
input_params.rate = SAMPLE_FREQUENCY;
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
output_params.rate = SAMPLE_FREQUENCY;
output_params.channels = 1;
output_params.layout = CUBEB_LAYOUT_MONO;
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
ASSERT_TRUE(!!user_data) << "Error allocating user data";
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup an input stream with loopback */
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
device_id.c_str(), &input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
/* setup an output stream */
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
NULL, NULL, device_id.c_str(), &output_params, latency_frames,
is_float ? data_cb_playback<float> : data_cb_playback<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
cubeb_stream_start(input_stream);
cubeb_stream_start(output_stream);
delay(150);
cubeb_stream_stop(output_stream);
cubeb_stream_stop(input_stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & output_frames = user_data->output_frames;
std::vector<double> & input_frames = user_data->input_frames;
ASSERT_LE(output_frames.size(), input_frames.size())
<< "#Output frames should be less or equal to #input frames";
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
/* extract vectors of just the relevant signal from output and input */
auto output_frames_signal_start = output_frames.begin();
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
auto input_frames_signal_start = input_frames.begin() + phase;
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
compare_signals(trimmed_output_frames, trimmed_input_frames);
}
TEST(cubeb, loopback_device_selection)
{
run_loopback_device_selection_test(true);
run_loopback_device_selection_test(false);
}

View File

@ -114,7 +114,8 @@ downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_l
STREAM_FORMAT,
STREAM_FREQUENCY,
layout_infos[in_layout].channels,
in_layout
in_layout,
CUBEB_STREAM_PREF_NONE
};
cubeb_stream_params out_params = {
@ -124,7 +125,8 @@ downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_l
// smaller than or equal to the input channels.
(out_layout == CUBEB_LAYOUT_UNDEFINED) ?
layout_infos[in_layout].channels : layout_infos[out_layout].channels,
out_layout
out_layout,
CUBEB_STREAM_PREF_NONE
};
if (!cubeb_should_downmix(&in_params, &out_params)) {

View File

@ -68,6 +68,7 @@ TEST(cubeb, overload_callback)
output_params.rate = 48000;
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_STEREO;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK);

View File

@ -94,6 +94,7 @@ TEST(cubeb, record)
params.rate = SAMPLE_FREQUENCY;
params.channels = 1;
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, &params, NULL, nullptr,
4096, data_cb_record, state_cb_record, &stream_state);

View File

@ -333,6 +333,7 @@ void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
input_params.rate = input_rate;
state.output_rate = output_params.rate = output_rate;
state.target_rate = target_rate;
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
long got;
cubeb_resampler * resampler =

View File

@ -125,6 +125,8 @@ TEST(cubeb, context_variables)
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_get_min_latency(ctx, &params, &value);
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
if (r == CUBEB_OK) {
@ -160,6 +162,7 @@ TEST(cubeb, init_destroy_stream)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
@ -186,6 +189,7 @@ TEST(cubeb, init_destroy_multiple_streams)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
@ -216,6 +220,7 @@ TEST(cubeb, configure_stream)
params.rate = STREAM_RATE;
params.channels = 2; // panning
params.layout = CUBEB_LAYOUT_STEREO;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
@ -247,6 +252,7 @@ TEST(cubeb, configure_stream_undefined_layout)
params.rate = STREAM_RATE;
params.channels = 2; // panning
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
@ -282,6 +288,7 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
@ -366,6 +373,7 @@ TEST(cubeb, init_destroy_multiple_contexts_and_streams)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
r = common_init(&ctx[i], "test_sanity");
@ -404,6 +412,7 @@ TEST(cubeb, basic_stream_operations)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
@ -452,6 +461,7 @@ TEST(cubeb, stream_position)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
@ -585,6 +595,7 @@ TEST(cubeb, drain)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_drain_data_callback, test_drain_state_callback, &dummy);

View File

@ -95,6 +95,7 @@ TEST(cubeb, tone)
params.rate = SAMPLE_FREQUENCY;
params.channels = 1;
params.layout = CUBEB_LAYOUT_MONO;
params.prefs = CUBEB_STREAM_PREF_NONE;
std::unique_ptr<cb_user_data> user_data(new cb_user_data());
ASSERT_TRUE(!!user_data) << "Error allocating user data";

View File

@ -53,16 +53,18 @@ extern "C" {
output_params.format = CUBEB_SAMPLE_FLOAT32NE;
output_params.rate = rate;
output_params.channels = 2;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
cubeb_stream_params input_params;
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
input_params.rate = rate;
input_params.channels = 1;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
cubeb_stream * stm;
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
NULL, input_params,
NULL, output_params,
NULL, &input_params,
NULL, &output_params,
latency_frames,
data_cb, state_cb,
NULL);
@ -213,6 +215,15 @@ typedef enum {
CUBEB_LAYOUT_MAX
} cubeb_channel_layout;
/** Miscellaneous stream preferences. */
typedef enum {
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be
specified on the input params and an
output device to loopback from should
be passed in place of an input device. */
} cubeb_stream_prefs;
/** Stream format initialization parameters. */
typedef struct {
cubeb_sample_format format; /**< Requested sample format. One of
@ -220,6 +231,7 @@ typedef struct {
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. */
cubeb_stream_prefs prefs; /**< Requested preferences. */
} cubeb_stream_params;
/** Audio device description */

View File

@ -890,6 +890,10 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
*stream = NULL;
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
switch (stream_params->format) {
case CUBEB_SAMPLE_S16LE:
format = SND_PCM_FORMAT_S16_LE;

View File

@ -135,8 +135,8 @@ struct cubeb_stream {
cubeb_device_changed_callback device_changed_callback = nullptr;
owned_critical_section device_changed_callback_lock;
/* Stream creation parameters */
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
device_info input_device;
device_info output_device;
/* User pointer of data_callback */
@ -2360,6 +2360,12 @@ audiounit_setup_stream(cubeb_stream * stm)
{
stm->mutex.assert_current_thread_owns();
if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) ||
(stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) {
LOG("(%p) Loopback not supported for audiounit.", stm);
return CUBEB_ERROR_NOT_SUPPORTED;
}
int r = 0;
device_info in_dev_info = stm->input_device;

View File

@ -743,6 +743,12 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
if (input_device || output_device)
return CUBEB_ERROR_NOT_SUPPORTED;
// Loopback is unsupported
if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
(output_stream_params && (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
*stream = NULL;
// Find a free stream.

View File

@ -1291,6 +1291,11 @@ opensl_validate_stream_param(cubeb_stream_params * stream_params)
(stream_params->channels < 1 || stream_params->channels > 32))) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if ((stream_params &&
(stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
LOG("Loopback is not supported");
return CUBEB_ERROR_NOT_SUPPORTED;
}
return CUBEB_OK;
}

View File

@ -807,6 +807,9 @@ create_pa_stream(cubeb_stream * stm,
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels))));
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
*pa_stm = NULL;
pa_sample_spec ss;
ss.format = to_pulse_format(stream_params->format);

View File

@ -279,11 +279,19 @@ sndio_stream_init(cubeb * context,
memset(s, 0, sizeof(cubeb_stream));
s->mode = 0;
if (input_stream_params) {
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
DPR("sndio_stream_init(), loopback not supported\n");
goto err;
}
s->mode |= SIO_REC;
format = input_stream_params->format;
rate = input_stream_params->rate;
}
if (output_stream_params) {
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
DPR("sndio_stream_init(), loopback not supported\n");
goto err;
}
s->mode |= SIO_PLAY;
format = output_stream_params->format;
rate = output_stream_params->rate;

View File

@ -193,12 +193,12 @@ struct cubeb_stream {
/* Mixer pameters. We need to convert the input stream to this
samplerate/channel layout, as WASAPI does not resample nor upmix
itself. */
cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
/* Stream parameters. This is what the client requested,
* and what will be presented in the callback. */
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
/* The input and output device, or NULL for default. */
std::unique_ptr<const wchar_t[]> input_device;
std::unique_ptr<const wchar_t[]> output_device;
@ -207,6 +207,10 @@ struct cubeb_stream {
cubeb_state_callback state_callback = nullptr;
cubeb_data_callback data_callback = nullptr;
wasapi_refill_callback refill_callback = nullptr;
/* True when a loopback device is requested with no output device. In this
case a dummy output device is opened to drive the loopback, but should not
be exposed. */
bool has_dummy_output = false;
void * user_ptr = nullptr;
/* Lifetime considerations:
- client, render_client, audio_clock and audio_stream_volume are interface
@ -564,9 +568,9 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
{
XASSERT(!stm->draining);
/* If we need to upmix after resampling, resample into the mix buffer to
avoid a copy. */
avoid a copy. Avoid exposing output if it is a dummy stream. */
void * dest = nullptr;
if (has_output(stm)) {
if (has_output(stm) && !stm->has_dummy_output) {
if (cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
dest = stm->mix_buffer.data();
} else {
@ -595,9 +599,10 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
/* If this is not true, there will be glitches.
It is alright to have produced less frames if we are draining, though. */
XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm));
XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm) || stm->has_dummy_output);
if (has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
// We don't bother mixing dummy output as it will be silenced, otherwise mix output if needed
if (!stm->has_dummy_output && has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
XASSERT(dest == stm->mix_buffer.data());
unsigned long dest_len = out_frames * stm->output_stream_params.channels;
XASSERT(dest_len <= stm->mix_buffer.size() / stm->bytes_per_sample);
@ -612,22 +617,12 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
/* This helper grabs all the frames available from a capture client, put them in
* linear_input_buffer. linear_input_buffer should be cleared before the
* callback exits. */
* callback exits. This helper does not work with exclusive mode streams. */
bool get_input_buffer(cubeb_stream * stm)
{
HRESULT hr;
UINT32 padding_in;
XASSERT(has_input(stm));
hr = stm->input_client->GetCurrentPadding(&padding_in);
if (FAILED(hr)) {
LOG("Failed to get padding");
return false;
}
XASSERT(padding_in <= stm->input_buffer_frame_count);
UINT32 total_available_input = padding_in;
HRESULT hr;
BYTE * input_packet = NULL;
DWORD flags;
UINT64 dev_pos;
@ -635,17 +630,18 @@ bool get_input_buffer(cubeb_stream * stm)
/* Get input packets until we have captured enough frames, and put them in a
* contiguous buffer. */
uint32_t offset = 0;
while (offset != total_available_input) {
hr = stm->capture_client->GetNextPacketSize(&next);
// If the input stream is event driven we should only ever expect to read a
// single packet each time. However, if we're pulling from the stream we may
// need to grab multiple packets worth of frames that have accumulated (so
// need a loop).
for (hr = stm->capture_client->GetNextPacketSize(&next);
next > 0;
hr = stm->capture_client->GetNextPacketSize(&next)) {
if (FAILED(hr)) {
LOG("cannot get next packet size: %lx", hr);
return false;
}
/* This can happen if the capture stream has stopped. Just return in this
* case. */
if (!next) {
break;
}
UINT32 packet_size;
hr = stm->capture_client->GetBuffer(&input_packet,
@ -658,6 +654,21 @@ bool get_input_buffer(cubeb_stream * stm)
return false;
}
XASSERT(packet_size == next);
// We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
// flag. There a two primary (non exhaustive) scenarios we anticipate this
// flag being set in:
// - The first GetBuffer after Start has this flag undefined. In this
// case the flag may be set but is meaningless and can be ignored.
// - If a glitch is introduced into the input. This should not happen
// for event based inputs, and should be mitigated by using a dummy
// stream to drive input in the case of input only loopback. Without
// a dummy output, input only loopback would glitch on silence. However,
// the dummy input should push silence to the loopback and prevent
// discontinuities. See https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
// As the first scenario can be ignored, and we anticipate the second
// scenario is mitigated, we ignore the flag.
// For more info: https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx,
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd371458(v=vs.85).aspx
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
LOG("insert silence: ps=%u", packet_size);
stm->linear_input_buffer->push_silence(packet_size * stm->input_stream_params.channels);
@ -687,8 +698,7 @@ bool get_input_buffer(cubeb_stream * stm)
offset += packet_size;
}
XASSERT(stm->linear_input_buffer->length() >= total_available_input &&
offset == total_available_input);
XASSERT(stm->linear_input_buffer->length() >= offset);
return true;
}
@ -774,18 +784,36 @@ refill_callback_duplex(cubeb_stream * stm)
return false;
}
ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
input_frames, output_frames);
if (stm->has_dummy_output) {
ALOGV("Duplex callback (dummy output): input frames: %Iu, output frames: %Iu",
input_frames, output_frames);
refill(stm,
stm->linear_input_buffer->data(),
input_frames,
output_buffer,
output_frames);
// We don't want to expose the dummy output to the callback so don't pass
// the output buffer (it will be released later with silence in it)
refill(stm,
stm->linear_input_buffer->data(),
input_frames,
nullptr,
0);
} else {
ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
input_frames, output_frames);
refill(stm,
stm->linear_input_buffer->data(),
input_frames,
output_buffer,
output_frames);
}
stm->linear_input_buffer->clear();
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
if (stm->has_dummy_output) {
// If output is a dummy output, make sure it's silent
hr = stm->render_client->ReleaseBuffer(output_frames, AUDCLNT_BUFFERFLAGS_SILENT);
} else {
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
}
if (FAILED(hr)) {
LOG("failed to release buffer: %lx", hr);
return false;
@ -1517,6 +1545,11 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
{
com_ptr<IMMDevice> device;
HRESULT hr;
bool is_loopback = stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK;
if (is_loopback && direction != eCapture) {
LOG("Loopback pref can only be used with capture streams!\n");
return CUBEB_ERROR;
}
stm->stream_reset_lock.assert_current_thread_owns();
bool try_again = false;
@ -1530,9 +1563,16 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
return CUBEB_ERROR;
}
} else {
hr = get_default_endpoint(device, direction);
// If caller has requested loopback but not specified a device, look for
// the default render device. Otherwise look for the default device
// appropriate to the direction.
hr = get_default_endpoint(device, is_loopback ? eRender : direction);
if (FAILED(hr)) {
LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
if (is_loopback) {
LOG("Could not get default render endpoint for loopback, error: %lx\n", hr);
} else {
LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
}
return CUBEB_ERROR;
}
}
@ -1603,9 +1643,18 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
mix_params->format, mix_params->rate, mix_params->channels,
CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].name);
DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
// Check if a loopback device should be requested. Note that event callbacks
// do not work with loopback devices, so only request these if not looping.
if (is_loopback) {
flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
} else {
flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
}
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
AUDCLNT_STREAMFLAGS_NOPERSIST,
flags,
frames_to_hns(stm, stm->latency),
0,
mix_format.get(),
@ -1626,11 +1675,14 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
stm->mix_buffer.resize(frames_to_bytes_before_mix(stm, *buffer_frame_count));
}
hr = audio_client->SetEventHandle(event);
if (FAILED(hr)) {
LOG("Could set the event handle for the %s client %lx.",
DIRECTION_NAME, hr);
return CUBEB_ERROR;
// Events are used if not looping back
if (!is_loopback) {
hr = audio_client->SetEventHandle(event);
if (FAILED(hr)) {
LOG("Could set the event handle for the %s client %lx.",
DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
}
hr = audio_client->GetService(riid, render_or_capture_client.receive_vpp());
@ -1691,6 +1743,27 @@ int setup_wasapi_stream(cubeb_stream * stm)
silent_buffer_count);
}
// If we don't have an output device but are requesting a loopback device,
// we attempt to open that same device in output mode in order to drive the
// loopback via the output events.
stm->has_dummy_output = false;
if (!has_output(stm) && stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
stm->output_stream_params.rate = stm->input_stream_params.rate;
stm->output_stream_params.channels = stm->input_stream_params.channels;
stm->output_stream_params.layout = stm->input_stream_params.layout;
if (stm->input_device) {
size_t len = wcslen(stm->input_device.get());
std::unique_ptr<wchar_t[]> tmp(new wchar_t[len + 1]);
if (wcsncpy_s(tmp.get(), len + 1, stm->input_device.get(), len) != 0) {
LOG("Failed to copy device identifier while copying input stream"
" configuration to output stream configuration to drive loopback.");
return CUBEB_ERROR;
}
stm->output_device = move(tmp);
}
stm->has_dummy_output = true;
}
if (has_output(stm)) {
LOG("(%p) Setup render: device=%p", stm, stm->output_device.get());
rv = setup_wasapi_stream_one_side(stm,

View File

@ -414,6 +414,11 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
/* Loopback is not supported */
return CUBEB_ERROR_NOT_SUPPORTED;
}
*stream = NULL;
memset(&wfx, 0, sizeof(wfx));

View File

@ -42,6 +42,7 @@ cp $1/test/test_audio.cpp gtest
cp $1/test/test_devices.cpp gtest
cp $1/test/test_duplex.cpp gtest
cp $1/test/test_latency.cpp gtest
cp $1/test/test_loopback.cpp gtest
cp $1/test/test_mixer.cpp gtest
cp $1/test/test_overload_callback.cpp gtest
cp $1/test/test_record.cpp gtest

View File

@ -5808,9 +5808,6 @@ pref("media.block-autoplay-until-in-foreground", true);
// Is Stylo CSS support built and enabled?
// Only define these prefs if Stylo support is actually built in.
#ifdef MOZ_STYLO
// XXX: We should flip this pref to true once the blocked_domains is non-empty.
pref("layout.css.stylo-blocklist.enabled", false);
pref("layout.css.stylo-blocklist.blocked_domains", "");
#ifdef MOZ_STYLO_ENABLE
pref("layout.css.servo.enabled", true);
#else

1
servo/Cargo.lock generated
View File

@ -1024,7 +1024,6 @@ dependencies = [
"simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
"style_traits 0.0.1",
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -40,7 +40,6 @@ servo_geometry = {path = "../geometry"}
servo_url = {path = "../url"}
smallvec = "0.6"
style = {path = "../style"}
style_traits = {path = "../style_traits"}
time = "0.1.12"
unicode-bidi = {version = "0.3", features = ["with_serde"]}
unicode-script = {version = "0.1", features = ["harfbuzz"]}

View File

@ -29,14 +29,13 @@ use std::collections::HashMap;
use std::f32;
use std::fmt;
use std::sync::Arc;
use style::values::computed::Filter;
use style_traits::cursor::CursorKind;
use text::TextRun;
use text::glyph::ByteIndex;
use webrender_api::{BoxShadowClipMode, ClipId, ColorF, ExtendMode, GradientStop, ImageKey};
use webrender_api::{ImageRendering, LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D};
use webrender_api::{LineStyle, LocalClip, MixBlendMode, NormalBorder, RepeatMode, ScrollPolicy};
use webrender_api::{ScrollSensitivity, StickyOffsetBounds, TransformStyle};
use webrender_api::{BorderRadius, BorderWidths, BoxShadowClipMode, ClipId, ColorF, ExtendMode};
use webrender_api::{FilterOp, GradientStop, ImageBorder, ImageKey, ImageRendering, LayoutPoint};
use webrender_api::{LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip, MixBlendMode};
use webrender_api::{NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
use webrender_api::TransformStyle;
pub use style::dom::OpaqueNode;
@ -204,7 +203,7 @@ pub struct StackingContext {
pub z_index: i32,
/// CSS filters to be applied to this stacking context (including opacity).
pub filters: Vec<Filter>,
pub filters: Vec<FilterOp>,
/// The blend mode with which this stacking context blends with its backdrop.
pub mix_blend_mode: MixBlendMode,
@ -233,7 +232,7 @@ impl StackingContext {
bounds: &Rect<Au>,
overflow: &Rect<Au>,
z_index: i32,
filters: Vec<Filter>,
filters: Vec<FilterOp>,
mix_blend_mode: MixBlendMode,
transform: Option<Transform3D<f32>>,
transform_style: TransformStyle,
@ -370,7 +369,7 @@ pub struct ClipScrollNode {
pub clip: ClippingRegion,
/// The rect of the contents that can be scrolled inside of the scroll root.
pub content_rect: Rect<Au>,
pub content_rect: LayoutRect,
/// The type of this ClipScrollNode.
pub node_type: ClipScrollNodeType,
@ -419,7 +418,7 @@ pub struct BaseDisplayItem {
impl BaseDisplayItem {
#[inline(always)]
pub fn new(bounds: &Rect<Au>,
pub fn new(bounds: Rect<Au>,
metadata: DisplayItemMetadata,
local_clip: LocalClip,
section: DisplayListSection,
@ -427,12 +426,12 @@ impl BaseDisplayItem {
clipping_and_scrolling: ClippingAndScrolling)
-> BaseDisplayItem {
BaseDisplayItem {
bounds: *bounds,
metadata: metadata,
local_clip: local_clip,
section: section,
stacking_context_id: stacking_context_id,
clipping_and_scrolling: clipping_and_scrolling,
bounds,
metadata,
local_clip,
section,
stacking_context_id,
clipping_and_scrolling,
}
}
@ -646,7 +645,7 @@ pub struct DisplayItemMetadata {
pub node: OpaqueNode,
/// The value of the `cursor` property when the mouse hovers over this display item. If `None`,
/// this display item is ineligible for pointer events (`pointer-events: none`).
pub pointing: Option<CursorKind>,
pub pointing: Option<u16>,
}
/// Paints a solid color.
@ -789,28 +788,6 @@ pub struct RadialGradientDisplayItem {
pub tile_spacing: LayoutSize,
}
/// A border that is made of image segments.
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct ImageBorder {
/// The image this border uses, border-image-source.
pub image: WebRenderImageInfo,
/// How to slice the image, as per border-image-slice.
pub slice: SideOffsets2D<u32>,
/// Outsets for the border, as per border-image-outset.
pub outset: SideOffsets2D<f32>,
/// If fill is true, draw the center patch of the image.
pub fill: bool,
/// How to repeat or stretch horizontal edges (border-image-repeat).
pub repeat_horizontal: RepeatMode,
/// How to repeat or stretch vertical edges (border-image-repeat).
pub repeat_vertical: RepeatMode,
}
/// A border that is made of linear gradient
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct GradientBorder {
@ -847,7 +824,7 @@ pub struct BorderDisplayItem {
pub base: BaseDisplayItem,
/// Border widths.
pub border_widths: SideOffsets2D<Au>,
pub border_widths: BorderWidths,
/// Details for specific border type
pub details: BorderDetails,
@ -949,7 +926,7 @@ pub struct BoxShadowDisplayItem {
pub spread_radius: f32,
/// The border radius of this shadow.
pub border_radius: BorderRadii<Au>,
pub border_radius: BorderRadius,
/// How we should clip the result.
pub clip_mode: BoxShadowClipMode,

View File

@ -58,7 +58,6 @@ extern crate servo_url;
extern crate simd;
extern crate smallvec;
extern crate style;
extern crate style_traits;
extern crate time;
extern crate unicode_bidi;
extern crate unicode_script;

View File

@ -30,7 +30,7 @@ use gfx::display_list::{BaseDisplayItem, BorderDetails, BorderDisplayItem, BLUR_
use gfx::display_list::{BorderRadii, BoxShadowDisplayItem, ClipScrollNode};
use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
use gfx::display_list::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
use gfx::display_list::{DisplayListSection, GradientDisplayItem, IframeDisplayItem, ImageBorder};
use gfx::display_list::{DisplayListSection, GradientDisplayItem, IframeDisplayItem};
use gfx::display_list::{ImageDisplayItem, LineDisplayItem, OpaqueNode};
use gfx::display_list::{PopAllTextShadowsDisplayItem, PushTextShadowDisplayItem};
use gfx::display_list::{RadialGradientDisplayItem, SolidColorDisplayItem, StackingContext};
@ -68,15 +68,15 @@ use style::values::computed::{Gradient, NumberOrPercentage};
use style::values::computed::effects::SimpleShadow;
use style::values::computed::pointing::Cursor;
use style::values::generics::background::BackgroundSize;
use style::values::generics::effects::Filter;
use style::values::generics::image::{GradientKind, Image, PaintWorklet};
use style_traits::CSSPixel;
use style_traits::ToCss;
use style_traits::cursor::CursorKind;
use table_cell::CollapsedBordersForCell;
use webrender_api::{self, BoxShadowClipMode, ClipId, ClipMode, ColorF, ComplexClipRegion};
use webrender_api::{ImageRendering, LayoutSize, LayoutVector2D, LineStyle};
use webrender_api::{LocalClip, NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
use webrender_api::{self, BorderSide, BoxShadowClipMode, ClipId, ClipMode, ColorF};
use webrender_api::{ComplexClipRegion, FilterOp, ImageBorder, ImageRendering, LayoutRect};
use webrender_api::{LayoutSize, LayoutVector2D, LineStyle, LocalClip, NinePatchDescriptor};
use webrender_api::{NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
trait ResolvePercentage {
fn resolve(&self, length: u32) -> u32;
@ -246,7 +246,7 @@ impl StackingContextCollectionState {
id: Some(ClipId::root_scroll_node(pipeline_id.to_webrender())),
parent_index: ClipScrollNodeIndex(0),
clip: ClippingRegion::from_rect(&TypedRect::zero()),
content_rect: Rect::zero(),
content_rect: LayoutRect::zero(),
node_type: ClipScrollNodeType::ScrollFrame(ScrollSensitivity::ScriptAndInputEvents),
};
@ -387,10 +387,11 @@ impl<'a> DisplayListBuildState<'a> {
};
BaseDisplayItem::new(
&bounds,
*bounds,
DisplayItemMetadata {
node: node,
pointing: cursor,
node,
// Store cursor id in display list.
pointing: cursor.map(|x| x as u16),
},
clip,
section,
@ -816,7 +817,7 @@ fn calculate_inner_bounds(mut bounds: Rect<Au>, offsets: SideOffsets2D<Au>) -> R
}
fn simple_normal_border(color: ColorF, style: webrender_api::BorderStyle) -> NormalBorder {
let side = webrender_api::BorderSide { color, style };
let side = BorderSide { color, style };
NormalBorder {
left: side,
right: side,
@ -844,6 +845,36 @@ fn calculate_inner_border_radii(
radii
}
fn build_image_border_details(
webrender_image: WebRenderImageInfo,
border_style_struct: &style_structs::Border,
) -> Option<BorderDetails> {
let corners = &border_style_struct.border_image_slice.offsets;
let border_image_repeat = &border_style_struct.border_image_repeat;
if let Some(image_key) = webrender_image.key {
Some(BorderDetails::Image(ImageBorder {
image_key: image_key,
patch: NinePatchDescriptor {
width: webrender_image.width,
height: webrender_image.height,
slice: SideOffsets2D::new(
corners.0.resolve(webrender_image.height),
corners.1.resolve(webrender_image.width),
corners.2.resolve(webrender_image.height),
corners.3.resolve(webrender_image.width),
),
},
fill: border_style_struct.border_image_slice.fill,
// TODO(gw): Support border-image-outset
outset: SideOffsets2D::zero(),
repeat_horizontal: border_image_repeat.0.to_layout(),
repeat_vertical: border_image_repeat.1.to_layout(),
}))
} else {
None
}
}
impl FragmentDisplayListBuilding for Fragment {
fn collect_stacking_contexts_for_blocklike_fragment(
&mut self,
@ -1292,7 +1323,7 @@ impl FragmentDisplayListBuilding for Fragment {
),
blur_radius: box_shadow.base.blur.px(),
spread_radius: box_shadow.spread.px(),
border_radius,
border_radius: border_radius.to_border_radius(),
clip_mode: if box_shadow.inset {
BoxShadowClipMode::Inset
} else {
@ -1352,13 +1383,6 @@ impl FragmentDisplayListBuilding for Fragment {
);
}
let colors = SideOffsets2D::new(
style.resolve_color(colors.top),
style.resolve_color(colors.right),
style.resolve_color(colors.bottom),
style.resolve_color(colors.left),
);
// If this border collapses, then we draw outside the boundaries we were given.
let mut bounds = *bounds;
if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
@ -1376,35 +1400,28 @@ impl FragmentDisplayListBuilding for Fragment {
let border_radius = build_border_radius(&bounds, border_style_struct);
match border_style_struct.border_image_source {
Either::First(_) => {
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: border.to_physical(style.writing_mode),
details: BorderDetails::Normal(NormalBorder {
left: webrender_api::BorderSide {
color: colors.left.to_layout(),
style: border_style.left.to_layout(),
},
right: webrender_api::BorderSide {
color: colors.right.to_layout(),
style: border_style.right.to_layout(),
},
top: webrender_api::BorderSide {
color: colors.top.to_layout(),
style: border_style.top.to_layout(),
},
bottom: webrender_api::BorderSide {
color: colors.bottom.to_layout(),
style: border_style.bottom.to_layout(),
},
radius: border_radius.to_border_radius(),
}),
})));
},
let details = match border_style_struct.border_image_source {
Either::First(_) => Some(BorderDetails::Normal(NormalBorder {
left: BorderSide {
color: style.resolve_color(colors.left).to_layout(),
style: border_style.left.to_layout(),
},
right: BorderSide {
color: style.resolve_color(colors.right).to_layout(),
style: border_style.right.to_layout(),
},
top: BorderSide {
color: style.resolve_color(colors.top).to_layout(),
style: border_style.top.to_layout(),
},
bottom: BorderSide {
color: style.resolve_color(colors.bottom).to_layout(),
style: border_style.bottom.to_layout(),
},
radius: border_radius.to_border_radius(),
})),
Either::Second(Image::Gradient(ref gradient)) => {
let border_widths = border.to_physical(style.writing_mode);
let details = match gradient.kind {
Some(match gradient.kind {
GradientKind::Linear(angle_or_corner) => {
BorderDetails::Gradient(display_list::GradientBorder {
gradient: convert_linear_gradient(
@ -1430,80 +1447,39 @@ impl FragmentDisplayListBuilding for Fragment {
outset: SideOffsets2D::zero(),
})
},
};
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base,
border_widths,
details,
})));
})
},
Either::Second(Image::PaintWorklet(ref paint_worklet)) => {
// TODO: this size should be increased by border-image-outset
let size = self.border_box.size.to_physical(style.writing_mode);
let webrender_image =
self.get_webrender_image_for_paint_worklet(state, style, paint_worklet, size);
if let Some(webrender_image) = webrender_image {
let corners = &border_style_struct.border_image_slice.offsets;
let border_image_repeat = &border_style_struct.border_image_repeat;
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: border.to_physical(style.writing_mode),
details: BorderDetails::Image(ImageBorder {
image: webrender_image,
fill: border_style_struct.border_image_slice.fill,
slice: SideOffsets2D::new(
corners.0.resolve(webrender_image.height),
corners.1.resolve(webrender_image.width),
corners.2.resolve(webrender_image.height),
corners.3.resolve(webrender_image.width),
),
// TODO(gw): Support border-image-outset
outset: SideOffsets2D::zero(),
repeat_horizontal: border_image_repeat.0.to_layout(),
repeat_vertical: border_image_repeat.1.to_layout(),
}),
})));
}
self.get_webrender_image_for_paint_worklet(state, style, paint_worklet, size)
.and_then(|image| build_image_border_details(image, border_style_struct))
},
Either::Second(Image::Rect(..)) => {
// TODO: Handle border-image with `-moz-image-rect`.
None
},
Either::Second(Image::Element(..)) => {
// TODO: Handle border-image with `-moz-element`.
None
},
Either::Second(Image::Url(ref image_url)) => {
if let Some(url) = image_url.url() {
let webrender_image = state.layout_context.get_webrender_image_for_url(
Either::Second(Image::Url(ref image_url)) => image_url
.url()
.and_then(|url| {
state.layout_context.get_webrender_image_for_url(
self.node,
url.clone(),
UsePlaceholder::No,
);
if let Some(webrender_image) = webrender_image {
let corners = &border_style_struct.border_image_slice.offsets;
let border_image_repeat = &border_style_struct.border_image_repeat;
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: border.to_physical(style.writing_mode),
details: BorderDetails::Image(ImageBorder {
image: webrender_image,
fill: border_style_struct.border_image_slice.fill,
slice: SideOffsets2D::new(
corners.0.resolve(webrender_image.height),
corners.1.resolve(webrender_image.width),
corners.2.resolve(webrender_image.height),
corners.3.resolve(webrender_image.width),
),
// TODO(gw): Support border-image-outset
outset: SideOffsets2D::zero(),
repeat_horizontal: border_image_repeat.0.to_layout(),
repeat_vertical: border_image_repeat.1.to_layout(),
}),
})));
}
}
},
)
})
.and_then(|image| build_image_border_details(image, border_style_struct)),
};
if let Some(details) = details {
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base,
border_widths: border.to_physical(style.writing_mode).to_layout(),
details,
})));
}
}
@ -1531,10 +1507,7 @@ impl FragmentDisplayListBuilding for Fragment {
// absolute bounds.
let mut bounds = *bounds;
let offset = width + Au::from(style.get_outline().outline_offset);
bounds.origin.x = bounds.origin.x - offset;
bounds.origin.y = bounds.origin.y - offset;
bounds.size.width = bounds.size.width + offset + offset;
bounds.size.height = bounds.size.height + offset + offset;
bounds = bounds.inflate(offset, offset);
// Append the outline to the display list.
let color = style
@ -1549,7 +1522,7 @@ impl FragmentDisplayListBuilding for Fragment {
);
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(width),
border_widths: SideOffsets2D::new_all_same(width).to_layout(),
details: BorderDetails::Normal(simple_normal_border(color, outline_style.to_layout())),
})));
}
@ -1576,7 +1549,7 @@ impl FragmentDisplayListBuilding for Fragment {
);
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
details: BorderDetails::Normal(simple_normal_border(
ColorF::rgb(0, 0, 200),
webrender_api::BorderStyle::Solid,
@ -1623,7 +1596,7 @@ impl FragmentDisplayListBuilding for Fragment {
);
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
details: BorderDetails::Normal(simple_normal_border(
ColorF::rgb(0, 0, 200),
webrender_api::BorderStyle::Solid,
@ -2084,9 +2057,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Create the filter pipeline.
let effects = self.style().get_effects();
let mut filters = effects.filter.0.clone();
let mut filters: Vec<FilterOp> = effects.filter.0.iter().map(ToLayout::to_layout).collect();
if effects.opacity != 1.0 {
filters.push(Filter::Opacity(effects.opacity.into()))
filters.push(FilterOp::Opacity(effects.opacity.into(), effects.opacity));
}
StackingContext::new(
@ -2095,7 +2068,7 @@ impl FragmentDisplayListBuilding for Fragment {
&border_box,
&overflow,
self.effective_z_index(),
filters.into(),
filters,
self.style().get_effects().mix_blend_mode.to_layout(),
self.transform_matrix(&border_box),
self.style().get_used_transform_style().to_layout(),
@ -2171,8 +2144,7 @@ impl FragmentDisplayListBuilding for Fragment {
}
// Create display items for text decorations.
let text_decorations =
self.style().get_inheritedtext().text_decorations_in_effect;
let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect;
let stacking_relative_content_box = LogicalRect::from_physical(
self.style.writing_mode,
@ -2697,7 +2669,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
id: None,
parent_index: self.clipping_and_scrolling().scrolling,
clip: ClippingRegion::from_rect(border_box),
content_rect: Rect::zero(),
content_rect: LayoutRect::zero(),
node_type: ClipScrollNodeType::StickyFrame(sticky_frame_data),
});
@ -2755,7 +2727,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
id: Some(new_clip_scroll_node_id),
parent_index: self.clipping_and_scrolling().scrolling,
clip: clip,
content_rect: Rect::new(content_box.origin, content_size),
content_rect: Rect::new(content_box.origin, content_size).to_layout(),
node_type: ClipScrollNodeType::ScrollFrame(sensitivity),
});
@ -2808,7 +2780,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
id: None,
parent_index: self.clipping_and_scrolling().scrolling,
clip: ClippingRegion::from_rect(&clip_rect),
content_rect: Rect::zero(), // content_rect isn't important for clips.
content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
node_type: ClipScrollNodeType::Clip,
});
@ -3148,7 +3120,7 @@ impl BaseFlowDisplayListBuilding for BaseFlow {
);
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(Au::from_px(2)),
border_widths: SideOffsets2D::new_all_same(Au::from_px(2)).to_layout(),
details: BorderDetails::Normal(simple_normal_border(
color,
webrender_api::BorderStyle::Solid,

View File

@ -77,7 +77,7 @@ impl WebRenderDisplayListConverter for DisplayList {
impl WebRenderDisplayItemConverter for DisplayItem {
fn prim_info(&self) -> webrender_api::LayoutPrimitiveInfo {
let tag = match self.base().metadata.pointing {
Some(cursor) => Some((self.base().metadata.node.0 as u64, cursor as u16)),
Some(cursor) => Some((self.base().metadata.node.0 as u64, cursor)),
None => None,
};
webrender_api::LayoutPrimitiveInfo {
@ -173,29 +173,11 @@ impl WebRenderDisplayItemConverter for DisplayItem {
}
},
DisplayItem::Border(ref item) => {
let widths = item.border_widths.to_layout();
let details = match item.details {
BorderDetails::Normal(ref border) => {
webrender_api::BorderDetails::Normal(*border)
},
BorderDetails::Image(ref image) => match image.image.key {
None => return,
Some(key) => {
webrender_api::BorderDetails::Image(webrender_api::ImageBorder {
image_key: key,
patch: webrender_api::NinePatchDescriptor {
width: image.image.width,
height: image.image.height,
slice: image.slice,
},
fill: image.fill,
outset: image.outset,
repeat_horizontal: image.repeat_horizontal,
repeat_vertical: image.repeat_vertical,
})
},
},
BorderDetails::Image(ref image) => webrender_api::BorderDetails::Image(*image),
BorderDetails::Gradient(ref gradient) => {
webrender_api::BorderDetails::Gradient(webrender_api::GradientBorder {
gradient: builder.create_gradient(
@ -222,7 +204,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
},
};
builder.push_border(&self.prim_info(), widths, details);
builder.push_border(&self.prim_info(), item.border_widths, details);
},
DisplayItem::Gradient(ref item) => {
let gradient = builder.create_gradient(
@ -265,7 +247,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
item.color,
item.blur_radius,
item.spread_radius,
item.border_radius.to_border_radius(),
item.border_radius,
item.clip_mode,
);
},
@ -303,11 +285,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
stacking_context.transform_style,
perspective,
stacking_context.mix_blend_mode,
stacking_context
.filters
.iter()
.map(ToLayout::to_layout)
.collect(),
stacking_context.filters.clone(),
);
},
DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(),
@ -328,7 +306,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
.define_scroll_frame_with_parent(
node.id,
parent_id,
node.content_rect.to_layout(),
node.content_rect,
node.clip.main.to_layout(),
node.clip.get_complex_clips(),
None,

View File

@ -714,10 +714,13 @@ impl MallocSizeOf for url::Host {
}
}
}
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::BorderRadius);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::BorderStyle);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::BorderWidths);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::BoxShadowClipMode);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::ClipAndScrollInfo);
@ -728,8 +731,12 @@ malloc_size_of_is_0!(webrender_api::ColorF);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::ExtendMode);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::FilterOp);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::GradientStop);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::ImageBorder);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::ImageKey);
#[cfg(feature = "servo")]
malloc_size_of_is_0!(webrender_api::ImageRendering);

View File

@ -54,6 +54,7 @@ use dom::htmloptionelement::HTMLOptionElement;
use dom::htmloutputelement::HTMLOutputElement;
use dom::htmlparagraphelement::HTMLParagraphElement;
use dom::htmlparamelement::HTMLParamElement;
use dom::htmlpictureelement::HTMLPictureElement;
use dom::htmlpreelement::HTMLPreElement;
use dom::htmlprogresselement::HTMLProgressElement;
use dom::htmlquoteelement::HTMLQuoteElement;
@ -309,6 +310,7 @@ pub fn create_native_html_element(
local_name!("output") => make!(HTMLOutputElement),
local_name!("p") => make!(HTMLParagraphElement),
local_name!("param") => make!(HTMLParamElement),
local_name!("picture") => make!(HTMLPictureElement),
local_name!("plaintext") => make!(HTMLPreElement),
local_name!("pre") => make!(HTMLPreElement),
local_name!("progress") => make!(HTMLProgressElement),

View File

@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::HTMLPictureElementBinding;
use dom::bindings::root::DomRoot;
use dom::document::Document;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
#[dom_struct]
pub struct HTMLPictureElement {
htmlelement: HTMLElement,
}
impl HTMLPictureElement {
fn new_inherited(local_name: LocalName,
prefix: Option<Prefix>,
document: &Document) -> HTMLPictureElement {
HTMLPictureElement {
htmlelement:
HTMLElement::new_inherited(local_name, prefix, document)
}
}
#[allow(unrooted_must_root)]
pub fn new(local_name: LocalName,
prefix: Option<Prefix>,
document: &Document) -> DomRoot<HTMLPictureElement> {
Node::reflect_node(Box::new(HTMLPictureElement::new_inherited(local_name, prefix, document)),
document,
HTMLPictureElementBinding::Wrap)
}
}

View File

@ -348,6 +348,7 @@ pub mod htmloptionscollection;
pub mod htmloutputelement;
pub mod htmlparagraphelement;
pub mod htmlparamelement;
pub mod htmlpictureelement;
pub mod htmlpreelement;
pub mod htmlprogresselement;
pub mod htmlquoteelement;

View File

@ -0,0 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#htmlpictureelement
[HTMLConstructor]
interface HTMLPictureElement : HTMLElement {};

View File

@ -132,3 +132,14 @@ yaml:
- '**/*.yml'
- '**/*.yaml'
- '**/.ymllint'
shellcheck:
description: shellcheck run over the gecko codebase
platform: lint/opt
treeherder:
symbol: shell
run:
mach: lint -l shellcheck -f treeherder
when:
files-changed:
- '**/*.sh'

View File

@ -62,6 +62,7 @@ linux64-nightly/opt:
- opt-only-tests
- desktop-screenshot-capture
- awsy
- linux-talos-profiling
linux64-devedition/opt:
build-platform: linux64-devedition-nightly/opt
test-sets:

View File

@ -135,6 +135,22 @@ linux-qr-tests:
- reftest
- xpcshell
linux-talos-profiling:
- talos-chrome-profiling
- talos-dromaeojs-profiling
- talos-g1-profiling
- talos-g2-profiling
- talos-g3-profiling
- talos-g4-profiling
- talos-g5-profiling
- talos-other-profiling
- talos-perf-reftest-profiling
- talos-perf-reftest-singletons-profiling
- talos-speedometer-profiling
- talos-svgr-profiling
- talos-tp5o-profiling
- talos-tp6-profiling
windows-qr-tests:
- crashtest
- mochitest-gpu
@ -297,6 +313,7 @@ macosx64-talos-profiling:
- talos-perf-reftest-singletons-profiling
- talos-speedometer-profiling
- talos-svgr-profiling
- talos-tp5o-profiling
- talos-tp6-profiling
linux32-tests:

View File

@ -47,7 +47,7 @@ for locale in $locales; do
done
# Generate snapcraft manifest
sed -e "s/@VERSION@/${VERSION}/g" -e "s/@BUILD_NUMBER@/${BUILD_NUMBER}/g" snapcraft.yaml.in > ${WORKSPACE}/snapcraft.yaml
sed -e "s/@VERSION@/${VERSION}/g" -e "s/@BUILD_NUMBER@/${BUILD_NUMBER}/g" snapcraft.yaml.in > "${WORKSPACE}/snapcraft.yaml"
cd "${WORKSPACE}"
snapcraft

View File

@ -23,6 +23,8 @@ test -n "$IMAGE_NAME" || raise_error "IMAGE_NAME must be provided."
# proxy socket that the worker user can use.
export DOCKER_SOCKET=/var/run/docker.proxy
socat UNIX-LISTEN:$DOCKER_SOCKET,fork,group=worker,mode=0775 UNIX-CLIENT:/var/run/docker.sock </dev/null &
# Disable check until new version is tested.
# shellcheck disable=SC2064
trap "kill $!" EXIT
LOAD_COMMAND=

View File

@ -5,9 +5,7 @@
cd /setup || exit
# shellcheck source=taskcluster/docker/recipes/common.sh
. /setup/common.sh
# shellcheck source=taskcluster/docker/recipes/install-mercurial.sh
. /setup/install-mercurial.sh
rm -rf /setup

View File

@ -91,16 +91,13 @@ apt-get install -y -f "${apt_packages[@]}"
dpkg-reconfigure locales
# shellcheck source=taskcluster/docker/recipes/common.sh
. /setup/common.sh
# shellcheck source=taskcluster/docker/recipes/install-mercurial.sh
. /setup/install-mercurial.sh
pip install --upgrade pip
pip install virtualenv
# shellcheck source=taskcluster/docker/recipes/install-node.sh
. /setup/install-node.sh
# Install custom-built Debian packages. These come from a set of repositories

View File

@ -3,6 +3,10 @@
"chromez-e10s": {
"tests": ["tresize"]
},
"chromez-profiling-e10s": {
"talos_options": ["--geckoProfile"],
"tests": ["tresize"]
},
"chromez-stylo-disabled-e10s": {
"talos_options": ["--disable-stylo"],
"tests": ["tresize"]
@ -10,6 +14,10 @@
"dromaeojs-e10s": {
"tests": ["dromaeo_css", "kraken"]
},
"dromaeojs-profiling-e10s": {
"talos_options": ["--geckoProfile"],
"tests": ["dromaeo_css", "kraken"]
},
"dromaeojs-stylo-disabled-e10s": {
"talos_options": ["--disable-stylo"],
"tests": ["dromaeo_css", "kraken"]
@ -17,6 +25,10 @@
"other-e10s": {
"tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
},
"other-profiling-e10s": {
"talos_options": ["--geckoProfile"],
"tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
},
"other-stylo-disabled-e10s": {
"talos_options": ["--disable-stylo"],
"tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
@ -25,6 +37,11 @@
"tests": ["tp5o_scroll", "glterrain"],
"pagesets_name": "tp5n.zip"
},
"g1-profiling-e10s": {
"tests": ["tp5o_scroll", "glterrain"],
"talos_options": ["--geckoProfile"],
"pagesets_name": "tp5n.zip"
},
"g1-stylo-disabled-e10s": {
"tests": ["tp5o_scroll", "glterrain"],
"talos_options": ["--disable-stylo"],
@ -34,6 +51,11 @@
"tests": ["damp", "tps"],
"pagesets_name": "tp5n.zip"
},
"g2-profiling-e10s": {
"tests": ["damp", "tps"],
"talos_options": ["--geckoProfile"],
"pagesets_name": "tp5n.zip"
},
"g2-stylo-disabled-e10s": {
"tests": ["damp", "tps"],
"talos_options": ["--disable-stylo"],
@ -42,6 +64,10 @@
"g3-e10s": {
"tests": ["dromaeo_dom"]
},
"g3-profiling-e10s": {
"tests": ["dromaeo_dom"],
"talos_options": ["--geckoProfile"]
},
"g3-stylo-disabled-e10s": {
"tests": ["dromaeo_dom"],
"talos_options": ["--disable-stylo"]
@ -49,6 +75,10 @@
"g4-e10s": {
"tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg", "rasterflood_gradient"]
},
"g4-profiling-e10s": {
"tests": ["basic_compositor_video", "glvideo"],
"talos_options": ["--geckoProfile"]
},
"g4-stylo-disabled-e10s": {
"tests": ["basic_compositor_video", "glvideo"],
"talos_options": ["--disable-stylo"]
@ -57,6 +87,11 @@
"tests": ["ts_paint_webext", "tp5o_webext"],
"pagesets_name": "tp5n.zip"
},
"g5-profiling-e10s": {
"tests": ["ts_paint_webext", "tp5o_webext"],
"talos_options": ["--geckoProfile"],
"pagesets_name": "tp5n.zip"
},
"g5-stylo-disabled-e10s": {
"tests": ["ts_paint_webext", "tp5o_webext"],
"talos_options": ["--disable-stylo"],
@ -65,6 +100,10 @@
"svgr-e10s": {
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "tsvg_static"]
},
"svgr-profiling-e10s": {
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "tsvg_static"],
"talos_options": ["--geckoProfile"]
},
"svgr-stylo-disabled-e10s": {
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "tsvg_static"],
"talos_options": ["--disable-stylo"]
@ -72,6 +111,10 @@
"perf-reftest-e10s": {
"tests": ["perf_reftest"]
},
"perf-reftest-profiling-e10s": {
"tests": ["perf_reftest"],
"talos_options": ["--geckoProfile"]
},
"perf-reftest-stylo-disabled-e10s": {
"tests": ["perf_reftest"],
"talos_options": ["--disable-stylo"]
@ -79,6 +122,10 @@
"perf-reftest-singletons-e10s": {
"tests": ["perf_reftest_singletons"]
},
"perf-reftest-singletons-profiling-e10s": {
"tests": ["perf_reftest_singletons"],
"talos_options": ["--geckoProfile"]
},
"perf-reftest-singletons-stylo-disabled-e10s": {
"tests": ["perf_reftest_singletons"],
"talos_options": ["--disable-stylo"]
@ -86,6 +133,10 @@
"speedometer-e10s": {
"tests": ["speedometer", "stylebench"]
},
"speedometer-profiling-e10s": {
"talos_options": ["--geckoProfile"],
"tests": ["speedometer"]
},
"speedometer-stylo-disabled-e10s": {
"talos_options": ["--disable-stylo"],
"tests": ["speedometer"]
@ -94,6 +145,11 @@
"tests": ["tp5o"],
"pagesets_name": "tp5n.zip"
},
"tp5o-profiling-e10s": {
"tests": ["tp5o"],
"pagesets_name": "tp5n.zip",
"talos_options": ["--geckoProfile"]
},
"tp5o-stylo-disabled-e10s": {
"tests": ["tp5o"],
"pagesets_name": "tp5n.zip",
@ -127,6 +183,18 @@
"--firstNonBlankPaint"
]
},
"tp6-profiling-e10s": {
"tests": ["tp6_google", "tp6_youtube", "tp6_amazon", "tp6_facebook"],
"mitmproxy_release_bin_osx": "mitmproxy-2.0.2-osx.tar.gz",
"mitmproxy_release_bin_linux64": "mitmproxy-2.0.2-linux.tar.gz",
"mitmproxy_recording_set": "mitmproxy-recording-set-win10.zip",
"talos_options": [
"--geckoProfile",
"--mitmproxy",
"mitmproxy-recording-google.mp mitmproxy-recording-youtube.mp mitmproxy-recording-amazon.mp mitmproxy-recording-facebook.mp",
"--firstNonBlankPaint"
]
},
"tp6-stylo-disabled-e10s": {
"tests": ["tp6_google", "tp6_youtube", "tp6_amazon", "tp6_facebook"],
"mitmproxy_release_bin_osx": "mitmproxy-2.0.2-osx.tar.gz",

View File

@ -45,7 +45,7 @@ dependencies = [
[[package]]
name = "audioipc"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -68,7 +68,7 @@ dependencies = [
name = "audioipc-client"
version = "0.2.0"
dependencies = [
"audioipc 0.2.0",
"audioipc 0.2.1",
"cubeb-backend 0.2.0",
"cubeb-core 0.1.0",
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -81,11 +81,11 @@ dependencies = [
[[package]]
name = "audioipc-server"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"audioipc 0.2.0",
"audioipc 0.2.1",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.3.1",
"cubeb 0.3.2",
"cubeb-core 0.1.0",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -316,7 +316,7 @@ dependencies = [
[[package]]
name = "cubeb"
version = "0.3.1"
version = "0.3.2"
dependencies = [
"cubeb-core 0.1.0",
"libcubeb-sys 0.1.0",
@ -582,9 +582,9 @@ name = "gkrust-shared"
version = "0.1.0"
dependencies = [
"audioipc-client 0.2.0",
"audioipc-server 0.2.0",
"audioipc-server 0.2.1",
"cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.3.1",
"cubeb 0.3.2",
"cubeb-backend 0.2.0",
"cubeb-core 0.1.0",
"cubeb-pulse 0.0.2",

View File

@ -45,7 +45,7 @@ dependencies = [
[[package]]
name = "audioipc"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -68,7 +68,7 @@ dependencies = [
name = "audioipc-client"
version = "0.2.0"
dependencies = [
"audioipc 0.2.0",
"audioipc 0.2.1",
"cubeb-backend 0.2.0",
"cubeb-core 0.1.0",
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -81,11 +81,11 @@ dependencies = [
[[package]]
name = "audioipc-server"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"audioipc 0.2.0",
"audioipc 0.2.1",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.3.1",
"cubeb 0.3.2",
"cubeb-core 0.1.0",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -316,7 +316,7 @@ dependencies = [
[[package]]
name = "cubeb"
version = "0.3.1"
version = "0.3.2"
dependencies = [
"cubeb-core 0.1.0",
"libcubeb-sys 0.1.0",
@ -580,9 +580,9 @@ name = "gkrust-shared"
version = "0.1.0"
dependencies = [
"audioipc-client 0.2.0",
"audioipc-server 0.2.0",
"audioipc-server 0.2.1",
"cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.3.1",
"cubeb 0.3.2",
"cubeb-backend 0.2.0",
"cubeb-core 0.1.0",
"cubeb-pulse 0.0.2",

View File

@ -19,7 +19,6 @@
fill-opacity: 0.2;
}
image.close-icon,
.close-icon > .button-icon,
.close-icon > .button-box > .button-icon,
.close-icon > .toolbarbutton-icon {

View File

@ -155,7 +155,7 @@ def lint(paths, config, **lintargs):
files = find_shell_scripts(config, paths)
base_command = [binary, '-x', '-f', 'json']
base_command = [binary, '-f', 'json']
if config.get('excludecodes'):
base_command.extend(['-e', ','.join(config.get('excludecodes'))])

View File

@ -2,11 +2,11 @@
shellcheck:
description: Shell script linter
include:
- taskcluster/docker/funsize-update-generator/scripts/
- taskcluster/docker/
exclude: []
# 1090: https://github.com/koalaman/shellcheck/wiki/SC1090
# 'Can't follow a non-constant source'
extensions: ['sh']
excludecodes: ['1090']
excludecodes: ['1090', '1091']
type: external
payload: shell:lint

View File

@ -6887,7 +6887,13 @@ nsWindow::GetCSDSupportLevel() {
} else if (strstr(currentDesktop, "LXQt") != nullptr) {
sCSDSupportLevel = CSD_SUPPORT_FULL;
} else {
// Release or beta builds are not supposed to be broken
// so disable titlebar rendering on untested/unknown systems.
#if defined(RELEASE_OR_BETA)
sCSDSupportLevel = CSD_SUPPORT_NONE;
#else
sCSDSupportLevel = CSD_SUPPORT_FLAT;
#endif
}
} else {
sCSDSupportLevel = CSD_SUPPORT_NONE;