mirror of https://github.com/oupson/FooTerm.git
Initial support for ssh
This commit is contained in:
parent
0b8689ef6c
commit
dba7b10698
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "bindings/vapi"]
|
||||||
|
path = bindings/vapi
|
||||||
|
url = https://gitlab.gnome.org/GNOME/vala-extra-vapis.git
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 9ce7531a398c3ba629c89aad6ba43fb73c6060b6
|
|
@ -37,13 +37,33 @@
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
"config-opts": ["-Dgtk4=true", "-Dgtk3=false", "-Dsixel=true"],
|
"config-opts": ["-Dgtk4=true", "-Dgtk3=false", "-Dsixel=true"],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://gitlab.gnome.org/GNOME/vte/-/archive/015ca4d2fdc57b625add7b23b0afa7193adc45a9/vte-015ca4d2fdc57b625add7b23b0afa7193adc45a9.tar.gz",
|
"url": "https://gitlab.gnome.org/GNOME/vte/-/archive/015ca4d2fdc57b625add7b23b0afa7193adc45a9/vte-015ca4d2fdc57b625add7b23b0afa7193adc45a9.tar.gz",
|
||||||
"sha256": "ba918109936692fe555f1b28f428680ed4940a350709bcf908bdbf9a93498c08"
|
"sha256": "ba918109936692fe555f1b28f428680ed4940a350709bcf908bdbf9a93498c08"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name" : "libssh2",
|
||||||
|
"buildsystem" : "cmake-ninja",
|
||||||
|
"config-opts" : [
|
||||||
|
"-DCMAKE_BUILD_TYPE=RelWithDebInfo",
|
||||||
|
"-DCMAKE_INSTALL_LIBDIR:PATH=/app/lib",
|
||||||
|
"-DBUILD_SHARED_LIBS:BOOL=ON"
|
||||||
|
],
|
||||||
|
"cleanup" : [
|
||||||
|
"/share/doc"
|
||||||
|
],
|
||||||
|
"sources" : [
|
||||||
|
{
|
||||||
|
"type" : "git",
|
||||||
|
"url" : "https://github.com/libssh2/libssh2.git",
|
||||||
|
"tag" : "libssh2-1.10.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name" : "footerm",
|
"name" : "footerm",
|
||||||
"builddir" : true,
|
"builddir" : true,
|
||||||
|
|
10
meson.build
10
meson.build
|
@ -7,7 +7,17 @@ project('footerm', ['c', 'vala'],
|
||||||
i18n = import('i18n')
|
i18n = import('i18n')
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
|
|
||||||
|
metadata_dir = meson.project_source_root() / 'bindings'/ 'metadata'
|
||||||
|
vapi_dir = meson.project_source_root() / 'bindings' / 'vapi'
|
||||||
|
|
||||||
|
add_project_arguments([
|
||||||
|
# Make sure Meson can find custom VAPIs
|
||||||
|
'--vapidir', vapi_dir,
|
||||||
|
'--metadatadir', metadata_dir,
|
||||||
|
],
|
||||||
|
language: 'vala'
|
||||||
|
)
|
||||||
|
add_project_arguments('-D_GNU_SOURCE', language: 'c')
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/fr/oupson/FooTerm">
|
<gresource prefix="/fr/oupson/FooTerm">
|
||||||
<file preprocess="xml-stripblanks">window.ui</file>
|
<file preprocess="xml-stripblanks">window.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">newpane.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">terminalpane.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">newserver.ui</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|
|
@ -19,6 +19,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int main (string[] args) {
|
int main (string[] args) {
|
||||||
|
var rc = SSH2.init (0);
|
||||||
|
if (rc != SSH2.Error.NONE) {
|
||||||
|
stdout.printf ("libssh2 initialization failed (%d)\n", rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
var app = new Footerm.Application ();
|
var app = new Footerm.Application ();
|
||||||
return app.run (args);
|
var res = app.run(args);
|
||||||
|
|
||||||
|
SSH2.exit ();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,21 @@ footerm_sources = [
|
||||||
'main.vala',
|
'main.vala',
|
||||||
'application.vala',
|
'application.vala',
|
||||||
'window.vala',
|
'window.vala',
|
||||||
|
'newpane.vala',
|
||||||
|
'newserver.vala',
|
||||||
|
'terminalpane.vala',
|
||||||
]
|
]
|
||||||
|
|
||||||
footerm_deps = [
|
footerm_deps = [
|
||||||
dependency('gtk4'),
|
dependency('gtk4'),
|
||||||
dependency('libadwaita-1'),
|
dependency('libadwaita-1'),
|
||||||
dependency('vte-2.91-gtk4', version: '>= 0.70.0'),
|
dependency('vte-2.91-gtk4', version: '>= 0.70.0'),
|
||||||
|
dependency('json-glib-1.0', version: '>= 1.2.0'),
|
||||||
|
dependency('libssh2'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
subdir('model')
|
||||||
|
|
||||||
footerm_sources += gnome.compile_resources('footerm-resources',
|
footerm_sources += gnome.compile_resources('footerm-resources',
|
||||||
'footerm.gresource.xml',
|
'footerm.gresource.xml',
|
||||||
c_name: 'footerm'
|
c_name: 'footerm'
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* Server.vala
|
||||||
|
*
|
||||||
|
* Copyright 2023 oupson
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FooTerm.Model {
|
||||||
|
public class Server {
|
||||||
|
public string hostname;
|
||||||
|
public ushort port;
|
||||||
|
public string username;
|
||||||
|
public string password;
|
||||||
|
|
||||||
|
public Server(string hostname, ushort port, string username, string password) {
|
||||||
|
this.hostname = hostname;
|
||||||
|
this.port = port;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
footerm_sources += files(
|
||||||
|
'Server.vala'
|
||||||
|
)
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<requires lib="Adw" version="1.0"/>
|
||||||
|
<requires lib="gtk" version="4.0"/>
|
||||||
|
<template class="FooTermNewPane" parent="GtkBox">
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="newpane_stack">
|
||||||
|
<child>
|
||||||
|
<object class="AdwClamp">
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup" id="server_list">
|
||||||
|
<property name="margin-bottom">32</property>
|
||||||
|
<property name="margin-top">32</property>
|
||||||
|
<property name="margin-start">32</property>
|
||||||
|
<property name="margin-end">32</property>
|
||||||
|
<property name="title" translatable="yes">Servers</property>
|
||||||
|
<child type="header-suffix">
|
||||||
|
<object class="GtkButton" id="newpane_add_button">
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="child">
|
||||||
|
<object class="AdwButtonContent">
|
||||||
|
<property name="icon-name">list-add-symbolic</property>
|
||||||
|
<property name="label" translatable="yes">Add new</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
<style>
|
||||||
|
<class name="flat"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwClamp">
|
||||||
|
<child>
|
||||||
|
<object class="FooTermNewServer" id="new_server"></object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>"
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* newpane.vala
|
||||||
|
*
|
||||||
|
* Copyright 2023 oupson
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FooTerm {
|
||||||
|
[GtkTemplate (ui = "/fr/oupson/FooTerm/newpane.ui")]
|
||||||
|
public class NewPane : Gtk.Box {
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.PreferencesGroup server_list;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned FooTerm.NewServer new_server;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Gtk.Stack newpane_stack;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Gtk.Button newpane_add_button;
|
||||||
|
|
||||||
|
public signal void on_server_selected(FooTerm.Model.Server server);
|
||||||
|
|
||||||
|
construct {
|
||||||
|
this.new_server.on_new_server.connect((s) => {
|
||||||
|
this.newpane_stack.set_visible_child(server_list.get_parent());
|
||||||
|
var action_row = new Adw.ActionRow();
|
||||||
|
action_row.set_title(s.hostname);
|
||||||
|
action_row.set_activatable(true);
|
||||||
|
action_row.activated.connect(() => {
|
||||||
|
this.on_server_selected(s);
|
||||||
|
});
|
||||||
|
server_list.add(action_row);
|
||||||
|
});
|
||||||
|
this.newpane_add_button.clicked.connect (() => {
|
||||||
|
this.newpane_stack.set_visible_child(new_server.get_parent());
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
var config_dir = GLib.File.new_for_path(GLib.Environment.get_user_config_dir());
|
||||||
|
var server_files = config_dir.get_child("servers.json");
|
||||||
|
|
||||||
|
if (server_files.query_exists()) {
|
||||||
|
var parser = new Json.Parser();
|
||||||
|
|
||||||
|
parser.load_from_file(server_files.get_path());
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
GLib.debug("Server file does not exist");
|
||||||
|
}
|
||||||
|
} catch (Error e) {
|
||||||
|
GLib.warning("Failed to read server list : %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<template class="FooTermNewServer" parent="GtkBox">
|
||||||
|
<property name="margin-bottom">32</property>
|
||||||
|
<property name="margin-top">32</property>
|
||||||
|
<property name="margin-start">32</property>
|
||||||
|
<property name="margin-end">32</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="hostname_entry">
|
||||||
|
<property name="title" translatable="yes">Hostname</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="port_entry">
|
||||||
|
<property name="title" translatable="yes">Port</property>
|
||||||
|
<property name="input-purpose">number</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="username_entry">
|
||||||
|
<property name="title" translatable="yes">Username</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPasswordEntryRow" id="password_entry">
|
||||||
|
<property name="title" translatable="yes">Password</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="add_server_button">
|
||||||
|
<property name="label" translatable="yes">Add</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action" />
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* newserver.vala
|
||||||
|
*
|
||||||
|
* Copyright 2023 oupson
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FooTerm {
|
||||||
|
[GtkTemplate (ui = "/fr/oupson/FooTerm/newserver.ui")]
|
||||||
|
public class NewServer : Gtk.Box {
|
||||||
|
public signal void on_new_server(FooTerm.Model.Server server);
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.EntryRow hostname_entry;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.EntryRow port_entry;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.EntryRow username_entry;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.PasswordEntryRow password_entry;
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Gtk.Button add_server_button;
|
||||||
|
|
||||||
|
construct {
|
||||||
|
add_server_button.clicked.connect (this.on_add_button_clicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_add_button_clicked() {
|
||||||
|
var hostname = this.hostname_entry.get_text();
|
||||||
|
var port = int.parse(this.port_entry.get_text());
|
||||||
|
var username = this.username_entry.get_text();
|
||||||
|
var password = this.password_entry.get_text();
|
||||||
|
|
||||||
|
if (port > 65545 || port < 0) {
|
||||||
|
// Port is invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
this.on_new_server(new FooTerm.Model.Server(hostname, (ushort)port, username, password));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<requires lib="vte-2.91-gtk4" version="0.70.0"/>
|
||||||
|
<template class="FooTermTerminalPane" parent="GtkBox">
|
||||||
|
<child>
|
||||||
|
<object class="VteTerminal" id="terminal">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
|
@ -0,0 +1,190 @@
|
||||||
|
/* terminalpane.vala
|
||||||
|
*
|
||||||
|
* Copyright 2023 oupson
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FooTerm {
|
||||||
|
[GtkTemplate (ui = "/fr/oupson/FooTerm/terminalpane.ui")]
|
||||||
|
public class TerminalPane : Gtk.Box {
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Vte.Terminal terminal;
|
||||||
|
|
||||||
|
private SSH2.Session<bool>? session;
|
||||||
|
private SSH2.Channel? channel;
|
||||||
|
private Socket socket;
|
||||||
|
private int slave_pty;
|
||||||
|
|
||||||
|
private FooTerm.Model.Server server;
|
||||||
|
|
||||||
|
public TerminalPane(FooTerm.Model.Server server) {
|
||||||
|
this.server = server;
|
||||||
|
this.terminal.set_enable_sixel (true);
|
||||||
|
this.connect_to_server();
|
||||||
|
this.terminal.char_size_changed.connect(() => {
|
||||||
|
int rows = 0;
|
||||||
|
int columns = 0;
|
||||||
|
this.terminal.get_pty().get_size(out rows, out columns);
|
||||||
|
this.channel.request_pty_size(columns, rows);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~TerminalPane() {
|
||||||
|
// TODO DESTRUCT
|
||||||
|
// session.disconnect( "Normal Shutdown, Thank you for playing");
|
||||||
|
// session = null;
|
||||||
|
// Posix.close(sock);
|
||||||
|
// stdout.printf("all done!\n");)
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connect_to_server() throws GLib.IOError, GLib.Error {
|
||||||
|
this.socket = new Socket (SocketFamily.IPV4, SocketType.STREAM, SocketProtocol.TCP);
|
||||||
|
var addrs = new NetworkAddress(this.server.hostname, this.server.port);
|
||||||
|
socket.connect(addrs.enumerate().next(), null);
|
||||||
|
|
||||||
|
var sock = socket.get_fd(); // TODO
|
||||||
|
|
||||||
|
this.session = SSH2.Session.create<bool>();
|
||||||
|
if (session.handshake(sock) != SSH2.Error.NONE) {
|
||||||
|
stderr.printf("Failure establishing SSH session\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fingerprint = session.get_host_key_hash(SSH2.HashType.SHA1);
|
||||||
|
stdout.printf("Fingerprint: ");
|
||||||
|
for(var i = 0; i < 20; i++) {
|
||||||
|
stdout.printf("%02X ", fingerprint[i]);
|
||||||
|
}
|
||||||
|
stdout.printf("\n");
|
||||||
|
|
||||||
|
if (session.auth_password(this.server.username, this.server.password) != SSH2.Error.NONE) {
|
||||||
|
stdout.printf("\tAuthentication by password failed!\n");
|
||||||
|
session.disconnect( "Normal Shutdown, Thank you for playing");
|
||||||
|
session = null;
|
||||||
|
Posix.close(sock);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
stdout.printf("\tAuthentication by password succeeded.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channel = null;
|
||||||
|
if (session.authenticated && (channel = session.open_channel()) == null) {
|
||||||
|
stderr.printf("Unable to open a session\n");
|
||||||
|
} else {
|
||||||
|
if (channel.request_pty("xterm-256color".data) != SSH2.Error.NONE) {
|
||||||
|
stderr.printf("Failed requesting pty\n");
|
||||||
|
session.disconnect( "Normal Shutdown, Thank you for playing");
|
||||||
|
session = null;
|
||||||
|
Posix.close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.set_env ("TERM", "xterm-256color");
|
||||||
|
|
||||||
|
if (channel.start_shell() != SSH2.Error.NONE) {
|
||||||
|
stderr.printf("Unable to request shell on allocated pty\n");
|
||||||
|
session.disconnect( "Normal Shutdown, Thank you for playing");
|
||||||
|
session = null;
|
||||||
|
Posix.close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
var master_pty = Posix.posix_openpt(Posix.O_RDWR);
|
||||||
|
if (master_pty == -1) {
|
||||||
|
throw GLib.IOError.from_errno (Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = Posix.termios();
|
||||||
|
Posix.cfmakeraw (ref settings);
|
||||||
|
|
||||||
|
if (Posix.tcsetattr (master_pty, Posix.TCSANOW, settings) == -1) {
|
||||||
|
throw GLib.IOError.from_errno (Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Posix.grantpt(master_pty) == -1) {
|
||||||
|
throw GLib.IOError.from_errno (Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Posix.unlockpt(master_pty) == -1) {
|
||||||
|
throw GLib.IOError.from_errno (Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pts_name = Posix.ptsname(master_pty);
|
||||||
|
if (pts_name == null) {
|
||||||
|
throw GLib.IOError.from_errno (Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slave_pty = Posix.open(pts_name, Posix.O_RDWR);
|
||||||
|
if (this.slave_pty < 0) {
|
||||||
|
throw GLib.IOError.from_errno (Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
var vte_pty = new Vte.Pty.foreign_sync(master_pty, null);
|
||||||
|
this.terminal.set_pty(vte_pty);
|
||||||
|
|
||||||
|
session.blocking = false;
|
||||||
|
|
||||||
|
var sock_channel = new GLib.IOChannel.unix_new(sock);
|
||||||
|
sock_channel.set_encoding (null);
|
||||||
|
sock_channel.set_buffered (false);
|
||||||
|
sock_channel.set_close_on_unref (false);
|
||||||
|
|
||||||
|
var slave_channel = new GLib.IOChannel.unix_new(slave_pty);
|
||||||
|
slave_channel.set_encoding (null);
|
||||||
|
slave_channel.set_buffered (false);
|
||||||
|
|
||||||
|
sock_channel.add_watch (GLib.IOCondition.IN, (source, condition) => {
|
||||||
|
if (condition == IOCondition.HUP) {
|
||||||
|
print ("The connection has been broken.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var buffer = new uint8[1024];
|
||||||
|
var size = this.channel.read(buffer);
|
||||||
|
|
||||||
|
if (Posix.write(this.slave_pty, buffer, size) < 0) {
|
||||||
|
throw GLib.IOError.from_errno(Posix.errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch(Error e) {
|
||||||
|
GLib.warning("Failed to read from ssh : %s", e.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
slave_channel.add_watch (GLib.IOCondition.IN, (source, condition) => {
|
||||||
|
if (condition == IOCondition.HUP) {
|
||||||
|
print ("The connection has been broken.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var buffer = new char[1024];
|
||||||
|
size_t size = 0;
|
||||||
|
source.read_chars(buffer, out size);
|
||||||
|
|
||||||
|
this.channel.write ((uint8[])buffer[0:size]);
|
||||||
|
return true;
|
||||||
|
} catch (Error e) {
|
||||||
|
GLib.warning("Failed to read from terminal : %s", e.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk" version="4.0"/>
|
<requires lib="gtk" version="4.0"/>
|
||||||
<requires lib="Adw" version="1.0"/>
|
<requires lib="Adw" version="1.0"/>
|
||||||
<requires lib="vte-2.91-gtk4" version="0.70.0"/>
|
|
||||||
<template class="FootermWindow" parent="AdwApplicationWindow">
|
<template class="FootermWindow" parent="AdwApplicationWindow">
|
||||||
<property name="default-width">600</property>
|
<property name="default-width">600</property>
|
||||||
<property name="default-height">300</property>
|
<property name="default-height">300</property>
|
||||||
|
@ -20,8 +19,8 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="VteTerminal" id="terminal">
|
<object class="GtkBox" id="pane_content">
|
||||||
<property name="hexpand">true</property>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -22,22 +22,21 @@ namespace Footerm {
|
||||||
[GtkTemplate (ui = "/fr/oupson/FooTerm/window.ui")]
|
[GtkTemplate (ui = "/fr/oupson/FooTerm/window.ui")]
|
||||||
public class Window : Adw.ApplicationWindow {
|
public class Window : Adw.ApplicationWindow {
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private unowned Vte.Terminal terminal;
|
private unowned Gtk.Box pane_content;
|
||||||
|
|
||||||
public Window (Gtk.Application app) {
|
public Window (Gtk.Application app) {
|
||||||
Object (application: app);
|
Object (application: app);
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
this.terminal.set_enable_sixel (true);
|
var new_pane = new FooTerm.NewPane();
|
||||||
this.terminal.spawn_async (Vte.PtyFlags.NO_CTTY, null, {"flatpak-spawn", "--host", "bash"}, null, 0, null, -1, null, this.on_spawn_finished);
|
ulong handler_id;
|
||||||
}
|
handler_id = new_pane.on_server_selected.connect((s) => {
|
||||||
|
new_pane.disconnect (handler_id);
|
||||||
|
pane_content.remove(new_pane);
|
||||||
private void on_spawn_finished (Vte.Terminal t, Pid _pid, GLib.Error? error) {
|
pane_content.append(new FooTerm.TerminalPane(s));
|
||||||
if (error != null) {
|
});
|
||||||
warning ("%s", error.message);
|
pane_content.append(new_pane);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue