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

View file

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

210
unlock.ui
View file

@ -1,46 +1,97 @@
<?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="width_request">700</property>
<property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="resizable">False</property> <property name="halign">center</property>
<property name="decorated">False</property> <property name="valign">center</property>
<property name="has_resize_grip">False</property> <property name="show_tabs">False</property>
<signal name="size-allocate" handler="login_window_size_allocate_cb" swapped="no"/>
<child> <child>
<object class="GtkNotebook" id="login_notebook"> <object class="GtkHBox" id="box1">
<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="show_tabs">False</property> <property name="spacing">40</property>
<child> <child>
<object class="GtkHBox" id="box1"> <object class="GtkImage" id="image1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="spacing">40</property> <property name="pixbuf">siegel.png</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child> <child>
<object class="GtkImage" id="image1"> <object class="GtkLabel" id="lock_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="pixbuf">siegel.png</property> <property name="label" comments="This is a placeholder string and will be replaced with the hostname of the system">[lock]</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">0</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkVBox" id="vbox2"> <object class="GtkVBox" id="login_box">
<property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="border_width">12</property> <property name="spacing">6</property>
<property name="spacing">12</property>
<child> <child>
<object class="GtkLabel" id="lock_label"> <object class="GtkHBox" id="prompt_box">
<property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" comments="This is a placeholder string and will be replaced with the hostname of the system">[lock]</property> <property name="spacing">6</property>
<child>
<object class="GtkLabel" id="prompt_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Password:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="prompt_entry">
<property name="visible">True</property>
<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"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="message_label">
<property name="can_focus">False</property>
<property name="yalign">0</property>
<property name="label" comments="This is a placeholder string and will be replaced with a message from PAM">[message]</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@ -49,124 +100,67 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkVBox" id="login_box"> <object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="spacing">6</property> <property name="spacing">20</property>
<child> <child>
<object class="GtkHBox" id="prompt_box"> <object class="GtkButton" id="unlock_button">
<property name="can_focus">False</property> <property name="label" translatable="yes">Unlock</property>
<property name="spacing">6</property> <property name="use_action_appearance">False</property>
<child> <property name="visible">True</property>
<object class="GtkLabel" id="prompt_label"> <property name="can_focus">True</property>
<property name="visible">True</property> <property name="receives_default">True</property>
<property name="can_focus">False</property> <property name="use_action_appearance">False</property>
<property name="label" translatable="yes">Password:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="prompt_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="invisible_char">•</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<signal name="activate" handler="login_cb" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="message_label">
<property name="can_focus">False</property>
<property name="yalign">0</property>
<property name="label" comments="This is a placeholder string and will be replaced with a message from PAM">[message]</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkHBox" id="hbox2"> <object class="GtkButton" id="logout_button">
<property name="label" translatable="yes">Logout</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="spacing">20</property> <property name="receives_default">True</property>
<child> <property name="use_action_appearance">False</property>
<object class="GtkButton" id="unlock_button">
<property name="label" translatable="yes">Unlock</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="logout_button">
<property name="label" translatable="yes">Logout</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">3</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
</object> </object>
</child> <packing>
<child type="tab"> <property name="expand">False</property>
<placeholder/> <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
<child type="tab">
<placeholder/>
</child>
</object> </object>
</interface> </interface>