%PDF- %PDF-
Direktori : /usr/share/system-config-printer/ |
Current File : //usr/share/system-config-printer/scp-dbus-service.py |
#!/usr/bin/python3 ## system-config-printer ## Copyright (C) 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. ## Authors: ## Tim Waugh <twaugh@redhat.com> ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import gi import dbus.service from gi.repository import GObject from gi.repository import GLib gi.require_version('Gdk', '3.0') from gi.repository import Gdk gi.require_version('Gtk', '3.0') from gi.repository import Gtk import sys from debug import * import asyncconn import config import cups import cupshelpers import dnssdresolve import jobviewer import killtimer import newprinter import PhysicalDevice import ppdcache import printerproperties cups.require ("1.9.52") CONFIG_BUS='org.fedoraproject.Config.Printing' CONFIG_PATH='/org/fedoraproject/Config/Printing' CONFIG_IFACE='org.fedoraproject.Config.Printing' CONFIG_NEWPRINTERDIALOG_IFACE=CONFIG_IFACE + ".NewPrinterDialog" CONFIG_PRINTERPROPERTIESDIALOG_IFACE=CONFIG_IFACE + ".PrinterPropertiesDialog" CONFIG_JOBVIEWER_IFACE=CONFIG_IFACE + ".JobViewer" g_ppds = None g_killtimer = None #set program name GLib.set_prgname("system-config-printer") class FetchedPPDs(GObject.GObject): __gsignals__ = { 'ready': (GObject.SignalFlags.RUN_LAST, None, ()), 'error': (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_PYOBJECT,)) } def __init__ (self, cupsconn, language): GObject.GObject.__init__ (self) self._cupsconn = cupsconn self._language = language self._ppds = None def is_ready (self): return self._ppds is not None def get_ppds (self): return self._ppds def run (self): debugprint ("FetchPPDs: running") self._ppds = None self._cupsconn.getPPDs2 (reply_handler=self._cups_getppds_reply, error_handler=self._cups_error) def _cups_error (self, conn, exc): debugprint ("FetchPPDs: error: %s" % repr (exc)) self.emit ('error', exc) def _cups_getppds_reply (self, conn, result): debugprint ("FetchPPDs: success") self._ppds = cupshelpers.ppds.PPDs (result, language=self._language) self.emit ('ready') class GetBestDriversRequest: def __init__ (self, device_id, device_make_and_model, device_uri, cupsconn, language, reply_handler, error_handler): self.device_id = device_id self.device_make_and_model = device_make_and_model self.device_uri = device_uri self.cupsconn = cupsconn self.language = language self.reply_handler = reply_handler self.error_handler = error_handler self._signals = [] self.installed_files = [] self.download_tried = False debugprint ("+%s" % self) g_killtimer.add_hold () global g_ppds if g_ppds is None: debugprint ("GetBestDrivers request: need to fetch PPDs") g_ppds = FetchedPPDs (self.cupsconn, self.language) self._signals.append (g_ppds.connect ('ready', self._ppds_ready)) self._signals.append (g_ppds.connect ('error', self._ppds_error)) g_ppds.run () else: if g_ppds.is_ready (): debugprint ("GetBestDrivers request: PPDs already fetched") self._ppds_ready (g_ppds) else: debugprint ("GetBestDrivers request: waiting for PPDs") self._signals.append (g_ppds.connect ('ready', self._ppds_ready)) self._signals.append (g_ppds.connect ('error', self._ppds_error)) def __del__ (self): debugprint ("-%s" % self) def _disconnect_signals (self): for s in self._signals: g_ppds.disconnect (s) def _ppds_error (self, fetchedppds, exc): self._disconnect_signals () self.error_handler (exc) def _ppds_ready (self, fetchedppds): if not fetchedppds.is_ready (): # PPDs being reloaded. Wait for next 'ready' signal. return self._disconnect_signals () ppds = fetchedppds.get_ppds () try: if self.device_id: id_dict = cupshelpers.parseDeviceID (self.device_id) else: id_dict = {} (mfg, mdl) = cupshelpers.ppds.ppdMakeModelSplit (self.device_make_and_model) id_dict["MFG"] = mfg id_dict["MDL"] = mdl id_dict["DES"] = "" id_dict["CMD"] = [] self.device_id = "MFG:%s;MDL:%s;" % (mfg, mdl) fit = ppds.getPPDNamesFromDeviceID (id_dict["MFG"], id_dict["MDL"], id_dict["DES"], id_dict["CMD"], self.device_uri, self.device_make_and_model) ppdnamelist = ppds.orderPPDNamesByPreference (fit.keys (), self.installed_files, devid=id_dict, fit=fit) ppdname = ppdnamelist[0] status = fit[ppdname] try: if status != "exact" and not self.download_tried: self.download_tried = True self.dialog = newprinter.NewPrinterGUI() self.dialog.NewPrinterWindow.set_modal (False) self.handles = \ [self.dialog.connect ('dialog-canceled', self.on_dialog_canceled), self.dialog.connect ('driver-download-checked', self.on_driver_download_checked)] self.reply_if_fail = [(x, fit[x]) for x in ppdnamelist] if not self.dialog.init ('download_driver', devid=self.device_id): try: g_killtimer.remove_hold () finally: e = RuntimeError ("Failed to launch dialog") self.error_handler (e) return except: # Ignore driver download if packages needed for the GUI are not # installed or if no windows can be opened pass g_killtimer.remove_hold () self.reply_handler ([(x, fit[x]) for x in ppdnamelist]) except Exception as e: try: g_killtimer.remove_hold () except: pass self.error_handler (e) def _destroy_dialog (self): for handle in self.handles: self.dialog.disconnect (handle) self.dialog.destroy () del self.dialog def on_driver_download_checked(self, obj, installed_files): if len (installed_files) > 0: debugprint ("GetBestDrivers request: Re-fetch PPDs after driver download") self._signals.append (g_ppds.connect ('ready', self._ppds_ready)) self._signals.append (g_ppds.connect ('error', self._ppds_error)) g_ppds.run () return g_killtimer.remove_hold () self._destroy_dialog () self.reply_handler (self.reply_if_fail) def on_dialog_canceled(self, obj): g_killtimer.remove_hold () self._destroy_dialog () self.reply_handler (self.reply_if_fail) class GroupPhysicalDevicesRequest: def __init__ (self, devices, reply_handler, error_handler): self.devices = devices self.reply_handler = reply_handler self.error_handler = error_handler debugprint ("+%s" % self) try: g_killtimer.add_hold () need_resolving = {} self.deviceobjs = {} for device_uri, device_dict in self.devices.items (): deviceobj = cupshelpers.Device (device_uri, **device_dict) self.deviceobjs[device_uri] = deviceobj if device_uri.startswith ("dnssd://"): need_resolving[device_uri] = deviceobj if len (need_resolving) > 0: resolver = dnssdresolve.DNSSDHostNamesResolver (need_resolving) resolver.resolve (reply_handler=self._group) else: self._group () except Exception as e: g_killtimer.remove_hold () self.error_handler (e) def __del__ (self): debugprint ("-%s" % self) def _group (self, resolved_devices=None): # We can ignore resolved_devices because the actual objects # (in self.devices) have been modified. try: self.physdevs = [] for device_uri, deviceobj in self.deviceobjs.items (): newphysicaldevice = PhysicalDevice.PhysicalDevice (deviceobj) matched = False try: i = self.physdevs.index (newphysicaldevice) self.physdevs[i].add_device (deviceobj) except ValueError: self.physdevs.append (newphysicaldevice) uris_by_phys = [] for physdev in self.physdevs: uris_by_phys.append ([x.uri for x in physdev.get_devices ()]) g_killtimer.remove_hold () self.reply_handler (uris_by_phys) except Exception as e: g_killtimer.remove_hold () self.error_handler (e) class ConfigPrintingNewPrinterDialog(dbus.service.Object): def __init__ (self, bus, path, cupsconn): bus_name = dbus.service.BusName (CONFIG_BUS, bus=bus) dbus.service.Object.__init__ (self, bus_name, path) self.dialog = newprinter.NewPrinterGUI() self.dialog.NewPrinterWindow.set_modal (False) self.handles = [self.dialog.connect ('dialog-canceled', self.on_dialog_canceled), self.dialog.connect ('printer-added', self.on_printer_added), self.dialog.connect ('printer-modified', self.on_printer_modified), self.dialog.connect ('driver-download-checked', self.on_driver_download_checked)] self._ppdcache = ppdcache.PPDCache () self._cupsconn = cupsconn debugprint ("+%s" % self) def __del__ (self): self.dialog.destroy () debugprint ("-%s" % self) @dbus.service.method(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, in_signature='uss', out_signature='') def NewPrinterFromDevice(self, xid, device_uri, device_id): g_killtimer.add_hold () self.dialog.init ('printer_with_uri', device_uri=device_uri, devid=device_id, xid=xid) @dbus.service.method(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, in_signature='us', out_signature='') def DownloadDriverForDeviceID(self, xid, device_id): g_killtimer.add_hold () self.dialog.init ('download_driver', devid=device_id, xid=xid) @dbus.service.method(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, in_signature='uss', out_signature='') def ChangePPD(self, xid, name, device_id): g_killtimer.add_hold () self.xid = xid self.name = name self.device_id = device_id self._ppdcache.fetch_ppd (name, self._change_ppd_got_ppd) def _change_ppd_got_ppd(self, name, ppd, exc): # Got PPD; now find device URI. self.ppd = ppd self._cupsconn.getPrinters (reply_handler=self._change_ppd_with_dev, error_handler=self._do_change_ppd) def _change_ppd_with_dev (self, conn, result): self.device_uri = result.get (self.name, {}).get ('device-uri', None) self._do_change_ppd (conn) def _do_change_ppd(self, conn, exc=None): self.dialog.init ('ppd', device_uri=self.device_uri, name=self.name, ppd=self.ppd, devid=self.device_id, xid=self.xid) @dbus.service.signal(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, signature='') def DialogCanceled(self): pass @dbus.service.signal(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, signature='s') def PrinterAdded(self, name): pass @dbus.service.signal(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, signature='sb') def PrinterModified(self, name, ppd_has_changed): pass @dbus.service.signal(dbus_interface=CONFIG_NEWPRINTERDIALOG_IFACE, signature='a(s)') def DriverDownloadChecked(self, installed_files): pass def on_dialog_canceled(self, obj): debugprint ("%s: dialog canceled" % self) g_killtimer.remove_hold () self.DialogCanceled () self.remove_handles () self.remove_from_connection () def on_printer_added(self, obj, name): debugprint ("%s: printer added" % self) g_killtimer.remove_hold () self.PrinterAdded (name) self.remove_handles () self.remove_from_connection () def on_printer_modified(self, obj, name, ppd_has_changed): debugprint ("%s: printer modified" % self) g_killtimer.remove_hold () self.PrinterModified (name, ppd_has_changed) self.remove_handles () self.remove_from_connection () def on_driver_download_checked(self, obj, installed_files): debugprint ("%s: driver download checked" % self) g_killtimer.remove_hold () self.DriverDownloadChecked (installed_files) self.remove_handles () self.remove_from_connection () def remove_handles (self): for handle in self.handles: self.dialog.disconnect (handle) class ConfigPrintingPrinterPropertiesDialog(dbus.service.Object): def __init__ (self, bus, path, xid, name): bus_name = dbus.service.BusName (CONFIG_BUS, bus=bus) dbus.service.Object.__init__ (self, bus_name=bus_name, object_path=path) self.dialog = printerproperties.PrinterPropertiesDialog () self.dialog.dialog.set_modal (False) handle = self.dialog.connect ('dialog-closed', self.on_dialog_closed) self.closed_handle = handle self.dialog.show (name) self.dialog.dialog.set_modal (False) g_killtimer.add_hold () @dbus.service.method(dbus_interface=CONFIG_PRINTERPROPERTIESDIALOG_IFACE, in_signature='', out_signature='') def PrintTestPage (self): debugprint ("Printing test page") return self.dialog.printTestPage () @dbus.service.signal(dbus_interface=CONFIG_PRINTERPROPERTIESDIALOG_IFACE, signature='') def Finished (self): pass def on_dialog_closed (self, dialog): dialog.destroy () g_killtimer.remove_hold () self.Finished () self.dialog.disconnect (self.closed_handle) self.remove_from_connection () class ConfigPrintingJobApplet(dbus.service.Object): def __init__ (self, bus, path): bus_name = dbus.service.BusName (CONFIG_BUS, bus=bus) dbus.service.Object.__init__ (self, bus_name=bus_name, object_path=path) Gdk.threads_enter () self.jobapplet = jobviewer.JobViewer(bus=dbus.SystemBus (), applet=True, my_jobs=True) self.jobapplet.set_process_pending (False) Gdk.threads_leave () handle = self.jobapplet.connect ('finished', self.on_jobapplet_finished) self.finished_handle = handle self.has_finished = False g_killtimer.add_hold () debugprint ("+%s" % self) def __del__ (self): debugprint ("-%s" % self) @dbus.service.method(dbus_interface=CONFIG_JOBVIEWER_IFACE, in_signature='', out_signature='') def Quit(self): if not self.has_finished: self.jobapplet.cleanup () @dbus.service.signal(dbus_interface=CONFIG_JOBVIEWER_IFACE, signature='') def Finished(self): pass def on_jobapplet_finished (self, jobapplet): self.Finished () g_killtimer.remove_hold () self.has_finished = True self.jobapplet.disconnect (self.finished_handle) self.remove_from_connection () class ConfigPrinting(dbus.service.Object): def __init__ (self): self.bus = dbus.SessionBus () bus_name = dbus.service.BusName (CONFIG_BUS, bus=self.bus) dbus.service.Object.__init__ (self, bus_name, CONFIG_PATH) self._cupsconn = asyncconn.Connection () self._pathn = 0 self._jobapplet = None self._jobappletpath = None self._ppds = None self._language = locale.getlocale (locale.LC_MESSAGES)[0] if not self._language: self._language = locale.getlocale (locale.LC_CTYPE)[0] def destroy (self): self._cupsconn.destroy () @dbus.service.method(dbus_interface=CONFIG_IFACE, in_signature='', out_signature='s') def NewPrinterDialog(self): self._pathn += 1 path = "%s/NewPrinterDialog/%s" % (CONFIG_PATH, self._pathn) ConfigPrintingNewPrinterDialog (self.bus, path, self._cupsconn) g_killtimer.alive () return path @dbus.service.method(dbus_interface=CONFIG_IFACE, in_signature='us', out_signature='s') def PrinterPropertiesDialog(self, xid, name): self._pathn += 1 path = "%s/PrinterPropertiesDialog/%s" % (CONFIG_PATH, self._pathn) ConfigPrintingPrinterPropertiesDialog (self.bus, path, xid, name) g_killtimer.alive () return path @dbus.service.method(dbus_interface=CONFIG_IFACE, in_signature='', out_signature='s') def JobApplet(self): if self._jobapplet is None or self._jobapplet.has_finished: self._pathn += 1 path = "%s/JobApplet/%s" % (CONFIG_PATH, self._pathn) self._jobapplet = ConfigPrintingJobApplet (self.bus, path) self._jobappletpath = path return self._jobappletpath @dbus.service.method(dbus_interface=CONFIG_IFACE, in_signature='sss', out_signature='a(ss)', async_callbacks=('reply_handler', 'error_handler')) def GetBestDrivers(self, device_id, device_make_and_model, device_uri, reply_handler, error_handler): GetBestDriversRequest (device_id, device_make_and_model, device_uri, self._cupsconn, self._language, reply_handler, error_handler) @dbus.service.method(dbus_interface=CONFIG_IFACE, in_signature='s', out_signature='as') def MissingExecutables(self, ppd_filename): ppd = cups.PPD (ppd_filename) return cupshelpers.missingExecutables (ppd) @dbus.service.method(dbus_interface=CONFIG_IFACE, in_signature='a{sa{ss}}', out_signature='aas', async_callbacks=('reply_handler', 'error_handler')) def GroupPhysicalDevices(self, devices, reply_handler, error_handler): GroupPhysicalDevicesRequest (devices, reply_handler, error_handler) def _client_demo (): # Client demo if len (sys.argv) > 2: device_uri = sys.argv[2] device_id = '' if (len (sys.argv) > 4 and sys.argv[3] == '--devid'): device_id = sys.argv[4] else: print ("Device URI required") return from gi.repository import Gtk bus = dbus.SessionBus () obj = bus.get_object (CONFIG_BUS, CONFIG_PATH) iface = dbus.Interface (obj, CONFIG_IFACE) path = iface.NewPrinterDialog () debugprint (path) obj = bus.get_object (CONFIG_BUS, path) iface = dbus.Interface (obj, CONFIG_NEWPRINTERDIALOG_IFACE) loop = GObject.MainLoop () def on_canceled(path=None): print ("%s: Dialog canceled" % path) loop.quit () def on_added(name, path=None): print ("%s: Printer '%s' added" % (path, name)) loop.quit () iface.connect_to_signal ("DialogCanceled", on_canceled, path_keyword="path") iface.connect_to_signal ("PrinterAdded", on_added, path_keyword="path") iface.NewPrinterFromDevice (0, device_uri, device_id) loop.run () if __name__ == '__main__': import ppdippstr import config import gettext gettext.install(domain=config.PACKAGE, localedir=config.localedir) import locale try: locale.setlocale (locale.LC_ALL, "") except: pass ppdippstr.init () Gdk.threads_init () from dbus.glib import DBusGMainLoop DBusGMainLoop (set_as_default=True) client_demo = False if len (sys.argv) > 1: for opt in sys.argv[1:]: if opt == "--debug": set_debugging (True) cupshelpers.set_debugprint_fn (debugprint) elif opt == "--client": client_demo = True if client_demo: _client_demo () sys.exit (0) debugprint ("Service running...") g_killtimer = killtimer.KillTimer (killfunc=Gtk.main_quit) cp = ConfigPrinting () Gdk.threads_enter () Gtk.main () Gdk.threads_leave () cp.destroy ()