From caa1f49279f2cf1b034a592d67c46b9df5428f65 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 22 Mar 2012 16:32:01 +0100 Subject: Throw out DBus, add X11 based lock activation --- DBus.py | 90 ------------------------- LockWindow.py | 53 +++++++++------ Locker.py | 2 +- Message.py | 78 ++++++++++++++++++++++ pylock.py | 20 +++++- unlock.ui | 210 ++++++++++++++++++++++++++++------------------------------ 6 files changed, 229 insertions(+), 224 deletions(-) delete mode 100644 DBus.py create mode 100644 Message.py diff --git a/DBus.py b/DBus.py deleted file mode 100644 index 6f24694..0000000 --- a/DBus.py +++ /dev/null @@ -1,90 +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. - - -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 = ''' - - - - - -''' - -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) diff --git a/LockWindow.py b/LockWindow.py index e040ea8..2ead7be 100644 --- a/LockWindow.py +++ b/LockWindow.py @@ -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.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_decorated(False) - self.set_skip_taskbar_hint(True) - self.set_skip_pager_hint(True) - self.set_keep_above(True) - self.fullscreen() + 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) diff --git a/Locker.py b/Locker.py index a872b38..f8e97ce 100644 --- a/Locker.py +++ b/Locker.py @@ -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 diff --git a/Message.py b/Message.py new file mode 100644 index 0000000..b78a0f3 --- /dev/null +++ b/Message.py @@ -0,0 +1,78 @@ +# 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/pylock.py b/pylock.py index dda47ba..f4841cd 100644 --- a/pylock.py +++ b/pylock.py @@ -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() diff --git a/unlock.ui b/unlock.ui index 8b33ab7..89bc3b8 100644 --- a/unlock.ui +++ b/unlock.ui @@ -1,172 +1,166 @@ - + + 700 + True False - False - False - False - + center + center + False - - 700 + True - True - False + False + 40 - + True False - 40 + siegel.png + + + False + True + 0 + + + + + True + False + 12 + 12 - + True False - siegel.png + [lock] - False + True True - 0 + 1 - - True + False - 12 - 12 + 6 - - True - False - [lock] - - - True - True - 1 - - - - + False 6 - + + True False - 6 - - - True - False - Password: - - - False - True - 0 - - - - - True - True - False - - False - False - - - - True - True - 1 - - + Password: - True + False True 0 - - False - 0 - [message] + + True + True + False + + True + False + False + + + + True + True + 1 + + + + + True + True + 0 + + + + + False + 0 + [message] + + + True + True + 1 + + + + + True + False + 20 + + + Unlock + False + True + True + True + False True True + end 1 - + + Logout + False True - False - 20 - - - Unlock - False - True - True - True - False - - - True - True - end - 1 - - - - - Logout - False - True - True - True - False - - - True - True - end - 2 - - + True + True + False True True + end 2 - False + True True - 3 + 2 False True - 1 + 3 - - - + + False + True + 1 + + + + -- cgit v1.2.3