Merge remote-tracking branch 'upstream/master' into master-windows-changes
This commit is contained in:
commit
7603990740
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
fetch-depth: 0
|
||||
- run: sudo apt-get update
|
||||
- run: sudo apt-get remove libunwind-14-dev
|
||||
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev
|
||||
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev libsignal-protocol-c-dev
|
||||
- run: ./configure --with-tests --with-libsignal-in-tree
|
||||
- run: make
|
||||
- run: build/xmpp-vala-test
|
||||
|
@ -36,4 +36,5 @@ jobs:
|
|||
fetch-depth: 0
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
||||
with:
|
||||
manifest-path: im.dino.Dino.json
|
||||
manifest-path: im.dino.Dino.json
|
||||
build-bundle: false
|
||||
|
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -1,4 +0,0 @@
|
|||
[submodule "libsignal-protocol-c"]
|
||||
path = plugins/signal-protocol/libsignal-protocol-c
|
||||
url = https://github.com/WhisperSystems/libsignal-protocol-c.git
|
||||
branch = v2.3.3
|
53
configure
vendored
53
configure
vendored
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
OPTS=`getopt -o "h" --long \
|
||||
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsignal-in-tree,with-libsoup3,\
|
||||
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsoup3,\
|
||||
enable-plugin:,disable-plugin:,\
|
||||
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
|
||||
bindir:,libdir:,includedir:,datadir:,\
|
||||
|
@ -15,13 +15,11 @@ eval set -- "$OPTS"
|
|||
PREFIX=${PREFIX:-/usr/local}
|
||||
ENABLED_PLUGINS=
|
||||
DISABLED_PLUGINS=
|
||||
BUILD_LIBSIGNAL_IN_TREE=
|
||||
BUILD_TESTS=
|
||||
BUILD_TYPE=Debug
|
||||
DISABLE_FAST_VAPI=
|
||||
LIB_SUFFIX=
|
||||
NO_DEBUG=
|
||||
FETCH_ONLY=
|
||||
USE_SOUP3=
|
||||
|
||||
EXEC_PREFIX=
|
||||
|
@ -55,8 +53,6 @@ Configuration:
|
|||
without network access later and exit.
|
||||
--no-debug Build without debug symbols
|
||||
--release Configure to build an optimized release version
|
||||
--with-libsignal-in-tree Build libsignal-protocol-c in tree and link it
|
||||
statically.
|
||||
--with-libsoup3 Build with libsoup-3.0
|
||||
--with-tests Also build tests.
|
||||
|
||||
|
@ -112,11 +108,9 @@ while true; do
|
|||
--valac ) VALA_EXECUTABLE="$2"; shift; shift ;;
|
||||
--valac-flags ) VALAC_FLAGS="$2"; shift; shift ;;
|
||||
--lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;;
|
||||
--with-libsignal-in-tree ) BUILD_LIBSIGNAL_IN_TREE=yes; shift ;;
|
||||
--with-libsoup3 ) USE_SOUP3=yes; shift ;;
|
||||
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
|
||||
--no-debug ) NO_DEBUG=yes; shift ;;
|
||||
--fetch-only ) FETCH_ONLY=yes; shift ;;
|
||||
--release ) BUILD_TYPE=RelWithDebInfo; shift ;;
|
||||
--with-tests ) BUILD_TESTS=yes; shift ;;
|
||||
# Autotools paths
|
||||
|
@ -141,50 +135,6 @@ while true; do
|
|||
esac
|
||||
done
|
||||
|
||||
if [ "$BUILD_LIBSIGNAL_IN_TREE" = "yes" ] || [ "$FETCH_ONLY" = "yes" ]; then
|
||||
if [ -d ".git" ]; then
|
||||
git submodule update --init 2>/dev/null
|
||||
else
|
||||
tmp=0
|
||||
for i in $(cat .gitmodules | grep -n submodule | awk -F ':' '{print $1}') $(wc -l .gitmodules | awk '{print $1}'); do
|
||||
if ! [ $tmp -eq 0 ]; then
|
||||
name=$(cat .gitmodules | head -n $tmp | tail -n 1 | awk -F '"' '{print $2}')
|
||||
def=$(cat .gitmodules | head -n $i | tail -n $(expr "$i" - "$tmp") | awk -F ' ' '{print $1 $2 $3}')
|
||||
path=$(echo "$def" | grep '^path=' | awk -F '=' '{print $2}')
|
||||
url=$(echo "$def" | grep '^url=' | awk -F '=' '{print $2}')
|
||||
branch=$(echo "$def" | grep '^branch=' | awk -F '=' '{print $2}')
|
||||
|
||||
if ! ls "$path"/* >/dev/null 2>/dev/null; then
|
||||
git=$(which git)
|
||||
if ! [ $? -eq 0 ] || ! [ -x $git ]; then
|
||||
echo "Failed retrieving missing files"
|
||||
exit 5
|
||||
fi
|
||||
res=$(git clone "$url" "$path" 2>&1)
|
||||
if ! [ $? -eq 0 ] || ! [ -d $path ]; then
|
||||
echo "Failed retrieving missing files: $res"
|
||||
exit 5
|
||||
fi
|
||||
if [ -n "$branch" ]; then
|
||||
olddir="$(pwd)"
|
||||
cd "$path"
|
||||
res=$(git checkout "$branch" 2>&1)
|
||||
if ! [ $? -eq 0 ]; then
|
||||
echo "Failed retrieving missing files: $res"
|
||||
exit 5
|
||||
fi
|
||||
cd "$olddir"
|
||||
fi
|
||||
echo "Submodule path '$path': checked out '$branch' (via git clone)"
|
||||
fi
|
||||
fi
|
||||
tmp=$i
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FETCH_ONLY" = "yes" ]; then exit 0; fi
|
||||
|
||||
if [ ! -x "$(which cmake 2>/dev/null)" ]
|
||||
then
|
||||
echo "-!- CMake required."
|
||||
|
@ -258,7 +208,6 @@ cmake -G "$cmake_type" \
|
|||
-DENABLED_PLUGINS="$ENABLED_PLUGINS" \
|
||||
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
|
||||
-DBUILD_TESTS="$BUILD_TESTS" \
|
||||
-DBUILD_LIBSIGNAL_IN_TREE="$BUILD_LIBSIGNAL_IN_TREE" \
|
||||
-DUSE_SOUP3="$USE_SOUP3" \
|
||||
-DVALA_EXECUTABLE="$VALAC" \
|
||||
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
"--socket=wayland",
|
||||
"--socket=pulseaudio",
|
||||
"--socket=gpg-agent",
|
||||
"--filesystem=xdg-run/pipewire-0",
|
||||
"--share=network",
|
||||
"--device=all",
|
||||
"--device=dri",
|
||||
"--talk-name=org.freedesktop.Notifications"
|
||||
],
|
||||
"modules": [
|
||||
|
|
|
@ -18,7 +18,6 @@ public class Account : Object {
|
|||
public string? alias { get; set; }
|
||||
public bool enabled { get; set; default = false; }
|
||||
public string? roster_version { get; set; }
|
||||
public DateTime mam_earliest_synced { get; set; default=new DateTime.from_unix_utc(0); }
|
||||
|
||||
private Database? db;
|
||||
|
||||
|
@ -50,7 +49,6 @@ public class Account : Object {
|
|||
alias = row[db.account.alias];
|
||||
enabled = row[db.account.enabled];
|
||||
roster_version = row[db.account.roster_version];
|
||||
mam_earliest_synced = new DateTime.from_unix_utc(row[db.account.mam_earliest_synced]);
|
||||
|
||||
notify.connect(on_update);
|
||||
}
|
||||
|
@ -66,7 +64,6 @@ public class Account : Object {
|
|||
.value(db.account.alias, alias)
|
||||
.value(db.account.enabled, enabled)
|
||||
.value(db.account.roster_version, roster_version)
|
||||
.value(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix())
|
||||
.perform();
|
||||
|
||||
notify.connect(on_update);
|
||||
|
@ -106,8 +103,6 @@ public class Account : Object {
|
|||
update.set(db.account.enabled, enabled); break;
|
||||
case "roster-version":
|
||||
update.set(db.account.roster_version, roster_version); break;
|
||||
case "mam-earliest-synced":
|
||||
update.set(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()); break;
|
||||
}
|
||||
update.perform();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ public class Database : Qlite.Database {
|
|||
public Column<string> alias = new Column.Text("alias");
|
||||
public Column<bool> enabled = new Column.BoolInt("enabled");
|
||||
public Column<string> roster_version = new Column.Text("roster_version") { min_version=2 };
|
||||
// no longer used. all usages already removed. remove db column at some point.
|
||||
public Column<long> mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 };
|
||||
|
||||
internal AccountTable(Database db) {
|
||||
|
|
|
@ -90,11 +90,9 @@ public class Dino.HistorySync {
|
|||
if (!is_muc_mam && !from_our_server) return;
|
||||
|
||||
// Get the server time of the message and store it in `mam_times`
|
||||
Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null;
|
||||
if (mam_flag == null) return;
|
||||
string? id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", "id");
|
||||
string? id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", "id");
|
||||
if (id == null) return;
|
||||
StanzaNode? delay_node = message.stanza.get_deep_subnode(mam_flag.ns_ver + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
||||
StanzaNode? delay_node = message.stanza.get_deep_subnode(Xmpp.MessageArchiveManagement.NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
||||
if (delay_node == null) {
|
||||
warning("MAM result did not contain delayed time %s", message.stanza.to_string());
|
||||
return;
|
||||
|
@ -104,7 +102,7 @@ public class Dino.HistorySync {
|
|||
mam_times[account][id] = time;
|
||||
|
||||
// Check if this is the target message
|
||||
string? query_id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", mam_flag.ns_ver + ":queryid");
|
||||
string? query_id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", Xmpp.MessageArchiveManagement.NS_URI + ":queryid");
|
||||
if (query_id != null && id == catchup_until_id[account]) {
|
||||
debug("[%s] Hitted range (id) %s", account.bare_jid.to_string(), id);
|
||||
hitted_range[query_id] = -2;
|
||||
|
|
|
@ -38,7 +38,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
|||
received_pipeline.connect(new FilterMessageListener());
|
||||
received_pipeline.connect(new StoreMessageListener(this, stream_interactor));
|
||||
received_pipeline.connect(new StoreContentItemListener(stream_interactor));
|
||||
received_pipeline.connect(new MamMessageListener(stream_interactor));
|
||||
|
||||
stream_interactor.account_added.connect(on_account_added);
|
||||
|
||||
|
@ -170,19 +169,21 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
|||
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message);
|
||||
Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null;
|
||||
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||
if (mam_message_flag != null && mam_flag != null && mam_flag.ns_ver == Xmpp.MessageArchiveManagement.NS_URI_2 && mam_message_flag.mam_id != null) {
|
||||
new_message.server_id = mam_message_flag.mam_id;
|
||||
if (mam_message_flag != null && mam_message_flag.mam_id != null) {
|
||||
bool server_does_mam = entity_info.has_feature_cached(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI);
|
||||
if (server_does_mam) {
|
||||
new_message.server_id = mam_message_flag.mam_id;
|
||||
}
|
||||
} else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
|
||||
bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||
(yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2));
|
||||
(yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
|
||||
if (server_supports_sid) {
|
||||
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
|
||||
}
|
||||
} else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) {
|
||||
bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||
(yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2));
|
||||
(yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
|
||||
if (server_supports_sid) {
|
||||
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid);
|
||||
}
|
||||
|
@ -363,29 +364,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
|||
}
|
||||
}
|
||||
|
||||
private class MamMessageListener : MessageListener {
|
||||
|
||||
public string[] after_actions_const = new string[]{ "DEDUPLICATE" };
|
||||
public override string action_group { get { return "MAM_NODE"; } }
|
||||
public override string[] after_actions { get { return after_actions_const; } }
|
||||
|
||||
private StreamInteractor stream_interactor;
|
||||
|
||||
public MamMessageListener(StreamInteractor stream_interactor) {
|
||||
this.stream_interactor = stream_interactor;
|
||||
}
|
||||
|
||||
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
|
||||
bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null;
|
||||
XmppStream? stream = stream_interactor.get_stream(conversation.account);
|
||||
Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null;
|
||||
if (is_mam_message || (mam_flag != null && mam_flag.cought_up == true)) {
|
||||
conversation.account.mam_earliest_synced = message.local_time;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Entities.Message create_out_message(string text, Conversation conversation) {
|
||||
Entities.Message message = new Entities.Message(text);
|
||||
message.type_ = Util.get_message_type_for_conversation(conversation);
|
||||
|
|
|
@ -72,7 +72,7 @@ public class MucManager : StreamInteractionModule, Object {
|
|||
|
||||
bool receive_history = true;
|
||||
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||
bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI_2);
|
||||
bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI);
|
||||
if (can_do_mam) {
|
||||
receive_history = false;
|
||||
history_since = null;
|
||||
|
|
|
@ -64,7 +64,7 @@ public class Dino.Reactions : StreamInteractionModule, Object {
|
|||
// The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids)
|
||||
var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||
bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||
(entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2));
|
||||
(entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
|
||||
if (!server_supports_sid) return false;
|
||||
|
||||
bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI);
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace Dino {
|
|||
if (self_word != null && (account.alias == null || account.alias.length == 0)) {
|
||||
return self_word;
|
||||
}
|
||||
if (account.alias != null && account.alias.length == 0) return null;
|
||||
return account.alias;
|
||||
}
|
||||
Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid);
|
||||
|
|
|
@ -396,17 +396,13 @@ box.dino-input-error .chat-input-status.input-status-highlight-once {
|
|||
text-shadow: 0 0 2px black;
|
||||
}
|
||||
|
||||
.dino-call-window .call-header-background {
|
||||
.dino-call-window .participant-header-bar {
|
||||
background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0));
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.dino-call-window .participant-header-bar button {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.dino-call-window .participant-header-bar button:hover {
|
||||
.dino-call-window .participant-header-bar button:hover:not(.close) {
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-color: rgba(255,255,255,0);
|
||||
box-shadow: none;
|
||||
|
|
|
@ -22,7 +22,12 @@ namespace Dino.Ui {
|
|||
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
|
||||
private ArrayList<string> participants = new ArrayList<string>();
|
||||
|
||||
private EventControllerFocus this_focus_events = new EventControllerFocus();
|
||||
private GestureClick this_gesture_events = new GestureClick() { touch_only=true, propagation_phase=Gtk.PropagationPhase.CAPTURE };
|
||||
private EventControllerMotion this_motion_events = new EventControllerMotion();
|
||||
private double latest_motion_x = -1;
|
||||
private double latest_motion_y = -1;
|
||||
private const double MOTION_RELEVANCE_THRESHOLD = 2;
|
||||
|
||||
private int own_video_width = 150;
|
||||
private int own_video_height = 100;
|
||||
|
@ -54,9 +59,20 @@ namespace Dino.Ui {
|
|||
this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE);
|
||||
|
||||
((Widget) this).add_controller(this_motion_events);
|
||||
this_motion_events.motion.connect(reveal_control_elements);
|
||||
this_motion_events.enter.connect(reveal_control_elements);
|
||||
this_motion_events.leave.connect(reveal_control_elements);
|
||||
this_motion_events.motion.connect((x, y) => {
|
||||
if ((latest_motion_x - x).abs() <= MOTION_RELEVANCE_THRESHOLD && (latest_motion_y - y).abs() <= MOTION_RELEVANCE_THRESHOLD) return;
|
||||
|
||||
latest_motion_x = x;
|
||||
latest_motion_y = y;
|
||||
reveal_control_elements();
|
||||
});
|
||||
|
||||
((Widget) this).add_controller(this_focus_events);
|
||||
this_focus_events.enter.connect(reveal_control_elements);
|
||||
this_focus_events.leave.connect(reveal_control_elements);
|
||||
|
||||
((Widget) this).add_controller(this_gesture_events);
|
||||
this_gesture_events.pressed.connect_after(reveal_control_elements);
|
||||
|
||||
this.notify["default-width"].connect(reveal_control_elements);
|
||||
this.notify["default-height"].connect(reveal_control_elements);
|
||||
|
|
|
@ -74,14 +74,11 @@ namespace Dino.Ui {
|
|||
|
||||
header_bar.show_title_buttons = is_highest_row;
|
||||
if (is_highest_row) {
|
||||
header_bar.add_css_class("call-header-background");
|
||||
Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
|
||||
if (gtk_settings != null) {
|
||||
string[] buttons = gtk_settings.gtk_decoration_layout.split(":");
|
||||
header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : "");
|
||||
}
|
||||
} else {
|
||||
header_bar.remove_css_class("call-header-background");
|
||||
}
|
||||
reveal_or_hide_controls();
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ public class View : Box {
|
|||
chooser.emoji_picked.connect((emoji) => {
|
||||
chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
|
||||
});
|
||||
chooser.closed.connect(do_focus);
|
||||
|
||||
emoji_button.set_popover(chooser);
|
||||
|
||||
file_button.tooltip_text = Util.string_if_tooltips_active(_("Send a file"));
|
||||
|
|
|
@ -215,7 +215,12 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
|
|||
}
|
||||
if (new_width == active_caps_width) return;
|
||||
int new_height = device_caps_height * new_width / device_caps_width;
|
||||
Gst.Caps new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null);
|
||||
Gst.Caps new_caps;
|
||||
if (device_caps_framerate_den != 0) {
|
||||
new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null);
|
||||
} else {
|
||||
new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, null);
|
||||
}
|
||||
double required_bitrate = get_target_bitrate(new_caps);
|
||||
debug("Changing resolution width from %d to %d (requires bitrate %f, current target is %u)", active_caps_width, new_width, required_bitrate, bitrate);
|
||||
if (bitrate < required_bitrate && new_width > active_caps_width) return;
|
||||
|
@ -347,7 +352,7 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
|
|||
if (media == "audio") {
|
||||
return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1");
|
||||
} else if (media == "video" && device.caps.get_size() > 0) {
|
||||
int best_index = 0;
|
||||
int best_index = -1;
|
||||
Value? best_fraction = null;
|
||||
int best_fps = 0;
|
||||
int best_width = 0;
|
||||
|
@ -390,10 +395,25 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
|
|||
best_fraction = best_fraction_now;
|
||||
}
|
||||
}
|
||||
if (best_index == -1) {
|
||||
// No caps in first round, try without framerate
|
||||
for (int i = 0; i < device.caps.get_size(); i++) {
|
||||
unowned Gst.Structure? that = device.caps.get_structure(i);
|
||||
if (!that.has_name("video/x-raw")) continue;
|
||||
int width = 0, height = 0;
|
||||
if (!that.has_field("width") || !that.get_int("width", out width)) continue;
|
||||
if (!that.has_field("height") || !that.get_int("height", out height)) continue;
|
||||
if (best_width < width || best_width == width && best_height < height) {
|
||||
best_width = width;
|
||||
best_height = height;
|
||||
best_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
Gst.Caps res = caps_copy_nth(device.caps, best_index);
|
||||
unowned Gst.Structure? that = res.get_structure(0);
|
||||
Value framerate = that.get_value("framerate");
|
||||
if (framerate.type() == typeof(Gst.ValueList)) {
|
||||
Value? framerate = that.get_value("framerate");
|
||||
if (framerate != null && framerate.type() == typeof(Gst.ValueList) && best_fraction != null) {
|
||||
that.set_value("framerate", best_fraction);
|
||||
}
|
||||
debug("Selected caps %s", res.to_string());
|
||||
|
|
|
@ -428,11 +428,11 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
|
|||
|
||||
if (media == "video") {
|
||||
// Pick best FPS
|
||||
int max_fps = 0;
|
||||
int max_fps = -1;
|
||||
Device? max_fps_device = null;
|
||||
foreach (Device device in devices) {
|
||||
int fps = get_max_fps(device);
|
||||
if (fps > max_fps) {
|
||||
if (fps > max_fps || max_fps_device == null) {
|
||||
max_fps = fps;
|
||||
max_fps_device = device;
|
||||
}
|
||||
|
|
|
@ -102,6 +102,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
|||
send_rtp.drop = true;
|
||||
send_rtp.wait_on_eos = false;
|
||||
send_rtp.new_sample.connect(on_new_sample);
|
||||
#if GST_1_20
|
||||
send_rtp.new_serialized_event.connect(on_new_event);
|
||||
#endif
|
||||
send_rtp.connect("signal::eos", on_eos_static, this);
|
||||
pipe.add(send_rtp);
|
||||
|
||||
|
@ -294,6 +297,60 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
|||
}
|
||||
}
|
||||
|
||||
bool flip = false;
|
||||
uint8 rotation = 0;
|
||||
#if GST_1_20
|
||||
private bool on_new_event(Gst.App.Sink sink) {
|
||||
if (sink == null || sink != send_rtp) {
|
||||
return false;
|
||||
}
|
||||
Gst.MiniObject obj = sink.try_pull_object(0);
|
||||
if (obj.type == typeof(Gst.Event)) {
|
||||
unowned Gst.TagList tags;
|
||||
if (((Gst.Event)obj).type == Gst.EventType.TAG) {
|
||||
((Gst.Event)obj).parse_tag(out tags);
|
||||
Gst.Video.OrientationMethod orientation_method;
|
||||
Gst.Video.Orientation.from_tag(tags, out orientation_method);
|
||||
switch (orientation_method) {
|
||||
case Gst.Video.OrientationMethod.IDENTITY:
|
||||
case Gst.Video.OrientationMethod.VERT:
|
||||
default:
|
||||
rotation = 0;
|
||||
break;
|
||||
case Gst.Video.OrientationMethod.@90R:
|
||||
case Gst.Video.OrientationMethod.UL_LR:
|
||||
rotation = 1;
|
||||
break;
|
||||
case Gst.Video.OrientationMethod.@180:
|
||||
case Gst.Video.OrientationMethod.HORIZ:
|
||||
rotation = 2;
|
||||
break;
|
||||
case Gst.Video.OrientationMethod.@90L:
|
||||
case Gst.Video.OrientationMethod.UR_LL:
|
||||
rotation = 3;
|
||||
break;
|
||||
}
|
||||
switch (orientation_method) {
|
||||
case Gst.Video.OrientationMethod.IDENTITY:
|
||||
case Gst.Video.OrientationMethod.@90R:
|
||||
case Gst.Video.OrientationMethod.@180:
|
||||
case Gst.Video.OrientationMethod.@90L:
|
||||
default:
|
||||
flip = false;
|
||||
break;
|
||||
case Gst.Video.OrientationMethod.VERT:
|
||||
case Gst.Video.OrientationMethod.UL_LR:
|
||||
case Gst.Video.OrientationMethod.HORIZ:
|
||||
case Gst.Video.OrientationMethod.UR_LL:
|
||||
flip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
private Gst.FlowReturn on_new_sample(Gst.App.Sink sink) {
|
||||
if (sink == null) {
|
||||
debug("Sink is null");
|
||||
|
@ -323,6 +380,24 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if GST_1_20
|
||||
if (sink == send_rtp) {
|
||||
Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation");
|
||||
if (ext != null) {
|
||||
buffer = (Gst.Buffer) buffer.make_writable();
|
||||
Gst.RTP.Buffer rtp_buffer;
|
||||
if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.WRITE, out rtp_buffer)) {
|
||||
uint8[] extension_data = new uint8[1];
|
||||
bool camera = false;
|
||||
extension_data[0] = extension_data[0] | (rotation & 0x3);
|
||||
if (flip) extension_data[0] = extension_data[0] | 0x4;
|
||||
if (camera) extension_data[0] = extension_data[0] | 0x8;
|
||||
rtp_buffer.add_extension_onebyte_header(ext.id, extension_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
prepare_local_crypto();
|
||||
|
||||
uint8[] data;
|
||||
|
@ -489,8 +564,8 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
|||
}
|
||||
}
|
||||
|
||||
private uint16 previous_video_orientation_degree = uint16.MAX;
|
||||
public signal void video_orientation_changed(uint16 degree);
|
||||
private uint16 previous_incoming_video_orientation_degree = uint16.MAX;
|
||||
public signal void incoming_video_orientation_changed(uint16 degree);
|
||||
|
||||
public override void on_recv_rtp_data(Bytes bytes) {
|
||||
if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) {
|
||||
|
@ -545,9 +620,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
|||
case 2: rotation_degree = 180; break;
|
||||
case 3: rotation_degree = 270; break;
|
||||
}
|
||||
if (rotation_degree != previous_video_orientation_degree) {
|
||||
video_orientation_changed(rotation_degree);
|
||||
previous_video_orientation_degree = rotation_degree;
|
||||
if (rotation_degree != previous_incoming_video_orientation_degree) {
|
||||
incoming_video_orientation_changed(rotation_degree);
|
||||
previous_incoming_video_orientation_degree = rotation_degree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -723,7 +798,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream {
|
|||
private Gee.List<Gst.Element> outputs = new ArrayList<Gst.Element>();
|
||||
private Gst.Element output_tee;
|
||||
private Gst.Element rotate;
|
||||
private ulong video_orientation_changed_handler;
|
||||
private ulong incoming_video_orientation_changed_handler;
|
||||
|
||||
public VideoStream(Plugin plugin, Xmpp.Xep.Jingle.Content content) {
|
||||
base(plugin, content);
|
||||
|
@ -731,7 +806,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream {
|
|||
}
|
||||
|
||||
public override void create() {
|
||||
video_orientation_changed_handler = video_orientation_changed.connect(on_video_orientation_changed);
|
||||
incoming_video_orientation_changed_handler = incoming_video_orientation_changed.connect(on_video_orientation_changed);
|
||||
plugin.pause();
|
||||
rotate = Gst.ElementFactory.make("videoflip", @"video_rotate_$rtpid");
|
||||
pipe.add(rotate);
|
||||
|
@ -780,7 +855,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream {
|
|||
output_tee.set_state(Gst.State.NULL);
|
||||
pipe.remove(output_tee);
|
||||
output_tee = null;
|
||||
disconnect(video_orientation_changed_handler);
|
||||
disconnect(incoming_video_orientation_changed_handler);
|
||||
}
|
||||
|
||||
public override void add_output(Gst.Element element, Xmpp.Jid? participant) {
|
||||
|
|
|
@ -227,9 +227,21 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Widget, Dino.Plugins.VideoCallWi
|
|||
if (connected_device == null) return;
|
||||
plugin.pause();
|
||||
pipe.add(sink);
|
||||
#if GST_1_20
|
||||
prepare = Gst.parse_bin_from_description(@"videoflip video-direction=auto name=video_widget_$(id)_orientation ! videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true);
|
||||
#else
|
||||
prepare = Gst.parse_bin_from_description(@"videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true);
|
||||
#endif
|
||||
prepare.name = @"video_widget_$(id)_prepare";
|
||||
prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed);
|
||||
#if GST_1_20
|
||||
if (prepare is Gst.Bin) {
|
||||
((Gst.Bin) prepare).get_by_name(@"video_widget_$(id)_flip").get_static_pad("sink").notify["caps"].connect(input_caps_changed);
|
||||
} else {
|
||||
#endif
|
||||
prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed);
|
||||
#if GST_1_20
|
||||
}
|
||||
#endif
|
||||
pipe.add(prepare);
|
||||
connected_device_element = connected_device.link_source();
|
||||
connected_device_element.link(prepare);
|
||||
|
|
|
@ -28,54 +28,10 @@ GENERATE_HEADER
|
|||
set(C_HEADERS_SRC "")
|
||||
set(C_HEADERS_TARGET "")
|
||||
|
||||
if(NOT BUILD_LIBSIGNAL_IN_TREE)
|
||||
# libsignal-protocol-c has a history of breaking compatibility on the patch level
|
||||
# we'll have to check compatibility for every new release
|
||||
# distro maintainers may update this dependency after compatibility tests
|
||||
find_package(SignalProtocol 2.3.2 REQUIRED)
|
||||
else()
|
||||
add_subdirectory(libsignal-protocol-c EXCLUDE_FROM_ALL)
|
||||
set_property(TARGET curve25519 PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET protobuf-c PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET signal-protocol-c PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(SIGNAL_PROTOCOL_C_HEADERS
|
||||
signal_protocol.h
|
||||
signal_protocol_types.h
|
||||
curve.h
|
||||
hkdf.h
|
||||
ratchet.h
|
||||
protocol.h
|
||||
session_state.h
|
||||
session_record.h
|
||||
session_pre_key.h
|
||||
session_builder.h
|
||||
session_cipher.h
|
||||
key_helper.h
|
||||
sender_key.h
|
||||
sender_key_state.h
|
||||
sender_key_record.h
|
||||
group_session_builder.h
|
||||
group_cipher.h
|
||||
fingerprint.h
|
||||
device_consistency.h
|
||||
)
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/exports/signal")
|
||||
|
||||
foreach(f ${SIGNAL_PROTOCOL_C_HEADERS})
|
||||
list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}")
|
||||
list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal/${f}")
|
||||
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/signal/${f}"
|
||||
COMMAND
|
||||
cp "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}" "${CMAKE_BINARY_DIR}/exports/signal/${f}"
|
||||
DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}"
|
||||
COMMENT
|
||||
Copy header file signal/${f}
|
||||
)
|
||||
endforeach(f)
|
||||
endif()
|
||||
# libsignal-protocol-c has a history of breaking compatibility on the patch level
|
||||
# we'll have to check compatibility for every new release
|
||||
# distro maintainers may update this dependency after compatibility tests
|
||||
find_package(SignalProtocol 2.3.2 REQUIRED)
|
||||
|
||||
list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h")
|
||||
list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal_helper.h")
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 3a83a4f4ed2302ff6e68ab569c88793b50c22d28
|
|
@ -58,7 +58,7 @@ namespace Xmpp.MessageArchiveManagement.V2 {
|
|||
fields.add(field);
|
||||
}
|
||||
|
||||
return MessageArchiveManagement.create_base_query(stream, MessageArchiveManagement.NS_URI_2, mam_params.query_id, fields);
|
||||
return MessageArchiveManagement.create_base_query(stream, mam_params.query_id, fields);
|
||||
}
|
||||
|
||||
public async QueryResult query_archive(XmppStream stream, MamQueryParams mam_params, Cancellable? cancellable = null) {
|
||||
|
@ -67,14 +67,14 @@ namespace Xmpp.MessageArchiveManagement.V2 {
|
|||
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(mam_params.end_id));
|
||||
}
|
||||
|
||||
return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node, cancellable);
|
||||
return yield MessageArchiveManagement.query_archive(stream, mam_params.mam_server, query_node, cancellable);
|
||||
}
|
||||
|
||||
public async QueryResult page_through_results(XmppStream stream, MamQueryParams mam_params, QueryResult prev_result, Cancellable? cancellable = null) {
|
||||
var query_node = create_base_query(stream, mam_params);
|
||||
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(prev_result.first));
|
||||
|
||||
return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node, cancellable);
|
||||
return yield MessageArchiveManagement.query_archive(stream, mam_params.mam_server, query_node, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ using Xmpp.Xep;
|
|||
namespace Xmpp.MessageArchiveManagement {
|
||||
|
||||
public const string NS_URI = "urn:xmpp:mam:2";
|
||||
public const string NS_URI_2 = "urn:xmpp:mam:2";
|
||||
public const string NS_URI_1 = "urn:xmpp:mam:1";
|
||||
|
||||
public class QueryResult {
|
||||
public bool error { get; set; default=false; }
|
||||
|
@ -36,43 +34,33 @@ public class Module : XmppStreamModule {
|
|||
|
||||
private async void query_availability(XmppStream stream) {
|
||||
Jid own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid;
|
||||
|
||||
bool ver_2_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI);
|
||||
if (ver_2_available) {
|
||||
stream.add_flag(new Flag(NS_URI));
|
||||
bool mam_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI);
|
||||
if (mam_available) {
|
||||
feature_available(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
bool ver_1_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI_1);
|
||||
if (ver_1_available) {
|
||||
stream.add_flag(new Flag(NS_URI_1));
|
||||
feature_available(stream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal StanzaNode create_base_query(XmppStream stream, string ns, string? queryid, Gee.List<DataForms.DataForm.Field> fields) {
|
||||
internal StanzaNode create_base_query(XmppStream stream, string? queryid, Gee.List<DataForms.DataForm.Field> fields) {
|
||||
DataForms.DataForm data_form = new DataForms.DataForm();
|
||||
|
||||
DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" };
|
||||
form_type_field.set_value_string(NS_VER(stream));
|
||||
form_type_field.set_value_string(NS_URI);
|
||||
data_form.add_field(form_type_field);
|
||||
|
||||
foreach (var field in fields) {
|
||||
data_form.add_field(field);
|
||||
}
|
||||
|
||||
StanzaNode query_node = new StanzaNode.build("query", NS_VER(stream)).add_self_xmlns().put_node(data_form.get_submit_node());
|
||||
StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(data_form.get_submit_node());
|
||||
query_node.put_attribute("queryid", queryid);
|
||||
return query_node;
|
||||
}
|
||||
|
||||
internal async QueryResult query_archive(XmppStream stream, string ns, Jid? mam_server, StanzaNode query_node, Cancellable? cancellable = null) {
|
||||
internal async QueryResult query_archive(XmppStream stream, Jid? mam_server, StanzaNode query_node, Cancellable? cancellable = null) {
|
||||
|
||||
var res = new QueryResult();
|
||||
Flag? flag = stream.get_flag(Flag.IDENTITY);
|
||||
Flag flag = Flag.get_flag(stream);
|
||||
string? query_id = query_node.get_attribute("queryid");
|
||||
if (flag == null || query_id == null) { res.error = true; return res; }
|
||||
flag.active_query_ids.add(query_id);
|
||||
|
@ -83,7 +71,7 @@ public class Module : XmppStreamModule {
|
|||
Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq, Priority.LOW, cancellable);
|
||||
|
||||
// Parse the response IQ into a QueryResult.
|
||||
StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", ns);
|
||||
StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", NS_URI);
|
||||
if (fin_node == null) { res.malformed = true; return res; }
|
||||
|
||||
StanzaNode? rsm_node = fin_node.get_subnode("set", Xmpp.ResultSetManagement.NS_URI);
|
||||
|
@ -92,7 +80,7 @@ public class Module : XmppStreamModule {
|
|||
res.first = rsm_node.get_deep_string_content("first");
|
||||
res.last = rsm_node.get_deep_string_content("last");
|
||||
if ((res.first == null) != (res.last == null)) { res.malformed = true; return res; }
|
||||
res.complete = fin_node.get_attribute_bool("complete", false, ns);
|
||||
res.complete = fin_node.get_attribute_bool("complete", false, NS_URI);
|
||||
|
||||
Idle.add(() => {
|
||||
flag.active_query_ids.remove(query_id);
|
||||
|
@ -110,15 +98,14 @@ public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
|
|||
public override string[] after_actions { get { return after_actions_const; } }
|
||||
|
||||
public override async bool run(XmppStream stream, MessageStanza message) {
|
||||
Flag? flag = stream.get_flag(Flag.IDENTITY);
|
||||
if (flag == null) return false;
|
||||
Flag flag = Flag.get_flag(stream);
|
||||
|
||||
StanzaNode? message_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message");
|
||||
StanzaNode? message_node = message.stanza.get_deep_subnode(NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message");
|
||||
if (message_node != null) {
|
||||
StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
||||
StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
||||
DateTime? datetime = DelayedDelivery.get_time_for_node(forward_node);
|
||||
string? mam_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":id");
|
||||
string? query_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":queryid");
|
||||
string? mam_id = message.stanza.get_deep_attribute(NS_URI + ":result", NS_URI + ":id");
|
||||
string? query_id = message.stanza.get_deep_attribute(NS_URI + ":result", NS_URI + ":queryid");
|
||||
|
||||
if (query_id == null) {
|
||||
warning("Received MAM message without queryid from %s, ignoring", message.from.to_string());
|
||||
|
@ -154,10 +141,14 @@ public class Flag : XmppStreamFlag {
|
|||
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "message_archive_management");
|
||||
public bool cought_up { get; set; default=false; }
|
||||
public Gee.Set<string> active_query_ids { get; set; default = new HashSet<string>(); }
|
||||
public string ns_ver;
|
||||
|
||||
public Flag(string ns_ver) {
|
||||
this.ns_ver = ns_ver;
|
||||
public static Flag get_flag(XmppStream stream) {
|
||||
Flag? flag = stream.get_flag(Flag.IDENTITY);
|
||||
if (flag == null) {
|
||||
flag = new Flag();
|
||||
stream.add_flag(flag);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
@ -185,8 +176,4 @@ public class MessageFlag : Xmpp.MessageFlag {
|
|||
public override string get_id() { return ID; }
|
||||
}
|
||||
|
||||
private static string NS_VER(XmppStream stream) {
|
||||
return stream.get_flag(Flag.IDENTITY).ns_ver;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue