mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-28 00:33:06 +08:00
Compare commits
5
Commits
afe42be672
...
9abdfbf684
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9abdfbf684 | ||
|
|
31a0b86cee | ||
|
|
fb34f42744 | ||
|
|
4302ab291c | ||
|
|
4bf92c9e2c |
@@ -2014,7 +2014,7 @@ void GuiApplication::ShareLocalCursorState() {
|
||||
action.type = ControlType::cursor_state;
|
||||
action.cs = outgoing;
|
||||
const std::string message = action.to_json();
|
||||
const int result = SendDataFrameToPeer(
|
||||
const int result = SendReliableDataFrameToPeer(
|
||||
peer_, message.c_str(), message.size(), mouse_label_.c_str(),
|
||||
recipient.remote_id.c_str(), recipient.remote_id.size());
|
||||
if (result != 0) {
|
||||
@@ -2652,6 +2652,8 @@ void GuiApplication::SyncStreamWindow() {
|
||||
(*ui_->stream)->set_selected_display(props->selected_display_);
|
||||
|
||||
const auto& net = props->net_traffic_stats_;
|
||||
(*ui_->stream)->set_stats_encryption_enabled(
|
||||
status == ConnectionStatus::Connected && net.srtp_active);
|
||||
std::vector<ui::NetworkStatsRow> stats_rows;
|
||||
stats_rows.reserve(4);
|
||||
const auto append_stats_row = [&](const std::string& label,
|
||||
@@ -3318,8 +3320,8 @@ void GuiApplication::SendPointerInput(int button, int kind, float x, float y) {
|
||||
std::lock_guard lock(props->remote_cursor_state_mutex_);
|
||||
props->cursor_presentation_.NoteInput(SDL_GetTicks());
|
||||
}
|
||||
SendDataFrame(props->peer_, message.c_str(), message.size(),
|
||||
props->mouse_label_.c_str());
|
||||
SendReliableDataFrame(props->peer_, message.c_str(), message.size(),
|
||||
props->mouse_label_.c_str());
|
||||
}
|
||||
|
||||
void GuiApplication::SendScrollInput(float delta_x, float delta_y, float x,
|
||||
@@ -3362,8 +3364,8 @@ void GuiApplication::SendScrollInput(float delta_x, float delta_y, float x,
|
||||
}
|
||||
controlled_remote_id_ = props->remote_id_;
|
||||
const std::string message = action.to_json();
|
||||
SendDataFrame(props->peer_, message.c_str(), message.size(),
|
||||
props->mouse_label_.c_str());
|
||||
SendReliableDataFrame(props->peer_, message.c_str(), message.size(),
|
||||
props->mouse_label_.c_str());
|
||||
}
|
||||
|
||||
void GuiApplication::SendKeyInput(const std::string& text, bool pressed,
|
||||
|
||||
@@ -366,8 +366,8 @@ int GuiApplication::ProcessMouseEvent(const SDL_Event &event) {
|
||||
}
|
||||
if (props->peer_) {
|
||||
std::string msg = remote_action.to_json();
|
||||
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->mouse_label_.c_str());
|
||||
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->mouse_label_.c_str());
|
||||
}
|
||||
} else if (SDL_EVENT_MOUSE_WHEEL == event.type &&
|
||||
last_mouse_event.button.x >= render_rect.x &&
|
||||
@@ -412,8 +412,8 @@ int GuiApplication::ProcessMouseEvent(const SDL_Event &event) {
|
||||
}
|
||||
if (props->peer_) {
|
||||
std::string msg = remote_action.to_json();
|
||||
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->mouse_label_.c_str());
|
||||
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->mouse_label_.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +233,9 @@ struct TranslationRow {
|
||||
X(connection_mode, u8"连接模式", "Mode", u8"Режим") \
|
||||
X(connection_mode_direct, u8"直连", "Direct", u8"Прямой") \
|
||||
X(connection_mode_relay, u8"中继", "Relay", u8"Релейный") \
|
||||
X(transport_encryption, u8"加密传输", "Encryption", u8"Шифрование") \
|
||||
X(transport_encryption_enabled, u8"已开启", "Enabled", u8"Включено") \
|
||||
X(transport_encryption_disabled, u8"未开启", "Disabled", u8"Выключено") \
|
||||
X(online, u8"在线", "Online", u8"Онлайн") \
|
||||
X(offline, u8"离线", "Offline", u8"Офлайн") \
|
||||
X(device_offline, u8"设备离线", "Device Offline", u8"Устройство офлайн") \
|
||||
|
||||
@@ -518,7 +518,7 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
||||
}
|
||||
AddAudioStream(props->peer_, props->audio_label_.c_str());
|
||||
AddDataStream(props->peer_, props->data_label_.c_str(), false);
|
||||
AddDataStream(props->peer_, props->mouse_label_.c_str(), false);
|
||||
AddDataStream(props->peer_, props->mouse_label_.c_str(), true);
|
||||
AddDataStream(props->peer_, props->keyboard_label_.c_str(), true);
|
||||
AddDataStream(props->peer_, props->control_data_label_.c_str(), true);
|
||||
AddDataStream(props->peer_, props->file_label_.c_str(), true);
|
||||
|
||||
@@ -199,7 +199,7 @@ int GuiRuntime::CreateConnectionPeer() {
|
||||
|
||||
AddAudioStream(peer_, audio_label_.c_str());
|
||||
AddDataStream(peer_, data_label_.c_str(), false);
|
||||
AddDataStream(peer_, mouse_label_.c_str(), false);
|
||||
AddDataStream(peer_, mouse_label_.c_str(), true);
|
||||
AddDataStream(peer_, keyboard_label_.c_str(), true);
|
||||
AddDataStream(peer_, control_data_label_.c_str(), true);
|
||||
AddDataStream(peer_, file_label_.c_str(), true);
|
||||
|
||||
@@ -45,6 +45,9 @@ export global StreamStrings {
|
||||
in-out property <string> stats-loss-rate: "Loss Rate";
|
||||
in-out property <string> stats-resolution: "Res";
|
||||
in-out property <string> stats-connection-mode: "Mode";
|
||||
in-out property <string> stats-encryption: "Encryption";
|
||||
in-out property <string> stats-encryption-enabled: "Enabled";
|
||||
in-out property <string> stats-encryption-disabled: "Disabled";
|
||||
}
|
||||
|
||||
struct ControlBarTooltip {
|
||||
@@ -166,6 +169,7 @@ export component StreamWindow inherits Window {
|
||||
in property <string> stats-fps: "0";
|
||||
in property <string> stats-resolution: "";
|
||||
in property <string> stats-connection-mode: "";
|
||||
in property <bool> stats-encryption-enabled: false;
|
||||
in property <bool> file-transfer-visible: false;
|
||||
in property <[FileTransferEntry]> file-transfers;
|
||||
|
||||
@@ -194,6 +198,12 @@ export component StreamWindow inherits Window {
|
||||
callback keyboard-focus-changed(bool);
|
||||
|
||||
private property <bool> control-expanded: true;
|
||||
private property <length> control-toolbar-height: 38px;
|
||||
private property <length> control-button-step: 32px;
|
||||
private property <length> stats-row-height: 18px;
|
||||
private property <length> stats-header-height: 18px;
|
||||
private property <length> stats-content-height:
|
||||
root.stats-header-height + root.stats-rows.length * root.stats-row-height + 44px;
|
||||
private property <bool> display-menu-open: false;
|
||||
private property <bool> display-menu-animation-enabled: false;
|
||||
private property <float> display-menu-progress: 0.0;
|
||||
@@ -283,7 +293,7 @@ export component StreamWindow inherits Window {
|
||||
&& pointer-x <= display-menu-x + 180px
|
||||
&& pointer-y >= display-menu-y
|
||||
&& pointer-y <= display-menu-y + display-menu-height;
|
||||
let shortcut-menu-x = control.x + (root.control-docked-left ? 41px : 74px);
|
||||
let shortcut-menu-x = control.x + shortcut-button.x;
|
||||
let shortcut-menu-y = control.y + 40px;
|
||||
let over-shortcut-menu = root.shortcut-menu-open
|
||||
&& pointer-x >= shortcut-menu-x
|
||||
@@ -682,8 +692,10 @@ export component StreamWindow inherits Window {
|
||||
control := Rectangle {
|
||||
x: root.control-dragging ? root.control-drag-x : root.control-docked-left ? 0px : parent.width - self.width;
|
||||
y: min(max(0px, root.control-pos-y), parent.height - self.height);
|
||||
width: root.control-expanded ? min(332px, parent.width) : 20px;
|
||||
height: root.stats-visible && root.control-expanded ? min(180px, parent.height) : 38px;
|
||||
width: root.control-expanded ? min(76px + 8 * root.control-button-step, parent.width) : 20px;
|
||||
height: root.stats-visible && root.control-expanded
|
||||
? min(root.control-toolbar-height + root.stats-content-height + 8px, parent.height)
|
||||
: root.control-toolbar-height;
|
||||
background: white;
|
||||
border-width: 1px;
|
||||
border-color: ImGuiLineStyle.border;
|
||||
@@ -719,7 +731,7 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
shortcut-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 41px : 74px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.keyboard; tooltip: StreamStrings.send-shortcut;
|
||||
clicked => {
|
||||
display-menu-open-timer.running = false;
|
||||
@@ -731,51 +743,51 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
mouse-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 73px : 106px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 2 * root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.computer-mouse; slashed: !root.mouse-control-enabled;
|
||||
tooltip: root.mouse-control-enabled ? StreamStrings.release-mouse : StreamStrings.control-mouse;
|
||||
clicked => { root.toggle-mouse-control(); }
|
||||
}
|
||||
audio-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 137px : 170px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 4 * root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.volume-high; slashed: !root.audio-enabled;
|
||||
tooltip: root.audio-enabled ? StreamStrings.mute : StreamStrings.audio;
|
||||
clicked => { root.toggle-audio(); }
|
||||
}
|
||||
file-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 169px : 202px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 5 * root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.folder-open; tooltip: StreamStrings.select-file;
|
||||
clicked => { root.select-file(); }
|
||||
}
|
||||
stats-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 201px : 234px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 6 * root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.signal; selected: root.stats-visible;
|
||||
tooltip: root.stats-visible ? StreamStrings.hide-stats : StreamStrings.show-stats;
|
||||
clicked => { root.toggle-stats(); }
|
||||
}
|
||||
fullscreen-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 233px : 266px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 7 * root.control-button-step; y: 7px;
|
||||
icon: root.fullscreen-enabled ? FontAwesomeIcons.compress : FontAwesomeIcons.expand;
|
||||
tooltip: root.fullscreen-enabled ? StreamStrings.exit-fullscreen : StreamStrings.fullscreen;
|
||||
clicked => { root.toggle-fullscreen(); }
|
||||
}
|
||||
close-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 265px : 298px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 8 * root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.xmark; tooltip: StreamStrings.disconnect;
|
||||
clicked => { root.disconnect(); }
|
||||
}
|
||||
|
||||
if root.control-expanded && root.control-docked-left: Rectangle {
|
||||
x: 299px; y: 12px; width: 2px; height: 15px;
|
||||
x: parent.width - 33px; y: 12px; width: 2px; height: 15px;
|
||||
background: ImGuiLineStyle.control-separator;
|
||||
}
|
||||
collapse-button := ControlBarButton {
|
||||
x: root.control-expanded ? (root.control-docked-left ? 307px : 10px) : 2px;
|
||||
x: root.control-expanded ? (root.control-docked-left ? parent.width - 25px : 10px) : 2px;
|
||||
y: 7px;
|
||||
width: 15px;
|
||||
icon: root.control-expanded
|
||||
@@ -787,7 +799,7 @@ export component StreamWindow inherits Window {
|
||||
|
||||
privacy-button := ControlBarButton {
|
||||
visible: root.control-expanded;
|
||||
x: root.control-docked-left ? 105px : 138px; y: 7px;
|
||||
x: (root.control-docked-left ? 9px : 42px) + 3 * root.control-button-step; y: 7px;
|
||||
icon: FontAwesomeIcons.user-secret;
|
||||
slashed: root.privacy-off && !root.privacy-unsupported;
|
||||
muted: root.privacy-unsupported;
|
||||
@@ -799,75 +811,110 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
|
||||
if root.stats-visible && root.control-expanded: stats-table := Rectangle {
|
||||
x: 14px;
|
||||
y: 38px;
|
||||
width: parent.width - 28px;
|
||||
height: 128px;
|
||||
x: 8px;
|
||||
y: root.control-toolbar-height + 2px;
|
||||
width: parent.width - 16px;
|
||||
height: root.stats-content-height;
|
||||
background: transparent;
|
||||
private property <length> row-height: 16px;
|
||||
private property <length> label-column-width: 60px;
|
||||
private property <length> loss-column-width: 66px;
|
||||
private property <length> row-height: root.stats-row-height;
|
||||
private property <length> label-column-width: 50px;
|
||||
private property <length> loss-column-width:
|
||||
max((self.width - self.label-column-width) / 3,
|
||||
stats-loss-label.preferred-width + 8px);
|
||||
private property <length> value-column-width:
|
||||
(self.width - self.label-column-width - self.loss-column-width) / 2;
|
||||
private property <length> details-right-width:
|
||||
right-label-width-probe.preferred-width + 6px
|
||||
+ max(resolution-width-probe.preferred-width,
|
||||
encryption-width-probe.preferred-width
|
||||
+ (root.stats-encryption-enabled ? 14px : 0px));
|
||||
private property <length> details-right-x: self.width / 2;
|
||||
|
||||
Rectangle {
|
||||
y: 0px;
|
||||
height: 1px;
|
||||
background: ImGuiLineStyle.border;
|
||||
left-label-width-probe := Text {
|
||||
visible: false;
|
||||
text: "FPS\n" + StreamStrings.stats-connection-mode;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
}
|
||||
right-label-width-probe := Text {
|
||||
visible: false;
|
||||
text: StreamStrings.stats-resolution + "\n" + StreamStrings.stats-encryption;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
}
|
||||
resolution-width-probe := Text {
|
||||
visible: false;
|
||||
text: root.stats-resolution;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: 600;
|
||||
}
|
||||
encryption-width-probe := Text {
|
||||
visible: false;
|
||||
text: root.stats-encryption-enabled
|
||||
? StreamStrings.stats-encryption-enabled : StreamStrings.stats-encryption-disabled;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: 0px;
|
||||
y: 0px;
|
||||
height: stats-table.row-height;
|
||||
width: parent.width;
|
||||
height: root.stats-header-height;
|
||||
background: #f3f5f8;
|
||||
border-radius: 4px;
|
||||
Text {
|
||||
x: stats-table.label-column-width + 4px;
|
||||
width: stats-table.value-column-width - 8px;
|
||||
text: StreamStrings.stats-in;
|
||||
color: #30343b;
|
||||
color: #667085;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
x: stats-table.label-column-width + stats-table.value-column-width + 4px;
|
||||
width: stats-table.value-column-width - 8px;
|
||||
text: StreamStrings.stats-out;
|
||||
color: #30343b;
|
||||
color: #667085;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
stats-loss-label := Text {
|
||||
x: stats-table.label-column-width + stats-table.value-column-width * 2 + 4px;
|
||||
width: stats-table.loss-column-width - 8px;
|
||||
text: StreamStrings.stats-loss-rate;
|
||||
color: #30343b;
|
||||
color: #667085;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
Rectangle {
|
||||
y: parent.height - 1px;
|
||||
height: 1px;
|
||||
background: ImGuiLineStyle.border;
|
||||
}
|
||||
}
|
||||
|
||||
for row[index] in root.stats-rows: Rectangle {
|
||||
y: (index + 1) * stats-table.row-height;
|
||||
x: 0px;
|
||||
y: root.stats-header-height + index * stats-table.row-height;
|
||||
width: parent.width;
|
||||
height: stats-table.row-height;
|
||||
background: index == root.stats-rows.length - 1 ? #edf3fc : transparent;
|
||||
border-radius: 4px;
|
||||
Text {
|
||||
x: 4px;
|
||||
width: stats-table.label-column-width - 8px;
|
||||
text: row.label;
|
||||
color: #30343b;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
color: #475467;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: index == root.stats-rows.length - 1 ? 600 : 400;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
x: stats-table.label-column-width + 4px;
|
||||
width: stats-table.value-column-width - 8px;
|
||||
text: row.inbound;
|
||||
color: #30343b;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
color: #253247;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: index == root.stats-rows.length - 1 ? 600 : 400;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
@@ -875,8 +922,10 @@ export component StreamWindow inherits Window {
|
||||
x: stats-table.label-column-width + stats-table.value-column-width + 4px;
|
||||
width: stats-table.value-column-width - 8px;
|
||||
text: row.outbound;
|
||||
color: #30343b;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
color: #253247;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: index == root.stats-rows.length - 1 ? 600 : 400;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
@@ -884,46 +933,73 @@ export component StreamWindow inherits Window {
|
||||
x: stats-table.label-column-width + stats-table.value-column-width * 2 + 4px;
|
||||
width: stats-table.loss-column-width - 8px;
|
||||
text: row.loss-rate;
|
||||
color: #30343b;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
color: #475467;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: index == root.stats-rows.length - 1 ? 600 : 400;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
Rectangle {
|
||||
if index < root.stats-rows.length - 2: Rectangle {
|
||||
x: 4px;
|
||||
y: parent.height - 1px;
|
||||
width: parent.width - 8px;
|
||||
height: 1px;
|
||||
background: ImGuiLineStyle.border;
|
||||
background: #edf0f4;
|
||||
}
|
||||
}
|
||||
|
||||
for metric[index] in [
|
||||
{ label: "FPS:", value: root.stats-fps },
|
||||
{ label: StreamStrings.stats-resolution + ":", value: root.stats-resolution },
|
||||
{ label: StreamStrings.stats-connection-mode + ":", value: root.stats-connection-mode },
|
||||
{ label: "FPS", value: root.stats-fps },
|
||||
{ label: StreamStrings.stats-resolution, value: root.stats-resolution },
|
||||
{ label: StreamStrings.stats-connection-mode, value: root.stats-connection-mode },
|
||||
{ label: StreamStrings.stats-encryption, value: root.stats-encryption-enabled
|
||||
? StreamStrings.stats-encryption-enabled : StreamStrings.stats-encryption-disabled },
|
||||
]: Rectangle {
|
||||
y: (index + 5) * stats-table.row-height;
|
||||
height: stats-table.row-height;
|
||||
Text {
|
||||
x: 4px;
|
||||
width: stats-table.label-column-width - 8px;
|
||||
private property <length> encryption-icon-width:
|
||||
index == 3 && root.stats-encryption-enabled ? 14px : 0px;
|
||||
x: mod(index, 2) == 1 ? stats-table.details-right-x : 6px;
|
||||
y: root.stats-header-height + root.stats-rows.length * stats-table.row-height
|
||||
+ 2px + floor(index / 2) * 21px;
|
||||
width: mod(index, 2) == 1 ? stats-table.details-right-width
|
||||
: stats-table.details-right-x - 14px;
|
||||
height: 21px;
|
||||
metric-label := Text {
|
||||
x: 0px;
|
||||
y: 0px;
|
||||
width: mod(index, 2) == 1 ? right-label-width-probe.preferred-width
|
||||
: left-label-width-probe.preferred-width;
|
||||
height: parent.height;
|
||||
text: metric.label;
|
||||
color: #30343b;
|
||||
color: #667085;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
x: stats-table.label-column-width + 4px;
|
||||
width: stats-table.value-column-width - 8px;
|
||||
metric-value := Text {
|
||||
x: metric-label.width + 6px;
|
||||
y: 0px;
|
||||
height: parent.height;
|
||||
width: parent.width - self.x - parent.encryption-icon-width;
|
||||
text: metric.value;
|
||||
color: #30343b;
|
||||
font-size: ImGuiFontStyle.small;
|
||||
color: index == 3 && root.stats-encryption-enabled ? #18794e : #253247;
|
||||
font-size: ImGuiFontStyle.body;
|
||||
font-weight: 600;
|
||||
horizontal-alignment: left;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
Rectangle {
|
||||
y: parent.height - 1px;
|
||||
height: 1px;
|
||||
background: ImGuiLineStyle.border;
|
||||
if index == 3 && root.stats-encryption-enabled: Text {
|
||||
x: metric-value.x + min(metric-value.preferred-width, metric-value.width) + 4px;
|
||||
y: metric-value.y + (metric-value.height - self.height) / 2;
|
||||
width: 10px;
|
||||
height: 12px;
|
||||
text: FontAwesomeIcons.shield-halved;
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
font-size: 10px;
|
||||
color: #238653;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1007,7 +1083,7 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
|
||||
if root.shortcut-menu-open: Rectangle {
|
||||
x: control.x + (root.control-docked-left ? 41px : 74px); y: control.y + 40px; width: 180px; height: 68px;
|
||||
x: control.x + shortcut-button.x; y: control.y + 40px; width: 180px; height: 68px;
|
||||
background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 5px; z: 11;
|
||||
VerticalLayout {
|
||||
padding: 4px; spacing: 1px;
|
||||
@@ -1039,8 +1115,9 @@ export component StreamWindow inherits Window {
|
||||
x: min(max(4px, control.x + root.hovered-control.anchor-x - self.width / 2),
|
||||
video.width - self.width - 4px);
|
||||
height: control-tip.preferred-height + 12px;
|
||||
y: control.y + control.height + self.height + 8px <= video.height
|
||||
? control.y + control.height + 4px : max(4px, control.y - self.height - 4px);
|
||||
// Anchor to the button row so expanded stats stay beneath the tooltip.
|
||||
y: control.y + root.control-toolbar-height + self.height + 8px <= video.height
|
||||
? control.y + root.control-toolbar-height + 4px : max(4px, control.y - self.height - 4px);
|
||||
background: #30343bf5; border-radius: 5px; z: 12;
|
||||
control-tip := Text {
|
||||
x: 8px; y: 6px; width: parent.width - 16px;
|
||||
|
||||
@@ -166,6 +166,12 @@ inline int ApplyStreamWindowStrings(
|
||||
strings.set_stats_resolution(Text(localization::resolution[language]));
|
||||
strings.set_stats_connection_mode(
|
||||
Text(localization::connection_mode[language]));
|
||||
strings.set_stats_encryption(
|
||||
Text(localization::transport_encryption[language]));
|
||||
strings.set_stats_encryption_enabled(
|
||||
Text(localization::transport_encryption_enabled[language]));
|
||||
strings.set_stats_encryption_disabled(
|
||||
Text(localization::transport_encryption_disabled[language]));
|
||||
return language;
|
||||
}
|
||||
|
||||
|
||||
@@ -621,21 +621,15 @@ Params MakeParams(const RTCState &state, const std::string &user_id,
|
||||
y:(float)y
|
||||
action:(CrossDeskPointerAction)action {
|
||||
const std::string message = MouseJSON(x, y, 0, static_cast<int>(action));
|
||||
// Movement is latency-sensitive and can be superseded by newer positions.
|
||||
// Button transitions must not be lost or reordered, otherwise a tap can be
|
||||
// applied at a stale pointer position or leave a button logically pressed.
|
||||
// MiniRTC reliability is configured per stream, so transitions use the
|
||||
// existing reliable control stream while movement stays on the mouse stream.
|
||||
const BOOL is_movement = action == CrossDeskPointerActionMove;
|
||||
// Keep movement, button transitions, and scrolling ordered on the reliable
|
||||
// mouse stream.
|
||||
#if DEBUG
|
||||
if (!is_movement) {
|
||||
if (action != CrossDeskPointerActionMove) {
|
||||
NSLog(@"CrossDesk pointer action=%ld normalized=(%.5f, %.5f)",
|
||||
static_cast<long>(action), x, y);
|
||||
}
|
||||
#endif
|
||||
[self sendMessage:message
|
||||
reliable:!is_movement
|
||||
stream:is_movement ? kMouseStream : kControlStream];
|
||||
[self sendMessage:message reliable:YES stream:kMouseStream];
|
||||
}
|
||||
|
||||
- (void)sendScrollX:(float)x
|
||||
@@ -648,7 +642,7 @@ Params MakeParams(const RTCState &state, const std::string &user_id,
|
||||
const int flag = vertical ? 7 : 8;
|
||||
const int wheel = raw_delta > 0 ? 1 : -1;
|
||||
const std::string message = MouseJSON(x, y, wheel, flag);
|
||||
[self sendMessage:message reliable:NO stream:kMouseStream];
|
||||
[self sendMessage:message reliable:YES stream:kMouseStream];
|
||||
}
|
||||
|
||||
- (void)sendWindowsKeyCode:(NSUInteger)keyCode isDown:(BOOL)isDown {
|
||||
@@ -893,7 +887,7 @@ Params MakeParams(const RTCState &state, const std::string &user_id,
|
||||
// display. Remote display receivers are created from the host's SDP.
|
||||
AddAudioStream(_state->controller_peer, kAudioStream);
|
||||
AddDataStream(_state->controller_peer, kDataStream, false);
|
||||
AddDataStream(_state->controller_peer, kMouseStream, false);
|
||||
AddDataStream(_state->controller_peer, kMouseStream, true);
|
||||
AddDataStream(_state->controller_peer, kKeyboardStream, true);
|
||||
AddDataStream(_state->controller_peer, kControlStream, true);
|
||||
AddDataStream(_state->controller_peer, kFileStream, true);
|
||||
|
||||
Vendored
+1
-1
Submodule deps/submodules/minirtc updated: 5b0ce34383...96b765f818
@@ -0,0 +1,10 @@
|
||||
# **更新内容**
|
||||
|
||||
1. 新增Windows、macOS与Linux X11隐私屏,支持遮蔽被控端本地画面、屏蔽本地键鼠输入,并在断开连接或异常时自动恢复;远程会话保持正常操作。
|
||||
2. 增强桌面端与iOS端P2P穿透能力,优化UDP打洞、ICE连接建立与带宽恢复,支持使用服务器下发的连接配置,并改善加密传输与旧版客户端的兼容性。
|
||||
3. 新增Windows屏幕采集方式设置,支持DXGI、WGC与GDI;修复锁屏、隐私屏及未连接物理鼠标时远程光标不可见、位置异常和拖动窗口时出现重复光标的问题。
|
||||
4. 优化画面缩小时的显示清晰度、帧率与分辨率恢复,修复Windows、macOS与Linux系统音频采集可能卡住的问题,提升远程画面与音频的稳定性。
|
||||
5. 修复Linux X11远程键盘输入与macOS窗口缩放光标识别,改进鼠标操作和光标状态传输的可靠性;优化控制栏提示、网络状态布局与加密状态显示,并修复部分平台的Dock及任务栏图标显示问题。
|
||||
6. 完善iOS/iPadOS连接失败提示,保留密码错误、设备不可用和网络中断等具体原因,简化服务器配置并优化原生客户端构建流程。
|
||||
|
||||
PC客户端版本为v1.5.1,iOS/iPadOS客户端暂未上架App Store,目前仅支持作为控制端连接远端电脑。隐私屏需控制端与被控端均支持,暂不支持Web及旧版客户端;macOS要求14.0及以上版本,Linux仅支持X11会话。
|
||||
Reference in New Issue
Block a user