diff --git a/app_config.json b/app_config.json
new file mode 100644
index 0000000..a842cd3
--- /dev/null
+++ b/app_config.json
@@ -0,0 +1,4 @@
+{
+ "licenser-server": "https://tnsoehasuhaons.beta.exd.sergiotarxz.me",
+ "debug": true
+}
diff --git a/exd-logo.png b/exd-logo.png
new file mode 100644
index 0000000..76ef86a
Binary files /dev/null and b/exd-logo.png differ
diff --git a/exd.svg b/exd.svg
index ac50b58..eeeeafe 100644
--- a/exd.svg
+++ b/exd.svg
@@ -7,10 +7,11 @@
viewBox="0 0 512 512"
version="1.1"
id="svg1"
- inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
- sodipodi:docname="exd.svg"
+ sodipodi:docname="exd-final.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id="path132"
+ style="fill:#378c6d;fill-opacity:1;stroke-width:114.401;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
+ d="m 161.18607,272.63285 -7.90117,8.87913 -5.7407,-6.45092 c -1.33434,1.44066 -2.16044,3.4001 -2.16044,5.57675 v 16.74012 c 0,4.43679 3.40156,8.00497 7.6311,8.00497 h 205.97018 c 4.22954,0 7.63109,-3.56818 7.63109,-8.00497 v -16.74012 c 0,-2.17665 -0.82615,-4.13609 -2.16049,-5.57675 l -5.74067,6.45092 -7.90114,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90115,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90115,-8.87913 -7.90116,8.87913 -7.90115,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90114,-8.87913 -7.90117,8.87913 -7.90115,-8.87913 -7.90116,8.87913 z"
+ sodipodi:nodetypes="cccssssssccccccccccccccccccccccccccc" />
+ id="path133"
+ style="fill:#136246;fill-opacity:1;stroke-width:114.401;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
+ d="m 161.18607,276.74258 -7.90117,8.87912 -5.7407,-6.45091 c -1.33434,1.44067 -2.16044,3.40014 -2.16044,5.57676 v 15.96472 c 0,4.43673 3.40156,8.00496 7.6311,8.00496 h 205.97018 c 4.22954,0 7.63109,-3.56823 7.63109,-8.00496 v -15.96472 c 0,-2.17662 -0.82615,-4.13609 -2.16049,-5.57676 l -5.74067,6.45091 -7.90114,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90115,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90115,-8.87912 -7.90116,8.87912 -7.90115,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90114,-8.87912 -7.90117,8.87912 -7.90115,-8.87912 -7.90116,8.87912 z"
+ sodipodi:nodetypes="cccssssssccccccccccccccccccccccccccc" />
+ style="fill:url(#linearGradient144);fill-opacity:1;stroke:none;stroke-width:0.182682;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
+ d="m 9.5988037,20.108333 c 0,-1.286464 0.3197629,-2.116666 0.7756213,-2.116666 h 1.513594 v 2.116666 z"
+ id="path134"
+ sodipodi:nodetypes="ccccc"
+ inkscape:transform-center-x="1.4581046"
+ inkscape:transform-center-y="-4.3858528"
+ clip-path="url(#clipPath29)"
+ transform="matrix(14.931319,0,0,15.662846,3.1629451,10.340015)" />
+ id="path135"
+ style="fill:url(#linearGradient143);fill-opacity:1;stroke:none;stroke-width:2.79371;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
+ inkscape:transform-center-x="-11.49783"
+ inkscape:transform-center-y="-2.1929348"
+ d="m 158.06883,292.14071 c 6.80652,0 11.01839,13.19126 11.01839,33.15303 v 99.45902 l 7.90116,-8.2882 7.90115,8.2882 7.90118,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90115,8.2882 7.90117,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90114,8.2882 7.90116,-8.2882 7.90115,8.2882 7.90117,-8.2882 7.90114,8.2882 7.90117,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90115,8.2882 7.90116,-8.2882 7.90115,8.2882 v -99.45902 c 0,-19.96177 -4.21186,-33.15303 -11.0184,-33.15303 z"
+ sodipodi:nodetypes="cscccccccccccccccccccccccccccssc" />
+
EXD
-
-
-
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:66.4819px;font-family:Cantarell;-inkscape-font-specification:'Cantarell Bold';text-align:justify;opacity:0.349358;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66206;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers stroke fill"
+ x="240.62625"
+ y="390.69879"
+ id="text136"
+ transform="scale(0.97636847,1.0242035)">EXD
+ style="opacity:0.962206;fill:#959492;fill-opacity:1;stroke:none;stroke-width:15.5961;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+ d="m 319.2092,333.58203 v 8.28825 h 39.50577 v -8.28825 z"
+ id="path137" />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/Exd.pm b/lib/Exd.pm
new file mode 100644
index 0000000..1e66197
--- /dev/null
+++ b/lib/Exd.pm
@@ -0,0 +1,72 @@
+package Exd;
+
+use v5.40.0;
+
+use strict;
+use warnings;
+use utf8;
+
+use Moo;
+use JSON;
+use Path::Tiny;
+use UUID::URandom qw/create_uuid_string/;
+
+has config => ( is => 'lazy' );
+
+sub _build_config($self) {
+ return JSON::from_json( $self->_config_file_name->slurp_utf8 );
+}
+
+sub _config_file_name($self) {
+ return path(__FILE__)->parent->parent->child('app_config.json');
+}
+
+sub licenser_server($self) {
+ return $self->config->{'licenser-server'};
+}
+
+sub debug($self) {
+ return $self->config->{debug};
+}
+
+sub uuid($self) {
+ require Exd::DB;
+ my $dbh = Exd::DB->connect;
+ my $key = 'licenser-uuid';
+ my $option =
+ $dbh->selectrow_hashref( 'SELECT value FROM options WHERE name = ?;',
+ {}, $key );
+ my $uuid;
+ if (defined $option) {
+ $uuid = $option->{value};
+ }
+ if ( !defined $uuid ) {
+ $uuid = create_uuid_string();
+ $dbh->do( 'INSERT OR IGNORE INTO options (name, value) VALUES (?, ?);',
+ {}, $key, $uuid );
+ }
+ return $uuid;
+}
+
+{
+ my $key = 'activated';
+ sub is_activated_license($self) {
+ require Exd::DB;
+ my $dbh = Exd::DB->connect;
+ my $option =
+ $dbh->selectrow_hashref( 'SELECT value FROM options WHERE name = ?;',
+ {}, $key );
+ if (!defined $option) {
+ return 0;
+ }
+ return $option->{value};
+ }
+
+ sub activate_license($self) {
+ require Exd::DB;
+ my $dbh = Exd::DB->connect;
+ $dbh->do( 'INSERT OR IGNORE INTO options (name, value) VALUES (?, ?);',
+ {}, $key, 1 );
+ }
+}
+1;
diff --git a/lib/Exd/DeviceToCatPrinter.pm b/lib/Exd/DeviceToCatPrinter.pm
index 31d2155..e55a711 100644
--- a/lib/Exd/DeviceToCatPrinter.pm
+++ b/lib/Exd/DeviceToCatPrinter.pm
@@ -56,7 +56,6 @@ sub _build__gatt($self) {
sub try_to_connect($self) {
my $device_api = $self->_device;
- say $device_api->Connected;
return if $device_api->Connected;
$device_api->Connect;
diff --git a/lib/Exd/Gui.pm b/lib/Exd/Gui.pm
index 3be0a4b..31bdd16 100644
--- a/lib/Exd/Gui.pm
+++ b/lib/Exd/Gui.pm
@@ -15,6 +15,7 @@ use Glib::IO;
use Glib::Object::Introspection;
use Path::Tiny;
use GD::Image;
+use Mojo::UserAgent;
use IO::Select;
use Capture::Tiny qw/capture/;
@@ -25,6 +26,7 @@ use Exd::FileFormat;
use Exd::Gui::PrinterConfigure;
use Exd::Gui::FontGallery;
use Exd::Gui::Instance;
+use Exd;
use JSON;
use Net::DBus;
@@ -53,6 +55,12 @@ Glib::Object::Introspection->setup(
package => 'Gtk4::Source',
);
+Glib::Object::Introspection->setup(
+ basename => 'Adw',
+ version => '1',
+ package => 'Adw',
+);
+
has _app => ( is => 'rw', );
has _select => ( is => 'lazy', );
@@ -74,10 +82,28 @@ has _first_time => ( is => 'rw', default => sub { 1 } );
has _last_instance_id => ( is => 'rw', default => sub { 0 } );
has parent_pid => ( is => 'rw' );
has instances => ( is => 'rw', default => sub { return {} } );
+has _gresources_path => ( is => 'lazy', );
+has _exd => ( is => 'lazy' );
-sub start( $self ) {
- my $app = Gtk4::Application->new( 'me.sergiotarxz.Exd', [qw/default-flags handles-command-line handles-open/] );
- my $bus = Net::DBus->session;
+sub _build__gresources_path($self) {
+ my $root = path(__FILE__)->parent->parent->parent;
+ my $gresources = $root->child('resources.gresource');
+ 0 == system( 'which', 'glib-compile-resources' )
+ && system( 'glib-compile-resources', $root->child('resources.xml') );
+ if ( !-e $gresources ) {
+ {
+ die 'No gresources';
+ }
+ }
+ return $gresources;
+}
+
+sub start($self) {
+ Glib::IO::resources_register(
+ Glib::IO::Resource::load( $self->_gresources_path ) );
+ my $app = Adw::Application->new( 'me.sergiotarxz.Exd',
+ [qw/default-flags handles-command-line handles-open/] );
+ my $bus = Net::DBus->session;
my $started = 0;
eval {
$bus->get_service('me.sergiotarxz.Exd');
@@ -87,7 +113,7 @@ sub start( $self ) {
$self->_app($app);
my $arguments = [];
$app->signal_connect(
- command_line => sub($app, $command_line) {
+ command_line => sub( $app, $command_line ) {
$arguments = $command_line->get_arguments;
my $exd_file = shift @$arguments;
$self->_activate($exd_file);
@@ -105,11 +131,9 @@ sub start( $self ) {
}
);
say @ARGV;
- $app->run(\@ARGV);
+ $app->run( \@ARGV );
}
-
-
sub _startup($self) {
my $app = $self->_app;
}
@@ -120,6 +144,15 @@ sub send_packet_to_daemon( $self, $instance, $packet ) {
$self->_write_to_script->flush;
}
+sub send_packet_check_if_activated( $self, $instance, $uuid) {
+ $self->send_packet_to_daemon($instance, {
+ type => 'check-activated',
+ data => {
+ uuid => $uuid,
+ }
+ });
+}
+
sub _daemon_runner($self) {
my ( $read_from_script, $write_to_parent );
my ( $read_from_parent, $write_to_script );
@@ -165,12 +198,51 @@ sub _daemon_runner($self) {
if ( $packet->{type} eq 'font-scan' ) {
next;
}
+ if ( $packet->{type} eq 'check-activated') {
+ $self->_on_check_activated($instance_id, $packet->{data});
+ next;
+ }
+ warn 'Packet not recognized: '. Data::Dumper::Dumper $packet;
}
exit;
}
close $read_from_parent;
}
+sub _build__exd($self) {
+ require Exd;
+ return Exd->new;
+}
+
+sub _on_check_activated($self, $instance_id, $data) {
+ my $pid = fork;
+ if (!$pid) {
+ while (1) {
+ eval {
+ my $url = $self->_exd->licenser_server.'/get_paid/'.$self->_exd->uuid;
+ my $ua = Mojo::UserAgent->new;
+ my $activated = $ua->get($url)->result->json;
+ if ($activated) {
+ say 'Program sucessfully activated, thank you!';
+ $self->_exd->activate_license;
+ $self->send_packet_to_window($instance_id, {
+ type => 'activated!',
+ });
+ exit;
+ }
+ };
+ if ($@) {
+ warn $@;
+ }
+ sleep 5;
+ if (!kill 0, $self->parent_pid) {
+ exit;
+ }
+ }
+ exit;
+ }
+}
+
sub send_packet_to_window( $self, $instance_id, $packet ) {
$packet->{instance_id} = $instance_id;
$self->_write_to_parent->say( JSON::to_json($packet) );
@@ -196,8 +268,8 @@ sub _on_save_packet( $self, $instance_id, $json ) {
$self->_save( $instance_id, $exd, $dest_file );
};
if ($@) {
- $self->_log($instance_id, $@);
- $self->_send_safe_to_exit($instance_id, 1);
+ $self->_log( $instance_id, $@ );
+ $self->_send_safe_to_exit( $instance_id, 1 );
}
exit;
}
@@ -222,7 +294,7 @@ sub _on_run_script_packet( $self, $instance_id, $json ) {
$json->@{ 'exd_dir', 'device', 'verbose' };
$device = $self->device_hash_to_object($device);
- my ($read, $write);
+ my ( $read, $write );
pipe $read, $write;
my $new_pid = fork;
@@ -239,12 +311,12 @@ sub _on_run_script_packet( $self, $instance_id, $json ) {
close $write;
if ($verbose) {
my $log_getter_pid = fork;
- if (!$log_getter_pid) {
- $self->_log($instance_id, '### STARTED EXECUTION ###');
- while (my $line = <$read>) {
- $self->_log($instance_id, $line);
+ if ( !$log_getter_pid ) {
+ $self->_log( $instance_id, '### STARTED EXECUTION ###' );
+ while ( my $line = <$read> ) {
+ $self->_log( $instance_id, $line );
}
- $self->_log($instance_id, '### TERMINATED EXECUTION ###');
+ $self->_log( $instance_id, '### TERMINATED EXECUTION ###' );
exit;
}
}
@@ -353,12 +425,13 @@ sub _log( $self, $instance_id, $message ) {
}
);
}
+
sub _save( $self, $instance_id, $exd, $dest_file ) {
$self->_log( $instance_id, "Pending save to $dest_file." );
- $self->_send_safe_to_exit($instance_id, 0);
+ $self->_send_safe_to_exit( $instance_id, 0 );
my $zip = $exd->to_zip;
$zip->writeToFileNamed( '' . $dest_file );
- $self->_send_safe_to_exit($instance_id, 1);
+ $self->_send_safe_to_exit( $instance_id, 1 );
$self->_log( $instance_id, "Save completed to $dest_file." );
}
diff --git a/lib/Exd/Gui/Instance.pm b/lib/Exd/Gui/Instance.pm
index 1fef9bf..f30940f 100644
--- a/lib/Exd/Gui/Instance.pm
+++ b/lib/Exd/Gui/Instance.pm
@@ -12,13 +12,18 @@ use Glib;
use Glib::IO;
use Glib::Object::Introspection;
use Path::Tiny;
+use Mojo::UserAgent;
-has app => ( is => 'rw', required => 1, weak_ref => 1 );
-has instance_id => ( is => 'rw', required => 1 );
-has window => ( is => 'rw' );
-has _editor => ( is => 'rw', );
-has _safe_to_exit => ( is => 'rw', default => sub { 1 } );
-has device => ( is => 'rw' );
+has app => ( is => 'rw', required => 1, weak_ref => 1 );
+has instance_id => ( is => 'rw', required => 1 );
+has window => ( is => 'rw' );
+has _editor => ( is => 'rw', );
+has _safe_to_exit => ( is => 'rw', default => sub { 1 } );
+has _transparent_avoid_input => ( is => 'rw' );
+has _paywall => ( is => 'rw' );
+has device => ( is => 'rw' );
+has _pay_url => ( is => 'lazy' );
+has _activated => ( is => 'rw' );
has file_format => (
is => 'rw',
default => sub {
@@ -46,7 +51,13 @@ has _tempdir_previews_guts => ( is => 'rw' );
has _can_open_printer_configure => ( is => 'rw', default => sub { 1 } );
has _preview_number => ( is => 'rw', default => sub { 0 } );
has _save_path => ( is => 'rw' );
-has _want_to_close => ( is => 'rw' );
+has _want_to_close => ( is => 'rw' );
+has _exd => ( is => 'lazy' );
+has _overlay => ( is => 'rw' );
+
+sub _build__exd($self) {
+ return Exd->new;
+}
sub start( $self, $exd_file ) {
my $win = $self->app->create_application_window;
@@ -116,7 +127,7 @@ sub start( $self, $exd_file ) {
$execute_log->set_editable(0);
$execute_log->set_cursor_visible(0);
- $win->set_title('Exd (Thermal Printer)');
+ $win->set_title('Hiperthermia (Thermal Printer) ' . ($self->_exd->debug ? 'DEBUG' : '') );
$win->set_default_size( 1200, 900 );
$execute_log->set_hexpand(1);
@@ -150,20 +161,192 @@ sub start( $self, $exd_file ) {
);
$execute_log_window->set_child($execute_log);
$box_vertical->append($execute_log_window);
- $win->set_child($box_vertical);
+ my $overlay = Gtk4::Overlay->new;
+ $self->_overlay($overlay);
+ $overlay->set_child($box_vertical);
+ $self->_activated( $self->_exd->is_activated_license );
+ if ( !$self->_activated ) {
+ my $uuid = $self->_exd->uuid;
+ my $instance_id = $self->instance_id;
+ $self->_add_timeout_paywall($overlay);
+ $self->app->send_packet_check_if_activated( $self, $uuid );
+ }
+ $win->set_child($overlay);
$win->present;
}
+sub _show_about_dialog($self) {
+ my $window = Gtk4::Window->new;
+ $window->set_default_size( 650, 650 );
+ $window->set_transient_for( $self->window );
+ $window->set_title('About Hiperthermia');
+ my $picture = Gtk4::Picture->new;
+ $picture->set_property('width-request', 256);
+ $picture->set_property('height-request', 256);
+ $picture->set_filename(path(__FILE__)->parent->parent->parent->parent->child('exd-logo.png').'');
+ my $label = Gtk4::Label->new(undef);
+ $label->set_markup(<<"EOF");
+Welcome to Hiperthermia.
+
+This is a paid program but fully open source for everyone,
+have a copy of the source code at our source repo under the AGPLv3 license.
+
+NO WARRANTY TO THE EXTENT ALLOWED BY LAW, but fell free to drop
+\@sergiotarxz a message if you find bugs, issues or need support
+to use this program.
+
+Our amazing contributors:
+
+Sergiotarxz (Developer):
+ Gitea
+ \@sergiotarxz in Telegram.
+ \@sergiotarxz\@owlcode.tech in XMPP.
+(Open to freelance work and full time employement,
+accepts ETH, BTC and Bank transfers)
+
+Lucidraws (Logo creator):
+ lintr.ee
+(Open to graphic design comissions,
+accepts ETH, BTC and Mercado Pago payments)
+EOF
+ my $box = Gtk4::Box->new( 'vertical', 10 );
+ $box->set_hexpand(1);
+ $box->set_vexpand(1);
+ $box->set_halign('center');
+ $box->set_valign('center');
+ $box->append($picture);
+ $box->append($label);
+ my $scroll = Gtk4::ScrolledWindow->new;
+ $scroll->set_vexpand(1);
+ $scroll->set_hexpand(1);
+ $scroll->set_child($box);
+ $window->set_child($scroll);
+ $window->present;
+}
+
+sub _add_timeout_paywall( $self, $overlay ) {
+ if ( $self->_activated ) {
+ return 0;
+ }
+ if ( defined $self->_pay_url ) {
+ Glib::Timeout->add(
+ $self->_exd->debug ? 1_000 : 20_000,
+ sub {
+ my $box = Gtk4::Box->new( 'vertical', 10 );
+ $self->_transparent_avoid_input($box);
+ $box->set_opacity(0.7);
+ $box->add_css_class('avoid-main-input');
+ $box->set_halign('fill');
+ $box->set_valign('fill');
+ my $paywall = Gtk4::Box->new( 'vertical', 10 );
+ $self->_paywall($paywall);
+
+ $paywall->set_property( 'width-request', 800 );
+ $paywall->set_property( 'height-request', 800 );
+ $paywall->add_css_class('paywall');
+ my $inner_paywall_box = Gtk4::Box->new( 'vertical', 10 );
+ my $title = Gtk4::Label->new('This program is not activated');
+ my $activate = Gtk4::Button->new_with_label('Pay and activate');
+ my $about = Gtk4::Button->new_with_label('More about the program');
+ my $remind_me_later =
+ Gtk4::Button->new_with_label('Remind me later');
+ $about->signal_connect(
+ clicked => sub {
+ $self->_show_about_dialog;
+ }
+ );
+ $activate->signal_connect(
+ clicked => sub {
+ my $launcher =
+ Gtk4::UriLauncher->new( $self->_pay_url );
+ $launcher->launch( $self->window, undef, undef );
+ }
+ );
+ $remind_me_later->signal_connect(
+ 'clicked' => sub {
+ $overlay->remove_overlay($paywall);
+ $overlay->remove_overlay($box);
+ $self->_paywall(undef);
+ $self->_transparent_avoid_input(undef);
+ $self->_add_timeout_paywall($overlay);
+ }
+ );
+ my $picture = Gtk4::Image->new_from_file(path(__FILE__)->parent->parent->parent->parent->child('exd-logo.png').'');
+ $picture->set_pixel_size(256);
+ $inner_paywall_box->append($picture);
+ $inner_paywall_box->append($title);
+ if ($self->_exd->debug) {
+ my $debug_message = Gtk4::Label->new('This is debug compilation, do not use your real payment data but a Stripe test card');
+ $inner_paywall_box->append($debug_message);
+ }
+ $inner_paywall_box->append($activate);
+ $inner_paywall_box->append($remind_me_later);
+ $inner_paywall_box->append($about);
+ $inner_paywall_box->set_valign('center');
+ $inner_paywall_box->set_vexpand(1);
+ $paywall->set_vexpand(1);
+ $inner_paywall_box->set_halign('center');
+ $paywall->set_halign('center');
+ $paywall->set_valign('fill');
+ $overlay->add_overlay($box);
+ $overlay->add_overlay($paywall);
+ $paywall->append($inner_paywall_box);
+ Glib::Timeout->add(
+ 1_000,
+ sub {
+ return 0 if !defined $self->_transparent_avoid_input;
+ if ($self->_activated) {
+ if ( defined $self->_paywall ) {
+ $self->_overlay->remove_overlay( $self->_paywall );
+ }
+ if ( defined $self->_transparent_avoid_input ) {
+ $self->_overlay->remove_overlay(
+ $self->_transparent_avoid_input );
+ }
+ return 0;
+ }
+ return 1;
+ }
+ );
+ return 0;
+ }
+ );
+ }
+}
+
+sub _build__pay_url($self) {
+ my $url;
+ eval {
+ my $ua = Mojo::UserAgent->new;
+ $url = $ua->get(
+ $self->_exd->licenser_server . '/get_pay_url/' . $self->_exd->uuid )
+ ->result->json->{url};
+ };
+ if ($@) {
+ warn $@;
+ }
+ return $url;
+}
+
sub _create_popover_menu( $self, $box ) {
my $menu_model = Glib::IO::Menu->new;
my $file_menu = Glib::IO::Menu->new;
+ my $help_menu = Glib::IO::Menu->new;
my $open_action =
Glib::IO::SimpleAction->new( 'open.' . $self->instance_id, undef );
my $save_action =
Glib::IO::SimpleAction->new( 'save.' . $self->instance_id, undef );
my $save_as_action =
Glib::IO::SimpleAction->new( 'save-as.' . $self->instance_id, undef );
+ my $about_action =
+ Glib::IO::SimpleAction->new( 'about.' . $self->instance_id, undef );
my $app = $self->app->_app;
+ $about_action->signal_connect(
+ 'activate',
+ sub {
+ $self->_show_about_dialog;
+ }
+ );
$open_action->signal_connect(
'activate',
sub {
@@ -185,10 +368,13 @@ sub _create_popover_menu( $self, $box ) {
$app->add_action($open_action);
$app->add_action($save_action);
$app->add_action($save_as_action);
+ $app->add_action($about_action);
$file_menu->append( 'Open', 'app.open.' . $self->instance_id );
$file_menu->append( 'Save', 'app.save.' . $self->instance_id );
$file_menu->append( 'Save as', 'app.save-as.' . $self->instance_id );
+ $help_menu->append( 'About', 'app.about.' . $self->instance_id);
$menu_model->append_submenu( 'File', $file_menu );
+ $menu_model->append_submenu( 'Help', $help_menu );
my $popover = Gtk4::PopoverMenuBar->new_from_model($menu_model);
$box->append($popover);
}
@@ -243,7 +429,7 @@ sub _populate_editor_and_preview( $self, $box_vertical ) {
sub _update_editor_buffer($self) {
my $editor = $self->_editor;
- if (defined $editor) {
+ if ( defined $editor ) {
my $buffer = $editor->get_buffer();
$buffer->set_text( $self->file_format->get_script, -1 );
}
@@ -324,6 +510,15 @@ sub receive_packet( $self, $packet ) {
}
return;
}
+ if ( $packet->{type} eq 'activated!' ) {
+ $self->mark_activated;
+ return;
+ }
+ warn 'Packet not recognized ' . Data::Dumper::Dumper $packet;
+}
+
+sub mark_activated($self) {
+ $self->_activated(1);
}
sub _add_to_log( $self, $text ) {
@@ -562,7 +757,7 @@ sub _open_file( $self, $file ) {
$self->_save_path($file);
my $window = $self->window;
$window->set_title(
- "Exd (Thermal Printer) " . path( $self->_save_path )->basename );
+ "Hiperthermia (Thermal Printer) " . ($self->_exd->debug ? 'DEBUG' : '') . path( $self->_save_path )->basename );
$self->file_format( Exd::FileFormat->from_zip_file($file) );
$self->_update_editor_buffer;
}
diff --git a/lib/Exd/Gui/PrinterConfigure.pm b/lib/Exd/Gui/PrinterConfigure.pm
index 366a4a3..6cbcac7 100644
--- a/lib/Exd/Gui/PrinterConfigure.pm
+++ b/lib/Exd/Gui/PrinterConfigure.pm
@@ -17,18 +17,18 @@ use Exd::DeviceToBluetooth;
use Exd::DeviceToRawFile;
use Exd::DeviceToCatPrinter;
-has app => (is => 'ro', required => 1);
-has instance => (is => 'ro', required => 1);
-has on_close => ( is => 'ro', required => 1 );
-has _pid_daemon => (is => 'rw');
-has _read_from_daemon => (is => 'rw');
-has _write_to_app => (is => 'rw');
-has _select => ( is => 'lazy', );
-has _window => ( is => 'rw' );
+has app => ( is => 'ro', required => 1 );
+has instance => ( is => 'ro', required => 1 );
+has on_close => ( is => 'ro', required => 1 );
+has _pid_daemon => ( is => 'rw' );
+has _read_from_daemon => ( is => 'rw' );
+has _write_to_app => ( is => 'rw' );
+has _select => ( is => 'lazy', );
+has _window => ( is => 'rw' );
has _bluetooth_printers => ( is => 'rw', default => sub { [] } );
-has _usb_printers => ( is => 'rw', default => sub { [] });
-has _cat_printers => ( is => 'rw', default => sub { [] });
-has _stop_timeout => ( is => 'rw' );
+has _usb_printers => ( is => 'rw', default => sub { [] } );
+has _cat_printers => ( is => 'rw', default => sub { [] } );
+has _stop_timeout => ( is => 'rw' );
sub _build__select($self) {
return IO::Select->new;
@@ -38,123 +38,208 @@ sub _read_bluetooth_printers($self) {
my @fhs = $self->_select->can_read(0);
for my $fh (@fhs) {
$fh->blocking(0);
- while (defined(my $line = <$fh>)) {
- my @return = map {$self->app->device_hash_to_object($_)} @{JSON::from_json($line)};
- $self->_bluetooth_printers(\@return);
- Exd::DeviceToBluetooth->cache_printers($self->_bluetooth_printers);
+ while ( defined( my $line = <$fh> ) ) {
+ my @return = map { $self->app->device_hash_to_object($_) }
+ @{ JSON::from_json($line) };
+ $self->_bluetooth_printers( \@return );
+ Exd::DeviceToBluetooth->cache_printers(
+ $self->_bluetooth_printers );
}
}
}
-sub _usb_printer_print_to_box($self, $device, $box) {
+sub _usb_printer_print_to_box( $self, $device, $box ) {
my $window = $self->_window;
- my $button = Gtk4::Button->new_with_label($device->output_file);
- $button->signal_connect('clicked', sub {
- $self->instance->device($device);
- $window->close;
- });
+ my $button = Gtk4::Button->new_with_label( $device->output_file );
+ $button->signal_connect(
+ 'clicked',
+ sub {
+ $self->instance->device($device);
+ $window->close;
+ }
+ );
$box->append($button);
}
-sub _cat_printer_print_to_box($self, $device, $box) {
+sub _cat_printer_print_to_box( $self, $device, $box ) {
my $window = $self->_window;
- my $button = Gtk4::Button->new_with_label($device->path);
- $button->signal_connect('clicked', sub {
- $self->instance->device($device);
- $window->close;
- });
+ my $button = Gtk4::Button->new_with_label( $device->path );
+ $button->signal_connect(
+ 'clicked',
+ sub {
+ $self->instance->device($device);
+ $window->close;
+ }
+ );
$box->append($button);
}
-sub _bluetoth_printer_print_to_box($self, $device, $box) {
+sub _bluetoth_printer_print_to_box( $self, $device, $box ) {
my $window = $self->_window;
- my $button = Gtk4::Button->new_with_label($device->name.':'.$device->address);
- $button->signal_connect('clicked', sub {
- $self->instance->device($device);
- $window->close;
- });
+ my $button =
+ Gtk4::Button->new_with_label( $device->name . ':' . $device->address );
+ $button->signal_connect(
+ 'clicked',
+ sub {
+ $self->instance->device($device);
+ $window->close;
+ }
+ );
$box->append($button);
}
+
sub _read_usb_printers($self) {
- my @printers = map { Exd::DeviceToRawFile->new(output_file => $_) } glob '/dev/usb/lp*';
- $self->_usb_printers(\@printers);
+ my @printers = map { Exd::DeviceToRawFile->new( output_file => $_ ) }
+ glob '/dev/usb/lp*';
+ $self->_usb_printers( \@printers );
+}
+
+sub _device_api( $self, $path ) {
+ my $session = Net::DBus->system( private => 1 );
+ my $service = $session->get_service('org.bluez');
+ return $service->get_object( $path, 'org.bluez.Device1' );
+}
+
+has _pid_read_special_cat_printer => ( is => 'rw' );
+
+sub _try_to_connect_difficult_cat_printer_devices( $self, $key, $device ) {
+ if ( defined $device && $device->{Name} eq 'SC05-6F4C' ) {
+ my $device_api = $self->_device_api($key);
+ if ( $device_api->Connected ) {
+ return;
+ }
+ $device_api->get_service->get_bus->get_connection->disconnect;
+ if ( defined $self->_pid_read_special_cat_printer && kill 0,
+ $self->_pid_read_special_cat_printer )
+ {
+ # Ignoring if there is a fork running.
+ return;
+ }
+ $self->_pid_read_special_cat_printer(fork);
+ if ( !$self->_pid_read_special_cat_printer ) {
+ my $device_api = $self->_device_api($key);
+ eval {
+ $device_api->CancelPairing;
+ sleep 1;
+ };
+ if ($@) {
+ warn $@;
+ }
+ eval {
+ $device_api->Pair;
+ sleep 1;
+ };
+ if ($@) {
+ warn $@;
+ }
+ eval {
+ $device_api->Connect;
+ sleep 1;
+ };
+ if ($@) {
+ warn $@;
+ }
+ $device_api->get_service->get_bus->get_connection->disconnect;
+ exit;
+ }
+ }
}
sub _read_cat_printers($self) {
- my $session = Net::DBus->system;
- my $service = $session->get_service('org.bluez');
- my $path = '/';
+ my $session = Net::DBus->system( private => 1 );
+ my $service = $session->get_service('org.bluez');
+ my $path = '/';
my $object_manager_interface = 'org.freedesktop.DBus.ObjectManager';
- my $object = $service->get_object($path, $object_manager_interface);
+ my $object = $service->get_object( $path, $object_manager_interface );
use Data::Dumper;
- my $items = $object->GetManagedObjects;
+ my $items = $object->GetManagedObjects;
+ $object->get_service->get_bus->get_connection->disconnect;
my @paths_tx;
- for my $key (keys %$items) {
- my $item = $items->{$key}{'org.bluez.GattCharacteristic1'};
- if (!defined $item) {
+
+ for my $key ( keys %$items ) {
+ my $device = $items->{$key}{'org.bluez.Device1'};
+ my $item = $items->{$key}{'org.bluez.GattCharacteristic1'};
+
+ {
+ next if defined $item;
+ $self->_try_to_connect_difficult_cat_printer_devices( $key,
+ $device );
+ }
+ if ( !defined $item ) {
next;
}
- if ($item->{UUID} eq '0000ae01-0000-1000-8000-00805f9b34fb') {
+ if ( $item->{UUID} eq '0000ae01-0000-1000-8000-00805f9b34fb' ) {
push @paths_tx, $key;
}
}
- $self->_cat_printers([map { Exd::DeviceToCatPrinter->new(path => $_) } sort {$a cmp $b} @paths_tx]);
+ $self->_cat_printers(
+ [
+ map { Exd::DeviceToCatPrinter->new( path => $_ ) }
+ sort { $a cmp $b } @paths_tx
+ ]
+ );
}
sub start($self) {
$self->_start_daemon_bluetooth_search;
my $parent_window = $self->instance->window;
- my $window = Gtk4::Window->new;
+ my $window = Gtk4::Window->new;
$self->_window($window);
$window->set_default_size( 600, 600 );
- $window->signal_connect('close-request', sub{
- $self->_on_close;
- return 0;
- });
- $window->set_title('Printer Selection');
- $window->set_child(Gtk4::Label->new('Searching for printers...'));
- $self->_select->add($self->_read_from_daemon);
- $self->_bluetooth_printers(Exd::DeviceToBluetooth->devices_from_cache);
- Glib::Timeout->add(1000, sub {
- return 0 if $self->_stop_timeout;
- eval {
- $self->_read_usb_printers;
- $self->_read_cat_printers;
- $self->_read_bluetooth_printers;
- $self->_update_box;
- };
- if ($@) {
- die $@;
+ $window->signal_connect(
+ 'close-request',
+ sub {
+ $self->_on_close;
+ return 0;
}
- return 1;
- });
+ );
+ $window->set_title('Printer Selection');
+ $window->set_child( Gtk4::Label->new('Searching for printers...') );
+ $self->_select->add( $self->_read_from_daemon );
+ $self->_bluetooth_printers( Exd::DeviceToBluetooth->devices_from_cache );
+ Glib::Timeout->add(
+ 1000,
+ sub {
+ return 0 if $self->_stop_timeout;
+ eval {
+ $self->_read_usb_printers;
+ $self->_read_cat_printers;
+ $self->_read_bluetooth_printers;
+ $self->_update_box;
+ };
+ if ($@) {
+ warn $@;
+ }
+ return 1;
+ }
+ );
$window->present;
- $window->set_transient_for($self->instance->window);
+ $window->set_transient_for( $self->instance->window );
}
sub _update_box($self) {
- my $window = $self->_window;
- my $box = Gtk4::Box->new( 'vertical', 10 );
+ my $window = $self->_window;
+ my $box = Gtk4::Box->new( 'vertical', 10 );
my $bluetooth_printers = $self->_bluetooth_printers;
- for my $device ($bluetooth_printers->@*) {
- $self->_bluetoth_printer_print_to_box($device, $box);
+ for my $device ( $bluetooth_printers->@* ) {
+ $self->_bluetoth_printer_print_to_box( $device, $box );
}
- for my $device ($self->_usb_printers->@*) {
- $self->_usb_printer_print_to_box($device, $box);
+ for my $device ( $self->_usb_printers->@* ) {
+ $self->_usb_printer_print_to_box( $device, $box );
}
- for my $device ($self->_cat_printers->@*) {
- $self->_cat_printer_print_to_box($device, $box);
+ for my $device ( $self->_cat_printers->@* ) {
+ $self->_cat_printer_print_to_box( $device, $box );
}
$window->set_child($box);
}
sub _start_daemon_bluetooth_search($self) {
- my ($read, $write);
+ my ( $read, $write );
pipe $read, $write;
$self->_read_from_daemon($read);
$self->_write_to_app($write);
my $pid = fork;
- if (!$pid) {
+ if ( !$pid ) {
close $read;
while (1) {
$self->_daemon_bluetooth_search_iteration;
@@ -166,27 +251,37 @@ sub _start_daemon_bluetooth_search($self) {
}
sub _daemon_bluetooth_search_iteration($self) {
-# say 'Listing bluetooth devices';
- if (!kill 0, $self->app->parent_pid) {
+
+ # say 'Listing bluetooth devices';
+ if ( !kill 0, $self->app->parent_pid ) {
say 'Parent died at bluetooth search iteration, exiting...';
exit 1;
}
my $devices = Net::Bluetooth::get_remote_devices;
my @return;
- for my $address (keys %$devices) {
- say "Detected $devices->{$address}:$address, looking if it supports Serial Port...";
- my $search = Net::Bluetooth::sdp_search($address, "1101", "");
- if (defined $search && defined $search->{RFCOMM}) {
- say "$devices->{$address}:$address supports Serial Port and may be a printer, the port is: $search->{RFCOMM}";
+ for my $address ( keys %$devices ) {
+ next if $devices->{$address} eq 'SC05-6F4C';
+ say
+"Detected $devices->{$address}:$address, looking if it supports Serial Port...";
+ my $search = Net::Bluetooth::sdp_search( $address, "1101", "" );
+ if ( defined $search && defined $search->{RFCOMM} ) {
+ say
+"$devices->{$address}:$address supports Serial Port and may be a printer, the port is: $search->{RFCOMM}";
say $devices->{$address};
- push @return, Exd::DeviceToBluetooth->new(name => $devices->{$address}, address => $address, port => $search->{RFCOMM});
+ push @return,
+ Exd::DeviceToBluetooth->new(
+ name => $devices->{$address},
+ address => $address,
+ port => $search->{RFCOMM}
+ );
}
}
-# say 'Finishing listing bluetooth devices';
+
+ # say 'Finishing listing bluetooth devices';
@return = map { $_->serialize } sort { $a->name cmp $b->name } @return;
- $self->_write_to_app->say(JSON::to_json(\@return));
+ $self->_write_to_app->say( JSON::to_json( \@return ) );
$self->_write_to_app->flush;
-
+
}
sub _on_close($self) {
diff --git a/lib/Exd/Printer.pm b/lib/Exd/Printer.pm
index c34b737..b937047 100644
--- a/lib/Exd/Printer.pm
+++ b/lib/Exd/Printer.pm
@@ -121,7 +121,7 @@ sub print($self) {
if system(
qw/convert/,
$image_file_in,
- qw/-resize 384x -brightness-contrast +10 -dither FloydSteinberg -remap pattern:gray50/,
+ qw/-resize 384x -brightness-contrast +0.8 -dither FloydSteinberg -remap pattern:gray50/,
$image_file_out
);
$image_final = Exd::Utils::get_gd_image( $image_file_out . '' );
diff --git a/style-dark.css b/style-dark.css
new file mode 100644
index 0000000..6dd8ff1
--- /dev/null
+++ b/style-dark.css
@@ -0,0 +1,7 @@
+headerbar box {
+ background: #015544;
+}
+
+box.paywall {
+ background: #010F0C;
+}
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..f81adc0
--- /dev/null
+++ b/style.css
@@ -0,0 +1,15 @@
+headerbar box {
+ background: #48E1C0;
+}
+
+button {
+ min-width: 0;
+}
+
+box.paywall {
+ background: white;
+}
+
+box.avoid-main-input {
+ background: black;
+}