/*
* 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: Daniel van Vugt
*/
#define MIR_INCLUDE_DEPRECATED_EVENT_HEADER
#include "eglapp.h"
#include "mir_toolkit/mir_client_library.h"
#include
#include
#include
#include
#include
#include
#include
float mir_eglapp_background_opacity = 1.0f;
static const char appname[] = "egldemo";
static MirConnection *connection;
static MirSurface *surface;
static EGLDisplay egldisplay;
static EGLSurface eglsurface;
static volatile sig_atomic_t running = 0;
#define CHECK(_cond, _err) \
if (!(_cond)) \
{ \
printf("%s\n", (_err)); \
return 0; \
}
void mir_eglapp_shutdown(void)
{
eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(egldisplay);
mir_surface_release_sync(surface);
surface = NULL;
mir_connection_release(connection);
connection = NULL;
}
static void shutdown(int signum)
{
if (running)
{
running = 0;
printf("Signal %d received. Good night.\n", signum);
}
}
mir_eglapp_bool mir_eglapp_running(void)
{
return running;
}
void mir_eglapp_swap_buffers(void)
{
EGLint width, height;
if (!running)
return;
eglSwapBuffers(egldisplay, eglsurface);
/*
* Querying the surface (actually the current buffer) dimensions here is
* the only truly safe way to be sure that the dimensions we think we
* have are those of the buffer being rendered to. But this should be
* improved in future; https://bugs.launchpad.net/mir/+bug/1194384
*/
if (eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, &width) &&
eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, &height))
{
glViewport(0, 0, width, height);
}
}
static void mir_eglapp_handle_input_event(MirInputEvent const* event)
{
if (mir_input_event_get_type(event) != mir_input_event_type_key)
return;
MirKeyInputEvent const* kev = mir_input_event_get_key_input_event(event);
if (mir_key_input_event_get_action(kev) != mir_key_input_event_action_up)
return;
if (mir_key_input_event_get_key_code(kev) != XKB_KEY_q)
return;
running = 0;
}
static void mir_eglapp_handle_surface_event(MirSurfaceEvent const* sev)
{
MirSurfaceAttrib attrib = mir_surface_event_get_attribute(sev);
if (attrib != mir_surface_attrib_visibility)
return;
switch (mir_surface_event_get_attribute_value(sev))
{
case mir_surface_visibility_exposed:
printf("Surface exposed\n");
break;
case mir_surface_visibility_occluded:
printf("Surface occluded\n");
break;
default:
break;
}
}
static void mir_eglapp_handle_event(MirSurface* surface, MirEvent const* ev, void* context)
{
(void) surface;
(void) context;
switch (mir_event_get_type(ev))
{
case mir_event_type_input:
mir_eglapp_handle_input_event(mir_event_get_input_event(ev));
break;
case mir_event_type_surface:
mir_eglapp_handle_surface_event(mir_event_get_surface_event(ev));
break;
case mir_event_type_resize:
/*
* FIXME: https://bugs.launchpad.net/mir/+bug/1194384
* It is unsafe to set the width and height here because we're in a
* different thread to that doing the rendering. So we either need
* support for event queuing (directing them to another thread) or
* full single-threaded callbacks. (LP: #1194384).
*/
printf("Resized to %dx%d\n", ev->resize.width, ev->resize.height);
break;
case mir_event_type_close_surface:
printf("Received close event from server.\n");
running = 0;
break;
default:
break;
}
}
static const MirDisplayOutput *find_active_output(
const MirDisplayConfiguration *conf)
{
const MirDisplayOutput *output = NULL;
int d;
for (d = 0; d < (int)conf->num_outputs; d++)
{
const MirDisplayOutput *out = conf->outputs + d;
if (out->used &&
out->connected &&
out->num_modes &&
out->current_mode < out->num_modes)
{
output = out;
break;
}
}
return output;
}
mir_eglapp_bool mir_eglapp_init(int argc, char *argv[],
unsigned int *width, unsigned int *height)
{
EGLint ctxattribs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
MirEventDelegate delegate =
{
mir_eglapp_handle_event,
NULL
};
EGLConfig eglconfig;
EGLint neglconfigs;
EGLContext eglctx;
EGLBoolean ok;
EGLint swapinterval = 1;
unsigned int output_id = mir_display_output_id_invalid;
char *mir_socket = NULL;
char const* cursor_name = mir_default_cursor_name;
if (argc > 1)
{
int i;
for (i = 1; i < argc; i++)
{
mir_eglapp_bool help = 0;
const char *arg = argv[i];
if (arg[0] == '-')
{
switch (arg[1])
{
case 'b':
{
float alpha = 1.0f;
arg += 2;
if (!arg[0] && i < argc-1)
{
i++;
arg = argv[i];
}
if (sscanf(arg, "%f", &alpha) == 1)
{
mir_eglapp_background_opacity = alpha;
}
else
{
printf("Invalid opacity value: %s\n", arg);
help = 1;
}
}
break;
case 'n':
swapinterval = 0;
break;
case 'o':
{
unsigned int the_id = 0;
arg += 2;
if (!arg[0] && i < argc-1)
{
i++;
arg = argv[i];
}
if (sscanf(arg, "%u", &the_id) == 1)
{
output_id = the_id;
}
else
{
printf("Invalid output ID: %s\n", arg);
help = 1;
}
}
break;
case 'f':
*width = 0;
*height = 0;
break;
case 's':
{
unsigned int w, h;
arg += 2;
if (!arg[0] && i < argc-1)
{
i++;
arg = argv[i];
}
if (sscanf(arg, "%ux%u", &w, &h) == 2)
{
*width = w;
*height = h;
}
else
{
printf("Invalid size: %s\n", arg);
help = 1;
}
}
break;
case 'm':
mir_socket = argv[++i];
break;
case 'c':
cursor_name = argv[++i];
break;
case 'q':
{
FILE *unused = freopen("/dev/null", "a", stdout);
(void)unused;
break;
}
case 'h':
default:
help = 1;
break;
}
}
else
{
help = 1;
}
if (help)
{
printf("Usage: %s []\n"
" -b Background opacity (0.0 - 1.0)\n"
" -h Show this help text\n"
" -f Force full screen\n"
" -o ID Force placement on output monitor ID\n"
" -n Don't sync to vblank\n"
" -m socket Mir server socket\n"
" -s WIDTHxHEIGHT Force surface size\n"
" -c name Request cursor image by name\n"
" -q Quiet mode (no messages output)\n"
, argv[0]);
return 0;
}
}
}
connection = mir_connect_sync(mir_socket, appname);
CHECK(mir_connection_is_valid(connection), "Can't get connection");
/* eglapps are interested in the screen size, so
use mir_connection_create_display_config */
MirDisplayConfiguration* display_config =
mir_connection_create_display_config(connection);
const MirDisplayOutput *output = find_active_output(display_config);
if (output == NULL)
{
printf("No active outputs found.\n");
return 0;
}
const MirDisplayMode *mode = &output->modes[output->current_mode];
unsigned int format[mir_pixel_formats];
unsigned int nformats;
mir_connection_get_available_surface_formats(connection,
format, mir_pixel_formats, &nformats);
MirPixelFormat pixel_format = format[0];
for (unsigned int f = 0; f < nformats; f++)
{
const int opaque = (format[f] == mir_pixel_format_xbgr_8888 ||
format[f] == mir_pixel_format_xrgb_8888 ||
format[f] == mir_pixel_format_bgr_888);
if ((mir_eglapp_background_opacity == 1.0f && opaque) ||
(mir_eglapp_background_opacity < 1.0f && !opaque))
{
pixel_format = format[f];
break;
}
}
printf("Current active output is %dx%d %+d%+d\n",
mode->horizontal_resolution, mode->vertical_resolution,
output->position_x, output->position_y);
if (*width == 0)
*width = mode->horizontal_resolution;
if (*height == 0)
*height = mode->vertical_resolution;
mir_display_config_destroy(display_config);
printf("Server supports %d of %d surface pixel formats. Using format: %d\n",
nformats, mir_pixel_formats, pixel_format);
unsigned int bpp = 8 * MIR_BYTES_PER_PIXEL(pixel_format);
EGLint attribs[] =
{
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
EGL_BUFFER_SIZE, bpp,
EGL_NONE
};
MirSurfaceSpec *spec =
mir_connection_create_spec_for_normal_surface(connection, *width, *height, pixel_format);
CHECK(spec != NULL, "Can't create a surface spec");
mir_surface_spec_set_name(spec, "eglappsurface");
if (output_id != mir_display_output_id_invalid)
mir_surface_spec_set_fullscreen_on_output(spec, output_id);
surface = mir_surface_create_sync(spec);
mir_surface_spec_release(spec);
CHECK(mir_surface_is_valid(surface), "Can't create a surface");
mir_surface_set_event_handler(surface, &delegate);
MirCursorConfiguration *conf = mir_cursor_configuration_from_name(cursor_name);
mir_surface_configure_cursor(surface, conf);
mir_cursor_configuration_destroy(conf);
egldisplay = eglGetDisplay(
mir_connection_get_egl_native_display(connection));
CHECK(egldisplay != EGL_NO_DISPLAY, "Can't eglGetDisplay");
ok = eglInitialize(egldisplay, NULL, NULL);
CHECK(ok, "Can't eglInitialize");
ok = eglChooseConfig(egldisplay, attribs, &eglconfig, 1, &neglconfigs);
CHECK(ok, "Could not eglChooseConfig");
CHECK(neglconfigs > 0, "No EGL config available");
eglsurface = eglCreateWindowSurface(egldisplay, eglconfig,
(EGLNativeWindowType)mir_surface_get_egl_native_window(surface),
NULL);
CHECK(eglsurface != EGL_NO_SURFACE, "eglCreateWindowSurface failed");
eglctx = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT,
ctxattribs);
CHECK(eglctx != EGL_NO_CONTEXT, "eglCreateContext failed");
ok = eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglctx);
CHECK(ok, "Can't eglMakeCurrent");
signal(SIGINT, shutdown);
signal(SIGTERM, shutdown);
signal(SIGHUP, shutdown);
printf("Surface %d DPI\n", mir_surface_get_dpi(surface));
eglSwapInterval(egldisplay, swapinterval);
running = 1;
return 1;
}
struct MirConnection* mir_eglapp_native_connection()
{
return connection;
}
struct MirSurface* mir_eglapp_native_surface()
{
return surface;
}