source: trunk/debathena/config/lightdm-config/debian/debathena-lightdm-greeter @ 25717

Revision 25717, 28.9 KB checked in by jdreed, 12 years ago (diff)
In lightdm-config: * Populate the update start time correctly
  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2#
3
4from gi.repository import GObject
5from gi.repository import GLib
6from gi.repository import Gio
7from gi.repository import Gtk
8from gi.repository import Gdk
9from gi.repository import GdkPixbuf
10from gi.repository import LightDM
11
12import sys
13import platform
14import subprocess
15import pwd
16import time
17import os.path
18from optparse import OptionParser
19import ConfigParser
20import io
21
22KIOSK_LAUNCH_CMD="/usr/lib/debathena-kiosk/lightdm-launch-kiosk"
23PICKBOARD_CMD="/usr/bin/onboard"
24UI_FILE="/usr/share/debathena-lightdm-config/debathena-lightdm-greeter.ui"
25CONFIG_FILE="/etc/debathena-lightdm-greeter.ini"
26CONFIG_DEFAULTS={"minimum_uid": 1,
27                 "nologin_file": "/var/run/athena-nologin",
28                 "background_image": "/usr/share/debathena-lightdm-config/background.jpg",
29                 "base_logo_file": "/usr/share/debathena-lightdm-config/debathena.png",
30                 "extra_logo_frames": 8,
31                 "motd_filename": "/afs/athena.mit.edu/system/config/motd/login.debathena",
32                 "append_codename_to_motd": "true",
33                 "time_pedantry": "true"}
34
35# See below, and then go cry.
36_OBJS_TO_RENAME=["miShutdown", "miRestart", "miSuspend", "miHibernate"]
37
38class DebathenaGreeter:
39    animation_loop_frames = 300
40   
41   
42    def _debug(self, *args):
43        if self.debugMode:
44            if type(args[0]) is str and len(args) > 1:
45                print >> sys.stderr, "D: " + args[0], args[1:]
46            else:
47                print >> sys.stderr, "D: ", args[0]
48
49    def __init__(self, options, config):
50        self.debugMode = options.debug
51        if self.debugMode:
52            # Sigh.  In theory, APPMENU_DISPLAY_BOTH=1 should give me both
53            # an appmenu and the "real" menubar.
54            # In reality, it doesn't. 
55            # QT_X11_NO_NATIVE_MENUBAR=1 is the KDE equivalent
56            self._debug("Attempting to turn off appmenu...")
57            os.putenv('UBUNTU_MENUPROXY', '')
58
59        # Load the configuration, with type checking
60        try:
61            self.timePedantry = config.getboolean('Greeter', 'time_pedantry')
62        except:
63            self.timePedantry = config.getboolean('DEFAULT', 'time_pedantry')
64
65        try:
66            self.appendCodenameToMotd = config.getboolean('Greeter', 'append_codename_to_motd')
67        except:
68            self.appendCodenameToMotd = config.getboolean('DEFAULT', 'append_codename_to_motd')
69
70        try:
71            self.minimumUID = config.getint('Greeter', 'minimum_uid')
72        except:
73            self.minimumUID = config.getint('DEFAULT', 'minimum_uid')
74
75        try:
76            extraLogoFrames = config.getint('Greeter', 'extra_logo_frames')
77        except:
78            extraLogoFrames = config.getint('DEFAULT', 'extra_logo_frames')
79
80        baseFile = config.get('Greeter', 'base_logo_file')
81        self.logoFiles = [baseFile, ]
82        fileName, fileExt = os.path.splitext(baseFile)
83        if extraLogoFrames > 0:
84            for i in range(1,extraLogoFrames + 1):
85                self.logoFiles.append("%s%d%s" % (fileName, i, fileExt))
86
87        # No need to check these, they get checked later.
88        self.backgroundImageFile = config.get('Greeter', 'background_image')
89        self.motdFilename = config.get('Greeter', 'motd_filename')
90        if self.appendCodenameToMotd:
91            try:
92                codename = subprocess.Popen(["lsb_release", "-s", "-c"], stdout=subprocess.PIPE).communicate()[0]
93                if codename and os.path.exists(self.motdFilename + "." + codename.strip()):
94                    self.motdFilename += "." + codename.strip()
95            except OSError:
96                print >>sys.stderr, "Couldn't append codename to motd_filename.  Oh well..."
97        self.nologinFile = config.get('Greeter', 'nologin_file')
98       
99        # Set up and connect to the greeter
100        self.greeter = LightDM.Greeter()
101        self.greeter.connect("authentication-complete",
102                             self.cbAuthenticationComplete)
103        self.greeter.connect("show-message", self.cbShowMessage)
104        self.greeter.connect("show-prompt", self.cbShowPrompt)
105        self.greeter.connect_sync()
106
107        # Gtk signal handlers
108        handlers = {
109            "login_cb": self.cbLogin,
110            "cancel_cb": self.cancelLogin,
111            "kpEvent": self.cbKeyPress,
112            "power_cb": self.doPowerOperation,
113            "contrast_cb": self.toggleContrast,
114            "pickboard_cb": self.togglePickboard,
115            "large_font_cb": self.toggleLargeFont,
116            "browse_cb": self.spawnBrowser
117        }
118
119        # Sigh.  Pre lightdm-1.1, cancel_authentication() calls the
120        # authentication-complete callback, so when we're in that
121        # callback, we need to know if we cancelled, or if we typed
122        # the password wrong.  The lightdm daemon does in fact know
123        # the difference (return codes of 7 or 10), but gir has no way
124        # to get that info, AFAICT
125        # So beingCancelled is set to True when the user hits Cancel
126        # (or Esc) and set back to False in the authentication-complete
127        # callback or before we try and send anything else to the greeter
128        # This only controls UI, and has no effect on whether LightDM
129        # thinks the authentication process is being cancelled.
130        self.beingCancelled=False
131 
132        # Save the screen size for various window operations
133        defaultScreen = Gdk.Screen.get_default()
134        # Kids these days have too many monitors
135        self.monitorGeometry = defaultScreen.get_monitor_geometry(defaultScreen.get_primary_monitor())
136        # Don't use this for window centering calculations
137        self.screenSize = (self.monitorGeometry.x + self.monitorGeometry.width,
138                           self.monitorGeometry.y + self.monitorGeometry.height)
139        self.get_workstation_information()
140
141        # Load the UI and get objects we care about
142        self.builder = Gtk.Builder()
143        try:
144            self.builder.add_from_file(options.ui_file)
145        except GLib.GError, e:
146            print >> sys.stderr, "FATAL: Unable to load UI: ", e
147            sys.exit(-1)
148
149        # This is just ridiculous.  Due to a GtkBuilder bug, it scribbles over
150        # the widget's name when instantiating them (via get_object()), but we
151        # can then call set_name to re-name them.  In a perfect world, this
152        # would be a no-op.
153        for obj in _OBJS_TO_RENAME:
154            self.builder.get_object(obj).set_name(obj)
155       
156        # For the pickboard
157        self.keyboardWindow = None
158
159        # The login window
160        self.winLogin = self.builder.get_object("winLogin")
161        # A box containing the prompt label, entry, and a spinner
162        self.prompt_box = self.builder.get_object("boxPrompt")
163        self.prompt_label = self.builder.get_object("lblPrompt")
164        self.prompt_entry = self.builder.get_object("entryPrompt")
165        self.loginSpinner = self.builder.get_object("loginSpinner")
166        # A label where we display messages received from the greeter
167        self.message_label = self.builder.get_object("lblMessage")
168        # The owl
169        self.imgDebathena = self.builder.get_object("imgDebathena")
170        # The workstation's hostname
171        lblHostname = self.builder.get_object("lblHostname")
172        lblHostname.set_text(LightDM.get_hostname())
173        # The buttons
174        self.btnCancel = self.builder.get_object("btnCancel")
175        self.btnLogin = self.builder.get_object("btnLogin")
176        # The session combo box
177        self.cmbSession = self.builder.get_object("cmbSession")
178        self.sessionBox = self.builder.get_object("sessionBox")
179        # Sigh.  Needed for Oneiric.  No-op on Precise
180        # See GNOME Bugzilla #650369 and 653579
181        # GtkBuilder calls g_object_new, not gtk_combo_box_text_new()
182        # so the properties don't get set.
183        self.cmbSession.set_entry_text_column(0);
184        self.cmbSession.set_id_column(1);
185        for s in LightDM.get_sessions():
186            self.cmbSession.append(s.get_key(), s.get_name())
187        # Select the first session
188        # TODO: Select the configured default session or the user's session
189        self.cmbSession.set_active(0)
190
191        self.loginNotebook = self.builder.get_object("notebook1")
192
193        # Scaling factor for smaller displays
194        logoScale = 0.75 if self.screenSize[1] <= 768 else 1.0
195        self.animate = self.setup_owl(logoScale)
196       
197        self.winLogin.set_position(Gtk.WindowPosition.CENTER)
198        self.winLogin.show()
199        self.initBackgroundWindow()
200        self.initBrandingWindow()
201        self.initMotdWindow()
202        # Connect Gtk+ signal handlers
203        self.builder.connect_signals(handlers)
204        # GNOME 3 turns off button images by default.  Turn it on
205        # for the "Panel" window
206        self.gtkSettings = Gtk.Settings.get_default()
207        self.gtkSettings.set_property('gtk-button-images', True)
208        self.origTheme = self.gtkSettings.get_property('gtk-theme-name')
209        # Set a cursor for the root window, otherwise there isn't one
210        rw = Gdk.get_default_root_window()
211        rw.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
212        self.noLoginMonitor = Gio.File.new_for_path(self.nologinFile).monitor_file(Gio.FileMonitorFlags.NONE, None)
213        # Check if the file is there right now...
214        if os.path.isfile(self.nologinFile):
215            self.loginNotebook.set_current_page(1)
216            self.builder.get_object("lblUpdTime").set_text("Update started at %s" % (time.strftime("%Y-%m-%d %H:%M")))
217        # and then connect
218        self.noLoginMonitor.connect("changed", self._file_changed)
219
220        if not os.path.exists(KIOSK_LAUNCH_CMD):
221            self.builder.get_object("mnuBrowse").hide()
222        # Setup the login window for first login
223        self.resetLoginWindow()
224
225    def initMotdWindow(self):
226        self.winMotd = self.builder.get_object("winMotd")
227        motdFile=None
228        try:
229            motdFile = open(self.motdFilename, "r")
230        except IOError, e:
231            print >>sys.stderr, "Can't open MOTD file %s: %s" % (self.motdFilename, str(e))
232        motdTxt = ''
233        # Avoid huge files messing up the greeter
234        # At most 10 lines of 80 characters per line
235        # Pango ellipsizing and geometry hints won't accomplish this
236        if motdFile:
237            lines=0
238            while lines <= 10:
239                line = motdFile.readline()
240                if not line:
241                    break
242                lines += 1
243                if len(line) > 80:
244                    line = line[:74] + " [...]\n"
245                motdTxt += line
246            if motdFile.read():
247                motdTxt += "[...]\n"
248            motdFile.close()
249        if motdTxt:
250            self.builder.get_object('lblMotd').set_markup(motdTxt.strip())
251            width, height = self.winMotd.get_size()
252            self.winMotd.set_gravity(Gdk.Gravity.SOUTH)
253            self.winMotd.move(self.monitorGeometry.x + ((self.monitorGeometry.width - width )/ 2), self.screenSize[1] - height - 10)
254            self.winMotd.show_all()
255
256    def initBackgroundWindow(self):
257        # The background image
258        self.winBg = self.builder.get_object("winBg")
259        self.imgBg = self.builder.get_object("imgBg")
260        try:
261            bg_pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.backgroundImageFile)
262            bg_scaled = bg_pixbuf.scale_simple(self.screenSize[0], self.screenSize[1], GdkPixbuf.InterpType.BILINEAR)
263        except GLib.GError, e:
264            print >> sys.stderr, "Glib Error while loading background image:", e
265            # Just a plain black background
266            bg_scaled = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, self.screenSize[0], self.screenSize[1])
267        self.imgBg.set_from_pixbuf(bg_scaled)
268           
269        # The menubar
270        # LightDM checks with PolKit for the various "get_can_foo()" functions
271        # We call .set_name() here because of a GtkBuilder bug
272        self.builder.get_object("miShutdown").set_sensitive(LightDM.get_can_shutdown())
273        self.builder.get_object("miRestart").set_sensitive(LightDM.get_can_restart())
274#        self.builder.get_object("miRestart").set_name('restart')
275        # We don't allow suspend/hibernate on cluster
276        self.builder.get_object("miHibernate").set_sensitive(LightDM.get_can_hibernate() and self.metapackage != "debathena-cluster")
277        self.builder.get_object("miSuspend").set_sensitive(LightDM.get_can_suspend() and self.metapackage != "debathena-cluster")
278
279        # We just want images.  A Glade bug removes the "label" property
280        # completely if it's null, which results in bad redrawing artifacts
281        # So we set the label to text we don't care about in Glade, and
282        # change it here.
283        for menu in ("mnuPower", "mnuAccess", "mnuBrowse"):
284            self.builder.get_object(menu).set_property('label', '')
285        # Used in the updateTime callback
286        self.mnuTime = self.builder.get_object("mnuClock")
287       
288        self.builder.get_object("miPickboard").set_sensitive(os.path.exists(PICKBOARD_CMD))
289
290        self.winBg.show_all()
291
292    def initBrandingWindow(self):
293        # The "branding window", in the bottom right
294        winBranding = self.builder.get_object("winBranding")
295        lblBranding = self.builder.get_object("lblBranding")
296        arch = platform.machine()
297        if arch != "x86_64":
298            arch = "<b>" + arch + "</b>"
299        # Possibly no longer needed, workaround for a Glade bug in Gtk+ 2
300        lblBranding.set_property('can_focus', False)
301        winBranding.set_property('can_focus', False)
302        lblBranding.set_markup(self.metapackage + "\n" + self.baseos + "\n" + arch)
303        winBranding.set_gravity(Gdk.Gravity.SOUTH_EAST)
304        width, height = winBranding.get_size()
305        winBranding.move(self.screenSize[0] - width, self.screenSize[1] - height)
306        winBranding.show_all()
307
308    def doPowerOperation(self, widget):
309        # This only works because of the calls to .set_name() above.
310        # Other stupid ideas include:
311        #   if widget == self.builder.get_object('whatever'):
312        #
313        # N.B. user_data for GtkBuilder is a complete mess and all
314        # kinds of wrong (you can only specify objects defined in the
315        # builder xml, and it forces the 'swap' flag when
316        # autoconnected, AND the object you get _replaces_ the widget
317        # in the callback)
318        actions = {"miShutdown": LightDM.shutdown,
319                   "miRestart": LightDM.restart,
320                   "miHibernate": LightDM.hibernate,
321                   "miSuspend": LightDM.suspend}
322        try:
323            actions[widget.get_name()]()
324        except KeyError:
325            # "won't" happen
326            print >>sys.stderr, "ERR: No action for widget name: "+ widget.get_name()
327        except GLib.GError, e:
328            # It's possible we should look at the error text to see if
329            # it's ConsoleKit that's whining?  Because you get a
330            # (valid) error from UPower if you try to suspend when
331            # your hardware doesn't support it
332            self.errDialog("An error occurred while trying to perform a power-related operation.  The most common cause of this is trying to shutdown, reboot, or suspend the machine when someone else is logged in remotely or on one of the virtual terminals.  The full error text appears below:\n\n" + str(e))
333
334
335    def togglePickboard(self, widget):
336        if not widget.get_active():
337            if self.keyboardWindow:
338                self.keyboardWindow.destroy()
339            if self.onboardProc:
340                self.onboardProc.terminate()
341        else:
342            self.onboardProc = None
343            xid = None
344            try:
345                self.onboardProc = subprocess.Popen([PICKBOARD_CMD, "--xid"],
346                                                stdout=subprocess.PIPE)
347                xid = int(self.onboardProc.stdout.readline())
348            except OSError, e:
349                print >>sys.stderr, "Failed to spawn /usr/bin/onboard", str(e)
350            except ValueError:
351                print >>sys.stderr, "onboard didn't return an integer xid (shouldn't happen)"
352                self.onboardProc.kill()
353
354            if self.onboardProc is None or xid is None:
355                self.errDialog("An error occurred while starting the on-screen keyboard.")
356                widget.set_sensitive(False)
357                # Remember, this will call this callback again
358                widget.set_active(False)
359                return
360
361            self.keyboardWindow = Gtk.Window()
362            self.keyboardWindow.show()
363            self.keyboardWindow.accept_focus = False;
364            self.keyboardWindow.focus_on_map = False;
365            keyboardSocket = Gtk.Socket()
366            keyboardSocket.show()
367            self.keyboardWindow.add(keyboardSocket)
368            keyboardSocket.add_id(xid)
369            self.keyboardWindow.move(0, self.screenSize[1] - 200)
370            self.keyboardWindow.resize(int(self.screenSize[0] / 3), 200)
371
372    def toggleLargeFont(self, widget):
373        pass
374
375    def toggleContrast(self, widget):
376        if widget.get_active():
377            self.gtkSettings.set_property('gtk-theme-name', 'HighContrastInverse')
378        else:
379            self.gtkSettings.set_property('gtk-theme-name', self.origTheme)
380
381
382    def _file_changed(self, monitor, file1, file2, evt_type):
383        if evt_type == Gio.FileMonitorEvent.CREATED:
384            self.loginNotebook.set_current_page(1)
385            self.builder.get_object("lblUpdTime").set_text("Update started at %s" % (time.strftime("%Y-%m-%d %H:%M")))
386        if evt_type == Gio.FileMonitorEvent.DELETED:
387            self.loginNotebook.set_current_page(0)
388
389    # Update the time in the "panel"
390    def updateTime(self):
391        timeFmt="%a, %b %e %Y %l:%M" + ":%S" if self.timePedantry else ""
392        # every second counts
393        timeFmt=timeFmt + " %p"
394        self.mnuTime.set_label(time.strftime(timeFmt, time.localtime(time.time())))
395        return True
396
397    # Reset the UI and prepare for a new login
398    def resetLoginWindow(self):
399        self.spin(False)
400        self.clearMessage()
401        self.btnCancel.hide()
402        self.sessionBox.hide()
403        self.prompted=False
404        self.prompt_label.set_text("")
405        self.prompt_entry.set_text("")
406        self.prompt_box.hide()
407        self.btnLogin.grab_focus()
408        # Because there's no WM, we need to focus the actual X window
409        Gdk.Window.focus(self.winLogin.get_window(), Gdk.CURRENT_TIME)
410
411    def getSelectedSession(self):
412        i = self.cmbSession.get_active_iter()
413        session_name = self.cmbSession.get_model().get_value(i, 1)
414        self._debug("selected session is " + session_name)
415        return session_name
416
417    def startOver(self):
418        self.greeter.cancel_authentication()
419        self.greeter.authenticate(None)
420
421    # LightDM Callbacks
422    # The workflow is this:
423    # - call .authenticate() with a username
424    # - lightdm responds with a prompt for password
425    # - call .respond with whatever the user provides
426    # - lightdm responds with authentication-complete
427    #   N.B. complete != successful
428    # - .cancel_authentication will cancel the authentication in progress
429    #   call .authenticate with a new username to restart it
430    #
431    # Calling .authenticate with None (or NULL in C) will cause lightdm
432    # to first prompt for a username, then a password.  This means two
433    # show-prompt callbacks and thus two .respond calls
434   
435    # This callback is called when the authentication process is
436    # complete.  "complete" means a username and password have been
437    # received, and PAM has done its thing.  "complete" does not
438    # mean "successful".
439    def cbAuthenticationComplete(self, greeter):
440        self.spin(False)
441        self._debug("cbAuthenticationComplete: received authentication-complete message")
442        if greeter.get_is_authenticated():
443            self.spin(True)
444            self._debug("Authentication was successful.")
445            session_name = self.getSelectedSession()
446            #FIXME: Make sure it's a valid session
447            self._debug("User has selected '%s' session" % (session_name))
448            if not greeter.start_session_sync(session_name):
449                self._debug("Failed to start session")
450                print >> sys.stderr, "Failed to start session"
451        elif not self.beingCancelled:
452            self._debug("Authentication failed.")
453            self.displayMessage("Authentication failed, please try again")
454            self.greeter.authenticate(None)
455        else:
456            self.beingCancelled=False
457            self.resetLoginWindow()
458
459    # The show-prompt message is emitted when LightDM wants you to
460    # show a prompt to the user, and respond with the user's response.
461    # Currently, the prompts we care about are "login:" and
462    # "Password: " (yes, with the trailing space), which ask for the
463    # username and password respectively.  promptType is one of
464    # LightDM.PromptType.SECRET or LightDM.PromptType.QUESTION, which
465    # mean that the text of the user's response should or should not be
466    # masked/invisible, respectively.
467
468    def cbShowPrompt(self, greeter, text, promptType):
469        self._debug("cbShowPrompt: Received show-prompt message: ",
470                   text, promptType)
471        self.prompted=True
472        # Make things pretty
473        if text == "login:":
474            text = "Username: "
475        # Sanity check the username
476        currUser = self.greeter.get_authentication_user()
477        if currUser:
478            self._debug("Current user being authenticated is " + currUser)
479            # See if the user exists
480            try:
481                passwd=pwd.getpwnam(currUser)
482            except KeyError:
483                # Why are we not using the message label here?
484                # Because what will happen is that someone will quickly
485                # typo their username, and then type their password without
486                # looking at the screen, which would otherwise result in the
487                # window resetting after the first error, and then they end
488                # up typing their password into the username box.
489                self.errDialog("The username '%s' is invalid.\n\n(Tip: Please ensure you're typing your username in lowercase letters.\nDo not add '@mit.edu' or any other suffix to your username.)" % (currUser))
490                self.startOver()
491                return True
492            # There's probably a better way
493            if passwd.pw_uid < self.minimumUID:
494                self.errDialog("Logging in as '%s' disallowed by configuation" % (currUser))
495                self.startOver()
496                return True
497
498        # Set the label to the value of the prompt
499        self.prompt_label.set_text(text)
500        # clear the entry and get focus
501        self.prompt_entry.set_text("")
502        self.prompt_entry.set_sensitive(True)
503        self.prompt_box.show()
504        self.prompt_entry.grab_focus()
505        # Mask the input if requested
506        if promptType == LightDM.PromptType.SECRET:
507            self.prompt_entry.set_visibility(False)
508        else:
509            self.prompt_entry.set_visibility(True)
510        self.spin(False)
511
512    # show-message is emitted when LightDM has something to say to the user
513    # Typically, these are emitted by PAM modules (e.g. pam_echo)
514    # Note that this is _not_ "authentication failed" (unless a PAM module
515    # specifically says that). 
516    #
517    # The docs which say to check .is_authenticated() in the
518    # authentication-complete callback to determine login success or
519    # failure.
520    #
521    # messageType is one of LightDM.MessageType.{ERROR,INFO}
522    def cbShowMessage(self, greeter, text, messageType):
523        self._debug("cbShowMessage: Received show-messsage message",
524                   text, messageType)
525        # TODO: Wrap text
526        self.displayMessage(text)
527        self.spin(False)
528
529    def cbKeyPress(self, widget, event):
530        if event.keyval == Gdk.KEY_Escape:
531            self.cancelLogin(widget)
532
533    def cancelLogin(self, widget=None):
534        self._debug("Cancelling authentication.  User=",
535                   self.greeter.get_authentication_user())
536        self.beingCancelled=True
537        self.greeter.cancel_authentication()
538        self.resetLoginWindow()
539
540    def displayMessage(self, msg):
541        self.message_label.set_text(msg)
542        self.message_label.show()
543
544    def clearMessage(self):
545        self.message_label.set_text("")
546        self.message_label.hide()
547
548    def spawnBrowser(self, event):
549        subprocess.call(KIOSK_LAUNCH_CMD)
550
551    def errDialog(self, errText):
552        dlg = Gtk.MessageDialog(self.winLogin,
553                                Gtk.DialogFlags.DESTROY_WITH_PARENT,
554                                Gtk.MessageType.ERROR,
555                                Gtk.ButtonsType.CLOSE,
556                                errText)
557        dlg.run()
558        dlg.destroy()
559
560
561    def spin(self, start):
562        if start:
563            self.loginSpinner.show()
564            self.loginSpinner.start()
565        else:
566            self.loginSpinner.stop()
567            self.loginSpinner.hide()
568
569    # Some greeter implementations check .get_is_authenticated() here
570    # and then start the session.  I think that's only relevant
571    # dealing with a user-picker and passwordless users (that is, you
572    # would call .authenticate(joeuser), and then click the button,
573    # and you'd just be logged in.  But we disable the user picker, so
574    # that's not relevant.
575    def cbLogin(self, widget):
576        # Because we just entered some text and are about to send it,
577        # we're no longer in the middle of a cancellation
578        self.beingCancelled=False
579        self.clearMessage()
580        self._debug("In cbLogin")
581        if self.prompted:
582            response = self.prompt_entry.get_text()
583            self._debug("Sending response to prompt", response if self.prompt_entry.get_visibility() else "[redacted]")
584            self.spin(True)
585            self.greeter.respond(response)
586            self.prompted=False
587        else:
588            self._debug("No prompt.  Beginning new authentication process.")
589            # Show the "Cancel" button"
590            self.sessionBox.show()
591            self.btnCancel.show()
592            self.greeter.authenticate(None)
593 
594    # Load the Debathena owl image and generate self.logo_pixbufs, the list of
595    # animation frames.  Returns True if successful, False otherwise.
596    def setup_owl(self,logoScale):
597        self.logo_pixbufs = []
598        num_pixbufs = 0
599        # Eyes go closed.
600       
601        for img in self.logoFiles:
602            try:
603                pixbuf = GdkPixbuf.Pixbuf.new_from_file(img)
604                self.logo_pixbufs.append(pixbuf.scale_simple(int(pixbuf.get_width() * logoScale), int(pixbuf.get_height() * logoScale), GdkPixbuf.InterpType.BILINEAR))
605                num_pixbufs += 1
606            except GLib.GError, e:
607                print >> sys.stderr, "Glib Error:", e
608                return False
609        # Eyes come open.
610        for pixbuf in self.logo_pixbufs[::-1]:
611            self.logo_pixbufs.append(pixbuf)
612            num_pixbufs += 1
613        # Eyes stay open.
614        self.logo_pixbufs.extend([None] * (self.animation_loop_frames - num_pixbufs))
615        self.img_idx = -1
616        # Set it to the first image so that the window can size itself
617        # accordingly
618        self.imgDebathena.set_from_pixbuf(self.logo_pixbufs[0])
619        self._debug("Owl setup done")
620        return True
621   
622    def update_owl(self):
623        if not self.animate:
624            self._debug("Owl loading failed, ending update_owl timer")
625            return False
626        self.img_idx = (self.img_idx + 1) % self.animation_loop_frames
627        pixbuf = self.logo_pixbufs[self.img_idx]
628        if pixbuf is not None:
629            self.imgDebathena.set_from_pixbuf(pixbuf)
630        return True
631
632
633    def get_workstation_information(self):
634        try:
635            self.metapackage = subprocess.Popen(["machtype", "-L"], stdout=subprocess.PIPE).communicate()[0].rstrip()
636        except OSError:
637            self.metapackage = '(unknown metapackage)'
638        try:
639            self.baseos = subprocess.Popen(["machtype", "-E"], stdout=subprocess.PIPE).communicate()[0].rstrip()
640        except OSError:
641            self.baseos = '(unknown OS)'
642
643
644
645
646if __name__ == '__main__':
647    parser = OptionParser()
648    parser.set_defaults(debug=False)
649    parser.add_option("--debug", action="store_true", dest="debug")
650    parser.add_option("--ui", action="store", type="string",
651                      default=UI_FILE, dest="ui_file")
652    parser.add_option("--cfg", action="store", type="string",
653                      default=CONFIG_FILE, dest="config_file")
654    (options, args) = parser.parse_args()
655    config = ConfigParser.RawConfigParser(CONFIG_DEFAULTS)
656    # Hack to create a 'Greeter' section so that we can just use that
657    # in any calls
658    config.readfp(io.BytesIO("[Greeter]\n"))
659    config.read(options.config_file)
660    print >>sys.stderr, config.get('Greeter', 'time_PEDANTRY')
661    Gtk.init(None);
662    main_loop = GObject.MainLoop ()
663    dagreeter = DebathenaGreeter(options, config)
664    # Add a timeout for the owl animation
665    GObject.timeout_add(50, dagreeter.update_owl)
666    # Add a timeout for the clock in the panel
667    GObject.timeout_add(30, dagreeter.updateTime)
668
669    main_loop.run ()
Note: See TracBrowser for help on using the repository browser.