Add singbox log
Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
parent
80a3498ff4
commit
bf7c271556
4 changed files with 157 additions and 41 deletions
|
@ -17,3 +17,22 @@
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
}
|
}
|
||||||
|
.singbox-status > sheet > stack > button,
|
||||||
|
.singbox-status > sheet > stack > widget {
|
||||||
|
background-color: alpha(var(--success-bg-color), 0.1);
|
||||||
|
}
|
||||||
|
.singbox-status.singbox-fail > sheet > stack > button,
|
||||||
|
.singbox-status.singbox-fail > sheet > stack > widget {
|
||||||
|
background-color: alpha(var(--error-bg-color), 0.1);
|
||||||
|
}
|
||||||
|
.singbox-status > sheet > stack > button {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
.singbox-log {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
.singbox-status > sheet > stack > widget > scrolledwindow {
|
||||||
|
padding: 12px;
|
||||||
|
padding-top: 32px;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ template $SingularityWindow: Adw.ApplicationWindow {
|
||||||
content: Adw.ToolbarView {
|
content: Adw.ToolbarView {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
|
styles [
|
||||||
|
"flat",
|
||||||
|
]
|
||||||
|
|
||||||
[end]
|
[end]
|
||||||
MenuButton {
|
MenuButton {
|
||||||
primary: true;
|
primary: true;
|
||||||
|
@ -18,6 +22,11 @@ template $SingularityWindow: Adw.ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content: Adw.BottomSheet sheet {
|
||||||
|
styles [
|
||||||
|
"singbox-status",
|
||||||
|
]
|
||||||
|
|
||||||
content: Adw.Clamp {
|
content: Adw.Clamp {
|
||||||
ScrolledWindow {
|
ScrolledWindow {
|
||||||
hscrollbar-policy: never;
|
hscrollbar-policy: never;
|
||||||
|
@ -34,6 +43,25 @@ template $SingularityWindow: Adw.ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bottom-bar: Label singbox_status {};
|
||||||
|
|
||||||
|
sheet: ScrolledWindow {
|
||||||
|
propagate-natural-height: true;
|
||||||
|
|
||||||
|
TextView {
|
||||||
|
styles [
|
||||||
|
"singbox-log",
|
||||||
|
]
|
||||||
|
|
||||||
|
editable: false;
|
||||||
|
cursor-visible: false;
|
||||||
|
monospace: true;
|
||||||
|
|
||||||
|
buffer: TextBuffer singbox_log {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,28 +18,50 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[GtkTemplate (ui = "/io/gitlab/nekocwd/singularity/gtk/window.ui")]
|
[GtkTemplate(ui = "/io/gitlab/nekocwd/singularity/gtk/window.ui")]
|
||||||
public class Singularity.Window : Adw.ApplicationWindow {
|
public class Singularity.Window : Adw.ApplicationWindow {
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.BottomSheet sheet;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private unowned Gtk.ListView outbounds;
|
private unowned Gtk.ListView outbounds;
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Gtk.TextBuffer singbox_log;
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Gtk.Label singbox_status;
|
||||||
|
|
||||||
public Window (Gtk.Application app) {
|
private void on_singbox_status_change() {
|
||||||
Object (application: app);
|
var status = SingBox.instance.singbox_status;
|
||||||
var factory = new Gtk.SignalListItemFactory ();
|
singbox_status.label = "%s %s".printf(_("SingBox"), status ? _("running") : _("stopped"));
|
||||||
factory.setup.connect ((obj) => {
|
if (status) {
|
||||||
|
sheet.remove_css_class("singbox-fail");
|
||||||
|
} else {
|
||||||
|
sheet.add_css_class("singbox-fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window(Gtk.Application app) {
|
||||||
|
Object(application: app);
|
||||||
|
|
||||||
|
SingBox.instance.notify["singbox-status"].connect(() => on_singbox_status_change());
|
||||||
|
SingBox.instance.singbox_message.connect((message) => singbox_log.text += "\n" + message);
|
||||||
|
on_singbox_status_change();
|
||||||
|
|
||||||
|
var factory = new Gtk.SignalListItemFactory();
|
||||||
|
factory.setup.connect((obj) => {
|
||||||
var item = (Gtk.ListItem) obj;
|
var item = (Gtk.ListItem) obj;
|
||||||
item.set_child (new Ui.OutboundRow ());
|
item.set_child(new Ui.OutboundRow());
|
||||||
});
|
});
|
||||||
factory.bind.connect ((obj) => {
|
factory.bind.connect((obj) => {
|
||||||
var item = (Gtk.ListItem) obj;
|
var item = (Gtk.ListItem) obj;
|
||||||
var row = (Ui.OutboundRow) item.child;
|
var row = (Ui.OutboundRow) item.child;
|
||||||
var data = (Outbound.Outbound) item.item;
|
var data = (Outbound.Outbound) item.item;
|
||||||
row.set_outbound (data);
|
row.set_outbound(data);
|
||||||
});
|
});
|
||||||
outbounds.set_model (SingBox.instance.outbound_selection);
|
outbounds.set_model(SingBox.instance.outbound_selection);
|
||||||
outbounds.set_factory (factory);
|
outbounds.set_factory(factory);
|
||||||
close_request.connect (() => {
|
|
||||||
SingBox.instance.singbox.force_exit ();
|
close_request.connect(() => {
|
||||||
|
SingBox.instance.singbox.force_exit();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,24 +19,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Singularity.SingBox : Object {
|
class Singularity.SingBox : Object {
|
||||||
|
public bool singbox_status { get; set; }
|
||||||
|
public signal void singbox_message (string message);
|
||||||
|
|
||||||
public ListStore outbound_store = new ListStore (typeof (Outbound.Outbound));
|
public ListStore outbound_store = new ListStore (typeof (Outbound.Outbound));
|
||||||
public Gtk.SingleSelection outbound_selection = null;
|
public Gtk.SingleSelection outbound_selection = null;
|
||||||
public Subprocess? singbox = null;
|
public Subprocess? singbox = null;
|
||||||
|
bool can_use_singbox = true;
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
outbound_selection = new Gtk.SingleSelection (outbound_store);
|
outbound_selection = new Gtk.SingleSelection (outbound_store);
|
||||||
outbound_selection.notify["selected"].connect (() => {
|
outbound_selection.notify["selected"].connect (() => set_up.begin ());
|
||||||
message ("Selected %s", ((Outbound.Outbound) outbound_selection.selected_item).name);
|
singbox_message.connect ((msg) => message ("%s", msg));
|
||||||
set_up ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void set_up () {
|
|
||||||
if (singbox != null) {
|
|
||||||
message ("killing singbox");
|
|
||||||
singbox.force_exit ();
|
|
||||||
}
|
}
|
||||||
|
private void setup_config_dir () {
|
||||||
var conf_dir = File.new_build_filename (Environment.get_user_config_dir (), "Singularity");
|
var conf_dir = File.new_build_filename (Environment.get_user_config_dir (), "Singularity");
|
||||||
try {
|
try {
|
||||||
conf_dir.make_directory ();
|
conf_dir.make_directory ();
|
||||||
|
@ -45,7 +41,10 @@ class Singularity.SingBox : Object {
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
warning ("Error during creating config dir: %s", err.message);
|
warning ("Error during creating config dir: %s", err.message);
|
||||||
}
|
}
|
||||||
var base_config = conf_dir.get_child ("BaseConfig.json");
|
}
|
||||||
|
|
||||||
|
private string get_base_config () {
|
||||||
|
var base_config = File.new_build_filename (Environment.get_user_config_dir (), "Singularity", "BaseConfig.json");
|
||||||
if (!base_config.query_exists ()) {
|
if (!base_config.query_exists ()) {
|
||||||
try {
|
try {
|
||||||
message ("Saving base config");
|
message ("Saving base config");
|
||||||
|
@ -54,7 +53,11 @@ class Singularity.SingBox : Object {
|
||||||
warning ("Error during saving base config: %s", err.message);
|
warning ("Error during saving base config: %s", err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var outbound_config = conf_dir.get_child ("Outbound.json");
|
return base_config.get_path ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string get_outbound_config () {
|
||||||
|
var outbound_config = File.new_build_filename (Environment.get_user_config_dir (), "Singularity", "Outbound.json");
|
||||||
try {
|
try {
|
||||||
message ("Saving outbound config");
|
message ("Saving outbound config");
|
||||||
var generator = new Json.Generator ();
|
var generator = new Json.Generator ();
|
||||||
|
@ -66,14 +69,58 @@ class Singularity.SingBox : Object {
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
warning ("Error during saving outbound config: %s", err.message);
|
warning ("Error during saving outbound config: %s", err.message);
|
||||||
}
|
}
|
||||||
singbox = new Subprocess.newv ({ "sing-box", "--disable-color", "-c", base_config.get_path (), "-c", outbound_config.get_path (), "run" }, GLib.SubprocessFlags.STDERR_PIPE);
|
return outbound_config.get_path ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void wait_for_singbox () {
|
||||||
|
while (!can_use_singbox) {
|
||||||
|
SourceFunc callback = wait_for_singbox.callback;
|
||||||
|
Idle.add ((owned) callback);
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void set_up () {
|
||||||
|
if (singbox != null) {
|
||||||
|
singbox.force_exit ();
|
||||||
|
yield wait_for_singbox ();
|
||||||
|
}
|
||||||
|
singbox_message ("[Singularity] Starting outbound: %s".printf (((Outbound.Outbound) outbound_selection.selected_item).name));
|
||||||
|
setup_config_dir ();
|
||||||
|
var base_cfg = get_base_config ();
|
||||||
|
var outbound_cfg = get_outbound_config ();
|
||||||
|
|
||||||
|
try {
|
||||||
|
singbox = new Subprocess.newv ({ "sing-box", "--disable-color", "-c", base_cfg, "-c", outbound_cfg, "run" }, SubprocessFlags.STDERR_PIPE);
|
||||||
|
} catch (Error err) {
|
||||||
|
singbox_message ("[Singularity] Failed to launch singbox: %s".printf (err.message));
|
||||||
|
}
|
||||||
meow_with_stream.begin (new DataInputStream (singbox.get_stderr_pipe ()));
|
meow_with_stream.begin (new DataInputStream (singbox.get_stderr_pipe ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
async void meow_with_stream (DataInputStream input) {
|
async void meow_with_stream (DataInputStream input) {
|
||||||
for (string line = ""; line != null; line = yield input.read_line_async ())
|
singbox_status = true;
|
||||||
message ("SingBox: %s", line);
|
can_use_singbox = false;
|
||||||
message ("SingBox stream closed");
|
string line = "";
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
line = yield input.read_line_async ();
|
||||||
|
} catch (Error err) {
|
||||||
|
warning ("Error during read_line: %s", err.message);
|
||||||
|
}
|
||||||
|
if (line != null)
|
||||||
|
singbox_message (line);
|
||||||
|
} while (line != null);
|
||||||
|
singbox_message ("[Singularity]: Singbox log closed");
|
||||||
|
try {
|
||||||
|
yield singbox.wait_async ();
|
||||||
|
|
||||||
|
singbox_status = false;
|
||||||
|
} catch (Error err) {
|
||||||
|
warning ("Failed to wait for singbox %s", err.message);
|
||||||
|
}
|
||||||
|
singbox_message ("[Singularity]: Singbox exited with code %d\n".printf (singbox.get_status ()));
|
||||||
|
can_use_singbox = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SingBox () {}
|
private SingBox () {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue