Adding missing database for cache, missing file format and some image

logic.
This commit is contained in:
Sergiotarxz 2024-10-16 13:51:06 +02:00
parent ac40d32ef6
commit 3285ac3987
4 changed files with 273 additions and 43 deletions

112
lib/Exd/DB.pm Normal file
View File

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

24
lib/Exd/DB/Migrations.pm Normal file
View File

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

83
lib/Exd/FileFormat.pm Normal file
View File

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

View File

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