Journal Articles

CVu Journal Vol 31, #5 - November 2019 + Design of applications and programs
Browse in : All > Journals > CVu > 315 (7)
All > Topics > Design (236)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Making a Linux Desktop: Painting Some Wallpaper

Author: Bob Schmidt

Date: 04 November 2019 18:17:17 +00:00 or Mon, 04 November 2019 18:17:17 +00:00

Summary: Alan Griffiths continues his series on coding with the Mir library.

Body: 

I’m working on a project (Mir) that, among other things, aims to make it easy to develop graphical ‘desktop environments’ for Linux. There are a lot of features that are common between all designs for desktop environments and, in addition, a lot that are common between the majority of designs. For example, it is common for applications to draw ‘windows’ and for these to be combined onto the screen.

By providing the common features, and for the rest offering both sensible defaults and an easy way to customise them, Mir is designed to support a range of possible designs.

Last issue we finished with a basic but functional desktop shell. This time we’ll extend that with the capability to launch full screen ‘applications’ within the desktop and use that to paint some a simple wallpaper.

Preparation

The code in this article needs Mir 1.2 (or later). On Ubuntu 18.04 (and later), Mir 1.4 is available from the mir-team/release PPA. It is also useful to install the weston package as the example makes use of weston-terminal as a Wayland based terminal application and the Qt toolkit’s Wayland support: qtwayland5. And finally, the g++ compiler and cmake.

  $ sudo apt-add-repository ppa:mir-team/release
  $ sudo apt install libmiral-dev mir-graphics-  drivers-desktop
  $ sudo apt install weston qtwayland5
  $ sudo apt install g++ cmake

All these we installed last time; this time we’ll use a few more:

  $ sudo apt install libwayland-dev libfreetype6-  dev libxkbcommon-dev

We need libwayland-dev to write our full-screen client, to draw some text and to support the keyboard handling we’ll need in the following articles.

Mir 1.4 is available on Fedora and can be built from source for many versions of Linux.

Building the example

The full code for this example is available on github:

  $ git clone https://github.com/AlanGriffiths/  egmde.git
  $ git checkout Article-2

Naturally, the code is likely to evolve, so you will find other branches, but the Article-2 branch goes with this article. Assuming that you’ve MirAL installed as described above you can now build egmde as follows:

  $ mkdir egmde/build
  $ cd egmde/build
  $ cmake ..
  $ make

Running the example

After this you can start egmde:

  $ ./egmde

This time the Mir-on-X window should have a coloured gradient with the words “Ctrl-Alt-T = terminal | Ctrl-Alt-BkSp = quit” at the bottom. But there’s more: the background can be configured by the user. If you type:

  $ ./egmde –help

you’ll get a list of options that includes the following:

  --wallpaper-top arg (=0x000000)      Colour of   wallpaper RGB
  --wallpaper-bottom arg (=0x92006a)   Colour of   wallpaper RGB

These are two options that allow you to experiment with different colours for the background gradient. For example:

  $ ./egmde --wallpaper-top 0x171717 --wallpaper-  bottom 0x7f7f7f

Once you have found some favoured colours you can save them in a config file:

  $ cat ~/.config/egmde.config 
  wallpaper-top=0x170000
  wallpaper-bottom=0x7f1f1f

A background that can be configured by the user makes egmde look a little more finished than it did last time (even if it doesn’t do cat photos).

Installing the example

If you install egmde and it will be added to the greeter menu (typically a ‘cog wheel’ on the sign-on screen):

  $ sudo make install

Depending upon the greeter used on your system, it may be necessary to reboot before egmde appears as a login shell.

To register applications with deskop environments we install a .desktop file in /usr/share/applications and to register Wayland based desktops with the greeter we install a .desktop file in /usr/share/wayland-sessions/. The content needed for both cases is similar, so for this example we use the same .desktop file and install it to both places.

The example code

There’s a lot more code (about 20×) than last time, so I won’t attempt to put it all in a listing with the article:

  $ wc -l *.cpp *.h
    616 egfullscreenclient.cpp
     90 egmde.cpp
    176 egwallpaper.cpp
     46 egwindowmanager.cpp
    201 printer.cpp
    236 egfullscreenclient.h
     58 egwallpaper.h
     43 egwindowmanager.h
     54 printer.h
   1520 total

egmde.cpp

First let’s look at the changes to egmde.cpp:

  #include "egwallpaper.h"
  #include "egwindowmanager.h"
  #include <miral/command_line_option.h>
  #include <miral/internal_client.h>

There are two headers from the example project and three from the MirAL library. We’ll see the use of the MirAL library in the rest of this file and examine the example files after that.

This creates a Wallpaper object and ensures that the runner calls stop() on it just before closing down the server:

  egmde::Wallpaper wallpaper;
  runner.add_stop_callback([&] { 
    wallpaper.stop(); });

Instead of the window manager provided by MirAL we use our own. There is only a small change and we’ll see what that is later:


set_window_management_policy
  <MinimalWindowManager>(),

  set_window_management_policy
    <egmde::WindowManager>(wallpaper),

These three lines provide configuration options for the wallpaper and start an ‘internal client’ application that provides the wallpaper:

  CommandLineOption{[&](auto& option) {
    wallpaper.top(option);}, "wallpaper-top",
    "Colour of wallpaper RGB", "0x000000"},
  CommandLineOption{[&](auto& option) {
    wallpaper.bottom(option);}, "wallpaper-bottom",
    "Colour of wallpaper RGB",
    EGMDE_WALLPAPER_BOTTOM},
  StartupInternalClient{std::ref(wallpaper)},

As we saw above, the configuration options are not just ‘command-line’: like other Mir configuration options they can be provided via environment variables and a config file.

egfullscreenclient.*

The largest chunk of new code is an abstract FullscreenClient class that provides a framework for managing full-screen windows. While useful, both for this article and for the next, it doesn’t use the Mir APIs and won’t be explored further here.

egwallpaper.*

The example Wallpaper class (Listing 1) provides the interface required to start an ‘internal client’ application: two function call operators, one to provide the Wayland connection to use, one for the server-side ‘session’ used by the window manager to identify the application.

class Wallpaper
{
public:
  void operator()(wl_display* display);
  void operator()(std::weak_ptr
    <mir::scene::Session> const& session);
  auto session() const ->
    std::shared_ptr<mir::scene::Session>;
  void stop();
  // Used in initialization to set colour
  void bottom(std::string const& option);
  void top(std::string const& option);
private:
  std::mutex mutable mutex;
  std::weak_ptr<mir::scene::Session>
    weak_session;
  uint8_t bottom_colour[4] = { 0x0a, 0x24, 0x77,
    0xFF };
  uint8_t top_colour[4] = { 0x00, 0x00, 0x00,
    0xFF };
  struct Self;
  std::weak_ptr<Self> self;
};
			
Listing 1

It also provides two functions we need to retrieve the ‘session’ and to stop the wallpaper application running on shutdown (as seen in runner.add_stop_callback() above).

Finally, it provides two functions to allow the wallpaper colour gradient to be configured.

Internally, the Wallpaper::Self class derives from FullscreenClient and provides (Wayland based) code to paint the wallpaper on each screen. This is also where the printer.* files are used; they are a convenience wrapper around libfreetype for drawing text into a graphics buffer. Once again, a close examination of that code goes beyond this exploration of the Mir APIs.

egwindowmanager.*

The example WindowManager class (Listing 2) extends the MinimalWindowManager we saw in the last issue. The only change it makes is to extend the place_new_window() to check the session against the wallpaper and, for the wallpaper, ensure that the window can never get input focus (and, as a side effect, is never raised above other windows). There’s currently no Wayland extension that allows a client to exploit the rich semantics of Mir’s window type systems. But, as this is an internal client, we can work-around it by setting the type ourselves.

class WindowManager : public MinimalWindowManager
{
public:
  WindowManager(WindowManagerTools const& tools,
    Wallpaper const& wallpaper);
  auto place_new_window(
    ApplicationInfo const& app_info,
    WindowSpecification const&
      requested_specification)
  -> miral::WindowSpecification override;
private:
  Wallpaper const* const wallpaper;
};
// Implementation
auto WindowManager::place_new_window(
  ApplicationInfo const& app_info,
  WindowSpecification const&
    requested_specification)
-> WindowSpecification
{
  auto result =
    MinimalWindowManager::place_new_window(
    app_info, requested_specification);
  if (app_info.application() == 
    wallpaper->session())
  {
    // Setting the window type.
    result.type() = mir_window_type_decoration;
  }
  return result;
}
			
Listing 2

Conclusion

This article shows how the Mir library can provide a stable starting point for adding functionality to a ‘desktop environment’. The next article will continue from this point and add a ‘launcher’ for selecting and starting applications.

References

Alan Griffiths Alan Griffiths has delivered working software and development processes to a range of organizations, written for a number of magazines, spoken at several conferences, and made many friends.

Notes: 

More fields may be available via dynamicdata ..