Adding missing database for cache, missing file format and some image
logic.
This commit is contained in:
parent
ac40d32ef6
commit
3285ac3987
|
@ -0,0 +1,112 @@
|
||||||
|
package Exd::DB;
|
||||||
|
|
||||||
|
use v5.40.0;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use DBI;
|
||||||
|
use Path::Tiny;
|
||||||
|
|
||||||
|
use Exd::DB::Migrations;
|
||||||
|
|
||||||
|
my $dbh;
|
||||||
|
|
||||||
|
sub connect {
|
||||||
|
if ( defined $dbh ) {
|
||||||
|
return $dbh;
|
||||||
|
}
|
||||||
|
my $class = shift;
|
||||||
|
my $database = $class->database;
|
||||||
|
$dbh = DBI->connect(
|
||||||
|
"dbi:SQLite:dbname=$database",
|
||||||
|
,
|
||||||
|
undef, undef,
|
||||||
|
{
|
||||||
|
RaiseError => 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
$class->_migrate($dbh);
|
||||||
|
return $dbh;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub database($class) {
|
||||||
|
my $home = $ENV{HOME};
|
||||||
|
my $flatpak_config_dir = $ENV{XDG_CONFIG_HOME};
|
||||||
|
my $db_path = '';
|
||||||
|
{
|
||||||
|
if ($flatpak_config_dir) {
|
||||||
|
$db_path = $flatpak_config_dir . '/';
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$db_path = $home . '/' if $home;
|
||||||
|
}
|
||||||
|
$db_path .= '.exd/db.sqlite';
|
||||||
|
path($db_path)->parent->mkpath;
|
||||||
|
return $db_path;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _migrate {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
local $dbh->{RaiseError} = 0;
|
||||||
|
local $dbh->{PrintError} = 0;
|
||||||
|
my @migrations = Exd::DB::Migrations::MIGRATIONS();
|
||||||
|
if ( $class->get_current_migration($dbh) > @migrations ) {
|
||||||
|
warn "Something happened there, wrong migration number.";
|
||||||
|
}
|
||||||
|
if ( $class->get_current_migration($dbh) >= @migrations ) {
|
||||||
|
say STDERR "Migrations already applied.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$class->_apply_migrations( $dbh, \@migrations );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _apply_migrations {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
my $migrations = shift;
|
||||||
|
for (
|
||||||
|
my $i = $class->get_current_migration($dbh) ;
|
||||||
|
$i < @$migrations ;
|
||||||
|
$i++
|
||||||
|
)
|
||||||
|
{
|
||||||
|
local $dbh->{RaiseError} = 1;
|
||||||
|
my $current_migration = $migrations->[$i];
|
||||||
|
my $migration_number = $i + 1;
|
||||||
|
$class->_apply_migration( $dbh, $current_migration, $migration_number );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _apply_migration {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
my $current_migration = shift;
|
||||||
|
my $migration_number = shift;
|
||||||
|
{
|
||||||
|
if (ref $current_migration eq 'CODE') {
|
||||||
|
$current_migration->($dbh);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$dbh->do($current_migration);
|
||||||
|
}
|
||||||
|
$dbh->do( <<'EOF', undef, 'current_migration', $migration_number );
|
||||||
|
INSERT INTO options
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (name) DO
|
||||||
|
UPDATE SET value = $2;
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_current_migration {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
my $result = $dbh->selectrow_hashref( <<'EOF', undef, 'current_migration' );
|
||||||
|
select value from options where name = ?;
|
||||||
|
EOF
|
||||||
|
return int( $result->{value} // 0 );
|
||||||
|
}
|
||||||
|
1;
|
|
@ -0,0 +1,24 @@
|
||||||
|
package Exd::DB::Migrations;
|
||||||
|
|
||||||
|
use v5.40.0;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use feature 'signatures';
|
||||||
|
|
||||||
|
sub MIGRATIONS {
|
||||||
|
return (
|
||||||
|
'CREATE TABLE options (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
value TEXT
|
||||||
|
);',
|
||||||
|
'CREATE TABLE cached_bluetooth_printers (
|
||||||
|
address TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
port INTEGER NOT NULL
|
||||||
|
);',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
1;
|
|
@ -0,0 +1,83 @@
|
||||||
|
package Exd::FileFormat;
|
||||||
|
|
||||||
|
use v5.40.0;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use Moo;
|
||||||
|
use Path::Tiny;
|
||||||
|
use Archive::Zip;
|
||||||
|
|
||||||
|
use Digest::SHA qw/sha256_hex/;
|
||||||
|
|
||||||
|
has dir => ( is => 'rw' );
|
||||||
|
|
||||||
|
sub new_tmp($class) {
|
||||||
|
my $tempdir = Path::Tiny->tempdir();
|
||||||
|
return $class->new( dir => $tempdir );
|
||||||
|
}
|
||||||
|
|
||||||
|
has _images_dir => ( is => 'lazy' );
|
||||||
|
|
||||||
|
sub _build__images_dir($self) {
|
||||||
|
my $dir = $self->dir;
|
||||||
|
my $images = $dir->child('images');
|
||||||
|
$images->mkpath;
|
||||||
|
return $images;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub add_png_image( $self, $file_contents ) {
|
||||||
|
my $sha_image = sha256_hex($file_contents);
|
||||||
|
$self->_images_dir->child("$sha_image.png")->spew_raw($file_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_image( $self, $sha ) {
|
||||||
|
return $self->_images_dir->child("$sha.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_script($self) {
|
||||||
|
return $self->dir->child('script.pl')->slurp_raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_script( $self, $script_contents ) {
|
||||||
|
return $self->dir->child('script.pl')->spew_raw($script_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub to_zip($self) {
|
||||||
|
my $zip = Archive::Zip->new;
|
||||||
|
$self->dir->visit(
|
||||||
|
sub( $path, $state ) {
|
||||||
|
return if $path->is_dir;
|
||||||
|
my $path_relative = $path->relative( $self->dir );
|
||||||
|
$zip->addFile( $path, $path_relative );
|
||||||
|
},
|
||||||
|
{ recurse => 1 }
|
||||||
|
);
|
||||||
|
return $zip;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub from_zip_file( $class, $zip_file ) {
|
||||||
|
my $tempdir = Path::Tiny->tempdir();
|
||||||
|
my $zip = Archive::Zip->new($zip_file);
|
||||||
|
$zip->extractTree( '.', $tempdir );
|
||||||
|
return $class->new( dir => $tempdir );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub execute( $self, $printer ) {
|
||||||
|
my $script = $self->get_script;
|
||||||
|
my $sub =
|
||||||
|
eval
|
||||||
|
'use v5.40.0; use strict; use warnings; use utf8; no Carp::Always; use Cairo; use Pango;'
|
||||||
|
. $script;
|
||||||
|
if ($@) {
|
||||||
|
die $@;
|
||||||
|
}
|
||||||
|
$sub->($self, $printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub save( $self, $output_file ) {
|
||||||
|
$self->to_zip->writeToFileNamed($output_file);
|
||||||
|
}
|
||||||
|
1;
|
|
@ -21,6 +21,7 @@ use Capture::Tiny qw/capture/;
|
||||||
use Exd::Printer;
|
use Exd::Printer;
|
||||||
use Exd::DeviceToBluetooth;
|
use Exd::DeviceToBluetooth;
|
||||||
use Exd::DeviceToImage;
|
use Exd::DeviceToImage;
|
||||||
|
use Exd::FileFormat;
|
||||||
use Exd::Gui::PrinterConfigure;
|
use Exd::Gui::PrinterConfigure;
|
||||||
|
|
||||||
use JSON;
|
use JSON;
|
||||||
|
@ -45,7 +46,7 @@ Glib::Object::Introspection->setup(
|
||||||
|
|
||||||
has _app => ( is => 'rw', );
|
has _app => ( is => 'rw', );
|
||||||
|
|
||||||
has window => ( is => 'rw', );
|
has window => ( is => 'rw', );
|
||||||
has _scroll_log_upper => ( is => 'rw', );
|
has _scroll_log_upper => ( is => 'rw', );
|
||||||
|
|
||||||
has _execute_log => ( is => 'rw', );
|
has _execute_log => ( is => 'rw', );
|
||||||
|
@ -60,7 +61,28 @@ has _read_from_script => ( is => 'rw' );
|
||||||
has _read_from_parent => ( is => 'rw' );
|
has _read_from_parent => ( is => 'rw' );
|
||||||
has _write_to_parent => ( is => 'rw' );
|
has _write_to_parent => ( is => 'rw' );
|
||||||
has _write_to_script => ( is => 'rw' );
|
has _write_to_script => ( is => 'rw' );
|
||||||
has device => (is => 'rw');
|
has device => ( is => 'rw' );
|
||||||
|
has _file_format => (
|
||||||
|
is => 'rw',
|
||||||
|
default => sub {
|
||||||
|
my $tmp = Exd::FileFormat->new_tmp;
|
||||||
|
say $tmp->dir;
|
||||||
|
$tmp->set_script(<<'EOF');
|
||||||
|
sub ($exd, $printer) {
|
||||||
|
$printer->print_text(
|
||||||
|
[
|
||||||
|
'hola mundo'
|
||||||
|
],
|
||||||
|
30
|
||||||
|
);
|
||||||
|
$printer->print_n_lf(2);
|
||||||
|
|
||||||
|
$printer->print;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
return $tmp;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
sub _tempdir_previews($self) {
|
sub _tempdir_previews($self) {
|
||||||
if ( !defined $self->_tempdir_previews_guts ) {
|
if ( !defined $self->_tempdir_previews_guts ) {
|
||||||
|
@ -115,7 +137,7 @@ sub _run_script( $self, $device = undef, $verbose = 1 ) {
|
||||||
my $fh = $self->_write_to_script;
|
my $fh = $self->_write_to_script;
|
||||||
print $fh JSON::to_json(
|
print $fh JSON::to_json(
|
||||||
{
|
{
|
||||||
script => $script,
|
exd_dir => ''.$self->_file_format->dir,
|
||||||
device => $device->serialize,
|
device => $device->serialize,
|
||||||
verbose => $verbose,
|
verbose => $verbose,
|
||||||
}
|
}
|
||||||
|
@ -144,8 +166,8 @@ sub _daemon_script_runner($self) {
|
||||||
my $fh = $self->_read_from_parent;
|
my $fh = $self->_read_from_parent;
|
||||||
my $line = <$fh>;
|
my $line = <$fh>;
|
||||||
my $data = JSON::from_json($line);
|
my $data = JSON::from_json($line);
|
||||||
my ( $script, $device, $verbose ) =
|
my ( $exd_dir, $device, $verbose ) =
|
||||||
$data->@{ 'script', 'device', 'verbose' };
|
$data->@{ 'exd_dir', 'device', 'verbose' };
|
||||||
$device = $self->device_hash_to_object($device);
|
$device = $self->device_hash_to_object($device);
|
||||||
if ( $last_pid
|
if ( $last_pid
|
||||||
&& $last_device->isa('Exd::DeviceToImage')
|
&& $last_device->isa('Exd::DeviceToImage')
|
||||||
|
@ -157,7 +179,7 @@ sub _daemon_script_runner($self) {
|
||||||
my $new_pid = fork;
|
my $new_pid = fork;
|
||||||
if ( !$new_pid ) {
|
if ( !$new_pid ) {
|
||||||
eval {
|
eval {
|
||||||
$self->_on_run_script( $script, $device, $write_to_parent,
|
$self->_on_run_script( path($exd_dir), $device, $write_to_parent,
|
||||||
$verbose );
|
$verbose );
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
|
@ -193,20 +215,15 @@ sub device_hash_to_object( $self, $device_hash ) {
|
||||||
return $sub->();
|
return $sub->();
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _on_run_script( $self, $script, $device, $write_to_parent, $verbose = 1 ) {
|
sub _on_run_script( $self, $exd_dir, $device, $write_to_parent, $verbose = 1 ) {
|
||||||
my $printer = Exd::Printer->new( device => $device );
|
my $printer = Exd::Printer->new( device => $device );
|
||||||
local $| = 1;
|
local $| = 1;
|
||||||
|
|
||||||
|
my $exd = Exd::FileFormat->new(dir => $exd_dir);
|
||||||
|
|
||||||
my ( $stdout, $stderr, $exit ) = capture {
|
my ( $stdout, $stderr, $exit ) = capture {
|
||||||
eval {
|
eval {
|
||||||
my $sub =
|
$exd->execute($printer);
|
||||||
eval
|
|
||||||
'use v5.40.0; use strict; use warnings; use utf8; no Carp::Always; use Cairo; use Pango;'
|
|
||||||
. $script;
|
|
||||||
if ($@) {
|
|
||||||
die $@;
|
|
||||||
}
|
|
||||||
$sub->($printer);
|
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
if ($verbose) {
|
if ($verbose) {
|
||||||
|
@ -227,8 +244,8 @@ sub _monitor_run($self) {
|
||||||
for my $fh (@fhs) {
|
for my $fh (@fhs) {
|
||||||
$fh->blocking(0);
|
$fh->blocking(0);
|
||||||
my $text = '';
|
my $text = '';
|
||||||
while (my $line = <$fh>) {
|
while ( my $line = <$fh> ) {
|
||||||
if (!defined $line) {
|
if ( !defined $line ) {
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
$text .= $line;
|
$text .= $line;
|
||||||
|
@ -238,12 +255,12 @@ sub _monitor_run($self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub add_to_log($self, $text) {
|
sub add_to_log( $self, $text ) {
|
||||||
my $execute_log = $self->_execute_log;
|
my $execute_log = $self->_execute_log;
|
||||||
my $buffer = $execute_log->get_buffer;
|
my $buffer = $execute_log->get_buffer;
|
||||||
my $end_iter = $buffer->get_end_iter;
|
my $end_iter = $buffer->get_end_iter;
|
||||||
|
|
||||||
$buffer->insert( $end_iter, $text, -1 );
|
$buffer->insert( $end_iter, $text, -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _build__select($self) {
|
sub _build__select($self) {
|
||||||
|
@ -262,8 +279,8 @@ sub _on_preview($self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _populate_editor($self, $box_editor_preview) {
|
sub _populate_editor( $self, $box_editor_preview ) {
|
||||||
my $editor = Gtk4::Source::View->new;
|
my $editor = Gtk4::Source::View->new;
|
||||||
$self->_editor($editor);
|
$self->_editor($editor);
|
||||||
$editor->set_hexpand(1);
|
$editor->set_hexpand(1);
|
||||||
$editor->set_vexpand(1);
|
$editor->set_vexpand(1);
|
||||||
|
@ -274,25 +291,19 @@ sub _populate_editor($self, $box_editor_preview) {
|
||||||
$editor->set_smart_backspace(1);
|
$editor->set_smart_backspace(1);
|
||||||
$editor->set_show_line_numbers(1);
|
$editor->set_show_line_numbers(1);
|
||||||
my $buffer = $editor->get_buffer();
|
my $buffer = $editor->get_buffer();
|
||||||
$buffer->set_text( <<'EOF', -1 );
|
$buffer->set_text( $self->_file_format->get_script, -1 );
|
||||||
sub ($printer) {
|
|
||||||
$printer->print_text(
|
|
||||||
[
|
|
||||||
'hola mundo'
|
|
||||||
],
|
|
||||||
30
|
|
||||||
);
|
|
||||||
$printer->print_n_lf(2);
|
|
||||||
|
|
||||||
$printer->print;
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
$buffer->set_language(
|
$buffer->set_language(
|
||||||
Gtk4::Source::LanguageManager::get_default()->get_language('perl') );
|
Gtk4::Source::LanguageManager::get_default()->get_language('perl') );
|
||||||
$buffer->set_highlight_syntax(1);
|
$buffer->set_highlight_syntax(1);
|
||||||
$buffer->signal_connect(
|
$buffer->signal_connect(
|
||||||
'changed',
|
'changed',
|
||||||
sub {
|
sub {
|
||||||
|
my $buffer = $self->_editor->get_buffer;
|
||||||
|
my $begin_iter = $buffer->get_iter_at_offset(0);
|
||||||
|
my $end_iter = $buffer->get_end_iter;
|
||||||
|
my $script = $buffer->get_text( $begin_iter, $end_iter, 0 );
|
||||||
|
|
||||||
|
$self->_file_format->set_script($script);
|
||||||
$self->_generate_preview_file(0);
|
$self->_generate_preview_file(0);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -302,7 +313,7 @@ EOF
|
||||||
$box_editor_preview->append($editor_scroll_window);
|
$box_editor_preview->append($editor_scroll_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _populate_editor_and_preview($self, $box_vertical) {
|
sub _populate_editor_and_preview( $self, $box_vertical ) {
|
||||||
my $box_editor_preview = Gtk4::Box->new( 'horizontal', 10 );
|
my $box_editor_preview = Gtk4::Box->new( 'horizontal', 10 );
|
||||||
$self->_populate_editor($box_editor_preview);
|
$self->_populate_editor($box_editor_preview);
|
||||||
$self->_populate_preview($box_editor_preview);
|
$self->_populate_preview($box_editor_preview);
|
||||||
|
@ -316,8 +327,8 @@ sub _populate_editor_and_preview($self, $box_vertical) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _populate_preview($self, $box_editor_preview) {
|
sub _populate_preview( $self, $box_editor_preview ) {
|
||||||
my $preview_picture = Gtk4::Picture->new;
|
my $preview_picture = Gtk4::Picture->new;
|
||||||
$self->_preview_widget($preview_picture);
|
$self->_preview_widget($preview_picture);
|
||||||
my $preview_scroll_window = Gtk4::ScrolledWindow->new;
|
my $preview_scroll_window = Gtk4::ScrolledWindow->new;
|
||||||
$preview_scroll_window->set_child($preview_picture);
|
$preview_scroll_window->set_child($preview_picture);
|
||||||
|
@ -345,7 +356,7 @@ sub _activate($self) {
|
||||||
$select_printer->signal_connect(
|
$select_printer->signal_connect(
|
||||||
'clicked',
|
'clicked',
|
||||||
sub {
|
sub {
|
||||||
Exd::Gui::PrinterConfigure->new(app => $self)->start;
|
Exd::Gui::PrinterConfigure->new( app => $self )->start;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$preview_button->signal_connect(
|
$preview_button->signal_connect(
|
||||||
|
@ -381,8 +392,8 @@ sub _activate($self) {
|
||||||
$box_vertical->append($box_buttons);
|
$box_vertical->append($box_buttons);
|
||||||
my $execute_log_window = Gtk4::ScrolledWindow->new;
|
my $execute_log_window = Gtk4::ScrolledWindow->new;
|
||||||
$execute_log_window->set_vexpand(0);
|
$execute_log_window->set_vexpand(0);
|
||||||
$execute_log_window->set_property('height-request', 300);
|
$execute_log_window->set_property( 'height-request', 300 );
|
||||||
my $scroll = $execute_log_window->get_vadjustment;
|
my $scroll = $execute_log_window->get_vadjustment;
|
||||||
$self->_scroll_log_upper( $scroll->get_upper );
|
$self->_scroll_log_upper( $scroll->get_upper );
|
||||||
$scroll->signal_connect(
|
$scroll->signal_connect(
|
||||||
'changed',
|
'changed',
|
||||||
|
|
Loading…
Reference in New Issue