Initial commit
This commit is contained in:
commit
295a111cec
89 changed files with 2897 additions and 0 deletions
|
|
@ -0,0 +1,3 @@
|
|||
# Disable blank screen
|
||||
[org/gnome/desktop/session]
|
||||
idle-delay=uint32 0
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Disable lock on blank screen
|
||||
[org/gnome/desktop/screensaver]
|
||||
lock-enabled = false
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Disable suspend when inactive
|
||||
[org/gnome/settings-daemon/plugins/power]
|
||||
sleep-inactive-ac-type = 'nothing'
|
||||
sleep-inactive-battery-type = 'nothing'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# This prevents "Updates available" notifications
|
||||
[org/gnome/software]
|
||||
allow-updates = false
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
[org/gnome/shell]
|
||||
enabled-extensions = ['contest-lock@soi.ch', 'user-indicator@soi.ch']
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Disable locking the screen
|
||||
[org/gnome/desktop/lockdown]
|
||||
disable-lock-screen=true
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"policies": {
|
||||
"OverrideFirstRunPage": "",
|
||||
"NoDefaultBookmarks": true,
|
||||
"DisableProfileImport": true,
|
||||
"Preferences": {
|
||||
"datareporting.policy.dataSubmissionPolicyBypassNotification": true,
|
||||
"security.default_personal_cert": "Select Automatically"
|
||||
},
|
||||
"Homepage": {
|
||||
"URL": "https://finals.soi.ch/",
|
||||
"StartPage": "homepage"
|
||||
},
|
||||
"DisplayBookmarksToolbar": true,
|
||||
"Bookmarks": [
|
||||
{
|
||||
"Title": "Grader",
|
||||
"URL": "https://finals.soi.ch/",
|
||||
"Placement": "toolbar"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
33
config/simplefiles/CONTESTANT/etc/hosts
Normal file
33
config/simplefiles/CONTESTANT/etc/hosts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
127.0.0.1 localhost
|
||||
|
||||
# The following lines are desirable for IPv6 capable hosts
|
||||
::1 ip6-localhost ip6-loopback
|
||||
fe00::0 ip6-localnet
|
||||
ff00::0 ip6-mcastprefix
|
||||
ff02::1 ip6-allnodes
|
||||
ff02::2 ip6-allrouters
|
||||
|
||||
10.0.0.8 finals.soi.ch
|
||||
10.0.0.9 contestserver
|
||||
|
||||
10.0.0.10 contestant10
|
||||
10.0.0.11 contestant11
|
||||
10.0.0.12 contestant12
|
||||
10.0.0.13 contestant13
|
||||
10.0.0.14 contestant14
|
||||
10.0.0.15 contestant15
|
||||
10.0.0.16 contestant16
|
||||
10.0.0.17 contestant17
|
||||
10.0.0.18 contestant18
|
||||
10.0.0.19 contestant19
|
||||
10.0.0.20 contestant20
|
||||
10.0.0.21 contestant21
|
||||
10.0.0.22 contestant22
|
||||
10.0.0.23 contestant23
|
||||
10.0.0.24 contestant24
|
||||
10.0.0.25 contestant25
|
||||
10.0.0.26 contestant26
|
||||
10.0.0.27 contestant27
|
||||
10.0.0.28 contestant28
|
||||
|
||||
10.0.0.40 test40
|
||||
45
config/simplefiles/CONTESTANT/etc/nftables.conf
Executable file
45
config/simplefiles/CONTESTANT/etc/nftables.conf
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0;
|
||||
|
||||
ct state invalid drop
|
||||
ct state { established, related } accept
|
||||
|
||||
# Accept loopback
|
||||
iif lo accept
|
||||
|
||||
# Accept ICMP
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
|
||||
# Accept incoming connections to these ports
|
||||
tcp dport { ssh } accept
|
||||
|
||||
reject
|
||||
}
|
||||
chain forward {
|
||||
type filter hook forward priority 0;
|
||||
reject
|
||||
}
|
||||
chain output {
|
||||
type filter hook output priority 0;
|
||||
|
||||
ct state invalid drop
|
||||
ct state { established, related } accept
|
||||
|
||||
# Accept loopback
|
||||
oif lo accept
|
||||
|
||||
# Accept outgoing connections to these addresses
|
||||
ip daddr { 10.0.0.1-10.0.0.9 } accept
|
||||
|
||||
# Accept any connections by root user
|
||||
#meta skuid root accept
|
||||
|
||||
reject
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Show a root password prompt for these actions:
|
||||
# - change network settings
|
||||
# - hibernate
|
||||
# - change package download proxy
|
||||
# - mount removable storage, perform other disk operations
|
||||
[Disallow actions]
|
||||
Identity=unix-user:*
|
||||
Action=org.freedesktop.ModemManager1.*;org.freedesktop.NetworkManager.*;org.freedesktop.login1.hibernate;org.freedesktop.packagekit.system-network-proxy-configure;org.freedesktop.udisks2.*
|
||||
ResultActive=auth_admin
|
||||
ResultInactive=auth_admin
|
||||
ResultAny=auth_admin
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[Time]
|
||||
NTP=finals.soi.ch
|
||||
FallbackNTP=10.0.0.9
|
||||
0
config/simplefiles/CONTESTANT/root/.ssh/authorized_keys
Normal file
0
config/simplefiles/CONTESTANT/root/.ssh/authorized_keys
Normal file
49
config/simplefiles/CONTESTANT/usr/local/bin/install-client-cert
Executable file
49
config/simplefiles/CONTESTANT/usr/local/bin/install-client-cert
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This tool installs the client certificate in Firefox and Chromium.
|
||||
|
||||
username="$1"
|
||||
|
||||
userhome="/home/$username"
|
||||
certificate="$userhome/.config/clientcert.p12"
|
||||
|
||||
runuser -u "$username" -- mkdir -p "$userhome/.config"
|
||||
mv "$userhome/clientcert.p12" "$certificate"
|
||||
chown "$username:$username" "$certificate"
|
||||
|
||||
# Delete all Firefox data
|
||||
rm -rf "$userhome/.mozilla/"
|
||||
|
||||
# Create an empty profile
|
||||
runuser -u "$username" -- mkdir -p "$userhome/.mozilla/firefox/main"
|
||||
|
||||
# Tell Firefox to user this profile
|
||||
cat <<EOF >"$userhome/.mozilla/firefox/profiles.ini"
|
||||
[Profile0]
|
||||
Name=main
|
||||
IsRelative=1
|
||||
Path=main
|
||||
|
||||
[General]
|
||||
StartWithLastProfile=1
|
||||
Version=2
|
||||
|
||||
[Install3B6073811A6ABF12]
|
||||
Default=main
|
||||
Locked=1
|
||||
|
||||
EOF
|
||||
|
||||
chown "$username:$username" "$userhome/.mozilla/firefox/profiles.ini"
|
||||
|
||||
# Create a certificate database
|
||||
runuser -u "$username" -- certutil -d "sql:$userhome/.mozilla/firefox/main/" -N --empty-password
|
||||
|
||||
# Import the client certificate
|
||||
runuser -u "$username" -- pk12util -d "sql:$userhome/.mozilla/firefox/main/" -i "$certificate" -K "" -W ""
|
||||
|
||||
# Do the same for the NSS shared certificate database, used by Chromium
|
||||
rm -rf "$userhome/.pki/"
|
||||
runuser -u "$username" -- mkdir -p "$userhome/.pki/nssdb"
|
||||
runuser -u "$username" -- certutil -d "sql:$userhome/.pki/nssdb/" -N --empty-password
|
||||
runuser -u "$username" -- pk12util -d "sql:$userhome/.pki/nssdb/" -i "$certificate" -K "" -W ""
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
||||
<TITLE>Bookmarks</TITLE>
|
||||
<H1>Bookmarks</H1>
|
||||
<DL><p>
|
||||
<DT><H3 PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Bar</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://finals.soi.ch/" ICON="data:image/png;base64,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">Grader</A>
|
||||
</DL><p>
|
||||
</DL><p>
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
// Portions of this file are taken from GNOME Shell and adapted.
|
||||
// Because of that, this gnome extension is distributed under
|
||||
// the terms of the GNU General Public License, version 2.
|
||||
|
||||
|
||||
const {
|
||||
AccountsService, Atk, Clutter, Gio,
|
||||
GLib, Graphene, Meta, Shell, St,
|
||||
} = imports.gi;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
// half of the time for which a frame is displayed
|
||||
const HALF_FRAME_TIME_MS = 8;
|
||||
|
||||
const BLUR_BRIGHTNESS = 0.55;
|
||||
const BLUR_SIGMA = 60;
|
||||
|
||||
const POINTER_HIDE_TIMEOUT = 10 * GLib.USEC_PER_SEC;
|
||||
|
||||
let actor;
|
||||
let lockDialog;
|
||||
let labelCountdown;
|
||||
let labelTitle;
|
||||
let labelMessage;
|
||||
let labelUser;
|
||||
|
||||
const bgManagers = [];
|
||||
let backgroundGroup;
|
||||
|
||||
let cursorTracker;
|
||||
let motionId = 0;
|
||||
let lastMotionTime = 0;
|
||||
let pointerHidden = false;
|
||||
let pointerHideId = 0;
|
||||
|
||||
let user;
|
||||
|
||||
let grab;
|
||||
let countdownTimeoutId = 0;
|
||||
|
||||
let configFile;
|
||||
let configMonitor;
|
||||
let config;
|
||||
let startTime;
|
||||
|
||||
let isExtensionEnabled = false;
|
||||
let isActive = false;
|
||||
let isShellReady = false;
|
||||
let isActiveChanging = false;
|
||||
|
||||
function extLog (msg) {
|
||||
log(`[contest-lock] ${msg}`)
|
||||
}
|
||||
|
||||
function extLogError (msg) {
|
||||
printerr(`[contest-lock] Error: ${msg}`);
|
||||
}
|
||||
|
||||
function loadConfig () {
|
||||
configFile.load_contents_async(null, (obj, res) => {
|
||||
// If there is a poblem with the config file, log an error and keep
|
||||
// using the old config.
|
||||
let newConfig;
|
||||
try {
|
||||
const [ok, bytes] = configFile.load_contents_finish(res);
|
||||
// TextDecoder is used in upstream gnome-shell, but not yet
|
||||
// supported in current Debian.
|
||||
const contentStr = imports.byteArray.toString(bytes);
|
||||
//const contentStr = new TextDecoder().decode(bytes);
|
||||
newConfig = JSON.parse(contentStr);
|
||||
} catch (err) {
|
||||
logError(err, '[contest-lock] config file');
|
||||
return;
|
||||
}
|
||||
if (!(typeof newConfig === 'object' && newConfig != null)) {
|
||||
extLogError('config file: invalid format');
|
||||
return;
|
||||
}
|
||||
if (typeof newConfig.title !== 'string') {
|
||||
extLogError('config file: "title" must be a string');
|
||||
return;
|
||||
}
|
||||
if (typeof newConfig.message !== 'string') {
|
||||
extLogError('config file: "message" must be a string');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
typeof newConfig.startTime !== 'string' ||
|
||||
!/^\d{4,}-\d\d-\d\dT\d\d:\d\d:\d\d\+\d\d:\d\d$/.test(newConfig.startTime)
|
||||
) {
|
||||
extLogError('config file: "startTime" must be a string with format 0000-00-00T00:00:00+00:00');
|
||||
return;
|
||||
}
|
||||
|
||||
extLog('Loaded new config.')
|
||||
config = newConfig;
|
||||
startTime = (new Date(newConfig.startTime)).getTime();
|
||||
|
||||
updateConfig();
|
||||
syncActive();
|
||||
});
|
||||
}
|
||||
|
||||
function syncActive () {
|
||||
if (isActiveChanging) return;
|
||||
let beforeStart = false;
|
||||
if (startTime != null) {
|
||||
const now = new Date();
|
||||
const timeToStart = startTime - now.getTime() - HALF_FRAME_TIME_MS;
|
||||
beforeStart = timeToStart > 0;
|
||||
}
|
||||
// ignore disable event when active
|
||||
if (beforeStart && isShellReady && (isExtensionEnabled || isActive)) {
|
||||
activate();
|
||||
} else {
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
function updateConfig () {
|
||||
if (labelTitle != null) {
|
||||
labelTitle.text = config.title;
|
||||
}
|
||||
if (labelMessage != null) {
|
||||
labelMessage.text = config.message;
|
||||
}
|
||||
}
|
||||
|
||||
function updateUser () {
|
||||
if (labelUser != null) {
|
||||
const realName = user.get_real_name();
|
||||
if (realName != null) labelUser.text = realName;
|
||||
}
|
||||
}
|
||||
|
||||
function updateCountdown () {
|
||||
countdownTimeoutId = 0;
|
||||
const now = new Date();
|
||||
const nowTime = now.getTime() + HALF_FRAME_TIME_MS;
|
||||
const timeToStart = startTime - nowTime;
|
||||
const beforeStart = timeToStart > 0;
|
||||
if (!beforeStart) {
|
||||
deactivate();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
const allSecondsToStart = Math.floor(timeToStart / 1000);
|
||||
const secondsToStart = allSecondsToStart % 60
|
||||
const allMinutesToStart = Math.floor(allSecondsToStart / 60);
|
||||
const minutesToStart = allMinutesToStart % 60;
|
||||
const hoursToStart = Math.floor(allMinutesToStart / 60);
|
||||
|
||||
let hoursString = '';
|
||||
if (hoursToStart !== 0) hoursString = `${hoursToStart}∶`;
|
||||
labelCountdown.text = hoursString +
|
||||
minutesToStart.toString().padStart(2, '0') + '∶' +
|
||||
secondsToStart.toString().padStart(2, '0');
|
||||
|
||||
const nextUpdateTime = 1000 - nowTime % 1000
|
||||
countdownTimeoutId = GLib.timeout_add(
|
||||
GLib.PRIORITY_HIGH,
|
||||
nextUpdateTime,
|
||||
updateCountdown
|
||||
);
|
||||
GLib.Source.set_name_by_id(countdownTimeoutId, '[contest-lock] updateCountdown');
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
function updateBackgrounds () {
|
||||
if (!isActive) return;
|
||||
while (bgManagers.length) bgManagers.pop().destroy();
|
||||
backgroundGroup.destroy_all_children();
|
||||
|
||||
for (let monitorIndex = 0; monitorIndex < Main.layoutManager.monitors.length; monitorIndex++) {
|
||||
const monitor = Main.layoutManager.monitors[monitorIndex];
|
||||
const widget = new St.Widget({
|
||||
style_class: 'screen-shield-background',
|
||||
x: monitor.x,
|
||||
y: monitor.y,
|
||||
width: monitor.width,
|
||||
height: monitor.height,
|
||||
effect: new Shell.BlurEffect({
|
||||
name: 'blur',
|
||||
brightness: BLUR_BRIGHTNESS,
|
||||
sigma: BLUR_SIGMA,
|
||||
}),
|
||||
});
|
||||
|
||||
const bgManager = new Background.BackgroundManager({
|
||||
container: widget,
|
||||
monitorIndex,
|
||||
controlPosition: false,
|
||||
});
|
||||
|
||||
bgManagers.push(bgManager);
|
||||
|
||||
backgroundGroup.add_child(widget);
|
||||
}
|
||||
}
|
||||
|
||||
function pointerHideTimer () {
|
||||
if (pointerHideId !== 0) {
|
||||
GLib.source_remove(pointerHideId);
|
||||
pointerHideId = 0;
|
||||
}
|
||||
if (!isActive) return GLib.SOURCE_REMOVE;
|
||||
|
||||
const timeToHide = lastMotionTime + POINTER_HIDE_TIMEOUT - GLib.get_monotonic_time();
|
||||
if (timeToHide <= 0) {
|
||||
cursorTracker.set_pointer_visible(false);
|
||||
pointerHidden = true;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
pointerHideId = GLib.timeout_add(
|
||||
GLib.PRIORITY_HIGH,
|
||||
timeToHide / 1000 + 20,
|
||||
pointerHideTimer
|
||||
);
|
||||
GLib.Source.set_name_by_id(pointerHideId, '[contest-lock] pointerHide');
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
function activate () {
|
||||
if (isActive) return;
|
||||
isActiveChanging = true;
|
||||
isActive = true;
|
||||
|
||||
grab = Main.pushModal(Main.uiGroup, { actionMode: Shell.ActionMode.LOCK_SCREEN });
|
||||
if (typeof grab === 'boolean') { // gnome 38
|
||||
if (!grab) {
|
||||
grab = Main.pushModal(Main.uiGroup, {
|
||||
options: Meta.ModalOptions.POINTER_ALREADY_GRABBED,
|
||||
actionMode: Shell.ActionMode.LOCK_SCREEN
|
||||
});
|
||||
}
|
||||
if (!grab) {
|
||||
extLogError('Failed to activate: Could not obtain keyboard grab.');
|
||||
return;
|
||||
}
|
||||
grab = Main.uiGroup;
|
||||
} else if ((grab.get_seat_state() & Clutter.GrabState.KEYBOARD) === 0) {
|
||||
Main.popModal(grab);
|
||||
grab = null;
|
||||
extLogError('Failed to activate: Could not obtain keyboard grab.');
|
||||
return;
|
||||
}
|
||||
|
||||
actor.show();
|
||||
|
||||
Main.sessionMode.pushMode('unlock-dialog');
|
||||
|
||||
backgroundGroup = new Clutter.Actor();
|
||||
|
||||
motionId = global.stage.connect('captured-event', (stage, event) => {
|
||||
if (event.type() === Clutter.EventType.MOTION) {
|
||||
lastMotionTime = GLib.get_monotonic_time();
|
||||
if (pointerHidden) {
|
||||
cursorTracker.set_pointer_visible(true);
|
||||
pointerHidden = false;
|
||||
pointerHideTimer();
|
||||
}
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
});
|
||||
cursorTracker.set_pointer_visible(false);
|
||||
pointerHidden = true;
|
||||
|
||||
labelCountdown = new St.Label({
|
||||
style_class: 'contest-lock-countdown',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
labelTitle = new St.Label({
|
||||
style_class: 'contest-lock-title',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
labelMessage = new St.Label({
|
||||
style_class: 'contest-lock-message',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
labelUser = new St.Label({
|
||||
style_class: 'contest-lock-user',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
|
||||
const stack = new St.BoxLayout({
|
||||
style_class: 'contest-lock-stack',
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
})
|
||||
stack.add_child(labelUser);
|
||||
stack.add_child(labelCountdown);
|
||||
stack.add_child(labelTitle);
|
||||
stack.add_child(labelMessage);
|
||||
|
||||
const mainBox = new St.BoxLayout();
|
||||
mainBox.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
mainBox.add_child(stack);
|
||||
|
||||
lockDialog = new St.Widget({
|
||||
name: 'contestLockDialog',
|
||||
accessible_role: Atk.Role.WINDOW,
|
||||
visible: false,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
|
||||
});
|
||||
lockDialog.add_child(backgroundGroup);
|
||||
lockDialog.add_child(mainBox);
|
||||
|
||||
updateConfig();
|
||||
updateUser();
|
||||
updateCountdown();
|
||||
updateBackgrounds();
|
||||
|
||||
// countdown may have just expired before we called updateCountdown
|
||||
if (!isActive) return;
|
||||
|
||||
actor.add_child(lockDialog);
|
||||
lockDialog.show();
|
||||
|
||||
extLog('Activated.')
|
||||
isActiveChanging = false;
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
if (!isActive) return;
|
||||
isActiveChanging = true;
|
||||
isActive = false;
|
||||
|
||||
if (Main.sessionMode.currentMode === 'unlock-dialog') {
|
||||
Main.sessionMode.popMode('unlock-dialog');
|
||||
}
|
||||
|
||||
Main.popModal(grab);
|
||||
grab = null;
|
||||
|
||||
if (countdownTimeoutId !== 0) {
|
||||
GLib.source_remove(countdownTimeoutId);
|
||||
countdownTimeoutId = 0;
|
||||
}
|
||||
|
||||
actor.hide();
|
||||
labelCountdown = null;
|
||||
labelTitle = null;
|
||||
labelMessage = null;
|
||||
labelUser = null;
|
||||
while (bgManagers.length) bgManagers.pop().destroy();
|
||||
lockDialog.destroy();
|
||||
lockDialog = null;
|
||||
|
||||
if (motionId) {
|
||||
global.stage.disconnect(motionId);
|
||||
motionId = 0;
|
||||
}
|
||||
cursorTracker.set_pointer_visible(true);
|
||||
|
||||
extLog('Deactivated.');
|
||||
isActiveChanging = false;
|
||||
}
|
||||
|
||||
function init (extension) {
|
||||
actor = Main.layoutManager.screenShieldGroup;
|
||||
|
||||
const userName = GLib.get_user_name();
|
||||
user = AccountsService.UserManager.get_default().get_user(userName);
|
||||
if (!user) return;
|
||||
|
||||
user.connect('notify::is-loaded', updateUser);
|
||||
user.connect('changed', updateUser);
|
||||
updateUser();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', updateBackgrounds);
|
||||
|
||||
cursorTracker = Meta.CursorTracker.get_for_display(global.display);
|
||||
|
||||
if (!Main.layoutManager._startingUp) {
|
||||
isShellReady = true;
|
||||
} else {
|
||||
Main.layoutManager.connect('startup-complete', () => {
|
||||
isShellReady = true;
|
||||
syncActive();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: When we drop compatibility with gnome <42, remove this code,
|
||||
// rename the stylesheet back to stylesheet.css (so that it is loaded
|
||||
// by the extension system) and add a session-modes property which
|
||||
// includes unlock-dialog to metadata.json.
|
||||
const theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
const stylesheetFile = extension.dir.get_child('stylesheet-always.css');
|
||||
theme.load_stylesheet(stylesheetFile);
|
||||
|
||||
// TODO: When we drop compatibility with gnome <42, remove this code.
|
||||
// gnome 38 has a bug that causes extensions to break when running
|
||||
// `dconf update` while the screen is locked.
|
||||
Main.extensionManager.reloadExtension = function () {};
|
||||
|
||||
configFile = Gio.File.new_for_path('/etc/contest-lock.json');
|
||||
configMonitor = configFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
configMonitor.set_rate_limit(1000);
|
||||
configMonitor.connect('changed', loadConfig);
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
function enable () {
|
||||
isExtensionEnabled = true;
|
||||
syncActive();
|
||||
}
|
||||
|
||||
function disable () {
|
||||
isExtensionEnabled = false;
|
||||
syncActive();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extension-id": "contest-lock",
|
||||
"uuid": "contest-lock@soi.ch",
|
||||
"name": "Contest lock screen",
|
||||
"description": "A custom lock screen for contests.",
|
||||
"shell-version": [ "3.38", "42" ],
|
||||
"url": ""
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
.contest-lock-stack {
|
||||
color: white;
|
||||
text-align: center;
|
||||
spacing: 24px;
|
||||
}
|
||||
|
||||
.contest-lock-countdown {
|
||||
font-size: 64pt;
|
||||
font-weight: 300;
|
||||
font-feature-settings: "tnum"; /* tabular figures */
|
||||
}
|
||||
|
||||
.contest-lock-title {
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
.contest-lock-user {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.contest-lock-message {
|
||||
padding-top: 24px;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
const { St, Clutter, GLib, Gio, AccountsService } = imports.gi;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
let panelBin;
|
||||
let userLabel;
|
||||
|
||||
let user;
|
||||
|
||||
function updateUser () {
|
||||
const realName = user.get_real_name();
|
||||
if (realName != null) userLabel.text = realName;
|
||||
}
|
||||
|
||||
function init () {
|
||||
panelBin = new St.Bin({
|
||||
style_class: 'panel-bin',
|
||||
});
|
||||
userLabel = new St.Label({
|
||||
text: 'No user',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
panelBin.set_child(userLabel);
|
||||
|
||||
const userName = GLib.get_user_name();
|
||||
user = AccountsService.UserManager.get_default().get_user(userName);
|
||||
if (!user) return;
|
||||
|
||||
user.connect('notify::is-loaded', updateUser);
|
||||
user.connect('changed', updateUser);
|
||||
updateUser();
|
||||
}
|
||||
|
||||
function enable () {
|
||||
Main.panel._rightBox.insert_child_at_index(panelBin, 0);
|
||||
}
|
||||
|
||||
function disable () {
|
||||
Main.panel._rightBox.remove_child(panelBin);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extension-id": "user-indicator",
|
||||
"uuid": "user-indicator@soi.ch",
|
||||
"name": "User indicator",
|
||||
"description": "Shows the user's real name in the top bar.",
|
||||
"shell-version": [ "3.38", "42" ],
|
||||
"url": ""
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.panel-bin {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px; }
|
||||
|
|
@ -0,0 +1 @@
|
|||
deb [arch=amd64 signed-by=/usr/share/keyrings/atom-archive-keyring.gpg] http://packagecloud.io/AtomEditor/atom/any/ any main
|
||||
|
|
@ -0,0 +1 @@
|
|||
deb [arch=amd64 signed-by=/usr/share/keyrings/sublimehq-archive-keyring.gpg] http://download.sublimetext.com/ apt/stable/
|
||||
|
|
@ -0,0 +1 @@
|
|||
deb [arch=amd64,arm64,armhf signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] http://packages.microsoft.com/repos/code stable main
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
[org/gnome/desktop/background]
|
||||
picture-uri = 'file:///usr/local/share/backgrounds/wallpaper-soi.svg'
|
||||
picture-options = 'scaled'
|
||||
primary-color = '#e6e6e6'
|
||||
secondary-color = '#555555'
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
[org/gnome/shell]
|
||||
favorite-apps = ['firefox-esr.desktop', 'gnome-terminal.desktop', 'nautilus.desktop']
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Configure the default keyboard layouts shown in the switcher
|
||||
# According to Wikipedia, the Italian-speaking part of Switzerland also uses the ch+fr layout.
|
||||
# The first option is the default.
|
||||
[org/gnome/desktop/input-sources]
|
||||
sources = [('xkb', 'ch'), ('xkb', 'ch+fr'), ('xkb', 'us')]
|
||||
2
config/simplefiles/PARTICIPANT/etc/dconf/profile/user
Normal file
2
config/simplefiles/PARTICIPANT/etc/dconf/profile/user
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
user-db:user
|
||||
system-db:local
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
PasswordAuthentication no
|
||||
AllowUsers root
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="-20 -20 153.6 86.4" width="1920" height="1080" version="1.1" id="svg22" sodipodi:docname="wallpaper.svg" inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<metadata id="metadata28">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title>Swiss Olympiad in Informatics</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs id="defs26"/>
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1669" inkscape:window-height="913" id="namedview24" showgrid="false" inkscape:zoom="0.472" inkscape:cx="811.36486" inkscape:cy="502.72794" inkscape:window-x="67" inkscape:window-y="27" inkscape:window-maximized="0" inkscape:current-layer="svg22"/>
|
||||
<title id="title2">Swiss Olympiad in Informatics</title>
|
||||
<rect style="fill:#e6e6e6;fill-opacity:1;stroke:#f9fdff;stroke-width:0.60472441;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect837" width="160" height="97.627121" x="-23.050846" y="-24.955931"/>
|
||||
<circle r="18.903814" id="circle4" cx="57.553818" cy="24.70454" style="fill:none;stroke:#303030;stroke-width:3.43705702"/>
|
||||
<path d="M 68.019104,24.704538 57.553815,14.239249 75.801426,3.0788495 M 47.088526,24.704538 H 68.019104 L 57.553815,35.169826 47.088526,24.704538 37.798573,43.321151" id="path6" inkscape:connector-curvature="0" style="fill:none;stroke:#1eadf5;stroke-width:2.74964571"/>
|
||||
<g id="g20" style="fill:#1862ff" transform="matrix(1.3748228,0,0,1.3748228,57.553815,24.704538)">
|
||||
<circle r="3" cy="7.6121001" id="circle8" cx="0"/>
|
||||
<circle r="3" cx="7.6121001" id="circle10" cy="0"/>
|
||||
<circle r="3" cy="-7.6121001" id="circle12" cx="0"/>
|
||||
<circle r="3" cx="-7.6121001" id="circle14" cy="0"/>
|
||||
<circle r="3" cx="13.2727" cy="-15.7298" id="circle16"/>
|
||||
<circle r="3" cx="-14.3693" cy="13.5411" id="circle18"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in a new issue