# Copyright (c) 2012, Matthias Schiffer # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import sys import os import locale from gi.repository import Gtk, Gdk, GObject, GLib _ = locale.gettext PACKAGE_DIR = os.path.dirname(__file__) class LockWindow(Gtk.Window): __gsignals__ = { 'logout': (GObject.SIGNAL_RUN_FIRST, None, ()), 'tryUnlock': (GObject.SIGNAL_RUN_FIRST, None, (str,)) } def __init__(self): Gtk.Window.__init__(self) self.display = Gdk.Display.get_default() self.screen = self.display.get_default_screen() self.devicesGrabbed = False self.overlay = Gtk.Overlay() self.add(self.overlay) self.bg = Gtk.Image.new_from_file(os.path.join(PACKAGE_DIR, 'data/bg.svg')) self.overlay.add(self.bg) self.realize() self.get_window().set_override_redirect(True) self.set_default_size(self.screen.get_width(), self.screen.get_height()) self.set_has_resize_grip(False) builder = Gtk.Builder() if not builder.add_from_file(os.path.join(PACKAGE_DIR, 'data/unlock.ui')): sys.exit(1) self.unlockWindow = builder.get_object('unlock_window') self.lockLabel = builder.get_object('lock_label') self.promptEntry = builder.get_object('prompt_entry') self.messageLabel = builder.get_object('message_label') self.logoutButton = builder.get_object('logout_button') self.unlockButton = builder.get_object('unlock_button') self.overlay.add_overlay(self.unlockWindow) self.connect('delete-event', lambda w, e: True) self.promptEntry.connect('activate', lambda w: self._tryUnlock()) self.logoutButton.connect('clicked', lambda w: self._logout()) self.unlockButton.connect('clicked', lambda w: self._tryUnlock()) self.reset(False, True) self.deviceManager = self.display.get_device_manager() self.connect('map-event', self._grabDevices) self.connect('configure-event', lambda w, e: self.get_window().move_resize(0, 0, self.screen.get_width(), self.screen.get_height()) and False) def _logout(self): self.promptEntry.set_sensitive(False) self.unlockButton.set_sensitive(False) self.logoutButton.set_sensitive(False) GLib.idle_add(lambda: self.emit('logout')) def _tryUnlock(self): self.promptEntry.set_sensitive(False) self.unlockButton.set_sensitive(False) self.logoutButton.set_sensitive(False) GLib.idle_add(lambda: self.emit('tryUnlock', self.promptEntry.get_text())) def _update(self): self.promptEntry.set_sensitive(self._enablePromptEntry) self.logoutButton.set_sensitive(self._enableLogoutButton) self.unlockButton.set_sensitive(self._enableUnlockButton) def reset(self, regrab = True, resetButtons = False): if resetButtons: self._enablePromptEntry = True self._enableLogoutButton = False self._enableUnlockButton = True self.promptEntry.set_text('') self.messageLabel.set_label('') self._update() if regrab: self._grabDevices(None, None) def updateLockMessage(self, username, logoutTime = None): if logoutTime is None: self.lockLabel.set_markup(_('This computer is currently locked by the user {username}.').format(username=GLib.markup_escape_text(username))) self._enableLogoutButton = False elif logoutTime > 0: self.lockLabel.set_markup( _('This computer is currently locked by the user {username}.\nThe user can be logged out in {minutes:02}:{seconds:02} minutes.').format( username=GLib.markup_escape_text(username), minutes=logoutTime//60, seconds=logoutTime%60)) self._enableLogoutButton = False else: self.lockLabel.set_markup( _('This computer is currently locked by the user {username}.\nThe user can be logged out now.').format( username=GLib.markup_escape_text(username))) self._enableLogoutButton = True self._update() def setAuthMessage(self, message): self.messageLabel.set_label(message) def _grabDevices(self, w, e): self.promptEntry.grab_focus() if not self.devicesGrabbed: for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER): if device.grab(self.get_window(), Gdk.GrabOwnership.APPLICATION, True, Gdk.EventMask.ALL_EVENTS_MASK, None, Gdk.CURRENT_TIME) != Gdk.GrabStatus.SUCCESS: self._ungrabDevices() self.get_window().lower() return False self.present() self.devicesGrabbed = True return False def _ungrabDevices(self): for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER): device.ungrab(Gdk.CURRENT_TIME) self.devicesGrabbed = False def lock(self): self.show_all() self.display.sync() self._grabDevices(self, None) return self.devicesGrabbed def unlock(self): self._ungrabDevices() self.hide() self.reset(False, True)