gnome: Add app to provide a graphical desktop environment

- This app is useful for people who can connect a monitor, a keyboard, and a
mouse to their FreedomBox.

- Later this app allow a headless FreedomBox to be used as a remote desktop
server. Users will be able to connect and access desktop applications from LAN
or WAN.

- No functional tests as they will likely fail in CI and container setups.

Tests:

- In a VM, install the app. Installation succeeds.

- Disabling the app makes systemd switch to multi-user.target shutting down any
service that any mean for GUI such as gnome-remote-desktop.service. Graphical
login is not shown on the VM's virtual monitor.

- Enabling the app make systemd switch to graphical.target and all the services
are started again. Graphical login is shown on the VM's virtual monitor.

- Login to desktop using VM's graphical terminal works. Settings, apps, browser
etc. work.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
Sunil Mohan Adapa 2025-01-12 20:49:23 -08:00 committed by Veiko Aasa
parent dbb4391b61
commit 837ed09dc3
No known key found for this signature in database
GPG Key ID: 478539CAE680674E
7 changed files with 667 additions and 0 deletions

2
debian/copyright vendored
View File

@ -64,6 +64,8 @@ Files: plinth/modules/ejabberd/static/icons/ejabberd.png
plinth/modules/ejabberd/static/icons/ejabberd.svg
plinth/modules/email/static/icons/email.png
plinth/modules/email/static/icons/email.svg
plinth/modules/email/static/icons/gnome.png
plinth/modules/email/static/icons/gnome.svg
plinth/modules/matrixsynapse/static/icons/matrixsynapse.svg
plinth/modules/privoxy/static/icons/privoxy.png
plinth/modules/privoxy/static/icons/privoxy.svg

View File

@ -0,0 +1,90 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""FreedomBox app to configure GNOME desktop."""
from django.utils.translation import gettext_lazy as _
from plinth import action_utils
from plinth import app as app_module
from plinth import cfg, menu
from plinth.modules.backups.components import BackupRestore
from plinth.package import Packages
from plinth.privileged import service as service_privileged
from plinth.utils import format_lazy
from . import manifest
_description = [
_('GNOME is a desktop environment that focuses on simplicity and ease of '
'use.'),
format_lazy(
_('This app turns your {box_name} into a desktop computer if you '
'physically connect a monitor, a keyboard, and a mouse to it. A '
'browser, an office suite, and other basic utilities are available. '
'You may install further graphical applications using the software '
'center provided within.'), box_name=_(cfg.box_name)),
_('This app is not suitable for low-end hardware. It requires at least '
'4GiB for RAM, 4GiB or disk space and a GPU capable of basic 3D '
'acceleration.'),
]
class GNOMEApp(app_module.App):
"""FreedomBox app for GNOME desktop."""
app_id = 'gnome'
_version = 1
def __init__(self) -> None:
"""Create components for the app."""
super().__init__()
info = app_module.Info(app_id=self.app_id, version=self._version,
name=_('GNOME'), icon_filename='gnome',
description=_description, manual_page='GNOME',
donation_url='https://www.gnome.org/donate/',
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-gnome', info.name, info.icon_filename,
info.tags, 'gnome:index', parent_url_name='apps')
self.add(menu_item)
packages = Packages('packages-gnome', ['gnome'])
self.add(packages)
system_target = SystemTarget('system-target-gnome', 'graphical.target')
self.add(system_target)
backup_restore = BackupRestore('backup-restore-gnome',
**manifest.backup)
self.add(backup_restore)
def setup(self, old_version):
"""Install and configure the app."""
super().setup(old_version)
if not old_version:
self.enable()
class SystemTarget(app_module.LeaderComponent):
"""Component to set the default target systemd will boot into."""
_DEFAULT_TARGET: str = 'multi-user.target'
def __init__(self, component_id: str, target: str):
"""Initialize the component."""
super().__init__(component_id)
self.target = target
def is_enabled(self) -> bool:
"""Return whether the component is enabled."""
return action_utils.systemd_get_default() == self.target
def enable(self) -> None:
"""Run operations to enable the component."""
service_privileged.systemd_set_default(self.target)
def disable(self) -> None:
"""Run operations to disable the component."""
service_privileged.systemd_set_default(self._DEFAULT_TARGET)

View File

@ -0,0 +1 @@
plinth.modules.gnome

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Application manifest for GNOME."""
from django.utils.translation import gettext_lazy as _
backup = {}
tags = [
_('Desktop'),
_('Browser'),
_('Office suite'),
_('Software store'),
_('GUI'),
_('Graphical apps')
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,549 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
version="1.1"
id="svg140"
sodipodi:docname="gnome.svg"
inkscape:export-filename="gnome.png"
inkscape:export-xdpi="48"
inkscape:export-ydpi="48"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview142"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.96950969"
inkscape:cx="68.591372"
inkscape:cy="440.42881"
inkscape:window-width="3840"
inkscape:window-height="2091"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg140" />
<defs
id="defs84">
<linearGradient
id="k">
<stop
offset="0"
stop-color="#d8dfd6"
id="stop2" />
<stop
offset="1"
stop-color="#d8dfd6"
stop-opacity="0"
id="stop4" />
</linearGradient>
<linearGradient
id="i">
<stop
offset="0"
stop-color="#9d9d9d"
id="stop7" />
<stop
offset="1"
stop-color="#b9b9b9"
id="stop9" />
</linearGradient>
<linearGradient
id="g">
<stop
offset="0"
stop-color="#909090"
id="stop12" />
<stop
offset="1"
stop-color="#bebebe"
stop-opacity="0"
id="stop14" />
</linearGradient>
<linearGradient
id="f">
<stop
offset="0"
stop-color="#585956"
id="stop17" />
<stop
offset="1"
stop-color="#bbbeb8"
id="stop19" />
</linearGradient>
<linearGradient
id="e">
<stop
offset="0"
id="stop22" />
<stop
offset="1"
stop-opacity="0"
id="stop24" />
</linearGradient>
<linearGradient
id="d">
<stop
offset="0"
stop-color="#5b5b97"
id="stop27" />
<stop
offset="1"
stop-color="#1b1b43"
id="stop29" />
</linearGradient>
<linearGradient
id="c">
<stop
offset="0"
stop-color="#fff"
id="stop32" />
<stop
offset="1"
stop-color="#fcfcff"
stop-opacity="0"
id="stop34" />
</linearGradient>
<linearGradient
id="h">
<stop
offset="0"
stop-color="#f9fff5"
id="stop37" />
<stop
offset="1"
stop-color="#f9fff5"
stop-opacity="0"
id="stop39" />
</linearGradient>
<linearGradient
id="l">
<stop
offset="0"
id="stop42" />
<stop
offset="1"
stop-opacity="0"
id="stop44" />
</linearGradient>
<linearGradient
id="b">
<stop
offset="0"
stop-color="#fff"
id="stop47" />
<stop
offset="1"
stop-color="#fff"
stop-opacity="0"
id="stop49" />
</linearGradient>
<linearGradient
id="j">
<stop
offset="0"
stop-color="#8f8f8f"
id="stop52" />
<stop
offset="1"
stop-color="#494949"
id="stop54" />
</linearGradient>
<linearGradient
id="a">
<stop
offset="0"
stop-color="#dde1d9"
id="stop57" />
<stop
offset="1"
stop-color="#cacdc6"
id="stop59" />
</linearGradient>
<linearGradient
xlink:href="#a"
id="q"
x1="8.6116238"
x2="34.784473"
y1="7.2293582"
y2="33.339787"
gradientTransform="matrix(12.591195,0,0,9.8631362,-5.8847298,-27.860012)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#b"
id="u"
x1="17.698339"
x2="34.974548"
y1="13.004725"
y2="55.200756"
gradientTransform="matrix(12.348367,0,0,10.057154,23.370366,-2.1800188)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#c"
id="v"
x1="11.492236"
x2="17.199417"
y1="1.6537577"
y2="26.729263"
gradientTransform="matrix(13.807232,0,0,9.9844946,34.28426,-28.162594)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#d"
id="s"
x1="19.150396"
x2="16.315819"
y1="32.622238"
y2="8.8666229"
gradientTransform="matrix(13.084653,0,0,10.535901,30.091445,-28.162594)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#e"
id="t"
x1="3.7069976"
x2="3.7069974"
y1="171.29134"
y2="162.45061"
gradientTransform="matrix(63.57848,0,0,1.9533258,23.182557,-20.895004)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#f"
id="p"
x1="12.206709"
x2="12.127711"
y1="53.535141"
y2="64.892525"
gradientTransform="matrix(20.241462,0,0,6.1354577,10.917863,-13.32405)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#g"
id="D"
x1="34.300991"
x2="35.520542"
y1="3.9384086"
y2="3.8451097"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.83538,0,0,7.3377873,-259.98661,18.479522)" />
<linearGradient
xlink:href="#g"
id="E"
x1="34.300991"
x2="35.520542"
y1="3.9384086"
y2="3.8451097"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.83538,0,0,7.3377873,-259.9866,41.946455)" />
<linearGradient
xlink:href="#g"
id="F"
x1="34.300991"
x2="35.520542"
y1="3.9384086"
y2="3.8451097"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.83538,0,0,7.3377873,-259.9866,64.234517)" />
<linearGradient
xlink:href="#g"
id="G"
x1="34.300991"
x2="35.520542"
y1="3.9384086"
y2="3.8451097"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.83538,0,0,7.3377873,-259.98661,86.52258)" />
<linearGradient
xlink:href="#g"
id="H"
x1="34.300991"
x2="35.520542"
y1="3.9384086"
y2="3.8451097"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14.83538,0,0,7.3377873,-259.98662,108.81063)" />
<linearGradient
xlink:href="#h"
id="C"
x1="13.62871"
x2="8.6485014"
y1="101.2846"
y2="74.098007"
gradientTransform="matrix(23.88868,0,0,5.1986904,22.418008,-18.994423)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#i"
id="z"
x1="8.1134243"
x2="8.1134233"
y1="88.509071"
y2="100.20015"
gradientTransform="matrix(25.74104,0,0,4.8245853,-142.74252,-13.32405)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#j"
id="r"
x1="10.390738"
x2="32.536823"
y1="5.3817744"
y2="31.246054"
gradientTransform="matrix(12.307468,0,0,10.090586,12.22432,-13.32405)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#a"
id="x"
x1="18.316999"
x2="18.176752"
y1="48.643234"
y2="52.536461"
gradientTransform="matrix(12.591195,0,0,9.8631362,11.239333,-13.32405)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#k"
id="n"
x1="-23.8857"
x2="-23.8857"
y1="49.953003"
y2="44.878883"
gradientTransform="matrix(15.642886,0,0,7.018955,629.64278,25.916246)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#i"
id="B"
x1="8.1134243"
x2="8.1134233"
y1="88.509071"
y2="100.20015"
gradientTransform="matrix(25.74104,0,0,4.8245853,55.242987,-13.32405)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#i"
id="A"
x1="8.1134243"
x2="8.1134233"
y1="88.509071"
y2="100.20015"
gradientTransform="matrix(25.74104,0,0,4.8245853,50.810482,-13.32405)"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#j"
id="y"
x1="10.390738"
x2="32.536823"
y1="5.3817744"
y2="31.246054"
gradientTransform="matrix(12.307468,0,0,10.090586,11.239333,-13.32405)"
gradientUnits="userSpaceOnUse" />
<radialGradient
xlink:href="#l"
id="m"
cx="12.57571"
cy="67.501709"
r="8.7662792"
fx="12.57571"
fy="67.501709"
gradientTransform="matrix(21.461174,0,0,7.9215182,244.5075,-162.63215)"
gradientUnits="userSpaceOnUse" />
<radialGradient
xlink:href="#l"
id="o"
cx="12.57571"
cy="67.501709"
r="8.7662792"
fx="12.57571"
fy="67.501709"
gradientTransform="matrix(21.461174,0,0,7.9215182,-13.890674,-225.29756)"
gradientUnits="userSpaceOnUse" />
<radialGradient
xlink:href="#l"
id="w"
cx="12.57571"
cy="67.501709"
r="8.7662792"
fx="12.57571"
fy="67.501709"
gradientTransform="matrix(27.135509,0,0,7.4720691,-85.249767,-57.904888)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5716"
id="linearGradient32682"
gradientUnits="userSpaceOnUse"
x1="29.089951"
y1="11.772627"
x2="33.971455"
y2="9.7093649"
gradientTransform="matrix(6.372617,0,0,6.372617,57.029062,91.84993)" />
<linearGradient
id="linearGradient5716">
<stop
style="stop-color:#727272;stop-opacity:1;"
offset="0"
id="stop5718" />
<stop
style="stop-color:#484848;stop-opacity:1;"
offset="1"
id="stop5720" />
</linearGradient>
</defs>
<path
fill="#adb0aa"
fill-rule="evenodd"
stroke="#4b4d4a"
d="m 360.69169,353.53372 a 104.69169,43.902974 0 1 1 -209.38339,0 104.6917,43.902972 0 1 1 209.38339,0 z"
color="#000000"
overflow="visible"
style="stroke-width:11.144;marker:none"
id="path88" />
<path
fill="none"
stroke="#7b7f7a"
d="m 354.43845,345.65284 a 98.438459,41.280646 0 1 1 -196.87691,0 98.438459,41.280646 0 1 1 196.87691,0 z"
color="#000000"
overflow="visible"
style="stroke-width:10.4784;marker:none"
id="path90" />
<path
fill="none"
stroke="url(#n)"
stroke-width="7.13216"
d="m 354.43845,337.29482 a 98.438459,41.280646 0 1 1 -196.87691,0 98.438459,41.280646 0 1 1 196.87691,0 z"
color="#000000"
overflow="visible"
style="stroke:url(#n);marker:none"
id="path92" />
<path
fill="#d0d0d0"
fill-rule="evenodd"
stroke="#979797"
stroke-linejoin="round"
stroke-width="4.45761"
d="m 137.35944,307.24786 -0.6965,11.14402 c 0,0 48.18799,40.10923 100.29629,46.83289 26.05413,3.36181 52.97509,7.82769 75.2222,13.23353 22.2471,5.40583 39.98875,12.47692 45.96913,18.4573 3.45923,3.45923 5.02666,6.39264 5.57202,8.70627 0.54538,2.31361 0.42593,3.95406 -1.04476,6.26852 -2.94127,4.62889 -12.03397,10.78355 -27.51182,15.6713 -30.95573,9.77548 -86.03665,16.01954 -162.28496,16.01954 v 11.14403 c 76.84921,0 132.20905,-5.88038 165.41922,-16.3678 16.60506,-5.2437 28.13802,-11.31885 33.78034,-20.19855 2.82115,-4.43986 3.98199,-9.90091 2.78601,-14.9748 -1.19599,-5.07389 -4.2855,-9.85751 -8.70629,-14.27828 -9.77966,-9.77968 -28.27059,-15.67342 -51.19288,-21.24332 -22.9223,-5.5699 -50.3235,-10.18931 -76.61521,-13.58178 -52.58341,-6.78496 -100.99279,-46.83287 -100.99279,-46.83287 z"
id="path94" />
<path
fill="url(#q)"
fill-rule="evenodd"
stroke="url(#r)"
d="M 91.134117,5.5720155 H 420.86588 c 10.15603,0 18.09954,6.5023395 18.57433,15.6193295 l 14.84434,285.043295 c 0.6487,12.45607 -10.04139,22.51435 -22.51434,22.51435 H 80.22979 c -12.472947,0 -23.163025,-10.05828 -22.514344,-22.51435 L 72.559783,21.191345 C 73.00871,12.571051 78.66117,5.5720155 91.134117,5.5720155 Z"
color="#000000"
overflow="visible"
style="fill:url(#q);stroke:url(#r);stroke-width:11.144;marker:none"
id="path100" />
<path
fill="url(#s)"
fill-rule="evenodd"
stroke="#000079"
stroke-linejoin="round"
stroke-width="5.57202"
d="M 115.77466,34.660223 101.93161,280.33393 H 410.0684 L 395.19094,35.701393 Z"
id="path102"
style="fill:url(#s)" />
<path
fill="none"
stroke="url(#t)"
stroke-linecap="round"
stroke-opacity="0.248408"
stroke-width="11.1015"
d="M 80.880032,306.90848 H 431.11997"
id="path104"
style="stroke:url(#t)" />
<path
fill="none"
stroke="url(#u)"
stroke-opacity="0.700637"
stroke-width="11.144"
d="M 89.282836,16.835483 421.88205,16.414224 c 3.16154,-0.004 6.23288,2.64392 6.487,7.271968 l 15.17696,276.408168 c 0.6468,11.77959 -6.01498,19.9018 -17.81229,19.9018 H 85.573654 c -11.797326,0 -17.752602,-8.12167 -17.115799,-19.9018 L 83.21356,27.130602 c 0.431228,-7.977238 1.854204,-10.28978 6.069276,-10.295119 z"
color="#000000"
overflow="visible"
style="stroke:url(#u);marker:none"
id="path106" />
<path
fill="url(#v)"
fill-rule="evenodd"
d="M 124.78475,39.569563 115.84134,238.71708 C 238.92314,211.96439 243.12248,121.79169 396.15866,92.209285 l -1.79945,-51.903503 z"
opacity="0.531429"
id="path108"
style="fill:url(#v);stroke-width:11.144" />
<path
fill="url(#w)"
fill-rule="evenodd"
d="m 493.87848,446.47497 a 237.8785,65.502559 0 1 1 -475.756961,0 237.8785,65.502559 0 1 1 475.756961,0 z"
color="#000000"
overflow="visible"
style="fill:url(#w);stroke-width:14.2394;marker:none"
id="path110" />
<path
fill="url(#x)"
fill-rule="evenodd"
stroke="url(#y)"
d="M 83.254112,396.97079 H 428.74589 c 12.47294,0 10.89167,3.0249 12.17182,6.82957 l 31.58938,93.88633 c 1.28012,3.80468 0.3011,6.82958 -12.17182,6.82958 H 51.664732 c -12.472947,0 -13.451955,-3.0249 -12.171817,-6.82958 l 31.58938,-93.88633 c 1.280137,-3.80468 -0.301134,-6.82957 12.171817,-6.82957 z"
color="#000000"
overflow="visible"
style="fill:url(#x);stroke:url(#y);stroke-width:11.144;marker:none"
id="path112" />
<path
fill="#7a7d77"
fill-rule="evenodd"
d="m 122.03967,419.38879 -19.70005,57.13015 h 61.07015 l 5.91002,-22.65505 H 334.8002 l 6.12564,23.12514 h 68.73454 l -18.71502,-57.60024 z"
id="path114"
style="stroke-width:11.144" />
<path
fill="#777874"
fill-rule="evenodd"
d="m 178.67731,457.80391 -4.92502,18.71505 h 164.49542 l -4.92502,-19.70005 z"
id="path116"
style="stroke-width:11.144" />
<path
fill="#777a75"
fill-rule="evenodd"
d="m 276.19255,419.38881 18.71505,56.14515 -61.07016,-0.98503 -16.74504,-54.17514 z"
color="#000000"
overflow="visible"
style="stroke-width:11.144;marker:none"
id="path118" />
<path
fill="url(#z)"
fill-rule="evenodd"
d="M 276.19255,413.47881 294.9076,469.62396 233.83744,468.63894 217.0924,414.4638 Z"
color="#000000"
overflow="visible"
style="fill:url(#z);stroke-width:11.144;marker:none"
id="path120" />
<path
fill="url(#A)"
fill-rule="evenodd"
d="m 122.03967,412.49381 -19.70005,57.13015 h 61.07015 l 5.91002,-22.65506 H 334.8002 l 6.12564,23.12514 h 68.73454 l -18.71502,-57.60023 z"
id="path122"
style="fill:url(#A);stroke-width:11.144" />
<path
fill="url(#B)"
fill-rule="evenodd"
d="m 178.67731,451.89391 -4.92502,18.71506 h 164.49542 l -4.92502,-19.70006 z"
color="#000000"
overflow="visible"
style="fill:url(#B);stroke-width:11.144;marker:none"
id="path124" />
<path
fill="none"
stroke="url(#C)"
stroke-linejoin="round"
stroke-width="5.57202"
d="m 79.56258,405.44765 h 354.66789 l 29.38879,90.18377 H 48.380735 Z"
color="#000000"
overflow="visible"
style="stroke:url(#C);marker:none"
id="path126" />
<path
sodipodi:nodetypes="cccccccc"
id="path5565"
d="m 226.57187,106.62161 v 104.76731 l 23.65714,-23.09387 13.51835,27.59999 c 3.3112,7.46474 20.52532,1.46267 15.63062,-8.51939 l -13.37756,-28.65611 29.85305,-2.8e-4 z"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient32682);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:6.37262;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""URLs for the GNOME module."""
from django.urls import re_path
from plinth.views import AppView
urlpatterns = [
re_path(r'^apps/gnome/$', AppView.as_view(app_id='gnome'), name='index'),
]