diff --git a/Makefile.am b/Makefile.am
index 9993ca0db..b2d56649f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -113,6 +113,7 @@ nobase_pkgdata_DATA= \
gui.py \
gtkinklevel.py \
installpackage.py \
+ ipp_pd_async.py \
jobviewer.py \
killtimer.py \
monitor.py \
@@ -135,6 +136,8 @@ nobase_pkgdata_DATA= \
userdefault.py \
ui/AboutDialog.ui \
ui/ConnectDialog.ui \
+ ui/DiscoveredPrintersDialog.ui \
+ ui/PrinterItem.ui \
ui/ConnectingDialog.ui \
ui/InstallDialog.ui \
ui/JobsWindow.ui \
diff --git a/ipp_pd_async.py b/ipp_pd_async.py
new file mode 100644
index 000000000..37d7067ec
--- /dev/null
+++ b/ipp_pd_async.py
@@ -0,0 +1,82 @@
+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..e1c7ea5f1 100755
--- a/system-config-printer.py
+++ b/system-config-printer.py
@@ -28,6 +28,9 @@
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
@@ -216,11 +219,21 @@ def __init__(self):
"btnAddFirstPrinter",
"btnStartService",
"btnConnectNoService",
+ "btnDiscoverPrinters",
"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":
@@ -516,6 +529,7 @@ def __init__(self):
self.dests_iconview_drag_data_get)
self.btnStartService.connect ('clicked', self.on_start_service_clicked)
self.btnConnectNoService.connect ('clicked', self.on_connect_activate)
+ self.btnDiscoverPrinters.connect ('clicked', self.on_discover_printers_button_click)
self.btnAddFirstPrinter.connect ('clicked',
self.on_new_printer_activate)
@@ -543,9 +557,13 @@ def __init__(self):
elif len (self.printers) > 1:
self.PrintersWindow.set_default_size (500, 180)
-
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 ()
iter = model.get_iter_first ()
@@ -614,6 +632,39 @@ def dests_iconview_item_activated (self, iconview, path):
self.monitor.update ()
return
+ def on_discover_printers_button_click(self, widget):
+ 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 = 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 ()
@@ -2278,4 +2329,4 @@ def main(show_jobs):
if opt == "--embedded":
PlugWindowId = int(optarg)
- main(show_jobs)
+ main(show_jobs)
\ No newline at end of file
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 @@
+
+
+
+
+
+
diff --git a/ui/PrinterItem.ui b/ui/PrinterItem.ui
new file mode 100644
index 000000000..0276511be
--- /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
+
+
+
+
+
+
+ Website
+ True
+
+
+
+
+
+
+
+
+
+ horizontal
+ True
+ 5
+ 5
+
+
+
+
diff --git a/ui/PrintersWindow.ui b/ui/PrintersWindow.ui
index 22f337fd3..b5f6f9ca7 100644
--- a/ui/PrintersWindow.ui
+++ b/ui/PrintersWindow.ui
@@ -280,6 +280,21 @@
0
+
+
+ _Discover Printers
+ True
+ True
+ True
+
+
+
+
+ True
+ True
+ 1
+
+
Connect