From b0e4b8eb976d3fc63d695ee8a24f7a0a0ac2cf43 Mon Sep 17 00:00:00 2001 From: TheJayas Date: Wed, 12 Feb 2025 19:07:57 +0530 Subject: [PATCH] Services Discovery Functionality --- Makefile.am | 3 ++ ipp_pd_async.py | 83 ++++++++++++++++++++++++++++++++++ system-config-printer.py | 77 +++++++++++++++++++++++++++++++ ui/AvahiWarningDialog.ui | 53 ++++++++++++++++++++++ ui/DiscoveredPrintersDialog.ui | 55 ++++++++++++++++++++++ ui/PrinterItem.ui | 57 +++++++++++++++++++++++ ui/PrintersWindow.ui | 14 ++++++ 7 files changed, 342 insertions(+) create mode 100644 ipp_pd_async.py create mode 100644 ui/AvahiWarningDialog.ui create mode 100644 ui/DiscoveredPrintersDialog.ui create mode 100644 ui/PrinterItem.ui diff --git a/Makefile.am b/Makefile.am index 9993ca0db..f31f56a0a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -136,6 +136,9 @@ nobase_pkgdata_DATA= \ ui/AboutDialog.ui \ ui/ConnectDialog.ui \ ui/ConnectingDialog.ui \ + ui/DiscoveredPrintersDialog.ui \ + ui/PrinterItem.ui \ + ui/AvahiWarningDialog.ui \ ui/InstallDialog.ui \ ui/JobsWindow.ui \ ui/NewPrinterName.ui \ diff --git a/ipp_pd_async.py b/ipp_pd_async.py new file mode 100644 index 000000000..8328b42ff --- /dev/null +++ b/ipp_pd_async.py @@ -0,0 +1,83 @@ +import dbus +import dbus.mainloop.glib +import gi +from gi.repository import GLib +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +import avahi + +SERVICE_TYPES = [ + "_http._tcp", "_https._tcp" + # "_ipp._tcp", "_ipps-system._tcp", + # "_nvstream._tcp", "_nvstream_dbd._tcp", "_airplay._tcp", "_raop._tcp" +] + +class AvahiServiceBrowser: + def __init__(self): + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.discovered_services = [] # List to store unique discovered services + + def get_all_discovered_services(self): + return self.discovered_services + + def on_service_found(self, interface, protocol, name, stype, domain, adminurl=None): + # Check for duplicates before adding + if not any(service['name'] == name and service['link'] == adminurl for service in self.discovered_services): + print(f"Service found: {name}, type: {stype}, domain: {domain}, admin URL: {adminurl}") + service_info = { + "name": name, + "link": adminurl + } + self.discovered_services.append(service_info) + + def resolve_service(self, interface, protocol, name, stype, domain, flags): + server = self.bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER) + + try: + # Resolve the service to obtain its host and port + resolved = server.ResolveService( + interface, protocol, name, stype, domain, + avahi.PROTO_UNSPEC, dbus.UInt32(0), + dbus_interface=avahi.DBUS_INTERFACE_SERVER + ) + + # Construct the admin URL + host = resolved[5] # The resolved hostname + port = resolved[8] # The resolved port + adminurl = f"http://{host}:{port}" + + # Pass all information to `on_service_found` + self.on_service_found(interface, protocol, name, stype, domain, adminurl) + + except dbus.DBusException as e: + print(f"Failed to resolve service {name}: {e}") + + def get_services(self, service_type): + try: + server = dbus.Interface( + self.bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), + avahi.DBUS_INTERFACE_SERVER + ) + sbrowser = dbus.Interface( + self.bus.get_object(avahi.DBUS_NAME, server.ServiceBrowserNew( + avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, service_type, 'local', dbus.UInt32(0))), + avahi.DBUS_INTERFACE_SERVICE_BROWSER + ) + # Connect ItemNew to resolve service when a new item is found + sbrowser.connect_to_signal("ItemNew", self.resolve_service) + print(f"Started service browser for: {service_type}") + + except dbus.DBusException as e: + print(f"DBusException: {e}") + + def run(self): + for service_type in SERVICE_TYPES: + self.get_services(service_type) + loop = GLib.MainLoop() + loop.run() + +if __name__ == "__main__": + browser = AvahiServiceBrowser() + browser.run() + diff --git a/system-config-printer.py b/system-config-printer.py index c9689de07..570bfb923 100755 --- a/system-config-printer.py +++ b/system-config-printer.py @@ -28,12 +28,23 @@ import _thread import dbus import gi +import webbrowser +import asyncio +from ipp_pd_async import AvahiServiceBrowser + try: gi.require_version('Polkit', '1.0') from gi.repository import Polkit except: Polkit = False +try: + import avahi + AVAHI_AVAILABLE = True +except ImportError: + print("Warning: Avahi module not found. Service discovery will be disabled.") + AVAHI_AVAILABLE = False + gi.require_version('GdkPixbuf', '2.0') from gi.repository import GdkPixbuf try: @@ -215,12 +226,25 @@ def __init__(self): "dests_iconview", "btnAddFirstPrinter", "btnStartService", + "btnDiscoverPrinters", + "btnConnectNoService", "btnConnectNoService", "statusbarMain", "toolbar", "server_menubar_item", "printer_menubar_item", "view_discovered_printers"], + "DiscoveredPrintersDialog": [ + "DiscoveredPrintersDialog", + "printers-flowbox", + "buttonOk" + ], + "PrinterItem": [ + "printer-item", + "printer-icon", + "printer-name", + "open-link-button" + ], "AboutDialog": ["AboutDialog"], "ConnectDialog": @@ -515,6 +539,7 @@ def __init__(self): self.dests_iconview.connect ("drag-data-get", self.dests_iconview_drag_data_get) self.btnStartService.connect ('clicked', self.on_start_service_clicked) + self.btnDiscoverPrinters.connect ('clicked', self.on_discover_printers_button_click) self.btnConnectNoService.connect ('clicked', self.on_connect_activate) self.btnAddFirstPrinter.connect ('clicked', self.on_new_printer_activate) @@ -545,6 +570,11 @@ def __init__(self): self.PrintersWindow.show() + self.builder = Gtk.Builder() + self.service_browser = AvahiServiceBrowser() + self.service_browser.run() + self.discovered_services = self.service_browser.get_all_discovered_services() # To store discovered services + def display_properties_dialog_for (self, queue): model = self.dests_iconview.get_model () @@ -614,6 +644,53 @@ def dests_iconview_item_activated (self, iconview, path): self.monitor.update () return + def on_discover_printers_button_click(self, widget): + if not AVAHI_AVAILABLE: + self.builder.add_from_file("ui/AvahiWarningDialog.ui") + warning_dialog = self.builder.get_object("AvahiWarningDialog") + warning_dialog.set_transient_for(self.PrintersWindow) + warning_dialog.show_all() + return # Exit function early + + # Clear previous printers in flowbox + self.builder.add_from_file("ui/DiscoveredPrintersDialog.ui") + self.discovered_printers_dialog = self.builder.get_object("DiscoveredPrintersDialog") + + self.discovered_services = self.service_browser.get_all_discovered_services() + printers = [ + {"name": "Printer 1", "link": "https://example.com/printer1"}, + {"name": "Printer 2", "link": "https://example.com/printer2"}, + {"name": "Printer 3", "link": "https://example.com/printer3"}, + ] + printers = self.discovered_services + printers_flowbox = self.builder.get_object("printers-flowbox") + for printer in printers: + item_builder = Gtk.Builder() + item_builder.add_from_file("ui/PrinterItem.ui") + + # Configure each printer item + printer_item = item_builder.get_object("printer-item") + printer_name_label = item_builder.get_object("printer-name") + open_link_button = item_builder.get_object("open-link-button") + + printer_name_label.set_text(printer["name"]) + open_link_button.connect("clicked", self.on_open_link_clicked, printer["link"]) + + # Add the item to the flowbox + printers_flowbox.add(printer_item) + printer_item.show_all() + + # Display the dialog + self.discovered_printers_dialog.set_transient_for(self.PrintersWindow) + self.discovered_printers_dialog.show_all() + + def on_open_link_clicked(self, widget, link): + # Open the provided link in the web browser + webbrowser.open(link) + + def on_discovered_printers_dialog_close(self, widget): + self.discovered_printers_dialog.hide() + def on_properties_dialog_closed (self, obj): self.sensitise_main_window_widgets () diff --git a/ui/AvahiWarningDialog.ui b/ui/AvahiWarningDialog.ui new file mode 100644 index 000000000..530c1736b --- /dev/null +++ b/ui/AvahiWarningDialog.ui @@ -0,0 +1,53 @@ + + + + + Warning + True + center-on-parent + 350 + 150 + + + + True + vertical + 6 + + + + dialog-warning + True + 48 + + + + + + + Avahi module is not installed. + Service discovery will not work. + + True + True + + + + + + + + True + end + + + OK + True + True + + + + + + + diff --git a/ui/DiscoveredPrintersDialog.ui b/ui/DiscoveredPrintersDialog.ui new file mode 100644 index 000000000..4e256aad8 --- /dev/null +++ b/ui/DiscoveredPrintersDialog.ui @@ -0,0 +1,55 @@ + + + + + + Discovered Printers + True + center-on-parent + 400 + 300 + + + + + True + vertical + 6 + + + + + True + True + True + + + + True + 1 + none + True + True + + + + + + + + + + + True + end + + + _OK + True + + + + + + + diff --git a/ui/PrinterItem.ui b/ui/PrinterItem.ui new file mode 100644 index 000000000..1fb358968 --- /dev/null +++ b/ui/PrinterItem.ui @@ -0,0 +1,57 @@ + + + + + + vertical + 6 + True + True + + + + + horizontal + 10 + True + + + + + printer-symbolic + True + 48 + + + + + + + Printer Name + True + True + + + + + + + Open Link + True + + + + + + + + + + horizontal + True + 5 + 5 + + + + diff --git a/ui/PrintersWindow.ui b/ui/PrintersWindow.ui index 22f337fd3..3a7fd9b33 100644 --- a/ui/PrintersWindow.ui +++ b/ui/PrintersWindow.ui @@ -280,6 +280,20 @@ 0 + + + _Discover Printers + True + True + True + + + + True + True + 1 + + Connect