Chapter 35. Splash windows

Index

Creating splash windows
Using the standard HTML 3.2 color palette
Splash windows.

A splash window has no title, no decorations, and no window manager borders around it. It's not draggable and it does not have a close button. Large applications that loads many shared libraries and create complicated windows usually take a while to get ready and prepare their main window, so the splash window with just the application's name, in pretty colors, appears quickly, up front. A splash window provides immediate feedback when launching the application.

The usual approach involves launching the application launched indirectly. The initial loader is a small program that quickly shows the splash window, and does nothing else except starting another process that runs and initializes the main application. The launcher process terminates, and the splash window disappears, when the main application window opens for business.

splash.C shows a basic splash window containing nothing but a simple Loading... message, with a gradient background. splash.C pauses for two seconds, to simulate a large application getting loaded, then starts another example program: table2.C from the section called “Adjustable tables”.

splash.C creates a pipe and attaches it to table2.C's file descriptor 3. table2.C closes its file descriptor 3 after it finishes creating and showing its main window. splash.C waits for the pipe to close, then terminates itself, closing its splash window.

/*
** Copyright 2019 Double Precision, Inc.
** See COPYING for distribution information.
*/

#include "config.h"

#include <x/exception.H>
#include <x/destroy_callback.H>

#include <x/w/main_window.H>
#include <x/w/main_window_appearance.H>
#include <x/w/gridlayoutmanager.H>
#include <x/w/gridfactory.H>
#include <x/w/label.H>
#include <x/w/text_param_literals.H>
#include "splash.H"

#include <x/pidinfo.H>
#include <x/forkexec.H>
#include <x/fditer.H>

#include <string>

// Creator for the splash window, factored out for readability.

void create_splash(const x::w::main_window &main_window)
{
	x::w::gridlayoutmanager glm=main_window->get_layoutmanager();

	x::w::gridfactory f=glm->append_row();

	// Not much of a splash window. Just a large, black "Loading..."
	// label.

	f->create_label({
			 "serif; point_size=48"_theme_font,
			 x::w::black,
			 "Loading..."
		});
}

x::w::main_window create_mainwindow(const options &options)
{
	// main_window's create() takes an optional initial parameter that
	// precedes the creator lambda, which is x::w::main_window_config
	// by default.
	//
	// Passing in an x::w::transparent_splash_window_config or a
	// x::w::splash_window_config creates a splash window.

	x::w::transparent_splash_window_config transparent_splash_config;

	// transparent_splash_window_config creates a splash window with a
	// transparent background color, ostensibly for properly displaying
	// a rounded border around the contents of the splash window.
	//
	// splash_window_config creates a normal non-transparent window.
	//
	// transparent_splash_window_config inherits from splash_window_config,
	// and if the display screen does not have an alpha channel
	// (for transparency), it ends up creating a normal, non-transparent
	// window by using its splash_window_config superclass.

	x::w::splash_window_config &splash_config=transparent_splash_config;

	// We specify a custom appearance of the splash window, with
	// a custom background color, a vertical gradient
	//
	// This background color also gets used for the transparent window as
	// well. This sets the background color for the internal container
	// that's just inside the transparent splash window's borders. The
	// splash window's background color is transparent, hence it appears
	// to have rounded borders. The application should not use
	// set_background_color() with a transparent main_window, but rather
	// specify the apparent background color of the splash window in the
	// splash_config.

	splash_config.appearance=splash_config.appearance->modify
		([]
		 (const x::w::main_window_appearance &custom_appearance)
		 {
			 custom_appearance->background_color=
				 x::w::linear_gradient{0, 0, 0, 1,
						       0, 0,
						       {
							{0, x::w::silver},
							{1, x::w::white},
							{2, x::w::silver}
						       }};
		 });

	// splash_window_config's border member specifies an ordinary, non-
	// rounded border.
	//
	// This is an extra field in the splash_window_config that's not a
	// part of its appearance object.

	x::w::border_infomm square_border;

	square_border.color1=x::w::black;
	square_border.width=.5;
	square_border.height=.5;

	splash_config.border=square_border;

	// Its transparent_splash_window_config also has a "border", for the
	// transparent splash window, if one ends up getting created, so
	// we configure the same border there, except that it's rounded.

	x::w::border_infomm rounded_border=square_border;

	rounded_border.rounded=true;
	rounded_border.hradius=1;
	rounded_border.vradius=1;

	transparent_splash_config.border=rounded_border;

	// After passing either an x::w::splash_window_config or a
	// x::w::transparent_splash_window_config to main_window's create(),
	// the next parameter is the creator lambda, as usual.

	if (options.square->value)
		return x::w::main_window::create(splash_config, create_splash);

	return x::w::main_window
		::create(transparent_splash_config, create_splash);
}

