/*
* Client-side display configuration demo.
*
* Copyright © 2013 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* 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 .
*
* Author: Alexandros Frantzis
*/
#include "eglapp.h"
#include "mir_toolkit/mir_client_library.h"
#include
#include
#include
#include
#include
#include
#include
#include
typedef enum
{
configuration_mode_unknown,
configuration_mode_clone,
configuration_mode_horizontal,
configuration_mode_vertical,
configuration_mode_single
} ConfigurationMode;
struct ClientContext
{
MirConnection *connection;
ConfigurationMode mode;
int mode_data;
volatile sig_atomic_t running;
volatile sig_atomic_t reconfigure;
};
struct Card
{
uint32_t card_id;
uint32_t available_outputs;
};
struct Cards
{
size_t num_cards;
struct Card *cards;
};
static struct Cards *cards_create(struct MirDisplayConfiguration *conf)
{
struct Cards *cards = (struct Cards*) calloc(1, sizeof(struct Cards));
cards->num_cards = conf->num_cards;
cards->cards = (struct Card*) calloc(cards->num_cards, sizeof(struct Card));
for (size_t i = 0; i < cards->num_cards; i++)
{
cards->cards[i].card_id = conf->cards[i].card_id;
cards->cards[i].available_outputs =
conf->cards[i].max_simultaneous_outputs;
}
return cards;
}
static void cards_free(struct Cards *cards)
{
free(cards->cards);
free(cards);
}
static struct Card *cards_find_card(struct Cards *cards, uint32_t card_id)
{
for (size_t i = 0; i < cards->num_cards; i++)
{
if (cards->cards[i].card_id == card_id)
return &cards->cards[i];
}
fprintf(stderr, "Error: Couldn't find card with id: %u\n", card_id);
abort();
}
static void print_current_configuration(MirConnection *connection)
{
MirDisplayConfiguration *conf = mir_connection_create_display_config(connection);
for (uint32_t i = 0; i < conf->num_outputs; i++)
{
MirDisplayOutput *output = &conf->outputs[i];
printf("Output id: %d connected: %d used: %d position_x: %d position_y: %d",
output->output_id, output->connected,
output->used, output->position_x, output->position_y);
if (output->current_mode < output->num_modes)
{
MirDisplayMode *mode = &output->modes[output->current_mode];
printf(" mode: %dx%d@%.2f\n",
mode->horizontal_resolution,
mode->vertical_resolution,
mode->refresh_rate);
}
else
{
printf("\n");
}
}
mir_display_config_destroy(conf);
}
static int apply_configuration(MirConnection *connection, MirDisplayConfiguration *conf)
{
MirWaitHandle* handle = mir_connection_apply_display_config(connection, conf);
if (!handle)
{
printf("Failed to apply configuration, check that the configuration is valid.\n");
return 0;
}
mir_wait_for(handle);
const char* error = mir_connection_get_error_message(connection);
if (!strcmp(error, ""))
{
printf("Succeeded.\n");
return 1;
}
else
{
printf("Failed to apply configuration with error '%s'.\n", error);
return 0;
}
}
static void configure_display_clone(struct MirDisplayConfiguration *conf)
{
struct Cards *cards = cards_create(conf);
for (uint32_t i = 0; i < conf->num_outputs; i++)
{
MirDisplayOutput *output = &conf->outputs[i];
struct Card *card = cards_find_card(cards, output->card_id);
if (output->connected && output->num_modes > 0 &&
card->available_outputs > 0)
{
output->used = 1;
output->current_mode = 0;
output->position_x = 0;
output->position_y = 0;
--card->available_outputs;
}
}
cards_free(cards);
}
static void configure_display_horizontal(struct MirDisplayConfiguration *conf)
{
struct Cards *cards = cards_create(conf);
uint32_t max_x = 0;
for (uint32_t i = 0; i < conf->num_outputs; i++)
{
MirDisplayOutput *output = &conf->outputs[i];
struct Card *card = cards_find_card(cards, output->card_id);
if (output->connected && output->num_modes > 0 &&
card->available_outputs > 0)
{
output->used = 1;
output->current_mode = 0;
output->position_x = max_x;
output->position_y = 0;
max_x += output->modes[0].horizontal_resolution;
--card->available_outputs;
}
}
cards_free(cards);
}
static void configure_display_vertical(struct MirDisplayConfiguration *conf)
{
struct Cards *cards = cards_create(conf);
uint32_t max_y = 0;
for (uint32_t i = 0; i < conf->num_outputs; i++)
{
MirDisplayOutput *output = &conf->outputs[i];
struct Card *card = cards_find_card(cards, output->card_id);
if (output->connected && output->num_modes > 0 &&
card->available_outputs > 0)
{
output->used = 1;
output->current_mode = 0;
output->position_x = 0;
output->position_y = max_y;
max_y += output->modes[0].vertical_resolution;
--card->available_outputs;
}
}
cards_free(cards);
}
static void configure_display_single(struct MirDisplayConfiguration *conf, int output_num)
{
uint32_t num_connected = 0;
uint32_t output_to_enable = output_num;
for (uint32_t i = 0; i < conf->num_outputs; i++)
{
MirDisplayOutput *output = &conf->outputs[i];
if (output->connected && output->num_modes > 0)
++num_connected;
}
if (output_to_enable > num_connected)
output_to_enable = num_connected;
for (uint32_t i = 0; i < conf->num_outputs; i++)
{
MirDisplayOutput *output = &conf->outputs[i];
if (output->connected && output->num_modes > 0)
{
output->used = (--output_to_enable == 0);
output->current_mode = 0;
output->position_x = 0;
output->position_y = 0;
}
}
}
static void configure_display(struct ClientContext *context, ConfigurationMode mode,
int mode_data)
{
MirDisplayConfiguration *conf =
mir_connection_create_display_config(context->connection);
if (mode == configuration_mode_clone)
{
configure_display_clone(conf);
printf("Applying clone configuration: ");
}
else if (mode == configuration_mode_vertical)
{
configure_display_vertical(conf);
printf("Applying vertical configuration: ");
}
else if (mode == configuration_mode_horizontal)
{
configure_display_horizontal(conf);
printf("Applying horizontal configuration: ");
}
else if (mode == configuration_mode_single)
{
configure_display_single(conf, mode_data);
printf("Applying single configuration for output %d: ", mode_data);
}
if (apply_configuration(context->connection, conf))
{
context->mode = mode;
context->mode_data = mode_data;
}
mir_display_config_destroy(conf);
}
static void display_change_callback(MirConnection *connection, void *context)
{
(void)context;
printf("=== Display configuration changed === \n");
print_current_configuration(connection);
struct ClientContext *ctx = (struct ClientContext*) context;
ctx->reconfigure = 1;
}
static void handle_keyboard_event(struct ClientContext *ctx, MirKeyboardEvent const* event)
{
if (mir_keyboard_event_action(event) != mir_keyboard_action_up)
return;
xkb_keysym_t key_code = mir_keyboard_event_key_code(event);
if (key_code >= XKB_KEY_1 &&
key_code <= XKB_KEY_9)
{
configure_display(ctx, configuration_mode_single,
key_code - XKB_KEY_0);
}
switch (key_code)
{
case XKB_KEY_q:
ctx->running = 0;
break;
case XKB_KEY_c:
configure_display(ctx, configuration_mode_clone, 0);
break;
case XKB_KEY_h:
configure_display(ctx, configuration_mode_horizontal, 0);
break;
case XKB_KEY_v:
configure_display(ctx, configuration_mode_vertical, 0);
break;
case XKB_KEY_p:
print_current_configuration(ctx->connection);
break;
}
}
static void event_callback(
MirSurface* surface, MirEvent const* event, void* context)
{
(void) surface;
struct ClientContext *ctx = (struct ClientContext*) context;
if (mir_event_get_type(event) != mir_event_type_input)
return;
MirInputEvent const* input_event = mir_event_get_input_event(event);
if (mir_input_event_get_type(input_event) != mir_input_event_type_key)
return;
handle_keyboard_event(ctx, mir_input_event_get_keyboard_event(input_event));
}
int main(int argc, char *argv[])
{
unsigned int width = 256, height = 256;
if (!mir_eglapp_init(argc, argv, &width, &height))
{
printf("A demo client that allows changing the display configuration. While the client\n"
"has the focus, use the following keys to change and get information about the\n"
"display configuration:\n"
" c: clone outputs\n"
" h: arrange outputs horizontally in the virtual space\n"
" v: arrange outputs vertically in the virtual space\n"
" 1-9: enable only the Nth connected output (in the order returned by the hardware)\n"
" p: print current display configuration\n");
return 1;
}
MirConnection *connection = mir_eglapp_native_connection();
MirSurface *surface = mir_eglapp_native_surface();
struct ClientContext ctx = {connection, configuration_mode_unknown, 0, 1, 0};
mir_connection_set_display_config_change_callback(
connection, display_change_callback, &ctx);
mir_surface_set_event_handler(surface, event_callback, &ctx);
time_t start = time(NULL);
while (ctx.running && mir_eglapp_running())
{
time_t elapsed = time(NULL) - start;
int mod = elapsed % 3;
glClearColor(mod == 0 ? 1.0f : 0.0f,
mod == 1 ? 1.0f : 0.0f,
mod == 2 ? 1.0f : 0.0f,
1.0f);
glClear(GL_COLOR_BUFFER_BIT);
mir_eglapp_swap_buffers();
if (ctx.reconfigure)
{
configure_display(&ctx, ctx.mode, ctx.mode_data);
ctx.reconfigure = 0;
}
}
mir_eglapp_shutdown();
return 0;
}