From 3285ac39870e04fe597531f790c9df335d6ae4d8 Mon Sep 17 00:00:00 2001 From: Sergiotarxz Date: Wed, 16 Oct 2024 13:51:06 +0200 Subject: [PATCH] Adding missing database for cache, missing file format and some image logic. --- lib/Exd/DB.pm | 112 +++++++++++++++++++++++++++++++++++++++ lib/Exd/DB/Migrations.pm | 24 +++++++++ lib/Exd/FileFormat.pm | 83 +++++++++++++++++++++++++++++ lib/Exd/Gui.pm | 97 ++++++++++++++++++--------------- 4 files changed, 273 insertions(+), 43 deletions(-) create mode 100644 lib/Exd/DB.pm create mode 100644 lib/Exd/DB/Migrations.pm create mode 100644 lib/Exd/FileFormat.pm diff --git a/lib/Exd/DB.pm b/lib/Exd/DB.pm new file mode 100644 index 0000000..dbf2b49 --- /dev/null +++ b/lib/Exd/DB.pm @@ -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; diff --git a/lib/Exd/DB/Migrations.pm b/lib/Exd/DB/Migrations.pm new file mode 100644 index 0000000..b5d3a7e --- /dev/null +++ b/lib/Exd/DB/Migrations.pm @@ -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; diff --git a/lib/Exd/FileFormat.pm b/lib/Exd/FileFormat.pm new file mode 100644 index 0000000..a255103 --- /dev/null +++ b/lib/Exd/FileFormat.pm @@ -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; diff --git a/lib/Exd/Gui.pm b/lib/Exd/Gui.pm index 3d562b3..8e4c75e 100644 --- a/lib/Exd/Gui.pm +++ b/lib/Exd/Gui.pm @@ -21,6 +21,7 @@ use Capture::Tiny qw/capture/; use Exd::Printer; use Exd::DeviceToBluetooth; use Exd::DeviceToImage; +use Exd::FileFormat; use Exd::Gui::PrinterConfigure; use JSON; @@ -45,7 +46,7 @@ Glib::Object::Introspection->setup( has _app => ( is => 'rw', ); -has window => ( is => 'rw', ); +has window => ( is => 'rw', ); has _scroll_log_upper => ( is => 'rw', ); has _execute_log => ( is => 'rw', ); @@ -60,7 +61,28 @@ has _read_from_script => ( is => 'rw' ); has _read_from_parent => ( is => 'rw' ); has _write_to_parent => ( 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) { if ( !defined $self->_tempdir_previews_guts ) { @@ -115,7 +137,7 @@ sub _run_script( $self, $device = undef, $verbose = 1 ) { my $fh = $self->_write_to_script; print $fh JSON::to_json( { - script => $script, + exd_dir => ''.$self->_file_format->dir, device => $device->serialize, verbose => $verbose, } @@ -144,8 +166,8 @@ sub _daemon_script_runner($self) { my $fh = $self->_read_from_parent; my $line = <$fh>; my $data = JSON::from_json($line); - my ( $script, $device, $verbose ) = - $data->@{ 'script', 'device', 'verbose' }; + my ( $exd_dir, $device, $verbose ) = + $data->@{ 'exd_dir', 'device', 'verbose' }; $device = $self->device_hash_to_object($device); if ( $last_pid && $last_device->isa('Exd::DeviceToImage') @@ -157,7 +179,7 @@ sub _daemon_script_runner($self) { my $new_pid = fork; if ( !$new_pid ) { eval { - $self->_on_run_script( $script, $device, $write_to_parent, + $self->_on_run_script( path($exd_dir), $device, $write_to_parent, $verbose ); }; if ($@) { @@ -193,20 +215,15 @@ sub device_hash_to_object( $self, $device_hash ) { 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 ); local $| = 1; + my $exd = Exd::FileFormat->new(dir => $exd_dir); + my ( $stdout, $stderr, $exit ) = capture { eval { - my $sub = - eval -'use v5.40.0; use strict; use warnings; use utf8; no Carp::Always; use Cairo; use Pango;' - . $script; - if ($@) { - die $@; - } - $sub->($printer); + $exd->execute($printer); }; if ($@) { if ($verbose) { @@ -227,8 +244,8 @@ sub _monitor_run($self) { for my $fh (@fhs) { $fh->blocking(0); my $text = ''; - while (my $line = <$fh>) { - if (!defined $line) { + while ( my $line = <$fh> ) { + if ( !defined $line ) { next; } $text .= $line; @@ -238,12 +255,12 @@ sub _monitor_run($self) { } } -sub add_to_log($self, $text) { - my $execute_log = $self->_execute_log; - my $buffer = $execute_log->get_buffer; - my $end_iter = $buffer->get_end_iter; +sub add_to_log( $self, $text ) { + my $execute_log = $self->_execute_log; + my $buffer = $execute_log->get_buffer; + my $end_iter = $buffer->get_end_iter; - $buffer->insert( $end_iter, $text, -1 ); + $buffer->insert( $end_iter, $text, -1 ); } sub _build__select($self) { @@ -262,8 +279,8 @@ sub _on_preview($self) { } } -sub _populate_editor($self, $box_editor_preview) { - my $editor = Gtk4::Source::View->new; +sub _populate_editor( $self, $box_editor_preview ) { + my $editor = Gtk4::Source::View->new; $self->_editor($editor); $editor->set_hexpand(1); $editor->set_vexpand(1); @@ -274,25 +291,19 @@ sub _populate_editor($self, $box_editor_preview) { $editor->set_smart_backspace(1); $editor->set_show_line_numbers(1); my $buffer = $editor->get_buffer(); - $buffer->set_text( <<'EOF', -1 ); -sub ($printer) { - $printer->print_text( - [ - 'hola mundo' - ], - 30 - ); - $printer->print_n_lf(2); - - $printer->print; -} -EOF + $buffer->set_text( $self->_file_format->get_script, -1 ); $buffer->set_language( Gtk4::Source::LanguageManager::get_default()->get_language('perl') ); $buffer->set_highlight_syntax(1); $buffer->signal_connect( 'changed', 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); } ); @@ -302,7 +313,7 @@ EOF $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 ); $self->_populate_editor($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) { - my $preview_picture = Gtk4::Picture->new; +sub _populate_preview( $self, $box_editor_preview ) { + my $preview_picture = Gtk4::Picture->new; $self->_preview_widget($preview_picture); my $preview_scroll_window = Gtk4::ScrolledWindow->new; $preview_scroll_window->set_child($preview_picture); @@ -345,7 +356,7 @@ sub _activate($self) { $select_printer->signal_connect( 'clicked', sub { - Exd::Gui::PrinterConfigure->new(app => $self)->start; + Exd::Gui::PrinterConfigure->new( app => $self )->start; } ); $preview_button->signal_connect( @@ -381,8 +392,8 @@ sub _activate($self) { $box_vertical->append($box_buttons); my $execute_log_window = Gtk4::ScrolledWindow->new; $execute_log_window->set_vexpand(0); - $execute_log_window->set_property('height-request', 300); - my $scroll = $execute_log_window->get_vadjustment; + $execute_log_window->set_property( 'height-request', 300 ); + my $scroll = $execute_log_window->get_vadjustment; $self->_scroll_log_upper( $scroll->get_upper ); $scroll->signal_connect( 'changed',