Throw out DBus, add X11 based lock activation
This commit is contained in:
parent
58cf425fb0
commit
caa1f49279
6 changed files with 229 additions and 224 deletions
90
DBus.py
90
DBus.py
|
@ -1,90 +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.
|
|
||||||
|
|
||||||
|
|
||||||
from gi.repository import GLib, Gio
|
|
||||||
|
|
||||||
from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, pointer
|
|
||||||
from ctypes import c_void_p, c_char_p, c_bool
|
|
||||||
from ctypes.util import find_library
|
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
|
|
||||||
dbus_xml = '''
|
|
||||||
<node>
|
|
||||||
<interface name="net.universe_factory.Pylock">
|
|
||||||
<method name="Lock"/>
|
|
||||||
</interface>
|
|
||||||
</node>
|
|
||||||
'''
|
|
||||||
|
|
||||||
gio = CDLL(find_library('gio-2.0'))
|
|
||||||
|
|
||||||
nodeInfo = Gio.DBusNodeInfo.new_for_xml(dbus_xml)
|
|
||||||
interfaceInfo = nodeInfo.lookup_interface('net.universe_factory.Pylock')
|
|
||||||
|
|
||||||
METHOD_CALL_FUNC = CFUNCTYPE(None, c_void_p, c_char_p, c_char_p, c_char_p, c_char_p, c_void_p, c_void_p, c_void_p)
|
|
||||||
GET_PROPERTY_FUNC = CFUNCTYPE(c_void_p, c_void_p, c_char_p, c_char_p, c_char_p, c_char_p, c_void_p, c_void_p)
|
|
||||||
SET_PROPERTY_FUNC = CFUNCTYPE(c_bool, c_void_p, c_char_p, c_char_p, c_char_p, c_char_p, c_void_p, c_void_p, c_void_p)
|
|
||||||
|
|
||||||
def method_call(locker, connection, sender, object_path, interface_name, method_name, parameters, invocation, user_data):
|
|
||||||
assert str(method_name, 'ascii') == 'Lock'
|
|
||||||
|
|
||||||
locker.lock()
|
|
||||||
gio.g_dbus_method_invocation_return_value(invocation, hash(GLib.Variant.new_tuple()))
|
|
||||||
|
|
||||||
@GET_PROPERTY_FUNC
|
|
||||||
def get_property(connection, sender, object_path, interface_name, property_name, error, user_data):
|
|
||||||
assert False
|
|
||||||
|
|
||||||
@SET_PROPERTY_FUNC
|
|
||||||
def set_property(connection, sender, object_path, interface_name, property_name, value, error, user_data):
|
|
||||||
assert False
|
|
||||||
|
|
||||||
class VTable(Structure):
|
|
||||||
_fields_ = [
|
|
||||||
("method_call", METHOD_CALL_FUNC),
|
|
||||||
("get_property", GET_PROPERTY_FUNC),
|
|
||||||
("set_property", SET_PROPERTY_FUNC)
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, locker):
|
|
||||||
Structure.__init__(self)
|
|
||||||
self.method_call = METHOD_CALL_FUNC(partial(method_call, locker))
|
|
||||||
self.get_property = get_property
|
|
||||||
self.set_property = set_property
|
|
||||||
|
|
||||||
gio.g_dbus_connection_register_object.argtypes = [c_void_p, c_char_p, c_void_p, POINTER(VTable), c_void_p, c_void_p, c_void_p]
|
|
||||||
gio.g_dbus_method_invocation_return_value.argtypes = [c_void_p, c_void_p]
|
|
||||||
|
|
||||||
|
|
||||||
class DBus(object):
|
|
||||||
def __init__(self, locker):
|
|
||||||
self.vtable = VTable(locker)
|
|
||||||
|
|
||||||
Gio.bus_own_name(Gio.BusType.SESSION, 'net.universe_factory.Pylock', Gio.BusNameOwnerFlags.NONE, self.bus_aquired, None, None)
|
|
||||||
|
|
||||||
def bus_aquired(self, connection, name):
|
|
||||||
b = lambda s: bytes(s, 'ascii')
|
|
||||||
gio.g_dbus_connection_register_object(c_void_p(hash(connection)), b('/net/universe_factory/Pylock'), c_void_p(hash(interfaceInfo)), pointer(self.vtable), None, None, None)
|
|
|
@ -40,14 +40,19 @@ class LockWindow(Gtk.Window):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Gtk.Window.__init__(self)
|
Gtk.Window.__init__(self)
|
||||||
|
|
||||||
bg = Gtk.Image.new_from_file("bg.svg")
|
self.display = Gdk.Display.get_default()
|
||||||
self.add(bg)
|
self.screen = self.display.get_default_screen()
|
||||||
|
|
||||||
self.set_decorated(False)
|
self.overlay = Gtk.Overlay()
|
||||||
self.set_skip_taskbar_hint(True)
|
self.add(self.overlay)
|
||||||
self.set_skip_pager_hint(True)
|
|
||||||
self.set_keep_above(True)
|
self.bg = Gtk.Image.new_from_file("bg.svg")
|
||||||
self.fullscreen()
|
self.overlay.add(self.bg)
|
||||||
|
|
||||||
|
self.realize()
|
||||||
|
self.get_window().set_override_redirect(True)
|
||||||
|
|
||||||
|
self.set_has_resize_grip(False)
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
|
|
||||||
|
@ -61,12 +66,9 @@ class LockWindow(Gtk.Window):
|
||||||
self.logoutButton = builder.get_object('logout_button')
|
self.logoutButton = builder.get_object('logout_button')
|
||||||
self.unlockButton = builder.get_object('unlock_button')
|
self.unlockButton = builder.get_object('unlock_button')
|
||||||
|
|
||||||
self.unlockWindow.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
|
self.overlay.add_overlay(self.unlockWindow)
|
||||||
self.unlockWindow.set_transient_for(self)
|
|
||||||
self.unlockWindow.set_modal(True)
|
|
||||||
|
|
||||||
self.connect('delete-event', lambda w, e: True)
|
self.connect('delete-event', lambda w, e: True)
|
||||||
self.unlockWindow.connect('delete-event', lambda w, e: True)
|
|
||||||
|
|
||||||
self.promptEntry.connect('activate', lambda w: self._tryUnlock())
|
self.promptEntry.connect('activate', lambda w: self._tryUnlock())
|
||||||
self.logoutButton.connect('clicked', lambda w: self._logout())
|
self.logoutButton.connect('clicked', lambda w: self._logout())
|
||||||
|
@ -74,10 +76,9 @@ class LockWindow(Gtk.Window):
|
||||||
|
|
||||||
self.reset(True)
|
self.reset(True)
|
||||||
|
|
||||||
self.display = Gdk.Display.get_default()
|
|
||||||
self.deviceManager = self.display.get_device_manager()
|
self.deviceManager = self.display.get_device_manager()
|
||||||
|
|
||||||
self.unlockWindow.connect('map-event', self._grab_devices)
|
self.connect('map-event', self._grabDevices)
|
||||||
|
|
||||||
def _logout(self):
|
def _logout(self):
|
||||||
self.promptEntry.set_sensitive(False)
|
self.promptEntry.set_sensitive(False)
|
||||||
|
@ -128,26 +129,34 @@ class LockWindow(Gtk.Window):
|
||||||
def setAuthMessage(self, message):
|
def setAuthMessage(self, message):
|
||||||
self.messageLabel.set_label(message)
|
self.messageLabel.set_label(message)
|
||||||
|
|
||||||
def _grab_devices(self, w, e):
|
def _grabDevices(self, w, e):
|
||||||
|
self.get_window().move_resize(0, 0, self.screen.get_width(), self.screen.get_height())
|
||||||
|
|
||||||
|
self.promptEntry.grab_focus()
|
||||||
|
|
||||||
for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER):
|
for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER):
|
||||||
device.grab(self.unlockWindow.get_window(), Gdk.GrabOwnership.APPLICATION, True, Gdk.EventMask.ALL_EVENTS_MASK, None, Gdk.CURRENT_TIME)
|
device.grab(self.get_window(), Gdk.GrabOwnership.APPLICATION, True, Gdk.EventMask.ALL_EVENTS_MASK, None, Gdk.CURRENT_TIME)
|
||||||
|
|
||||||
|
self.present()
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _ungrab_devices(self):
|
def _ungrabDevices(self):
|
||||||
for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER):
|
for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER):
|
||||||
device.ungrab(Gdk.CURRENT_TIME)
|
device.ungrab(Gdk.CURRENT_TIME)
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
self.show_all()
|
#geom = Gdk.Geometry()
|
||||||
self.unlockWindow.show_all()
|
#geom.min_width = self.screen.get_width()
|
||||||
|
#geom.max_width = self.screen.get_width()
|
||||||
|
#geom.min_height = self.screen.get_height()
|
||||||
|
#geom.max_height = self.screen.get_height()
|
||||||
|
#self.set_geometry_hints(self.overlay, geom, Gdk.WindowHints.MAX_SIZE)
|
||||||
|
|
||||||
self.promptEntry.grab_focus()
|
self.show_all()
|
||||||
|
|
||||||
def unlock(self):
|
def unlock(self):
|
||||||
self._ungrab_devices()
|
self._ungrabDevices()
|
||||||
|
|
||||||
self.unlockWindow.hide()
|
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
self.reset(True)
|
self.reset(True)
|
||||||
|
|
|
@ -62,8 +62,8 @@ class Locker(object):
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
if not self.locked:
|
if not self.locked:
|
||||||
self.doLock(self.logoutTimeout)
|
|
||||||
self.locked = True
|
self.locked = True
|
||||||
|
self.doLock(self.logoutTimeout)
|
||||||
|
|
||||||
if self.doLogout is not None:
|
if self.doLogout is not None:
|
||||||
self.currentLogoutTimeout = self.logoutTimeout
|
self.currentLogoutTimeout = self.logoutTimeout
|
||||||
|
|
78
Message.py
Normal file
78
Message.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# 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 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)
|
20
pylock.py
20
pylock.py
|
@ -34,9 +34,9 @@ import argparse
|
||||||
|
|
||||||
from gi.repository import Gtk, Gdk, Gio
|
from gi.repository import Gtk, Gdk, Gio
|
||||||
|
|
||||||
from DBus import DBus
|
|
||||||
from Locker import Locker
|
from Locker import Locker
|
||||||
from LockWindow import LockWindow
|
from LockWindow import LockWindow
|
||||||
|
import Message
|
||||||
import pam
|
import pam
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,8 +74,20 @@ locale.textdomain('pylock')
|
||||||
Gtk.Settings.get_default().set_property('gtk-theme-name', theme)
|
Gtk.Settings.get_default().set_property('gtk-theme-name', 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)
|
||||||
|
|
||||||
|
|
||||||
window = LockWindow()
|
window = LockWindow()
|
||||||
|
|
||||||
|
if not Message.acquireSelection(window):
|
||||||
|
print('Unable to register for lock messages', file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def lock(timeLeft):
|
def lock(timeLeft):
|
||||||
window.updateLockMessage(args.username, timeLeft)
|
window.updateLockMessage(args.username, timeLeft)
|
||||||
|
@ -98,8 +110,10 @@ else:
|
||||||
locker = Locker(args.timeout, lock, window.unlock, args.logout, logout, updateTimeout)
|
locker = Locker(args.timeout, lock, window.unlock, args.logout, logout, updateTimeout)
|
||||||
|
|
||||||
|
|
||||||
if args.timeout != 0:
|
def _triggerLock(w, e):
|
||||||
dbus = DBus(locker)
|
locker.lock()
|
||||||
|
|
||||||
|
window.connect('map-event', _triggerLock)
|
||||||
|
|
||||||
|
|
||||||
pamAuth = pam.pam()
|
pamAuth = pam.pam()
|
||||||
|
|
16
unlock.ui
16
unlock.ui
|
@ -1,17 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="2.24"/>
|
<requires lib="gtk+" version="2.24"/>
|
||||||
<object class="GtkWindow" id="unlock_window">
|
<object class="GtkNotebook" id="unlock_window">
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="resizable">False</property>
|
|
||||||
<property name="decorated">False</property>
|
|
||||||
<property name="has_resize_grip">False</property>
|
|
||||||
<signal name="size-allocate" handler="login_window_size_allocate_cb" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkNotebook" id="login_notebook">
|
|
||||||
<property name="width_request">700</property>
|
<property name="width_request">700</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
<property name="show_tabs">False</property>
|
<property name="show_tabs">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkHBox" id="box1">
|
<object class="GtkHBox" id="box1">
|
||||||
|
@ -74,6 +69,7 @@
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="visibility">False</property>
|
<property name="visibility">False</property>
|
||||||
<property name="invisible_char">•</property>
|
<property name="invisible_char">•</property>
|
||||||
|
<property name="invisible_char_set">True</property>
|
||||||
<property name="primary_icon_activatable">False</property>
|
<property name="primary_icon_activatable">False</property>
|
||||||
<property name="secondary_icon_activatable">False</property>
|
<property name="secondary_icon_activatable">False</property>
|
||||||
<signal name="activate" handler="login_cb" swapped="no"/>
|
<signal name="activate" handler="login_cb" swapped="no"/>
|
||||||
|
@ -167,6 +163,4 @@
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
</interface>
|
||||||
|
|
Reference in a new issue