#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Lite Welcome - GTK4 Native Version
# Copyright 2012-2013 "Korora Project" <[email protected]>
# Copyright 2013-2015 "Manjaro Linux" <[email protected]>
# Copyright 2014-2025 "Jerry Bezencon" <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import signal
import subprocess
from webbrowser import open_new_tab
# Force CPU/Cairo rendering for VM compatibility (fixes white window issues)
os.environ['GSK_RENDERER'] = 'cairo'
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Gdk, GLib, Gio, Adw, GdkPixbuf
# Try to import GdkX11 for window centering on X11 systems
try:
gi.require_version('GdkX11', '4.0')
from gi.repository import GdkX11
HAS_X11 = True
except (ValueError, ImportError):
HAS_X11 = False
ICON_NAME = "litewelcome"
# Detect Live mode by checking for ubiquity installer on desktop
IS_LIVE_MODE = os.path.exists(os.path.expanduser("~/Desktop/ubiquity.desktop"))
def load_image_from_file(filepath, width=None, height=None):
"""
Load an image from file using GdkPixbuf (CPU-based, reliable).
"""
if not os.path.exists(filepath):
print(f"File not found: {filepath}")
return None
try:
# Use GdkPixbuf for reliable CPU-based rendering
if width and height:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(filepath, width, height, True)
else:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
# Create Gtk.Image directly from pixbuf for GTK4
texture = Gdk.Texture.new_for_pixbuf(pixbuf)
image = Gtk.Image.new_from_paintable(texture)
return image
except Exception as e:
print(f"Error loading image {filepath}: {e}")
return None
def show_feedback(self, message):
"""Show toast feedback if app reference is available."""
if self.app and hasattr(self.app, 'show_toast'):
self.app.show_toast(message)
class NavigationPage(Gtk.Box):
def __init__(self, app, title=""):
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=0)
self.app = app
self.title = title
self.cmd_handler = app.cmd_handler # Use app's command handler for toast support
# Logo - use PNG for maximum compatibility (SVG has rendering issues in some VMs)
logo_path = os.path.join(self.data_path, "img", "lite-welcome.png")
if os.path.exists(logo_path):
# Use Gtk.Picture for proper image display at desired size
logo = Gtk.Picture.new_for_filename(logo_path)
logo.set_content_fit(Gtk.ContentFit.CONTAIN)
logo.set_can_shrink(True)
logo.set_size_request(291, 160) # Explicit minimum size
summary1 = Gtk.Label()
summary1.set_wrap(True)
summary1.set_justify(Gtk.Justification.CENTER)
summary1.set_markup("Linux Lite is free for everyone to use and share, and suitable for people who are new to Linux or for people who want a lightweight environment that is also fully functional.")
summary_box.append(summary1)
summary2 = Gtk.Label()
summary2.set_wrap(True)
summary2.set_justify(Gtk.Justification.CENTER)
summary2.set_markup("Linux Lite provides a basic collection of everyday tools: a web browser, an email client, a media player, an office suite, an image editor, and so on.")
summary_box.append(summary2)
content.append(summary_box)
header = self.create_header("Starting with Linux Lite")
content.append(header)
# Intro text
intro = Gtk.Label()
intro.set_markup("Once you have installed Linux Lite and before you can begin using it, it is strongly recommended that you first complete the 4 steps listed below.\n\n<b>If you are running the Live version of Linux Lite, do not attempt these steps below.</b>\n\nIf you have just finished installing Linux Lite in a language other than English, please restart your computer after these steps to activate all supported Menu translations.")
intro.set_wrap(True)
intro.set_justify(Gtk.Justification.CENTER)
content.append(intro)
# Step 1: Install Updates
step1_desc = "First you need to update your system. Click on the button below to Install Updates now.\n\nOn the window that pops up, enter the password of the user you created during the installation.\n\nYou can also Install Updates via the menu. Click on Menu, Favorites, Install Updates."
self.sections["updates"] = self.create_step_section("✓ Step 1: Install Updates", step1_desc, "Install Updates", "software-update-available-symbolic", "start_updates")
content.append(self.sections["updates"])
# Step 2: Install Drivers
step2_desc = "Now, let's see if you need any drivers installed. Click on the button below to check.\n\nYou can also Install Drivers via the menu. Click on Menu, Settings, Install Drivers."
self.sections["drivers"] = self.create_step_section("✓ Step 2: Install Drivers", step2_desc, "Install Drivers", "preferences-system-symbolic", "install_drivers")
content.append(self.sections["drivers"])
# Step 3: Restore Point
step3_desc = "Last step is to create, just like on Windows, a restore point that you can restore from in case something goes wrong. When you are ready, click on the button below.\n\nYou can also access Timeshift via the menu. Click on Menu, System, Timeshift."
self.sections["restore"] = self.create_step_section("✓ Step 3: Setting a Restore Point", step3_desc, "Set a Restore Point", "computer-symbolic", "timeshift")
content.append(self.sections["restore"])
# Step 4: Language Support
step4_desc = "Click on the button below to install Language Support for Linux Lite.\n\nYou can also install Language Support via the menu. Click on Menu, Settings, Language Support.\n\n<b>NOTE: Don't forget to restart your computer after you have finished installing additional Language Support.</b>"
self.sections["language"] = self.create_step_section("✓ Step 4: Installing Language Support", step4_desc, "Install Language Support", "preferences-desktop-locale-symbolic", "installlang")
content.append(self.sections["language"])
# UEFI and Secure Boot section
self.sections["uefi"] = self.create_uefi_section()
content.append(self.sections["uefi"])
def scroll_to_section(self, section_name):
if section_name not in self.sections:
return
self.pending_scroll = section_name
GLib.idle_add(self._perform_scroll)
def _perform_scroll(self):
if not hasattr(self, 'pending_scroll') or not self.pending_scroll:
return False
section_name = self.pending_scroll
self.pending_scroll = None
if section_name not in self.sections:
return False
y_pos = 0
child = content.get_first_child() if content else None
while child:
if child == target:
break
h = child.get_height()
if h <= 0:
h = child.get_preferred_size().minimum_size.height
y_pos += h + 24
child = child.get_next_sibling()
uefi_text = """<b>How do I know if my computer has UEFI?</b>
In Windows Search, type <b>msinfo</b> or <b>msinfo32</b> and launch the desktop app named System Information. Look for the BIOS Mode item, and if the value for it is <b>UEFI</b>, then you have the UEFI firmware. If it says BIOS Mode Legacy, then that's the firmware you're running.
If you bought the computer/motherboard after 2010, chances are you have a UEFI system. If you are still unsure, download the UEFI version as it will also detect and run on a BIOS-Legacy computer.
<b>Secure Boot</b>
Linux Lite recommends that you disable Secure Boot in your BIOS. This will save you potentially a lot of headaches during the use of your system. Linux Lite will run with Secure Boot enabled, but we highly recommend that you don't."""
title_label = Gtk.Label()
title_label.set_markup("<span size='large' weight='bold'>? Select a Light or Dark Theme</span>")
title_label.set_halign(Gtk.Align.CENTER)
box.append(title_label)
desc_label = Gtk.Label(label="Click on a button below to select either a Light Theme or a Dark Theme. The Light Theme is already the default theme.")
desc_label.set_wrap(True)
desc_label.set_halign(Gtk.Align.CENTER)
desc_label.set_justify(Gtk.Justification.CENTER)
box.append(desc_label)
kb_text = """There are thousands of computing configurations in existence. As a result, different manufacturers have different ways of implementing their Keyboard and Numlock settings. When you boot Linux Lite for the first time and are having issues with your Keyboard and or Numlock, try each of the following solutions:
• Check your BIOS/UEFI configuration
• Menu, Settings, Lite Tweaks, Numlock
• FN (Function) + NUM LOCK
• FN + F11 (Acer, Toshiba, Samsung)
• Shift + NUM LOCK
• FN + NUM LOCK (Sony, Gateway, Lenovo, ASUS)
• FN + F8 (HP)
• Ctrl + F11
• FN + Shift + NUM LOCK
• FN + F4 (Dell)
<b>Keep Numlock working between boots</b> - Menu, Settings, Keyboard, Behavior tab > Enable or Disable - <b>Restore num lock state on startup</b>"""
upgrade_text = """Each <b>Series</b> of Linux Lite lasts 2 years and is based off LTS (Long Term Support) which continues to provide updates for 5 years. eg. Linux Lite <b>4.0</b> - Linux Lite <b>4.8</b> is <b>Series 4</b>, Linux Lite <b>5.0</b> is the start of <b>Series 5</b>, and so on.
Upgrading within <b>Series 7</b> is simple. Click on <b>Menu, Settings, Lite Upgrade</b> and follow the prompts to get the latest version of Linux Lite.
Upgrading can only occur from within a <b>Series</b>. For example we will upgrade you from Linux Lite <b>7.0</b> to Linux Lite <b>7.8</b>, but not from Linux Lite <b>6.0</b> to Linux Lite <b>7.8</b>."""
hw_text = """Linux Lite can run on a wide range of hardware. Our online Hardware Database contains a growing list of computers that can run Linux Lite.
<b>Recommended Computer Requirements:</b>
• 1.5 Ghz Dual Core Processor
• 4 GB Memory
• 40 GB HDD/SSD/NVME
• VGA, DVI, DP or HDMI screen capable of 1366x768 resolution
• DVD drive or USB port for the ISO image
• Disable Secure Boot
<b>TIP:</b> Check out the Linux Lite Hardware Database for a list of over 97,000 computers that can run Linux Lite."""
intro_desc = Gtk.Label(label="Linux Lite aims to provide you with a variety of Support options.")
intro_desc.set_wrap(True)
intro_desc.set_halign(Gtk.Align.CENTER)
intro_desc.set_justify(Gtk.Justification.CENTER)
intro_box.append(intro_desc)
# Linux Lite Wiki section
wiki_frame = Gtk.Frame()
wiki_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
wiki_box.add_css_class("section")
wiki_box.set_margin_top(16)
wiki_box.set_margin_bottom(16)
wiki_box.set_margin_start(16)
wiki_box.set_margin_end(16)
wiki_title = Gtk.Label()
wiki_title.set_markup("<span size='large' weight='bold'>? Linux Lite Wiki</span>")
wiki_title.set_halign(Gtk.Align.CENTER)
wiki_box.append(wiki_title)
wiki_desc = Gtk.Label()
wiki_desc.set_markup("Click on <b>Menu, Favorites, Linux Lite Wiki</b>. The Wiki is divided into clear categories - <b>Install Guide, Network, Software and Hardware</b>. Each tutorial has step by step instructions, with accompanying pictures. You can also access our Wiki online by clicking below:")
wiki_desc.set_wrap(True)
wiki_desc.set_halign(Gtk.Align.CENTER)
wiki_desc.set_justify(Gtk.Justification.CENTER)
wiki_box.append(wiki_desc)
forums_desc = Gtk.Label()
forums_desc.set_markup("Forums are a great resource for information. Begin by searching for your problem:\n\nIf no results turn up, by all means please post a new thread in the correct section clearly describing your situation. Once you have activated your account, you'll need to login before you can post.")
forums_desc.set_wrap(True)
forums_desc.set_halign(Gtk.Align.CENTER)
forums_desc.set_justify(Gtk.Justification.CENTER)
forums_box.append(forums_desc)
hw_desc = Gtk.Label()
hw_desc.set_markup("The purpose of the <b>Linux Lite Hardware Database</b> is to give people an idea of different computer configurations from within a Linux Lite Series including, <b>Make and Model, CPU, Graphics, Audio, Network and Storage</b> technical specifications.\n\nThese hardware combinations provide a snapshot of the kind of computers people are able to use with Linux Lite. This makes it a great resource for people either thinking of buying new hardware, or seeing if their existing machine is capable of running Linux Lite.")
hw_desc.set_wrap(True)
hw_desc.set_halign(Gtk.Align.CENTER)
hw_desc.set_justify(Gtk.Justification.CENTER)
hw_box.append(hw_desc)
tip_label = Gtk.Label()
tip_label.set_markup("<b>TIP:</b> The Forums are the best avenue for seeking answers. Our members are very generous with their time and there are always people willing to help you. Get help today.")
tip_label.set_wrap(True)
tip_label.set_halign(Gtk.Align.CENTER)
tip_label.set_justify(Gtk.Justification.CENTER)
tip_box.append(tip_label)
def scroll_to_section(self, section_name):
if section_name not in self.sections:
return
self.pending_scroll = section_name
GLib.idle_add(self._perform_scroll)
def _perform_scroll(self):
if not hasattr(self, 'pending_scroll') or not self.pending_scroll:
return False
section_name = self.pending_scroll
self.pending_scroll = None
if section_name not in self.sections:
return False
y_pos = 0
child = content.get_first_child() if content else None
while child:
if child == target:
break
h = child.get_height()
if h <= 0:
h = child.get_preferred_size().minimum_size.height
y_pos += h + 24
child = child.get_next_sibling()
code_desc = Gtk.Label()
code_desc.set_markup("This is a very exciting time in Linux Lite development. We are in the process of producing custom, free software for Linux Lite. Our philosophy is basic code, simple design and minimal dependency. Our applications should be clean, fast and simple. Intelligent, well thought out design with great attention to detail.\n\nWe use the GPL v2. If you're a developer and would like to help with Linux Lite, click below. We also pay developers to help improve our software.")
code_desc.set_wrap(True)
code_desc.set_halign(Gtk.Align.CENTER)
code_desc.set_justify(Gtk.Justification.CENTER)
code_box.append(code_desc)
donate_desc = Gtk.Label()
donate_desc.set_markup("Linux Lite is free and can best be described as a labour of love.\n\nThe goal of this project is to work on it full time so that we can deliver to you a better operating system with each release. More time, more features, greater options. If you use Linux Lite and want to contribute towards its success, consider donating.\n\nDonations go towards development and online services such as websites and repositories, as well as hardware purchases. Every donor is listed on our donate page, regardless of their contribution. Thanks for making a difference and for supporting free software.\n\nLinux Lite pays developers to help improve our custom software through developer services such as UpWork.")
donate_desc.set_wrap(True)
donate_desc.set_halign(Gtk.Align.CENTER)
donate_desc.set_justify(Gtk.Justification.CENTER)
donate_box.append(donate_desc)
mirror_desc = Gtk.Label()
mirror_desc.set_markup("Help support the Linux Lite community by hosting an official mirror. By providing a mirror, you'll help users worldwide enjoy faster downloads and better access to Linux Lite updates.\n\nOnce your mirror is approved and added, it will appear on our official Mirrors page. For details on how to set up and contribute a mirror, please contact us at our Feedback page.")
mirror_desc.set_wrap(True)
mirror_desc.set_halign(Gtk.Align.CENTER)
mirror_desc.set_justify(Gtk.Justification.CENTER)
mirror_box.append(mirror_desc)
# Social Media section
social_frame = Gtk.Frame()
social_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
social_box.add_css_class("section")
social_box.set_margin_top(16)
social_box.set_margin_bottom(16)
social_box.set_margin_start(16)
social_box.set_margin_end(16)
social_title = Gtk.Label()
social_title.set_markup("<span size='large' weight='bold'>? Social Media</span>")
social_title.set_halign(Gtk.Align.CENTER)
social_box.append(social_title)
social_desc = Gtk.Label()
social_desc.set_markup("Social Media has become a very powerful and effective way of proliferating knowledge throughout the world. We encourage you to spread the word about free operating systems such as Linux Lite.")
social_desc.set_wrap(True)
social_desc.set_halign(Gtk.Align.CENTER)
social_desc.set_justify(Gtk.Justification.CENTER)
social_box.append(social_desc)
# Social media buttons
social_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
social_buttons.set_halign(Gtk.Align.CENTER)
tip_label = Gtk.Label()
tip_label.set_markup("<b>TIP:</b> Social Media is a great way to spread the word about free operating systems such as Linux Lite. If you've come to Linux for the first time, share your experiences with the people you know online via Facebook, Twitter, Discord, Instagram, LinkedIn, YouTube, Reddit, and other Social Networking sites.")
tip_label.set_wrap(True)
tip_label.set_halign(Gtk.Align.CENTER)
tip_label.set_justify(Gtk.Justification.CENTER)
tip_box.append(tip_label)
def scroll_to_section(self, section_name):
if section_name not in self.sections:
return
self.pending_scroll = section_name
GLib.idle_add(self._perform_scroll)
def _perform_scroll(self):
if not hasattr(self, 'pending_scroll') or not self.pending_scroll:
return False
section_name = self.pending_scroll
self.pending_scroll = None
if section_name not in self.sections:
return False
y_pos = 0
child = content.get_first_child() if content else None
while child:
if child == target:
break
h = child.get_height()
if h <= 0:
h = child.get_preferred_size().minimum_size.height
y_pos += h + 24
child = child.get_next_sibling()
class LiteWelcomeApp(Adw.Application):
def __init__(self):
super().__init__(application_id=APP_ID, flags=Gio.ApplicationFlags.FLAGS_NONE)
self.window = None
self.config = WelcomeConfig()
self.cmd_handler = CommandHandler(self) # Pass app reference for toast notifications
self.stack = None
self.toast_overlay = None # For progress feedback
self.css_provider = None # For theme switching
here = os.path.dirname(os.path.abspath(__file__))
if os.path.exists('/usr/share/litewelcome'):
self.data_path = '/usr/share/litewelcome'
else:
self.data_path = here
def load_css(self):
"""Load custom CSS for the application based on system theme."""
# Get the style manager to detect system color scheme
style_manager = Adw.StyleManager.get_default()
is_dark = style_manager.get_dark()
# Connect to color scheme changes
style_manager.connect("notify::dark", self._on_color_scheme_changed)
self._apply_css(is_dark)
def _on_color_scheme_changed(self, style_manager, param):
"""Called when system color scheme changes."""
is_dark = style_manager.get_dark()
self._apply_css(is_dark)
def _apply_css(self, is_dark):
"""Apply CSS based on whether dark mode is active."""
# Remove old CSS provider if exists
if self.css_provider:
Gtk.StyleContext.remove_provider_for_display(
Gdk.Display.get_default(),
self.css_provider
)
if is_dark:
css = b"""
/* Dark mode - dark backgrounds */
window, .background {
background-color: #2d2d2d;
}
/* Content area */
scrolledwindow, viewport {
background-color: #2d2d2d;
}
def do_activate(self):
if not self.window:
self.load_css()
self.window = Adw.ApplicationWindow(application=self)
self.window.set_title(APP_NAME)
self.window.set_default_size(850, 768)
# Set taskbar icon by adding search path and using icon name
icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
icon_theme.add_search_path("/usr/share/litewelcome/img")
self.window.set_icon_name("litewelcome")
self.build_ui()
# Center window on screen after it's realized
self.window.connect("realize", self._on_window_realize)
self.window.present()
def _on_window_realize(self, window):
"""Called when window is realized - schedule centering."""
# Use idle_add to ensure the window is fully mapped before centering
GLib.idle_add(self._center_window_on_screen)
def _center_window_on_screen(self):
"""Center the window on the primary monitor."""
try:
display = Gdk.Display.get_default()
if not display:
return False
# Get list of monitors
monitors = display.get_monitors()
if monitors.get_n_items() == 0:
return False
# Get the primary/first monitor
monitor = monitors.get_item(0)
geometry = monitor.get_geometry()
# Wrap main content in toast overlay
self.toast_overlay.set_child(main_box)
self.window.set_content(self.toast_overlay)
def show_toast(self, message, timeout=2):
"""Show a toast notification with the given message."""
if self.toast_overlay:
toast = Adw.Toast.new(message)
toast.set_timeout(timeout)
self.toast_overlay.add_toast(toast)
# Social media buttons with images - larger for better click targets
social_links = [
("social-facebook.png", "https://www.facebook.com/linuxliteos", "Facebook"),
("gitlab.svg", "https://gitlab.com/linuxlite", "GitLab"),
("social-twitter.png", "https://twitter.com/LinuxLite", "Twitter"),
("youtube.svg", "https://www.youtube.com/c/linuxliteos", "YouTube"),
("social-reddit.svg", "https://www.reddit.com/r/LinuxLite/", "Reddit"),
("social-discord.png", "https://discord.gg/bQSFaFAUkm", "Discord"),
]
for img_file, url, tooltip in social_links:
btn = Gtk.Button()
btn.add_css_class("flat")
btn.add_css_class("social-button") # Custom class for larger click target
btn.set_tooltip_text(tooltip)
# Load the social media icon - larger size (28x28)
img_path = os.path.join(self.data_path, "img", img_file)
icon = load_image_from_file(img_path, width=28, height=28)
if icon:
btn.set_child(icon)
else:
# Fallback to first letter if image fails
btn.set_label(tooltip[0])
# If a section was specified, scroll to it
if section:
page_widget = self.stack.get_child_by_name(page)
if hasattr(page_widget, 'scroll_to_section'):
page_widget.scroll_to_section(section)
I replaced the code in the /bin/lite-welcome file with the code provided and then the app wouldn't open at all. I noticed that the new code is around 300 lines longer (aprox 1600 lines) than the old code (aprox. 1300 lines). Was the new code supposed to be that different than the previous?
Ok, it's working now. I'm not sure why it didn't work the first time because I had made the file executable.Thanks![attachment=303 Wrote:valtam pid='62774' dateline='1767585564']Yes, different code. Make sure the application file is executable.
Should the included version of GIMP be updated to 3.x series?
The current version of GIMP that is included with LL is 2.10.36. The 3.x version has been available for quite awhile now and 3.06 is the default on the gimp.org website. When you open in LL and go to "Help > About" it recommends updating.
All opinions expressed and all advice given by Trinidad Cruz on this forum are his responsibility alone and do not necessarily reflect the views or methods of the developers of Linux Lite. He is a citizen of the United States where it is acceptable to occasionally be uninformed and inept as long as you pay your taxes.
I added the ubuntuhandbook1/gimp-3 PPA and it upgraded fine that way afterwards but it doesn't sound like it's as up-to-date as the flatpak or snap versions so maybe not the best way to go but it's nice that it will updates with the rest of the system via "apt update" etc.
Option 3: Ubuntu PPA (unofficial)
For those who prefer the native
Code:
.deb
package format, I’ve built GIMP 3.0 into this unofficial PPA for Ubuntu 22.04, Ubuntu 24.04, and Ubuntu 24.10 on
Code:
amd64
,
Code:
arm64
/
Code:
armhf
platforms. NOTE: The PPA package has few downsides:
It’s built with system GTK3 library, which is a bit outdated. While, GIMP 3.0 relies on the most recent GTK3 library for some fixes.
Also due to outdated library (libglib2.0-dev), the Ubuntu 22.04 package reversed this commit, or it won’t build.
For Ubuntu 22.04, the PPA also contains updated versions of libheif, libwebp, kvazaar HEVC encoder that might conflict to other 3rd party packages.
To add the PPA, press
Code:
Ctrl+Alt+T
to open up a terminal window, and run command:
sudo add-apt-repository ppa:ubuntuhandbook1/gimp-3