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):
|
||||
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)
|
||||
|
|
|
@ -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
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 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()
|
||||
|
|
210
unlock.ui
210
unlock.ui
|
@ -1,46 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<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="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"/>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="show_tabs">False</property>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="login_notebook">
|
||||
<property name="width_request">700</property>
|
||||
<object class="GtkHBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="show_tabs">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">40</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="box1">
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkImage" id="image1">
|
||||
<object class="GtkLabel" id="lock_label">
|
||||
<property name="visible">True</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>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<object class="GtkVBox" id="login_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lock_label">
|
||||
<property name="visible">True</property>
|
||||
<object class="GtkHBox" id="prompt_box">
|
||||
<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>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -49,124 +100,67 @@
|
|||
</packing>
|
||||
</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="spacing">6</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="prompt_box">
|
||||
<property name="can_focus">False</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="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 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="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="can_focus">False</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<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>
|
||||
<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>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<placeholder/>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
Reference in a new issue