From 4659883ba00bd499f41f4cc1a5f96e4bdd5ac9de Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 6 Mar 2013 14:16:47 +0100 Subject: Rework pylock to behave like xlock --- Idle.py | 71 ------------------------------------------------------ Locker.py | 25 ++----------------- Message.py | 78 ------------------------------------------------------------ Selection.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ pylock.py | 77 +++++++++++++++++++++++++++-------------------------------- 5 files changed, 103 insertions(+), 214 deletions(-) delete mode 100644 Idle.py delete mode 100644 Message.py create mode 100644 Selection.py diff --git a/Idle.py b/Idle.py deleted file mode 100644 index d9ea53a..0000000 --- a/Idle.py +++ /dev/null @@ -1,71 +0,0 @@ -# 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 ctypes -import ctypes.util - -from gi.repository import GdkX11 - - -class XScreenSaverInfo(ctypes.Structure): - _fields_ = [ - ('window', ctypes.c_ulong), - ('state', ctypes.c_int), - ('kind', ctypes.c_int), - ('til_or_since', ctypes.c_ulong), - ('idle', ctypes.c_ulong), - ('eventMask', ctypes.c_ulong) - ] -XScreenSaverInfo_p = ctypes.POINTER(XScreenSaverInfo) - -display_p = ctypes.c_void_p -xid = ctypes.c_ulong -c_int_p = ctypes.POINTER(ctypes.c_int) - -libXsspath = ctypes.util.find_library('Xss') -if libXsspath == None: - raise OSError('libXss could not be found.') -libXss = ctypes.cdll.LoadLibrary(libXsspath) -libXss.XScreenSaverQueryExtension.argtypes = display_p, c_int_p, c_int_p -libXss.XScreenSaverAllocInfo.restype = XScreenSaverInfo_p -libXss.XScreenSaverQueryInfo.argtypes = (display_p, xid, XScreenSaverInfo_p) - -dpy_p = hash(GdkX11.x11_get_default_xdisplay()) -rootwindow = GdkX11.x11_get_default_root_xwindow() - -_event_basep = ctypes.c_int() -_error_basep = ctypes.c_int() -if libXss.XScreenSaverQueryExtension(ctypes.c_void_p(dpy_p), ctypes.byref(_event_basep), - ctypes.byref(_error_basep)) == 0: - raise OSError('XScreenSaver Extension not available on display.') - -xss_info_p = libXss.XScreenSaverAllocInfo() -if xss_info_p == None: - raise OSError('XScreenSaverAllocInfo: Out of Memory.') - -def getIdleSec(): - if libXss.XScreenSaverQueryInfo(dpy_p, rootwindow, xss_info_p) == 0: - return 0 - else: - return int(xss_info_p.contents.idle) / 1000 diff --git a/Locker.py b/Locker.py index 0f10fe0..5243609 100644 --- a/Locker.py +++ b/Locker.py @@ -24,35 +24,16 @@ from gi.repository import Gtk, GLib -import Idle - class Locker(object): - def __init__(self, lockTimeout, doLock, doUnlock, logoutTimeout = None, doLogout = None, updateLogoutTimeout = None): + def __init__(self, doLock, doUnlock, logoutTimeout = None, doLogout = None, updateLogoutTimeout = None): self.locked = False - self.lockTimeout = lockTimeout self.logoutTimeout = logoutTimeout self.doLock = doLock self.doUnlock = doUnlock self.doLogout = doLogout self.updateLogoutTimeout = updateLogoutTimeout - if self.lockTimeout > 0: - GLib.timeout_add_seconds(1, self._checkLock) - else: - GLib.idle_add(self.lock) - - def _checkLock(self): - if self.locked: - return False - - idle = Idle.getIdleSec() - if (idle >= self.lockTimeout): - GLib.idle_add(self.lock) - return False - else: - return True - def _checkLogout(self): if not self.locked: return False @@ -68,7 +49,7 @@ class Locker(object): self.locked = True if not self.doLock(self.logoutTimeout): self.locked = False - GLib.timeout_add_seconds(1, self._checkLock) + GLib.timeout_add_seconds(1, self.lock) return False if self.doLogout is not None: @@ -84,8 +65,6 @@ class Locker(object): self.doUnlock() self.locked = False - GLib.timeout_add_seconds(1, self._checkLock) - def _canLogout(self): return (self.locked and self.doLogout is not None and self.currentLogoutTimeout <= 0) diff --git a/Message.py b/Message.py deleted file mode 100644 index b78a0f3..0000000 --- a/Message.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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 ctypes -import ctypes.util - -from gi.repository import Gdk, GdkX11 - - -XEvent_p = ctypes.c_void_p -display_p = ctypes.c_void_p -xid = ctypes.c_ulong -x11_bool = ctypes.c_int -x11_time = ctypes.c_ulong -status = ctypes.c_int - - -libX11 = ctypes.util.find_library('X11') -if libX11 == None: - raise OSError('libX11 could not be found.') -libX11 = ctypes.cdll.LoadLibrary(libX11) - -libX11.XSendEvent.argtypes = display_p, xid, x11_bool, ctypes.c_long, XEvent_p -libX11.XSendEvent.restype = status -libX11.XSetSelectionOwner.argtypes = display_p, xid, xid, x11_time -libX11.XSetSelectionOwner.restype = ctypes.c_int -libX11.XGetSelectionOwner.argtypes = display_p, xid -libX11.XGetSelectionOwner.restype = xid - - -display = Gdk.Display.get_default() -dpy_p = display_p(hash(GdkX11.X11Display.get_xdisplay(display))) -atomPylockWindow = GdkX11.x11_get_xatom_by_name("PYLOCK_WINDOW") -atomPylockActionLock = GdkX11.x11_get_xatom_by_name("PYLOCK_ACTION_LOCK") - - -def getSelection(): - return libX11.XGetSelectionOwner(dpy_p, atomPylockWindow) - -def sendLockMessage(): - window = getSelection() - if window == 0: - return - - gdkWindow = GdkX11.X11Window.foreign_new_for_display(display, window) - gdkWindow.show() - display.sync() - -def acquireSelection(window): - if getSelection() != 0: - return False - - window.realize() - xid = GdkX11.X11Window.get_xid(window.get_window()) - libX11.XSetSelectionOwner(dpy_p, atomPylockWindow, xid, 0) - - return (getSelection() == xid) diff --git a/Selection.py b/Selection.py new file mode 100644 index 0000000..11a7285 --- /dev/null +++ b/Selection.py @@ -0,0 +1,66 @@ +# Copyright (c) 2012-2013, 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 ctypes +import ctypes.util + +from gi.repository import Gdk, GdkX11 + + +XEvent_p = ctypes.c_void_p +display_p = ctypes.c_void_p +xid = ctypes.c_ulong +x11_bool = ctypes.c_int +x11_time = ctypes.c_ulong +status = ctypes.c_int + + +libX11 = ctypes.util.find_library('X11') +if libX11 == None: + raise OSError('libX11 could not be found.') +libX11 = ctypes.cdll.LoadLibrary(libX11) + +libX11.XSetSelectionOwner.argtypes = display_p, xid, xid, x11_time +libX11.XSetSelectionOwner.restype = ctypes.c_int +libX11.XGetSelectionOwner.argtypes = display_p, xid +libX11.XGetSelectionOwner.restype = xid + + +display = Gdk.Display.get_default() +dpy_p = display_p(hash(GdkX11.X11Display.get_xdisplay(display))) +atomPylockWindow = GdkX11.x11_get_xatom_by_name("PYLOCK_WINDOW") + + +def get(): + return libX11.XGetSelectionOwner(dpy_p, atomPylockWindow) + +def acquire(window): + if get() != 0: + return False + + window.realize() + xid = GdkX11.X11Window.get_xid(window.get_window()) + libX11.XSetSelectionOwner(dpy_p, atomPylockWindow, xid, 0) + + return (get() == xid) diff --git a/pylock.py b/pylock.py index be8e901..32e9149 100644 --- a/pylock.py +++ b/pylock.py @@ -30,33 +30,27 @@ import sys import os import pwd import locale -import argparse +import configparser -from gi.repository import Gtk, Gdk, Gio +from gi.repository import Gtk, GLib from Locker import Locker from LockWindow import LockWindow -import Message +import Selection import pam -_ = locale.gettext - +CONFIG_FILE = '/etc/pylock.conf' -def get_username(): - return pwd.getpwuid(os.getuid())[0] - -theme = 'UzL-login' +_ = locale.gettext -parser = argparse.ArgumentParser(prog='pylock', description=_('GTK-based screen locker.')) -parser.add_argument('-t', '--timeout', type=int, default=0, help=_('Start in daemon mode that will automatically activate after a given time of inactivity.')) -parser.add_argument('-l', '--logout', type=int, default=0, help=_('Allow to logout the current user after a given time.')) -parser.add_argument('-c', '--logout-command', help=_('Specifies the command to be called to log out a user.')) -parser.add_argument('-u', '--username', default=get_username(), help=_('Set the name of the user that is able to unlock the screen.')) +configParser = configparser.ConfigParser() +configParser.read_file(open(CONFIG_FILE)) -args = parser.parse_args() +config = configParser['pylock'] +username = pwd.getpwuid(os.getuid())[0] def handler(signum, frame): @@ -71,66 +65,65 @@ signal.signal(signal.SIGQUIT, handler) locale.setlocale(locale.LC_ALL, '') locale.textdomain('pylock') -Gtk.Settings.get_default().set_property('gtk-theme-name', theme) +Gtk.Settings.get_default().set_property('gtk-theme-name', config['theme']) -if Message.getSelection() != 0: - if args.timeout == 0: - Message.sendLockMessage() - exit(0) - else: - print('There seems to be a Pylock instance already running.', file=sys.stderr) - exit(1) +def waitForSelection(): + if Selection.get() != 0: + return True + + Gtk.main_quit() + return False + +if Selection.get() != 0: + GLib.timeout_add_seconds(1, waitForSelection) + Gtk.main() + exit(0) window = LockWindow() -if not Message.acquireSelection(window): - print('Unable to register for lock messages', file=sys.stderr) +if not Selection.acquire(window): + print('Unable to register pylock at X server', file=sys.stderr) + exit(1) def lock(timeLeft): - window.updateLockMessage(args.username, timeLeft) + window.updateLockMessage(username, timeLeft) return window.lock() def logout(): try: - os.system(args.logout_command) + os.system(config['logout_command']) except: pass window.reset(False) def updateTimeout(timeLeft): - window.updateLockMessage(args.username, timeLeft) + window.updateLockMessage(username, timeLeft) -if args.logout_command is None: - locker = Locker(args.timeout, lock, window.unlock) +if not ('logout_timeout' in config and 'logout_command' in config): + locker = Locker(lock, window.unlock) else: - locker = Locker(args.timeout, lock, window.unlock, args.logout, logout, updateTimeout) - - -def _triggerLock(w, e): - locker.lock() - -window.connect('map-event', _triggerLock) - + locker = Locker(lock, window.unlock, int(config['logout_timeout']), logout, updateTimeout) pamAuth = pam.pam() def tryUnlock(w, password): - if pamAuth.authenticate(args.username, password): + if pamAuth.authenticate(username, password): locker.unlock() - - if args.timeout == 0: - Gtk.main_quit() + Gtk.main_quit() else: window.reset() window.setAuthMessage(pamAuth.reason) return True + window.connect('logout', lambda w: locker.logout()) window.connect('tryUnlock', tryUnlock) +GLib.idle_add(lambda: locker.lock()) + Gtk.main() -- cgit v1.2.3