mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'experimental-2'
This commit is contained in:
commit
5b2395a9e4
1920 changed files with 487540 additions and 7840 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -17,12 +17,12 @@ url=https://github.com/ali-fareed/rules_swift.git
|
|||
[submodule "build-system/tulsi"]
|
||||
path = build-system/tulsi
|
||||
url=https://github.com/bazelbuild/tulsi.git
|
||||
[submodule "third-party/webrtc/webrtc-ios"]
|
||||
path = third-party/webrtc/webrtc-ios
|
||||
url=https://github.com/ali-fareed/webrtc-ios.git
|
||||
[submodule "submodules/TgVoipWebrtc/tgcalls"]
|
||||
path = submodules/TgVoipWebrtc/tgcalls
|
||||
url=../tgcalls.git
|
||||
[submodule "third-party/libvpx/libvpx"]
|
||||
path = third-party/libvpx/libvpx
|
||||
url = https://github.com/webmproject/libvpx.git
|
||||
[submodule "third-party/webrtc/webrtc"]
|
||||
path = third-party/webrtc/webrtc
|
||||
url = https://github.com/ali-fareed/webrtc.git
|
||||
|
|
|
|||
|
|
@ -5902,6 +5902,9 @@ Sorry for the inconvenience.";
|
|||
|
||||
"InviteLink.InviteLinkCopiedText" = "Invite link copied to clipboard";
|
||||
|
||||
"InviteLink.OtherAdminsLinks" = "Invite Links Created By Other Admins";
|
||||
"InviteLink.OtherPermanentLinkInfo" = "**%1$@** can see this link and use it to invite new members to **%2$@**.";
|
||||
|
||||
"Conversation.ChecksTooltip.Delivered" = "Delivered";
|
||||
"Conversation.ChecksTooltip.Read" = "Read";
|
||||
|
||||
|
|
@ -5996,3 +5999,5 @@ Sorry for the inconvenience.";
|
|||
|
||||
"PeerInfo.AutoremoveMessages" = "Auto-Delete Messages";
|
||||
"PeerInfo.AutoremoveMessagesDisabled" = "Never";
|
||||
|
||||
"Conversation.AutoremoveChanged" = "Auto-Delete timer set to %@";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 734518e85d769de070b5a78b234080d9580ae625
|
||||
Subproject commit b965114f31a464185318c71ef3d0c1538d0c52de
|
||||
|
|
@ -96,6 +96,7 @@ public final class PresentationCallVideoView {
|
|||
case rotation270
|
||||
}
|
||||
|
||||
public let holder: AnyObject
|
||||
public let view: UIView
|
||||
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
||||
|
||||
|
|
@ -105,6 +106,7 @@ public final class PresentationCallVideoView {
|
|||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||
|
||||
public init(
|
||||
holder: AnyObject,
|
||||
view: UIView,
|
||||
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
||||
getOrientation: @escaping () -> Orientation,
|
||||
|
|
@ -112,6 +114,7 @@ public final class PresentationCallVideoView {
|
|||
setOnOrientationUpdated: @escaping (((Orientation, CGFloat) -> Void)?) -> Void,
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||
) {
|
||||
self.holder = holder
|
||||
self.view = view
|
||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||
self.getOrientation = getOrientation
|
||||
|
|
@ -277,13 +280,15 @@ public protocol PresentationGroupCall: class {
|
|||
var internalId: CallSessionInternalId { get }
|
||||
var peerId: PeerId { get }
|
||||
|
||||
var isVideo: Bool { get }
|
||||
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
|
||||
|
||||
var canBeRemoved: Signal<Bool, NoError> { get }
|
||||
var state: Signal<PresentationGroupCallState, NoError> { get }
|
||||
var summaryState: Signal<PresentationGroupCallSummaryState?, NoError> { get }
|
||||
var members: Signal<PresentationGroupCallMembers?, NoError> { get }
|
||||
var audioLevels: Signal<[(PeerId, Float, Bool)], NoError> { get }
|
||||
var audioLevels: Signal<[(PeerId, UInt32, Float, Bool)], NoError> { get }
|
||||
var myAudioLevel: Signal<Float, NoError> { get }
|
||||
var isMuted: Signal<Bool, NoError> { get }
|
||||
|
||||
|
|
@ -293,8 +298,11 @@ public protocol PresentationGroupCall: class {
|
|||
|
||||
func toggleIsMuted()
|
||||
func setIsMuted(action: PresentationGroupCallMuteAction)
|
||||
func requestVideo()
|
||||
func disableVideo()
|
||||
func updateDefaultParticipantsAreMuted(isMuted: Bool)
|
||||
func setVolume(peerId: PeerId, volume: Int32, sync: Bool)
|
||||
func setFullSizeVideo(peerId: PeerId?)
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput)
|
||||
|
||||
func updateMuteState(peerId: PeerId, isMuted: Bool) -> GroupCallParticipantsContext.Participant.MuteState?
|
||||
|
|
@ -303,6 +311,10 @@ public protocol PresentationGroupCall: class {
|
|||
func removedPeer(_ peerId: PeerId)
|
||||
var invitedPeers: Signal<[PeerId], NoError> { get }
|
||||
|
||||
var incomingVideoSources: Signal<[PeerId: UInt32], NoError> { get }
|
||||
|
||||
func makeIncomingVideoView(source: UInt32, completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
|
||||
func loadMoreMembers(token: String)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
|||
var headerInsets = layout.insets(options: [.input])
|
||||
headerInsets.top += actualNavigationBarHeight
|
||||
|
||||
let countPanelHeight = self.countPanelNode.updateLayout(width: layout.size.width, bottomInset: layout.intrinsicInsets.bottom, transition: transition)
|
||||
let countPanelHeight = self.countPanelNode.updateLayout(width: layout.size.width, sideInset: layout.safeInsets.left, bottomInset: layout.intrinsicInsets.bottom, transition: transition)
|
||||
if self.selectionState.selectedContactIndices.isEmpty {
|
||||
transition.updateFrame(node: self.countPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: countPanelHeight)))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ final class InviteContactsCountPanelNode: ASDisplayNode {
|
|||
private let separatorNode: ASDisplayNode
|
||||
private let button: SolidRoundedButtonNode
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat)?
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
||||
|
||||
var count: Int = 0 {
|
||||
didSet {
|
||||
if self.count != oldValue && self.count > 0 {
|
||||
self.button.title = self.strings.Contacts_InviteContacts(Int32(self.count))
|
||||
|
||||
if let (width, bottomInset) = self.validLayout {
|
||||
let _ = self.updateLayout(width: width, bottomInset: bottomInset, transition: .immediate)
|
||||
if let (width, sideInset, bottomInset) = self.validLayout {
|
||||
let _ = self.updateLayout(width: width, sideInset: sideInset, bottomInset: bottomInset, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,13 +47,13 @@ final class InviteContactsCountPanelNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
func updateLayout(width: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = (width, bottomInset)
|
||||
func updateLayout(width: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = (width, sideInset, bottomInset)
|
||||
let topInset: CGFloat = 9.0
|
||||
var bottomInset = bottomInset
|
||||
bottomInset += topInset - (bottomInset.isZero ? 0.0 : 4.0)
|
||||
|
||||
let buttonInset: CGFloat = 16.0
|
||||
let buttonInset: CGFloat = 16.0 + sideInset
|
||||
let buttonWidth = width - buttonInset * 2.0
|
||||
let buttonHeight = self.button.updateLayout(width: buttonWidth, transition: transition)
|
||||
transition.updateFrame(node: self.button, frame: CGRect(x: buttonInset, y: topInset, width: buttonWidth, height: buttonHeight))
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@ import AsyncDisplayKit
|
|||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
|
||||
private let textFont = Font.regular(13.0)
|
||||
private let selectedTextFont = Font.bold(13.0)
|
||||
|
||||
public final class DatePickerTheme: Equatable {
|
||||
public let backgroundColor: UIColor
|
||||
public let textColor: UIColor
|
||||
|
|
@ -15,18 +12,16 @@ public final class DatePickerTheme: Equatable {
|
|||
public let accentColor: UIColor
|
||||
public let disabledColor: UIColor
|
||||
public let selectionColor: UIColor
|
||||
public let selectedCurrentTextColor: UIColor
|
||||
public let secondarySelectionColor: UIColor
|
||||
public let selectionTextColor: UIColor
|
||||
|
||||
public init(backgroundColor: UIColor, textColor: UIColor, secondaryTextColor: UIColor, accentColor: UIColor, disabledColor: UIColor, selectionColor: UIColor, selectedCurrentTextColor: UIColor, secondarySelectionColor: UIColor) {
|
||||
public init(backgroundColor: UIColor, textColor: UIColor, secondaryTextColor: UIColor, accentColor: UIColor, disabledColor: UIColor, selectionColor: UIColor, selectionTextColor: UIColor) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.textColor = textColor
|
||||
self.secondaryTextColor = secondaryTextColor
|
||||
self.accentColor = accentColor
|
||||
self.disabledColor = disabledColor
|
||||
self.selectionColor = selectionColor
|
||||
self.selectedCurrentTextColor = selectedCurrentTextColor
|
||||
self.secondarySelectionColor = secondarySelectionColor
|
||||
self.selectionTextColor = selectionTextColor
|
||||
}
|
||||
|
||||
public static func ==(lhs: DatePickerTheme, rhs: DatePickerTheme) -> Bool {
|
||||
|
|
@ -45,31 +40,26 @@ public final class DatePickerTheme: Equatable {
|
|||
if lhs.selectionColor != rhs.selectionColor {
|
||||
return false
|
||||
}
|
||||
if lhs.selectedCurrentTextColor != rhs.selectedCurrentTextColor {
|
||||
return false
|
||||
}
|
||||
if lhs.secondarySelectionColor != rhs.secondarySelectionColor {
|
||||
if lhs.selectionTextColor != rhs.selectionTextColor {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
//public extension DatePickerTheme {
|
||||
// convenience init(theme: PresentationTheme) {
|
||||
// self.init(backgroundColor: theme.rootController.navigationBar.segmentedBackgroundColor, foregroundColor: theme.rootController.navigationBar.segmentedForegroundColor, shadowColor: .black, textColor: theme.rootController.navigationBar.segmentedTextColor, dividerColor: theme.rootController.navigationBar.segmentedDividerColor)
|
||||
// }
|
||||
//}
|
||||
|
||||
private class SegmentedControlItemNode: HighlightTrackingButtonNode {
|
||||
public extension DatePickerTheme {
|
||||
convenience init(theme: PresentationTheme) {
|
||||
self.init(backgroundColor: theme.list.itemBlocksBackgroundColor, textColor: theme.list.itemPrimaryTextColor, secondaryTextColor: theme.list.itemSecondaryTextColor, accentColor: theme.list.itemAccentColor, disabledColor: theme.list.itemDisabledTextColor, selectionColor: theme.list.itemCheckColors.fillColor, selectionTextColor: theme.list.itemCheckColors.foregroundColor)
|
||||
}
|
||||
}
|
||||
|
||||
private let telegramReleaseDate = Date(timeIntervalSince1970: 1376438400.0)
|
||||
private let upperLimitDate = Date(timeIntervalSince1970: Double(Int32.max - 1))
|
||||
|
||||
private let controlFont = Font.regular(17.0)
|
||||
private let dayFont = Font.regular(13.0)
|
||||
private let dateFont = Font.with(size: 13.0, design: .regular, traits: .monospacedNumbers)
|
||||
private let selectedDateFont = Font.bold(13.0)
|
||||
private let dateFont = Font.with(size: 17.0, design: .regular, traits: .monospacedNumbers)
|
||||
private let selectedDateFont = Font.with(size: 17.0, design: .regular, traits: [.bold, .monospacedNumbers])
|
||||
|
||||
private let calendar = Calendar(identifier: .gregorian)
|
||||
|
||||
|
|
@ -81,12 +71,46 @@ private func monthForDate(_ date: Date) -> Date {
|
|||
return calendar.date(from: components)!
|
||||
}
|
||||
|
||||
public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private func generateSmallArrowImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 7.0, height: 12.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.setLineWidth(2.0)
|
||||
context.setLineCap(.round)
|
||||
context.beginPath()
|
||||
context.move(to: CGPoint(x: 1.0, y: 1.0))
|
||||
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height / 2.0))
|
||||
context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0))
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
||||
|
||||
private func generateNavigationArrowImage(color: UIColor, mirror: Bool) -> UIImage? {
|
||||
return generateImage(CGSize(width: 10.0, height: 17.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.setLineWidth(2.0)
|
||||
context.setLineCap(.round)
|
||||
context.beginPath()
|
||||
if mirror {
|
||||
context.translateBy(x: 5.0, y: 8.5)
|
||||
context.scaleBy(x: -1.0, y: 1.0)
|
||||
context.translateBy(x: -5.0, y: -8.5)
|
||||
}
|
||||
context.move(to: CGPoint(x: 1.0, y: 1.0))
|
||||
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height / 2.0))
|
||||
context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0))
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
||||
|
||||
public final class DatePickerNode: ASDisplayNode {
|
||||
class MonthNode: ASDisplayNode {
|
||||
private let month: Date
|
||||
|
||||
var theme: DatePickerTheme {
|
||||
didSet {
|
||||
self.selectionNode.image = generateStretchableFilledCircleImage(diameter: 44.0, color: self.theme.selectionColor)
|
||||
if let size = self.validSize {
|
||||
self.updateLayout(size: size)
|
||||
}
|
||||
|
|
@ -136,6 +160,7 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.selectionNode = ASImageNode()
|
||||
self.selectionNode.displaysAsynchronously = false
|
||||
self.selectionNode.displayWithoutProcessing = true
|
||||
self.selectionNode.image = generateStretchableFilledCircleImage(diameter: 44.0, color: theme.selectionColor)
|
||||
|
||||
self.dateNodes = (0..<42).map { _ in ImmediateTextNode() }
|
||||
|
||||
|
|
@ -147,7 +172,7 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.numberOfDays = calendar.range(of: .day, in: .month, for: month)!.count
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.addSubnode(self.selectionNode)
|
||||
self.dateNodes.forEach { self.addSubnode($0) }
|
||||
}
|
||||
|
|
@ -157,6 +182,10 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
var started = false
|
||||
var count = 0
|
||||
|
||||
let sideInset: CGFloat = 12.0
|
||||
let cellSize: CGFloat = floor((size.width - sideInset * 2.0) / 7.0)
|
||||
|
||||
self.selectionNode.isHidden = true
|
||||
for i in 0 ..< 42 {
|
||||
let row: Int = Int(floor(Float(i) / 7.0))
|
||||
let col: Int = i % 7
|
||||
|
|
@ -164,49 +193,55 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
if !started && weekday == self.startWeekday {
|
||||
started = true
|
||||
}
|
||||
weekday += 1
|
||||
if started {
|
||||
count += 1
|
||||
|
||||
var isAvailableDate = true
|
||||
var components = calendar.dateComponents([.year, .month], from: self.month)
|
||||
components.day = count
|
||||
components.hour = 0
|
||||
components.minute = 0
|
||||
let date = calendar.date(from: components)!
|
||||
|
||||
if let minimumDate = self.minimumDate {
|
||||
var components = calendar.dateComponents([.year, .month], from: self.month)
|
||||
components.day = count
|
||||
components.hour = 0
|
||||
components.minute = 0
|
||||
let date = calendar.date(from: components)!
|
||||
if date < minimumDate {
|
||||
isAvailableDate = false
|
||||
}
|
||||
}
|
||||
if let maximumDate = self.maximumDate {
|
||||
var components = calendar.dateComponents([.year, .month], from: self.month)
|
||||
components.day = count
|
||||
components.hour = 0
|
||||
components.minute = 0
|
||||
let date = calendar.date(from: components)!
|
||||
if date > maximumDate {
|
||||
isAvailableDate = false
|
||||
}
|
||||
}
|
||||
var isSelectedDate = false
|
||||
var isSelectedAndCurrentDate = false
|
||||
|
||||
let isToday = calendar.isDateInToday(date)
|
||||
let isSelected = self.date.flatMap { calendar.isDate(date, equalTo: $0, toGranularity: .day) } ?? false
|
||||
|
||||
let color: UIColor
|
||||
if !isAvailableDate {
|
||||
color = self.theme.disabledColor
|
||||
} else if isSelectedAndCurrentDate {
|
||||
color = .white
|
||||
} else if isSelectedDate {
|
||||
if isSelected {
|
||||
color = self.theme.selectionTextColor
|
||||
} else if isToday {
|
||||
color = self.theme.accentColor
|
||||
} else if !isAvailableDate {
|
||||
color = self.theme.disabledColor
|
||||
} else {
|
||||
color = self.theme.textColor
|
||||
}
|
||||
|
||||
let textNode = self.dateNodes[i]
|
||||
textNode.attributedText = NSAttributedString(string: "\(count)", font: dateFont, textColor: color)
|
||||
|
||||
textNode.attributedText = NSAttributedString(string: "\(count)", font: isSelected ? selectedDateFont : dateFont, textColor: color)
|
||||
|
||||
let textSize = textNode.updateLayout(size)
|
||||
textNode.frame = CGRect(origin: CGPoint(x: CGFloat(col) * 20.0, y: CGFloat(row) * 20.0), size: textSize)
|
||||
|
||||
let cellFrame = CGRect(x: sideInset + CGFloat(col) * cellSize, y: 0.0 + CGFloat(row) * cellSize, width: cellSize, height: cellSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: cellFrame.minX + floor((cellFrame.width - textSize.width) / 2.0), y: cellFrame.minY + floor((cellFrame.height - textSize.height) / 2.0)), size: textSize)
|
||||
textNode.frame = textFrame
|
||||
|
||||
if isSelected {
|
||||
self.selectionNode.isHidden = false
|
||||
let selectionSize = CGSize(width: 44.0, height: 44.0)
|
||||
self.selectionNode.frame = CGRect(origin: CGPoint(x: cellFrame.minX + floor((cellFrame.width - selectionSize.width) / 2.0), y: cellFrame.minY + floor((cellFrame.height - selectionSize.height) / 2.0)), size: selectionSize)
|
||||
}
|
||||
|
||||
if count == self.numberOfDays {
|
||||
break
|
||||
|
|
@ -232,40 +267,75 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
|
||||
private let timeTitleNode: ImmediateTextNode
|
||||
private let timeFieldNode: ASImageNode
|
||||
|
||||
private let dayNodes: [ImmediateTextNode]
|
||||
private var currentIndex = 0
|
||||
private var months: [Date] = []
|
||||
private var monthNodes: [Date: MonthNode] = [:]
|
||||
private let contentNode: ASDisplayNode
|
||||
|
||||
private let pickerBackgroundNode: ASDisplayNode
|
||||
private var pickerNode: MonthPickerNode
|
||||
|
||||
private let monthButtonNode: HighlightTrackingButtonNode
|
||||
private let monthTextNode: ImmediateTextNode
|
||||
private let monthArrowNode: ASImageNode
|
||||
|
||||
private let previousButtonNode: HighlightableButtonNode
|
||||
private let nextButtonNode: HighlightableButtonNode
|
||||
|
||||
private let dayNodes: [ImmediateTextNode]
|
||||
private var previousMonthNode: MonthNode?
|
||||
private var currentMonthNode: MonthNode?
|
||||
private var nextMonthNode: MonthNode?
|
||||
private let scrollNode: ASScrollNode
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
||||
private var gestureRecognizer: UIPanGestureRecognizer?
|
||||
private var gestureSelectedIndex: Int?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
public var maximumDate: Date? {
|
||||
didSet {
|
||||
|
||||
public var maximumDate: Date {
|
||||
get {
|
||||
return self.state.maxDate
|
||||
}
|
||||
}
|
||||
public var minimumDate: Date = telegramReleaseDate {
|
||||
didSet {
|
||||
|
||||
}
|
||||
}
|
||||
public var date: Date = Date() {
|
||||
didSet {
|
||||
guard self.date != oldValue else {
|
||||
set {
|
||||
guard newValue != self.maximumDate else {
|
||||
return
|
||||
}
|
||||
|
||||
let updatedState = State(minDate: self.state.minDate, maxDate: newValue, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
|
||||
self.updateState(updatedState, animated: false)
|
||||
|
||||
if let size = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
public var minimumDate: Date {
|
||||
get {
|
||||
return self.state.minDate
|
||||
}
|
||||
set {
|
||||
guard newValue != self.minimumDate else {
|
||||
return
|
||||
}
|
||||
|
||||
let updatedState = State(minDate: newValue, maxDate: self.state.maxDate, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
|
||||
self.updateState(updatedState, animated: false)
|
||||
|
||||
if let size = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
public var date: Date {
|
||||
get {
|
||||
return self.state.date
|
||||
}
|
||||
set {
|
||||
guard newValue != self.date else {
|
||||
return
|
||||
}
|
||||
|
||||
let updatedState = State(minDate: self.state.minDate, maxDate: self.state.maxDate, date: newValue, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
|
||||
self.updateState(updatedState, animated: false)
|
||||
|
||||
if let size = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
|
|
@ -282,33 +352,74 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.timeFieldNode.displaysAsynchronously = false
|
||||
self.timeFieldNode.displayWithoutProcessing = true
|
||||
|
||||
self.dayNodes = (0..<7).map { _ in ImmediateTextNode() }
|
||||
|
||||
self.contentNode = ASDisplayNode()
|
||||
|
||||
self.pickerBackgroundNode = ASDisplayNode()
|
||||
self.pickerBackgroundNode.alpha = 0.0
|
||||
self.pickerBackgroundNode.backgroundColor = theme.backgroundColor
|
||||
self.pickerBackgroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.pickerNode = MonthPickerNode(theme: theme, strings: strings, date: self.state.date, yearRange: 2013 ..< 2038, valueChanged: { date in
|
||||
|
||||
})
|
||||
|
||||
self.monthButtonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.monthTextNode = ImmediateTextNode()
|
||||
|
||||
self.monthArrowNode = ASImageNode()
|
||||
self.monthArrowNode.displaysAsynchronously = false
|
||||
self.monthArrowNode.displayWithoutProcessing = true
|
||||
|
||||
self.previousButtonNode = HighlightableButtonNode()
|
||||
self.previousButtonNode.hitTestSlop = UIEdgeInsets(top: -6.0, left: -10.0, bottom: -6.0, right: -10.0)
|
||||
self.nextButtonNode = HighlightableButtonNode()
|
||||
|
||||
self.dayNodes = (0..<7).map { _ in ImmediateTextNode() }
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.nextButtonNode.hitTestSlop = UIEdgeInsets(top: -6.0, left: -10.0, bottom: -6.0, right: -10.0)
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
self.dayNodes.forEach { self.addSubnode($0) }
|
||||
|
||||
self.addSubnode(self.previousButtonNode)
|
||||
self.addSubnode(self.nextButtonNode)
|
||||
|
||||
self.addSubnode(self.pickerBackgroundNode)
|
||||
self.pickerBackgroundNode.addSubnode(self.pickerNode)
|
||||
|
||||
self.addSubnode(self.monthTextNode)
|
||||
self.addSubnode(self.monthArrowNode)
|
||||
self.addSubnode(self.monthButtonNode)
|
||||
|
||||
self.addSubnode(self.previousButtonNode)
|
||||
self.addSubnode(self.nextButtonNode)
|
||||
self.monthArrowNode.image = generateSmallArrowImage(color: theme.accentColor)
|
||||
self.previousButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: true), for: .normal)
|
||||
self.nextButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: false), for: .normal)
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.setupItems()
|
||||
|
||||
self.monthButtonNode.addTarget(self, action: #selector(self.monthButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.monthButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.monthTextNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.monthTextNode.alpha = 0.4
|
||||
strongSelf.monthArrowNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.monthArrowNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.monthTextNode.alpha = 1.0
|
||||
strongSelf.monthTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
strongSelf.monthArrowNode.alpha = 1.0
|
||||
strongSelf.monthArrowNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.previousButtonNode.addTarget(self, action: #selector(self.previousButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.nextButtonNode.addTarget(self, action: #selector(self.nextButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
|
|
@ -316,15 +427,62 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
|
||||
self.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
|
||||
self.scrollNode.view.isPagingEnabled = true
|
||||
self.scrollNode.view.delegate = self
|
||||
self.contentNode.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||
self.contentNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
}
|
||||
|
||||
private func updateState(_ state: State, animated: Bool) {
|
||||
let previousState = self.state
|
||||
self.state = state
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
||||
|
||||
if previousState.minDate != state.minDate || previousState.maxDate != state.maxDate {
|
||||
self.setupItems()
|
||||
} else if previousState.selectedMonth != state.selectedMonth {
|
||||
for i in 0 ..< self.months.count {
|
||||
if self.months[i].timeIntervalSince1970 > state.selectedMonth.timeIntervalSince1970 {
|
||||
self.currentIndex = max(0, min(self.months.count - 1, i - 1))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .spring) : .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupItems() {
|
||||
let startMonth = monthForDate(self.state.minDate)
|
||||
let endMonth = monthForDate(self.state.maxDate)
|
||||
let selectedMonth = monthForDate(self.state.selectedMonth)
|
||||
|
||||
let calendar = Calendar.current
|
||||
|
||||
var currentIndex = 0
|
||||
|
||||
var months: [Date] = [startMonth]
|
||||
var index = 1
|
||||
|
||||
var nextMonth = startMonth
|
||||
while true {
|
||||
if let month = calendar.date(byAdding: .month, value: 1, to: nextMonth) {
|
||||
nextMonth = month
|
||||
if nextMonth == selectedMonth {
|
||||
currentIndex = index
|
||||
}
|
||||
if nextMonth >= endMonth {
|
||||
break
|
||||
} else {
|
||||
months.append(nextMonth)
|
||||
}
|
||||
index += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.months = months
|
||||
self.currentIndex = currentIndex
|
||||
}
|
||||
|
||||
public func updateTheme(_ theme: DatePickerTheme) {
|
||||
|
|
@ -334,55 +492,262 @@ public final class DatePickerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.theme = theme
|
||||
|
||||
self.backgroundColor = self.theme.backgroundColor
|
||||
}
|
||||
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.view.window?.endEditing(true)
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if !decelerate {
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
self.monthArrowNode.image = generateSmallArrowImage(color: theme.accentColor)
|
||||
self.previousButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: true), for: .normal)
|
||||
self.nextButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: false), for: .normal)
|
||||
|
||||
for (_, monthNode) in self.monthNodes {
|
||||
monthNode.theme = theme
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: .immediate)
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
self.view.window?.endEditing(true)
|
||||
case .changed:
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var transitionFraction = translation.x / self.bounds.width
|
||||
if self.currentIndex <= 0 {
|
||||
transitionFraction = min(0.0, transitionFraction)
|
||||
}
|
||||
if self.currentIndex >= self.months.count - 1 {
|
||||
transitionFraction = max(0.0, transitionFraction)
|
||||
}
|
||||
self.transitionFraction = transitionFraction
|
||||
if let size = self.validLayout {
|
||||
let topInset: CGFloat = 78.0
|
||||
let containerSize = CGSize(width: size.width, height: size.height - topInset)
|
||||
self.updateItems(size: containerSize, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
let velocity = recognizer.velocity(in: self.view)
|
||||
var directionIsToRight: Bool?
|
||||
if abs(velocity.x) > 10.0 {
|
||||
directionIsToRight = velocity.x < 0.0
|
||||
} else if abs(self.transitionFraction) > 0.5 {
|
||||
directionIsToRight = self.transitionFraction < 0.0
|
||||
}
|
||||
var updatedIndex = self.currentIndex
|
||||
if let directionIsToRight = directionIsToRight {
|
||||
if directionIsToRight {
|
||||
updatedIndex = min(updatedIndex + 1, self.months.count - 1)
|
||||
} else {
|
||||
updatedIndex = max(updatedIndex - 1, 0)
|
||||
}
|
||||
}
|
||||
self.currentIndex = updatedIndex
|
||||
self.transitionFraction = 0.0
|
||||
|
||||
let updatedState = State(minDate: self.state.minDate, maxDate: self.state.maxDate, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.months[updatedIndex])
|
||||
self.updateState(updatedState, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func updateItems(size: CGSize, update: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||
var validIds: [Date] = []
|
||||
|
||||
if self.currentIndex >= 0 && self.currentIndex < self.months.count {
|
||||
let preloadSpan: Int = 1
|
||||
for i in max(0, self.currentIndex - preloadSpan) ... min(self.currentIndex + preloadSpan, self.months.count - 1) {
|
||||
validIds.append(self.months[i])
|
||||
var itemNode: MonthNode?
|
||||
var wasAdded = false
|
||||
if let current = self.monthNodes[self.months[i]] {
|
||||
itemNode = current
|
||||
current.updateLayout(size: size)
|
||||
} else {
|
||||
wasAdded = true
|
||||
let addedItemNode = MonthNode(theme: self.theme, month: self.months[i], minimumDate: self.minimumDate, maximumDate: self.maximumDate, date: self.date)
|
||||
itemNode = addedItemNode
|
||||
self.monthNodes[self.months[i]] = addedItemNode
|
||||
self.contentNode.addSubnode(addedItemNode)
|
||||
}
|
||||
if let itemNode = itemNode {
|
||||
let indexOffset = CGFloat(i - self.currentIndex)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width, y: 0.0), size: size)
|
||||
|
||||
if wasAdded {
|
||||
itemNode.frame = itemFrame
|
||||
itemNode.updateLayout(size: size)
|
||||
} else {
|
||||
transition.updateFrame(node: itemNode, frame: itemFrame)
|
||||
itemNode.updateLayout(size: size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var removeIds: [Date] = []
|
||||
for (id, _) in self.monthNodes {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
if let itemNode = self.monthNodes.removeValue(forKey: id) {
|
||||
itemNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
let topInset: CGFloat = 60.0
|
||||
let topInset: CGFloat = 78.0
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
let scrollSize = CGSize(width: size.width, height: size.height - topInset)
|
||||
self.scrollNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: scrollSize)
|
||||
self.scrollNode.view.contentSize = CGSize(width: scrollSize.width * 3.0, height: scrollSize.height)
|
||||
self.scrollNode.view.contentOffset = CGPoint(x: scrollSize.width, y: 0.0)
|
||||
let month = monthForDate(self.state.selectedMonth)
|
||||
let components = calendar.dateComponents([.month, .year], from: month)
|
||||
|
||||
self.monthTextNode.attributedText = NSAttributedString(string: stringForMonth(strings: self.strings, month: components.month.flatMap { Int32($0) - 1 } ?? 0, ofYear: components.year.flatMap { Int32($0) - 1900 } ?? 100), font: controlFont, textColor: theme.textColor)
|
||||
let monthSize = self.monthTextNode.updateLayout(size)
|
||||
|
||||
let monthTextFrame = CGRect(x: sideInset, y: 10.0, width: monthSize.width, height: monthSize.height)
|
||||
self.monthTextNode.frame = monthTextFrame
|
||||
self.monthArrowNode.frame = CGRect(x: monthTextFrame.maxX + 10.0, y: monthTextFrame.minY + 4.0, width: 7.0, height: 12.0)
|
||||
self.monthButtonNode.frame = monthTextFrame.inset(by: UIEdgeInsets(top: -6.0, left: -6.0, bottom: -6.0, right: -30.0))
|
||||
|
||||
self.previousButtonNode.frame = CGRect(x: size.width - sideInset - 54.0, y: monthTextFrame.minY + 1.0, width: 10.0, height: 17.0)
|
||||
self.nextButtonNode.frame = CGRect(x: size.width - sideInset - 13.0, y: monthTextFrame.minY + 1.0, width: 10.0, height: 17.0)
|
||||
|
||||
let daysSideInset: CGFloat = 12.0
|
||||
let cellSize: CGFloat = floor((size.width - daysSideInset * 2.0) / 7.0)
|
||||
|
||||
for i in 0 ..< self.dayNodes.count {
|
||||
let dayNode = self.dayNodes[i]
|
||||
dayNode.attributedText = NSAttributedString(string: shortStringForDayOfWeek(strings: self.strings, day: Int32(i)).uppercased(), font: dayFont, textColor: theme.secondaryTextColor)
|
||||
|
||||
let day = Int32(i)
|
||||
dayNode.attributedText = NSAttributedString(string: shortStringForDayOfWeek(strings: self.strings, day: day), font: dayFont, textColor: theme.secondaryTextColor)
|
||||
let size = dayNode.updateLayout(size)
|
||||
dayNode.frame = CGRect(origin: CGPoint(x: CGFloat(i) * 20.0, y: 0.0), size: size)
|
||||
let textSize = dayNode.updateLayout(size)
|
||||
let cellFrame = CGRect(x: daysSideInset + CGFloat(i) * cellSize, y: 40.0, width: cellSize, height: cellSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: cellFrame.minX + floor((cellFrame.width - textSize.width) / 2.0), y: cellFrame.minY + floor((cellFrame.height - textSize.height) / 2.0)), size: textSize)
|
||||
|
||||
dayNode.frame = textFrame
|
||||
}
|
||||
|
||||
let containerSize = CGSize(width: size.width, height: size.height - topInset)
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: containerSize)
|
||||
|
||||
self.updateItems(size: containerSize, transition: transition)
|
||||
|
||||
self.pickerBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.pickerBackgroundNode.isUserInteractionEnabled = self.state.displayingMonthSelection
|
||||
transition.updateAlpha(node: self.pickerBackgroundNode, alpha: self.state.displayingMonthSelection ? 1.0 : 0.0)
|
||||
|
||||
self.pickerNode.frame = CGRect(x: sideInset, y: topInset, width: size.width - sideInset * 2.0, height: 180.0)
|
||||
}
|
||||
|
||||
@objc private func monthButtonPressed() {
|
||||
let updatedState = State(minDate: self.state.minDate, maxDate: self.state.maxDate, date: self.state.date, displayingMonthSelection: !self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
|
||||
self.updateState(updatedState, animated: true)
|
||||
}
|
||||
|
||||
@objc private func previousButtonPressed() {
|
||||
guard let month = calendar.date(byAdding: .month, value: -1, to: self.state.selectedMonth), let size = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let updatedState = State(minDate: self.state.minDate, maxDate: self.state.maxDate, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: month)
|
||||
self.updateState(updatedState, animated: false)
|
||||
|
||||
self.contentNode.layer.animatePosition(from: CGPoint(x: -size.width, y: 0.0), to: CGPoint(), duration: 0.3, additive: true)
|
||||
}
|
||||
|
||||
@objc private func nextButtonPressed() {
|
||||
guard let month = calendar.date(byAdding: .month, value: 1, to: self.state.selectedMonth), let size = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let updatedState = State(minDate: self.state.minDate, maxDate: self.state.maxDate, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: month)
|
||||
self.updateState(updatedState, animated: false)
|
||||
|
||||
self.contentNode.layer.animatePosition(from: CGPoint(x: size.width, y: 0.0), to: CGPoint(), duration: 0.3, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
private let theme: DatePickerTheme
|
||||
private let strings: PresentationStrings
|
||||
|
||||
private var date: Date
|
||||
private var yearRange: Range<Int> {
|
||||
didSet {
|
||||
self.pickerView.reloadAllComponents()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func monthButtonPressed(_ button: SegmentedControlItemNode) {
|
||||
private let valueChanged: (Date) -> Void
|
||||
private let pickerView: UIPickerView
|
||||
|
||||
init(theme: DatePickerTheme, strings: PresentationStrings, date: Date, yearRange: Range<Int>, valueChanged: @escaping (Date) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
self.date = date
|
||||
self.yearRange = yearRange
|
||||
|
||||
self.valueChanged = valueChanged
|
||||
|
||||
self.pickerView = UIPickerView()
|
||||
|
||||
super.init()
|
||||
|
||||
self.pickerView.delegate = self
|
||||
self.pickerView.dataSource = self
|
||||
self.view.addSubview(self.pickerView)
|
||||
|
||||
self.pickerView.reloadAllComponents()
|
||||
|
||||
// self.pickerView.selectRow(index, inComponent: 0, animated: false)
|
||||
}
|
||||
|
||||
@objc private func previousButtonPressed(_ button: SegmentedControlItemNode) {
|
||||
|
||||
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
return CGSize(width: constrainedSize.width, height: 180.0)
|
||||
}
|
||||
|
||||
@objc private func nextButtonPressed(_ button: SegmentedControlItemNode) {
|
||||
|
||||
func numberOfComponents(in pickerView: UIPickerView) -> Int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
if component == 1 {
|
||||
return self.yearRange.count
|
||||
} else {
|
||||
return 12
|
||||
}
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
|
||||
return 40.0
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
|
||||
let string: String
|
||||
if component == 1 {
|
||||
string = "\(self.yearRange.startIndex + row)"
|
||||
} else {
|
||||
string = stringForMonth(strings: self.strings, month: Int32(row))
|
||||
}
|
||||
return NSAttributedString(string: string, font: Font.medium(15.0), textColor: self.theme.textColor)
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||
// self.valueChanged(timeoutValues[row])
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.pickerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.bounds.size.width, height: 180.0))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public struct Font {
|
|||
}
|
||||
var updatedDescriptor: UIFontDescriptor? = descriptor.withSymbolicTraits(symbolicTraits)
|
||||
if traits.contains(.monospacedNumbers) {
|
||||
updatedDescriptor = descriptor.addingAttributes([
|
||||
updatedDescriptor = updatedDescriptor?.addingAttributes([
|
||||
UIFontDescriptor.AttributeName.featureSettings: [
|
||||
[UIFontDescriptor.FeatureKey.featureIdentifier:
|
||||
kNumberSpacingType,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
(int)sourceSampleRate,
|
||||
0,
|
||||
NULL);
|
||||
_ratio = MAX(1, destinationSampleRate / sourceSampleRate) * MAX(1, destinationChannelCount / sourceChannelCount) * 2;
|
||||
_ratio = MAX(1, destinationSampleRate / MAX(sourceSampleRate, 1)) * MAX(1, destinationChannelCount / sourceChannelCount) * 2;
|
||||
swr_init(_context);
|
||||
}
|
||||
return self;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ swift_library(
|
|||
"//submodules/RadialStatusNode:RadialStatusNode",
|
||||
"//submodules/SectionHeaderItem:SectionHeaderItem",
|
||||
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||
arguments.dismissInput()
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.pickingTimeLimit = !state.pickingTimeLimit
|
||||
// updatedState.pickingTimeLimit = !state.pickingTimeLimit
|
||||
return updatedState
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ private enum InviteLinkInviteEntry: Comparable, Identifiable {
|
|||
}, viewAction: {
|
||||
})
|
||||
case let .links(_, _, invites):
|
||||
return ItemListInviteLinkGridItem(presentationData: ItemListPresentationData(presentationData), invites: invites, share: true, sectionId: 1, style: .plain, tapAction: { invite in
|
||||
return ItemListInviteLinkGridItem(presentationData: ItemListPresentationData(presentationData), invites: invites, count: 0, share: true, sectionId: 1, style: .plain, tapAction: { invite in
|
||||
interaction.copyLink(invite)
|
||||
}, contextAction: { invite, _ in
|
||||
interaction.shareLink(invite)
|
||||
|
|
@ -218,7 +218,7 @@ public final class InviteLinkInviteController: ViewController {
|
|||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, peerId: self.peerId, controller: self)
|
||||
}
|
||||
|
||||
|
||||
private var didAppearOnce: Bool = false
|
||||
private var isDismissed: Bool = false
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
|
|
@ -288,7 +288,7 @@ public final class InviteLinkInviteController: ViewController {
|
|||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
self.controller = controller
|
||||
|
||||
self.invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: false)
|
||||
self.invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: nil, revoked: false, forceUpdate: false)
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
|
|
@ -348,16 +348,16 @@ public final class InviteLinkInviteController: ViewController {
|
|||
}
|
||||
})))
|
||||
|
||||
// items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
// }, action: { _, f in
|
||||
// f(.dismissWithoutContent)
|
||||
//
|
||||
// if let invite = invite {
|
||||
// let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
// self?.controller?.present(controller, in: .window(.root))
|
||||
// }
|
||||
// })))
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let invite = invite {
|
||||
let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
self?.controller?.present(controller, in: .window(.root))
|
||||
}
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
|
|
@ -395,7 +395,7 @@ public final class InviteLinkInviteController: ViewController {
|
|||
let shareController = ShareController(context: context, subject: .url(invite.link))
|
||||
self?.controller?.present(shareController, in: .window(.root))
|
||||
}, manageLinks: { [weak self] in
|
||||
let controller = inviteLinkListController(context: context, peerId: peerId)
|
||||
let controller = inviteLinkListController(context: context, peerId: peerId, admin: nil)
|
||||
self?.controller?.parentNavigationController?.pushViewController(controller)
|
||||
self?.controller?.dismiss()
|
||||
})
|
||||
|
|
@ -403,8 +403,7 @@ public final class InviteLinkInviteController: ViewController {
|
|||
let previousEntries = Atomic<[InviteLinkInviteEntry]?>(value: nil)
|
||||
|
||||
let peerView = context.account.postbox.peerView(id: peerId)
|
||||
let invites: Signal<PeerExportedInvitationsState, NoError> = .single(PeerExportedInvitationsState())
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), peerView, invites)
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), peerView, self.invitesContext.state)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, view, invites in
|
||||
if let strongSelf = self {
|
||||
var entries: [InviteLinkInviteEntry] = []
|
||||
|
|
@ -423,19 +422,19 @@ public final class InviteLinkInviteController: ViewController {
|
|||
entries.append(.mainLink(presentationData.theme, mainInvite))
|
||||
}
|
||||
|
||||
// let additionalInvites = invites.invitations.filter { $0.link != mainInvite?.link }
|
||||
// var index: Int32 = 0
|
||||
// for i in stride(from: 0, to: additionalInvites.endIndex, by: 2) {
|
||||
// var invitesPair: [ExportedInvitation] = []
|
||||
// invitesPair.append(additionalInvites[i])
|
||||
// if i + 1 < additionalInvites.count {
|
||||
// invitesPair.append(additionalInvites[i + 1])
|
||||
// }
|
||||
// entries.append(.links(index, presentationData.theme, invitesPair))
|
||||
// index += 1
|
||||
// }
|
||||
let additionalInvites = invites.invitations.filter { $0.link != mainInvite?.link }
|
||||
var index: Int32 = 0
|
||||
for i in stride(from: 0, to: additionalInvites.endIndex, by: 2) {
|
||||
var invitesPair: [ExportedInvitation] = []
|
||||
invitesPair.append(additionalInvites[i])
|
||||
if i + 1 < additionalInvites.count {
|
||||
invitesPair.append(additionalInvites[i + 1])
|
||||
}
|
||||
entries.append(.links(index, presentationData.theme, invitesPair))
|
||||
index += 1
|
||||
}
|
||||
|
||||
// entries.append(.manage(presentationData.theme, presentationData.strings.InviteLink_Manage, additionalInvites.isEmpty))
|
||||
entries.append(.manage(presentationData.theme, presentationData.strings.InviteLink_Manage, additionalInvites.isEmpty))
|
||||
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import AppBundle
|
|||
import ContextUI
|
||||
import TelegramStringFormatting
|
||||
import ItemListPeerActionItem
|
||||
import ItemListPeerItem
|
||||
import ShareController
|
||||
import UndoUI
|
||||
|
||||
|
|
@ -30,9 +31,10 @@ private final class InviteLinkListControllerArguments {
|
|||
let createLink: () -> Void
|
||||
let openLink: (ExportedInvitation) -> Void
|
||||
let linkContextAction: (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void
|
||||
let openAdmin: (ExportedInvitationCreator) -> Void
|
||||
let deleteAllRevokedLinks: () -> Void
|
||||
|
||||
init(context: AccountContext, shareMainLink: @escaping (ExportedInvitation) -> Void, openMainLink: @escaping (ExportedInvitation) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, mainLinkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, createLink: @escaping () -> Void, openLink: @escaping (ExportedInvitation?) -> Void, linkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, deleteAllRevokedLinks: @escaping () -> Void) {
|
||||
init(context: AccountContext, shareMainLink: @escaping (ExportedInvitation) -> Void, openMainLink: @escaping (ExportedInvitation) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, mainLinkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, createLink: @escaping () -> Void, openLink: @escaping (ExportedInvitation?) -> Void, linkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, openAdmin: @escaping (ExportedInvitationCreator) -> Void, deleteAllRevokedLinks: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.shareMainLink = shareMainLink
|
||||
self.openMainLink = openMainLink
|
||||
|
|
@ -41,6 +43,7 @@ private final class InviteLinkListControllerArguments {
|
|||
self.createLink = createLink
|
||||
self.openLink = openLink
|
||||
self.linkContextAction = linkContextAction
|
||||
self.openAdmin = openAdmin
|
||||
self.deleteAllRevokedLinks = deleteAllRevokedLinks
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +52,7 @@ private enum InviteLinksListSection: Int32 {
|
|||
case header
|
||||
case mainLink
|
||||
case links
|
||||
case admins
|
||||
case revokedLinks
|
||||
}
|
||||
|
||||
|
|
@ -57,11 +61,16 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
|
||||
case mainLinkHeader(PresentationTheme, String)
|
||||
case mainLink(PresentationTheme, ExportedInvitation?, [Peer], Int32, Bool)
|
||||
case mainLinkOtherInfo(PresentationTheme, String)
|
||||
|
||||
case linksHeader(PresentationTheme, String)
|
||||
case linksCreate(PresentationTheme, String)
|
||||
case links(Int32, PresentationTheme, [ExportedInvitation]?)
|
||||
case links(Int32, PresentationTheme, [ExportedInvitation]?, Int)
|
||||
case linksInfo(PresentationTheme, String)
|
||||
|
||||
case adminsHeader(PresentationTheme, String)
|
||||
case admin(Int32, PresentationTheme, ExportedInvitationCreator)
|
||||
|
||||
case revokedLinksHeader(PresentationTheme, String)
|
||||
case revokedLinksDeleteAll(PresentationTheme, String)
|
||||
case revokedLinks(Int32, PresentationTheme, [ExportedInvitation]?)
|
||||
|
|
@ -70,10 +79,12 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
switch self {
|
||||
case .header:
|
||||
return InviteLinksListSection.header.rawValue
|
||||
case .mainLinkHeader, .mainLink:
|
||||
case .mainLinkHeader, .mainLink, .mainLinkOtherInfo:
|
||||
return InviteLinksListSection.mainLink.rawValue
|
||||
case .linksHeader, .linksCreate, .links, .linksInfo:
|
||||
return InviteLinksListSection.links.rawValue
|
||||
case .adminsHeader, .admin:
|
||||
return InviteLinksListSection.admins.rawValue
|
||||
case .revokedLinksHeader, .revokedLinksDeleteAll, .revokedLinks:
|
||||
return InviteLinksListSection.revokedLinks.rawValue
|
||||
}
|
||||
|
|
@ -87,20 +98,26 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
return 1
|
||||
case .mainLink:
|
||||
return 2
|
||||
case .linksHeader:
|
||||
case .mainLinkOtherInfo:
|
||||
return 3
|
||||
case .linksCreate:
|
||||
case .linksHeader:
|
||||
return 4
|
||||
case let .links(index, _, _):
|
||||
return 5 + index
|
||||
case .linksCreate:
|
||||
return 5
|
||||
case let .links(index, _, _, _):
|
||||
return 6 + index
|
||||
case .linksInfo:
|
||||
return 10000
|
||||
case .revokedLinksHeader:
|
||||
case .adminsHeader:
|
||||
return 10001
|
||||
case let .admin(index, _, _):
|
||||
return 10002 + index
|
||||
case .revokedLinksHeader:
|
||||
return 20001
|
||||
case .revokedLinksDeleteAll:
|
||||
return 10002
|
||||
return 20002
|
||||
case let .revokedLinks(index, _, _):
|
||||
return 10003 + index
|
||||
return 20003 + index
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +141,12 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .mainLinkOtherInfo(lhsTheme, lhsText):
|
||||
if case let .mainLinkOtherInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .linksHeader(lhsTheme, lhsText):
|
||||
if case let .linksHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
|
|
@ -136,8 +159,8 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .links(lhsIndex, lhsTheme, lhsLinks):
|
||||
if case let .links(rhsIndex, rhsTheme, rhsLinks) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsLinks == rhsLinks {
|
||||
case let .links(lhsIndex, lhsTheme, lhsLinks, lhsCount):
|
||||
if case let .links(rhsIndex, rhsTheme, rhsLinks, rhsCount) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsLinks == rhsLinks, lhsCount == rhsCount {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
|
@ -148,6 +171,18 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .adminsHeader(lhsTheme, lhsText):
|
||||
if case let .adminsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .admin(lhsIndex, lhsTheme, lhsCreator):
|
||||
if case let .admin(rhsIndex, rhsTheme, rhsCreator) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsCreator == rhsCreator {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .revokedLinksHeader(lhsTheme, lhsText):
|
||||
if case let .revokedLinksHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
|
|
@ -196,20 +231,28 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
arguments.openLink(invite)
|
||||
}
|
||||
})
|
||||
case let .mainLinkOtherInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: nil, style: .blocks, tag: nil)
|
||||
case let .linksHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .linksCreate(theme, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, hasSeparator: false, sectionId: self.section, editing: false, action: {
|
||||
arguments.createLink()
|
||||
})
|
||||
case let .links(_, _, invites):
|
||||
return ItemListInviteLinkGridItem(presentationData: presentationData, invites: invites, share: false, sectionId: self.section, style: .blocks, tapAction: { invite in
|
||||
case let .links(_, _, invites, count):
|
||||
return ItemListInviteLinkGridItem(presentationData: presentationData, invites: invites, count: count, share: false, sectionId: self.section, style: .blocks, tapAction: { invite in
|
||||
arguments.openLink(invite)
|
||||
}, contextAction: { invite, node in
|
||||
arguments.linkContextAction(invite, node, nil)
|
||||
})
|
||||
case let .linksInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .adminsHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .admin(_, _, creator):
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .monthFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: "."), nameDisplayOrder: .firstLast, context: arguments.context, peer: creator.peer.peer!, height: .peerList, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .none, label: .disclosure("\(creator.count)"), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
|
||||
arguments.openAdmin(creator)
|
||||
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil)
|
||||
case let .revokedLinksHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .revokedLinksDeleteAll(theme, text):
|
||||
|
|
@ -217,7 +260,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
arguments.deleteAllRevokedLinks()
|
||||
})
|
||||
case let .revokedLinks(_, _, invites):
|
||||
return ItemListInviteLinkGridItem(presentationData: presentationData, invites: invites, share: false, sectionId: self.section, style: .blocks, tapAction: { invite in
|
||||
return ItemListInviteLinkGridItem(presentationData: presentationData, invites: invites, count: 0, share: false, sectionId: self.section, style: .blocks, tapAction: { invite in
|
||||
arguments.openLink(invite)
|
||||
}, contextAction: { invite, node in
|
||||
arguments.linkContextAction(invite, node, nil)
|
||||
|
|
@ -226,22 +269,23 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||
}
|
||||
}
|
||||
|
||||
private func inviteLinkListControllerEntries(presentationData: PresentationData, view: PeerView, invites: [ExportedInvitation]?, revokedInvites: [ExportedInvitation]?, importers: PeerInvitationImportersState?) -> [InviteLinksListEntry] {
|
||||
private func inviteLinkListControllerEntries(presentationData: PresentationData, view: PeerView, invites: [ExportedInvitation]?, revokedInvites: [ExportedInvitation]?, importers: PeerInvitationImportersState?, creators: [ExportedInvitationCreator], admin: ExportedInvitationCreator?) -> [InviteLinksListEntry] {
|
||||
var entries: [InviteLinksListEntry] = []
|
||||
|
||||
entries.append(.header(presentationData.theme, presentationData.strings.InviteLink_CreatePrivateLinkHelp))
|
||||
|
||||
|
||||
if admin == nil {
|
||||
entries.append(.header(presentationData.theme, presentationData.strings.InviteLink_CreatePrivateLinkHelp))
|
||||
}
|
||||
|
||||
let mainInvite: ExportedInvitation?
|
||||
var isPublic = false
|
||||
if let peer = peerViewMainPeer(view), let address = peer.addressName, !address.isEmpty {
|
||||
if let peer = peerViewMainPeer(view), let address = peer.addressName, !address.isEmpty && admin == nil {
|
||||
mainInvite = ExportedInvitation(link: "t.me/\(address)", isPermanent: true, isRevoked: false, adminId: PeerId(0), date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil)
|
||||
isPublic = true
|
||||
} else if let invites = invites, let invite = invites.first(where: { $0.isPermanent && !$0.isRevoked }) {
|
||||
mainInvite = invite
|
||||
} else if let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation {
|
||||
} else if let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation, admin == nil {
|
||||
mainInvite = invite
|
||||
} else if let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation {
|
||||
} else if let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation, admin == nil {
|
||||
mainInvite = invite
|
||||
} else {
|
||||
mainInvite = nil
|
||||
|
|
@ -259,10 +303,15 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
|
|||
}
|
||||
|
||||
entries.append(.mainLink(presentationData.theme, mainInvite, importers?.importers.prefix(3).compactMap { $0.peer.peer } ?? [], importersCount, isPublic))
|
||||
if let adminPeer = admin?.peer.peer, let peer = peerViewMainPeer(view) {
|
||||
let string = presentationData.strings.InviteLink_OtherPermanentLinkInfo(adminPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))
|
||||
entries.append(.mainLinkOtherInfo(presentationData.theme, string.0))
|
||||
}
|
||||
|
||||
entries.append(.linksHeader(presentationData.theme, presentationData.strings.InviteLink_AdditionalLinks.uppercased()))
|
||||
entries.append(.linksCreate(presentationData.theme, presentationData.strings.InviteLink_Create))
|
||||
|
||||
if admin == nil {
|
||||
entries.append(.linksCreate(presentationData.theme, presentationData.strings.InviteLink_Create))
|
||||
}
|
||||
var additionalInvites: [ExportedInvitation]?
|
||||
if let invites = invites {
|
||||
additionalInvites = invites.filter { $0.link != mainInvite?.link }
|
||||
|
|
@ -275,16 +324,34 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
|
|||
if i + 1 < additionalInvites.count {
|
||||
invitesPair.append(additionalInvites[i + 1])
|
||||
}
|
||||
entries.append(.links(index, presentationData.theme, invitesPair))
|
||||
entries.append(.links(index, presentationData.theme, invitesPair, invitesPair.count))
|
||||
index += 1
|
||||
}
|
||||
} else if let admin = admin {
|
||||
var index: Int32 = 0
|
||||
for _ in stride(from: 0, to: admin.count, by: 2) {
|
||||
entries.append(.links(index, presentationData.theme, nil, 1))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
entries.append(.linksInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
|
||||
if !creators.isEmpty {
|
||||
entries.append(.adminsHeader(presentationData.theme, presentationData.strings.InviteLink_OtherAdminsLinks.uppercased()))
|
||||
var index: Int32 = 0
|
||||
for creator in creators {
|
||||
if let _ = creator.peer.peer {
|
||||
entries.append(.admin(index, presentationData.theme, creator))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let revokedInvites = revokedInvites, !revokedInvites.isEmpty {
|
||||
entries.append(.revokedLinksHeader(presentationData.theme, presentationData.strings.InviteLink_RevokedLinks.uppercased()))
|
||||
entries.append(.revokedLinksDeleteAll(presentationData.theme, presentationData.strings.InviteLink_DeleteAllRevokedLinks))
|
||||
|
||||
if admin == nil {
|
||||
entries.append(.revokedLinksDeleteAll(presentationData.theme, presentationData.strings.InviteLink_DeleteAllRevokedLinks))
|
||||
}
|
||||
var index: Int32 = 0
|
||||
for i in stride(from: 0, to: revokedInvites.endIndex, by: 2) {
|
||||
var invitesPair: [ExportedInvitation] = []
|
||||
|
|
@ -305,7 +372,7 @@ private struct InviteLinkListControllerState: Equatable {
|
|||
}
|
||||
|
||||
|
||||
public func inviteLinkListController(context: AccountContext, peerId: PeerId) -> ViewController {
|
||||
public func inviteLinkListController(context: AccountContext, peerId: PeerId, admin: ExportedInvitationCreator?) -> ViewController {
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var presentInGlobalOverlayImpl: ((ViewController) -> Void)?
|
||||
|
|
@ -326,8 +393,16 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||
|
||||
var getControllerImpl: (() -> ViewController?)?
|
||||
|
||||
let invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: false)
|
||||
let revokedInvitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: true, forceUpdate: true)
|
||||
let adminId = admin?.peer.peer?.id
|
||||
let invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: adminId, revoked: false, forceUpdate: false)
|
||||
let revokedInvitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: adminId, revoked: true, forceUpdate: true)
|
||||
|
||||
let creators: Signal<[ExportedInvitationCreator], NoError>
|
||||
if adminId == nil {
|
||||
creators = .single([]) |> then(peerExportedInvitationsCreators(account: context.account, peerId: peerId))
|
||||
} else {
|
||||
creators = .single([])
|
||||
}
|
||||
|
||||
let arguments = InviteLinkListControllerArguments(context: context, shareMainLink: { invite in
|
||||
let shareController = ShareController(context: context, subject: .url(invite.link))
|
||||
|
|
@ -546,6 +621,9 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||
|
||||
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, openAdmin: { admin in
|
||||
let controller = inviteLinkListController(context: context, peerId: peerId, admin: admin)
|
||||
pushControllerImpl?(controller)
|
||||
}, deleteAllRevokedLinks: {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
|
|
@ -596,9 +674,9 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||
}
|
||||
|
||||
let previousRevokedInvites = Atomic<PeerExportedInvitationsState?>(value: nil)
|
||||
let signal = combineLatest(context.sharedContext.presentationData, peerView, importersContext, importersState.get(), invitesContext.state, revokedInvitesContext.state)
|
||||
let signal = combineLatest(context.sharedContext.presentationData, peerView, importersContext, importersState.get(), invitesContext.state, revokedInvitesContext.state, creators)
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, view, importersContext, importers, invites, revokedInvites -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|> map { presentationData, view, importersContext, importers, invites, revokedInvites, creators -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let previousRevokedInvites = previousRevokedInvites.swap(invites)
|
||||
|
||||
var crossfade = false
|
||||
|
|
@ -606,8 +684,15 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||
crossfade = true
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.InviteLink_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkListControllerEntries(presentationData: presentationData, view: view, invites: invites.invitations, revokedInvites: revokedInvites.invitations, importers: importers), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: false)
|
||||
let title: ItemListControllerTitle
|
||||
if let admin = admin, let peer = admin.peer.peer {
|
||||
title = .textWithSubtitle(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), "\(admin.count) invite links")
|
||||
} else {
|
||||
title = .text(presentationData.strings.InviteLink_Title)
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkListControllerEntries(presentationData: presentationData, view: view, invites: invites.hasLoadedOnce ? invites.invitations : nil, revokedInvites: revokedInvites.invitations, importers: importers, creators: creators, admin: admin), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ private class ItemNode: ASDisplayNode {
|
|||
|
||||
private var updateTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private var params: (size: CGSize, wide: Bool, invite: ExportedInvitation, color: ItemBackgroundColor, presentationData: ItemListPresentationData)?
|
||||
private var params: (size: CGSize, wide: Bool, invite: ExportedInvitation?, color: ItemBackgroundColor, presentationData: ItemListPresentationData)?
|
||||
|
||||
var action: (() -> Void)?
|
||||
var contextAction: ((ASDisplayNode) -> Void)?
|
||||
|
|
@ -214,44 +214,50 @@ private class ItemNode: ASDisplayNode {
|
|||
self.contextAction?(self.extractedContainerNode)
|
||||
}
|
||||
|
||||
func update(size: CGSize, wide: Bool, share: Bool, invite: ExportedInvitation, presentationData: ItemListPresentationData, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
func update(size: CGSize, wide: Bool, share: Bool, invite: ExportedInvitation?, presentationData: ItemListPresentationData, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
let availability = invitationAvailability(invite)
|
||||
let availability = invite.flatMap { invitationAvailability($0) } ?? 0.0
|
||||
let transitionFraction: CGFloat
|
||||
let color: ItemBackgroundColor
|
||||
let nextColor: ItemBackgroundColor
|
||||
if invite.isRevoked {
|
||||
if let invite = invite {
|
||||
if invite.isRevoked {
|
||||
color = .gray
|
||||
nextColor = .gray
|
||||
transitionFraction = 0.0
|
||||
} else if invite.expireDate == nil && invite.usageLimit == nil {
|
||||
color = .blue
|
||||
nextColor = .blue
|
||||
transitionFraction = 0.0
|
||||
} else if availability >= 0.5 {
|
||||
color = .green
|
||||
nextColor = .yellow
|
||||
transitionFraction = (availability - 0.5) / 0.5
|
||||
} else if availability > 0.0 {
|
||||
color = .yellow
|
||||
nextColor = .red
|
||||
transitionFraction = availability / 0.5
|
||||
} else {
|
||||
color = .red
|
||||
nextColor = .red
|
||||
transitionFraction = 0.0
|
||||
}
|
||||
} else {
|
||||
color = .gray
|
||||
nextColor = .gray
|
||||
transitionFraction = 0.0
|
||||
} else if invite.expireDate == nil && invite.usageLimit == nil {
|
||||
color = .blue
|
||||
nextColor = .blue
|
||||
transitionFraction = 0.0
|
||||
} else if availability >= 0.5 {
|
||||
color = .green
|
||||
nextColor = .yellow
|
||||
transitionFraction = (availability - 0.5) / 0.5
|
||||
} else if availability > 0.0 {
|
||||
color = .yellow
|
||||
nextColor = .red
|
||||
transitionFraction = availability / 0.5
|
||||
} else {
|
||||
color = .red
|
||||
nextColor = .red
|
||||
transitionFraction = 0.0
|
||||
}
|
||||
|
||||
|
||||
let previousParams = self.params
|
||||
self.params = (size, wide, invite, color, presentationData)
|
||||
|
||||
let previousExpireDate = previousParams?.invite.expireDate
|
||||
if previousExpireDate != invite.expireDate {
|
||||
let previousExpireDate = previousParams?.invite?.expireDate
|
||||
if previousExpireDate != invite?.expireDate {
|
||||
self.updateTimer?.invalidate()
|
||||
self.updateTimer = nil
|
||||
|
||||
if let expireDate = invite.expireDate, availability > 0.0 {
|
||||
if let expireDate = invite?.expireDate, availability > 0.0 {
|
||||
let timeout = min(2.0, max(0.001, Double(expireDate - currentTime)))
|
||||
let updateTimer = SwiftSignalKit.Timer(timeout: timeout, repeat: true, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
|
|
@ -272,8 +278,13 @@ private class ItemNode: ASDisplayNode {
|
|||
let bottomColor = color.colors.bottom
|
||||
let nextTopColor = nextColor.colors.top
|
||||
let nextBottomColor = nextColor.colors.bottom
|
||||
let colors: NSArray = [nextTopColor.mixedWith(topColor, alpha: transitionFraction).cgColor, nextBottomColor.mixedWith(bottomColor, alpha: transitionFraction).cgColor]
|
||||
|
||||
let colors: NSArray
|
||||
if let invite = invite {
|
||||
colors = [nextTopColor.mixedWith(topColor, alpha: transitionFraction).cgColor, nextBottomColor.mixedWith(bottomColor, alpha: transitionFraction).cgColor]
|
||||
} else {
|
||||
colors = [UIColor(rgb: 0xf2f2f7).cgColor, UIColor(rgb: 0xf2f2f7).cgColor]
|
||||
}
|
||||
|
||||
if let (_, _, previousInvite, previousColor, _) = previousParams, previousInvite == invite {
|
||||
if previousColor != color && color == .red {
|
||||
if let snapshotView = self.wrapperNode.view.snapshotContentTree() {
|
||||
|
|
@ -298,7 +309,7 @@ private class ItemNode: ASDisplayNode {
|
|||
let secondaryTextColor = nextColor.colors.text.mixedWith(color.colors.text, alpha: transitionFraction)
|
||||
|
||||
let itemWidth = wide ? size.width : floor((size.width - itemSpacing) / 2.0)
|
||||
var inviteLink = invite.link.replacingOccurrences(of: "https://", with: "")
|
||||
var inviteLink = invite?.link.replacingOccurrences(of: "https://", with: "") ?? ""
|
||||
if !wide {
|
||||
inviteLink = inviteLink.replacingOccurrences(of: "joinchat/", with: "joinchat/\n")
|
||||
inviteLink = inviteLink.replacingOccurrences(of: "join/", with: "join/\n")
|
||||
|
|
@ -314,65 +325,72 @@ private class ItemNode: ASDisplayNode {
|
|||
self.buttonIconNode.image = share ? shareIcon : moreIcon
|
||||
|
||||
var subtitleText: String = ""
|
||||
if let count = invite.count {
|
||||
subtitleText = presentationData.strings.InviteLink_PeopleJoinedShort(count)
|
||||
if let invite = invite {
|
||||
if let count = invite.count {
|
||||
subtitleText = presentationData.strings.InviteLink_PeopleJoinedShort(count)
|
||||
} else {
|
||||
subtitleText = [.red, .gray].contains(color) ? presentationData.strings.InviteLink_PeopleJoinedShortNoneExpired : presentationData.strings.InviteLink_PeopleJoinedShortNone
|
||||
}
|
||||
if invite.isRevoked {
|
||||
if !subtitleText.isEmpty {
|
||||
subtitleText += " • "
|
||||
}
|
||||
subtitleText += presentationData.strings.InviteLink_Revoked
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
} else if let expireDate = invite.expireDate, currentTime >= expireDate {
|
||||
if !subtitleText.isEmpty {
|
||||
subtitleText += " • "
|
||||
}
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_Expired
|
||||
} else {
|
||||
subtitleText += presentationData.strings.InviteLink_Expired
|
||||
}
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
} else if let usageLimit = invite.usageLimit, let count = invite.count, count >= usageLimit {
|
||||
if !subtitleText.isEmpty {
|
||||
subtitleText += " • "
|
||||
}
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_UsageLimitReached
|
||||
} else {
|
||||
subtitleText += presentationData.strings.InviteLink_UsageLimitReached
|
||||
}
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
} else if let expireDate = invite.expireDate {
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Flame"), color: .white)
|
||||
let timerNode: TimerNode
|
||||
if let current = self.timerNode {
|
||||
timerNode = current
|
||||
} else {
|
||||
timerNode = TimerNode()
|
||||
timerNode.isUserInteractionEnabled = false
|
||||
self.timerNode = timerNode
|
||||
self.addSubnode(timerNode)
|
||||
}
|
||||
timerNode.update(color: UIColor.white, creationTimestamp: invite.startDate ?? invite.date, deadlineTimestamp: expireDate)
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_TapToCopy
|
||||
}
|
||||
} else {
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Link"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_TapToCopy
|
||||
}
|
||||
}
|
||||
self.iconNode.isHidden = false
|
||||
self.buttonIconNode.isHidden = false
|
||||
} else {
|
||||
subtitleText = [.red, .gray].contains(color) ? presentationData.strings.InviteLink_PeopleJoinedShortNoneExpired : presentationData.strings.InviteLink_PeopleJoinedShortNone
|
||||
}
|
||||
if invite.isRevoked {
|
||||
if !subtitleText.isEmpty {
|
||||
subtitleText += " • "
|
||||
}
|
||||
subtitleText += presentationData.strings.InviteLink_Revoked
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
} else if let expireDate = invite.expireDate, currentTime >= expireDate {
|
||||
if !subtitleText.isEmpty {
|
||||
subtitleText += " • "
|
||||
}
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_Expired
|
||||
} else {
|
||||
subtitleText += presentationData.strings.InviteLink_Expired
|
||||
}
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
} else if let usageLimit = invite.usageLimit, let count = invite.count, count >= usageLimit {
|
||||
if !subtitleText.isEmpty {
|
||||
subtitleText += " • "
|
||||
}
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_UsageLimitReached
|
||||
} else {
|
||||
subtitleText += presentationData.strings.InviteLink_UsageLimitReached
|
||||
}
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
} else if let expireDate = invite.expireDate {
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Flame"), color: .white)
|
||||
let timerNode: TimerNode
|
||||
if let current = self.timerNode {
|
||||
timerNode = current
|
||||
} else {
|
||||
timerNode = TimerNode()
|
||||
timerNode.isUserInteractionEnabled = false
|
||||
self.timerNode = timerNode
|
||||
self.addSubnode(timerNode)
|
||||
}
|
||||
timerNode.update(color: UIColor.white, creationTimestamp: invite.startDate ?? invite.date, deadlineTimestamp: expireDate)
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_TapToCopy
|
||||
}
|
||||
} else {
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Link"), color: .white)
|
||||
self.timerNode?.removeFromSupernode()
|
||||
self.timerNode = nil
|
||||
if share {
|
||||
subtitleText = presentationData.strings.InviteLink_TapToCopy
|
||||
}
|
||||
self.iconNode.isHidden = true
|
||||
self.buttonIconNode.isHidden = true
|
||||
}
|
||||
|
||||
self.iconNode.frame = CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0)
|
||||
|
|
@ -407,7 +425,7 @@ private class ItemNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
class InviteLinksGridNode: ASDisplayNode {
|
||||
private var items: [ExportedInvitation] = []
|
||||
private var items: [ExportedInvitation]?
|
||||
private var itemNodes: [String: ItemNode] = [:]
|
||||
|
||||
var action: ((ExportedInvitation) -> Void)?
|
||||
|
|
@ -418,7 +436,7 @@ class InviteLinksGridNode: ASDisplayNode {
|
|||
return result
|
||||
}
|
||||
|
||||
func update(size: CGSize, safeInset: CGFloat, items: [ExportedInvitation], share: Bool, presentationData: ItemListPresentationData, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
func update(size: CGSize, safeInset: CGFloat, items: [ExportedInvitation]?, count: Int, share: Bool, presentationData: ItemListPresentationData, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
self.items = items
|
||||
|
||||
var contentSize: CGSize = size
|
||||
|
|
@ -428,24 +446,37 @@ class InviteLinksGridNode: ASDisplayNode {
|
|||
|
||||
var validIds = Set<String>()
|
||||
|
||||
for i in 0 ..< self.items.count {
|
||||
let invite = self.items[i]
|
||||
validIds.insert(invite.link)
|
||||
let count = items?.count ?? count
|
||||
|
||||
for i in 0 ..< count {
|
||||
let invite: ExportedInvitation?
|
||||
let id: String
|
||||
if let items = items, i < items.count {
|
||||
invite = items[i]
|
||||
id = invite!.link
|
||||
} else {
|
||||
invite = nil
|
||||
id = "placeholder_\(i)"
|
||||
}
|
||||
|
||||
validIds.insert(id)
|
||||
|
||||
var itemNode: ItemNode?
|
||||
var wasAdded = false
|
||||
if let current = self.itemNodes[invite.link] {
|
||||
|
||||
if let current = self.itemNodes[id] {
|
||||
itemNode = current
|
||||
} else {
|
||||
wasAdded = true
|
||||
let addedItemNode = ItemNode()
|
||||
itemNode = addedItemNode
|
||||
self.itemNodes[invite.link] = addedItemNode
|
||||
self.itemNodes[id] = addedItemNode
|
||||
self.addSubnode(addedItemNode)
|
||||
}
|
||||
if let itemNode = itemNode {
|
||||
let col = CGFloat(i % 2)
|
||||
let row = floor(CGFloat(i) / 2.0)
|
||||
let wide = (i == self.items.count - 1 && (self.items.count % 2) != 0)
|
||||
let wide = (i == count - 1 && (count % 2) != 0)
|
||||
let itemSize = itemNode.update(size: CGSize(width: size.width - sideInset * 2.0, height: size.height), wide: wide, share: share, invite: invite, presentationData: presentationData, transition: transition)
|
||||
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: 4.0 + row * (122.0 + itemSpacing)), size: itemSize)
|
||||
if !wide && col > 0 {
|
||||
|
|
@ -460,10 +491,14 @@ class InviteLinksGridNode: ASDisplayNode {
|
|||
transition.updateFrame(node: itemNode, frame: itemFrame)
|
||||
}
|
||||
itemNode.action = { [weak self] in
|
||||
self?.action?(invite)
|
||||
if let invite = invite {
|
||||
self?.action?(invite)
|
||||
}
|
||||
}
|
||||
itemNode.contextAction = { [weak self] node in
|
||||
self?.contextAction?(node, invite)
|
||||
if let invite = invite {
|
||||
self?.contextAction?(node, invite)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import ItemListUI
|
|||
public class ItemListInviteLinkGridItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let invites: [ExportedInvitation]?
|
||||
let count: Int
|
||||
let share: Bool
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
|
|
@ -20,6 +21,7 @@ public class ItemListInviteLinkGridItem: ListViewItem, ItemListItem {
|
|||
public init(
|
||||
presentationData: ItemListPresentationData,
|
||||
invites: [ExportedInvitation]?,
|
||||
count: Int,
|
||||
share: Bool,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle,
|
||||
|
|
@ -29,6 +31,7 @@ public class ItemListInviteLinkGridItem: ListViewItem, ItemListItem {
|
|||
) {
|
||||
self.presentationData = presentationData
|
||||
self.invites = invites
|
||||
self.count = count
|
||||
self.share = share
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
|
|
@ -133,13 +136,14 @@ public class ItemListInviteLinkGridItemNode: ListViewItemNode, ItemListItemNode
|
|||
let topInset: CGFloat
|
||||
if case .plain = item.style, case .otherSection = neighbors.top {
|
||||
topInset = 16.0
|
||||
} else if case .blocks = item.style, case .sameSection(true) = neighbors.top {
|
||||
topInset = 16.0
|
||||
} else {
|
||||
topInset = 4.0
|
||||
}
|
||||
|
||||
|
||||
var height: CGFloat
|
||||
let count = item.invites?.count ?? 0
|
||||
let count = item.invites?.count ?? item.count
|
||||
if count > 0 {
|
||||
if count % 2 == 0 {
|
||||
height = topInset + 122.0 + 6.0
|
||||
|
|
@ -176,7 +180,7 @@ public class ItemListInviteLinkGridItemNode: ListViewItemNode, ItemListItemNode
|
|||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
}
|
||||
|
||||
let gridSize = strongSelf.gridNode.update(size: contentSize, safeInset: params.leftInset, items: item.invites ?? [], share: item.share, presentationData: item.presentationData, transition: .immediate)
|
||||
let gridSize = strongSelf.gridNode.update(size: contentSize, safeInset: params.leftInset, items: item.invites, count: item.count, share: item.share, presentationData: item.presentationData, transition: .immediate)
|
||||
strongSelf.gridNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topInset - 4.0), size: gridSize)
|
||||
strongSelf.gridNode.action = { invite in
|
||||
item.tapAction?(invite)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import TelegramPresentationData
|
|||
import ItemListUI
|
||||
import SolidRoundedButtonNode
|
||||
import AnimatedAvatarSetNode
|
||||
import ShimmerEffect
|
||||
|
||||
private func actionButtonImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 24.0, height: 24.0), contextGenerator: { size, context in
|
||||
|
|
@ -132,6 +133,8 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||
private var avatarsContent: AnimatedAvatarSetContext.Content?
|
||||
private let avatarsNode: AnimatedAvatarSetNode
|
||||
private let invitedPeersNode: TextNode
|
||||
private var peersPlaceholderNode: ShimmerEffectNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
|
|
@ -180,7 +183,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||
self.avatarsContext = AnimatedAvatarSetContext()
|
||||
self.avatarsNode = AnimatedAvatarSetNode()
|
||||
self.invitedPeersNode = TextNode()
|
||||
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
|
@ -355,7 +358,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||
strongSelf.fieldNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: item.presentationData.theme.list.itemInputField.backgroundColor)
|
||||
strongSelf.addressButtonIconNode.image = actionButtonImage(color: item.presentationData.theme.list.itemInputField.controlColor)
|
||||
}
|
||||
|
||||
|
||||
let _ = addressApply()
|
||||
let _ = invitedPeersApply()
|
||||
|
||||
|
|
@ -470,12 +473,45 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||
strongSelf.invitedPeersNode.frame = CGRect(origin: CGPoint(x: leftOrigin, y: fieldFrame.maxY + 92.0), size: invitedPeersLayout.size)
|
||||
|
||||
strongSelf.avatarsButtonNode.frame = CGRect(x: floorToScreenPixels((params.width - totalWidth) / 2.0), y: fieldFrame.maxY + 87.0, width: totalWidth, height: 32.0)
|
||||
strongSelf.avatarsButtonNode.isUserInteractionEnabled = !item.peers.isEmpty
|
||||
strongSelf.avatarsButtonNode.isUserInteractionEnabled = !item.peers.isEmpty && item.invite != nil
|
||||
|
||||
strongSelf.addressButtonNode.isUserInteractionEnabled = item.invite != nil
|
||||
strongSelf.fieldButtonNode.isUserInteractionEnabled = item.invite != nil
|
||||
strongSelf.addressButtonIconNode.alpha = item.invite != nil ? 1.0 : 0.0
|
||||
|
||||
strongSelf.shareButtonNode?.isUserInteractionEnabled = item.invite != nil
|
||||
strongSelf.shareButtonNode?.alpha = item.invite != nil ? 1.0 : 0.4
|
||||
strongSelf.shareButtonNode?.isHidden = !item.displayButton
|
||||
strongSelf.avatarsButtonNode.isHidden = !item.displayImporters
|
||||
strongSelf.avatarsNode.isHidden = !item.displayImporters
|
||||
strongSelf.invitedPeersNode.isHidden = !item.displayImporters
|
||||
strongSelf.avatarsNode.isHidden = !item.displayImporters || item.invite == nil
|
||||
strongSelf.invitedPeersNode.isHidden = !item.displayImporters || item.invite == nil
|
||||
|
||||
if item.invite == nil {
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.peersPlaceholderNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.peersPlaceholderNode = shimmerNode
|
||||
strongSelf.insertSubnode(shimmerNode, belowSubnode: strongSelf.fieldNode)
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
|
||||
let lineWidth: CGFloat = 180.0
|
||||
let lineDiameter: CGFloat = 10.0
|
||||
|
||||
let titleFrame = strongSelf.invitedPeersNode.frame
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: floor(titleFrame.center.x - lineWidth / 2.0), y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: lineWidth, diameter: lineDiameter))
|
||||
shimmerNode.update(backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: item.presentationData.theme.list.mediaPlaceholderColor, shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: layout.contentSize)
|
||||
} else if let shimmerNode = strongSelf.peersPlaceholderNode {
|
||||
strongSelf.peersPlaceholderNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -492,4 +528,13 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
var rect = rect
|
||||
rect.origin.y += self.insets.top
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
if let shimmerNode = self.peersPlaceholderNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1086,7 +1086,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelLayout.size.width) / 2.0, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
||||
strongSelf.labelNode.frame = labelFrame
|
||||
} else {
|
||||
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - rightLabelInset - rightInset, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
||||
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - rightLabelInset, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
||||
transition.updateFrame(node: strongSelf.labelNode, frame: labelFrame)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||
case let .privateLinkHeader(_, title):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||
case let .privateLink(_, invite, displayImporters):
|
||||
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: displayImporters, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||
if let invite = invite {
|
||||
arguments.copyLink(invite)
|
||||
}
|
||||
|
|
@ -598,13 +598,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
} else {
|
||||
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePublicLinkHelp))
|
||||
}
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
}
|
||||
case .privateChannel:
|
||||
let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation
|
||||
|
|
@ -615,13 +615,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
} else {
|
||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePrivateLinkHelp))
|
||||
}
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
}
|
||||
} else if let _ = view.peers[view.peerId] as? TelegramGroup {
|
||||
switch mode {
|
||||
|
|
@ -630,13 +630,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
|
||||
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
case .generic, .initialSetup:
|
||||
let selectedType: CurrentChannelType
|
||||
if let current = state.selectedType {
|
||||
|
|
@ -729,13 +729,13 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
|
||||
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
||||
// switch mode {
|
||||
// case .initialSetup:
|
||||
// break
|
||||
// case .generic, .privateLink:
|
||||
// entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
// entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
// }
|
||||
switch mode {
|
||||
case .initialSetup:
|
||||
break
|
||||
case .generic, .privateLink:
|
||||
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
|
||||
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -943,27 +943,27 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
})
|
||||
})))
|
||||
|
||||
// items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
// }, action: { _, f in
|
||||
// f(.dismissWithoutContent)
|
||||
//
|
||||
// let _ = (context.account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
// if let cachedData = transaction.getPeerCachedData(peerId: peerId) {
|
||||
// if let cachedData = cachedData as? CachedChannelData {
|
||||
// return cachedData.exportedInvitation
|
||||
// } else if let cachedData = cachedData as? CachedGroupData {
|
||||
// return cachedData.exportedInvitation
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// } |> deliverOnMainQueue).start(next: { invite in
|
||||
// if let invite = invite {
|
||||
// let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
// presentControllerImpl?(controller, nil)
|
||||
// }
|
||||
// })
|
||||
// })))
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
let _ = (context.account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) {
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.exportedInvitation
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.exportedInvitation
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} |> deliverOnMainQueue).start(next: { invite in
|
||||
if let invite = invite {
|
||||
let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
presentControllerImpl?(controller, nil)
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
|
|
@ -1006,7 +1006,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: nil)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, manageInviteLinks: {
|
||||
let controller = inviteLinkListController(context: context, peerId: peerId)
|
||||
let controller = inviteLinkListController(context: context, peerId: peerId, admin: nil)
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ private enum PeerAutoremoveSetupSection: Int32 {
|
|||
private enum PeerAutoremoveSetupEntry: ItemListNodeEntry {
|
||||
case header
|
||||
case timeHeader(String)
|
||||
case timeValue(Int32)
|
||||
case timeValue(Int32, Int32)
|
||||
case timeComment(String)
|
||||
case globalSwitch(String, Bool)
|
||||
|
||||
|
|
@ -74,8 +74,8 @@ private enum PeerAutoremoveSetupEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .timeValue(lhsValue):
|
||||
if case let .timeValue(rhsValue) = rhs, lhsValue == rhsValue {
|
||||
case let .timeValue(lhsValue, lhsMaxValue):
|
||||
if case let .timeValue(rhsValue, rhsMaxValue) = rhs, lhsValue == rhsValue, lhsMaxValue == rhsMaxValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
|
@ -106,8 +106,8 @@ private enum PeerAutoremoveSetupEntry: ItemListNodeEntry {
|
|||
return ChatListFilterSettingsHeaderItem(theme: presentationData.theme, text: "", animation: .autoRemove, sectionId: self.section)
|
||||
case let .timeHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .timeValue(value):
|
||||
return PeerRemoveTimeoutItem(theme: presentationData.theme, value: value, maxValue: Int32.max, enabled: true, sectionId: self.section, updated: { value in
|
||||
case let .timeValue(value, maxValue):
|
||||
return PeerRemoveTimeoutItem(theme: presentationData.theme, value: value, maxValue: maxValue, enabled: true, sectionId: self.section, updated: { value in
|
||||
arguments.updateValue(value)
|
||||
}, tag: nil)
|
||||
case let .timeComment(text):
|
||||
|
|
@ -126,15 +126,25 @@ private struct PeerAutoremoveSetupState: Equatable {
|
|||
var applyingSetting: Bool = false
|
||||
}
|
||||
|
||||
private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: PresentationData, defaultValue: Int32?, defaultGlobalValue: Bool, state: PeerAutoremoveSetupState) -> [PeerAutoremoveSetupEntry] {
|
||||
private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: PresentationData, defaultMyValue: Int32, peerValue: Int32, defaultGlobalValue: Bool, state: PeerAutoremoveSetupState) -> [PeerAutoremoveSetupEntry] {
|
||||
var entries: [PeerAutoremoveSetupEntry] = []
|
||||
let value = state.changedValue ?? defaultValue
|
||||
let globalValue = state.changedGlobalValue ?? defaultGlobalValue
|
||||
|
||||
let resolvedValue: Int32
|
||||
let resolvedMaxValue: Int32
|
||||
|
||||
if peer is TelegramUser {
|
||||
resolvedValue = state.changedValue ?? defaultMyValue
|
||||
resolvedMaxValue = peerValue
|
||||
} else {
|
||||
resolvedValue = state.changedValue ?? peerValue
|
||||
resolvedMaxValue = Int32.max
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
entries.append(.header)
|
||||
entries.append(.timeHeader("AUTO-DELETE MESSAGES"))
|
||||
entries.append(.timeValue(value ?? Int32.max))
|
||||
entries.append(.timeValue(resolvedValue, resolvedMaxValue))
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
entries.append(.timeComment("Automatically delete messages sent in this channel after a certain period of time."))
|
||||
} else {
|
||||
|
|
@ -184,25 +194,25 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
|
|||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.viewTracker.peerView(peerId))
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
var defaultValue: Int32 = Int32.max
|
||||
var maxValue: Int32 = Int32.max
|
||||
var defaultMyValue: Int32 = Int32.max
|
||||
var peerValue: Int32 = Int32.max
|
||||
var defaultGlobalValue = true
|
||||
if let cachedData = view.cachedData as? CachedChannelData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
defaultValue = value?.myValue ?? Int32.max
|
||||
maxValue = value?.peerValue ?? Int32.max
|
||||
defaultMyValue = value?.myValue ?? Int32.max
|
||||
peerValue = value?.peerValue ?? Int32.max
|
||||
defaultGlobalValue = value?.isGlobal ?? true
|
||||
}
|
||||
} else if let cachedData = view.cachedData as? CachedGroupData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
defaultValue = value?.myValue ?? Int32.max
|
||||
maxValue = value?.peerValue ?? Int32.max
|
||||
defaultMyValue = value?.myValue ?? Int32.max
|
||||
peerValue = value?.peerValue ?? Int32.max
|
||||
defaultGlobalValue = value?.isGlobal ?? true
|
||||
}
|
||||
} else if let cachedData = view.cachedData as? CachedUserData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
defaultValue = value?.myValue ?? Int32.max
|
||||
maxValue = value?.peerValue ?? Int32.max
|
||||
defaultMyValue = value?.myValue ?? Int32.max
|
||||
peerValue = value?.peerValue ?? Int32.max
|
||||
defaultGlobalValue = value?.isGlobal ?? true
|
||||
}
|
||||
}
|
||||
|
|
@ -217,35 +227,47 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
|
|||
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||
} else {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
var value: Int32?
|
||||
var changedValue: Int32?
|
||||
var globalValue: Bool?
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.applyingSetting = true
|
||||
value = state.changedValue
|
||||
changedValue = state.changedValue
|
||||
globalValue = state.changedGlobalValue
|
||||
return state
|
||||
}
|
||||
|
||||
let resolvedDefaultValue: Int32
|
||||
if peer is TelegramUser {
|
||||
resolvedDefaultValue = defaultMyValue
|
||||
} else {
|
||||
resolvedDefaultValue = peerValue
|
||||
}
|
||||
|
||||
var updated = false
|
||||
if let value = value, value != defaultValue {
|
||||
if let changedValue = changedValue, changedValue != resolvedDefaultValue {
|
||||
updated = true
|
||||
}
|
||||
if let globalValue = globalValue, globalValue != defaultGlobalValue {
|
||||
updated = true
|
||||
}
|
||||
if updated {
|
||||
let resolvedValue = value ?? defaultValue
|
||||
var resolvedValue: Int32? = changedValue ?? resolvedDefaultValue
|
||||
if resolvedValue == Int32.max {
|
||||
resolvedValue = nil
|
||||
}
|
||||
|
||||
let resolvedGlobalValue = globalValue ?? defaultGlobalValue
|
||||
|
||||
let signal = setChatMessageAutoremoveTimeoutInteractively(account: context.account, peerId: peerId, timeout: resolvedValue == Int32.max ? nil : resolvedValue, isGlobal: resolvedGlobalValue)
|
||||
let signal = setChatMessageAutoremoveTimeoutInteractively(account: context.account, peerId: peerId, timeout: resolvedValue, isGlobal: resolvedGlobalValue)
|
||||
|> deliverOnMainQueue
|
||||
|
||||
applyDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
}, completed: {
|
||||
dismissImpl?()
|
||||
if resolvedValue != defaultValue {
|
||||
completion(.updated(resolvedValue))
|
||||
if resolvedValue != resolvedDefaultValue {
|
||||
completion(.updated(changedValue))
|
||||
} else {
|
||||
completion(.unchanged)
|
||||
}
|
||||
|
|
@ -254,51 +276,12 @@ public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, c
|
|||
dismissImpl?()
|
||||
completion(.unchanged)
|
||||
}
|
||||
/*if let value = value, value != defaultValue {
|
||||
if peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
let signal = convertGroupToSupergroup(account: context.account, peerId: peerId)
|
||||
|> mapToSignal { upgradedPeerId -> Signal<PeerId?, ConvertGroupToSupergroupError> in
|
||||
return updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: upgradedPeerId, historyAvailableForNewMembers: value)
|
||||
|> `catch` { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<PeerId?, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> then(.single(upgradedPeerId))
|
||||
|> castError(ConvertGroupToSupergroupError.self)
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
applyDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(next: { upgradedPeerId in
|
||||
if let upgradedPeerId = upgradedPeerId {
|
||||
upgradedToSupergroup(upgradedPeerId, {
|
||||
dismissImpl?()
|
||||
})
|
||||
}
|
||||
}, error: { error in
|
||||
switch error {
|
||||
case .tooManyChannels:
|
||||
pushControllerImpl?(oldChannelsController(context: context, intent: .upgrade))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
applyDisposable.set((updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: peerId, historyAvailableForNewMembers: value)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
dismissImpl?()
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
dismissImpl?()
|
||||
}*/
|
||||
})
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Auto-Deletion"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peerAutoremoveSetupEntries(peer: peer, presentationData: presentationData, defaultValue: defaultValue, defaultGlobalValue: defaultGlobalValue, state: state), style: .blocks)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peerAutoremoveSetupEntries(peer: peer, presentationData: presentationData, defaultMyValue: defaultMyValue, peerValue: peerValue, defaultGlobalValue: defaultGlobalValue, state: state), style: .blocks)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,28 @@ import ItemListUI
|
|||
import PresentationDataUtils
|
||||
import AppBundle
|
||||
|
||||
private func mapTimeoutToSliderValue(_ value: Int32) -> CGFloat {
|
||||
switch value {
|
||||
case 24 * 60 * 60:
|
||||
return 0.0
|
||||
case 7 * 24 * 60 * 60:
|
||||
return 1.0
|
||||
default:
|
||||
return 2.0
|
||||
}
|
||||
}
|
||||
|
||||
private func mapSliderValueToTimeout(_ value: CGFloat) -> Int32 {
|
||||
switch value {
|
||||
case 0.0:
|
||||
return 24 * 60 * 60
|
||||
case 1.0:
|
||||
return 7 * 24 * 60 * 60
|
||||
default:
|
||||
return Int32.max
|
||||
}
|
||||
}
|
||||
|
||||
class PeerRemoveTimeoutItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
let value: Int32
|
||||
|
|
@ -129,21 +151,17 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode {
|
|||
sliderView.startValue = 0.0
|
||||
sliderView.positionsCount = 3
|
||||
sliderView.useLinesForPositions = true
|
||||
sliderView.minimumUndottedValue = 2
|
||||
sliderView.minimumUndottedValue = 0
|
||||
sliderView.disablesInteractiveTransitionGestureRecognizer = true
|
||||
if let item = self.item, let params = self.layoutParams {
|
||||
sliderView.isUserInteractionEnabled = item.enabled
|
||||
|
||||
let value: CGFloat
|
||||
switch item.value {
|
||||
case 24 * 60 * 60:
|
||||
value = 0.0
|
||||
case 7 * 24 * 60 * 60:
|
||||
value = 1.0
|
||||
default:
|
||||
value = 2.0
|
||||
}
|
||||
sliderView.value = value
|
||||
sliderView.minimumUndottedValue = 0
|
||||
|
||||
sliderView.value = mapTimeoutToSliderValue(item.value)
|
||||
|
||||
sliderView.minimumUndottedValue = 2 - Int32(mapTimeoutToSliderValue(item.maxValue))
|
||||
|
||||
sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
sliderView.backColor = item.theme.list.disclosureArrowColor
|
||||
sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// SQueueLocalObject.h
|
||||
// SSignalKit
|
||||
//
|
||||
// Created by Mikhail Filimonov on 13.01.2021.
|
||||
// Copyright © 2021 Telegram. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SSignalKit/SQueue.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SQueueLocalObject : NSObject
|
||||
-(id)initWithQueue:(SQueue *)queue generate:(id (^)(void))next;
|
||||
-(void)with:(void (^)(id object))f;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// SQueueLocalObject.m
|
||||
// SSignalKit
|
||||
//
|
||||
// Created by Mikhail Filimonov on 13.01.2021.
|
||||
// Copyright © 2021 Telegram. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SQueueLocalObject.h"
|
||||
|
||||
@implementation SQueueLocalObject {
|
||||
SQueue *queue;
|
||||
id valueRef;
|
||||
}
|
||||
-(id)initWithQueue:(SQueue *)queue generate:(id _Nonnull (^)(void))next {
|
||||
if (self = [super init]) {
|
||||
self->queue = queue;
|
||||
[queue dispatch:^{
|
||||
self->valueRef = next();
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)with:(void (^)(id object))f {
|
||||
[self->queue dispatch:^{
|
||||
f(self->valueRef);
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
__block id value = self->valueRef;
|
||||
self->valueRef = nil;
|
||||
[queue dispatch:^{
|
||||
value = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
@ -35,3 +35,4 @@ FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[];
|
|||
#import <SSignalKit/SMulticastSignalManager.h>
|
||||
#import <SSignalKit/STimer.h>
|
||||
#import <SSignalKit/SVariable.h>
|
||||
#import <SSignalKit/SQueueLocalObject.h>
|
||||
|
|
|
|||
|
|
@ -748,6 +748,9 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||
})
|
||||
case .voiceConference:
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Voice Conference (Test)", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
guard let _ = arguments.context else {
|
||||
return
|
||||
}
|
||||
})
|
||||
case let .preferredVideoCodec(_, title, value, isSelected):
|
||||
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
|
|
|
|||
|
|
@ -3,27 +3,47 @@ import Postbox
|
|||
|
||||
public enum CachedPeerAutoremoveTimeout: Equatable, PostboxCoding {
|
||||
public struct Value: Equatable, PostboxCoding {
|
||||
public var myValue: Int32
|
||||
public var peerValue: Int32
|
||||
public var myValue: Int32?
|
||||
public var peerValue: Int32?
|
||||
public var isGlobal: Bool
|
||||
|
||||
public init(myValue: Int32, peerValue: Int32, isGlobal: Bool) {
|
||||
public init(myValue: Int32?, peerValue: Int32?, isGlobal: Bool) {
|
||||
self.myValue = myValue
|
||||
self.peerValue = peerValue
|
||||
self.isGlobal = isGlobal
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.myValue = decoder.decodeInt32ForKey("myValue", orElse: 7 * 60 * 60)
|
||||
self.peerValue = decoder.decodeInt32ForKey("peerValue", orElse: 7 * 60 * 60)
|
||||
self.myValue = decoder.decodeOptionalInt32ForKey("myValue")
|
||||
self.peerValue = decoder.decodeOptionalInt32ForKey("peerValue")
|
||||
self.isGlobal = decoder.decodeInt32ForKey("isGlobal", orElse: 1) != 0
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.myValue, forKey: "myValue")
|
||||
encoder.encodeInt32(self.peerValue, forKey: "peerValue")
|
||||
if let myValue = self.myValue {
|
||||
encoder.encodeInt32(myValue, forKey: "myValue")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "myValue")
|
||||
}
|
||||
if let peerValue = self.peerValue {
|
||||
encoder.encodeInt32(peerValue, forKey: "peerValue")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "peerValue")
|
||||
}
|
||||
encoder.encodeInt32(self.isGlobal ? 1 : 0, forKey: "isGlobal")
|
||||
}
|
||||
|
||||
public var effectiveValue: Int32? {
|
||||
if let myValue = self.myValue, let peerValue = self.peerValue {
|
||||
return min(myValue, peerValue)
|
||||
} else if let myValue = self.myValue {
|
||||
return myValue
|
||||
} else if let peerValue = self.peerValue {
|
||||
return peerValue
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case unknown
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
|
||||
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
|
||||
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
|
||||
dict[-66811386] = { return Api.ChatFull.parse_channelFull($0) }
|
||||
dict[-500874592] = { return Api.ChatFull.parse_chatFull($0) }
|
||||
dict[-66811386] = { return Api.ChatFull.parse_channelFull($0) }
|
||||
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
|
||||
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
|
||||
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
|
||||
|
|
@ -33,20 +33,20 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1216809369] = { return Api.PageBlock.parse_pageBlockFooter($0) }
|
||||
dict[-618614392] = { return Api.PageBlock.parse_pageBlockDivider($0) }
|
||||
dict[-837994576] = { return Api.PageBlock.parse_pageBlockAnchor($0) }
|
||||
dict[-454524911] = { return Api.PageBlock.parse_pageBlockList($0) }
|
||||
dict[641563686] = { return Api.PageBlock.parse_pageBlockBlockquote($0) }
|
||||
dict[1329878739] = { return Api.PageBlock.parse_pageBlockPullquote($0) }
|
||||
dict[972174080] = { return Api.PageBlock.parse_pageBlockCover($0) }
|
||||
dict[-283684427] = { return Api.PageBlock.parse_pageBlockChannel($0) }
|
||||
dict[504660880] = { return Api.PageBlock.parse_pageBlockKicker($0) }
|
||||
dict[-1085412734] = { return Api.PageBlock.parse_pageBlockTable($0) }
|
||||
dict[391759200] = { return Api.PageBlock.parse_pageBlockPhoto($0) }
|
||||
dict[2089805750] = { return Api.PageBlock.parse_pageBlockVideo($0) }
|
||||
dict[-2143067670] = { return Api.PageBlock.parse_pageBlockAudio($0) }
|
||||
dict[972174080] = { return Api.PageBlock.parse_pageBlockCover($0) }
|
||||
dict[-1468953147] = { return Api.PageBlock.parse_pageBlockEmbed($0) }
|
||||
dict[-229005301] = { return Api.PageBlock.parse_pageBlockEmbedPost($0) }
|
||||
dict[1705048653] = { return Api.PageBlock.parse_pageBlockCollage($0) }
|
||||
dict[52401552] = { return Api.PageBlock.parse_pageBlockSlideshow($0) }
|
||||
dict[-454524911] = { return Api.PageBlock.parse_pageBlockList($0) }
|
||||
dict[-283684427] = { return Api.PageBlock.parse_pageBlockChannel($0) }
|
||||
dict[-2143067670] = { return Api.PageBlock.parse_pageBlockAudio($0) }
|
||||
dict[504660880] = { return Api.PageBlock.parse_pageBlockKicker($0) }
|
||||
dict[-1085412734] = { return Api.PageBlock.parse_pageBlockTable($0) }
|
||||
dict[-1702174239] = { return Api.PageBlock.parse_pageBlockOrderedList($0) }
|
||||
dict[1987480557] = { return Api.PageBlock.parse_pageBlockDetails($0) }
|
||||
dict[370236054] = { return Api.PageBlock.parse_pageBlockRelatedArticles($0) }
|
||||
|
|
@ -66,10 +66,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[590459437] = { return Api.Photo.parse_photoEmpty($0) }
|
||||
dict[-82216347] = { return Api.Photo.parse_photo($0) }
|
||||
dict[-1683826688] = { return Api.Chat.parse_chatEmpty($0) }
|
||||
dict[120753115] = { return Api.Chat.parse_chatForbidden($0) }
|
||||
dict[681420594] = { return Api.Chat.parse_channelForbidden($0) }
|
||||
dict[1004149726] = { return Api.Chat.parse_chat($0) }
|
||||
dict[120753115] = { return Api.Chat.parse_chatForbidden($0) }
|
||||
dict[-753232354] = { return Api.Chat.parse_channel($0) }
|
||||
dict[681420594] = { return Api.Chat.parse_channelForbidden($0) }
|
||||
dict[1202287072] = { return Api.StatsURL.parse_statsURL($0) }
|
||||
dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) }
|
||||
dict[-540871282] = { return Api.ChatInvite.parse_chatInvite($0) }
|
||||
|
|
@ -113,10 +113,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[414687501] = { return Api.DcOption.parse_dcOption($0) }
|
||||
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
|
||||
dict[-1705233435] = { return Api.account.PasswordSettings.parse_passwordSettings($0) }
|
||||
dict[-1945767479] = { return Api.help.SupportName.parse_supportName($0) }
|
||||
dict[-288727837] = { return Api.LangPackLanguage.parse_langPackLanguage($0) }
|
||||
dict[-399391402] = { return Api.VideoSize.parse_videoSize($0) }
|
||||
dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) }
|
||||
dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) }
|
||||
dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) }
|
||||
dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) }
|
||||
dict[-815649386] = { return Api.PeerHistoryTTL.parse_peerHistoryTTLPM($0) }
|
||||
dict[1041354473] = { return Api.PeerHistoryTTL.parse_peerHistoryTTL($0) }
|
||||
|
|
@ -141,10 +142,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1690108678] = { return Api.InputEncryptedFile.parse_inputEncryptedFileUploaded($0) }
|
||||
dict[1511503333] = { return Api.InputEncryptedFile.parse_inputEncryptedFile($0) }
|
||||
dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
|
||||
dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) }
|
||||
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) }
|
||||
dict[1690708501] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[2023913382] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
|
||||
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
|
||||
dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) }
|
||||
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
|
||||
dict[-855308010] = { return Api.auth.Authorization.parse_authorization($0) }
|
||||
dict[1148485274] = { return Api.auth.Authorization.parse_authorizationSignUpRequired($0) }
|
||||
|
|
@ -154,6 +157,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1160714821] = { return Api.Peer.parse_peerChat($0) }
|
||||
dict[-1109531342] = { return Api.Peer.parse_peerChannel($0) }
|
||||
dict[410107472] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) }
|
||||
dict[572915951] = { return Api.messages.ExportedChatInvite.parse_exportedChatInviteReplaced($0) }
|
||||
dict[-1868808300] = { return Api.PaymentRequestedInfo.parse_paymentRequestedInfo($0) }
|
||||
dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) }
|
||||
dict[-306628279] = { return Api.UserStatus.parse_userStatusOnline($0) }
|
||||
|
|
@ -207,12 +211,14 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) }
|
||||
dict[-298113238] = { return Api.Update.parse_updatePrivacy($0) }
|
||||
dict[314130811] = { return Api.Update.parse_updateUserPhone($0) }
|
||||
dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) }
|
||||
dict[791617983] = { return Api.Update.parse_updateReadHistoryOutbox($0) }
|
||||
dict[2139689491] = { return Api.Update.parse_updateWebPage($0) }
|
||||
dict[1757493555] = { return Api.Update.parse_updateReadMessagesContents($0) }
|
||||
dict[-352032773] = { return Api.Update.parse_updateChannelTooLong($0) }
|
||||
dict[-1227598250] = { return Api.Update.parse_updateChannel($0) }
|
||||
dict[1656358105] = { return Api.Update.parse_updateNewChannelMessage($0) }
|
||||
dict[856380452] = { return Api.Update.parse_updateReadChannelInbox($0) }
|
||||
dict[-1015733815] = { return Api.Update.parse_updateDeleteChannelMessages($0) }
|
||||
dict[-1734268085] = { return Api.Update.parse_updateChannelMessageViews($0) }
|
||||
dict[-1232070311] = { return Api.Update.parse_updateChatParticipantAdmin($0) }
|
||||
|
|
@ -220,6 +226,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[196268545] = { return Api.Update.parse_updateStickerSetsOrder($0) }
|
||||
dict[1135492588] = { return Api.Update.parse_updateStickerSets($0) }
|
||||
dict[-1821035490] = { return Api.Update.parse_updateSavedGifs($0) }
|
||||
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
|
||||
dict[239663460] = { return Api.Update.parse_updateBotInlineSend($0) }
|
||||
dict[457133559] = { return Api.Update.parse_updateEditChannelMessage($0) }
|
||||
dict[-415938591] = { return Api.Update.parse_updateBotCallbackQuery($0) }
|
||||
|
|
@ -232,25 +239,23 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1574314746] = { return Api.Update.parse_updateConfig($0) }
|
||||
dict[861169551] = { return Api.Update.parse_updatePtsChanged($0) }
|
||||
dict[1081547008] = { return Api.Update.parse_updateChannelWebPage($0) }
|
||||
dict[1852826908] = { return Api.Update.parse_updateDialogPinned($0) }
|
||||
dict[-99664734] = { return Api.Update.parse_updatePinnedDialogs($0) }
|
||||
dict[-2095595325] = { return Api.Update.parse_updateBotWebhookJSON($0) }
|
||||
dict[-1684914010] = { return Api.Update.parse_updateBotWebhookJSONQuery($0) }
|
||||
dict[-523384512] = { return Api.Update.parse_updateBotShippingQuery($0) }
|
||||
dict[1563376297] = { return Api.Update.parse_updateBotPrecheckoutQuery($0) }
|
||||
dict[-1425052898] = { return Api.Update.parse_updatePhoneCall($0) }
|
||||
dict[1180041828] = { return Api.Update.parse_updateLangPackTooLong($0) }
|
||||
dict[1442983757] = { return Api.Update.parse_updateLangPack($0) }
|
||||
dict[-451831443] = { return Api.Update.parse_updateFavedStickers($0) }
|
||||
dict[-1987495099] = { return Api.Update.parse_updateChannelReadMessagesContents($0) }
|
||||
dict[1887741886] = { return Api.Update.parse_updateContactsReset($0) }
|
||||
dict[1893427255] = { return Api.Update.parse_updateChannelAvailableMessages($0) }
|
||||
dict[-513517117] = { return Api.Update.parse_updateDialogUnreadMark($0) }
|
||||
dict[1180041828] = { return Api.Update.parse_updateLangPackTooLong($0) }
|
||||
dict[-1398708869] = { return Api.Update.parse_updateMessagePoll($0) }
|
||||
dict[1421875280] = { return Api.Update.parse_updateChatDefaultBannedRights($0) }
|
||||
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
||||
dict[1852826908] = { return Api.Update.parse_updateDialogPinned($0) }
|
||||
dict[-99664734] = { return Api.Update.parse_updatePinnedDialogs($0) }
|
||||
dict[856380452] = { return Api.Update.parse_updateReadChannelInbox($0) }
|
||||
dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) }
|
||||
dict[1786671974] = { return Api.Update.parse_updatePeerSettings($0) }
|
||||
dict[-1263546448] = { return Api.Update.parse_updatePeerLocated($0) }
|
||||
dict[967122427] = { return Api.Update.parse_updateNewScheduledMessage($0) }
|
||||
|
|
@ -274,16 +279,15 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[321954198] = { return Api.Update.parse_updateChat($0) }
|
||||
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
||||
dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) }
|
||||
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
|
||||
dict[19291112] = { return Api.Update.parse_updatePeerHistoryTTL($0) }
|
||||
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
|
||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
|
||||
dict[-1557620115] = { return Api.ChannelParticipant.parse_channelParticipantSelf($0) }
|
||||
dict[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) }
|
||||
dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) }
|
||||
dict[1149094475] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) }
|
||||
dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) }
|
||||
dict[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) }
|
||||
dict[-1010402965] = { return Api.ChannelParticipant.parse_channelParticipantLeft($0) }
|
||||
dict[-1567730343] = { return Api.MessageUserVote.parse_messageUserVote($0) }
|
||||
dict[909603888] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) }
|
||||
|
|
@ -292,9 +296,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-513392236] = { return Api.contacts.Blocked.parse_blockedSlice($0) }
|
||||
dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) }
|
||||
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
|
||||
dict[-994444869] = { return Api.Error.parse_error($0) }
|
||||
dict[-1560655744] = { return Api.KeyboardButton.parse_keyboardButton($0) }
|
||||
dict[629866245] = { return Api.KeyboardButton.parse_keyboardButtonUrl($0) }
|
||||
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
|
||||
dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
|
||||
dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
|
||||
dict[90744648] = { return Api.KeyboardButton.parse_keyboardButtonSwitchInline($0) }
|
||||
|
|
@ -303,7 +307,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) }
|
||||
dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) }
|
||||
dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
|
||||
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
|
||||
dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) }
|
||||
dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) }
|
||||
dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) }
|
||||
|
|
@ -325,33 +328,32 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1881892265] = { return Api.account.WallPapers.parse_wallPapers($0) }
|
||||
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
||||
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
||||
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
|
||||
dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) }
|
||||
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
|
||||
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
|
||||
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
|
||||
dict[1251338318] = { return Api.InputNotifyPeer.parse_inputNotifyChats($0) }
|
||||
dict[-1311015810] = { return Api.InputNotifyPeer.parse_inputNotifyBroadcasts($0) }
|
||||
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
|
||||
dict[-317144808] = { return Api.EncryptedMessage.parse_encryptedMessage($0) }
|
||||
dict[594758406] = { return Api.EncryptedMessage.parse_encryptedMessageService($0) }
|
||||
dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) }
|
||||
dict[-1268741783] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsAdmins($0) }
|
||||
dict[-1548400251] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsKicked($0) }
|
||||
dict[-1328445861] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsBots($0) }
|
||||
dict[338142689] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsBanned($0) }
|
||||
dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) }
|
||||
dict[-1548400251] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsKicked($0) }
|
||||
dict[-1150621555] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsContacts($0) }
|
||||
dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) }
|
||||
dict[-350980120] = { return Api.WebPage.parse_webPageEmpty($0) }
|
||||
dict[-981018084] = { return Api.WebPage.parse_webPagePending($0) }
|
||||
dict[-392411726] = { return Api.WebPage.parse_webPage($0) }
|
||||
dict[1930545681] = { return Api.WebPage.parse_webPageNotModified($0) }
|
||||
dict[1036876423] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageText($0) }
|
||||
dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) }
|
||||
dict[864077702] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaAuto($0) }
|
||||
dict[1036876423] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageText($0) }
|
||||
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
|
||||
dict[1098628881] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaVenue($0) }
|
||||
dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) }
|
||||
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
|
||||
dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) }
|
||||
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
|
||||
dict[1088567208] = { return Api.StickerSet.parse_stickerSet($0) }
|
||||
dict[-1111085620] = { return Api.messages.ExportedChatInvites.parse_exportedChatInvites($0) }
|
||||
|
|
@ -380,21 +382,20 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[2131196633] = { return Api.contacts.ResolvedPeer.parse_resolvedPeer($0) }
|
||||
dict[-1964327229] = { return Api.SecureData.parse_secureData($0) }
|
||||
dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) }
|
||||
dict[-104578748] = { return Api.InputMedia.parse_inputMediaGeoPoint($0) }
|
||||
dict[1212395773] = { return Api.InputMedia.parse_inputMediaGifExternal($0) }
|
||||
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
|
||||
dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) }
|
||||
dict[-186607933] = { return Api.InputMedia.parse_inputMediaInvoice($0) }
|
||||
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
|
||||
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
|
||||
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
|
||||
dict[-104578748] = { return Api.InputMedia.parse_inputMediaGeoPoint($0) }
|
||||
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
|
||||
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
|
||||
dict[860303448] = { return Api.InputMedia.parse_inputMediaDocument($0) }
|
||||
dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) }
|
||||
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
|
||||
dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) }
|
||||
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
|
||||
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
|
||||
dict[-186607933] = { return Api.InputMedia.parse_inputMediaInvoice($0) }
|
||||
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
|
||||
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
|
||||
dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) }
|
||||
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
|
||||
dict[860303448] = { return Api.InputMedia.parse_inputMediaDocument($0) }
|
||||
dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) }
|
||||
dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) }
|
||||
dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) }
|
||||
|
|
@ -407,8 +408,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
|
||||
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
||||
dict[1648543603] = { return Api.FileHash.parse_fileHash($0) }
|
||||
dict[400266251] = { return Api.BotInlineResult.parse_botInlineMediaResult($0) }
|
||||
dict[295067450] = { return Api.BotInlineResult.parse_botInlineResult($0) }
|
||||
dict[400266251] = { return Api.BotInlineResult.parse_botInlineMediaResult($0) }
|
||||
dict[911761060] = { return Api.messages.BotCallbackAnswer.parse_botCallbackAnswer($0) }
|
||||
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
||||
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
|
||||
|
|
@ -431,6 +432,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-421545947] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeTitle($0) }
|
||||
dict[1427671598] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeAbout($0) }
|
||||
dict[1783299128] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeUsername($0) }
|
||||
dict[1129042607] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangePhoto($0) }
|
||||
dict[460916654] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleInvites($0) }
|
||||
dict[648939889] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleSignatures($0) }
|
||||
dict[-370660328] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionUpdatePinned($0) }
|
||||
|
|
@ -445,7 +447,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1599903217] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionTogglePreHistoryHidden($0) }
|
||||
dict[771095562] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionDefaultBannedRights($0) }
|
||||
dict[-1895328189] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionStopPoll($0) }
|
||||
dict[1129042607] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangePhoto($0) }
|
||||
dict[-1569748965] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeLinkedChat($0) }
|
||||
dict[241923758] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeLocation($0) }
|
||||
dict[1401984889] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleSlowMode($0) }
|
||||
|
|
@ -454,6 +455,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-115071790] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantMute($0) }
|
||||
dict[-431740480] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantUnmute($0) }
|
||||
dict[1456906823] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleGroupCallSetting($0) }
|
||||
dict[1557846647] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoinByInvite($0) }
|
||||
dict[1515256996] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteDelete($0) }
|
||||
dict[1091179342] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteRevoke($0) }
|
||||
dict[-384910503] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteEdit($0) }
|
||||
dict[1048537159] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantVolume($0) }
|
||||
dict[-543777747] = { return Api.auth.ExportedAuthorization.parse_exportedAuthorization($0) }
|
||||
dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) }
|
||||
dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) }
|
||||
|
|
@ -464,6 +470,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) }
|
||||
dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) }
|
||||
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
|
||||
dict[1968737087] = { return Api.InputClientProxy.parse_inputClientProxy($0) }
|
||||
dict[649453030] = { return Api.messages.MessageEditData.parse_messageEditData($0) }
|
||||
dict[-886477832] = { return Api.LabeledPrice.parse_labeledPrice($0) }
|
||||
dict[-438840932] = { return Api.messages.ChatFull.parse_chatFull($0) }
|
||||
|
|
@ -477,15 +484,15 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1490799288] = { return Api.ReportReason.parse_inputReportReasonSpam($0) }
|
||||
dict[505595789] = { return Api.ReportReason.parse_inputReportReasonViolence($0) }
|
||||
dict[777640226] = { return Api.ReportReason.parse_inputReportReasonPornography($0) }
|
||||
dict[-1376497949] = { return Api.ReportReason.parse_inputReportReasonChildAbuse($0) }
|
||||
dict[-512463606] = { return Api.ReportReason.parse_inputReportReasonOther($0) }
|
||||
dict[-1685456582] = { return Api.ReportReason.parse_inputReportReasonCopyright($0) }
|
||||
dict[-1376497949] = { return Api.ReportReason.parse_inputReportReasonChildAbuse($0) }
|
||||
dict[-606798099] = { return Api.ReportReason.parse_inputReportReasonGeoIrrelevant($0) }
|
||||
dict[-170010905] = { return Api.ReportReason.parse_inputReportReasonFake($0) }
|
||||
dict[-247351839] = { return Api.InputEncryptedChat.parse_inputEncryptedChat($0) }
|
||||
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
|
||||
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
|
||||
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
|
||||
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
|
||||
dict[-1014526429] = { return Api.help.Country.parse_country($0) }
|
||||
dict[418631927] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
|
||||
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
|
||||
|
|
@ -501,13 +508,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-2036501105] = { return Api.SecureValueError.parse_secureValueError($0) }
|
||||
dict[-1592506512] = { return Api.SecureValueError.parse_secureValueErrorTranslationFile($0) }
|
||||
dict[878931416] = { return Api.SecureValueError.parse_secureValueErrorTranslationFiles($0) }
|
||||
dict[-6249322] = { return Api.InputStickerSetItem.parse_inputStickerSetItem($0) }
|
||||
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
||||
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
|
||||
dict[-1613493288] = { return Api.NotifyPeer.parse_notifyPeer($0) }
|
||||
dict[-1261946036] = { return Api.NotifyPeer.parse_notifyUsers($0) }
|
||||
dict[-1073230141] = { return Api.NotifyPeer.parse_notifyChats($0) }
|
||||
dict[-703403793] = { return Api.NotifyPeer.parse_notifyBroadcasts($0) }
|
||||
dict[-582464156] = { return Api.wallet.KeySecretSalt.parse_secretSalt($0) }
|
||||
dict[1335282456] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyStatusTimestamp($0) }
|
||||
dict[-1107622874] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyChatInvite($0) }
|
||||
dict[-88417185] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyPhoneCall($0) }
|
||||
|
|
@ -529,13 +536,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-316748368] = { return Api.SecureValueHash.parse_secureValueHash($0) }
|
||||
dict[1722485756] = { return Api.phone.GroupCall.parse_groupCall($0) }
|
||||
dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) }
|
||||
dict[-2128698738] = { return Api.auth.CheckedPhone.parse_checkedPhone($0) }
|
||||
dict[-1188055347] = { return Api.PageListItem.parse_pageListItemText($0) }
|
||||
dict[635466748] = { return Api.PageListItem.parse_pageListItemBlocks($0) }
|
||||
dict[-386039788] = { return Api.PeerBlocked.parse_peerBlocked($0) }
|
||||
dict[-1182234929] = { return Api.InputUser.parse_inputUserEmpty($0) }
|
||||
dict[-138301121] = { return Api.InputUser.parse_inputUserSelf($0) }
|
||||
dict[-668391402] = { return Api.InputUser.parse_inputUser($0) }
|
||||
dict[756118935] = { return Api.InputUser.parse_inputUserFromMessage($0) }
|
||||
dict[-1738178803] = { return Api.Page.parse_page($0) }
|
||||
dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) }
|
||||
dict[-875679776] = { return Api.StatsPercentValue.parse_statsPercentValue($0) }
|
||||
|
|
@ -568,51 +575,50 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1502174430] = { return Api.InputMessage.parse_inputMessageID($0) }
|
||||
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
|
||||
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
|
||||
dict[-1392895362] = { return Api.InputMessage.parse_inputMessageCallbackQuery($0) }
|
||||
dict[-58224696] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) }
|
||||
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }
|
||||
dict[-275956116] = { return Api.messages.AffectedFoundMessages.parse_affectedFoundMessages($0) }
|
||||
dict[-1567175714] = { return Api.MessageFwdAuthor.parse_messageFwdAuthor($0) }
|
||||
dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) }
|
||||
dict[-1963717851] = { return Api.WallPaper.parse_wallPaperNoFile($0) }
|
||||
dict[-1938715001] = { return Api.messages.Messages.parse_messages($0) }
|
||||
dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) }
|
||||
dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) }
|
||||
dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) }
|
||||
dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) }
|
||||
dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) }
|
||||
dict[-1022713000] = { return Api.Invoice.parse_invoice($0) }
|
||||
dict[1933519201] = { return Api.PeerSettings.parse_peerSettings($0) }
|
||||
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
|
||||
dict[480546647] = { return Api.InputChatPhoto.parse_inputChatPhotoEmpty($0) }
|
||||
dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) }
|
||||
dict[-968723890] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) }
|
||||
dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) }
|
||||
dict[-1228606141] = { return Api.messages.MessageViews.parse_messageViews($0) }
|
||||
dict[375566091] = { return Api.messages.HistoryImport.parse_historyImport($0) }
|
||||
dict[-368917890] = { return Api.PaymentCharge.parse_paymentCharge($0) }
|
||||
dict[-1387279939] = { return Api.MessageInteractionCounters.parse_messageInteractionCounters($0) }
|
||||
dict[-1107852396] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
|
||||
dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) }
|
||||
dict[-84936653] = { return Api.Updates.parse_updateShortMessage($0) }
|
||||
dict[290961496] = { return Api.Updates.parse_updateShortChatMessage($0) }
|
||||
dict[2027216577] = { return Api.Updates.parse_updateShort($0) }
|
||||
dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) }
|
||||
dict[1957577280] = { return Api.Updates.parse_updates($0) }
|
||||
dict[-84936653] = { return Api.Updates.parse_updateShortMessage($0) }
|
||||
dict[290961496] = { return Api.Updates.parse_updateShortChatMessage($0) }
|
||||
dict[-1877614335] = { return Api.Updates.parse_updateShortSentMessage($0) }
|
||||
dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
|
||||
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
|
||||
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
|
||||
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
|
||||
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
|
||||
dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) }
|
||||
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
|
||||
dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
|
||||
dict[-1557277184] = { return Api.MessageMedia.parse_messageMediaWebPage($0) }
|
||||
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
|
||||
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
|
||||
dict[-2074799289] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
|
||||
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
|
||||
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
|
||||
dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
|
||||
dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) }
|
||||
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
|
||||
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
|
||||
dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) }
|
||||
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
|
||||
dict[-842892769] = { return Api.PaymentSavedCredentials.parse_paymentSavedCredentialsCard($0) }
|
||||
dict[1450380236] = { return Api.Null.parse_null($0) }
|
||||
dict[1923290508] = { return Api.auth.CodeType.parse_codeTypeSms($0) }
|
||||
dict[1948046307] = { return Api.auth.CodeType.parse_codeTypeCall($0) }
|
||||
dict[577556219] = { return Api.auth.CodeType.parse_codeTypeFlashCall($0) }
|
||||
|
|
@ -642,21 +648,21 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1231326505] = { return Api.messages.ChatAdminsWithInvites.parse_chatAdminsWithInvites($0) }
|
||||
dict[-1729618630] = { return Api.BotInfo.parse_botInfo($0) }
|
||||
dict[-1519637954] = { return Api.updates.State.parse_state($0) }
|
||||
dict[372165663] = { return Api.FoundGif.parse_foundGif($0) }
|
||||
dict[-1670052855] = { return Api.FoundGif.parse_foundGifCached($0) }
|
||||
dict[537022650] = { return Api.User.parse_userEmpty($0) }
|
||||
dict[-1820043071] = { return Api.User.parse_user($0) }
|
||||
dict[678405636] = { return Api.Message.parse_messageService($0) }
|
||||
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
|
||||
dict[-1125940270] = { return Api.Message.parse_message($0) }
|
||||
dict[678405636] = { return Api.Message.parse_messageService($0) }
|
||||
dict[831924812] = { return Api.StatsGroupTopInviter.parse_statsGroupTopInviter($0) }
|
||||
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
|
||||
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
|
||||
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
|
||||
dict[-876089816] = { return Api.InputFileLocation.parse_inputSecureFileLocation($0) }
|
||||
dict[-539317279] = { return Api.InputFileLocation.parse_inputFileLocation($0) }
|
||||
dict[1075322878] = { return Api.InputFileLocation.parse_inputPhotoFileLocation($0) }
|
||||
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
|
||||
dict[-1160743548] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) }
|
||||
dict[-876089816] = { return Api.InputFileLocation.parse_inputSecureFileLocation($0) }
|
||||
dict[700340377] = { return Api.InputFileLocation.parse_inputTakeoutFileLocation($0) }
|
||||
dict[1075322878] = { return Api.InputFileLocation.parse_inputPhotoFileLocation($0) }
|
||||
dict[-667654413] = { return Api.InputFileLocation.parse_inputPhotoLegacyFileLocation($0) }
|
||||
dict[668375447] = { return Api.InputFileLocation.parse_inputPeerPhotoFileLocation($0) }
|
||||
dict[230353641] = { return Api.InputFileLocation.parse_inputStickerSetThumb($0) }
|
||||
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
|
||||
|
|
@ -703,8 +709,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1816636575] = { return Api.LangPackString.parse_langPackStringPluralized($0) }
|
||||
dict[695856818] = { return Api.LangPackString.parse_langPackStringDeleted($0) }
|
||||
dict[-1036396922] = { return Api.InputWebFileLocation.parse_inputWebFileLocation($0) }
|
||||
dict[1430205163] = { return Api.InputWebFileLocation.parse_inputWebFileGeoMessageLocation($0) }
|
||||
dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) }
|
||||
dict[-1275374751] = { return Api.EmojiLanguage.parse_emojiLanguage($0) }
|
||||
dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
|
||||
dict[-1012849566] = { return Api.BaseTheme.parse_baseThemeClassic($0) }
|
||||
dict[-69724536] = { return Api.BaseTheme.parse_baseThemeDay($0) }
|
||||
|
|
@ -716,7 +722,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1777752804] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotos($0) }
|
||||
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
|
||||
dict[1458172132] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotoVideo($0) }
|
||||
dict[-648121413] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotoVideoDocuments($0) }
|
||||
dict[-1629621880] = { return Api.MessagesFilter.parse_inputMessagesFilterDocument($0) }
|
||||
dict[2129714567] = { return Api.MessagesFilter.parse_inputMessagesFilterUrl($0) }
|
||||
dict[-3644025] = { return Api.MessagesFilter.parse_inputMessagesFilterGif($0) }
|
||||
|
|
@ -727,7 +732,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[2054952868] = { return Api.MessagesFilter.parse_inputMessagesFilterRoundVoice($0) }
|
||||
dict[-1253451181] = { return Api.MessagesFilter.parse_inputMessagesFilterRoundVideo($0) }
|
||||
dict[-1040652646] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentions($0) }
|
||||
dict[1187706024] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentionsUnread($0) }
|
||||
dict[-419271411] = { return Api.MessagesFilter.parse_inputMessagesFilterGeo($0) }
|
||||
dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($0) }
|
||||
dict[464520273] = { return Api.MessagesFilter.parse_inputMessagesFilterPinned($0) }
|
||||
|
|
@ -739,13 +743,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[594408994] = { return Api.EmojiKeyword.parse_emojiKeywordDeleted($0) }
|
||||
dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) }
|
||||
dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) }
|
||||
dict[1984136919] = { return Api.wallet.LiteResponse.parse_liteResponse($0) }
|
||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
|
||||
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
|
||||
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
|
||||
dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
|
||||
dict[-1970903652] = { return Api.BotInlineMessage.parse_botInlineMessageMediaVenue($0) }
|
||||
dict[416402882] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) }
|
||||
dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
|
||||
dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) }
|
||||
dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) }
|
||||
dict[-209768682] = { return Api.messages.FavedStickers.parse_favedStickers($0) }
|
||||
|
|
@ -776,10 +779,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-732254058] = { return Api.PasswordKdfAlgo.parse_passwordKdfAlgoUnknown($0) }
|
||||
dict[982592842] = { return Api.PasswordKdfAlgo.parse_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow($0) }
|
||||
dict[-1390001672] = { return Api.account.Password.parse_password($0) }
|
||||
dict[-2000710887] = { return Api.InputBotInlineResult.parse_inputBotInlineResult($0) }
|
||||
dict[-1462213465] = { return Api.InputBotInlineResult.parse_inputBotInlineResultPhoto($0) }
|
||||
dict[-459324] = { return Api.InputBotInlineResult.parse_inputBotInlineResultDocument($0) }
|
||||
dict[1336154098] = { return Api.InputBotInlineResult.parse_inputBotInlineResultGame($0) }
|
||||
dict[-2000710887] = { return Api.InputBotInlineResult.parse_inputBotInlineResult($0) }
|
||||
dict[1352683077] = { return Api.account.PrivacyRules.parse_privacyRules($0) }
|
||||
dict[-123988] = { return Api.PrivacyRule.parse_privacyValueAllowContacts($0) }
|
||||
dict[1698855810] = { return Api.PrivacyRule.parse_privacyValueAllowAll($0) }
|
||||
|
|
@ -815,7 +818,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1730095465] = { return Api.MessageAction.parse_messageActionGeoProximityReached($0) }
|
||||
dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) }
|
||||
dict[1991897370] = { return Api.MessageAction.parse_messageActionInviteToGroupCall($0) }
|
||||
dict[-1441072131] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) }
|
||||
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
|
||||
dict[462375633] = { return Api.PhoneCall.parse_phoneCallWaiting($0) }
|
||||
dict[-2014659757] = { return Api.PhoneCall.parse_phoneCallRequested($0) }
|
||||
|
|
@ -827,8 +829,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) }
|
||||
dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) }
|
||||
dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) }
|
||||
dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) }
|
||||
dict[475467473] = { return Api.WebDocument.parse_webDocument($0) }
|
||||
dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) }
|
||||
dict[42930452] = { return Api.Theme.parse_theme($0) }
|
||||
dict[-1290580579] = { return Api.contacts.Found.parse_found($0) }
|
||||
dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) }
|
||||
|
|
@ -841,8 +843,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1775479590] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) }
|
||||
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
|
||||
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
|
||||
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
|
||||
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
|
||||
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
|
||||
dict[-1815339214] = { return Api.help.CountriesList.parse_countriesListNotModified($0) }
|
||||
dict[-2016381538] = { return Api.help.CountriesList.parse_countriesList($0) }
|
||||
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
|
||||
|
|
@ -880,8 +882,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1730311882] = { return Api.PageListOrderedItem.parse_pageListOrderedItemBlocks($0) }
|
||||
dict[-1417756512] = { return Api.EncryptedChat.parse_encryptedChatEmpty($0) }
|
||||
dict[1006044124] = { return Api.EncryptedChat.parse_encryptedChatWaiting($0) }
|
||||
dict[-94974410] = { return Api.EncryptedChat.parse_encryptedChat($0) }
|
||||
dict[1651608194] = { return Api.EncryptedChat.parse_encryptedChatRequested($0) }
|
||||
dict[-94974410] = { return Api.EncryptedChat.parse_encryptedChat($0) }
|
||||
dict[505183301] = { return Api.EncryptedChat.parse_encryptedChatDiscarded($0) }
|
||||
dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) }
|
||||
dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) }
|
||||
|
|
@ -1010,6 +1012,8 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.account.PasswordSettings:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.SupportName:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.LangPackLanguage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.VideoSize:
|
||||
|
|
@ -1036,12 +1040,16 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputEncryptedFile:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.account.Takeout:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.InactiveChats:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.GroupCallParticipant:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.SentEncryptedMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SavedContact:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ExportedMessageLink:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.auth.Authorization:
|
||||
|
|
@ -1080,8 +1088,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputDialogPeer:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Error:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.KeyboardButton:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ContactStatus:
|
||||
|
|
@ -1104,8 +1110,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputTheme:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.FoundGifs:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.FileLocation:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Poll:
|
||||
|
|
@ -1202,6 +1206,8 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ChatBannedRights:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputClientProxy:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.MessageEditData:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.LabeledPrice:
|
||||
|
|
@ -1240,12 +1246,12 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SecureValueError:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputStickerSetItem:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.PromoData:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.NotifyPeer:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.wallet.KeySecretSalt:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputPrivacyKey:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.RecentMeUrls:
|
||||
|
|
@ -1268,8 +1274,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.SearchCounter:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.auth.CheckedPhone:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.PageListItem:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.PeerBlocked:
|
||||
|
|
@ -1328,8 +1332,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.AffectedFoundMessages:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageFwdAuthor:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.WallPaper:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.Messages:
|
||||
|
|
@ -1362,8 +1364,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.PaymentSavedCredentials:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Null:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.auth.CodeType:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.DocumentAttribute:
|
||||
|
|
@ -1392,8 +1392,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.updates.State:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.FoundGif:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.User:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Message:
|
||||
|
|
@ -1458,6 +1456,8 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputWebFileLocation:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.EmojiLanguage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageFwdHeader:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BaseTheme:
|
||||
|
|
@ -1474,8 +1474,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.upload.CdnFile:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.wallet.LiteResponse:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.InviteText:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BotInlineMessage:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1075,42 +1075,6 @@ public struct auth {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum CheckedPhone: TypeConstructorDescription {
|
||||
case checkedPhone(phoneRegistered: Api.Bool)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .checkedPhone(let phoneRegistered):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2128698738)
|
||||
}
|
||||
phoneRegistered.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .checkedPhone(let phoneRegistered):
|
||||
return ("checkedPhone", [("phoneRegistered", phoneRegistered)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_checkedPhone(_ reader: BufferReader) -> CheckedPhone? {
|
||||
var _1: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.auth.CheckedPhone.checkedPhone(phoneRegistered: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum SentCode: TypeConstructorDescription {
|
||||
case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?)
|
||||
|
|
@ -1774,18 +1738,46 @@ public struct contacts {
|
|||
}
|
||||
public extension Api {
|
||||
public struct help {
|
||||
public enum AppUpdate: TypeConstructorDescription {
|
||||
case noAppUpdate
|
||||
case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?)
|
||||
public enum SupportName: TypeConstructorDescription {
|
||||
case supportName(name: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .noAppUpdate:
|
||||
case .supportName(let name):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1000708810)
|
||||
buffer.appendInt32(-1945767479)
|
||||
}
|
||||
|
||||
serializeString(name, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .supportName(let name):
|
||||
return ("supportName", [("name", name)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_supportName(_ reader: BufferReader) -> SupportName? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.help.SupportName.supportName(name: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum AppUpdate: TypeConstructorDescription {
|
||||
case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?)
|
||||
case noAppUpdate
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url):
|
||||
if boxed {
|
||||
buffer.appendInt32(497489295)
|
||||
|
|
@ -1801,22 +1793,25 @@ public struct help {
|
|||
}
|
||||
if Int(flags) & Int(1 << 1) != 0 {document!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .noAppUpdate:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1000708810)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .noAppUpdate:
|
||||
return ("noAppUpdate", [])
|
||||
case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url):
|
||||
return ("appUpdate", [("flags", flags), ("id", id), ("version", version), ("text", text), ("entities", entities), ("document", document), ("url", url)])
|
||||
case .noAppUpdate:
|
||||
return ("noAppUpdate", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_noAppUpdate(_ reader: BufferReader) -> AppUpdate? {
|
||||
return Api.help.AppUpdate.noAppUpdate
|
||||
}
|
||||
public static func parse_appUpdate(_ reader: BufferReader) -> AppUpdate? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
|
@ -1850,6 +1845,9 @@ public struct help {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_noAppUpdate(_ reader: BufferReader) -> AppUpdate? {
|
||||
return Api.help.AppUpdate.noAppUpdate
|
||||
}
|
||||
|
||||
}
|
||||
public enum PassportConfig: TypeConstructorDescription {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -305,9 +305,9 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
|||
var effectiveLevel: Float = 0.0
|
||||
var audioLevels = audioLevels
|
||||
if !strongSelf.currentIsMuted {
|
||||
audioLevels.append((PeerId(0), myAudioLevel, true))
|
||||
audioLevels.append((PeerId(0), 0, myAudioLevel, true))
|
||||
}
|
||||
effectiveLevel = audioLevels.map { $0.1 }.max() ?? 0.0
|
||||
effectiveLevel = audioLevels.map { $0.2 }.max() ?? 0.0
|
||||
strongSelf.backgroundNode.audioLevel = effectiveLevel
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -928,6 +928,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
|
|
@ -997,6 +998,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,27 @@ import UniversalMediaPlayer
|
|||
import AccountContext
|
||||
import DeviceProximity
|
||||
|
||||
private extension GroupCallParticipantsContext.Participant {
|
||||
var allSsrcs: Set<UInt32> {
|
||||
var participantSsrcs = Set<UInt32>()
|
||||
participantSsrcs.insert(self.ssrc)
|
||||
if let jsonParams = self.jsonParams, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
if let groups = json["ssrc-groups"] as? [Any] {
|
||||
for group in groups {
|
||||
if let group = group as? [String: Any] {
|
||||
if let groupSources = group["sources"] as? [UInt32] {
|
||||
for source in groupSources {
|
||||
participantSsrcs.insert(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return participantSsrcs
|
||||
}
|
||||
}
|
||||
|
||||
public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
public final class Proxy {
|
||||
public let context: AccountGroupCallContextImpl
|
||||
|
|
@ -241,7 +262,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}
|
||||
|
||||
private let audioLevelsPromise = Promise<[(PeerId, Float, Bool)]>()
|
||||
private let audioLevelsPromise = Promise<[(PeerId, UInt32, Float, Bool)]>()
|
||||
|
||||
init() {
|
||||
}
|
||||
|
|
@ -278,10 +299,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}
|
||||
|
||||
var audioLevels: [(PeerId, Float, Bool)] = []
|
||||
for (peerId, _, level, hasVoice) in levels {
|
||||
var audioLevels: [(PeerId, UInt32, Float, Bool)] = []
|
||||
for (peerId, source, level, hasVoice) in levels {
|
||||
if level > 0.001 {
|
||||
audioLevels.append((peerId, level, hasVoice))
|
||||
audioLevels.append((peerId, source, level, hasVoice))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +315,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
return self.speakingParticipantsPromise.get()
|
||||
}
|
||||
|
||||
func getAudioLevels() -> Signal<[(PeerId, Float, Bool)], NoError> {
|
||||
func getAudioLevels() -> Signal<[(PeerId, UInt32, Float, Bool)], NoError> {
|
||||
return self.audioLevelsPromise.get()
|
||||
}
|
||||
}
|
||||
|
|
@ -314,6 +335,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
public let peerId: PeerId
|
||||
public let peer: Peer?
|
||||
|
||||
public private(set) var isVideo: Bool
|
||||
|
||||
private let temporaryJoinTimestamp: Int32
|
||||
|
||||
private var internalState: InternalState = .requesting
|
||||
|
|
@ -321,6 +344,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
private var callContext: OngoingGroupCallContext?
|
||||
private var ssrcMapping: [UInt32: PeerId] = [:]
|
||||
|
||||
private var requestedSsrcs = Set<UInt32>()
|
||||
|
||||
private var summaryInfoState = Promise<SummaryInfoState?>(nil)
|
||||
private var summaryParticipantsState = Promise<SummaryParticipantsState?>(nil)
|
||||
|
||||
|
|
@ -363,7 +388,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
|
||||
private let speakingParticipantsContext = SpeakingParticipantsContext()
|
||||
private var speakingParticipantsReportTimestamp: [PeerId: Double] = [:]
|
||||
public var audioLevels: Signal<[(PeerId, Float, Bool)], NoError> {
|
||||
public var audioLevels: Signal<[(PeerId, UInt32, Float, Bool)], NoError> {
|
||||
return self.speakingParticipantsContext.getAudioLevels()
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +460,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
private let memberEventsPipeDisposable = MetaDisposable()
|
||||
|
||||
private let joinDisposable = MetaDisposable()
|
||||
private let requestDisposable = MetaDisposable()
|
||||
private var groupCallParticipantUpdatesDisposable: Disposable?
|
||||
|
||||
|
|
@ -456,6 +482,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
private var didConnectOnce: Bool = false
|
||||
private var toneRenderer: PresentationCallToneRenderer?
|
||||
|
||||
private var videoCapturer: OngoingCallVideoCapturer?
|
||||
|
||||
private let incomingVideoSourcePromise = Promise<[PeerId: UInt32]>([:])
|
||||
public var incomingVideoSources: Signal<[PeerId: UInt32], NoError> {
|
||||
return self.incomingVideoSourcePromise.get()
|
||||
}
|
||||
|
||||
private var missingSsrcs = Set<UInt32>()
|
||||
private var processedMissingSsrcs = Set<UInt32>()
|
||||
private let missingSsrcsDisposable = MetaDisposable()
|
||||
private var isRequestingMissingSsrcs: Bool = false
|
||||
|
||||
init(
|
||||
accountContext: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
|
|
@ -479,6 +517,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
|
||||
self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
|
||||
//self.videoCapturer = OngoingCallVideoCapturer(keepLandscape: true)
|
||||
self.isVideo = self.videoCapturer != nil
|
||||
|
||||
var didReceiveAudioOutputs = false
|
||||
|
||||
if !audioSession.getIsHeadsetPluggedIn() {
|
||||
|
|
@ -581,6 +622,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
return
|
||||
}
|
||||
if case let .estabilished(callInfo, _, _, _) = strongSelf.internalState {
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
var removedSsrc: [UInt32] = []
|
||||
for (callId, update) in updates {
|
||||
if callId == callInfo.id {
|
||||
|
|
@ -600,6 +642,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
strongSelf._canBeRemoved.set(.single(true))
|
||||
}
|
||||
} else if case .joined = participantUpdate.participationStatusChange {
|
||||
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams))
|
||||
} else if strongSelf.ssrcMapping[participantUpdate.ssrc] == nil {
|
||||
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams))
|
||||
}
|
||||
}
|
||||
case let .call(isTerminated, _):
|
||||
|
|
@ -612,6 +657,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
if !removedSsrc.isEmpty {
|
||||
strongSelf.callContext?.removeSsrcs(ssrcs: removedSsrc)
|
||||
}
|
||||
//strongSelf.callContext?.addParticipants(participants: addedParticipants)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -669,6 +715,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
participants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: accountPeer,
|
||||
ssrc: 0,
|
||||
jsonParams: nil,
|
||||
joinTimestamp: strongSelf.temporaryJoinTimestamp,
|
||||
activityTimestamp: nil,
|
||||
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||
|
|
@ -731,6 +778,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.audioSessionActiveDisposable?.dispose()
|
||||
self.summaryStateDisposable?.dispose()
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.joinDisposable.dispose()
|
||||
self.requestDisposable.dispose()
|
||||
self.groupCallParticipantUpdatesDisposable?.dispose()
|
||||
self.leaveDisposable.dispose()
|
||||
|
|
@ -742,6 +790,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.participantsContextStateDisposable.dispose()
|
||||
self.myAudioLevelDisposable.dispose()
|
||||
self.memberEventsPipeDisposable.dispose()
|
||||
self.missingSsrcsDisposable.dispose()
|
||||
|
||||
self.myAudioLevelTimer?.invalidate()
|
||||
self.typingDisposable.dispose()
|
||||
|
|
@ -788,10 +837,39 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
break
|
||||
default:
|
||||
if case let .active(callInfo) = internalState {
|
||||
let callContext = OngoingGroupCallContext()
|
||||
let callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.maybeRequestParticipants(ssrcs: ssrcs)
|
||||
}
|
||||
})
|
||||
self.incomingVideoSourcePromise.set(callContext.videoSources
|
||||
|> deliverOnMainQueue
|
||||
|> map { [weak self] sources -> [PeerId: UInt32] in
|
||||
guard let strongSelf = self else {
|
||||
return [:]
|
||||
}
|
||||
var result: [PeerId: UInt32] = [:]
|
||||
for source in sources {
|
||||
if let peerId = strongSelf.ssrcMapping[source] {
|
||||
result[peerId] = source
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
self.callContext = callContext
|
||||
self.requestDisposable.set((callContext.joinPayload
|
||||
|> take(1)
|
||||
self.joinDisposable.set((callContext.joinPayload
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
if lhs.0 != rhs.0 {
|
||||
return false
|
||||
}
|
||||
if lhs.1 != rhs.1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { [weak self] joinPayload, ssrc in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -809,6 +887,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
return
|
||||
}
|
||||
if let clientParams = joinCallResult.callInfo.clientParams {
|
||||
strongSelf.ssrcMapping.removeAll()
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
for participant in joinCallResult.state.participants {
|
||||
strongSelf.ssrcMapping[participant.ssrc] = participant.peer.id
|
||||
//addedParticipants.append((participant.ssrc, participant.jsonParams))
|
||||
}
|
||||
strongSelf.callContext?.setJoinResponse(payload: clientParams, participants: addedParticipants)
|
||||
|
||||
strongSelf.updateSessionState(internalState: .estabilished(info: joinCallResult.callInfo, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: strongSelf.audioSessionControl)
|
||||
}
|
||||
}, error: { error in
|
||||
|
|
@ -941,14 +1027,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.stateValue.defaultParticipantMuteState = initialState.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
|
||||
}
|
||||
|
||||
self.ssrcMapping.removeAll()
|
||||
var ssrcs: [UInt32] = []
|
||||
for participant in initialState.participants {
|
||||
self.ssrcMapping[participant.ssrc] = participant.peer.id
|
||||
ssrcs.append(participant.ssrc)
|
||||
}
|
||||
self.callContext?.setJoinResponse(payload: clientParams, ssrcs: ssrcs)
|
||||
|
||||
let accountContext = self.accountContext
|
||||
let peerId = self.peerId
|
||||
let rawAdminIds = Signal<Set<PeerId>, NoError> { subscriber in
|
||||
|
|
@ -1118,6 +1196,73 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}
|
||||
|
||||
private func maybeRequestParticipants(ssrcs: Set<UInt32>) {
|
||||
var missingSsrcs = ssrcs
|
||||
missingSsrcs.subtract(self.processedMissingSsrcs)
|
||||
if missingSsrcs.isEmpty {
|
||||
return
|
||||
}
|
||||
self.processedMissingSsrcs.formUnion(ssrcs)
|
||||
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
|
||||
if let membersValue = self.membersValue {
|
||||
for participant in membersValue.participants {
|
||||
let participantSsrcs = participant.allSsrcs
|
||||
|
||||
if !missingSsrcs.intersection(participantSsrcs).isEmpty {
|
||||
missingSsrcs.subtract(participantSsrcs)
|
||||
self.processedMissingSsrcs.formUnion(participantSsrcs)
|
||||
|
||||
addedParticipants.append((participant.ssrc, participant.jsonParams))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !addedParticipants.isEmpty {
|
||||
self.callContext?.addParticipants(participants: addedParticipants)
|
||||
}
|
||||
|
||||
if !missingSsrcs.isEmpty {
|
||||
self.missingSsrcs.formUnion(missingSsrcs)
|
||||
self.maybeRequestMissingSsrcs()
|
||||
}
|
||||
}
|
||||
|
||||
private func maybeRequestMissingSsrcs() {
|
||||
if self.isRequestingMissingSsrcs {
|
||||
return
|
||||
}
|
||||
if self.missingSsrcs.isEmpty {
|
||||
return
|
||||
}
|
||||
if case let .estabilished(callInfo, _, _, _) = self.internalState {
|
||||
self.isRequestingMissingSsrcs = true
|
||||
|
||||
let requestedSsrcs = self.missingSsrcs
|
||||
self.missingSsrcsDisposable.set((getGroupCallParticipants(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash, offset: "", ssrcs: Array(requestedSsrcs), limit: 100)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isRequestingMissingSsrcs = false
|
||||
strongSelf.missingSsrcs.subtract(requestedSsrcs)
|
||||
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
|
||||
for participant in state.participants {
|
||||
addedParticipants.append((participant.ssrc, participant.jsonParams))
|
||||
}
|
||||
|
||||
if !addedParticipants.isEmpty {
|
||||
strongSelf.callContext?.addParticipants(participants: addedParticipants)
|
||||
}
|
||||
|
||||
strongSelf.maybeRequestMissingSsrcs()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
private func startCheckingCallIfNeeded() {
|
||||
if self.checkCallDisposable != nil {
|
||||
return
|
||||
|
|
@ -1253,7 +1398,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.stateValue.muteState = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func requestVideo() {
|
||||
if self.videoCapturer == nil {
|
||||
let videoCapturer = OngoingCallVideoCapturer()
|
||||
self.videoCapturer = videoCapturer
|
||||
}
|
||||
self.isVideo = true
|
||||
if let videoCapturer = self.videoCapturer {
|
||||
self.callContext?.requestVideo(videoCapturer)
|
||||
}
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
self.isVideo = false
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
self.callContext?.disableVideo()
|
||||
}
|
||||
}
|
||||
|
||||
public func setVolume(peerId: PeerId, volume: Int32, sync: Bool) {
|
||||
for (ssrc, id) in self.ssrcMapping {
|
||||
if id == peerId {
|
||||
|
|
@ -1266,6 +1430,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}
|
||||
|
||||
public func setFullSizeVideo(peerId: PeerId?) {
|
||||
var resolvedSsrc: UInt32?
|
||||
if let peerId = peerId {
|
||||
for (ssrc, id) in self.ssrcMapping {
|
||||
if id == peerId {
|
||||
resolvedSsrc = ssrc
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
self.callContext?.setFullSizeVideoSsrc(ssrc: resolvedSsrc)
|
||||
}
|
||||
|
||||
public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
guard self.currentSelectedAudioOutputValue != output else {
|
||||
return
|
||||
|
|
@ -1376,6 +1553,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.callContext?.stop()
|
||||
self.callContext = nil
|
||||
|
||||
self.missingSsrcsDisposable.set(nil)
|
||||
self.missingSsrcs.removeAll()
|
||||
self.processedMissingSsrcs.removeAll()
|
||||
|
||||
self.internalState = .requesting
|
||||
self.isCurrentlyConnecting = nil
|
||||
|
||||
|
|
@ -1407,6 +1588,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}
|
||||
|
||||
self.joinDisposable.set(nil)
|
||||
self.requestDisposable.set((currentOrRequestedCall
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
|
|
@ -1497,6 +1679,71 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.participantsContext?.updateDefaultParticipantsAreMuted(isMuted: isMuted)
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(source: UInt32, completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
self.callContext?.makeIncomingVideoView(source: source, completion: { view in
|
||||
if let view = view {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch view.getOrientation() {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
return mappedValue
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
}
|
||||
))
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func loadMoreMembers(token: String) {
|
||||
self.participantsContext?.loadMore(token: token)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,169 @@ private final class VoiceChatControllerTitleNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
final class GroupVideoNode: ASDisplayNode {
|
||||
private let videoViewContainer: UIView
|
||||
private let videoView: PresentationCallVideoView
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
var tapped: (() -> Void)?
|
||||
|
||||
init(videoView: PresentationCallVideoView) {
|
||||
self.videoViewContainer = UIView()
|
||||
self.videoView = videoView
|
||||
|
||||
super.init()
|
||||
|
||||
self.videoViewContainer.addSubview(self.videoView.view)
|
||||
self.view.addSubview(self.videoViewContainer)
|
||||
|
||||
videoView.setOnFirstFrameReceived({ [weak self] _ in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
videoView.setOnOrientationUpdated({ [weak self] _, _ in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.tapped?()
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
self.videoViewContainer.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let orientation = self.videoView.getOrientation()
|
||||
var aspect = self.videoView.getAspect()
|
||||
if aspect <= 0.01 {
|
||||
aspect = 3.0 / 4.0
|
||||
}
|
||||
|
||||
let rotatedAspect: CGFloat
|
||||
let angle: CGFloat
|
||||
let switchOrientation: Bool
|
||||
switch orientation {
|
||||
case .rotation0:
|
||||
angle = 0.0
|
||||
rotatedAspect = 1 / aspect
|
||||
switchOrientation = false
|
||||
case .rotation90:
|
||||
angle = CGFloat.pi / 2.0
|
||||
rotatedAspect = aspect
|
||||
switchOrientation = true
|
||||
case .rotation180:
|
||||
angle = CGFloat.pi
|
||||
rotatedAspect = 1 / aspect
|
||||
switchOrientation = false
|
||||
case .rotation270:
|
||||
angle = CGFloat.pi * 3.0 / 2.0
|
||||
rotatedAspect = aspect
|
||||
switchOrientation = true
|
||||
}
|
||||
|
||||
var rotatedVideoSize = CGSize(width: 100.0, height: rotatedAspect * 100.0)
|
||||
|
||||
if size.width < 100.0 || true {
|
||||
rotatedVideoSize = rotatedVideoSize.aspectFilled(size)
|
||||
} else {
|
||||
rotatedVideoSize = rotatedVideoSize.aspectFitted(size)
|
||||
}
|
||||
|
||||
if switchOrientation {
|
||||
rotatedVideoSize = CGSize(width: rotatedVideoSize.height, height: rotatedVideoSize.width)
|
||||
}
|
||||
var rotatedVideoFrame = CGRect(origin: CGPoint(x: floor((size.width - rotatedVideoSize.width) / 2.0), y: floor((size.height - rotatedVideoSize.height) / 2.0)), size: rotatedVideoSize)
|
||||
rotatedVideoFrame.origin.x = floor(rotatedVideoFrame.origin.x)
|
||||
rotatedVideoFrame.origin.y = floor(rotatedVideoFrame.origin.y)
|
||||
rotatedVideoFrame.size.width = ceil(rotatedVideoFrame.size.width)
|
||||
rotatedVideoFrame.size.height = ceil(rotatedVideoFrame.size.height)
|
||||
self.videoView.view.center = rotatedVideoFrame.center
|
||||
self.videoView.view.bounds = CGRect(origin: CGPoint(), size: rotatedVideoFrame.size)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
transition.updateTransformRotation(view: self.videoView.view, angle: angle)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MainVideoContainerNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private let call: PresentationGroupCall
|
||||
|
||||
private var currentVideoNode: GroupVideoNode?
|
||||
private var currentPeer: (PeerId, UInt32)?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
init(context: AccountContext, call: PresentationGroupCall) {
|
||||
self.context = context
|
||||
self.call = call
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = .black
|
||||
}
|
||||
|
||||
func updatePeer(peer: (peerId: PeerId, source: UInt32)?) {
|
||||
if self.currentPeer?.0 == peer?.0 && self.currentPeer?.1 == peer?.1 {
|
||||
return
|
||||
}
|
||||
self.currentPeer = peer
|
||||
if let (peerId, source) = peer {
|
||||
self.call.makeIncomingVideoView(source: source, completion: { [weak self] videoView in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self, let videoView = videoView else {
|
||||
return
|
||||
}
|
||||
let videoNode = GroupVideoNode(videoView: videoView)
|
||||
if let currentVideoNode = strongSelf.currentVideoNode {
|
||||
currentVideoNode.removeFromSupernode()
|
||||
strongSelf.currentVideoNode = nil
|
||||
}
|
||||
strongSelf.currentVideoNode = videoNode
|
||||
strongSelf.addSubnode(videoNode)
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.update(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let currentVideoNode = self.currentVideoNode {
|
||||
currentVideoNode.removeFromSupernode()
|
||||
self.currentVideoNode = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
if let currentVideoNode = self.currentVideoNode {
|
||||
transition.updateFrame(node: currentVideoNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
currentVideoNode.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class VoiceChatController: ViewController {
|
||||
private final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
|
||||
private struct ListTransition {
|
||||
|
|
@ -137,22 +300,28 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
private final class Interaction {
|
||||
let updateIsMuted: (PeerId, Bool) -> Void
|
||||
let openPeer: (PeerId) -> Void
|
||||
let openInvite: () -> Void
|
||||
let peerContextAction: (PeerEntry, ASDisplayNode, ContextGesture?) -> Void
|
||||
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let getPeerVideo: (UInt32) -> GroupVideoNode?
|
||||
|
||||
private var audioLevels: [PeerId: ValuePipe<Float>] = [:]
|
||||
|
||||
init(
|
||||
updateIsMuted: @escaping (PeerId, Bool) -> Void,
|
||||
openPeer: @escaping (PeerId) -> Void,
|
||||
openInvite: @escaping () -> Void,
|
||||
peerContextAction: @escaping (PeerEntry, ASDisplayNode, ContextGesture?) -> Void,
|
||||
setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void
|
||||
setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void,
|
||||
getPeerVideo: @escaping (UInt32) -> GroupVideoNode?
|
||||
) {
|
||||
self.updateIsMuted = updateIsMuted
|
||||
self.openPeer = openPeer
|
||||
self.openInvite = openInvite
|
||||
self.peerContextAction = peerContextAction
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
self.getPeerVideo = getPeerVideo
|
||||
}
|
||||
|
||||
func getAudioLevel(_ peerId: PeerId) -> Signal<Float, NoError> {
|
||||
|
|
@ -170,9 +339,9 @@ public final class VoiceChatController: ViewController {
|
|||
}
|
||||
}
|
||||
|
||||
func updateAudioLevels(_ levels: [(PeerId, Float, Bool)], reset: Bool = false) {
|
||||
func updateAudioLevels(_ levels: [(PeerId, UInt32, Float, Bool)], reset: Bool = false) {
|
||||
var updated = Set<PeerId>()
|
||||
for (peerId, level, _) in levels {
|
||||
for (peerId, _, level, _) in levels {
|
||||
if let pipe = self.audioLevels[peerId] {
|
||||
if reset {
|
||||
pipe.putNext(level)
|
||||
|
|
@ -200,6 +369,7 @@ public final class VoiceChatController: ViewController {
|
|||
}
|
||||
|
||||
var peer: Peer
|
||||
var ssrc: UInt32
|
||||
var presence: TelegramUserPresence?
|
||||
var activityTimestamp: Int32
|
||||
var state: State
|
||||
|
|
@ -216,6 +386,9 @@ public final class VoiceChatController: ViewController {
|
|||
if !lhs.peer.isEqual(rhs.peer) {
|
||||
return false
|
||||
}
|
||||
if lhs.ssrc != rhs.ssrc {
|
||||
return false
|
||||
}
|
||||
if lhs.presence != rhs.presence {
|
||||
return false
|
||||
}
|
||||
|
|
@ -366,7 +539,9 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
|
||||
|
||||
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != context.account.peerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != context.account.peerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
|
||||
return interaction.getPeerVideo(peerEntry.ssrc)
|
||||
}, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
}, action: { node in
|
||||
interaction.peerContextAction(peerEntry, node, nil)
|
||||
|
|
@ -396,6 +571,7 @@ public final class VoiceChatController: ViewController {
|
|||
private let dimNode: ASDisplayNode
|
||||
private let contentContainer: ASDisplayNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let mainVideoContainer: MainVideoContainerNode
|
||||
private let listNode: ListView
|
||||
private let topPanelNode: ASDisplayNode
|
||||
private let topPanelEdgeNode: ASDisplayNode
|
||||
|
|
@ -408,6 +584,7 @@ public final class VoiceChatController: ViewController {
|
|||
private let bottomPanelBackgroundNode: ASDisplayNode
|
||||
private let bottomCornersNode: ASImageNode
|
||||
fileprivate let audioOutputNode: CallControllerButtonItemNode
|
||||
fileprivate let cameraButtonNode: CallControllerButtonItemNode
|
||||
fileprivate let leaveNode: CallControllerButtonItemNode
|
||||
fileprivate let actionButton: VoiceChatActionButton
|
||||
private let leftBorderNode: ASDisplayNode
|
||||
|
|
@ -468,6 +645,12 @@ public final class VoiceChatController: ViewController {
|
|||
private let inviteDisposable = MetaDisposable()
|
||||
|
||||
private let memberEventsDisposable = MetaDisposable()
|
||||
private let voiceSourcesDisposable = MetaDisposable()
|
||||
|
||||
private var requestedVideoSources = Set<UInt32>()
|
||||
private var videoNodes: [(PeerId, UInt32, GroupVideoNode)] = []
|
||||
|
||||
private var currentDominantSpeakerWithVideo: (PeerId, UInt32)?
|
||||
|
||||
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
||||
self.controller = controller
|
||||
|
|
@ -489,6 +672,8 @@ public final class VoiceChatController: ViewController {
|
|||
self.backgroundNode.backgroundColor = secondaryPanelBackgroundColor
|
||||
self.backgroundNode.clipsToBounds = false
|
||||
|
||||
self.mainVideoContainer = MainVideoContainerNode(context: call.accountContext, call: call)
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
|
||||
self.listNode.clipsToBounds = true
|
||||
|
|
@ -537,6 +722,7 @@ public final class VoiceChatController: ViewController {
|
|||
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: false)
|
||||
|
||||
self.audioOutputNode = CallControllerButtonItemNode()
|
||||
self.cameraButtonNode = CallControllerButtonItemNode()
|
||||
self.leaveNode = CallControllerButtonItemNode()
|
||||
self.actionButton = VoiceChatActionButton()
|
||||
|
||||
|
|
@ -561,6 +747,34 @@ public final class VoiceChatController: ViewController {
|
|||
self.itemInteraction = Interaction(
|
||||
updateIsMuted: { [weak self] peerId, isMuted in
|
||||
let _ = self?.call.updateMuteState(peerId: peerId, isMuted: isMuted)
|
||||
}, openPeer: { [weak self] peerId in
|
||||
if let strongSelf = self, let navigationController = strongSelf.controller?.parentNavigationController {
|
||||
/*let context = strongSelf.context
|
||||
strongSelf.controller?.dismiss(completion: {
|
||||
Queue.mainQueue().justDispatch {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), keepStack: .always, purposefulAction: {}, peekData: nil))
|
||||
}
|
||||
})*/
|
||||
for entry in strongSelf.currentEntries {
|
||||
switch entry {
|
||||
case let .peer(peer):
|
||||
if peer.peer.id == peerId {
|
||||
let source = peer.ssrc
|
||||
if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source {
|
||||
strongSelf.currentDominantSpeakerWithVideo = (peerId, source)
|
||||
strongSelf.call.setFullSizeVideo(peerId: peerId)
|
||||
strongSelf.mainVideoContainer.updatePeer(peer: (peerId: peerId, source: source))
|
||||
} else {
|
||||
strongSelf.currentDominantSpeakerWithVideo = nil
|
||||
strongSelf.call.setFullSizeVideo(peerId: nil)
|
||||
strongSelf.mainVideoContainer.updatePeer(peer: nil)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}, openInvite: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -815,6 +1029,17 @@ public final class VoiceChatController: ViewController {
|
|||
}), true))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "Toggle Full Screen", icon: { theme in
|
||||
return nil
|
||||
}, action: { _, f in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.itemInteraction?.openPeer(peer.id)
|
||||
f(.default)
|
||||
})))
|
||||
|
||||
if peer.id != strongSelf.context.account.peerId {
|
||||
if let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) {
|
||||
if callState.adminIds.contains(peer.id) {
|
||||
|
|
@ -955,6 +1180,16 @@ public final class VoiceChatController: ViewController {
|
|||
updated.revealedPeerId = peerId
|
||||
return updated
|
||||
}
|
||||
}, getPeerVideo: { [weak self] ssrc in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
}
|
||||
for (_, listSsrc, videoNode) in strongSelf.videoNodes {
|
||||
if listSsrc == ssrc {
|
||||
return videoNode
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
self.topPanelNode.addSubnode(self.topPanelEdgeNode)
|
||||
|
|
@ -968,6 +1203,7 @@ public final class VoiceChatController: ViewController {
|
|||
self.bottomPanelNode.addSubnode(self.bottomCornersNode)
|
||||
self.bottomPanelNode.addSubnode(self.bottomPanelBackgroundNode)
|
||||
self.bottomPanelNode.addSubnode(self.audioOutputNode)
|
||||
self.bottomPanelNode.addSubnode(self.cameraButtonNode)
|
||||
self.bottomPanelNode.addSubnode(self.leaveNode)
|
||||
self.bottomPanelNode.addSubnode(self.actionButton)
|
||||
|
||||
|
|
@ -976,6 +1212,7 @@ public final class VoiceChatController: ViewController {
|
|||
self.contentContainer.addSubnode(self.backgroundNode)
|
||||
|
||||
self.contentContainer.addSubnode(self.listNode)
|
||||
self.contentContainer.addSubnode(self.mainVideoContainer)
|
||||
self.contentContainer.addSubnode(self.topPanelNode)
|
||||
self.contentContainer.addSubnode(self.leftBorderNode)
|
||||
self.contentContainer.addSubnode(self.rightBorderNode)
|
||||
|
|
@ -1092,6 +1329,28 @@ public final class VoiceChatController: ViewController {
|
|||
if strongSelf.effectiveMuteState != nil {
|
||||
levels = levels.filter { $0.0 != strongSelf.context.account.peerId }
|
||||
}
|
||||
|
||||
var maxLevelWithVideo: (PeerId, UInt32, Float)?
|
||||
for (peerId, source, level, hasSpeech) in levels {
|
||||
if hasSpeech && source != 0 {
|
||||
if let (_, _, currentLevel) = maxLevelWithVideo {
|
||||
if currentLevel < level {
|
||||
maxLevelWithVideo = (peerId, source, level)
|
||||
}
|
||||
} else {
|
||||
maxLevelWithVideo = (peerId, source, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (peerId, source, _) = maxLevelWithVideo {
|
||||
if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source {
|
||||
strongSelf.currentDominantSpeakerWithVideo = (peerId, source)
|
||||
strongSelf.call.setFullSizeVideo(peerId: peerId)
|
||||
strongSelf.mainVideoContainer.updatePeer(peer: (peerId: peerId, source: source))
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.itemInteraction?.updateAudioLevels(levels)
|
||||
})
|
||||
|
||||
|
|
@ -1113,6 +1372,8 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
self.audioOutputNode.addTarget(self, action: #selector(self.audioOutputPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.cameraButtonNode.addTarget(self, action: #selector(self.cameraPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.optionsButton.contextAction = { [weak self] sourceNode, gesture in
|
||||
guard let strongSelf = self, let controller = strongSelf.controller else {
|
||||
return
|
||||
|
|
@ -1246,6 +1507,90 @@ public final class VoiceChatController: ViewController {
|
|||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: event.peer, text: strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).0), action: { _ in return false })
|
||||
}
|
||||
}))
|
||||
|
||||
self.voiceSourcesDisposable.set((self.call.incomingVideoSources
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sources in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var validSources = Set<UInt32>()
|
||||
for (peerId, source) in sources {
|
||||
validSources.insert(source)
|
||||
|
||||
if !strongSelf.requestedVideoSources.contains(source) {
|
||||
strongSelf.requestedVideoSources.insert(source)
|
||||
strongSelf.call.makeIncomingVideoView(source: source, completion: { videoView in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self, let videoView = videoView else {
|
||||
return
|
||||
}
|
||||
let videoNode = GroupVideoNode(videoView: videoView)
|
||||
strongSelf.videoNodes.append((peerId, source, videoNode))
|
||||
//strongSelf.addSubnode(videoNode)
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||
|
||||
loop: for i in 0 ..< strongSelf.currentEntries.count {
|
||||
let entry = strongSelf.currentEntries[i]
|
||||
switch entry {
|
||||
case let .peer(peerEntry):
|
||||
if peerEntry.ssrc == source {
|
||||
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.item(context: strongSelf.context, presentationData: presentationData, interaction: strongSelf.itemInteraction!), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var updated = false
|
||||
for i in (0 ..< strongSelf.videoNodes.count).reversed() {
|
||||
if !validSources.contains(strongSelf.videoNodes[i].1) {
|
||||
let ssrc = strongSelf.videoNodes[i].1
|
||||
strongSelf.videoNodes.remove(at: i)
|
||||
|
||||
loop: for j in 0 ..< strongSelf.currentEntries.count {
|
||||
let entry = strongSelf.currentEntries[j]
|
||||
switch entry {
|
||||
case let .peer(peerEntry):
|
||||
if peerEntry.ssrc == ssrc {
|
||||
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.item(context: strongSelf.context, presentationData: presentationData, interaction: strongSelf.itemInteraction!), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//strongSelf.videoNodes[i].2.removeFromSupernode()
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
if let (_, source) = strongSelf.currentDominantSpeakerWithVideo {
|
||||
if !validSources.contains(source) {
|
||||
strongSelf.currentDominantSpeakerWithVideo = nil
|
||||
strongSelf.call.setFullSizeVideo(peerId: nil)
|
||||
strongSelf.mainVideoContainer.updatePeer(peer: nil)
|
||||
}
|
||||
}
|
||||
|
||||
if updated {
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
self.isFullscreen = true
|
||||
self.isExpanded = true
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
|
@ -1259,6 +1604,8 @@ public final class VoiceChatController: ViewController {
|
|||
self.audioLevelsDisposable?.dispose()
|
||||
self.myAudioLevelDisposable?.dispose()
|
||||
self.inviteDisposable.dispose()
|
||||
self.memberEventsDisposable.dispose()
|
||||
self.voiceSourcesDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
|
@ -1390,7 +1737,7 @@ public final class VoiceChatController: ViewController {
|
|||
self.call.setIsMuted(action: .muted(isPushToTalkActive: false))
|
||||
}
|
||||
|
||||
self.itemInteraction?.updateAudioLevels([(self.context.account.peerId, 0.0, false)], reset: true)
|
||||
self.itemInteraction?.updateAudioLevels([(self.context.account.peerId, 0, 0.0, false)], reset: true)
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||
|
|
@ -1468,6 +1815,14 @@ public final class VoiceChatController: ViewController {
|
|||
}
|
||||
}
|
||||
|
||||
@objc private func cameraPressed() {
|
||||
if self.call.isVideo {
|
||||
self.call.disableVideo()
|
||||
} else {
|
||||
self.call.requestVideo()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, completion: (() -> Void)? = nil) {
|
||||
guard let (layout, _) = self.validLayout else {
|
||||
return
|
||||
|
|
@ -1507,6 +1862,10 @@ public final class VoiceChatController: ViewController {
|
|||
let panelOffset = max(layoutTopInset, rawPanelOffset)
|
||||
let topPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOffset), size: CGSize(width: size.width, height: topPanelHeight))
|
||||
|
||||
let videoContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: layout.size.width, height: 200.0))
|
||||
transition.updateFrameAdditive(node: self.mainVideoContainer, frame: videoContainerFrame)
|
||||
self.mainVideoContainer.update(size: videoContainerFrame.size, transition: transition)
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: size.width, height: layout.size.height))
|
||||
let sideInset: CGFloat = 16.0
|
||||
let leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - 16.0), size: CGSize(width: sideInset, height: layout.size.height))
|
||||
|
|
@ -1720,6 +2079,10 @@ public final class VoiceChatController: ViewController {
|
|||
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
|
||||
self.audioOutputNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage), text: soundTitle, transition: .animated(duration: 0.3, curve: .linear))
|
||||
|
||||
let cameraButtonSize = CGSize(width: 40.0, height: 40.0)
|
||||
|
||||
self.cameraButtonNode.update(size: cameraButtonSize, content: CallControllerButtonItemNode.Content(appearance: CallControllerButtonItemNode.Content.Appearance.blurred(isFilled: false), image: .camera), text: " ", transition: .animated(duration: 0.3, curve: .linear))
|
||||
|
||||
self.leaveNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: .color(.custom(0xff3b30, 0.3)), image: .end), text: self.presentationData.strings.VoiceChat_Leave, transition: .immediate)
|
||||
}
|
||||
|
||||
|
|
@ -1769,7 +2132,7 @@ public final class VoiceChatController: ViewController {
|
|||
}
|
||||
|
||||
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||
let listTopInset = layoutTopInset + topPanelHeight
|
||||
let listTopInset = layoutTopInset + topPanelHeight + 200.0
|
||||
let listSize = CGSize(width: size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
||||
|
||||
let topInset: CGFloat
|
||||
|
|
@ -1800,6 +2163,7 @@ public final class VoiceChatController: ViewController {
|
|||
transition.updateFrame(node: self.bottomPanelNode, frame: bottomPanelFrame)
|
||||
|
||||
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
|
||||
let cameraButtonSize = CGSize(width: 40.0, height: 40.0)
|
||||
let centralButtonSize = CGSize(width: 440.0, height: 440.0)
|
||||
|
||||
let actionButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - centralButtonSize.width) / 2.0), y: floorToScreenPixels((bottomAreaHeight - centralButtonSize.height) / 2.0)), size: centralButtonSize)
|
||||
|
|
@ -1851,13 +2215,36 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
self.updateButtons(transition: transition)
|
||||
|
||||
/*var currentVideoOrigin = CGPoint(x: 4.0, y: (layout.statusBarHeight ?? 0.0) + 4.0)
|
||||
for (_, _, videoNode) in self.videoNodes {
|
||||
let videoSize = CGSize(width: 300.0, height: 500.0)
|
||||
if currentVideoOrigin.x + videoSize.width > layout.size.width {
|
||||
currentVideoOrigin.x = 0.0
|
||||
currentVideoOrigin.y += videoSize.height
|
||||
}
|
||||
|
||||
videoNode.frame = CGRect(origin: currentVideoOrigin, size: videoSize)
|
||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||
if videoNode.supernode == nil {
|
||||
self.contentContainer.addSubnode(videoNode)
|
||||
}
|
||||
|
||||
currentVideoOrigin.x += videoSize.width + 4.0
|
||||
}*/
|
||||
|
||||
let sideButtonMinimalInset: CGFloat = 16.0
|
||||
let sideButtonOffset = min(36.0, floor((((size.width - 144.0) / 2.0) - sideButtonSize.width) / 2.0))
|
||||
let sideButtonOrigin = max(sideButtonMinimalInset, floor((size.width - 144.0) / 2.0) - sideButtonOffset - sideButtonSize.width)
|
||||
|
||||
if self.audioOutputNode.supernode === self.bottomPanelNode {
|
||||
transition.updateFrame(node: self.audioOutputNode, frame: CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize))
|
||||
let cameraButtonDistance: CGFloat = 4.0
|
||||
|
||||
let audioOutputFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((bottomAreaHeight - sideButtonSize.height - cameraButtonDistance - cameraButtonSize.height) / 2.0) + cameraButtonDistance + cameraButtonSize.height), size: sideButtonSize)
|
||||
|
||||
transition.updateFrame(node: self.audioOutputNode, frame: audioOutputFrame)
|
||||
transition.updateFrame(node: self.leaveNode, frame: CGRect(origin: CGPoint(x: size.width - sideButtonOrigin - sideButtonSize.width, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize))
|
||||
|
||||
transition.updateFrame(node: self.cameraButtonNode, frame: CGRect(origin: CGPoint(x: floor(audioOutputFrame.midX - cameraButtonSize.width / 2.0), y: audioOutputFrame.minY - cameraButtonDistance - cameraButtonSize.height), size: cameraButtonSize))
|
||||
}
|
||||
if isFirstTime {
|
||||
while !self.enqueuedTransitions.isEmpty {
|
||||
|
|
@ -1883,10 +2270,13 @@ public final class VoiceChatController: ViewController {
|
|||
if self.actionButton.supernode !== self.bottomPanelNode {
|
||||
self.actionButton.ignoreHierarchyChanges = true
|
||||
self.audioOutputNode.isHidden = false
|
||||
self.cameraButtonNode.isHidden = false
|
||||
self.leaveNode.isHidden = false
|
||||
self.audioOutputNode.layer.removeAllAnimations()
|
||||
self.cameraButtonNode.layer.removeAllAnimations()
|
||||
self.leaveNode.layer.removeAllAnimations()
|
||||
self.bottomPanelNode.addSubnode(self.audioOutputNode)
|
||||
self.bottomPanelNode.addSubnode(self.cameraButtonNode)
|
||||
self.bottomPanelNode.addSubnode(self.leaveNode)
|
||||
self.bottomPanelNode.addSubnode(self.actionButton)
|
||||
self.containerLayoutUpdated(layout, navigationHeight :navigationHeight, transition: .immediate)
|
||||
|
|
@ -2085,6 +2475,7 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
entries.append(.peer(PeerEntry(
|
||||
peer: member.peer,
|
||||
ssrc: member.ssrc,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: memberState,
|
||||
|
|
@ -2098,6 +2489,7 @@ public final class VoiceChatController: ViewController {
|
|||
if let accountPeer = self.accountPeer, !processedPeerIds.contains(accountPeer.id) {
|
||||
entries.insert(.peer(PeerEntry(
|
||||
peer: accountPeer,
|
||||
ssrc: 0,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: .listening,
|
||||
|
|
@ -2115,6 +2507,7 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
entries.append(.peer(PeerEntry(
|
||||
peer: peer,
|
||||
ssrc: 0,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: .invited,
|
||||
|
|
@ -2135,7 +2528,7 @@ public final class VoiceChatController: ViewController {
|
|||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer is DirectionalPanGestureRecognizer {
|
||||
let location = gestureRecognizer.location(in: self.bottomPanelNode.view)
|
||||
if self.audioOutputNode.frame.contains(location) || self.leaveNode.frame.contains(location) {
|
||||
if self.audioOutputNode.frame.contains(location) || (!self.cameraButtonNode.isHidden && self.cameraButtonNode.frame.contains(location)) || self.leaveNode.frame.contains(location) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -2328,6 +2721,14 @@ public final class VoiceChatController: ViewController {
|
|||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
|
||||
if let result = result {
|
||||
for (_, _, videoNode) in self.videoNodes {
|
||||
if videoNode.view === result || result.isDescendant(of: videoNode.view) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result === self.topPanelNode.view {
|
||||
return self.view
|
||||
|
|
|
|||
|
|
@ -62,30 +62,34 @@ final class VoiceChatParticipantItem: ListViewItem {
|
|||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let context: AccountContext
|
||||
let peer: Peer
|
||||
let ssrc: UInt32?
|
||||
let presence: PeerPresence?
|
||||
let text: ParticipantText
|
||||
let icon: Icon
|
||||
let enabled: Bool
|
||||
public let selectable: Bool
|
||||
let getAudioLevel: (() -> Signal<Float, NoError>)?
|
||||
let getVideo: () -> GroupVideoNode?
|
||||
let revealOptions: [RevealOption]
|
||||
let revealed: Bool?
|
||||
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let action: ((ASDisplayNode) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, presence: PeerPresence?, text: ParticipantText, icon: Icon, enabled: Bool, selectable: Bool, getAudioLevel: (() -> Signal<Float, NoError>)?, revealOptions: [RevealOption], revealed: Bool?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, action: ((ASDisplayNode) -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
|
||||
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, ssrc: UInt32?, presence: PeerPresence?, text: ParticipantText, icon: Icon, enabled: Bool, selectable: Bool, getAudioLevel: (() -> Signal<Float, NoError>)?, getVideo: @escaping () -> GroupVideoNode?, revealOptions: [RevealOption], revealed: Bool?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, action: ((ASDisplayNode) -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.ssrc = ssrc
|
||||
self.presence = presence
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.enabled = enabled
|
||||
self.selectable = selectable
|
||||
self.getAudioLevel = getAudioLevel
|
||||
self.getVideo = getVideo
|
||||
self.revealOptions = revealOptions
|
||||
self.revealed = revealed
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
|
|
@ -171,6 +175,12 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||
private var layoutParams: (VoiceChatParticipantItem, ListViewItemLayoutParams, Bool, Bool)?
|
||||
private var wavesColor: UIColor?
|
||||
|
||||
private var videoNode: GroupVideoNode?
|
||||
|
||||
var item: VoiceChatParticipantItem? {
|
||||
return self.layoutParams?.0
|
||||
}
|
||||
|
||||
init() {
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
|
@ -188,7 +198,6 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||
self.offsetContainerNode = ASDisplayNode()
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarFont)
|
||||
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||
self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 40.0))
|
||||
|
||||
self.titleNode = TextNode()
|
||||
|
|
@ -667,11 +676,31 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||
})
|
||||
}
|
||||
|
||||
let videoSize = CGSize(width: avatarSize, height: avatarSize)
|
||||
|
||||
let videoNode = item.getVideo()
|
||||
if let current = strongSelf.videoNode, current !== videoNode {
|
||||
current.removeFromSupernode()
|
||||
}
|
||||
let actionOffset: CGFloat = 0.0
|
||||
strongSelf.videoNode = videoNode
|
||||
if let videoNode = videoNode {
|
||||
|
||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||
if videoNode.supernode !== strongSelf.avatarNode {
|
||||
videoNode.clipsToBounds = true
|
||||
videoNode.cornerRadius = avatarSize / 2.0
|
||||
strongSelf.avatarNode.addSubnode(videoNode)
|
||||
}
|
||||
|
||||
videoNode.frame = CGRect(origin: CGPoint(), size: videoSize)
|
||||
}
|
||||
|
||||
let animationSize = CGSize(width: 36.0, height: 36.0)
|
||||
strongSelf.iconNode?.frame = CGRect(origin: CGPoint(), size: animationSize)
|
||||
strongSelf.animationNode?.frame = CGRect(origin: CGPoint(), size: animationSize)
|
||||
|
||||
strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0 - params.rightInset, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height)
|
||||
strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0 - params.rightInset + actionOffset, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height)
|
||||
|
||||
if let presence = item.presence as? TelegramUserPresence {
|
||||
strongSelf.peerPresenceManager?.reset(presence: presence)
|
||||
|
|
|
|||
|
|
@ -230,6 +230,16 @@ public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: Pe
|
|||
action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: false)
|
||||
case let .channelAdminLogEventActionToggleGroupCallSetting(joinMuted):
|
||||
action = .updateGroupCallSettings(joinMuted: joinMuted == .boolTrue)
|
||||
case .channelAdminLogEventActionParticipantJoinByInvite:
|
||||
break
|
||||
case .channelAdminLogEventActionExportedInviteDelete:
|
||||
break
|
||||
case .channelAdminLogEventActionExportedInviteRevoke:
|
||||
break
|
||||
case .channelAdminLogEventActionExportedInviteEdit:
|
||||
break
|
||||
case .channelAdminLogEventActionParticipantVolume:
|
||||
break
|
||||
}
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
if let action = action {
|
||||
|
|
|
|||
|
|
@ -336,15 +336,15 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
|||
var messageAutoremoveTimeout: Int32?
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
messageAutoremoveTimeout = value?.peerValue
|
||||
messageAutoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
messageAutoremoveTimeout = value?.peerValue
|
||||
messageAutoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedData = cachedData as? CachedChannelData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
messageAutoremoveTimeout = value?.peerValue
|
||||
messageAutoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
|
|||
|
||||
loop: for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
|
|
@ -103,9 +103,17 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
|
|||
} else if mutedByYou {
|
||||
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou)
|
||||
}
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
parsedParticipants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: peer,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
|
|
@ -228,7 +236,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
|
|||
|
||||
loop: for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
|
|
@ -243,9 +251,17 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
|
|||
} else if mutedByYou {
|
||||
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou)
|
||||
}
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
parsedParticipants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: peer,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
|
|
@ -564,6 +580,7 @@ public final class GroupCallParticipantsContext {
|
|||
|
||||
public var peer: Peer
|
||||
public var ssrc: UInt32
|
||||
public var jsonParams: String?
|
||||
public var joinTimestamp: Int32
|
||||
public var activityTimestamp: Double?
|
||||
public var muteState: MuteState?
|
||||
|
|
@ -572,6 +589,7 @@ public final class GroupCallParticipantsContext {
|
|||
public init(
|
||||
peer: Peer,
|
||||
ssrc: UInt32,
|
||||
jsonParams: String?,
|
||||
joinTimestamp: Int32,
|
||||
activityTimestamp: Double?,
|
||||
muteState: MuteState?,
|
||||
|
|
@ -579,6 +597,7 @@ public final class GroupCallParticipantsContext {
|
|||
) {
|
||||
self.peer = peer
|
||||
self.ssrc = ssrc
|
||||
self.jsonParams = jsonParams
|
||||
self.joinTimestamp = joinTimestamp
|
||||
self.activityTimestamp = activityTimestamp
|
||||
self.muteState = muteState
|
||||
|
|
@ -687,11 +706,32 @@ public final class GroupCallParticipantsContext {
|
|||
|
||||
public var peerId: PeerId
|
||||
public var ssrc: UInt32
|
||||
public var jsonParams: String?
|
||||
public var joinTimestamp: Int32
|
||||
public var activityTimestamp: Double?
|
||||
public var muteState: Participant.MuteState?
|
||||
public var participationStatusChange: ParticipationStatusChange
|
||||
public var volume: Int32?
|
||||
|
||||
init(
|
||||
peerId: PeerId,
|
||||
ssrc: UInt32,
|
||||
jsonParams: String?,
|
||||
joinTimestamp: Int32,
|
||||
activityTimestamp: Double?,
|
||||
muteState: Participant.MuteState?,
|
||||
participationStatusChange: ParticipationStatusChange,
|
||||
volume: Int32?
|
||||
) {
|
||||
self.peerId = peerId
|
||||
self.ssrc = ssrc
|
||||
self.jsonParams = jsonParams
|
||||
self.joinTimestamp = joinTimestamp
|
||||
self.activityTimestamp = activityTimestamp
|
||||
self.muteState = muteState
|
||||
self.participationStatusChange = participationStatusChange
|
||||
self.volume = volume
|
||||
}
|
||||
}
|
||||
|
||||
public var participantUpdates: [ParticipantUpdate]
|
||||
|
|
@ -1107,6 +1147,7 @@ public final class GroupCallParticipantsContext {
|
|||
let participant = Participant(
|
||||
peer: peer,
|
||||
ssrc: participantUpdate.ssrc,
|
||||
jsonParams: participantUpdate.jsonParams,
|
||||
joinTimestamp: participantUpdate.joinTimestamp,
|
||||
activityTimestamp: activityTimestamp,
|
||||
muteState: participantUpdate.muteState,
|
||||
|
|
@ -1322,7 +1363,7 @@ public final class GroupCallParticipantsContext {
|
|||
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
||||
init(_ apiParticipant: Api.GroupCallParticipant) {
|
||||
switch apiParticipant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
let muted = (flags & (1 << 0)) != 0
|
||||
|
|
@ -1346,9 +1387,18 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
|||
participationStatusChange = .none
|
||||
}
|
||||
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
|
||||
self.init(
|
||||
peerId: peerId,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
|
|
@ -1364,7 +1414,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
|
|||
var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = []
|
||||
for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
let muted = (flags & (1 << 0)) != 0
|
||||
|
|
@ -1388,9 +1438,18 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
|
|||
participationStatusChange = .none
|
||||
}
|
||||
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
|
||||
participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(
|
||||
peerId: peerId,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
|
|
|
|||
|
|
@ -7,71 +7,12 @@ import MtProtoKit
|
|||
import SyncCore
|
||||
|
||||
|
||||
public func ensuredExistingPeerExportedInvitation(account: Account, peerId: PeerId, revokeExisted: Bool = false) -> Signal<ExportedInvitation?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
if let _ = peer as? TelegramChannel {
|
||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, cachedData.exportedInvitation != nil && !revokeExisted {
|
||||
return .single(cachedData.exportedInvitation)
|
||||
} else {
|
||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: 0, peer: inputPeer, expireDate: nil, usageLimit: nil))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
|
||||
return account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
if let invitation = ExportedInvitation(apiExportedInvite: result) {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedExportedInvitation(invitation)
|
||||
} else {
|
||||
return CachedChannelData().withUpdatedExportedInvitation(invitation)
|
||||
}
|
||||
})
|
||||
return invitation
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let _ = peer as? TelegramGroup {
|
||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData, cachedData.exportedInvitation != nil && !revokeExisted {
|
||||
return .single(cachedData.exportedInvitation)
|
||||
} else {
|
||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: 0, peer: inputPeer, expireDate: nil, usageLimit: nil))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
|
||||
return account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
if let invitation = ExportedInvitation(apiExportedInvite: result) {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedExportedInvitation(invitation)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
return invitation
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
|
||||
public func revokePersistentPeerExportedInvitation(account: Account, peerId: PeerId) -> Signal<ExportedInvitation?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
let flags: Int32 = (1 << 2)
|
||||
if let _ = peer as? TelegramChannel {
|
||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: 0, peer: inputPeer, expireDate: nil, usageLimit: nil))
|
||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
|
||||
return account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
|
|
@ -90,7 +31,7 @@ public func revokePersistentPeerExportedInvitation(account: Account, peerId: Pee
|
|||
}
|
||||
}
|
||||
} else if let _ = peer as? TelegramGroup {
|
||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: 0, peer: inputPeer, expireDate: nil, usageLimit: nil))
|
||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
|
||||
return account.postbox.transaction { transaction -> ExportedInvitation? in
|
||||
|
|
@ -122,8 +63,7 @@ public enum CreatePeerExportedInvitationError {
|
|||
}
|
||||
|
||||
public func createPeerExportedInvitation(account: Account, peerId: PeerId, expireDate: Int32?, usageLimit: Int32?) -> Signal<ExportedInvitation?, CreatePeerExportedInvitationError> {
|
||||
return .fail(.generic)
|
||||
/*return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, CreatePeerExportedInvitationError> in
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, CreatePeerExportedInvitationError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
var flags: Int32 = 0
|
||||
if let _ = expireDate {
|
||||
|
|
@ -146,7 +86,7 @@ public func createPeerExportedInvitation(account: Account, peerId: PeerId, expir
|
|||
}
|
||||
}
|
||||
|> castError(CreatePeerExportedInvitationError.self)
|
||||
|> switchToLatest*/
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
public enum EditPeerExportedInvitationError {
|
||||
|
|
@ -154,8 +94,7 @@ public enum EditPeerExportedInvitationError {
|
|||
}
|
||||
|
||||
public func editPeerExportedInvitation(account: Account, peerId: PeerId, link: String, expireDate: Int32?, usageLimit: Int32?) -> Signal<ExportedInvitation?, EditPeerExportedInvitationError> {
|
||||
return .fail(.generic)
|
||||
/*return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, EditPeerExportedInvitationError> in
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, EditPeerExportedInvitationError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
var flags: Int32 = 0
|
||||
if let _ = expireDate {
|
||||
|
|
@ -188,7 +127,7 @@ public func editPeerExportedInvitation(account: Account, peerId: PeerId, link: S
|
|||
}
|
||||
}
|
||||
|> castError(EditPeerExportedInvitationError.self)
|
||||
|> switchToLatest*/
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
public enum RevokePeerExportedInvitationError {
|
||||
|
|
@ -196,8 +135,7 @@ public enum RevokePeerExportedInvitationError {
|
|||
}
|
||||
|
||||
public func revokePeerExportedInvitation(account: Account, peerId: PeerId, link: String) -> Signal<ExportedInvitation?, RevokePeerExportedInvitationError> {
|
||||
return .fail(.generic)
|
||||
/*return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, RevokePeerExportedInvitationError> in
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, RevokePeerExportedInvitationError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
let flags: Int32 = (1 << 2)
|
||||
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: nil, usageLimit: nil))
|
||||
|
|
@ -224,7 +162,7 @@ public func revokePeerExportedInvitation(account: Account, peerId: PeerId, link:
|
|||
}
|
||||
}
|
||||
|> castError(RevokePeerExportedInvitationError.self)
|
||||
|> switchToLatest*/
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
public struct ExportedInvitations : Equatable {
|
||||
|
|
@ -232,10 +170,9 @@ public struct ExportedInvitations : Equatable {
|
|||
public let totalCount: Int32
|
||||
}
|
||||
|
||||
public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: Bool, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
|
||||
return .single(nil)
|
||||
/*return account.postbox.transaction { transaction -> Signal<ExportedInvitations?, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitations?, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer), let adminPeer = transaction.getPeer(adminId ?? account.peerId), let adminId = apiInputUser(adminPeer) {
|
||||
var flags: Int32 = 0
|
||||
if let _ = offsetLink {
|
||||
flags |= (1 << 2)
|
||||
|
|
@ -243,7 +180,7 @@ public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: B
|
|||
if revoked {
|
||||
flags |= (1 << 3)
|
||||
}
|
||||
return account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: nil, offsetDate: offsetLink?.date, offsetLink: offsetLink?.link, limit: 50))
|
||||
return account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: adminId, offsetDate: offsetLink?.date, offsetLink: offsetLink?.link, limit: 50))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.ExportedChatInvites?, NoError> in
|
||||
return .single(nil)
|
||||
|
|
@ -277,7 +214,7 @@ public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: B
|
|||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
} |> switchToLatest*/
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -286,8 +223,7 @@ public enum DeletePeerExportedInvitationError {
|
|||
}
|
||||
|
||||
public func deletePeerExportedInvitation(account: Account, peerId: PeerId, link: String) -> Signal<Never, DeletePeerExportedInvitationError> {
|
||||
return .fail(.generic)
|
||||
/*return account.postbox.transaction { transaction -> Signal<Never, DeletePeerExportedInvitationError> in
|
||||
return account.postbox.transaction { transaction -> Signal<Never, DeletePeerExportedInvitationError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return account.network.request(Api.functions.messages.deleteExportedChatInvite(peer: inputPeer, link: link))
|
||||
|> mapError { _ in return DeletePeerExportedInvitationError.generic }
|
||||
|
|
@ -297,14 +233,13 @@ public func deletePeerExportedInvitation(account: Account, peerId: PeerId, link:
|
|||
}
|
||||
}
|
||||
|> castError(DeletePeerExportedInvitationError.self)
|
||||
|> switchToLatest*/
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
public func deleteAllRevokedPeerExportedInvitations(account: Account, peerId: PeerId) -> Signal<Never, NoError> {
|
||||
return .complete()
|
||||
/*return account.postbox.transaction { transaction -> Signal<Never, NoError> in
|
||||
return account.postbox.transaction { transaction -> Signal<Never, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return account.network.request(Api.functions.messages.deleteRevokedExportedChatInvites(peer: inputPeer))
|
||||
return account.network.request(Api.functions.messages.deleteRevokedExportedChatInvites(peer: inputPeer, adminId: .inputUserEmpty))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|
|
@ -313,7 +248,7 @@ public func deleteAllRevokedPeerExportedInvitations(account: Account, peerId: Pe
|
|||
return .complete()
|
||||
}
|
||||
}
|
||||
|> switchToLatest*/
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
private let cachedPeerExportedInvitationsCollectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 10, highWaterItemCount: 20)
|
||||
|
|
@ -377,6 +312,7 @@ private final class PeerExportedInvitationsContextImpl {
|
|||
private let queue: Queue
|
||||
private let account: Account
|
||||
private let peerId: PeerId
|
||||
private let adminId: PeerId
|
||||
private let revoked: Bool
|
||||
private var forceUpdate: Bool
|
||||
private let disposable = MetaDisposable()
|
||||
|
|
@ -388,36 +324,41 @@ private final class PeerExportedInvitationsContextImpl {
|
|||
private var results: [ExportedInvitation] = []
|
||||
private var count: Int32
|
||||
private var populateCache: Bool = true
|
||||
private var isMainList: Bool
|
||||
|
||||
let state = Promise<PeerExportedInvitationsState>()
|
||||
|
||||
init(queue: Queue, account: Account, peerId: PeerId, revoked: Bool, forceUpdate: Bool) {
|
||||
init(queue: Queue, account: Account, peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.adminId = adminId ?? account.peerId
|
||||
self.revoked = revoked
|
||||
self.forceUpdate = forceUpdate
|
||||
self.isMainList = adminId == nil
|
||||
|
||||
self.count = 0
|
||||
|
||||
self.isLoadingMore = true
|
||||
self.disposable.set((account.postbox.transaction { transaction -> CachedPeerExportedInvitations? in
|
||||
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerExportedInvitations, key: CachedPeerExportedInvitations.key(peerId: peerId, revoked: revoked))) as? CachedPeerExportedInvitations
|
||||
if adminId == nil {
|
||||
self.isLoadingMore = true
|
||||
self.disposable.set((account.postbox.transaction { transaction -> CachedPeerExportedInvitations? in
|
||||
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerExportedInvitations, key: CachedPeerExportedInvitations.key(peerId: peerId, revoked: revoked))) as? CachedPeerExportedInvitations
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] cachedResult in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isLoadingMore = false
|
||||
if let cachedResult = cachedResult {
|
||||
strongSelf.results = cachedResult.invitations
|
||||
strongSelf.count = cachedResult.count
|
||||
strongSelf.hasLoadedOnce = true
|
||||
strongSelf.canLoadMore = cachedResult.canLoadMore
|
||||
strongSelf.loadedFromCache = true
|
||||
}
|
||||
strongSelf.loadMore()
|
||||
}))
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] cachedResult in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isLoadingMore = false
|
||||
if let cachedResult = cachedResult {
|
||||
strongSelf.results = cachedResult.invitations
|
||||
strongSelf.count = cachedResult.count
|
||||
strongSelf.hasLoadedOnce = true
|
||||
strongSelf.canLoadMore = cachedResult.canLoadMore
|
||||
strongSelf.loadedFromCache = true
|
||||
}
|
||||
strongSelf.loadMore()
|
||||
}))
|
||||
|
||||
self.loadMore()
|
||||
}
|
||||
|
|
@ -433,17 +374,18 @@ private final class PeerExportedInvitationsContextImpl {
|
|||
}
|
||||
|
||||
func loadMore() {
|
||||
/*if self.isLoadingMore {
|
||||
if self.isLoadingMore {
|
||||
return
|
||||
}
|
||||
self.isLoadingMore = true
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let adminId = self.adminId
|
||||
let revoked = self.revoked
|
||||
var lastResult = self.results.last
|
||||
|
||||
if self.forceUpdate {
|
||||
self.populateCache = true
|
||||
self.populateCache = self.isMainList
|
||||
self.forceUpdate = false
|
||||
lastResult = nil
|
||||
} else if self.loadedFromCache {
|
||||
|
|
@ -452,11 +394,11 @@ private final class PeerExportedInvitationsContextImpl {
|
|||
}
|
||||
let populateCache = self.populateCache
|
||||
|
||||
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
self.disposable.set((self.account.postbox.transaction { transaction -> (peerId: Api.InputPeer?, adminId: Api.InputUser?) in
|
||||
return (transaction.getPeer(peerId).flatMap(apiInputPeer), transaction.getPeer(adminId).flatMap(apiInputUser))
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<([ExportedInvitation], Int32), NoError> in
|
||||
if let inputPeer = inputPeer {
|
||||
|> mapToSignal { inputPeer, adminId -> Signal<([ExportedInvitation], Int32), NoError> in
|
||||
if let inputPeer = inputPeer, let adminId = adminId {
|
||||
let offsetLink = lastResult?.link
|
||||
let offsetDate = lastResult?.date
|
||||
var flags: Int32 = 0
|
||||
|
|
@ -466,7 +408,7 @@ private final class PeerExportedInvitationsContextImpl {
|
|||
if revoked {
|
||||
flags |= (1 << 3)
|
||||
}
|
||||
let signal = account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: nil, offsetDate: offsetDate, offsetLink: offsetLink, limit: lastResult == nil ? 50 : 100))
|
||||
let signal = account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: adminId, offsetDate: offsetDate, offsetLink: offsetLink, limit: lastResult == nil ? 50 : 100))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.ExportedChatInvites?, NoError> in
|
||||
return .single(nil)
|
||||
|
|
@ -527,7 +469,7 @@ private final class PeerExportedInvitationsContextImpl {
|
|||
strongSelf.loadMore()
|
||||
}
|
||||
}))
|
||||
self.updateState()*/
|
||||
self.updateState()
|
||||
}
|
||||
|
||||
public func add(_ invite: ExportedInvitation) {
|
||||
|
|
@ -600,10 +542,10 @@ public final class PeerExportedInvitationsContext {
|
|||
}
|
||||
}
|
||||
|
||||
public init(account: Account, peerId: PeerId, revoked: Bool, forceUpdate: Bool) {
|
||||
public init(account: Account, peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return PeerExportedInvitationsContextImpl(queue: queue, account: account, peerId: peerId, revoked: revoked, forceUpdate: forceUpdate)
|
||||
return PeerExportedInvitationsContextImpl(queue: queue, account: account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -783,7 +725,7 @@ private final class PeerInvitationImportersContextImpl {
|
|||
if self.isLoadingMore {
|
||||
return
|
||||
}
|
||||
/*self.isLoadingMore = true
|
||||
self.isLoadingMore = true
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let link = self.link
|
||||
|
|
@ -871,7 +813,7 @@ private final class PeerInvitationImportersContextImpl {
|
|||
}
|
||||
strongSelf.updateState()
|
||||
}))
|
||||
self.updateState()*/
|
||||
self.updateState()
|
||||
}
|
||||
|
||||
private func updateState() {
|
||||
|
|
@ -908,3 +850,62 @@ public final class PeerInvitationImportersContext {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExportedInvitationCreator : Equatable {
|
||||
public let peer: RenderedPeer
|
||||
public let count: Int32
|
||||
}
|
||||
|
||||
public func peerExportedInvitationsCreators(account: Account, peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<[ExportedInvitationCreator], NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
var isCreator = false
|
||||
if let peer = peer as? TelegramGroup, case .creator = peer.role {
|
||||
isCreator = true
|
||||
} else if let peer = peer as? TelegramChannel, peer.flags.contains(.isCreator) {
|
||||
isCreator = true
|
||||
}
|
||||
if !isCreator {
|
||||
return .single([])
|
||||
} else {
|
||||
return account.network.request(Api.functions.messages.getAdminsWithInvites(peer: inputPeer))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.ChatAdminsWithInvites?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<[ExportedInvitationCreator], NoError> in
|
||||
return account.postbox.transaction { transaction -> [ExportedInvitationCreator] in
|
||||
if let result = result, case let .chatAdminsWithInvites(admins, users) = result {
|
||||
var creators: [ExportedInvitationCreator] = []
|
||||
var peers: [Peer] = []
|
||||
var peersMap: [PeerId: Peer] = [:]
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peersMap[telegramUser.id] = telegramUser
|
||||
}
|
||||
|
||||
for case let .chatAdminWithInvites(adminId, invitesCount) in admins {
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId)
|
||||
if let peer = peersMap[peerId], peerId != account.peerId {
|
||||
creators.append(ExportedInvitationCreator(peer: RenderedPeer(peer: peer), count: invitesCount))
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
|
||||
return creators
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single([])
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
|||
return (state?.main.nextRate ?? 0, lowerBound, inputPeer)
|
||||
} else {
|
||||
return (0, lowerBound, .inputPeerEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
return account.network.request(Api.functions.messages.searchGlobal(flags: flags, folderId: folderId, q: query, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 124
|
||||
return 125
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||
}
|
||||
|
||||
switch action {
|
||||
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL:
|
||||
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall:
|
||||
break
|
||||
case let .messageActionChannelMigrateFrom(_, chatId):
|
||||
result.append(PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId))
|
||||
|
|
@ -371,7 +371,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
|||
extension StoreMessage {
|
||||
convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||
switch apiMessage {
|
||||
case let .message(flags, id, fromId, chatPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, restrictionReason, ttlPeriod):
|
||||
case let .message(flags, id, fromId, chatPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, restrictionReason, _):
|
||||
let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId
|
||||
|
||||
let peerId: PeerId
|
||||
|
|
@ -468,21 +468,13 @@ extension StoreMessage {
|
|||
|
||||
var consumableContent: (Bool, Bool)? = nil
|
||||
|
||||
var addedAutoremoveAttribute = false
|
||||
if let ttlPeriod = ttlPeriod {
|
||||
addedAutoremoveAttribute = true
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttlPeriod, countdownBeginTime: date))
|
||||
}
|
||||
|
||||
if let media = media {
|
||||
let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
||||
if let mediaValue = mediaValue {
|
||||
medias.append(mediaValue)
|
||||
|
||||
if let expirationTimer = expirationTimer, expirationTimer > 0 {
|
||||
if !addedAutoremoveAttribute {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: expirationTimer, countdownBeginTime: nil))
|
||||
}
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: expirationTimer, countdownBeginTime: nil))
|
||||
|
||||
consumableContent = (true, false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,8 +71,6 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||
PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
}))
|
||||
}
|
||||
case let .messageActionSetMessagesTTL(period):
|
||||
return TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -300,8 +300,6 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||
}
|
||||
}
|
||||
|
||||
let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(chatFull.ttl))
|
||||
|
||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
||||
let previous: CachedGroupData
|
||||
if let current = current as? CachedGroupData {
|
||||
|
|
@ -317,7 +315,6 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||
.withUpdatedAbout(chatFull.about)
|
||||
.withUpdatedFlags(flags)
|
||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
|
||||
.withUpdatedInvitedBy(invitedBy)
|
||||
.withUpdatedPhoto(photo)
|
||||
.withUpdatedActiveCall(updatedActiveCall)
|
||||
|
|
@ -358,7 +355,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||
}
|
||||
|
||||
switch fullChat {
|
||||
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, inputCall, ttlPeriod):
|
||||
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, inputCall, _):
|
||||
var channelFlags = CachedChannelFlags()
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
channelFlags.insert(.canDisplayParticipants)
|
||||
|
|
@ -513,8 +510,6 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||
|
||||
minAvailableMessageIdUpdated = previous.minAvailableMessageId != minAvailableMessageId
|
||||
|
||||
let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(ttlPeriod))
|
||||
|
||||
return previous.withUpdatedFlags(channelFlags)
|
||||
.withUpdatedAbout(about)
|
||||
.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: bannedCount, kickedCount: kickedCount))
|
||||
|
|
@ -529,7 +524,6 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||
.withUpdatedSlowModeTimeout(slowmodeSeconds)
|
||||
.withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate)
|
||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
|
||||
.withUpdatedStatsDatacenterId(statsDc ?? 0)
|
||||
.withUpdatedInvitedBy(invitedBy)
|
||||
.withUpdatedPhoto(photo)
|
||||
|
|
@ -571,11 +565,8 @@ extension CachedPeerAutoremoveTimeout.Value {
|
|||
if let apiValue = apiValue {
|
||||
switch apiValue {
|
||||
case let .peerHistoryTTLPM(flags, ttlPeriodMy, ttlPeriodPeer):
|
||||
guard let ttlPeriodPeer = ttlPeriodPeer else {
|
||||
return nil
|
||||
}
|
||||
let pmOneSide = flags & (1 << 0) != 0
|
||||
self.init(myValue: ttlPeriodMy ?? ttlPeriodPeer, peerValue: ttlPeriodPeer, isGlobal: !pmOneSide)
|
||||
self.init(myValue: ttlPeriodMy, peerValue: ttlPeriodPeer, isGlobal: !pmOneSide)
|
||||
case let .peerHistoryTTL(ttlPeriodPeer):
|
||||
self.init(myValue: ttlPeriodPeer, peerValue: ttlPeriodPeer, isGlobal: true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKit
|
||||
import TelegramApi
|
||||
|
||||
public enum GetServerWalletSaltError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func getServerWalletSalt(network: Network) -> Signal<Data, GetServerWalletSaltError> {
|
||||
return network.request(Api.functions.wallet.getKeySecretSalt(revoke: .boolFalse))
|
||||
|> mapError { _ -> GetServerWalletSaltError in
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> Data in
|
||||
switch result {
|
||||
case let .secretSalt(salt):
|
||||
return salt.makeData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum WalletProxyRequestError {
|
||||
case generic(Int32, String)
|
||||
}
|
||||
|
||||
public func walletProxyRequest(network: Network, data: Data) -> Signal<Data, WalletProxyRequestError> {
|
||||
return network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: data)))
|
||||
|> mapError { error -> WalletProxyRequestError in
|
||||
return .generic(error.errorCode, error.errorDescription)
|
||||
}
|
||||
|> map { result -> Data in
|
||||
switch result {
|
||||
case let .liteResponse(response):
|
||||
return response.makeData()
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -2903,7 +2903,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
hasBots = true
|
||||
}
|
||||
if case let .known(value) = cachedGroupData.autoremoveTimeout {
|
||||
autoremoveTimeout = value?.peerValue
|
||||
autoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedChannelData = peerView.cachedData as? CachedChannelData {
|
||||
if let channel = peer as? TelegramChannel, case .group = channel.info {
|
||||
|
|
@ -2912,11 +2912,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
}
|
||||
if case let .known(value) = cachedChannelData.autoremoveTimeout {
|
||||
autoremoveTimeout = value?.peerValue
|
||||
autoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedUserData = peerView.cachedData as? CachedUserData {
|
||||
if case let .known(value) = cachedUserData.autoremoveTimeout {
|
||||
autoremoveTimeout = value?.peerValue
|
||||
autoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7681,16 +7681,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
guard let actionSheet = actionSheet else {
|
||||
return
|
||||
}
|
||||
actionSheet.dismissAnimated()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.8, {
|
||||
self?.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
})
|
||||
|
||||
actionSheet.dismissAnimated()
|
||||
|
||||
let controller = peerAutoremoveSetupScreen(context: strongSelf.context, peerId: peer.id, completion: { updatedValue in
|
||||
if case .updated = updatedValue {
|
||||
if currentAutoremoveTimeout == nil {
|
||||
self?.navigationButtonAction(.clearHistory)
|
||||
if case let .updated(value) = updatedValue {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let value = value {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .succeed(text: strongSelf.presentationData.strings.Conversation_AutoremoveChanged("\(timeIntervalString(strings: strongSelf.presentationData.strings, value: value))").0), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -654,17 +654,17 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||
discussionPeer = peer
|
||||
}
|
||||
|
||||
// if currentInvitationsContext == nil {
|
||||
// var canManageInvitations = false
|
||||
// if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
// canManageInvitations = true
|
||||
// }
|
||||
// if canManageInvitations {
|
||||
// let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||
// invitationsContextPromise.set(.single(invitationsContext))
|
||||
// invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
// }
|
||||
// }
|
||||
if currentInvitationsContext == nil {
|
||||
var canManageInvitations = false
|
||||
if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
canManageInvitations = true
|
||||
}
|
||||
if canManageInvitations {
|
||||
let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: nil, revoked: false, forceUpdate: true)
|
||||
invitationsContextPromise.set(.single(invitationsContext))
|
||||
invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
}
|
||||
}
|
||||
|
||||
return PeerInfoScreenData(
|
||||
peer: peerView.peers[peerId],
|
||||
|
|
@ -811,19 +811,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||
}
|
||||
}
|
||||
|
||||
// if currentInvitationsContext == nil {
|
||||
// var canManageInvitations = false
|
||||
// if let group = peerViewMainPeer(peerView) as? TelegramGroup, case .creator = group.role {
|
||||
// canManageInvitations = true
|
||||
// } else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
// canManageInvitations = true
|
||||
// }
|
||||
// if canManageInvitations {
|
||||
// let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||
// invitationsContextPromise.set(.single(invitationsContext))
|
||||
// invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
// }
|
||||
// }
|
||||
if currentInvitationsContext == nil {
|
||||
var canManageInvitations = false
|
||||
if let group = peerViewMainPeer(peerView) as? TelegramGroup, case .creator = group.role {
|
||||
canManageInvitations = true
|
||||
} else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
|
||||
canManageInvitations = true
|
||||
}
|
||||
if canManageInvitations {
|
||||
let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: nil, revoked: false, forceUpdate: true)
|
||||
invitationsContextPromise.set(.single(invitationsContext))
|
||||
invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||
}
|
||||
}
|
||||
|
||||
return PeerInfoScreenData(
|
||||
peer: peerView.peers[groupId],
|
||||
|
|
|
|||
|
|
@ -931,8 +931,8 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||
var directionIsToRight: Bool?
|
||||
if abs(velocity.x) > 10.0 {
|
||||
directionIsToRight = velocity.x < 0.0
|
||||
} else if abs(transitionFraction) > 0.5 {
|
||||
directionIsToRight = transitionFraction < 0.0
|
||||
} else if abs(self.transitionFraction) > 0.5 {
|
||||
directionIsToRight = self.transitionFraction < 0.0
|
||||
}
|
||||
var updatedIndex = self.currentIndex
|
||||
if let directionIsToRight = directionIsToRight {
|
||||
|
|
|
|||
|
|
@ -1224,18 +1224,18 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||
invitesText = ""
|
||||
}
|
||||
|
||||
// items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
// interaction.editingOpenInviteLinksSetup()
|
||||
// }))
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemDiscussionGroup, label: .text(discussionGroupTitle), text: presentationData.strings.Channel_DiscussionGroup, action: {
|
||||
interaction.editingOpenDiscussionGroupSetup()
|
||||
}))
|
||||
|
||||
if channel.hasPermission(.changeInfo) {
|
||||
/*if channel.hasPermission(.changeInfo) {
|
||||
let timeoutString: String
|
||||
if case let .known(value) = (data.cachedData as? CachedChannelData)?.autoremoveTimeout {
|
||||
if let value = value?.peerValue {
|
||||
if let value = value?.effectiveValue {
|
||||
timeoutString = timeIntervalString(strings: presentationData.strings, value: value)
|
||||
} else {
|
||||
timeoutString = presentationData.strings.PeerInfo_AutoremoveMessagesDisabled
|
||||
|
|
@ -1247,7 +1247,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAutoremove, label: .text(timeoutString), text: presentationData.strings.PeerInfo_AutoremoveMessages, action: {
|
||||
interaction.editingOpenAutoremoveMesages()
|
||||
}))
|
||||
}
|
||||
}*/
|
||||
|
||||
let messagesShouldHaveSignatures: Bool
|
||||
switch channel.info {
|
||||
|
|
@ -1326,9 +1326,9 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||
invitesText = ""
|
||||
}
|
||||
|
||||
// items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
// interaction.editingOpenInviteLinksSetup()
|
||||
// }))
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
}
|
||||
|
||||
if cachedData.flags.contains(.canChangeUsername) {
|
||||
|
|
@ -1353,7 +1353,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||
/*if channel.hasPermission(.changeInfo) {
|
||||
let timeoutString: String
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
if let value = value?.peerValue {
|
||||
if let value = value?.effectiveValue {
|
||||
timeoutString = timeIntervalString(strings: presentationData.strings, value: value)
|
||||
} else {
|
||||
timeoutString = presentationData.strings.PeerInfo_AutoremoveMessagesDisabled
|
||||
|
|
@ -1428,9 +1428,9 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||
invitesText = ""
|
||||
}
|
||||
|
||||
// items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
// interaction.editingOpenInviteLinksSetup()
|
||||
// }))
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistory, action: {
|
||||
interaction.editingOpenPreHistorySetup()
|
||||
|
|
@ -3808,7 +3808,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||
}
|
||||
|
||||
private func editingOpenInviteLinksSetup() {
|
||||
self.controller?.push(inviteLinkListController(context: self.context, peerId: self.peerId))
|
||||
self.controller?.push(inviteLinkListController(context: self.context, peerId: self.peerId, admin: nil))
|
||||
}
|
||||
|
||||
private func editingOpenDiscussionGroupSetup() {
|
||||
|
|
|
|||
|
|
@ -48,12 +48,16 @@ public final class OngoingGroupCallContext {
|
|||
let isMuted = ValuePromise<Bool>(true, ignoreRepeated: true)
|
||||
let audioLevels = ValuePipe<[(AudioLevelKey, Float, Bool)]>()
|
||||
|
||||
init(queue: Queue, inputDeviceId: String, outputDeviceId: String) {
|
||||
let videoSources = ValuePromise<Set<UInt32>>(Set(), ignoreRepeated: true)
|
||||
|
||||
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void) {
|
||||
self.queue = queue
|
||||
|
||||
var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)?
|
||||
var audioLevelsUpdatedImpl: (([NSNumber]) -> Void)?
|
||||
var participantDescriptionsRequiredImpl: (([NSNumber]) -> Void)?
|
||||
|
||||
let videoSources = self.videoSources
|
||||
self.context = GroupCallThreadLocalContext(
|
||||
queue: ContextQueueImpl(queue: queue),
|
||||
networkStateUpdated: { state in
|
||||
|
|
@ -63,7 +67,14 @@ public final class OngoingGroupCallContext {
|
|||
audioLevelsUpdatedImpl?(levels)
|
||||
},
|
||||
inputDeviceId: inputDeviceId,
|
||||
outputDeviceId: outputDeviceId
|
||||
outputDeviceId: outputDeviceId,
|
||||
videoCapturer: video?.impl,
|
||||
incomingVideoSourcesUpdated: { ssrcs in
|
||||
videoSources.set(Set(ssrcs.map { $0.uint32Value }))
|
||||
},
|
||||
participantDescriptionsRequired: { ssrcs in
|
||||
participantDescriptionsRequired(Set(ssrcs.map { $0.uint32Value }))
|
||||
}
|
||||
)
|
||||
|
||||
let queue = self.queue
|
||||
|
|
@ -116,9 +127,10 @@ public final class OngoingGroupCallContext {
|
|||
})
|
||||
}
|
||||
|
||||
func setJoinResponse(payload: String, ssrcs: [UInt32]) {
|
||||
self.context.setJoinResponsePayload(payload)
|
||||
self.addSsrcs(ssrcs: ssrcs)
|
||||
func setJoinResponse(payload: String, participants: [(UInt32, String?)]) {
|
||||
self.context.setJoinResponsePayload(payload, participants: participants.map { participant -> OngoingGroupCallParticipantDescription in
|
||||
return OngoingGroupCallParticipantDescription(audioSsrc: participant.0, jsonParams: participant.1)
|
||||
})
|
||||
}
|
||||
|
||||
func addSsrcs(ssrcs: [UInt32]) {
|
||||
|
|
@ -137,6 +149,19 @@ public final class OngoingGroupCallContext {
|
|||
self.context.setVolumeForSsrc(ssrc, volume: volume)
|
||||
}
|
||||
|
||||
func setFullSizeVideoSsrc(ssrc: UInt32?) {
|
||||
self.context.setFullSizeVideoSsrc(ssrc ?? 0)
|
||||
}
|
||||
|
||||
func addParticipants(participants: [(UInt32, String?)]) {
|
||||
if participants.isEmpty {
|
||||
return
|
||||
}
|
||||
self.context.addParticipants(participants.map { participant -> OngoingGroupCallParticipantDescription in
|
||||
return OngoingGroupCallParticipantDescription(audioSsrc: participant.0, jsonParams: participant.1)
|
||||
})
|
||||
}
|
||||
|
||||
func stop() {
|
||||
self.context.stop()
|
||||
}
|
||||
|
|
@ -146,12 +171,111 @@ public final class OngoingGroupCallContext {
|
|||
self.context.setIsMuted(isMuted)
|
||||
}
|
||||
|
||||
func requestVideo(_ capturer: OngoingCallVideoCapturer?) {
|
||||
let queue = self.queue
|
||||
self.context.requestVideo(capturer?.impl, completion: { [weak self] payload, ssrc in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.joinPayload.set(.single((payload, ssrc)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
let queue = self.queue
|
||||
self.context.disableVideo({ [weak self] payload, ssrc in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.joinPayload.set(.single((payload, ssrc)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func switchAudioInput(_ deviceId: String) {
|
||||
self.context.switchAudioInput(deviceId)
|
||||
}
|
||||
|
||||
func switchAudioOutput(_ deviceId: String) {
|
||||
self.context.switchAudioOutput(deviceId)
|
||||
}
|
||||
|
||||
func makeIncomingVideoView(source: UInt32, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||
self.context.makeIncomingVideoView(withSsrc: source, completion: { view in
|
||||
if let view = view {
|
||||
#if os(iOS)
|
||||
completion(OngoingCallContextPresentationCallVideoView(
|
||||
view: view,
|
||||
setOnFirstFrameReceived: { [weak view] f in
|
||||
view?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
return OngoingCallVideoOrientation(view.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak view] f in
|
||||
view?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak view] f in
|
||||
view?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
}
|
||||
))
|
||||
#else
|
||||
completion(OngoingCallContextPresentationCallVideoView(
|
||||
view: view,
|
||||
setOnFirstFrameReceived: { [weak view] f in
|
||||
view?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
return OngoingCallVideoOrientation(view.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak view] f in
|
||||
view?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
}, setVideoContentMode: { [weak view] mode in
|
||||
view?.setVideoContentMode(mode)
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak view] f in
|
||||
view?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
}
|
||||
))
|
||||
#endif
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private let queue = Queue()
|
||||
|
|
@ -205,10 +329,22 @@ public final class OngoingGroupCallContext {
|
|||
}
|
||||
}
|
||||
|
||||
public init(inputDeviceId: String = "", outputDeviceId: String = "") {
|
||||
public var videoSources: Signal<Set<UInt32>, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.videoSources.get().start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void) {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId)
|
||||
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +354,18 @@ public final class OngoingGroupCallContext {
|
|||
}
|
||||
}
|
||||
|
||||
public func requestVideo(_ capturer: OngoingCallVideoCapturer?) {
|
||||
self.impl.with { impl in
|
||||
impl.requestVideo(capturer)
|
||||
}
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
self.impl.with { impl in
|
||||
impl.disableVideo()
|
||||
}
|
||||
}
|
||||
|
||||
public func switchAudioInput(_ deviceId: String) {
|
||||
self.impl.with { impl in
|
||||
impl.switchAudioInput(deviceId)
|
||||
|
|
@ -228,9 +376,9 @@ public final class OngoingGroupCallContext {
|
|||
impl.switchAudioOutput(deviceId)
|
||||
}
|
||||
}
|
||||
public func setJoinResponse(payload: String, ssrcs: [UInt32]) {
|
||||
public func setJoinResponse(payload: String, participants: [(UInt32, String?)]) {
|
||||
self.impl.with { impl in
|
||||
impl.setJoinResponse(payload: payload, ssrcs: ssrcs)
|
||||
impl.setJoinResponse(payload: payload, participants: participants)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,9 +400,27 @@ public final class OngoingGroupCallContext {
|
|||
}
|
||||
}
|
||||
|
||||
public func setFullSizeVideoSsrc(ssrc: UInt32?) {
|
||||
self.impl.with { impl in
|
||||
impl.setFullSizeVideoSsrc(ssrc: ssrc)
|
||||
}
|
||||
}
|
||||
|
||||
public func addParticipants(participants: [(UInt32, String?)]) {
|
||||
self.impl.with { impl in
|
||||
impl.addParticipants(participants: participants)
|
||||
}
|
||||
}
|
||||
|
||||
public func stop() {
|
||||
self.impl.with { impl in
|
||||
impl.stop()
|
||||
}
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(source: UInt32, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||
self.impl.with { impl in
|
||||
impl.makeIncomingVideoView(source: source, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -335,8 +335,8 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
|||
public final class OngoingCallVideoCapturer {
|
||||
internal let impl: OngoingCallThreadLocalContextVideoCapturer
|
||||
|
||||
public init() {
|
||||
self.impl = OngoingCallThreadLocalContextVideoCapturer()
|
||||
public init(keepLandscape: Bool = false) {
|
||||
self.impl = OngoingCallThreadLocalContextVideoCapturer(deviceId: "", keepLandscape: keepLandscape)
|
||||
}
|
||||
|
||||
public func switchVideoInput(isFront: Bool) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ objc_library(
|
|||
copts = [
|
||||
"-I{}/PublicHeaders/TgVoip".format(package_name()),
|
||||
"-I{}/libtgvoip".format(package_name()),
|
||||
"-I{}/third-party/webrtc/webrtc-ios/src".format(package_name()),
|
||||
"-I{}/third-party/webrtc/webrtc".format(package_name()),
|
||||
"-Isubmodules/Opus/Public/opus",
|
||||
"-DTGVOIP_USE_INSTALLED_OPUS",
|
||||
"-Drtc=rtc1",
|
||||
|
|
|
|||
|
|
@ -26,15 +26,16 @@ objc_library(
|
|||
]),
|
||||
copts = [
|
||||
"-I{}/tgcalls/tgcalls".format(package_name()),
|
||||
"-Ithird-party/webrtc/webrtc-ios/src",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/third_party/abseil-cpp",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/sdk/objc",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/base",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/components/renderer/metal",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/components/renderer/opengl",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/components/video_codec",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/third_party/libyuv/include",
|
||||
"-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/api/video_codec",
|
||||
"-Ithird-party/webrtc/webrtc",
|
||||
"-Ithird-party/webrtc/dependencies",
|
||||
"-Ithird-party/webrtc/dependencies/third_party/abseil-cpp",
|
||||
"-Ithird-party/webrtc/webrtc/sdk/objc",
|
||||
"-Ithird-party/webrtc/webrtc/sdk/objc/base",
|
||||
"-Ithird-party/webrtc/webrtc/sdk/objc/components/renderer/metal",
|
||||
"-Ithird-party/webrtc/webrtc/sdk/objc/components/renderer/opengl",
|
||||
"-Ithird-party/webrtc/webrtc/sdk/objc/components/video_codec",
|
||||
"-Ithird-party/webrtc/dependencies/third_party/libyuv/include",
|
||||
"-Ithird-party/webrtc/webrtc/sdk/objc/api/video_codec",
|
||||
"-DWEBRTC_IOS",
|
||||
"-DWEBRTC_MAC",
|
||||
"-DWEBRTC_POSIX",
|
||||
|
|
|
|||
|
|
@ -108,8 +108,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||
|
||||
@interface OngoingCallThreadLocalContextVideoCapturer : NSObject
|
||||
|
||||
- (instancetype _Nonnull)init;
|
||||
- (instancetype _Nonnull)initWithDeviceId:(NSString * _Nonnull)deviceId;
|
||||
- (instancetype _Nonnull)initWithDeviceId:(NSString * _Nonnull)deviceId keepLandscape:(bool)keepLandscape;
|
||||
|
||||
- (void)switchVideoInput:(NSString * _Nonnull)deviceId;
|
||||
- (void)setIsVideoEnabled:(bool)isVideoEnabled;
|
||||
|
|
@ -157,21 +156,35 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) {
|
|||
GroupCallNetworkStateConnected
|
||||
};
|
||||
|
||||
@interface OngoingGroupCallParticipantDescription : NSObject
|
||||
|
||||
@property (nonatomic, readonly) uint32_t audioSsrc;
|
||||
@property (nonatomic, strong, readonly) NSString * _Nullable jsonParams;
|
||||
|
||||
- (instancetype _Nonnull)initWithAudioSsrc:(uint32_t)audioSsrc jsonParams:(NSString * _Nullable)jsonParams;
|
||||
|
||||
@end
|
||||
|
||||
@interface GroupCallThreadLocalContext : NSObject
|
||||
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId;
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired;
|
||||
|
||||
- (void)stop;
|
||||
|
||||
- (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
|
||||
- (void)setJoinResponsePayload:(NSString * _Nonnull)payload;
|
||||
- (void)setJoinResponsePayload:(NSString * _Nonnull)payload participants:(NSArray<OngoingGroupCallParticipantDescription *> * _Nonnull)participants;
|
||||
- (void)removeSsrcs:(NSArray<NSNumber *> * _Nonnull)ssrcs;
|
||||
- (void)addParticipants:(NSArray<OngoingGroupCallParticipantDescription *> * _Nonnull)participants;
|
||||
- (void)setIsMuted:(bool)isMuted;
|
||||
- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer completion:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
|
||||
- (void)disableVideo:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
|
||||
|
||||
- (void)setVolumeForSsrc:(uint32_t)ssrc volume:(double)volume;
|
||||
- (void)setFullSizeVideoSsrc:(uint32_t)ssrc;
|
||||
|
||||
- (void)switchAudioOutput:(NSString * _Nonnull)deviceId;
|
||||
- (void)switchAudioInput:(NSString * _Nonnull)deviceId;
|
||||
- (void)makeIncomingVideoViewWithSsrc:(uint32_t)ssrc completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#endif
|
||||
|
||||
#import "group/GroupInstanceImpl.h"
|
||||
#import "group/GroupInstanceCustomImpl.h"
|
||||
|
||||
@implementation OngoingCallConnectionDescriptionWebrtc
|
||||
|
||||
|
|
@ -140,16 +141,24 @@
|
|||
|
||||
@end
|
||||
|
||||
@implementation OngoingCallThreadLocalContextVideoCapturer
|
||||
|
||||
- (instancetype _Nonnull)init {
|
||||
return [self initWithDeviceId:@""];
|
||||
@interface OngoingCallThreadLocalContextVideoCapturer () {
|
||||
bool _keepLandscape;
|
||||
}
|
||||
|
||||
- (instancetype _Nonnull)initWithDeviceId:(NSString * _Nonnull)deviceId {
|
||||
@end
|
||||
|
||||
@implementation OngoingCallThreadLocalContextVideoCapturer
|
||||
|
||||
- (instancetype _Nonnull)initWithDeviceId:(NSString * _Nonnull)deviceId keepLandscape:(bool)keepLandscape {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_interface = tgcalls::VideoCaptureInterface::Create(deviceId.UTF8String);
|
||||
_keepLandscape = keepLandscape;
|
||||
|
||||
std::string resolvedId = deviceId.UTF8String;
|
||||
if (keepLandscape) {
|
||||
resolvedId += std::string(":landscape");
|
||||
}
|
||||
_interface = tgcalls::VideoCaptureInterface::Create(resolvedId);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
@ -159,7 +168,11 @@
|
|||
}
|
||||
|
||||
- (void)switchVideoInput:(NSString * _Nonnull)deviceId {
|
||||
_interface->switchToDevice(deviceId.UTF8String);
|
||||
std::string resolvedId = deviceId.UTF8String;
|
||||
if (_keepLandscape) {
|
||||
resolvedId += std::string(":landscape");
|
||||
}
|
||||
_interface->switchToDevice(resolvedId);
|
||||
}
|
||||
|
||||
- (void)setIsVideoEnabled:(bool)isVideoEnabled {
|
||||
|
|
@ -183,7 +196,10 @@
|
|||
completion(remoteRenderer);
|
||||
} else {
|
||||
GLVideoView *remoteRenderer = [[GLVideoView alloc] initWithFrame:CGRectZero];
|
||||
|
||||
#ifndef WEBRTC_IOS
|
||||
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;
|
||||
#endif
|
||||
|
||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||
interface->setOutput(sink);
|
||||
|
||||
|
|
@ -807,7 +823,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
@interface GroupCallThreadLocalContext () {
|
||||
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
|
||||
|
||||
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
|
||||
std::unique_ptr<tgcalls::GroupInstanceInterface> _instance;
|
||||
OngoingCallThreadLocalContextVideoCapturer *_videoCapturer;
|
||||
|
||||
void (^_networkStateUpdated)(GroupCallNetworkState);
|
||||
|
|
@ -817,15 +833,16 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
|
||||
@implementation GroupCallThreadLocalContext
|
||||
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId {
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
|
||||
_networkStateUpdated = [networkStateUpdated copy];
|
||||
_videoCapturer = videoCapturer;
|
||||
|
||||
__weak GroupCallThreadLocalContext *weakSelf = self;
|
||||
_instance.reset(new tgcalls::GroupInstanceImpl((tgcalls::GroupInstanceDescriptor){
|
||||
_instance.reset(new tgcalls::GroupInstanceCustomImpl((tgcalls::GroupInstanceDescriptor){
|
||||
.networkStateUpdated = [weakSelf, queue, networkStateUpdated](bool isConnected) {
|
||||
[queue dispatch:^{
|
||||
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||
|
|
@ -845,7 +862,22 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
audioLevelsUpdated(result);
|
||||
},
|
||||
.initialInputDeviceId = inputDeviceId.UTF8String,
|
||||
.initialOutputDeviceId = outputDeviceId.UTF8String
|
||||
.initialOutputDeviceId = outputDeviceId.UTF8String,
|
||||
.videoCapture = [_videoCapturer getInterface],
|
||||
.incomingVideoSourcesUpdated = [incomingVideoSourcesUpdated](std::vector<uint32_t> const &ssrcs) {
|
||||
NSMutableArray<NSNumber *> *mappedSources = [[NSMutableArray alloc] init];
|
||||
for (auto it : ssrcs) {
|
||||
[mappedSources addObject:@(it)];
|
||||
}
|
||||
incomingVideoSourcesUpdated(mappedSources);
|
||||
},
|
||||
.participantDescriptionsRequired = [participantDescriptionsRequired](std::vector<uint32_t> const &ssrcs) {
|
||||
NSMutableArray<NSNumber *> *mappedSources = [[NSMutableArray alloc] init];
|
||||
for (auto it : ssrcs) {
|
||||
[mappedSources addObject:@(it)];
|
||||
}
|
||||
participantDescriptionsRequired(mappedSources);
|
||||
}
|
||||
}));
|
||||
}
|
||||
return self;
|
||||
|
|
@ -858,37 +890,111 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
}
|
||||
}
|
||||
|
||||
static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonnull completion)(NSString * _Nonnull, uint32_t)) {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
int32_t signedSsrc = *(int32_t *)&payload.ssrc;
|
||||
|
||||
dict[@"ssrc"] = @(signedSsrc);
|
||||
dict[@"ufrag"] = [NSString stringWithUTF8String:payload.ufrag.c_str()];
|
||||
dict[@"pwd"] = [NSString stringWithUTF8String:payload.pwd.c_str()];
|
||||
|
||||
NSMutableArray *fingerprints = [[NSMutableArray alloc] init];
|
||||
for (auto &fingerprint : payload.fingerprints) {
|
||||
[fingerprints addObject:@{
|
||||
@"hash": [NSString stringWithUTF8String:fingerprint.hash.c_str()],
|
||||
@"fingerprint": [NSString stringWithUTF8String:fingerprint.fingerprint.c_str()],
|
||||
@"setup": [NSString stringWithUTF8String:fingerprint.setup.c_str()]
|
||||
}];
|
||||
}
|
||||
|
||||
dict[@"fingerprints"] = fingerprints;
|
||||
|
||||
NSMutableArray *parsedVideoSsrcGroups = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *parsedVideoSources = [[NSMutableArray alloc] init];
|
||||
for (auto &group : payload.videoSourceGroups) {
|
||||
NSMutableDictionary *parsedGroup = [[NSMutableDictionary alloc] init];
|
||||
parsedGroup[@"semantics"] = [NSString stringWithUTF8String:group.semantics.c_str()];
|
||||
NSMutableArray *sources = [[NSMutableArray alloc] init];
|
||||
for (auto &source : group.ssrcs) {
|
||||
[sources addObject:@(source)];
|
||||
if (![parsedVideoSources containsObject:@(source)]) {
|
||||
[parsedVideoSources addObject:@(source)];
|
||||
}
|
||||
}
|
||||
parsedGroup[@"sources"] = sources;
|
||||
[parsedVideoSsrcGroups addObject:parsedGroup];
|
||||
}
|
||||
if (parsedVideoSsrcGroups.count != 0) {
|
||||
dict[@"ssrc-groups"] = parsedVideoSsrcGroups;
|
||||
}
|
||||
|
||||
NSMutableArray *videoPayloadTypes = [[NSMutableArray alloc] init];
|
||||
for (auto &payloadType : payload.videoPayloadTypes) {
|
||||
NSMutableDictionary *parsedType = [[NSMutableDictionary alloc] init];
|
||||
parsedType[@"id"] = @(payloadType.id);
|
||||
NSString *name = [NSString stringWithUTF8String:payloadType.name.c_str()];
|
||||
parsedType[@"name"] = name;
|
||||
parsedType[@"clockrate"] = @(payloadType.clockrate);
|
||||
if (![name isEqualToString:@"rtx"]) {
|
||||
parsedType[@"channels"] = @(payloadType.channels);
|
||||
}
|
||||
|
||||
NSMutableDictionary *parsedParameters = [[NSMutableDictionary alloc] init];
|
||||
for (auto &it : payloadType.parameters) {
|
||||
NSString *key = [NSString stringWithUTF8String:it.first.c_str()];
|
||||
NSString *value = [NSString stringWithUTF8String:it.second.c_str()];
|
||||
parsedParameters[key] = value;
|
||||
}
|
||||
if (parsedParameters.count != 0) {
|
||||
parsedType[@"parameters"] = parsedParameters;
|
||||
}
|
||||
|
||||
if (![name isEqualToString:@"rtx"]) {
|
||||
NSMutableArray *parsedFbs = [[NSMutableArray alloc] init];
|
||||
for (auto &it : payloadType.feedbackTypes) {
|
||||
NSMutableDictionary *parsedFb = [[NSMutableDictionary alloc] init];
|
||||
parsedFb[@"type"] = [NSString stringWithUTF8String:it.type.c_str()];
|
||||
if (it.subtype.size() != 0) {
|
||||
parsedFb[@"subtype"] = [NSString stringWithUTF8String:it.subtype.c_str()];
|
||||
}
|
||||
[parsedFbs addObject:parsedFb];
|
||||
}
|
||||
parsedType[@"rtcp-fbs"] = parsedFbs;
|
||||
}
|
||||
|
||||
[videoPayloadTypes addObject:parsedType];
|
||||
}
|
||||
if (videoPayloadTypes.count != 0) {
|
||||
dict[@"payload-types"] = videoPayloadTypes;
|
||||
}
|
||||
|
||||
NSMutableArray *parsedExtensions = [[NSMutableArray alloc] init];
|
||||
for (auto &it : payload.videoExtensionMap) {
|
||||
NSMutableDictionary *parsedExtension = [[NSMutableDictionary alloc] init];
|
||||
parsedExtension[@"id"] = @(it.first);
|
||||
parsedExtension[@"uri"] = [NSString stringWithUTF8String:it.second.c_str()];
|
||||
[parsedExtensions addObject:parsedExtension];
|
||||
}
|
||||
if (parsedExtensions.count != 0) {
|
||||
dict[@"rtp-hdrexts"] = parsedExtensions;
|
||||
}
|
||||
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
|
||||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
|
||||
completion(string, payload.ssrc);
|
||||
}
|
||||
|
||||
- (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion {
|
||||
if (_instance) {
|
||||
_instance->emitJoinPayload([completion](tgcalls::GroupJoinPayload payload) {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
int32_t signedSsrc = *(int32_t *)&payload.ssrc;
|
||||
|
||||
dict[@"ssrc"] = @(signedSsrc);
|
||||
dict[@"ufrag"] = [NSString stringWithUTF8String:payload.ufrag.c_str()];
|
||||
dict[@"pwd"] = [NSString stringWithUTF8String:payload.pwd.c_str()];
|
||||
|
||||
NSMutableArray *fingerprints = [[NSMutableArray alloc] init];
|
||||
for (auto &fingerprint : payload.fingerprints) {
|
||||
[fingerprints addObject:@{
|
||||
@"hash": [NSString stringWithUTF8String:fingerprint.hash.c_str()],
|
||||
@"fingerprint": [NSString stringWithUTF8String:fingerprint.fingerprint.c_str()],
|
||||
@"setup": [NSString stringWithUTF8String:fingerprint.setup.c_str()]
|
||||
}];
|
||||
}
|
||||
|
||||
dict[@"fingerprints"] = fingerprints;
|
||||
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
|
||||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
|
||||
completion(string, payload.ssrc);
|
||||
processJoinPayload(payload, completion);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setJoinResponsePayload:(NSString * _Nonnull)payload {
|
||||
- (void)setJoinResponsePayload:(NSString * _Nonnull)payload participants:(NSArray<OngoingGroupCallParticipantDescription *> * _Nonnull)participants {
|
||||
tgcalls::GroupJoinResponsePayload result;
|
||||
|
||||
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
|
@ -921,7 +1027,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
|
||||
NSArray *fingerprintsValue = transport[@"fingerprints"];
|
||||
if (![fingerprintsValue isKindOfClass:[NSArray class]]) {
|
||||
return;
|
||||
//return;
|
||||
}
|
||||
|
||||
for (NSDictionary *fingerprintValue in fingerprintsValue) {
|
||||
|
|
@ -1031,8 +1137,19 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
result.candidates.push_back(candidate);
|
||||
}
|
||||
|
||||
std::vector<tgcalls::GroupParticipantDescription> parsedParticipants;
|
||||
for (OngoingGroupCallParticipantDescription *participant in participants) {
|
||||
tgcalls::GroupParticipantDescription parsedParticipant;
|
||||
parsedParticipant.audioSsrc = participant.audioSsrc;
|
||||
|
||||
if (participant.jsonParams.length != 0) {
|
||||
[self parseJsonIntoParticipant:participant.jsonParams participant:parsedParticipant];
|
||||
}
|
||||
parsedParticipants.push_back(parsedParticipant);
|
||||
}
|
||||
|
||||
if (_instance) {
|
||||
_instance->setJoinResponsePayload(result);
|
||||
_instance->setJoinResponsePayload(result, std::move(parsedParticipants));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1046,18 +1163,189 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)parseJsonIntoParticipant:(NSString *)payload participant:(tgcalls::GroupParticipantDescription &)participant {
|
||||
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (payloadData == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:payloadData options:0 error:nil];
|
||||
if (![dict isKindOfClass:[NSDictionary class]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *endpointId = dict[@"endpoint"];
|
||||
if (![endpointId isKindOfClass:[NSString class]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
participant.endpointId = [endpointId UTF8String];
|
||||
|
||||
NSArray *ssrcGroups = dict[@"ssrc-groups"];
|
||||
if ([ssrcGroups isKindOfClass:[NSArray class]]) {
|
||||
for (NSDictionary *group in ssrcGroups) {
|
||||
if (![group isKindOfClass:[NSDictionary class]]) {
|
||||
continue;
|
||||
}
|
||||
NSString *semantics = group[@"semantics"];
|
||||
if (![semantics isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
NSArray *sources = group[@"sources"];
|
||||
if (![sources isKindOfClass:[NSArray class]]) {
|
||||
continue;
|
||||
}
|
||||
tgcalls::GroupJoinPayloadVideoSourceGroup groupDesc;
|
||||
for (NSNumber *nSsrc in sources) {
|
||||
if ([nSsrc isKindOfClass:[NSNumber class]]) {
|
||||
groupDesc.ssrcs.push_back([nSsrc unsignedIntValue]);
|
||||
}
|
||||
}
|
||||
groupDesc.semantics = [semantics UTF8String];
|
||||
participant.videoSourceGroups.push_back(groupDesc);
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *hdrExts = dict[@"rtp-hdrexts"];
|
||||
if ([hdrExts isKindOfClass:[NSArray class]]) {
|
||||
for (NSDictionary *extDict in hdrExts) {
|
||||
if (![extDict isKindOfClass:[NSDictionary class]]) {
|
||||
continue;
|
||||
}
|
||||
NSNumber *nId = extDict[@"id"];
|
||||
if (![nId isKindOfClass:[NSNumber class]]) {
|
||||
continue;
|
||||
}
|
||||
NSString *uri = extDict[@"uri"];
|
||||
if (![uri isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
participant.videoExtensionMap.push_back(std::make_pair((uint32_t)[nId unsignedIntValue], (std::string)[uri UTF8String]));
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *payloadTypes = dict[@"payload-types"];
|
||||
if ([payloadTypes isKindOfClass:[NSArray class]]) {
|
||||
for (NSDictionary *payloadDict in payloadTypes) {
|
||||
if (![payloadDict isKindOfClass:[NSDictionary class]]) {
|
||||
continue;
|
||||
}
|
||||
NSNumber *nId = payloadDict[@"id"];
|
||||
if (![nId isKindOfClass:[NSNumber class]]) {
|
||||
continue;
|
||||
}
|
||||
NSNumber *nClockrate = payloadDict[@"clockrate"];
|
||||
if (nClockrate != nil && ![nClockrate isKindOfClass:[NSNumber class]]) {
|
||||
continue;
|
||||
}
|
||||
NSNumber *nChannels = payloadDict[@"channels"];
|
||||
if (nChannels != nil && ![nChannels isKindOfClass:[NSNumber class]]) {
|
||||
continue;
|
||||
}
|
||||
NSString *name = payloadDict[@"name"];
|
||||
if (![name isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tgcalls::GroupJoinPayloadVideoPayloadType parsedPayload;
|
||||
parsedPayload.id = [nId unsignedIntValue];
|
||||
parsedPayload.clockrate = [nClockrate unsignedIntValue];
|
||||
parsedPayload.channels = [nChannels unsignedIntValue];
|
||||
parsedPayload.name = [name UTF8String];
|
||||
|
||||
NSArray *fbs = payloadDict[@"rtcp-fbs"];
|
||||
if ([fbs isKindOfClass:[NSArray class]]) {
|
||||
for (NSDictionary *fbDict in fbs) {
|
||||
if (![fbDict isKindOfClass:[NSDictionary class]]) {
|
||||
continue;
|
||||
}
|
||||
NSString *type = fbDict[@"type"];
|
||||
if (![type isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *subtype = fbDict[@"subtype"];
|
||||
if (subtype != nil && ![subtype isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tgcalls::GroupJoinPayloadVideoPayloadFeedbackType parsedFeedback;
|
||||
parsedFeedback.type = [type UTF8String];
|
||||
if (subtype != nil) {
|
||||
parsedFeedback.subtype = [subtype UTF8String];
|
||||
}
|
||||
parsedPayload.feedbackTypes.push_back(parsedFeedback);
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *parameters = payloadDict[@"parameters"];
|
||||
if ([parameters isKindOfClass:[NSDictionary class]]) {
|
||||
for (NSString *nKey in parameters) {
|
||||
if (![nKey isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
NSString *value = parameters[nKey];
|
||||
if (![value isKindOfClass:[NSString class]]) {
|
||||
continue;
|
||||
}
|
||||
parsedPayload.parameters.push_back(std::make_pair((std::string)[nKey UTF8String], (std::string)[value UTF8String]));
|
||||
}
|
||||
}
|
||||
participant.videoPayloadTypes.push_back(parsedPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addParticipants:(NSArray<OngoingGroupCallParticipantDescription *> * _Nonnull)participants {
|
||||
if (_instance) {
|
||||
std::vector<tgcalls::GroupParticipantDescription> parsedParticipants;
|
||||
for (OngoingGroupCallParticipantDescription *participant in participants) {
|
||||
tgcalls::GroupParticipantDescription parsedParticipant;
|
||||
parsedParticipant.audioSsrc = participant.audioSsrc;
|
||||
|
||||
if (participant.jsonParams.length != 0) {
|
||||
[self parseJsonIntoParticipant:participant.jsonParams participant:parsedParticipant];
|
||||
}
|
||||
parsedParticipants.push_back(parsedParticipant);
|
||||
}
|
||||
_instance->addParticipants(std::move(parsedParticipants));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIsMuted:(bool)isMuted {
|
||||
if (_instance) {
|
||||
_instance->setIsMuted(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer completion:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion {
|
||||
if (_instance) {
|
||||
_instance->setVideoCapture([videoCapturer getInterface], [completion](auto payload){
|
||||
processJoinPayload(payload, completion);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)disableVideo:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion {
|
||||
if (_instance) {
|
||||
_instance->setVideoCapture(nullptr, [completion](auto payload){
|
||||
processJoinPayload(payload, completion);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setVolumeForSsrc:(uint32_t)ssrc volume:(double)volume {
|
||||
if (_instance) {
|
||||
_instance->setVolume(ssrc, volume);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFullSizeVideoSsrc:(uint32_t)ssrc {
|
||||
if (_instance) {
|
||||
_instance->setFullSizeVideoSsrc(ssrc);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)switchAudioOutput:(NSString * _Nonnull)deviceId {
|
||||
if (_instance) {
|
||||
_instance->setAudioOutputDevice(deviceId.UTF8String);
|
||||
|
|
@ -1069,5 +1357,58 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)makeIncomingVideoViewWithSsrc:(uint32_t)ssrc completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion {
|
||||
if (_instance) {
|
||||
__weak GroupCallThreadLocalContext *weakSelf = self;
|
||||
id<OngoingCallThreadLocalContextQueueWebrtc> queue = _queue;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([VideoMetalView isSupported]) {
|
||||
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
||||
#if TARGET_OS_IPHONE
|
||||
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
||||
#else
|
||||
remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
|
||||
#endif
|
||||
|
||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||
|
||||
[queue dispatch:^{
|
||||
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||
if (strongSelf && strongSelf->_instance) {
|
||||
strongSelf->_instance->addIncomingVideoOutput(ssrc, sink);
|
||||
}
|
||||
}];
|
||||
|
||||
completion(remoteRenderer);
|
||||
} else {
|
||||
GLVideoView *remoteRenderer = [[GLVideoView alloc] initWithFrame:CGRectZero];
|
||||
// [remoteRenderer setVideoContentMode:kCAGravityResizeAspectFill];
|
||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||
|
||||
[queue dispatch:^{
|
||||
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||
if (strongSelf && strongSelf->_instance) {
|
||||
strongSelf->_instance->addIncomingVideoOutput(ssrc, sink);
|
||||
}
|
||||
}];
|
||||
|
||||
completion(remoteRenderer);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation OngoingGroupCallParticipantDescription
|
||||
|
||||
- (instancetype _Nonnull)initWithAudioSsrc:(uint32_t)audioSsrc jsonParams:(NSString * _Nullable)jsonParams {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_audioSsrc = audioSsrc;
|
||||
_jsonParams = jsonParams;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit ff6c6f97a6546049f0543bb5322789348e119b9d
|
||||
Subproject commit 672791f633be6ed042bf6340738e17297bd68de3
|
||||
|
|
@ -490,36 +490,73 @@ public func parseWallpaperUrl(_ url: String) -> WallpaperUrlParameter? {
|
|||
return nil
|
||||
}
|
||||
|
||||
private struct UrlHandlingConfiguration {
|
||||
static var defaultValue: UrlHandlingConfiguration {
|
||||
return UrlHandlingConfiguration(token: nil, domains: [])
|
||||
}
|
||||
|
||||
public let token: String?
|
||||
public let domains: [String]
|
||||
|
||||
fileprivate init(token: String?, domains: [String]) {
|
||||
self.token = token
|
||||
self.domains = domains
|
||||
}
|
||||
|
||||
static func with(appConfiguration: AppConfiguration) -> UrlHandlingConfiguration {
|
||||
if let data = appConfiguration.data, let token = data["autologin_token"] as? String, let domains = data["autologin_domains"] as? [String] {
|
||||
return UrlHandlingConfiguration(token: token, domains: domains)
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func resolveUrlImpl(account: Account, url: String) -> Signal<ResolvedUrl, NoError> {
|
||||
let schemes = ["http://", "https://", ""]
|
||||
for basePath in baseTelegramMePaths {
|
||||
for scheme in schemes {
|
||||
let basePrefix = scheme + basePath + "/"
|
||||
if url.lowercased().hasPrefix(basePrefix) {
|
||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
||||
return resolveInternalUrl(account: account, url: internalUrl)
|
||||
|> map { resolved -> ResolvedUrl in
|
||||
if let resolved = resolved {
|
||||
return resolved
|
||||
} else {
|
||||
return .externalUrl(url)
|
||||
|
||||
return account.postbox.transaction { transaction -> Signal<ResolvedUrl, NoError> in
|
||||
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
|
||||
let urlHandlingConfiguration = UrlHandlingConfiguration.with(appConfiguration: appConfiguration)
|
||||
|
||||
var url = url
|
||||
if let urlValue = URL(string: url), let host = urlValue.host, urlHandlingConfiguration.domains.contains(host), var components = URLComponents(string: url) {
|
||||
components.scheme = "https"
|
||||
var queryItems = components.queryItems ?? []
|
||||
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
|
||||
components.queryItems = queryItems
|
||||
url = components.url?.absoluteString ?? url
|
||||
}
|
||||
|
||||
for basePath in baseTelegramMePaths {
|
||||
for scheme in schemes {
|
||||
let basePrefix = scheme + basePath + "/"
|
||||
if url.lowercased().hasPrefix(basePrefix) {
|
||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
||||
return resolveInternalUrl(account: account, url: internalUrl)
|
||||
|> map { resolved -> ResolvedUrl in
|
||||
if let resolved = resolved {
|
||||
return resolved
|
||||
} else {
|
||||
return .externalUrl(url)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.externalUrl(url))
|
||||
}
|
||||
} else {
|
||||
return .single(.externalUrl(url))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for basePath in baseTelegraPhPaths {
|
||||
for scheme in schemes {
|
||||
let basePrefix = scheme + basePath
|
||||
if url.lowercased().hasPrefix(basePrefix) {
|
||||
return resolveInstantViewUrl(account: account, url: url)
|
||||
for basePath in baseTelegraPhPaths {
|
||||
for scheme in schemes {
|
||||
let basePrefix = scheme + basePath
|
||||
if url.lowercased().hasPrefix(basePrefix) {
|
||||
return resolveInstantViewUrl(account: account, url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .single(.externalUrl(url))
|
||||
return .single(.externalUrl(url))
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
public func resolveInstantViewUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
From 654c44d26bd86efec4019990beac67900231f868 Mon Sep 17 00:00:00 2001
|
||||
From 012fe706a281cc5e9586dc0ad7b0c59baf84feb1 Mon Sep 17 00:00:00 2001
|
||||
From: Ali <>
|
||||
Date: Wed, 16 Dec 2020 17:11:29 +0400
|
||||
Subject: [PATCH] Add support for arm64-iphonesimulator-gcc
|
||||
Date: Wed, 30 Dec 2020 00:13:06 +0400
|
||||
Subject: [PATCH] Support arm64 simulator
|
||||
|
||||
---
|
||||
build/make/configure.sh | 73 ++++++++++++++++++++++++++++++++++++++++-
|
||||
|
|
@ -9,10 +9,10 @@ Subject: [PATCH] Add support for arm64-iphonesimulator-gcc
|
|||
2 files changed, 74 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/build/make/configure.sh b/build/make/configure.sh
|
||||
index 206b54f77..1c113e51d 100644
|
||||
index c4e938fc7..ccd7066a5 100644
|
||||
--- a/build/make/configure.sh
|
||||
+++ b/build/make/configure.sh
|
||||
@@ -845,6 +845,14 @@ process_common_toolchain() {
|
||||
@@ -852,6 +852,14 @@ process_common_toolchain() {
|
||||
# Handle darwin variants. Newer SDKs allow targeting older
|
||||
# platforms, so use the newest one available.
|
||||
case ${toolchain} in
|
||||
|
|
@ -24,10 +24,10 @@ index 206b54f77..1c113e51d 100644
|
|||
+ add_ldflags "-isysroot ${iphonesimulator_sdk_dir}"
|
||||
+ fi
|
||||
+ ;;
|
||||
arm*-darwin*)
|
||||
arm*-darwin-*)
|
||||
add_cflags "-miphoneos-version-min=${IOS_VERSION_MIN}"
|
||||
iphoneos_sdk_dir="$(show_darwin_sdk_path iphoneos)"
|
||||
@@ -934,7 +942,7 @@ process_common_toolchain() {
|
||||
@@ -945,7 +953,7 @@ process_common_toolchain() {
|
||||
|
||||
# Process ARM architecture variants
|
||||
case ${toolchain} in
|
||||
|
|
@ -36,8 +36,8 @@ index 206b54f77..1c113e51d 100644
|
|||
# on arm, isa versions are supersets
|
||||
case ${tgt_isa} in
|
||||
arm64|armv8)
|
||||
@@ -1144,6 +1152,69 @@ EOF
|
||||
asm_conversion_cmd="${source_path}/build/make/ads2gas_apple.pl"
|
||||
@@ -1155,6 +1163,69 @@ EOF
|
||||
asm_conversion_cmd="${source_path_mk}/build/make/ads2gas_apple.pl"
|
||||
;;
|
||||
|
||||
+ iphonesimulator*)
|
||||
|
|
@ -107,18 +107,18 @@ index 206b54f77..1c113e51d 100644
|
|||
enable_feature linux
|
||||
if enabled rvct; then
|
||||
diff --git a/configure b/configure
|
||||
index 32272ce36..826769948 100755
|
||||
index f7e11aaf2..af625ee35 100755
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -99,6 +99,7 @@ EOF
|
||||
# alphabetically by architecture, generic-gnu last.
|
||||
@@ -100,6 +100,7 @@ EOF
|
||||
all_platforms="${all_platforms} arm64-android-gcc"
|
||||
all_platforms="${all_platforms} arm64-darwin-gcc"
|
||||
all_platforms="${all_platforms} arm64-darwin20-gcc"
|
||||
+all_platforms="${all_platforms} arm64-iphonesimulator-gcc"
|
||||
all_platforms="${all_platforms} arm64-linux-gcc"
|
||||
all_platforms="${all_platforms} arm64-win64-gcc"
|
||||
all_platforms="${all_platforms} arm64-win64-vs15"
|
||||
@@ -733,7 +734,7 @@ process_toolchain() {
|
||||
@@ -735,7 +736,7 @@ process_toolchain() {
|
||||
soft_enable libyuv
|
||||
# GTestLog must be modified to use Android logging utilities.
|
||||
;;
|
||||
7
third-party/libvpx/BUILD
vendored
7
third-party/libvpx/BUILD
vendored
|
|
@ -10,6 +10,7 @@ headers = [
|
|||
"vpx_image.h",
|
||||
"vpx_integer.h",
|
||||
"vpx_version.h",
|
||||
"vpx_ext_ratectrl.h",
|
||||
]
|
||||
|
||||
libs = [
|
||||
|
|
@ -28,7 +29,7 @@ genrule(
|
|||
name = "libvpx_build",
|
||||
srcs = [
|
||||
"build-libvpx-bazel.sh",
|
||||
"0001-Add-support-for-arm64-iphonesimulator-gcc.patch",
|
||||
"0001-Support-arm64-simulator.patch",
|
||||
":libvpx_sources",
|
||||
],
|
||||
cmd_bash =
|
||||
|
|
@ -62,14 +63,14 @@ genrule(
|
|||
ABS_YASM_DIR="$$(pwd)/$$(dirname $$YASM_DIR)/$$(basename $$YASM_DIR)"
|
||||
|
||||
cp $(location :build-libvpx-bazel.sh) "$$BUILD_DIR/"
|
||||
cp $(location :0001-Add-support-for-arm64-iphonesimulator-gcc.patch) "$$BUILD_DIR/"
|
||||
cp $(location :0001-Support-arm64-simulator.patch) "$$BUILD_DIR/"
|
||||
|
||||
SOURCE_PATH="third-party/libvpx/libvpx"
|
||||
|
||||
cp -R "$$SOURCE_PATH" "$$BUILD_DIR/"
|
||||
|
||||
pushd "$$BUILD_DIR/libvpx"
|
||||
patch -p1 < ../0001-Add-support-for-arm64-iphonesimulator-gcc.patch
|
||||
patch -p1 < ../0001-Support-arm64-simulator.patch
|
||||
popd
|
||||
|
||||
mkdir -p "$$BUILD_DIR/Public/libvpx"
|
||||
|
|
|
|||
2
third-party/libvpx/libvpx
vendored
2
third-party/libvpx/libvpx
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 2d20a42ab60f8fed37612361f2ea776d0bc7ca1a
|
||||
Subproject commit 3a38edea2cd114d53914cab017cab2e43a600031
|
||||
637
third-party/webrtc/BUILD
vendored
637
third-party/webrtc/BUILD
vendored
File diff suppressed because it is too large
Load diff
80
third-party/webrtc/dependencies/base/third_party/libevent/BUILD.gn
vendored
Normal file
80
third-party/webrtc/dependencies/base/third_party/libevent/BUILD.gn
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//build/config/nacl/config.gni")
|
||||
|
||||
static_library("libevent") {
|
||||
sources = [
|
||||
"buffer.c",
|
||||
"evbuffer.c",
|
||||
"evdns.c",
|
||||
"evdns.h",
|
||||
"event-config.h",
|
||||
"event-internal.h",
|
||||
"event.c",
|
||||
"event.h",
|
||||
"event_tagging.c",
|
||||
"evhttp.h",
|
||||
"evrpc-internal.h",
|
||||
"evrpc.c",
|
||||
"evrpc.h",
|
||||
"evsignal.h",
|
||||
"evutil.c",
|
||||
"evutil.h",
|
||||
"http-internal.h",
|
||||
"http.c",
|
||||
"log.c",
|
||||
"log.h",
|
||||
"min_heap.h",
|
||||
"poll.c",
|
||||
"select.c",
|
||||
"signal.c",
|
||||
"strlcpy-internal.h",
|
||||
"strlcpy.c",
|
||||
]
|
||||
|
||||
defines = [ "HAVE_CONFIG_H" ]
|
||||
|
||||
if (is_mac || is_ios) {
|
||||
sources += [
|
||||
"kqueue.c",
|
||||
"mac/config.h",
|
||||
"mac/event-config.h",
|
||||
]
|
||||
include_dirs = [ "mac" ]
|
||||
} else if (is_linux) {
|
||||
sources += [
|
||||
"epoll.c",
|
||||
"linux/config.h",
|
||||
"linux/event-config.h",
|
||||
]
|
||||
include_dirs = [ "linux" ]
|
||||
} else if (is_android) {
|
||||
sources += [
|
||||
"android/config.h",
|
||||
"android/event-config.h",
|
||||
"epoll.c",
|
||||
]
|
||||
include_dirs = [ "android" ]
|
||||
} else if (is_nacl_nonsfi) {
|
||||
sources -= [
|
||||
"evdns.c",
|
||||
"event_tagging.c",
|
||||
"evrpc.c",
|
||||
"http.c",
|
||||
"select.c",
|
||||
"signal.c",
|
||||
]
|
||||
sources += [
|
||||
"nacl_nonsfi/config.h",
|
||||
"nacl_nonsfi/event-config.h",
|
||||
"nacl_nonsfi/random.c",
|
||||
"nacl_nonsfi/signal_stub.c",
|
||||
]
|
||||
include_dirs = [ "nacl_nonsfi" ]
|
||||
}
|
||||
|
||||
configs -= [ "//build/config/compiler:chromium_code" ]
|
||||
configs += [ "//build/config/compiler:no_chromium_code" ]
|
||||
}
|
||||
253
third-party/webrtc/dependencies/base/third_party/libevent/ChangeLog
vendored
Normal file
253
third-party/webrtc/dependencies/base/third_party/libevent/ChangeLog
vendored
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
Changes in 1.4.15-stable (5 January 2015)
|
||||
|
||||
o Avoid integer overflow bugs in evbuffer_add() and related functions. See CVE-2014-6272 advisory for more information. (d49bc0e88b81a5812116074dc007f1db0ca1eecd)
|
||||
|
||||
o Pass flags to fcntl(F_SETFL) as int, not long (b3d0382)
|
||||
o Backport and tweak the LICENSE file for 1.4 (8a5ebd3)
|
||||
o set close-on-exec bit for filedescriptors created by dns subsystem (9985231 Ralf Schmitt)
|
||||
o Replace unused case of FD_CLOSEONEXEC with a proper null statement. (44f04a2)
|
||||
o Fix kqueue correctness test on x84_64 (1c25b07)
|
||||
o Avoid deadlock when activating signals. (e0e6958)
|
||||
o Backport doc fix for evhttp_bind_socket. (95b71d0 Marco)
|
||||
o Fix an issue with forking and signal socketpairs in select/poll backends (f0ff765)
|
||||
o Fix compilation on Visual Studio 2010 (53c47c2 VDm)
|
||||
o Defensive programming to prevent (hopefully impossible) stack-stomping (2d8cf0b)
|
||||
o Check for POLLERR, POLLHUP and POLLNVAL for Solaris event ports (353b4ac Trond Norbye)
|
||||
o Fix a bug that could allow dns requests with duplicate tx ids (e50ba5b)
|
||||
o Avoid truncating huge values for content-length (1d6e30e)
|
||||
o Take generated files out of git; add correct m4 magic for libtool to auto* files (7cf794b)
|
||||
o Prefer autoregen -ivf to manual autogen.sh (823d9be)
|
||||
|
||||
|
||||
Changes in 1.4.14b-stable
|
||||
o Set the VERSION_INFO correctly for 1.4.14
|
||||
|
||||
|
||||
Changes in 1.4.14-stable
|
||||
o Add a .gitignore file for the 1.4 branch. (d014edb)
|
||||
o Backport evbuffer_readln(). (b04cc60 Nicholas Marriott)
|
||||
o Make the evbuffer_readln backport follow the current API (c545485)
|
||||
o Valgrind fix: Clear struct kevent before checking for OSX bug. (5713d5d William Ahern)
|
||||
o Fix a crash when reading badly formatted resolve.conf (5b10d00 Yasuoka Masahiko)
|
||||
o Fix memory-leak of signal handler array with kqueue. [backport] (01f3775)
|
||||
o Update sample/signal-test.c to use newer APIs and not leak. (891765c Evan Jones)
|
||||
o Correct all versions in 1.4 branch (ac0d213)
|
||||
o Make evutil_make_socket_nonblocking() leave any other flags alone. (81c26ba Jardel Weyrich)
|
||||
o Adjusted fcntl() retval comparison on evutil_make_socket_nonblocking(). (5f2e250 Jardel Weyrich)
|
||||
o Correct a debug message in evhttp_parse_request_line (35df59e)
|
||||
o Merge branch 'readln-backport' into patches-1.4 (8771d5b)
|
||||
o Do not send an HTTP error when we've already closed or responded. (4fd2dd9 Pavel Plesov)
|
||||
o Re-add event_siglcb; some old code _was_ still using it. :( (bd03d06)
|
||||
o Make Libevent 1.4 build on win32 with Unicode enabled. (bce58d6 Brodie Thiesfield)
|
||||
o Distribute nmake makefile for 1.4 (20d706d)
|
||||
o do not fail while sending on http connections the client closed. (5c8b446)
|
||||
o make evhttp_send() safe against terminated connections, too (01ea0c5)
|
||||
o Fix a free(NULL) in min_heap.h (2458934)
|
||||
o Fix memory leak when setting up priorities; reported by Alexander Drozdov (cb1a722)
|
||||
o Clean up properly when adding a signal handler fails. (ae6ece0 Gilad Benjamini)
|
||||
o Do not abort HTTP requests missing a reason string. (29d7b32 Pierre Phaneuf)
|
||||
o Fix compile warning in http.c (906d573)
|
||||
o Define _REENTRANT as needed on Solaris, elsewhere (6cbea13)
|
||||
|
||||
|
||||
Changes in 1.4.13-stable:
|
||||
o If the kernel tells us that there are a negative number of bytes to read from a socket, do not believe it. Fixes bug 2841177; found by Alexander Pronchenkov.
|
||||
o Do not allocate the maximum event queue and fd array for the epoll backend at startup. Instead, start out accepting 32 events at a time, and double the queue's size when it seems that the OS is generating events faster than we're requesting them. Saves up to 512K per epoll-based event_base. Resolves bug 2839240.
|
||||
o Fix compilation on Android, which forgot to define fd_mask in its sys/select.h
|
||||
o Do not drop data from evbuffer when out of memory; reported by Jacek Masiulaniec
|
||||
o Rename our replacement compat/sys/_time.h header to avoid build a conflict on HPUX; reported by Kathryn Hogg.
|
||||
o Build kqueue.c correctly on GNU/kFreeBSD platforms. Patch pulled upstream from Debian.
|
||||
o Fix a problem with excessive memory allocation when using multiple event priorities.
|
||||
o When running set[ug]id, don't check the environment. Based on a patch from OpenBSD.
|
||||
|
||||
|
||||
Changes in 1.4.12-stable:
|
||||
o Try to contain degree of failure when running on a win32 version so heavily firewalled that we can't fake a socketpair.
|
||||
o Fix an obscure timing-dependent, allocator-dependent crash in the evdns code.
|
||||
o Use __VA_ARGS__ syntax for varargs macros in event_rpcgen when compiler is not GCC.
|
||||
o Activate fd events in a pseudorandom order with O(N) backends, so that we don't systematically favor low fds (select) or earlier-added fds (poll, win32).
|
||||
o Fix another pair of fencepost bugs in epoll.c. [Patch from Adam Langley.]
|
||||
o Do not break evdns connections to nameservers when our IP changes.
|
||||
o Set truncated flag correctly in evdns server replies.
|
||||
o Disable strict aliasing with GCC: our code is not compliant with it.
|
||||
|
||||
Changes in 1.4.11-stable:
|
||||
o Fix a bug when removing a timeout from the heap. [Patch from Marko Kreen]
|
||||
o Remove the limit on size of HTTP headers by removing static buffers.
|
||||
o Fix a nasty dangling pointer bug in epoll.c that could occur after epoll_recalc(). [Patch from Kevin Springborn]
|
||||
o Distribute Win32-Code/event-config.h, not ./event-config.h
|
||||
|
||||
Changes in 1.4.10-stable:
|
||||
o clean up buffered http connection data on reset; reported by Brian O'Kelley
|
||||
o bug fix and potential race condition in signal handling; from Alexander Drozdov
|
||||
o rename the Solaris event ports backend to evport
|
||||
o support compilation on Haiku
|
||||
o fix signal processing when a signal callback delivers a signal; from Alexander Drozdov
|
||||
o const-ify some arguments to evdns functions.
|
||||
o off-by-one error in epoll_recalc; reported by Victor Goya
|
||||
o include Doxyfile in tar ball; from Jeff Garzik
|
||||
o correctly parse queries with encoded \r, \n or + characters
|
||||
|
||||
Changes in 1.4.9-stable:
|
||||
o event_add would not return error for some backends; from Dean McNamee
|
||||
o Clear the timer cache on entering the event loop; reported by Victor Chang
|
||||
o Only bind the socket on connect when a local address has been provided; reported by Alejo Sanchez
|
||||
o Allow setting of local port for evhttp connections to support millions of connections from a single system; from Richard Jones.
|
||||
o Clear the timer cache when leaving the event loop; reported by Robin Haberkorn
|
||||
o Fix a typo in setting the global event base; reported by lance.
|
||||
o Fix a memory leak when reading multi-line headers
|
||||
o Fix a memory leak by not running explicit close detection for server connections
|
||||
|
||||
Changes in 1.4.8-stable:
|
||||
o Match the query in DNS replies to the query in the request; from Vsevolod Stakhov.
|
||||
o Fix a merge problem in which name_from_addr returned pointers to the stack; found by Jiang Hong.
|
||||
o Do not remove Accept-Encoding header
|
||||
|
||||
Changes in 1.4.7-stable:
|
||||
o Fix a bug where headers arriving in multiple packets were not parsed; fix from Jiang Hong; test by me.
|
||||
|
||||
Changes in 1.4.6-stable:
|
||||
o evutil.h now includes <stdarg.h> directly
|
||||
o switch all uses of [v]snprintf over to evutil
|
||||
o Correct handling of trailing headers in chunked replies; from Scott Lamb.
|
||||
o Support multi-line HTTP headers; based on a patch from Moshe Litvin
|
||||
o Reject negative Content-Length headers; anonymous bug report
|
||||
o Detect CLOCK_MONOTONIC at runtime for evdns; anonymous bug report
|
||||
o Fix a bug where deleting signals with the kqueue backend would cause subsequent adds to fail
|
||||
o Support multiple events listening on the same signal; make signals regular events that go on the same event queue; problem report by Alexander Drozdov.
|
||||
o Deal with evbuffer_read() returning -1 on EINTR|EAGAIN; from Adam Langley.
|
||||
o Fix a bug in which the DNS server would incorrectly set the type of a cname reply to a.
|
||||
o Fix a bug where setting the timeout on a bufferevent would take not effect if the event was already pending.
|
||||
o Fix a memory leak when using signals for some event bases; reported by Alexander Drozdov.
|
||||
o Add libevent.vcproj file to distribution to help with Windows build.
|
||||
o Fix a problem with epoll() and reinit; problem report by Alexander Drozdov.
|
||||
o Fix off-by-one errors in devpoll; from Ian Bell
|
||||
o Make event_add not change any state if it fails; reported by Ian Bell.
|
||||
o Do not warn on accept when errno is either EAGAIN or EINTR
|
||||
|
||||
Changes in 1.4.5-stable:
|
||||
o Fix connection keep-alive behavior for HTTP/1.0
|
||||
o Fix use of freed memory in event_reinit; pointed out by Peter Postma
|
||||
o Constify struct timeval * where possible; pointed out by Forest Wilkinson
|
||||
o allow min_heap_erase to be called on removed members; from liusifan.
|
||||
o Rename INPUT and OUTPUT to EVRPC_INPUT and EVRPC_OUTPUT. Retain INPUT/OUTPUT aliases on on-win32 platforms for backwards compatibility.
|
||||
o Do not use SO_REUSEADDR when connecting
|
||||
o Fix Windows build
|
||||
o Fix a bug in event_rpcgen when generated fixed-sized entries
|
||||
|
||||
Changes in 1.4.4-stable:
|
||||
o Correct the documentation on buffer printf functions.
|
||||
o Don't warn on unimplemented epoll_create(): this isn't a problem, just a reason to fall back to poll or select.
|
||||
o Correctly handle timeouts larger than 35 minutes on Linux with epoll.c. This is probably a kernel defect, but we'll have to support old kernels anyway even if it gets fixed.
|
||||
o Fix a potential stack corruption bug in tagging on 64-bit CPUs.
|
||||
o expose bufferevent_setwatermark via header files and fix high watermark on read
|
||||
o fix a bug in bufferevent read water marks and add a test for them
|
||||
o introduce bufferevent_setcb and bufferevent_setfd to allow better manipulation of bufferevents
|
||||
o use libevent's internal timercmp on all platforms, to avoid bugs on old platforms where timercmp(a,b,<=) is buggy.
|
||||
o reduce system calls for getting current time by caching it.
|
||||
o fix evhttp_bind_socket() so that multiple sockets can be bound by the same http server.
|
||||
o Build test directory correctly with CPPFLAGS set.
|
||||
o Fix build under Visual C++ 2005.
|
||||
o Expose evhttp_accept_socket() API.
|
||||
o Merge windows gettimeofday() replacement into a new evutil_gettimeofday() function.
|
||||
o Fix autoconf script behavior on IRIX.
|
||||
o Make sure winsock2.h include always comes before windows.h include.
|
||||
|
||||
Changes in 1.4.3-stable:
|
||||
o include Content-Length in reply for HTTP/1.0 requests with keep-alive
|
||||
o Patch from Tani Hosokawa: make some functions in http.c threadsafe.
|
||||
o Do not free the kqop file descriptor in other processes, also allow it to be 0; from Andrei Nigmatulin
|
||||
o make event_rpcgen.py generate code include event-config.h; reported by Sam Banks.
|
||||
o make event methods static so that they are not exported; from Andrei Nigmatulin
|
||||
o make RPC replies use application/octet-stream as mime type
|
||||
o do not delete uninitialized timeout event in evdns
|
||||
|
||||
Changes in 1.4.2-rc:
|
||||
o remove pending timeouts on event_base_free()
|
||||
o also check EAGAIN for Solaris' event ports; from W.C.A. Wijngaards
|
||||
o devpoll and evport need reinit; tested by W.C.A Wijngaards
|
||||
o event_base_get_method; from Springande Ulv
|
||||
o Send CRLF after each chunk in HTTP output, for compliance with RFC2626. Patch from "propanbutan". Fixes bug 1894184.
|
||||
o Add a int64_t parsing function, with unit tests, so we can apply Scott Lamb's fix to allow large HTTP values.
|
||||
o Use a 64-bit field to hold HTTP content-lengths. Patch from Scott Lamb.
|
||||
o Allow regression code to build even without Python installed
|
||||
o remove NDEBUG ifdefs from evdns.c
|
||||
o update documentation of event_loop and event_base_loop; from Tani Hosokawa.
|
||||
o detect integer types properly on platforms without stdint.h
|
||||
o Remove "AM_MAINTAINER_MODE" declaration in configure.in: now makefiles and configure should get re-generated automatically when Makefile.am or configure.in chanes.
|
||||
o do not insert event into list when evsel->add fails
|
||||
|
||||
Changes in 1.4.1-beta:
|
||||
o free minheap on event_base_free(); from Christopher Layne
|
||||
o debug cleanups in signal.c; from Christopher Layne
|
||||
o provide event_base_new() that does not set the current_base global
|
||||
o bufferevent_write now uses a const source argument; report from Charles Kerr
|
||||
o better documentation for event_base_loopexit; from Scott Lamb.
|
||||
o Make kqueue have the same behavior as other backends when a signal is caught between event_add() and event_loop(). Previously, it would catch and ignore such signals.
|
||||
o Make kqueue restore signal handlers correctly when event_del() is called.
|
||||
o provide event_reinit() to reintialize an event_base after fork
|
||||
o small improvements to evhttp documentation
|
||||
o always generate Date and Content-Length headers for HTTP/1.1 replies
|
||||
o set the correct event base for HTTP close events
|
||||
o New function, event_{base_}loopbreak. Like event_loopexit, it makes an event loop stop executing and return. Unlike event_loopexit, it keeps subsequent pending events from getting executed. Patch from Scott Lamb
|
||||
o Removed obsoleted recalc code
|
||||
o pull setters/getters out of RPC structures into a base class to which we just need to store a pointer; this reduces the memory footprint of these structures.
|
||||
o fix a bug with event_rpcgen for integers
|
||||
o move EV_PERSIST handling out of the event backends
|
||||
o support for 32-bit tag numbers in rpc structures; this is wire compatible, but changes the API slightly.
|
||||
o prefix {encode,decode}_tag functions with evtag to avoid collisions
|
||||
o Correctly handle DNS replies with no answers set (Fixes bug 1846282)
|
||||
o The configure script now takes an --enable-gcc-warnigns option that turns on many optional gcc warnings. (Nick has been building with these for a while, but they might be useful to other developers.)
|
||||
o When building with GCC, use the "format" attribute to verify type correctness of calls to printf-like functions.
|
||||
o removed linger from http server socket; reported by Ilya Martynov
|
||||
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
|
||||
o demote most http warnings to debug messages
|
||||
o Fix Solaris compilation; from Magne Mahre
|
||||
o Add a "Date" header to HTTP responses, as required by HTTP 1.1.
|
||||
o Support specifying the local address of an evhttp_connection using set_local_address
|
||||
o Fix a memory leak in which failed HTTP connections would not free the request object
|
||||
o Make adding of array members in event_rpcgen more efficient, but doubling memory allocation
|
||||
o Fix a memory leak in the DNS server
|
||||
o Fix compilation when DNS_USE_OPENSSL_FOR_ID is enabled
|
||||
o Fix buffer size and string generation in evdns_resolve_reverse_ipv6().
|
||||
o Respond to nonstandard DNS queries with "NOTIMPL" rather than by ignoring them.
|
||||
o In DNS responses, the CD flag should be preserved, not the TC flag.
|
||||
o Fix http.c to compile properly with USE_DEBUG; from Christopher Layne
|
||||
o Handle NULL timeouts correctly on Solaris; from Trond Norbye
|
||||
o Recalculate pending events properly when reallocating event array on Solaris; from Trond Norbye
|
||||
o Add Doxygen documentation to header files; from Mark Heily
|
||||
o Add a evdns_set_transaction_id_fn() function to override the default
|
||||
transaction ID generation code.
|
||||
o Add an evutil module (with header evutil.h) to implement our standard cross-platform hacks, on the theory that somebody else would like to use them too.
|
||||
o Fix signals implementation on windows.
|
||||
o Fix http module on windows to close sockets properly.
|
||||
o Make autogen.sh script run correctly on systems where /bin/sh isn't bash. (Patch from Trond Norbye, rewritten by Hagne Mahre and then Hannah Schroeter.)
|
||||
o Skip calling gettime() in timeout_process if we are not in fact waiting for any events. (Patch from Trond Norbye)
|
||||
o Make test subdirectory compile under mingw.
|
||||
o Fix win32 buffer.c behavior so that it is correct for sockets (which do not like ReadFile and WriteFile).
|
||||
o Make the test.sh script run unit tests for the evpoll method.
|
||||
o Make the entire evdns.h header enclosed in "extern C" as appropriate.
|
||||
o Fix implementation of strsep on platforms that lack it
|
||||
o Fix implementation of getaddrinfo on platforms that lack it; mainly, this will make Windows http.c work better. Original patch by Lubomir Marinov.
|
||||
o Fix evport implementation: port_disassociate called on unassociated events resulting in bogus errors; more efficient memory management; from Trond Norbye and Prakash Sangappa
|
||||
o support for hooks on rpc input and output; can be used to implement rpc independent processing such as compression or authentication.
|
||||
o use a min heap instead of a red-black tree for timeouts; as a result finding the min is a O(1) operation now; from Maxim Yegorushkin
|
||||
o associate an event base with an rpc pool
|
||||
o added two additional libraries: libevent_core and libevent_extra in addition to the regular libevent. libevent_core contains only the event core whereas libevent_extra contains dns, http and rpc support
|
||||
o Begin using libtool's library versioning support correctly. If we don't mess up, this will more or less guarantee binaries linked against old versions of libevent continue working when we make changes to libevent that do not break backward compatibility.
|
||||
o Fix evhttp.h compilation when TAILQ_ENTRY is not defined.
|
||||
o Small code cleanups in epoll_dispatch().
|
||||
o Increase the maximum number of addresses read from a packet in evdns to 32.
|
||||
o Remove support for the rtsig method: it hasn't compiled for a while, and nobody seems to miss it very much. Let us know if there's a good reason to put it back in.
|
||||
o Rename the "class" field in evdns_server_request to dns_question_class, so that it won't break compilation under C++. Use a macro so that old code won't break. Mark the macro as deprecated.
|
||||
o Fix DNS unit tests so that having a DNS server with broken IPv6 support is no longer cause for aborting the unit tests.
|
||||
o Make event_base_free() succeed even if there are pending non-internal events on a base. This may still leak memory and fds, but at least it no longer crashes.
|
||||
o Post-process the config.h file into a new, installed event-config.h file that we can install, and whose macros will be safe to include in header files.
|
||||
o Remove the long-deprecated acconfig.h file.
|
||||
o Do not require #include <sys/types.h> before #include <event.h>.
|
||||
o Add new evutil_timer* functions to wrap (or replace) the regular timeval manipulation functions.
|
||||
o Fix many build issues when using the Microsoft C compiler.
|
||||
o Remove a bash-ism in autogen.sh
|
||||
o When calling event_del on a signal, restore the signal handler's previous value rather than setting it to SIG_DFL. Patch from Christopher Layne.
|
||||
o Make the logic for active events work better with internal events; patch from Christopher Layne.
|
||||
o We do not need to specially remove a timeout before calling event_del; patch from Christopher Layne.
|
||||
230
third-party/webrtc/dependencies/base/third_party/libevent/Doxyfile
vendored
Normal file
230
third-party/webrtc/dependencies/base/third_party/libevent/Doxyfile
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
# Doxyfile 1.5.1
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project
|
||||
#
|
||||
# All text after a hash (#) is considered a comment and will be ignored
|
||||
# The format is:
|
||||
# TAG = value [value, ...]
|
||||
# For lists items can also be appended using:
|
||||
# TAG += value [value, ...]
|
||||
# Values that contain spaces should be placed between quotes (" ")
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
PROJECT_NAME = libevent
|
||||
|
||||
# Place all output under 'doxygen/'
|
||||
|
||||
OUTPUT_DIRECTORY = doxygen/
|
||||
|
||||
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
|
||||
# will interpret the first line (until the first dot) of a JavaDoc-style
|
||||
# comment as the brief description. If set to NO, the JavaDoc
|
||||
# comments will behave just like the Qt-style comments (thus requiring an
|
||||
# explicit @brief command for a brief description.
|
||||
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
|
||||
# sources only. Doxygen will then generate output that is more tailored for C.
|
||||
# For instance, some of the names that are used will be different. The list
|
||||
# of all members will be omitted, etc.
|
||||
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
|
||||
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
|
||||
# brief documentation of file, namespace and class members alphabetically
|
||||
# by member name. If set to NO (the default) the members will appear in
|
||||
# declaration order.
|
||||
|
||||
SORT_BRIEF_DOCS = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# The INPUT tag can be used to specify the files and/or directories that contain
|
||||
# documented source files. You may enter file names like "myfile.cpp" or
|
||||
# directories like "/usr/src/myproject". Separate the files or directories
|
||||
# with spaces.
|
||||
|
||||
INPUT = event.h evdns.h evhttp.h evrpc.h
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
|
||||
# generate HTML output.
|
||||
|
||||
GENERATE_HTML = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
|
||||
# generate Latex output.
|
||||
|
||||
GENERATE_LATEX = YES
|
||||
|
||||
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
|
||||
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
|
||||
# put in front of it. If left blank `latex' will be used as the default path.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked. If left blank `latex' will be used as the default command name.
|
||||
|
||||
LATEX_CMD_NAME = latex
|
||||
|
||||
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
|
||||
# generate index for LaTeX. If left blank `makeindex' will be used as the
|
||||
# default command name.
|
||||
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
|
||||
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
|
||||
# LaTeX documents. This may be useful for small projects and may help to
|
||||
# save some trees in general.
|
||||
|
||||
COMPACT_LATEX = NO
|
||||
|
||||
# The PAPER_TYPE tag can be used to set the paper type that is used
|
||||
# by the printer. Possible values are: a4, a4wide, letter, legal and
|
||||
# executive. If left blank a4wide will be used.
|
||||
|
||||
PAPER_TYPE = a4wide
|
||||
|
||||
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
|
||||
# packages that should be included in the LaTeX output.
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
|
||||
# the generated latex document. The header should contain everything until
|
||||
# the first chapter. If it is left blank doxygen will generate a
|
||||
# standard header. Notice: only use this tag if you know what you are doing!
|
||||
|
||||
LATEX_HEADER =
|
||||
|
||||
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
|
||||
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
|
||||
# contain links (just like the HTML output) instead of page references
|
||||
# This makes the output suitable for online browsing using a pdf viewer.
|
||||
|
||||
PDF_HYPERLINKS = NO
|
||||
|
||||
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
|
||||
# plain latex in the generated Makefile. Set this option to YES to get a
|
||||
# higher quality PDF documentation.
|
||||
|
||||
USE_PDFLATEX = NO
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep
|
||||
# running if errors occur, instead of asking the user for help.
|
||||
# This option is also used when generating formulas in HTML.
|
||||
|
||||
LATEX_BATCHMODE = NO
|
||||
|
||||
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
|
||||
# include the index chapters (such as File Index, Compound Index, etc.)
|
||||
# in the output.
|
||||
|
||||
LATEX_HIDE_INDICES = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
|
||||
# generate man pages
|
||||
|
||||
GENERATE_MAN = YES
|
||||
|
||||
# The MAN_EXTENSION tag determines the extension that is added to
|
||||
# the generated man pages (default is the subroutine's section .3)
|
||||
|
||||
MAN_EXTENSION = .3
|
||||
|
||||
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
|
||||
# then it will generate one additional man file for each entity
|
||||
# documented in the real man page(s). These additional files
|
||||
# only source the real man page, but without them the man command
|
||||
# would be unable to find the correct page. The default is NO.
|
||||
|
||||
MAN_LINKS = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
|
||||
# evaluate all C-preprocessor directives found in the sources and include
|
||||
# files.
|
||||
|
||||
ENABLE_PREPROCESSING = YES
|
||||
|
||||
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
|
||||
# names in the source code. If set to NO (the default) only conditional
|
||||
# compilation will be performed. Macro expansion can be done in a controlled
|
||||
# way by setting EXPAND_ONLY_PREDEF to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
|
||||
# then the macro expansion is limited to the macros specified with the
|
||||
# PREDEFINED and EXPAND_AS_DEFINED tags.
|
||||
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
|
||||
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
|
||||
# in the INCLUDE_PATH (see below) will be search if a #include is found.
|
||||
|
||||
SEARCH_INCLUDES = YES
|
||||
|
||||
# The INCLUDE_PATH tag can be used to specify one or more directories that
|
||||
# contain include files that are not input files but should be processed by
|
||||
# the preprocessor.
|
||||
|
||||
INCLUDE_PATH =
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
# directories. If left blank, the patterns specified with FILE_PATTERNS will
|
||||
# be used.
|
||||
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
|
||||
# The PREDEFINED tag can be used to specify one or more macro names that
|
||||
# are defined before the preprocessor is started (similar to the -D option of
|
||||
# gcc). The argument of the tag is a list of macros of the form: name
|
||||
# or name=definition (no spaces). If the definition and the = are
|
||||
# omitted =1 is assumed. To prevent a macro definition from being
|
||||
# undefined via #undef or recursively expanded use the := operator
|
||||
# instead of the = operator.
|
||||
|
||||
PREDEFINED = TAILQ_ENTRY RB_ENTRY _EVENT_DEFINED_TQENTRY
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
|
||||
# this tag can be used to specify a list of macro names that should be expanded.
|
||||
# The macro definition that is found in the sources will be used.
|
||||
# Use the PREDEFINED tag if you want to use a different macro definition.
|
||||
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
|
||||
# doxygen's preprocessor will remove all function-like macros that are alone
|
||||
# on a line, have an all uppercase name, and do not end with a semicolon. Such
|
||||
# function macros are typically used for boiler-plate code, and will confuse
|
||||
# the parser if not removed.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
53
third-party/webrtc/dependencies/base/third_party/libevent/LICENSE
vendored
Normal file
53
third-party/webrtc/dependencies/base/third_party/libevent/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
Libevent is available for use under the following license, commonly known
|
||||
as the 3-clause (or "modified") BSD license:
|
||||
|
||||
==============================
|
||||
Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu>
|
||||
Copyright (c) 2007-2010 Niels Provos and Nick Mathewson
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================
|
||||
|
||||
Portions of Libevent are based on works by others, also made available by
|
||||
them under the three-clause BSD license above. The copyright notices are
|
||||
available in the corresponding source files; the license is as above. Here's
|
||||
a list:
|
||||
|
||||
log.c:
|
||||
Copyright (c) 2000 Dug Song <dugsong@monkey.org>
|
||||
Copyright (c) 1993 The Regents of the University of California.
|
||||
|
||||
strlcpy.c:
|
||||
Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
|
||||
win32.c:
|
||||
Copyright (c) 2003 Michael A. Davis <mike@datanerds.net>
|
||||
|
||||
evport.c:
|
||||
Copyright (c) 2007 Sun Microsystems
|
||||
|
||||
min_heap.h:
|
||||
Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@gmail.com>
|
||||
|
||||
tree.h:
|
||||
Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
152
third-party/webrtc/dependencies/base/third_party/libevent/Makefile.am
vendored
Normal file
152
third-party/webrtc/dependencies/base/third_party/libevent/Makefile.am
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
AUTOMAKE_OPTIONS = foreign no-dependencies
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
# This is the point release for libevent. It shouldn't include any
|
||||
# a/b/c/d/e notations.
|
||||
RELEASE = 1.4
|
||||
|
||||
# This is the version info for the libevent binary API. It has three
|
||||
# numbers:
|
||||
# Current -- the number of the binary API that we're implementing
|
||||
# Revision -- which iteration of the implementation of the binary
|
||||
# API are we supplying?
|
||||
# Age -- How many previous binary API versions do we also
|
||||
# support?
|
||||
#
|
||||
# If we release a new version that does not change the binary API,
|
||||
# increment Revision.
|
||||
#
|
||||
# If we release a new version that changes the binary API, but does
|
||||
# not break programs compiled against the old binary API, increment
|
||||
# Current and Age. Set Revision to 0, since this is the first
|
||||
# implementation of the new API.
|
||||
#
|
||||
# Otherwise, we're changing the binary API and breaking bakward
|
||||
# compatibility with old binaries. Increment Current. Set Age to 0,
|
||||
# since we're backward compatible with no previous APIs. Set Revision
|
||||
# to 0 too.
|
||||
VERSION_INFO = 4:1:2
|
||||
|
||||
###
|
||||
# History:
|
||||
# We started using Libtool around version 1.0d. For all versions from
|
||||
# 1.0d through 1.3e, we set RELEASE to the version name, and
|
||||
# VERSION_INFO to something haphazard. The didn't matter, since
|
||||
# setting RELEASE meant that no version of Libevent was treated as
|
||||
# binary-compatible with any other version.
|
||||
#
|
||||
# As of 1.4.0-beta, we set RELEASE to "1.4", so that releases in the
|
||||
# 1.4.x series could be potentially binary-compatible with one another,
|
||||
# but not with any other series. (They aren't.) We didn't necessarily
|
||||
# set VERSION_INFO correctly, or update it as often as we should have.
|
||||
# The VERSION_INFO values were:
|
||||
# 1.4.0-beta .. 1.4.4-stable : 2:0:0 [See note 1]
|
||||
# 1.4.5-stable : 3:0:1 (compatible ABI change)
|
||||
# 1.4.6-stable : 3:1:1 (no ABI change)
|
||||
# 1.4.7-stable : 3:1:1 [see note 1]
|
||||
# 1.4.8-stable : 3:2:1 (no ABI change)
|
||||
# 1.4.9-stable : 3:2:1 [see note 1]
|
||||
# 1.4.10-stable : 3:3:1 (no ABI change)
|
||||
# 1.4.11-stable .. 1.4.13-stable : 3:3:1 [see note 1]
|
||||
# 1.4.14a-stable: : 3:3:2 [see note 2]
|
||||
# 1.4.14b-stable: : 4:0:2 (compatible ABI change)
|
||||
# 1.4.15-stable: : 4:1:2 (no ABI change)
|
||||
#
|
||||
# [1]: Using the same VERSION_INFO value was wrong; we should have been
|
||||
# updating the Revision field.
|
||||
# [2]: We set the VERSION_INFO completely wrong on 1.4.14b-stable
|
||||
|
||||
bin_SCRIPTS = event_rpcgen.py
|
||||
|
||||
EXTRA_DIST = autogen.sh event.h event-internal.h log.h evsignal.h evdns.3 \
|
||||
evrpc.h evrpc-internal.h min_heap.h \
|
||||
event.3 \
|
||||
Doxyfile \
|
||||
kqueue.c epoll_sub.c epoll.c select.c poll.c signal.c \
|
||||
evport.c devpoll.c event_rpcgen.py \
|
||||
sample/Makefile.am sample/Makefile.in sample/event-test.c \
|
||||
sample/signal-test.c sample/time-test.c \
|
||||
test/Makefile.am test/Makefile.in test/bench.c test/regress.c \
|
||||
test/test-eof.c test/test-weof.c test/test-time.c \
|
||||
test/test-init.c test/test.sh \
|
||||
compat/sys/queue.h compat/sys/_libevent_time.h \
|
||||
WIN32-Code/config.h \
|
||||
WIN32-Code/event-config.h \
|
||||
WIN32-Code/win32.c \
|
||||
WIN32-Code/tree.h \
|
||||
WIN32-Prj/event_test/event_test.dsp \
|
||||
WIN32-Prj/event_test/test.txt WIN32-Prj/libevent.dsp \
|
||||
WIN32-Prj/libevent.dsw WIN32-Prj/signal_test/signal_test.dsp \
|
||||
WIN32-Prj/time_test/time_test.dsp WIN32-Prj/regress/regress.vcproj \
|
||||
WIN32-Prj/libevent.sln WIN32-Prj/libevent.vcproj \
|
||||
Makefile.nmake test/Makefile.nmake \
|
||||
LICENSE
|
||||
|
||||
lib_LTLIBRARIES = libevent.la libevent_core.la libevent_extra.la
|
||||
|
||||
if BUILD_WIN32
|
||||
|
||||
SUBDIRS = . sample
|
||||
SYS_LIBS = -lws2_32
|
||||
SYS_SRC = WIN32-Code/win32.c
|
||||
SYS_INCLUDES = -IWIN32-Code
|
||||
|
||||
else
|
||||
|
||||
SUBDIRS = . sample test
|
||||
SYS_LIBS =
|
||||
SYS_SRC =
|
||||
SYS_INCLUDES =
|
||||
|
||||
endif
|
||||
|
||||
BUILT_SOURCES = event-config.h
|
||||
|
||||
event-config.h: config.h
|
||||
echo '/* event-config.h' > $@
|
||||
echo ' * Generated by autoconf; post-processed by libevent.' >> $@
|
||||
echo ' * Do not edit this file.' >> $@
|
||||
echo ' * Do not rely on macros in this file existing in later versions.'>> $@
|
||||
echo ' */' >> $@
|
||||
echo '#ifndef _EVENT_CONFIG_H_' >> $@
|
||||
echo '#define _EVENT_CONFIG_H_' >> $@
|
||||
|
||||
sed -e 's/#define /#define _EVENT_/' \
|
||||
-e 's/#undef /#undef _EVENT_/' \
|
||||
-e 's/#ifndef /#ifndef _EVENT_/' < config.h >> $@
|
||||
echo "#endif" >> $@
|
||||
|
||||
CORE_SRC = event.c buffer.c evbuffer.c log.c evutil.c $(SYS_SRC)
|
||||
EXTRA_SRC = event_tagging.c http.c evhttp.h http-internal.h evdns.c \
|
||||
evdns.h evrpc.c evrpc.h evrpc-internal.h \
|
||||
strlcpy.c strlcpy-internal.h strlcpy-internal.h
|
||||
|
||||
libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC)
|
||||
libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
|
||||
libevent_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO)
|
||||
|
||||
libevent_core_la_SOURCES = $(CORE_SRC)
|
||||
libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
|
||||
libevent_core_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO)
|
||||
|
||||
libevent_extra_la_SOURCES = $(EXTRA_SRC)
|
||||
libevent_extra_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
|
||||
libevent_extra_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO)
|
||||
|
||||
include_HEADERS = event.h evhttp.h evdns.h evrpc.h evutil.h
|
||||
|
||||
nodist_include_HEADERS = event-config.h
|
||||
|
||||
INCLUDES = -I$(srcdir)/compat $(SYS_INCLUDES)
|
||||
|
||||
man_MANS = event.3 evdns.3
|
||||
|
||||
verify: libevent.la
|
||||
cd test && make verify
|
||||
|
||||
doxygen: FORCE
|
||||
doxygen $(srcdir)/Doxyfile
|
||||
FORCE:
|
||||
|
||||
DISTCLEANFILES = *~ event-config.h
|
||||
48
third-party/webrtc/dependencies/base/third_party/libevent/Makefile.nmake
vendored
Normal file
48
third-party/webrtc/dependencies/base/third_party/libevent/Makefile.nmake
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# WATCH OUT! This makefile is a work in progress. It is probably missing
|
||||
# tons of important things. DO NOT RELY ON IT TO BUILD A GOOD LIBEVENT.
|
||||
|
||||
# Needed for correctness
|
||||
CFLAGS=/Iinclude /Icompat /IWIN32-Code /DWIN32 /DHAVE_CONFIG_H /I.
|
||||
|
||||
# For optimization and warnings
|
||||
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo
|
||||
|
||||
# XXXX have a debug mode
|
||||
|
||||
LIBFLAGS=/nologo
|
||||
|
||||
|
||||
CORE_OBJS=event.obj buffer.obj evbuffer.obj \
|
||||
log.obj evutil.obj \
|
||||
strlcpy.obj signal.obj win32.obj
|
||||
EXTRA_OBJS=event_tagging.obj http.obj evdns.obj evrpc.obj
|
||||
|
||||
ALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
|
||||
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib
|
||||
|
||||
|
||||
all: static_libs tests
|
||||
|
||||
static_libs: $(STATIC_LIBS)
|
||||
|
||||
win32.obj: WIN32-Code\win32.c
|
||||
$(CC) $(CFLAGS) /c WIN32-Code\win32.c
|
||||
|
||||
libevent_core.lib: $(CORE_OBJS)
|
||||
lib $(LIBFLAGS) $(CORE_OBJS) /out:libevent_core.lib
|
||||
|
||||
libevent_extras.lib: $(EXTRA_OBJS)
|
||||
lib $(LIBFLAGS) $(EXTRA_OBJS) /out:libevent_extras.lib
|
||||
|
||||
libevent.lib: $(CORE_OBJ) $(EXTRA_OBJS)
|
||||
lib $(LIBFLAGS) $(CORE_OBJS) $(EXTRA_OBJS) /out:libevent.lib
|
||||
|
||||
clean:
|
||||
del $(ALL_OBJS)
|
||||
del $(STATIC_LIBS)
|
||||
cd test
|
||||
$(MAKE) /F Makefile.nmake clean
|
||||
|
||||
tests:
|
||||
cd test
|
||||
$(MAKE) /F Makefile.nmake
|
||||
57
third-party/webrtc/dependencies/base/third_party/libevent/README
vendored
Normal file
57
third-party/webrtc/dependencies/base/third_party/libevent/README
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
To build libevent, type
|
||||
|
||||
$ ./configure && make
|
||||
|
||||
(If you got libevent from the subversion repository, you will
|
||||
first need to run the included "autogen.sh" script in order to
|
||||
generate the configure script.)
|
||||
|
||||
Install as root via
|
||||
|
||||
# make install
|
||||
|
||||
You can run the regression tests by
|
||||
|
||||
$ make verify
|
||||
|
||||
Before, reporting any problems, please run the regression tests.
|
||||
|
||||
To enable the low-level tracing build the library as:
|
||||
|
||||
CFLAGS=-DUSE_DEBUG ./configure [...]
|
||||
|
||||
Acknowledgements:
|
||||
-----------------
|
||||
|
||||
The following people have helped with suggestions, ideas, code or
|
||||
fixing bugs:
|
||||
|
||||
Alejo
|
||||
Weston Andros Adamson
|
||||
William Ahern
|
||||
Stas Bekman
|
||||
Andrew Danforth
|
||||
Mike Davis
|
||||
Shie Erlich
|
||||
Alexander von Gernler
|
||||
Artur Grabowski
|
||||
Aaron Hopkins
|
||||
Claudio Jeker
|
||||
Scott Lamb
|
||||
Adam Langley
|
||||
Philip Lewis
|
||||
David Libenzi
|
||||
Nick Mathewson
|
||||
Andrey Matveev
|
||||
Richard Nyberg
|
||||
Jon Oberheide
|
||||
Phil Oleson
|
||||
Dave Pacheco
|
||||
Tassilo von Parseval
|
||||
Pierre Phaneuf
|
||||
Jon Poland
|
||||
Bert JW Regeer
|
||||
Dug Song
|
||||
Taral
|
||||
|
||||
If I have forgotten your name, please contact me.
|
||||
40
third-party/webrtc/dependencies/base/third_party/libevent/README.chromium
vendored
Normal file
40
third-party/webrtc/dependencies/base/third_party/libevent/README.chromium
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
Name: libevent
|
||||
URL: http://libevent.org/
|
||||
Version: 1.4.15
|
||||
License: BSD
|
||||
Security Critical: yes
|
||||
|
||||
Local Modifications:
|
||||
Rather than use libevent's own build system, we just build a Chrome
|
||||
static library using GYP.
|
||||
|
||||
1) Run configure and "make event-config.h" on Linux, FreeBSD, Solaris,
|
||||
and Mac and copy config.h and event-config.h to linux/, freebsd/,
|
||||
solaris/, and mac/ respectively.
|
||||
2) Add libevent.gyp.
|
||||
3) chromium.patch is applied to make the following changes:
|
||||
- Allow libevent to be used without being installed by changing <...>
|
||||
#includes to "...".
|
||||
- Fix a race condition in event_del.
|
||||
- Optimistically assume CLOCK_MONOTONIC is available and fallback if it
|
||||
fails, rather than explicitly testing for it.
|
||||
- Remove an unneeded variable that causes a -Werror build failure.
|
||||
- Add an #ifndef to fix a preprocessor redefined -Werror build failure.
|
||||
- Revert the patch from http://sourceforge.net/p/levent/bugs/223/ that
|
||||
introduces use-after-free memory corruption when an event callback frees
|
||||
the struct event memory.
|
||||
- Remove deprecated global variables, event_sigcb and event_gotsig
|
||||
(essentially unused) that trigger tsan errors. (crbug/605894)
|
||||
4) The directories WIN32-Code and WIN32-Prj are not included.
|
||||
5) The configs for android were copied from Linux's which were very close to
|
||||
android one with the exception of HAVE_FD_MASK and HAVE_STRLCPY.
|
||||
6) Add files to support building with the PNaCl toolchain. Added
|
||||
libevent_nacl_nonsfi.gyp for build rule. nacl_nonsfi/config.h and
|
||||
nacl_nonsfi/event-config.h are derived from linux/ counterparts.
|
||||
nacl_nonsfi/random.c is also added to provide the random() function,
|
||||
which is missing in the newlib-based PNaCl toolchain.
|
||||
7) Stub out signal.c for nacl_helper_nonsfi. socketpair() will be prohibited
|
||||
by sandbox in nacl_helper_nonsfi.
|
||||
8) Remove an unnecessary workaround for OS X 10.4 from kqueue.c. It was causing
|
||||
problems on macOS Sierra.
|
||||
9) Change buffer.c to not redefine _GNU_SOURCE.
|
||||
276
third-party/webrtc/dependencies/base/third_party/libevent/aix/config.h
vendored
Normal file
276
third-party/webrtc/dependencies/base/third_party/libevent/aix/config.h
vendored
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define if clock_gettime is available in libc */
|
||||
#define DNS_USE_CPU_CLOCK_FOR_ID 1
|
||||
|
||||
/* Define is no secure id variant is available */
|
||||
/* #undef DNS_USE_GETTIMEOFDAY_FOR_ID */
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define if /dev/poll is available */
|
||||
/* #undef HAVE_DEVPOLL */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define if your system supports the epoll system calls */
|
||||
/* #undef HAVE_EPOLL */
|
||||
|
||||
/* Define to 1 if you have the `epoll_ctl' function. */
|
||||
/* #undef HAVE_EPOLL_CTL */
|
||||
|
||||
/* Define if your system supports event ports */
|
||||
/* #undef HAVE_EVENT_PORTS */
|
||||
|
||||
/* Define to 1 if you have the `fcntl' function. */
|
||||
#define HAVE_FCNTL 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `fd_mask'. */
|
||||
/* #undef HAVE_FD_MASK */
|
||||
|
||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||
#define HAVE_GETADDRINFO 1
|
||||
|
||||
/* Define to 1 if you have the `getegid' function. */
|
||||
#define HAVE_GETEGID 1
|
||||
|
||||
/* Define to 1 if you have the `geteuid' function. */
|
||||
#define HAVE_GETEUID 1
|
||||
|
||||
/* Define to 1 if you have the `getnameinfo' function. */
|
||||
#define HAVE_GETNAMEINFO 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#define HAVE_GETTIMEOFDAY 1
|
||||
|
||||
/* Define to 1 if you have the `inet_ntop' function. */
|
||||
#define HAVE_INET_NTOP 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `issetugid' function. */
|
||||
/* #undef HAVE_ISSETUGID */
|
||||
|
||||
/* Define to 1 if you have the `kqueue' function. */
|
||||
/* #undef HAVE_KQUEUE */
|
||||
|
||||
/* Define to 1 if you have the `nsl' library (-lnsl). */
|
||||
#define HAVE_LIBNSL 1
|
||||
|
||||
/* Define to 1 if you have the `resolv' library (-lresolv). */
|
||||
/* #undef HAVE_LIBRESOLV */
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#define HAVE_LIBRT 1
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
/* #undef HAVE_LIBSOCKET */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/in6.h> header file. */
|
||||
/* #undef HAVE_NETINET_IN6_H */
|
||||
|
||||
/* Define to 1 if you have the `poll' function. */
|
||||
#define HAVE_POLL 1
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define HAVE_POLL_H 1
|
||||
|
||||
/* Define to 1 if you have the `port_create' function. */
|
||||
/* #undef HAVE_PORT_CREATE */
|
||||
|
||||
/* Define to 1 if you have the <port.h> header file. */
|
||||
/* #undef HAVE_PORT_H */
|
||||
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
#define HAVE_SELECT 1
|
||||
|
||||
/* Define if F_SETFD is defined in <fcntl.h> */
|
||||
#define HAVE_SETFD 1
|
||||
|
||||
/* Define to 1 if you have the `sigaction' function. */
|
||||
#define HAVE_SIGACTION 1
|
||||
|
||||
/* Define to 1 if you have the `signal' function. */
|
||||
#define HAVE_SIGNAL 1
|
||||
|
||||
/* Define to 1 if you have the <signal.h> header file. */
|
||||
#define HAVE_SIGNAL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#define HAVE_STDARG_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strlcpy' function. */
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
/* Define to 1 if you have the `strsep' function. */
|
||||
#define HAVE_STRSEP 1
|
||||
|
||||
/* Define to 1 if you have the `strtok_r' function. */
|
||||
#define HAVE_STRTOK_R 1
|
||||
|
||||
/* Define to 1 if you have the `strtoll' function. */
|
||||
#define HAVE_STRTOLL 1
|
||||
|
||||
/* Define to 1 if the system has the type `struct in6_addr'. */
|
||||
#define HAVE_STRUCT_IN6_ADDR 1
|
||||
|
||||
/* Define to 1 if you have the <sys/devpoll.h> header file. */
|
||||
/* #undef HAVE_SYS_DEVPOLL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/epoll.h> header file. */
|
||||
/* #undef HAVE_SYS_EPOLL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/event.h> header file. */
|
||||
/* #undef HAVE_SYS_EVENT_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#define HAVE_SYS_IOCTL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#define HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/queue.h> header file. */
|
||||
#define HAVE_SYS_QUEUE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#define HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
|
||||
#define HAVE_TAILQFOREACH 1
|
||||
|
||||
/* Define if timeradd is defined in <sys/time.h> */
|
||||
/* #undef HAVE_TIMERADD */
|
||||
|
||||
/* Define if timerclear is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERCLEAR 1
|
||||
|
||||
/* Define if timercmp is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERCMP 1
|
||||
|
||||
/* Define if timerisset is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERISSET 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#define HAVE_UINT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint32_t'. */
|
||||
#define HAVE_UINT32_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint64_t'. */
|
||||
#define HAVE_UINT64_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint8_t'. */
|
||||
#define HAVE_UINT8_T 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `vasprintf' function. */
|
||||
/* #undef HAVE_VASPRINTF */
|
||||
|
||||
/* Define if kqueue works correctly with pipes */
|
||||
/* #undef HAVE_WORKING_KQUEUE */
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Numeric representation of the version */
|
||||
#define NUMERIC_VERSION 0x01040f00
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "libevent"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME ""
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING ""
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME ""
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION ""
|
||||
|
||||
/* The size of `int', as computed by sizeof. */
|
||||
#define SIZEOF_INT 4
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define SIZEOF_LONG 4
|
||||
|
||||
/* The size of `long long', as computed by sizeof. */
|
||||
#define SIZEOF_LONG_LONG 8
|
||||
|
||||
/* The size of `short', as computed by sizeof. */
|
||||
#define SIZEOF_SHORT 2
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "1.4.15"
|
||||
|
||||
/* Define to appropriate substitue if compiler doesnt have __func__ */
|
||||
/* #undef __func__ */
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
/* #undef inline */
|
||||
#endif
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef pid_t */
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
|
||||
/* Define to unsigned int if you dont have it */
|
||||
/* #undef socklen_t */
|
||||
284
third-party/webrtc/dependencies/base/third_party/libevent/aix/event-config.h
vendored
Normal file
284
third-party/webrtc/dependencies/base/third_party/libevent/aix/event-config.h
vendored
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/* event-config.h
|
||||
* Generated by autoconf; post-processed by libevent.
|
||||
* Do not edit this file.
|
||||
* Do not rely on macros in this file existing in later versions.
|
||||
*/
|
||||
#ifndef _EVENT_CONFIG_H_
|
||||
#define _EVENT_CONFIG_H_
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define if clock_gettime is available in libc */
|
||||
#define _EVENT_DNS_USE_CPU_CLOCK_FOR_ID 1
|
||||
|
||||
/* Define is no secure id variant is available */
|
||||
/* #undef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID */
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#define _EVENT_HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define if /dev/poll is available */
|
||||
/* #undef _EVENT_HAVE_DEVPOLL */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define _EVENT_HAVE_DLFCN_H 1
|
||||
|
||||
/* Define if your system supports the epoll system calls */
|
||||
/* #undef _EVENT_HAVE_EPOLL */
|
||||
|
||||
/* Define to 1 if you have the `epoll_ctl' function. */
|
||||
/* #undef _EVENT_HAVE_EPOLL_CTL */
|
||||
|
||||
/* Define if your system supports event ports */
|
||||
/* #undef _EVENT_HAVE_EVENT_PORTS */
|
||||
|
||||
/* Define to 1 if you have the `fcntl' function. */
|
||||
#define _EVENT_HAVE_FCNTL 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define _EVENT_HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `fd_mask'. */
|
||||
#define _EVENT_HAVE_FD_MASK 1
|
||||
|
||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||
#define _EVENT_HAVE_GETADDRINFO 1
|
||||
|
||||
/* Define to 1 if you have the `getegid' function. */
|
||||
#define _EVENT_HAVE_GETEGID 1
|
||||
|
||||
/* Define to 1 if you have the `geteuid' function. */
|
||||
#define _EVENT_HAVE_GETEUID 1
|
||||
|
||||
/* Define to 1 if you have the `getnameinfo' function. */
|
||||
#define _EVENT_HAVE_GETNAMEINFO 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#define _EVENT_HAVE_GETTIMEOFDAY 1
|
||||
|
||||
/* Define to 1 if you have the `inet_ntop' function. */
|
||||
#define _EVENT_HAVE_INET_NTOP 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define _EVENT_HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `issetugid' function. */
|
||||
/* #undef _EVENT_HAVE_ISSETUGID */
|
||||
|
||||
/* Define to 1 if you have the `kqueue' function. */
|
||||
/* #undef _EVENT_HAVE_KQUEUE */
|
||||
|
||||
/* Define to 1 if you have the `nsl' library (-lnsl). */
|
||||
#define _EVENT_HAVE_LIBNSL 1
|
||||
|
||||
/* Define to 1 if you have the `resolv' library (-lresolv). */
|
||||
/* #undef _EVENT_HAVE_LIBRESOLV */
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#define _EVENT_HAVE_LIBRT 1
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
/* #undef _EVENT_HAVE_LIBSOCKET */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define _EVENT_HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/in6.h> header file. */
|
||||
/* #undef _EVENT_HAVE_NETINET_IN6_H */
|
||||
|
||||
/* Define to 1 if you have the `poll' function. */
|
||||
#define _EVENT_HAVE_POLL 1
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define _EVENT_HAVE_POLL_H 1
|
||||
|
||||
/* Define to 1 if you have the `port_create' function. */
|
||||
/* #undef _EVENT_HAVE_PORT_CREATE */
|
||||
|
||||
/* Define to 1 if you have the <port.h> header file. */
|
||||
/* #undef _EVENT_HAVE_PORT_H */
|
||||
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
#define _EVENT_HAVE_SELECT 1
|
||||
|
||||
/* Define if F_SETFD is defined in <fcntl.h> */
|
||||
#define _EVENT_HAVE_SETFD 1
|
||||
|
||||
/* Define to 1 if you have the `sigaction' function. */
|
||||
#define _EVENT_HAVE_SIGACTION 1
|
||||
|
||||
/* Define to 1 if you have the `signal' function. */
|
||||
#define _EVENT_HAVE_SIGNAL 1
|
||||
|
||||
/* Define to 1 if you have the <signal.h> header file. */
|
||||
#define _EVENT_HAVE_SIGNAL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#define _EVENT_HAVE_STDARG_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define _EVENT_HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define _EVENT_HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define _EVENT_HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define _EVENT_HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strlcpy' function. */
|
||||
#define _EVENT_HAVE_STRLCPY 1
|
||||
|
||||
/* Define to 1 if you have the `strsep' function. */
|
||||
#define _EVENT_HAVE_STRSEP 1
|
||||
|
||||
/* Define to 1 if you have the `strtok_r' function. */
|
||||
#define _EVENT_HAVE_STRTOK_R 1
|
||||
|
||||
/* Define to 1 if you have the `strtoll' function. */
|
||||
#define _EVENT_HAVE_STRTOLL 1
|
||||
|
||||
/* Define to 1 if the system has the type `struct in6_addr'. */
|
||||
#define _EVENT_HAVE_STRUCT_IN6_ADDR 1
|
||||
|
||||
/* Define to 1 if you have the <sys/devpoll.h> header file. */
|
||||
/* #undef _EVENT_HAVE_SYS_DEVPOLL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/epoll.h> header file. */
|
||||
/* #undef _EVENT_HAVE_SYS_EPOLL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/event.h> header file. */
|
||||
/* #undef _EVENT_HAVE_SYS_EVENT_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_IOCTL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/queue.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_QUEUE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
|
||||
#define _EVENT_HAVE_TAILQFOREACH 1
|
||||
|
||||
/* Define if timeradd is defined in <sys/time.h> */
|
||||
/* #undef _EVENT_HAVE_TIMERADD */
|
||||
|
||||
/* Define if timerclear is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERCLEAR 1
|
||||
|
||||
/* Define if timercmp is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERCMP 1
|
||||
|
||||
/* Define if timerisset is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERISSET 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#define _EVENT_HAVE_UINT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint32_t'. */
|
||||
#define _EVENT_HAVE_UINT32_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint64_t'. */
|
||||
#define _EVENT_HAVE_UINT64_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint8_t'. */
|
||||
#define _EVENT_HAVE_UINT8_T 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define _EVENT_HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `vasprintf' function. */
|
||||
/* #undef _EVENT_HAVE_VASPRINTF */
|
||||
|
||||
/* Define if kqueue works correctly with pipes */
|
||||
/* #undef _EVENT_HAVE_WORKING_KQUEUE */
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define _EVENT_LT_OBJDIR ".libs/"
|
||||
|
||||
/* Numeric representation of the version */
|
||||
#define _EVENT_NUMERIC_VERSION 0x01040f00
|
||||
|
||||
/* Name of package */
|
||||
#define _EVENT_PACKAGE "libevent"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define _EVENT_PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define _EVENT_PACKAGE_NAME ""
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define _EVENT_PACKAGE_STRING ""
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define _EVENT_PACKAGE_TARNAME ""
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define _EVENT_PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define _EVENT_PACKAGE_VERSION ""
|
||||
|
||||
/* The size of `int', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_INT 4
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_LONG 4
|
||||
|
||||
/* The size of `long long', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_LONG_LONG 8
|
||||
|
||||
/* The size of `short', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_SHORT 2
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define _EVENT_STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define _EVENT_TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Version number of package */
|
||||
#define _EVENT_VERSION "1.4.15"
|
||||
|
||||
/* Define to appropriate substitue if compiler doesnt have __func__ */
|
||||
/* #undef _EVENT___func__ */
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef _EVENT_const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef _EVENT___cplusplus
|
||||
/* #undef _EVENT_inline */
|
||||
#endif
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef _EVENT_pid_t */
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef _EVENT_size_t */
|
||||
|
||||
/* Define to unsigned int if you dont have it */
|
||||
/* #undef _EVENT_socklen_t */
|
||||
#endif
|
||||
266
third-party/webrtc/dependencies/base/third_party/libevent/android/config.h
vendored
Normal file
266
third-party/webrtc/dependencies/base/third_party/libevent/android/config.h
vendored
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/* Copied from Linux version and changed the features according Android, which
|
||||
* is close to Linux */
|
||||
|
||||
/* Define if clock_gettime is available in libc */
|
||||
#define DNS_USE_CPU_CLOCK_FOR_ID 1
|
||||
|
||||
/* Define is no secure id variant is available */
|
||||
/* #undef DNS_USE_GETTIMEOFDAY_FOR_ID */
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define if /dev/poll is available */
|
||||
/* #undef HAVE_DEVPOLL */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define if your system supports the epoll system calls */
|
||||
#define HAVE_EPOLL 1
|
||||
|
||||
/* Define to 1 if you have the `epoll_ctl' function. */
|
||||
#define HAVE_EPOLL_CTL 1
|
||||
|
||||
/* Define if your system supports event ports */
|
||||
/* #undef HAVE_EVENT_PORTS */
|
||||
|
||||
/* Define to 1 if you have the `fcntl' function. */
|
||||
#define HAVE_FCNTL 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `fd_mask'. */
|
||||
/* #undef HAVE_FD_MASK */
|
||||
|
||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||
#define HAVE_GETADDRINFO 1
|
||||
|
||||
/* Define to 1 if you have the `getegid' function. */
|
||||
#define HAVE_GETEGID 1
|
||||
|
||||
/* Define to 1 if you have the `geteuid' function. */
|
||||
#define HAVE_GETEUID 1
|
||||
|
||||
/* Define to 1 if you have the `getnameinfo' function. */
|
||||
#define HAVE_GETNAMEINFO 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#define HAVE_GETTIMEOFDAY 1
|
||||
|
||||
/* Define to 1 if you have the `inet_ntop' function. */
|
||||
#define HAVE_INET_NTOP 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `issetugid' function. */
|
||||
/* #undef HAVE_ISSETUGID */
|
||||
|
||||
/* Define to 1 if you have the `kqueue' function. */
|
||||
/* #undef HAVE_KQUEUE */
|
||||
|
||||
/* Define to 1 if you have the `nsl' library (-lnsl). */
|
||||
#define HAVE_LIBNSL 1
|
||||
|
||||
/* Define to 1 if you have the `resolv' library (-lresolv). */
|
||||
#define HAVE_LIBRESOLV 1
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#define HAVE_LIBRT 1
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
/* #undef HAVE_LIBSOCKET */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/in6.h> header file. */
|
||||
/* #undef HAVE_NETINET_IN6_H */
|
||||
|
||||
/* Define to 1 if you have the `poll' function. */
|
||||
#define HAVE_POLL 1
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define HAVE_POLL_H 1
|
||||
|
||||
/* Define to 1 if you have the `port_create' function. */
|
||||
/* #undef HAVE_PORT_CREATE */
|
||||
|
||||
/* Define to 1 if you have the <port.h> header file. */
|
||||
/* #undef HAVE_PORT_H */
|
||||
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
#define HAVE_SELECT 1
|
||||
|
||||
/* Define if F_SETFD is defined in <fcntl.h> */
|
||||
#define HAVE_SETFD 1
|
||||
|
||||
/* Define to 1 if you have the `sigaction' function. */
|
||||
#define HAVE_SIGACTION 1
|
||||
|
||||
/* Define to 1 if you have the `signal' function. */
|
||||
#define HAVE_SIGNAL 1
|
||||
|
||||
/* Define to 1 if you have the <signal.h> header file. */
|
||||
#define HAVE_SIGNAL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#define HAVE_STDARG_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strlcpy' function. */
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
/* Define to 1 if you have the `strsep' function. */
|
||||
#define HAVE_STRSEP 1
|
||||
|
||||
/* Define to 1 if you have the `strtok_r' function. */
|
||||
#define HAVE_STRTOK_R 1
|
||||
|
||||
/* Define to 1 if you have the `strtoll' function. */
|
||||
#define HAVE_STRTOLL 1
|
||||
|
||||
/* Define to 1 if the system has the type `struct in6_addr'. */
|
||||
#define HAVE_STRUCT_IN6_ADDR 1
|
||||
|
||||
/* Define to 1 if you have the <sys/devpoll.h> header file. */
|
||||
/* #undef HAVE_SYS_DEVPOLL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/epoll.h> header file. */
|
||||
#define HAVE_SYS_EPOLL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/event.h> header file. */
|
||||
/* #undef HAVE_SYS_EVENT_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#define HAVE_SYS_IOCTL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#define HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/queue.h> header file. */
|
||||
#define HAVE_SYS_QUEUE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#define HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
|
||||
#define HAVE_TAILQFOREACH 1
|
||||
|
||||
/* Define if timeradd is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERADD 1
|
||||
|
||||
/* Define if timerclear is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERCLEAR 1
|
||||
|
||||
/* Define if timercmp is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERCMP 1
|
||||
|
||||
/* Define if timerisset is defined in <sys/time.h> */
|
||||
#define HAVE_TIMERISSET 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#define HAVE_UINT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint32_t'. */
|
||||
#define HAVE_UINT32_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint64_t'. */
|
||||
#define HAVE_UINT64_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint8_t'. */
|
||||
#define HAVE_UINT8_T 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `vasprintf' function. */
|
||||
#define HAVE_VASPRINTF 1
|
||||
|
||||
/* Define if kqueue works correctly with pipes */
|
||||
/* #undef HAVE_WORKING_KQUEUE */
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "libevent"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME ""
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING ""
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION ""
|
||||
|
||||
/* The size of `int', as computed by sizeof. */
|
||||
#define SIZEOF_INT 4
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define SIZEOF_LONG 8
|
||||
|
||||
/* The size of `long long', as computed by sizeof. */
|
||||
#define SIZEOF_LONG_LONG 8
|
||||
|
||||
/* The size of `short', as computed by sizeof. */
|
||||
#define SIZEOF_SHORT 2
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "1.4.13-stable"
|
||||
|
||||
/* Define to appropriate substitue if compiler doesnt have __func__ */
|
||||
/* #undef __func__ */
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
/* #undef inline */
|
||||
#endif
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef pid_t */
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
||||
|
||||
/* Define to unsigned int if you dont have it */
|
||||
/* #undef socklen_t */
|
||||
281
third-party/webrtc/dependencies/base/third_party/libevent/android/event-config.h
vendored
Normal file
281
third-party/webrtc/dependencies/base/third_party/libevent/android/event-config.h
vendored
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
/* Copied from Linux version and changed the features according Android, which
|
||||
* is close to Linux */
|
||||
#ifndef _EVENT_CONFIG_H_
|
||||
#define _EVENT_CONFIG_H_
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define if clock_gettime is available in libc */
|
||||
#define _EVENT_DNS_USE_CPU_CLOCK_FOR_ID 1
|
||||
|
||||
/* Define is no secure id variant is available */
|
||||
/* #undef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID */
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#define _EVENT_HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define if /dev/poll is available */
|
||||
/* #undef _EVENT_HAVE_DEVPOLL */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define _EVENT_HAVE_DLFCN_H 1
|
||||
|
||||
/* Define if your system supports the epoll system calls */
|
||||
#define _EVENT_HAVE_EPOLL 1
|
||||
|
||||
/* Define to 1 if you have the `epoll_ctl' function. */
|
||||
#define _EVENT_HAVE_EPOLL_CTL 1
|
||||
|
||||
/* Define if your system supports event ports */
|
||||
/* #undef _EVENT_HAVE_EVENT_PORTS */
|
||||
|
||||
/* Define to 1 if you have the `fcntl' function. */
|
||||
#define _EVENT_HAVE_FCNTL 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define _EVENT_HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `fd_mask'. */
|
||||
/* #undef _EVENT_HAVE_FD_MASK 1 */
|
||||
|
||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||
#define _EVENT_HAVE_GETADDRINFO 1
|
||||
|
||||
/* Define to 1 if you have the `getegid' function. */
|
||||
#define _EVENT_HAVE_GETEGID 1
|
||||
|
||||
/* Define to 1 if you have the `geteuid' function. */
|
||||
#define _EVENT_HAVE_GETEUID 1
|
||||
|
||||
/* Define to 1 if you have the `getnameinfo' function. */
|
||||
#define _EVENT_HAVE_GETNAMEINFO 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#define _EVENT_HAVE_GETTIMEOFDAY 1
|
||||
|
||||
/* Define to 1 if you have the `inet_ntop' function. */
|
||||
#define _EVENT_HAVE_INET_NTOP 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define _EVENT_HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `issetugid' function. */
|
||||
/* #undef _EVENT_HAVE_ISSETUGID */
|
||||
|
||||
/* Define to 1 if you have the `kqueue' function. */
|
||||
/* #undef _EVENT_HAVE_KQUEUE */
|
||||
|
||||
/* Define to 1 if you have the `nsl' library (-lnsl). */
|
||||
#define _EVENT_HAVE_LIBNSL 1
|
||||
|
||||
/* Define to 1 if you have the `resolv' library (-lresolv). */
|
||||
#define _EVENT_HAVE_LIBRESOLV 1
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#define _EVENT_HAVE_LIBRT 1
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
/* #undef _EVENT_HAVE_LIBSOCKET */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define _EVENT_HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/in6.h> header file. */
|
||||
/* #undef _EVENT_HAVE_NETINET_IN6_H */
|
||||
|
||||
/* Define to 1 if you have the `poll' function. */
|
||||
#define _EVENT_HAVE_POLL 1
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define _EVENT_HAVE_POLL_H 1
|
||||
|
||||
/* Define to 1 if you have the `port_create' function. */
|
||||
/* #undef _EVENT_HAVE_PORT_CREATE */
|
||||
|
||||
/* Define to 1 if you have the <port.h> header file. */
|
||||
/* #undef _EVENT_HAVE_PORT_H */
|
||||
|
||||
/* Define to 1 if you have the `select' function. */
|
||||
#define _EVENT_HAVE_SELECT 1
|
||||
|
||||
/* Define if F_SETFD is defined in <fcntl.h> */
|
||||
#define _EVENT_HAVE_SETFD 1
|
||||
|
||||
/* Define to 1 if you have the `sigaction' function. */
|
||||
#define _EVENT_HAVE_SIGACTION 1
|
||||
|
||||
/* Define to 1 if you have the `signal' function. */
|
||||
#define _EVENT_HAVE_SIGNAL 1
|
||||
|
||||
/* Define to 1 if you have the <signal.h> header file. */
|
||||
#define _EVENT_HAVE_SIGNAL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#define _EVENT_HAVE_STDARG_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define _EVENT_HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define _EVENT_HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define _EVENT_HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define _EVENT_HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strlcpy' function. */
|
||||
#define _EVENT_HAVE_STRLCPY 1
|
||||
|
||||
/* Define to 1 if you have the `strsep' function. */
|
||||
#define _EVENT_HAVE_STRSEP 1
|
||||
|
||||
/* Define to 1 if you have the `strtok_r' function. */
|
||||
#define _EVENT_HAVE_STRTOK_R 1
|
||||
|
||||
/* Define to 1 if you have the `strtoll' function. */
|
||||
#define _EVENT_HAVE_STRTOLL 1
|
||||
|
||||
/* Define to 1 if the system has the type `struct in6_addr'. */
|
||||
#define _EVENT_HAVE_STRUCT_IN6_ADDR 1
|
||||
|
||||
/* Define to 1 if you have the <sys/devpoll.h> header file. */
|
||||
/* #undef _EVENT_HAVE_SYS_DEVPOLL_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/epoll.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_EPOLL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/event.h> header file. */
|
||||
/* #undef _EVENT_HAVE_SYS_EVENT_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_IOCTL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/queue.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_QUEUE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define _EVENT_HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
|
||||
#define _EVENT_HAVE_TAILQFOREACH 1
|
||||
|
||||
/* Define if timeradd is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERADD 1
|
||||
|
||||
/* Define if timerclear is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERCLEAR 1
|
||||
|
||||
/* Define if timercmp is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERCMP 1
|
||||
|
||||
/* Define if timerisset is defined in <sys/time.h> */
|
||||
#define _EVENT_HAVE_TIMERISSET 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#define _EVENT_HAVE_UINT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint32_t'. */
|
||||
#define _EVENT_HAVE_UINT32_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint64_t'. */
|
||||
#define _EVENT_HAVE_UINT64_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint8_t'. */
|
||||
#define _EVENT_HAVE_UINT8_T 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define _EVENT_HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `vasprintf' function. */
|
||||
#define _EVENT_HAVE_VASPRINTF 1
|
||||
|
||||
/* Define if kqueue works correctly with pipes */
|
||||
/* #undef _EVENT_HAVE_WORKING_KQUEUE */
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define _EVENT_LT_OBJDIR ".libs/"
|
||||
|
||||
/* Numeric representation of the version */
|
||||
#define _EVENT_NUMERIC_VERSION 0x01040f00
|
||||
|
||||
/* Name of package */
|
||||
#define _EVENT_PACKAGE "libevent"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define _EVENT_PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define _EVENT_PACKAGE_NAME ""
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define _EVENT_PACKAGE_STRING ""
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define _EVENT_PACKAGE_TARNAME ""
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define _EVENT_PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define _EVENT_PACKAGE_VERSION ""
|
||||
|
||||
/* The size of `int', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_INT 4
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_LONG 8
|
||||
|
||||
/* The size of `long long', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_LONG_LONG 8
|
||||
|
||||
/* The size of `short', as computed by sizeof. */
|
||||
#define _EVENT_SIZEOF_SHORT 2
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define _EVENT_STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define _EVENT_TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Version number of package */
|
||||
#define _EVENT_VERSION "1.4.15"
|
||||
|
||||
/* Define to appropriate substitue if compiler doesnt have __func__ */
|
||||
/* #undef _EVENT___func__ */
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef _EVENT_const */
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef _EVENT___cplusplus
|
||||
/* #undef _EVENT_inline */
|
||||
#endif
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef _EVENT_pid_t */
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
/* #undef _EVENT_size_t */
|
||||
|
||||
/* Define to unsigned int if you dont have it */
|
||||
/* #undef _EVENT_socklen_t */
|
||||
#endif
|
||||
15
third-party/webrtc/dependencies/base/third_party/libevent/autogen.sh
vendored
Executable file
15
third-party/webrtc/dependencies/base/third_party/libevent/autogen.sh
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
if [ -x "`which autoreconf 2>/dev/null`" ] ; then
|
||||
exec autoreconf -ivf
|
||||
fi
|
||||
|
||||
LIBTOOLIZE=libtoolize
|
||||
SYSNAME=`uname`
|
||||
if [ "x$SYSNAME" = "xDarwin" ] ; then
|
||||
LIBTOOLIZE=glibtoolize
|
||||
fi
|
||||
aclocal && \
|
||||
autoheader && \
|
||||
$LIBTOOLIZE && \
|
||||
autoconf && \
|
||||
automake --add-missing --copy
|
||||
554
third-party/webrtc/dependencies/base/third_party/libevent/buffer.c
vendored
Normal file
554
third-party/webrtc/dependencies/base/third_party/libevent/buffer.c
vendored
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
/*
|
||||
* Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_VASPRINTF) && !defined(_GNU_SOURCE)
|
||||
/* If we have vasprintf, we need to define this before we include stdio.h. */
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "config.h"
|
||||
#include "evutil.h"
|
||||
#include "./log.h"
|
||||
|
||||
struct evbuffer *
|
||||
evbuffer_new(void)
|
||||
{
|
||||
struct evbuffer *buffer;
|
||||
|
||||
buffer = calloc(1, sizeof(struct evbuffer));
|
||||
|
||||
return (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
evbuffer_free(struct evbuffer *buffer)
|
||||
{
|
||||
if (buffer->orig_buffer != NULL)
|
||||
free(buffer->orig_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a destructive add. The data from one buffer moves into
|
||||
* the other buffer.
|
||||
*/
|
||||
|
||||
#define SWAP(x,y) do { \
|
||||
(x)->buffer = (y)->buffer; \
|
||||
(x)->orig_buffer = (y)->orig_buffer; \
|
||||
(x)->misalign = (y)->misalign; \
|
||||
(x)->totallen = (y)->totallen; \
|
||||
(x)->off = (y)->off; \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Short cut for better performance */
|
||||
if (outbuf->off == 0) {
|
||||
struct evbuffer tmp;
|
||||
size_t oldoff = inbuf->off;
|
||||
|
||||
/* Swap them directly */
|
||||
SWAP(&tmp, outbuf);
|
||||
SWAP(outbuf, inbuf);
|
||||
SWAP(inbuf, &tmp);
|
||||
|
||||
/*
|
||||
* Optimization comes with a price; we need to notify the
|
||||
* buffer if necessary of the changes. oldoff is the amount
|
||||
* of data that we transfered from inbuf to outbuf
|
||||
*/
|
||||
if (inbuf->off != oldoff && inbuf->cb != NULL)
|
||||
(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
|
||||
if (oldoff && outbuf->cb != NULL)
|
||||
(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
|
||||
if (res == 0) {
|
||||
/* We drain the input buffer on success */
|
||||
evbuffer_drain(inbuf, inbuf->off);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buffer;
|
||||
size_t space;
|
||||
size_t oldoff = buf->off;
|
||||
int sz;
|
||||
va_list aq;
|
||||
|
||||
/* make sure that at least some space is available */
|
||||
if (evbuffer_expand(buf, 64) < 0)
|
||||
return (-1);
|
||||
for (;;) {
|
||||
size_t used = buf->misalign + buf->off;
|
||||
buffer = (char *)buf->buffer + buf->off;
|
||||
assert(buf->totallen >= used);
|
||||
space = buf->totallen - used;
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
|
||||
#endif
|
||||
va_copy(aq, ap);
|
||||
|
||||
sz = evutil_vsnprintf(buffer, space, fmt, aq);
|
||||
|
||||
va_end(aq);
|
||||
|
||||
if (sz < 0)
|
||||
return (-1);
|
||||
if ((size_t)sz < space) {
|
||||
buf->off += sz;
|
||||
if (buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
return (sz);
|
||||
}
|
||||
if (evbuffer_expand(buf, sz + 1) == -1)
|
||||
return (-1);
|
||||
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
|
||||
{
|
||||
int res = -1;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
res = evbuffer_add_vprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/* Reads data from an event buffer and drains the bytes read */
|
||||
|
||||
int
|
||||
evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
|
||||
{
|
||||
size_t nread = datlen;
|
||||
if (nread >= buf->off)
|
||||
nread = buf->off;
|
||||
|
||||
memcpy(data, buf->buffer, nread);
|
||||
evbuffer_drain(buf, nread);
|
||||
|
||||
return (nread);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
|
||||
* The returned buffer needs to be freed by the called.
|
||||
*/
|
||||
|
||||
char *
|
||||
evbuffer_readline(struct evbuffer *buffer)
|
||||
{
|
||||
u_char *data = EVBUFFER_DATA(buffer);
|
||||
size_t len = EVBUFFER_LENGTH(buffer);
|
||||
char *line;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == '\r' || data[i] == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == len)
|
||||
return (NULL);
|
||||
|
||||
if ((line = malloc(i + 1)) == NULL) {
|
||||
fprintf(stderr, "%s: out of memory\n", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
memcpy(line, data, i);
|
||||
line[i] = '\0';
|
||||
|
||||
/*
|
||||
* Some protocols terminate a line with '\r\n', so check for
|
||||
* that, too.
|
||||
*/
|
||||
if ( i < len - 1 ) {
|
||||
char fch = data[i], sch = data[i+1];
|
||||
|
||||
/* Drain one more character if needed */
|
||||
if ( (sch == '\r' || sch == '\n') && sch != fch )
|
||||
i += 1;
|
||||
}
|
||||
|
||||
evbuffer_drain(buffer, i + 1);
|
||||
|
||||
return (line);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
|
||||
enum evbuffer_eol_style eol_style)
|
||||
{
|
||||
u_char *data = EVBUFFER_DATA(buffer);
|
||||
u_char *start_of_eol, *end_of_eol;
|
||||
size_t len = EVBUFFER_LENGTH(buffer);
|
||||
char *line;
|
||||
unsigned int i, n_to_copy, n_to_drain;
|
||||
|
||||
if (n_read_out)
|
||||
*n_read_out = 0;
|
||||
|
||||
/* depending on eol_style, set start_of_eol to the first character
|
||||
* in the newline, and end_of_eol to one after the last character. */
|
||||
switch (eol_style) {
|
||||
case EVBUFFER_EOL_ANY:
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == '\r' || data[i] == '\n')
|
||||
break;
|
||||
}
|
||||
if (i == len)
|
||||
return (NULL);
|
||||
start_of_eol = data+i;
|
||||
++i;
|
||||
for ( ; i < len; i++) {
|
||||
if (data[i] != '\r' && data[i] != '\n')
|
||||
break;
|
||||
}
|
||||
end_of_eol = data+i;
|
||||
break;
|
||||
case EVBUFFER_EOL_CRLF:
|
||||
end_of_eol = memchr(data, '\n', len);
|
||||
if (!end_of_eol)
|
||||
return (NULL);
|
||||
if (end_of_eol > data && *(end_of_eol-1) == '\r')
|
||||
start_of_eol = end_of_eol - 1;
|
||||
else
|
||||
start_of_eol = end_of_eol;
|
||||
end_of_eol++; /*point to one after the LF. */
|
||||
break;
|
||||
case EVBUFFER_EOL_CRLF_STRICT: {
|
||||
u_char *cp = data;
|
||||
while ((cp = memchr(cp, '\r', len-(cp-data)))) {
|
||||
if (cp < data+len-1 && *(cp+1) == '\n')
|
||||
break;
|
||||
if (++cp >= data+len) {
|
||||
cp = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cp)
|
||||
return (NULL);
|
||||
start_of_eol = cp;
|
||||
end_of_eol = cp+2;
|
||||
break;
|
||||
}
|
||||
case EVBUFFER_EOL_LF:
|
||||
start_of_eol = memchr(data, '\n', len);
|
||||
if (!start_of_eol)
|
||||
return (NULL);
|
||||
end_of_eol = start_of_eol + 1;
|
||||
break;
|
||||
default:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
n_to_copy = start_of_eol - data;
|
||||
n_to_drain = end_of_eol - data;
|
||||
|
||||
if ((line = malloc(n_to_copy+1)) == NULL) {
|
||||
event_warn("%s: out of memory\n", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
memcpy(line, data, n_to_copy);
|
||||
line[n_to_copy] = '\0';
|
||||
|
||||
evbuffer_drain(buffer, n_to_drain);
|
||||
if (n_read_out)
|
||||
*n_read_out = (size_t)n_to_copy;
|
||||
|
||||
return (line);
|
||||
}
|
||||
|
||||
/* Adds data to an event buffer */
|
||||
|
||||
static void
|
||||
evbuffer_align(struct evbuffer *buf)
|
||||
{
|
||||
memmove(buf->orig_buffer, buf->buffer, buf->off);
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
}
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t)-1)
|
||||
#endif
|
||||
|
||||
/* Expands the available space in the event buffer to at least datlen */
|
||||
|
||||
int
|
||||
evbuffer_expand(struct evbuffer *buf, size_t datlen)
|
||||
{
|
||||
size_t used = buf->misalign + buf->off;
|
||||
|
||||
assert(buf->totallen >= used);
|
||||
|
||||
/* If we can fit all the data, then we don't have to do anything */
|
||||
if (buf->totallen - used >= datlen)
|
||||
return (0);
|
||||
/* If we would need to overflow to fit this much data, we can't
|
||||
* do anything. */
|
||||
if (datlen > SIZE_MAX - buf->off)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* If the misalignment fulfills our data needs, we just force an
|
||||
* alignment to happen. Afterwards, we have enough space.
|
||||
*/
|
||||
if (buf->totallen - buf->off >= datlen) {
|
||||
evbuffer_align(buf);
|
||||
} else {
|
||||
void *newbuf;
|
||||
size_t length = buf->totallen;
|
||||
size_t need = buf->off + datlen;
|
||||
|
||||
if (length < 256)
|
||||
length = 256;
|
||||
if (need < SIZE_MAX / 2) {
|
||||
while (length < need) {
|
||||
length <<= 1;
|
||||
}
|
||||
} else {
|
||||
length = need;
|
||||
}
|
||||
|
||||
if (buf->orig_buffer != buf->buffer)
|
||||
evbuffer_align(buf);
|
||||
if ((newbuf = realloc(buf->buffer, length)) == NULL)
|
||||
return (-1);
|
||||
|
||||
buf->orig_buffer = buf->buffer = newbuf;
|
||||
buf->totallen = length;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
|
||||
{
|
||||
size_t used = buf->misalign + buf->off;
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (buf->totallen - used < datlen) {
|
||||
if (evbuffer_expand(buf, datlen) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(buf->buffer + buf->off, data, datlen);
|
||||
buf->off += datlen;
|
||||
|
||||
if (datlen && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evbuffer_drain(struct evbuffer *buf, size_t len)
|
||||
{
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (len >= buf->off) {
|
||||
buf->off = 0;
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
buf->buffer += len;
|
||||
buf->misalign += len;
|
||||
|
||||
buf->off -= len;
|
||||
|
||||
done:
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads data from a file descriptor into a buffer.
|
||||
*/
|
||||
|
||||
#define EVBUFFER_MAX_READ 4096
|
||||
|
||||
int
|
||||
evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
|
||||
{
|
||||
u_char *p;
|
||||
size_t oldoff = buf->off;
|
||||
int n = EVBUFFER_MAX_READ;
|
||||
|
||||
#if defined(FIONREAD)
|
||||
#ifdef WIN32
|
||||
long lng = n;
|
||||
if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) <= 0) {
|
||||
#else
|
||||
if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) {
|
||||
#endif
|
||||
n = EVBUFFER_MAX_READ;
|
||||
} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
|
||||
/*
|
||||
* It's possible that a lot of data is available for
|
||||
* reading. We do not want to exhaust resources
|
||||
* before the reader has a chance to do something
|
||||
* about it. If the reader does not tell us how much
|
||||
* data we should read, we artifically limit it.
|
||||
*/
|
||||
if ((size_t)n > buf->totallen << 2)
|
||||
n = buf->totallen << 2;
|
||||
if (n < EVBUFFER_MAX_READ)
|
||||
n = EVBUFFER_MAX_READ;
|
||||
}
|
||||
#endif
|
||||
if (howmuch < 0 || howmuch > n)
|
||||
howmuch = n;
|
||||
|
||||
/* If we don't have FIONREAD, we might waste some space here */
|
||||
if (evbuffer_expand(buf, howmuch) == -1)
|
||||
return (-1);
|
||||
|
||||
/* We can append new data at this point */
|
||||
p = buf->buffer + buf->off;
|
||||
|
||||
#ifndef WIN32
|
||||
n = read(fd, p, howmuch);
|
||||
#else
|
||||
n = recv(fd, p, howmuch, 0);
|
||||
#endif
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
|
||||
buf->off += n;
|
||||
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_write(struct evbuffer *buffer, int fd)
|
||||
{
|
||||
int n;
|
||||
|
||||
#ifndef WIN32
|
||||
n = write(fd, buffer->buffer, buffer->off);
|
||||
#else
|
||||
n = send(fd, buffer->buffer, buffer->off, 0);
|
||||
#endif
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
evbuffer_drain(buffer, n);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
u_char *
|
||||
evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
|
||||
{
|
||||
u_char *search = buffer->buffer, *end = search + buffer->off;
|
||||
u_char *p;
|
||||
|
||||
while (search < end &&
|
||||
(p = memchr(search, *what, end - search)) != NULL) {
|
||||
if (p + len > end)
|
||||
break;
|
||||
if (memcmp(p, what, len) == 0)
|
||||
return (p);
|
||||
search = p + 1;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void evbuffer_setcb(struct evbuffer *buffer,
|
||||
void (*cb)(struct evbuffer *, size_t, size_t, void *),
|
||||
void *cbarg)
|
||||
{
|
||||
buffer->cb = cb;
|
||||
buffer->cbarg = cbarg;
|
||||
}
|
||||
226
third-party/webrtc/dependencies/base/third_party/libevent/chromium.patch
vendored
Normal file
226
third-party/webrtc/dependencies/base/third_party/libevent/chromium.patch
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
diff --git a/third_party/libevent/buffer.c b/third_party/libevent/buffer.c
|
||||
index 64324bb..ebf35c9 100644
|
||||
--- a/third_party/libevent/buffer.c
|
||||
+++ b/third_party/libevent/buffer.c
|
||||
@@ -356,7 +356,6 @@ int
|
||||
evbuffer_expand(struct evbuffer *buf, size_t datlen)
|
||||
{
|
||||
size_t used = buf->misalign + buf->off;
|
||||
- size_t need;
|
||||
|
||||
assert(buf->totallen >= used);
|
||||
|
||||
diff --git a/third_party/libevent/evdns.c b/third_party/libevent/evdns.c
|
||||
index fa23163..f1c70d0 100644
|
||||
--- a/third_party/libevent/evdns.c
|
||||
+++ b/third_party/libevent/evdns.c
|
||||
@@ -55,7 +55,9 @@
|
||||
#endif
|
||||
|
||||
/* #define _POSIX_C_SOURCE 200507 */
|
||||
+#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
+#endif
|
||||
|
||||
#ifdef DNS_USE_CPU_CLOCK_FOR_ID
|
||||
#ifdef DNS_USE_OPENSSL_FOR_ID
|
||||
@@ -134,7 +136,7 @@
|
||||
typedef ev_uint8_t u_char;
|
||||
typedef unsigned int uint;
|
||||
#endif
|
||||
-#include <event.h>
|
||||
+#include "event.h"
|
||||
|
||||
#define u64 ev_uint64_t
|
||||
#define u32 ev_uint32_t
|
||||
diff --git a/third_party/libevent/evdns.h b/third_party/libevent/evdns.h
|
||||
index 1eb5c38..fca4ac3 100644
|
||||
--- a/third_party/libevent/evdns.h
|
||||
+++ b/third_party/libevent/evdns.h
|
||||
@@ -165,7 +165,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* For integer types. */
|
||||
-#include <evutil.h>
|
||||
+#include "evutil.h"
|
||||
|
||||
/** Error codes 0-5 are as described in RFC 1035. */
|
||||
#define DNS_ERR_NONE 0
|
||||
diff --git a/third_party/libevent/event.c b/third_party/libevent/event.c
|
||||
index da6cd42..36b1c51 100644
|
||||
--- a/third_party/libevent/event.c
|
||||
+++ b/third_party/libevent/event.c
|
||||
@@ -107,11 +107,7 @@ static const struct eventop *eventops[] = {
|
||||
/* Global state */
|
||||
struct event_base *current_base = NULL;
|
||||
extern struct event_base *evsignal_base;
|
||||
-static int use_monotonic;
|
||||
-
|
||||
-/* Handle signals - This is a deprecated interface */
|
||||
-int (*event_sigcb)(void); /* Signal callback when gotsig is set */
|
||||
-volatile sig_atomic_t event_gotsig; /* Set in signal handler */
|
||||
+static int use_monotonic = 1;
|
||||
|
||||
/* Prototypes */
|
||||
static void event_queue_insert(struct event_base *, struct event *, int);
|
||||
@@ -124,17 +120,6 @@ static int timeout_next(struct event_base *, struct timeval **);
|
||||
static void timeout_process(struct event_base *);
|
||||
static void timeout_correct(struct event_base *, struct timeval *);
|
||||
|
||||
-static void
|
||||
-detect_monotonic(void)
|
||||
-{
|
||||
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||
- struct timespec ts;
|
||||
-
|
||||
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
|
||||
- use_monotonic = 1;
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
static int
|
||||
gettime(struct event_base *base, struct timeval *tp)
|
||||
{
|
||||
@@ -144,18 +129,18 @@ gettime(struct event_base *base, struct timeval *tp)
|
||||
}
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||
- if (use_monotonic) {
|
||||
- struct timespec ts;
|
||||
-
|
||||
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
|
||||
- return (-1);
|
||||
+ struct timespec ts;
|
||||
|
||||
+ if (use_monotonic &&
|
||||
+ clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
|
||||
tp->tv_sec = ts.tv_sec;
|
||||
tp->tv_usec = ts.tv_nsec / 1000;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
+ use_monotonic = 0;
|
||||
+
|
||||
return (evutil_gettimeofday(tp, NULL));
|
||||
}
|
||||
|
||||
@@ -179,10 +164,6 @@ event_base_new(void)
|
||||
if ((base = calloc(1, sizeof(struct event_base))) == NULL)
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
|
||||
- event_sigcb = NULL;
|
||||
- event_gotsig = 0;
|
||||
-
|
||||
- detect_monotonic();
|
||||
gettime(base, &base->event_tv);
|
||||
|
||||
min_heap_ctor(&base->timeheap);
|
||||
@@ -398,12 +379,9 @@ event_process_active(struct event_base *base)
|
||||
ncalls--;
|
||||
ev->ev_ncalls = ncalls;
|
||||
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
|
||||
- if (event_gotsig || base->event_break) {
|
||||
- ev->ev_pncalls = NULL;
|
||||
+ if (base->event_break)
|
||||
return;
|
||||
- }
|
||||
}
|
||||
- ev->ev_pncalls = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,18 +484,6 @@ event_base_loop(struct event_base *base, int flags)
|
||||
break;
|
||||
}
|
||||
|
||||
- /* You cannot use this interface for multi-threaded apps */
|
||||
- while (event_gotsig) {
|
||||
- event_gotsig = 0;
|
||||
- if (event_sigcb) {
|
||||
- res = (*event_sigcb)();
|
||||
- if (res == -1) {
|
||||
- errno = EINTR;
|
||||
- return (-1);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
timeout_correct(base, &tv);
|
||||
|
||||
tv_p = &tv;
|
||||
@@ -808,8 +774,6 @@ int
|
||||
event_del(struct event *ev)
|
||||
{
|
||||
struct event_base *base;
|
||||
- const struct eventop *evsel;
|
||||
- void *evbase;
|
||||
|
||||
event_debug(("event_del: %p, callback %p",
|
||||
ev, ev->ev_callback));
|
||||
@@ -819,8 +783,6 @@ event_del(struct event *ev)
|
||||
return (-1);
|
||||
|
||||
base = ev->ev_base;
|
||||
- evsel = base->evsel;
|
||||
- evbase = base->evbase;
|
||||
|
||||
assert(!(ev->ev_flags & ~EVLIST_ALL));
|
||||
|
||||
@@ -838,7 +800,7 @@ event_del(struct event *ev)
|
||||
|
||||
if (ev->ev_flags & EVLIST_INSERTED) {
|
||||
event_queue_remove(base, ev, EVLIST_INSERTED);
|
||||
- return (evsel->del(evbase, ev));
|
||||
+ return (base->evsel->del(base->evbase, ev));
|
||||
}
|
||||
|
||||
return (0);
|
||||
diff --git a/third_party/libevent/event.h b/third_party/libevent/event.h
|
||||
index d1f5d9e..f0887b9 100644
|
||||
--- a/third_party/libevent/event.h
|
||||
+++ b/third_party/libevent/event.h
|
||||
@@ -159,7 +159,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
-#include <event-config.h>
|
||||
+#include "event-config.h"
|
||||
#ifdef _EVENT_HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
@@ -172,7 +172,7 @@ extern "C" {
|
||||
#include <stdarg.h>
|
||||
|
||||
/* For int types. */
|
||||
-#include <evutil.h>
|
||||
+#include "evutil.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
diff --git a/third_party/libevent/evhttp.h b/third_party/libevent/evhttp.h
|
||||
index cba8be1..48c1d91 100644
|
||||
--- a/third_party/libevent/evhttp.h
|
||||
+++ b/third_party/libevent/evhttp.h
|
||||
@@ -27,7 +27,7 @@
|
||||
#ifndef _EVHTTP_H_
|
||||
#define _EVHTTP_H_
|
||||
|
||||
-#include <event.h>
|
||||
+#include "event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
diff --git a/third_party/libevent/evutil.h b/third_party/libevent/evutil.h
|
||||
index dcb0013..8b664b9 100644
|
||||
--- a/third_party/libevent/evutil.h
|
||||
+++ b/third_party/libevent/evutil.h
|
||||
@@ -38,7 +38,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
-#include <event-config.h>
|
||||
+#include "event-config.h"
|
||||
#ifdef _EVENT_HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
163
third-party/webrtc/dependencies/base/third_party/libevent/compat/sys/_libevent_time.h
vendored
Normal file
163
third-party/webrtc/dependencies/base/third_party/libevent/compat/sys/_libevent_time.h
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/* $OpenBSD: time.h,v 1.11 2000/10/10 13:36:48 itojun Exp $ */
|
||||
/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)time.h 8.2 (Berkeley) 7/10/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_TIME_H_
|
||||
#define _SYS_TIME_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* Structure returned by gettimeofday(2) system call,
|
||||
* and used in other calls.
|
||||
*/
|
||||
struct timeval {
|
||||
long tv_sec; /* seconds */
|
||||
long tv_usec; /* and microseconds */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure defined by POSIX.1b to be like a timeval.
|
||||
*/
|
||||
struct timespec {
|
||||
time_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* and nanoseconds */
|
||||
};
|
||||
|
||||
#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
|
||||
(ts)->tv_sec = (tv)->tv_sec; \
|
||||
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
|
||||
}
|
||||
#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
|
||||
(tv)->tv_sec = (ts)->tv_sec; \
|
||||
(tv)->tv_usec = (ts)->tv_nsec / 1000; \
|
||||
}
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest; /* minutes west of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
#define DST_NONE 0 /* not on dst */
|
||||
#define DST_USA 1 /* USA style dst */
|
||||
#define DST_AUST 2 /* Australian style dst */
|
||||
#define DST_WET 3 /* Western European dst */
|
||||
#define DST_MET 4 /* Middle European dst */
|
||||
#define DST_EET 5 /* Eastern European dst */
|
||||
#define DST_CAN 6 /* Canada */
|
||||
|
||||
/* Operations on timevals. */
|
||||
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
|
||||
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
|
||||
#define timercmp(tvp, uvp, cmp) \
|
||||
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
|
||||
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
|
||||
((tvp)->tv_sec cmp (uvp)->tv_sec))
|
||||
#define timeradd(tvp, uvp, vvp) \
|
||||
do { \
|
||||
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
|
||||
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
|
||||
if ((vvp)->tv_usec >= 1000000) { \
|
||||
(vvp)->tv_sec++; \
|
||||
(vvp)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timersub(tvp, uvp, vvp) \
|
||||
do { \
|
||||
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
|
||||
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
|
||||
if ((vvp)->tv_usec < 0) { \
|
||||
(vvp)->tv_sec--; \
|
||||
(vvp)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Operations on timespecs. */
|
||||
#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0
|
||||
#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
|
||||
#define timespeccmp(tsp, usp, cmp) \
|
||||
(((tsp)->tv_sec == (usp)->tv_sec) ? \
|
||||
((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
|
||||
((tsp)->tv_sec cmp (usp)->tv_sec))
|
||||
#define timespecadd(tsp, usp, vsp) \
|
||||
do { \
|
||||
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
|
||||
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
|
||||
if ((vsp)->tv_nsec >= 1000000000L) { \
|
||||
(vsp)->tv_sec++; \
|
||||
(vsp)->tv_nsec -= 1000000000L; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timespecsub(tsp, usp, vsp) \
|
||||
do { \
|
||||
(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
|
||||
(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
|
||||
if ((vsp)->tv_nsec < 0) { \
|
||||
(vsp)->tv_sec--; \
|
||||
(vsp)->tv_nsec += 1000000000L; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Names of the interval timers, and structure
|
||||
* defining a timer setting.
|
||||
*/
|
||||
#define ITIMER_REAL 0
|
||||
#define ITIMER_VIRTUAL 1
|
||||
#define ITIMER_PROF 2
|
||||
|
||||
struct itimerval {
|
||||
struct timeval it_interval; /* timer interval */
|
||||
struct timeval it_value; /* current value */
|
||||
};
|
||||
|
||||
/*
|
||||
* Getkerninfo clock information structure
|
||||
*/
|
||||
struct clockinfo {
|
||||
int hz; /* clock frequency */
|
||||
int tick; /* micro-seconds per hz tick */
|
||||
int tickadj; /* clock skew rate for adjtime() */
|
||||
int stathz; /* statistics clock frequency */
|
||||
int profhz; /* profiling clock frequency */
|
||||
};
|
||||
|
||||
#define CLOCK_REALTIME 0
|
||||
#define CLOCK_VIRTUAL 1
|
||||
#define CLOCK_PROF 2
|
||||
|
||||
#define TIMER_RELTIME 0x0 /* relative timer */
|
||||
#define TIMER_ABSTIME 0x1 /* absolute timer */
|
||||
|
||||
/* --- stuff got cut here - niels --- */
|
||||
|
||||
#endif /* !_SYS_TIME_H_ */
|
||||
488
third-party/webrtc/dependencies/base/third_party/libevent/compat/sys/queue.h
vendored
Normal file
488
third-party/webrtc/dependencies/base/third_party/libevent/compat/sys/queue.h
vendored
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#ifndef WIN32
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
|
||||
if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* tail queue access methods
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue access methods
|
||||
*/
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_END(head) ((void *)(head))
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
#define CIRCLEQ_EMPTY(head) \
|
||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_NEXT(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for((var) = CIRCLEQ_LAST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_PREV(var, field))
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head).cqh_last = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head).cqh_first = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
421
third-party/webrtc/dependencies/base/third_party/libevent/configure.in
vendored
Normal file
421
third-party/webrtc/dependencies/base/third_party/libevent/configure.in
vendored
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
dnl configure.in for libevent
|
||||
dnl Dug Song <dugsong@monkey.org>
|
||||
AC_INIT(event.c)
|
||||
|
||||
AM_INIT_AUTOMAKE(libevent,1.4.15)
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
dnl AM_MAINTAINER_MODE
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
AC_DEFINE(NUMERIC_VERSION, 0x01040f00, [Numeric representation of the version])
|
||||
|
||||
dnl Initialize prefix.
|
||||
if test "$prefix" = "NONE"; then
|
||||
prefix="/usr/local"
|
||||
fi
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
if test "$GCC" = yes ; then
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
# And disable the strict-aliasing optimization, since it breaks
|
||||
# our sockaddr-handling code in strange ways.
|
||||
CFLAGS="$CFLAGS -fno-strict-aliasing"
|
||||
fi
|
||||
|
||||
dnl Libevent 1.4 isn't multithreaded, but some of its functions are
|
||||
dnl documented to be reentrant. If you don't define the right macros
|
||||
dnl on some platforms, you get non-reentrant versions of the libc
|
||||
dnl functinos (like an errno that's shared by all threads).
|
||||
AC_MSG_CHECKING([whether we need extra flags to make libc reentrant])
|
||||
case $host in
|
||||
*solaris* | *-osf* | *-hpux* )
|
||||
AC_MSG_RESULT([-D_REENTRANT])
|
||||
CFLAGS="$CFLAGS -D_REENTRANT"
|
||||
;;
|
||||
*-aix* | *-freebsd* | *-darwin* )
|
||||
AC_MSG_RESULT([-D_THREAD_SAFE])
|
||||
CFLAGS="$CFLAGS -D_THREAD_SAFE"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT(no)
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_ARG_ENABLE(gcc-warnings,
|
||||
AS_HELP_STRING(--enable-gcc-warnings, enable verbose warnings with GCC))
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
dnl Uncomment "AC_DISABLE_SHARED" to make shared librraries not get
|
||||
dnl built by default. You can also turn shared libs on and off from
|
||||
dnl the command line with --enable-shared and --disable-shared.
|
||||
dnl AC_DISABLE_SHARED
|
||||
AC_SUBST(LIBTOOL_DEPS)
|
||||
|
||||
dnl Checks for libraries.
|
||||
AC_CHECK_LIB(socket, socket)
|
||||
AC_CHECK_LIB(resolv, inet_aton)
|
||||
AC_CHECK_LIB(rt, clock_gettime)
|
||||
AC_CHECK_LIB(nsl, inet_ntoa)
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h)
|
||||
if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
|
||||
AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
|
||||
AC_EGREP_CPP(yes,
|
||||
[
|
||||
#include <sys/queue.h>
|
||||
#ifdef TAILQ_FOREACH
|
||||
yes
|
||||
#endif
|
||||
], [AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_TAILQFOREACH, 1,
|
||||
[Define if TAILQ_FOREACH is defined in <sys/queue.h>])],
|
||||
AC_MSG_RESULT(no)
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$ac_cv_header_sys_time_h" = "xyes"; then
|
||||
AC_MSG_CHECKING(for timeradd in sys/time.h)
|
||||
AC_EGREP_CPP(yes,
|
||||
[
|
||||
#include <sys/time.h>
|
||||
#ifdef timeradd
|
||||
yes
|
||||
#endif
|
||||
], [ AC_DEFINE(HAVE_TIMERADD, 1,
|
||||
[Define if timeradd is defined in <sys/time.h>])
|
||||
AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$ac_cv_header_sys_time_h" = "xyes"; then
|
||||
AC_MSG_CHECKING(for timercmp in sys/time.h)
|
||||
AC_EGREP_CPP(yes,
|
||||
[
|
||||
#include <sys/time.h>
|
||||
#ifdef timercmp
|
||||
yes
|
||||
#endif
|
||||
], [ AC_DEFINE(HAVE_TIMERCMP, 1,
|
||||
[Define if timercmp is defined in <sys/time.h>])
|
||||
AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$ac_cv_header_sys_time_h" = "xyes"; then
|
||||
AC_MSG_CHECKING(for timerclear in sys/time.h)
|
||||
AC_EGREP_CPP(yes,
|
||||
[
|
||||
#include <sys/time.h>
|
||||
#ifdef timerclear
|
||||
yes
|
||||
#endif
|
||||
], [ AC_DEFINE(HAVE_TIMERCLEAR, 1,
|
||||
[Define if timerclear is defined in <sys/time.h>])
|
||||
AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$ac_cv_header_sys_time_h" = "xyes"; then
|
||||
AC_MSG_CHECKING(for timerisset in sys/time.h)
|
||||
AC_EGREP_CPP(yes,
|
||||
[
|
||||
#include <sys/time.h>
|
||||
#ifdef timerisset
|
||||
yes
|
||||
#endif
|
||||
], [ AC_DEFINE(HAVE_TIMERISSET, 1,
|
||||
[Define if timerisset is defined in <sys/time.h>])
|
||||
AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
|
||||
)
|
||||
fi
|
||||
|
||||
dnl - check if the macro WIN32 is defined on this compiler.
|
||||
dnl - (this is how we check for a windows version of GCC)
|
||||
AC_MSG_CHECKING(for WIN32)
|
||||
AC_TRY_COMPILE(,
|
||||
[
|
||||
#ifndef WIN32
|
||||
die horribly
|
||||
#endif
|
||||
],
|
||||
bwin32=true; AC_MSG_RESULT(yes),
|
||||
bwin32=false; AC_MSG_RESULT(no),
|
||||
)
|
||||
|
||||
AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue)
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_HEADER_TIME
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop signal sigaction strtoll issetugid geteuid getegid)
|
||||
|
||||
AC_CHECK_SIZEOF(long)
|
||||
|
||||
if test "x$ac_cv_func_clock_gettime" = "xyes"; then
|
||||
AC_DEFINE(DNS_USE_CPU_CLOCK_FOR_ID, 1, [Define if clock_gettime is available in libc])
|
||||
else
|
||||
AC_DEFINE(DNS_USE_GETTIMEOFDAY_FOR_ID, 1, [Define is no secure id variant is available])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for F_SETFD in fcntl.h)
|
||||
AC_EGREP_CPP(yes,
|
||||
[
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#ifdef F_SETFD
|
||||
yes
|
||||
#endif
|
||||
], [ AC_DEFINE(HAVE_SETFD, 1,
|
||||
[Define if F_SETFD is defined in <fcntl.h>])
|
||||
AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no))
|
||||
|
||||
needsignal=no
|
||||
haveselect=no
|
||||
AC_CHECK_FUNCS(select, [haveselect=yes], )
|
||||
if test "x$haveselect" = "xyes" ; then
|
||||
AC_LIBOBJ(select)
|
||||
needsignal=yes
|
||||
fi
|
||||
|
||||
havepoll=no
|
||||
AC_CHECK_FUNCS(poll, [havepoll=yes], )
|
||||
if test "x$havepoll" = "xyes" ; then
|
||||
AC_LIBOBJ(poll)
|
||||
needsignal=yes
|
||||
fi
|
||||
|
||||
haveepoll=no
|
||||
AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], )
|
||||
if test "x$haveepoll" = "xyes" ; then
|
||||
AC_DEFINE(HAVE_EPOLL, 1,
|
||||
[Define if your system supports the epoll system calls])
|
||||
AC_LIBOBJ(epoll)
|
||||
needsignal=yes
|
||||
fi
|
||||
|
||||
havedevpoll=no
|
||||
if test "x$ac_cv_header_sys_devpoll_h" = "xyes"; then
|
||||
AC_DEFINE(HAVE_DEVPOLL, 1,
|
||||
[Define if /dev/poll is available])
|
||||
AC_LIBOBJ(devpoll)
|
||||
fi
|
||||
|
||||
havekqueue=no
|
||||
if test "x$ac_cv_header_sys_event_h" = "xyes"; then
|
||||
AC_CHECK_FUNCS(kqueue, [havekqueue=yes], )
|
||||
if test "x$havekqueue" = "xyes" ; then
|
||||
AC_MSG_CHECKING(for working kqueue)
|
||||
AC_TRY_RUN(
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/event.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int kq;
|
||||
int n;
|
||||
int fd[[2]];
|
||||
struct kevent ev;
|
||||
struct timespec ts;
|
||||
char buf[[8000]];
|
||||
|
||||
if (pipe(fd) == -1)
|
||||
exit(1);
|
||||
if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1)
|
||||
exit(1);
|
||||
|
||||
while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf))
|
||||
;
|
||||
|
||||
if ((kq = kqueue()) == -1)
|
||||
exit(1);
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.ident = fd[[1]];
|
||||
ev.filter = EVFILT_WRITE;
|
||||
ev.flags = EV_ADD | EV_ENABLE;
|
||||
n = kevent(kq, &ev, 1, NULL, 0, NULL);
|
||||
if (n == -1)
|
||||
exit(1);
|
||||
|
||||
read(fd[[0]], buf, sizeof(buf));
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
n = kevent(kq, NULL, 0, &ev, 1, &ts);
|
||||
if (n == -1 || n == 0)
|
||||
exit(1);
|
||||
|
||||
exit(0);
|
||||
}, [AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_WORKING_KQUEUE, 1,
|
||||
[Define if kqueue works correctly with pipes])
|
||||
AC_LIBOBJ(kqueue)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
|
||||
fi
|
||||
fi
|
||||
|
||||
haveepollsyscall=no
|
||||
if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then
|
||||
if test "x$haveepoll" = "xno" ; then
|
||||
AC_MSG_CHECKING(for epoll system call)
|
||||
AC_TRY_RUN(
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
epoll_create(int size)
|
||||
{
|
||||
return (syscall(__NR_epoll_create, size));
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int epfd;
|
||||
|
||||
epfd = epoll_create(256);
|
||||
exit (epfd == -1 ? 1 : 0);
|
||||
}, [AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_EPOLL, 1,
|
||||
[Define if your system supports the epoll system calls])
|
||||
needsignal=yes
|
||||
AC_LIBOBJ(epoll_sub)
|
||||
AC_LIBOBJ(epoll)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
|
||||
fi
|
||||
fi
|
||||
|
||||
haveeventports=no
|
||||
AC_CHECK_FUNCS(port_create, [haveeventports=yes], )
|
||||
if test "x$haveeventports" = "xyes" ; then
|
||||
AC_DEFINE(HAVE_EVENT_PORTS, 1,
|
||||
[Define if your system supports event ports])
|
||||
AC_LIBOBJ(evport)
|
||||
needsignal=yes
|
||||
fi
|
||||
if test "x$bwin32" = "xtrue"; then
|
||||
needsignal=yes
|
||||
fi
|
||||
if test "x$bwin32" = "xtrue"; then
|
||||
needsignal=yes
|
||||
fi
|
||||
if test "x$needsignal" = "xyes" ; then
|
||||
AC_LIBOBJ(signal)
|
||||
fi
|
||||
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_CHECK_TYPES([uint64_t, uint32_t, uint16_t, uint8_t], , ,
|
||||
[#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#elif defined(HAVE_INTTYPES_H)
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif])
|
||||
AC_CHECK_TYPES([fd_mask], , ,
|
||||
[#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SELECT_H
|
||||
#include <select.h>
|
||||
#endif])
|
||||
|
||||
AC_CHECK_SIZEOF(long long)
|
||||
AC_CHECK_SIZEOF(int)
|
||||
AC_CHECK_SIZEOF(short)
|
||||
AC_CHECK_TYPES([struct in6_addr], , ,
|
||||
[#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETINET_IN6_H
|
||||
#include <netinet/in6.h>
|
||||
#endif])
|
||||
|
||||
AC_MSG_CHECKING([for socklen_t])
|
||||
AC_TRY_COMPILE([
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],
|
||||
[socklen_t x;],
|
||||
AC_MSG_RESULT([yes]),
|
||||
[AC_MSG_RESULT([no])
|
||||
AC_DEFINE(socklen_t, unsigned int,
|
||||
[Define to unsigned int if you dont have it])]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING([whether our compiler supports __func__])
|
||||
AC_TRY_COMPILE([],
|
||||
[ const char *cp = __func__; ],
|
||||
AC_MSG_RESULT([yes]),
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_CHECKING([whether our compiler supports __FUNCTION__])
|
||||
AC_TRY_COMPILE([],
|
||||
[ const char *cp = __FUNCTION__; ],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(__func__, __FUNCTION__,
|
||||
[Define to appropriate substitue if compiler doesnt have __func__]),
|
||||
AC_MSG_RESULT([no])
|
||||
AC_DEFINE(__func__, __FILE__,
|
||||
[Define to appropriate substitue if compiler doesnt have __func__])))
|
||||
|
||||
|
||||
# Add some more warnings which we use in development but not in the
|
||||
# released versions. (Some relevant gcc versions can't handle these.)
|
||||
if test x$enable_gcc_warnings = xyes; then
|
||||
|
||||
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([], [
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 4)
|
||||
#error
|
||||
#endif]), have_gcc4=yes, have_gcc4=no)
|
||||
|
||||
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([], [
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
|
||||
#error
|
||||
#endif]), have_gcc42=yes, have_gcc42=no)
|
||||
|
||||
CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat=2 -Wwrite-strings -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wbad-function-cast -Wswitch-enum -Werror"
|
||||
CFLAGS="$CFLAGS -Wno-unused-parameter -Wno-sign-compare -Wstrict-aliasing"
|
||||
|
||||
if test x$have_gcc4 = xyes ; then
|
||||
# These warnings break gcc 3.3.5 and work on gcc 4.0.2
|
||||
CFLAGS="$CFLAGS -Winit-self -Wmissing-field-initializers -Wdeclaration-after-statement"
|
||||
#CFLAGS="$CFLAGS -Wold-style-definition"
|
||||
fi
|
||||
|
||||
if test x$have_gcc42 = xyes ; then
|
||||
# These warnings break gcc 4.0.2 and work on gcc 4.2
|
||||
CFLAGS="$CFLAGS -Waddress -Wnormalized=id -Woverride-init"
|
||||
fi
|
||||
|
||||
##This will break the world on some 64-bit architectures
|
||||
# CFLAGS="$CFLAGS -Winline"
|
||||
|
||||
fi
|
||||
|
||||
AC_OUTPUT(Makefile test/Makefile sample/Makefile)
|
||||
417
third-party/webrtc/dependencies/base/third_party/libevent/devpoll.c
vendored
Normal file
417
third-party/webrtc/dependencies/base/third_party/libevent/devpoll.c
vendored
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* Copyright 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_libevent_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/devpoll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "evsignal.h"
|
||||
#include "log.h"
|
||||
|
||||
/* due to limitations in the devpoll interface, we need to keep track of
|
||||
* all file descriptors outself.
|
||||
*/
|
||||
struct evdevpoll {
|
||||
struct event *evread;
|
||||
struct event *evwrite;
|
||||
};
|
||||
|
||||
struct devpollop {
|
||||
struct evdevpoll *fds;
|
||||
int nfds;
|
||||
struct pollfd *events;
|
||||
int nevents;
|
||||
int dpfd;
|
||||
struct pollfd *changes;
|
||||
int nchanges;
|
||||
};
|
||||
|
||||
static void *devpoll_init (struct event_base *);
|
||||
static int devpoll_add (void *, struct event *);
|
||||
static int devpoll_del (void *, struct event *);
|
||||
static int devpoll_dispatch (struct event_base *, void *, struct timeval *);
|
||||
static void devpoll_dealloc (struct event_base *, void *);
|
||||
|
||||
const struct eventop devpollops = {
|
||||
"devpoll",
|
||||
devpoll_init,
|
||||
devpoll_add,
|
||||
devpoll_del,
|
||||
devpoll_dispatch,
|
||||
devpoll_dealloc,
|
||||
1 /* need reinit */
|
||||
};
|
||||
|
||||
#define NEVENT 32000
|
||||
|
||||
static int
|
||||
devpoll_commit(struct devpollop *devpollop)
|
||||
{
|
||||
/*
|
||||
* Due to a bug in Solaris, we have to use pwrite with an offset of 0.
|
||||
* Write is limited to 2GB of data, until it will fail.
|
||||
*/
|
||||
if (pwrite(devpollop->dpfd, devpollop->changes,
|
||||
sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
|
||||
return(-1);
|
||||
|
||||
devpollop->nchanges = 0;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
devpoll_queue(struct devpollop *devpollop, int fd, int events) {
|
||||
struct pollfd *pfd;
|
||||
|
||||
if (devpollop->nchanges >= devpollop->nevents) {
|
||||
/*
|
||||
* Change buffer is full, must commit it to /dev/poll before
|
||||
* adding more
|
||||
*/
|
||||
if (devpoll_commit(devpollop) != 0)
|
||||
return(-1);
|
||||
}
|
||||
|
||||
pfd = &devpollop->changes[devpollop->nchanges++];
|
||||
pfd->fd = fd;
|
||||
pfd->events = events;
|
||||
pfd->revents = 0;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void *
|
||||
devpoll_init(struct event_base *base)
|
||||
{
|
||||
int dpfd, nfiles = NEVENT;
|
||||
struct rlimit rl;
|
||||
struct devpollop *devpollop;
|
||||
|
||||
/* Disable devpoll when this environment variable is set */
|
||||
if (evutil_getenv("EVENT_NODEVPOLL"))
|
||||
return (NULL);
|
||||
|
||||
if (!(devpollop = calloc(1, sizeof(struct devpollop))))
|
||||
return (NULL);
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
|
||||
rl.rlim_cur != RLIM_INFINITY)
|
||||
nfiles = rl.rlim_cur;
|
||||
|
||||
/* Initialize the kernel queue */
|
||||
if ((dpfd = open("/dev/poll", O_RDWR)) == -1) {
|
||||
event_warn("open: /dev/poll");
|
||||
free(devpollop);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
devpollop->dpfd = dpfd;
|
||||
|
||||
/* Initialize fields */
|
||||
devpollop->events = calloc(nfiles, sizeof(struct pollfd));
|
||||
if (devpollop->events == NULL) {
|
||||
free(devpollop);
|
||||
close(dpfd);
|
||||
return (NULL);
|
||||
}
|
||||
devpollop->nevents = nfiles;
|
||||
|
||||
devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll));
|
||||
if (devpollop->fds == NULL) {
|
||||
free(devpollop->events);
|
||||
free(devpollop);
|
||||
close(dpfd);
|
||||
return (NULL);
|
||||
}
|
||||
devpollop->nfds = nfiles;
|
||||
|
||||
devpollop->changes = calloc(nfiles, sizeof(struct pollfd));
|
||||
if (devpollop->changes == NULL) {
|
||||
free(devpollop->fds);
|
||||
free(devpollop->events);
|
||||
free(devpollop);
|
||||
close(dpfd);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
evsignal_init(base);
|
||||
|
||||
return (devpollop);
|
||||
}
|
||||
|
||||
static int
|
||||
devpoll_recalc(struct event_base *base, void *arg, int max)
|
||||
{
|
||||
struct devpollop *devpollop = arg;
|
||||
|
||||
if (max >= devpollop->nfds) {
|
||||
struct evdevpoll *fds;
|
||||
int nfds;
|
||||
|
||||
nfds = devpollop->nfds;
|
||||
while (nfds <= max)
|
||||
nfds <<= 1;
|
||||
|
||||
fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll));
|
||||
if (fds == NULL) {
|
||||
event_warn("realloc");
|
||||
return (-1);
|
||||
}
|
||||
devpollop->fds = fds;
|
||||
memset(fds + devpollop->nfds, 0,
|
||||
(nfds - devpollop->nfds) * sizeof(struct evdevpoll));
|
||||
devpollop->nfds = nfds;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
|
||||
{
|
||||
struct devpollop *devpollop = arg;
|
||||
struct pollfd *events = devpollop->events;
|
||||
struct dvpoll dvp;
|
||||
struct evdevpoll *evdp;
|
||||
int i, res, timeout = -1;
|
||||
|
||||
if (devpollop->nchanges)
|
||||
devpoll_commit(devpollop);
|
||||
|
||||
if (tv != NULL)
|
||||
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
|
||||
|
||||
dvp.dp_fds = devpollop->events;
|
||||
dvp.dp_nfds = devpollop->nevents;
|
||||
dvp.dp_timeout = timeout;
|
||||
|
||||
res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
|
||||
|
||||
if (res == -1) {
|
||||
if (errno != EINTR) {
|
||||
event_warn("ioctl: DP_POLL");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
evsignal_process(base);
|
||||
return (0);
|
||||
} else if (base->sig.evsignal_caught) {
|
||||
evsignal_process(base);
|
||||
}
|
||||
|
||||
event_debug(("%s: devpoll_wait reports %d", __func__, res));
|
||||
|
||||
for (i = 0; i < res; i++) {
|
||||
int which = 0;
|
||||
int what = events[i].revents;
|
||||
struct event *evread = NULL, *evwrite = NULL;
|
||||
|
||||
assert(events[i].fd < devpollop->nfds);
|
||||
evdp = &devpollop->fds[events[i].fd];
|
||||
|
||||
if (what & POLLHUP)
|
||||
what |= POLLIN | POLLOUT;
|
||||
else if (what & POLLERR)
|
||||
what |= POLLIN | POLLOUT;
|
||||
|
||||
if (what & POLLIN) {
|
||||
evread = evdp->evread;
|
||||
which |= EV_READ;
|
||||
}
|
||||
|
||||
if (what & POLLOUT) {
|
||||
evwrite = evdp->evwrite;
|
||||
which |= EV_WRITE;
|
||||
}
|
||||
|
||||
if (!which)
|
||||
continue;
|
||||
|
||||
if (evread != NULL && !(evread->ev_events & EV_PERSIST))
|
||||
event_del(evread);
|
||||
if (evwrite != NULL && evwrite != evread &&
|
||||
!(evwrite->ev_events & EV_PERSIST))
|
||||
event_del(evwrite);
|
||||
|
||||
if (evread != NULL)
|
||||
event_active(evread, EV_READ, 1);
|
||||
if (evwrite != NULL)
|
||||
event_active(evwrite, EV_WRITE, 1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
devpoll_add(void *arg, struct event *ev)
|
||||
{
|
||||
struct devpollop *devpollop = arg;
|
||||
struct evdevpoll *evdp;
|
||||
int fd, events;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_add(ev));
|
||||
|
||||
fd = ev->ev_fd;
|
||||
if (fd >= devpollop->nfds) {
|
||||
/* Extend the file descriptor array as necessary */
|
||||
if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1)
|
||||
return (-1);
|
||||
}
|
||||
evdp = &devpollop->fds[fd];
|
||||
|
||||
/*
|
||||
* It's not necessary to OR the existing read/write events that we
|
||||
* are currently interested in with the new event we are adding.
|
||||
* The /dev/poll driver ORs any new events with the existing events
|
||||
* that it has cached for the fd.
|
||||
*/
|
||||
|
||||
events = 0;
|
||||
if (ev->ev_events & EV_READ) {
|
||||
if (evdp->evread && evdp->evread != ev) {
|
||||
/* There is already a different read event registered */
|
||||
return(-1);
|
||||
}
|
||||
events |= POLLIN;
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
if (evdp->evwrite && evdp->evwrite != ev) {
|
||||
/* There is already a different write event registered */
|
||||
return(-1);
|
||||
}
|
||||
events |= POLLOUT;
|
||||
}
|
||||
|
||||
if (devpoll_queue(devpollop, fd, events) != 0)
|
||||
return(-1);
|
||||
|
||||
/* Update events responsible */
|
||||
if (ev->ev_events & EV_READ)
|
||||
evdp->evread = ev;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
evdp->evwrite = ev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
devpoll_del(void *arg, struct event *ev)
|
||||
{
|
||||
struct devpollop *devpollop = arg;
|
||||
struct evdevpoll *evdp;
|
||||
int fd, events;
|
||||
int needwritedelete = 1, needreaddelete = 1;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_del(ev));
|
||||
|
||||
fd = ev->ev_fd;
|
||||
if (fd >= devpollop->nfds)
|
||||
return (0);
|
||||
evdp = &devpollop->fds[fd];
|
||||
|
||||
events = 0;
|
||||
if (ev->ev_events & EV_READ)
|
||||
events |= POLLIN;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
events |= POLLOUT;
|
||||
|
||||
/*
|
||||
* The only way to remove an fd from the /dev/poll monitored set is
|
||||
* to use POLLREMOVE by itself. This removes ALL events for the fd
|
||||
* provided so if we care about two events and are only removing one
|
||||
* we must re-add the other event after POLLREMOVE.
|
||||
*/
|
||||
|
||||
if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
|
||||
return(-1);
|
||||
|
||||
if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
|
||||
/*
|
||||
* We're not deleting all events, so we must resubmit the
|
||||
* event that we are still interested in if one exists.
|
||||
*/
|
||||
|
||||
if ((events & POLLIN) && evdp->evwrite != NULL) {
|
||||
/* Deleting read, still care about write */
|
||||
devpoll_queue(devpollop, fd, POLLOUT);
|
||||
needwritedelete = 0;
|
||||
} else if ((events & POLLOUT) && evdp->evread != NULL) {
|
||||
/* Deleting write, still care about read */
|
||||
devpoll_queue(devpollop, fd, POLLIN);
|
||||
needreaddelete = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (needreaddelete)
|
||||
evdp->evread = NULL;
|
||||
if (needwritedelete)
|
||||
evdp->evwrite = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
devpoll_dealloc(struct event_base *base, void *arg)
|
||||
{
|
||||
struct devpollop *devpollop = arg;
|
||||
|
||||
evsignal_dealloc(base);
|
||||
if (devpollop->fds)
|
||||
free(devpollop->fds);
|
||||
if (devpollop->events)
|
||||
free(devpollop->events);
|
||||
if (devpollop->changes)
|
||||
free(devpollop->changes);
|
||||
if (devpollop->dpfd >= 0)
|
||||
close(devpollop->dpfd);
|
||||
|
||||
memset(devpollop, 0, sizeof(struct devpollop));
|
||||
free(devpollop);
|
||||
}
|
||||
377
third-party/webrtc/dependencies/base/third_party/libevent/epoll.c
vendored
Normal file
377
third-party/webrtc/dependencies/base/third_party/libevent/epoll.c
vendored
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_libevent_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "evsignal.h"
|
||||
#include "log.h"
|
||||
|
||||
/* due to limitations in the epoll interface, we need to keep track of
|
||||
* all file descriptors outself.
|
||||
*/
|
||||
struct evepoll {
|
||||
struct event *evread;
|
||||
struct event *evwrite;
|
||||
};
|
||||
|
||||
struct epollop {
|
||||
struct evepoll *fds;
|
||||
int nfds;
|
||||
struct epoll_event *events;
|
||||
int nevents;
|
||||
int epfd;
|
||||
};
|
||||
|
||||
static void *epoll_init (struct event_base *);
|
||||
static int epoll_add (void *, struct event *);
|
||||
static int epoll_del (void *, struct event *);
|
||||
static int epoll_dispatch (struct event_base *, void *, struct timeval *);
|
||||
static void epoll_dealloc (struct event_base *, void *);
|
||||
|
||||
const struct eventop epollops = {
|
||||
"epoll",
|
||||
epoll_init,
|
||||
epoll_add,
|
||||
epoll_del,
|
||||
epoll_dispatch,
|
||||
epoll_dealloc,
|
||||
1 /* need reinit */
|
||||
};
|
||||
|
||||
#ifdef HAVE_SETFD
|
||||
#define FD_CLOSEONEXEC(x) do { \
|
||||
if (fcntl(x, F_SETFD, 1) == -1) \
|
||||
event_warn("fcntl(%d, F_SETFD)", x); \
|
||||
} while (0)
|
||||
#else
|
||||
#define FD_CLOSEONEXEC(x)
|
||||
#endif
|
||||
|
||||
/* On Linux kernels at least up to 2.6.24.4, epoll can't handle timeout
|
||||
* values bigger than (LONG_MAX - 999ULL)/HZ. HZ in the wild can be
|
||||
* as big as 1000, and LONG_MAX can be as small as (1<<31)-1, so the
|
||||
* largest number of msec we can support here is 2147482. Let's
|
||||
* round that down by 47 seconds.
|
||||
*/
|
||||
#define MAX_EPOLL_TIMEOUT_MSEC (35*60*1000)
|
||||
|
||||
#define INITIAL_NFILES 32
|
||||
#define INITIAL_NEVENTS 32
|
||||
#define MAX_NEVENTS 4096
|
||||
|
||||
static void *
|
||||
epoll_init(struct event_base *base)
|
||||
{
|
||||
int epfd;
|
||||
struct epollop *epollop;
|
||||
|
||||
/* Disable epollueue when this environment variable is set */
|
||||
if (evutil_getenv("EVENT_NOEPOLL"))
|
||||
return (NULL);
|
||||
|
||||
/* Initalize the kernel queue */
|
||||
if ((epfd = epoll_create(32000)) == -1) {
|
||||
if (errno != ENOSYS)
|
||||
event_warn("epoll_create");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
FD_CLOSEONEXEC(epfd);
|
||||
|
||||
if (!(epollop = calloc(1, sizeof(struct epollop))))
|
||||
return (NULL);
|
||||
|
||||
epollop->epfd = epfd;
|
||||
|
||||
/* Initalize fields */
|
||||
epollop->events = malloc(INITIAL_NEVENTS * sizeof(struct epoll_event));
|
||||
if (epollop->events == NULL) {
|
||||
free(epollop);
|
||||
return (NULL);
|
||||
}
|
||||
epollop->nevents = INITIAL_NEVENTS;
|
||||
|
||||
epollop->fds = calloc(INITIAL_NFILES, sizeof(struct evepoll));
|
||||
if (epollop->fds == NULL) {
|
||||
free(epollop->events);
|
||||
free(epollop);
|
||||
return (NULL);
|
||||
}
|
||||
epollop->nfds = INITIAL_NFILES;
|
||||
|
||||
evsignal_init(base);
|
||||
|
||||
return (epollop);
|
||||
}
|
||||
|
||||
static int
|
||||
epoll_recalc(struct event_base *base, void *arg, int max)
|
||||
{
|
||||
struct epollop *epollop = arg;
|
||||
|
||||
if (max >= epollop->nfds) {
|
||||
struct evepoll *fds;
|
||||
int nfds;
|
||||
|
||||
nfds = epollop->nfds;
|
||||
while (nfds <= max)
|
||||
nfds <<= 1;
|
||||
|
||||
fds = realloc(epollop->fds, nfds * sizeof(struct evepoll));
|
||||
if (fds == NULL) {
|
||||
event_warn("realloc");
|
||||
return (-1);
|
||||
}
|
||||
epollop->fds = fds;
|
||||
memset(fds + epollop->nfds, 0,
|
||||
(nfds - epollop->nfds) * sizeof(struct evepoll));
|
||||
epollop->nfds = nfds;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
|
||||
{
|
||||
struct epollop *epollop = arg;
|
||||
struct epoll_event *events = epollop->events;
|
||||
struct evepoll *evep;
|
||||
int i, res, timeout = -1;
|
||||
|
||||
if (tv != NULL)
|
||||
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
|
||||
|
||||
if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {
|
||||
/* Linux kernels can wait forever if the timeout is too big;
|
||||
* see comment on MAX_EPOLL_TIMEOUT_MSEC. */
|
||||
timeout = MAX_EPOLL_TIMEOUT_MSEC;
|
||||
}
|
||||
|
||||
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
|
||||
|
||||
if (res == -1) {
|
||||
if (errno != EINTR) {
|
||||
event_warn("epoll_wait");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
evsignal_process(base);
|
||||
return (0);
|
||||
} else if (base->sig.evsignal_caught) {
|
||||
evsignal_process(base);
|
||||
}
|
||||
|
||||
event_debug(("%s: epoll_wait reports %d", __func__, res));
|
||||
|
||||
for (i = 0; i < res; i++) {
|
||||
int what = events[i].events;
|
||||
struct event *evread = NULL, *evwrite = NULL;
|
||||
int fd = events[i].data.fd;
|
||||
|
||||
if (fd < 0 || fd >= epollop->nfds)
|
||||
continue;
|
||||
evep = &epollop->fds[fd];
|
||||
|
||||
if (what & (EPOLLHUP|EPOLLERR)) {
|
||||
evread = evep->evread;
|
||||
evwrite = evep->evwrite;
|
||||
} else {
|
||||
if (what & EPOLLIN) {
|
||||
evread = evep->evread;
|
||||
}
|
||||
|
||||
if (what & EPOLLOUT) {
|
||||
evwrite = evep->evwrite;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(evread||evwrite))
|
||||
continue;
|
||||
|
||||
if (evread != NULL)
|
||||
event_active(evread, EV_READ, 1);
|
||||
if (evwrite != NULL)
|
||||
event_active(evwrite, EV_WRITE, 1);
|
||||
}
|
||||
|
||||
if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
|
||||
/* We used all of the event space this time. We should
|
||||
be ready for more events next time. */
|
||||
int new_nevents = epollop->nevents * 2;
|
||||
struct epoll_event *new_events;
|
||||
|
||||
new_events = realloc(epollop->events,
|
||||
new_nevents * sizeof(struct epoll_event));
|
||||
if (new_events) {
|
||||
epollop->events = new_events;
|
||||
epollop->nevents = new_nevents;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
epoll_add(void *arg, struct event *ev)
|
||||
{
|
||||
struct epollop *epollop = arg;
|
||||
struct epoll_event epev = {0, {0}};
|
||||
struct evepoll *evep;
|
||||
int fd, op, events;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_add(ev));
|
||||
|
||||
fd = ev->ev_fd;
|
||||
if (fd >= epollop->nfds) {
|
||||
/* Extent the file descriptor array as necessary */
|
||||
if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
|
||||
return (-1);
|
||||
}
|
||||
evep = &epollop->fds[fd];
|
||||
op = EPOLL_CTL_ADD;
|
||||
events = 0;
|
||||
if (evep->evread != NULL) {
|
||||
events |= EPOLLIN;
|
||||
op = EPOLL_CTL_MOD;
|
||||
}
|
||||
if (evep->evwrite != NULL) {
|
||||
events |= EPOLLOUT;
|
||||
op = EPOLL_CTL_MOD;
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_READ)
|
||||
events |= EPOLLIN;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
events |= EPOLLOUT;
|
||||
|
||||
epev.data.fd = fd;
|
||||
epev.events = events;
|
||||
if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
|
||||
return (-1);
|
||||
|
||||
/* Update events responsible */
|
||||
if (ev->ev_events & EV_READ)
|
||||
evep->evread = ev;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
evep->evwrite = ev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
epoll_del(void *arg, struct event *ev)
|
||||
{
|
||||
struct epollop *epollop = arg;
|
||||
struct epoll_event epev = {0, {0}};
|
||||
struct evepoll *evep;
|
||||
int fd, events, op;
|
||||
int needwritedelete = 1, needreaddelete = 1;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_del(ev));
|
||||
|
||||
fd = ev->ev_fd;
|
||||
if (fd >= epollop->nfds)
|
||||
return (0);
|
||||
evep = &epollop->fds[fd];
|
||||
|
||||
op = EPOLL_CTL_DEL;
|
||||
events = 0;
|
||||
|
||||
if (ev->ev_events & EV_READ)
|
||||
events |= EPOLLIN;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
events |= EPOLLOUT;
|
||||
|
||||
if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) {
|
||||
if ((events & EPOLLIN) && evep->evwrite != NULL) {
|
||||
needwritedelete = 0;
|
||||
events = EPOLLOUT;
|
||||
op = EPOLL_CTL_MOD;
|
||||
} else if ((events & EPOLLOUT) && evep->evread != NULL) {
|
||||
needreaddelete = 0;
|
||||
events = EPOLLIN;
|
||||
op = EPOLL_CTL_MOD;
|
||||
}
|
||||
}
|
||||
|
||||
epev.events = events;
|
||||
epev.data.fd = fd;
|
||||
|
||||
if (needreaddelete)
|
||||
evep->evread = NULL;
|
||||
if (needwritedelete)
|
||||
evep->evwrite = NULL;
|
||||
|
||||
if (epoll_ctl(epollop->epfd, op, fd, &epev) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
epoll_dealloc(struct event_base *base, void *arg)
|
||||
{
|
||||
struct epollop *epollop = arg;
|
||||
|
||||
evsignal_dealloc(base);
|
||||
if (epollop->fds)
|
||||
free(epollop->fds);
|
||||
if (epollop->events)
|
||||
free(epollop->events);
|
||||
if (epollop->epfd >= 0)
|
||||
close(epollop->epfd);
|
||||
|
||||
memset(epollop, 0, sizeof(struct epollop));
|
||||
free(epollop);
|
||||
}
|
||||
52
third-party/webrtc/dependencies/base/third_party/libevent/epoll_sub.c
vendored
Normal file
52
third-party/webrtc/dependencies/base/third_party/libevent/epoll_sub.c
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
epoll_create(int size)
|
||||
{
|
||||
return (syscall(__NR_epoll_create, size));
|
||||
}
|
||||
|
||||
int
|
||||
epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
|
||||
{
|
||||
|
||||
return (syscall(__NR_epoll_ctl, epfd, op, fd, event));
|
||||
}
|
||||
|
||||
int
|
||||
epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
|
||||
{
|
||||
return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout));
|
||||
}
|
||||
455
third-party/webrtc/dependencies/base/third_party/libevent/evbuffer.c
vendored
Normal file
455
third-party/webrtc/dependencies/base/third_party/libevent/evbuffer.c
vendored
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
/*
|
||||
* Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include "evutil.h"
|
||||
#include "event.h"
|
||||
|
||||
/* prototypes */
|
||||
|
||||
void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
|
||||
|
||||
static int
|
||||
bufferevent_add(struct event *ev, int timeout)
|
||||
{
|
||||
struct timeval tv, *ptv = NULL;
|
||||
|
||||
if (timeout) {
|
||||
evutil_timerclear(&tv);
|
||||
tv.tv_sec = timeout;
|
||||
ptv = &tv;
|
||||
}
|
||||
|
||||
return (event_add(ev, ptv));
|
||||
}
|
||||
|
||||
/*
|
||||
* This callback is executed when the size of the input buffer changes.
|
||||
* We use it to apply back pressure on the reading side.
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
|
||||
void *arg) {
|
||||
struct bufferevent *bufev = arg;
|
||||
/*
|
||||
* If we are below the watermark then reschedule reading if it's
|
||||
* still enabled.
|
||||
*/
|
||||
if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
|
||||
evbuffer_setcb(buf, NULL, NULL);
|
||||
|
||||
if (bufev->enabled & EV_READ)
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bufferevent_readcb(int fd, short event, void *arg)
|
||||
{
|
||||
struct bufferevent *bufev = arg;
|
||||
int res = 0;
|
||||
short what = EVBUFFER_READ;
|
||||
size_t len;
|
||||
int howmuch = -1;
|
||||
|
||||
if (event == EV_TIMEOUT) {
|
||||
what |= EVBUFFER_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a high watermark configured then we don't want to
|
||||
* read more data than would make us reach the watermark.
|
||||
*/
|
||||
if (bufev->wm_read.high != 0) {
|
||||
howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input);
|
||||
/* we might have lowered the watermark, stop reading */
|
||||
if (howmuch <= 0) {
|
||||
struct evbuffer *buf = bufev->input;
|
||||
event_del(&bufev->ev_read);
|
||||
evbuffer_setcb(buf,
|
||||
bufferevent_read_pressure_cb, bufev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res = evbuffer_read(bufev->input, fd, howmuch);
|
||||
if (res == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
goto reschedule;
|
||||
/* error case */
|
||||
what |= EVBUFFER_ERROR;
|
||||
} else if (res == 0) {
|
||||
/* eof case */
|
||||
what |= EVBUFFER_EOF;
|
||||
}
|
||||
|
||||
if (res <= 0)
|
||||
goto error;
|
||||
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
|
||||
/* See if this callbacks meets the water marks */
|
||||
len = EVBUFFER_LENGTH(bufev->input);
|
||||
if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
|
||||
return;
|
||||
if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) {
|
||||
struct evbuffer *buf = bufev->input;
|
||||
event_del(&bufev->ev_read);
|
||||
|
||||
/* Now schedule a callback for us when the buffer changes */
|
||||
evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
|
||||
}
|
||||
|
||||
/* Invoke the user callback - must always be called last */
|
||||
if (bufev->readcb != NULL)
|
||||
(*bufev->readcb)(bufev, bufev->cbarg);
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
return;
|
||||
|
||||
error:
|
||||
(*bufev->errorcb)(bufev, what, bufev->cbarg);
|
||||
}
|
||||
|
||||
static void
|
||||
bufferevent_writecb(int fd, short event, void *arg)
|
||||
{
|
||||
struct bufferevent *bufev = arg;
|
||||
int res = 0;
|
||||
short what = EVBUFFER_WRITE;
|
||||
|
||||
if (event == EV_TIMEOUT) {
|
||||
what |= EVBUFFER_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(bufev->output)) {
|
||||
res = evbuffer_write(bufev->output, fd);
|
||||
if (res == -1) {
|
||||
#ifndef WIN32
|
||||
/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
|
||||
*set errno. thus this error checking is not portable*/
|
||||
if (errno == EAGAIN ||
|
||||
errno == EINTR ||
|
||||
errno == EINPROGRESS)
|
||||
goto reschedule;
|
||||
/* error case */
|
||||
what |= EVBUFFER_ERROR;
|
||||
|
||||
#else
|
||||
goto reschedule;
|
||||
#endif
|
||||
|
||||
} else if (res == 0) {
|
||||
/* eof case */
|
||||
what |= EVBUFFER_EOF;
|
||||
}
|
||||
if (res <= 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(bufev->output) != 0)
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
|
||||
/*
|
||||
* Invoke the user callback if our buffer is drained or below the
|
||||
* low watermark.
|
||||
*/
|
||||
if (bufev->writecb != NULL &&
|
||||
EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
|
||||
(*bufev->writecb)(bufev, bufev->cbarg);
|
||||
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
if (EVBUFFER_LENGTH(bufev->output) != 0)
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
return;
|
||||
|
||||
error:
|
||||
(*bufev->errorcb)(bufev, what, bufev->cbarg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new buffered event object.
|
||||
*
|
||||
* The read callback is invoked whenever we read new data.
|
||||
* The write callback is invoked whenever the output buffer is drained.
|
||||
* The error callback is invoked on a write/read error or on EOF.
|
||||
*
|
||||
* Both read and write callbacks maybe NULL. The error callback is not
|
||||
* allowed to be NULL and have to be provided always.
|
||||
*/
|
||||
|
||||
struct bufferevent *
|
||||
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
|
||||
everrorcb errorcb, void *cbarg)
|
||||
{
|
||||
struct bufferevent *bufev;
|
||||
|
||||
if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((bufev->input = evbuffer_new()) == NULL) {
|
||||
free(bufev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((bufev->output = evbuffer_new()) == NULL) {
|
||||
evbuffer_free(bufev->input);
|
||||
free(bufev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
|
||||
event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
|
||||
|
||||
bufferevent_setcb(bufev, readcb, writecb, errorcb, cbarg);
|
||||
|
||||
/*
|
||||
* Set to EV_WRITE so that using bufferevent_write is going to
|
||||
* trigger a callback. Reading needs to be explicitly enabled
|
||||
* because otherwise no data will be available.
|
||||
*/
|
||||
bufev->enabled = EV_WRITE;
|
||||
|
||||
return (bufev);
|
||||
}
|
||||
|
||||
void
|
||||
bufferevent_setcb(struct bufferevent *bufev,
|
||||
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg)
|
||||
{
|
||||
bufev->readcb = readcb;
|
||||
bufev->writecb = writecb;
|
||||
bufev->errorcb = errorcb;
|
||||
|
||||
bufev->cbarg = cbarg;
|
||||
}
|
||||
|
||||
void
|
||||
bufferevent_setfd(struct bufferevent *bufev, int fd)
|
||||
{
|
||||
event_del(&bufev->ev_read);
|
||||
event_del(&bufev->ev_write);
|
||||
|
||||
event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
|
||||
event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
|
||||
if (bufev->ev_base != NULL) {
|
||||
event_base_set(bufev->ev_base, &bufev->ev_read);
|
||||
event_base_set(bufev->ev_base, &bufev->ev_write);
|
||||
}
|
||||
|
||||
/* might have to manually trigger event registration */
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_priority_set(struct bufferevent *bufev, int priority)
|
||||
{
|
||||
if (event_priority_set(&bufev->ev_read, priority) == -1)
|
||||
return (-1);
|
||||
if (event_priority_set(&bufev->ev_write, priority) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Closing the file descriptor is the responsibility of the caller */
|
||||
|
||||
void
|
||||
bufferevent_free(struct bufferevent *bufev)
|
||||
{
|
||||
event_del(&bufev->ev_read);
|
||||
event_del(&bufev->ev_write);
|
||||
|
||||
evbuffer_free(bufev->input);
|
||||
evbuffer_free(bufev->output);
|
||||
|
||||
free(bufev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 on success;
|
||||
* -1 on failure.
|
||||
*/
|
||||
|
||||
int
|
||||
bufferevent_write(struct bufferevent *bufev, const void *data, size_t size)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = evbuffer_add(bufev->output, data, size);
|
||||
|
||||
if (res == -1)
|
||||
return (res);
|
||||
|
||||
/* If everything is okay, we need to schedule a write */
|
||||
if (size > 0 && (bufev->enabled & EV_WRITE))
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = bufferevent_write(bufev, buf->buffer, buf->off);
|
||||
if (res != -1)
|
||||
evbuffer_drain(buf, buf->off);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
size_t
|
||||
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
|
||||
{
|
||||
struct evbuffer *buf = bufev->input;
|
||||
|
||||
if (buf->off < size)
|
||||
size = buf->off;
|
||||
|
||||
/* Copy the available data to the user buffer */
|
||||
memcpy(data, buf->buffer, size);
|
||||
|
||||
if (size)
|
||||
evbuffer_drain(buf, size);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_enable(struct bufferevent *bufev, short event)
|
||||
{
|
||||
if (event & EV_READ) {
|
||||
if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
|
||||
return (-1);
|
||||
}
|
||||
if (event & EV_WRITE) {
|
||||
if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufev->enabled |= event;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_disable(struct bufferevent *bufev, short event)
|
||||
{
|
||||
if (event & EV_READ) {
|
||||
if (event_del(&bufev->ev_read) == -1)
|
||||
return (-1);
|
||||
}
|
||||
if (event & EV_WRITE) {
|
||||
if (event_del(&bufev->ev_write) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufev->enabled &= ~event;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the read and write timeout for a buffered event.
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_settimeout(struct bufferevent *bufev,
|
||||
int timeout_read, int timeout_write) {
|
||||
bufev->timeout_read = timeout_read;
|
||||
bufev->timeout_write = timeout_write;
|
||||
|
||||
if (event_pending(&bufev->ev_read, EV_READ, NULL))
|
||||
bufferevent_add(&bufev->ev_read, timeout_read);
|
||||
if (event_pending(&bufev->ev_write, EV_WRITE, NULL))
|
||||
bufferevent_add(&bufev->ev_write, timeout_write);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the water marks
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_setwatermark(struct bufferevent *bufev, short events,
|
||||
size_t lowmark, size_t highmark)
|
||||
{
|
||||
if (events & EV_READ) {
|
||||
bufev->wm_read.low = lowmark;
|
||||
bufev->wm_read.high = highmark;
|
||||
}
|
||||
|
||||
if (events & EV_WRITE) {
|
||||
bufev->wm_write.low = lowmark;
|
||||
bufev->wm_write.high = highmark;
|
||||
}
|
||||
|
||||
/* If the watermarks changed then see if we should call read again */
|
||||
bufferevent_read_pressure_cb(bufev->input,
|
||||
0, EVBUFFER_LENGTH(bufev->input), bufev);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
|
||||
{
|
||||
int res;
|
||||
|
||||
bufev->ev_base = base;
|
||||
|
||||
res = event_base_set(base, &bufev->ev_read);
|
||||
if (res == -1)
|
||||
return (res);
|
||||
|
||||
res = event_base_set(base, &bufev->ev_write);
|
||||
return (res);
|
||||
}
|
||||
322
third-party/webrtc/dependencies/base/third_party/libevent/evdns.3
vendored
Normal file
322
third-party/webrtc/dependencies/base/third_party/libevent/evdns.3
vendored
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
.\"
|
||||
.\" Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\"
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\" 3. The name of the author may not be used to endorse or promote products
|
||||
.\" derived from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd October 7, 2006
|
||||
.Dt EVDNS 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm evdns_init
|
||||
.Nm evdns_shutdown
|
||||
.Nm evdns_err_to_string
|
||||
.Nm evdns_nameserver_add
|
||||
.Nm evdns_count_nameservers
|
||||
.Nm evdns_clear_nameservers_and_suspend
|
||||
.Nm evdns_resume
|
||||
.Nm evdns_nameserver_ip_add
|
||||
.Nm evdns_resolve_ipv4
|
||||
.Nm evdns_resolve_reverse
|
||||
.Nm evdns_resolv_conf_parse
|
||||
.Nm evdns_config_windows_nameservers
|
||||
.Nm evdns_search_clear
|
||||
.Nm evdns_search_add
|
||||
.Nm evdns_search_ndots_set
|
||||
.Nm evdns_set_log_fn
|
||||
.Nd asynchronous functions for DNS resolution.
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <sys/time.h>
|
||||
.Fd #include <event.h>
|
||||
.Fd #include <evdns.h>
|
||||
.Ft int
|
||||
.Fn evdns_init
|
||||
.Ft void
|
||||
.Fn evdns_shutdown "int fail_requests"
|
||||
.Ft "const char *"
|
||||
.Fn evdns_err_to_string "int err"
|
||||
.Ft int
|
||||
.Fn evdns_nameserver_add "unsigned long int address"
|
||||
.Ft int
|
||||
.Fn evdns_count_nameservers
|
||||
.Ft int
|
||||
.Fn evdns_clear_nameservers_and_suspend
|
||||
.Ft int
|
||||
.Fn evdns_resume
|
||||
.Ft int
|
||||
.Fn evdns_nameserver_ip_add(const char *ip_as_string);
|
||||
.Ft int
|
||||
.Fn evdns_resolve_ipv4 "const char *name" "int flags" "evdns_callback_type callback" "void *ptr"
|
||||
.Ft int
|
||||
.Fn evdns_resolve_reverse "struct in_addr *in" "int flags" "evdns_callback_type callback" "void *ptr"
|
||||
.Ft int
|
||||
.Fn evdns_resolv_conf_parse "int flags" "const char *"
|
||||
.Ft void
|
||||
.Fn evdns_search_clear
|
||||
.Ft void
|
||||
.Fn evdns_search_add "const char *domain"
|
||||
.Ft void
|
||||
.Fn evdns_search_ndots_set "const int ndots"
|
||||
.Ft void
|
||||
.Fn evdns_set_log_fn "evdns_debug_log_fn_type fn"
|
||||
.Ft int
|
||||
.Fn evdns_config_windows_nameservers
|
||||
.Sh DESCRIPTION
|
||||
Welcome, gentle reader
|
||||
.Pp
|
||||
Async DNS lookups are really a whole lot harder than they should be,
|
||||
mostly stemming from the fact that the libc resolver has never been
|
||||
very good at them. Before you use this library you should see if libc
|
||||
can do the job for you with the modern async call getaddrinfo_a
|
||||
(see http://www.imperialviolet.org/page25.html#e498). Otherwise,
|
||||
please continue.
|
||||
.Pp
|
||||
This code is based on libevent and you must call event_init before
|
||||
any of the APIs in this file. You must also seed the OpenSSL random
|
||||
source if you are using OpenSSL for ids (see below).
|
||||
.Pp
|
||||
This library is designed to be included and shipped with your source
|
||||
code. You statically link with it. You should also test for the
|
||||
existence of strtok_r and define HAVE_STRTOK_R if you have it.
|
||||
.Pp
|
||||
The DNS protocol requires a good source of id numbers and these
|
||||
numbers should be unpredictable for spoofing reasons. There are
|
||||
three methods for generating them here and you must define exactly
|
||||
one of them. In increasing order of preference:
|
||||
.Pp
|
||||
.Bl -tag -width "DNS_USE_GETTIMEOFDAY_FOR_ID" -compact -offset indent
|
||||
.It DNS_USE_GETTIMEOFDAY_FOR_ID
|
||||
Using the bottom 16 bits of the usec result from gettimeofday. This
|
||||
is a pretty poor solution but should work anywhere.
|
||||
.It DNS_USE_CPU_CLOCK_FOR_ID
|
||||
Using the bottom 16 bits of the nsec result from the CPU's time
|
||||
counter. This is better, but may not work everywhere. Requires
|
||||
POSIX realtime support and you'll need to link against -lrt on
|
||||
glibc systems at least.
|
||||
.It DNS_USE_OPENSSL_FOR_ID
|
||||
Uses the OpenSSL RAND_bytes call to generate the data. You must
|
||||
have seeded the pool before making any calls to this library.
|
||||
.El
|
||||
.Pp
|
||||
The library keeps track of the state of nameservers and will avoid
|
||||
them when they go down. Otherwise it will round robin between them.
|
||||
.Pp
|
||||
Quick start guide:
|
||||
#include "evdns.h"
|
||||
void callback(int result, char type, int count, int ttl,
|
||||
void *addresses, void *arg);
|
||||
evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
|
||||
evdns_resolve("www.hostname.com", 0, callback, NULL);
|
||||
.Pp
|
||||
When the lookup is complete the callback function is called. The
|
||||
first argument will be one of the DNS_ERR_* defines in evdns.h.
|
||||
Hopefully it will be DNS_ERR_NONE, in which case type will be
|
||||
DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
|
||||
which the data can be cached for (in seconds), addresses will point
|
||||
to an array of uint32_t's and arg will be whatever you passed to
|
||||
evdns_resolve.
|
||||
.Pp
|
||||
Searching:
|
||||
.Pp
|
||||
In order for this library to be a good replacement for glibc's resolver it
|
||||
supports searching. This involves setting a list of default domains, in
|
||||
which names will be queried for. The number of dots in the query name
|
||||
determines the order in which this list is used.
|
||||
.Pp
|
||||
Searching appears to be a single lookup from the point of view of the API,
|
||||
although many DNS queries may be generated from a single call to
|
||||
evdns_resolve. Searching can also drastically slow down the resolution
|
||||
of names.
|
||||
.Pp
|
||||
To disable searching:
|
||||
.Bl -enum -compact -offset indent
|
||||
.It
|
||||
Never set it up. If you never call
|
||||
.Fn evdns_resolv_conf_parse,
|
||||
.Fn evdns_init,
|
||||
or
|
||||
.Fn evdns_search_add
|
||||
then no searching will occur.
|
||||
.It
|
||||
If you do call
|
||||
.Fn evdns_resolv_conf_parse
|
||||
then don't pass
|
||||
.Va DNS_OPTION_SEARCH
|
||||
(or
|
||||
.Va DNS_OPTIONS_ALL,
|
||||
which implies it).
|
||||
.It
|
||||
When calling
|
||||
.Fn evdns_resolve,
|
||||
pass the
|
||||
.Va DNS_QUERY_NO_SEARCH
|
||||
flag.
|
||||
.El
|
||||
.Pp
|
||||
The order of searches depends on the number of dots in the name. If the
|
||||
number is greater than the ndots setting then the names is first tried
|
||||
globally. Otherwise each search domain is appended in turn.
|
||||
.Pp
|
||||
The ndots setting can either be set from a resolv.conf, or by calling
|
||||
evdns_search_ndots_set.
|
||||
.Pp
|
||||
For example, with ndots set to 1 (the default) and a search domain list of
|
||||
["myhome.net"]:
|
||||
Query: www
|
||||
Order: www.myhome.net, www.
|
||||
.Pp
|
||||
Query: www.abc
|
||||
Order: www.abc., www.abc.myhome.net
|
||||
.Pp
|
||||
.Sh API reference
|
||||
.Pp
|
||||
.Bl -tag -width 0123456
|
||||
.It Ft int Fn evdns_init
|
||||
Initializes support for non-blocking name resolution by calling
|
||||
.Fn evdns_resolv_conf_parse
|
||||
on UNIX and
|
||||
.Fn evdns_config_windows_nameservers
|
||||
on Windows.
|
||||
.It Ft int Fn evdns_nameserver_add "unsigned long int address"
|
||||
Add a nameserver. The address should be an IP address in
|
||||
network byte order. The type of address is chosen so that
|
||||
it matches in_addr.s_addr.
|
||||
Returns non-zero on error.
|
||||
.It Ft int Fn evdns_nameserver_ip_add "const char *ip_as_string"
|
||||
This wraps the above function by parsing a string as an IP
|
||||
address and adds it as a nameserver.
|
||||
Returns non-zero on error
|
||||
.It Ft int Fn evdns_resolve "const char *name" "int flags" "evdns_callback_type callback" "void *ptr"
|
||||
Resolve a name. The name parameter should be a DNS name.
|
||||
The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
|
||||
which disables searching for this query. (see defn of
|
||||
searching above).
|
||||
.Pp
|
||||
The callback argument is a function which is called when
|
||||
this query completes and ptr is an argument which is passed
|
||||
to that callback function.
|
||||
.Pp
|
||||
Returns non-zero on error
|
||||
.It Ft void Fn evdns_search_clear
|
||||
Clears the list of search domains
|
||||
.It Ft void Fn evdns_search_add "const char *domain"
|
||||
Add a domain to the list of search domains
|
||||
.It Ft void Fn evdns_search_ndots_set "int ndots"
|
||||
Set the number of dots which, when found in a name, causes
|
||||
the first query to be without any search domain.
|
||||
.It Ft int Fn evdns_count_nameservers "void"
|
||||
Return the number of configured nameservers (not necessarily the
|
||||
number of running nameservers). This is useful for double-checking
|
||||
whether our calls to the various nameserver configuration functions
|
||||
have been successful.
|
||||
.It Ft int Fn evdns_clear_nameservers_and_suspend "void"
|
||||
Remove all currently configured nameservers, and suspend all pending
|
||||
resolves. Resolves will not necessarily be re-attempted until
|
||||
evdns_resume() is called.
|
||||
.It Ft int Fn evdns_resume "void"
|
||||
Re-attempt resolves left in limbo after an earlier call to
|
||||
evdns_clear_nameservers_and_suspend().
|
||||
.It Ft int Fn evdns_config_windows_nameservers "void"
|
||||
Attempt to configure a set of nameservers based on platform settings on
|
||||
a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
|
||||
looks in the registry. Returns 0 on success, nonzero on failure.
|
||||
.It Ft int Fn evdns_resolv_conf_parse "int flags" "const char *filename"
|
||||
Parse a resolv.conf like file from the given filename.
|
||||
.Pp
|
||||
See the man page for resolv.conf for the format of this file.
|
||||
The flags argument determines what information is parsed from
|
||||
this file:
|
||||
.Bl -tag -width "DNS_OPTION_NAMESERVERS" -offset indent -compact -nested
|
||||
.It DNS_OPTION_SEARCH
|
||||
domain, search and ndots options
|
||||
.It DNS_OPTION_NAMESERVERS
|
||||
nameserver lines
|
||||
.It DNS_OPTION_MISC
|
||||
timeout and attempts options
|
||||
.It DNS_OPTIONS_ALL
|
||||
all of the above
|
||||
.El
|
||||
.Pp
|
||||
The following directives are not parsed from the file:
|
||||
sortlist, rotate, no-check-names, inet6, debug
|
||||
.Pp
|
||||
Returns non-zero on error:
|
||||
.Bl -tag -width "0" -offset indent -compact -nested
|
||||
.It 0
|
||||
no errors
|
||||
.It 1
|
||||
failed to open file
|
||||
.It 2
|
||||
failed to stat file
|
||||
.It 3
|
||||
file too large
|
||||
.It 4
|
||||
out of memory
|
||||
.It 5
|
||||
short read from file
|
||||
.El
|
||||
.El
|
||||
.Sh Internals:
|
||||
Requests are kept in two queues. The first is the inflight queue. In
|
||||
this queue requests have an allocated transaction id and nameserver.
|
||||
They will soon be transmitted if they haven't already been.
|
||||
.Pp
|
||||
The second is the waiting queue. The size of the inflight ring is
|
||||
limited and all other requests wait in waiting queue for space. This
|
||||
bounds the number of concurrent requests so that we don't flood the
|
||||
nameserver. Several algorithms require a full walk of the inflight
|
||||
queue and so bounding its size keeps thing going nicely under huge
|
||||
(many thousands of requests) loads.
|
||||
.Pp
|
||||
If a nameserver loses too many requests it is considered down and we
|
||||
try not to use it. After a while we send a probe to that nameserver
|
||||
(a lookup for google.com) and, if it replies, we consider it working
|
||||
again. If the nameserver fails a probe we wait longer to try again
|
||||
with the next probe.
|
||||
.Sh SEE ALSO
|
||||
.Xr event 3 ,
|
||||
.Xr gethostbyname 3 ,
|
||||
.Xr resolv.conf 5
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm evdns
|
||||
API was developed by Adam Langley on top of the
|
||||
.Nm libevent
|
||||
API.
|
||||
The code was integrate into
|
||||
.Nm Tor
|
||||
by Nick Mathewson and finally put into
|
||||
.Nm libevent
|
||||
itself by Niels Provos.
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm evdns
|
||||
API and code was written by Adam Langley with significant
|
||||
contributions by Nick Mathewson.
|
||||
.Sh BUGS
|
||||
This documentation is neither complete nor authoritative.
|
||||
If you are in doubt about the usage of this API then
|
||||
check the source code to find out how it works, write
|
||||
up the missing piece of documentation and send it to
|
||||
me for inclusion in this man page.
|
||||
3192
third-party/webrtc/dependencies/base/third_party/libevent/evdns.c
vendored
Normal file
3192
third-party/webrtc/dependencies/base/third_party/libevent/evdns.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
528
third-party/webrtc/dependencies/base/third_party/libevent/evdns.h
vendored
Normal file
528
third-party/webrtc/dependencies/base/third_party/libevent/evdns.h
vendored
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The original DNS code is due to Adam Langley with heavy
|
||||
* modifications by Nick Mathewson. Adam put his DNS software in the
|
||||
* public domain. You can find his original copyright below. Please,
|
||||
* aware that the code as part of libevent is governed by the 3-clause
|
||||
* BSD license above.
|
||||
*
|
||||
* This software is Public Domain. To view a copy of the public domain dedication,
|
||||
* visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
|
||||
* Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
|
||||
*
|
||||
* I ask and expect, but do not require, that all derivative works contain an
|
||||
* attribution similar to:
|
||||
* Parts developed by Adam Langley <agl@imperialviolet.org>
|
||||
*
|
||||
* You may wish to replace the word "Parts" with something else depending on
|
||||
* the amount of original code.
|
||||
*
|
||||
* (Derivative works does not include programs which link against, run or include
|
||||
* the source verbatim in their source distributions)
|
||||
*/
|
||||
|
||||
/** @file evdns.h
|
||||
*
|
||||
* Welcome, gentle reader
|
||||
*
|
||||
* Async DNS lookups are really a whole lot harder than they should be,
|
||||
* mostly stemming from the fact that the libc resolver has never been
|
||||
* very good at them. Before you use this library you should see if libc
|
||||
* can do the job for you with the modern async call getaddrinfo_a
|
||||
* (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
|
||||
* please continue.
|
||||
*
|
||||
* This code is based on libevent and you must call event_init before
|
||||
* any of the APIs in this file. You must also seed the OpenSSL random
|
||||
* source if you are using OpenSSL for ids (see below).
|
||||
*
|
||||
* This library is designed to be included and shipped with your source
|
||||
* code. You statically link with it. You should also test for the
|
||||
* existence of strtok_r and define HAVE_STRTOK_R if you have it.
|
||||
*
|
||||
* The DNS protocol requires a good source of id numbers and these
|
||||
* numbers should be unpredictable for spoofing reasons. There are
|
||||
* three methods for generating them here and you must define exactly
|
||||
* one of them. In increasing order of preference:
|
||||
*
|
||||
* DNS_USE_GETTIMEOFDAY_FOR_ID:
|
||||
* Using the bottom 16 bits of the usec result from gettimeofday. This
|
||||
* is a pretty poor solution but should work anywhere.
|
||||
* DNS_USE_CPU_CLOCK_FOR_ID:
|
||||
* Using the bottom 16 bits of the nsec result from the CPU's time
|
||||
* counter. This is better, but may not work everywhere. Requires
|
||||
* POSIX realtime support and you'll need to link against -lrt on
|
||||
* glibc systems at least.
|
||||
* DNS_USE_OPENSSL_FOR_ID:
|
||||
* Uses the OpenSSL RAND_bytes call to generate the data. You must
|
||||
* have seeded the pool before making any calls to this library.
|
||||
*
|
||||
* The library keeps track of the state of nameservers and will avoid
|
||||
* them when they go down. Otherwise it will round robin between them.
|
||||
*
|
||||
* Quick start guide:
|
||||
* #include "evdns.h"
|
||||
* void callback(int result, char type, int count, int ttl,
|
||||
* void *addresses, void *arg);
|
||||
* evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
|
||||
* evdns_resolve("www.hostname.com", 0, callback, NULL);
|
||||
*
|
||||
* When the lookup is complete the callback function is called. The
|
||||
* first argument will be one of the DNS_ERR_* defines in evdns.h.
|
||||
* Hopefully it will be DNS_ERR_NONE, in which case type will be
|
||||
* DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
|
||||
* which the data can be cached for (in seconds), addresses will point
|
||||
* to an array of uint32_t's and arg will be whatever you passed to
|
||||
* evdns_resolve.
|
||||
*
|
||||
* Searching:
|
||||
*
|
||||
* In order for this library to be a good replacement for glibc's resolver it
|
||||
* supports searching. This involves setting a list of default domains, in
|
||||
* which names will be queried for. The number of dots in the query name
|
||||
* determines the order in which this list is used.
|
||||
*
|
||||
* Searching appears to be a single lookup from the point of view of the API,
|
||||
* although many DNS queries may be generated from a single call to
|
||||
* evdns_resolve. Searching can also drastically slow down the resolution
|
||||
* of names.
|
||||
*
|
||||
* To disable searching:
|
||||
* 1. Never set it up. If you never call evdns_resolv_conf_parse or
|
||||
* evdns_search_add then no searching will occur.
|
||||
*
|
||||
* 2. If you do call evdns_resolv_conf_parse then don't pass
|
||||
* DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it).
|
||||
*
|
||||
* 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag.
|
||||
*
|
||||
* The order of searches depends on the number of dots in the name. If the
|
||||
* number is greater than the ndots setting then the names is first tried
|
||||
* globally. Otherwise each search domain is appended in turn.
|
||||
*
|
||||
* The ndots setting can either be set from a resolv.conf, or by calling
|
||||
* evdns_search_ndots_set.
|
||||
*
|
||||
* For example, with ndots set to 1 (the default) and a search domain list of
|
||||
* ["myhome.net"]:
|
||||
* Query: www
|
||||
* Order: www.myhome.net, www.
|
||||
*
|
||||
* Query: www.abc
|
||||
* Order: www.abc., www.abc.myhome.net
|
||||
*
|
||||
* Internals:
|
||||
*
|
||||
* Requests are kept in two queues. The first is the inflight queue. In
|
||||
* this queue requests have an allocated transaction id and nameserver.
|
||||
* They will soon be transmitted if they haven't already been.
|
||||
*
|
||||
* The second is the waiting queue. The size of the inflight ring is
|
||||
* limited and all other requests wait in waiting queue for space. This
|
||||
* bounds the number of concurrent requests so that we don't flood the
|
||||
* nameserver. Several algorithms require a full walk of the inflight
|
||||
* queue and so bounding its size keeps thing going nicely under huge
|
||||
* (many thousands of requests) loads.
|
||||
*
|
||||
* If a nameserver loses too many requests it is considered down and we
|
||||
* try not to use it. After a while we send a probe to that nameserver
|
||||
* (a lookup for google.com) and, if it replies, we consider it working
|
||||
* again. If the nameserver fails a probe we wait longer to try again
|
||||
* with the next probe.
|
||||
*/
|
||||
|
||||
#ifndef EVENTDNS_H
|
||||
#define EVENTDNS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* For integer types. */
|
||||
#include "evutil.h"
|
||||
|
||||
/** Error codes 0-5 are as described in RFC 1035. */
|
||||
#define DNS_ERR_NONE 0
|
||||
/** The name server was unable to interpret the query */
|
||||
#define DNS_ERR_FORMAT 1
|
||||
/** The name server was unable to process this query due to a problem with the
|
||||
* name server */
|
||||
#define DNS_ERR_SERVERFAILED 2
|
||||
/** The domain name does not exist */
|
||||
#define DNS_ERR_NOTEXIST 3
|
||||
/** The name server does not support the requested kind of query */
|
||||
#define DNS_ERR_NOTIMPL 4
|
||||
/** The name server refuses to reform the specified operation for policy
|
||||
* reasons */
|
||||
#define DNS_ERR_REFUSED 5
|
||||
/** The reply was truncated or ill-formated */
|
||||
#define DNS_ERR_TRUNCATED 65
|
||||
/** An unknown error occurred */
|
||||
#define DNS_ERR_UNKNOWN 66
|
||||
/** Communication with the server timed out */
|
||||
#define DNS_ERR_TIMEOUT 67
|
||||
/** The request was canceled because the DNS subsystem was shut down. */
|
||||
#define DNS_ERR_SHUTDOWN 68
|
||||
|
||||
#define DNS_IPv4_A 1
|
||||
#define DNS_PTR 2
|
||||
#define DNS_IPv6_AAAA 3
|
||||
|
||||
#define DNS_QUERY_NO_SEARCH 1
|
||||
|
||||
#define DNS_OPTION_SEARCH 1
|
||||
#define DNS_OPTION_NAMESERVERS 2
|
||||
#define DNS_OPTION_MISC 4
|
||||
#define DNS_OPTIONS_ALL 7
|
||||
|
||||
/**
|
||||
* The callback that contains the results from a lookup.
|
||||
* - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA
|
||||
* - count contains the number of addresses of form type
|
||||
* - ttl is the number of seconds the resolution may be cached for.
|
||||
* - addresses needs to be cast according to type
|
||||
*/
|
||||
typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);
|
||||
|
||||
/**
|
||||
Initialize the asynchronous DNS library.
|
||||
|
||||
This function initializes support for non-blocking name resolution by
|
||||
calling evdns_resolv_conf_parse() on UNIX and
|
||||
evdns_config_windows_nameservers() on Windows.
|
||||
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_shutdown()
|
||||
*/
|
||||
int evdns_init(void);
|
||||
|
||||
|
||||
/**
|
||||
Shut down the asynchronous DNS resolver and terminate all active requests.
|
||||
|
||||
If the 'fail_requests' option is enabled, all active requests will return
|
||||
an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise,
|
||||
the requests will be silently discarded.
|
||||
|
||||
@param fail_requests if zero, active requests will be aborted; if non-zero,
|
||||
active requests will return DNS_ERR_SHUTDOWN.
|
||||
@see evdns_init()
|
||||
*/
|
||||
void evdns_shutdown(int fail_requests);
|
||||
|
||||
|
||||
/**
|
||||
Convert a DNS error code to a string.
|
||||
|
||||
@param err the DNS error code
|
||||
@return a string containing an explanation of the error code
|
||||
*/
|
||||
const char *evdns_err_to_string(int err);
|
||||
|
||||
|
||||
/**
|
||||
Add a nameserver.
|
||||
|
||||
The address should be an IPv4 address in network byte order.
|
||||
The type of address is chosen so that it matches in_addr.s_addr.
|
||||
|
||||
@param address an IP address in network byte order
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_nameserver_ip_add()
|
||||
*/
|
||||
int evdns_nameserver_add(unsigned long int address);
|
||||
|
||||
|
||||
/**
|
||||
Get the number of configured nameservers.
|
||||
|
||||
This returns the number of configured nameservers (not necessarily the
|
||||
number of running nameservers). This is useful for double-checking
|
||||
whether our calls to the various nameserver configuration functions
|
||||
have been successful.
|
||||
|
||||
@return the number of configured nameservers
|
||||
@see evdns_nameserver_add()
|
||||
*/
|
||||
int evdns_count_nameservers(void);
|
||||
|
||||
|
||||
/**
|
||||
Remove all configured nameservers, and suspend all pending resolves.
|
||||
|
||||
Resolves will not necessarily be re-attempted until evdns_resume() is called.
|
||||
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_resume()
|
||||
*/
|
||||
int evdns_clear_nameservers_and_suspend(void);
|
||||
|
||||
|
||||
/**
|
||||
Resume normal operation and continue any suspended resolve requests.
|
||||
|
||||
Re-attempt resolves left in limbo after an earlier call to
|
||||
evdns_clear_nameservers_and_suspend().
|
||||
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_clear_nameservers_and_suspend()
|
||||
*/
|
||||
int evdns_resume(void);
|
||||
|
||||
|
||||
/**
|
||||
Add a nameserver.
|
||||
|
||||
This wraps the evdns_nameserver_add() function by parsing a string as an IP
|
||||
address and adds it as a nameserver.
|
||||
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_nameserver_add()
|
||||
*/
|
||||
int evdns_nameserver_ip_add(const char *ip_as_string);
|
||||
|
||||
|
||||
/**
|
||||
Lookup an A record for a given name.
|
||||
|
||||
@param name a DNS hostname
|
||||
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
|
||||
@param callback a callback function to invoke when the request is completed
|
||||
@param ptr an argument to pass to the callback function
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6()
|
||||
*/
|
||||
int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr);
|
||||
|
||||
|
||||
/**
|
||||
Lookup an AAAA record for a given name.
|
||||
|
||||
@param name a DNS hostname
|
||||
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
|
||||
@param callback a callback function to invoke when the request is completed
|
||||
@param ptr an argument to pass to the callback function
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6()
|
||||
*/
|
||||
int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr);
|
||||
|
||||
struct in_addr;
|
||||
struct in6_addr;
|
||||
|
||||
/**
|
||||
Lookup a PTR record for a given IP address.
|
||||
|
||||
@param in an IPv4 address
|
||||
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
|
||||
@param callback a callback function to invoke when the request is completed
|
||||
@param ptr an argument to pass to the callback function
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_resolve_reverse_ipv6()
|
||||
*/
|
||||
int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr);
|
||||
|
||||
|
||||
/**
|
||||
Lookup a PTR record for a given IPv6 address.
|
||||
|
||||
@param in an IPv6 address
|
||||
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
|
||||
@param callback a callback function to invoke when the request is completed
|
||||
@param ptr an argument to pass to the callback function
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_resolve_reverse_ipv6()
|
||||
*/
|
||||
int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr);
|
||||
|
||||
|
||||
/**
|
||||
Set the value of a configuration option.
|
||||
|
||||
The currently available configuration options are:
|
||||
|
||||
ndots, timeout, max-timeouts, max-inflight, and attempts
|
||||
|
||||
@param option the name of the configuration option to be modified
|
||||
@param val the value to be set
|
||||
@param flags either 0 | DNS_OPTION_SEARCH | DNS_OPTION_MISC
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
*/
|
||||
int evdns_set_option(const char *option, const char *val, int flags);
|
||||
|
||||
|
||||
/**
|
||||
Parse a resolv.conf file.
|
||||
|
||||
The 'flags' parameter determines what information is parsed from the
|
||||
resolv.conf file. See the man page for resolv.conf for the format of this
|
||||
file.
|
||||
|
||||
The following directives are not parsed from the file: sortlist, rotate,
|
||||
no-check-names, inet6, debug.
|
||||
|
||||
If this function encounters an error, the possible return values are: 1 =
|
||||
failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of
|
||||
memory, 5 = short read from file, 6 = no nameservers listed in the file
|
||||
|
||||
@param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC|
|
||||
DNS_OPTIONS_ALL
|
||||
@param filename the path to the resolv.conf file
|
||||
@return 0 if successful, or various positive error codes if an error
|
||||
occurred (see above)
|
||||
@see resolv.conf(3), evdns_config_windows_nameservers()
|
||||
*/
|
||||
int evdns_resolv_conf_parse(int flags, const char *const filename);
|
||||
|
||||
|
||||
/**
|
||||
Obtain nameserver information using the Windows API.
|
||||
|
||||
Attempt to configure a set of nameservers based on platform settings on
|
||||
a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
|
||||
looks in the registry.
|
||||
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
@see evdns_resolv_conf_parse()
|
||||
*/
|
||||
#ifdef WIN32
|
||||
int evdns_config_windows_nameservers(void);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
Clear the list of search domains.
|
||||
*/
|
||||
void evdns_search_clear(void);
|
||||
|
||||
|
||||
/**
|
||||
Add a domain to the list of search domains
|
||||
|
||||
@param domain the domain to be added to the search list
|
||||
*/
|
||||
void evdns_search_add(const char *domain);
|
||||
|
||||
|
||||
/**
|
||||
Set the 'ndots' parameter for searches.
|
||||
|
||||
Sets the number of dots which, when found in a name, causes
|
||||
the first query to be without any search domain.
|
||||
|
||||
@param ndots the new ndots parameter
|
||||
*/
|
||||
void evdns_search_ndots_set(const int ndots);
|
||||
|
||||
/**
|
||||
A callback that is invoked when a log message is generated
|
||||
|
||||
@param is_warning indicates if the log message is a 'warning'
|
||||
@param msg the content of the log message
|
||||
*/
|
||||
typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
|
||||
|
||||
|
||||
/**
|
||||
Set the callback function to handle log messages.
|
||||
|
||||
@param fn the callback to be invoked when a log message is generated
|
||||
*/
|
||||
void evdns_set_log_fn(evdns_debug_log_fn_type fn);
|
||||
|
||||
/**
|
||||
Set a callback that will be invoked to generate transaction IDs. By
|
||||
default, we pick transaction IDs based on the current clock time.
|
||||
|
||||
@param fn the new callback, or NULL to use the default.
|
||||
*/
|
||||
void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
|
||||
|
||||
#define DNS_NO_SEARCH 1
|
||||
|
||||
/*
|
||||
* Structures and functions used to implement a DNS server.
|
||||
*/
|
||||
|
||||
struct evdns_server_request {
|
||||
int flags;
|
||||
int nquestions;
|
||||
struct evdns_server_question **questions;
|
||||
};
|
||||
struct evdns_server_question {
|
||||
int type;
|
||||
#ifdef __cplusplus
|
||||
int dns_question_class;
|
||||
#else
|
||||
/* You should refer to this field as "dns_question_class". The
|
||||
* name "class" works in C for backward compatibility, and will be
|
||||
* removed in a future version. (1.5 or later). */
|
||||
int class;
|
||||
#define dns_question_class class
|
||||
#endif
|
||||
char name[1];
|
||||
};
|
||||
typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *);
|
||||
#define EVDNS_ANSWER_SECTION 0
|
||||
#define EVDNS_AUTHORITY_SECTION 1
|
||||
#define EVDNS_ADDITIONAL_SECTION 2
|
||||
|
||||
#define EVDNS_TYPE_A 1
|
||||
#define EVDNS_TYPE_NS 2
|
||||
#define EVDNS_TYPE_CNAME 5
|
||||
#define EVDNS_TYPE_SOA 6
|
||||
#define EVDNS_TYPE_PTR 12
|
||||
#define EVDNS_TYPE_MX 15
|
||||
#define EVDNS_TYPE_TXT 16
|
||||
#define EVDNS_TYPE_AAAA 28
|
||||
|
||||
#define EVDNS_QTYPE_AXFR 252
|
||||
#define EVDNS_QTYPE_ALL 255
|
||||
|
||||
#define EVDNS_CLASS_INET 1
|
||||
|
||||
struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data);
|
||||
void evdns_close_server_port(struct evdns_server_port *port);
|
||||
|
||||
int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char *data);
|
||||
int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
|
||||
int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl);
|
||||
int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl);
|
||||
int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl);
|
||||
|
||||
int evdns_server_request_respond(struct evdns_server_request *req, int err);
|
||||
int evdns_server_request_drop(struct evdns_server_request *req);
|
||||
struct sockaddr;
|
||||
int evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !EVENTDNS_H */
|
||||
24
third-party/webrtc/dependencies/base/third_party/libevent/event-config.h
vendored
Normal file
24
third-party/webrtc/dependencies/base/third_party/libevent/event-config.h
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file is Chromium-specific, and brings in the appropriate
|
||||
// event-config.h depending on your platform.
|
||||
|
||||
#if defined(__native_client_nonsfi__)
|
||||
#include "base/third_party/libevent/nacl_nonsfi/event-config.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "base/third_party/libevent/mac/event-config.h"
|
||||
#elif defined(ANDROID)
|
||||
#include "base/third_party/libevent/android/event-config.h"
|
||||
#elif defined(__linux__)
|
||||
#include "base/third_party/libevent/linux/event-config.h"
|
||||
#elif defined(__FreeBSD__)
|
||||
#include "base/third_party/libevent/freebsd/event-config.h"
|
||||
#elif defined(__sun)
|
||||
#include "base/third_party/libevent/solaris/event-config.h"
|
||||
#elif defined(_AIX)
|
||||
#include "base/third_party/libevent/aix/event-config.h"
|
||||
#else
|
||||
#error generate event-config.h for your platform
|
||||
#endif
|
||||
101
third-party/webrtc/dependencies/base/third_party/libevent/event-internal.h
vendored
Normal file
101
third-party/webrtc/dependencies/base/third_party/libevent/event-internal.h
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _EVENT_INTERNAL_H_
|
||||
#define _EVENT_INTERNAL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "min_heap.h"
|
||||
#include "evsignal.h"
|
||||
|
||||
struct eventop {
|
||||
const char *name;
|
||||
void *(*init)(struct event_base *);
|
||||
int (*add)(void *, struct event *);
|
||||
int (*del)(void *, struct event *);
|
||||
int (*dispatch)(struct event_base *, void *, struct timeval *);
|
||||
void (*dealloc)(struct event_base *, void *);
|
||||
/* set if we need to reinitialize the event base */
|
||||
int need_reinit;
|
||||
};
|
||||
|
||||
struct event_base {
|
||||
const struct eventop *evsel;
|
||||
void *evbase;
|
||||
int event_count; /* counts number of total events */
|
||||
int event_count_active; /* counts number of active events */
|
||||
|
||||
int event_gotterm; /* Set to terminate loop */
|
||||
int event_break; /* Set to terminate loop immediately */
|
||||
|
||||
/* active event management */
|
||||
struct event_list **activequeues;
|
||||
int nactivequeues;
|
||||
|
||||
/* signal handling info */
|
||||
struct evsignal_info sig;
|
||||
|
||||
struct event_list eventqueue;
|
||||
struct timeval event_tv;
|
||||
|
||||
struct min_heap timeheap;
|
||||
|
||||
struct timeval tv_cache;
|
||||
};
|
||||
|
||||
/* Internal use only: Functions that might be missing from <sys/queue.h> */
|
||||
#ifndef HAVE_TAILQFOREACH
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
#endif /* TAILQ_FOREACH */
|
||||
|
||||
int _evsignal_set_handler(struct event_base *base, int evsignal,
|
||||
void (*fn)(int));
|
||||
int _evsignal_restore_handler(struct event_base *base, int evsignal);
|
||||
|
||||
/* defined in evutil.c */
|
||||
const char *evutil_getenv(const char *varname);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EVENT_INTERNAL_H_ */
|
||||
624
third-party/webrtc/dependencies/base/third_party/libevent/event.3
vendored
Normal file
624
third-party/webrtc/dependencies/base/third_party/libevent/event.3
vendored
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\"
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\" 3. The name of the author may not be used to endorse or promote products
|
||||
.\" derived from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd August 8, 2000
|
||||
.Dt EVENT 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm event_init ,
|
||||
.Nm event_dispatch ,
|
||||
.Nm event_loop ,
|
||||
.Nm event_loopexit ,
|
||||
.Nm event_loopbreak ,
|
||||
.Nm event_set ,
|
||||
.Nm event_base_dispatch ,
|
||||
.Nm event_base_loop ,
|
||||
.Nm event_base_loopexit ,
|
||||
.Nm event_base_loopbreak ,
|
||||
.Nm event_base_set ,
|
||||
.Nm event_base_free ,
|
||||
.Nm event_add ,
|
||||
.Nm event_del ,
|
||||
.Nm event_once ,
|
||||
.Nm event_base_once ,
|
||||
.Nm event_pending ,
|
||||
.Nm event_initialized ,
|
||||
.Nm event_priority_init ,
|
||||
.Nm event_priority_set ,
|
||||
.Nm evtimer_set ,
|
||||
.Nm evtimer_add ,
|
||||
.Nm evtimer_del ,
|
||||
.Nm evtimer_pending ,
|
||||
.Nm evtimer_initialized ,
|
||||
.Nm signal_set ,
|
||||
.Nm signal_add ,
|
||||
.Nm signal_del ,
|
||||
.Nm signal_pending ,
|
||||
.Nm signal_initialized ,
|
||||
.Nm bufferevent_new ,
|
||||
.Nm bufferevent_free ,
|
||||
.Nm bufferevent_write ,
|
||||
.Nm bufferevent_write_buffer ,
|
||||
.Nm bufferevent_read ,
|
||||
.Nm bufferevent_enable ,
|
||||
.Nm bufferevent_disable ,
|
||||
.Nm bufferevent_settimeout ,
|
||||
.Nm bufferevent_base_set ,
|
||||
.Nm evbuffer_new ,
|
||||
.Nm evbuffer_free ,
|
||||
.Nm evbuffer_add ,
|
||||
.Nm evbuffer_add_buffer ,
|
||||
.Nm evbuffer_add_printf ,
|
||||
.Nm evbuffer_add_vprintf ,
|
||||
.Nm evbuffer_drain ,
|
||||
.Nm evbuffer_write ,
|
||||
.Nm evbuffer_read ,
|
||||
.Nm evbuffer_find ,
|
||||
.Nm evbuffer_readline ,
|
||||
.Nm evhttp_new ,
|
||||
.Nm evhttp_bind_socket ,
|
||||
.Nm evhttp_free
|
||||
.Nd execute a function when a specific event occurs
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <sys/time.h>
|
||||
.Fd #include <event.h>
|
||||
.Ft "struct event_base *"
|
||||
.Fn "event_init" "void"
|
||||
.Ft int
|
||||
.Fn "event_dispatch" "void"
|
||||
.Ft int
|
||||
.Fn "event_loop" "int flags"
|
||||
.Ft int
|
||||
.Fn "event_loopexit" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_loopbreak" "void"
|
||||
.Ft void
|
||||
.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
|
||||
.Ft int
|
||||
.Fn "event_base_dispatch" "struct event_base *base"
|
||||
.Ft int
|
||||
.Fn "event_base_loop" "struct event_base *base" "int flags"
|
||||
.Ft int
|
||||
.Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_base_loopbreak" "struct event_base *base"
|
||||
.Ft int
|
||||
.Fn "event_base_set" "struct event_base *base" "struct event *"
|
||||
.Ft void
|
||||
.Fn "event_base_free" "struct event_base *base"
|
||||
.Ft int
|
||||
.Fn "event_add" "struct event *ev" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_del" "struct event *ev"
|
||||
.Ft int
|
||||
.Fn "event_once" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_base_once" "struct event_base *base" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_initialized" "struct event *ev"
|
||||
.Ft int
|
||||
.Fn "event_priority_init" "int npriorities"
|
||||
.Ft int
|
||||
.Fn "event_priority_set" "struct event *ev" "int priority"
|
||||
.Ft void
|
||||
.Fn "evtimer_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg"
|
||||
.Ft void
|
||||
.Fn "evtimer_add" "struct event *ev" "struct timeval *"
|
||||
.Ft void
|
||||
.Fn "evtimer_del" "struct event *ev"
|
||||
.Ft int
|
||||
.Fn "evtimer_pending" "struct event *ev" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "evtimer_initialized" "struct event *ev"
|
||||
.Ft void
|
||||
.Fn "signal_set" "struct event *ev" "int signal" "void (*fn)(int, short, void *)" "void *arg"
|
||||
.Ft void
|
||||
.Fn "signal_add" "struct event *ev" "struct timeval *"
|
||||
.Ft void
|
||||
.Fn "signal_del" "struct event *ev"
|
||||
.Ft int
|
||||
.Fn "signal_pending" "struct event *ev" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "signal_initialized" "struct event *ev"
|
||||
.Ft "struct bufferevent *"
|
||||
.Fn "bufferevent_new" "int fd" "evbuffercb readcb" "evbuffercb writecb" "everrorcb" "void *cbarg"
|
||||
.Ft void
|
||||
.Fn "bufferevent_free" "struct bufferevent *bufev"
|
||||
.Ft int
|
||||
.Fn "bufferevent_write" "struct bufferevent *bufev" "void *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "bufferevent_write_buffer" "struct bufferevent *bufev" "struct evbuffer *buf"
|
||||
.Ft size_t
|
||||
.Fn "bufferevent_read" "struct bufferevent *bufev" "void *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "bufferevent_enable" "struct bufferevent *bufev" "short event"
|
||||
.Ft int
|
||||
.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event"
|
||||
.Ft void
|
||||
.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write"
|
||||
.Ft int
|
||||
.Fn "bufferevent_base_set" "struct event_base *base" "struct bufferevent *bufev"
|
||||
.Ft "struct evbuffer *"
|
||||
.Fn "evbuffer_new" "void"
|
||||
.Ft void
|
||||
.Fn "evbuffer_free" "struct evbuffer *buf"
|
||||
.Ft int
|
||||
.Fn "evbuffer_add" "struct evbuffer *buf" "const void *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src"
|
||||
.Ft int
|
||||
.Fn "evbuffer_add_printf" "struct evbuffer *buf" "const char *fmt" "..."
|
||||
.Ft int
|
||||
.Fn "evbuffer_add_vprintf" "struct evbuffer *buf" "const char *fmt" "va_list ap"
|
||||
.Ft void
|
||||
.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size"
|
||||
.Ft int
|
||||
.Fn "evbuffer_write" "struct evbuffer *buf" "int fd"
|
||||
.Ft int
|
||||
.Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size"
|
||||
.Ft "u_char *"
|
||||
.Fn "evbuffer_find" "struct evbuffer *buf" "const u_char *data" "size_t size"
|
||||
.Ft "char *"
|
||||
.Fn "evbuffer_readline" "struct evbuffer *buf"
|
||||
.Ft "struct evhttp *"
|
||||
.Fn "evhttp_new" "struct event_base *base"
|
||||
.Ft int
|
||||
.Fn "evhttp_bind_socket" "struct evhttp *http" "const char *address" "u_short port"
|
||||
.Ft "void"
|
||||
.Fn "evhttp_free" "struct evhttp *http"
|
||||
.Ft int
|
||||
.Fa (*event_sigcb)(void) ;
|
||||
.Ft volatile sig_atomic_t
|
||||
.Fa event_gotsig ;
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm event
|
||||
API provides a mechanism to execute a function when a specific event
|
||||
on a file descriptor occurs or after a given time has passed.
|
||||
.Pp
|
||||
The
|
||||
.Nm event
|
||||
API needs to be initialized with
|
||||
.Fn event_init
|
||||
before it can be used.
|
||||
.Pp
|
||||
In order to process events, an application needs to call
|
||||
.Fn event_dispatch .
|
||||
This function only returns on error, and should replace the event core
|
||||
of the application program.
|
||||
.Pp
|
||||
The function
|
||||
.Fn event_set
|
||||
prepares the event structure
|
||||
.Fa ev
|
||||
to be used in future calls to
|
||||
.Fn event_add
|
||||
and
|
||||
.Fn event_del .
|
||||
The event will be prepared to call the function specified by the
|
||||
.Fa fn
|
||||
argument with an
|
||||
.Fa int
|
||||
argument indicating the file descriptor, a
|
||||
.Fa short
|
||||
argument indicating the type of event, and a
|
||||
.Fa void *
|
||||
argument given in the
|
||||
.Fa arg
|
||||
argument.
|
||||
The
|
||||
.Fa fd
|
||||
indicates the file descriptor that should be monitored for events.
|
||||
The events can be either
|
||||
.Va EV_READ ,
|
||||
.Va EV_WRITE ,
|
||||
or both,
|
||||
indicating that an application can read or write from the file descriptor
|
||||
respectively without blocking.
|
||||
.Pp
|
||||
The function
|
||||
.Fa fn
|
||||
will be called with the file descriptor that triggered the event and
|
||||
the type of event which will be either
|
||||
.Va EV_TIMEOUT ,
|
||||
.Va EV_SIGNAL ,
|
||||
.Va EV_READ ,
|
||||
or
|
||||
.Va EV_WRITE .
|
||||
Additionally, an event which has registered interest in more than one of the
|
||||
preceeding events, via bitwise-OR to
|
||||
.Fn event_set ,
|
||||
can provide its callback function with a bitwise-OR of more than one triggered
|
||||
event.
|
||||
The additional flag
|
||||
.Va EV_PERSIST
|
||||
makes an
|
||||
.Fn event_add
|
||||
persistent until
|
||||
.Fn event_del
|
||||
has been called.
|
||||
.Pp
|
||||
Once initialized, the
|
||||
.Fa ev
|
||||
structure can be used repeatedly with
|
||||
.Fn event_add
|
||||
and
|
||||
.Fn event_del
|
||||
and does not need to be reinitialized unless the function called and/or
|
||||
the argument to it are to be changed.
|
||||
However, when an
|
||||
.Fa ev
|
||||
structure has been added to libevent using
|
||||
.Fn event_add
|
||||
the structure must persist until the event occurs (assuming
|
||||
.Fa EV_PERSIST
|
||||
is not set) or is removed
|
||||
using
|
||||
.Fn event_del .
|
||||
You may not reuse the same
|
||||
.Fa ev
|
||||
structure for multiple monitored descriptors; each descriptor
|
||||
needs its own
|
||||
.Fa ev .
|
||||
.Pp
|
||||
The function
|
||||
.Fn event_add
|
||||
schedules the execution of the
|
||||
.Fa ev
|
||||
event when the event specified in
|
||||
.Fn event_set
|
||||
occurs or in at least the time specified in the
|
||||
.Fa tv .
|
||||
If
|
||||
.Fa tv
|
||||
is
|
||||
.Dv NULL ,
|
||||
no timeout occurs and the function will only be called
|
||||
if a matching event occurs on the file descriptor.
|
||||
The event in the
|
||||
.Fa ev
|
||||
argument must be already initialized by
|
||||
.Fn event_set
|
||||
and may not be used in calls to
|
||||
.Fn event_set
|
||||
until it has timed out or been removed with
|
||||
.Fn event_del .
|
||||
If the event in the
|
||||
.Fa ev
|
||||
argument already has a scheduled timeout, the old timeout will be
|
||||
replaced by the new one.
|
||||
.Pp
|
||||
The function
|
||||
.Fn event_del
|
||||
will cancel the event in the argument
|
||||
.Fa ev .
|
||||
If the event has already executed or has never been added
|
||||
the call will have no effect.
|
||||
.Pp
|
||||
The functions
|
||||
.Fn evtimer_set ,
|
||||
.Fn evtimer_add ,
|
||||
.Fn evtimer_del ,
|
||||
.Fn evtimer_initialized ,
|
||||
and
|
||||
.Fn evtimer_pending
|
||||
are abbreviations for common situations where only a timeout is required.
|
||||
The file descriptor passed will be \-1, and the event type will be
|
||||
.Va EV_TIMEOUT .
|
||||
.Pp
|
||||
The functions
|
||||
.Fn signal_set ,
|
||||
.Fn signal_add ,
|
||||
.Fn signal_del ,
|
||||
.Fn signal_initialized ,
|
||||
and
|
||||
.Fn signal_pending
|
||||
are abbreviations.
|
||||
The event type will be a persistent
|
||||
.Va EV_SIGNAL .
|
||||
That means
|
||||
.Fn signal_set
|
||||
adds
|
||||
.Va EV_PERSIST .
|
||||
.Pp
|
||||
In order to avoid races in signal handlers, the
|
||||
.Nm event
|
||||
API provides two variables:
|
||||
.Va event_sigcb
|
||||
and
|
||||
.Va event_gotsig .
|
||||
A signal handler
|
||||
sets
|
||||
.Va event_gotsig
|
||||
to indicate that a signal has been received.
|
||||
The application sets
|
||||
.Va event_sigcb
|
||||
to a callback function.
|
||||
After the signal handler sets
|
||||
.Va event_gotsig ,
|
||||
.Nm event_dispatch
|
||||
will execute the callback function to process received signals.
|
||||
The callback returns 1 when no events are registered any more.
|
||||
It can return \-1 to indicate an error to the
|
||||
.Nm event
|
||||
library, causing
|
||||
.Fn event_dispatch
|
||||
to terminate with
|
||||
.Va errno
|
||||
set to
|
||||
.Er EINTR .
|
||||
.Pp
|
||||
The function
|
||||
.Fn event_once
|
||||
is similar to
|
||||
.Fn event_set .
|
||||
However, it schedules a callback to be called exactly once and does not
|
||||
require the caller to prepare an
|
||||
.Fa event
|
||||
structure.
|
||||
This function supports
|
||||
.Fa EV_TIMEOUT ,
|
||||
.Fa EV_READ ,
|
||||
and
|
||||
.Fa EV_WRITE .
|
||||
.Pp
|
||||
The
|
||||
.Fn event_pending
|
||||
function can be used to check if the event specified by
|
||||
.Fa event
|
||||
is pending to run.
|
||||
If
|
||||
.Va EV_TIMEOUT
|
||||
was specified and
|
||||
.Fa tv
|
||||
is not
|
||||
.Dv NULL ,
|
||||
the expiration time of the event will be returned in
|
||||
.Fa tv .
|
||||
.Pp
|
||||
The
|
||||
.Fn event_initialized
|
||||
macro can be used to check if an event has been initialized.
|
||||
.Pp
|
||||
The
|
||||
.Nm event_loop
|
||||
function provides an interface for single pass execution of pending
|
||||
events.
|
||||
The flags
|
||||
.Va EVLOOP_ONCE
|
||||
and
|
||||
.Va EVLOOP_NONBLOCK
|
||||
are recognized.
|
||||
The
|
||||
.Nm event_loopexit
|
||||
function exits from the event loop. The next
|
||||
.Fn event_loop
|
||||
iteration after the
|
||||
given timer expires will complete normally (handling all queued events) then
|
||||
exit without blocking for events again. Subsequent invocations of
|
||||
.Fn event_loop
|
||||
will proceed normally.
|
||||
The
|
||||
.Nm event_loopbreak
|
||||
function exits from the event loop immediately.
|
||||
.Fn event_loop
|
||||
will abort after the next event is completed;
|
||||
.Fn event_loopbreak
|
||||
is typically invoked from this event's callback. This behavior is analogous
|
||||
to the "break;" statement. Subsequent invocations of
|
||||
.Fn event_loop
|
||||
will proceed normally.
|
||||
.Pp
|
||||
It is the responsibility of the caller to provide these functions with
|
||||
pre-allocated event structures.
|
||||
.Pp
|
||||
.Sh EVENT PRIORITIES
|
||||
By default
|
||||
.Nm libevent
|
||||
schedules all active events with the same priority.
|
||||
However, sometimes it is desirable to process some events with a higher
|
||||
priority than others.
|
||||
For that reason,
|
||||
.Nm libevent
|
||||
supports strict priority queues.
|
||||
Active events with a lower priority are always processed before events
|
||||
with a higher priority.
|
||||
.Pp
|
||||
The number of different priorities can be set initially with the
|
||||
.Fn event_priority_init
|
||||
function.
|
||||
This function should be called before the first call to
|
||||
.Fn event_dispatch .
|
||||
The
|
||||
.Fn event_priority_set
|
||||
function can be used to assign a priority to an event.
|
||||
By default,
|
||||
.Nm libevent
|
||||
assigns the middle priority to all events unless their priority
|
||||
is explicitly set.
|
||||
.Sh THREAD SAFE EVENTS
|
||||
.Nm Libevent
|
||||
has experimental support for thread-safe events.
|
||||
When initializing the library via
|
||||
.Fn event_init ,
|
||||
an event base is returned.
|
||||
This event base can be used in conjunction with calls to
|
||||
.Fn event_base_set ,
|
||||
.Fn event_base_dispatch ,
|
||||
.Fn event_base_loop ,
|
||||
.Fn event_base_loopexit ,
|
||||
.Fn bufferevent_base_set
|
||||
and
|
||||
.Fn event_base_free .
|
||||
.Fn event_base_set
|
||||
should be called after preparing an event with
|
||||
.Fn event_set ,
|
||||
as
|
||||
.Fn event_set
|
||||
assigns the provided event to the most recently created event base.
|
||||
.Fn bufferevent_base_set
|
||||
should be called after preparing a bufferevent with
|
||||
.Fn bufferevent_new .
|
||||
.Fn event_base_free
|
||||
should be used to free memory associated with the event base
|
||||
when it is no longer needed.
|
||||
.Sh BUFFERED EVENTS
|
||||
.Nm libevent
|
||||
provides an abstraction on top of the regular event callbacks.
|
||||
This abstraction is called a
|
||||
.Va "buffered event" .
|
||||
A buffered event provides input and output buffers that get filled
|
||||
and drained automatically.
|
||||
The user of a buffered event no longer deals directly with the IO,
|
||||
but instead is reading from input and writing to output buffers.
|
||||
.Pp
|
||||
A new bufferevent is created by
|
||||
.Fn bufferevent_new .
|
||||
The parameter
|
||||
.Fa fd
|
||||
specifies the file descriptor from which data is read and written to.
|
||||
This file descriptor is not allowed to be a
|
||||
.Xr pipe 2 .
|
||||
The next three parameters are callbacks.
|
||||
The read and write callback have the following form:
|
||||
.Ft void
|
||||
.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" .
|
||||
The error callback has the following form:
|
||||
.Ft void
|
||||
.Fn "(*cb)" "struct bufferevent *bufev" "short what" "void *arg" .
|
||||
The argument is specified by the fourth parameter
|
||||
.Fa "cbarg" .
|
||||
A
|
||||
.Fa bufferevent struct
|
||||
pointer is returned on success, NULL on error.
|
||||
Both the read and the write callback may be NULL.
|
||||
The error callback has to be always provided.
|
||||
.Pp
|
||||
Once initialized, the bufferevent structure can be used repeatedly with
|
||||
bufferevent_enable() and bufferevent_disable().
|
||||
The flags parameter can be a combination of
|
||||
.Va EV_READ
|
||||
and
|
||||
.Va EV_WRITE .
|
||||
When read enabled the bufferevent will try to read from the file
|
||||
descriptor and call the read callback.
|
||||
The write callback is executed
|
||||
whenever the output buffer is drained below the write low watermark,
|
||||
which is
|
||||
.Va 0
|
||||
by default.
|
||||
.Pp
|
||||
The
|
||||
.Fn bufferevent_write
|
||||
function can be used to write data to the file descriptor.
|
||||
The data is appended to the output buffer and written to the descriptor
|
||||
automatically as it becomes available for writing.
|
||||
.Fn bufferevent_write
|
||||
returns 0 on success or \-1 on failure.
|
||||
The
|
||||
.Fn bufferevent_read
|
||||
function is used to read data from the input buffer,
|
||||
returning the amount of data read.
|
||||
.Pp
|
||||
If multiple bases are in use, bufferevent_base_set() must be called before
|
||||
enabling the bufferevent for the first time.
|
||||
.Sh NON-BLOCKING HTTP SUPPORT
|
||||
.Nm libevent
|
||||
provides a very thin HTTP layer that can be used both to host an HTTP
|
||||
server and also to make HTTP requests.
|
||||
An HTTP server can be created by calling
|
||||
.Fn evhttp_new .
|
||||
It can be bound to any port and address with the
|
||||
.Fn evhttp_bind_socket
|
||||
function.
|
||||
When the HTTP server is no longer used, it can be freed via
|
||||
.Fn evhttp_free .
|
||||
.Pp
|
||||
To be notified of HTTP requests, a user needs to register callbacks with the
|
||||
HTTP server.
|
||||
This can be done by calling
|
||||
.Fn evhttp_set_cb .
|
||||
The second argument is the URI for which a callback is being registered.
|
||||
The corresponding callback will receive an
|
||||
.Va struct evhttp_request
|
||||
object that contains all information about the request.
|
||||
.Pp
|
||||
This section does not document all the possible function calls; please
|
||||
check
|
||||
.Va event.h
|
||||
for the public interfaces.
|
||||
.Sh ADDITIONAL NOTES
|
||||
It is possible to disable support for
|
||||
.Va epoll , kqueue , devpoll , poll
|
||||
or
|
||||
.Va select
|
||||
by setting the environment variable
|
||||
.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL , EVENT_NOPOLL
|
||||
or
|
||||
.Va EVENT_NOSELECT ,
|
||||
respectively.
|
||||
By setting the environment variable
|
||||
.Va EVENT_SHOW_METHOD ,
|
||||
.Nm libevent
|
||||
displays the kernel notification method that it uses.
|
||||
.Sh RETURN VALUES
|
||||
Upon successful completion
|
||||
.Fn event_add
|
||||
and
|
||||
.Fn event_del
|
||||
return 0.
|
||||
Otherwise, \-1 is returned and the global variable errno is
|
||||
set to indicate the error.
|
||||
.Sh SEE ALSO
|
||||
.Xr kqueue 2 ,
|
||||
.Xr poll 2 ,
|
||||
.Xr select 2 ,
|
||||
.Xr evdns 3 ,
|
||||
.Xr timeout 9
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm event
|
||||
API manpage is based on the
|
||||
.Xr timeout 9
|
||||
manpage by Artur Grabowski.
|
||||
The port of
|
||||
.Nm libevent
|
||||
to Windows is due to Michael A. Davis.
|
||||
Support for real-time signals is due to Taral.
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm event
|
||||
library was written by Niels Provos.
|
||||
.Sh BUGS
|
||||
This documentation is neither complete nor authoritative.
|
||||
If you are in doubt about the usage of this API then
|
||||
check the source code to find out how it works, write
|
||||
up the missing piece of documentation and send it to
|
||||
me for inclusion in this man page.
|
||||
998
third-party/webrtc/dependencies/base/third_party/libevent/event.c
vendored
Normal file
998
third-party/webrtc/dependencies/base/third_party/libevent/event.c
vendored
Normal file
|
|
@ -0,0 +1,998 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_libevent_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "evutil.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef HAVE_EVENT_PORTS
|
||||
extern const struct eventop evportops;
|
||||
#endif
|
||||
#ifdef HAVE_SELECT
|
||||
extern const struct eventop selectops;
|
||||
#endif
|
||||
#ifdef HAVE_POLL
|
||||
extern const struct eventop pollops;
|
||||
#endif
|
||||
#ifdef HAVE_EPOLL
|
||||
extern const struct eventop epollops;
|
||||
#endif
|
||||
#ifdef HAVE_WORKING_KQUEUE
|
||||
extern const struct eventop kqops;
|
||||
#endif
|
||||
#ifdef HAVE_DEVPOLL
|
||||
extern const struct eventop devpollops;
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
extern const struct eventop win32ops;
|
||||
#endif
|
||||
|
||||
/* In order of preference */
|
||||
static const struct eventop *eventops[] = {
|
||||
#ifdef HAVE_EVENT_PORTS
|
||||
&evportops,
|
||||
#endif
|
||||
#ifdef HAVE_WORKING_KQUEUE
|
||||
&kqops,
|
||||
#endif
|
||||
#ifdef HAVE_EPOLL
|
||||
&epollops,
|
||||
#endif
|
||||
#ifdef HAVE_DEVPOLL
|
||||
&devpollops,
|
||||
#endif
|
||||
#ifdef HAVE_POLL
|
||||
&pollops,
|
||||
#endif
|
||||
#ifdef HAVE_SELECT
|
||||
&selectops,
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
&win32ops,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Global state */
|
||||
struct event_base *current_base = NULL;
|
||||
extern struct event_base *evsignal_base;
|
||||
static int use_monotonic = 1;
|
||||
|
||||
/* Prototypes */
|
||||
static void event_queue_insert(struct event_base *, struct event *, int);
|
||||
static void event_queue_remove(struct event_base *, struct event *, int);
|
||||
static int event_haveevents(struct event_base *);
|
||||
|
||||
static void event_process_active(struct event_base *);
|
||||
|
||||
static int timeout_next(struct event_base *, struct timeval **);
|
||||
static void timeout_process(struct event_base *);
|
||||
static void timeout_correct(struct event_base *, struct timeval *);
|
||||
|
||||
static int
|
||||
gettime(struct event_base *base, struct timeval *tp)
|
||||
{
|
||||
if (base->tv_cache.tv_sec) {
|
||||
*tp = base->tv_cache;
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||
struct timespec ts;
|
||||
|
||||
if (use_monotonic &&
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
|
||||
tp->tv_sec = ts.tv_sec;
|
||||
tp->tv_usec = ts.tv_nsec / 1000;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
use_monotonic = 0;
|
||||
|
||||
return (evutil_gettimeofday(tp, NULL));
|
||||
}
|
||||
|
||||
struct event_base *
|
||||
event_init(void)
|
||||
{
|
||||
struct event_base *base = event_base_new();
|
||||
|
||||
if (base != NULL)
|
||||
current_base = base;
|
||||
|
||||
return (base);
|
||||
}
|
||||
|
||||
struct event_base *
|
||||
event_base_new(void)
|
||||
{
|
||||
int i;
|
||||
struct event_base *base;
|
||||
|
||||
if ((base = calloc(1, sizeof(struct event_base))) == NULL)
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
|
||||
gettime(base, &base->event_tv);
|
||||
|
||||
min_heap_ctor(&base->timeheap);
|
||||
TAILQ_INIT(&base->eventqueue);
|
||||
base->sig.ev_signal_pair[0] = -1;
|
||||
base->sig.ev_signal_pair[1] = -1;
|
||||
|
||||
base->evbase = NULL;
|
||||
for (i = 0; eventops[i] && !base->evbase; i++) {
|
||||
base->evsel = eventops[i];
|
||||
|
||||
base->evbase = base->evsel->init(base);
|
||||
}
|
||||
|
||||
if (base->evbase == NULL)
|
||||
event_errx(1, "%s: no event mechanism available", __func__);
|
||||
|
||||
if (evutil_getenv("EVENT_SHOW_METHOD"))
|
||||
event_msgx("libevent using: %s\n",
|
||||
base->evsel->name);
|
||||
|
||||
/* allocate a single active event queue */
|
||||
event_base_priority_init(base, 1);
|
||||
|
||||
return (base);
|
||||
}
|
||||
|
||||
void
|
||||
event_base_free(struct event_base *base)
|
||||
{
|
||||
int i, n_deleted=0;
|
||||
struct event *ev;
|
||||
|
||||
if (base == NULL && current_base)
|
||||
base = current_base;
|
||||
if (base == current_base)
|
||||
current_base = NULL;
|
||||
|
||||
/* XXX(niels) - check for internal events first */
|
||||
assert(base);
|
||||
/* Delete all non-internal events. */
|
||||
for (ev = TAILQ_FIRST(&base->eventqueue); ev; ) {
|
||||
struct event *next = TAILQ_NEXT(ev, ev_next);
|
||||
if (!(ev->ev_flags & EVLIST_INTERNAL)) {
|
||||
event_del(ev);
|
||||
++n_deleted;
|
||||
}
|
||||
ev = next;
|
||||
}
|
||||
while ((ev = min_heap_top(&base->timeheap)) != NULL) {
|
||||
event_del(ev);
|
||||
++n_deleted;
|
||||
}
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
for (ev = TAILQ_FIRST(base->activequeues[i]); ev; ) {
|
||||
struct event *next = TAILQ_NEXT(ev, ev_active_next);
|
||||
if (!(ev->ev_flags & EVLIST_INTERNAL)) {
|
||||
event_del(ev);
|
||||
++n_deleted;
|
||||
}
|
||||
ev = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_deleted)
|
||||
event_debug(("%s: %d events were still set in base",
|
||||
__func__, n_deleted));
|
||||
|
||||
if (base->evsel->dealloc != NULL)
|
||||
base->evsel->dealloc(base, base->evbase);
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i)
|
||||
assert(TAILQ_EMPTY(base->activequeues[i]));
|
||||
|
||||
assert(min_heap_empty(&base->timeheap));
|
||||
min_heap_dtor(&base->timeheap);
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i)
|
||||
free(base->activequeues[i]);
|
||||
free(base->activequeues);
|
||||
|
||||
assert(TAILQ_EMPTY(&base->eventqueue));
|
||||
|
||||
free(base);
|
||||
}
|
||||
|
||||
/* reinitialized the event base after a fork */
|
||||
int
|
||||
event_reinit(struct event_base *base)
|
||||
{
|
||||
const struct eventop *evsel = base->evsel;
|
||||
void *evbase = base->evbase;
|
||||
int res = 0;
|
||||
struct event *ev;
|
||||
|
||||
#if 0
|
||||
/* Right now, reinit always takes effect, since even if the
|
||||
backend doesn't require it, the signal socketpair code does.
|
||||
*/
|
||||
/* check if this event mechanism requires reinit */
|
||||
if (!evsel->need_reinit)
|
||||
return (0);
|
||||
#endif
|
||||
|
||||
/* prevent internal delete */
|
||||
if (base->sig.ev_signal_added) {
|
||||
/* we cannot call event_del here because the base has
|
||||
* not been reinitialized yet. */
|
||||
event_queue_remove(base, &base->sig.ev_signal,
|
||||
EVLIST_INSERTED);
|
||||
if (base->sig.ev_signal.ev_flags & EVLIST_ACTIVE)
|
||||
event_queue_remove(base, &base->sig.ev_signal,
|
||||
EVLIST_ACTIVE);
|
||||
base->sig.ev_signal_added = 0;
|
||||
}
|
||||
|
||||
if (base->evsel->dealloc != NULL)
|
||||
base->evsel->dealloc(base, base->evbase);
|
||||
evbase = base->evbase = evsel->init(base);
|
||||
if (base->evbase == NULL)
|
||||
event_errx(1, "%s: could not reinitialize event mechanism",
|
||||
__func__);
|
||||
|
||||
TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
|
||||
if (evsel->add(evbase, ev) == -1)
|
||||
res = -1;
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
event_priority_init(int npriorities)
|
||||
{
|
||||
return event_base_priority_init(current_base, npriorities);
|
||||
}
|
||||
|
||||
int
|
||||
event_base_priority_init(struct event_base *base, int npriorities)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (base->event_count_active)
|
||||
return (-1);
|
||||
|
||||
if (npriorities == base->nactivequeues)
|
||||
return (0);
|
||||
|
||||
if (base->nactivequeues) {
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
free(base->activequeues[i]);
|
||||
}
|
||||
free(base->activequeues);
|
||||
}
|
||||
|
||||
/* Allocate our priority queues */
|
||||
base->nactivequeues = npriorities;
|
||||
base->activequeues = (struct event_list **)
|
||||
calloc(base->nactivequeues, sizeof(struct event_list *));
|
||||
if (base->activequeues == NULL)
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
base->activequeues[i] = malloc(sizeof(struct event_list));
|
||||
if (base->activequeues[i] == NULL)
|
||||
event_err(1, "%s: malloc", __func__);
|
||||
TAILQ_INIT(base->activequeues[i]);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
event_haveevents(struct event_base *base)
|
||||
{
|
||||
return (base->event_count > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Active events are stored in priority queues. Lower priorities are always
|
||||
* process before higher priorities. Low priority events can starve high
|
||||
* priority ones.
|
||||
*/
|
||||
|
||||
static void
|
||||
event_process_active(struct event_base *base)
|
||||
{
|
||||
struct event *ev;
|
||||
struct event_list *activeq = NULL;
|
||||
int i;
|
||||
short ncalls;
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
|
||||
activeq = base->activequeues[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(activeq != NULL);
|
||||
|
||||
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
|
||||
if (ev->ev_events & EV_PERSIST)
|
||||
event_queue_remove(base, ev, EVLIST_ACTIVE);
|
||||
else
|
||||
event_del(ev);
|
||||
|
||||
/* Allows deletes to work */
|
||||
ncalls = ev->ev_ncalls;
|
||||
ev->ev_pncalls = &ncalls;
|
||||
while (ncalls) {
|
||||
ncalls--;
|
||||
ev->ev_ncalls = ncalls;
|
||||
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
|
||||
if (base->event_break)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait continously for events. We exit only if no events are left.
|
||||
*/
|
||||
|
||||
int
|
||||
event_dispatch(void)
|
||||
{
|
||||
return (event_loop(0));
|
||||
}
|
||||
|
||||
int
|
||||
event_base_dispatch(struct event_base *event_base)
|
||||
{
|
||||
return (event_base_loop(event_base, 0));
|
||||
}
|
||||
|
||||
const char *
|
||||
event_base_get_method(struct event_base *base)
|
||||
{
|
||||
assert(base);
|
||||
return (base->evsel->name);
|
||||
}
|
||||
|
||||
static void
|
||||
event_loopexit_cb(int fd, short what, void *arg)
|
||||
{
|
||||
struct event_base *base = arg;
|
||||
base->event_gotterm = 1;
|
||||
}
|
||||
|
||||
/* not thread safe */
|
||||
int
|
||||
event_loopexit(const struct timeval *tv)
|
||||
{
|
||||
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
|
||||
current_base, tv));
|
||||
}
|
||||
|
||||
int
|
||||
event_base_loopexit(struct event_base *event_base, const struct timeval *tv)
|
||||
{
|
||||
return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb,
|
||||
event_base, tv));
|
||||
}
|
||||
|
||||
/* not thread safe */
|
||||
int
|
||||
event_loopbreak(void)
|
||||
{
|
||||
return (event_base_loopbreak(current_base));
|
||||
}
|
||||
|
||||
int
|
||||
event_base_loopbreak(struct event_base *event_base)
|
||||
{
|
||||
if (event_base == NULL)
|
||||
return (-1);
|
||||
|
||||
event_base->event_break = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* not thread safe */
|
||||
|
||||
int
|
||||
event_loop(int flags)
|
||||
{
|
||||
return event_base_loop(current_base, flags);
|
||||
}
|
||||
|
||||
int
|
||||
event_base_loop(struct event_base *base, int flags)
|
||||
{
|
||||
const struct eventop *evsel = base->evsel;
|
||||
void *evbase = base->evbase;
|
||||
struct timeval tv;
|
||||
struct timeval *tv_p;
|
||||
int res, done;
|
||||
|
||||
/* clear time cache */
|
||||
base->tv_cache.tv_sec = 0;
|
||||
|
||||
if (base->sig.ev_signal_added)
|
||||
evsignal_base = base;
|
||||
done = 0;
|
||||
while (!done) {
|
||||
/* Terminate the loop if we have been asked to */
|
||||
if (base->event_gotterm) {
|
||||
base->event_gotterm = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (base->event_break) {
|
||||
base->event_break = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
timeout_correct(base, &tv);
|
||||
|
||||
tv_p = &tv;
|
||||
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
|
||||
timeout_next(base, &tv_p);
|
||||
} else {
|
||||
/*
|
||||
* if we have active events, we just poll new events
|
||||
* without waiting.
|
||||
*/
|
||||
evutil_timerclear(&tv);
|
||||
}
|
||||
|
||||
/* If we have no events, we just exit */
|
||||
if (!event_haveevents(base)) {
|
||||
event_debug(("%s: no events registered.", __func__));
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* update last old time */
|
||||
gettime(base, &base->event_tv);
|
||||
|
||||
/* clear time cache */
|
||||
base->tv_cache.tv_sec = 0;
|
||||
|
||||
res = evsel->dispatch(base, evbase, tv_p);
|
||||
|
||||
if (res == -1)
|
||||
return (-1);
|
||||
gettime(base, &base->tv_cache);
|
||||
|
||||
timeout_process(base);
|
||||
|
||||
if (base->event_count_active) {
|
||||
event_process_active(base);
|
||||
if (!base->event_count_active && (flags & EVLOOP_ONCE))
|
||||
done = 1;
|
||||
} else if (flags & EVLOOP_NONBLOCK)
|
||||
done = 1;
|
||||
}
|
||||
|
||||
/* clear time cache */
|
||||
base->tv_cache.tv_sec = 0;
|
||||
|
||||
event_debug(("%s: asked to terminate loop.", __func__));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Sets up an event for processing once */
|
||||
|
||||
struct event_once {
|
||||
struct event ev;
|
||||
|
||||
void (*cb)(int, short, void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/* One-time callback, it deletes itself */
|
||||
|
||||
static void
|
||||
event_once_cb(int fd, short events, void *arg)
|
||||
{
|
||||
struct event_once *eonce = arg;
|
||||
|
||||
(*eonce->cb)(fd, events, eonce->arg);
|
||||
free(eonce);
|
||||
}
|
||||
|
||||
/* not threadsafe, event scheduled once. */
|
||||
int
|
||||
event_once(int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg, const struct timeval *tv)
|
||||
{
|
||||
return event_base_once(current_base, fd, events, callback, arg, tv);
|
||||
}
|
||||
|
||||
/* Schedules an event once */
|
||||
int
|
||||
event_base_once(struct event_base *base, int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg, const struct timeval *tv)
|
||||
{
|
||||
struct event_once *eonce;
|
||||
struct timeval etv;
|
||||
int res;
|
||||
|
||||
/* We cannot support signals that just fire once */
|
||||
if (events & EV_SIGNAL)
|
||||
return (-1);
|
||||
|
||||
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
|
||||
return (-1);
|
||||
|
||||
eonce->cb = callback;
|
||||
eonce->arg = arg;
|
||||
|
||||
if (events == EV_TIMEOUT) {
|
||||
if (tv == NULL) {
|
||||
evutil_timerclear(&etv);
|
||||
tv = &etv;
|
||||
}
|
||||
|
||||
evtimer_set(&eonce->ev, event_once_cb, eonce);
|
||||
} else if (events & (EV_READ|EV_WRITE)) {
|
||||
events &= EV_READ|EV_WRITE;
|
||||
|
||||
event_set(&eonce->ev, fd, events, event_once_cb, eonce);
|
||||
} else {
|
||||
/* Bad event combination */
|
||||
free(eonce);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
res = event_base_set(base, &eonce->ev);
|
||||
if (res == 0)
|
||||
res = event_add(&eonce->ev, tv);
|
||||
if (res != 0) {
|
||||
free(eonce);
|
||||
return (res);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
event_set(struct event *ev, int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg)
|
||||
{
|
||||
/* Take the current base - caller needs to set the real base later */
|
||||
ev->ev_base = current_base;
|
||||
|
||||
ev->ev_callback = callback;
|
||||
ev->ev_arg = arg;
|
||||
ev->ev_fd = fd;
|
||||
ev->ev_events = events;
|
||||
ev->ev_res = 0;
|
||||
ev->ev_flags = EVLIST_INIT;
|
||||
ev->ev_ncalls = 0;
|
||||
ev->ev_pncalls = NULL;
|
||||
|
||||
min_heap_elem_init(ev);
|
||||
|
||||
/* by default, we put new events into the middle priority */
|
||||
if(current_base)
|
||||
ev->ev_pri = current_base->nactivequeues/2;
|
||||
}
|
||||
|
||||
int
|
||||
event_base_set(struct event_base *base, struct event *ev)
|
||||
{
|
||||
/* Only innocent events may be assigned to a different base */
|
||||
if (ev->ev_flags != EVLIST_INIT)
|
||||
return (-1);
|
||||
|
||||
ev->ev_base = base;
|
||||
ev->ev_pri = base->nactivequeues/2;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set's the priority of an event - if an event is already scheduled
|
||||
* changing the priority is going to fail.
|
||||
*/
|
||||
|
||||
int
|
||||
event_priority_set(struct event *ev, int pri)
|
||||
{
|
||||
if (ev->ev_flags & EVLIST_ACTIVE)
|
||||
return (-1);
|
||||
if (pri < 0 || pri >= ev->ev_base->nactivequeues)
|
||||
return (-1);
|
||||
|
||||
ev->ev_pri = pri;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if a specific event is pending or scheduled.
|
||||
*/
|
||||
|
||||
int
|
||||
event_pending(struct event *ev, short event, struct timeval *tv)
|
||||
{
|
||||
struct timeval now, res;
|
||||
int flags = 0;
|
||||
|
||||
if (ev->ev_flags & EVLIST_INSERTED)
|
||||
flags |= (ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL));
|
||||
if (ev->ev_flags & EVLIST_ACTIVE)
|
||||
flags |= ev->ev_res;
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
flags |= EV_TIMEOUT;
|
||||
|
||||
event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
|
||||
|
||||
/* See if there is a timeout that we should report */
|
||||
if (tv != NULL && (flags & event & EV_TIMEOUT)) {
|
||||
gettime(ev->ev_base, &now);
|
||||
evutil_timersub(&ev->ev_timeout, &now, &res);
|
||||
/* correctly remap to real time */
|
||||
evutil_gettimeofday(&now, NULL);
|
||||
evutil_timeradd(&now, &res, tv);
|
||||
}
|
||||
|
||||
return (flags & event);
|
||||
}
|
||||
|
||||
int
|
||||
event_add(struct event *ev, const struct timeval *tv)
|
||||
{
|
||||
struct event_base *base = ev->ev_base;
|
||||
const struct eventop *evsel = base->evsel;
|
||||
void *evbase = base->evbase;
|
||||
int res = 0;
|
||||
|
||||
event_debug((
|
||||
"event_add: event: %p, %s%s%scall %p",
|
||||
ev,
|
||||
ev->ev_events & EV_READ ? "EV_READ " : " ",
|
||||
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
|
||||
tv ? "EV_TIMEOUT " : " ",
|
||||
ev->ev_callback));
|
||||
|
||||
assert(!(ev->ev_flags & ~EVLIST_ALL));
|
||||
|
||||
/*
|
||||
* prepare for timeout insertion further below, if we get a
|
||||
* failure on any step, we should not change any state.
|
||||
*/
|
||||
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
|
||||
if (min_heap_reserve(&base->timeheap,
|
||||
1 + min_heap_size(&base->timeheap)) == -1)
|
||||
return (-1); /* ENOMEM == errno */
|
||||
}
|
||||
|
||||
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
|
||||
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
|
||||
res = evsel->add(evbase, ev);
|
||||
if (res != -1)
|
||||
event_queue_insert(base, ev, EVLIST_INSERTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* we should change the timout state only if the previous event
|
||||
* addition succeeded.
|
||||
*/
|
||||
if (res != -1 && tv != NULL) {
|
||||
struct timeval now;
|
||||
|
||||
/*
|
||||
* we already reserved memory above for the case where we
|
||||
* are not replacing an exisiting timeout.
|
||||
*/
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
event_queue_remove(base, ev, EVLIST_TIMEOUT);
|
||||
|
||||
/* Check if it is active due to a timeout. Rescheduling
|
||||
* this timeout before the callback can be executed
|
||||
* removes it from the active list. */
|
||||
if ((ev->ev_flags & EVLIST_ACTIVE) &&
|
||||
(ev->ev_res & EV_TIMEOUT)) {
|
||||
/* See if we are just active executing this
|
||||
* event in a loop
|
||||
*/
|
||||
if (ev->ev_ncalls && ev->ev_pncalls) {
|
||||
/* Abort loop */
|
||||
*ev->ev_pncalls = 0;
|
||||
}
|
||||
|
||||
event_queue_remove(base, ev, EVLIST_ACTIVE);
|
||||
}
|
||||
|
||||
gettime(base, &now);
|
||||
evutil_timeradd(&now, tv, &ev->ev_timeout);
|
||||
|
||||
event_debug((
|
||||
"event_add: timeout in %ld seconds, call %p",
|
||||
tv->tv_sec, ev->ev_callback));
|
||||
|
||||
event_queue_insert(base, ev, EVLIST_TIMEOUT);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
event_del(struct event *ev)
|
||||
{
|
||||
struct event_base *base;
|
||||
|
||||
event_debug(("event_del: %p, callback %p",
|
||||
ev, ev->ev_callback));
|
||||
|
||||
/* An event without a base has not been added */
|
||||
if (ev->ev_base == NULL)
|
||||
return (-1);
|
||||
|
||||
base = ev->ev_base;
|
||||
|
||||
assert(!(ev->ev_flags & ~EVLIST_ALL));
|
||||
|
||||
/* See if we are just active executing this event in a loop */
|
||||
if (ev->ev_ncalls && ev->ev_pncalls) {
|
||||
/* Abort loop */
|
||||
*ev->ev_pncalls = 0;
|
||||
}
|
||||
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
event_queue_remove(base, ev, EVLIST_TIMEOUT);
|
||||
|
||||
if (ev->ev_flags & EVLIST_ACTIVE)
|
||||
event_queue_remove(base, ev, EVLIST_ACTIVE);
|
||||
|
||||
if (ev->ev_flags & EVLIST_INSERTED) {
|
||||
event_queue_remove(base, ev, EVLIST_INSERTED);
|
||||
return (base->evsel->del(base->evbase, ev));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
event_active(struct event *ev, int res, short ncalls)
|
||||
{
|
||||
/* We get different kinds of events, add them together */
|
||||
if (ev->ev_flags & EVLIST_ACTIVE) {
|
||||
ev->ev_res |= res;
|
||||
return;
|
||||
}
|
||||
|
||||
ev->ev_res = res;
|
||||
ev->ev_ncalls = ncalls;
|
||||
ev->ev_pncalls = NULL;
|
||||
event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
|
||||
}
|
||||
|
||||
static int
|
||||
timeout_next(struct event_base *base, struct timeval **tv_p)
|
||||
{
|
||||
struct timeval now;
|
||||
struct event *ev;
|
||||
struct timeval *tv = *tv_p;
|
||||
|
||||
if ((ev = min_heap_top(&base->timeheap)) == NULL) {
|
||||
/* if no time-based events are active wait for I/O */
|
||||
*tv_p = NULL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (gettime(base, &now) == -1)
|
||||
return (-1);
|
||||
|
||||
if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {
|
||||
evutil_timerclear(tv);
|
||||
return (0);
|
||||
}
|
||||
|
||||
evutil_timersub(&ev->ev_timeout, &now, tv);
|
||||
|
||||
assert(tv->tv_sec >= 0);
|
||||
assert(tv->tv_usec >= 0);
|
||||
|
||||
event_debug(("timeout_next: in %ld seconds", tv->tv_sec));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the time is running backwards by comparing the current
|
||||
* time against the last time we checked. Not needed when using clock
|
||||
* monotonic.
|
||||
*/
|
||||
|
||||
static void
|
||||
timeout_correct(struct event_base *base, struct timeval *tv)
|
||||
{
|
||||
struct event **pev;
|
||||
unsigned int size;
|
||||
struct timeval off;
|
||||
|
||||
if (use_monotonic)
|
||||
return;
|
||||
|
||||
/* Check if time is running backwards */
|
||||
gettime(base, tv);
|
||||
if (evutil_timercmp(tv, &base->event_tv, >=)) {
|
||||
base->event_tv = *tv;
|
||||
return;
|
||||
}
|
||||
|
||||
event_debug(("%s: time is running backwards, corrected",
|
||||
__func__));
|
||||
evutil_timersub(&base->event_tv, tv, &off);
|
||||
|
||||
/*
|
||||
* We can modify the key element of the node without destroying
|
||||
* the key, beause we apply it to all in the right order.
|
||||
*/
|
||||
pev = base->timeheap.p;
|
||||
size = base->timeheap.n;
|
||||
for (; size-- > 0; ++pev) {
|
||||
struct timeval *ev_tv = &(**pev).ev_timeout;
|
||||
evutil_timersub(ev_tv, &off, ev_tv);
|
||||
}
|
||||
/* Now remember what the new time turned out to be. */
|
||||
base->event_tv = *tv;
|
||||
}
|
||||
|
||||
void
|
||||
timeout_process(struct event_base *base)
|
||||
{
|
||||
struct timeval now;
|
||||
struct event *ev;
|
||||
|
||||
if (min_heap_empty(&base->timeheap))
|
||||
return;
|
||||
|
||||
gettime(base, &now);
|
||||
|
||||
while ((ev = min_heap_top(&base->timeheap))) {
|
||||
if (evutil_timercmp(&ev->ev_timeout, &now, >))
|
||||
break;
|
||||
|
||||
/* delete this event from the I/O queues */
|
||||
event_del(ev);
|
||||
|
||||
event_debug(("timeout_process: call %p",
|
||||
ev->ev_callback));
|
||||
event_active(ev, EV_TIMEOUT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
event_queue_remove(struct event_base *base, struct event *ev, int queue)
|
||||
{
|
||||
if (!(ev->ev_flags & queue))
|
||||
event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
|
||||
ev, ev->ev_fd, queue);
|
||||
|
||||
if (~ev->ev_flags & EVLIST_INTERNAL)
|
||||
base->event_count--;
|
||||
|
||||
ev->ev_flags &= ~queue;
|
||||
switch (queue) {
|
||||
case EVLIST_INSERTED:
|
||||
TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
|
||||
break;
|
||||
case EVLIST_ACTIVE:
|
||||
base->event_count_active--;
|
||||
TAILQ_REMOVE(base->activequeues[ev->ev_pri],
|
||||
ev, ev_active_next);
|
||||
break;
|
||||
case EVLIST_TIMEOUT:
|
||||
min_heap_erase(&base->timeheap, ev);
|
||||
break;
|
||||
default:
|
||||
event_errx(1, "%s: unknown queue %x", __func__, queue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
event_queue_insert(struct event_base *base, struct event *ev, int queue)
|
||||
{
|
||||
if (ev->ev_flags & queue) {
|
||||
/* Double insertion is possible for active events */
|
||||
if (queue & EVLIST_ACTIVE)
|
||||
return;
|
||||
|
||||
event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
|
||||
ev, ev->ev_fd, queue);
|
||||
}
|
||||
|
||||
if (~ev->ev_flags & EVLIST_INTERNAL)
|
||||
base->event_count++;
|
||||
|
||||
ev->ev_flags |= queue;
|
||||
switch (queue) {
|
||||
case EVLIST_INSERTED:
|
||||
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
|
||||
break;
|
||||
case EVLIST_ACTIVE:
|
||||
base->event_count_active++;
|
||||
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
|
||||
ev,ev_active_next);
|
||||
break;
|
||||
case EVLIST_TIMEOUT: {
|
||||
min_heap_push(&base->timeheap, ev);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
event_errx(1, "%s: unknown queue %x", __func__, queue);
|
||||
}
|
||||
}
|
||||
|
||||
/* Functions for debugging */
|
||||
|
||||
const char *
|
||||
event_get_version(void)
|
||||
{
|
||||
return (VERSION);
|
||||
}
|
||||
|
||||
/*
|
||||
* No thread-safe interface needed - the information should be the same
|
||||
* for all threads.
|
||||
*/
|
||||
|
||||
const char *
|
||||
event_get_method(void)
|
||||
{
|
||||
return (current_base->evsel->name);
|
||||
}
|
||||
1212
third-party/webrtc/dependencies/base/third_party/libevent/event.h
vendored
Normal file
1212
third-party/webrtc/dependencies/base/third_party/libevent/event.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1423
third-party/webrtc/dependencies/base/third_party/libevent/event_rpcgen.py
vendored
Executable file
1423
third-party/webrtc/dependencies/base/third_party/libevent/event_rpcgen.py
vendored
Executable file
File diff suppressed because it is too large
Load diff
443
third-party/webrtc/dependencies/base/third_party/libevent/event_tagging.c
vendored
Normal file
443
third-party/webrtc/dependencies/base/third_party/libevent/event_tagging.c
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <sys/queue.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "evutil.h"
|
||||
#include "log.h"
|
||||
|
||||
int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf);
|
||||
int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag);
|
||||
int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf);
|
||||
|
||||
static struct evbuffer *_buf; /* not thread safe */
|
||||
|
||||
void
|
||||
evtag_init(void)
|
||||
{
|
||||
if (_buf != NULL)
|
||||
return;
|
||||
|
||||
if ((_buf = evbuffer_new()) == NULL)
|
||||
event_err(1, "%s: malloc", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* We encode integer's by nibbles; the first nibble contains the number
|
||||
* of significant nibbles - 1; this allows us to encode up to 64-bit
|
||||
* integers. This function is byte-order independent.
|
||||
*/
|
||||
|
||||
void
|
||||
encode_int(struct evbuffer *evbuf, ev_uint32_t number)
|
||||
{
|
||||
int off = 1, nibbles = 0;
|
||||
ev_uint8_t data[5];
|
||||
|
||||
memset(data, 0, sizeof(ev_uint32_t)+1);
|
||||
while (number) {
|
||||
if (off & 0x1)
|
||||
data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f);
|
||||
else
|
||||
data[off/2] = (data[off/2] & 0x0f) |
|
||||
((number & 0x0f) << 4);
|
||||
number >>= 4;
|
||||
off++;
|
||||
}
|
||||
|
||||
if (off > 2)
|
||||
nibbles = off - 2;
|
||||
|
||||
/* Off - 1 is the number of encoded nibbles */
|
||||
data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4);
|
||||
|
||||
evbuffer_add(evbuf, data, (off + 1) / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Support variable length encoding of tags; we use the high bit in each
|
||||
* octet as a continuation signal.
|
||||
*/
|
||||
|
||||
int
|
||||
evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag)
|
||||
{
|
||||
int bytes = 0;
|
||||
ev_uint8_t data[5];
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
do {
|
||||
ev_uint8_t lower = tag & 0x7f;
|
||||
tag >>= 7;
|
||||
|
||||
if (tag)
|
||||
lower |= 0x80;
|
||||
|
||||
data[bytes++] = lower;
|
||||
} while (tag);
|
||||
|
||||
if (evbuf != NULL)
|
||||
evbuffer_add(evbuf, data, bytes);
|
||||
|
||||
return (bytes);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain)
|
||||
{
|
||||
ev_uint32_t number = 0;
|
||||
ev_uint8_t *data = EVBUFFER_DATA(evbuf);
|
||||
int len = EVBUFFER_LENGTH(evbuf);
|
||||
int count = 0, shift = 0, done = 0;
|
||||
|
||||
while (count++ < len) {
|
||||
ev_uint8_t lower = *data++;
|
||||
number |= (lower & 0x7f) << shift;
|
||||
shift += 7;
|
||||
|
||||
if (!(lower & 0x80)) {
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done)
|
||||
return (-1);
|
||||
|
||||
if (dodrain)
|
||||
evbuffer_drain(evbuf, count);
|
||||
|
||||
if (ptag != NULL)
|
||||
*ptag = number;
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf)
|
||||
{
|
||||
return (decode_tag_internal(ptag, evbuf, 1 /* dodrain */));
|
||||
}
|
||||
|
||||
/*
|
||||
* Marshal a data type, the general format is as follows:
|
||||
*
|
||||
* tag number: one byte; length: var bytes; payload: var bytes
|
||||
*/
|
||||
|
||||
void
|
||||
evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag,
|
||||
const void *data, ev_uint32_t len)
|
||||
{
|
||||
evtag_encode_tag(evbuf, tag);
|
||||
encode_int(evbuf, len);
|
||||
evbuffer_add(evbuf, (void *)data, len);
|
||||
}
|
||||
|
||||
/* Marshaling for integers */
|
||||
void
|
||||
evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer)
|
||||
{
|
||||
evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
|
||||
encode_int(_buf, integer);
|
||||
|
||||
evtag_encode_tag(evbuf, tag);
|
||||
encode_int(evbuf, EVBUFFER_LENGTH(_buf));
|
||||
evbuffer_add_buffer(evbuf, _buf);
|
||||
}
|
||||
|
||||
void
|
||||
evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string)
|
||||
{
|
||||
evtag_marshal(buf, tag, string, strlen(string));
|
||||
}
|
||||
|
||||
void
|
||||
evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv)
|
||||
{
|
||||
evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
|
||||
|
||||
encode_int(_buf, tv->tv_sec);
|
||||
encode_int(_buf, tv->tv_usec);
|
||||
|
||||
evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf),
|
||||
EVBUFFER_LENGTH(_buf));
|
||||
}
|
||||
|
||||
static int
|
||||
decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain)
|
||||
{
|
||||
ev_uint32_t number = 0;
|
||||
ev_uint8_t *data = EVBUFFER_DATA(evbuf);
|
||||
int len = EVBUFFER_LENGTH(evbuf);
|
||||
int nibbles = 0;
|
||||
|
||||
if (!len)
|
||||
return (-1);
|
||||
|
||||
nibbles = ((data[0] & 0xf0) >> 4) + 1;
|
||||
if (nibbles > 8 || (nibbles >> 1) + 1 > len)
|
||||
return (-1);
|
||||
len = (nibbles >> 1) + 1;
|
||||
|
||||
while (nibbles > 0) {
|
||||
number <<= 4;
|
||||
if (nibbles & 0x1)
|
||||
number |= data[nibbles >> 1] & 0x0f;
|
||||
else
|
||||
number |= (data[nibbles >> 1] & 0xf0) >> 4;
|
||||
nibbles--;
|
||||
}
|
||||
|
||||
if (dodrain)
|
||||
evbuffer_drain(evbuf, len);
|
||||
|
||||
*pnumber = number;
|
||||
|
||||
return (len);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf)
|
||||
{
|
||||
return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag)
|
||||
{
|
||||
return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */));
|
||||
}
|
||||
|
||||
int
|
||||
evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength)
|
||||
{
|
||||
struct evbuffer tmp;
|
||||
int res, len;
|
||||
|
||||
len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */);
|
||||
if (len == -1)
|
||||
return (-1);
|
||||
|
||||
tmp = *evbuf;
|
||||
tmp.buffer += len;
|
||||
tmp.off -= len;
|
||||
|
||||
res = decode_int_internal(plength, &tmp, 0);
|
||||
if (res == -1)
|
||||
return (-1);
|
||||
|
||||
*plength += res + len;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength)
|
||||
{
|
||||
struct evbuffer tmp;
|
||||
int res, len;
|
||||
|
||||
len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */);
|
||||
if (len == -1)
|
||||
return (-1);
|
||||
|
||||
tmp = *evbuf;
|
||||
tmp.buffer += len;
|
||||
tmp.off -= len;
|
||||
|
||||
res = decode_int_internal(plength, &tmp, 0);
|
||||
if (res == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_consume(struct evbuffer *evbuf)
|
||||
{
|
||||
ev_uint32_t len;
|
||||
if (decode_tag_internal(NULL, evbuf, 1 /* dodrain */) == -1)
|
||||
return (-1);
|
||||
if (evtag_decode_int(&len, evbuf) == -1)
|
||||
return (-1);
|
||||
evbuffer_drain(evbuf, len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Reads the data type from an event buffer */
|
||||
|
||||
int
|
||||
evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst)
|
||||
{
|
||||
ev_uint32_t len;
|
||||
ev_uint32_t integer;
|
||||
|
||||
if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1)
|
||||
return (-1);
|
||||
if (evtag_decode_int(&integer, src) == -1)
|
||||
return (-1);
|
||||
len = integer;
|
||||
|
||||
if (EVBUFFER_LENGTH(src) < len)
|
||||
return (-1);
|
||||
|
||||
if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1)
|
||||
return (-1);
|
||||
|
||||
evbuffer_drain(src, len);
|
||||
|
||||
return (len);
|
||||
}
|
||||
|
||||
/* Marshaling for integers */
|
||||
|
||||
int
|
||||
evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag,
|
||||
ev_uint32_t *pinteger)
|
||||
{
|
||||
ev_uint32_t tag;
|
||||
ev_uint32_t len;
|
||||
ev_uint32_t integer;
|
||||
|
||||
if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1)
|
||||
return (-1);
|
||||
if (need_tag != tag)
|
||||
return (-1);
|
||||
if (evtag_decode_int(&integer, evbuf) == -1)
|
||||
return (-1);
|
||||
len = integer;
|
||||
|
||||
if (EVBUFFER_LENGTH(evbuf) < len)
|
||||
return (-1);
|
||||
|
||||
evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
|
||||
if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1)
|
||||
return (-1);
|
||||
|
||||
evbuffer_drain(evbuf, len);
|
||||
|
||||
return (evtag_decode_int(pinteger, _buf));
|
||||
}
|
||||
|
||||
/* Unmarshal a fixed length tag */
|
||||
|
||||
int
|
||||
evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data,
|
||||
size_t len)
|
||||
{
|
||||
ev_uint32_t tag;
|
||||
|
||||
/* Initialize this event buffer so that we can read into it */
|
||||
evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
|
||||
|
||||
/* Now unmarshal a tag and check that it matches the tag we want */
|
||||
if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag)
|
||||
return (-1);
|
||||
|
||||
if (EVBUFFER_LENGTH(_buf) != len)
|
||||
return (-1);
|
||||
|
||||
memcpy(data, EVBUFFER_DATA(_buf), len);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag,
|
||||
char **pstring)
|
||||
{
|
||||
ev_uint32_t tag;
|
||||
|
||||
evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
|
||||
|
||||
if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
|
||||
return (-1);
|
||||
|
||||
*pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1);
|
||||
if (*pstring == NULL)
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag,
|
||||
struct timeval *ptv)
|
||||
{
|
||||
ev_uint32_t tag;
|
||||
ev_uint32_t integer;
|
||||
|
||||
evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
|
||||
if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
|
||||
return (-1);
|
||||
|
||||
if (evtag_decode_int(&integer, _buf) == -1)
|
||||
return (-1);
|
||||
ptv->tv_sec = integer;
|
||||
if (evtag_decode_int(&integer, _buf) == -1)
|
||||
return (-1);
|
||||
ptv->tv_usec = integer;
|
||||
|
||||
return (0);
|
||||
}
|
||||
375
third-party/webrtc/dependencies/base/third_party/libevent/evhttp.h
vendored
Normal file
375
third-party/webrtc/dependencies/base/third_party/libevent/evhttp.h
vendored
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _EVHTTP_H_
|
||||
#define _EVHTTP_H_
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/** @file evhttp.h
|
||||
*
|
||||
* Basic support for HTTP serving.
|
||||
*
|
||||
* As libevent is a library for dealing with event notification and most
|
||||
* interesting applications are networked today, I have often found the
|
||||
* need to write HTTP code. The following prototypes and definitions provide
|
||||
* an application with a minimal interface for making HTTP requests and for
|
||||
* creating a very simple HTTP server.
|
||||
*/
|
||||
|
||||
/* Response codes */
|
||||
#define HTTP_OK 200
|
||||
#define HTTP_NOCONTENT 204
|
||||
#define HTTP_MOVEPERM 301
|
||||
#define HTTP_MOVETEMP 302
|
||||
#define HTTP_NOTMODIFIED 304
|
||||
#define HTTP_BADREQUEST 400
|
||||
#define HTTP_NOTFOUND 404
|
||||
#define HTTP_SERVUNAVAIL 503
|
||||
|
||||
struct evhttp;
|
||||
struct evhttp_request;
|
||||
struct evkeyvalq;
|
||||
|
||||
/** Create a new HTTP server
|
||||
*
|
||||
* @param base (optional) the event base to receive the HTTP events
|
||||
* @return a pointer to a newly initialized evhttp server structure
|
||||
*/
|
||||
struct evhttp *evhttp_new(struct event_base *base);
|
||||
|
||||
/**
|
||||
* Binds an HTTP server on the specified address and port.
|
||||
*
|
||||
* Can be called multiple times to bind the same http server
|
||||
* to multiple different ports.
|
||||
*
|
||||
* @param http a pointer to an evhttp object
|
||||
* @param address a string containing the IP address to listen(2) on
|
||||
* @param port the port number to listen on
|
||||
* @return 0 on success, -1 on failure
|
||||
* @see evhttp_free()
|
||||
*/
|
||||
int evhttp_bind_socket(struct evhttp *http, const char *address, u_short port);
|
||||
|
||||
/**
|
||||
* Makes an HTTP server accept connections on the specified socket
|
||||
*
|
||||
* This may be useful to create a socket and then fork multiple instances
|
||||
* of an http server, or when a socket has been communicated via file
|
||||
* descriptor passing in situations where an http servers does not have
|
||||
* permissions to bind to a low-numbered port.
|
||||
*
|
||||
* Can be called multiple times to have the http server listen to
|
||||
* multiple different sockets.
|
||||
*
|
||||
* @param http a pointer to an evhttp object
|
||||
* @param fd a socket fd that is ready for accepting connections
|
||||
* @return 0 on success, -1 on failure.
|
||||
* @see evhttp_free(), evhttp_bind_socket()
|
||||
*/
|
||||
int evhttp_accept_socket(struct evhttp *http, int fd);
|
||||
|
||||
/**
|
||||
* Free the previously created HTTP server.
|
||||
*
|
||||
* Works only if no requests are currently being served.
|
||||
*
|
||||
* @param http the evhttp server object to be freed
|
||||
* @see evhttp_start()
|
||||
*/
|
||||
void evhttp_free(struct evhttp* http);
|
||||
|
||||
/** Set a callback for a specified URI */
|
||||
void evhttp_set_cb(struct evhttp *, const char *,
|
||||
void (*)(struct evhttp_request *, void *), void *);
|
||||
|
||||
/** Removes the callback for a specified URI */
|
||||
int evhttp_del_cb(struct evhttp *, const char *);
|
||||
|
||||
/** Set a callback for all requests that are not caught by specific callbacks
|
||||
*/
|
||||
void evhttp_set_gencb(struct evhttp *,
|
||||
void (*)(struct evhttp_request *, void *), void *);
|
||||
|
||||
/**
|
||||
* Set the timeout for an HTTP request.
|
||||
*
|
||||
* @param http an evhttp object
|
||||
* @param timeout_in_secs the timeout, in seconds
|
||||
*/
|
||||
void evhttp_set_timeout(struct evhttp *, int timeout_in_secs);
|
||||
|
||||
/* Request/Response functionality */
|
||||
|
||||
/**
|
||||
* Send an HTML error message to the client.
|
||||
*
|
||||
* @param req a request object
|
||||
* @param error the HTTP error code
|
||||
* @param reason a brief explanation of the error
|
||||
*/
|
||||
void evhttp_send_error(struct evhttp_request *req, int error,
|
||||
const char *reason);
|
||||
|
||||
/**
|
||||
* Send an HTML reply to the client.
|
||||
*
|
||||
* @param req a request object
|
||||
* @param code the HTTP response code to send
|
||||
* @param reason a brief message to send with the response code
|
||||
* @param databuf the body of the response
|
||||
*/
|
||||
void evhttp_send_reply(struct evhttp_request *req, int code,
|
||||
const char *reason, struct evbuffer *databuf);
|
||||
|
||||
/* Low-level response interface, for streaming/chunked replies */
|
||||
void evhttp_send_reply_start(struct evhttp_request *, int, const char *);
|
||||
void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *);
|
||||
void evhttp_send_reply_end(struct evhttp_request *);
|
||||
|
||||
/**
|
||||
* Start an HTTP server on the specified address and port
|
||||
*
|
||||
* DEPRECATED: it does not allow an event base to be specified
|
||||
*
|
||||
* @param address the address to which the HTTP server should be bound
|
||||
* @param port the port number on which the HTTP server should listen
|
||||
* @return an struct evhttp object
|
||||
*/
|
||||
struct evhttp *evhttp_start(const char *address, u_short port);
|
||||
|
||||
/*
|
||||
* Interfaces for making requests
|
||||
*/
|
||||
enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
|
||||
|
||||
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
|
||||
|
||||
/**
|
||||
* the request structure that a server receives.
|
||||
* WARNING: expect this structure to change. I will try to provide
|
||||
* reasonable accessors.
|
||||
*/
|
||||
struct evhttp_request {
|
||||
#if defined(TAILQ_ENTRY)
|
||||
TAILQ_ENTRY(evhttp_request) next;
|
||||
#else
|
||||
struct {
|
||||
struct evhttp_request *tqe_next;
|
||||
struct evhttp_request **tqe_prev;
|
||||
} next;
|
||||
#endif
|
||||
|
||||
/* the connection object that this request belongs to */
|
||||
struct evhttp_connection *evcon;
|
||||
int flags;
|
||||
#define EVHTTP_REQ_OWN_CONNECTION 0x0001
|
||||
#define EVHTTP_PROXY_REQUEST 0x0002
|
||||
|
||||
struct evkeyvalq *input_headers;
|
||||
struct evkeyvalq *output_headers;
|
||||
|
||||
/* address of the remote host and the port connection came from */
|
||||
char *remote_host;
|
||||
u_short remote_port;
|
||||
|
||||
enum evhttp_request_kind kind;
|
||||
enum evhttp_cmd_type type;
|
||||
|
||||
char *uri; /* uri after HTTP request was parsed */
|
||||
|
||||
char major; /* HTTP Major number */
|
||||
char minor; /* HTTP Minor number */
|
||||
|
||||
int response_code; /* HTTP Response code */
|
||||
char *response_code_line; /* Readable response */
|
||||
|
||||
struct evbuffer *input_buffer; /* read data */
|
||||
ev_int64_t ntoread;
|
||||
int chunked:1, /* a chunked request */
|
||||
userdone:1; /* the user has sent all data */
|
||||
|
||||
struct evbuffer *output_buffer; /* outgoing post or data */
|
||||
|
||||
/* Callback */
|
||||
void (*cb)(struct evhttp_request *, void *);
|
||||
void *cb_arg;
|
||||
|
||||
/*
|
||||
* Chunked data callback - call for each completed chunk if
|
||||
* specified. If not specified, all the data is delivered via
|
||||
* the regular callback.
|
||||
*/
|
||||
void (*chunk_cb)(struct evhttp_request *, void *);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new request object that needs to be filled in with the request
|
||||
* parameters. The callback is executed when the request completed or an
|
||||
* error occurred.
|
||||
*/
|
||||
struct evhttp_request *evhttp_request_new(
|
||||
void (*cb)(struct evhttp_request *, void *), void *arg);
|
||||
|
||||
/** enable delivery of chunks to requestor */
|
||||
void evhttp_request_set_chunked_cb(struct evhttp_request *,
|
||||
void (*cb)(struct evhttp_request *, void *));
|
||||
|
||||
/** Frees the request object and removes associated events. */
|
||||
void evhttp_request_free(struct evhttp_request *req);
|
||||
|
||||
/** Returns the connection object associated with the request or NULL */
|
||||
struct evhttp_connection *evhttp_request_get_connection(struct evhttp_request *req);
|
||||
|
||||
/**
|
||||
* A connection object that can be used to for making HTTP requests. The
|
||||
* connection object tries to establish the connection when it is given an
|
||||
* http request object.
|
||||
*/
|
||||
struct evhttp_connection *evhttp_connection_new(
|
||||
const char *address, unsigned short port);
|
||||
|
||||
/** Frees an http connection */
|
||||
void evhttp_connection_free(struct evhttp_connection *evcon);
|
||||
|
||||
/** sets the ip address from which http connections are made */
|
||||
void evhttp_connection_set_local_address(struct evhttp_connection *evcon,
|
||||
const char *address);
|
||||
|
||||
/** sets the local port from which http connections are made */
|
||||
void evhttp_connection_set_local_port(struct evhttp_connection *evcon,
|
||||
unsigned short port);
|
||||
|
||||
/** Sets the timeout for events related to this connection */
|
||||
void evhttp_connection_set_timeout(struct evhttp_connection *evcon,
|
||||
int timeout_in_secs);
|
||||
|
||||
/** Sets the retry limit for this connection - -1 repeats indefnitely */
|
||||
void evhttp_connection_set_retries(struct evhttp_connection *evcon,
|
||||
int retry_max);
|
||||
|
||||
/** Set a callback for connection close. */
|
||||
void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
|
||||
void (*)(struct evhttp_connection *, void *), void *);
|
||||
|
||||
/**
|
||||
* Associates an event base with the connection - can only be called
|
||||
* on a freshly created connection object that has not been used yet.
|
||||
*/
|
||||
void evhttp_connection_set_base(struct evhttp_connection *evcon,
|
||||
struct event_base *base);
|
||||
|
||||
/** Get the remote address and port associated with this connection. */
|
||||
void evhttp_connection_get_peer(struct evhttp_connection *evcon,
|
||||
char **address, u_short *port);
|
||||
|
||||
/** The connection gets ownership of the request */
|
||||
int evhttp_make_request(struct evhttp_connection *evcon,
|
||||
struct evhttp_request *req,
|
||||
enum evhttp_cmd_type type, const char *uri);
|
||||
|
||||
const char *evhttp_request_uri(struct evhttp_request *req);
|
||||
|
||||
/* Interfaces for dealing with HTTP headers */
|
||||
|
||||
const char *evhttp_find_header(const struct evkeyvalq *, const char *);
|
||||
int evhttp_remove_header(struct evkeyvalq *, const char *);
|
||||
int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
|
||||
void evhttp_clear_headers(struct evkeyvalq *);
|
||||
|
||||
/* Miscellaneous utility functions */
|
||||
|
||||
|
||||
/**
|
||||
Helper function to encode a URI.
|
||||
|
||||
The returned string must be freed by the caller.
|
||||
|
||||
@param uri an unencoded URI
|
||||
@return a newly allocated URI-encoded string
|
||||
*/
|
||||
char *evhttp_encode_uri(const char *uri);
|
||||
|
||||
|
||||
/**
|
||||
Helper function to decode a URI.
|
||||
|
||||
The returned string must be freed by the caller.
|
||||
|
||||
@param uri an encoded URI
|
||||
@return a newly allocated unencoded URI
|
||||
*/
|
||||
char *evhttp_decode_uri(const char *uri);
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to parse out arguments in a query.
|
||||
*
|
||||
* Parsing a uri like
|
||||
*
|
||||
* http://foo.com/?q=test&s=some+thing
|
||||
*
|
||||
* will result in two entries in the key value queue.
|
||||
|
||||
* The first entry is: key="q", value="test"
|
||||
* The second entry is: key="s", value="some thing"
|
||||
*
|
||||
* @param uri the request URI
|
||||
* @param headers the head of the evkeyval queue
|
||||
*/
|
||||
void evhttp_parse_query(const char *uri, struct evkeyvalq *headers);
|
||||
|
||||
|
||||
/**
|
||||
* Escape HTML character entities in a string.
|
||||
*
|
||||
* Replaces <, >, ", ' and & with <, >, ",
|
||||
* ' and & correspondingly.
|
||||
*
|
||||
* The returned string needs to be freed by the caller.
|
||||
*
|
||||
* @param html an unescaped HTML string
|
||||
* @return an escaped HTML string
|
||||
*/
|
||||
char *evhttp_htmlescape(const char *html);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EVHTTP_H_ */
|
||||
519
third-party/webrtc/dependencies/base/third_party/libevent/evport.c
vendored
Normal file
519
third-party/webrtc/dependencies/base/third_party/libevent/evport.c
vendored
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* Submitted by David Pacheco (dp.spambait@gmail.com)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Sun Microsystems. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* evport.c: event backend using Solaris 10 event ports. See port_create(3C).
|
||||
* This implementation is loosely modeled after the one used for select(2) (in
|
||||
* select.c).
|
||||
*
|
||||
* The outstanding events are tracked in a data structure called evport_data.
|
||||
* Each entry in the ed_fds array corresponds to a file descriptor, and contains
|
||||
* pointers to the read and write events that correspond to that fd. (That is,
|
||||
* when the file is readable, the "read" event should handle it, etc.)
|
||||
*
|
||||
* evport_add and evport_del update this data structure. evport_dispatch uses it
|
||||
* to determine where to callback when an event occurs (which it gets from
|
||||
* port_getn).
|
||||
*
|
||||
* Helper functions are used: grow() grows the file descriptor array as
|
||||
* necessary when large fd's come in. reassociate() takes care of maintaining
|
||||
* the proper file-descriptor/event-port associations.
|
||||
*
|
||||
* As in the select(2) implementation, signals are handled by evsignal.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
#include <sys/queue.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <port.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#ifdef CHECK_INVARIANTS
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "log.h"
|
||||
#include "evsignal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Default value for ed_nevents, which is the maximum file descriptor number we
|
||||
* can handle. If an event comes in for a file descriptor F > nevents, we will
|
||||
* grow the array of file descriptors, doubling its size.
|
||||
*/
|
||||
#define DEFAULT_NFDS 16
|
||||
|
||||
|
||||
/*
|
||||
* EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
|
||||
* any particular call. You can speed things up by increasing this, but it will
|
||||
* (obviously) require more memory.
|
||||
*/
|
||||
#define EVENTS_PER_GETN 8
|
||||
|
||||
/*
|
||||
* Per-file-descriptor information about what events we're subscribed to. These
|
||||
* fields are NULL if no event is subscribed to either of them.
|
||||
*/
|
||||
|
||||
struct fd_info {
|
||||
struct event* fdi_revt; /* the event responsible for the "read" */
|
||||
struct event* fdi_wevt; /* the event responsible for the "write" */
|
||||
};
|
||||
|
||||
#define FDI_HAS_READ(fdi) ((fdi)->fdi_revt != NULL)
|
||||
#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL)
|
||||
#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
|
||||
#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
|
||||
(FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
|
||||
|
||||
struct evport_data {
|
||||
int ed_port; /* event port for system events */
|
||||
int ed_nevents; /* number of allocated fdi's */
|
||||
struct fd_info *ed_fds; /* allocated fdi table */
|
||||
/* fdi's that we need to reassoc */
|
||||
int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */
|
||||
};
|
||||
|
||||
static void* evport_init (struct event_base *);
|
||||
static int evport_add (void *, struct event *);
|
||||
static int evport_del (void *, struct event *);
|
||||
static int evport_dispatch (struct event_base *, void *, struct timeval *);
|
||||
static void evport_dealloc (struct event_base *, void *);
|
||||
|
||||
const struct eventop evportops = {
|
||||
"evport",
|
||||
evport_init,
|
||||
evport_add,
|
||||
evport_del,
|
||||
evport_dispatch,
|
||||
evport_dealloc,
|
||||
1 /* need reinit */
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the event port implementation.
|
||||
*/
|
||||
|
||||
static void*
|
||||
evport_init(struct event_base *base)
|
||||
{
|
||||
struct evport_data *evpd;
|
||||
int i;
|
||||
/*
|
||||
* Disable event ports when this environment variable is set
|
||||
*/
|
||||
if (evutil_getenv("EVENT_NOEVPORT"))
|
||||
return (NULL);
|
||||
|
||||
if (!(evpd = calloc(1, sizeof(struct evport_data))))
|
||||
return (NULL);
|
||||
|
||||
if ((evpd->ed_port = port_create()) == -1) {
|
||||
free(evpd);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize file descriptor structure
|
||||
*/
|
||||
evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info));
|
||||
if (evpd->ed_fds == NULL) {
|
||||
close(evpd->ed_port);
|
||||
free(evpd);
|
||||
return (NULL);
|
||||
}
|
||||
evpd->ed_nevents = DEFAULT_NFDS;
|
||||
for (i = 0; i < EVENTS_PER_GETN; i++)
|
||||
evpd->ed_pending[i] = -1;
|
||||
|
||||
evsignal_init(base);
|
||||
|
||||
return (evpd);
|
||||
}
|
||||
|
||||
#ifdef CHECK_INVARIANTS
|
||||
/*
|
||||
* Checks some basic properties about the evport_data structure. Because it
|
||||
* checks all file descriptors, this function can be expensive when the maximum
|
||||
* file descriptor ever used is rather large.
|
||||
*/
|
||||
|
||||
static void
|
||||
check_evportop(struct evport_data *evpd)
|
||||
{
|
||||
assert(evpd);
|
||||
assert(evpd->ed_nevents > 0);
|
||||
assert(evpd->ed_port > 0);
|
||||
assert(evpd->ed_fds > 0);
|
||||
|
||||
/*
|
||||
* Verify the integrity of the fd_info struct as well as the events to
|
||||
* which it points (at least, that they're valid references and correct
|
||||
* for their position in the structure).
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < evpd->ed_nevents; ++i) {
|
||||
struct event *ev;
|
||||
struct fd_info *fdi;
|
||||
|
||||
fdi = &evpd->ed_fds[i];
|
||||
if ((ev = fdi->fdi_revt) != NULL) {
|
||||
assert(ev->ev_fd == i);
|
||||
}
|
||||
if ((ev = fdi->fdi_wevt) != NULL) {
|
||||
assert(ev->ev_fd == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies very basic integrity of a given port_event.
|
||||
*/
|
||||
static void
|
||||
check_event(port_event_t* pevt)
|
||||
{
|
||||
/*
|
||||
* We've only registered for PORT_SOURCE_FD events. The only
|
||||
* other thing we can legitimately receive is PORT_SOURCE_ALERT,
|
||||
* but since we're not using port_alert either, we can assume
|
||||
* PORT_SOURCE_FD.
|
||||
*/
|
||||
assert(pevt->portev_source == PORT_SOURCE_FD);
|
||||
assert(pevt->portev_user == NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
#define check_evportop(epop)
|
||||
#define check_event(pevt)
|
||||
#endif /* CHECK_INVARIANTS */
|
||||
|
||||
/*
|
||||
* Doubles the size of the allocated file descriptor array.
|
||||
*/
|
||||
static int
|
||||
grow(struct evport_data *epdp, int factor)
|
||||
{
|
||||
struct fd_info *tmp;
|
||||
int oldsize = epdp->ed_nevents;
|
||||
int newsize = factor * oldsize;
|
||||
assert(factor > 1);
|
||||
|
||||
check_evportop(epdp);
|
||||
|
||||
tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
|
||||
if (NULL == tmp)
|
||||
return -1;
|
||||
epdp->ed_fds = tmp;
|
||||
memset((char*) (epdp->ed_fds + oldsize), 0,
|
||||
(newsize - oldsize)*sizeof(struct fd_info));
|
||||
epdp->ed_nevents = newsize;
|
||||
|
||||
check_evportop(epdp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (Re)associates the given file descriptor with the event port. The OS events
|
||||
* are specified (implicitly) from the fd_info struct.
|
||||
*/
|
||||
static int
|
||||
reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
|
||||
{
|
||||
int sysevents = FDI_TO_SYSEVENTS(fdip);
|
||||
|
||||
if (sysevents != 0) {
|
||||
if (port_associate(epdp->ed_port, PORT_SOURCE_FD,
|
||||
fd, sysevents, NULL) == -1) {
|
||||
event_warn("port_associate");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
check_evportop(epdp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main event loop - polls port_getn for some number of events, and processes
|
||||
* them.
|
||||
*/
|
||||
|
||||
static int
|
||||
evport_dispatch(struct event_base *base, void *arg, struct timeval *tv)
|
||||
{
|
||||
int i, res;
|
||||
struct evport_data *epdp = arg;
|
||||
port_event_t pevtlist[EVENTS_PER_GETN];
|
||||
|
||||
/*
|
||||
* port_getn will block until it has at least nevents events. It will
|
||||
* also return how many it's given us (which may be more than we asked
|
||||
* for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
|
||||
* nevents.
|
||||
*/
|
||||
int nevents = 1;
|
||||
|
||||
/*
|
||||
* We have to convert a struct timeval to a struct timespec
|
||||
* (only difference is nanoseconds vs. microseconds). If no time-based
|
||||
* events are active, we should wait for I/O (and tv == NULL).
|
||||
*/
|
||||
struct timespec ts;
|
||||
struct timespec *ts_p = NULL;
|
||||
if (tv != NULL) {
|
||||
ts.tv_sec = tv->tv_sec;
|
||||
ts.tv_nsec = tv->tv_usec * 1000;
|
||||
ts_p = &ts;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before doing anything else, we need to reassociate the events we hit
|
||||
* last time which need reassociation. See comment at the end of the
|
||||
* loop below.
|
||||
*/
|
||||
for (i = 0; i < EVENTS_PER_GETN; ++i) {
|
||||
struct fd_info *fdi = NULL;
|
||||
if (epdp->ed_pending[i] != -1) {
|
||||
fdi = &(epdp->ed_fds[epdp->ed_pending[i]]);
|
||||
}
|
||||
|
||||
if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
|
||||
int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd :
|
||||
fdi->fdi_wevt->ev_fd;
|
||||
reassociate(epdp, fdi, fd);
|
||||
epdp->ed_pending[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
|
||||
(unsigned int *) &nevents, ts_p)) == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
evsignal_process(base);
|
||||
return (0);
|
||||
} else if (errno == ETIME) {
|
||||
if (nevents == 0)
|
||||
return (0);
|
||||
} else {
|
||||
event_warn("port_getn");
|
||||
return (-1);
|
||||
}
|
||||
} else if (base->sig.evsignal_caught) {
|
||||
evsignal_process(base);
|
||||
}
|
||||
|
||||
event_debug(("%s: port_getn reports %d events", __func__, nevents));
|
||||
|
||||
for (i = 0; i < nevents; ++i) {
|
||||
struct event *ev;
|
||||
struct fd_info *fdi;
|
||||
port_event_t *pevt = &pevtlist[i];
|
||||
int fd = (int) pevt->portev_object;
|
||||
|
||||
check_evportop(epdp);
|
||||
check_event(pevt);
|
||||
epdp->ed_pending[i] = fd;
|
||||
|
||||
/*
|
||||
* Figure out what kind of event it was
|
||||
* (because we have to pass this to the callback)
|
||||
*/
|
||||
res = 0;
|
||||
if (pevt->portev_events & POLLIN)
|
||||
res |= EV_READ;
|
||||
if (pevt->portev_events & POLLOUT)
|
||||
res |= EV_WRITE;
|
||||
|
||||
/*
|
||||
* Check for the error situations or a hangup situation
|
||||
*/
|
||||
if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL))
|
||||
res |= EV_READ|EV_WRITE;
|
||||
|
||||
assert(epdp->ed_nevents > fd);
|
||||
fdi = &(epdp->ed_fds[fd]);
|
||||
|
||||
/*
|
||||
* We now check for each of the possible events (READ
|
||||
* or WRITE). Then, we activate the event (which will
|
||||
* cause its callback to be executed).
|
||||
*/
|
||||
|
||||
if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) {
|
||||
event_active(ev, res, 1);
|
||||
}
|
||||
|
||||
if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) {
|
||||
event_active(ev, res, 1);
|
||||
}
|
||||
} /* end of all events gotten */
|
||||
|
||||
check_evportop(epdp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Adds the given event (so that you will be notified when it happens via
|
||||
* the callback function).
|
||||
*/
|
||||
|
||||
static int
|
||||
evport_add(void *arg, struct event *ev)
|
||||
{
|
||||
struct evport_data *evpd = arg;
|
||||
struct fd_info *fdi;
|
||||
int factor;
|
||||
|
||||
check_evportop(evpd);
|
||||
|
||||
/*
|
||||
* Delegate, if it's not ours to handle.
|
||||
*/
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_add(ev));
|
||||
|
||||
/*
|
||||
* If necessary, grow the file descriptor info table
|
||||
*/
|
||||
|
||||
factor = 1;
|
||||
while (ev->ev_fd >= factor * evpd->ed_nevents)
|
||||
factor *= 2;
|
||||
|
||||
if (factor > 1) {
|
||||
if (-1 == grow(evpd, factor)) {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
fdi = &evpd->ed_fds[ev->ev_fd];
|
||||
if (ev->ev_events & EV_READ)
|
||||
fdi->fdi_revt = ev;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
fdi->fdi_wevt = ev;
|
||||
|
||||
return reassociate(evpd, fdi, ev->ev_fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the given event from the list of events to wait for.
|
||||
*/
|
||||
|
||||
static int
|
||||
evport_del(void *arg, struct event *ev)
|
||||
{
|
||||
struct evport_data *evpd = arg;
|
||||
struct fd_info *fdi;
|
||||
int i;
|
||||
int associated = 1;
|
||||
|
||||
check_evportop(evpd);
|
||||
|
||||
/*
|
||||
* Delegate, if it's not ours to handle
|
||||
*/
|
||||
if (ev->ev_events & EV_SIGNAL) {
|
||||
return (evsignal_del(ev));
|
||||
}
|
||||
|
||||
if (evpd->ed_nevents < ev->ev_fd) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < EVENTS_PER_GETN; ++i) {
|
||||
if (evpd->ed_pending[i] == ev->ev_fd) {
|
||||
associated = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fdi = &evpd->ed_fds[ev->ev_fd];
|
||||
if (ev->ev_events & EV_READ)
|
||||
fdi->fdi_revt = NULL;
|
||||
if (ev->ev_events & EV_WRITE)
|
||||
fdi->fdi_wevt = NULL;
|
||||
|
||||
if (associated) {
|
||||
if (!FDI_HAS_EVENTS(fdi) &&
|
||||
port_dissociate(evpd->ed_port, PORT_SOURCE_FD,
|
||||
ev->ev_fd) == -1) {
|
||||
/*
|
||||
* Ignre EBADFD error the fd could have been closed
|
||||
* before event_del() was called.
|
||||
*/
|
||||
if (errno != EBADFD) {
|
||||
event_warn("port_dissociate");
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
if (FDI_HAS_EVENTS(fdi)) {
|
||||
return (reassociate(evpd, fdi, ev->ev_fd));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fdi->fdi_revt == NULL && fdi->fdi_wevt == NULL) {
|
||||
evpd->ed_pending[i] = -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
evport_dealloc(struct event_base *base, void *arg)
|
||||
{
|
||||
struct evport_data *evpd = arg;
|
||||
|
||||
evsignal_dealloc(base);
|
||||
|
||||
close(evpd->ed_port);
|
||||
|
||||
if (evpd->ed_fds)
|
||||
free(evpd->ed_fds);
|
||||
free(evpd);
|
||||
}
|
||||
87
third-party/webrtc/dependencies/base/third_party/libevent/evrpc-internal.h
vendored
Normal file
87
third-party/webrtc/dependencies/base/third_party/libevent/evrpc-internal.h
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _EVRPC_INTERNAL_H_
|
||||
#define _EVRPC_INTERNAL_H_
|
||||
|
||||
#include "http-internal.h"
|
||||
|
||||
struct evrpc;
|
||||
|
||||
#define EVRPC_URI_PREFIX "/.rpc."
|
||||
|
||||
struct evrpc_hook {
|
||||
TAILQ_ENTRY(evrpc_hook) (next);
|
||||
|
||||
/* returns -1; if the rpc should be aborted, is allowed to rewrite */
|
||||
int (*process)(struct evhttp_request *, struct evbuffer *, void *);
|
||||
void *process_arg;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(evrpc_hook_list, evrpc_hook);
|
||||
|
||||
/*
|
||||
* this is shared between the base and the pool, so that we can reuse
|
||||
* the hook adding functions; we alias both evrpc_pool and evrpc_base
|
||||
* to this common structure.
|
||||
*/
|
||||
struct _evrpc_hooks {
|
||||
/* hooks for processing outbound and inbound rpcs */
|
||||
struct evrpc_hook_list in_hooks;
|
||||
struct evrpc_hook_list out_hooks;
|
||||
};
|
||||
|
||||
#define input_hooks common.in_hooks
|
||||
#define output_hooks common.out_hooks
|
||||
|
||||
struct evrpc_base {
|
||||
struct _evrpc_hooks common;
|
||||
|
||||
/* the HTTP server under which we register our RPC calls */
|
||||
struct evhttp* http_server;
|
||||
|
||||
/* a list of all RPCs registered with us */
|
||||
TAILQ_HEAD(evrpc_list, evrpc) registered_rpcs;
|
||||
};
|
||||
|
||||
struct evrpc_req_generic;
|
||||
void evrpc_reqstate_free(struct evrpc_req_generic* rpc_state);
|
||||
|
||||
/* A pool for holding evhttp_connection objects */
|
||||
struct evrpc_pool {
|
||||
struct _evrpc_hooks common;
|
||||
|
||||
struct event_base *base;
|
||||
|
||||
struct evconq connections;
|
||||
|
||||
int timeout;
|
||||
|
||||
TAILQ_HEAD(evrpc_requestq, evrpc_request_wrapper) requests;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _EVRPC_INTERNAL_H_ */
|
||||
657
third-party/webrtc/dependencies/base/third_party/libevent/evrpc.c
vendored
Normal file
657
third-party/webrtc/dependencies/base/third_party/libevent/evrpc.c
vendored
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "evrpc.h"
|
||||
#include "evrpc-internal.h"
|
||||
#include "evhttp.h"
|
||||
#include "evutil.h"
|
||||
#include "log.h"
|
||||
|
||||
struct evrpc_base *
|
||||
evrpc_init(struct evhttp *http_server)
|
||||
{
|
||||
struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base));
|
||||
if (base == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* we rely on the tagging sub system */
|
||||
evtag_init();
|
||||
|
||||
TAILQ_INIT(&base->registered_rpcs);
|
||||
TAILQ_INIT(&base->input_hooks);
|
||||
TAILQ_INIT(&base->output_hooks);
|
||||
base->http_server = http_server;
|
||||
|
||||
return (base);
|
||||
}
|
||||
|
||||
void
|
||||
evrpc_free(struct evrpc_base *base)
|
||||
{
|
||||
struct evrpc *rpc;
|
||||
struct evrpc_hook *hook;
|
||||
|
||||
while ((rpc = TAILQ_FIRST(&base->registered_rpcs)) != NULL) {
|
||||
assert(evrpc_unregister_rpc(base, rpc->uri));
|
||||
}
|
||||
while ((hook = TAILQ_FIRST(&base->input_hooks)) != NULL) {
|
||||
assert(evrpc_remove_hook(base, EVRPC_INPUT, hook));
|
||||
}
|
||||
while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) {
|
||||
assert(evrpc_remove_hook(base, EVRPC_OUTPUT, hook));
|
||||
}
|
||||
free(base);
|
||||
}
|
||||
|
||||
void *
|
||||
evrpc_add_hook(void *vbase,
|
||||
enum EVRPC_HOOK_TYPE hook_type,
|
||||
int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
|
||||
void *cb_arg)
|
||||
{
|
||||
struct _evrpc_hooks *base = vbase;
|
||||
struct evrpc_hook_list *head = NULL;
|
||||
struct evrpc_hook *hook = NULL;
|
||||
switch (hook_type) {
|
||||
case EVRPC_INPUT:
|
||||
head = &base->in_hooks;
|
||||
break;
|
||||
case EVRPC_OUTPUT:
|
||||
head = &base->out_hooks;
|
||||
break;
|
||||
default:
|
||||
assert(hook_type == EVRPC_INPUT || hook_type == EVRPC_OUTPUT);
|
||||
}
|
||||
|
||||
hook = calloc(1, sizeof(struct evrpc_hook));
|
||||
assert(hook != NULL);
|
||||
|
||||
hook->process = cb;
|
||||
hook->process_arg = cb_arg;
|
||||
TAILQ_INSERT_TAIL(head, hook, next);
|
||||
|
||||
return (hook);
|
||||
}
|
||||
|
||||
static int
|
||||
evrpc_remove_hook_internal(struct evrpc_hook_list *head, void *handle)
|
||||
{
|
||||
struct evrpc_hook *hook = NULL;
|
||||
TAILQ_FOREACH(hook, head, next) {
|
||||
if (hook == handle) {
|
||||
TAILQ_REMOVE(head, hook, next);
|
||||
free(hook);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove the hook specified by the handle
|
||||
*/
|
||||
|
||||
int
|
||||
evrpc_remove_hook(void *vbase, enum EVRPC_HOOK_TYPE hook_type, void *handle)
|
||||
{
|
||||
struct _evrpc_hooks *base = vbase;
|
||||
struct evrpc_hook_list *head = NULL;
|
||||
switch (hook_type) {
|
||||
case EVRPC_INPUT:
|
||||
head = &base->in_hooks;
|
||||
break;
|
||||
case EVRPC_OUTPUT:
|
||||
head = &base->out_hooks;
|
||||
break;
|
||||
default:
|
||||
assert(hook_type == EVRPC_INPUT || hook_type == EVRPC_OUTPUT);
|
||||
}
|
||||
|
||||
return (evrpc_remove_hook_internal(head, handle));
|
||||
}
|
||||
|
||||
static int
|
||||
evrpc_process_hooks(struct evrpc_hook_list *head,
|
||||
struct evhttp_request *req, struct evbuffer *evbuf)
|
||||
{
|
||||
struct evrpc_hook *hook;
|
||||
TAILQ_FOREACH(hook, head, next) {
|
||||
if (hook->process(req, evbuf, hook->process_arg) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void evrpc_pool_schedule(struct evrpc_pool *pool);
|
||||
static void evrpc_request_cb(struct evhttp_request *, void *);
|
||||
void evrpc_request_done(struct evrpc_req_generic*);
|
||||
|
||||
/*
|
||||
* Registers a new RPC with the HTTP server. The evrpc object is expected
|
||||
* to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn
|
||||
* calls this function.
|
||||
*/
|
||||
|
||||
static char *
|
||||
evrpc_construct_uri(const char *uri)
|
||||
{
|
||||
char *constructed_uri;
|
||||
int constructed_uri_len;
|
||||
|
||||
constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(uri) + 1;
|
||||
if ((constructed_uri = malloc(constructed_uri_len)) == NULL)
|
||||
event_err(1, "%s: failed to register rpc at %s",
|
||||
__func__, uri);
|
||||
memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX));
|
||||
memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX), uri, strlen(uri));
|
||||
constructed_uri[constructed_uri_len - 1] = '\0';
|
||||
|
||||
return (constructed_uri);
|
||||
}
|
||||
|
||||
int
|
||||
evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
|
||||
void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg)
|
||||
{
|
||||
char *constructed_uri = evrpc_construct_uri(rpc->uri);
|
||||
|
||||
rpc->base = base;
|
||||
rpc->cb = cb;
|
||||
rpc->cb_arg = cb_arg;
|
||||
|
||||
TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next);
|
||||
|
||||
evhttp_set_cb(base->http_server,
|
||||
constructed_uri,
|
||||
evrpc_request_cb,
|
||||
rpc);
|
||||
|
||||
free(constructed_uri);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evrpc_unregister_rpc(struct evrpc_base *base, const char *name)
|
||||
{
|
||||
char *registered_uri = NULL;
|
||||
struct evrpc *rpc;
|
||||
|
||||
/* find the right rpc; linear search might be slow */
|
||||
TAILQ_FOREACH(rpc, &base->registered_rpcs, next) {
|
||||
if (strcmp(rpc->uri, name) == 0)
|
||||
break;
|
||||
}
|
||||
if (rpc == NULL) {
|
||||
/* We did not find an RPC with this name */
|
||||
return (-1);
|
||||
}
|
||||
TAILQ_REMOVE(&base->registered_rpcs, rpc, next);
|
||||
|
||||
free((char *)rpc->uri);
|
||||
free(rpc);
|
||||
|
||||
registered_uri = evrpc_construct_uri(name);
|
||||
|
||||
/* remove the http server callback */
|
||||
assert(evhttp_del_cb(base->http_server, registered_uri) == 0);
|
||||
|
||||
free(registered_uri);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
evrpc_request_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evrpc *rpc = arg;
|
||||
struct evrpc_req_generic *rpc_state = NULL;
|
||||
|
||||
/* let's verify the outside parameters */
|
||||
if (req->type != EVHTTP_REQ_POST ||
|
||||
EVBUFFER_LENGTH(req->input_buffer) <= 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* we might want to allow hooks to suspend the processing,
|
||||
* but at the moment, we assume that they just act as simple
|
||||
* filters.
|
||||
*/
|
||||
if (evrpc_process_hooks(&rpc->base->input_hooks,
|
||||
req, req->input_buffer) == -1)
|
||||
goto error;
|
||||
|
||||
rpc_state = calloc(1, sizeof(struct evrpc_req_generic));
|
||||
if (rpc_state == NULL)
|
||||
goto error;
|
||||
|
||||
/* let's check that we can parse the request */
|
||||
rpc_state->request = rpc->request_new();
|
||||
if (rpc_state->request == NULL)
|
||||
goto error;
|
||||
|
||||
rpc_state->rpc = rpc;
|
||||
|
||||
if (rpc->request_unmarshal(
|
||||
rpc_state->request, req->input_buffer) == -1) {
|
||||
/* we failed to parse the request; that's a bummer */
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* at this point, we have a well formed request, prepare the reply */
|
||||
|
||||
rpc_state->reply = rpc->reply_new();
|
||||
if (rpc_state->reply == NULL)
|
||||
goto error;
|
||||
|
||||
rpc_state->http_req = req;
|
||||
rpc_state->done = evrpc_request_done;
|
||||
|
||||
/* give the rpc to the user; they can deal with it */
|
||||
rpc->cb(rpc_state, rpc->cb_arg);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
evrpc_reqstate_free(rpc_state);
|
||||
evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
evrpc_reqstate_free(struct evrpc_req_generic* rpc_state)
|
||||
{
|
||||
/* clean up all memory */
|
||||
if (rpc_state != NULL) {
|
||||
struct evrpc *rpc = rpc_state->rpc;
|
||||
|
||||
if (rpc_state->request != NULL)
|
||||
rpc->request_free(rpc_state->request);
|
||||
if (rpc_state->reply != NULL)
|
||||
rpc->reply_free(rpc_state->reply);
|
||||
free(rpc_state);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
evrpc_request_done(struct evrpc_req_generic* rpc_state)
|
||||
{
|
||||
struct evhttp_request *req = rpc_state->http_req;
|
||||
struct evrpc *rpc = rpc_state->rpc;
|
||||
struct evbuffer* data = NULL;
|
||||
|
||||
if (rpc->reply_complete(rpc_state->reply) == -1) {
|
||||
/* the reply was not completely filled in. error out */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((data = evbuffer_new()) == NULL) {
|
||||
/* out of memory */
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* serialize the reply */
|
||||
rpc->reply_marshal(data, rpc_state->reply);
|
||||
|
||||
/* do hook based tweaks to the request */
|
||||
if (evrpc_process_hooks(&rpc->base->output_hooks,
|
||||
req, data) == -1)
|
||||
goto error;
|
||||
|
||||
/* on success, we are going to transmit marshaled binary data */
|
||||
if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
|
||||
evhttp_add_header(req->output_headers,
|
||||
"Content-Type", "application/octet-stream");
|
||||
}
|
||||
|
||||
evhttp_send_reply(req, HTTP_OK, "OK", data);
|
||||
|
||||
evbuffer_free(data);
|
||||
|
||||
evrpc_reqstate_free(rpc_state);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
if (data != NULL)
|
||||
evbuffer_free(data);
|
||||
evrpc_reqstate_free(rpc_state);
|
||||
evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Client implementation of RPC site */
|
||||
|
||||
static int evrpc_schedule_request(struct evhttp_connection *connection,
|
||||
struct evrpc_request_wrapper *ctx);
|
||||
|
||||
struct evrpc_pool *
|
||||
evrpc_pool_new(struct event_base *base)
|
||||
{
|
||||
struct evrpc_pool *pool = calloc(1, sizeof(struct evrpc_pool));
|
||||
if (pool == NULL)
|
||||
return (NULL);
|
||||
|
||||
TAILQ_INIT(&pool->connections);
|
||||
TAILQ_INIT(&pool->requests);
|
||||
|
||||
TAILQ_INIT(&pool->input_hooks);
|
||||
TAILQ_INIT(&pool->output_hooks);
|
||||
|
||||
pool->base = base;
|
||||
pool->timeout = -1;
|
||||
|
||||
return (pool);
|
||||
}
|
||||
|
||||
static void
|
||||
evrpc_request_wrapper_free(struct evrpc_request_wrapper *request)
|
||||
{
|
||||
free(request->name);
|
||||
free(request);
|
||||
}
|
||||
|
||||
void
|
||||
evrpc_pool_free(struct evrpc_pool *pool)
|
||||
{
|
||||
struct evhttp_connection *connection;
|
||||
struct evrpc_request_wrapper *request;
|
||||
struct evrpc_hook *hook;
|
||||
|
||||
while ((request = TAILQ_FIRST(&pool->requests)) != NULL) {
|
||||
TAILQ_REMOVE(&pool->requests, request, next);
|
||||
/* if this gets more complicated we need our own function */
|
||||
evrpc_request_wrapper_free(request);
|
||||
}
|
||||
|
||||
while ((connection = TAILQ_FIRST(&pool->connections)) != NULL) {
|
||||
TAILQ_REMOVE(&pool->connections, connection, next);
|
||||
evhttp_connection_free(connection);
|
||||
}
|
||||
|
||||
while ((hook = TAILQ_FIRST(&pool->input_hooks)) != NULL) {
|
||||
assert(evrpc_remove_hook(pool, EVRPC_INPUT, hook));
|
||||
}
|
||||
|
||||
while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) {
|
||||
assert(evrpc_remove_hook(pool, EVRPC_OUTPUT, hook));
|
||||
}
|
||||
|
||||
free(pool);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a connection to the RPC pool. A request scheduled on the pool
|
||||
* may use any available connection.
|
||||
*/
|
||||
|
||||
void
|
||||
evrpc_pool_add_connection(struct evrpc_pool *pool,
|
||||
struct evhttp_connection *connection) {
|
||||
assert(connection->http_server == NULL);
|
||||
TAILQ_INSERT_TAIL(&pool->connections, connection, next);
|
||||
|
||||
/*
|
||||
* associate an event base with this connection
|
||||
*/
|
||||
if (pool->base != NULL)
|
||||
evhttp_connection_set_base(connection, pool->base);
|
||||
|
||||
/*
|
||||
* unless a timeout was specifically set for a connection,
|
||||
* the connection inherits the timeout from the pool.
|
||||
*/
|
||||
if (connection->timeout == -1)
|
||||
connection->timeout = pool->timeout;
|
||||
|
||||
/*
|
||||
* if we have any requests pending, schedule them with the new
|
||||
* connections.
|
||||
*/
|
||||
|
||||
if (TAILQ_FIRST(&pool->requests) != NULL) {
|
||||
struct evrpc_request_wrapper *request =
|
||||
TAILQ_FIRST(&pool->requests);
|
||||
TAILQ_REMOVE(&pool->requests, request, next);
|
||||
evrpc_schedule_request(connection, request);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs)
|
||||
{
|
||||
struct evhttp_connection *evcon;
|
||||
TAILQ_FOREACH(evcon, &pool->connections, next) {
|
||||
evcon->timeout = timeout_in_secs;
|
||||
}
|
||||
pool->timeout = timeout_in_secs;
|
||||
}
|
||||
|
||||
|
||||
static void evrpc_reply_done(struct evhttp_request *, void *);
|
||||
static void evrpc_request_timeout(int, short, void *);
|
||||
|
||||
/*
|
||||
* Finds a connection object associated with the pool that is currently
|
||||
* idle and can be used to make a request.
|
||||
*/
|
||||
static struct evhttp_connection *
|
||||
evrpc_pool_find_connection(struct evrpc_pool *pool)
|
||||
{
|
||||
struct evhttp_connection *connection;
|
||||
TAILQ_FOREACH(connection, &pool->connections, next) {
|
||||
if (TAILQ_FIRST(&connection->requests) == NULL)
|
||||
return (connection);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume that the ctx is no longer queued on the pool.
|
||||
*/
|
||||
static int
|
||||
evrpc_schedule_request(struct evhttp_connection *connection,
|
||||
struct evrpc_request_wrapper *ctx)
|
||||
{
|
||||
struct evhttp_request *req = NULL;
|
||||
struct evrpc_pool *pool = ctx->pool;
|
||||
struct evrpc_status status;
|
||||
char *uri = NULL;
|
||||
int res = 0;
|
||||
|
||||
if ((req = evhttp_request_new(evrpc_reply_done, ctx)) == NULL)
|
||||
goto error;
|
||||
|
||||
/* serialize the request data into the output buffer */
|
||||
ctx->request_marshal(req->output_buffer, ctx->request);
|
||||
|
||||
uri = evrpc_construct_uri(ctx->name);
|
||||
if (uri == NULL)
|
||||
goto error;
|
||||
|
||||
/* we need to know the connection that we might have to abort */
|
||||
ctx->evcon = connection;
|
||||
|
||||
/* apply hooks to the outgoing request */
|
||||
if (evrpc_process_hooks(&pool->output_hooks,
|
||||
req, req->output_buffer) == -1)
|
||||
goto error;
|
||||
|
||||
if (pool->timeout > 0) {
|
||||
/*
|
||||
* a timeout after which the whole rpc is going to be aborted.
|
||||
*/
|
||||
struct timeval tv;
|
||||
evutil_timerclear(&tv);
|
||||
tv.tv_sec = pool->timeout;
|
||||
evtimer_add(&ctx->ev_timeout, &tv);
|
||||
}
|
||||
|
||||
/* start the request over the connection */
|
||||
res = evhttp_make_request(connection, req, EVHTTP_REQ_POST, uri);
|
||||
free(uri);
|
||||
|
||||
if (res == -1)
|
||||
goto error;
|
||||
|
||||
return (0);
|
||||
|
||||
error:
|
||||
memset(&status, 0, sizeof(status));
|
||||
status.error = EVRPC_STATUS_ERR_UNSTARTED;
|
||||
(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
|
||||
evrpc_request_wrapper_free(ctx);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
evrpc_make_request(struct evrpc_request_wrapper *ctx)
|
||||
{
|
||||
struct evrpc_pool *pool = ctx->pool;
|
||||
|
||||
/* initialize the event structure for this rpc */
|
||||
evtimer_set(&ctx->ev_timeout, evrpc_request_timeout, ctx);
|
||||
if (pool->base != NULL)
|
||||
event_base_set(pool->base, &ctx->ev_timeout);
|
||||
|
||||
/* we better have some available connections on the pool */
|
||||
assert(TAILQ_FIRST(&pool->connections) != NULL);
|
||||
|
||||
/*
|
||||
* if no connection is available, we queue the request on the pool,
|
||||
* the next time a connection is empty, the rpc will be send on that.
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&pool->requests, ctx, next);
|
||||
|
||||
evrpc_pool_schedule(pool);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
evrpc_reply_done(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evrpc_request_wrapper *ctx = arg;
|
||||
struct evrpc_pool *pool = ctx->pool;
|
||||
struct evrpc_status status;
|
||||
int res = -1;
|
||||
|
||||
/* cancel any timeout we might have scheduled */
|
||||
event_del(&ctx->ev_timeout);
|
||||
|
||||
memset(&status, 0, sizeof(status));
|
||||
status.http_req = req;
|
||||
|
||||
/* we need to get the reply now */
|
||||
if (req != NULL) {
|
||||
/* apply hooks to the incoming request */
|
||||
if (evrpc_process_hooks(&pool->input_hooks,
|
||||
req, req->input_buffer) == -1) {
|
||||
status.error = EVRPC_STATUS_ERR_HOOKABORTED;
|
||||
res = -1;
|
||||
} else {
|
||||
res = ctx->reply_unmarshal(ctx->reply,
|
||||
req->input_buffer);
|
||||
if (res == -1) {
|
||||
status.error = EVRPC_STATUS_ERR_BADPAYLOAD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status.error = EVRPC_STATUS_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (res == -1) {
|
||||
/* clear everything that we might have written previously */
|
||||
ctx->reply_clear(ctx->reply);
|
||||
}
|
||||
|
||||
(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
|
||||
|
||||
evrpc_request_wrapper_free(ctx);
|
||||
|
||||
/* the http layer owns the request structure */
|
||||
|
||||
/* see if we can schedule another request */
|
||||
evrpc_pool_schedule(pool);
|
||||
}
|
||||
|
||||
static void
|
||||
evrpc_pool_schedule(struct evrpc_pool *pool)
|
||||
{
|
||||
struct evrpc_request_wrapper *ctx = TAILQ_FIRST(&pool->requests);
|
||||
struct evhttp_connection *evcon;
|
||||
|
||||
/* if no requests are pending, we have no work */
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
if ((evcon = evrpc_pool_find_connection(pool)) != NULL) {
|
||||
TAILQ_REMOVE(&pool->requests, ctx, next);
|
||||
evrpc_schedule_request(evcon, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
evrpc_request_timeout(int fd, short what, void *arg)
|
||||
{
|
||||
struct evrpc_request_wrapper *ctx = arg;
|
||||
struct evhttp_connection *evcon = ctx->evcon;
|
||||
assert(evcon != NULL);
|
||||
|
||||
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
|
||||
}
|
||||
486
third-party/webrtc/dependencies/base/third_party/libevent/evrpc.h
vendored
Normal file
486
third-party/webrtc/dependencies/base/third_party/libevent/evrpc.h
vendored
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
* Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _EVRPC_H_
|
||||
#define _EVRPC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @file evrpc.h
|
||||
*
|
||||
* This header files provides basic support for an RPC server and client.
|
||||
*
|
||||
* To support RPCs in a server, every supported RPC command needs to be
|
||||
* defined and registered.
|
||||
*
|
||||
* EVRPC_HEADER(SendCommand, Request, Reply);
|
||||
*
|
||||
* SendCommand is the name of the RPC command.
|
||||
* Request is the name of a structure generated by event_rpcgen.py.
|
||||
* It contains all parameters relating to the SendCommand RPC. The
|
||||
* server needs to fill in the Reply structure.
|
||||
* Reply is the name of a structure generated by event_rpcgen.py. It
|
||||
* contains the answer to the RPC.
|
||||
*
|
||||
* To register an RPC with an HTTP server, you need to first create an RPC
|
||||
* base with:
|
||||
*
|
||||
* struct evrpc_base *base = evrpc_init(http);
|
||||
*
|
||||
* A specific RPC can then be registered with
|
||||
*
|
||||
* EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg);
|
||||
*
|
||||
* when the server receives an appropriately formatted RPC, the user callback
|
||||
* is invokved. The callback needs to fill in the reply structure.
|
||||
*
|
||||
* void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg);
|
||||
*
|
||||
* To send the reply, call EVRPC_REQUEST_DONE(rpc);
|
||||
*
|
||||
* See the regression test for an example.
|
||||
*/
|
||||
|
||||
struct evbuffer;
|
||||
struct event_base;
|
||||
struct evrpc_req_generic;
|
||||
|
||||
/* Encapsulates a request */
|
||||
struct evrpc {
|
||||
TAILQ_ENTRY(evrpc) next;
|
||||
|
||||
/* the URI at which the request handler lives */
|
||||
const char* uri;
|
||||
|
||||
/* creates a new request structure */
|
||||
void *(*request_new)(void);
|
||||
|
||||
/* frees the request structure */
|
||||
void (*request_free)(void *);
|
||||
|
||||
/* unmarshals the buffer into the proper request structure */
|
||||
int (*request_unmarshal)(void *, struct evbuffer *);
|
||||
|
||||
/* creates a new reply structure */
|
||||
void *(*reply_new)(void);
|
||||
|
||||
/* creates a new reply structure */
|
||||
void (*reply_free)(void *);
|
||||
|
||||
/* verifies that the reply is valid */
|
||||
int (*reply_complete)(void *);
|
||||
|
||||
/* marshals the reply into a buffer */
|
||||
void (*reply_marshal)(struct evbuffer*, void *);
|
||||
|
||||
/* the callback invoked for each received rpc */
|
||||
void (*cb)(struct evrpc_req_generic *, void *);
|
||||
void *cb_arg;
|
||||
|
||||
/* reference for further configuration */
|
||||
struct evrpc_base *base;
|
||||
};
|
||||
|
||||
/** The type of a specific RPC Message
|
||||
*
|
||||
* @param rpcname the name of the RPC message
|
||||
*/
|
||||
#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname
|
||||
|
||||
struct evhttp_request;
|
||||
struct evrpc_status;
|
||||
|
||||
/* We alias the RPC specific structs to this voided one */
|
||||
struct evrpc_req_generic {
|
||||
/* the unmarshaled request object */
|
||||
void *request;
|
||||
|
||||
/* the empty reply object that needs to be filled in */
|
||||
void *reply;
|
||||
|
||||
/*
|
||||
* the static structure for this rpc; that can be used to
|
||||
* automatically unmarshal and marshal the http buffers.
|
||||
*/
|
||||
struct evrpc *rpc;
|
||||
|
||||
/*
|
||||
* the http request structure on which we need to answer.
|
||||
*/
|
||||
struct evhttp_request* http_req;
|
||||
|
||||
/*
|
||||
* callback to reply and finish answering this rpc
|
||||
*/
|
||||
void (*done)(struct evrpc_req_generic* rpc);
|
||||
};
|
||||
|
||||
/** Creates the definitions and prototypes for an RPC
|
||||
*
|
||||
* You need to use EVRPC_HEADER to create structures and function prototypes
|
||||
* needed by the server and client implementation. The structures have to be
|
||||
* defined in an .rpc file and converted to source code via event_rpcgen.py
|
||||
*
|
||||
* @param rpcname the name of the RPC
|
||||
* @param reqstruct the name of the RPC request structure
|
||||
* @param replystruct the name of the RPC reply structure
|
||||
* @see EVRPC_GENERATE()
|
||||
*/
|
||||
#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \
|
||||
EVRPC_STRUCT(rpcname) { \
|
||||
struct reqstruct* request; \
|
||||
struct rplystruct* reply; \
|
||||
struct evrpc* rpc; \
|
||||
struct evhttp_request* http_req; \
|
||||
void (*done)(struct evrpc_status *, \
|
||||
struct evrpc* rpc, void *request, void *reply); \
|
||||
}; \
|
||||
int evrpc_send_request_##rpcname(struct evrpc_pool *, \
|
||||
struct reqstruct *, struct rplystruct *, \
|
||||
void (*)(struct evrpc_status *, \
|
||||
struct reqstruct *, struct rplystruct *, void *cbarg), \
|
||||
void *);
|
||||
|
||||
/** Generates the code for receiving and sending an RPC message
|
||||
*
|
||||
* EVRPC_GENERATE is used to create the code corresponding to sending
|
||||
* and receiving a particular RPC message
|
||||
*
|
||||
* @param rpcname the name of the RPC
|
||||
* @param reqstruct the name of the RPC request structure
|
||||
* @param replystruct the name of the RPC reply structure
|
||||
* @see EVRPC_HEADER()
|
||||
*/
|
||||
#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \
|
||||
int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \
|
||||
struct reqstruct *request, struct rplystruct *reply, \
|
||||
void (*cb)(struct evrpc_status *, \
|
||||
struct reqstruct *, struct rplystruct *, void *cbarg), \
|
||||
void *cbarg) { \
|
||||
struct evrpc_status status; \
|
||||
struct evrpc_request_wrapper *ctx; \
|
||||
ctx = (struct evrpc_request_wrapper *) \
|
||||
malloc(sizeof(struct evrpc_request_wrapper)); \
|
||||
if (ctx == NULL) \
|
||||
goto error; \
|
||||
ctx->pool = pool; \
|
||||
ctx->evcon = NULL; \
|
||||
ctx->name = strdup(#rpcname); \
|
||||
if (ctx->name == NULL) { \
|
||||
free(ctx); \
|
||||
goto error; \
|
||||
} \
|
||||
ctx->cb = (void (*)(struct evrpc_status *, \
|
||||
void *, void *, void *))cb; \
|
||||
ctx->cb_arg = cbarg; \
|
||||
ctx->request = (void *)request; \
|
||||
ctx->reply = (void *)reply; \
|
||||
ctx->request_marshal = (void (*)(struct evbuffer *, void *))reqstruct##_marshal; \
|
||||
ctx->reply_clear = (void (*)(void *))rplystruct##_clear; \
|
||||
ctx->reply_unmarshal = (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal; \
|
||||
return (evrpc_make_request(ctx)); \
|
||||
error: \
|
||||
memset(&status, 0, sizeof(status)); \
|
||||
status.error = EVRPC_STATUS_ERR_UNSTARTED; \
|
||||
(*(cb))(&status, request, reply, cbarg); \
|
||||
return (-1); \
|
||||
}
|
||||
|
||||
/** Provides access to the HTTP request object underlying an RPC
|
||||
*
|
||||
* Access to the underlying http object; can be used to look at headers or
|
||||
* for getting the remote ip address
|
||||
*
|
||||
* @param rpc_req the rpc request structure provided to the server callback
|
||||
* @return an struct evhttp_request object that can be inspected for
|
||||
* HTTP headers or sender information.
|
||||
*/
|
||||
#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req
|
||||
|
||||
/** Creates the reply to an RPC request
|
||||
*
|
||||
* EVRPC_REQUEST_DONE is used to answer a request; the reply is expected
|
||||
* to have been filled in. The request and reply pointers become invalid
|
||||
* after this call has finished.
|
||||
*
|
||||
* @param rpc_req the rpc request structure provided to the server callback
|
||||
*/
|
||||
#define EVRPC_REQUEST_DONE(rpc_req) do { \
|
||||
struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \
|
||||
_req->done(_req); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Takes a request object and fills it in with the right magic */
|
||||
#define EVRPC_REGISTER_OBJECT(rpc, name, request, reply) \
|
||||
do { \
|
||||
(rpc)->uri = strdup(#name); \
|
||||
if ((rpc)->uri == NULL) { \
|
||||
fprintf(stderr, "failed to register object\n"); \
|
||||
exit(1); \
|
||||
} \
|
||||
(rpc)->request_new = (void *(*)(void))request##_new; \
|
||||
(rpc)->request_free = (void (*)(void *))request##_free; \
|
||||
(rpc)->request_unmarshal = (int (*)(void *, struct evbuffer *))request##_unmarshal; \
|
||||
(rpc)->reply_new = (void *(*)(void))reply##_new; \
|
||||
(rpc)->reply_free = (void (*)(void *))reply##_free; \
|
||||
(rpc)->reply_complete = (int (*)(void *))reply##_complete; \
|
||||
(rpc)->reply_marshal = (void (*)(struct evbuffer*, void *))reply##_marshal; \
|
||||
} while (0)
|
||||
|
||||
struct evrpc_base;
|
||||
struct evhttp;
|
||||
|
||||
/* functions to start up the rpc system */
|
||||
|
||||
/** Creates a new rpc base from which RPC requests can be received
|
||||
*
|
||||
* @param server a pointer to an existing HTTP server
|
||||
* @return a newly allocated evrpc_base struct
|
||||
* @see evrpc_free()
|
||||
*/
|
||||
struct evrpc_base *evrpc_init(struct evhttp *server);
|
||||
|
||||
/**
|
||||
* Frees the evrpc base
|
||||
*
|
||||
* For now, you are responsible for making sure that no rpcs are ongoing.
|
||||
*
|
||||
* @param base the evrpc_base object to be freed
|
||||
* @see evrpc_init
|
||||
*/
|
||||
void evrpc_free(struct evrpc_base *base);
|
||||
|
||||
/** register RPCs with the HTTP Server
|
||||
*
|
||||
* registers a new RPC with the HTTP server, each RPC needs to have
|
||||
* a unique name under which it can be identified.
|
||||
*
|
||||
* @param base the evrpc_base structure in which the RPC should be
|
||||
* registered.
|
||||
* @param name the name of the RPC
|
||||
* @param request the name of the RPC request structure
|
||||
* @param reply the name of the RPC reply structure
|
||||
* @param callback the callback that should be invoked when the RPC
|
||||
* is received. The callback has the following prototype
|
||||
* void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg)
|
||||
* @param cbarg an additional parameter that can be passed to the callback.
|
||||
* The parameter can be used to carry around state.
|
||||
*/
|
||||
#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \
|
||||
do { \
|
||||
struct evrpc* rpc = (struct evrpc *)calloc(1, sizeof(struct evrpc)); \
|
||||
EVRPC_REGISTER_OBJECT(rpc, name, request, reply); \
|
||||
evrpc_register_rpc(base, rpc, \
|
||||
(void (*)(struct evrpc_req_generic*, void *))callback, cbarg); \
|
||||
} while (0)
|
||||
|
||||
int evrpc_register_rpc(struct evrpc_base *, struct evrpc *,
|
||||
void (*)(struct evrpc_req_generic*, void *), void *);
|
||||
|
||||
/**
|
||||
* Unregisters an already registered RPC
|
||||
*
|
||||
* @param base the evrpc_base object from which to unregister an RPC
|
||||
* @param name the name of the rpc to unregister
|
||||
* @return -1 on error or 0 when successful.
|
||||
* @see EVRPC_REGISTER()
|
||||
*/
|
||||
#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc(base, #name)
|
||||
|
||||
int evrpc_unregister_rpc(struct evrpc_base *base, const char *name);
|
||||
|
||||
/*
|
||||
* Client-side RPC support
|
||||
*/
|
||||
|
||||
struct evrpc_pool;
|
||||
struct evhttp_connection;
|
||||
|
||||
/**
|
||||
* provides information about the completed RPC request.
|
||||
*/
|
||||
struct evrpc_status {
|
||||
#define EVRPC_STATUS_ERR_NONE 0
|
||||
#define EVRPC_STATUS_ERR_TIMEOUT 1
|
||||
#define EVRPC_STATUS_ERR_BADPAYLOAD 2
|
||||
#define EVRPC_STATUS_ERR_UNSTARTED 3
|
||||
#define EVRPC_STATUS_ERR_HOOKABORTED 4
|
||||
int error;
|
||||
|
||||
/* for looking at headers or other information */
|
||||
struct evhttp_request *http_req;
|
||||
};
|
||||
|
||||
struct evrpc_request_wrapper {
|
||||
TAILQ_ENTRY(evrpc_request_wrapper) next;
|
||||
|
||||
/* pool on which this rpc request is being made */
|
||||
struct evrpc_pool *pool;
|
||||
|
||||
/* connection on which the request is being sent */
|
||||
struct evhttp_connection *evcon;
|
||||
|
||||
/* event for implementing request timeouts */
|
||||
struct event ev_timeout;
|
||||
|
||||
/* the name of the rpc */
|
||||
char *name;
|
||||
|
||||
/* callback */
|
||||
void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg);
|
||||
void *cb_arg;
|
||||
|
||||
void *request;
|
||||
void *reply;
|
||||
|
||||
/* unmarshals the buffer into the proper request structure */
|
||||
void (*request_marshal)(struct evbuffer *, void *);
|
||||
|
||||
/* removes all stored state in the reply */
|
||||
void (*reply_clear)(void *);
|
||||
|
||||
/* marshals the reply into a buffer */
|
||||
int (*reply_unmarshal)(void *, struct evbuffer*);
|
||||
};
|
||||
|
||||
/** launches an RPC and sends it to the server
|
||||
*
|
||||
* EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server.
|
||||
*
|
||||
* @param name the name of the RPC
|
||||
* @param pool the evrpc_pool that contains the connection objects over which
|
||||
* the request should be sent.
|
||||
* @param request a pointer to the RPC request structure - it contains the
|
||||
* data to be sent to the server.
|
||||
* @param reply a pointer to the RPC reply structure. It is going to be filled
|
||||
* if the request was answered successfully
|
||||
* @param cb the callback to invoke when the RPC request has been answered
|
||||
* @param cbarg an additional argument to be passed to the client
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \
|
||||
evrpc_send_request_##name(pool, request, reply, cb, cbarg)
|
||||
|
||||
int evrpc_make_request(struct evrpc_request_wrapper *);
|
||||
|
||||
/** creates an rpc connection pool
|
||||
*
|
||||
* a pool has a number of connections associated with it.
|
||||
* rpc requests are always made via a pool.
|
||||
*
|
||||
* @param base a pointer to an struct event_based object; can be left NULL
|
||||
* in singled-threaded applications
|
||||
* @return a newly allocated struct evrpc_pool object
|
||||
* @see evrpc_pool_free()
|
||||
*/
|
||||
struct evrpc_pool *evrpc_pool_new(struct event_base *base);
|
||||
/** frees an rpc connection pool
|
||||
*
|
||||
* @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new()
|
||||
* @see evrpc_pool_new()
|
||||
*/
|
||||
void evrpc_pool_free(struct evrpc_pool *pool);
|
||||
/*
|
||||
* adds a connection over which rpc can be dispatched. the connection
|
||||
* object must have been newly created.
|
||||
*/
|
||||
void evrpc_pool_add_connection(struct evrpc_pool *,
|
||||
struct evhttp_connection *);
|
||||
|
||||
/**
|
||||
* Sets the timeout in secs after which a request has to complete. The
|
||||
* RPC is completely aborted if it does not complete by then. Setting
|
||||
* the timeout to 0 means that it never timeouts and can be used to
|
||||
* implement callback type RPCs.
|
||||
*
|
||||
* Any connection already in the pool will be updated with the new
|
||||
* timeout. Connections added to the pool after set_timeout has be
|
||||
* called receive the pool timeout only if no timeout has been set
|
||||
* for the connection itself.
|
||||
*
|
||||
* @param pool a pointer to a struct evrpc_pool object
|
||||
* @param timeout_in_secs the number of seconds after which a request should
|
||||
* timeout and a failure be returned to the callback.
|
||||
*/
|
||||
void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs);
|
||||
|
||||
/**
|
||||
* Hooks for changing the input and output of RPCs; this can be used to
|
||||
* implement compression, authentication, encryption, ...
|
||||
*/
|
||||
|
||||
enum EVRPC_HOOK_TYPE {
|
||||
EVRPC_INPUT, /**< apply the function to an input hook */
|
||||
EVRPC_OUTPUT /**< apply the function to an output hook */
|
||||
};
|
||||
|
||||
#ifndef WIN32
|
||||
/** Deprecated alias for EVRPC_INPUT. Not available on windows, where it
|
||||
* conflicts with platform headers. */
|
||||
#define INPUT EVRPC_INPUT
|
||||
/** Deprecated alias for EVRPC_OUTPUT. Not available on windows, where it
|
||||
* conflicts with platform headers. */
|
||||
#define OUTPUT EVRPC_OUTPUT
|
||||
#endif
|
||||
|
||||
/** adds a processing hook to either an rpc base or rpc pool
|
||||
*
|
||||
* If a hook returns -1, the processing is aborted.
|
||||
*
|
||||
* The add functions return handles that can be used for removing hooks.
|
||||
*
|
||||
* @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
|
||||
* @param hook_type either INPUT or OUTPUT
|
||||
* @param cb the callback to call when the hook is activated
|
||||
* @param cb_arg an additional argument for the callback
|
||||
* @return a handle to the hook so it can be removed later
|
||||
* @see evrpc_remove_hook()
|
||||
*/
|
||||
void *evrpc_add_hook(void *vbase,
|
||||
enum EVRPC_HOOK_TYPE hook_type,
|
||||
int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
|
||||
void *cb_arg);
|
||||
|
||||
/** removes a previously added hook
|
||||
*
|
||||
* @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
|
||||
* @param hook_type either INPUT or OUTPUT
|
||||
* @param handle a handle returned by evrpc_add_hook()
|
||||
* @return 1 on success or 0 on failure
|
||||
* @see evrpc_add_hook()
|
||||
*/
|
||||
int evrpc_remove_hook(void *vbase,
|
||||
enum EVRPC_HOOK_TYPE hook_type,
|
||||
void *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EVRPC_H_ */
|
||||
52
third-party/webrtc/dependencies/base/third_party/libevent/evsignal.h
vendored
Normal file
52
third-party/webrtc/dependencies/base/third_party/libevent/evsignal.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _EVSIGNAL_H_
|
||||
#define _EVSIGNAL_H_
|
||||
|
||||
typedef void (*ev_sighandler_t)(int);
|
||||
|
||||
struct evsignal_info {
|
||||
struct event ev_signal;
|
||||
int ev_signal_pair[2];
|
||||
int ev_signal_added;
|
||||
volatile sig_atomic_t evsignal_caught;
|
||||
struct event_list evsigevents[NSIG];
|
||||
sig_atomic_t evsigcaught[NSIG];
|
||||
#ifdef HAVE_SIGACTION
|
||||
struct sigaction **sh_old;
|
||||
#else
|
||||
ev_sighandler_t **sh_old;
|
||||
#endif
|
||||
int sh_old_max;
|
||||
};
|
||||
int evsignal_init(struct event_base *);
|
||||
void evsignal_process(struct event_base *);
|
||||
int evsignal_add(struct event *);
|
||||
int evsignal_del(struct event *);
|
||||
void evsignal_dealloc(struct event_base *);
|
||||
|
||||
#endif /* _EVSIGNAL_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue