Merge remote-tracking branch 'upstream/master' into master-windows-changes

This commit is contained in:
LAGonauta 2023-04-24 19:01:02 -03:00
commit 7603990740
23 changed files with 193 additions and 213 deletions

View file

@ -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
View file

@ -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
View file

@ -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" \

View file

@ -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": [

View file

@ -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();
}

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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();
}

View file

@ -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"));

View 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());

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;
}
}