x::fd spawn_application()
{
	auto me=x::exename(); // My path

	// Compute the path to the compiled "table2" program in the same
	// directory.
	me=me.substr(0, me.rfind('/')+1) + "table2";

	x::forkexec fe{me};

	// Attach a pipe to file descriptor 3.
	//
	// table2 closes file descriptor 3 when it finishes initialization.

	x::fd pipe=fe.pipe_from(3);

	fe.spawn_detached();

	// The object for the write end of the pipe is still in the forkexec
	// object. Return from here destroys it, so only the read side of
	// the pipe is open in this process. The write side of the pipe
	return pipe;
}

void splashwindow(const options &opts)
{
	x::destroy_callback::base::guard guard;

	auto main_window=create_mainwindow(opts);

	main_window->on_disconnect([]
				   {
					   _exit(1);
				   });

	guard(main_window->connection_mcguffin());

	main_window->set_window_class("splash",
				      "splash@examples.w.libcxx.com");

	main_window->show_all();

	sleep(2);

	auto pipe=spawn_application();

	// Now read from the pipe until file descriptor 3 is closed.

	x::fdinputiter b{pipe}, e;

	while (b != e)
		++b;
}

int main(int argc, char **argv)
{
	try {
		options opts;

		opts.parse(argc, argv);
		splashwindow(opts);
	} catch (const x::exception &e)
	{
		e->caught();
		exit(1);
	}
	return 0;
}

Creating splash windows

auto main_window=x::w::main_window::create(
    x::w::splash_window_config{},
    [&]
    (const x::w::main_window &mw)
    {
          // ...
    });

x::w::main_window's create() has an optional parameter that precedes the creator lambda. Passing an x::w::main_window_config parameter (the default parameter if not specified) creates a normal application window. Passing an x::w::transparent_splash_window_config creates a splash window instead of a normal application window.

The created x::w::main_window behaves normally, except for the following differences:

No title, dragging, or normal window manager decorations

Splash windows use X protocol's override-redirect flag to shed the title bar and the close button from the display's window manager. The on_delete() callback that normally responds to the close button will never get called, so there's no purpose to installing it.

Position and appearance

Splash windows appear above other windows (with most window managers), and always appear centered on the display.

x::w::splash_window_config creates a plain, rectangular splash window with a custom border. x::w::transparent_splash_window_config creates a splash window with a transparent background. This provides the means for drawing a rounded border around the contents of the splash window.

x::w::transparent_splash_window_config inherits from x::w::splash_window_config, and uses it to create a plain rectangular splash window if the display server does not provide an alpha channel for transparent windows.

Default splash window background and border

Both x::w::splash_window_config and x::w::transparent_splash_window_config contain several fields that control the splash window's appearance. They inherit from x::w::main_window_config, including its appearance object, whose background_color field sets the splash window's background color. Supplementing the appearance object, both x::w::splash_window_config and x::w::transparent_splash_window_config contain a border that sets the splash window's border; with x::w::transparent_splash_window_config default theme border being typically a rounded border, and x::w::splash_window_config having a plain border; and in this manner displays that do not have a non-alpha channel will also have x::w::splash_window_config's border also, due to x::w::transparent_splash_window_config inheriting from it.

x::w::transparent_splash_window_config also uses the background_color from the appearance object to draw the default background inside its border. A transparent splash window's background gets set to the transparent color, by definition, for the benefit of its rounded border. For this reason, a splash window should not have its background color modified with set_background_color, after its creation. Set the splash window's background color only using a custom appearance object when creating the splash window.