diff --git a/lib/Exd/DeviceToBluetooth.pm b/lib/Exd/DeviceToBluetooth.pm index f261f11..4f3d04f 100644 --- a/lib/Exd/DeviceToBluetooth.pm +++ b/lib/Exd/DeviceToBluetooth.pm @@ -23,6 +23,10 @@ has port => ( required => 1, ); +has name => ( + is => 'ro', +); + has _guts_device => ( is => 'rw' ); has _tempdir => ( is => 'rw', ); @@ -95,6 +99,9 @@ sub serialize($self) { my $hash = {}; $hash->{address} = $self->address; $hash->{port} = $self->port; + if (defined $self->name) { + $hash->{name} = $self->name; + } $hash->{type} = 'bluetooth'; return $hash; } diff --git a/lib/Exd/DeviceToRawFile.pm b/lib/Exd/DeviceToRawFile.pm index 9342a3e..26c21fc 100644 --- a/lib/Exd/DeviceToRawFile.pm +++ b/lib/Exd/DeviceToRawFile.pm @@ -28,15 +28,30 @@ sub _build__guts_device($self) { } sub image( $self, $image ) { - $self->_guts_device->printer->image($image); + eval { + $self->_guts_device->printer->image($image); + }; + if ($@) { + die 'Unable to write to file does ' . $self->output_file . " exist?, error: $@."; + } } sub lf($self) { - $self->_guts_device->printer->lf; + eval { + $self->_guts_device->printer->lf; + }; + if ($@) { + die 'Unable to write to file does ' . $self->output_file . " exist?, error: $@."; + } } sub print($self) { - $self->_guts_device->printer->print; + eval { + $self->_guts_device->printer->print; + }; + if ($@) { + die 'Unable to write to file, does ' . $self->output_file . " exist?, error: $@."; + } } sub serialize($self) { diff --git a/lib/Exd/Gui.pm b/lib/Exd/Gui.pm index 5ba6087..3d562b3 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::Gui::PrinterConfigure; use JSON; @@ -44,7 +45,7 @@ Glib::Object::Introspection->setup( has _app => ( is => 'rw', ); -has _win => ( is => 'rw', ); +has window => ( is => 'rw', ); has _scroll_log_upper => ( is => 'rw', ); has _execute_log => ( is => 'rw', ); @@ -59,6 +60,7 @@ 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'); sub _tempdir_previews($self) { if ( !defined $self->_tempdir_previews_guts ) { @@ -106,10 +108,8 @@ sub _run_script( $self, $device = undef, $verbose = 1 ) { my $end_iter = $buffer->get_end_iter; my $script = $buffer->get_text( $begin_iter, $end_iter, 0 ); if ( !defined $device ) { - $device = Exd::DeviceToBluetooth->new( - address => '5A:4A:AE:8C:E9:D2', - port => 1 - ); + $self->add_to_log('You have to select a printer.'); + return; } local $| = 1; my $fh = $self->_write_to_script; @@ -146,7 +146,7 @@ sub _daemon_script_runner($self) { my $data = JSON::from_json($line); my ( $script, $device, $verbose ) = $data->@{ 'script', 'device', 'verbose' }; - $device = $self->_device_hash_to_object($device); + $device = $self->device_hash_to_object($device); if ( $last_pid && $last_device->isa('Exd::DeviceToImage') && !$verbose ) @@ -173,7 +173,7 @@ sub _daemon_script_runner($self) { close $write_to_parent; } -sub _device_hash_to_object( $self, $device_hash ) { +sub device_hash_to_object( $self, $device_hash ) { my $type = delete $device_hash->{type}; my %dispatch_table = ( image => sub { @@ -225,13 +225,8 @@ sub _on_run_script( $self, $script, $device, $write_to_parent, $verbose = 1 ) { sub _monitor_run($self) { my @fhs = $self->_select->can_read(0); for my $fh (@fhs) { - my $execute_log = $self->_execute_log; - my $buffer = $execute_log->get_buffer; - my $begin_iter = $buffer->get_iter_at_offset(0); - my $end_iter = $buffer->get_end_iter; - - my $text = ''; $fh->blocking(0); + my $text = ''; while (my $line = <$fh>) { if (!defined $line) { next; @@ -239,10 +234,18 @@ sub _monitor_run($self) { $text .= $line; } return if $text =~ /^\s*$/; - $buffer->insert( $end_iter, $text, -1 ); + $self->add_to_log($text); } } +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 ); +} + sub _build__select($self) { return IO::Select->new; } @@ -333,11 +336,18 @@ sub _activate($self) { ); my $app = $self->_app; my $win = Gtk4::ApplicationWindow->new($app); - $self->_win($win); + $self->window($win); my $box_vertical = Gtk4::Box->new( 'vertical', 10 ); my $execute_log = Gtk4::TextView->new; my $run_button = Gtk4::Button->new_with_label('Run'); my $preview_button = Gtk4::Button->new_with_label('Preview'); + my $select_printer = Gtk4::Button->new_with_label('Select Printer'); + $select_printer->signal_connect( + 'clicked', + sub { + Exd::Gui::PrinterConfigure->new(app => $self)->start; + } + ); $preview_button->signal_connect( 'clicked', sub { @@ -348,7 +358,7 @@ sub _activate($self) { $run_button->signal_connect( clicked => sub { - $self->_run_script( undef, 1 ); + $self->_run_script( $self->device, 1 ); } ); @@ -366,6 +376,7 @@ sub _activate($self) { my $box_buttons = Gtk4::Box->new( 'horizontal', 10 ); $box_buttons->append($run_button); $box_buttons->append($preview_button); + $box_buttons->append($select_printer); $self->_populate_editor_and_preview($box_vertical); $box_vertical->append($box_buttons); my $execute_log_window = Gtk4::ScrolledWindow->new; diff --git a/lib/Exd/Gui/PrinterConfigure.pm b/lib/Exd/Gui/PrinterConfigure.pm new file mode 100644 index 0000000..5a6973a --- /dev/null +++ b/lib/Exd/Gui/PrinterConfigure.pm @@ -0,0 +1,144 @@ +package Exd::Gui::PrinterConfigure; + +use v5.40.0; + +use strict; +use warnings; + +use Moo; +use Glib; +use Glib::IO; +use Glib::Object::Introspection; +use JSON; + +use Net::Bluetooth qw//; + +has app => (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 { [] }); + +my $cache; + +sub _build__select($self) { + return IO::Select->new; +} + +sub _read_bluetooth_printers($self) { + my @fhs = $self->_select->can_read(0); + if (scalar @fhs == 0) { + } + for my $fh (@fhs) { + $fh->blocking(0); + while (my $line = <$fh>) { + my @return = map {$self->app->device_hash_to_object($_)} @{JSON::from_json($line)}; + $self->_bluetooth_printers(\@return); + } + } +} + +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->app->device($device); + $window->close; + }); + $box->append($button); +} + +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->app->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); +} + +sub start($self) { + $self->_start_daemon_bluetooth_search; + my $parent_window = $self->app->window; + 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); + Glib::Timeout->add(1000, sub { + eval { + $self->_read_usb_printers; + $self->_read_bluetooth_printers; + $self->_update_box; + }; + return 1; + }); + $window->present; +} + +sub _update_box($self) { + my $window = $self->_window; + my $box = Gtk4::Box->new( 'vertical', 10 ); + for my $device ($self->_bluetooth_printers->@*) { + $self->_bluetoth_printer_print_to_box($device, $box); + } + for my $device ($self->_usb_printers->@*) { + $self->_usb_printer_print_to_box($device, $box); + } + $window->set_child($box); +} + +sub _start_daemon_bluetooth_search($self) { + my ($read, $write); + pipe $read, $write; + $self->_read_from_daemon($read); + $self->_write_to_app($write); + my $pid = fork; + if (!$pid) { + close $read; + while (1) { + $self->_daemon_bluetooth_search_iteration; + } + exit 0; + } + close $write; + $self->_pid_daemon($pid); +} + +sub _daemon_bluetooth_search_iteration($self) { + say 'Listing bluetooth devices'; + 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}"; + push @return, Exd::DeviceToBluetooth->new(name => $devices->{$address}, address => $address, port => $search->{RFCOMM}); + } + } + @return = map { $_->serialize } sort { $a->name cmp $b->name } @return; + $self->_write_to_app->print(JSON::to_json(\@return)); + $self->_write_to_app->flush; + +} + +sub _on_close($self) { + kill 'TERM', $self->_pid_daemon; + waitpid $self->_pid_daemon, 0; + say 'Finished printer select daemon'; +} +1;