A simple program using the mir library.
#include "server_configuration.h"
#define GLM_FORCE_RADIANS
#include <glm/gtc/matrix_transform.hpp>
#include <thread>
#include <atomic>
#include <chrono>
#include <csignal>
#include <iostream>
#include <sstream>
#include <vector>
namespace mg = mir::graphics;
namespace mc = mir::compositor;
namespace ms = mir::scene;
namespace mf = mir::frontend;
namespace mo = mir::options;
namespace msh = mir::shell;
namespace mi = mir::input;
namespace geom = mir::geometry;
namespace mt = mir::tools;
namespace me = mir::examples;
namespace
{
std::atomic<bool> created{false};
bool input_is_on = false;
std::weak_ptr<mg::Cursor> cursor;
static const uint32_t bg_color = 0x00000000;
static const uint32_t fg_color = 0xffdd4814;
static const float min_alpha = 0.3f;
void update_cursor(uint32_t bg_color, uint32_t fg_color)
{
if (auto cursor = ::cursor.lock())
{
static const int width = 64;
std::vector<uint32_t> image(height * width, bg_color);
for (int i = 0; i != width-1; ++i)
{
if (i < 16)
{
image[0 * height + i] = fg_color;
image[1 * height + i] = fg_color;
image[i * height + 0] = fg_color;
image[i * height + 1] = fg_color;
}
image[i * height + i] = fg_color;
image[(i+1) * height + i] = fg_color;
image[i * height + i + 1] = fg_color;
}
}
}
void animate_cursor()
{
if (!input_is_on)
{
if (auto cursor = ::cursor.lock())
{
static int cursor_pos = 0;
if (++cursor_pos == 300)
{
cursor_pos = 0;
static const uint32_t fg_colors[3] = { fg_color, 0xffffffff, 0x3f000000 };
static int fg_color = 0;
if (++fg_color == 3) fg_color = 0;
update_cursor(bg_color, fg_colors[fg_color]);
}
}
}
}
char const* const surfaces_to_render = "surfaces-to-render";
char const* const display_cursor = "display-cursor";
class StopWatch
{
public:
StopWatch() : start(std::chrono::high_resolution_clock::now()),
last(start),
now(last)
{
}
void stop()
{
now = std::chrono::high_resolution_clock::now();
}
double elapsed_seconds_since_start()
{
auto elapsed = now - start;
float elapsed_sec = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() / 1000000.0f;
return elapsed_sec;
}
double elapsed_seconds_since_last_restart()
{
auto elapsed = now - last;
float elapsed_sec = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() / 1000000.0f;
return elapsed_sec;
}
void restart()
{
std::swap(last, now);
}
private:
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::time_point last;
std::chrono::high_resolution_clock::time_point now;
};
class Moveable
{
public:
Moveable() {}
Moveable(std::shared_ptr<msh::Surface>
const& s,
const geom::Size& display_size,
float dx,
float dy,
const glm::vec3& rotation_axis,
float alpha_offset)
: surface(s), display_size(display_size),
x{
static_cast<float>(s->top_left().x.as_uint32_t())},
y{
static_cast<float>(s->top_left().y.as_uint32_t())},
w{static_cast<float>(s->size().width.as_uint32_t())},
h{static_cast<float>(s->size().height.as_uint32_t())},
dx{dx},
dy{dy},
rotation_axis(rotation_axis),
alpha_offset{alpha_offset}
{
}
void step()
{
stop_watch.stop();
float elapsed_sec = stop_watch.elapsed_seconds_since_last_restart();
float total_elapsed_sec = stop_watch.elapsed_seconds_since_start();
stop_watch.restart();
bool should_update = true;
float new_x =
x + elapsed_sec *
dx;
float new_y =
y + elapsed_sec *
dy;
{
should_update = false;
}
{
should_update = false;
}
if (should_update)
{
surface->move_to({new_x, new_y});
}
glm::mat4 trans = glm::rotate(glm::mat4(1.0f),
glm::radians(total_elapsed_sec * 120.0f),
rotation_axis);
surface->set_transformation(trans);
float const alpha_amplitude = (1.0f - min_alpha) / 2.0f;
surface->set_alpha(min_alpha + alpha_amplitude +
alpha_amplitude *
sin(alpha_offset + 2 * M_PI * total_elapsed_sec /
3.0));
}
private:
std::shared_ptr<msh::Surface> surface;
float w;
float h;
StopWatch stop_watch;
glm::vec3 rotation_axis;
float alpha_offset;
};
{
public:
RenderSurfacesServerConfiguration(int argc, char const** argv) :
ServerConfiguration([argc, argv]
{
auto result = std::make_shared<mo::DefaultConfiguration>(argc, argv);
namespace po = boost::program_options;
result->add_options()
(surfaces_to_render, po::value<int>()->default_value(5),
"Number of surfaces to render")
(display_cursor, po::value<bool>()->default_value(false),
"Display test cursor. (If input is disabled it gets animated.)");
return result;
}())
{
}
std::shared_ptr<mf::Connector> the_connector() override
{
{
void start() {}
void stop() {}
int client_socket_fd() const override { return 0; }
void remove_endpoint() const override { }
};
return std::make_shared<NullConnector>();
}
std::shared_ptr<mg::BufferInitializer> the_buffer_initializer() override
{
{
public:
RenderResourcesBufferInitializer(std::unique_ptr<mg::GLContext> gl_context)
: gl_context{std::move(gl_context)}
{
}
{
mir_image.bytes_per_pixel};
brt.make_current();
img_renderer.render();
}
private:
std::unique_ptr<mg::GLContext> const gl_context;
};
return std::make_shared<RenderResourcesBufferInitializer>(the_display()->create_gl_context());
}
std::shared_ptr<mir::ServerStatusListener> the_server_status_listener()
{
{
ServerStatusListener(std::function<void()> create_surfaces, std::shared_ptr<mir::ServerStatusListener> wrapped) :
create_surfaces(create_surfaces), wrapped(wrapped) {}
virtual void paused() override { wrapped->paused(); }
virtual void resumed() override { wrapped->resumed(); }
virtual void started() override { wrapped->started(); create_surfaces(); create_surfaces = []{}; }
std::function<void()> create_surfaces;
std::shared_ptr<mir::ServerStatusListener> const wrapped;
};
return server_status_listener(
[this]()
{
auto wrapped = ServerConfiguration::the_server_status_listener();
return std::make_shared<ServerStatusListener>([this] { create_surfaces(); }, wrapped);
});
}
std::shared_ptr<mc::DisplayBufferCompositorFactory> the_display_buffer_compositor_factory() override
{
{
public:
RenderSurfacesDisplayBufferCompositor(
std::unique_ptr<DisplayBufferCompositor> db_compositor,
std::vector<Moveable>& moveables)
: db_compositor{std::move(db_compositor)},
moveables(moveables),
frames{0}
{
}
{
while (!created) std::this_thread::yield();
animate_cursor();
stop_watch.stop();
if (stop_watch.elapsed_seconds_since_last_restart() >= 1)
{
std::cout << "FPS: " << frames << " Frame Time: " << 1.0 / frames << std::endl;
frames = 0;
stop_watch.restart();
}
glClearColor(0.0, 1.0, 0.0, 1.0);
db_compositor->composite();
for (auto& m : moveables)
m.step();
frames++;
return false;
}
private:
std::unique_ptr<DisplayBufferCompositor> const db_compositor;
StopWatch stop_watch;
std::vector<Moveable>& moveables;
uint32_t frames;
};
{
public:
RenderSurfacesDisplayBufferCompositorFactory(
std::shared_ptr<mc::DisplayBufferCompositorFactory> const& factory,
std::vector<Moveable>& moveables)
: factory{factory},
moveables(moveables)
{
}
std::unique_ptr<mc::DisplayBufferCompositor> create_compositor_for(
mg::DisplayBuffer& display_buffer)
{
auto compositor = factory->create_compositor_for(display_buffer);
auto raw = new RenderSurfacesDisplayBufferCompositor(
std::move(compositor), moveables);
return std::unique_ptr<RenderSurfacesDisplayBufferCompositor>(raw);
}
private:
std::shared_ptr<mc::DisplayBufferCompositorFactory> const factory;
std::vector<Moveable>& moveables;
};
return std::make_shared<RenderSurfacesDisplayBufferCompositorFactory>(
moveables);
}
void create_surfaces()
{
moveables.resize(the_options()->get<int>(surfaces_to_render));
std::cout << "Rendering " << moveables.size() << " surfaces" << std::endl;
auto const display = the_display();
auto const surface_coordinator = the_surface_coordinator();
{
});
uint32_t const surface_side{300};
geom::Size const surface_size{surface_side, surface_side};
float const angular_step = 2.0 * M_PI / moveables.size();
auto const surface_pf = the_buffer_allocator()->supported_pixel_formats()[0];
int i = 0;
for (auto& m : moveables)
{
auto const s = surface_coordinator->add_surface(
.of_pixel_format(surface_pf)
.of_buffer_usage(mg::BufferUsage::hardware),
{});
{
auto const complete = [&](
mg::Buffer* new_buf){ buffer = new_buf; };
s->swap_buffers(buffer, complete);
s->swap_buffers(buffer, complete);
}
uint32_t
const x = w * (0.5 + 0.25 * cos(i * angular_step)) - surface_side / 2.0;
uint32_t
const y = h * (0.5 + 0.25 * sin(i * angular_step)) - surface_side / 2.0;
m = Moveable(s, display_size,
cos(0.1f + i * M_PI / 6.0f) * w / 3.0f,
sin(0.1f + i * M_PI / 6.0f) * h / 3.0f,
glm::vec3{(i % 3 == 0) * 1.0f, (i % 3 == 1) * 1.0f, (i % 3 == 2) * 1.0f},
2.0f * M_PI * cos(i));
++i;
}
created = true;
}
bool input_is_on()
{
}
std::weak_ptr<mg::Cursor> the_cursor()
{
if (the_options()->get<bool>(display_cursor))
{
return the_display()->the_cursor();
}
else
{
return {};
}
}
private:
std::vector<Moveable> moveables;
};
}
int main(
int argc,
char const** argv)
try
{
RenderSurfacesServerConfiguration conf{argc, argv};
{
cursor = conf.the_cursor();
input_is_on = conf.input_is_on();
});
return 0;
}
catch (...)
{
return 1;
}