Rework pylock to behave like xlock
This commit is contained in:
parent
e342d5c724
commit
4659883ba0
4 changed files with 43 additions and 154 deletions
71
Idle.py
71
Idle.py
|
@ -1,71 +0,0 @@
|
||||||
# Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
|
|
||||||
# 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
|
|
25
Locker.py
25
Locker.py
|
@ -24,35 +24,16 @@
|
||||||
|
|
||||||
from gi.repository import Gtk, GLib
|
from gi.repository import Gtk, GLib
|
||||||
|
|
||||||
import Idle
|
|
||||||
|
|
||||||
|
|
||||||
class Locker(object):
|
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.locked = False
|
||||||
self.lockTimeout = lockTimeout
|
|
||||||
self.logoutTimeout = logoutTimeout
|
self.logoutTimeout = logoutTimeout
|
||||||
self.doLock = doLock
|
self.doLock = doLock
|
||||||
self.doUnlock = doUnlock
|
self.doUnlock = doUnlock
|
||||||
self.doLogout = doLogout
|
self.doLogout = doLogout
|
||||||
self.updateLogoutTimeout = updateLogoutTimeout
|
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):
|
def _checkLogout(self):
|
||||||
if not self.locked:
|
if not self.locked:
|
||||||
return False
|
return False
|
||||||
|
@ -68,7 +49,7 @@ class Locker(object):
|
||||||
self.locked = True
|
self.locked = True
|
||||||
if not self.doLock(self.logoutTimeout):
|
if not self.doLock(self.logoutTimeout):
|
||||||
self.locked = False
|
self.locked = False
|
||||||
GLib.timeout_add_seconds(1, self._checkLock)
|
GLib.timeout_add_seconds(1, self.lock)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.doLogout is not None:
|
if self.doLogout is not None:
|
||||||
|
@ -84,8 +65,6 @@ class Locker(object):
|
||||||
self.doUnlock()
|
self.doUnlock()
|
||||||
self.locked = False
|
self.locked = False
|
||||||
|
|
||||||
GLib.timeout_add_seconds(1, self._checkLock)
|
|
||||||
|
|
||||||
def _canLogout(self):
|
def _canLogout(self):
|
||||||
return (self.locked and self.doLogout is not None and self.currentLogoutTimeout <= 0)
|
return (self.locked and self.doLogout is not None and self.currentLogoutTimeout <= 0)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
|
# Copyright (c) 2012-2013, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -41,8 +41,6 @@ if libX11 == None:
|
||||||
raise OSError('libX11 could not be found.')
|
raise OSError('libX11 could not be found.')
|
||||||
libX11 = ctypes.cdll.LoadLibrary(libX11)
|
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.argtypes = display_p, xid, xid, x11_time
|
||||||
libX11.XSetSelectionOwner.restype = ctypes.c_int
|
libX11.XSetSelectionOwner.restype = ctypes.c_int
|
||||||
libX11.XGetSelectionOwner.argtypes = display_p, xid
|
libX11.XGetSelectionOwner.argtypes = display_p, xid
|
||||||
|
@ -52,27 +50,17 @@ libX11.XGetSelectionOwner.restype = xid
|
||||||
display = Gdk.Display.get_default()
|
display = Gdk.Display.get_default()
|
||||||
dpy_p = display_p(hash(GdkX11.X11Display.get_xdisplay(display)))
|
dpy_p = display_p(hash(GdkX11.X11Display.get_xdisplay(display)))
|
||||||
atomPylockWindow = GdkX11.x11_get_xatom_by_name("PYLOCK_WINDOW")
|
atomPylockWindow = GdkX11.x11_get_xatom_by_name("PYLOCK_WINDOW")
|
||||||
atomPylockActionLock = GdkX11.x11_get_xatom_by_name("PYLOCK_ACTION_LOCK")
|
|
||||||
|
|
||||||
|
|
||||||
def getSelection():
|
def get():
|
||||||
return libX11.XGetSelectionOwner(dpy_p, atomPylockWindow)
|
return libX11.XGetSelectionOwner(dpy_p, atomPylockWindow)
|
||||||
|
|
||||||
def sendLockMessage():
|
def acquire(window):
|
||||||
window = getSelection()
|
if get() != 0:
|
||||||
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
|
return False
|
||||||
|
|
||||||
window.realize()
|
window.realize()
|
||||||
xid = GdkX11.X11Window.get_xid(window.get_window())
|
xid = GdkX11.X11Window.get_xid(window.get_window())
|
||||||
libX11.XSetSelectionOwner(dpy_p, atomPylockWindow, xid, 0)
|
libX11.XSetSelectionOwner(dpy_p, atomPylockWindow, xid, 0)
|
||||||
|
|
||||||
return (getSelection() == xid)
|
return (get() == xid)
|
75
pylock.py
75
pylock.py
|
@ -30,33 +30,27 @@ import sys
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
import locale
|
import locale
|
||||||
import argparse
|
import configparser
|
||||||
|
|
||||||
from gi.repository import Gtk, Gdk, Gio
|
from gi.repository import Gtk, GLib
|
||||||
|
|
||||||
from Locker import Locker
|
from Locker import Locker
|
||||||
from LockWindow import LockWindow
|
from LockWindow import LockWindow
|
||||||
import Message
|
import Selection
|
||||||
import pam
|
import pam
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_FILE = '/etc/pylock.conf'
|
||||||
|
|
||||||
|
|
||||||
_ = locale.gettext
|
_ = locale.gettext
|
||||||
|
|
||||||
|
|
||||||
def get_username():
|
configParser = configparser.ConfigParser()
|
||||||
return pwd.getpwuid(os.getuid())[0]
|
configParser.read_file(open(CONFIG_FILE))
|
||||||
|
|
||||||
|
config = configParser['pylock']
|
||||||
theme = 'UzL-login'
|
username = pwd.getpwuid(os.getuid())[0]
|
||||||
|
|
||||||
|
|
||||||
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.'))
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def handler(signum, frame):
|
def handler(signum, frame):
|
||||||
|
@ -71,58 +65,54 @@ signal.signal(signal.SIGQUIT, handler)
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
locale.textdomain('pylock')
|
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:
|
def waitForSelection():
|
||||||
if args.timeout == 0:
|
if Selection.get() != 0:
|
||||||
Message.sendLockMessage()
|
return True
|
||||||
|
|
||||||
|
Gtk.main_quit()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if Selection.get() != 0:
|
||||||
|
GLib.timeout_add_seconds(1, waitForSelection)
|
||||||
|
Gtk.main()
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
|
||||||
print('There seems to be a Pylock instance already running.', file=sys.stderr)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
window = LockWindow()
|
window = LockWindow()
|
||||||
|
|
||||||
if not Message.acquireSelection(window):
|
if not Selection.acquire(window):
|
||||||
print('Unable to register for lock messages', file=sys.stderr)
|
print('Unable to register pylock at X server', file=sys.stderr)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
def lock(timeLeft):
|
def lock(timeLeft):
|
||||||
window.updateLockMessage(args.username, timeLeft)
|
window.updateLockMessage(username, timeLeft)
|
||||||
return window.lock()
|
return window.lock()
|
||||||
|
|
||||||
def logout():
|
def logout():
|
||||||
try:
|
try:
|
||||||
os.system(args.logout_command)
|
os.system(config['logout_command'])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
window.reset(False)
|
window.reset(False)
|
||||||
|
|
||||||
def updateTimeout(timeLeft):
|
def updateTimeout(timeLeft):
|
||||||
window.updateLockMessage(args.username, timeLeft)
|
window.updateLockMessage(username, timeLeft)
|
||||||
|
|
||||||
if args.logout_command is None:
|
if not ('logout_timeout' in config and 'logout_command' in config):
|
||||||
locker = Locker(args.timeout, lock, window.unlock)
|
locker = Locker(lock, window.unlock)
|
||||||
else:
|
else:
|
||||||
locker = Locker(args.timeout, lock, window.unlock, args.logout, logout, updateTimeout)
|
locker = Locker(lock, window.unlock, int(config['logout_timeout']), logout, updateTimeout)
|
||||||
|
|
||||||
|
|
||||||
def _triggerLock(w, e):
|
|
||||||
locker.lock()
|
|
||||||
|
|
||||||
window.connect('map-event', _triggerLock)
|
|
||||||
|
|
||||||
|
|
||||||
pamAuth = pam.pam()
|
pamAuth = pam.pam()
|
||||||
|
|
||||||
def tryUnlock(w, password):
|
def tryUnlock(w, password):
|
||||||
if pamAuth.authenticate(args.username, password):
|
if pamAuth.authenticate(username, password):
|
||||||
locker.unlock()
|
locker.unlock()
|
||||||
|
|
||||||
if args.timeout == 0:
|
|
||||||
Gtk.main_quit()
|
Gtk.main_quit()
|
||||||
else:
|
else:
|
||||||
window.reset()
|
window.reset()
|
||||||
|
@ -130,7 +120,10 @@ def tryUnlock(w, password):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
window.connect('logout', lambda w: locker.logout())
|
window.connect('logout', lambda w: locker.logout())
|
||||||
window.connect('tryUnlock', tryUnlock)
|
window.connect('tryUnlock', tryUnlock)
|
||||||
|
|
||||||
|
GLib.idle_add(lambda: locker.lock())
|
||||||
|
|
||||||
Gtk.main()
|
Gtk.main()
|
||||||
|
|
Reference in a new issue