Throw out DBus, add X11 based lock activation

This commit is contained in:
Matthias Schiffer 2012-03-22 16:32:01 +01:00
parent 58cf425fb0
commit caa1f49279
6 changed files with 229 additions and 224 deletions

90
DBus.py
View file

@ -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)

View file

@ -40,14 +40,19 @@ class LockWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
bg = Gtk.Image.new_from_file("bg.svg")
self.add(bg)
self.display = Gdk.Display.get_default()
self.screen = self.display.get_default_screen()
self.set_decorated(False)
self.set_skip_taskbar_hint(True)
self.set_skip_pager_hint(True)
self.set_keep_above(True)
self.fullscreen()
self.overlay = Gtk.Overlay()
self.add(self.overlay)
self.bg = Gtk.Image.new_from_file("bg.svg")
self.overlay.add(self.bg)
self.realize()
self.get_window().set_override_redirect(True)
self.set_has_resize_grip(False)
builder = Gtk.Builder()
@ -61,12 +66,9 @@ class LockWindow(Gtk.Window):
self.logoutButton = builder.get_object('logout_button')
self.unlockButton = builder.get_object('unlock_button')
self.unlockWindow.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
self.unlockWindow.set_transient_for(self)
self.unlockWindow.set_modal(True)
self.overlay.add_overlay(self.unlockWindow)
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.logoutButton.connect('clicked', lambda w: self._logout())
@ -74,10 +76,9 @@ class LockWindow(Gtk.Window):
self.reset(True)
self.display = Gdk.Display.get_default()
self.deviceManager = self.display.get_device_manager()
self.unlockWindow.connect('map-event', self._grab_devices)
self.connect('map-event', self._grabDevices)
def _logout(self):
self.promptEntry.set_sensitive(False)
@ -128,26 +129,34 @@ class LockWindow(Gtk.Window):
def setAuthMessage(self, 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):
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
def _ungrab_devices(self):
def _ungrabDevices(self):
for device in self.deviceManager.list_devices(Gdk.DeviceType.MASTER):
device.ungrab(Gdk.CURRENT_TIME)
def lock(self):
self.show_all()
self.unlockWindow.show_all()
#geom = Gdk.Geometry()
#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):
self._ungrab_devices()
self._ungrabDevices()
self.unlockWindow.hide()
self.hide()
self.reset(True)

View file

@ -62,8 +62,8 @@ class Locker(object):
def lock(self):
if not self.locked:
self.doLock(self.logoutTimeout)
self.locked = True
self.doLock(self.logoutTimeout)
if self.doLogout is not None:
self.currentLogoutTimeout = self.logoutTimeout

78
Message.py Normal file
View 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)

View file

@ -34,9 +34,9 @@ import argparse
from gi.repository import Gtk, Gdk, Gio
from DBus import DBus
from Locker import Locker
from LockWindow import LockWindow
import Message
import pam
@ -74,8 +74,20 @@ locale.textdomain('pylock')
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()
if not Message.acquireSelection(window):
print('Unable to register for lock messages', file=sys.stderr)
def lock(timeLeft):
window.updateLockMessage(args.username, timeLeft)
@ -98,8 +110,10 @@ else:
locker = Locker(args.timeout, lock, window.unlock, args.logout, logout, updateTimeout)
if args.timeout != 0:
dbus = DBus(locker)
def _triggerLock(w, e):
locker.lock()
window.connect('map-event', _triggerLock)
pamAuth = pam.pam()

View file

@ -1,17 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.24"/>
<object class="GtkWindow" 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">
<object class="GtkNotebook" id="unlock_window">
<property name="width_request">700</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>
<child>
<object class="GtkHBox" id="box1">
@ -74,6 +69,7 @@
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<signal name="activate" handler="login_cb" swapped="no"/>
@ -167,6 +163,4 @@
<placeholder/>
</child>
</object>
</child>
</object>
</interface>